Merge branch 'topic/msm8916' of git://git.kernel.org/pub/scm/linux/kernel/git/broonie...
authorMark Brown <broonie@kernel.org>
Mon, 21 Aug 2017 20:48:37 +0000 (21:48 +0100)
committerMark Brown <broonie@kernel.org>
Mon, 21 Aug 2017 20:48:37 +0000 (21:48 +0100)
1  2 
sound/soc/codecs/msm8916-wcd-analog.c
sound/soc/codecs/rt5663.c

index f07674eddc52a6535a7fec13f9e6c927a725752b,f834a639b3505d8d9c5846fa38f95a366c7087d8..4aa878d29098f5fa09ce08e90e9991f7cd6e6fa9
  #include <sound/pcm.h>
  #include <sound/pcm_params.h>
  #include <sound/tlv.h>
+ #include <sound/jack.h>
  
  #define CDC_D_REVISION1                       (0xf000)
  #define CDC_D_PERPH_SUBTYPE           (0xf005)
+ #define CDC_D_INT_EN_SET              (0x015)
+ #define CDC_D_INT_EN_CLR              (0x016)
+ #define MBHC_SWITCH_INT                       BIT(7)
+ #define MBHC_MIC_ELECTRICAL_INS_REM_DET       BIT(6)
+ #define MBHC_BUTTON_PRESS_DET         BIT(5)
+ #define MBHC_BUTTON_RELEASE_DET               BIT(4)
  #define CDC_D_CDC_RST_CTL             (0xf046)
  #define RST_CTL_DIG_SW_RST_N_MASK     BIT(7)
  #define RST_CTL_DIG_SW_RST_N_RESET    0
@@@ -36,7 -43,9 +43,9 @@@
  #define CDC_D_CDC_DIG_CLK_CTL         (0xf04A)
  #define DIG_CLK_CTL_RXD1_CLK_EN               BIT(0)
  #define DIG_CLK_CTL_RXD2_CLK_EN               BIT(1)
- #define DIG_CLK_CTL_RXD3_CLK_EN               BIT(3)
+ #define DIG_CLK_CTL_RXD3_CLK_EN               BIT(2)
+ #define DIG_CLK_CTL_D_MBHC_CLK_EN_MASK        BIT(3)
+ #define DIG_CLK_CTL_D_MBHC_CLK_EN     BIT(3)
  #define DIG_CLK_CTL_TXD_CLK_EN                BIT(4)
  #define DIG_CLK_CTL_NCP_CLK_EN_MASK   BIT(6)
  #define DIG_CLK_CTL_NCP_CLK_EN                BIT(6)
  #define MICB_1_EN_TX3_GND_SEL_TX_GND  0
  
  #define CDC_A_MICB_1_VAL              (0xf141)
+ #define MICB_MIN_VAL 1600
+ #define MICB_STEP_SIZE 50
+ #define MICB_VOLTAGE_REGVAL(v)                ((v - MICB_MIN_VAL)/MICB_STEP_SIZE)
  #define MICB_1_VAL_MICB_OUT_VAL_MASK  GENMASK(7, 3)
  #define MICB_1_VAL_MICB_OUT_VAL_V2P70V        ((0x16)  << 3)
+ #define MICB_1_VAL_MICB_OUT_VAL_V1P80V        ((0x4)  << 3)
  #define CDC_A_MICB_1_CTL              (0xf142)
  
  #define MICB_1_CTL_CFILT_REF_SEL_MASK         BIT(1)
  #define MICB_1_INT_TX3_INT_PULLUP_EN_TX1N_TO_GND      0
  
  #define CDC_A_MICB_2_EN                       (0xf144)
+ #define CDC_A_MICB_2_EN_ENABLE                BIT(7)
+ #define CDC_A_MICB_2_PULL_DOWN_EN_MASK        BIT(5)
+ #define CDC_A_MICB_2_PULL_DOWN_EN     BIT(5)
  #define CDC_A_TX_1_2_ATEST_CTL_2      (0xf145)
  #define CDC_A_MASTER_BIAS_CTL         (0xf146)
