blob: b3dc0e0890e6009550e9549bdbdfcddc83c67a56 [file] [log] [blame]
/*
* Copyright (C) 2011-2014 MediaTek Inc.
*
* This program is free software: you can redistribute it and/or modify it under the terms of the
* GNU General Public License version 2 as published by the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
* without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
* See the GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License along with this program.
* If not, see <http://www.gnu.org/licenses/>.
*/
#include "accdet.h"
//#include <mach/mt_boot.h>
#include <cust_eint.h>
#include <cust_gpio_usage.h>
#include <mach/mt_gpio.h>
#include <mach/eint.h>
#define SW_WORK_AROUND_ACCDET_REMOTE_BUTTON_ISSUE
#define DEBUG_THREAD 1
/*----------------------------------------------------------------------
static variable defination
----------------------------------------------------------------------*/
#define REGISTER_VALUE(x) (x - 1)
static int button_press_debounce = 0x400;
static int debug_enable = 1;
struct headset_mode_settings *cust_headset_settings = NULL;
#define ACCDET_DEBUG(format, args...) do{ \
if(debug_enable) \
{\
printk(KERN_WARNING format,##args);\
}\
}while(0)
static struct switch_dev accdet_data;
static struct input_dev *kpd_accdet_dev;
static struct cdev *accdet_cdev;
static struct class *accdet_class = NULL;
static struct device *accdet_nor_device = NULL;
static dev_t accdet_devno;
static int pre_status = 0;
static int pre_state_swctrl = 0;
static int accdet_status = PLUG_OUT;
static int cable_type = 0;
#if defined ACCDET_EINT && defined ACCDET_PIN_RECOGNIZATION
//add for new feature PIN recognition
static int cable_pin_recognition = 0;
static int show_icon_delay = 0;
#endif
//ALPS443614: EINT trigger during accdet irq flow running, need add sync method
//between both
static int eint_accdet_sync_flag = 0;
static s64 long_press_time_ns = 0 ;
static int g_accdet_first = 1;
static bool IRQ_CLR_FLAG = FALSE;
static volatile int call_status =0;
static volatile int button_status = 0;
struct wake_lock accdet_suspend_lock;
struct wake_lock accdet_irq_lock;
struct wake_lock accdet_key_lock;
struct wake_lock accdet_timer_lock;
static struct work_struct accdet_work;
static struct workqueue_struct * accdet_workqueue = NULL;
static int long_press_time;
static DEFINE_MUTEX(accdet_eint_irq_sync_mutex);
static inline void clear_accdet_interrupt(void);
#ifdef ACCDET_EINT
#ifndef ACCDET_MULTI_KEY_FEATURE
static int g_accdet_working_in_suspend =0;
#endif
static struct work_struct accdet_eint_work;
static struct workqueue_struct * accdet_eint_workqueue = NULL;
static inline void accdet_init(void);
#ifdef ACCDET_LOW_POWER
#include <linux/timer.h>
#define MICBIAS_DISABLE_TIMER (6 *HZ) //6 seconds
struct timer_list micbias_timer;
static void disable_micbias(unsigned long a);
/* Used to let accdet know if the pin has been fully plugged-in */
#define EINT_PIN_PLUG_IN (1)
#define EINT_PIN_PLUG_OUT (0)
int cur_eint_state = EINT_PIN_PLUG_OUT;
static struct work_struct accdet_disable_work;
static struct workqueue_struct * accdet_disable_workqueue = NULL;
static U32 accdet_2v8_mode = 0;
#endif
#endif//end ACCDET_EINT
extern S32 pwrap_read( U32 adr, U32 *rdata );
extern S32 pwrap_write( U32 adr, U32 wdata );
extern struct headset_mode_settings* get_cust_headset_settings(void);
extern struct headset_key_custom* get_headset_key_custom_setting(void);
extern int PMIC_IMM_GetOneChannelValue(int dwChannel, int deCount, int trimd);
extern struct file_operations *accdet_get_fops(void);//from accdet_drv.c
extern struct platform_driver accdet_driver_func(void);//from accdet_drv.c
extern struct accdet_ssb_data *accdet_tuning_data;
#ifdef DEBUG_THREAD
extern void accdet_create_attr_func(void); //from accdet_drv.c
#endif
static U32 pmic_pwrap_read(U32 addr);
static void pmic_pwrap_write(U32 addr, unsigned int wdata);
/****************************************************************/
/*** export function **/
/****************************************************************/
void accdet_detect(void)
{
int ret = 0 ;
ACCDET_DEBUG("[Accdet]accdet_detect\n");
accdet_status = PLUG_OUT;
ret = queue_work(accdet_workqueue, &accdet_work);
if(!ret)
{
ACCDET_DEBUG("[Accdet]accdet_detect:accdet_work return:%d!\n", ret);
}
return;
}
EXPORT_SYMBOL(accdet_detect);
void accdet_state_reset(void)
{
ACCDET_DEBUG("[Accdet]accdet_state_reset\n");
accdet_status = PLUG_OUT;
cable_type = NO_DEVICE;
return;
}
EXPORT_SYMBOL(accdet_state_reset);
int accdet_get_cable_type(void)
{
return cable_type;
}
void accdet_auxadc_switch(int enable)
{
#ifdef ACCDET_SSB
if (enable) {
if(accdet_2v8_mode == 0){
pmic_pwrap_write(ACCDET_RSV, ACCDET_1V9_MODE_ON);
ACCDET_DEBUG("ACCDET enable switch in 1.9v mode SSB\n");
}else{
pmic_pwrap_write(ACCDET_RSV, ACCDET_2V8_MODE_ON);
ACCDET_DEBUG("ACCDET enable switch in 2.8v mode SSB\n");
}
}else{
if(accdet_2v8_mode == 0){
pmic_pwrap_write(ACCDET_RSV, ACCDET_1V9_MODE_OFF);
ACCDET_DEBUG("ACCDET diable switch in 1.9v mode SSB\n");
}else{
pmic_pwrap_write(ACCDET_RSV, ACCDET_2V8_MODE_OFF);
ACCDET_DEBUG("ACCDET diable switch in 2.8v mode SSB\n");
}
}
#else
if (enable) {
#ifndef ACCDET_28V_MODE
pmic_pwrap_write(ACCDET_RSV, ACCDET_1V9_MODE_ON);
ACCDET_DEBUG("ACCDET enable switch in 1.9v mode \n");
#else
pmic_pwrap_write(ACCDET_RSV, ACCDET_2V8_MODE_ON);
ACCDET_DEBUG("ACCDET enable switch in 2.8v mode \n");
#endif
}else {
#ifndef ACCDET_28V_MODE
pmic_pwrap_write(ACCDET_RSV, ACCDET_1V9_MODE_OFF);
ACCDET_DEBUG("ACCDET diable switch in 1.9v mode \n");
#else
pmic_pwrap_write(ACCDET_RSV, ACCDET_2V8_MODE_OFF);
ACCDET_DEBUG("ACCDET diable switch in 2.8v mode \n");
#endif
}
#endif
}
/****************************************************************/
/*******static function defination **/
/****************************************************************/
// pmic wrap read and write func
static U32 pmic_pwrap_read(U32 addr)
{
U32 val =0;
pwrap_read(addr, &val);
//ACCDET_DEBUG("[Accdet]wrap write func addr=0x%x, val=0x%x\n", addr, val);
return val;
}
static void pmic_pwrap_write(unsigned int addr, unsigned int wdata)
{
pwrap_write(addr, wdata);
//ACCDET_DEBUG("[Accdet]wrap write func addr=0x%x, wdate=0x%x\n", addr, wdata);
}
#ifndef ACCDET_MULTI_KEY_FEATURE
//detect if remote button is short pressed or long pressed
static bool is_long_press(void)
{
int current_status = 0;
int index = 0;
int count = long_press_time / 100;
while(index++ < count)
{
current_status = ((pmic_pwrap_read(ACCDET_STATE_RG) & 0xc0)>>6);
if(current_status != 0)
{
return false;
}
msleep(100);
}
return true;
}
#endif
#ifdef ACCDET_PIN_RECOGNIZATION
static void headset_standard_judge_message(void)
{
ACCDET_DEBUG("[Accdet]Dear user: You plug in a headset which this phone doesn't support!!\n");
}
#endif
#ifdef ACCDET_PIN_SWAP
static void accdet_FSA8049_enable(void)
{
mt_set_gpio_mode(GPIO_FSA8049_PIN, GPIO_FSA8049_PIN_M_GPIO);
mt_set_gpio_dir(GPIO_FSA8049_PIN, GPIO_DIR_OUT);
mt_set_gpio_out(GPIO_FSA8049_PIN, GPIO_OUT_ONE);
}
static void accdet_FSA8049_disable(void)
{
mt_set_gpio_mode(GPIO_FSA8049_PIN, GPIO_FSA8049_PIN_M_GPIO);
mt_set_gpio_dir(GPIO_FSA8049_PIN, GPIO_DIR_OUT);
mt_set_gpio_out(GPIO_FSA8049_PIN, GPIO_OUT_ZERO);
}
#endif
static void inline headset_plug_out(void)
{
accdet_status = PLUG_OUT;
cable_type = NO_DEVICE;
//update the cable_type
switch_set_state((struct switch_dev *)&accdet_data, cable_type);
ACCDET_DEBUG( " [accdet] set state in cable_type = NO_DEVICE\n");
}
//Accdet only need this func
static void inline enable_accdet(u32 state_swctrl)
{
// enable ACCDET unit
ACCDET_DEBUG("accdet: enable_accdet\n");
//enable clock
pmic_pwrap_write(TOP_CKPDN_CLR, RG_ACCDET_CLK_CLR);
pmic_pwrap_write(ACCDET_STATE_SWCTRL, pmic_pwrap_read(ACCDET_STATE_SWCTRL)|state_swctrl);
pmic_pwrap_write(ACCDET_CTRL, ACCDET_ENABLE);
}
#ifdef ACCDET_EINT
static void inline disable_accdet(void)
{
int irq_temp = 0;
//sync with accdet_irq_handler set clear accdet irq bit to avoid set clear accdet irq bit after disable accdet
//disable accdet irq
pmic_pwrap_write(INT_CON_ACCDET_CLR, RG_ACCDET_IRQ_CLR);
clear_accdet_interrupt();
udelay(200);
mutex_lock(&accdet_eint_irq_sync_mutex);
while(pmic_pwrap_read(ACCDET_IRQ_STS) & IRQ_STATUS_BIT)
{
ACCDET_DEBUG("[Accdet]check_cable_type: Clear interrupt on-going....\n");
msleep(5);
}
irq_temp = pmic_pwrap_read(ACCDET_IRQ_STS);
irq_temp = irq_temp & (~IRQ_CLR_BIT);
pmic_pwrap_write(ACCDET_IRQ_STS, irq_temp);
ACCDET_DEBUG("[Accdet]disable_accdet:Clear interrupt:Done[0x%x]!\n", pmic_pwrap_read(ACCDET_IRQ_STS));
mutex_unlock(&accdet_eint_irq_sync_mutex);
// disable ACCDET unit
ACCDET_DEBUG("accdet: disable_accdet\n");
pre_state_swctrl = pmic_pwrap_read(ACCDET_STATE_SWCTRL);
pmic_pwrap_write(ACCDET_CTRL, ACCDET_DISABLE);
pmic_pwrap_write(ACCDET_STATE_SWCTRL, 0);
//disable clock
pmic_pwrap_write(TOP_CKPDN_SET, RG_ACCDET_CLK_SET);
}
static void disable_micbias(unsigned long a)
{
int ret = 0;
ret = queue_work(accdet_disable_workqueue, &accdet_disable_work);
if(!ret)
{
ACCDET_DEBUG("[Accdet]disable_micbias:accdet_work return:%d!\n", ret);
}
}
static void disable_micbias_callback(struct work_struct *work)
{
if(cable_type == HEADSET_NO_MIC) {
#ifdef ACCDET_PIN_RECOGNIZATION
show_icon_delay = 0;
cable_pin_recognition = 0;
ACCDET_DEBUG("[Accdet] cable_pin_recognition = %d\n", cable_pin_recognition);
pmic_pwrap_write(ACCDET_PWM_WIDTH, cust_headset_settings->pwm_width);
pmic_pwrap_write(ACCDET_PWM_THRESH, cust_headset_settings->pwm_thresh);
#endif
// setting pwm idle;
#ifdef ACCDET_MULTI_KEY_FEATURE
pmic_pwrap_write(ACCDET_STATE_SWCTRL, pmic_pwrap_read(ACCDET_STATE_SWCTRL)&~ACCDET_SWCTRL_IDLE_EN);
#endif
#ifdef ACCDET_PIN_SWAP
//accdet_FSA8049_disable(); //disable GPIO209 for PIN swap
//ACCDET_DEBUG("[Accdet] FSA8049 disable!\n");
#endif
disable_accdet();
ACCDET_DEBUG("[Accdet] more than 5s MICBIAS : Disabled\n");
}
#ifdef ACCDET_PIN_RECOGNIZATION
else if(cable_type == HEADSET_MIC) {
pmic_pwrap_write(ACCDET_PWM_WIDTH, cust_headset_settings->pwm_width);
pmic_pwrap_write(ACCDET_PWM_THRESH, cust_headset_settings->pwm_thresh);
ACCDET_DEBUG("[Accdet]pin recog after 5s recover micbias polling!\n");
}
#endif
}
static void accdet_eint_work_callback(struct work_struct *work)
{
//KE under fastly plug in and plug out
mt_eint_mask(CUST_EINT_ACCDET_NUM);
if (cur_eint_state == EINT_PIN_PLUG_IN) {
ACCDET_DEBUG("[Accdet]EINT func :plug-in\n");
mutex_lock(&accdet_eint_irq_sync_mutex);
eint_accdet_sync_flag = 1;
mutex_unlock(&accdet_eint_irq_sync_mutex);
#ifdef ACCDET_LOW_POWER
wake_lock_timeout(&accdet_timer_lock, 7*HZ);
#endif
ACCDET_DEBUG("[Accdet]EINT func :check mode begin\n");
if(accdet_2v8_mode == 1){
pmic_pwrap_write(ACCDET_RSV, ACCDET_2V8_MODE_OFF);
ACCDET_DEBUG("ACCDET use in 2.8V mode!! SSB\n");
}
ACCDET_DEBUG("[Accdet]EINT func :check mode end\n");
#ifdef ACCDET_28V_MODE
pmic_pwrap_write(ACCDET_RSV, ACCDET_2V8_MODE_OFF);
ACCDET_DEBUG("ACCDET use in 2.8V mode!! \n");
#endif
#ifdef ACCDET_PIN_SWAP
pmic_pwrap_write(0x0400, pmic_pwrap_read(0x0400)|(1<<14));
msleep(800);
accdet_FSA8049_enable(); //enable GPIO209 for PIN swap
ACCDET_DEBUG("[Accdet] FSA8049 enable!\n");
msleep(250); //PIN swap need ms
#endif
accdet_init();// do set pwm_idle on in accdet_init
#ifdef ACCDET_PIN_RECOGNIZATION
show_icon_delay = 1;
//micbias always on during detected PIN recognition
pmic_pwrap_write(ACCDET_PWM_WIDTH, cust_headset_settings->pwm_width);
pmic_pwrap_write(ACCDET_PWM_THRESH, cust_headset_settings->pwm_width);
ACCDET_DEBUG("[Accdet]pin recog start! micbias always on!\n");
#endif
//set PWM IDLE on
#ifdef ACCDET_MULTI_KEY_FEATURE
pmic_pwrap_write(ACCDET_STATE_SWCTRL, (pmic_pwrap_read(ACCDET_STATE_SWCTRL)|ACCDET_SWCTRL_IDLE_EN));
#endif
//enable ACCDET unit
enable_accdet(ACCDET_SWCTRL_EN);
} else {
//EINT_PIN_PLUG_OUT
//Disable ACCDET
ACCDET_DEBUG("[Accdet]EINT func :plug-out\n");
mutex_lock(&accdet_eint_irq_sync_mutex);
eint_accdet_sync_flag = 0;
mutex_unlock(&accdet_eint_irq_sync_mutex);
#ifdef ACCDET_LOW_POWER
del_timer_sync(&micbias_timer);
#endif
#ifdef ACCDET_PIN_RECOGNIZATION
show_icon_delay = 0;
cable_pin_recognition = 0;
#endif
#ifdef ACCDET_PIN_SWAP
pmic_pwrap_write(0x0400, pmic_pwrap_read(0x0400)&~(1<<14));
accdet_FSA8049_disable(); //disable GPIO209 for PIN swap
ACCDET_DEBUG("[Accdet] FSA8049 disable!\n");
#endif
accdet_auxadc_switch(0);
disable_accdet();
headset_plug_out();
if(accdet_2v8_mode == 1){
pmic_pwrap_write(ACCDET_RSV, ACCDET_1V9_MODE_OFF);
ACCDET_DEBUG("ACCDET use in 1.9V mode!! SSB\n");
}
#ifdef ACCDET_28V_MODE
pmic_pwrap_write(ACCDET_RSV, ACCDET_1V9_MODE_OFF);
ACCDET_DEBUG("ACCDET use in 1.9V mode!! \n");
#endif
}
//unmask EINT
//msleep(500);
mt_eint_unmask(CUST_EINT_ACCDET_NUM);
ACCDET_DEBUG("[Accdet]eint unmask !!!!!!\n");
}
static void accdet_eint_func(void)
{
int ret=0;
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 (CUST_EINT_ACCDET_TYPE == CUST_EINTF_TRIGGER_HIGH){
mt_eint_set_polarity(CUST_EINT_ACCDET_NUM, (1));
}else{
mt_eint_set_polarity(CUST_EINT_ACCDET_NUM, (0));
}
#ifdef ACCDET_SHORT_PLUGOUT_DEBOUNCE
mt_eint_set_hw_debounce(CUST_EINT_ACCDET_NUM, CUST_EINT_ACCDET_DEBOUNCE_CN);
#endif
/* update the eint status */
cur_eint_state = EINT_PIN_PLUG_OUT;
//#ifdef ACCDET_LOW_POWER
// del_timer_sync(&micbias_timer);
//#endif
}
else
{
/*
To trigger EINT when the headset was plugged out
We set the opposite polarity to what we initialed.
*/
if (CUST_EINT_ACCDET_TYPE == CUST_EINTF_TRIGGER_HIGH){
mt_eint_set_polarity(CUST_EINT_ACCDET_NUM, !(1));
}else{
mt_eint_set_polarity(CUST_EINT_ACCDET_NUM, !(0));
}
/* update the eint status */
#ifdef ACCDET_SHORT_PLUGOUT_DEBOUNCE
mt_eint_set_hw_debounce(CUST_EINT_ACCDET_NUM, ACCDET_SHORT_PLUGOUT_DEBOUNCE_CN);
#endif
cur_eint_state = EINT_PIN_PLUG_IN;
#ifdef ACCDET_LOW_POWER
//INIT the timer to disable micbias.
init_timer(&micbias_timer);
micbias_timer.expires = jiffies + MICBIAS_DISABLE_TIMER;
micbias_timer.function = &disable_micbias;
micbias_timer.data = ((unsigned long) 0 );
add_timer(&micbias_timer);
#endif
}
ret = queue_work(accdet_eint_workqueue, &accdet_eint_work);
if(!ret)
{
//ACCDET_DEBUG("[Accdet]accdet_eint_func:accdet_work return:%d!\n", ret);
}
}
static inline int accdet_setup_eint(void)
{
/*configure to GPIO function, external interrupt*/
ACCDET_DEBUG("[Accdet]accdet_setup_eint\n");
mt_set_gpio_mode(GPIO_ACCDET_EINT_PIN, GPIO_ACCDET_EINT_PIN_M_EINT);
mt_set_gpio_dir(GPIO_ACCDET_EINT_PIN, GPIO_DIR_IN);
mt_set_gpio_pull_enable(GPIO_ACCDET_EINT_PIN, GPIO_PULL_DISABLE); //To disable GPIO PULL.
mt_eint_set_hw_debounce(CUST_EINT_ACCDET_NUM, CUST_EINT_ACCDET_DEBOUNCE_CN);
mt_eint_registration(CUST_EINT_ACCDET_NUM, CUST_EINT_ACCDET_TYPE, accdet_eint_func, 0);
ACCDET_DEBUG("[Accdet]accdet set EINT finished, accdet_eint_num=%d, accdet_eint_debounce_en=%d, accdet_eint_polarity=%d\n", CUST_EINT_ACCDET_NUM, CUST_EINT_ACCDET_DEBOUNCE_EN, CUST_EINT_ACCDET_TYPE);
mt_eint_unmask(CUST_EINT_ACCDET_NUM);
return 0;
}
#endif//endif ACCDET_EINT
#ifdef ACCDET_MULTI_KEY_FEATURE
extern int PMIC_IMM_GetOneChannelValue(int dwChannel, int deCount, int trimd);
#define KEY_SAMPLE_PERIOD (60) //ms
#define MULTIKEY_ADC_CHANNEL (8)
#define NO_KEY (0x0)
#define UP_KEY (0x01)
#define MD_KEY (0x02)
#define DW_KEY (0x04)
#define SHORT_PRESS (0x0)
#define LONG_PRESS (0x10)
#define SHORT_UP ((UP_KEY) | SHORT_PRESS)
#define SHORT_MD ((MD_KEY) | SHORT_PRESS)
#define SHORT_DW ((DW_KEY) | SHORT_PRESS)
#define LONG_UP ((UP_KEY) | LONG_PRESS)
#define LONG_MD ((MD_KEY) | LONG_PRESS)
#define LONG_DW ((DW_KEY) | LONG_PRESS)
#define KEYDOWN_FLAG 1
#define KEYUP_FLAG 0
//static int g_adcMic_channel_num =0;
static DEFINE_MUTEX(accdet_multikey_mutex);
/*
MD UP DW
|---------|-----------|----------|
0V<=MD< 0.09V<= UP<0.24V<=DW <0.5V
*/
#define DW_KEY_HIGH_THR (500) //0.50v=500000uv
#define DW_KEY_THR (240) //0.24v=240000uv
#define UP_KEY_THR (90) //0.09v=90000uv
#define MD_KEY_THR (0)
static int key_check(int b)
{
//ACCDET_DEBUG("adc_data: %d v\n",b);
/* 0.24V ~ */
ACCDET_DEBUG("accdet: come in key_check!!\n");
if((b<DW_KEY_HIGH_THR)&&(b >= DW_KEY_THR))
{
ACCDET_DEBUG("adc_data: %d mv\n",b);
return DW_KEY;
}
else if ((b < DW_KEY_THR)&& (b >= UP_KEY_THR))
{
ACCDET_DEBUG("adc_data: %d mv\n",b);
return UP_KEY;
}
else if ((b < UP_KEY_THR) && (b >= MD_KEY_THR))
{
ACCDET_DEBUG("adc_data: %d mv\n",b);
return MD_KEY;
}
ACCDET_DEBUG("accdet: leave key_check!!\n");
return NO_KEY;
}
static void send_key_event(int keycode,int flag)
{
input_report_key(kpd_accdet_dev, KEY_PLAYPAUSE, flag);
input_sync(kpd_accdet_dev);
ACCDET_DEBUG("HEADSETHOOK %d\n",flag);
#if 0
if(call_status == 0)
{
switch (keycode)
{
case DW_KEY:
input_report_key(kpd_accdet_dev, KEY_NEXTSONG, flag);
input_sync(kpd_accdet_dev);
ACCDET_DEBUG("KEY_NEXTSONG %d\n",flag);
break;
case UP_KEY:
input_report_key(kpd_accdet_dev, KEY_PREVIOUSSONG, flag);
input_sync(kpd_accdet_dev);
ACCDET_DEBUG("KEY_PREVIOUSSONG %d\n",flag);
break;
}
}
else
{
switch (keycode)
{
case DW_KEY:
input_report_key(kpd_accdet_dev, KEY_VOLUMEDOWN, flag);
input_sync(kpd_accdet_dev);
ACCDET_DEBUG("KEY_VOLUMEDOWN %d\n",flag);
break;
case UP_KEY:
input_report_key(kpd_accdet_dev, KEY_VOLUMEUP, flag);
input_sync(kpd_accdet_dev);
ACCDET_DEBUG("KEY_VOLUMEUP %d\n",flag);
break;
}
}
#endif
}
static int multi_key_detection(int current_status)
{
//int current_status = 0;
int index = 0;
int count = long_press_time / (KEY_SAMPLE_PERIOD + 40 ); //ADC delay
int m_key = 0;
int cur_key = 0;
int cali_voltage=0;
cali_voltage = PMIC_IMM_GetOneChannelValue(MULTIKEY_ADC_CHANNEL,1,1);
ACCDET_DEBUG("[Accdet]adc cali_voltage1 = %d mv\n", cali_voltage);
m_key = cur_key = key_check(cali_voltage);
//send_key_event(m_key, KEYDOWN_FLAG);
send_key_event(m_key, !current_status);
#if 0
while(index++ < count)
{
/* Check if the current state has been changed */
current_status = ((pmic_pwrap_read(ACCDET_STATE_RG) & 0xc0)>>6);
ACCDET_DEBUG("[Accdet]accdet current_status = %d\n", current_status);
if(current_status != 0)
{
send_key_event(m_key, KEYUP_FLAG);
return (m_key | SHORT_PRESS);
}
/* Check if the voltage has been changed (press one key and another) */
//IMM_GetOneChannelValue(g_adcMic_channel_num, adc_data, &adc_raw);
cali_voltage = PMIC_IMM_GetOneChannelValue(MULTIKEY_ADC_CHANNEL,1,1);
ACCDET_DEBUG("[Accdet]adc in while loop [%d]= %d mv\n", index, cali_voltage);
cur_key = key_check(cali_voltage);
if(m_key != cur_key)
{
send_key_event(m_key, KEYUP_FLAG);
ACCDET_DEBUG("[Accdet]accdet press one key and another happen!!\n");
return (m_key | SHORT_PRESS);
}
else
{
m_key = cur_key;
}
msleep(KEY_SAMPLE_PERIOD);
}
return (m_key | LONG_PRESS);
#endif
}
#endif
static void accdet_workqueue_func(void)
{
int ret;
ret = queue_work(accdet_workqueue, &accdet_work);
if(!ret)
{
ACCDET_DEBUG("[Accdet]accdet_work return:%d!\n", ret);
}
}
int accdet_irq_handler(void)
{
int i = 0;
if((pmic_pwrap_read(ACCDET_IRQ_STS) & IRQ_STATUS_BIT)) {
clear_accdet_interrupt();
}
#ifdef ACCDET_MULTI_KEY_FEATURE
if (accdet_status == MIC_BIAS){
accdet_auxadc_switch(1);
pmic_pwrap_write(ACCDET_PWM_WIDTH, REGISTER_VALUE(cust_headset_settings->pwm_width));
pmic_pwrap_write(ACCDET_PWM_THRESH, REGISTER_VALUE(cust_headset_settings->pwm_width));
}
#endif
accdet_workqueue_func();
while(((pmic_pwrap_read(ACCDET_IRQ_STS) & IRQ_STATUS_BIT) && i<10)) {
i++;
udelay(200);
}
return 1;
}
//clear ACCDET IRQ in accdet register
static inline void clear_accdet_interrupt(void)
{
//it is safe by using polling to adjust when to clear IRQ_CLR_BIT
pmic_pwrap_write(ACCDET_IRQ_STS, (IRQ_CLR_BIT));
ACCDET_DEBUG("[Accdet]clear_accdet_interrupt: ACCDET_IRQ_STS = 0x%x\n", pmic_pwrap_read(ACCDET_IRQ_STS));
}
#ifdef SW_WORK_AROUND_ACCDET_REMOTE_BUTTON_ISSUE
#define ACC_ANSWER_CALL 1
#define ACC_END_CALL 2
#define ACC_MEDIA_PLAYPAUSE 3
#ifdef ACCDET_MULTI_KEY_FEATURE
#define ACC_MEDIA_STOP 4
#define ACC_MEDIA_NEXT 5
#define ACC_MEDIA_PREVIOUS 6
#define ACC_VOLUMEUP 7
#define ACC_VOLUMEDOWN 8
#endif
static atomic_t send_event_flag = ATOMIC_INIT(0);
static DECLARE_WAIT_QUEUE_HEAD(send_event_wq);
static int accdet_key_event=0;
static int sendKeyEvent(void *unuse)
{
while(1)
{
ACCDET_DEBUG( " accdet:sendKeyEvent wait\n");
//wait for signal
wait_event_interruptible(send_event_wq, (atomic_read(&send_event_flag) != 0));
wake_lock_timeout(&accdet_key_lock, 2*HZ); //set the wake lock.
ACCDET_DEBUG( " accdet:going to send event %d\n", accdet_key_event);
if(PLUG_OUT !=accdet_status)
{
//send key event
if(ACC_END_CALL == accdet_key_event)
{
ACCDET_DEBUG("[Accdet] end call!\n");
input_report_key(kpd_accdet_dev, KEY_ENDCALL, 1);
input_report_key(kpd_accdet_dev, KEY_ENDCALL, 0);
input_sync(kpd_accdet_dev);
}
#ifdef ACCDET_MULTI_KEY_FEATURE
if(ACC_MEDIA_PLAYPAUSE == accdet_key_event)
{
ACCDET_DEBUG("[Accdet] PLAY_PAUSE !\n");
input_report_key(kpd_accdet_dev, KEY_PLAYPAUSE, 1);
input_report_key(kpd_accdet_dev, KEY_PLAYPAUSE, 0);
input_sync(kpd_accdet_dev);
}
// next, previous, volumeup, volumedown send key in multi_key_detection()
if(ACC_MEDIA_NEXT == accdet_key_event)
{
ACCDET_DEBUG("[Accdet] NEXT ..\n");
}
if(ACC_MEDIA_PREVIOUS == accdet_key_event)
{
ACCDET_DEBUG("[Accdet] PREVIOUS..\n");
}
if(ACC_VOLUMEUP== accdet_key_event)
{
ACCDET_DEBUG("[Accdet] VOLUMUP ..\n");
}
if(ACC_VOLUMEDOWN== accdet_key_event)
{
ACCDET_DEBUG("[Accdet] VOLUMDOWN..\n");
}
#else
if(ACC_MEDIA_PLAYPAUSE == accdet_key_event)
{
ACCDET_DEBUG("[Accdet] PLAY_PAUSE !\n");
input_report_key(kpd_accdet_dev, KEY_PLAYPAUSE, 1);
input_report_key(kpd_accdet_dev, KEY_PLAYPAUSE, 0);
input_sync(kpd_accdet_dev);
}
#endif
}
atomic_set(&send_event_flag, 0);
// wake_unlock(&accdet_key_lock); //unlock wake lock
}
return 0;
}
static ssize_t notify_sendKeyEvent(int event)
{
accdet_key_event = event;
atomic_set(&send_event_flag, 1);
wake_up(&send_event_wq);
ACCDET_DEBUG( " accdet:notify_sendKeyEvent !\n");
return 0;
}
#endif
static inline void check_cable_type(void)
{
int current_status = 0;
int irq_temp = 0; //for clear IRQ_bit
int wait_clear_irq_times = 0;
#ifdef ACCDET_PIN_RECOGNIZATION
int pin_adc_value = 0;
#define PIN_ADC_CHANNEL 5
#endif
current_status = ((pmic_pwrap_read(ACCDET_STATE_RG) & 0xc0)>>6); //A=bit1; B=bit0
ACCDET_DEBUG("[Accdet]accdet interrupt happen:[%s]current AB = %d\n",
accdet_status_string[accdet_status], current_status);
button_status = 0;
pre_status = accdet_status;
ACCDET_DEBUG("[Accdet]check_cable_type: ACCDET_IRQ_STS = 0x%x\n", pmic_pwrap_read(ACCDET_IRQ_STS));
IRQ_CLR_FLAG = FALSE;
switch(accdet_status)
{
case PLUG_OUT:
#ifdef ACCDET_PIN_RECOGNIZATION
pmic_pwrap_write(ACCDET_DEBOUNCE1, cust_headset_settings->debounce1);
#endif
if(current_status == 0)
{
#ifdef ACCDET_PIN_RECOGNIZATION
//micbias always on during detected PIN recognition
pmic_pwrap_write(ACCDET_PWM_WIDTH, cust_headset_settings->pwm_width);
pmic_pwrap_write(ACCDET_PWM_THRESH, cust_headset_settings->pwm_width);
ACCDET_DEBUG("[Accdet]PIN recognition micbias always on!\n");
ACCDET_DEBUG("[Accdet]before adc read, pin_adc_value = %d mv!\n", pin_adc_value);
msleep(1000);
current_status = ((pmic_pwrap_read(ACCDET_STATE_RG) & 0xc0)>>6); //A=bit1; B=bit0
if (current_status == 0 && show_icon_delay != 0)
{
accdet_auxadc_switch(1);//switch on when need to use auxadc read voltage
pin_adc_value = PMIC_IMM_GetOneChannelValue(8,10,1);
ACCDET_DEBUG("[Accdet]pin_adc_value = %d mv!\n", pin_adc_value);
accdet_auxadc_switch(0);
if (200 > pin_adc_value && pin_adc_value> 100) //100mv ilegal headset
{
headset_standard_judge_message();
mutex_lock(&accdet_eint_irq_sync_mutex);
if(1 == eint_accdet_sync_flag) {
cable_type = HEADSET_NO_MIC;
accdet_status = HOOK_SWITCH;
cable_pin_recognition = 1;
ACCDET_DEBUG("[Accdet] cable_pin_recognition = %d\n", cable_pin_recognition);
}else {
ACCDET_DEBUG("[Accdet] Headset has plugged out\n");
}
mutex_unlock(&accdet_eint_irq_sync_mutex);
}
else
{
mutex_lock(&accdet_eint_irq_sync_mutex);
if(1 == eint_accdet_sync_flag) {
cable_type = HEADSET_NO_MIC;
accdet_status = HOOK_SWITCH;
}else {
ACCDET_DEBUG("[Accdet] Headset has plugged out\n");
}
mutex_unlock(&accdet_eint_irq_sync_mutex);
}
}
#else
mutex_lock(&accdet_eint_irq_sync_mutex);
if(1 == eint_accdet_sync_flag) {
cable_type = HEADSET_NO_MIC;
accdet_status = HOOK_SWITCH;
}else {
ACCDET_DEBUG("[Accdet] Headset has plugged out\n");
}
mutex_unlock(&accdet_eint_irq_sync_mutex);
#endif
}
else if(current_status == 1)
{
mutex_lock(&accdet_eint_irq_sync_mutex);
if(1 == eint_accdet_sync_flag) {
accdet_status = MIC_BIAS;
cable_type = HEADSET_MIC;
}else {
ACCDET_DEBUG("[Accdet] Headset has plugged out\n");
}
mutex_unlock(&accdet_eint_irq_sync_mutex);
//ALPS00038030:reduce the time of remote button pressed during incoming call
//solution: reduce hook switch debounce time to 0x400
pmic_pwrap_write(ACCDET_DEBOUNCE0, button_press_debounce);
//recover polling set AB 00-01
#ifdef ACCDET_PIN_RECOGNIZATION
pmic_pwrap_write(ACCDET_PWM_WIDTH, REGISTER_VALUE(cust_headset_settings->pwm_width));
pmic_pwrap_write(ACCDET_PWM_THRESH, REGISTER_VALUE(cust_headset_settings->pwm_thresh));
#endif
//#ifdef ACCDET_LOW_POWER
//wake_unlock(&accdet_timer_lock);//add for suspend disable accdet more than 5S
//#endif
}
else if(current_status == 3)
{
ACCDET_DEBUG("[Accdet]PLUG_OUT state not change!\n");
#ifdef ACCDET_MULTI_KEY_FEATURE
ACCDET_DEBUG("[Accdet] do not send plug out event in plug out\n");
#else
mutex_lock(&accdet_eint_irq_sync_mutex);
if(1 == eint_accdet_sync_flag) {
accdet_status = PLUG_OUT;
cable_type = NO_DEVICE;
}else {
ACCDET_DEBUG("[Accdet] Headset has plugged out\n");
}
mutex_unlock(&accdet_eint_irq_sync_mutex);
#ifdef ACCDET_EINT
disable_accdet();
#endif
#endif
}
else
{
ACCDET_DEBUG("[Accdet]PLUG_OUT can't change to this state!\n");
}
break;
case MIC_BIAS:
//ALPS00038030:reduce the time of remote button pressed during incoming call
//solution: resume hook switch debounce time
pmic_pwrap_write(ACCDET_DEBOUNCE0, cust_headset_settings->debounce0);
if(current_status == 0)
{
mutex_lock(&accdet_eint_irq_sync_mutex);
if(1 == eint_accdet_sync_flag) {
while((pmic_pwrap_read(ACCDET_IRQ_STS) & IRQ_STATUS_BIT) && (wait_clear_irq_times<3))
{
ACCDET_DEBUG("[Accdet]check_cable_type: MIC BIAS clear IRQ on-going1....\n");
wait_clear_irq_times++;
msleep(5);
}
irq_temp = pmic_pwrap_read(ACCDET_IRQ_STS);
irq_temp = irq_temp & (~IRQ_CLR_BIT);
pmic_pwrap_write(ACCDET_IRQ_STS, irq_temp);
IRQ_CLR_FLAG = TRUE;
accdet_status = HOOK_SWITCH;
}else {
ACCDET_DEBUG("[Accdet] Headset has plugged out\n");
}
mutex_unlock(&accdet_eint_irq_sync_mutex);
button_status = 1;
if(button_status)
{
// #ifdef ACCDET_MULTI_KEY_FEATURE
int multi_key = NO_KEY;
//mdelay(10);
//if plug out don't send key
mutex_lock(&accdet_eint_irq_sync_mutex);
if(1 == eint_accdet_sync_flag) {
multi_key = multi_key_detection(current_status);
}else {
ACCDET_DEBUG("[Accdet] multi_key_detection: Headset has plugged out\n");
}
mutex_unlock(&accdet_eint_irq_sync_mutex);
accdet_auxadc_switch(0);
//recover pwm frequency and duty
pmic_pwrap_write(ACCDET_PWM_WIDTH, REGISTER_VALUE(cust_headset_settings->pwm_width));
pmic_pwrap_write(ACCDET_PWM_THRESH, REGISTER_VALUE(cust_headset_settings->pwm_thresh));
#if 0
switch (multi_key)
{
case SHORT_UP:
ACCDET_DEBUG("[Accdet] Short press up (0x%x)\n", multi_key);
if(call_status == 0)
{
notify_sendKeyEvent(ACC_MEDIA_PREVIOUS);
}
else
{
notify_sendKeyEvent(ACC_VOLUMEUP);
}
break;
case SHORT_MD:
ACCDET_DEBUG("[Accdet] Short press middle (0x%x)\n", multi_key);
notify_sendKeyEvent(ACC_MEDIA_PLAYPAUSE);
break;
case SHORT_DW:
ACCDET_DEBUG("[Accdet] Short press down (0x%x)\n", multi_key);
if(call_status == 0)
{
notify_sendKeyEvent(ACC_MEDIA_NEXT);
}
else
{
notify_sendKeyEvent(ACC_VOLUMEDOWN);
}
break;
case LONG_UP:
ACCDET_DEBUG("[Accdet] Long press up (0x%x)\n", multi_key);
send_key_event(UP_KEY, KEYUP_FLAG);
break;
case LONG_MD:
ACCDET_DEBUG("[Accdet] Long press middle (0x%x)\n", multi_key);
notify_sendKeyEvent(ACC_END_CALL);
break;
case LONG_DW:
ACCDET_DEBUG("[Accdet] Long press down (0x%x)\n", multi_key);
send_key_event(DW_KEY, KEYUP_FLAG);
break;
default:
ACCDET_DEBUG("[Accdet] unkown key (0x%x)\n", multi_key);
break;
}
#endif
/*#else
if(call_status != 0)
{
if(is_long_press())
{
ACCDET_DEBUG("[Accdet]send long press remote button event %d \n",ACC_END_CALL);
notify_sendKeyEvent(ACC_END_CALL);
} else {
ACCDET_DEBUG("[Accdet]send short press remote button event %d\n",ACC_ANSWER_CALL);
notify_sendKeyEvent(ACC_MEDIA_PLAYPAUSE);
}
}
#endif////end ifdef ACCDET_MULTI_KEY_FEATURE else
*/
}
}
else if(current_status == 1)
{
mutex_lock(&accdet_eint_irq_sync_mutex);
if(1 == eint_accdet_sync_flag) {
accdet_status = MIC_BIAS;
cable_type = HEADSET_MIC;
ACCDET_DEBUG("[Accdet]MIC_BIAS state not change!\n");
}else {
ACCDET_DEBUG("[Accdet] Headset has plugged out\n");
}
mutex_unlock(&accdet_eint_irq_sync_mutex);
}
else if(current_status == 3)
{
#ifdef ACCDET_MULTI_KEY_FEATURE
ACCDET_DEBUG("[Accdet]do not send plug ou in micbiast\n");
mutex_lock(&accdet_eint_irq_sync_mutex);
if(1 == eint_accdet_sync_flag) {
accdet_status = PLUG_OUT;
}else {
ACCDET_DEBUG("[Accdet] Headset has plugged out\n");
}
mutex_unlock(&accdet_eint_irq_sync_mutex);
#else
mutex_lock(&accdet_eint_irq_sync_mutex);
if(1 == eint_accdet_sync_flag) {
accdet_status = PLUG_OUT;
cable_type = NO_DEVICE;
}else {
ACCDET_DEBUG("[Accdet] Headset has plugged out\n");
}
mutex_unlock(&accdet_eint_irq_sync_mutex);
#ifdef ACCDET_EINT
disable_accdet();
#endif
#endif
}
else
{
ACCDET_DEBUG("[Accdet]MIC_BIAS can't change to this state!\n");
}
break;
case HOOK_SWITCH:
if(current_status == 0)
{
mutex_lock(&accdet_eint_irq_sync_mutex);
if(1 == eint_accdet_sync_flag) {
//for avoid 01->00 framework of Headset will report press key info for Audio
//cable_type = HEADSET_NO_MIC;
//accdet_status = HOOK_SWITCH;
ACCDET_DEBUG("[Accdet]HOOK_SWITCH state not change!\n");
}else {
ACCDET_DEBUG("[Accdet] Headset has plugged out\n");
}
mutex_unlock(&accdet_eint_irq_sync_mutex);
}
else if(current_status == 1)
{
mutex_lock(&accdet_eint_irq_sync_mutex);
if(1 == eint_accdet_sync_flag) {
int multi_key = NO_KEY;
multi_key = multi_key_detection(current_status);
accdet_status = MIC_BIAS;
cable_type = HEADSET_MIC;
}else {
ACCDET_DEBUG("[Accdet] Headset has plugged out\n");
}
mutex_unlock(&accdet_eint_irq_sync_mutex);
accdet_auxadc_switch(0);
#ifdef ACCDET_PIN_RECOGNIZATION
cable_pin_recognition = 0;
ACCDET_DEBUG("[Accdet] cable_pin_recognition = %d\n", cable_pin_recognition);
pmic_pwrap_write(ACCDET_PWM_WIDTH, REGISTER_VALUE(cust_headset_settings->pwm_width));
pmic_pwrap_write(ACCDET_PWM_THRESH, REGISTER_VALUE(cust_headset_settings->pwm_thresh));
#endif
//ALPS00038030:reduce the time of remote button pressed during incoming call
//solution: reduce hook switch debounce time to 0x400
pmic_pwrap_write(ACCDET_DEBOUNCE0, button_press_debounce);
//#ifdef ACCDET_LOW_POWER
//wake_unlock(&accdet_timer_lock);//add for suspend disable accdet more than 5S
//#endif
}
else if(current_status == 3)
{
#ifdef ACCDET_PIN_RECOGNIZATION
cable_pin_recognition = 0;
ACCDET_DEBUG("[Accdet] cable_pin_recognition = %d\n", cable_pin_recognition);
mutex_lock(&accdet_eint_irq_sync_mutex);
if(1 == eint_accdet_sync_flag) {
accdet_status = PLUG_OUT;
}else {
ACCDET_DEBUG("[Accdet] Headset has plugged out\n");
}
mutex_unlock(&accdet_eint_irq_sync_mutex);
#endif
#ifdef ACCDET_MULTI_KEY_FEATURE
ACCDET_DEBUG("[Accdet] do not send plug out event in hook switch\n");
mutex_lock(&accdet_eint_irq_sync_mutex);
if(1 == eint_accdet_sync_flag) {
accdet_status = PLUG_OUT;
}else {
ACCDET_DEBUG("[Accdet] Headset has plugged out\n");
}
mutex_unlock(&accdet_eint_irq_sync_mutex);
#else
mutex_lock(&accdet_eint_irq_sync_mutex);
if(1 == eint_accdet_sync_flag) {
accdet_status = PLUG_OUT;
cable_type = NO_DEVICE;
}else {
ACCDET_DEBUG("[Accdet] Headset has plugged out\n");
}
mutex_unlock(&accdet_eint_irq_sync_mutex);
#ifdef ACCDET_EINT
disable_accdet();
#endif
#endif
}
else
{
ACCDET_DEBUG("[Accdet]HOOK_SWITCH can't change to this state!\n");
}
break;
case STAND_BY:
if(current_status == 3)
{
#ifdef ACCDET_MULTI_KEY_FEATURE
ACCDET_DEBUG("[Accdet]accdet do not send plug out event in stand by!\n");
#else
mutex_lock(&accdet_eint_irq_sync_mutex);
if(1 == eint_accdet_sync_flag) {
accdet_status = PLUG_OUT;
cable_type = NO_DEVICE;
}else {
ACCDET_DEBUG("[Accdet] Headset has plugged out\n");
}
mutex_unlock(&accdet_eint_irq_sync_mutex);
#ifdef ACCDET_EINT
disable_accdet();
#endif
#endif
}
else
{
ACCDET_DEBUG("[Accdet]STAND_BY can't change to this state!\n");
}
break;
default:
ACCDET_DEBUG("[Accdet]check_cable_type: accdet current status error!\n");
break;
}
if(!IRQ_CLR_FLAG)
{
mutex_lock(&accdet_eint_irq_sync_mutex);
if(1 == eint_accdet_sync_flag) {
while((pmic_pwrap_read(ACCDET_IRQ_STS) & IRQ_STATUS_BIT) && (wait_clear_irq_times<3))
{
ACCDET_DEBUG("[Accdet]check_cable_type: Clear interrupt on-going2....\n");
wait_clear_irq_times++;
msleep(5);
}
}
irq_temp = pmic_pwrap_read(ACCDET_IRQ_STS);
irq_temp = irq_temp & (~IRQ_CLR_BIT);
pmic_pwrap_write(ACCDET_IRQ_STS, irq_temp);
mutex_unlock(&accdet_eint_irq_sync_mutex);
IRQ_CLR_FLAG = TRUE;
ACCDET_DEBUG("[Accdet]check_cable_type:Clear interrupt:Done[0x%x]!\n", pmic_pwrap_read(ACCDET_IRQ_STS));
}
else
{
IRQ_CLR_FLAG = FALSE;
}
ACCDET_DEBUG("[Accdet]cable type:[%s], status switch:[%s]->[%s]\n",
accdet_report_string[cable_type], accdet_status_string[pre_status],
accdet_status_string[accdet_status]);
}
static void accdet_work_callback(struct work_struct *work)
{
wake_lock(&accdet_irq_lock);
check_cable_type();
mutex_lock(&accdet_eint_irq_sync_mutex);
if(1 == eint_accdet_sync_flag) {
switch_set_state((struct switch_dev *)&accdet_data, cable_type);
}else {
ACCDET_DEBUG("[Accdet] Headset has plugged out don't set accdet state\n");
}
mutex_unlock(&accdet_eint_irq_sync_mutex);
ACCDET_DEBUG( " [accdet] set state in cable_type status\n");
wake_unlock(&accdet_irq_lock);
}
//ACCDET hardware initial
static inline void accdet_init(void)
{
ACCDET_DEBUG("[Accdet]accdet hardware init\n");
pmic_pwrap_write(TOP_CKPDN_CLR, RG_ACCDET_CLK_CLR);
ACCDET_DEBUG("[Accdet]accdet TOP_CKPDN=0x%x!\n", pmic_pwrap_read(TOP_CKPDN));
//reset the accdet unit
ACCDET_DEBUG("ACCDET reset : reset start!! \n\r");
pmic_pwrap_write(TOP_RST_ACCDET_SET, ACCDET_RESET_SET);
ACCDET_DEBUG("ACCDET reset function test: reset finished!! \n\r");
pmic_pwrap_write(TOP_RST_ACCDET_CLR, ACCDET_RESET_CLR);
//init pwm frequency and duty
pmic_pwrap_write(ACCDET_PWM_WIDTH, REGISTER_VALUE(cust_headset_settings->pwm_width));
pmic_pwrap_write(ACCDET_PWM_THRESH, REGISTER_VALUE(cust_headset_settings->pwm_thresh));
pwrap_write(ACCDET_STATE_SWCTRL, 0x07);
pmic_pwrap_write(ACCDET_EN_DELAY_NUM,
(cust_headset_settings->fall_delay << 15 | cust_headset_settings->rise_delay));
// init the debounce time
#ifdef ACCDET_PIN_RECOGNIZATION
pmic_pwrap_write(ACCDET_DEBOUNCE0, cust_headset_settings->debounce0);
pmic_pwrap_write(ACCDET_DEBOUNCE1, 0xFFFF);
pmic_pwrap_write(ACCDET_DEBOUNCE3, cust_headset_settings->debounce3);
#else
pmic_pwrap_write(ACCDET_DEBOUNCE0, cust_headset_settings->debounce0);
pmic_pwrap_write(ACCDET_DEBOUNCE1, cust_headset_settings->debounce1);
pmic_pwrap_write(ACCDET_DEBOUNCE3, cust_headset_settings->debounce3);
#endif
pmic_pwrap_write(ACCDET_IRQ_STS, pmic_pwrap_read(ACCDET_IRQ_STS)&(~IRQ_CLR_BIT));
pmic_pwrap_write(INT_CON_ACCDET_SET, RG_ACCDET_IRQ_SET);
#ifdef ACCDET_EINT
// disable ACCDET unit
pre_state_swctrl = pmic_pwrap_read(ACCDET_STATE_SWCTRL);
pmic_pwrap_write(ACCDET_CTRL, ACCDET_DISABLE);
pmic_pwrap_write(ACCDET_STATE_SWCTRL, 0x0);
pmic_pwrap_write(TOP_CKPDN_SET, RG_ACCDET_CLK_SET);
#else
// enable ACCDET unit
// pmic_pwrap_write(ACCDET_STATE_SWCTRL, ACCDET_SWCTRL_EN);
pmic_pwrap_write(ACCDET_CTRL, ACCDET_ENABLE);
#endif
#ifdef GPIO_FSA8049_PIN
//mt_set_gpio_out(GPIO_FSA8049_PIN, GPIO_OUT_ONE);
#endif
#ifdef FSA8049_V_POWER
hwPowerOn(FSA8049_V_POWER, VOL_2800, "ACCDET");
#endif
}
/*-----------------------------------sysfs-----------------------------------------*/
#if DEBUG_THREAD
static int dump_register(void)
{
int i=0;
for (i=0x077A; i<= 0x079A; i+=2)
{
ACCDET_DEBUG(" ACCDET_BASE + %x=%x\n",i,pmic_pwrap_read(ACCDET_BASE + i));
}
ACCDET_DEBUG(" TOP_RST_ACCDET =%x\n",pmic_pwrap_read(TOP_RST_ACCDET));// reset register in 6320
ACCDET_DEBUG(" INT_CON_ACCDET =%x\n",pmic_pwrap_read(INT_CON_ACCDET));//INT register in 6320
ACCDET_DEBUG(" TOP_CKPDN =%x\n",pmic_pwrap_read(TOP_CKPDN));// clock register in 6320
#ifdef ACCDET_PIN_SWAP
//ACCDET_DEBUG(" 0x00004000 =%x\n",pmic_pwrap_read(0x00004000));//VRF28 power for PIN swap feature
#endif
return 0;
}
static ssize_t accdet_store_call_state(struct device_driver *ddri, const char *buf, size_t count)
{
if (sscanf(buf, "%u", &call_status) != 1) {
ACCDET_DEBUG("accdet: Invalid values\n");
return -EINVAL;
}
switch(call_status)
{
case CALL_IDLE :
ACCDET_DEBUG("[Accdet]accdet call: Idle state!\n");
break;
case CALL_RINGING :
ACCDET_DEBUG("[Accdet]accdet call: ringing state!\n");
break;
case CALL_ACTIVE :
ACCDET_DEBUG("[Accdet]accdet call: active or hold state!\n");
ACCDET_DEBUG("[Accdet]accdet_ioctl : Button_Status=%d (state:%d)\n", button_status, accdet_data.state);
//return button_status;
break;
default:
ACCDET_DEBUG("[Accdet]accdet call : Invalid values\n");
break;
}
return count;
}
//#ifdef ACCDET_PIN_RECOGNIZATION
static ssize_t show_pin_recognition_state(struct device_driver *ddri, char *buf)
{
#ifdef ACCDET_PIN_RECOGNIZATION
ACCDET_DEBUG("ACCDET show_pin_recognition_state = %d\n", cable_pin_recognition);
return sprintf(buf, "%u\n", cable_pin_recognition);
#else
return sprintf(buf, "%u\n", 0);
#endif
}
static DRIVER_ATTR(accdet_pin_recognition, 0664, show_pin_recognition_state, NULL);
static DRIVER_ATTR(accdet_call_state, 0664, NULL, accdet_store_call_state);
static int g_start_debug_thread =0;
static struct task_struct *thread = NULL;
static int g_dump_register=0;
static int dbug_thread(void *unused)
{
while(g_start_debug_thread)
{
if(g_dump_register)
{
dump_register();
//dump_pmic_register();
}
msleep(500);
}
return 0;
}
//static ssize_t store_trace_value(struct device_driver *ddri, const char *buf, size_t count)
static ssize_t store_accdet_start_debug_thread(struct device_driver *ddri, const char *buf, size_t count)
{
unsigned int start_flag;
int error;
if (sscanf(buf, "%u", &start_flag) != 1) {
ACCDET_DEBUG("accdet: Invalid values\n");
return -EINVAL;
}
ACCDET_DEBUG("[Accdet] start flag =%d \n",start_flag);
g_start_debug_thread = start_flag;
if(1 == start_flag)
{
thread = kthread_run(dbug_thread, 0, "ACCDET");
if (IS_ERR(thread))
{
error = PTR_ERR(thread);
ACCDET_DEBUG( " failed to create kernel thread: %d\n", error);
}
}
return count;
}
static ssize_t store_accdet_set_headset_mode(struct device_driver *ddri, const char *buf, size_t count)
{
unsigned int value;
//int error;
if (sscanf(buf, "%u", &value) != 1) {
ACCDET_DEBUG("accdet: Invalid values\n");
return -EINVAL;
}
ACCDET_DEBUG("[Accdet]store_accdet_set_headset_mode value =%d \n",value);
return count;
}
static ssize_t store_accdet_dump_register(struct device_driver *ddri, const char *buf, size_t count)
{
unsigned int value;
// int error;
if (sscanf(buf, "%u", &value) != 1)
{
ACCDET_DEBUG("accdet: Invalid values\n");
return -EINVAL;
}
g_dump_register = value;
ACCDET_DEBUG("[Accdet]store_accdet_dump_register value =%d \n",value);
return count;
}
/*----------------------------------------------------------------------------*/
static DRIVER_ATTR(dump_register, S_IWUSR | S_IRUGO, NULL, store_accdet_dump_register);
static DRIVER_ATTR(set_headset_mode, S_IWUSR | S_IRUGO, NULL, store_accdet_set_headset_mode);
static DRIVER_ATTR(start_debug, S_IWUSR | S_IRUGO, NULL, store_accdet_start_debug_thread);
/*----------------------------------------------------------------------------*/
static struct driver_attribute *accdet_attr_list[] = {
&driver_attr_start_debug,
&driver_attr_set_headset_mode,
&driver_attr_dump_register,
&driver_attr_accdet_call_state,
//#ifdef ACCDET_PIN_RECOGNIZATION
&driver_attr_accdet_pin_recognition,
//#endif
};
static int accdet_create_attr(struct device_driver *driver)
{
int idx, err = 0;
int num = (int)(sizeof(accdet_attr_list)/sizeof(accdet_attr_list[0]));
if (driver == NULL)
{
return -EINVAL;
}
for(idx = 0; idx < num; idx++)
{
if((err = driver_create_file(driver, accdet_attr_list[idx])))
{
ACCDET_DEBUG("driver_create_file (%s) = %d\n", accdet_attr_list[idx]->attr.name, err);
break;
}
}
return err;
}
#endif
int mt_accdet_probe(void)
{
int ret = 0;
#ifdef SW_WORK_AROUND_ACCDET_REMOTE_BUTTON_ISSUE
struct task_struct *keyEvent_thread = NULL;
int error=0;
#endif
#if DEBUG_THREAD
struct platform_driver accdet_driver_hal = accdet_driver_func();
#endif
struct headset_key_custom* press_key_time = get_headset_key_custom_setting();
ACCDET_DEBUG("[Accdet]accdet_probe begin!\n");
if(accdet_tuning_data!=NULL){
ACCDET_DEBUG("ACCDET SSB Use accdet bin\n");
accdet_2v8_mode = accdet_tuning_data->accdet_mode;
}else{
ACCDET_DEBUG("ACCDET mode use default vaule\n");
accdet_2v8_mode = 1;
}
//------------------------------------------------------------------
// below register accdet as switch class
//------------------------------------------------------------------
accdet_data.name = "h2w";
accdet_data.index = 0;
accdet_data.state = NO_DEVICE;
cust_headset_settings = get_cust_headset_settings();
ret = switch_dev_register(&accdet_data);
if(ret)
{
ACCDET_DEBUG("[Accdet]switch_dev_register returned:%d!\n", ret);
return 1;
}
//------------------------------------------------------------------
// Create normal device for auido use
//------------------------------------------------------------------
ret = alloc_chrdev_region(&accdet_devno, 0, 1, ACCDET_DEVNAME);
if (ret)
{
ACCDET_DEBUG("[Accdet]alloc_chrdev_region: Get Major number error!\n");
}
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)
{
ACCDET_DEBUG("[Accdet]accdet error: cdev_add\n");
}
accdet_class = class_create(THIS_MODULE, ACCDET_DEVNAME);
// if we want auto creat device node, we must call this
accdet_nor_device = device_create(accdet_class, NULL, accdet_devno, NULL, ACCDET_DEVNAME);
//------------------------------------------------------------------
// Create input device
//------------------------------------------------------------------
kpd_accdet_dev = input_allocate_device();
if (!kpd_accdet_dev)
{
ACCDET_DEBUG("[Accdet]kpd_accdet_dev : fail!\n");
return -ENOMEM;
}
//define multi-key keycode
__set_bit(EV_KEY, kpd_accdet_dev->evbit);
__set_bit(KEY_CALL, kpd_accdet_dev->keybit);
__set_bit(KEY_ENDCALL, kpd_accdet_dev->keybit);
__set_bit(KEY_NEXTSONG, kpd_accdet_dev->keybit);
__set_bit(KEY_PREVIOUSSONG, kpd_accdet_dev->keybit);
__set_bit(KEY_PLAYPAUSE, kpd_accdet_dev->keybit);
__set_bit(KEY_STOPCD, kpd_accdet_dev->keybit);
__set_bit(KEY_VOLUMEDOWN, kpd_accdet_dev->keybit);
__set_bit(KEY_VOLUMEUP, kpd_accdet_dev->keybit);
kpd_accdet_dev->id.bustype = BUS_HOST;
kpd_accdet_dev->name = "ACCDET";
if(input_register_device(kpd_accdet_dev))
{
ACCDET_DEBUG("[Accdet]kpd_accdet_dev register : fail!\n");
}else
{
ACCDET_DEBUG("[Accdet]kpd_accdet_dev register : success!!\n");
}
//------------------------------------------------------------------
// Create workqueue
//------------------------------------------------------------------
accdet_workqueue = create_singlethread_workqueue("accdet");
INIT_WORK(&accdet_work, accdet_work_callback);
//------------------------------------------------------------------
// wake lock
//------------------------------------------------------------------
wake_lock_init(&accdet_suspend_lock, WAKE_LOCK_SUSPEND, "accdet wakelock");
wake_lock_init(&accdet_irq_lock, WAKE_LOCK_SUSPEND, "accdet irq wakelock");
wake_lock_init(&accdet_key_lock, WAKE_LOCK_SUSPEND, "accdet key wakelock");
wake_lock_init(&accdet_timer_lock, WAKE_LOCK_SUSPEND, "accdet timer wakelock");
#ifdef SW_WORK_AROUND_ACCDET_REMOTE_BUTTON_ISSUE
init_waitqueue_head(&send_event_wq);
//start send key event thread
keyEvent_thread = kthread_run(sendKeyEvent, 0, "keyEvent_send");
if (IS_ERR(keyEvent_thread))
{
error = PTR_ERR(keyEvent_thread);
ACCDET_DEBUG( " failed to create kernel thread: %d\n", error);
}
#endif
#if DEBUG_THREAD
if((ret = accdet_create_attr(&accdet_driver_hal.driver))!=0)
{
ACCDET_DEBUG("create attribute err = %d\n", ret);
}
#endif
long_press_time = press_key_time->headset_long_press_time;
ACCDET_DEBUG("[Accdet]accdet_probe : ACCDET_INIT\n");
if (g_accdet_first == 1)
{
long_press_time_ns = (s64)long_press_time * NSEC_PER_MSEC;
eint_accdet_sync_flag = 1;
//Accdet Hardware Init
pmic_pwrap_write(ACCDET_RSV, ACCDET_1V9_MODE_OFF);
ACCDET_DEBUG("ACCDET use in 1.9V mode!! \n");
accdet_init();
queue_work(accdet_workqueue, &accdet_work); //schedule a work for the first detection
#ifdef ACCDET_EINT
accdet_eint_workqueue = create_singlethread_workqueue("accdet_eint");
INIT_WORK(&accdet_eint_work, accdet_eint_work_callback);
accdet_setup_eint();
accdet_disable_workqueue = create_singlethread_workqueue("accdet_disable");
INIT_WORK(&accdet_disable_work, disable_micbias_callback);
#endif
g_accdet_first = 0;
}
ACCDET_DEBUG("[Accdet]accdet_probe done!\n");
//#ifdef ACCDET_PIN_SWAP
//pmic_pwrap_write(0x0400, 0x1000);
//ACCDET_DEBUG("[Accdet]accdet enable VRF28 power!\n");
//#endif
return 0;
}
void mt_accdet_remove(void)
{
ACCDET_DEBUG("[Accdet]accdet_remove begin!\n");
//cancel_delayed_work(&accdet_work);
#ifdef ACCDET_EINT
destroy_workqueue(accdet_eint_workqueue);
#endif
destroy_workqueue(accdet_workqueue);
switch_dev_unregister(&accdet_data);
device_del(accdet_nor_device);
class_destroy(accdet_class);
cdev_del(accdet_cdev);
unregister_chrdev_region(accdet_devno,1);
input_unregister_device(kpd_accdet_dev);
ACCDET_DEBUG("[Accdet]accdet_remove Done!\n");
}
void mt_accdet_suspend(void) // only one suspend mode
{
//#ifdef ACCDET_PIN_SWAP
// pmic_pwrap_write(0x0400, 0x0);
// accdet_FSA8049_disable();
//#endif
#ifdef ACCDET_MULTI_KEY_FEATURE
ACCDET_DEBUG("[Accdet] in suspend1: ACCDET_IRQ_STS = 0x%x\n", pmic_pwrap_read(ACCDET_IRQ_STS));
#else
#if 0
#ifdef ACCDET_EINT
if(accdet_get_enable_RG()&& call_status == 0)
{
//record accdet status
//ACCDET_DEBUG("[Accdet]accdet_working_in_suspend\n");
printk(KERN_DEBUG "[Accdet]accdet_working_in_suspend\n");
g_accdet_working_in_suspend = 1;
pre_state_swctrl = accdet_get_swctrl();
// disable ACCDET unit
accdet_disable_hal();
//disable_clock
accdet_disable_clk();
}
#else
// disable ACCDET unit
if(call_status == 0)
{
pre_state_swctrl = accdet_get_swctrl();
accdet_disable_hal();
//disable_clock
accdet_disable_clk();
}
#endif
#endif
printk(KERN_DEBUG "[Accdet]accdet_suspend: ACCDET_CTRL=[0x%x], STATE=[0x%x]->[0x%x]\n", pmic_pwrap_read(ACCDET_CTRL), pre_state_swctrl, pmic_pwrap_read(ACCDET_STATE_SWCTRL));
#endif
}
void mt_accdet_resume(void) // wake up
{
//#ifdef ACCDET_PIN_SWAP
// pmic_pwrap_write(0x0400, 0x1000);
// accdet_FSA8049_enable();
//#endif
#ifdef ACCDET_MULTI_KEY_FEATURE
ACCDET_DEBUG("[Accdet] in resume1: ACCDET_IRQ_STS = 0x%x\n", pmic_pwrap_read(ACCDET_IRQ_STS));
#else
#if 0
#ifdef ACCDET_EINT
if(1==g_accdet_working_in_suspend && 0== call_status)
{
//enable_clock
accdet_enable_hal(pre_state_swctrl);
//clear g_accdet_working_in_suspend
g_accdet_working_in_suspend =0;
ACCDET_DEBUG("[Accdet]accdet_resume : recovery accdet register\n");
}
#else
if(call_status == 0)
{
accdet_enable_hal(pre_state_swctrl);
}
#endif
#endif
printk(KERN_DEBUG "[Accdet]accdet_resume: ACCDET_CTRL=[0x%x], STATE_SWCTRL=[0x%x]\n", pmic_pwrap_read(ACCDET_CTRL), pmic_pwrap_read(ACCDET_STATE_SWCTRL));
#endif
}
/**********************************************************************
//add for IPO-H need update headset state when resume
***********************************************************************/
#ifdef ACCDET_PIN_RECOGNIZATION
struct timer_list accdet_disable_ipoh_timer;
static void mt_accdet_pm_disable(unsigned long a)
{
if (cable_type == NO_DEVICE && eint_accdet_sync_flag ==0) {
//disable accdet
pre_state_swctrl = pmic_pwrap_read(ACCDET_STATE_SWCTRL);
pmic_pwrap_write(ACCDET_CTRL, ACCDET_DISABLE);
pmic_pwrap_write(ACCDET_STATE_SWCTRL, 0);
//disable clock
pmic_pwrap_write(TOP_CKPDN_SET, RG_ACCDET_CLK_SET);
printk("[Accdet]daccdet_pm_disable: disable!\n");
}
else
{
printk("[Accdet]daccdet_pm_disable: enable!\n");
}
}
#endif
void mt_accdet_pm_restore_noirq(void)
{
int current_status_restore = 0;
printk("[Accdet]accdet_pm_restore_noirq start!\n");
//enable accdet
pmic_pwrap_write(ACCDET_STATE_SWCTRL, (pmic_pwrap_read(ACCDET_STATE_SWCTRL)|ACCDET_SWCTRL_IDLE_EN));
// enable ACCDET unit
ACCDET_DEBUG("accdet: enable_accdet\n");
//enable clock
pmic_pwrap_write(TOP_CKPDN_CLR, RG_ACCDET_CLK_CLR);
enable_accdet(ACCDET_SWCTRL_EN);
eint_accdet_sync_flag = 1;
current_status_restore = ((pmic_pwrap_read(ACCDET_STATE_RG) & 0xc0)>>6); //AB
switch (current_status_restore) {
case 0: //AB=0
cable_type = HEADSET_NO_MIC;
accdet_status = HOOK_SWITCH;
break;
case 1: //AB=1
cable_type = HEADSET_MIC;
accdet_status = MIC_BIAS;
break;
case 3: //AB=3
cable_type = NO_DEVICE;
accdet_status = PLUG_OUT;
break;
default:
printk("[Accdet]accdet_pm_restore_noirq: accdet current status error!\n");
break;
}
switch_set_state((struct switch_dev *)&accdet_data, cable_type);
if (cable_type == NO_DEVICE) {
#ifdef ACCDET_PIN_RECOGNIZATION
init_timer(&accdet_disable_ipoh_timer);
accdet_disable_ipoh_timer.expires = jiffies + 3*HZ;
accdet_disable_ipoh_timer.function = &mt_accdet_pm_disable;
accdet_disable_ipoh_timer.data = ((unsigned long) 0 );
add_timer(&accdet_disable_ipoh_timer);
printk("[Accdet]enable! pm timer\n");
#else
//eint_accdet_sync_flag = 0;
//disable accdet
pre_state_swctrl = pmic_pwrap_read(ACCDET_STATE_SWCTRL);
pmic_pwrap_write(ACCDET_CTRL, ACCDET_DISABLE);
pmic_pwrap_write(ACCDET_STATE_SWCTRL, 0);
//disable clock
pmic_pwrap_write(TOP_CKPDN_SET, RG_ACCDET_CLK_SET);
#endif
}
}
////////////////////////////////////IPO_H end/////////////////////////////////////////////
long mt_accdet_unlocked_ioctl(unsigned int cmd, unsigned long arg)
{
// bool ret = true;
switch(cmd)
{
case ACCDET_INIT :
break;
case SET_CALL_STATE :
call_status = (int)arg;
ACCDET_DEBUG("[Accdet]accdet_ioctl : CALL_STATE=%d \n", call_status);
break;
case GET_BUTTON_STATUS :
ACCDET_DEBUG("[Accdet]accdet_ioctl : Button_Status=%d (state:%d)\n", button_status, accdet_data.state);
return button_status;
default:
ACCDET_DEBUG("[Accdet]accdet_ioctl : default\n");
break;
}
return 0;
}