linux-stable/sound/soc/codecs/mt6359-accdet.c
Rob Herring 340d79a14d
ASoC: Explicitly include correct DT includes
The DT of_device.h and of_platform.h date back to the separate
of_platform_bus_type before it was merged into the regular platform bus.
As part of that merge prepping Arm DT support 13 years ago, they
"temporarily" include each other. They also include platform_device.h
and of.h. As a result, there's a pretty much random mix of those include
files used throughout the tree. In order to detangle these headers and
replace the implicit includes with struct declarations, users need to
explicitly include the correct includes.

Acked-by: Jernej Skrabec <jernej.skrabec@gmail.com>
Reviewed-by: AngeloGioacchino Del Regno <angelogioacchino.delregno@collabora.com>
Acked-by: Charles Keepax <ckeepax@opensource.cirrus.com>
Reviewed-by: Claudiu Beznea <claudiu.beznea@tuxon.dev> # for at91
Signed-off-by: Rob Herring <robh@kernel.org>
Link: https://lore.kernel.org/r/20231006-dt-asoc-header-cleanups-v3-1-13a4f0f7fee6@kernel.org
Signed-off-by: Mark Brown <broonie@kernel.org>
2023-10-09 13:13:56 +01:00

1063 lines
31 KiB
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");