+ #define CDC_A_MBHC_DET_CTL_1          (0xf147)
+ #define CDC_A_MBHC_DET_CTL_L_DET_EN                   BIT(7)
+ #define CDC_A_MBHC_DET_CTL_GND_DET_EN                 BIT(6)
+ #define CDC_A_MBHC_DET_CTL_MECH_DET_TYPE_INSERTION    BIT(5)
+ #define CDC_A_MBHC_DET_CTL_MECH_DET_TYPE_REMOVAL      (0)
+ #define CDC_A_MBHC_DET_CTL_MECH_DET_TYPE_MASK         BIT(5)
+ #define CDC_A_MBHC_DET_CTL_MECH_DET_TYPE_SHIFT                (5)
+ #define CDC_A_MBHC_DET_CTL_MIC_CLAMP_CTL_AUTO         BIT(4)
+ #define CDC_A_MBHC_DET_CTL_MIC_CLAMP_CTL_MANUAL               BIT(3)
+ #define CDC_A_MBHC_DET_CTL_MIC_CLAMP_CTL_MASK         GENMASK(4, 3)
+ #define CDC_A_MBHC_DET_CTL_MBHC_BIAS_EN                       BIT(2)
+ #define CDC_A_MBHC_DET_CTL_2          (0xf150)
+ #define CDC_A_MBHC_DET_CTL_HS_L_DET_PULL_UP_CTRL_I_3P0        (BIT(7) | BIT(6))
+ #define CDC_A_MBHC_DET_CTL_HS_L_DET_COMPA_CTRL_V0P9_VDD       BIT(5)
+ #define CDC_A_PLUG_TYPE_MASK                          GENMASK(4, 3)
+ #define CDC_A_HPHL_PLUG_TYPE_NO                               BIT(4)
+ #define CDC_A_GND_PLUG_TYPE_NO                                BIT(3)
+ #define CDC_A_MBHC_DET_CTL_HPHL_100K_TO_GND_EN_MASK   BIT(0)
+ #define CDC_A_MBHC_DET_CTL_HPHL_100K_TO_GND_EN                BIT(0)
+ #define CDC_A_MBHC_FSM_CTL            (0xf151)
+ #define CDC_A_MBHC_FSM_CTL_MBHC_FSM_EN                        BIT(7)
+ #define CDC_A_MBHC_FSM_CTL_MBHC_FSM_EN_MASK           BIT(7)
+ #define CDC_A_MBHC_FSM_CTL_BTN_ISRC_CTRL_I_100UA      (0x3 << 4)
+ #define CDC_A_MBHC_FSM_CTL_BTN_ISRC_CTRL_MASK         GENMASK(6, 4)
+ #define CDC_A_MBHC_DBNC_TIMER         (0xf152)
+ #define CDC_A_MBHC_DBNC_TIMER_BTN_DBNC_T_16MS         BIT(3)
+ #define CDC_A_MBHC_DBNC_TIMER_INSREM_DBNC_T_256_MS    (0x9 << 4)
+ #define CDC_A_MBHC_BTN0_ZDET_CTL_0    (0xf153)
+ #define CDC_A_MBHC_BTN1_ZDET_CTL_1    (0xf154)
+ #define CDC_A_MBHC_BTN2_ZDET_CTL_2    (0xf155)
+ #define CDC_A_MBHC_BTN3_CTL           (0xf156)
+ #define CDC_A_MBHC_BTN4_CTL           (0xf157)
+ #define CDC_A_MBHC_BTN_VREF_FINE_SHIFT        (2)
+ #define CDC_A_MBHC_BTN_VREF_FINE_MASK GENMASK(4, 2)
+ #define CDC_A_MBHC_BTN_VREF_COARSE_MASK       GENMASK(7, 5)
+ #define CDC_A_MBHC_BTN_VREF_COARSE_SHIFT (5)
+ #define CDC_A_MBHC_BTN_VREF_MASK      (CDC_A_MBHC_BTN_VREF_COARSE_MASK | \
+                                       CDC_A_MBHC_BTN_VREF_FINE_MASK)
+ #define CDC_A_MBHC_RESULT_1           (0xf158)
+ #define CDC_A_MBHC_RESULT_1_BTN_RESULT_MASK   GENMASK(4, 0)
  #define CDC_A_TX_1_EN                 (0xf160)
  #define CDC_A_TX_2_EN                 (0xf161)
  #define CDC_A_TX_1_2_TEST_CTL_1               (0xf162)
  #define MSM8916_WCD_ANALOG_FORMATS (SNDRV_PCM_FMTBIT_S16_LE |\
                                    SNDRV_PCM_FMTBIT_S24_LE)
  
