diff options
Diffstat (limited to 'sound/soc/codecs/mt6359-accdet.c')
-rw-r--r-- | sound/soc/codecs/mt6359-accdet.c | 1080 |
1 files changed, 1080 insertions, 0 deletions
diff --git a/sound/soc/codecs/mt6359-accdet.c b/sound/soc/codecs/mt6359-accdet.c new file mode 100644 index 000000000000..0bef6eaad5ad --- /dev/null +++ b/sound/soc/codecs/mt6359-accdet.c @@ -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, ®); + 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, ®); + 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 reate 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"); |