blob: bbbf4ac9168ee1ffb5360c67d90477b89e3742f5 [file] [log] [blame]
// SPDX-License-Identifier: GPL-2.0
/*
* Copyright (C) 2019 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/iio/consumer.h>
#include <linux/nvmem-consumer.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/mt6359/registers.h>
#include <linux/mfd/mt6397/core.h>
#include <linux/mfd/mt6359/core.h>
#include "mt6359-accdet.h"
#include "mt6359.h"
/* grobal variable definitions */
#define REGISTER_VAL(x) (x - 1)
#define HAS_CAP(_c, _x) (((_c) & (_x)) == (_x))
#define ACCDET_HW_FAST_DISCHRAGE BIT(0)
#define ACCDET_PMIC_EINT_IRQ 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_EINT BIT(5)
#define ACCDET_PMIC_INVERTER_EINT BIT(6)
#define ACCDET_AP_GPIO_EINT BIT(7)
#define ACCDET_THREE_KEY BIT(8)
#define ACCDET_FOUR_KEY BIT(9)
#define ACCDET_TRI_KEY_CDD BIT(10)
/* Used to let accdet know if the pin has been fully plugged-in */
#define EINT_PLUG_OUT (0)
#define EINT_PLUG_IN (1)
/* defines for codec accdet registers */
enum accdet_regs {
TOP_TOP0_ANA_ID,
TOP_SWCID,
TOP_RG_RTC32K_CK_PDN,
TOP_INT_STATUS_AUD_TOP,
PLT_CPU_INT_STA,
LDO_RG_LDO_VUSB_HW0_OP_EN,
ACCDET_AUXADC_RQST_CH5,
ACCDET_AUXADC_ACCDET_AUTO_SPL,
ACCDET_RG_ACCDET_CK_PDN,
ACCDET_RG_ACCDET_RST,
ACCDET_RG_INT_EN_ACCDET,
ACCDET_RG_INT_MASK_ACCDET,
ACCDET_RG_INT_STATUS_ACCDET,
ACCDET_AUDIO_DIG_ANA_ID,
ACCDET_RG_NCP_PDDIS_EN,
ACCDET_RG_AUDPREAMPLON,
ACCDET_RG_AUDPWDBMICBIAS0,
ACCDET_RG_AUDPWDBMICBIAS1,
ACCDET_RG_AUDACCDETMICBIAS0PULLLOW,
ACCDET_RG_EINT0CONFIGACCDET,
ACCDET_RG_EINT0HIRENB,
ACCDET_RG_EINT0NOHYS,
ACCDET_RG_ACCDET2AUXSWEN,
ACCDET_RG_EINT1CONFIGACCDET,
ACCDET_RG_MTEST_EN,
ACCDET_RG_MTEST_SEL,
ACCDET_RG_EINTCOMPVTH,
ACCDET_RG_ANALOGFDEN,
ACCDET_RG_EINT0CTURBO,
ACCDET_RG_EINT1CTURBO,
ACCDET_RG_EINT0EN,
ACCDET_RG_EINT1EN,
ACCDET_RG_EINT0CMPEN,
ACCDET_RG_ACCDETSPARE,
ACCDET_RG_CLKSQ_EN,
ACCDET_RG_HPLOUTPUTSTBENH_VAUDP32,
ACCDET_RG_HPROUTPUTSTBENH_VAUDP32,
ACCDET_AUDACCDETAUXADCSWCTRL_SW,
ACCDET_AUDACCDETAUXADCSWCTRL_SEL,
ACCDET_AUXADC_SEL,
ACCDET_EINT_M_DETECT_EN,
ACCDET_EINT0_INVERTER_SW_EN,
ACCDET_EINT1_INVERTER_SW_EN,
ACCDET_EINT0_M_SW_EN,
ACCDET_EINT1_M_SW_EN,
ACCDET_SEQ_INIT,
ACCDET_EINT0_SW_EN,
ACCDET_EINT1_SW_EN,
ACCDET_SW_EN,
ACCDET_CMP_PWM_EN,
ACCDET_PWM_WIDTH,
ACCDET_PWM_THRESH,
ACCDET_RISE_DELAY,
ACCDET_EINT_CMPMEN_PWM_THRESH,
ACCDET_DEBOUNCE0,
ACCDET_DEBOUNCE1,
ACCDET_DEBOUNCE2,
ACCDET_DEBOUNCE3,
ACCDET_CONNECT_AUXADC_TIME_DIG,
ACCDET_EINT_DEBOUNCE0,
ACCDET_EINT_DEBOUNCE1,
ACCDET_EINT_DEBOUNCE2,
ACCDET_EINT_DEBOUNCE3,
ACCDET_EINT_INVERTER_DEBOUNCE,
ACCDET_IRQ,
ACCDET_EINT_M_PLUG_IN_NUM,
ACCDET_DA_STABLE,
ACCDET_HWMODE_EN,
ACCDET_CMPEN_SEL,
ACCDET_EINT_CMPMOUT_SEL,
ACCDET_EINT_CMPMEN_SEL,
ACCDET_EINT_CTURBO_SEL,
ACCDET_EINT0_CTURBO_SW,
ACCDET_CMPEN_SW,
ACCDET_AD_AUDACCDETCMPOB,
ACCDET_MEM_IN,
ACCDET_AD_EINT0CMPMOUT,
ACCDET_EINT0_MEM_IN,
ACCDET_AD_EINT0INVOUT,
ACCDET_EN,
ACCDET_MON_FLAG_EN,
};
static const u32 mt6359_aud_regs[] = {
[TOP_TOP0_ANA_ID] = 0x0000,
[TOP_SWCID] = 0x000a,
[TOP_RG_RTC32K_CK_PDN] = 0x010c,
[TOP_INT_STATUS_AUD_TOP] = 0x019e,
[PLT_CPU_INT_STA] = 0x0452,
[LDO_RG_LDO_VUSB_HW0_OP_EN] = 0x1d0c,
[ACCDET_AUXADC_RQST_CH5] = 0x1108,
[ACCDET_AUXADC_ACCDET_AUTO_SPL] = 0x11ba,
[ACCDET_RG_ACCDET_CK_PDN] = 0x230c,
[ACCDET_RG_ACCDET_RST] = 0x2320,
[ACCDET_RG_INT_EN_ACCDET] = 0x2328,
[ACCDET_RG_INT_MASK_ACCDET] = 0x232e,
[ACCDET_RG_INT_STATUS_ACCDET] = 0x2334,
[ACCDET_AUDIO_DIG_ANA_ID] = 0x2380,
[ACCDET_RG_NCP_PDDIS_EN] = 0x24e2,
[ACCDET_RG_AUDPREAMPLON] = 0x2508,
[ACCDET_RG_AUDPWDBMICBIAS0] = 0x2526,
[ACCDET_RG_AUDPWDBMICBIAS1] = 0x2528,
[ACCDET_RG_AUDACCDETMICBIAS0PULLLOW] = 0x252c,
[ACCDET_RG_EINT0CONFIGACCDET] = 0x252c,
[ACCDET_RG_EINT0HIRENB] = 0x252c,
[ACCDET_RG_EINT0NOHYS] = 0x252c,
[ACCDET_RG_ACCDET2AUXSWEN] = 0x252c,
[ACCDET_RG_EINT1CONFIGACCDET] = 0x252e,
[ACCDET_RG_MTEST_EN] = 0x252e,
[ACCDET_RG_MTEST_SEL] = 0x252e,
[ACCDET_RG_EINTCOMPVTH] = 0x252e,
[ACCDET_RG_ANALOGFDEN] = 0x252e,
[ACCDET_RG_EINT0CTURBO] = 0x2530,
[ACCDET_RG_EINT1CTURBO] = 0x2530,
[ACCDET_RG_EINT0EN] = 0x2530,
[ACCDET_RG_EINT1EN] = 0x2530,
[ACCDET_RG_EINT0CMPEN] = 0x2530,
[ACCDET_RG_ACCDETSPARE] = 0x2532,
[ACCDET_RG_CLKSQ_EN] = 0x2536,
[ACCDET_RG_HPLOUTPUTSTBENH_VAUDP32] = 0x258c,
[ACCDET_RG_HPROUTPUTSTBENH_VAUDP32] = 0x258c,
[ACCDET_AUDACCDETAUXADCSWCTRL_SW] = 0x2688,
[ACCDET_AUDACCDETAUXADCSWCTRL_SEL] = 0x2688,
[ACCDET_AUXADC_SEL] = 0x2688,
[ACCDET_EINT_M_DETECT_EN] = 0x268a,
[ACCDET_EINT0_INVERTER_SW_EN] = 0x268a,
[ACCDET_EINT1_INVERTER_SW_EN] = 0x268a,
[ACCDET_EINT0_M_SW_EN] = 0x268a,
[ACCDET_EINT1_M_SW_EN] = 0x268a,
[ACCDET_SEQ_INIT] = 0x268a,
[ACCDET_EINT0_SW_EN] = 0x268a,
[ACCDET_EINT1_SW_EN] = 0x268a,
[ACCDET_SW_EN] = 0x268a,
[ACCDET_CMP_PWM_EN] = 0x268c,
[ACCDET_PWM_WIDTH] = 0x268e,
[ACCDET_PWM_THRESH] = 0x2690,
[ACCDET_RISE_DELAY] = 0x2692,
[ACCDET_EINT_CMPMEN_PWM_THRESH] = 0x2694,
[ACCDET_DEBOUNCE0] = 0x2698,
[ACCDET_DEBOUNCE1] = 0x269a,
[ACCDET_DEBOUNCE2] = 0x269c,
[ACCDET_DEBOUNCE3] = 0x269e,
[ACCDET_CONNECT_AUXADC_TIME_DIG] = 0x26a0,
[ACCDET_EINT_DEBOUNCE0] = 0x26a4,
[ACCDET_EINT_DEBOUNCE1] = 0x26a4,
[ACCDET_EINT_DEBOUNCE2] = 0x26a4,
[ACCDET_EINT_DEBOUNCE3] = 0x26a4,
[ACCDET_EINT_INVERTER_DEBOUNCE] = 0x26a6,
[ACCDET_IRQ] = 0x26ac,
[ACCDET_EINT_M_PLUG_IN_NUM] = 0x26ac,
[ACCDET_DA_STABLE] = 0x26ae,
[ACCDET_HWMODE_EN] = 0x26b0,
[ACCDET_CMPEN_SEL] = 0x26b4,
[ACCDET_EINT_CMPMOUT_SEL] = 0x26b4,
[ACCDET_EINT_CMPMEN_SEL] = 0x26b4,
[ACCDET_EINT_CTURBO_SEL] = 0x26b4,
[ACCDET_EINT0_CTURBO_SW] = 0x26b6,
[ACCDET_CMPEN_SW] = 0x26b6,
[ACCDET_AD_AUDACCDETCMPOB] = 0x26ba,
[ACCDET_MEM_IN] = 0x26ba,
[ACCDET_AD_EINT0CMPMOUT] = 0x26bc,
[ACCDET_EINT0_MEM_IN] = 0x26bc,
[ACCDET_AD_EINT0INVOUT] = 0x26c0,
[ACCDET_EN] = 0x26c4,
[ACCDET_MON_FLAG_EN] = 0x26d8,
};
struct mt63xx_accdet_data {
u32 base;
struct snd_soc_card card;
struct snd_soc_jack jack;
struct platform_device *pdev;
struct device *dev;
struct accdet_priv *data;
atomic_t init_once;
struct regmap *regmap;
struct iio_channel *accdet_auxadc;
struct nvmem_device *accdet_efuse;
int accdet_irq;
int accdet_eint0;
int accdet_eint1;
struct wakeup_source *wake_lock;
struct wakeup_source *timer_lock;
struct mutex res_lock;
dev_t accdet_devno;
struct class *accdet_class;
int button_status;
bool eint_sync_flag;
/* accdet FSM State & lock*/
u32 cur_eint_state;
u32 eint0_state;
u32 eint1_state;
u32 accdet_status;
u32 cable_type;
u32 cur_key;
u32 cali_voltage;
int auxadc_offset;
u32 eint_id;
bool thing_in_flag;
/* when caps include ACCDET_AP_GPIO_EINT */
struct pinctrl *pinctrl;
struct pinctrl_state *pins_eint;
u32 gpiopin;
u32 gpio_hp_deb;
u32 gpioirq;
u32 accdet_eint_type;
/* when MICBIAS_DISABLE_TIMER timeout, queue work: dis_micbias_work */
struct work_struct dis_micbias_work;
struct workqueue_struct *dis_micbias_workqueue;
struct work_struct accdet_work;
struct workqueue_struct *accdet_workqueue;
/* when eint issued, queue work: eint_work */
struct work_struct eint_work;
struct workqueue_struct *eint_workqueue;
};
static struct mt63xx_accdet_data *accdet;
static struct head_dts_data accdet_dts;
struct pwm_deb_settings *cust_pwm_deb;
struct accdet_priv {
const u32 *regs;
u32 caps;
struct snd_card *snd_card;
};
static struct accdet_priv mt6359_accdet[] = {
{
.regs = mt6359_aud_regs,
.caps = ACCDET_PMIC_EINT_IRQ | ACCDET_PMIC_EINT0
| ACCDET_PMIC_INVERTER_EINT | ACCDET_THREE_KEY,
},
};
const struct of_device_id accdet_of_match[] = {
{
.compatible = "mediatek,mt6359-accdet",
.data = &mt6359_accdet,
}, {
.compatible = "mediatek,mt8163-accdet",
}, {
.compatible = "mediatek,mt8173-accdet",
}, {
/* sentinel */
},
};
static struct snd_soc_jack_pin accdet_jack_pins[] = {
{
.pin = "Headset",
.mask = SND_JACK_HEADSET |
SND_JACK_LINEOUT |
SND_JACK_MECHANICAL,
},
};
/* micbias_timer: disable micbias if no accdet irq after eint,
* timeout: 6 seconds
* timerHandler: dis_micbias_timerhandler()
*/
#define MICBIAS_DISABLE_TIMER (6 * HZ)
static struct timer_list micbias_timer;
static void dis_micbias_timerhandler(struct timer_list *t);
static bool dis_micbias_done;
static u32 button_press_debounce = 0x400;
/* local function declaration */
static void accdet_init_once(void);
static inline void accdet_init(void);
static void accdet_init_debounce(void);
static u32 adjust_eint_analog_setting(void);
static u32 adjust_eint_setting(u32 eintsts);
static void config_digital_init_by_mode(void);
static void config_eint_init_by_mode(void);
static u32 get_triggered_eint(void);
static void recover_eint_analog_setting(void);
static void recover_eint_digital_setting(void);
static void recover_eint_setting(u32 eintsts);
static void send_status_event(u32 cable_type, u32 status);
/* global function declaration */
inline u32 accdet_read(enum accdet_regs addr)
{
u32 val = 0;
if (accdet->regmap) {
regmap_read(accdet->regmap,
accdet->base + accdet->data->regs[addr], &val);
} else
pr_notice("%s %d Error.\n", __func__, __LINE__);
return val;
}
inline u32 accdet_read_bits(enum accdet_regs addr, u32 shift, u32 mask)
{
u32 val = 0;
val = accdet_read(addr);
return ((val>>shift) & mask);
}
inline void accdet_write(enum accdet_regs addr, u32 wdata)
{
if (accdet->regmap) {
regmap_write(accdet->regmap,
accdet->base + accdet->data->regs[addr], wdata);
} else
pr_notice("%s %d Error.\n", __func__, __LINE__);
}
inline void accdet_update_bits(enum accdet_regs addr, u32 shift,
u32 mask, u32 data)
{
regmap_update_bits(accdet->regmap,
accdet->base + accdet->data->regs[addr],
mask << shift,
data << shift);
}
inline void accdet_update_bit(enum accdet_regs addr, unsigned int shift)
{
unsigned int mask = shift;
regmap_update_bits(accdet->regmap,
accdet->base + accdet->data->regs[addr],
BIT(mask),
BIT(shift));
}
inline void accdet_clear_bits(enum accdet_regs addr, unsigned int shift,
unsigned int mask, unsigned int data)
{
regmap_update_bits(accdet->regmap,
accdet->base + accdet->data->regs[addr],
mask << shift,
~(data << shift));
}
inline void accdet_clear_bit(enum accdet_regs addr, unsigned int shift)
{
unsigned int mask = shift;
regmap_update_bits(accdet->regmap,
accdet->base + accdet->data->regs[addr],
BIT(mask),
~(BIT(shift)));
}
static u64 accdet_get_current_time(void)
{
return sched_clock();
}
static bool accdet_timeout_ns(u64 start_time_ns, u64 timeout_time_ns)
{
u64 cur_time = 0;
u64 elapse_time = 0;
/* get current tick, ns */
cur_time = accdet_get_current_time();
if (cur_time < start_time_ns) {
start_time_ns = cur_time;
/* 400us */
timeout_time_ns = 400 * 1000;
}
elapse_time = cur_time - start_time_ns;
/* check if timeout */
if (timeout_time_ns <= elapse_time)
return false;
return true;
}
static u32 accdet_get_auxadc(void)
{
int vol = 0, ret = 0;
if (!IS_ERR(accdet->accdet_auxadc)) {
ret = iio_read_channel_processed(accdet->accdet_auxadc, &vol);
if (ret < 0) {
pr_notice("Error: %s read fail (%d)\n", __func__, ret);
return ret;
}
}
pr_info("%s() vol_val:%d offset:%d real vol:%d mv!\n", __func__, vol,
accdet->auxadc_offset,
(vol < accdet->auxadc_offset) ? 0 : (vol-accdet->auxadc_offset));
if (vol < accdet->auxadc_offset)
vol = 0;
else
vol -= accdet->auxadc_offset;
return vol;
}
static void accdet_get_efuse(void)
{
unsigned short efuseval = 0;
int ret = 0;
/* accdet offset efuse:
* this efuse must divided by 2
*/
ret = nvmem_device_read(accdet->accdet_efuse, 102*2, 2, &efuseval);
accdet->auxadc_offset = efuseval & 0xFF;
if (accdet->auxadc_offset > 128)
accdet->auxadc_offset -= 256;
accdet->auxadc_offset = (accdet->auxadc_offset >> 1);
pr_info("%s efuse=0x%x,auxadc_val=%dmv\n", __func__, efuseval,
accdet->auxadc_offset);
}
static void accdet_get_efuse_4key(void)
{
unsigned short tmp_val = 0;
unsigned short tmp_8bit = 0;
int ret = 0;
/* 4-key efuse:
* bit[9:2] efuse value is loaded, so every read out value need to be
* left shift 2 bit,and then compare with voltage get from AUXADC.
* AD efuse: key-A Voltage:0--AD;
* DB efuse: key-D Voltage: AD--DB;
* BC efuse: key-B Voltage:DB--BC;
* key-C Voltage: BC--600;
*/
ret = nvmem_device_read(accdet->accdet_efuse, 103*2, 2, &tmp_val);
tmp_8bit = tmp_val & ACCDET_CALI_MASK0;
accdet_dts.four_key.mid = tmp_8bit << 2;
tmp_8bit = (tmp_val >> 8) & ACCDET_CALI_MASK0;
accdet_dts.four_key.voice = tmp_8bit << 2;
ret = nvmem_device_read(accdet->accdet_efuse, 104*2, 2, &tmp_val);
tmp_8bit = tmp_val & ACCDET_CALI_MASK0;
accdet_dts.four_key.up = tmp_8bit << 2;
accdet_dts.four_key.down = 600;
pr_info("accdet key thresh: mid=%dmv,voice=%dmv,up=%dmv,down=%dmv\n",
accdet_dts.four_key.mid, accdet_dts.four_key.voice,
accdet_dts.four_key.up, accdet_dts.four_key.down);
}
static u32 key_check(u32 v)
{
if (HAS_CAP(accdet->data->caps, ACCDET_FOUR_KEY)) {
if ((v < accdet_dts.four_key.down) &&
(v >= accdet_dts.four_key.up))
return DW_KEY;
if ((v < accdet_dts.four_key.up) &&
(v >= accdet_dts.four_key.voice))
return UP_KEY;
if ((v < accdet_dts.four_key.voice) &&
(v >= accdet_dts.four_key.mid))
return AS_KEY;
if (v < accdet_dts.four_key.mid)
return MD_KEY;
} else {
if ((v < accdet_dts.three_key.down) &&
(v >= accdet_dts.three_key.up))
return DW_KEY;
if ((v < accdet_dts.three_key.up) &&
(v >= accdet_dts.three_key.mid))
return UP_KEY;
if (v < accdet_dts.three_key.mid)
return MD_KEY;
}
return NO_KEY;
}
static void send_key_event(u32 keycode, u32 flag)
{
int report = 0;
switch (keycode) {
case DW_KEY:
if (flag != 0)
report = SND_JACK_BTN_1;
snd_soc_jack_report(&accdet->jack, report,
SND_JACK_BTN_1);
break;
case UP_KEY:
if (flag != 0)
report = SND_JACK_BTN_2;
snd_soc_jack_report(&accdet->jack, report,
SND_JACK_BTN_2);
break;
case MD_KEY:
if (flag != 0)
report = SND_JACK_BTN_0;
snd_soc_jack_report(&accdet->jack, report,
SND_JACK_BTN_0);
break;
case AS_KEY:
if (flag != 0)
report = SND_JACK_BTN_3;
snd_soc_jack_report(&accdet->jack, report,
SND_JACK_BTN_3);
break;
}
}
static void send_status_event(u32 cable_type, u32 status)
{
int report = 0;
switch (cable_type) {
case HEADSET_NO_MIC:
if (status)
report = SND_JACK_HEADPHONE;
else
report = 0;
snd_soc_jack_report(&accdet->jack, report,
SND_JACK_HEADPHONE);
/* when plug 4-pole out, if both AB=3 AB=0 happen,3-pole plug
* in will be incorrectly reported, then 3-pole plug-out is
* reported,if no mantory 4-pole plug-out, icon would be
* visible.
*/
if (status == 0) {
report = 0;
snd_soc_jack_report(&accdet->jack, report,
SND_JACK_MICROPHONE);
}
pr_info("accdet HEADPHONE(3-pole) %s\n",
status ? "PlugIn" : "PlugOut");
break;
case HEADSET_MIC:
/* when plug 4-pole out, 3-pole plug out should also be
* reported for slow plug-in case
*/
if (status == 0) {
report = 0;
snd_soc_jack_report(&accdet->jack, report,
SND_JACK_HEADPHONE);
}
if (status)
report = SND_JACK_MICROPHONE;
else
report = 0;
snd_soc_jack_report(&accdet->jack, report,
SND_JACK_MICROPHONE);
pr_info("accdet MICROPHONE(4-pole) %s\n",
status ? "PlugIn" : "PlugOut");
break;
case LINE_OUT_DEVICE:
if (status)
report = SND_JACK_LINEOUT;
else
report = 0;
snd_soc_jack_report(&accdet->jack, report,
SND_JACK_LINEOUT);
pr_info("accdet LineOut %s\n",
status ? "PlugIn" : "PlugOut");
break;
default:
pr_info("%s Invalid cableType\n", __func__);
}
}
static void multi_key_detection(u32 cur_AB)
{
if (cur_AB == ACCDET_STATE_AB_00)
accdet->cur_key = key_check(accdet->cali_voltage);
/* delay to fix side effect key when plug-out, when plug-out,seldom
* issued AB=0 and Eint, delay to wait eint been flaged in register.
* or eint handler issued. accdet->cur_eint_state == PLUG_OUT
*/
usleep_range(10000, 12000);
if (HAS_CAP(accdet->data->caps, ACCDET_AP_GPIO_EINT)) {
if (accdet->cur_eint_state == EINT_PLUG_IN)
send_key_event(accdet->cur_key, !cur_AB);
else
accdet->cur_key = NO_KEY;
} else {
bool irq_bit;
irq_bit = !(accdet_read(ACCDET_IRQ) & ACCDET_EINT_IRQ_B2_B3);
/* send key when:
* no eint is flaged in reg, and now eint PLUG_IN
*/
if (irq_bit && (accdet->cur_eint_state == EINT_PLUG_IN))
send_key_event(accdet->cur_key, !cur_AB);
else
accdet->cur_key = NO_KEY;
}
if (cur_AB)
accdet->cur_key = NO_KEY;
}
static inline void clear_accdet_int(void)
{
/* it is safe by using polling to adjust when to clear IRQ_CLR_BIT */
accdet_update_bit(ACCDET_IRQ, PMIC_ACCDET_IRQ_CLR_SHIFT);
}
static inline void clear_accdet_int_check(void)
{
u64 cur_time = accdet_get_current_time();
while ((accdet_read(ACCDET_IRQ) & ACCDET_IRQ_B0) &&
(accdet_timeout_ns(cur_time, ACCDET_TIME_OUT)))
;
/* clear accdet int, modify for fix interrupt trigger twice error */
accdet_clear_bit(ACCDET_IRQ, PMIC_ACCDET_IRQ_CLR_SHIFT);
accdet_update_bit(ACCDET_RG_INT_STATUS_ACCDET,
PMIC_RG_INT_STATUS_ACCDET_SHIFT);
}
static inline void clear_accdet_eint(u32 eintid)
{
if ((eintid & PMIC_EINT0) == PMIC_EINT0) {
accdet_update_bit(ACCDET_IRQ,
PMIC_ACCDET_EINT0_IRQ_CLR_SHIFT);
}
if ((eintid & PMIC_EINT1) == PMIC_EINT1) {
accdet_update_bit(ACCDET_IRQ,
PMIC_ACCDET_EINT1_IRQ_CLR_SHIFT);
}
}
static inline void clear_accdet_eint_check(u32 eintid)
{
u64 cur_time = accdet_get_current_time();
if ((eintid & PMIC_EINT0) == PMIC_EINT0) {
while ((accdet_read(ACCDET_IRQ) & ACCDET_EINT0_IRQ_B2)
&& (accdet_timeout_ns(cur_time, ACCDET_TIME_OUT)))
;
accdet_clear_bit(ACCDET_IRQ,
PMIC_ACCDET_EINT0_IRQ_CLR_SHIFT);
accdet_update_bit(ACCDET_RG_INT_STATUS_ACCDET,
PMIC_RG_INT_STATUS_ACCDET_EINT0_SHIFT);
}
if ((eintid & PMIC_EINT1) == PMIC_EINT1) {
while ((accdet_read(ACCDET_IRQ) & ACCDET_EINT1_IRQ_B3)
&& (accdet_timeout_ns(cur_time, ACCDET_TIME_OUT)))
;
accdet_clear_bit(ACCDET_IRQ,
PMIC_ACCDET_EINT1_IRQ_CLR_SHIFT);
accdet_update_bit(ACCDET_RG_INT_STATUS_ACCDET,
PMIC_RG_INT_STATUS_ACCDET_EINT1_SHIFT);
}
}
static u32 adjust_eint_analog_setting(void)
{
if (accdet_dts.eint_detect_mode == 0x4) {
/* ESD switches off */
accdet_clear_bit(ACCDET_RG_ACCDETSPARE, 8);
}
if (accdet_dts.eint_detect_mode == 0x4) {
if (HAS_CAP(accdet->data->caps,
ACCDET_PMIC_EINT0)) {
/* enable RG_EINT0CONFIGACCDET */
accdet_update_bit(ACCDET_RG_EINT0CONFIGACCDET,
PMIC_RG_EINT0CONFIGACCDET_SHIFT);
} else if (HAS_CAP(accdet->data->caps,
ACCDET_PMIC_EINT1)) {
/* enable RG_EINT1CONFIGACCDET */
accdet_update_bit(ACCDET_RG_EINT1CONFIGACCDET,
PMIC_RG_EINT1CONFIGACCDET_SHIFT);
} else if (HAS_CAP(accdet->data->caps,
ACCDET_PMIC_BI_EINT)) {
/* enable RG_EINT0CONFIGACCDET */
accdet_update_bit(ACCDET_RG_EINT0CONFIGACCDET,
PMIC_RG_EINT0CONFIGACCDET_SHIFT);
/* enable RG_EINT1CONFIGACCDET */
accdet_update_bit(ACCDET_RG_EINT1CONFIGACCDET,
PMIC_RG_EINT1CONFIGACCDET_SHIFT);
}
}
return 0;
}
static u32 adjust_eint_digital_setting(void)
{
if (HAS_CAP(accdet->data->caps, ACCDET_PMIC_EINT0)) {
/* disable inverter */
accdet_clear_bit(ACCDET_EINT0_INVERTER_SW_EN,
PMIC_ACCDET_EINT0_INVERTER_SW_EN_SHIFT);
} else if (HAS_CAP(accdet->data->caps, ACCDET_PMIC_EINT1)) {
/* disable inverter */
accdet_clear_bit(ACCDET_EINT1_INVERTER_SW_EN,
PMIC_ACCDET_EINT1_INVERTER_SW_EN_SHIFT);
} else if (HAS_CAP(accdet->data->caps, ACCDET_PMIC_BI_EINT)) {
/* disable inverter */
accdet_clear_bit(ACCDET_EINT0_INVERTER_SW_EN,
PMIC_ACCDET_EINT0_INVERTER_SW_EN_SHIFT);
/* disable inverter */
accdet_clear_bit(ACCDET_EINT1_INVERTER_SW_EN,
PMIC_ACCDET_EINT1_INVERTER_SW_EN_SHIFT);
}
if (accdet_dts.eint_detect_mode == 0x4) {
if (HAS_CAP(accdet->data->caps,
ACCDET_PMIC_EINT0)) {
/* set DA stable signal */
accdet_clear_bit(ACCDET_DA_STABLE,
PMIC_ACCDET_EINT0_CEN_STABLE_SHIFT);
} else if (HAS_CAP(accdet->data->caps,
ACCDET_PMIC_EINT1)) {
/* set DA stable signal */
accdet_clear_bit(ACCDET_DA_STABLE,
PMIC_ACCDET_EINT1_CEN_STABLE_SHIFT);
} else if (HAS_CAP(accdet->data->caps,
ACCDET_PMIC_BI_EINT)) {
/* set DA stable signal */
accdet_clear_bit(ACCDET_DA_STABLE,
PMIC_ACCDET_EINT0_CEN_STABLE_SHIFT);
/* set DA stable signal */
accdet_clear_bit(ACCDET_DA_STABLE,
PMIC_ACCDET_EINT1_CEN_STABLE_SHIFT);
}
}
return 0;
}
static u32 adjust_eint_setting(u32 eintsts)
{
if (eintsts == M_PLUG_IN) {
/* adjust digital setting */
adjust_eint_digital_setting();
/* adjust analog setting */
adjust_eint_analog_setting();
} else if (eintsts == M_PLUG_OUT) {
/* set debounce to 1ms */
accdet_set_debounce(eint_state000,
accdet_dts.pwm_deb.eint_debounce0);
}
return 0;
}
static void recover_eint_analog_setting(void)
{
if (accdet_dts.eint_detect_mode == 0x4) {
/* ESD switches on */
accdet_update_bit(ACCDET_RG_ACCDETSPARE, 8);
if (HAS_CAP(accdet->data->caps,
ACCDET_PMIC_EINT0)) {
/* disable RG_EINT0CONFIGACCDET */
accdet_clear_bit(ACCDET_RG_EINT0CONFIGACCDET,
PMIC_RG_EINT0CONFIGACCDET_SHIFT);
} else if (HAS_CAP(accdet->data->caps,
ACCDET_PMIC_EINT1)) {
/* disable RG_EINT1CONFIGACCDET */
accdet_clear_bit(ACCDET_RG_EINT1CONFIGACCDET,
PMIC_RG_EINT1CONFIGACCDET_SHIFT);
} else if (HAS_CAP(accdet->data->caps,
ACCDET_PMIC_BI_EINT)) {
/* disable RG_EINT0CONFIGACCDET */
accdet_clear_bit(ACCDET_RG_EINT0CONFIGACCDET,
PMIC_RG_EINT0CONFIGACCDET_SHIFT);
/* disable RG_EINT0CONFIGACCDET */
accdet_clear_bit(ACCDET_RG_EINT1CONFIGACCDET,
PMIC_RG_EINT1CONFIGACCDET_SHIFT);
}
accdet_clear_bit(ACCDET_RG_EINT0HIRENB,
PMIC_RG_EINT0HIRENB_SHIFT);
}
}
static void recover_eint_digital_setting(void)
{
if (HAS_CAP(accdet->data->caps,
ACCDET_PMIC_EINT0)) {
accdet_clear_bit(ACCDET_EINT0_M_SW_EN,
PMIC_ACCDET_EINT0_M_SW_EN_SHIFT);
} else if (HAS_CAP(accdet->data->caps,
ACCDET_PMIC_EINT1)) {
accdet_clear_bit(ACCDET_EINT1_M_SW_EN,
PMIC_ACCDET_EINT1_M_SW_EN_SHIFT);
} else if (HAS_CAP(accdet->data->caps,
ACCDET_PMIC_BI_EINT)) {
accdet_clear_bit(ACCDET_EINT0_M_SW_EN,
PMIC_ACCDET_EINT0_M_SW_EN_SHIFT);
accdet_clear_bit(ACCDET_EINT1_M_SW_EN,
PMIC_ACCDET_EINT1_M_SW_EN_SHIFT);
}
if (accdet_dts.eint_detect_mode == 0x4) {
/* enable eint0cen */
if (HAS_CAP(accdet->data->caps,
ACCDET_PMIC_EINT0)) {
/* enable eint0cen */
accdet_update_bit(ACCDET_DA_STABLE,
PMIC_ACCDET_EINT0_CEN_STABLE_SHIFT);
} else if (HAS_CAP(accdet->data->caps,
ACCDET_PMIC_EINT1)) {
/* enable eint1cen */
accdet_update_bit(ACCDET_DA_STABLE,
PMIC_ACCDET_EINT1_CEN_STABLE_SHIFT);
} else if (HAS_CAP(accdet->data->caps,
ACCDET_PMIC_BI_EINT)) {
/* enable eint0cen */
accdet_update_bit(ACCDET_DA_STABLE,
PMIC_ACCDET_EINT0_CEN_STABLE_SHIFT);
/* enable eint1cen */
accdet_update_bit(ACCDET_DA_STABLE,
PMIC_ACCDET_EINT1_CEN_STABLE_SHIFT);
}
}
if (accdet_dts.eint_detect_mode != 0x1) {
if (HAS_CAP(accdet->data->caps,
ACCDET_PMIC_EINT0)) {
/* enable inverter */
accdet_update_bit(ACCDET_EINT0_INVERTER_SW_EN,
PMIC_ACCDET_EINT0_INVERTER_SW_EN_SHIFT);
} else if (HAS_CAP(accdet->data->caps,
ACCDET_PMIC_EINT1)) {
/* enable inverter */
accdet_update_bit(ACCDET_EINT1_INVERTER_SW_EN,
PMIC_ACCDET_EINT1_INVERTER_SW_EN_SHIFT);
} else if (HAS_CAP(accdet->data->caps,
ACCDET_PMIC_BI_EINT)) {
/* enable inverter */
accdet_update_bit(ACCDET_EINT0_INVERTER_SW_EN,
PMIC_ACCDET_EINT0_INVERTER_SW_EN_SHIFT);
/* enable inverter */
accdet_update_bit(ACCDET_EINT1_INVERTER_SW_EN,
PMIC_ACCDET_EINT1_INVERTER_SW_EN_SHIFT);
}
}
}
static void recover_eint_setting(u32 eintsts)
{
if (eintsts == M_PLUG_OUT) {
recover_eint_analog_setting();
recover_eint_digital_setting();
}
}
static u32 get_triggered_eint(void)
{
u32 eint_ID = NO_PMIC_EINT;
u32 irq_status = accdet_read(ACCDET_IRQ);
if (HAS_CAP(accdet->data->caps,
ACCDET_PMIC_EINT0)) {
if ((irq_status & ACCDET_EINT0_IRQ_B2) == ACCDET_EINT0_IRQ_B2)
eint_ID = PMIC_EINT0;
} else if (HAS_CAP(accdet->data->caps,
ACCDET_PMIC_EINT1)) {
if ((irq_status & ACCDET_EINT1_IRQ_B3) == ACCDET_EINT1_IRQ_B3)
eint_ID = PMIC_EINT1;
} else if (HAS_CAP(accdet->data->caps,
ACCDET_PMIC_BI_EINT)) {
if ((irq_status & ACCDET_EINT0_IRQ_B2) == ACCDET_EINT0_IRQ_B2)
eint_ID |= PMIC_EINT0;
if ((irq_status & ACCDET_EINT1_IRQ_B3) == ACCDET_EINT1_IRQ_B3)
eint_ID |= PMIC_EINT1;
}
return eint_ID;
}
static inline void enable_accdet(u32 state_swctrl)
{
/* enable ACCDET unit */
accdet_update_bit(ACCDET_SW_EN, PMIC_ACCDET_SW_EN_SHIFT);
}
static inline void disable_accdet(void)
{
/* sync with accdet_irq_handler set clear accdet irq bit to avoid to
* set clear accdet irq bit after disable accdet disable accdet irq
*/
clear_accdet_int();
udelay(200);
mutex_lock(&accdet->res_lock);
clear_accdet_int_check();
mutex_unlock(&accdet->res_lock);
/* recover accdet debounce0,3 */
accdet_set_debounce(accdet_state000, cust_pwm_deb->debounce0);
accdet_set_debounce(accdet_state011, cust_pwm_deb->debounce3);
}
static inline void headset_plug_out(void)
{
send_status_event(accdet->cable_type, 0);
accdet->accdet_status = PLUG_OUT;
accdet->cable_type = NO_DEVICE;
if (accdet->cur_key != 0) {
send_key_event(accdet->cur_key, 0);
accdet->cur_key = 0;
}
dis_micbias_done = false;
pr_info("accdet %s, set cable_type = NO_DEVICE %d\n", __func__,
dis_micbias_done);
}
static void dis_micbias_timerhandler(struct timer_list *t)
{
int ret = 0;
ret = queue_work(accdet->dis_micbias_workqueue,
&accdet->dis_micbias_work);
if (!ret)
pr_notice("Error: %s (%d)\n", __func__, ret);
}
static void dis_micbias_work_callback(struct work_struct *work)
{
u32 cur_AB, eintID;
/* check EINT0 status, if plug out,
* not need to disable accdet here
*/
eintID = accdet_read_bits(ACCDET_EINT0_MEM_IN,
PMIC_ACCDET_EINT0_MEM_IN_SHIFT,
PMIC_ACCDET_EINT0_MEM_IN_MASK);
if (eintID == M_PLUG_OUT) {
pr_notice("%s Plug-out, no dis micbias\n", __func__);
return;
}
/* if modify_vref_volt called, not need to dis micbias again */
if (dis_micbias_done == true) {
pr_notice("%s modify_vref_volt called\n", __func__);
return;
}
cur_AB = accdet_read(ACCDET_MEM_IN) >> ACCDET_STATE_MEM_IN_OFFSET;
cur_AB = cur_AB & ACCDET_STATE_AB_MASK;
/* if 3pole disable accdet
* if <20k + 4pole, disable accdet will disable accdet
* plug out interrupt. The behavior will same as 3pole
*/
if ((accdet->cable_type == HEADSET_NO_MIC) ||
(cur_AB == ACCDET_STATE_AB_00) ||
(cur_AB == ACCDET_STATE_AB_11)) {
/* disable accdet_sw_en=0
* disable accdet_hwmode_en=0
*/
accdet_clear_bit(ACCDET_SW_EN,
PMIC_ACCDET_SW_EN_SHIFT);
disable_accdet();
}
}
static void eint_work_callback(struct work_struct *work)
{
if (accdet->cur_eint_state == EINT_PLUG_IN) {
/* disable vusb LP */
accdet_write(LDO_RG_LDO_VUSB_HW0_OP_EN, 0x8000);
mutex_lock(&accdet->res_lock);
accdet->eint_sync_flag = true;
mutex_unlock(&accdet->res_lock);
__pm_wakeup_event(accdet->timer_lock,
jiffies_to_msecs(7 * HZ));
accdet_init();
enable_accdet(0);
} else {
mutex_lock(&accdet->res_lock);
accdet->eint_sync_flag = false;
accdet->thing_in_flag = false;
mutex_unlock(&accdet->res_lock);
del_timer_sync(&micbias_timer);
/* disable accdet_sw_en=0
*/
accdet_clear_bit(ACCDET_SW_EN,
PMIC_ACCDET_SW_EN_SHIFT);
disable_accdet();
headset_plug_out();
}
if (HAS_CAP(accdet->data->caps, ACCDET_PMIC_EINT_IRQ))
recover_eint_setting(accdet->eint_id);
else if (HAS_CAP(accdet->data->caps, ACCDET_AP_GPIO_EINT))
enable_irq(accdet->gpioirq);
}
void accdet_set_debounce(int state, unsigned int debounce)
{
switch (state) {
case accdet_state000:
/* set ACCDET debounce value = debounce/32 ms */
accdet_write(ACCDET_DEBOUNCE0, debounce);
break;
case accdet_state001:
accdet_write(ACCDET_DEBOUNCE1, debounce);
break;
case accdet_state010:
accdet_write(ACCDET_DEBOUNCE2, debounce);
break;
case accdet_state011:
accdet_write(ACCDET_DEBOUNCE3, debounce);
break;
case accdet_auxadc:
/* set auxadc debounce:0x42(2ms) */
accdet_write(ACCDET_CONNECT_AUXADC_TIME_DIG, debounce);
break;
case eint_state000:
accdet_update_bits(ACCDET_EINT_DEBOUNCE0,
PMIC_ACCDET_EINT_DEBOUNCE0_SHIFT,
PMIC_ACCDET_EINT_DEBOUNCE0_MASK,
debounce);
break;
case eint_state001:
accdet_update_bits(ACCDET_EINT_DEBOUNCE1,
PMIC_ACCDET_EINT_DEBOUNCE1_SHIFT,
PMIC_ACCDET_EINT_DEBOUNCE1_MASK,
debounce);
break;
case eint_state010:
accdet_update_bits(ACCDET_EINT_DEBOUNCE2,
PMIC_ACCDET_EINT_DEBOUNCE2_SHIFT,
PMIC_ACCDET_EINT_DEBOUNCE2_MASK,
debounce);
break;
case eint_state011:
accdet_update_bits(ACCDET_EINT_DEBOUNCE3,
PMIC_ACCDET_EINT_DEBOUNCE3_SHIFT,
PMIC_ACCDET_EINT_DEBOUNCE3_MASK,
debounce);
break;
case eint_inverter_state000:
accdet_write(ACCDET_EINT_INVERTER_DEBOUNCE, debounce);
break;
default:
pr_notice("Error: %s error state (%d)\n", __func__, state);
break;
}
}
static inline void check_cable_type(void)
{
u32 cur_AB;
cur_AB = accdet_read(ACCDET_MEM_IN) >> ACCDET_STATE_MEM_IN_OFFSET;
cur_AB = cur_AB & ACCDET_STATE_AB_MASK;
accdet->button_status = 0;
switch (accdet->accdet_status) {
case PLUG_OUT:
if (cur_AB == ACCDET_STATE_AB_00) {
mutex_lock(&accdet->res_lock);
if (accdet->eint_sync_flag) {
accdet->cable_type = HEADSET_NO_MIC;
accdet->accdet_status = HOOK_SWITCH;
} else
pr_notice("accdet hp has been plug-out\n");
mutex_unlock(&accdet->res_lock);
/* for IOT HP */
accdet_set_debounce(eint_state011,
accdet_dts.pwm_deb.eint_debounce3);
} else if (cur_AB == ACCDET_STATE_AB_01) {
mutex_lock(&accdet->res_lock);
if (accdet->eint_sync_flag) {
accdet->accdet_status = MIC_BIAS;
accdet->cable_type = HEADSET_MIC;
} else
pr_notice("accdet hp has been plug-out\n");
mutex_unlock(&accdet->res_lock);
/* solution: adjust hook switch debounce time
* for fast key press condition, avoid to miss key
*/
accdet_set_debounce(accdet_state000,
button_press_debounce);
/* for IOT HP */
accdet_set_debounce(eint_state011, 0x1);
} else if (cur_AB == ACCDET_STATE_AB_11) {
/* accdet PLUG_OUT state not change */
if (HAS_CAP(accdet->data->caps,
ACCDET_PMIC_EINT_IRQ)) {
mutex_lock(&accdet->res_lock);
if (accdet->eint_sync_flag) {
accdet->accdet_status = PLUG_OUT;
accdet->cable_type = NO_DEVICE;
} else
pr_notice("accdet hp been plug-out\n");
mutex_unlock(&accdet->res_lock);
}
} else {
pr_notice("accdet %s Invalid AB.Do nothing\n",
__func__);
}
break;
case MIC_BIAS:
if (cur_AB == ACCDET_STATE_AB_00) {
mutex_lock(&accdet->res_lock);
if (accdet->eint_sync_flag) {
accdet->button_status = 1;
accdet->accdet_status = HOOK_SWITCH;
multi_key_detection(cur_AB);
} else
pr_notice("accdet hp has been plug-out\n");
mutex_unlock(&accdet->res_lock);
} else if (cur_AB == ACCDET_STATE_AB_01) {
mutex_lock(&accdet->res_lock);
if (accdet->eint_sync_flag) {
accdet->accdet_status = MIC_BIAS;
accdet->cable_type = HEADSET_MIC;
/* accdet MIC_BIAS state not change */
} else
pr_notice("accdet hp has been plug-out\n");
mutex_unlock(&accdet->res_lock);
/* for IOT HP */
accdet_set_debounce(eint_state011, 0x1);
} else if (cur_AB == ACCDET_STATE_AB_11) {
/* accdet Don't send plug out in MIC_BIAS */
mutex_lock(&accdet->res_lock);
if (accdet->eint_sync_flag)
accdet->accdet_status = PLUG_OUT;
else
pr_notice("accdet hp has been plug-out\n");
mutex_unlock(&accdet->res_lock);
} else {
pr_notice("accdet %s Invalid AB.Do nothing\n",
__func__);
}
break;
case HOOK_SWITCH:
if (cur_AB == ACCDET_STATE_AB_00) {
} else if (cur_AB == ACCDET_STATE_AB_01) {
mutex_lock(&accdet->res_lock);
if (accdet->eint_sync_flag) {
multi_key_detection(cur_AB);
accdet->accdet_status = MIC_BIAS;
accdet->cable_type = HEADSET_MIC;
} else
pr_notice("accdet hp has been plug-out\n");
mutex_unlock(&accdet->res_lock);
/* for IOT HP */
accdet_set_debounce(eint_state011, 0x1);
} else if (cur_AB == ACCDET_STATE_AB_11) {
/* accdet Don't send plugout in HOOK_SWITCH */
mutex_lock(&accdet->res_lock);
if (accdet->eint_sync_flag)
accdet->accdet_status = PLUG_OUT;
else
pr_notice("accdet hp has been plug-out\n");
mutex_unlock(&accdet->res_lock);
} else {
pr_notice("accdet %s Invalid AB.Do nothing\n",
__func__);
}
break;
case STAND_BY:
/* accdet %s STANDBY state.Err!Do nothing */
break;
default:
/* accdet %s Error state.Do nothing */
break;
}
}
static void accdet_work_callback(struct work_struct *work)
{
u32 pre_cable_type = accdet->cable_type;
__pm_stay_awake(accdet->wake_lock);
check_cable_type();
mutex_lock(&accdet->res_lock);
if (accdet->eint_sync_flag) {
if (pre_cable_type != accdet->cable_type)
send_status_event(accdet->cable_type, 1);
}
mutex_unlock(&accdet->res_lock);
if (accdet->cable_type != NO_DEVICE) {
/* enable vusb LP */
accdet_write(LDO_RG_LDO_VUSB_HW0_OP_EN, 0x8005);
}
__pm_relax(accdet->wake_lock);
}
static void accdet_queue_work(void)
{
int ret;
if (accdet->accdet_status == MIC_BIAS)
accdet->cali_voltage = accdet_get_auxadc();
ret = queue_work(accdet->accdet_workqueue, &accdet->accdet_work);
if (!ret)
pr_notice("Error: %s (%d)\n", __func__, ret);
}
static int pmic_eint_queue_work(int eintID)
{
int ret = 0;
if (HAS_CAP(accdet->data->caps, ACCDET_PMIC_EINT0)) {
if (eintID == PMIC_EINT0) {
if (accdet->cur_eint_state == EINT_PLUG_IN) {
accdet_set_debounce(accdet_state011,
cust_pwm_deb->debounce3);
accdet->cur_eint_state = EINT_PLUG_OUT;
} else {
if (accdet->eint_id != M_PLUG_OUT) {
accdet->cur_eint_state = EINT_PLUG_IN;
mod_timer(&micbias_timer,
jiffies + MICBIAS_DISABLE_TIMER);
}
}
ret = queue_work(accdet->eint_workqueue,
&accdet->eint_work);
} else
pr_notice("%s invalid EINT ID!\n", __func__);
} else if (HAS_CAP(accdet->data->caps, ACCDET_PMIC_EINT1)) {
if (eintID == PMIC_EINT1) {
if (accdet->cur_eint_state == EINT_PLUG_IN) {
accdet_set_debounce(accdet_state011,
cust_pwm_deb->debounce3);
accdet->cur_eint_state = EINT_PLUG_OUT;
} else {
if (accdet->eint_id != M_PLUG_OUT) {
accdet->cur_eint_state = EINT_PLUG_IN;
mod_timer(&micbias_timer,
jiffies + MICBIAS_DISABLE_TIMER);
}
}
ret = queue_work(accdet->eint_workqueue,
&accdet->eint_work);
} else
pr_notice("%s invalid EINT ID!\n", __func__);
} else if (HAS_CAP(accdet->data->caps, ACCDET_PMIC_BI_EINT)) {
if ((eintID & PMIC_EINT0) == PMIC_EINT0) {
if (accdet->eint0_state == EINT_PLUG_IN) {
accdet_set_debounce(accdet_state011,
cust_pwm_deb->debounce3);
accdet->eint0_state = EINT_PLUG_OUT;
} else {
if (accdet->eint_id != M_PLUG_OUT)
accdet->eint0_state = EINT_PLUG_IN;
}
}
if ((eintID & PMIC_EINT1) == PMIC_EINT1) {
if (accdet->eint1_state == EINT_PLUG_IN) {
accdet_set_debounce(accdet_state011,
cust_pwm_deb->debounce3);
accdet->eint1_state = EINT_PLUG_OUT;
} else {
if (accdet->eint_id != M_PLUG_OUT)
accdet->eint1_state = EINT_PLUG_IN;
}
}
/* bi_eint trigger issued current state, may */
if (accdet->cur_eint_state == EINT_PLUG_OUT) {
accdet->cur_eint_state =
accdet->eint0_state & accdet->eint1_state;
if (accdet->cur_eint_state == EINT_PLUG_IN) {
mod_timer(&micbias_timer,
jiffies + MICBIAS_DISABLE_TIMER);
ret = queue_work(accdet->eint_workqueue,
&accdet->eint_work);
}
} else if (accdet->cur_eint_state == EINT_PLUG_IN) {
if ((accdet->eint0_state|accdet->eint1_state)
== EINT_PLUG_OUT) {
clear_accdet_eint_check(PMIC_EINT0);
clear_accdet_eint_check(PMIC_EINT1);
} else if ((accdet->eint0_state & accdet->eint1_state)
== EINT_PLUG_OUT) {
accdet->cur_eint_state = EINT_PLUG_OUT;
ret = queue_work(accdet->eint_workqueue,
&accdet->eint_work);
}
}
}
return ret;
}
void accdet_irq_handle(void)
{
u32 eintID = 0;
u32 irq_status, acc_sts, eint_sts;
eintID = get_triggered_eint();
irq_status = accdet_read(ACCDET_IRQ);
acc_sts = accdet_read(ACCDET_MEM_IN);
eint_sts = accdet_read(ACCDET_EINT0_MEM_IN);
if ((irq_status & ACCDET_IRQ_B0) && (eintID == 0)) {
clear_accdet_int();
accdet_queue_work();
clear_accdet_int_check();
} else if (eintID != NO_PMIC_EINT) {
/* check EINT0 status */
accdet->eint_id = accdet_read_bits(ACCDET_EINT0_MEM_IN,
PMIC_ACCDET_EINT0_MEM_IN_SHIFT,
PMIC_ACCDET_EINT0_MEM_IN_MASK);
/* adjust eint digital/analog setting */
adjust_eint_setting(accdet->eint_id);
clear_accdet_eint(eintID);
clear_accdet_eint_check(eintID);
pmic_eint_queue_work(eintID);
} else {
pr_notice("%s no interrupt detected!\n", __func__);
}
}
static irqreturn_t mtk_accdet_irq_handler_thread(int irq, void *data)
{
accdet_irq_handle();
return IRQ_HANDLED;
}
static irqreturn_t ex_eint_handler(int irq, void *data)
{
int ret = 0;
if (accdet->cur_eint_state == EINT_PLUG_IN) {
/* To trigger EINT when the headset was plugged in
* We set the polarity back as we initialed.
*/
if (accdet->accdet_eint_type == IRQ_TYPE_LEVEL_HIGH)
irq_set_irq_type(accdet->gpioirq, IRQ_TYPE_LEVEL_HIGH);
else
irq_set_irq_type(accdet->gpioirq, IRQ_TYPE_LEVEL_LOW);
gpio_set_debounce(accdet->gpiopin, accdet->gpio_hp_deb);
accdet->cur_eint_state = EINT_PLUG_OUT;
} else {
/* To trigger EINT when the headset was plugged out
* We set the opposite polarity to what we initialed.
*/
if (accdet->accdet_eint_type == IRQ_TYPE_LEVEL_HIGH)
irq_set_irq_type(accdet->gpioirq, IRQ_TYPE_LEVEL_LOW);
else
irq_set_irq_type(accdet->gpioirq, IRQ_TYPE_LEVEL_HIGH);
gpio_set_debounce(accdet->gpiopin,
accdet_dts.plugout_deb * 1000);
accdet->cur_eint_state = EINT_PLUG_IN;
mod_timer(&micbias_timer,
jiffies + MICBIAS_DISABLE_TIMER);
}
disable_irq_nosync(accdet->gpioirq);
ret = queue_work(accdet->eint_workqueue, &accdet->eint_work);
return IRQ_HANDLED;
}
static inline int ext_eint_setup(struct platform_device *platform_device)
{
int ret = 0;
u32 ints[4] = { 0 };
struct device_node *node = NULL;
struct pinctrl_state *pins_default = NULL;
accdet->pinctrl = devm_pinctrl_get(&platform_device->dev);
if (IS_ERR(accdet->pinctrl)) {
ret = PTR_ERR(accdet->pinctrl);
return ret;
}
pins_default = pinctrl_lookup_state(accdet->pinctrl, "default");
if (IS_ERR(pins_default))
ret = PTR_ERR(pins_default);
accdet->pins_eint = pinctrl_lookup_state(accdet->pinctrl,
"state_eint_as_int");
if (IS_ERR(accdet->pins_eint)) {
ret = PTR_ERR(accdet->pins_eint);
return ret;
}
pinctrl_select_state(accdet->pinctrl, accdet->pins_eint);
node = of_find_matching_node(node, accdet_of_match);
if (!node)
return -1;
accdet->gpiopin = of_get_named_gpio(node, "deb-gpios", 0);
ret = of_property_read_u32(node, "debounce",
&accdet->gpio_hp_deb);
if (ret < 0)
return ret;
gpio_set_debounce(accdet->gpiopin, accdet->gpio_hp_deb);
accdet->gpioirq = irq_of_parse_and_map(node, 0);
ret = of_property_read_u32_array(node, "interrupts", ints,
ARRAY_SIZE(ints));
if (ret)
return ret;
accdet->accdet_eint_type = ints[1];
ret = request_irq(accdet->gpioirq, ex_eint_handler, IRQF_TRIGGER_NONE,
"accdet-eint", NULL);
if (ret)
return ret;
return 0;
}
static int accdet_get_dts_data(void)
{
int ret;
struct device_node *node = NULL;
int pwm_deb[15];
int three_key[4];
node = of_find_matching_node(node, accdet_of_match);
if (!node) {
pr_notice("Error: %s can't find compatible dts node\n",
__func__);
return -1;
}
ret = of_property_read_u32(node, "eint_use_ext_res",
&accdet_dts.eint_use_ext_res);
if (ret) {
/* eint use internal resister */
accdet_dts.eint_use_ext_res = 0x0;
}
ret = of_property_read_u32(node, "eint_detect_mode",
&accdet_dts.eint_detect_mode);
if (ret) {
/* eint detection mode equals to EINT 2.1 */
accdet_dts.eint_detect_mode = 0x4;
}
accdet_dts.eint_detect_mode = 0x4;
ret = of_property_read_u32(node,
"accdet-mic-vol", &accdet_dts.mic_vol);
if (ret)
accdet_dts.mic_vol = 8;
ret = of_property_read_u32(node, "accdet-plugout-debounce",
&accdet_dts.plugout_deb);
if (ret)
accdet_dts.plugout_deb = 1;
ret = of_property_read_u32(node,
"accdet-mic-mode", &accdet_dts.mic_mode);
if (ret)
accdet_dts.mic_mode = 2;
if (HAS_CAP(accdet->data->caps, ACCDET_FOUR_KEY)) {
int four_key[5];
ret = of_property_read_u32_array(node,
"headset-four-key-threshold",
four_key, ARRAY_SIZE(four_key));
if (!ret)
memcpy(&accdet_dts.four_key, four_key+1,
sizeof(struct four_key_threshold));
else {
pr_notice("accdet no 4-key-thrsh dts, use efuse\n");
accdet_get_efuse_4key();
}
} else {
if (HAS_CAP(accdet->data->caps, ACCDET_THREE_KEY)) {
ret = of_property_read_u32_array(node,
"headset-three-key-threshold",
three_key, ARRAY_SIZE(three_key));
} else {
ret = of_property_read_u32_array(node,
"headset-three-key-threshold-CDD", three_key,
ARRAY_SIZE(three_key));
}
if (!ret)
memcpy(&accdet_dts.three_key, three_key+1,
sizeof(struct three_key_threshold));
}
ret = of_property_read_u32_array(node, "headset-mode-setting", pwm_deb,
ARRAY_SIZE(pwm_deb));
/* debounce8(auxadc debounce) is default, needn't get from dts */
if (!ret)
memcpy(&accdet_dts.pwm_deb, pwm_deb, sizeof(pwm_deb));
cust_pwm_deb = &accdet_dts.pwm_deb;
dis_micbias_done = false;
return 0;
}
static void config_digital_init_by_mode(void)
{
/* enable eint cmpmem pwm */
accdet_write(ACCDET_EINT_CMPMEN_PWM_THRESH,
(accdet_dts.pwm_deb.eint_pwm_width << 4 |
accdet_dts.pwm_deb.eint_pwm_thresh));
/* DA signal stable */
if (HAS_CAP(accdet->data->caps,
ACCDET_PMIC_EINT0)) {
accdet_write(ACCDET_DA_STABLE,
ACCDET_EINT0_STABLE_VAL);
} else if (HAS_CAP(accdet->data->caps,
ACCDET_PMIC_EINT1)) {
accdet_write(ACCDET_DA_STABLE,
ACCDET_EINT1_STABLE_VAL);
} else if (HAS_CAP(accdet->data->caps,
ACCDET_PMIC_BI_EINT)) {
accdet_write(ACCDET_DA_STABLE,
ACCDET_EINT0_STABLE_VAL);
accdet_write(ACCDET_DA_STABLE,
ACCDET_EINT1_STABLE_VAL);
}
/* after receive n+1 number, interrupt issued. now is 2 times */
accdet_update_bit(ACCDET_EINT_M_PLUG_IN_NUM,
PMIC_ACCDET_EINT_M_PLUG_IN_NUM_SHIFT);
/* disable hwmode */
accdet_write(ACCDET_HWMODE_EN, 0x100);
accdet_clear_bit(ACCDET_EINT_M_DETECT_EN,
PMIC_ACCDET_EINT_M_DETECT_EN_SHIFT);
/* enable PWM */
accdet_write(ACCDET_CMP_PWM_EN, 0x67);
/* enable inverter detection */
if (HAS_CAP(accdet->data->caps,
ACCDET_PMIC_EINT0)) {
accdet_update_bit(ACCDET_EINT0_INVERTER_SW_EN,
PMIC_ACCDET_EINT0_INVERTER_SW_EN_SHIFT);
} else if (HAS_CAP(accdet->data->caps,
ACCDET_PMIC_EINT1)) {
accdet_update_bit(ACCDET_EINT1_INVERTER_SW_EN,
PMIC_ACCDET_EINT1_INVERTER_SW_EN_SHIFT);
} else if (HAS_CAP(accdet->data->caps,
ACCDET_PMIC_BI_EINT)) {
accdet_update_bit(ACCDET_EINT0_INVERTER_SW_EN,
PMIC_ACCDET_EINT0_INVERTER_SW_EN_SHIFT);
accdet_update_bit(ACCDET_EINT1_INVERTER_SW_EN,
PMIC_ACCDET_EINT1_INVERTER_SW_EN_SHIFT);
}
}
static void config_eint_init_by_mode(void)
{
if (HAS_CAP(accdet->data->caps, ACCDET_PMIC_EINT0)) {
accdet_update_bit(ACCDET_RG_EINT0EN,
PMIC_RG_EINT0EN_SHIFT);
} else if (HAS_CAP(accdet->data->caps, ACCDET_PMIC_EINT1)) {
accdet_update_bit(ACCDET_RG_EINT1EN,
PMIC_RG_EINT1EN_SHIFT);
} else if (HAS_CAP(accdet->data->caps, ACCDET_PMIC_BI_EINT)) {
accdet_update_bit(ACCDET_RG_EINT0EN,
PMIC_RG_EINT0EN_SHIFT);
accdet_update_bit(ACCDET_RG_EINT1EN,
PMIC_RG_EINT1EN_SHIFT);
}
/* ESD switches on */
accdet_update_bit(ACCDET_RG_ACCDETSPARE, 8);
/* before playback, set NCP pull low before nagative voltage */
accdet_update_bit(ACCDET_RG_NCP_PDDIS_EN, PMIC_RG_NCP_PDDIS_EN_SHIFT);
if (accdet_dts.eint_detect_mode != 0x1) {
/* current detect set 0.25uA */
accdet_update_bits(ACCDET_RG_ACCDETSPARE,
PMIC_RG_ACCDETSPARE_SHIFT,
0x3, 0x3);
}
}
static void accdet_init_once(void)
{
unsigned int reg = 0;
/* reset the accdet unit */
accdet_update_bit(ACCDET_RG_ACCDET_RST, PMIC_RG_ACCDET_RST_SHIFT);
accdet_clear_bit(ACCDET_RG_ACCDET_RST, PMIC_RG_ACCDET_RST_SHIFT);
/* clear high micbias1 voltage setting */
accdet_clear_bits(ACCDET_RG_AUDPWDBMICBIAS1,
PMIC_RG_AUDMICBIAS1HVEN_SHIFT, 0x3, 0x3);
/* clear micbias1 voltage */
accdet_clear_bits(ACCDET_RG_AUDPWDBMICBIAS1,
PMIC_RG_AUDMICBIAS1VREF_SHIFT, 0x7, 0x7);
/* init pwm frequency, duty & rise/falling delay */
accdet_write(ACCDET_PWM_WIDTH,
REGISTER_VAL(cust_pwm_deb->pwm_width));
accdet_write(ACCDET_PWM_THRESH,
REGISTER_VAL(cust_pwm_deb->pwm_thresh));
accdet_write(ACCDET_RISE_DELAY,
(cust_pwm_deb->fall_delay << 15 | cust_pwm_deb->rise_delay));
/* config micbias voltage, micbias1 vref is only controlled by accdet
* if we need 2.8V, config [12:13]
*/
reg = accdet_read(ACCDET_RG_AUDPWDBMICBIAS1);
if (accdet_dts.mic_vol <= 7) {
/* micbias1 <= 2.7V */
accdet_write(ACCDET_RG_AUDPWDBMICBIAS1,
reg | (accdet_dts.mic_vol<<PMIC_RG_AUDMICBIAS1VREF_SHIFT) |
RG_AUD_MICBIAS1_LOWP_EN);
} else if (accdet_dts.mic_vol == 8) {
/* micbias1 = 2.8v */
accdet_write(ACCDET_RG_AUDPWDBMICBIAS1,
reg | (3<<PMIC_RG_AUDMICBIAS1HVEN_SHIFT) |
RG_AUD_MICBIAS1_LOWP_EN);
} else if (accdet_dts.mic_vol == 9) {
/* micbias1 = 2.85v */
accdet_write(ACCDET_RG_AUDPWDBMICBIAS1,
reg | (1<<PMIC_RG_AUDMICBIAS1HVEN_SHIFT) |
RG_AUD_MICBIAS1_LOWP_EN);
}
/* mic mode setting */
reg = accdet_read(ACCDET_RG_AUDACCDETMICBIAS0PULLLOW);
if (accdet_dts.mic_mode == HEADSET_MODE_1) {
/* ACC mode*/
accdet_write(ACCDET_RG_AUDACCDETMICBIAS0PULLLOW,
reg | RG_ACCDET_MODE_ANA11_MODE1);
/* enable analog fast discharge */
accdet_update_bit(ACCDET_RG_ANALOGFDEN,
PMIC_RG_ANALOGFDEN_SHIFT);
accdet_update_bits(ACCDET_RG_ACCDETSPARE, 11, 0x3, 0x3);
} else if (accdet_dts.mic_mode == HEADSET_MODE_2) {
/* DCC mode Low cost mode without internal bias*/
accdet_write(ACCDET_RG_AUDACCDETMICBIAS0PULLLOW,
reg | RG_ACCDET_MODE_ANA11_MODE2);
/* enable analog fast discharge */
accdet_update_bits(ACCDET_RG_ANALOGFDEN,
PMIC_RG_ANALOGFDEN_SHIFT, 0x3, 0x3);
} else if (accdet_dts.mic_mode == HEADSET_MODE_6) {
/* DCC mode Low cost mode with internal bias,
* bit8 = 1 to use internal bias
*/
accdet_write(ACCDET_RG_AUDACCDETMICBIAS0PULLLOW,
reg | RG_ACCDET_MODE_ANA11_MODE6);
accdet_update_bit(ACCDET_RG_AUDPWDBMICBIAS1,
PMIC_RG_AUDMICBIAS1DCSW1PEN_SHIFT);
/* enable analog fast discharge */
accdet_update_bits(ACCDET_RG_ANALOGFDEN,
PMIC_RG_ANALOGFDEN_SHIFT, 0x3, 0x3);
}
if (HAS_CAP(accdet->data->caps, ACCDET_PMIC_EINT_IRQ)) {
config_eint_init_by_mode();
config_digital_init_by_mode();
} else if (HAS_CAP(accdet->data->caps, ACCDET_AP_GPIO_EINT)) {
/* set pull low pads and DCC mode */
accdet_write(ACCDET_RG_AUDACCDETMICBIAS0PULLLOW,
0x8F);
/* disconnect configaccdet */
accdet_write(ACCDET_RG_EINT1CONFIGACCDET,
0x0);
/* disable eint comparator */
accdet_write(ACCDET_RG_EINT0CMPEN, 0x0);
/* enable PWM */
accdet_write(ACCDET_CMP_PWM_EN, 0x7);
/* enable accdet sw mode */
accdet_write(ACCDET_HWMODE_EN, 0x0);
/* set DA signal to stable */
accdet_write(ACCDET_DA_STABLE, 0x1);
/* disable eint/inverter/sw_en */
accdet_write(ACCDET_SW_EN, 0x0);
}
}
static void accdet_init_debounce(void)
{
/* set debounce to 1ms */
accdet_set_debounce(eint_state000,
accdet_dts.pwm_deb.eint_debounce0);
/* set debounce to 128ms */
accdet_set_debounce(eint_state011,
accdet_dts.pwm_deb.eint_debounce3);
}
static inline void accdet_init(void)
{
/* set and clear initial bit every eint interrutp */
accdet_update_bit(ACCDET_SEQ_INIT, PMIC_ACCDET_SEQ_INIT_SHIFT);
usleep_range(2000, 3000);
accdet_clear_bit(ACCDET_SEQ_INIT, PMIC_ACCDET_SEQ_INIT_SHIFT);
usleep_range(1000, 1500);
/* init the debounce time (debounce/32768)sec */
accdet_set_debounce(accdet_state000, cust_pwm_deb->debounce0);
accdet_set_debounce(accdet_state001, cust_pwm_deb->debounce1);
accdet_set_debounce(accdet_state011, cust_pwm_deb->debounce3);
/* auxadc:2ms */
accdet_set_debounce(accdet_auxadc, cust_pwm_deb->debounce4);
accdet_set_debounce(eint_state001,
accdet_dts.pwm_deb.eint_debounce1);
accdet_set_debounce(eint_inverter_state000,
accdet_dts.pwm_deb.eint_inverter_debounce);
}
static int accdet_probe(struct platform_device *pdev)
{
int ret = 0;
struct resource *res;
struct mt6397_chip *mt6397_chip = dev_get_drvdata(pdev->dev.parent);
const struct of_device_id *of_id =
of_match_device(accdet_of_match, &pdev->dev);
if (!of_id) {
dev_dbg(&pdev->dev, "Error: No device match found\n");
return -ENODEV;
}
accdet = devm_kzalloc(&pdev->dev, sizeof(*accdet), GFP_KERNEL);
if (!accdet)
return -ENOMEM;
accdet->data = (struct accdet_priv *)of_id->data;
accdet->pdev = pdev;
accdet->card.dev = &pdev->dev;
accdet->card.owner = THIS_MODULE;
ret = snd_soc_of_parse_card_name(&accdet->card, "accdet-name");
if (ret) {
dev_dbg(&pdev->dev, "Error: Parse card name failed (%d)\n",
ret);
return ret;
}
/* parse dts attributes */
ret = accdet_get_dts_data();
if (ret) {
dev_dbg(&pdev->dev, "Error: Get dts data failed (%d)\n",
ret);
return ret;
}
/* init lock */
accdet->wake_lock = wakeup_source_register(NULL, "accdet_wake_lock");
accdet->timer_lock = wakeup_source_register(NULL, "accdet_timer_lock");
mutex_init(&accdet->res_lock);
platform_set_drvdata(pdev, accdet);
snd_soc_card_set_drvdata(&accdet->card, accdet);
ret = devm_snd_soc_register_card(&pdev->dev, &accdet->card);
if (ret) {
dev_dbg(&pdev->dev, "Error: Register card failed (%d)\n",
ret);
return ret;
}
accdet->data->snd_card = accdet->card.snd_card;
ret = snd_soc_card_jack_new(&accdet->card,
accdet_jack_pins[0].pin,
accdet_jack_pins[0].mask,
&accdet->jack, accdet_jack_pins, 1);
if (ret) {
dev_dbg(&pdev->dev, "Error: New card jack failed (%d)\n",
ret);
return ret;
}
accdet->jack.jack->input_dev->id.bustype = BUS_HOST;
snd_jack_set_key(accdet->jack.jack, SND_JACK_BTN_0, KEY_PLAYPAUSE);
snd_jack_set_key(accdet->jack.jack, SND_JACK_BTN_1, KEY_VOLUMEDOWN);
snd_jack_set_key(accdet->jack.jack, SND_JACK_BTN_2, KEY_VOLUMEUP);
snd_jack_set_key(accdet->jack.jack, SND_JACK_BTN_3, KEY_VOICECOMMAND);
/* Important. must to register */
ret = snd_card_register(accdet->card.snd_card);
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
accdet->regmap = mt6397_chip->regmap;
accdet->dev = &pdev->dev;
/* get pmic auxadc iio channel handler */
accdet->accdet_auxadc = devm_iio_channel_get(&pdev->dev,
"pmic_accdet");
ret = PTR_ERR_OR_ZERO(accdet->accdet_auxadc);
if (ret) {
if (ret != -EPROBE_DEFER)
dev_dbg(&pdev->dev, "Error: Get iio ch failed (%d)\n",
ret);
return ret;
}
/* get pmic efuse handler */
accdet->accdet_efuse = devm_nvmem_device_get(&pdev->dev,
"mt63xx-accdet-efuse");
ret = PTR_ERR_OR_ZERO(accdet->accdet_efuse);
if (ret) {
if (ret != -EPROBE_DEFER)
dev_dbg(&pdev->dev, "Error: Get efuse failed (%d)\n",
ret);
return ret;
}
accdet_get_efuse();
/* register pmic interrupt */
accdet->accdet_irq = platform_get_irq(pdev, 0);
if (accdet->accdet_irq < 0) {
dev_dbg(&pdev->dev,
"Error: Get accdet irq failed (%d)\n",
accdet->accdet_irq);
return accdet->accdet_irq;
}
ret = devm_request_threaded_irq(&pdev->dev, accdet->accdet_irq,
NULL, mtk_accdet_irq_handler_thread,
IRQF_TRIGGER_HIGH | IRQF_ONESHOT,
"ACCDET_IRQ", accdet);
if (ret) {
dev_dbg(&pdev->dev,
"Error: Get thread irq request failed (%d) (%d)\n",
accdet->accdet_irq, ret);
return ret;
}
if (HAS_CAP(accdet->data->caps, ACCDET_PMIC_EINT0)) {
accdet->accdet_eint0 = platform_get_irq(pdev, 1);
if (accdet->accdet_eint0 < 0) {
dev_dbg(&pdev->dev,
"Error: Get eint0 irq failed (%d)\n",
accdet->accdet_eint0);
return accdet->accdet_eint0;
}
ret = devm_request_threaded_irq(&pdev->dev,
accdet->accdet_eint0,
NULL, mtk_accdet_irq_handler_thread,
IRQF_TRIGGER_HIGH | IRQF_ONESHOT,
"ACCDET_EINT0", accdet);
if (ret) {
dev_dbg(&pdev->dev,
"Error: Get eint0 irq request failed (%d)\n",
ret);
return ret;
}
} else if (HAS_CAP(accdet->data->caps, ACCDET_PMIC_EINT1)) {
accdet->accdet_eint1 = platform_get_irq(pdev, 2);
if (accdet->accdet_eint1 < 0) {
dev_dbg(&pdev->dev,
"Error: Get eint1 irq failed (%d)\n",
accdet->accdet_eint1);
return accdet->accdet_eint1;
}
ret = devm_request_threaded_irq(&pdev->dev,
accdet->accdet_eint1,
NULL, mtk_accdet_irq_handler_thread,
IRQF_TRIGGER_HIGH | IRQF_ONESHOT,
"ACCDET_EINT1", accdet);
if (ret) {
dev_dbg(&pdev->dev,
"Error: Get eint1 irq request failed (%d)\n",
ret);
return ret;
}
} else if (HAS_CAP(accdet->data->caps, ACCDET_PMIC_BI_EINT)) {
accdet->accdet_eint0 = platform_get_irq(pdev, 1);
if (accdet->accdet_eint0 < 0) {
dev_dbg(&pdev->dev,
"Error: Get eint0 irq failed (%d)\n",
accdet->accdet_eint0);
return accdet->accdet_eint0;
}
ret = devm_request_threaded_irq(&pdev->dev,
accdet->accdet_eint0,
NULL, mtk_accdet_irq_handler_thread,
IRQF_TRIGGER_HIGH | IRQF_ONESHOT,
"ACCDET_EINT0", accdet);
if (ret) {
dev_dbg(&pdev->dev,
"Error: Get eint0 irq request failed (%d)\n",
ret);
return ret;
}
accdet->accdet_eint1 = platform_get_irq(pdev, 2);
if (accdet->accdet_eint1 < 0) {
dev_dbg(&pdev->dev,
"Error: Get eint1 irq failed (%d)\n",
accdet->accdet_eint1);
return accdet->accdet_eint1;
}
ret = devm_request_threaded_irq(&pdev->dev,
accdet->accdet_eint1,
NULL, mtk_accdet_irq_handler_thread,
IRQF_TRIGGER_HIGH | IRQF_ONESHOT,
"ACCDET_EINT1", accdet);
if (ret) {
dev_dbg(&pdev->dev,
"Error: Get eint1 irq request failed (%d)\n",
ret);
return ret;
}
}
/* register char device number, Create normal device for auido use */
ret = alloc_chrdev_region(&accdet->accdet_devno, 0, 1, ACCDET_DEVNAME);
if (ret)
goto err_chrdevregion;
/* create class in sysfs, "sys/class/", so udev in userspace can create
* device node, when device_create is called
*/
accdet->accdet_class = class_create(THIS_MODULE, ACCDET_DEVNAME);
if (!accdet->accdet_class) {
dev_dbg(&pdev->dev,
"Error: Create class failed (%d)\n", ret);
ret = -1;
}
/* setup timer */
timer_setup(&micbias_timer, dis_micbias_timerhandler, 0);
micbias_timer.expires = jiffies + MICBIAS_DISABLE_TIMER;
/* Create workqueue */
accdet->accdet_workqueue = create_singlethread_workqueue("accdet");
INIT_WORK(&accdet->accdet_work, accdet_work_callback);
if (!accdet->accdet_workqueue) {
dev_dbg(&pdev->dev, "Error: Create accdet orkqueue failed\n");
ret = -1;
goto err_device_create;
}
accdet->dis_micbias_workqueue =
create_singlethread_workqueue("dismicQueue");
INIT_WORK(&accdet->dis_micbias_work, dis_micbias_work_callback);
if (!accdet->dis_micbias_workqueue) {
dev_dbg(&pdev->dev, "Error: Create dismic workqueue failed\n");
ret = -1;
goto err;
}
accdet->eint_workqueue = create_singlethread_workqueue("accdet_eint");
INIT_WORK(&accdet->eint_work, eint_work_callback);
if (!accdet->eint_workqueue) {
dev_dbg(&pdev->dev, "Error: Create eint workqueue failed\n");
ret = -1;
goto err_create_workqueue;
}
if (HAS_CAP(accdet->data->caps, ACCDET_AP_GPIO_EINT)) {
accdet->accdet_eint_type = IRQ_TYPE_LEVEL_LOW;
ret = ext_eint_setup(pdev);
if (ret)
destroy_workqueue(accdet->eint_workqueue);
}
accdet_init();
accdet_init_debounce();
accdet_init_once();
return 0;
err_create_workqueue:
destroy_workqueue(accdet->dis_micbias_workqueue);
err:
destroy_workqueue(accdet->accdet_workqueue);
err_device_create:
class_destroy(accdet->accdet_class);
err_chrdevregion:
pr_notice("%s error. now exit.!\n", __func__);
return ret;
}
static int accdet_remove(struct platform_device *pdev)
{
destroy_workqueue(accdet->eint_workqueue);
destroy_workqueue(accdet->dis_micbias_workqueue);
destroy_workqueue(accdet->accdet_workqueue);
class_destroy(accdet->accdet_class);
unregister_chrdev_region(accdet->accdet_devno, 1);
devm_kfree(&pdev->dev, accdet);
return 0;
}
static long mt_accdet_unlocked_ioctl(struct file *file, unsigned int cmd,
unsigned long arg)
{
switch (cmd) {
case GET_BUTTON_STATUS:
return accdet->button_status;
default:
break;
}
return 0;
}
static const struct file_operations accdet_fops = {
.owner = THIS_MODULE,
.unlocked_ioctl = mt_accdet_unlocked_ioctl,
};
const struct file_operations *accdet_get_fops(void)
{
return &accdet_fops;
}
static struct platform_driver accdet_driver = {
.probe = accdet_probe,
.remove = accdet_remove,
.driver = {
.name = "pmic-codec-accdet",
.of_match_table = accdet_of_match,
},
};
static int __init accdet_soc_init(void)
{
int ret = 0;
ret = platform_driver_register(&accdet_driver);
if (ret)
return -ENODEV;
return 0;
}
static void __exit accdet_soc_exit(void)
{
platform_driver_unregister(&accdet_driver);
}
module_init(accdet_soc_init);
module_exit(accdet_soc_exit);
/* Module information */
MODULE_DESCRIPTION("MT6359 ALSA SoC accdet driver");
MODULE_AUTHOR("Argus Lin <argus.lin@mediatek.com>");
MODULE_LICENSE("GPL v2");