+ static int btn_mask = SND_JACK_BTN_0 | SND_JACK_BTN_1 |
+              SND_JACK_BTN_2 | SND_JACK_BTN_3 | SND_JACK_BTN_4;
+ static int hs_jack_mask = SND_JACK_HEADPHONE | SND_JACK_HEADSET;
  static const char * const supply_names[] = {
        "vdd-cdc-io",
        "vdd-cdc-tx-rx-cx",
  };
  
+ #define MBHC_MAX_BUTTONS      (5)
  struct pm8916_wcd_analog_priv {
        u16 pmic_rev;
        u16 codec_version;
+       bool    mbhc_btn_enabled;
+       /* special event to detect accessory type */
+       bool    mbhc_btn0_pressed;
+       bool    detect_accessory_type;
        struct clk *mclk;
+       struct snd_soc_codec *codec;
        struct regulator_bulk_data supplies[ARRAY_SIZE(supply_names)];
+       struct snd_soc_jack *jack;
+       bool hphl_jack_type_normally_open;
+       bool gnd_jack_type_normally_open;
+       /* Voltage threshold when internal current source of 100uA is used */
+       u32 vref_btn_cs[MBHC_MAX_BUTTONS];
+       /* Voltage threshold when microphone bias is ON */
+       u32 vref_btn_micb[MBHC_MAX_BUTTONS];
        unsigned int micbias1_cap_mode;
        unsigned int micbias2_cap_mode;
+       unsigned int micbias_mv;
  };
  
  static const char *const adc2_mux_text[] = { "ZERO", "INP2", "INP3" };
@@@ -265,18 -340,25 +340,25 @@@ static const struct snd_kcontrol_new pm
  
  static void pm8916_wcd_analog_micbias_enable(struct snd_soc_codec *codec)
  {
+       struct pm8916_wcd_analog_priv *wcd = snd_soc_codec_get_drvdata(codec);
        snd_soc_update_bits(codec, CDC_A_MICB_1_CTL,
                            MICB_1_CTL_EXT_PRECHARG_EN_MASK |
                            MICB_1_CTL_INT_PRECHARG_BYP_MASK,
                            MICB_1_CTL_INT_PRECHARG_BYP_EXT_PRECHRG_SEL
                            | MICB_1_CTL_EXT_PRECHARG_EN_ENABLE);
  
-       snd_soc_write(codec, CDC_A_MICB_1_VAL, MICB_1_VAL_MICB_OUT_VAL_V2P70V);
-       /*
-        * Special headset needs MICBIAS as 2.7V so wait for
-        * 50 msec for the MICBIAS to reach 2.7 volts.
-        */
-       msleep(50);
+       if (wcd->micbias_mv) {
+               snd_soc_write(codec, CDC_A_MICB_1_VAL,
+                             MICB_VOLTAGE_REGVAL(wcd->micbias_mv));
+               /*
+                * Special headset needs MICBIAS as 2.7V so wait for
+                * 50 msec for the MICBIAS to reach 2.7 volts.
+                */
+               if (wcd->micbias_mv >= 2700)
+                       msleep(50);
+       }
        snd_soc_update_bits(codec, CDC_A_MICB_1_CTL,
                            MICB_1_CTL_EXT_PRECHARG_EN_MASK |
                            MICB_1_CTL_INT_PRECHARG_BYP_MASK, 0);
@@@ -361,6 -443,97 +443,97 @@@ static int pm8916_wcd_analog_enable_mic
                                                     wcd->micbias1_cap_mode);
  }
  
