diff mbox

ASoC: tlv320aic31xx: add explicit support for tlv320dac31xx

Message ID 1474574803-4007-1-git-send-email-nikita.yoush@cogentembedded.com (mailing list archive)
State Accepted
Commit 5e72f824a138a5e6c5600412f307dc279e66ab7a
Headers show

Commit Message

Nikita Yushchenko Sept. 22, 2016, 8:06 p.m. UTC
tlv320dac31xx is a subset of tlv320aic31xx:
- it does not have MIC inputs and ADC, thus capture is not supported,
- it has analog inputs AIN1/AIN2 that can be mixed into output.

Although tlv320dac31xx does work with tlv320aic31xx driver, this setup
does register non-existent widgets and non-existent capture stream.
Thus userspace lists non-existent objects in user interfaces, an can
access these, causing operations with device registers that are
declared as "reserved" in tlv320dac31xx datasheet.

This patch fixes this situation by separating controls/widgets/routes
into common, aic31xx-specific, and dac31xx-specific parts. Only parts
that match actual hardware (as declared in "compatible" device tree
property) are registered.

Signed-off-by: Nikita Yushchenko <nikita.yoush@cogentembedded.com>
---
 sound/soc/codecs/tlv320aic31xx.c | 212 ++++++++++++++++++++++++++++-----------
 sound/soc/codecs/tlv320aic31xx.h |   2 +
 2 files changed, 158 insertions(+), 56 deletions(-)

Comments

Chris Healy Sept. 23, 2016, 12:23 a.m. UTC | #1
Tested-by: Chris Healy <cphealy@gmail.com>

This was tested on an i.MX6 platform with TLV320DAC3100 DAC.  No
non-existent capture devices show up any longer.

On Thu, Sep 22, 2016 at 1:06 PM, Nikita Yushchenko <
nikita.yoush@cogentembedded.com> wrote:

