root/sound/soc/codecs/mt6359-accdet.c
// 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.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;
        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;
        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;

        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;
        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));
        }

        of_node_put(node);
        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;

        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 >= 0) {
                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 >= 0) {
                        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 >= 0) {
                        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,
};

module_platform_driver(mt6359_accdet_driver)

/* Module information */
MODULE_DESCRIPTION("MT6359 ALSA SoC codec jack driver");
MODULE_AUTHOR("Argus Lin <argus.lin@mediatek.com>");
MODULE_LICENSE("GPL v2");