+ static void pm8916_wcd_setup_mbhc(struct pm8916_wcd_analog_priv *wcd)
+ {
+       struct snd_soc_codec *codec = wcd->codec;
+       u32 plug_type = 0;
+       u32 int_en_mask;
+       snd_soc_write(codec, CDC_A_MBHC_DET_CTL_1,
+                     CDC_A_MBHC_DET_CTL_L_DET_EN |
+                     CDC_A_MBHC_DET_CTL_MECH_DET_TYPE_INSERTION |
+                     CDC_A_MBHC_DET_CTL_MIC_CLAMP_CTL_AUTO |
+                     CDC_A_MBHC_DET_CTL_MBHC_BIAS_EN);
+       if (wcd->hphl_jack_type_normally_open)
+               plug_type |= CDC_A_HPHL_PLUG_TYPE_NO;
+       if (wcd->gnd_jack_type_normally_open)
+               plug_type |= CDC_A_GND_PLUG_TYPE_NO;
+       snd_soc_write(codec, CDC_A_MBHC_DET_CTL_2,
+                     CDC_A_MBHC_DET_CTL_HS_L_DET_PULL_UP_CTRL_I_3P0 |
+                     CDC_A_MBHC_DET_CTL_HS_L_DET_COMPA_CTRL_V0P9_VDD |
+                     plug_type |
+                     CDC_A_MBHC_DET_CTL_HPHL_100K_TO_GND_EN);
+       snd_soc_write(codec, CDC_A_MBHC_DBNC_TIMER,
+                     CDC_A_MBHC_DBNC_TIMER_INSREM_DBNC_T_256_MS |
+                     CDC_A_MBHC_DBNC_TIMER_BTN_DBNC_T_16MS);
+       /* enable MBHC clock */
+       snd_soc_update_bits(codec, CDC_D_CDC_DIG_CLK_CTL,
+                           DIG_CLK_CTL_D_MBHC_CLK_EN_MASK,
+                           DIG_CLK_CTL_D_MBHC_CLK_EN);
+       int_en_mask = MBHC_SWITCH_INT;
+       if (wcd->mbhc_btn_enabled)
+               int_en_mask |= MBHC_BUTTON_PRESS_DET | MBHC_BUTTON_RELEASE_DET;
+       snd_soc_update_bits(codec, CDC_D_INT_EN_CLR, int_en_mask, 0);
+       snd_soc_update_bits(codec, CDC_D_INT_EN_SET, int_en_mask, int_en_mask);
+       wcd->mbhc_btn0_pressed = false;
+       wcd->detect_accessory_type = true;
+ }
+ static int pm8916_mbhc_configure_bias(struct pm8916_wcd_analog_priv *priv,
+                                     bool micbias2_enabled)
+ {
+       struct snd_soc_codec *codec = priv->codec;
+       u32 coarse, fine, reg_val, reg_addr;
+       int *vrefs, i;
+       if (!micbias2_enabled) { /* use internal 100uA Current source */
+               /* Enable internal 2.2k Internal Rbias Resistor */
+               snd_soc_update_bits(codec, CDC_A_MICB_1_INT_RBIAS,
+                                   MICB_1_INT_TX2_INT_RBIAS_EN_MASK,
+                                   MICB_1_INT_TX2_INT_RBIAS_EN_ENABLE);
+               /* Remove pull down on MIC BIAS2 */
+               snd_soc_update_bits(codec, CDC_A_MICB_2_EN,
+                                  CDC_A_MICB_2_PULL_DOWN_EN_MASK,
+                                  0);
+               /* enable 100uA internal current source */
+               snd_soc_update_bits(codec, CDC_A_MBHC_FSM_CTL,
+                                   CDC_A_MBHC_FSM_CTL_BTN_ISRC_CTRL_MASK,
+                                   CDC_A_MBHC_FSM_CTL_BTN_ISRC_CTRL_I_100UA);
+       }
+       snd_soc_update_bits(codec, CDC_A_MBHC_FSM_CTL,
+                       CDC_A_MBHC_FSM_CTL_MBHC_FSM_EN_MASK,
+                       CDC_A_MBHC_FSM_CTL_MBHC_FSM_EN);
+       if (micbias2_enabled)
+               vrefs = &priv->vref_btn_micb[0];
+       else
+               vrefs = &priv->vref_btn_cs[0];
+       /* program vref ranges for all the buttons */
+       reg_addr = CDC_A_MBHC_BTN0_ZDET_CTL_0;
+       for (i = 0; i <  MBHC_MAX_BUTTONS; i++) {
+               /* split mv in to coarse parts of 100mv & fine parts of 12mv */
+               coarse = (vrefs[i] / 100);
+               fine = ((vrefs[i] % 100) / 12);
+               reg_val = (coarse << CDC_A_MBHC_BTN_VREF_COARSE_SHIFT) |
+                        (fine << CDC_A_MBHC_BTN_VREF_FINE_SHIFT);
+               snd_soc_update_bits(codec, reg_addr,
+                              CDC_A_MBHC_BTN_VREF_MASK,
+                              reg_val);
+               reg_addr++;
+       }
+       return 0;
+ }
  static int pm8916_wcd_analog_enable_micbias_int2(struct
                                                  snd_soc_dapm_widget
                                                  *w, struct snd_kcontrol
        struct snd_soc_codec *codec = snd_soc_dapm_to_codec(w->dapm);
        struct pm8916_wcd_analog_priv *wcd = snd_soc_codec_get_drvdata(codec);
  
+       switch (event) {
+       case SND_SOC_DAPM_POST_PMU:
+               pm8916_mbhc_configure_bias(wcd, true);
+               break;
+       case SND_SOC_DAPM_POST_PMD:
+               pm8916_mbhc_configure_bias(wcd, false);
+               break;
+       }
        return pm8916_wcd_analog_enable_micbias_int(codec, event, w->reg,
                                                     wcd->micbias2_cap_mode);
  }