> tlv320dac31xx is a subset of tlv320aic31xx:
> - it does not have MIC inputs and ADC, thus capture is not supported,
> - it has analog inputs AIN1/AIN2 that can be mixed into output.
>
> Although tlv320dac31xx does work with tlv320aic31xx driver, this setup
> does register non-existent widgets and non-existent capture stream.
> Thus userspace lists non-existent objects in user interfaces, an can
> access these, causing operations with device registers that are
> declared as "reserved" in tlv320dac31xx datasheet.
>
> This patch fixes this situation by separating controls/widgets/routes
> into common, aic31xx-specific, and dac31xx-specific parts. Only parts
> that match actual hardware (as declared in "compatible" device tree
> property) are registered.
>
> Signed-off-by: Nikita Yushchenko <nikita.yoush@cogentembedded.com>
> ---
>  sound/soc/codecs/tlv320aic31xx.c | 212 ++++++++++++++++++++++++++++--
> ---------
>  sound/soc/codecs/tlv320aic31xx.h |   2 +
>  2 files changed, 158 insertions(+), 56 deletions(-)
>
> diff --git a/sound/soc/codecs/tlv320aic31xx.c b/sound/soc/codecs/
> tlv320aic31xx.c
> index 3c5e1df..fb7d648 100644
> --- a/sound/soc/codecs/tlv320aic31xx.c
> +++ b/sound/soc/codecs/tlv320aic31xx.c
> @@ -273,10 +273,20 @@ static const DECLARE_TLV_DB_SCALE(sp_vol_tlv,
> -6350, 50, 0);
>  /*
>   * controls to be exported to the user space
>   */
> -static const struct snd_kcontrol_new aic31xx_snd_controls[] = {
> +static const struct snd_kcontrol_new common31xx_snd_controls[] = {
>         SOC_DOUBLE_R_S_TLV("DAC Playback Volume", AIC31XX_LDACVOL,
>                            AIC31XX_RDACVOL, 0, -127, 48, 7, 0,
> dac_vol_tlv),
>
> +       SOC_DOUBLE_R("HP Driver Playback Switch", AIC31XX_HPLGAIN,
> +                    AIC31XX_HPRGAIN, 2, 1, 0),
> +       SOC_DOUBLE_R_TLV("HP Driver Playback Volume", AIC31XX_HPLGAIN,
> +                        AIC31XX_HPRGAIN, 3, 0x09, 0, hp_drv_tlv),
> +
> +       SOC_DOUBLE_R_TLV("HP Analog Playback Volume", AIC31XX_LANALOGHPL,
> +                        AIC31XX_RANALOGHPR, 0, 0x7F, 1, hp_vol_tlv),
> +};
> +
> +static const struct snd_kcontrol_new aic31xx_snd_controls[] = {
>         SOC_SINGLE_TLV("ADC Fine Capture Volume", AIC31XX_ADCFGA, 4, 4, 1,
>                        adc_fgain_tlv),
>
> @@ -286,14 +296,6 @@ static const struct snd_kcontrol_new
> aic31xx_snd_controls[] = {
>
>         SOC_SINGLE_TLV("Mic PGA Capture Volume", AIC31XX_MICPGA, 0,
>                        119, 0, mic_pga_tlv),
> -
> -       SOC_DOUBLE_R("HP Driver Playback Switch", AIC31XX_HPLGAIN,
> -                    AIC31XX_HPRGAIN, 2, 1, 0),
> -       SOC_DOUBLE_R_TLV("HP Driver Playback Volume", AIC31XX_HPLGAIN,
> -                        AIC31XX_HPRGAIN, 3, 0x09, 0, hp_drv_tlv),
> -
> -       SOC_DOUBLE_R_TLV("HP Analog Playback Volume", AIC31XX_LANALOGHPL,
> -                        AIC31XX_RANALOGHPR, 0, 0x7F, 1, hp_vol_tlv),
>  };
>
>  static const struct snd_kcontrol_new aic311x_snd_controls[] = {
> @@ -397,17 +399,28 @@ static int aic31xx_dapm_power_event(struct
> snd_soc_dapm_widget *w,
>         return 0;
>  }
>
> -static const struct snd_kcontrol_new left_output_switches[] = {
> +static const struct snd_kcontrol_new aic31xx_left_output_switches[] = {
>         SOC_DAPM_SINGLE("From Left DAC", AIC31XX_DACMIXERROUTE, 6, 1, 0),
>         SOC_DAPM_SINGLE("From MIC1LP", AIC31XX_DACMIXERROUTE, 5, 1, 0),
>         SOC_DAPM_SINGLE("From MIC1RP", AIC31XX_DACMIXERROUTE, 4, 1, 0),
>  };
>
> -static const struct snd_kcontrol_new right_output_switches[] = {
> +static const struct snd_kcontrol_new aic31xx_right_output_switches[] = {
>         SOC_DAPM_SINGLE("From Right DAC", AIC31XX_DACMIXERROUTE, 2, 1, 0),
>         SOC_DAPM_SINGLE("From MIC1RP", AIC31XX_DACMIXERROUTE, 1, 1, 0),
>  };
>
> +static const struct snd_kcontrol_new dac31xx_left_output_switches[] = {
> +       SOC_DAPM_SINGLE("From Left DAC", AIC31XX_DACMIXERROUTE, 6, 1, 0),
> +       SOC_DAPM_SINGLE("From AIN1", AIC31XX_DACMIXERROUTE, 5, 1, 0),
> +       SOC_DAPM_SINGLE("From AIN2", AIC31XX_DACMIXERROUTE, 4, 1, 0),
> +};
> +
> +static const struct snd_kcontrol_new dac31xx_right_output_switches[] = {
> +       SOC_DAPM_SINGLE("From Right DAC", AIC31XX_DACMIXERROUTE, 2, 1, 0),
> +       SOC_DAPM_SINGLE("From AIN2", AIC31XX_DACMIXERROUTE, 1, 1, 0),
> +};
> +
>  static const struct snd_kcontrol_new p_term_mic1lp =
>         SOC_DAPM_ENUM("MIC1LP P-Terminal", mic1lp_p_enum);
>
> @@ -457,7 +470,7 @@ static int mic_bias_event(struct snd_soc_dapm_widget
> *w,
>         return 0;
>  }
>
> -static const struct snd_soc_dapm_widget aic31xx_dapm_widgets[] = {
> +static const struct snd_soc_dapm_widget common31xx_dapm_widgets[] = {
>         SND_SOC_DAPM_AIF_IN("DAC IN", "DAC Playback", 0, SND_SOC_NOPM, 0,
> 0),
>
>         SND_SOC_DAPM_MUX("DAC Left Input",
> @@ -473,14 +486,7 @@ static const struct snd_soc_dapm_widget
> aic31xx_dapm_widgets[] = {
>                            AIC31XX_DACSETUP, 6, 0,
> aic31xx_dapm_power_event,
>                            SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_POST_PMD),
>
> -       /* Output Mixers */
> -       SND_SOC_DAPM_MIXER("Output Left", SND_SOC_NOPM, 0, 0,
> -                          left_output_switches,
> -                          ARRAY_SIZE(left_output_switches)),
> -       SND_SOC_DAPM_MIXER("Output Right", SND_SOC_NOPM, 0, 0,
> -                          right_output_switches,
> -                          ARRAY_SIZE(right_output_switches)),
> -
> +       /* HP */
>         SND_SOC_DAPM_SWITCH("HP Left", SND_SOC_NOPM, 0, 0,
>                             &aic31xx_dapm_hpl_switch),
>         SND_SOC_DAPM_SWITCH("HP Right", SND_SOC_NOPM, 0, 0,
> @@ -494,10 +500,34 @@ static const struct snd_soc_dapm_widget
> aic31xx_dapm_widgets[] = {
>                                NULL, 0, aic31xx_dapm_power_event,
>                                SND_SOC_DAPM_POST_PMD |
> SND_SOC_DAPM_POST_PMU),
>
> -       /* ADC */
> -       SND_SOC_DAPM_ADC_E("ADC", "Capture", AIC31XX_ADCSETUP, 7, 0,
> -                          aic31xx_dapm_power_event, SND_SOC_DAPM_POST_PMU
> |
> -                          SND_SOC_DAPM_POST_PMD),
> +       /* Mic Bias */
> +       SND_SOC_DAPM_SUPPLY("MICBIAS", SND_SOC_NOPM, 0, 0, mic_bias_event,
> +                           SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_PRE_PMD),
> +
> +       /* Outputs */
> +       SND_SOC_DAPM_OUTPUT("HPL"),
> +       SND_SOC_DAPM_OUTPUT("HPR"),
> +};
> +
> +static const struct snd_soc_dapm_widget dac31xx_dapm_widgets[] = {
> +       /* Inputs */
> +       SND_SOC_DAPM_INPUT("AIN1"),
> +       SND_SOC_DAPM_INPUT("AIN2"),
> +
> +       /* Output Mixers */
> +       SND_SOC_DAPM_MIXER("Output Left", SND_SOC_NOPM, 0, 0,
> +                          dac31xx_left_output_switches,
> +                          ARRAY_SIZE(dac31xx_left_output_switches)),
> +       SND_SOC_DAPM_MIXER("Output Right", SND_SOC_NOPM, 0, 0,
> +                          dac31xx_right_output_switches,
> +                          ARRAY_SIZE(dac31xx_right_output_switches)),
> +};
> +
> +static const struct snd_soc_dapm_widget aic31xx_dapm_widgets[] = {
> +       /* Inputs */
> +       SND_SOC_DAPM_INPUT("MIC1LP"),
> +       SND_SOC_DAPM_INPUT("MIC1RP"),
> +       SND_SOC_DAPM_INPUT("MIC1LM"),
>
>         /* Input Selection to MIC_PGA */
>         SND_SOC_DAPM_MUX("MIC1LP P-Terminal", SND_SOC_NOPM, 0, 0,
> @@ -507,24 +537,25 @@ static const struct snd_soc_dapm_widget
> aic31xx_dapm_widgets[] = {
>         SND_SOC_DAPM_MUX("MIC1LM P-Terminal", SND_SOC_NOPM, 0, 0,
>                          &p_term_mic1lm),
>
> +       /* ADC */
> +       SND_SOC_DAPM_ADC_E("ADC", "Capture", AIC31XX_ADCSETUP, 7, 0,
> +                          aic31xx_dapm_power_event, SND_SOC_DAPM_POST_PMU
> |
> +                          SND_SOC_DAPM_POST_PMD),
> +
>         SND_SOC_DAPM_MUX("MIC1LM M-Terminal", SND_SOC_NOPM, 0, 0,
>                          &m_term_mic1lm),
> +
>         /* Enabling & Disabling MIC Gain Ctl */
>         SND_SOC_DAPM_PGA("MIC_GAIN_CTL", AIC31XX_MICPGA,
>                          7, 1, NULL, 0),
>
> -       /* Mic Bias */
> -       SND_SOC_DAPM_SUPPLY("MICBIAS", SND_SOC_NOPM, 0, 0, mic_bias_event,
> -                           SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_PRE_PMD),
> -
> -       /* Outputs */
> -       SND_SOC_DAPM_OUTPUT("HPL"),
> -       SND_SOC_DAPM_OUTPUT("HPR"),
> -
> -       /* Inputs */
> -       SND_SOC_DAPM_INPUT("MIC1LP"),
> -       SND_SOC_DAPM_INPUT("MIC1RP"),
> -       SND_SOC_DAPM_INPUT("MIC1LM"),
> +       /* Output Mixers */
> +       SND_SOC_DAPM_MIXER("Output Left", SND_SOC_NOPM, 0, 0,
> +                          aic31xx_left_output_switches,
> +                          ARRAY_SIZE(aic31xx_left_output_switches)),
> +       SND_SOC_DAPM_MIXER("Output Right", SND_SOC_NOPM, 0, 0,
> +                          aic31xx_right_output_switches,
> +                          ARRAY_SIZE(aic31xx_right_output_switches)),
>  };
>
>  static const struct snd_soc_dapm_widget aic311x_dapm_widgets[] = {
> @@ -554,7 +585,7 @@ static const struct snd_soc_dapm_widget
> aic310x_dapm_widgets[] = {
>  };
>
>  static const struct snd_soc_dapm_route
> -aic31xx_audio_map[] = {
> +common31xx_audio_map[] = {
>         /* DAC Input Routing */
>         {"DAC Left Input", "Left Data", "DAC IN"},
>         {"DAC Left Input", "Right Data", "DAC IN"},
> @@ -565,6 +596,31 @@ aic31xx_audio_map[] = {
>         {"DAC Left", NULL, "DAC Left Input"},
>         {"DAC Right", NULL, "DAC Right Input"},
>
> +       /* HPL path */
> +       {"HP Left", "Switch", "Output Left"},
> +       {"HPL Driver", NULL, "HP Left"},
> +       {"HPL", NULL, "HPL Driver"},
> +
> +       /* HPR path */
> +       {"HP Right", "Switch", "Output Right"},
> +       {"HPR Driver", NULL, "HP Right"},
> +       {"HPR", NULL, "HPR Driver"},
> +};
> +
> +static const struct snd_soc_dapm_route
> +dac31xx_audio_map[] = {
> +       /* Left Output */
> +       {"Output Left", "From Left DAC", "DAC Left"},
> +       {"Output Left", "From AIN1", "AIN1"},
> +       {"Output Left", "From AIN2", "AIN2"},
> +
> +       /* Right Output */
> +       {"Output Right", "From Right DAC", "DAC Right"},
> +       {"Output Right", "From AIN2", "AIN2"},
> +};
> +
> +static const struct snd_soc_dapm_route
> +aic31xx_audio_map[] = {
>         /* Mic input */
>         {"MIC1LP P-Terminal", "FFR 10 Ohm", "MIC1LP"},
>         {"MIC1LP P-Terminal", "FFR 20 Ohm", "MIC1LP"},
> @@ -595,16 +651,6 @@ aic31xx_audio_map[] = {
>         /* Right Output */
>         {"Output Right", "From Right DAC", "DAC Right"},
>         {"Output Right", "From MIC1RP", "MIC1RP"},
> -
> -       /* HPL path */
> -       {"HP Left", "Switch", "Output Left"},
> -       {"HPL Driver", NULL, "HP Left"},
> -       {"HPL", NULL, "HPL Driver"},
> -
> -       /* HPR path */
> -       {"HP Right", "Switch", "Output Right"},
> -       {"HPR Driver", NULL, "HP Right"},
> -       {"HPR", NULL, "HPR Driver"},
>  };
>
>  static const struct snd_soc_dapm_route
> @@ -633,6 +679,13 @@ static int aic31xx_add_controls(struct snd_soc_codec
> *codec)
>         int ret = 0;
>         struct aic31xx_priv *aic31xx = snd_soc_codec_get_drvdata(codec);
>
> +       if (!(aic31xx->pdata.codec_type & DAC31XX_BIT))
> +               ret = snd_soc_add_codec_controls(
> +                       codec, aic31xx_snd_controls,
> +                       ARRAY_SIZE(aic31xx_snd_controls));
> +       if (ret)
> +               return ret;
> +
>         if (aic31xx->pdata.codec_type & AIC31XX_STEREO_CLASS_D_BIT)
>                 ret = snd_soc_add_codec_controls(
>                         codec, aic311x_snd_controls,
> @@ -651,6 +704,30 @@ static int aic31xx_add_widgets(struct snd_soc_codec
> *codec)
>         struct aic31xx_priv *aic31xx = snd_soc_codec_get_drvdata(codec);
>         int ret = 0;
>
> +       if (aic31xx->pdata.codec_type & DAC31XX_BIT) {
> +               ret = snd_soc_dapm_new_controls(
> +                       dapm, dac31xx_dapm_widgets,
> +                       ARRAY_SIZE(dac31xx_dapm_widgets));
> +               if (ret)
> +                       return ret;
> +
> +               ret = snd_soc_dapm_add_routes(dapm, dac31xx_audio_map,
> +
>  ARRAY_SIZE(dac31xx_audio_map));
> +               if (ret)
> +                       return ret;
> +       } else {
> +               ret = snd_soc_dapm_new_controls(
> +                       dapm, aic31xx_dapm_widgets,
> +                       ARRAY_SIZE(aic31xx_dapm_widgets));
> +               if (ret)
> +                       return ret;
> +
> +               ret = snd_soc_dapm_add_routes(dapm, aic31xx_audio_map,
> +
>  ARRAY_SIZE(aic31xx_audio_map));
> +               if (ret)
> +                       return ret;
> +       }
> +
>         if (aic31xx->pdata.codec_type & AIC31XX_STEREO_CLASS_D_BIT) {
>                 ret = snd_soc_dapm_new_controls(
>                         dapm, aic311x_dapm_widgets,
> @@ -1114,12 +1191,12 @@ static struct snd_soc_codec_driver
> soc_codec_driver_aic31xx = {
>         .set_bias_level         = aic31xx_set_bias_level,
>         .suspend_bias_off       = true,
>
> -       .controls               = aic31xx_snd_controls,
> -       .num_controls           = ARRAY_SIZE(aic31xx_snd_controls),
> -       .dapm_widgets           = aic31xx_dapm_widgets,
> -       .num_dapm_widgets       = ARRAY_SIZE(aic31xx_dapm_widgets),
> -       .dapm_routes            = aic31xx_audio_map,
> -       .num_dapm_routes        = ARRAY_SIZE(aic31xx_audio_map),
> +       .controls               = common31xx_snd_controls,
> +       .num_controls           = ARRAY_SIZE(common31xx_snd_controls),
> +       .dapm_widgets           = common31xx_dapm_widgets,
> +       .num_dapm_widgets       = ARRAY_SIZE(common31xx_dapm_widgets),
> +       .dapm_routes            = common31xx_audio_map,
> +       .num_dapm_routes        = ARRAY_SIZE(common31xx_audio_map),
>  };
>
>  static const struct snd_soc_dai_ops aic31xx_dai_ops = {
> @@ -1129,6 +1206,21 @@ static const struct snd_soc_dai_ops aic31xx_dai_ops
> = {
>         .digital_mute   = aic31xx_dac_mute,
>  };
>
> +static struct snd_soc_dai_driver dac31xx_dai_driver[] = {
> +       {
> +               .name = "tlv32dac31xx-hifi",
> +               .playback = {
> +                       .stream_name     = "Playback",
> +                       .channels_min    = 1,
> +                       .channels_max    = 2,
> +                       .rates           = AIC31XX_RATES,
> +                       .formats         = AIC31XX_FORMATS,
> +               },
> +               .ops = &aic31xx_dai_ops,
> +               .symmetric_rates = 1,
> +       }
> +};
> +
>  static struct snd_soc_dai_driver aic31xx_dai_driver[] = {
>         {
>                 .name = "tlv320aic31xx-hifi",
> @@ -1259,9 +1351,16 @@ static int aic31xx_i2c_probe(struct i2c_client *i2c,
>         if (ret)
>                 return ret;
>
> -       return snd_soc_register_codec(&i2c->dev,
> &soc_codec_driver_aic31xx,
> -                                    aic31xx_dai_driver,
> -                                    ARRAY_SIZE(aic31xx_dai_driver));
> +       if (aic31xx->pdata.codec_type & DAC31XX_BIT)
> +               return snd_soc_register_codec(&i2c->dev,
> +                               &soc_codec_driver_aic31xx,
> +                               dac31xx_dai_driver,
> +                               ARRAY_SIZE(dac31xx_dai_driver));
> +       else
> +               return snd_soc_register_codec(&i2c->dev,
> +                               &soc_codec_driver_aic31xx,
> +                               aic31xx_dai_driver,
> +                               ARRAY_SIZE(aic31xx_dai_driver));
>  }
>
>  static int aic31xx_i2c_remove(struct i2c_client *i2c)
> @@ -1277,6 +1376,7 @@ static const struct i2c_device_id aic31xx_i2c_id[] =
> {
>         { "tlv320aic3110", AIC3110 },
>         { "tlv320aic3120", AIC3120 },
>         { "tlv320aic3111", AIC3111 },
> +       { "tlv320dac3100", DAC3100 },
>         { }
>  };
>  MODULE_DEVICE_TABLE(i2c, aic31xx_i2c_id);
> diff --git a/sound/soc/codecs/tlv320aic31xx.h b/sound/soc/codecs/
> tlv320aic31xx.h
> index ac9b146..5acd5b6 100644
> --- a/sound/soc/codecs/tlv320aic31xx.h
> +++ b/sound/soc/codecs/tlv320aic31xx.h
> @@ -24,12 +24,14 @@
>
>  #define AIC31XX_STEREO_CLASS_D_BIT     0x1
>  #define AIC31XX_MINIDSP_BIT            0x2
> +#define DAC31XX_BIT                    0x4
>
>  enum aic31xx_type {
>         AIC3100 = 0,
>         AIC3110 = AIC31XX_STEREO_CLASS_D_BIT,
>         AIC3120 = AIC31XX_MINIDSP_BIT,
>         AIC3111 = (AIC31XX_STEREO_CLASS_D_BIT | AIC31XX_MINIDSP_BIT),
> +       DAC3100 = DAC31XX_BIT,
>  };
>
>  struct aic31xx_pdata {
> --
> 2.1.4
>
>
diff mbox

Patch

diff --git a/sound/soc/codecs/tlv320aic31xx.c b/sound/soc/codecs/tlv320aic31xx.c
index 3c5e1df..fb7d648 100644
--- a/sound/soc/codecs/tlv320aic31xx.c
+++ b/sound/soc/codecs/tlv320aic31xx.c
@@ -273,10 +273,20 @@  static const DECLARE_TLV_DB_SCALE(sp_vol_tlv, -6350, 50, 0);
 /*
  * controls to be exported to the user space
  */
-static const struct snd_kcontrol_new aic31xx_snd_controls[] = {
+static const struct snd_kcontrol_new common31xx_snd_controls[] = {
 	SOC_DOUBLE_R_S_TLV("DAC Playback Volume", AIC31XX_LDACVOL,
 			   AIC31XX_RDACVOL, 0, -127, 48, 7, 0, dac_vol_tlv),
 
+	SOC_DOUBLE_R("HP Driver Playback Switch", AIC31XX_HPLGAIN,
+		     AIC31XX_HPRGAIN, 2, 1, 0),
+	SOC_DOUBLE_R_TLV("HP Driver Playback Volume", AIC31XX_HPLGAIN,
+			 AIC31XX_HPRGAIN, 3, 0x09, 0, hp_drv_tlv),
+
+	SOC_DOUBLE_R_TLV("HP Analog Playback Volume", AIC31XX_LANALOGHPL,
+			 AIC31XX_RANALOGHPR, 0, 0x7F, 1, hp_vol_tlv),
+};
+
+static const struct snd_kcontrol_new aic31xx_snd_controls[] = {
 	SOC_SINGLE_TLV("ADC Fine Capture Volume", AIC31XX_ADCFGA, 4, 4, 1,
 		       adc_fgain_tlv),
 
@@ -286,14 +296,6 @@  static const struct snd_kcontrol_new aic31xx_snd_controls[] = {
 
 	SOC_SINGLE_TLV("Mic PGA Capture Volume", AIC31XX_MICPGA, 0,
 		       119, 0, mic_pga_tlv),
-
-	SOC_DOUBLE_R("HP Driver Playback Switch", AIC31XX_HPLGAIN,
-		     AIC31XX_HPRGAIN, 2, 1, 0),
-	SOC_DOUBLE_R_TLV("HP Driver Playback Volume", AIC31XX_HPLGAIN,
-			 AIC31XX_HPRGAIN, 3, 0x09, 0, hp_drv_tlv),
-
-	SOC_DOUBLE_R_TLV("HP Analog Playback Volume", AIC31XX_LANALOGHPL,
-			 AIC31XX_RANALOGHPR, 0, 0x7F, 1, hp_vol_tlv),
 };
 
 static const struct snd_kcontrol_new aic311x_snd_controls[] = {
@@ -397,17 +399,28 @@  static int aic31xx_dapm_power_event(struct snd_soc_dapm_widget *w,
 	return 0;
 }
 
-static const struct snd_kcontrol_new left_output_switches[] = {
+static const struct snd_kcontrol_new aic31xx_left_output_switches[] = {
 	SOC_DAPM_SINGLE("From Left DAC", AIC31XX_DACMIXERROUTE, 6, 1, 0),
 	SOC_DAPM_SINGLE("From MIC1LP", AIC31XX_DACMIXERROUTE, 5, 1, 0),
 	SOC_DAPM_SINGLE("From MIC1RP", AIC31XX_DACMIXERROUTE, 4, 1, 0),
 };
 
-static const struct snd_kcontrol_new right_output_switches[] = {
+static const struct snd_kcontrol_new aic31xx_right_output_switches[] = {
 	SOC_DAPM_SINGLE("From Right DAC", AIC31XX_DACMIXERROUTE, 2, 1, 0),
 	SOC_DAPM_SINGLE("From MIC1RP", AIC31XX_DACMIXERROUTE, 1, 1, 0),
 };
 
+static const struct snd_kcontrol_new dac31xx_left_output_switches[] = {
+	SOC_DAPM_SINGLE("From Left DAC", AIC31XX_DACMIXERROUTE, 6, 1, 0),
+	SOC_DAPM_SINGLE("From AIN1", AIC31XX_DACMIXERROUTE, 5, 1, 0),
+	SOC_DAPM_SINGLE("From AIN2", AIC31XX_DACMIXERROUTE, 4, 1, 0),
+};
+
+static const struct snd_kcontrol_new dac31xx_right_output_switches[] = {
+	SOC_DAPM_SINGLE("From Right DAC", AIC31XX_DACMIXERROUTE, 2, 1, 0),
+	SOC_DAPM_SINGLE("From AIN2", AIC31XX_DACMIXERROUTE, 1, 1, 0),
+};
+
 static const struct snd_kcontrol_new p_term_mic1lp =
 	SOC_DAPM_ENUM("MIC1LP P-Terminal", mic1lp_p_enum);
 
@@ -457,7 +470,7 @@  static int mic_bias_event(struct snd_soc_dapm_widget *w,
 	return 0;
 }
 
-static const struct snd_soc_dapm_widget aic31xx_dapm_widgets[] = {
+static const struct snd_soc_dapm_widget common31xx_dapm_widgets[] = {
 	SND_SOC_DAPM_AIF_IN("DAC IN", "DAC Playback", 0, SND_SOC_NOPM, 0, 0),
 
 	SND_SOC_DAPM_MUX("DAC Left Input",
@@ -473,14 +486,7 @@  static const struct snd_soc_dapm_widget aic31xx_dapm_widgets[] = {
 			   AIC31XX_DACSETUP, 6, 0, aic31xx_dapm_power_event,
 			   SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_POST_PMD),
 
-	/* Output Mixers */
-	SND_SOC_DAPM_MIXER("Output Left", SND_SOC_NOPM, 0, 0,
-			   left_output_switches,
-			   ARRAY_SIZE(left_output_switches)),
-	SND_SOC_DAPM_MIXER("Output Right", SND_SOC_NOPM, 0, 0,
-			   right_output_switches,
-			   ARRAY_SIZE(right_output_switches)),
-
+	/* HP */
 	SND_SOC_DAPM_SWITCH("HP Left", SND_SOC_NOPM, 0, 0,
 			    &aic31xx_dapm_hpl_switch),
 	SND_SOC_DAPM_SWITCH("HP Right", SND_SOC_NOPM, 0, 0,
@@ -494,10 +500,34 @@  static const struct snd_soc_dapm_widget aic31xx_dapm_widgets[] = {
 			       NULL, 0, aic31xx_dapm_power_event,
 			       SND_SOC_DAPM_POST_PMD | SND_SOC_DAPM_POST_PMU),
 
-	/* ADC */
-	SND_SOC_DAPM_ADC_E("ADC", "Capture", AIC31XX_ADCSETUP, 7, 0,
-			   aic31xx_dapm_power_event, SND_SOC_DAPM_POST_PMU |
-			   SND_SOC_DAPM_POST_PMD),
+	/* Mic Bias */
+	SND_SOC_DAPM_SUPPLY("MICBIAS", SND_SOC_NOPM, 0, 0, mic_bias_event,
+			    SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_PRE_PMD),
+
+	/* Outputs */
+	SND_SOC_DAPM_OUTPUT("HPL"),
+	SND_SOC_DAPM_OUTPUT("HPR"),
+};
+
+static const struct snd_soc_dapm_widget dac31xx_dapm_widgets[] = {
+	/* Inputs */
+	SND_SOC_DAPM_INPUT("AIN1"),
+	SND_SOC_DAPM_INPUT("AIN2"),
+
+	/* Output Mixers */
+	SND_SOC_DAPM_MIXER("Output Left", SND_SOC_NOPM, 0, 0,
+			   dac31xx_left_output_switches,
+			   ARRAY_SIZE(dac31xx_left_output_switches)),
+	SND_SOC_DAPM_MIXER("Output Right", SND_SOC_NOPM, 0, 0,
+			   dac31xx_right_output_switches,
+			   ARRAY_SIZE(dac31xx_right_output_switches)),
+};
+
+static const struct snd_soc_dapm_widget aic31xx_dapm_widgets[] = {
+	/* Inputs */
+	SND_SOC_DAPM_INPUT("MIC1LP"),
+	SND_SOC_DAPM_INPUT("MIC1RP"),
+	SND_SOC_DAPM_INPUT("MIC1LM"),
 
 	/* Input Selection to MIC_PGA */
 	SND_SOC_DAPM_MUX("MIC1LP P-Terminal", SND_SOC_NOPM, 0, 0,
@@ -507,24 +537,25 @@  static const struct snd_soc_dapm_widget aic31xx_dapm_widgets[] = {
 	SND_SOC_DAPM_MUX("MIC1LM P-Terminal", SND_SOC_NOPM, 0, 0,
 			 &p_term_mic1lm),
 
+	/* ADC */
+	SND_SOC_DAPM_ADC_E("ADC", "Capture", AIC31XX_ADCSETUP, 7, 0,
+			   aic31xx_dapm_power_event, SND_SOC_DAPM_POST_PMU |
+			   SND_SOC_DAPM_POST_PMD),
+
 	SND_SOC_DAPM_MUX("MIC1LM M-Terminal", SND_SOC_NOPM, 0, 0,
 			 &m_term_mic1lm),
+
 	/* Enabling & Disabling MIC Gain Ctl */
 	SND_SOC_DAPM_PGA("MIC_GAIN_CTL", AIC31XX_MICPGA,
 			 7, 1, NULL, 0),
 
-	/* Mic Bias */
-	SND_SOC_DAPM_SUPPLY("MICBIAS", SND_SOC_NOPM, 0, 0, mic_bias_event,
-			    SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_PRE_PMD),
-
-	/* Outputs */
-	SND_SOC_DAPM_OUTPUT("HPL"),
-	SND_SOC_DAPM_OUTPUT("HPR"),
-
-	/* Inputs */
-	SND_SOC_DAPM_INPUT("MIC1LP"),
-	SND_SOC_DAPM_INPUT("MIC1RP"),
-	SND_SOC_DAPM_INPUT("MIC1LM"),
+	/* Output Mixers */
+	SND_SOC_DAPM_MIXER("Output Left", SND_SOC_NOPM, 0, 0,
+			   aic31xx_left_output_switches,
+			   ARRAY_SIZE(aic31xx_left_output_switches)),
+	SND_SOC_DAPM_MIXER("Output Right", SND_SOC_NOPM, 0, 0,
+			   aic31xx_right_output_switches,
+			   ARRAY_SIZE(aic31xx_right_output_switches)),
 };
 
 static const struct snd_soc_dapm_widget aic311x_dapm_widgets[] = {
@@ -554,7 +585,7 @@  static const struct snd_soc_dapm_widget aic310x_dapm_widgets[] = {
 };
 
 static const struct snd_soc_dapm_route
-aic31xx_audio_map[] = {
+common31xx_audio_map[] = {
 	/* DAC Input Routing */
 	{"DAC Left Input", "Left Data", "DAC IN"},
 	{"DAC Left Input", "Right Data", "DAC IN"},
@@ -565,6 +596,31 @@  aic31xx_audio_map[] = {
 	{"DAC Left", NULL, "DAC Left Input"},
 	{"DAC Right", NULL, "DAC Right Input"},
 
+	/* HPL path */
+	{"HP Left", "Switch", "Output Left"},
+	{"HPL Driver", NULL, "HP Left"},
+	{"HPL", NULL, "HPL Driver"},
+
+	/* HPR path */
+	{"HP Right", "Switch", "Output Right"},
+	{"HPR Driver", NULL, "HP Right"},
+	{"HPR", NULL, "HPR Driver"},
+};
+
+static const struct snd_soc_dapm_route
+dac31xx_audio_map[] = {
+	/* Left Output */
+	{"Output Left", "From Left DAC", "DAC Left"},
+	{"Output Left", "From AIN1", "AIN1"},
+	{"Output Left", "From AIN2", "AIN2"},
+
+	/* Right Output */
+	{"Output Right", "From Right DAC", "DAC Right"},
+	{"Output Right", "From AIN2", "AIN2"},
+};
+
+static const struct snd_soc_dapm_route
+aic31xx_audio_map[] = {
 	/* Mic input */
 	{"MIC1LP P-Terminal", "FFR 10 Ohm", "MIC1LP"},
 	{"MIC1LP P-Terminal", "FFR 20 Ohm", "MIC1LP"},
@@ -595,16 +651,6 @@  aic31xx_audio_map[] = {
 	/* Right Output */
 	{"Output Right", "From Right DAC", "DAC Right"},
 	{"Output Right", "From MIC1RP", "MIC1RP"},
-
-	/* HPL path */
-	{"HP Left", "Switch", "Output Left"},
-	{"HPL Driver", NULL, "HP Left"},
-	{"HPL", NULL, "HPL Driver"},
-
-	/* HPR path */
-	{"HP Right", "Switch", "Output Right"},
-	{"HPR Driver", NULL, "HP Right"},
-	{"HPR", NULL, "HPR Driver"},
 };
 
 static const struct snd_soc_dapm_route
@@ -633,6 +679,13 @@  static int aic31xx_add_controls(struct snd_soc_codec *codec)
 	int ret = 0;
 	struct aic31xx_priv *aic31xx = snd_soc_codec_get_drvdata(codec);
 
+	if (!(aic31xx->pdata.codec_type & DAC31XX_BIT))
+		ret = snd_soc_add_codec_controls(
+			codec, aic31xx_snd_controls,
+			ARRAY_SIZE(aic31xx_snd_controls));
+	if (ret)
+		return ret;
+
 	if (aic31xx->pdata.codec_type & AIC31XX_STEREO_CLASS_D_BIT)
 		ret = snd_soc_add_codec_controls(
 			codec, aic311x_snd_controls,
@@ -651,6 +704,30 @@  static int aic31xx_add_widgets(struct snd_soc_codec *codec)
 	struct aic31xx_priv *aic31xx = snd_soc_codec_get_drvdata(codec);
 	int ret = 0;
 
+	if (aic31xx->pdata.codec_type & DAC31XX_BIT) {
+		ret = snd_soc_dapm_new_controls(
+			dapm, dac31xx_dapm_widgets,
+			ARRAY_SIZE(dac31xx_dapm_widgets));
+		if (ret)
+			return ret;
+
+		ret = snd_soc_dapm_add_routes(dapm, dac31xx_audio_map,
+					      ARRAY_SIZE(dac31xx_audio_map));
+		if (ret)
+			return ret;
+	} else {
+		ret = snd_soc_dapm_new_controls(
+			dapm, aic31xx_dapm_widgets,
+			ARRAY_SIZE(aic31xx_dapm_widgets));
+		if (ret)
+			return ret;
+
+		ret = snd_soc_dapm_add_routes(dapm, aic31xx_audio_map,
+					      ARRAY_SIZE(aic31xx_audio_map));
+		if (ret)
+			return ret;
+	}
+
 	if (aic31xx->pdata.codec_type & AIC31XX_STEREO_CLASS_D_BIT) {
 		ret = snd_soc_dapm_new_controls(
 			dapm, aic311x_dapm_widgets,
@@ -1114,12 +1191,12 @@  static struct snd_soc_codec_driver soc_codec_driver_aic31xx = {
 	.set_bias_level		= aic31xx_set_bias_level,
 	.suspend_bias_off	= true,
 
-	.controls		= aic31xx_snd_controls,
-	.num_controls		= ARRAY_SIZE(aic31xx_snd_controls),
-	.dapm_widgets		= aic31xx_dapm_widgets,
-	.num_dapm_widgets	= ARRAY_SIZE(aic31xx_dapm_widgets),
-	.dapm_routes		= aic31xx_audio_map,
-	.num_dapm_routes	= ARRAY_SIZE(aic31xx_audio_map),
+	.controls		= common31xx_snd_controls,
+	.num_controls		= ARRAY_SIZE(common31xx_snd_controls),
+	.dapm_widgets		= common31xx_dapm_widgets,
+	.num_dapm_widgets	= ARRAY_SIZE(common31xx_dapm_widgets),
+	.dapm_routes		= common31xx_audio_map,
+	.num_dapm_routes	= ARRAY_SIZE(common31xx_audio_map),
 };
 
 static const struct snd_soc_dai_ops aic31xx_dai_ops = {
@@ -1129,6 +1206,21 @@  static const struct snd_soc_dai_ops aic31xx_dai_ops = {
 	.digital_mute	= aic31xx_dac_mute,
 };
 
+static struct snd_soc_dai_driver dac31xx_dai_driver[] = {
+	{
+		.name = "tlv32dac31xx-hifi",
+		.playback = {
+			.stream_name	 = "Playback",
+			.channels_min	 = 1,
+			.channels_max	 = 2,
+			.rates		 = AIC31XX_RATES,
+			.formats	 = AIC31XX_FORMATS,
+		},
+		.ops = &aic31xx_dai_ops,
+		.symmetric_rates = 1,
+	}
+};
+
 static struct snd_soc_dai_driver aic31xx_dai_driver[] = {
 	{
 		.name = "tlv320aic31xx-hifi",
@@ -1259,9 +1351,16 @@  static int aic31xx_i2c_probe(struct i2c_client *i2c,
 	if (ret)
 		return ret;
 
-	return snd_soc_register_codec(&i2c->dev, &soc_codec_driver_aic31xx,
-				     aic31xx_dai_driver,
-				     ARRAY_SIZE(aic31xx_dai_driver));
+	if (aic31xx->pdata.codec_type & DAC31XX_BIT)
+		return snd_soc_register_codec(&i2c->dev,
+				&soc_codec_driver_aic31xx,
+				dac31xx_dai_driver,
+				ARRAY_SIZE(dac31xx_dai_driver));
+	else
+		return snd_soc_register_codec(&i2c->dev,
+				&soc_codec_driver_aic31xx,
+				aic31xx_dai_driver,
+				ARRAY_SIZE(aic31xx_dai_driver));
 }
 
 static int aic31xx_i2c_remove(struct i2c_client *i2c)
@@ -1277,6 +1376,7 @@  static const struct i2c_device_id aic31xx_i2c_id[] = {
 	{ "tlv320aic3110", AIC3110 },
 	{ "tlv320aic3120", AIC3120 },
 	{ "tlv320aic3111", AIC3111 },
+	{ "tlv320dac3100", DAC3100 },
 	{ }
 };
 MODULE_DEVICE_TABLE(i2c, aic31xx_i2c_id);
diff --git a/sound/soc/codecs/tlv320aic31xx.h b/sound/soc/codecs/tlv320aic31xx.h
index ac9b146..5acd5b6 100644
--- a/sound/soc/codecs/tlv320aic31xx.h
+++ b/sound/soc/codecs/tlv320aic31xx.h
@@ -24,12 +24,14 @@ 
 
 #define AIC31XX_STEREO_CLASS_D_BIT	0x1
 #define AIC31XX_MINIDSP_BIT		0x2
+#define DAC31XX_BIT			0x4
 
 enum aic31xx_type {
 	AIC3100	= 0,
 	AIC3110 = AIC31XX_STEREO_CLASS_D_BIT,
 	AIC3120 = AIC31XX_MINIDSP_BIT,
 	AIC3111 = (AIC31XX_STEREO_CLASS_D_BIT | AIC31XX_MINIDSP_BIT),
+	DAC3100 = DAC31XX_BIT,
 };
 
 struct aic31xx_pdata {