// SPDX-License-Identifier: GPL-2.0 // // mt6359-accdet.c -- ALSA SoC mt6359 accdet driver // // Copyright (C) 2021 MediaTek Inc. // Author: Argus Lin // #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #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, ®); 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; 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 "); MODULE_LICENSE("GPL v2");