@@@ -536,6 -718,14 +718,14 @@@ static int pm8916_wcd_analog_probe(stru
                snd_soc_write(codec, wcd_reg_defaults_2_0[reg].reg,
                              wcd_reg_defaults_2_0[reg].def);
  
+       priv->codec = codec;
+       snd_soc_update_bits(codec, CDC_D_CDC_RST_CTL,
+                           RST_CTL_DIG_SW_RST_N_MASK,
+                           RST_CTL_DIG_SW_RST_N_REMOVE_RESET);
+       pm8916_wcd_setup_mbhc(priv);
        return 0;
  }
  
@@@ -543,6 -733,9 +733,9 @@@ static int pm8916_wcd_analog_remove(str
  {
        struct pm8916_wcd_analog_priv *priv = dev_get_drvdata(codec->dev);
  
+       snd_soc_update_bits(codec, CDC_D_CDC_RST_CTL,
+                           RST_CTL_DIG_SW_RST_N_MASK, 0);
        return regulator_bulk_disable(ARRAY_SIZE(priv->supplies),
                                      priv->supplies);
  }
@@@ -731,32 -924,129 +924,128 @@@ static const struct snd_soc_dapm_widge
        SND_SOC_DAPM_SUPPLY("A_MCLK2", CDC_D_CDC_TOP_CLK_CTL, 3, 0, NULL, 0),
  };
  
+ static int pm8916_wcd_analog_set_jack(struct snd_soc_codec *codec,
+                                     struct snd_soc_jack *jack,
+                                     void *data)
+ {
+       struct pm8916_wcd_analog_priv *wcd = snd_soc_codec_get_drvdata(codec);
+       wcd->jack = jack;
+       return 0;
+ }
  static struct regmap *pm8916_get_regmap(struct device *dev)
  {
        return dev_get_regmap(dev->parent, NULL);
  }
  
- static int pm8916_wcd_analog_startup(struct snd_pcm_substream *substream,
-                                     struct snd_soc_dai *dai)
+ static irqreturn_t mbhc_btn_release_irq_handler(int irq, void *arg)
  {
-       snd_soc_update_bits(dai->codec, CDC_D_CDC_RST_CTL,
-                           RST_CTL_DIG_SW_RST_N_MASK,
-                           RST_CTL_DIG_SW_RST_N_REMOVE_RESET);
+       struct pm8916_wcd_analog_priv *priv = arg;
  
-       return 0;
+       if (priv->detect_accessory_type) {
+               struct snd_soc_codec *codec = priv->codec;
+               u32 val = snd_soc_read(codec, CDC_A_MBHC_RESULT_1);
+               /* check if its BTN0 thats released */
+               if ((val >= 0) && !(val & CDC_A_MBHC_RESULT_1_BTN_RESULT_MASK))
+                       priv->mbhc_btn0_pressed = false;
+       } else {
+               snd_soc_jack_report(priv->jack, 0, btn_mask);
+       }
+       return IRQ_HANDLED;
  }
  
