6db4831e98
Android 14
3003 lines
86 KiB
C
3003 lines
86 KiB
C
/* SPDX-License-Identifier: GPL-2.0 */
|
|
/*
|
|
* Copyright (C) 2021 MediaTek Inc.
|
|
*/
|
|
|
|
#include "accdet.h"
|
|
#if PMIC_ACCDET_KERNEL
|
|
#ifdef CONFIG_ACCDET_EINT
|
|
#include <linux/of_gpio.h>
|
|
#endif
|
|
#include <linux/timer.h>
|
|
#include <linux/of.h>
|
|
#include <linux/of_irq.h>
|
|
#include <linux/device.h>
|
|
#include <linux/sched/clock.h>
|
|
#include <linux/irq.h>
|
|
#include "reg_accdet.h"
|
|
#if defined CONFIG_MTK_PMIC_NEW_ARCH
|
|
#include <upmu_common.h>
|
|
#include <mtk_auxadc_intf.h>
|
|
#include <mach/mtk_pmic.h>
|
|
#include <mach/upmu_hw.h>
|
|
#endif
|
|
#include <mach/mtk_pmic_wrap.h>
|
|
#ifdef CONFIG_MTK_PMIC_WRAP
|
|
#include <linux/regmap.h>
|
|
#include <linux/soc/mediatek/pmic_wrap.h>
|
|
#endif
|
|
#else
|
|
#include "string.h"
|
|
#include "reg_base.H"
|
|
#include "accdet_hw.h"
|
|
#include "accdet_sw.h"
|
|
#include "common.h"
|
|
#include "intrCtrl.h"
|
|
#include "api.h"
|
|
#ifdef MTKDRV_GPIO
|
|
#include "gpio.h"
|
|
#endif
|
|
#include "pmic_auxadc.h"
|
|
#endif /* end of #if PMIC_ACCDET_KERNEL */
|
|
#ifdef CONFIG_SND_SOC_SAMSUNG_AUDIO
|
|
#include "../../../../../sound/soc/samsung/sec_accdet_sysfs_cb.h"
|
|
#endif
|
|
|
|
/********************grobal variable definitions******************/
|
|
#if PMIC_ACCDET_CTP
|
|
#define CONFIG_ACCDET_EINT_IRQ
|
|
#define CONFIG_ACCDET_SUPPORT_EINT0
|
|
|
|
#define pr_info dbg_print
|
|
#define pr_debug dbg_print
|
|
#define pr_notice dbg_print
|
|
#define mdelay accdet_delay
|
|
#define udelay accdet_delay
|
|
#define atomic_t int
|
|
#define dev_t int
|
|
#define DEFINE_MUTEX(a)
|
|
#define mutex_lock(a)
|
|
#define mutex_unlock(a)
|
|
#define mod_timer(a, b)
|
|
#define __pm_wakeup_event(a, b)
|
|
#define del_timer_sync(a)
|
|
#define __pm_stay_awake(a)
|
|
#define __pm_relax(a)
|
|
|
|
#endif /* end of #if PMIC_ACCDET_CTP */
|
|
|
|
#ifdef CONFIG_ACCDET_EINT
|
|
#define ACCDET_SW_MOISTURE_V1 1 /* for titan */
|
|
#define NO_USE_COMPARATOR 1
|
|
#else
|
|
#define ACCDET_SW_MOISTURE_V1 0
|
|
#define NO_USE_COMPARATOR 1
|
|
#endif
|
|
|
|
#if ACCDET_SW_MOISTURE_V1
|
|
struct moist_l_det_threshold {/* mv */
|
|
unsigned int hp_water;
|
|
unsigned int water_open;
|
|
};
|
|
static struct moist_l_det_threshold l_det_th;
|
|
#endif
|
|
#if NO_USE_COMPARATOR
|
|
/* for headset pole type definition */
|
|
#define TYPE_AB_00 (0x00)/* 3-pole or hook_switch */
|
|
#define TYPE_AB_01 (0x01)/* 4-pole */
|
|
#define TYPE_AB_11 (0x03)/* plug-out */
|
|
#define TYPE_AB_10 (0x02)/* Illegal state */
|
|
struct Vol_Set {/* mv */
|
|
unsigned int vol_min_3pole;
|
|
unsigned int vol_max_3pole;
|
|
unsigned int vol_min_4pole;
|
|
unsigned int vol_max_4pole;
|
|
unsigned int vol_bias;/* >2500: 2800; others: 2500 */
|
|
};
|
|
static struct Vol_Set cust_vol_set;
|
|
#endif
|
|
|
|
#define REGISTER_VAL(x) (x - 1)
|
|
|
|
/* for accdet_read_audio_res, less than 5k ohm, return -1 , otherwise ret 0 */
|
|
#define RET_LT_5K (-1)
|
|
#define RET_GT_5K (0)
|
|
|
|
/* Used to let accdet know if the pin has been fully plugged-in */
|
|
#define EINT_PIN_PLUG_OUT (0)
|
|
#define EINT_PIN_PLUG_IN (1)
|
|
#define EINT_PIN_MOISTURE_DETECTED (2)
|
|
#define EINT_PIN_THING_IN (3)
|
|
#define ANALOG_FASTDISCHARGE_SUPPORT
|
|
|
|
#ifdef CONFIG_ACCDET_EINT_IRQ
|
|
enum pmic_eint_ID {
|
|
NO_PMIC_EINT = 0,
|
|
PMIC_EINT0 = 1,
|
|
PMIC_EINT1 = 2,
|
|
PMIC_BIEINT = 3,
|
|
};
|
|
/* #define HW_MODE_SUPPORT */
|
|
/* #define DIGITAL_FASTDISCHARGE_SUPPORT */
|
|
#endif
|
|
|
|
/* accdet_status_str: to record current 'accdet_status' by string,
|
|
*mapping to 'enum accdet_status'
|
|
*/
|
|
static char *accdet_status_str[] = {
|
|
"Plug_out",
|
|
"Headset_plug_in",
|
|
"Hook_switch",
|
|
"Bi_mic",
|
|
"Line_out",
|
|
"Stand_by"
|
|
};
|
|
|
|
/* accdet_report_str: to record current 'cable_type' by string,
|
|
* mapping to 'enum accdet_report_state'
|
|
*/
|
|
static char *accdet_report_str[] = {
|
|
"No_device",
|
|
"Headset_mic",
|
|
"Headset_no_mic",
|
|
"Headset_five_pole",
|
|
"Line_out_device"
|
|
};
|
|
|
|
/* accdet char device & class & device */
|
|
static dev_t accdet_devno;
|
|
static struct cdev *accdet_cdev;
|
|
static struct class *accdet_class;
|
|
static struct device *accdet_device;
|
|
static int s_button_status;
|
|
static int accdet_pmic;
|
|
|
|
/* accdet input device to report cable type and key event */
|
|
static struct input_dev *accdet_input_dev;
|
|
|
|
#if PMIC_ACCDET_KERNEL
|
|
#ifdef CONFIG_MTK_PMIC_WRAP
|
|
static struct regmap *accdet_regmap;
|
|
#endif
|
|
/* when MICBIAS_DISABLE_TIMER timeout, queue work: dis_micbias_work */
|
|
static struct work_struct dis_micbias_work;
|
|
static struct workqueue_struct *dis_micbias_workqueue;
|
|
/* when accdet irq issued, queue work: accdet_work work */
|
|
static struct work_struct accdet_work;
|
|
static struct workqueue_struct *accdet_workqueue;
|
|
#ifdef CONFIG_ACCDET_EINT_IRQ
|
|
/* when eint issued, queue work: eint_work */
|
|
static struct work_struct eint_work;
|
|
static struct workqueue_struct *eint_workqueue;
|
|
#endif
|
|
/* 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 char accdet_log_buf[1280];
|
|
|
|
/* accdet_init_timer: init accdet if audio doesn't call to accdet for DC trim
|
|
* timeout: 10 seconds
|
|
* timerHandler: delay_init_timerhandler()
|
|
*/
|
|
#define ACCDET_INIT_WAIT_TIMER (10 * HZ)
|
|
static struct timer_list accdet_init_timer;
|
|
static void delay_init_timerhandler(struct timer_list *t);
|
|
static struct wakeup_source *accdet_irq_lock;
|
|
static struct wakeup_source *accdet_timer_lock;
|
|
static DEFINE_MUTEX(accdet_eint_irq_sync_mutex);
|
|
static DEFINE_MUTEX(accdet_moist_sync_mutex);
|
|
#endif /* end of #if PMIC_ACCDET_KERNEL */
|
|
/* accdet customized info by dts*/
|
|
static struct head_dts_data accdet_dts;
|
|
struct pwm_deb_settings *cust_pwm_deb;
|
|
|
|
#define ACCDET_OPEN_CABLE_TIMER (1 * HZ)
|
|
static struct timer_list accdet_open_cable_timer;
|
|
static void check_open_cable_timerhandler(struct timer_list *t);
|
|
#ifdef CONFIG_ACCDET_EINT
|
|
#define ACCDET_MOIST_TIMER (1 * HZ)
|
|
static struct timer_list accdet_moist_timer;
|
|
static void check_moist_timerhandler(struct timer_list *t);
|
|
static struct pinctrl *accdet_pinctrl;
|
|
static struct pinctrl_state *pins_eint;
|
|
static struct pinctrl_state *pins_moisteint;
|
|
static u32 gpiopin, gpio_plugin_deb;
|
|
static u32 gpio_plugout_deb;
|
|
static u32 moist_gpiopin, moist_gpio_plugin_deb;
|
|
static u32 gpio_moist_plugout_deb;
|
|
static u32 accdet_irq;
|
|
static u32 moist_irq;
|
|
static u32 pre_eint_state = EINT_PIN_PLUG_OUT;
|
|
static u32 accdet_moist_eint_type = IRQ_TYPE_LEVEL_LOW;
|
|
#endif
|
|
#if ACCDET_SW_MOISTURE_V1
|
|
static int hw_rev = 0xff;
|
|
#endif
|
|
static int moisture_ver = 0xff;
|
|
|
|
/* accdet FSM State & lock*/
|
|
static bool eint_accdet_sync_flag;
|
|
static u32 cur_eint_state = EINT_PIN_PLUG_OUT;
|
|
#ifdef CONFIG_ACCDET_SUPPORT_BI_EINT
|
|
static u32 cur_eint0_state = EINT_PIN_PLUG_OUT;
|
|
static u32 cur_eint1_state = EINT_PIN_PLUG_OUT;
|
|
#endif
|
|
static u32 pre_status;
|
|
static u32 accdet_status = PLUG_OUT;
|
|
static u32 cable_type;
|
|
static u32 cur_key;
|
|
static u32 cali_voltage;
|
|
static int accdet_auxadc_offset;
|
|
|
|
static u32 accdet_eint_type = IRQ_TYPE_LEVEL_LOW;
|
|
static u32 button_press_debounce = 0x400;
|
|
static atomic_t accdet_first;
|
|
#ifdef HW_MODE_SUPPORT
|
|
#ifdef DIGITAL_FASTDISCHARGE_SUPPORT
|
|
static bool fast_discharge = true;
|
|
#endif
|
|
#endif
|
|
|
|
/* SW mode only, moisture vm, resister declaration */
|
|
static unsigned int moisture_vm = 50; /* TBD */
|
|
/* moisture resister ohm */
|
|
static int water_r = 10000;
|
|
/* accdet Moisture detect */
|
|
/* unit is mv */
|
|
static int moisture_vdd_offset;
|
|
static int moisture_offset;
|
|
/* unit is ohm */
|
|
static int moisture_eint_offset;
|
|
/* unit is ohm */
|
|
static int moisture_int_r = 47000;
|
|
/* unit is ohm */
|
|
static int moisture_ext_r = 470000;
|
|
|
|
static bool debug_thread_en;
|
|
static bool dump_reg;
|
|
static struct task_struct *thread;
|
|
#ifdef CONFIG_SND_SOC_SAMSUNG_AUDIO
|
|
static struct accdet_data accdet_pdata;
|
|
#endif
|
|
|
|
static void accdet_init_once(void);
|
|
static inline void accdet_init(void);
|
|
static void send_accdet_status_event(u32 cable_type, u32 status);
|
|
#if defined CONFIG_ACCDET_EINT_IRQ || defined CONFIG_ACCDET_EINT
|
|
static u32 moisture_detect(void);
|
|
#endif
|
|
#if ACCDET_SW_MOISTURE_V1
|
|
static void eint_work_callback(void);
|
|
#endif
|
|
static inline void check_cable_type(void);
|
|
#if NO_USE_COMPARATOR
|
|
static unsigned int check_pole_type(void);
|
|
#endif
|
|
|
|
#if !defined CONFIG_MTK_PMIC_NEW_ARCH
|
|
enum PMIC_FAKE_IRQ_ENUM {
|
|
INT_ACCDET,
|
|
INT_ACCDET_EINT0,
|
|
INT_ACCDET_EINT1,
|
|
};
|
|
|
|
void pmic_register_interrupt_callback(unsigned int intNo,
|
|
void(EINT_FUNC_PTR)(void))
|
|
{
|
|
}
|
|
|
|
void pmic_enable_interrupt(enum PMIC_FAKE_IRQ_ENUM intNo,
|
|
unsigned int en, char *str)
|
|
{
|
|
}
|
|
|
|
unsigned int pmic_Read_Efuse_HPOffset(int i)
|
|
{
|
|
return 0;
|
|
}
|
|
#endif
|
|
|
|
inline u32 pmic_read(u32 addr)
|
|
{
|
|
u32 val = 0;
|
|
#ifdef CONFIG_MTK_PMIC_WRAP
|
|
if (accdet_regmap)
|
|
regmap_read(accdet_regmap, addr, &val);
|
|
else
|
|
pr_notice("%s %d Error.\n", __func__, __LINE__);
|
|
#else
|
|
pwrap_read(addr, &val);
|
|
#endif
|
|
|
|
return val;
|
|
}
|
|
|
|
inline u32 pmic_read_mbit(u32 addr, u32 shift, u32 mask)
|
|
{
|
|
u32 val = 0;
|
|
|
|
#ifdef CONFIG_MTK_PMIC_WRAP
|
|
if (accdet_regmap)
|
|
regmap_read(accdet_regmap, addr, &val);
|
|
else
|
|
pr_notice("%s %d Error.\n", __func__, __LINE__);
|
|
#else
|
|
pwrap_read(addr, &val);
|
|
#endif
|
|
#if PMIC_ACCDET_DEBUG
|
|
if (dump_reg) {
|
|
pr_debug("%s [0x%x]=[0x%x], shift[0x%x], mask[0x%x]\n",
|
|
__func__, addr, ((val>>shift) & mask), shift, mask);
|
|
}
|
|
#endif
|
|
return ((val>>shift) & mask);
|
|
}
|
|
|
|
inline void pmic_write(u32 addr, u32 wdata)
|
|
{
|
|
#ifdef CONFIG_MTK_PMIC_WRAP
|
|
if (accdet_regmap)
|
|
regmap_write(accdet_regmap, addr, wdata);
|
|
else
|
|
pr_notice("%s %d Error.\n", __func__, __LINE__);
|
|
#else
|
|
pwrap_write(addr, wdata);
|
|
#endif
|
|
#if PMIC_ACCDET_DEBUG
|
|
if (dump_reg)
|
|
pr_debug("%s [0x%x]=[0x%x]\n", __func__, addr, wdata);
|
|
#endif
|
|
}
|
|
|
|
inline void pmic_write_mset(u32 addr, u32 shift, u32 mask, u32 data)
|
|
{
|
|
u32 pmic_reg = 0;
|
|
|
|
pmic_reg = pmic_read(addr);
|
|
pmic_reg &= ~(mask<<shift);
|
|
pmic_write(addr, pmic_reg | (data<<shift));
|
|
#if PMIC_ACCDET_DEBUG
|
|
if (dump_reg) {
|
|
pr_debug("%s [0x%x]=[0x%x], shift[0x%x], mask[0x%x], data[0x%x]\n",
|
|
__func__, addr, pmic_read(addr), shift, mask, data);
|
|
}
|
|
#endif
|
|
}
|
|
|
|
inline void pmic_write_set(u32 addr, u32 shift)
|
|
{
|
|
pmic_write(addr, pmic_read(addr) | (1<<shift));
|
|
#if PMIC_ACCDET_DEBUG
|
|
if (dump_reg) {
|
|
pr_debug("%s [0x%x]=[0x%x], shift[0x%x]\n",
|
|
__func__, addr, pmic_read(addr), shift);
|
|
}
|
|
#endif
|
|
}
|
|
|
|
inline void pmic_write_mclr(u32 addr, u32 shift, u32 data)
|
|
{
|
|
pmic_write(addr, pmic_read(addr) & ~(data<<shift));
|
|
#if PMIC_ACCDET_DEBUG
|
|
if (dump_reg) {
|
|
pr_debug("%s [0x%x]=[0x%x], shift[0x%x], data[0x%x]\n",
|
|
__func__, addr, pmic_read(addr), shift, data);
|
|
}
|
|
#endif
|
|
}
|
|
|
|
inline void pmic_write_clr(u32 addr, u32 shift)
|
|
{
|
|
pmic_write(addr, pmic_read(addr) & ~(1<<shift));
|
|
#if PMIC_ACCDET_DEBUG
|
|
if (dump_reg) {
|
|
pr_debug("%s [0x%x]=[0x%x], shift[0x%x]\n",
|
|
__func__, addr, pmic_read(addr), shift);
|
|
}
|
|
#endif
|
|
}
|
|
|
|
static void dump_register(void)
|
|
{
|
|
int i = 0;
|
|
|
|
#ifdef CONFIG_ACCDET_EINT_IRQ
|
|
#ifdef CONFIG_ACCDET_SUPPORT_EINT0
|
|
pr_info("Accdet EINT0 support,MODE_%d regs:\n", accdet_dts.mic_mode);
|
|
#elif defined CONFIG_ACCDET_SUPPORT_EINT1
|
|
pr_info("Accdet EINT1 support,MODE_%d regs:\n", accdet_dts.mic_mode);
|
|
#elif defined CONFIG_ACCDET_SUPPORT_BI_EINT
|
|
pr_info("Accdet BIEINT support,MODE_%d regs:\n",
|
|
accdet_dts.mic_mode);
|
|
#else
|
|
pr_info("ACCDET_EINT_IRQ:NO EINT configed.Error!!\n");
|
|
#endif
|
|
#elif defined CONFIG_ACCDET_EINT
|
|
pr_info("Accdet EINT,MODE_%d regs:\n", accdet_dts.mic_mode);
|
|
#endif
|
|
|
|
for (i = ACCDET_RSV; i <= ACCDET_EINT1_CUR_DEB; i += 2)
|
|
pr_info("ACCDET_ADDR:(0x%x)=0x%x\n", i, pmic_read(i));
|
|
|
|
pr_info("(0x%x)=0x%x\n", TOP_CKPDN_CON0, pmic_read(TOP_CKPDN_CON0));
|
|
pr_info("(0x%x)=0x%x\n", AUD_TOP_CKPDN_CON0,
|
|
pmic_read(AUD_TOP_CKPDN_CON0));
|
|
pr_info("(0x%x)=0x%x\n", AUD_TOP_RST_CON0,
|
|
pmic_read(AUD_TOP_RST_CON0));
|
|
pr_info("(0x%x)=0x%x\n", AUD_TOP_INT_CON0,
|
|
pmic_read(AUD_TOP_INT_CON0));
|
|
pr_info("(0x%x)=0x%x\n", AUD_TOP_INT_MASK_CON0,
|
|
pmic_read(AUD_TOP_INT_MASK_CON0));
|
|
pr_info("(0x%x)=0x%x\n", AUD_TOP_INT_STATUS0,
|
|
pmic_read(AUD_TOP_INT_STATUS0));
|
|
pr_info("(0x%x)=0x%x\n", AUDENC_ANA_CON6, pmic_read(AUDENC_ANA_CON6));
|
|
pr_info("(0x%x)=0x%x\n", AUDENC_ANA_CON9, pmic_read(AUDENC_ANA_CON9));
|
|
pr_info("(0x%x)=0x%x\n", AUDENC_ANA_CON10,
|
|
pmic_read(AUDENC_ANA_CON10));
|
|
pr_info("(0x%x)=0x%x\n", AUDENC_ANA_CON11,
|
|
pmic_read(AUDENC_ANA_CON11));
|
|
pr_info("(0x%x)=0x%x\n", AUXADC_RQST0, pmic_read(AUXADC_RQST0));
|
|
pr_info("(0x%x)=0x%x\n", AUXADC_ACCDET, pmic_read(AUXADC_ACCDET));
|
|
|
|
pr_info("accdet_dts:deb0=0x%x,deb1=0x%x,deb3=0x%x,deb4=0x%x\n",
|
|
cust_pwm_deb->debounce0, cust_pwm_deb->debounce1,
|
|
cust_pwm_deb->debounce3, cust_pwm_deb->debounce4);
|
|
}
|
|
|
|
|
|
#if PMIC_ACCDET_KERNEL
|
|
static void cat_register(char *buf)
|
|
{
|
|
int i = 0, ret = 0;
|
|
|
|
#ifdef CONFIG_ACCDET_EINT_IRQ
|
|
#ifdef CONFIG_ACCDET_SUPPORT_EINT0
|
|
ret = sprintf(accdet_log_buf, "[Accdet EINT0 support][MODE_%d]regs:\n",
|
|
accdet_dts.mic_mode);
|
|
if (ret < 0)
|
|
pr_notice("sprintf failed\n");
|
|
strncat(buf, accdet_log_buf, strlen(accdet_log_buf));
|
|
#elif defined CONFIG_ACCDET_SUPPORT_EINT1
|
|
ret = sprintf(accdet_log_buf, "[Accdet EINT1 support][MODE_%d]regs:\n",
|
|
accdet_dts.mic_mode);
|
|
if (ret < 0)
|
|
pr_notice("sprintf failed\n");
|
|
strncat(buf, accdet_log_buf, strlen(accdet_log_buf));
|
|
#elif defined CONFIG_ACCDET_SUPPORT_BI_EINT
|
|
ret = sprintf(accdet_log_buf, "[Accdet EINT support][MODE_%d] regs:\n",
|
|
accdet_dts.mic_mode);
|
|
if (ret < 0)
|
|
pr_notice("sprintf failed\n");
|
|
strncat(buf, accdet_log_buf, strlen(accdet_log_buf));
|
|
#else
|
|
strncat(buf, "ACCDET_EINT_IRQ:NO EINT configed.Error!!\n", 64);
|
|
#endif
|
|
#elif defined CONFIG_ACCDET_EINT
|
|
ret = sprintf(accdet_log_buf, "[Accdet AP EINT][MODE_%d] regs:\n",
|
|
accdet_dts.mic_mode);
|
|
if (ret < 0)
|
|
pr_notice("sprintf failed\n");
|
|
strncat(buf, accdet_log_buf, strlen(accdet_log_buf));
|
|
#else
|
|
strncat(buf, "ACCDET EINT:No configed.Error!!\n", 64);
|
|
#endif
|
|
|
|
for (i = ACCDET_RSV; i <= ACCDET_EINT1_CUR_DEB; i += 2) {
|
|
ret = sprintf(accdet_log_buf, "ADDR[0x%x]=0x%x\n",
|
|
i, pmic_read(i));
|
|
if (ret < 0)
|
|
pr_notice("sprintf failed\n");
|
|
strncat(buf, accdet_log_buf, strlen(accdet_log_buf));
|
|
}
|
|
|
|
ret = sprintf(accdet_log_buf, "[0x%x]=0x%x\n",
|
|
TOP_CKPDN_CON0, pmic_read(TOP_CKPDN_CON0));
|
|
if (ret < 0)
|
|
pr_notice("sprintf failed\n");
|
|
strncat(buf, accdet_log_buf, strlen(accdet_log_buf));
|
|
|
|
ret = sprintf(accdet_log_buf, "[0x%x]=0x%x\n",
|
|
AUD_TOP_RST_CON0, pmic_read(AUD_TOP_RST_CON0));
|
|
if (ret < 0)
|
|
pr_notice("sprintf failed\n");
|
|
strncat(buf, accdet_log_buf, strlen(accdet_log_buf));
|
|
|
|
ret = sprintf(accdet_log_buf, "[0x%x]=0x%x, [0x%x]=0x%x, [0x%x]=0x%x\n",
|
|
AUD_TOP_INT_CON0, pmic_read(AUD_TOP_INT_CON0),
|
|
AUD_TOP_INT_MASK_CON0, pmic_read(AUD_TOP_INT_MASK_CON0),
|
|
AUD_TOP_INT_STATUS0, pmic_read(AUD_TOP_INT_STATUS0));
|
|
if (ret < 0)
|
|
pr_notice("sprintf failed\n");
|
|
strncat(buf, accdet_log_buf, strlen(accdet_log_buf));
|
|
|
|
ret = sprintf(accdet_log_buf,
|
|
"[0x%x]=0x%x,[0x%x]=0x%x,[0x%x]=0x%x,[0x%x]=0x%x\n",
|
|
AUDENC_ANA_CON6, pmic_read(AUDENC_ANA_CON6),
|
|
AUDENC_ANA_CON9, pmic_read(AUDENC_ANA_CON9),
|
|
AUDENC_ANA_CON10, pmic_read(AUDENC_ANA_CON10),
|
|
AUDENC_ANA_CON11, pmic_read(AUDENC_ANA_CON11));
|
|
if (ret < 0)
|
|
pr_notice("sprintf failed\n");
|
|
strncat(buf, accdet_log_buf, strlen(accdet_log_buf));
|
|
|
|
ret = sprintf(accdet_log_buf, "[0x%x]=0x%x, [0x%x]=0x%x\n",
|
|
AUXADC_RQST0, pmic_read(AUXADC_RQST0),
|
|
AUXADC_ACCDET, pmic_read(AUXADC_ACCDET));
|
|
if (ret < 0)
|
|
pr_notice("sprintf failed\n");
|
|
strncat(buf, accdet_log_buf, strlen(accdet_log_buf));
|
|
|
|
ret = sprintf(accdet_log_buf,
|
|
"dtsInfo:deb0=0x%x,deb1=0x%x,deb3=0x%x,deb4=0x%x\n",
|
|
cust_pwm_deb->debounce0, cust_pwm_deb->debounce1,
|
|
cust_pwm_deb->debounce3, cust_pwm_deb->debounce4);
|
|
if (ret < 0)
|
|
pr_notice("sprintf failed\n");
|
|
strncat(buf, accdet_log_buf, strlen(accdet_log_buf));
|
|
}
|
|
|
|
static int dbug_thread(void *unused)
|
|
{
|
|
dump_register();
|
|
|
|
return 0;
|
|
}
|
|
|
|
static ssize_t start_debug_store(struct device_driver *ddri,
|
|
const char *buf, size_t count)
|
|
{
|
|
int error = 0;
|
|
int ret = 0;
|
|
|
|
if (strlen(buf) < 1) {
|
|
pr_notice("%s() Invalid input!!\n", __func__);
|
|
return -EINVAL;
|
|
}
|
|
|
|
ret = strncmp(buf, "0", 1);
|
|
/* fix syzkaller issue */
|
|
if (debug_thread_en == true) {
|
|
pr_info("%s() debug thread started, ret!\n", __func__);
|
|
return count;
|
|
}
|
|
|
|
if (ret) {
|
|
debug_thread_en = true;
|
|
thread = kthread_run(dbug_thread, 0, "ACCDET");
|
|
if (IS_ERR(thread)) {
|
|
error = PTR_ERR(thread);
|
|
pr_notice("%s() create thread failed,err:%d\n",
|
|
__func__, error);
|
|
} else
|
|
pr_info("%s() start debug thread!\n", __func__);
|
|
} else {
|
|
debug_thread_en = false;
|
|
pr_info("%s() stop debug thread!\n", __func__);
|
|
}
|
|
|
|
return count;
|
|
}
|
|
|
|
static ssize_t set_reg_store(struct device_driver *ddri,
|
|
const char *buf, size_t count)
|
|
{
|
|
int ret = 0;
|
|
u32 addr_tmp = 0;
|
|
u32 value_tmp = 0;
|
|
|
|
if (strlen(buf) < 3) {
|
|
pr_notice("%s() Invalid input!!\n", __func__);
|
|
return -EINVAL;
|
|
}
|
|
|
|
ret = sscanf(buf, "0x%x,0x%x", &addr_tmp, &value_tmp);
|
|
if (ret < 0)
|
|
return ret;
|
|
|
|
pr_info("%s() set addr[0x%x]=0x%x\n", __func__, addr_tmp, value_tmp);
|
|
|
|
if (addr_tmp < PMIC_TOP0_ANA_ID_ADDR)
|
|
pr_notice("%s() Illegal addr[0x%x]!!\n", __func__, addr_tmp);
|
|
else
|
|
pmic_write(addr_tmp, value_tmp);
|
|
|
|
return count;
|
|
}
|
|
|
|
static ssize_t dump_reg_show(struct device_driver *ddri, char *buf)
|
|
{
|
|
if (buf == NULL) {
|
|
pr_notice("%s() *buf is NULL\n", __func__);
|
|
return -EINVAL;
|
|
}
|
|
|
|
cat_register(buf);
|
|
pr_info("%s() buf_size:%d\n", __func__, (int)strlen(buf));
|
|
|
|
return strlen(buf);
|
|
}
|
|
|
|
static ssize_t dump_reg_store(struct device_driver *ddri,
|
|
const char *buf, size_t count)
|
|
{
|
|
int ret = 0;
|
|
|
|
if (strlen(buf) < 1) {
|
|
pr_notice("%s() Invalid input!!\n", __func__);
|
|
return -EINVAL;
|
|
}
|
|
|
|
ret = strncmp(buf, "0", 1);
|
|
if (ret) {
|
|
dump_reg = true;
|
|
pr_info("%s() start dump regs!\n", __func__);
|
|
} else {
|
|
dump_reg = false;
|
|
pr_info("%s() stop dump regs!\n", __func__);
|
|
}
|
|
|
|
return count;
|
|
}
|
|
|
|
static ssize_t set_headset_mode_store(struct device_driver *ddri,
|
|
const char *buf, size_t count)
|
|
{
|
|
int ret = 0;
|
|
int tmp_headset_mode = 0;
|
|
|
|
if (strlen(buf) < 1) {
|
|
pr_notice("%s() Invalid input!\n", __func__);
|
|
return -EINVAL;
|
|
}
|
|
|
|
ret = kstrtoint(buf, 10, &tmp_headset_mode);
|
|
if (ret < 0) {
|
|
pr_notice("%s() kstrtoint failed! ret:%d\n", __func__, ret);
|
|
return ret;
|
|
}
|
|
pr_info("%s() get mic mode: %d\n", __func__, tmp_headset_mode);
|
|
|
|
switch (tmp_headset_mode&0x0F) {
|
|
case HEADSET_MODE_1:
|
|
pr_info("%s() Don't support switch to mode_1!\n", __func__);
|
|
/* accdet_dts.mic_mode = tmp_headset_mode; */
|
|
/* accdet_init(); */
|
|
break;
|
|
case HEADSET_MODE_2:
|
|
accdet_dts.mic_mode = tmp_headset_mode;
|
|
accdet_init();
|
|
break;
|
|
case HEADSET_MODE_6:
|
|
accdet_dts.mic_mode = tmp_headset_mode;
|
|
accdet_init();
|
|
break;
|
|
default:
|
|
pr_info("%s() Invalid mode: %d\n", __func__, tmp_headset_mode);
|
|
break;
|
|
}
|
|
accdet_init_once();
|
|
|
|
return count;
|
|
}
|
|
|
|
static ssize_t state_show(struct device_driver *ddri, char *buf)
|
|
{
|
|
char temp_type = (char)cable_type;
|
|
int ret = 0;
|
|
|
|
if (buf == NULL) {
|
|
pr_notice("[%s] *buf is NULL!\n", __func__);
|
|
return -EINVAL;
|
|
}
|
|
ret = snprintf(buf, 3, "%d\n", temp_type);
|
|
if (ret < 0)
|
|
pr_notice("snprintf failed\n");
|
|
|
|
return strlen(buf);
|
|
}
|
|
|
|
static DRIVER_ATTR_WO(start_debug);
|
|
static DRIVER_ATTR_WO(set_reg);
|
|
static DRIVER_ATTR_RW(dump_reg);
|
|
static DRIVER_ATTR_WO(set_headset_mode);
|
|
static DRIVER_ATTR_RO(state);
|
|
|
|
static struct driver_attribute *accdet_attr_list[] = {
|
|
&driver_attr_start_debug,
|
|
&driver_attr_set_reg,
|
|
&driver_attr_dump_reg,
|
|
&driver_attr_set_headset_mode,
|
|
&driver_attr_state,
|
|
};
|
|
|
|
static int accdet_create_attr(struct device_driver *driver)
|
|
{
|
|
int idx, err;
|
|
int num = ARRAY_SIZE(accdet_attr_list);
|
|
|
|
if (driver == NULL)
|
|
return -EINVAL;
|
|
for (idx = 0; idx < num; idx++) {
|
|
err = driver_create_file(driver, accdet_attr_list[idx]);
|
|
if (err) {
|
|
pr_notice("%s() driver_create_file %s err:%d\n",
|
|
__func__, accdet_attr_list[idx]->attr.name, err);
|
|
break;
|
|
}
|
|
}
|
|
return err;
|
|
}
|
|
|
|
/* get plug-in Resister for audio call */
|
|
int accdet_read_audio_res(unsigned int res_value)
|
|
{
|
|
pr_info("%s() resister value: R=%u(ohm)\n", __func__, res_value);
|
|
|
|
/* if res < 5k ohm normal device; res >= 5k ohm, lineout device */
|
|
if (res_value < 5000)
|
|
return RET_LT_5K;
|
|
|
|
mutex_lock(&accdet_eint_irq_sync_mutex);
|
|
if (eint_accdet_sync_flag) {
|
|
cable_type = LINE_OUT_DEVICE;
|
|
accdet_status = LINE_OUT;
|
|
send_accdet_status_event(cable_type, 1);
|
|
pr_info("%s() update state:%d\n", __func__, cable_type);
|
|
}
|
|
mutex_unlock(&accdet_eint_irq_sync_mutex);
|
|
|
|
return RET_GT_5K;
|
|
}
|
|
EXPORT_SYMBOL(accdet_read_audio_res);
|
|
|
|
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) {
|
|
pr_notice("%s Timer overflow! start%lld cur timer%lld\n",
|
|
__func__, start_time_ns, cur_time);
|
|
start_time_ns = cur_time;
|
|
/* 400us */
|
|
timeout_time_ns = 400 * 1000;
|
|
pr_notice("%s Reset timer! start%lld setting%lld\n",
|
|
__func__, start_time_ns, timeout_time_ns);
|
|
}
|
|
elapse_time = cur_time - start_time_ns;
|
|
|
|
/* check if timeout */
|
|
if (timeout_time_ns <= elapse_time) {
|
|
pr_notice("%s IRQ clear Timeout\n", __func__);
|
|
return false;
|
|
}
|
|
return true;
|
|
}
|
|
#endif /* end of #if PMIC_ACCDET_KERNEL */
|
|
|
|
static u32 accdet_get_auxadc(int deCount)
|
|
{
|
|
#if defined CONFIG_MTK_PMIC_NEW_ARCH | defined PMIC_ACCDET_CTP
|
|
int vol = pmic_get_auxadc_value(AUXADC_LIST_ACCDET);
|
|
|
|
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;
|
|
#else
|
|
return 0;
|
|
#endif
|
|
}
|
|
|
|
static void accdet_get_efuse(void)
|
|
{
|
|
u32 efuseval = 0;
|
|
int tmp_div = 0, efuse_idx = 0;
|
|
unsigned int moisture_eint0;
|
|
unsigned int moisture_eint1;
|
|
int efuse[2][5] = {{110, 113, 114, 112, 113},
|
|
{115, 118, 119, 117, 118} };
|
|
|
|
if (accdet_pmic == 0x0)
|
|
efuse_idx = 0;
|
|
else if (accdet_pmic == 0x66)
|
|
efuse_idx = 1;
|
|
/* accdet offset efuse:
|
|
* this efuse must divided by 2
|
|
*/
|
|
efuseval = pmic_Read_Efuse_HPOffset(efuse[efuse_idx][0]);
|
|
accdet_auxadc_offset = efuseval & 0xFF;
|
|
if (accdet_auxadc_offset > 128)
|
|
accdet_auxadc_offset -= 256;
|
|
accdet_auxadc_offset = (accdet_auxadc_offset / 2);
|
|
pr_info("%s efuse=0x%x,auxadc_val=%dmv\n", __func__, efuseval,
|
|
accdet_auxadc_offset);
|
|
|
|
/* all of moisture_vdd/moisture_offset0/eint is 2'complement,
|
|
* we need to transfer it
|
|
*/
|
|
/* moisture vdd efuse offset */
|
|
efuseval = pmic_Read_Efuse_HPOffset(efuse[efuse_idx][1]);
|
|
moisture_vdd_offset = (int)((efuseval >> 8) & ACCDET_CALI_MASK0);
|
|
if (moisture_vdd_offset > 128)
|
|
moisture_vdd_offset -= 256;
|
|
pr_info("%s moisture_vdd efuse=0x%x, moisture_vdd_offset=%d mv\n",
|
|
__func__, efuseval, moisture_vdd_offset);
|
|
|
|
/* moisture offset */
|
|
efuseval = pmic_Read_Efuse_HPOffset(efuse[efuse_idx][2]);
|
|
moisture_offset = (int)(efuseval & ACCDET_CALI_MASK0);
|
|
if (moisture_offset > 128)
|
|
moisture_offset -= 256;
|
|
pr_info("%s moisture_efuse efuse=0x%x,moisture_offset=%d mv\n",
|
|
__func__, efuseval, moisture_offset);
|
|
|
|
if (accdet_dts.moisture_use_ext_res == 0x0) {
|
|
/* moisture eint efuse offset */
|
|
efuseval = pmic_Read_Efuse_HPOffset(efuse[efuse_idx][3]);
|
|
moisture_eint0 = (int)((efuseval >> 8) & ACCDET_CALI_MASK0);
|
|
pr_info("%s moisture_eint0 efuse=0x%x,moisture_eint0=0x%x\n",
|
|
__func__, efuseval, moisture_eint0);
|
|
|
|
efuseval = pmic_Read_Efuse_HPOffset(efuse[efuse_idx][4]);
|
|
moisture_eint1 = (int)(efuseval & ACCDET_CALI_MASK0);
|
|
pr_info("%s moisture_eint1 efuse=0x%x,moisture_eint1=0x%x\n",
|
|
__func__, efuseval, moisture_eint1);
|
|
|
|
moisture_eint_offset = (moisture_eint1 << 8) | moisture_eint0;
|
|
if (moisture_eint_offset > 32768)
|
|
moisture_eint_offset -= 65536;
|
|
pr_info("%s moisture_eint_offset=%d ohm\n", __func__,
|
|
moisture_eint_offset);
|
|
|
|
moisture_vm = (2800 + moisture_vdd_offset);
|
|
moisture_vm = moisture_vm * (water_r + moisture_int_r);
|
|
tmp_div = water_r + moisture_int_r +
|
|
8 * moisture_eint_offset + 450000;
|
|
moisture_vm = moisture_vm / tmp_div;
|
|
moisture_vm = moisture_vm + moisture_offset / 2;
|
|
pr_info("%s internal moisture_vm=%d mv\n", __func__,
|
|
moisture_vm);
|
|
} else if (accdet_dts.moisture_use_ext_res == 0x1) {
|
|
if (moisture_vm == 0x0) {
|
|
moisture_vm = (2800 + moisture_vdd_offset);
|
|
moisture_vm = moisture_vm * water_r;
|
|
moisture_vm = moisture_vm / (water_r + moisture_ext_r);
|
|
moisture_vm = moisture_vm + (moisture_offset >> 1);
|
|
pr_info("%s external moisture_vm=%d mv\n", __func__,
|
|
moisture_vm);
|
|
}
|
|
}
|
|
}
|
|
|
|
#ifdef CONFIG_FOUR_KEY_HEADSET
|
|
static void accdet_get_efuse_4key(void)
|
|
{
|
|
u32 tmp_val = 0;
|
|
u32 tmp_8bit = 0;
|
|
int efuse_idx = 0;
|
|
int efuse[2][2] = {{111, 112},
|
|
{116, 117} };
|
|
|
|
if (accdet_pmic == 0x0)
|
|
efuse_idx = 0;
|
|
else if (accdet_pmic == 0x66)
|
|
efuse_idx = 1;
|
|
|
|
/* 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;
|
|
*/
|
|
tmp_val = pmic_Read_Efuse_HPOffset(efuse[efuse_idx][0]);
|
|
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;
|
|
|
|
tmp_val = pmic_Read_Efuse_HPOffset(efuse[efuse_idx][1]);
|
|
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 ((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;
|
|
|
|
return NO_KEY;
|
|
}
|
|
#else
|
|
static u32 key_check(u32 v)
|
|
{
|
|
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;
|
|
}
|
|
#endif
|
|
|
|
#if PMIC_ACCDET_KERNEL
|
|
static void send_key_event(u32 keycode, u32 flag)
|
|
{
|
|
switch (keycode) {
|
|
case DW_KEY:
|
|
input_report_key(accdet_input_dev, KEY_VOLUMEDOWN, flag);
|
|
input_sync(accdet_input_dev);
|
|
pr_debug("accdet KEY_VOLUMEDOWN %d\n", flag);
|
|
break;
|
|
case UP_KEY:
|
|
input_report_key(accdet_input_dev, KEY_VOLUMEUP, flag);
|
|
input_sync(accdet_input_dev);
|
|
pr_debug("accdet KEY_VOLUMEUP %d\n", flag);
|
|
break;
|
|
case MD_KEY:
|
|
input_report_key(accdet_input_dev, KEY_MEDIA, flag);
|
|
input_sync(accdet_input_dev);
|
|
pr_debug("accdet KEY_MEDIA %d\n", flag);
|
|
break;
|
|
case AS_KEY:
|
|
input_report_key(accdet_input_dev, KEY_VOICECOMMAND, flag);
|
|
input_sync(accdet_input_dev);
|
|
pr_debug("accdet KEY_VOICECOMMAND %d\n", flag);
|
|
break;
|
|
}
|
|
#ifdef CONFIG_SND_SOC_SAMSUNG_AUDIO
|
|
accdet_pdata.key_state = (flag) ? 1 : 0;
|
|
#endif
|
|
}
|
|
|
|
static void send_accdet_status_event(u32 cable_type, u32 status)
|
|
{
|
|
switch (cable_type) {
|
|
case HEADSET_NO_MIC:
|
|
input_report_switch(accdet_input_dev, SW_HEADPHONE_INSERT,
|
|
status);
|
|
/* 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)
|
|
input_report_switch(accdet_input_dev,
|
|
SW_MICROPHONE_INSERT, status);
|
|
input_sync(accdet_input_dev);
|
|
pr_info("%s HEADPHONE(3-pole) %s\n", __func__,
|
|
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)
|
|
input_report_switch(accdet_input_dev,
|
|
SW_HEADPHONE_INSERT, status);
|
|
input_report_switch(accdet_input_dev, SW_MICROPHONE_INSERT,
|
|
status);
|
|
input_sync(accdet_input_dev);
|
|
pr_info("%s MICROPHONE(4-pole) %s\n", __func__,
|
|
status ? "PlugIn" : "PlugOut");
|
|
/* when press key for a long time then plug in
|
|
* even recoginized as 4-pole
|
|
* disable micbias timer still timeout after 6s
|
|
* it check AB=00(because keep to press key) then disable
|
|
* micbias, it will cause key no response
|
|
*/
|
|
del_timer_sync(&micbias_timer);
|
|
break;
|
|
case LINE_OUT_DEVICE:
|
|
input_report_switch(accdet_input_dev, SW_LINEOUT_INSERT,
|
|
status);
|
|
input_sync(accdet_input_dev);
|
|
pr_info("%s LineOut %s\n", __func__,
|
|
status ? "PlugIn" : "PlugOut");
|
|
break;
|
|
default:
|
|
pr_info("%s Invalid cableType\n", __func__);
|
|
}
|
|
#ifdef CONFIG_SND_SOC_SAMSUNG_AUDIO
|
|
if (status == 0)
|
|
accdet_pdata.jack_state = NO_DEVICE;
|
|
else
|
|
accdet_pdata.jack_state = cable_type;
|
|
#endif
|
|
}
|
|
#else
|
|
u64 accdet_get_current_time(void)
|
|
{
|
|
return 0;
|
|
}
|
|
|
|
static bool accdet_timeout_ns(u64 start_time_ns, u64 timeout_time_ns)
|
|
{
|
|
return true;
|
|
}
|
|
static void send_key_event(u32 keycode, u32 flag)
|
|
{
|
|
}
|
|
static void send_accdet_status_event(u32 cable_type, u32 status)
|
|
{
|
|
}
|
|
#endif /* end of #if PMIC_ACCDET_KERNEL */
|
|
|
|
static void multi_key_detection(u32 cur_AB)
|
|
{
|
|
#ifdef CONFIG_ACCDET_EINT_IRQ
|
|
bool irq_bit;
|
|
#endif
|
|
|
|
if (cur_AB == ACCDET_STATE_AB_00)
|
|
cur_key = key_check(cali_voltage);
|
|
#ifdef CONFIG_SND_SOC_SAMSUNG_AUDIO
|
|
if (cur_AB == ACCDET_STATE_AB_00)
|
|
accdet_pdata.mic_adc = cali_voltage;
|
|
else
|
|
accdet_pdata.mic_adc = 0;
|
|
#endif
|
|
|
|
/* 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. cur_eint_state == PLUG_OUT
|
|
*/
|
|
usleep_range(10000, 12000);
|
|
|
|
#ifdef CONFIG_ACCDET_EINT_IRQ
|
|
irq_bit = !(pmic_read(ACCDET_IRQ_STS) & ACCDET_EINT_IRQ_B2_B3);
|
|
/* send key when: no eint is flaged in reg, and now eint PLUG_IN */
|
|
if (irq_bit && (cur_eint_state == EINT_PIN_PLUG_IN))
|
|
#elif defined CONFIG_ACCDET_EINT
|
|
if (cur_eint_state == EINT_PIN_PLUG_IN)
|
|
#endif
|
|
send_key_event(cur_key, !cur_AB);
|
|
else {
|
|
pr_info("accdet plugout sideeffect key,do not report key=%d\n",
|
|
cur_key);
|
|
cur_key = NO_KEY;
|
|
}
|
|
|
|
if (cur_AB)
|
|
cur_key = NO_KEY;
|
|
}
|
|
|
|
/* clear ACCDET IRQ in accdet register */
|
|
static inline void clear_accdet_int(void)
|
|
{
|
|
/* it is safe by using polling to adjust when to clear IRQ_CLR_BIT */
|
|
pmic_write(ACCDET_IRQ_STS,
|
|
pmic_read(ACCDET_IRQ_STS) | ACCDET_IRQ_CLR_B8);
|
|
pr_debug("%s() IRQ_STS = 0x%x\n", __func__, pmic_read(ACCDET_IRQ_STS));
|
|
}
|
|
|
|
static inline void clear_accdet_int_check(void)
|
|
{
|
|
u64 cur_time = accdet_get_current_time();
|
|
|
|
while ((pmic_read(ACCDET_IRQ_STS) & ACCDET_IRQ_B0) &&
|
|
(accdet_timeout_ns(cur_time, ACCDET_TIME_OUT)))
|
|
;
|
|
/* clear accdet int, modify for fix interrupt trigger twice error */
|
|
pmic_write(ACCDET_IRQ_STS,
|
|
pmic_read(ACCDET_IRQ_STS) & (~ACCDET_IRQ_CLR_B8));
|
|
pmic_write(AUD_TOP_INT_STATUS0, RG_INT_STATUS_ACCDET_B5);
|
|
}
|
|
|
|
#ifdef CONFIG_ACCDET_EINT_IRQ
|
|
static inline void clear_accdet_eint(u32 eintid)
|
|
{
|
|
u32 reg_val = pmic_read(ACCDET_IRQ_STS);
|
|
|
|
if ((eintid & PMIC_EINT0) == PMIC_EINT0)
|
|
pmic_write(ACCDET_IRQ_STS, reg_val | ACCDET_EINT0_IRQ_CLR_B10);
|
|
|
|
if ((eintid & PMIC_EINT1) == PMIC_EINT1)
|
|
pmic_write(ACCDET_IRQ_STS, reg_val | ACCDET_EINT1_IRQ_CLR_B11);
|
|
|
|
pr_debug("%s() eint-%s IRQ-STS:[0x%x]=0x%x\n", __func__,
|
|
(eintid == PMIC_EINT0)?"0":((eintid == PMIC_EINT1)?"1":"BI"),
|
|
ACCDET_IRQ_STS, pmic_read(ACCDET_IRQ_STS));
|
|
}
|
|
|
|
static inline void clear_accdet_eint_check(u32 eintid)
|
|
{
|
|
u64 cur_time = accdet_get_current_time();
|
|
|
|
if ((eintid & PMIC_EINT0) == PMIC_EINT0) {
|
|
while ((pmic_read(ACCDET_IRQ_STS) & ACCDET_EINT0_IRQ_B2)
|
|
&& (accdet_timeout_ns(cur_time, ACCDET_TIME_OUT)))
|
|
;
|
|
pmic_write(ACCDET_IRQ_STS,
|
|
pmic_read(ACCDET_IRQ_STS)&(~ACCDET_EINT0_IRQ_CLR_B10));
|
|
pmic_write(AUD_TOP_INT_STATUS0, RG_INT_STATUS_ACCDET_EINT0_B6);
|
|
}
|
|
if ((eintid & PMIC_EINT1) == PMIC_EINT1) {
|
|
while ((pmic_read(ACCDET_IRQ_STS) & ACCDET_EINT1_IRQ_B3)
|
|
&& (accdet_timeout_ns(cur_time, ACCDET_TIME_OUT)))
|
|
;
|
|
pmic_write(ACCDET_IRQ_STS,
|
|
pmic_read(ACCDET_IRQ_STS)&(~ACCDET_EINT1_IRQ_CLR_B11));
|
|
pmic_write(AUD_TOP_INT_STATUS0, RG_INT_STATUS_ACCDET_EINT1_B7);
|
|
}
|
|
|
|
pr_debug("%s() eint-%s IRQ-STS:[0x%x]=0x%x TOP_INT_STS:[0x%x]:0x%x\n",
|
|
__func__,
|
|
(eintid == PMIC_EINT0)?"0":((eintid == PMIC_EINT1)?"1":"BI"),
|
|
ACCDET_IRQ_STS, pmic_read(ACCDET_IRQ_STS),
|
|
AUD_TOP_INT_STATUS0, pmic_read(AUD_TOP_INT_STATUS0));
|
|
|
|
}
|
|
|
|
static void eint_debounce_set(u32 eint_id, u32 debounce)
|
|
{
|
|
u32 reg_val;
|
|
|
|
if (eint_id == PMIC_EINT0) {
|
|
reg_val = pmic_read(ACCDET_EINT0_CTL);
|
|
pmic_write(ACCDET_EINT0_CTL,
|
|
reg_val & (~ACCDET_EINT0_DEB_CLR));
|
|
|
|
reg_val = pmic_read(ACCDET_EINT0_CTL);
|
|
pmic_write(ACCDET_EINT0_CTL,
|
|
reg_val | debounce);
|
|
} else if (eint_id == PMIC_EINT1) {
|
|
reg_val = pmic_read(ACCDET_EINT1_CTL);
|
|
pmic_write(ACCDET_EINT1_CTL,
|
|
reg_val & (~ACCDET_EINT1_DEB_CLR));
|
|
reg_val = pmic_read(ACCDET_EINT1_CTL);
|
|
pmic_write(ACCDET_EINT1_CTL,
|
|
reg_val | debounce);
|
|
}
|
|
}
|
|
|
|
static u32 get_triggered_eint(void)
|
|
{
|
|
u32 eint_ID = NO_PMIC_EINT;
|
|
u32 irq_status = pmic_read(ACCDET_IRQ_STS);
|
|
|
|
#ifdef CONFIG_ACCDET_SUPPORT_EINT0
|
|
if ((irq_status & ACCDET_EINT0_IRQ_B2) == ACCDET_EINT0_IRQ_B2)
|
|
eint_ID = PMIC_EINT0;
|
|
#elif defined CONFIG_ACCDET_SUPPORT_EINT1
|
|
if ((irq_status & ACCDET_EINT1_IRQ_B3) == ACCDET_EINT1_IRQ_B3)
|
|
eint_ID = PMIC_EINT1;
|
|
#elif defined CONFIG_ACCDET_SUPPORT_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;
|
|
#endif
|
|
return eint_ID;
|
|
}
|
|
|
|
|
|
static void eint_polarity_reverse(u32 eint_id)
|
|
{
|
|
u32 reg_val = pmic_read(ACCDET_IRQ_STS);
|
|
|
|
if (eint_id == PMIC_EINT0) {
|
|
if (cur_eint_state == EINT_PIN_PLUG_OUT) {
|
|
if (accdet_eint_type == IRQ_TYPE_LEVEL_HIGH)
|
|
pmic_write(ACCDET_IRQ_STS,
|
|
reg_val & (~ACCDET_EINT0_IRQ_POL_B14));
|
|
else
|
|
pmic_write(ACCDET_IRQ_STS,
|
|
reg_val | ACCDET_EINT0_IRQ_POL_B14);
|
|
} else {
|
|
if (accdet_eint_type == IRQ_TYPE_LEVEL_HIGH)
|
|
pmic_write(ACCDET_IRQ_STS,
|
|
reg_val | ACCDET_EINT0_IRQ_POL_B14);
|
|
else
|
|
pmic_write(ACCDET_IRQ_STS,
|
|
reg_val & (~ACCDET_EINT0_IRQ_POL_B14));
|
|
}
|
|
} else if (eint_id == PMIC_EINT1) {
|
|
|
|
if (cur_eint_state == EINT_PIN_PLUG_OUT) {
|
|
if (accdet_eint_type == IRQ_TYPE_LEVEL_HIGH)
|
|
pmic_write(ACCDET_IRQ_STS,
|
|
reg_val & (~ACCDET_EINT1_IRQ_POL_B15));
|
|
else
|
|
pmic_write(ACCDET_IRQ_STS,
|
|
reg_val | ACCDET_EINT1_IRQ_POL_B15);
|
|
} else {
|
|
if (accdet_eint_type == IRQ_TYPE_LEVEL_HIGH)
|
|
pmic_write(ACCDET_IRQ_STS,
|
|
reg_val | ACCDET_EINT1_IRQ_POL_B15);
|
|
else
|
|
pmic_write(ACCDET_IRQ_STS,
|
|
reg_val & (~ACCDET_EINT1_IRQ_POL_B15));
|
|
}
|
|
}
|
|
|
|
}
|
|
#endif
|
|
|
|
static inline void enable_accdet(u32 state_swctrl)
|
|
{
|
|
pmic_write(ACCDET_STATE_SWCTRL,
|
|
pmic_read(ACCDET_STATE_SWCTRL) | state_swctrl);
|
|
|
|
/* enable ACCDET unit */
|
|
#ifndef HW_MODE_SUPPORT
|
|
pmic_write(ACCDET_CTRL, pmic_read(ACCDET_CTRL) | ACCDET_ENABLE_B0);
|
|
#endif
|
|
pr_info("%s done IRQ-STS[0x%x]=0x%x,STATE_SWCTRL[0x%x]=0x%x\n",
|
|
__func__, ACCDET_IRQ_STS, pmic_read(ACCDET_IRQ_STS),
|
|
ACCDET_STATE_SWCTRL, pmic_read(ACCDET_STATE_SWCTRL));
|
|
}
|
|
|
|
static inline void disable_accdet(void)
|
|
{
|
|
/* sync with accdet_irq_handler set clear accdet irq bit to avoid */
|
|
/* set clear accdet irq bit after disable accdet disable accdet irq */
|
|
clear_accdet_int();
|
|
udelay(200);
|
|
mutex_lock(&accdet_eint_irq_sync_mutex);
|
|
clear_accdet_int_check();
|
|
mutex_unlock(&accdet_eint_irq_sync_mutex);
|
|
|
|
#ifndef HW_MODE_SUPPORT
|
|
pmic_write(ACCDET_CTRL,
|
|
pmic_read(ACCDET_CTRL) & (~ACCDET_ENABLE_B0));
|
|
#endif
|
|
/* clc ACCDET PWM enable to avoid power leakage */
|
|
pmic_write(ACCDET_STATE_SWCTRL,
|
|
pmic_read(ACCDET_STATE_SWCTRL) & (~ACCDET_PWM_EN));
|
|
pr_info("%s done IRQ-STS[0x%x]=0x%x,STATE_SWCTRL[0x%x]=0x%x\n",
|
|
__func__, ACCDET_IRQ_STS, pmic_read(ACCDET_IRQ_STS),
|
|
ACCDET_STATE_SWCTRL, pmic_read(ACCDET_STATE_SWCTRL));
|
|
}
|
|
|
|
static inline void headset_plug_out(void)
|
|
{
|
|
send_accdet_status_event(cable_type, 0);
|
|
accdet_status = PLUG_OUT;
|
|
cable_type = NO_DEVICE;
|
|
|
|
if (cur_key != 0) {
|
|
send_key_event(cur_key, 0);
|
|
pr_info("accdet %s, send key=%d release\n", __func__, cur_key);
|
|
cur_key = 0;
|
|
}
|
|
pr_info("accdet %s, set cable_type = NO_DEVICE\n", __func__);
|
|
#if PMIC_ACCDET_DEBUG
|
|
dump_register();
|
|
#endif
|
|
}
|
|
#if PMIC_ACCDET_KERNEL
|
|
static void dis_micbias_timerhandler(struct timer_list *t)
|
|
{
|
|
int ret = 0;
|
|
|
|
ret = queue_work(dis_micbias_workqueue, &dis_micbias_work);
|
|
if (!ret)
|
|
pr_info("accdet %s, queue work return:%d!\n", __func__, ret);
|
|
}
|
|
|
|
static void dis_micbias_work_callback(struct work_struct *work)
|
|
{
|
|
if (cable_type == HEADSET_NO_MIC) {
|
|
/* setting pwm idle; */
|
|
pmic_write(ACCDET_STATE_SWCTRL,
|
|
pmic_read(ACCDET_STATE_SWCTRL) & (~ACCDET_PWM_IDLE));
|
|
|
|
disable_accdet();
|
|
pr_info("%s more than 6s,MICBIAS:Disabled c_type:0x%x\n",
|
|
__func__, cable_type);
|
|
}
|
|
}
|
|
#endif /* end of #if PMIC_ACCDET_KERNEL */
|
|
#if ACCDET_SW_MOISTURE_V1
|
|
static void eint_work_callback(void)
|
|
#else
|
|
#if PMIC_ACCDET_KERNEL
|
|
static void eint_work_callback(struct work_struct *work)
|
|
#else
|
|
static void eint_work_callback(void)
|
|
#endif
|
|
#endif
|
|
{
|
|
pr_info("accdet %s(),DCC EINT func\n", __func__);
|
|
|
|
if (cur_eint_state == EINT_PIN_PLUG_IN) {
|
|
pr_info("accdet cur: plug-in, cur_eint_state = %d\n",
|
|
cur_eint_state);
|
|
mutex_lock(&accdet_eint_irq_sync_mutex);
|
|
eint_accdet_sync_flag = true;
|
|
mutex_unlock(&accdet_eint_irq_sync_mutex);
|
|
__pm_wakeup_event(accdet_timer_lock,
|
|
jiffies_to_msecs(7 * HZ));
|
|
|
|
accdet_init();
|
|
|
|
/* set PWM IDLE on */
|
|
pmic_write(ACCDET_STATE_SWCTRL,
|
|
(pmic_read(ACCDET_STATE_SWCTRL) | ACCDET_PWM_IDLE));
|
|
#ifdef CONFIG_ACCDET_EINT_IRQ
|
|
#ifdef CONFIG_ACCDET_SUPPORT_EINT0
|
|
enable_accdet(ACCDET_EINT0_PWM_IDLE_B11 | ACCDET_PWM_EN);
|
|
#elif defined CONFIG_ACCDET_SUPPORT_EINT1
|
|
enable_accdet(ACCDET_EINT1_PWM_IDLE_B12 | ACCDET_PWM_EN);
|
|
#elif defined CONFIG_ACCDET_SUPPORT_BI_EINT
|
|
enable_accdet(ACCDET_EINT_PWM_IDLE_B11_12 | ACCDET_PWM_EN);
|
|
#endif
|
|
#if NO_USE_COMPARATOR
|
|
msleep(180);/* may be need delay more, relevant to Bias vol. */
|
|
check_cable_type();
|
|
if (accdet_status == MIC_BIAS)
|
|
cali_voltage = accdet_get_auxadc(1);
|
|
mod_timer(&accdet_open_cable_timer,
|
|
jiffies + ACCDET_OPEN_CABLE_TIMER);
|
|
#endif
|
|
#else
|
|
enable_accdet(ACCDET_PWM_EN);
|
|
#if NO_USE_COMPARATOR
|
|
msleep(180);/* may be need delay more, relevant to Bias vol. */
|
|
check_cable_type();
|
|
if (accdet_status == MIC_BIAS)
|
|
cali_voltage = accdet_get_auxadc(1);
|
|
mod_timer(&accdet_open_cable_timer,
|
|
jiffies + ACCDET_OPEN_CABLE_TIMER);
|
|
#endif
|
|
#endif
|
|
} else {
|
|
pr_info("accdet cur:plug-out, cur_eint_state = %d\n",
|
|
cur_eint_state);
|
|
mutex_lock(&accdet_eint_irq_sync_mutex);
|
|
eint_accdet_sync_flag = false;
|
|
mutex_unlock(&accdet_eint_irq_sync_mutex);
|
|
del_timer_sync(&micbias_timer);
|
|
|
|
/* clc Accdet PWM idle */
|
|
pmic_write(ACCDET_STATE_SWCTRL,
|
|
pmic_read(ACCDET_STATE_SWCTRL) & (~ACCDET_PWM_IDLE));
|
|
disable_accdet();
|
|
headset_plug_out();
|
|
}
|
|
|
|
#ifdef CONFIG_ACCDET_EINT
|
|
pr_info("accdet %s done !!\n", __func__);
|
|
#endif
|
|
}
|
|
|
|
void accdet_set_debounce(int state, unsigned int debounce)
|
|
{
|
|
switch (state) {
|
|
case accdet_state000:
|
|
/* set ACCDET debounce value = debounce/32 ms */
|
|
pmic_write((unsigned int)ACCDET_DEBOUNCE0, debounce);
|
|
break;
|
|
case accdet_state010:
|
|
pmic_write((unsigned int)ACCDET_DEBOUNCE1, debounce);
|
|
break;
|
|
case accdet_state100:
|
|
pmic_write((unsigned int)ACCDET_DEBOUNCE2, debounce);
|
|
break;
|
|
case accdet_state110:
|
|
pmic_write((unsigned int)ACCDET_DEBOUNCE3, debounce);
|
|
break;
|
|
case accdet_auxadc:
|
|
/* set auxadc debounce:0x42(2ms) */
|
|
pmic_write((unsigned int)ACCDET_DEBOUNCE4, debounce);
|
|
break;
|
|
default:
|
|
pr_info("%s error state:%d!\n", __func__, state);
|
|
break;
|
|
}
|
|
}
|
|
|
|
#if NO_USE_COMPARATOR
|
|
static unsigned int check_pole_type(void)
|
|
{
|
|
unsigned int vol = 0;
|
|
|
|
vol = accdet_get_auxadc(1);
|
|
if ((vol < (cust_vol_set.vol_max_4pole + 1)) &&
|
|
(vol > (cust_vol_set.vol_min_4pole - 1))) {
|
|
pr_notice("[accdet] pole check:%d mv, AB=%d\n",
|
|
vol, TYPE_AB_01);
|
|
return TYPE_AB_01;
|
|
} else if ((vol < (cust_vol_set.vol_max_3pole + 1)) &&
|
|
(vol > cust_vol_set.vol_min_3pole)) {
|
|
pr_notice("[accdet] pole check:%d mv, AB=%d\n",
|
|
vol, TYPE_AB_00);
|
|
return TYPE_AB_00;
|
|
}
|
|
/* illegal state */
|
|
pr_notice("[accdet] pole check:%d mv, AB=%d\n", vol, TYPE_AB_10);
|
|
return TYPE_AB_10;
|
|
}
|
|
#endif
|
|
|
|
static inline void check_cable_type(void)
|
|
{
|
|
u32 cur_AB;
|
|
|
|
#if NO_USE_COMPARATOR
|
|
cur_AB = check_pole_type();
|
|
pr_notice("accdet %s(), cur_status:%s current AB = %d\n", __func__,
|
|
accdet_status_str[accdet_status], cur_AB);
|
|
#else
|
|
cur_AB = pmic_read(ACCDET_STATE_RG) >> ACCDET_STATE_MEM_IN_OFFSET;
|
|
cur_AB = cur_AB & ACCDET_STATE_AB_MASK;
|
|
pr_notice("accdet %s(), cur_status:%s current AB = %d\n", __func__,
|
|
accdet_status_str[accdet_status], cur_AB);
|
|
#endif
|
|
s_button_status = 0;
|
|
pre_status = accdet_status;
|
|
|
|
switch (accdet_status) {
|
|
case PLUG_OUT:
|
|
if (cur_AB == ACCDET_STATE_AB_00) {
|
|
mutex_lock(&accdet_eint_irq_sync_mutex);
|
|
if (eint_accdet_sync_flag) {
|
|
cable_type = HEADSET_NO_MIC;
|
|
accdet_status = HOOK_SWITCH;
|
|
} else
|
|
pr_info("accdet headset has been plug-out\n");
|
|
mutex_unlock(&accdet_eint_irq_sync_mutex);
|
|
} else if (cur_AB == ACCDET_STATE_AB_01) {
|
|
mutex_lock(&accdet_eint_irq_sync_mutex);
|
|
if (eint_accdet_sync_flag) {
|
|
accdet_status = MIC_BIAS;
|
|
cable_type = HEADSET_MIC;
|
|
#ifdef HW_MODE_SUPPORT
|
|
#ifdef DIGITAL_FASTDISCHARGE_SUPPORT
|
|
/* digital fast discharge bug, sw arround (2) :
|
|
* ACCDET_CON24[15:14]=00 and then wait xxms
|
|
* to set [15:14]=11b, [4]=1;
|
|
* please remember to find sw arround (1)
|
|
*/
|
|
if (!fast_discharge) {
|
|
pmic_write(ACCDET_HW_MODE_DFF,
|
|
ACCDET_FAST_DISCAHRGE_REVISE);
|
|
msleep(20);
|
|
pmic_write(ACCDET_HW_MODE_DFF,
|
|
ACCDET_FAST_DISCAHRGE_EN);
|
|
fast_discharge = true;
|
|
}
|
|
#endif
|
|
#endif
|
|
/* ABC=110 debounce=30ms */
|
|
accdet_set_debounce(accdet_state110,
|
|
cust_pwm_deb->debounce3 * 30);
|
|
} else
|
|
pr_info("accdet headset has been plug-out\n");
|
|
mutex_unlock(&accdet_eint_irq_sync_mutex);
|
|
/* solution: adjust hook switch debounce time
|
|
* for fast key press condition, avoid to miss key
|
|
*/
|
|
accdet_set_debounce(accdet_state000,
|
|
button_press_debounce);
|
|
} else if (cur_AB == ACCDET_STATE_AB_11) {
|
|
pr_info("accdet PLUG_OUT state not change!\n");
|
|
#ifdef CONFIG_ACCDET_EINT_IRQ
|
|
mutex_lock(&accdet_eint_irq_sync_mutex);
|
|
if (eint_accdet_sync_flag) {
|
|
accdet_status = PLUG_OUT;
|
|
cable_type = NO_DEVICE;
|
|
} else
|
|
pr_info("accdet headset has been plug-out\n");
|
|
mutex_unlock(&accdet_eint_irq_sync_mutex);
|
|
#endif
|
|
} else
|
|
pr_info("accdet %s Invalid AB.Do nothing\n", __func__);
|
|
break;
|
|
case MIC_BIAS:
|
|
if (cur_AB == ACCDET_STATE_AB_00) {
|
|
mutex_lock(&accdet_eint_irq_sync_mutex);
|
|
if (eint_accdet_sync_flag) {
|
|
s_button_status = 1;
|
|
accdet_status = HOOK_SWITCH;
|
|
multi_key_detection(cur_AB);
|
|
} else
|
|
pr_info("accdet headset has been plug-out\n");
|
|
mutex_unlock(&accdet_eint_irq_sync_mutex);
|
|
} else if (cur_AB == ACCDET_STATE_AB_01) {
|
|
mutex_lock(&accdet_eint_irq_sync_mutex);
|
|
if (eint_accdet_sync_flag) {
|
|
accdet_status = MIC_BIAS;
|
|
cable_type = HEADSET_MIC;
|
|
pr_info("accdet MIC_BIAS state not change!\n");
|
|
} else
|
|
pr_info("accdet headset has been plug-out\n");
|
|
mutex_unlock(&accdet_eint_irq_sync_mutex);
|
|
} else if (cur_AB == ACCDET_STATE_AB_11) {
|
|
pr_info("accdet Don't send plug out in MIC_BIAS\n");
|
|
mutex_lock(&accdet_eint_irq_sync_mutex);
|
|
if (eint_accdet_sync_flag)
|
|
accdet_status = PLUG_OUT;
|
|
else
|
|
pr_info("accdet headset has been plug-out\n");
|
|
mutex_unlock(&accdet_eint_irq_sync_mutex);
|
|
} else
|
|
pr_info("accdet %s Invalid AB.Do nothing\n", __func__);
|
|
break;
|
|
case HOOK_SWITCH:
|
|
if (cur_AB == ACCDET_STATE_AB_00) {
|
|
mutex_lock(&accdet_eint_irq_sync_mutex);
|
|
if (eint_accdet_sync_flag)
|
|
/* to avoid 01->00 framework of Headset will
|
|
* report press key info to Audio
|
|
*/
|
|
/* cable_type = HEADSET_NO_MIC; */
|
|
/* accdet_status = HOOK_SWITCH; */
|
|
pr_info("accdet HOOKSWITCH state no change\n");
|
|
else
|
|
pr_info("accdet headset has been plug-out\n");
|
|
mutex_unlock(&accdet_eint_irq_sync_mutex);
|
|
} else if (cur_AB == ACCDET_STATE_AB_01) {
|
|
mutex_lock(&accdet_eint_irq_sync_mutex);
|
|
if (eint_accdet_sync_flag) {
|
|
multi_key_detection(cur_AB);
|
|
accdet_status = MIC_BIAS;
|
|
cable_type = HEADSET_MIC;
|
|
} else
|
|
pr_info("accdet headset has been plug-out\n");
|
|
mutex_unlock(&accdet_eint_irq_sync_mutex);
|
|
} else if (cur_AB == ACCDET_STATE_AB_11) {
|
|
pr_info("accdet Don't send plugout in HOOK_SWITCH\n");
|
|
mutex_lock(&accdet_eint_irq_sync_mutex);
|
|
if (eint_accdet_sync_flag)
|
|
accdet_status = PLUG_OUT;
|
|
else
|
|
pr_info("accdet headset has been plug-out\n");
|
|
mutex_unlock(&accdet_eint_irq_sync_mutex);
|
|
} else
|
|
pr_info("accdet %s Invalid AB.Do nothing\n", __func__);
|
|
break;
|
|
case STAND_BY:
|
|
pr_info("accdet %s STANDBY state.Err!Do nothing!\n", __func__);
|
|
break;
|
|
default:
|
|
pr_info("accdet %s Error state.Do nothing!\n", __func__);
|
|
break;
|
|
}
|
|
|
|
pr_info("accdet cur cable type:[%s], status switch:[%s]->[%s]\n",
|
|
accdet_report_str[cable_type], accdet_status_str[pre_status],
|
|
accdet_status_str[accdet_status]);
|
|
}
|
|
|
|
#if PMIC_ACCDET_KERNEL
|
|
static void accdet_work_callback(struct work_struct *work)
|
|
#else
|
|
static void accdet_work_callback(void)
|
|
#endif
|
|
{
|
|
u32 pre_cable_type = cable_type;
|
|
|
|
__pm_stay_awake(accdet_irq_lock);
|
|
check_cable_type();
|
|
|
|
mutex_lock(&accdet_eint_irq_sync_mutex);
|
|
if (eint_accdet_sync_flag) {
|
|
if ((pre_cable_type != cable_type) ||
|
|
(cable_type == HEADSET_MIC))
|
|
send_accdet_status_event(cable_type, 1);
|
|
} else
|
|
pr_info("%s() Headset has been plugout. Don't set state\n",
|
|
__func__);
|
|
mutex_unlock(&accdet_eint_irq_sync_mutex);
|
|
pr_info("%s() report cable_type done\n", __func__);
|
|
__pm_relax(accdet_irq_lock);
|
|
}
|
|
|
|
static void accdet_queue_work(void)
|
|
{
|
|
int ret;
|
|
|
|
if (accdet_status == MIC_BIAS)
|
|
cali_voltage = accdet_get_auxadc(1);
|
|
|
|
#if PMIC_ACCDET_KERNEL
|
|
ret = queue_work(accdet_workqueue, &accdet_work);
|
|
#else
|
|
accdet_work_callback();
|
|
#endif /* end of #if PMIC_ACCDET_KERNEL */
|
|
if (!ret)
|
|
pr_info("queue work accdet_work return:%d!\n", ret);
|
|
}
|
|
|
|
#ifdef CONFIG_ACCDET_EINT_IRQ
|
|
static int pmic_eint_queue_work(int eintID)
|
|
{
|
|
int ret = 0;
|
|
|
|
pr_info("%s() Enter. eint-%s cur_eint_state:%d\n", __func__,
|
|
(eintID == PMIC_EINT0)?"0":((eintID == PMIC_EINT1)?"1":"BI"),
|
|
cur_eint_state);
|
|
|
|
#ifdef CONFIG_ACCDET_SUPPORT_EINT0
|
|
if (eintID == PMIC_EINT0) {
|
|
if (cur_eint_state == EINT_PIN_PLUG_IN) {
|
|
eint_debounce_set(eintID, ACCDET_EINT0_DEB_512);
|
|
accdet_set_debounce(accdet_state110,
|
|
cust_pwm_deb->debounce3);
|
|
cur_eint_state = EINT_PIN_PLUG_OUT;
|
|
} else {
|
|
eint_debounce_set(eintID, ACCDET_EINT0_DEB_OUT_012);
|
|
cur_eint_state = EINT_PIN_PLUG_IN;
|
|
|
|
mod_timer(&micbias_timer,
|
|
jiffies + MICBIAS_DISABLE_TIMER);
|
|
}
|
|
#if PMIC_ACCDET_KERNEL
|
|
ret = queue_work(eint_workqueue, &eint_work);
|
|
#else
|
|
eint_work_callback();
|
|
#endif /* end of #if PMIC_ACCDET_KERNEL */
|
|
} else
|
|
pr_info("%s invalid EINT ID!\n", __func__);
|
|
|
|
#elif defined CONFIG_ACCDET_SUPPORT_EINT1
|
|
if (eintID == PMIC_EINT1) {
|
|
if (cur_eint_state == EINT_PIN_PLUG_IN) {
|
|
eint_debounce_set(eintID, ACCDET_EINT1_DEB_IN_256);
|
|
accdet_set_debounce(accdet_state110,
|
|
cust_pwm_deb->debounce3);
|
|
cur_eint_state = EINT_PIN_PLUG_OUT;
|
|
} else {
|
|
eint_debounce_set(eintID, ACCDET_EINT1_DEB_OUT_012);
|
|
cur_eint_state = EINT_PIN_PLUG_IN;
|
|
|
|
mod_timer(&micbias_timer,
|
|
jiffies + MICBIAS_DISABLE_TIMER);
|
|
}
|
|
#if PMIC_ACCDET_KERNEL
|
|
ret = queue_work(eint_workqueue, &eint_work);
|
|
#else
|
|
eint_work_callback();
|
|
#endif /* end of #if PMIC_ACCDET_KERNEL */
|
|
} else
|
|
pr_info("%s invalid EINT ID!\n", __func__);
|
|
|
|
#elif defined CONFIG_ACCDET_SUPPORT_BI_EINT
|
|
if ((eintID & PMIC_EINT0) == PMIC_EINT0) {
|
|
if (cur_eint0_state == EINT_PIN_PLUG_IN) {
|
|
eint_debounce_set(PMIC_EINT0,
|
|
ACCDET_EINT0_DEB_IN_256);
|
|
accdet_set_debounce(accdet_state110,
|
|
cust_pwm_deb->debounce3);
|
|
cur_eint0_state = EINT_PIN_PLUG_OUT;
|
|
} else {
|
|
eint_debounce_set(PMIC_EINT0,
|
|
ACCDET_EINT0_DEB_OUT_012);
|
|
cur_eint0_state = EINT_PIN_PLUG_IN;
|
|
}
|
|
}
|
|
if ((eintID & PMIC_EINT1) == PMIC_EINT1) {
|
|
if (cur_eint1_state == EINT_PIN_PLUG_IN) {
|
|
eint_debounce_set(PMIC_EINT1,
|
|
ACCDET_EINT1_DEB_IN_256);
|
|
accdet_set_debounce(accdet_state110,
|
|
cust_pwm_deb->debounce3);
|
|
cur_eint1_state = EINT_PIN_PLUG_OUT;
|
|
} else {
|
|
eint_debounce_set(PMIC_EINT1,
|
|
ACCDET_EINT1_DEB_OUT_012);
|
|
cur_eint1_state = EINT_PIN_PLUG_IN;
|
|
}
|
|
}
|
|
|
|
/* bi_eint trigger issued current state, may */
|
|
if (cur_eint_state == EINT_PIN_PLUG_OUT) {
|
|
cur_eint_state = cur_eint0_state & cur_eint1_state;
|
|
if (cur_eint_state == EINT_PIN_PLUG_IN) {
|
|
mod_timer(&micbias_timer,
|
|
jiffies + MICBIAS_DISABLE_TIMER);
|
|
ret = queue_work(eint_workqueue, &eint_work);
|
|
} else
|
|
pr_info("%s wait eint.now:eint0=%d;eint1=%d\n",
|
|
__func__, cur_eint0_state, cur_eint1_state);
|
|
} else if (cur_eint_state == EINT_PIN_PLUG_IN) {
|
|
if ((cur_eint0_state|cur_eint1_state) == EINT_PIN_PLUG_OUT) {
|
|
clear_accdet_eint_check(PMIC_EINT0);
|
|
clear_accdet_eint_check(PMIC_EINT1);
|
|
} else if ((cur_eint0_state & cur_eint1_state) ==
|
|
EINT_PIN_PLUG_OUT) {
|
|
cur_eint_state = EINT_PIN_PLUG_OUT;
|
|
ret = queue_work(eint_workqueue, &eint_work);
|
|
} else
|
|
pr_info("%s wait eint.now:eint0=%d;eint1=%d\n",
|
|
__func__, cur_eint0_state, cur_eint1_state);
|
|
}
|
|
#endif
|
|
|
|
return ret;
|
|
}
|
|
#endif /* end of #ifdef CONFIG_ACCDET_EINT_IRQ */
|
|
static u32 moisture_detect(void)
|
|
{
|
|
u32 moisture_vol = 0;
|
|
u32 tmp_1, tmp_2, tmp_3;
|
|
|
|
tmp_1 = pmic_read(ACCDET_RSV);
|
|
tmp_2 = pmic_read(AUDENC_ANA_CON10);
|
|
tmp_3 = pmic_read(AUDENC_ANA_CON11);
|
|
|
|
/* Disable ACCDET to AUXADC */
|
|
pmic_write(AUDENC_ANA_CON11, pmic_read(AUDENC_ANA_CON11) & 0x1FFF);
|
|
pmic_write(ACCDET_RSV, pmic_read(ACCDET_RSV) & 0xFBFF);
|
|
pmic_write(ACCDET_RSV, pmic_read(ACCDET_RSV) | 0x800);
|
|
|
|
/* Enable moisture detection, set 219A bit[13] = 1*/
|
|
pmic_write(AUDENC_ANA_CON10, pmic_read(AUDENC_ANA_CON10) | 0x2000);
|
|
|
|
/* select PAD_HP_EINT for moisture detection, set 219A bit[14] = 0*/
|
|
pmic_write(AUDENC_ANA_CON10, pmic_read(AUDENC_ANA_CON10) & 0xBFFF);
|
|
|
|
if (accdet_dts.moisture_use_ext_res == 0x0) {
|
|
/* select VTH to 2v and 500k, use internal resitance,
|
|
* 219C bit[10][11][12] = 1
|
|
*/
|
|
pmic_write(AUDENC_ANA_CON11,
|
|
pmic_read(AUDENC_ANA_CON11) | 0x1C00);
|
|
} else if (accdet_dts.moisture_use_ext_res == 0x1) {
|
|
/* select VTH to 2v and 500k, use external resitance
|
|
* set 219C bit[10] = 1, bit[11] [12]= 0
|
|
*/
|
|
pmic_write(AUDENC_ANA_CON11,
|
|
pmic_read(AUDENC_ANA_CON11) & 0xE7FF);
|
|
pmic_write(AUDENC_ANA_CON11,
|
|
pmic_read(AUDENC_ANA_CON11) | 0x0400);
|
|
}
|
|
moisture_vol = accdet_get_auxadc(0);
|
|
pr_info("%s accdet Moisture Read Auxadc=%d\n", __func__, moisture_vol);
|
|
|
|
/* reverse register setting after reading moisture voltage */
|
|
pmic_write(ACCDET_RSV, tmp_1);
|
|
pmic_write(AUDENC_ANA_CON10, tmp_2);
|
|
pmic_write(AUDENC_ANA_CON11, tmp_3);
|
|
|
|
return moisture_vol;
|
|
|
|
}
|
|
|
|
void accdet_irq_handle(void)
|
|
{
|
|
u32 eintID = 0;
|
|
u32 irq_status;
|
|
#ifdef CONFIG_ACCDET_EINT_IRQ
|
|
unsigned int moisture_vol = 0;
|
|
eintID = get_triggered_eint();
|
|
#endif
|
|
irq_status = pmic_read(ACCDET_IRQ_STS);
|
|
|
|
if ((irq_status & ACCDET_IRQ_B0) && (eintID == 0)) {
|
|
/* delete open cable timer if normal HP in */
|
|
del_timer_sync(&accdet_open_cable_timer);
|
|
clear_accdet_int();
|
|
accdet_queue_work();
|
|
clear_accdet_int_check();
|
|
#ifdef CONFIG_ACCDET_EINT_IRQ
|
|
} else if (eintID != NO_PMIC_EINT) {
|
|
pr_info("%s() IRQ:0x%x, eint-%s trig. cur_eint_state:%d\n",
|
|
__func__, irq_status,
|
|
(eintID == PMIC_EINT0)?"0":((eintID == PMIC_EINT1)?"1":"BI"),
|
|
cur_eint_state);
|
|
|
|
if (water_r != 0) {
|
|
if (cur_eint_state == EINT_PIN_MOISTURE_DETECTED) {
|
|
pr_info("%s Moisture plug out detectecd\n",
|
|
__func__);
|
|
eint_polarity_reverse(eintID);
|
|
cur_eint_state = EINT_PIN_PLUG_OUT;
|
|
clear_accdet_eint(eintID);
|
|
clear_accdet_eint_check(eintID);
|
|
return;
|
|
}
|
|
|
|
if (cur_eint_state == EINT_PIN_PLUG_OUT) {
|
|
pr_info("%s now check moisture\n", __func__);
|
|
moisture_vol = moisture_detect();
|
|
if (moisture_vol > moisture_vm) {
|
|
eint_polarity_reverse(eintID);
|
|
cur_eint_state = EINT_PIN_MOISTURE_DETECTED;
|
|
clear_accdet_eint(eintID);
|
|
clear_accdet_eint_check(eintID);
|
|
pr_info("%s Moisture plug in detectecd!\n",
|
|
__func__);
|
|
return;
|
|
}
|
|
pr_info("%s check moisture done,not water.\n",
|
|
__func__);
|
|
}
|
|
}
|
|
eint_polarity_reverse(eintID);
|
|
clear_accdet_eint(eintID);
|
|
clear_accdet_eint_check(eintID);
|
|
pmic_eint_queue_work(eintID);
|
|
#endif
|
|
} else
|
|
pr_info("%s no interrupt detected!\n", __func__);
|
|
#if PMIC_ACCDET_CTP || PMIC_ACCDET_DEBUG
|
|
dump_register();
|
|
#endif
|
|
}
|
|
|
|
static void accdet_int_handler(void)
|
|
{
|
|
pr_debug("%s()\n", __func__);
|
|
accdet_irq_handle();
|
|
}
|
|
|
|
#ifdef CONFIG_ACCDET_EINT_IRQ
|
|
static void accdet_eint_handler(void)
|
|
{
|
|
accdet_irq_handle();
|
|
pr_info("%s() exit\n", __func__);
|
|
}
|
|
#endif
|
|
|
|
static void check_open_cable_timerhandler(struct timer_list *t)
|
|
{
|
|
int ret;
|
|
|
|
ret = queue_work(accdet_workqueue, &accdet_work);
|
|
if (!ret)
|
|
pr_info("%s return:%d!\n", __func__, ret);
|
|
}
|
|
|
|
#ifdef CONFIG_ACCDET_EINT
|
|
static void check_moist_timerhandler(struct timer_list *t)
|
|
{
|
|
if (cur_eint_state == EINT_PIN_THING_IN)
|
|
cur_eint_state = EINT_PIN_MOISTURE_DETECTED;
|
|
pr_info("%s Moisture in detectecd!, cur_eint_state=%d\n",
|
|
__func__, cur_eint_state);
|
|
}
|
|
|
|
static irqreturn_t ex_eint_handler_00(int irq, void *data)
|
|
{
|
|
unsigned int moisture_vol = 0;
|
|
|
|
__pm_stay_awake(accdet_irq_lock);
|
|
|
|
/* moisture detection */
|
|
if (water_r != 0) {
|
|
if (cur_eint_state == EINT_PIN_MOISTURE_DETECTED) {
|
|
if (accdet_eint_type == IRQ_TYPE_LEVEL_HIGH) {
|
|
irq_set_irq_type(accdet_irq,
|
|
IRQ_TYPE_LEVEL_LOW);
|
|
} else {
|
|
irq_set_irq_type(accdet_irq,
|
|
IRQ_TYPE_LEVEL_HIGH);
|
|
}
|
|
pr_info("%s Moisture out detectecd\n", __func__);
|
|
cur_eint_state = EINT_PIN_PLUG_OUT;
|
|
__pm_relax(accdet_irq_lock);
|
|
return IRQ_HANDLED;
|
|
}
|
|
|
|
if (cur_eint_state == EINT_PIN_PLUG_OUT) {
|
|
pr_info("%s now check moisture\n", __func__);
|
|
moisture_vol = moisture_detect();
|
|
if (moisture_vol < l_det_th.hp_water) {
|
|
pr_info("%s normal plug in.\n", __func__);
|
|
} else if ((moisture_vol < l_det_th.water_open) &&
|
|
(moisture_vol >= l_det_th.hp_water)) {
|
|
pr_info("%s Moisture in detectecd!\n",
|
|
__func__);
|
|
if (accdet_eint_type == IRQ_TYPE_LEVEL_HIGH) {
|
|
irq_set_irq_type(accdet_irq,
|
|
IRQ_TYPE_LEVEL_LOW);
|
|
} else {
|
|
irq_set_irq_type(accdet_irq,
|
|
IRQ_TYPE_LEVEL_HIGH);
|
|
}
|
|
cur_eint_state = EINT_PIN_MOISTURE_DETECTED;
|
|
__pm_relax(accdet_irq_lock);
|
|
return IRQ_HANDLED;
|
|
} else if (moisture_vol >= l_det_th.water_open) {
|
|
/* pr_info("%s open state.\n", __func__); */
|
|
__pm_relax(accdet_irq_lock);
|
|
return IRQ_HANDLED;
|
|
}
|
|
}
|
|
}
|
|
if (cur_eint_state == EINT_PIN_PLUG_IN) {
|
|
/* To trigger EINT when the headset was plugged in
|
|
* We set the polarity back as we initialed.
|
|
*/
|
|
if (accdet_eint_type == IRQ_TYPE_LEVEL_HIGH)
|
|
irq_set_irq_type(accdet_irq, IRQ_TYPE_LEVEL_HIGH);
|
|
else
|
|
irq_set_irq_type(accdet_irq, IRQ_TYPE_LEVEL_LOW);
|
|
|
|
cur_eint_state = EINT_PIN_PLUG_OUT;
|
|
eint_work_callback();
|
|
|
|
gpio_set_debounce(gpiopin, gpio_plugin_deb);
|
|
} else {
|
|
/* To trigger EINT when the headset was plugged out
|
|
* We set the opposite polarity to what we initialed.
|
|
*/
|
|
if (accdet_eint_type == IRQ_TYPE_LEVEL_HIGH)
|
|
irq_set_irq_type(accdet_irq, IRQ_TYPE_LEVEL_LOW);
|
|
else
|
|
irq_set_irq_type(accdet_irq, IRQ_TYPE_LEVEL_HIGH);
|
|
|
|
cur_eint_state = EINT_PIN_PLUG_IN;
|
|
eint_work_callback();
|
|
mod_timer(&micbias_timer, jiffies + MICBIAS_DISABLE_TIMER);
|
|
|
|
gpio_set_debounce(gpiopin, gpio_plugout_deb);
|
|
}
|
|
|
|
pr_info("accdet %s(), cur_eint_state=%d\n", __func__, cur_eint_state);
|
|
__pm_relax(accdet_irq_lock);
|
|
|
|
return IRQ_HANDLED;
|
|
}
|
|
|
|
static irqreturn_t ex_eint_handler_01(int irq, void *data)
|
|
{
|
|
__pm_stay_awake(accdet_irq_lock);
|
|
mutex_lock(&accdet_moist_sync_mutex);
|
|
|
|
/* moisture detection */
|
|
if (water_r != 0) {
|
|
if ((cur_eint_state == EINT_PIN_MOISTURE_DETECTED) ||
|
|
(cur_eint_state == EINT_PIN_THING_IN)) {
|
|
if (accdet_eint_type == IRQ_TYPE_LEVEL_HIGH) {
|
|
irq_set_irq_type(accdet_irq,
|
|
IRQ_TYPE_LEVEL_HIGH);
|
|
} else {
|
|
irq_set_irq_type(accdet_irq,
|
|
IRQ_TYPE_LEVEL_LOW);
|
|
}
|
|
pr_info("%s Moisture out or open detectecd\n",
|
|
__func__);
|
|
del_timer_sync(&accdet_moist_timer);
|
|
cur_eint_state = EINT_PIN_PLUG_OUT;
|
|
} else if (pre_eint_state == EINT_PIN_PLUG_OUT) {
|
|
pr_info("%s now check moisture\n", __func__);
|
|
if (accdet_eint_type == IRQ_TYPE_LEVEL_HIGH) {
|
|
irq_set_irq_type(accdet_irq,
|
|
IRQ_TYPE_LEVEL_LOW);
|
|
} else {
|
|
irq_set_irq_type(accdet_irq,
|
|
IRQ_TYPE_LEVEL_HIGH);
|
|
}
|
|
/* adjust debounce for plug out */
|
|
gpio_set_debounce(gpiopin, gpio_plugout_deb);
|
|
mod_timer(&accdet_moist_timer,
|
|
jiffies + ACCDET_MOIST_TIMER);
|
|
cur_eint_state = EINT_PIN_THING_IN;
|
|
irq_set_irq_type(moist_irq, IRQ_TYPE_LEVEL_LOW);
|
|
pr_info("%s check moisture done, cur_eint_state=%d\n",
|
|
__func__, cur_eint_state);
|
|
} else if (pre_eint_state == EINT_PIN_PLUG_IN) {
|
|
pr_info("%s() wait to plugout, cur_eint_state=%d\n",
|
|
__func__, cur_eint_state);
|
|
/* To trigger EINT when the headset was plugged in
|
|
* We set the polarity back as we initialed.
|
|
*/
|
|
if (accdet_moist_eint_type == IRQ_TYPE_LEVEL_HIGH) {
|
|
irq_set_irq_type(moist_irq,
|
|
IRQ_TYPE_LEVEL_HIGH);
|
|
} else {
|
|
irq_set_irq_type(moist_irq,
|
|
IRQ_TYPE_LEVEL_LOW);
|
|
}
|
|
gpio_set_debounce(moist_gpiopin,
|
|
moist_gpio_plugin_deb);
|
|
|
|
cur_eint_state = EINT_PIN_PLUG_OUT;
|
|
|
|
eint_work_callback();
|
|
pr_info("accdet %s(), cur_eint_state=%d\n",
|
|
__func__, cur_eint_state);
|
|
if (accdet_eint_type == IRQ_TYPE_LEVEL_HIGH) {
|
|
irq_set_irq_type(accdet_irq,
|
|
IRQ_TYPE_LEVEL_HIGH);
|
|
} else {
|
|
irq_set_irq_type(accdet_irq,
|
|
IRQ_TYPE_LEVEL_LOW);
|
|
}
|
|
/* adjust debounce for plug in */
|
|
gpio_set_debounce(gpiopin, gpio_plugin_deb);
|
|
pre_eint_state = EINT_PIN_PLUG_OUT;
|
|
enable_irq(moist_irq);
|
|
pr_info("%s plug out detectecd.\n", __func__);
|
|
}
|
|
}
|
|
mutex_unlock(&accdet_moist_sync_mutex);
|
|
__pm_relax(accdet_irq_lock);
|
|
|
|
return IRQ_HANDLED;
|
|
}
|
|
|
|
static irqreturn_t ex_eint_handler(int irq, void *data)
|
|
{
|
|
if (moisture_ver != 0x1)
|
|
return ex_eint_handler_01(irq, data);
|
|
else
|
|
return ex_eint_handler_00(irq, data);
|
|
|
|
return IRQ_HANDLED;
|
|
}
|
|
|
|
static irqreturn_t ex_moisteint_handler(int irq, void *data)
|
|
{
|
|
__pm_stay_awake(accdet_irq_lock);
|
|
mutex_lock(&accdet_moist_sync_mutex);
|
|
|
|
if ((cur_eint_state == EINT_PIN_THING_IN) ||
|
|
(cur_eint_state == EINT_PIN_MOISTURE_DETECTED)) {
|
|
del_timer_sync(&accdet_moist_timer);
|
|
/* To trigger EINT when the headset was plugged out
|
|
* We set the opposite polarity to what we initialed.
|
|
*/
|
|
if (accdet_moist_eint_type == IRQ_TYPE_LEVEL_HIGH)
|
|
irq_set_irq_type(moist_irq, IRQ_TYPE_LEVEL_LOW);
|
|
else
|
|
irq_set_irq_type(moist_irq, IRQ_TYPE_LEVEL_HIGH);
|
|
|
|
gpio_set_debounce(moist_gpiopin,
|
|
gpio_moist_plugout_deb);
|
|
|
|
cur_eint_state = EINT_PIN_PLUG_IN;
|
|
pre_eint_state = cur_eint_state;
|
|
|
|
mod_timer(&micbias_timer, jiffies + MICBIAS_DISABLE_TIMER);
|
|
|
|
eint_work_callback();
|
|
disable_irq_nosync(moist_irq);
|
|
pr_info("accdet %s(), cur_eint_state=%d\n",
|
|
__func__, cur_eint_state);
|
|
} else if (cur_eint_state == EINT_PIN_PLUG_OUT) {
|
|
if (accdet_moist_eint_type == IRQ_TYPE_LEVEL_HIGH)
|
|
irq_set_irq_type(moist_irq, IRQ_TYPE_LEVEL_LOW);
|
|
else
|
|
irq_set_irq_type(moist_irq, IRQ_TYPE_LEVEL_HIGH);
|
|
pr_info("%s() wait thing in interrupt, cur_eint_state=%d\n",
|
|
__func__, cur_eint_state);
|
|
} else {
|
|
pr_notice("%s() invalid interrupt, cur_eint_state=%d\n",
|
|
__func__, cur_eint_state);
|
|
}
|
|
mutex_unlock(&accdet_moist_sync_mutex);
|
|
__pm_relax(accdet_irq_lock);
|
|
|
|
return IRQ_HANDLED;
|
|
}
|
|
|
|
static inline int ext_eint_setup(struct platform_device *platform_device)
|
|
{
|
|
int ret = 0;
|
|
u32 ints[8] = { 0 };
|
|
struct device_node *node = NULL;
|
|
struct pinctrl_state *pins_default = NULL;
|
|
|
|
pr_info("accdet %s()\n", __func__);
|
|
accdet_pinctrl = devm_pinctrl_get(&platform_device->dev);
|
|
if (IS_ERR(accdet_pinctrl)) {
|
|
ret = PTR_ERR(accdet_pinctrl);
|
|
dev_notice(&platform_device->dev,
|
|
"get accdet_pinctrl fail.\n");
|
|
return ret;
|
|
}
|
|
|
|
pins_default = pinctrl_lookup_state(accdet_pinctrl, "default");
|
|
if (IS_ERR(pins_default)) {
|
|
ret = PTR_ERR(pins_default);
|
|
dev_notice(&platform_device->dev,
|
|
"deflt pinctrl not found, skip it\n");
|
|
}
|
|
|
|
pins_eint = pinctrl_lookup_state(accdet_pinctrl, "state_eint_as_int");
|
|
if (IS_ERR(pins_eint)) {
|
|
ret = PTR_ERR(pins_eint);
|
|
dev_notice(&platform_device->dev, "lookup eint pinctrl fail\n");
|
|
return ret;
|
|
}
|
|
pinctrl_select_state(accdet_pinctrl, pins_eint);
|
|
|
|
node = of_find_matching_node(node, accdet_of_match);
|
|
if (!node) {
|
|
pr_notice("accdet %s can't find compatible node\n", __func__);
|
|
return -1;
|
|
}
|
|
|
|
gpiopin = of_get_named_gpio(node, "deb-gpios", 0);
|
|
ret = of_property_read_u32(node, "plugin-debounce", &gpio_plugin_deb);
|
|
if (ret < 0) {
|
|
pr_notice("accdet %s gpio plugin deb not found,ret:%d\n",
|
|
__func__, ret);
|
|
return ret;
|
|
}
|
|
ret = of_property_read_u32(node, "plugout-debounce", &gpio_plugout_deb);
|
|
if (ret < 0) {
|
|
pr_notice("accdet %s gpio plugout deb not found,ret:%d\n",
|
|
__func__, ret);
|
|
return ret;
|
|
}
|
|
gpio_set_debounce(gpiopin, gpio_plugin_deb);
|
|
|
|
accdet_irq = platform_get_irq(platform_device, 0);
|
|
if (moisture_ver != 0x1)
|
|
ret = of_property_read_u32_array(node, "interrupts", ints,
|
|
ARRAY_SIZE(ints));
|
|
else
|
|
ret = of_property_read_u32_array(node, "interrupts", ints, 4);
|
|
|
|
if (ret) {
|
|
pr_notice("accdet %s interrupts not found,ret:%d\n",
|
|
__func__, ret);
|
|
return ret;
|
|
}
|
|
accdet_eint_type = ints[1];
|
|
pr_info("accdet set gpio EINT, gpiopin=%d, accdet_eint_type=%d\n",
|
|
gpiopin, accdet_eint_type);
|
|
ret = devm_request_threaded_irq(&platform_device->dev, accdet_irq,
|
|
NULL, ex_eint_handler,
|
|
IRQF_TRIGGER_LOW | IRQF_ONESHOT, "hp-eint", NULL);
|
|
if (ret) {
|
|
pr_notice("accdet %s request_irq fail, ret:%d.\n", __func__,
|
|
ret);
|
|
return ret;
|
|
}
|
|
if (moisture_ver != 0x1) {
|
|
pins_moisteint = pinctrl_lookup_state(accdet_pinctrl,
|
|
"state_moisteint");
|
|
if (IS_ERR(pins_moisteint)) {
|
|
ret = PTR_ERR(pins_moisteint);
|
|
dev_notice(&platform_device->dev,
|
|
"lookup moisteint pinctrl fail\n");
|
|
return ret;
|
|
}
|
|
pinctrl_select_state(accdet_pinctrl, pins_moisteint);
|
|
|
|
moist_gpiopin = of_get_named_gpio(node, "moist-deb-gpios", 0);
|
|
ret = of_property_read_u32(node, "moist-plugin-debounce",
|
|
&moist_gpio_plugin_deb);
|
|
if (ret < 0) {
|
|
pr_notice("accdet %s moist in deb not found,ret:%d\n",
|
|
__func__, ret);
|
|
return ret;
|
|
}
|
|
ret = of_property_read_u32(node, "moist-plugout-debounce",
|
|
&gpio_moist_plugout_deb);
|
|
if (ret < 0) {
|
|
pr_notice("accdet %s moist out deb not found,ret:%d\n",
|
|
__func__, ret);
|
|
return ret;
|
|
}
|
|
gpio_set_debounce(moist_gpiopin, moist_gpio_plugin_deb);
|
|
moist_irq = platform_get_irq(platform_device, 1);
|
|
accdet_moist_eint_type = ints[5];
|
|
ret = devm_request_threaded_irq(&platform_device->dev,
|
|
moist_irq,
|
|
NULL, ex_moisteint_handler,
|
|
IRQF_TRIGGER_LOW | IRQF_ONESHOT,
|
|
"moist-eint", NULL);
|
|
if (ret) {
|
|
pr_notice("accdet_moist %s request_irq fail, ret:%d.\n",
|
|
__func__, ret);
|
|
return ret;
|
|
}
|
|
pr_info("accdet set gpio EINT finished. ");
|
|
pr_info("gpiopin=%d, accdet_eint_type=%d ",
|
|
gpiopin, accdet_eint_type);
|
|
pr_info("irq=%d, gpio_plugin_deb=%d, ",
|
|
accdet_irq, gpio_plugin_deb);
|
|
pr_info("moist_gpiopin=moist accdet_moist_eint_type=%d\n",
|
|
moist_gpiopin, accdet_moist_eint_type);
|
|
pr_info("moist_irq=%d, moist_gpio_plugin_deb=%d\n", moist_irq,
|
|
moist_gpio_plugin_deb);
|
|
} else {
|
|
pr_info("accdet set gpio EINT finished, irq=%d, gpio_deb=%d\n",
|
|
accdet_irq, gpio_plugin_deb);
|
|
}
|
|
return 0;
|
|
}
|
|
#endif
|
|
|
|
static int accdet_get_dts_data(void)
|
|
{
|
|
#ifdef CONFIG_MTK_PMIC_WRAP
|
|
struct device_node *tmpnode, *accdet_node;
|
|
#endif
|
|
#if PMIC_ACCDET_KERNEL
|
|
int ret;
|
|
struct device_node *node = NULL;
|
|
#if ACCDET_SW_MOISTURE_V1
|
|
struct device_node *rootnode = NULL;
|
|
#endif
|
|
int pwm_deb[8];
|
|
#ifdef CONFIG_FOUR_KEY_HEADSET
|
|
int four_key[5];
|
|
#else
|
|
int three_key[4];
|
|
#endif
|
|
#if NO_USE_COMPARATOR
|
|
unsigned int vol_thresh[5] = { 0 };
|
|
#endif
|
|
#if ACCDET_SW_MOISTURE_V1
|
|
unsigned int l_det_threshold[3] = { 0 };
|
|
#endif
|
|
|
|
pr_debug("%s\n", __func__);
|
|
#ifdef CONFIG_MTK_PMIC_WRAP
|
|
tmpnode = of_find_compatible_node(NULL, NULL, "mediatek,pwraph");
|
|
accdet_node = of_parse_phandle(tmpnode, "mediatek,pwrap-regmap", 0);
|
|
if (accdet_node) {
|
|
accdet_regmap = pwrap_node_to_regmap(accdet_node);
|
|
if (IS_ERR(accdet_regmap)) {
|
|
pr_notice("%s %d Error.\n", __func__, __LINE__);
|
|
return PTR_ERR(accdet_regmap);
|
|
}
|
|
} else {
|
|
pr_notice("%s %d Error.\n", __func__, __LINE__);
|
|
return -EINVAL;
|
|
}
|
|
#endif
|
|
#if ACCDET_SW_MOISTURE_V1
|
|
rootnode = of_find_node_by_path("/");
|
|
if (IS_ERR_OR_NULL(rootnode)) {
|
|
pr_notice("root dev node is NULL\n");
|
|
return -1;
|
|
}
|
|
ret = of_property_read_u32(rootnode, "dtbo-hw_rev", &hw_rev);
|
|
if (ret)
|
|
hw_rev = 0x2;
|
|
#endif
|
|
node = of_find_matching_node(node, accdet_of_match);
|
|
if (!node) {
|
|
pr_notice("%s can't find compatible dts node\n", __func__);
|
|
return -1;
|
|
}
|
|
|
|
ret = of_property_read_u32(node, "mediatek,accdet-pmic", &accdet_pmic);
|
|
if (ret)
|
|
accdet_pmic = 0x0;
|
|
|
|
ret = of_property_read_u32(node, "moisture-ver", &moisture_ver);
|
|
if (ret)
|
|
moisture_ver = 0x2;
|
|
ret = of_property_read_u32(node, "moisture-water-r", &water_r);
|
|
if (ret) {
|
|
/* no moisture detection */
|
|
water_r = 0x0;
|
|
}
|
|
ret = of_property_read_u32(node, "moisture_use_ext_res",
|
|
&accdet_dts.moisture_use_ext_res);
|
|
if (ret) {
|
|
/* no moisture detection */
|
|
accdet_dts.moisture_use_ext_res = -1;
|
|
}
|
|
if (accdet_dts.moisture_use_ext_res == 0x1) {
|
|
of_property_read_u32(node, "moisture-external-r",
|
|
&moisture_ext_r);
|
|
pr_info("Moisture_EXT support water_r=%d, ext_r=%d\n",
|
|
water_r, moisture_ext_r);
|
|
} else if (accdet_dts.moisture_use_ext_res == 0x0) {
|
|
of_property_read_u32(node, "moisture-internal-r",
|
|
&moisture_int_r);
|
|
pr_info("Moisture_INT support water_r=%d, int_r=%d\n",
|
|
water_r, moisture_int_r);
|
|
}
|
|
#if ACCDET_SW_MOISTURE_V1
|
|
ret = of_property_read_u32_array(node, "moisture-plug-in-threshold",
|
|
l_det_threshold, ARRAY_SIZE(l_det_threshold));
|
|
if (!ret)
|
|
memcpy(&l_det_th, l_det_threshold+1,
|
|
sizeof(struct moist_l_det_threshold));
|
|
else {
|
|
pr_info("accdet get moist plug in threshold fail\n");
|
|
}
|
|
#endif
|
|
ret = of_property_read_u32(node, "moisture-vm", &moisture_vm);
|
|
if (ret) {
|
|
/* moisture_vm threshold */
|
|
moisture_vm = 0x0;
|
|
}
|
|
of_property_read_u32(node, "accdet-mic-vol", &accdet_dts.mic_vol);
|
|
of_property_read_u32(node, "accdet-plugout-debounce",
|
|
&accdet_dts.plugout_deb);
|
|
of_property_read_u32(node, "accdet-mic-mode", &accdet_dts.mic_mode);
|
|
of_property_read_u32(node, "headset-eint-level-pol",
|
|
&accdet_dts.eint_pol);
|
|
|
|
pr_info("accdet mic_vol=%d, plugout_deb=%d mic_mode=%d eint_pol=%d\n",
|
|
accdet_dts.mic_vol, accdet_dts.plugout_deb,
|
|
accdet_dts.mic_mode, accdet_dts.eint_pol);
|
|
|
|
#ifdef CONFIG_FOUR_KEY_HEADSET
|
|
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_info("accdet get 4-key-thrsh dts fail, use efuse\n");
|
|
accdet_get_efuse_4key();
|
|
}
|
|
|
|
pr_info("accdet key thresh mid = %d, voice = %d, up = %d, dwn = %d\n",
|
|
accdet_dts.four_key.mid, accdet_dts.four_key.voice,
|
|
accdet_dts.four_key.up, accdet_dts.four_key.down);
|
|
#else
|
|
#ifdef CONFIG_HEADSET_TRI_KEY_CDD
|
|
ret = of_property_read_u32_array(node,
|
|
"headset-three-key-threshold-CDD", three_key,
|
|
ARRAY_SIZE(three_key));
|
|
#else
|
|
ret = of_property_read_u32_array(node, "headset-three-key-threshold",
|
|
three_key, ARRAY_SIZE(three_key));
|
|
#endif
|
|
if (!ret)
|
|
memcpy(&accdet_dts.three_key, three_key+1,
|
|
sizeof(struct three_key_threshold));
|
|
else
|
|
pr_info("accdet get 3-key-thrsh fail\n");
|
|
|
|
pr_info("accdet key thresh mid = %d, up = %d, down = %d\n",
|
|
accdet_dts.three_key.mid, accdet_dts.three_key.up,
|
|
accdet_dts.three_key.down);
|
|
#endif
|
|
|
|
#if NO_USE_COMPARATOR
|
|
ret = of_property_read_u32_array(node, "headset-vol-threshold",
|
|
vol_thresh, ARRAY_SIZE(vol_thresh));
|
|
if (!ret)
|
|
memcpy(&cust_vol_set, vol_thresh, sizeof(vol_thresh));
|
|
else
|
|
pr_info("accdet get headset-vol-thrsh fail\n");
|
|
|
|
pr_info("[Accdet] min_3pole = %d, max_3pole = %d\n",
|
|
cust_vol_set.vol_min_3pole, cust_vol_set.vol_max_3pole);
|
|
pr_info("[Accdet] min_4pole = %d, max_4pole = %d\n",
|
|
cust_vol_set.vol_min_4pole, cust_vol_set.vol_max_4pole);
|
|
if (cust_vol_set.vol_bias > 2600) {
|
|
cust_vol_set.vol_bias = 2600;/* 2600mv */
|
|
pr_notice("[Accdet]bias vol set %d mv--->2600 mv\n",
|
|
cust_vol_set.vol_bias);
|
|
} else {
|
|
pr_info("[Accdet]bias vol set %d mv\n", cust_vol_set.vol_bias);
|
|
}
|
|
#endif
|
|
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));
|
|
else
|
|
pr_info("accdet get pwm-debounce setting fail\n");
|
|
|
|
/* for discharge:0xB00 about 86ms */
|
|
button_press_debounce = (accdet_dts.pwm_deb.debounce0 >> 1);
|
|
cust_pwm_deb = &accdet_dts.pwm_deb;
|
|
#else
|
|
accdet_dts.mic_vol = mic_vol;
|
|
accdet_dts.mic_mode = mic_mode;
|
|
accdet_dts.three_key.mid = 49;
|
|
accdet_dts.three_key.up = 220;
|
|
accdet_dts.three_key.down = 600;
|
|
accdet_dts.pwm_deb.pwm_width = 0x500;
|
|
accdet_dts.pwm_deb.pwm_thresh = 0x500;
|
|
accdet_dts.pwm_deb.fall_delay = 0x1;
|
|
accdet_dts.pwm_deb.rise_delay = 0x1f0;
|
|
accdet_dts.pwm_deb.debounce0 = 0x800;
|
|
accdet_dts.pwm_deb.debounce1 = 0x800;
|
|
accdet_dts.pwm_deb.debounce3 = 0x20;
|
|
accdet_dts.pwm_deb.debounce4 = 0x44;
|
|
accdet_dts.pwm_deb.eint_pwm_width = eint_pwm_width;/* 0x4; */
|
|
accdet_dts.pwm_deb.eint_pwm_thresh = eint_pwm_thresh;/* 0x1; */
|
|
/*default:0xe*/
|
|
accdet_dts.pwm_deb.eint_debounce0 = adjust_eint_debounce03;
|
|
accdet_dts.pwm_deb.eint_debounce1 = adjust_eint_debounce12;
|
|
accdet_dts.pwm_deb.eint_debounce2 = adjust_eint_debounce12;
|
|
/* default:0xe */
|
|
accdet_dts.pwm_deb.eint_debounce3 = adjust_eint_debounce03;
|
|
accdet_dts.pwm_deb.eint_inverter_debounce = eint_invert_debounce_index;
|
|
/* if we need moisture detection feature or not */
|
|
accdet_dts.moisture_detect_enable = moisture_detect_enable;
|
|
/* select moisture detection mode,
|
|
* 1: EINT 1.0, 2: EINT1.1, 3: EINT2.0, 4: EINT2.1, 5: EINT2.1_OPPO
|
|
*/
|
|
if (accdet_dts.moisture_detect_enable == 0x1) {
|
|
accdet_dts.eint_detect_mode = eint_detect_mode;
|
|
accdet_dts.moisture_detect_mode = moisture_detect_mode;
|
|
} else {
|
|
accdet_dts.eint_detect_mode = eint_detect_mode;
|
|
accdet_dts.moisture_detect_mode = moisture_detect_mode;
|
|
}
|
|
if ((accdet_dts.moisture_detect_enable == 0x1) &&
|
|
(accdet_dts.moisture_detect_mode !=
|
|
accdet_dts.eint_detect_mode)) {
|
|
pr_info("DTS setting error, eint mode != moisture mode\n\r");
|
|
}
|
|
accdet_dts.eint_use_ext_res = eint_use_ext_res;
|
|
accdet_dts.moisture_comp_vth = moisture_comp_vth; /* default 2.8v */
|
|
/* default 880mv */
|
|
accdet_dts.moisture_comp_vref2 = moisture_comp_vref2;
|
|
/* use internal resister */
|
|
accdet_dts.moisture_use_ext_res = moisture_use_ext_res;
|
|
water_r = water_r_t;
|
|
moisture_ext_r = moisture_ext_r_t;
|
|
moisture_int_r = moisture_int_r;
|
|
cust_pwm_deb = &accdet_dts.pwm_deb;
|
|
|
|
cust_pwm_deb->debounce0 = debounce0_test[debounce_index];
|
|
cust_pwm_deb->debounce1 = debounce1_test[debounce_index];
|
|
cust_pwm_deb->debounce3 = debounce3_test[debounce_index];
|
|
cust_pwm_deb->debounce4 = debounce4_test[debounce_index];
|
|
|
|
#endif /* end of #if PMIC_ACCDET_KERNEL */
|
|
pr_info("accdet pwm_width=0x%x, thresh=0x%x, fall=0x%x, rise=0x%x\n",
|
|
cust_pwm_deb->pwm_width, cust_pwm_deb->pwm_thresh,
|
|
cust_pwm_deb->fall_delay, cust_pwm_deb->rise_delay);
|
|
pr_info("deb0=0x%x, deb1=0x%x, deb3=0x%x, deb4=0x%x\n",
|
|
cust_pwm_deb->debounce0, cust_pwm_deb->debounce1,
|
|
cust_pwm_deb->debounce3, cust_pwm_deb->debounce4);
|
|
pr_info("e_pwm_width=0x%x, e_pwm_thresh=0x%x\n",
|
|
cust_pwm_deb->eint_pwm_width, cust_pwm_deb->eint_pwm_thresh);
|
|
pr_info("e_deb0=0x%x, deb1=0x%x, deb2=0x%x, deb3=0x%x\n",
|
|
cust_pwm_deb->eint_debounce0, cust_pwm_deb->eint_debounce1,
|
|
cust_pwm_deb->eint_debounce2, cust_pwm_deb->eint_debounce3);
|
|
pr_info("e_inv_deb=0x%x, mdet_en=0x%x, e_det_m=0x%x, m_det_m=0x%x\n",
|
|
cust_pwm_deb->eint_inverter_debounce,
|
|
accdet_dts.moisture_detect_enable,
|
|
accdet_dts.eint_detect_mode,
|
|
accdet_dts.moisture_detect_mode);
|
|
pr_info("m_vth=0x%x, m_vref2=0x%x, e_e_res=0x%x, m_e_res=0x%x\n",
|
|
accdet_dts.moisture_comp_vth,
|
|
accdet_dts.moisture_comp_vref2,
|
|
accdet_dts.eint_use_ext_res,
|
|
accdet_dts.moisture_use_ext_res);
|
|
|
|
return 0;
|
|
}
|
|
|
|
static inline void accdet_eint_high_level_support(void)
|
|
{
|
|
unsigned int reg_val = 0;
|
|
|
|
reg_val = 0;
|
|
pr_debug("[accdet]eint_high_level_support enter--->\n");
|
|
|
|
/* set high level trigger */
|
|
#ifdef CONFIG_ACCDET_EINT_IRQ
|
|
#ifdef CONFIG_ACCDET_SUPPORT_EINT0
|
|
if (cur_eint_state == EINT_PIN_PLUG_OUT &&
|
|
accdet_dts.eint_pol == IRQ_TYPE_LEVEL_HIGH) {
|
|
reg_val = pmic_read(ACCDET_IRQ_STS);
|
|
pr_info("[accdet]pol = %d\n", accdet_dts.eint_pol);
|
|
accdet_eint_type = IRQ_TYPE_LEVEL_HIGH;
|
|
pmic_write(ACCDET_IRQ_STS, reg_val|
|
|
ACCDET_EINT0_IRQ_POL_B14);
|
|
|
|
reg_val = pmic_read(ACCDET_CTRL);
|
|
/* set bit3 to enable default EINT init status */
|
|
pmic_write(ACCDET_CTRL, reg_val|
|
|
ACCDET_EINT0_SEQ_INIT_EN_B3);
|
|
mdelay(1); /* please do not change usleep_range(2000, 3000); */
|
|
reg_val = pmic_read(ACCDET_CTRL);
|
|
reg_val = pmic_read(ACCDET_DEFAULT_STATE_RG);
|
|
/* set default EINT init status */
|
|
pmic_write(ACCDET_DEFAULT_STATE_RG,
|
|
(reg_val|ACCDET_EINT0_IVAL_SEL_B14)&
|
|
(~ACCDET_EINT0_IVAL_B2_6_10));
|
|
mdelay(1); /* please do not change usleep_range(2000, 3000); */
|
|
reg_val = pmic_read(ACCDET_DEFAULT_STATE_RG);
|
|
/* clear bit3 to disable default EINT init status */
|
|
reg_val = pmic_read(ACCDET_CTRL);
|
|
pmic_write(ACCDET_CTRL, reg_val&
|
|
(~ACCDET_EINT0_SEQ_INIT_EN_B3));
|
|
reg_val = pmic_read(ACCDET_EINT0_CTL)&(~(0x0F<<3));
|
|
pmic_write(ACCDET_EINT0_CTL, reg_val);
|
|
pmic_write(ACCDET_EINT0_CTL, reg_val|
|
|
ACCDET_EINT0_DEB_IN_256);
|
|
}
|
|
#endif
|
|
#endif
|
|
}
|
|
|
|
|
|
static void accdet_init_once(void)
|
|
{
|
|
unsigned int reg = 0;
|
|
|
|
/* reset the accdet unit */
|
|
pmic_write(AUD_TOP_RST_CON0, RG_ACCDET_RST_B1);
|
|
pmic_write(AUD_TOP_RST_CON0,
|
|
pmic_read(AUD_TOP_RST_CON0)&(~RG_ACCDET_RST_B1));
|
|
|
|
|
|
/* init pwm frequency, duty & rise/falling delay */
|
|
pmic_write(ACCDET_PWM_WIDTH, REGISTER_VAL(cust_pwm_deb->pwm_width));
|
|
pmic_write(ACCDET_PWM_THRESH, REGISTER_VAL(cust_pwm_deb->pwm_thresh));
|
|
pmic_write(ACCDET_EN_DELAY_NUM,
|
|
(cust_pwm_deb->fall_delay << 15 | cust_pwm_deb->rise_delay));
|
|
|
|
/* config micbias voltage */
|
|
reg = pmic_read(AUDENC_ANA_CON10);
|
|
pmic_write(AUDENC_ANA_CON10,
|
|
reg | (accdet_dts.mic_vol<<4) | RG_AUD_MICBIAS1_LOWP_EN);
|
|
|
|
/* mic mode setting */
|
|
reg = pmic_read(AUDENC_ANA_CON11);
|
|
/* ACC mode*/
|
|
if (accdet_dts.mic_mode == HEADSET_MODE_1)
|
|
pmic_write(AUDENC_ANA_CON11,
|
|
reg | RG_ACCDET_MODE_ANA11_MODE1);
|
|
/* Low cost mode without internal bias*/
|
|
else if (accdet_dts.mic_mode == HEADSET_MODE_2)
|
|
pmic_write(AUDENC_ANA_CON11,
|
|
reg | RG_ACCDET_MODE_ANA11_MODE2);
|
|
/* Low cost mode with internal bias, bit8 = 1 to use internal bias */
|
|
else if (accdet_dts.mic_mode == HEADSET_MODE_6) {
|
|
pmic_write(AUDENC_ANA_CON11,
|
|
reg | RG_ACCDET_MODE_ANA11_MODE6);
|
|
pmic_write(AUDENC_ANA_CON10,
|
|
pmic_read(AUDENC_ANA_CON10) | RG_AUDMICBIAS1_DCSW1PEN);
|
|
}
|
|
|
|
/* sw trigger auxadc, disable auxadc auto sample */
|
|
/* pmic_write(AUXADC_ACCDET,
|
|
* pmic_read(AUXADC_ACCDET) | AUXADC_ACCDET_AUTO_SPL_EN);
|
|
*/
|
|
|
|
#ifdef ANALOG_FASTDISCHARGE_SUPPORT
|
|
if (accdet_dts.eint_pol == IRQ_TYPE_LEVEL_LOW) {
|
|
reg = pmic_read(AUDENC_ANA_CON6) |
|
|
RG_AUDSPARE_FSTDSCHRG_IMPR_EN |
|
|
RG_AUDSPARE_FSTDSCHRG_ANALOG_DIR_EN;
|
|
pmic_write(AUDENC_ANA_CON6, reg);
|
|
} else
|
|
pmic_write(AUDENC_ANA_CON6,
|
|
pmic_read(AUDENC_ANA_CON6) & (0xE0));
|
|
#endif
|
|
|
|
/* hw mode config , disable accdet */
|
|
#ifdef HW_MODE_SUPPORT
|
|
pmic_write(ACCDET_CTRL, pmic_read(ACCDET_CTRL)&(~ACCDET_ENABLE_B0));
|
|
|
|
#ifdef CONFIG_ACCDET_EINT_IRQ
|
|
reg = pmic_read(ACCDET_HW_MODE_DFF);
|
|
#ifdef CONFIG_ACCDET_SUPPORT_EINT0
|
|
reg |= ACCDET_HWEN_SEL_0 | ACCDET_HWMODE_SEL |
|
|
ACCDET_EINT_DEB_OUT_DFF | ACCDET_EINIT_REVERSE;
|
|
|
|
#elif defined CONFIG_ACCDET_SUPPORT_EINT1
|
|
reg |= ACCDET_HWEN_SEL_1 | ACCDET_HWMODE_SEL |
|
|
ACCDET_EINT_DEB_OUT_DFF | ACCDET_EINIT_REVERSE;
|
|
#elif defined CONFIG_ACCDET_SUPPORT_BI_EINT
|
|
reg |= ACCDET_HWEN_SEL_0_AND_1 | ACCDET_HWMODE_SEL |
|
|
ACCDET_EINT_DEB_OUT_DFF | ACCDET_EINIT_REVERSE;
|
|
#endif
|
|
pmic_write(ACCDET_HW_MODE_DFF, reg);
|
|
#endif
|
|
#else
|
|
/* sw mode, */
|
|
reg = pmic_read(ACCDET_HW_MODE_DFF);
|
|
pmic_write(ACCDET_HW_MODE_DFF,
|
|
(reg & (~ACCDET_HWMODE_SEL))|ACCDET_FAST_DISCAHRGE);
|
|
#endif
|
|
|
|
#ifdef CONFIG_ACCDET_EINT_IRQ
|
|
#ifdef CONFIG_ACCDET_SUPPORT_EINT0
|
|
/* set eint0 pwm width&thresh, and enable eint0 PWM */
|
|
reg = pmic_read(ACCDET_EINT0_CTL);
|
|
reg &= ~(ACCDET_EINT0_PWM_THRSH_MASK | ACCDET_EINT0_PWM_WIDTH_MASK);
|
|
pmic_write(ACCDET_EINT0_CTL, reg);
|
|
|
|
reg = pmic_read(ACCDET_EINT0_CTL);
|
|
pmic_write(ACCDET_EINT0_CTL,
|
|
reg | ACCDET_EINT0_PWM_THRSH | ACCDET_EINT0_PWM_WIDTH);
|
|
|
|
reg = pmic_read(ACCDET_STATE_SWCTRL);
|
|
pmic_write(ACCDET_STATE_SWCTRL,
|
|
reg | ACCDET_EINT0_PWM_EN_B3 | ACCDET_EINT0_PWM_IDLE_B11);
|
|
|
|
/* open accdet interrupt eint0 */
|
|
#if defined CONFIG_MTK_PMIC_WRAP_HAL
|
|
pwrap_write(ACCDET_CTRL, pmic_read(ACCDET_CTRL) | ACCDET_EINT0_EN_B2);
|
|
#endif
|
|
if (water_r == 0) {
|
|
/* For normal mode, select VTH to 2v and 500k, use internal resitance,
|
|
* 219C bit[10][11][12] = 1
|
|
*/
|
|
pmic_write(AUDENC_ANA_CON11,
|
|
pmic_read(AUDENC_ANA_CON11) | 0x1C00);
|
|
pr_info("%s: register AUDENC_ANA_CON11%x=%x",
|
|
__func__, AUDENC_ANA_CON11, pmic_read(AUDENC_ANA_CON11));
|
|
}
|
|
#elif defined CONFIG_ACCDET_SUPPORT_EINT1
|
|
/* set eint0 pwm width&thresh, and enable eint0 PWM */
|
|
reg = pmic_read(ACCDET_EINT1_CTL);
|
|
reg &= ~(ACCDET_EINT1_PWM_THRSH_MASK | ACCDET_EINT1_PWM_WIDTH_MASK);
|
|
pmic_write(ACCDET_EINT1_CTL, reg);
|
|
|
|
reg = pmic_read(ACCDET_EINT1_CTL);
|
|
pmic_write(ACCDET_EINT1_CTL,
|
|
reg | ACCDET_EINT1_PWM_THRSH | ACCDET_EINT1_PWM_WIDTH);
|
|
|
|
reg = pmic_read(ACCDET_STATE_SWCTRL);
|
|
pmic_write(ACCDET_STATE_SWCTRL,
|
|
reg | ACCDET_EINT1_PWM_EN_B4 | ACCDET_EINT1_PWM_IDLE_B12);
|
|
|
|
/* open accdet interrupt eint1 */
|
|
pwrap_write(ACCDET_CTRL,
|
|
pmic_read(ACCDET_CTRL) | ACCDET_EINT1_EN_B4);
|
|
|
|
#elif defined CONFIG_ACCDET_SUPPORT_BI_EINT
|
|
reg = pmic_read(ACCDET_EINT0_CTL);
|
|
reg &= ~(ACCDET_EINT0_PWM_THRSH_MASK | ACCDET_EINT0_PWM_WIDTH_MASK);
|
|
pmic_write(ACCDET_EINT0_CTL, reg);
|
|
reg = pmic_read(ACCDET_EINT0_CTL);
|
|
pmic_write(ACCDET_EINT0_CTL,
|
|
reg | ACCDET_EINT0_PWM_THRSH | ACCDET_EINT0_PWM_WIDTH);
|
|
|
|
reg = pmic_read(ACCDET_EINT1_CTL);
|
|
reg &= ~(ACCDET_EINT1_PWM_THRSH_MASK | ACCDET_EINT1_PWM_WIDTH_MASK);
|
|
pmic_write(ACCDET_EINT1_CTL, reg);
|
|
reg = pmic_read(ACCDET_EINT1_CTL);
|
|
pmic_write(ACCDET_EINT1_CTL,
|
|
reg | ACCDET_EINT1_PWM_THRSH | ACCDET_EINT1_PWM_WIDTH);
|
|
|
|
reg = pmic_read(ACCDET_STATE_SWCTRL);
|
|
pmic_write(ACCDET_STATE_SWCTRL,
|
|
reg | ACCDET_EINT_PWM_IDLE_B11_12 | ACCDET_EINT_PWM_EN_B3_4);
|
|
|
|
/* open accdet interrupt eint0&eint1 */
|
|
pwrap_write(ACCDET_CTRL,
|
|
pmic_read(ACCDET_CTRL) | ACCDET_EINT_EN_B2_4);
|
|
#endif
|
|
accdet_eint_high_level_support();
|
|
/* extend plug in debounce time to 512ms, default is 256ms */
|
|
eint_debounce_set(PMIC_EINT0, ACCDET_EINT0_DEB_512);
|
|
#endif
|
|
pr_info("%s() done.\n", __func__);
|
|
#if PMIC_ACCDET_DEBUG
|
|
dump_register();
|
|
#endif
|
|
}
|
|
|
|
static inline void accdet_init(void)
|
|
{
|
|
/* add new of DE for fix icon cann't appear */
|
|
#if PMIC_ACCDET_KERNEL
|
|
/* set and clear initial bit every eint interrutp */
|
|
pmic_write(ACCDET_CTRL, pmic_read(ACCDET_CTRL)|ACCDET_SEQ_INIT_EN_B1);
|
|
mdelay(2); /* please do not change usleep_range(2000, 3000); */
|
|
pmic_write(ACCDET_CTRL,
|
|
pmic_read(ACCDET_CTRL)&(~ACCDET_SEQ_INIT_EN_B1));
|
|
mdelay(1); /* please do not change usleep_range(1000, 1500); */
|
|
#endif
|
|
/* init the debounce time (debounce/32768)sec */
|
|
accdet_set_debounce(accdet_state000, cust_pwm_deb->debounce0);
|
|
accdet_set_debounce(accdet_state010, cust_pwm_deb->debounce1);
|
|
accdet_set_debounce(accdet_state110, cust_pwm_deb->debounce3);
|
|
/* auxadc:2ms */
|
|
accdet_set_debounce(accdet_auxadc, cust_pwm_deb->debounce4);
|
|
|
|
#ifdef HW_MODE_SUPPORT
|
|
#ifdef DIGITAL_FASTDISCHARGE_SUPPORT
|
|
/* workround for HW fast discharge, first disabel fast discharge */
|
|
pmic_write(ACCDET_HW_MODE_DFF, ACCDET_FAST_DISCAHRGE_DIS);
|
|
fast_discharge = false;
|
|
#endif
|
|
#endif
|
|
pr_info("%s() done.\n", __func__);
|
|
}
|
|
|
|
/* late init for DC trim, and this API Will be called by audio */
|
|
void accdet_late_init(unsigned long data)
|
|
{
|
|
pr_info("%s() now init accdet!\n", __func__);
|
|
#if PMIC_ACCDET_KERNEL
|
|
if (atomic_cmpxchg(&accdet_first, 1, 0)) {
|
|
del_timer_sync(&accdet_init_timer);
|
|
#else
|
|
if (true) {
|
|
accdet_get_dts_data();
|
|
accdet_get_efuse();
|
|
#endif
|
|
accdet_init();
|
|
/* just need run once */
|
|
accdet_init_once();
|
|
} else
|
|
pr_info("%s inited dts fail\n", __func__);
|
|
}
|
|
#if PMIC_ACCDET_KERNEL
|
|
EXPORT_SYMBOL(accdet_late_init);
|
|
|
|
static void delay_init_timerhandler(struct timer_list *t)
|
|
{
|
|
pr_info("%s() now init accdet!\n", __func__);
|
|
if (atomic_cmpxchg(&accdet_first, 1, 0)) {
|
|
accdet_init();
|
|
accdet_init_once();
|
|
} else
|
|
pr_info("%s inited dts fail\n", __func__);
|
|
}
|
|
|
|
int mt_accdet_probe(struct platform_device *dev)
|
|
{
|
|
int ret;
|
|
struct platform_driver accdet_driver_hal = accdet_driver_func();
|
|
|
|
pr_info("%s() begin!\n", __func__);
|
|
|
|
/* register char device number, Create normal device for auido use */
|
|
ret = alloc_chrdev_region(&accdet_devno, 0, 1, ACCDET_DEVNAME);
|
|
if (ret) {
|
|
pr_notice("%s alloc_chrdev_reg fail,ret:%d!\n", __func__, ret);
|
|
goto err_chrdevregion;
|
|
}
|
|
|
|
/* init cdev, and add it to system */
|
|
accdet_cdev = cdev_alloc();
|
|
accdet_cdev->owner = THIS_MODULE;
|
|
accdet_cdev->ops = accdet_get_fops();
|
|
ret = cdev_add(accdet_cdev, accdet_devno, 1);
|
|
if (ret) {
|
|
pr_notice("%s cdev_add fail.ret:%d\n", __func__, ret);
|
|
goto err_cdev_add;
|
|
}
|
|
|
|
/* create class in sysfs, "sys/class/", so udev in userspace can create
|
|
*device node, when device_create is called
|
|
*/
|
|
accdet_class = class_create(THIS_MODULE, ACCDET_DEVNAME);
|
|
if (!accdet_class) {
|
|
ret = -1;
|
|
pr_notice("%s class_create fail.\n", __func__);
|
|
goto err_class_create;
|
|
}
|
|
|
|
/* create device under /dev node
|
|
* if we want auto creat device node, we must call this
|
|
*/
|
|
accdet_device = device_create(accdet_class, NULL, accdet_devno,
|
|
NULL, ACCDET_DEVNAME);
|
|
if (!accdet_device) {
|
|
ret = -1;
|
|
pr_notice("%s device_create fail.\n", __func__);
|
|
goto err_device_create;
|
|
}
|
|
|
|
/* Create input device*/
|
|
accdet_input_dev = input_allocate_device();
|
|
if (!accdet_input_dev) {
|
|
ret = -ENOMEM;
|
|
pr_notice("%s input_allocate_device fail.\n", __func__);
|
|
goto err_input_alloc;
|
|
}
|
|
|
|
__set_bit(EV_KEY, accdet_input_dev->evbit);
|
|
__set_bit(KEY_MEDIA, accdet_input_dev->keybit);
|
|
__set_bit(KEY_VOLUMEDOWN, accdet_input_dev->keybit);
|
|
__set_bit(KEY_VOLUMEUP, accdet_input_dev->keybit);
|
|
__set_bit(KEY_VOICECOMMAND, accdet_input_dev->keybit);
|
|
|
|
__set_bit(EV_SW, accdet_input_dev->evbit);
|
|
__set_bit(SW_HEADPHONE_INSERT, accdet_input_dev->swbit);
|
|
__set_bit(SW_MICROPHONE_INSERT, accdet_input_dev->swbit);
|
|
__set_bit(SW_JACK_PHYSICAL_INSERT, accdet_input_dev->swbit);
|
|
__set_bit(SW_LINEOUT_INSERT, accdet_input_dev->swbit);
|
|
|
|
accdet_input_dev->id.bustype = BUS_HOST;
|
|
accdet_input_dev->name = "ACCDET";
|
|
ret = input_register_device(accdet_input_dev);
|
|
if (ret) {
|
|
pr_notice("%s input_register_device fail.ret:%d\n", __func__,
|
|
ret);
|
|
goto err_input_reg;
|
|
}
|
|
|
|
ret = accdet_create_attr(&accdet_driver_hal.driver);
|
|
if (ret) {
|
|
pr_notice("%s create_attr fail, ret = %d\n", __func__, ret);
|
|
goto err_create_attr;
|
|
}
|
|
|
|
/* modify timer api for kernel 4.19 */
|
|
timer_setup(&micbias_timer, dis_micbias_timerhandler, 0);
|
|
timer_setup(&accdet_init_timer, delay_init_timerhandler, 0);
|
|
micbias_timer.expires = jiffies + MICBIAS_DISABLE_TIMER;
|
|
accdet_init_timer.expires = jiffies + ACCDET_INIT_WAIT_TIMER;
|
|
#ifdef CONFIG_ACCDET_EINT
|
|
timer_setup(&accdet_moist_timer, check_moist_timerhandler, 0);
|
|
accdet_moist_timer.expires = jiffies + ACCDET_MOIST_TIMER;
|
|
#endif
|
|
timer_setup(&accdet_open_cable_timer,
|
|
check_open_cable_timerhandler, 0);
|
|
accdet_open_cable_timer.expires = jiffies + ACCDET_OPEN_CABLE_TIMER;
|
|
/* the third argument may include TIMER_* flags */
|
|
|
|
/* wake lock */
|
|
accdet_irq_lock = wakeup_source_register(NULL, "accdet_irq_lock");
|
|
if (!accdet_irq_lock)
|
|
return -ENOMEM;
|
|
accdet_timer_lock = wakeup_source_register(NULL, "accdet_timer_lock");
|
|
if (!accdet_timer_lock)
|
|
return -ENOMEM;
|
|
|
|
/* Create workqueue */
|
|
accdet_workqueue = create_singlethread_workqueue("accdet");
|
|
INIT_WORK(&accdet_work, accdet_work_callback);
|
|
if (!accdet_workqueue) {
|
|
ret = -1;
|
|
pr_notice("%s create accdet workqueue fail.\n", __func__);
|
|
goto err_create_attr;
|
|
}
|
|
|
|
/* register pmic interrupt */
|
|
pmic_register_interrupt_callback(INT_ACCDET, accdet_int_handler);
|
|
#ifdef CONFIG_ACCDET_EINT_IRQ
|
|
#ifdef CONFIG_ACCDET_SUPPORT_EINT0
|
|
pmic_register_interrupt_callback(INT_ACCDET_EINT0,
|
|
accdet_eint_handler);
|
|
#elif defined CONFIG_ACCDET_SUPPORT_EINT1
|
|
pmic_register_interrupt_callback(INT_ACCDET_EINT1,
|
|
accdet_eint_handler);
|
|
#elif defined CONFIG_ACCDET_BI_EINT
|
|
pmic_register_interrupt_callback(INT_ACCDET_EINT0,
|
|
accdet_eint_handler);
|
|
pmic_register_interrupt_callback(INT_ACCDET_EINT1,
|
|
accdet_eint_handler);
|
|
#endif
|
|
#endif
|
|
|
|
ret = accdet_get_dts_data();
|
|
if (ret) {
|
|
atomic_set(&accdet_first, 0);
|
|
pr_notice("%s accdet_get_dts_data err!\n", __func__);
|
|
goto err;
|
|
}
|
|
dis_micbias_workqueue = create_singlethread_workqueue("dismicQueue");
|
|
INIT_WORK(&dis_micbias_work, dis_micbias_work_callback);
|
|
if (!dis_micbias_workqueue) {
|
|
ret = -1;
|
|
pr_notice("%s create dis micbias workqueue fail.\n", __func__);
|
|
goto err;
|
|
}
|
|
#ifdef CONFIG_ACCDET_EINT_IRQ
|
|
eint_workqueue = create_singlethread_workqueue("accdet_eint");
|
|
INIT_WORK(&eint_work, eint_work_callback);
|
|
if (!eint_workqueue) {
|
|
ret = -1;
|
|
pr_notice("%s create eint workqueue fail.\n", __func__);
|
|
goto err_create_workqueue;
|
|
}
|
|
#endif
|
|
#ifdef CONFIG_ACCDET_EINT
|
|
ret = ext_eint_setup(dev);
|
|
if (ret) {
|
|
pr_notice("%s ap eint setup fail.ret:%d\n", __func__, ret);
|
|
goto err_eint_setup;
|
|
}
|
|
#endif
|
|
atomic_set(&accdet_first, 1);
|
|
mod_timer(&accdet_init_timer, (jiffies + ACCDET_INIT_WAIT_TIMER));
|
|
|
|
accdet_get_efuse();
|
|
|
|
/* open top accdet interrupt */
|
|
pmic_enable_interrupt(INT_ACCDET, 1, "ACCDET");
|
|
#ifdef CONFIG_ACCDET_EINT_IRQ
|
|
#ifdef CONFIG_ACCDET_SUPPORT_EINT0
|
|
/* open top interrupt eint0 */
|
|
pmic_enable_interrupt(INT_ACCDET_EINT0, 1, "ACCDET_EINT0");
|
|
#elif defined CONFIG_ACCDET_SUPPORT_EINT1
|
|
/* open top interrupt eint1 */
|
|
pmic_enable_interrupt(INT_ACCDET_EINT1, 1, "ACCDET_EINT1");
|
|
#elif defined CONFIG_ACCDET_SUPPORT_BI_EINT
|
|
/* open top interrupt eint0 & eint1 */
|
|
pmic_enable_interrupt(INT_ACCDET_EINT0, 1, "ACCDET_EINT0");
|
|
pmic_enable_interrupt(INT_ACCDET_EINT1, 1, "ACCDET_EINT1");
|
|
#endif
|
|
#endif
|
|
|
|
#ifdef CONFIG_SND_SOC_SAMSUNG_AUDIO
|
|
register_accdet_jack_cb(&accdet_pdata);
|
|
#endif
|
|
pr_info("%s done!\n", __func__);
|
|
return 0;
|
|
|
|
#ifdef CONFIG_ACCDET_EINT
|
|
err_eint_setup:
|
|
#ifdef CONFIG_ACCDET_EINT_IRQ
|
|
destroy_workqueue(eint_workqueue);
|
|
#endif
|
|
#endif
|
|
#ifdef CONFIG_ACCDET_EINT_IRQ
|
|
err_create_workqueue:
|
|
#endif
|
|
destroy_workqueue(dis_micbias_workqueue);
|
|
err:
|
|
destroy_workqueue(accdet_workqueue);
|
|
err_create_attr:
|
|
input_unregister_device(accdet_input_dev);
|
|
err_input_reg:
|
|
input_free_device(accdet_input_dev);
|
|
err_input_alloc:
|
|
device_del(accdet_device);
|
|
err_device_create:
|
|
class_destroy(accdet_class);
|
|
err_class_create:
|
|
cdev_del(accdet_cdev);
|
|
err_cdev_add:
|
|
unregister_chrdev_region(accdet_devno, 1);
|
|
err_chrdevregion:
|
|
pr_notice("%s error. now exit.!\n", __func__);
|
|
return ret;
|
|
}
|
|
|
|
void mt_accdet_remove(void)
|
|
{
|
|
pr_debug("%s enter!\n", __func__);
|
|
|
|
/* cancel_delayed_work(&accdet_work); */
|
|
#ifdef CONFIG_ACCDET_EINT_IRQ
|
|
destroy_workqueue(eint_workqueue);
|
|
#endif
|
|
destroy_workqueue(dis_micbias_workqueue);
|
|
destroy_workqueue(accdet_workqueue);
|
|
input_unregister_device(accdet_input_dev);
|
|
input_free_device(accdet_input_dev);
|
|
device_del(accdet_device);
|
|
class_destroy(accdet_class);
|
|
cdev_del(accdet_cdev);
|
|
unregister_chrdev_region(accdet_devno, 1);
|
|
pr_debug("%s done!\n", __func__);
|
|
}
|
|
#endif /* end of #if PMIC_ACCDET_KERNEL */
|
|
|
|
long mt_accdet_unlocked_ioctl(struct file *file, unsigned int cmd,
|
|
unsigned long arg)
|
|
{
|
|
switch (cmd) {
|
|
case ACCDET_INIT:
|
|
break;
|
|
case SET_CALL_STATE:
|
|
break;
|
|
case GET_BUTTON_STATUS:
|
|
return s_button_status;
|
|
default:
|
|
pr_debug("[Accdet]accdet_ioctl : default\n");
|
|
break;
|
|
}
|
|
return 0;
|
|
}
|