Merge tag 'sound-5.13-rc1' of git://git.kernel.org/pub/scm/linux/kernel/git/tiwai...
[sfrench/cifs-2.6.git] / sound / soc / codecs / mt6359-accdet.c
diff --git a/sound/soc/codecs/mt6359-accdet.c b/sound/soc/codecs/mt6359-accdet.c
new file mode 100644 (file)
index 0000000..4222aed
--- /dev/null
@@ -0,0 +1,1080 @@
+// SPDX-License-Identifier: GPL-2.0
+//
+// mt6359-accdet.c  --  ALSA SoC mt6359 accdet driver
+//
+// Copyright (C) 2021 MediaTek Inc.
+// Author: Argus Lin <argus.lin@mediatek.com>
+//
+
+#include <linux/of_gpio.h>
+#include <linux/of.h>
+#include <linux/of_irq.h>
+#include <linux/of_device.h>
+#include <linux/of_address.h>
+#include <linux/input.h>
+#include <linux/kthread.h>
+#include <linux/io.h>
+#include <linux/sched/clock.h>
+#include <linux/workqueue.h>
+#include <linux/timer.h>
+#include <linux/delay.h>
+#include <linux/module.h>
+#include <linux/platform_device.h>
+#include <linux/init.h>
+#include <linux/irqdomain.h>
+#include <linux/irq.h>
+#include <linux/regmap.h>
+#include <sound/soc.h>
+#include <sound/jack.h>
+#include <linux/mfd/mt6397/core.h>
+
+#include "mt6359-accdet.h"
+#include "mt6359.h"
+
+/* global variable definitions */
+#define REGISTER_VAL(x)        ((x) - 1)
+
+/* mt6359 accdet capability */
+#define ACCDET_PMIC_EINT_IRQ           BIT(0)
+#define ACCDET_AP_GPIO_EINT            BIT(1)
+
+#define ACCDET_PMIC_EINT0              BIT(2)
+#define ACCDET_PMIC_EINT1              BIT(3)
+#define ACCDET_PMIC_BI_EINT            BIT(4)
+
+#define ACCDET_PMIC_GPIO_TRIG_EINT     BIT(5)
+#define ACCDET_PMIC_INVERTER_TRIG_EINT BIT(6)
+#define ACCDET_PMIC_RSV_EINT           BIT(7)
+
+#define ACCDET_THREE_KEY               BIT(8)
+#define ACCDET_FOUR_KEY                        BIT(9)
+#define ACCDET_TRI_KEY_CDD             BIT(10)
+#define ACCDET_RSV_KEY                 BIT(11)
+
+#define ACCDET_ANALOG_FASTDISCHARGE    BIT(12)
+#define ACCDET_DIGITAL_FASTDISCHARGE   BIT(13)
+#define ACCDET_AD_FASTDISCHRAGE                BIT(14)
+
+static struct platform_driver mt6359_accdet_driver;
+static const struct snd_soc_component_driver mt6359_accdet_soc_driver;
+
+/* local function declaration */
+static void accdet_set_debounce(struct mt6359_accdet *priv, int state,
+                               unsigned int debounce);
+static unsigned int adjust_eint_analog_setting(struct mt6359_accdet *priv);
+static void config_digital_init_by_mode(struct mt6359_accdet *priv);
+static void config_eint_init_by_mode(struct mt6359_accdet *priv);
+static inline void mt6359_accdet_init(struct mt6359_accdet *priv);
+static unsigned int mt6359_accdet_jd_setting(struct mt6359_accdet *priv);
+static void mt6359_accdet_recover_jd_setting(struct mt6359_accdet *priv);
+static void mt6359_accdet_jack_report(struct mt6359_accdet *priv);
+static void recover_eint_analog_setting(struct mt6359_accdet *priv);
+static void recover_eint_digital_setting(struct mt6359_accdet *priv);
+static void recover_eint_setting(struct mt6359_accdet *priv);
+
+static unsigned int adjust_eint_analog_setting(struct mt6359_accdet *priv)
+{
+       if (priv->data->eint_detect_mode == 0x3 ||
+           priv->data->eint_detect_mode == 0x4) {
+               /* ESD switches off */
+               regmap_update_bits(priv->regmap,
+                                  RG_ACCDETSPARE_ADDR, 1 << 8, 0);
+       }
+       if (priv->data->eint_detect_mode == 0x4) {
+               if (priv->caps & ACCDET_PMIC_EINT0) {
+                       /* enable RG_EINT0CONFIGACCDET */
+                       regmap_update_bits(priv->regmap,
+                                          RG_EINT0CONFIGACCDET_ADDR,
+                                          RG_EINT0CONFIGACCDET_MASK_SFT,
+                                          BIT(RG_EINT0CONFIGACCDET_SFT));
+               } else if (priv->caps & ACCDET_PMIC_EINT1) {
+                       /* enable RG_EINT1CONFIGACCDET */
+                       regmap_update_bits(priv->regmap,
+                                          RG_EINT1CONFIGACCDET_ADDR,
+                                          RG_EINT1CONFIGACCDET_MASK_SFT,
+                                          BIT(RG_EINT1CONFIGACCDET_SFT));
+               }
+               if (priv->data->eint_use_ext_res == 0x3 ||
+                   priv->data->eint_use_ext_res == 0x4) {
+                       /*select 500k, use internal resistor */
+                       regmap_update_bits(priv->regmap,
+                                          RG_EINT0HIRENB_ADDR,
+                                          RG_EINT0HIRENB_MASK_SFT,
+                                          BIT(RG_EINT0HIRENB_SFT));
+               }
+       }
+       return 0;
+}
+
+static unsigned int adjust_eint_digital_setting(struct mt6359_accdet *priv)
+{
+       if (priv->caps & ACCDET_PMIC_EINT0) {
+               /* disable inverter */
+               regmap_update_bits(priv->regmap,
+                                  ACCDET_EINT0_INVERTER_SW_EN_ADDR,
+                                  ACCDET_EINT0_INVERTER_SW_EN_MASK_SFT, 0);
+       } else if (priv->caps & ACCDET_PMIC_EINT1) {
+               /* disable inverter */
+               regmap_update_bits(priv->regmap,
+                                  ACCDET_EINT1_INVERTER_SW_EN_ADDR,
+                                  ACCDET_EINT1_INVERTER_SW_EN_MASK_SFT, 0);
+       }
+
+       if (priv->data->eint_detect_mode == 0x4) {
+               if (priv->caps & ACCDET_PMIC_EINT0) {
+                       /* set DA stable signal */
+                       regmap_update_bits(priv->regmap,
+                                          ACCDET_DA_STABLE_ADDR,
+                                          ACCDET_EINT0_CEN_STABLE_MASK_SFT, 0);
+               } else if (priv->caps & ACCDET_PMIC_EINT1) {
+                       /* set DA stable signal */
+                       regmap_update_bits(priv->regmap,
+                                          ACCDET_DA_STABLE_ADDR,
+                                          ACCDET_EINT1_CEN_STABLE_MASK_SFT, 0);
+               }
+       }
+       return 0;
+}
+
+static unsigned int mt6359_accdet_jd_setting(struct mt6359_accdet *priv)
+{
+       if (priv->jd_sts == M_PLUG_IN) {
+               /* adjust digital setting */
+               adjust_eint_digital_setting(priv);
+               /* adjust analog setting */
+               adjust_eint_analog_setting(priv);
+       } else if (priv->jd_sts == M_PLUG_OUT) {
+               /* set debounce to 1ms */
+               accdet_set_debounce(priv, eint_state000,
+                                   priv->data->pwm_deb->eint_debounce0);
+       } else {
+               dev_dbg(priv->dev, "should not be here %s()\n", __func__);
+       }
+
+       return 0;
+}
+
+static void recover_eint_analog_setting(struct mt6359_accdet *priv)
+{
+       if (priv->data->eint_detect_mode == 0x3 ||
+           priv->data->eint_detect_mode == 0x4) {
+               /* ESD switches on */
+               regmap_update_bits(priv->regmap, RG_ACCDETSPARE_ADDR,
+                                  1 << 8, 1 << 8);
+       }
+       if (priv->data->eint_detect_mode == 0x4) {
+               if (priv->caps & ACCDET_PMIC_EINT0) {
+                       /* disable RG_EINT0CONFIGACCDET */
+                       regmap_update_bits(priv->regmap,
+                                          RG_EINT0CONFIGACCDET_ADDR,
+                                          RG_EINT0CONFIGACCDET_MASK_SFT, 0);
+               } else if (priv->caps & ACCDET_PMIC_EINT1) {
+                       /* disable RG_EINT1CONFIGACCDET */
+                       regmap_update_bits(priv->regmap,
+                                          RG_EINT1CONFIGACCDET_ADDR,
+                                          RG_EINT1CONFIGACCDET_MASK_SFT, 0);
+               }
+               regmap_update_bits(priv->regmap, RG_EINT0HIRENB_ADDR,
+                                  RG_EINT0HIRENB_MASK_SFT, 0);
+       }
+}
+
+static void recover_eint_digital_setting(struct mt6359_accdet *priv)
+{
+       if (priv->caps & ACCDET_PMIC_EINT0) {
+               regmap_update_bits(priv->regmap,
+                                  ACCDET_EINT0_M_SW_EN_ADDR,
+                                  ACCDET_EINT0_M_SW_EN_MASK_SFT, 0);
+       } else if (priv->caps & ACCDET_PMIC_EINT1) {
+               regmap_update_bits(priv->regmap,
+                                  ACCDET_EINT1_M_SW_EN_ADDR,
+                                  ACCDET_EINT1_M_SW_EN_MASK_SFT, 0);
+       }
+       if (priv->data->eint_detect_mode == 0x4) {
+               /* enable eint0cen */
+               if (priv->caps & ACCDET_PMIC_EINT0) {
+                       /* enable eint0cen */
+                       regmap_update_bits(priv->regmap,
+                                          ACCDET_DA_STABLE_ADDR,
+                                          ACCDET_EINT0_CEN_STABLE_MASK_SFT,
+                                          BIT(ACCDET_EINT0_CEN_STABLE_SFT));
+               } else if (priv->caps & ACCDET_PMIC_EINT1) {
+                       /* enable eint1cen */
+                       regmap_update_bits(priv->regmap,
+                                          ACCDET_DA_STABLE_ADDR,
+                                          ACCDET_EINT1_CEN_STABLE_MASK_SFT,
+                                          BIT(ACCDET_EINT1_CEN_STABLE_SFT));
+               }
+       }
+
+       if (priv->data->eint_detect_mode != 0x1) {
+               if (priv->caps & ACCDET_PMIC_EINT0) {
+                       /* enable inverter */
+                       regmap_update_bits(priv->regmap,
+                                          ACCDET_EINT0_INVERTER_SW_EN_ADDR,
+                                          ACCDET_EINT0_INVERTER_SW_EN_MASK_SFT,
+                                          BIT(ACCDET_EINT0_INVERTER_SW_EN_SFT));
+               } else if (priv->caps & ACCDET_PMIC_EINT1) {
+                       /* enable inverter */
+                       regmap_update_bits(priv->regmap,
+                                          ACCDET_EINT1_INVERTER_SW_EN_ADDR,
+                                          ACCDET_EINT1_INVERTER_SW_EN_MASK_SFT,
+                                          BIT(ACCDET_EINT1_INVERTER_SW_EN_SFT));
+               }
+       }
+}
+
+static void recover_eint_setting(struct mt6359_accdet *priv)
+{
+       if (priv->jd_sts == M_PLUG_OUT) {
+               recover_eint_analog_setting(priv);
+               recover_eint_digital_setting(priv);
+       }
+}
+
+static void mt6359_accdet_recover_jd_setting(struct mt6359_accdet *priv)
+{
+       int ret = 0;
+       unsigned int value = 0;
+
+       regmap_update_bits(priv->regmap, ACCDET_IRQ_ADDR,
+                          ACCDET_IRQ_CLR_MASK_SFT, BIT(ACCDET_IRQ_CLR_SFT));
+       usleep_range(200, 300);
+       ret = regmap_read_poll_timeout(priv->regmap,
+                                      ACCDET_IRQ_ADDR,
+                                      value,
+                                      (value & ACCDET_IRQ_MASK_SFT) == 0,
+                                      0,
+                                      1000);
+       if (ret)
+               dev_warn(priv->dev, "%s(), ret %d\n", __func__, ret);
+       /* clear accdet int, modify  for fix interrupt trigger twice error */
+       regmap_update_bits(priv->regmap, ACCDET_IRQ_ADDR,
+                          ACCDET_IRQ_CLR_MASK_SFT, 0);
+       regmap_update_bits(priv->regmap, RG_INT_STATUS_ACCDET_ADDR,
+                          RG_INT_STATUS_ACCDET_MASK_SFT,
+                          BIT(RG_INT_STATUS_ACCDET_SFT));
+
+       /* recover accdet debounce0,3 */
+       accdet_set_debounce(priv, accdet_state000,
+                           priv->data->pwm_deb->debounce0);
+       accdet_set_debounce(priv, accdet_state001,
+                           priv->data->pwm_deb->debounce1);
+       accdet_set_debounce(priv, accdet_state011,
+                           priv->data->pwm_deb->debounce3);
+
+       priv->jack_type = 0;
+       priv->btn_type = 0;
+       priv->accdet_status = 0x3;
+       mt6359_accdet_jack_report(priv);
+}
+
+static void accdet_set_debounce(struct mt6359_accdet *priv, int state,
+                               unsigned int debounce)
+{
+       switch (state) {
+       case accdet_state000:
+               regmap_write(priv->regmap, ACCDET_DEBOUNCE0_ADDR, debounce);
+               break;
+       case accdet_state001:
+               regmap_write(priv->regmap, ACCDET_DEBOUNCE1_ADDR, debounce);
+               break;
+       case accdet_state010:
+               regmap_write(priv->regmap, ACCDET_DEBOUNCE2_ADDR, debounce);
+               break;
+       case accdet_state011:
+               regmap_write(priv->regmap, ACCDET_DEBOUNCE3_ADDR, debounce);
+               break;
+       case accdet_auxadc:
+               regmap_write(priv->regmap,
+                            ACCDET_CONNECT_AUXADC_TIME_DIG_ADDR, debounce);
+               break;
+       case eint_state000:
+               regmap_update_bits(priv->regmap, ACCDET_EINT_DEBOUNCE0_ADDR,
+                                  0xF << ACCDET_EINT_DEBOUNCE0_SFT,
+                                  debounce << ACCDET_EINT_DEBOUNCE0_SFT);
+               break;
+       case eint_state001:
+               regmap_update_bits(priv->regmap, ACCDET_EINT_DEBOUNCE1_ADDR,
+                                  0xF << ACCDET_EINT_DEBOUNCE1_SFT,
+                                  debounce << ACCDET_EINT_DEBOUNCE1_SFT);
+               break;
+       case eint_state010:
+               regmap_update_bits(priv->regmap, ACCDET_EINT_DEBOUNCE2_ADDR,
+                                  0xF << ACCDET_EINT_DEBOUNCE2_SFT,
+                                  debounce << ACCDET_EINT_DEBOUNCE2_SFT);
+               break;
+       case eint_state011:
+               regmap_update_bits(priv->regmap, ACCDET_EINT_DEBOUNCE3_ADDR,
+                                  0xF << ACCDET_EINT_DEBOUNCE3_SFT,
+                                  debounce << ACCDET_EINT_DEBOUNCE3_SFT);
+               break;
+       case eint_inverter_state000:
+               regmap_write(priv->regmap, ACCDET_EINT_INVERTER_DEBOUNCE_ADDR,
+                            debounce);
+               break;
+       default:
+               dev_warn(priv->dev, "Error: %s error state (%d)\n", __func__,
+                        state);
+               break;
+       }
+}
+
+static void mt6359_accdet_jack_report(struct mt6359_accdet *priv)
+{
+       int report = 0;
+
+       if (!priv->jack)
+               return;
+
+       report = priv->jack_type | priv->btn_type;
+       snd_soc_jack_report(priv->jack, report, MT6359_ACCDET_JACK_MASK);
+}
+
+static unsigned int check_button(struct mt6359_accdet *priv, unsigned int v)
+{
+       if (priv->caps & ACCDET_FOUR_KEY) {
+               if (v < priv->data->four_key.down &&
+                   v >= priv->data->four_key.up)
+                       priv->btn_type = SND_JACK_BTN_1;
+               if (v < priv->data->four_key.up &&
+                   v >= priv->data->four_key.voice)
+                       priv->btn_type = SND_JACK_BTN_2;
+               if (v < priv->data->four_key.voice &&
+                   v >= priv->data->four_key.mid)
+                       priv->btn_type = SND_JACK_BTN_3;
+               if (v < priv->data->four_key.mid)
+                       priv->btn_type = SND_JACK_BTN_0;
+       } else {
+               if (v < priv->data->three_key.down &&
+                   v >= priv->data->three_key.up)
+                       priv->btn_type = SND_JACK_BTN_1;
+               if (v < priv->data->three_key.up &&
+                   v >= priv->data->three_key.mid)
+                       priv->btn_type = SND_JACK_BTN_2;
+               if (v < priv->data->three_key.mid)
+                       priv->btn_type = SND_JACK_BTN_0;
+       }
+       return 0;
+}
+
+static void is_key_pressed(struct mt6359_accdet *priv, bool pressed)
+{
+       priv->btn_type = priv->jack_type & ~MT6359_ACCDET_BTN_MASK;
+
+       if (pressed)
+               check_button(priv, priv->cali_voltage);
+}
+
+static inline void check_jack_btn_type(struct mt6359_accdet *priv)
+{
+       unsigned int val = 0;
+
+       regmap_read(priv->regmap, ACCDET_MEM_IN_ADDR, &val);
+
+       priv->accdet_status =
+               (val >> ACCDET_STATE_MEM_IN_OFFSET) & ACCDET_STATE_AB_MASK;
+
+       switch (priv->accdet_status) {
+       case 0:
+               if (priv->jack_type == SND_JACK_HEADSET)
+                       is_key_pressed(priv, true);
+               else
+                       priv->jack_type = SND_JACK_HEADPHONE;
+               break;
+       case 1:
+               if (priv->jack_type == SND_JACK_HEADSET) {
+                       is_key_pressed(priv, false);
+               } else {
+                       priv->jack_type = SND_JACK_HEADSET;
+                       accdet_set_debounce(priv, eint_state011, 0x1);
+               }
+               break;
+       case 3:
+       default:
+               priv->jack_type = 0;
+               break;
+       }
+}
+
+static void mt6359_accdet_work(struct work_struct *work)
+{
+       struct mt6359_accdet *priv =
+               container_of(work, struct mt6359_accdet, accdet_work);
+
+       mutex_lock(&priv->res_lock);
+       priv->pre_accdet_status = priv->accdet_status;
+       check_jack_btn_type(priv);
+
+       if (priv->jack_plugged &&
+           priv->pre_accdet_status != priv->accdet_status)
+               mt6359_accdet_jack_report(priv);
+       mutex_unlock(&priv->res_lock);
+}
+
+static void mt6359_accdet_jd_work(struct work_struct *work)
+{
+       int ret = 0;
+       unsigned int value = 0;
+
+       struct mt6359_accdet *priv =
+               container_of(work, struct mt6359_accdet, jd_work);
+
+       mutex_lock(&priv->res_lock);
+       if (priv->jd_sts == M_PLUG_IN) {
+               priv->jack_plugged = true;
+
+               /* set and clear initial bit every eint interrupt */
+               regmap_update_bits(priv->regmap, ACCDET_SEQ_INIT_ADDR,
+                                  ACCDET_SEQ_INIT_MASK_SFT,
+                                  BIT(ACCDET_SEQ_INIT_SFT));
+               regmap_update_bits(priv->regmap, ACCDET_SEQ_INIT_ADDR,
+                                  ACCDET_SEQ_INIT_MASK_SFT, 0);
+               ret = regmap_read_poll_timeout(priv->regmap,
+                                              ACCDET_SEQ_INIT_ADDR,
+                                              value,
+                                              (value & ACCDET_SEQ_INIT_MASK_SFT) == 0,
+                                              0,
+                                              1000);
+               if (ret)
+                       dev_err(priv->dev, "%s(), ret %d\n", __func__, ret);
+
+               /* enable ACCDET unit */
+               regmap_update_bits(priv->regmap, ACCDET_SW_EN_ADDR,
+                                  ACCDET_SW_EN_MASK_SFT, BIT(ACCDET_SW_EN_SFT));
+       } else if (priv->jd_sts == M_PLUG_OUT) {
+               priv->jack_plugged = false;
+
+               accdet_set_debounce(priv, accdet_state011,
+                                   priv->data->pwm_deb->debounce3);
+               regmap_update_bits(priv->regmap, ACCDET_SW_EN_ADDR,
+                                  ACCDET_SW_EN_MASK_SFT, 0);
+               mt6359_accdet_recover_jd_setting(priv);
+       }
+
+       if (priv->caps & ACCDET_PMIC_EINT_IRQ)
+               recover_eint_setting(priv);
+       mutex_unlock(&priv->res_lock);
+}
+
+static irqreturn_t mt6359_accdet_irq(int irq, void *data)
+{
+       struct mt6359_accdet *priv = data;
+       unsigned int irq_val = 0, val = 0, value = 0;
+       int ret = 0;
+
+       mutex_lock(&priv->res_lock);
+       regmap_read(priv->regmap, ACCDET_IRQ_ADDR, &irq_val);
+
+       if (irq_val & ACCDET_IRQ_MASK_SFT) {
+               regmap_update_bits(priv->regmap, ACCDET_IRQ_ADDR,
+                                  ACCDET_IRQ_CLR_MASK_SFT,
+                                  BIT(ACCDET_IRQ_CLR_SFT));
+               ret = regmap_read_poll_timeout(priv->regmap,
+                                              ACCDET_IRQ_ADDR,
+                                              value,
+                                              (value & ACCDET_IRQ_MASK_SFT) == 0,
+                                              0,
+                                              1000);
+               if (ret) {
+                       dev_err(priv->dev, "%s(), ret %d\n", __func__, ret);
+                       mutex_unlock(&priv->res_lock);
+                       return IRQ_NONE;
+               }
+               regmap_update_bits(priv->regmap, ACCDET_IRQ_ADDR,
+                                  ACCDET_IRQ_CLR_MASK_SFT, 0);
+               regmap_update_bits(priv->regmap, RG_INT_STATUS_ACCDET_ADDR,
+                                  RG_INT_STATUS_ACCDET_MASK_SFT,
+                                  BIT(RG_INT_STATUS_ACCDET_SFT));
+
+               queue_work(priv->accdet_workqueue, &priv->accdet_work);
+       } else {
+               if (irq_val & ACCDET_EINT0_IRQ_MASK_SFT) {
+                       regmap_update_bits(priv->regmap, ACCDET_IRQ_ADDR,
+                                          ACCDET_EINT0_IRQ_CLR_MASK_SFT,
+                                          BIT(ACCDET_EINT0_IRQ_CLR_SFT));
+                       ret = regmap_read_poll_timeout(priv->regmap,
+                                                      ACCDET_IRQ_ADDR,
+                                                      value,
+                                                      (value & ACCDET_EINT0_IRQ_MASK_SFT) == 0,
+                                                      0,
+                                                      1000);
+                       if (ret) {
+                               dev_err(priv->dev, "%s(), ret %d\n", __func__,
+                                       ret);
+                               mutex_unlock(&priv->res_lock);
+                               return IRQ_NONE;
+                       }
+                       regmap_update_bits(priv->regmap, ACCDET_IRQ_ADDR,
+                                          ACCDET_EINT0_IRQ_CLR_MASK_SFT, 0);
+                       regmap_update_bits(priv->regmap,
+                                          RG_INT_STATUS_ACCDET_ADDR,
+                                          RG_INT_STATUS_ACCDET_EINT0_MASK_SFT,
+                                          BIT(RG_INT_STATUS_ACCDET_EINT0_SFT));
+               }
+               if (irq_val & ACCDET_EINT1_IRQ_MASK_SFT) {
+                       regmap_update_bits(priv->regmap, ACCDET_IRQ_ADDR,
+                                          ACCDET_EINT1_IRQ_CLR_MASK_SFT,
+                                          BIT(ACCDET_EINT1_IRQ_CLR_SFT));
+                       ret = regmap_read_poll_timeout(priv->regmap,
+                                                      ACCDET_IRQ_ADDR,
+                                                      value,
+                                                      (value & ACCDET_EINT1_IRQ_MASK_SFT) == 0,
+                                                      0,
+                                                      1000);
+                       if (ret) {
+                               dev_err(priv->dev, "%s(), ret %d\n", __func__,
+                                       ret);
+                               mutex_unlock(&priv->res_lock);
+                               return IRQ_NONE;
+                       }
+                       regmap_update_bits(priv->regmap, ACCDET_IRQ_ADDR,
+                                          ACCDET_EINT1_IRQ_CLR_MASK_SFT, 0);
+                       regmap_update_bits(priv->regmap,
+                                          RG_INT_STATUS_ACCDET_ADDR,
+                                          RG_INT_STATUS_ACCDET_EINT1_MASK_SFT,
+                                          BIT(RG_INT_STATUS_ACCDET_EINT1_SFT));
+               }
+               /* get jack detection status */
+               regmap_read(priv->regmap, ACCDET_EINT0_MEM_IN_ADDR, &val);
+               priv->jd_sts = ((val >> ACCDET_EINT0_MEM_IN_SFT) &
+                                  ACCDET_EINT0_MEM_IN_MASK);
+               /* adjust eint digital/analog setting */
+               mt6359_accdet_jd_setting(priv);
+
+               queue_work(priv->jd_workqueue, &priv->jd_work);
+       }
+       mutex_unlock(&priv->res_lock);
+
+       return IRQ_HANDLED;
+}
+
+static int mt6359_accdet_parse_dt(struct mt6359_accdet *priv)
+{
+       int ret = 0;
+       struct device *dev = priv->dev;
+       struct device_node *node = NULL;
+       int pwm_deb[15] = {0};
+       unsigned int tmp = 0;
+
+       node = of_get_child_by_name(dev->parent->of_node, "accdet");
+       if (!node)
+               return -EINVAL;
+
+       ret = of_property_read_u32(node, "mediatek,mic-vol",
+                                  &priv->data->mic_vol);
+       if (ret)
+               priv->data->mic_vol = 8;
+
+       ret = of_property_read_u32(node, "mediatek,plugout-debounce",
+                                  &priv->data->plugout_deb);
+       if (ret)
+               priv->data->plugout_deb = 1;
+
+       ret = of_property_read_u32(node, "mediatek,mic-mode",
+                                  &priv->data->mic_mode);
+       if (ret)
+               priv->data->mic_mode = 2;
+
+       ret = of_property_read_u32_array(node, "mediatek,pwm-deb-setting",
+                                        pwm_deb, ARRAY_SIZE(pwm_deb));
+       /* debounce8(auxadc debounce) is default, needn't get from dts */
+       if (!ret)
+               memcpy(priv->data->pwm_deb, pwm_deb, sizeof(pwm_deb));
+
+       ret = of_property_read_u32(node, "mediatek,eint-level-pol",
+                                  &priv->data->eint_pol);
+       if (ret)
+               priv->data->eint_pol = 8;
+
+       ret = of_property_read_u32(node, "mediatek,eint-use-ap", &tmp);
+       if (ret)
+               tmp = 0;
+       if (tmp == 0)
+               priv->caps |= ACCDET_PMIC_EINT_IRQ;
+       else if (tmp == 1)
+               priv->caps |= ACCDET_AP_GPIO_EINT;
+
+       ret = of_property_read_u32(node, "mediatek,eint-detect-mode",
+                                  &priv->data->eint_detect_mode);
+       if (ret) {
+               /* eint detection mode equals to EINT HW Mode */
+               priv->data->eint_detect_mode = 0x4;
+       }
+
+       ret = of_property_read_u32(node, "mediatek,eint-num", &tmp);
+       if (ret)
+               tmp = 0;
+       if (tmp == 0)
+               priv->caps |= ACCDET_PMIC_EINT0;
+       else if (tmp == 1)
+               priv->caps |= ACCDET_PMIC_EINT1;
+       else if (tmp == 2)
+               priv->caps |= ACCDET_PMIC_BI_EINT;
+
+       ret = of_property_read_u32(node, "mediatek,eint-trig-mode",
+                                  &tmp);
+       if (ret)
+               tmp = 0;
+       if (tmp == 0)
+               priv->caps |= ACCDET_PMIC_GPIO_TRIG_EINT;
+       else if (tmp == 1)
+               priv->caps |= ACCDET_PMIC_INVERTER_TRIG_EINT;
+
+       ret = of_property_read_u32(node, "mediatek,eint-use-ext-res",
+                                  &priv->data->eint_use_ext_res);
+       if (ret) {
+               /* eint use internal resister */
+               priv->data->eint_use_ext_res = 0x0;
+       }
+
+       ret = of_property_read_u32(node, "mediatek,eint-comp-vth",
+                                  &priv->data->eint_comp_vth);
+       if (ret)
+               priv->data->eint_comp_vth = 0x0;
+
+       ret = of_property_read_u32(node, "mediatek,key-mode", &tmp);
+       if (ret)
+               tmp = 0;
+       if (tmp == 0) {
+               int three_key[4];
+
+               priv->caps |= ACCDET_THREE_KEY;
+               ret = of_property_read_u32_array(node,
+                                                "mediatek,three-key-thr",
+                                                three_key,
+                                                ARRAY_SIZE(three_key));
+               if (!ret)
+                       memcpy(&priv->data->three_key, three_key + 1,
+                              sizeof(struct three_key_threshold));
+       } else if (tmp == 1) {
+               int four_key[5];
+
+               priv->caps |= ACCDET_FOUR_KEY;
+               ret = of_property_read_u32_array(node,
+                                                "mediatek,four-key-thr",
+                                                four_key,
+                                                ARRAY_SIZE(four_key));
+               if (!ret) {
+                       memcpy(&priv->data->four_key, four_key + 1,
+                              sizeof(struct four_key_threshold));
+               } else {
+                       dev_warn(priv->dev,
+                                "accdet no 4-key-thrsh dts, use efuse\n");
+               }
+       } else if (tmp == 2) {
+               int three_key[4];
+
+               priv->caps |= ACCDET_TRI_KEY_CDD;
+               ret = of_property_read_u32_array(node,
+                                                "mediatek,tri-key-cdd-thr",
+                                                three_key,
+                                                ARRAY_SIZE(three_key));
+               if (!ret)
+                       memcpy(&priv->data->three_key, three_key + 1,
+                              sizeof(struct three_key_threshold));
+       }
+
+       dev_warn(priv->dev, "accdet caps=%x\n", priv->caps);
+
+       return 0;
+}
+
+static void config_digital_init_by_mode(struct mt6359_accdet *priv)
+{
+       /* enable eint cmpmem pwm */
+       regmap_write(priv->regmap, ACCDET_EINT_CMPMEN_PWM_THRESH_ADDR,
+                    (priv->data->pwm_deb->eint_pwm_width << 4 |
+                    priv->data->pwm_deb->eint_pwm_thresh));
+       /* DA signal stable */
+       if (priv->caps & ACCDET_PMIC_EINT0) {
+               regmap_write(priv->regmap, ACCDET_DA_STABLE_ADDR,
+                            ACCDET_EINT0_STABLE_VAL);
+       } else if (priv->caps & ACCDET_PMIC_EINT1) {
+               regmap_write(priv->regmap, ACCDET_DA_STABLE_ADDR,
+                            ACCDET_EINT1_STABLE_VAL);
+       }
+       /* after receive n+1 number, interrupt issued. */
+       regmap_update_bits(priv->regmap, ACCDET_EINT_M_PLUG_IN_NUM_ADDR,
+                          ACCDET_EINT_M_PLUG_IN_NUM_MASK_SFT,
+                          BIT(ACCDET_EINT_M_PLUG_IN_NUM_SFT));
+       /* setting HW mode, enable digital fast discharge
+        * if use EINT0 & EINT1 detection, please modify
+        * ACCDET_HWMODE_EN_ADDR[2:1]
+        */
+       regmap_write(priv->regmap, ACCDET_HWMODE_EN_ADDR, 0x100);
+
+       regmap_update_bits(priv->regmap, ACCDET_EINT_M_DETECT_EN_ADDR,
+                          ACCDET_EINT_M_DETECT_EN_MASK_SFT, 0);
+
+       /* enable PWM */
+       regmap_write(priv->regmap, ACCDET_CMP_PWM_EN_ADDR, 0x67);
+       /* enable inverter detection */
+       if (priv->data->eint_detect_mode == 0x1) {
+               /* disable inverter detection */
+               if (priv->caps & ACCDET_PMIC_EINT0) {
+                       regmap_update_bits(priv->regmap,
+                                          ACCDET_EINT0_INVERTER_SW_EN_ADDR,
+                                          ACCDET_EINT0_INVERTER_SW_EN_MASK_SFT,
+                                          0);
+               } else if (priv->caps & ACCDET_PMIC_EINT1) {
+                       regmap_update_bits(priv->regmap,
+                                          ACCDET_EINT1_INVERTER_SW_EN_ADDR,
+                                          ACCDET_EINT1_INVERTER_SW_EN_MASK_SFT,
+                                          0);
+               }
+       } else {
+               if (priv->caps & ACCDET_PMIC_EINT0) {
+                       regmap_update_bits(priv->regmap,
+                                          ACCDET_EINT0_INVERTER_SW_EN_ADDR,
+                                          ACCDET_EINT0_INVERTER_SW_EN_MASK_SFT,
+                                          BIT(ACCDET_EINT0_INVERTER_SW_EN_SFT));
+               } else if (priv->caps & ACCDET_PMIC_EINT1) {
+                       regmap_update_bits(priv->regmap,
+                                          ACCDET_EINT1_INVERTER_SW_EN_ADDR,
+                                          ACCDET_EINT1_INVERTER_SW_EN_MASK_SFT,
+                                          BIT(ACCDET_EINT1_INVERTER_SW_EN_SFT));
+               }
+       }
+}
+
+static void config_eint_init_by_mode(struct mt6359_accdet *priv)
+{
+       unsigned int val = 0;
+
+       if (priv->caps & ACCDET_PMIC_EINT0) {
+               regmap_update_bits(priv->regmap, RG_EINT0EN_ADDR,
+                                  RG_EINT0EN_MASK_SFT, BIT(RG_EINT0EN_SFT));
+       } else if (priv->caps & ACCDET_PMIC_EINT1) {
+               regmap_update_bits(priv->regmap, RG_EINT1EN_ADDR,
+                                  RG_EINT1EN_MASK_SFT, BIT(RG_EINT1EN_SFT));
+       }
+       /* ESD switches on */
+       regmap_update_bits(priv->regmap, RG_ACCDETSPARE_ADDR,
+                          1 << 8, 1 << 8);
+       /* before playback, set NCP pull low before nagative voltage */
+       regmap_update_bits(priv->regmap, RG_NCP_PDDIS_EN_ADDR,
+                          RG_NCP_PDDIS_EN_MASK_SFT, BIT(RG_NCP_PDDIS_EN_SFT));
+
+       if (priv->data->eint_detect_mode == 0x1 ||
+           priv->data->eint_detect_mode == 0x2 ||
+           priv->data->eint_detect_mode == 0x3) {
+               if (priv->data->eint_use_ext_res == 0x1) {
+                       if (priv->caps & ACCDET_PMIC_EINT0) {
+                               regmap_update_bits(priv->regmap,
+                                                  RG_EINT0CONFIGACCDET_ADDR,
+                                                  RG_EINT0CONFIGACCDET_MASK_SFT,
+                                                  0);
+                       } else if (priv->caps & ACCDET_PMIC_EINT1) {
+                               regmap_update_bits(priv->regmap,
+                                                  RG_EINT1CONFIGACCDET_ADDR,
+                                                  RG_EINT1CONFIGACCDET_MASK_SFT,
+                                                  0);
+                       }
+               } else {
+                       if (priv->caps & ACCDET_PMIC_EINT0) {
+                               regmap_update_bits(priv->regmap,
+                                                  RG_EINT0CONFIGACCDET_ADDR,
+                                                  RG_EINT0CONFIGACCDET_MASK_SFT,
+                                                  BIT(RG_EINT0CONFIGACCDET_SFT));
+                       } else if (priv->caps & ACCDET_PMIC_EINT1) {
+                               regmap_update_bits(priv->regmap,
+                                                  RG_EINT1CONFIGACCDET_ADDR,
+                                                  RG_EINT1CONFIGACCDET_MASK_SFT,
+                                                  BIT(RG_EINT1CONFIGACCDET_SFT));
+                       }
+               }
+       }
+
+       if (priv->data->eint_detect_mode != 0x1) {
+               /* current detect set 0.25uA */
+               regmap_update_bits(priv->regmap, RG_ACCDETSPARE_ADDR,
+                                  0x3 << RG_ACCDETSPARE_SFT,
+                                  0x3 << RG_ACCDETSPARE_SFT);
+       }
+       regmap_write(priv->regmap, RG_EINTCOMPVTH_ADDR,
+                    val | priv->data->eint_comp_vth << RG_EINTCOMPVTH_SFT);
+}
+
+static void mt6359_accdet_init(struct mt6359_accdet *priv)
+{
+       unsigned int reg = 0;
+
+       regmap_update_bits(priv->regmap, ACCDET_SEQ_INIT_ADDR,
+                          ACCDET_SEQ_INIT_MASK_SFT, BIT(ACCDET_SEQ_INIT_SFT));
+       mdelay(2);
+       regmap_update_bits(priv->regmap, ACCDET_SEQ_INIT_ADDR,
+                          ACCDET_SEQ_INIT_MASK_SFT, 0);
+       mdelay(1);
+       /* init the debounce time (debounce/32768)sec */
+       accdet_set_debounce(priv, accdet_state000,
+                           priv->data->pwm_deb->debounce0);
+       accdet_set_debounce(priv, accdet_state001,
+                           priv->data->pwm_deb->debounce1);
+       accdet_set_debounce(priv, accdet_state011,
+                           priv->data->pwm_deb->debounce3);
+       accdet_set_debounce(priv, accdet_auxadc,
+                           priv->data->pwm_deb->debounce4);
+
+       accdet_set_debounce(priv, eint_state000,
+                           priv->data->pwm_deb->eint_debounce0);
+       accdet_set_debounce(priv, eint_state001,
+                           priv->data->pwm_deb->eint_debounce1);
+       accdet_set_debounce(priv, eint_state011,
+                           priv->data->pwm_deb->eint_debounce3);
+       accdet_set_debounce(priv, eint_inverter_state000,
+                           priv->data->pwm_deb->eint_inverter_debounce);
+
+       regmap_update_bits(priv->regmap, RG_ACCDET_RST_ADDR,
+                          RG_ACCDET_RST_MASK_SFT, BIT(RG_ACCDET_RST_SFT));
+       regmap_update_bits(priv->regmap, RG_ACCDET_RST_ADDR,
+                          RG_ACCDET_RST_MASK_SFT, 0);
+
+       /* clear high micbias1 voltage setting */
+       regmap_update_bits(priv->regmap, RG_AUDPWDBMICBIAS1_ADDR,
+                          0x3 << RG_AUDMICBIAS1HVEN_SFT, 0);
+       regmap_update_bits(priv->regmap, RG_AUDPWDBMICBIAS1_ADDR,
+                          0x7 << RG_AUDMICBIAS1VREF_SFT, 0);
+
+       /* init pwm frequency, duty & rise/falling delay */
+       regmap_write(priv->regmap, ACCDET_PWM_WIDTH_ADDR,
+                    REGISTER_VAL(priv->data->pwm_deb->pwm_width));
+       regmap_write(priv->regmap, ACCDET_PWM_THRESH_ADDR,
+                    REGISTER_VAL(priv->data->pwm_deb->pwm_thresh));
+       regmap_write(priv->regmap, ACCDET_RISE_DELAY_ADDR,
+                    (priv->data->pwm_deb->fall_delay << 15 |
+                     priv->data->pwm_deb->rise_delay));
+
+       regmap_read(priv->regmap, RG_AUDPWDBMICBIAS1_ADDR, &reg);
+       if (priv->data->mic_vol <= 7) {
+               /* micbias1 <= 2.7V */
+               regmap_write(priv->regmap, RG_AUDPWDBMICBIAS1_ADDR,
+                            reg | (priv->data->mic_vol << RG_AUDMICBIAS1VREF_SFT) |
+                            RG_AUDMICBIAS1LOWPEN_MASK_SFT);
+       } else if (priv->data->mic_vol == 8) {
+               /* micbias1 = 2.8v */
+               regmap_write(priv->regmap, RG_AUDPWDBMICBIAS1_ADDR,
+                            reg | (3 << RG_AUDMICBIAS1HVEN_SFT) |
+                            RG_AUDMICBIAS1LOWPEN_MASK_SFT);
+       } else if (priv->data->mic_vol == 9) {
+               /* micbias1 = 2.85v */
+               regmap_write(priv->regmap, RG_AUDPWDBMICBIAS1_ADDR,
+                            reg | (1 << RG_AUDMICBIAS1HVEN_SFT) |
+                            RG_AUDMICBIAS1LOWPEN_MASK_SFT);
+       }
+       /* mic mode setting */
+       regmap_read(priv->regmap, RG_AUDACCDETMICBIAS0PULLLOW_ADDR, &reg);
+       if (priv->data->mic_mode == HEADSET_MODE_1) {
+               /* ACC mode*/
+               regmap_write(priv->regmap, RG_AUDACCDETMICBIAS0PULLLOW_ADDR,
+                            reg | RG_ACCDET_MODE_ANA11_MODE1);
+               /* enable analog fast discharge */
+               regmap_update_bits(priv->regmap, RG_ANALOGFDEN_ADDR,
+                                  RG_ANALOGFDEN_MASK_SFT,
+                                  BIT(RG_ANALOGFDEN_SFT));
+               regmap_update_bits(priv->regmap, RG_ACCDETSPARE_ADDR,
+                                  0x3 << 11, 0x3 << 11);
+       } else if (priv->data->mic_mode == HEADSET_MODE_2) {
+               /* DCC mode Low cost mode without internal bias */
+               regmap_write(priv->regmap, RG_AUDACCDETMICBIAS0PULLLOW_ADDR,
+                            reg | RG_ACCDET_MODE_ANA11_MODE2);
+               /* enable analog fast discharge */
+               regmap_update_bits(priv->regmap, RG_ANALOGFDEN_ADDR,
+                                  0x3 << RG_ANALOGFDEN_SFT,
+                                  0x3 << RG_ANALOGFDEN_SFT);
+       } else if (priv->data->mic_mode == HEADSET_MODE_6) {
+               /* DCC mode Low cost mode with internal bias,
+                * bit8 = 1 to use internal bias
+                */
+               regmap_write(priv->regmap, RG_AUDACCDETMICBIAS0PULLLOW_ADDR,
+                            reg | RG_ACCDET_MODE_ANA11_MODE6);
+               regmap_update_bits(priv->regmap, RG_AUDPWDBMICBIAS1_ADDR,
+                                  RG_AUDMICBIAS1DCSW1PEN_MASK_SFT,
+                                  BIT(RG_AUDMICBIAS1DCSW1PEN_SFT));
+               /* enable analog fast discharge */
+               regmap_update_bits(priv->regmap, RG_ANALOGFDEN_ADDR,
+                                  0x3 << RG_ANALOGFDEN_SFT,
+                                  0x3 << RG_ANALOGFDEN_SFT);
+       }
+
+       if (priv->caps & ACCDET_PMIC_EINT_IRQ) {
+               config_eint_init_by_mode(priv);
+               config_digital_init_by_mode(priv);
+       }
+}
+
+int mt6359_accdet_enable_jack_detect(struct snd_soc_component *component,
+                                    struct snd_soc_jack *jack)
+{
+       struct mt6359_accdet *priv =
+               snd_soc_component_get_drvdata(component);
+
+       snd_jack_set_key(jack->jack, SND_JACK_BTN_0, KEY_PLAYPAUSE);
+       snd_jack_set_key(jack->jack, SND_JACK_BTN_1, KEY_VOLUMEDOWN);
+       snd_jack_set_key(jack->jack, SND_JACK_BTN_2, KEY_VOLUMEUP);
+       snd_jack_set_key(jack->jack, SND_JACK_BTN_3, KEY_VOICECOMMAND);
+
+       priv->jack = jack;
+
+       mt6359_accdet_jack_report(priv);
+
+       return 0;
+}
+EXPORT_SYMBOL_GPL(mt6359_accdet_enable_jack_detect);
+
+static int mt6359_accdet_probe(struct platform_device *pdev)
+{
+       struct mt6359_accdet *priv;
+       struct mt6397_chip *mt6397 = dev_get_drvdata(pdev->dev.parent);
+       int ret = 0;
+
+       dev_dbg(&pdev->dev, "%s(), dev name %s\n",
+               __func__, dev_name(&pdev->dev));
+
+       priv = devm_kzalloc(&pdev->dev, sizeof(struct mt6359_accdet),
+                           GFP_KERNEL);
+       if (!priv)
+               return -ENOMEM;
+
+       priv->data = devm_kzalloc(&pdev->dev, sizeof(struct dts_data),
+                                 GFP_KERNEL);
+       if (!priv->data)
+               return -ENOMEM;
+
+       priv->data->pwm_deb = devm_kzalloc(&pdev->dev,
+                                          sizeof(struct pwm_deb_settings),
+                                          GFP_KERNEL);
+       if (!priv->data->pwm_deb)
+               return -ENOMEM;
+
+       priv->regmap = mt6397->regmap;
+       if (IS_ERR(priv->regmap)) {
+               ret = PTR_ERR(priv->regmap);
+               dev_err(&pdev->dev, "Failed to allocate register map: %d\n",
+                       ret);
+               return ret;
+       }
+       priv->dev = &pdev->dev;
+
+       ret = mt6359_accdet_parse_dt(priv);
+       if (ret) {
+               dev_err(&pdev->dev, "Failed to parse dts\n");
+               return ret;
+       }
+       mutex_init(&priv->res_lock);
+
+       priv->accdet_irq = platform_get_irq(pdev, 0);
+       if (priv->accdet_irq) {
+               ret = devm_request_threaded_irq(&pdev->dev, priv->accdet_irq,
+                                               NULL, mt6359_accdet_irq,
+                                               IRQF_TRIGGER_HIGH | IRQF_ONESHOT,
+                                               "ACCDET_IRQ", priv);
+               if (ret) {
+                       dev_err(&pdev->dev,
+                               "Failed to request IRQ: (%d)\n", ret);
+                       return ret;
+               }
+       }
+
+       if (priv->caps & ACCDET_PMIC_EINT0) {
+               priv->accdet_eint0 = platform_get_irq(pdev, 1);
+               if (priv->accdet_eint0) {
+                       ret = devm_request_threaded_irq(&pdev->dev,
+                                                       priv->accdet_eint0,
+                                                       NULL, mt6359_accdet_irq,
+                                                       IRQF_TRIGGER_HIGH | IRQF_ONESHOT,
+                                                       "ACCDET_EINT0", priv);
+                       if (ret) {
+                               dev_err(&pdev->dev,
+                                       "Failed to request eint0 IRQ (%d)\n",
+                                       ret);
+                               return ret;
+                       }
+               }
+       } else if (priv->caps & ACCDET_PMIC_EINT1) {
+               priv->accdet_eint1 = platform_get_irq(pdev, 2);
+               if (priv->accdet_eint1) {
+                       ret = devm_request_threaded_irq(&pdev->dev,
+                                                       priv->accdet_eint1,
+                                                       NULL, mt6359_accdet_irq,
+                                                       IRQF_TRIGGER_HIGH | IRQF_ONESHOT,
+                                                       "ACCDET_EINT1", priv);
+                       if (ret) {
+                               dev_err(&pdev->dev,
+                                       "Failed to request eint1 IRQ (%d)\n",
+                                       ret);
+                               return ret;
+                       }
+               }
+       }
+
+       priv->accdet_workqueue = create_singlethread_workqueue("accdet");
+       INIT_WORK(&priv->accdet_work, mt6359_accdet_work);
+       if (!priv->accdet_workqueue) {
+               dev_err(&pdev->dev, "Failed to create accdet workqueue\n");
+               ret = -1;
+               goto err_accdet_wq;
+       }
+
+       priv->jd_workqueue = create_singlethread_workqueue("mt6359_accdet_jd");
+       INIT_WORK(&priv->jd_work, mt6359_accdet_jd_work);
+       if (!priv->jd_workqueue) {
+               dev_err(&pdev->dev, "Failed to create jack detect workqueue\n");
+               ret = -1;
+               goto err_eint_wq;
+       }
+
+       platform_set_drvdata(pdev, priv);
+       ret = devm_snd_soc_register_component(&pdev->dev,
+                                             &mt6359_accdet_soc_driver,
+                                             NULL, 0);
+       if (ret) {
+               dev_err(&pdev->dev, "Failed to register component\n");
+               return ret;
+       }
+
+       priv->jd_sts = M_PLUG_OUT;
+       priv->jack_type = 0;
+       priv->btn_type = 0;
+       priv->accdet_status = 0x3;
+       mt6359_accdet_init(priv);
+
+       mt6359_accdet_jack_report(priv);
+
+       return 0;
+
+err_eint_wq:
+       destroy_workqueue(priv->accdet_workqueue);
+err_accdet_wq:
+       dev_err(&pdev->dev, "%s error. now exit.!\n", __func__);
+       return ret;
+}
+
+static struct platform_driver mt6359_accdet_driver = {
+       .driver = {
+               .name = "pmic-codec-accdet",
+       },
+       .probe = mt6359_accdet_probe,
+};
+
+static int __init mt6359_accdet_driver_init(void)
+{
+       int ret = 0;
+
+       ret = platform_driver_register(&mt6359_accdet_driver);
+       if (ret)
+               return -ENODEV;
+       return 0;
+}
+
+static void __exit mt6359_accdet_driver_exit(void)
+{
+       platform_driver_unregister(&mt6359_accdet_driver);
+}
+module_init(mt6359_accdet_driver_init);
+module_exit(mt6359_accdet_driver_exit);
+
+/* Module information */
+MODULE_DESCRIPTION("MT6359 ALSA SoC codec jack driver");
+MODULE_AUTHOR("Argus Lin <argus.lin@mediatek.com>");
+MODULE_LICENSE("GPL v2");