- static void pm8916_wcd_analog_shutdown(struct snd_pcm_substream *substream,
-                                        struct snd_soc_dai *dai)
+ static irqreturn_t mbhc_btn_press_irq_handler(int irq, void *arg)
  {
-       snd_soc_update_bits(dai->codec, CDC_D_CDC_RST_CTL,
-                           RST_CTL_DIG_SW_RST_N_MASK, 0);
+       struct pm8916_wcd_analog_priv *priv = arg;
+       struct snd_soc_codec *codec = priv->codec;
+       u32 btn_result;
+       btn_result = snd_soc_read(codec, CDC_A_MBHC_RESULT_1) &
+                                 CDC_A_MBHC_RESULT_1_BTN_RESULT_MASK;
+       switch (btn_result) {
+       case 0xf:
+               snd_soc_jack_report(priv->jack, SND_JACK_BTN_4, btn_mask);
+               break;
+       case 0x7:
+               snd_soc_jack_report(priv->jack, SND_JACK_BTN_3, btn_mask);
+               break;
+       case 0x3:
+               snd_soc_jack_report(priv->jack, SND_JACK_BTN_2, btn_mask);
+               break;
+       case 0x1:
+               snd_soc_jack_report(priv->jack, SND_JACK_BTN_1, btn_mask);
+               break;
+       case 0x0:
+               /* handle BTN_0 specially for type detection */
+               if (priv->detect_accessory_type)
+                       priv->mbhc_btn0_pressed = true;
+               else
+                       snd_soc_jack_report(priv->jack,
+                                           SND_JACK_BTN_0, btn_mask);
+               break;
+       default:
+               dev_err(codec->dev,
+                       "Unexpected button press result (%x)", btn_result);
+               break;
+       }
+       return IRQ_HANDLED;
  }
  
- static const struct snd_soc_dai_ops pm8916_wcd_analog_dai_ops = {
-       .startup = pm8916_wcd_analog_startup,
-       .shutdown = pm8916_wcd_analog_shutdown,
- };
 -
+ static irqreturn_t pm8916_mbhc_switch_irq_handler(int irq, void *arg)
+ {
+       struct pm8916_wcd_analog_priv *priv = arg;
+       struct snd_soc_codec *codec = priv->codec;
+       bool ins = false;
+       if (snd_soc_read(codec, CDC_A_MBHC_DET_CTL_1) &
+                               CDC_A_MBHC_DET_CTL_MECH_DET_TYPE_MASK)
+               ins = true;
+       /* Set the detection type appropriately */
+       snd_soc_update_bits(codec, CDC_A_MBHC_DET_CTL_1,
+                           CDC_A_MBHC_DET_CTL_MECH_DET_TYPE_MASK,
+                           (!ins << CDC_A_MBHC_DET_CTL_MECH_DET_TYPE_SHIFT));
+       if (ins) { /* hs insertion */
+               bool micbias_enabled = false;
+               if (snd_soc_read(codec, CDC_A_MICB_2_EN) &
+                               CDC_A_MICB_2_EN_ENABLE)
+                       micbias_enabled = true;
+               pm8916_mbhc_configure_bias(priv, micbias_enabled);
+               /*
+                * if only a btn0 press event is receive just before
+                * insert event then its a 3 pole headphone else if
+                * both press and release event received then its
+                * a headset.
+                */
+               if (priv->mbhc_btn0_pressed)
+                       snd_soc_jack_report(priv->jack,
+                                           SND_JACK_HEADPHONE, hs_jack_mask);
+               else
+                       snd_soc_jack_report(priv->jack,
+                                           SND_JACK_HEADSET, hs_jack_mask);
+               priv->detect_accessory_type = false;
+       } else { /* removal */
+               snd_soc_jack_report(priv->jack, 0, hs_jack_mask);
+               priv->detect_accessory_type = true;
+               priv->mbhc_btn0_pressed = false;
+       }
+       return IRQ_HANDLED;
+ }
  
  static struct snd_soc_dai_driver pm8916_wcd_analog_dai[] = {
        [0] = {
                            .channels_min = 1,
                            .channels_max = 3,
                            },
-              .ops = &pm8916_wcd_analog_dai_ops,
               },
        [1] = {
               .name = "pm8916_wcd_analog_pdm_tx",
                           .channels_min = 1,
                           .channels_max = 4,
                           },
-              .ops = &pm8916_wcd_analog_dai_ops,
               },
  };
  
  static const struct snd_soc_codec_driver pm8916_wcd_analog = {
        .probe = pm8916_wcd_analog_probe,
        .remove = pm8916_wcd_analog_remove,
+       .set_jack = pm8916_wcd_analog_set_jack,
        .get_regmap = pm8916_get_regmap,
        .component_driver = {
                .controls = pm8916_wcd_analog_snd_controls,
  static int pm8916_wcd_analog_parse_dt(struct device *dev,
                                       struct pm8916_wcd_analog_priv *priv)
  {
+       int rval;
  
        if (of_property_read_bool(dev->of_node, "qcom,micbias1-ext-cap"))
                priv->micbias1_cap_mode = MICB_1_EN_EXT_BYP_CAP;
        else
                priv->micbias2_cap_mode = MICB_1_EN_NO_EXT_BYP_CAP;
  
+       of_property_read_u32(dev->of_node, "qcom,micbias-lvl",
+                            &priv->micbias_mv);
+       if (of_property_read_bool(dev->of_node,
+                                 "qcom,hphl-jack-type-normally-open"))
+               priv->hphl_jack_type_normally_open = true;
+       else
+               priv->hphl_jack_type_normally_open = false;
+       if (of_property_read_bool(dev->of_node,
+                                 "qcom,gnd-jack-type-normally-open"))
+               priv->gnd_jack_type_normally_open = true;
+       else
+               priv->gnd_jack_type_normally_open = false;
+       priv->mbhc_btn_enabled = true;
+       rval = of_property_read_u32_array(dev->of_node,
+                                         "qcom,mbhc-vthreshold-low",
+                                         &priv->vref_btn_cs[0],
+                                         MBHC_MAX_BUTTONS);
+       if (rval < 0) {
+               priv->mbhc_btn_enabled = false;
+       } else {
+               rval = of_property_read_u32_array(dev->of_node,
+                                                 "qcom,mbhc-vthreshold-high",
+                                                 &priv->vref_btn_micb[0],
+                                                 MBHC_MAX_BUTTONS);
+               if (rval < 0)
+                       priv->mbhc_btn_enabled = false;
+       }
+       if (!priv->mbhc_btn_enabled)
+               dev_err(dev,
+                       "DT property missing, MBHC btn detection disabled\n");
        return 0;
  }
  
@@@ -820,7 -1146,7 +1145,7 @@@ static int pm8916_wcd_analog_spmi_probe
  {
        struct pm8916_wcd_analog_priv *priv;
        struct device *dev = &pdev->dev;
-       int ret, i;
+       int ret, i, irq;
  
        priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL);
        if (!priv)
                return ret;
        }
  
+       irq = platform_get_irq_byname(pdev, "mbhc_switch_int");
+       if (irq < 0) {
+               dev_err(dev, "failed to get mbhc switch irq\n");
+               return irq;
+       }
+       ret = devm_request_irq(dev, irq, pm8916_mbhc_switch_irq_handler,
+                              IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING |
+                              IRQF_ONESHOT,
+                              "mbhc switch irq", priv);
+       if (ret)
+               dev_err(dev, "cannot request mbhc switch irq\n");
+       if (priv->mbhc_btn_enabled) {
+               irq = platform_get_irq_byname(pdev, "mbhc_but_press_det");
+               if (irq < 0) {
+                       dev_err(dev, "failed to get button press irq\n");
+                       return irq;
+               }
+               ret = devm_request_irq(dev, irq, mbhc_btn_press_irq_handler,
+                                      IRQF_TRIGGER_RISING |
+                                      IRQF_TRIGGER_FALLING | IRQF_ONESHOT,
+                                      "mbhc btn press irq", priv);
+               if (ret)
+                       dev_err(dev, "cannot request mbhc button press irq\n");
+               irq = platform_get_irq_byname(pdev, "mbhc_but_rel_det");
+               if (irq < 0) {
+                       dev_err(dev, "failed to get button release irq\n");
+                       return irq;
+               }
+               ret = devm_request_irq(dev, irq, mbhc_btn_release_irq_handler,
+                                      IRQF_TRIGGER_RISING |
+                                      IRQF_TRIGGER_FALLING | IRQF_ONESHOT,
+                                      "mbhc btn release irq", priv);
+               if (ret)
+                       dev_err(dev, "cannot request mbhc button release irq\n");
+       }
        dev_set_drvdata(dev, priv);
  
        return snd_soc_register_codec(dev, &pm8916_wcd_analog,
index 1bbe36338cd33db9677cb3e7b2cf20fb8f93c2bf,31aeb2ba62045ba0037b2a87d1eead0f58c36628..7a6b1bb0b4377db7a2c8c774d1791bf9b819b8d6
@@@ -466,7 -466,7 +466,7 @@@ static const struct reg_default rt5663_
        { 0x0006, 0x1000 },
        { 0x000a, 0x0000 },
        { 0x0010, 0x000f },
-       { 0x0015, 0x42c1 },
+       { 0x0015, 0x42f1 },
        { 0x0016, 0x0000 },
        { 0x0018, 0x000b },
        { 0x0019, 0xafaf },
        { 0x008a, 0x0000 },
        { 0x008b, 0x0000 },
        { 0x008c, 0x0003 },
-       { 0x008e, 0x0004 },
+       { 0x008e, 0x0008 },
        { 0x008f, 0x1000 },
        { 0x0090, 0x0646 },
        { 0x0091, 0x0e3e },
        { 0x0098, 0x0000 },
        { 0x009a, 0x0000 },
        { 0x009f, 0x0000 },
-       { 0x00ae, 0x2000 },
+       { 0x00ae, 0x6000 },
        { 0x00af, 0x0000 },
        { 0x00b6, 0x0000 },
        { 0x00b7, 0x0000 },
        { 0x00d9, 0x08f9 },
        { 0x00db, 0x0008 },
        { 0x00dc, 0x00c0 },
-       { 0x00dd, 0x6724 },
+       { 0x00dd, 0x6729 },
        { 0x00de, 0x3131 },
        { 0x00df, 0x0008 },
        { 0x00e0, 0x4000 },
        { 0x0116, 0x0000 },
        { 0x0117, 0x0f00 },
        { 0x0118, 0x0006 },
-       { 0x0125, 0x2224 },
+       { 0x0125, 0x2424 },
        { 0x0126, 0x5550 },
        { 0x0127, 0x0400 },
        { 0x0128, 0x7711 },
        { 0x0145, 0x0002 },
        { 0x0146, 0x0000 },
        { 0x0160, 0x0e80 },
-       { 0x0161, 0x0020 },
-       { 0x0162, 0x0080 },
+       { 0x0161, 0x0080 },
+       { 0x0162, 0x0200 },
        { 0x0163, 0x0800 },
        { 0x0164, 0x0000 },
        { 0x0165, 0x0000 },
        { 0x0251, 0x0000 },
        { 0x0252, 0x028a },
        { 0x02fa, 0x0000 },
-       { 0x02fb, 0x0000 },
-       { 0x02fc, 0x0000 },
+       { 0x02fb, 0x00a4 },
+       { 0x02fc, 0x0300 },
        { 0x0300, 0x0000 },
        { 0x03d0, 0x0000 },
        { 0x03d1, 0x0000 },
@@@ -2860,7 -2860,7 +2860,7 @@@ static int rt5663_resume(struct snd_soc
  #define RT5663_FORMATS (SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S20_3LE | \
                        SNDRV_PCM_FMTBIT_S24_LE | SNDRV_PCM_FMTBIT_S8)
  
 -static struct snd_soc_dai_ops rt5663_aif_dai_ops = {
 +static const struct snd_soc_dai_ops rt5663_aif_dai_ops = {
        .hw_params = rt5663_hw_params,
        .set_fmt = rt5663_set_dai_fmt,
        .set_sysclk = rt5663_set_dai_sysclk,