blob: dd46cfe8017bff00284ba3caa5de5fcfdc72468a [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 "tpd.h"
#include <linux/interrupt.h>
#include <cust_eint.h>
#include <linux/i2c.h>
#include <linux/sched.h>
#include <linux/kthread.h>
#include <linux/rtpm_prio.h>
#include <linux/wait.h>
#include <linux/time.h>
#include <linux/delay.h>
#include <linux/dma-mapping.h>
#include "tpd_custom_ft6x06.h"
#include <mach/mt_pm_ldo.h>
#include <mach/mt_typedefs.h>
#include <mach/mt_boot.h>
#include <linux/jiffies.h>
#ifdef TPD_PROXIMITY
#include <linux/hwmsensor.h>
#include <linux/hwmsen_dev.h>
#include <linux/sensors_io.h>
#endif
#include "focaltech_ctl.h"
#include <cust_gpio_usage.h>
#ifdef FTS_AUTO_TP_UPGRADE
#include "ftbin_HRC.h"
#include "ftbin_yeji.h"
#include "ftbin_jiemian.h"
#endif
#define TPD_OK 0
//#define FTS_AUTO_TP_UPGRADE
#define FTS_SUPPORT_TRACK_ID
#define APS_ERR(fmt, args...) printk(KERN_ERR "%d : "fmt, __FUNCTION__, __LINE__, ##args)
extern struct tpd_device *tpd;
static DECLARE_WAIT_QUEUE_HEAD(waiter);
static void tpd_eint_interrupt_handler(void);
extern void mt_eint_unmask(unsigned int line);
extern void mt_eint_mask(unsigned int line);
extern void mt_eint_set_hw_debounce(unsigned int eint_num, unsigned int ms);
extern unsigned int mt_eint_set_sens(unsigned int eint_num, unsigned int sens);
extern void mt_eint_registration(unsigned int eint_num, unsigned int flag,
void (EINT_FUNC_PTR) (void), unsigned int is_auto_umask);
static int __devinit tpd_probe(struct i2c_client *client, const struct i2c_device_id *id);
static int tpd_detect(struct i2c_client *client, struct i2c_board_info *info);
static int __devexit tpd_remove(struct i2c_client *client);
static int touch_event_handler(void *unused);
static struct tag_para_touch_ssb_data_single touch_ssb_data = {0};
static int tpd_keys_local[TPD_KEY_COUNT] = TPD_KEYS;
static int tpd_keys_dim_local_BYD[TPD_KEY_COUNT][4] = TPD_KEYS_DIM_BYD;
static int tpd_keys_dim_local_NB[TPD_KEY_COUNT][4] = TPD_KEYS_DIM_NB;
kal_uint8 g_pre_tp_charger_flag = 0;
kal_uint8 g_tp_charger_flag = 0;
//BEIN <tp> <DATE20130514> <tp proximity> zhangxiaofei
#ifdef TPD_PROXIMITY
#define TPD_PROXIMITY_ENABLE_REG 0xB0
#define TPD_PROXIMITY_CLOSE_VALUE 0xC0
#define TPD_PROXIMITY_FARAWAY_VALUE 0xE0
static u8 tpd_proximity_flag = 0;
static u8 tpd_proximity_flag_one = 0; //add for tpd_proximity by wangdongfang
static u8 tpd_proximity_detect = 1; //0-->close ; 1--> far away
#endif
//END <tp> <DATE20130514> <tp proximity> zhangxiaofei
static void tinno_update_tp_button_dim(int panel_vendor)
{
if ( FTS_CTP_VENDOR_NANBO == panel_vendor ){
tpd_button_setting(TPD_KEY_COUNT, touch_ssb_data.tpd_key_local, touch_ssb_data.tpd_key_dim_local);
}else{
tpd_button_setting(TPD_KEY_COUNT, touch_ssb_data.tpd_key_local, touch_ssb_data.tpd_key_dim_local);
}
}
static char tpd_desc[50];
static int tpd_fw_version;
static tinno_ts_data *g_pts = NULL;
static volatile int tpd_flag;
#define DRIVER_NAME "ft6x06"
static const struct i2c_device_id ft6x06_tpd_id[] = {{DRIVER_NAME,0},{}};
static struct i2c_board_info __initdata ft6x06_i2c_tpd[]={{I2C_BOARD_INFO(DRIVER_NAME, TPD_I2C_SLAVE_ADDR)}};
static struct i2c_driver tpd_i2c_driver = {
.driver = {
.name = DRIVER_NAME,
},
.probe = tpd_probe,
.remove = __devexit_p(tpd_remove),
.id_table = ft6x06_tpd_id,
.detect = tpd_detect,
};
static void tpd_down(tinno_ts_data *ts, int x, int y, int pressure, int trackID)
{
CTP_DBG("x=%03d, y=%03d, pressure=%03d, ID=%03d", x, y, pressure, trackID);
input_report_abs(tpd->dev, ABS_PRESSURE, pressure);
input_report_abs(tpd->dev, ABS_MT_PRESSURE, pressure);
input_report_key(tpd->dev, BTN_TOUCH, 1);
input_report_abs(tpd->dev, ABS_MT_POSITION_X, x);
input_report_abs(tpd->dev, ABS_MT_POSITION_Y, y);
#ifdef FTS_SUPPORT_TRACK_ID
input_report_abs(tpd->dev, ABS_MT_TRACKING_ID, trackID);
#endif
input_report_abs(tpd->dev, ABS_MT_WIDTH_MAJOR, pressure*pressure/112);
input_report_abs(tpd->dev, ABS_MT_TOUCH_MAJOR, pressure*pressure/112);
input_mt_sync(tpd->dev);
__set_bit(trackID, &ts->fingers_flag);
ts->touch_point_pre[trackID].x=x;
ts->touch_point_pre[trackID].y=y;
if (FACTORY_BOOT == get_boot_mode() || RECOVERY_BOOT == get_boot_mode()) {
tpd_button(x, y, 1);
}
TPD_DOWN_DEBUG_TRACK(x,y);
}
static int tpd_up(tinno_ts_data *ts, int x, int y, int pressure, int trackID)
{
if (FACTORY_BOOT == get_boot_mode() || RECOVERY_BOOT == get_boot_mode()) {
CTP_DBG("x=%03d, y=%03d, ID=%03d", x, y, trackID);
input_report_abs(tpd->dev, ABS_PRESSURE, 0);
input_report_abs(tpd->dev, ABS_MT_PRESSURE, 0);
input_report_key(tpd->dev, BTN_TOUCH, 0);
input_report_abs(tpd->dev, ABS_MT_POSITION_X, x);
input_report_abs(tpd->dev, ABS_MT_POSITION_Y, y);
#ifdef FTS_SUPPORT_TRACK_ID
input_report_abs(tpd->dev, ABS_MT_TRACKING_ID, trackID);
#endif
input_report_abs(tpd->dev, ABS_MT_WIDTH_MAJOR, 0);
input_report_abs(tpd->dev, ABS_MT_TOUCH_MAJOR, 0);// This must be placed at the last one.
input_mt_sync(tpd->dev);
}else{//Android 4.0 don't need to report these up events.
int i, have_down_cnt = 0;
for ( i=0; i < TINNO_TOUCH_TRACK_IDS; i++ ){
if ( test_bit(i, &ts->fingers_flag) ){
++have_down_cnt;
}
}
if ( have_down_cnt < 2 ){
input_mt_sync(tpd->dev);
}
CTP_DBG("x=%03d, y=%03d, ID=%03d, have_down=%d", x, y, trackID, have_down_cnt);
}
__clear_bit(trackID, &ts->fingers_flag);
TPD_UP_DEBUG_TRACK(x,y);
if (FACTORY_BOOT == get_boot_mode() || RECOVERY_BOOT == get_boot_mode()) {
tpd_button(x, y, 0);
}
return 0;
}
static void tpd_dump_touchinfo(tinno_ts_data *ts)
{
uint8_t *pdata = ts->buffer;
CTP_DBG("0x%02x 0x%02x 0x%02x"
" 0x%02x 0x%02x 0x%02x 0x%02x 0x%02x 0x%02x"
" 0x%02x 0x%02x 0x%02x 0x%02x 0x%02x 0x%02x"
" 0x%02x 0x%02x 0x%02x 0x%02x 0x%02x 0x%02x"
" 0x%02x 0x%02x 0x%02x 0x%02x 0x%02x 0x%02x"
,
pdata[0], pdata[1], pdata[2],
pdata[3], pdata[4], pdata[5], pdata[6], pdata[7], pdata[8],
pdata[9], pdata[10], pdata[11], pdata[12], pdata[13], pdata[15],
pdata[15], pdata[16], pdata[17], pdata[18], pdata[19], pdata[20],
pdata[21], pdata[22], pdata[23], pdata[24], pdata[25], pdata[26]);
}
static void ft_map_coordinate(int *pX, int *pY)
{
int x = *pX, y = *pY;
*pX = x * 540 / 760;
*pY = y * 960 / 1280;
}
static int tpd_touchinfo(tinno_ts_data *ts, tinno_ts_point *touch_point)
{
int i = 0;
int iInvalidTrackIDs = 0;
int iTouchID, iSearchDeep;
fts_report_data_t *pReportData = (fts_report_data_t *)ts->buffer;
if ( tpd_read_touchinfo(ts) ){
CTP_DBG("Read touch information error. \n");
return -EAGAIN;
}
if ( 0 != pReportData->device_mode ){
CTP_DBG("device mode is %d\n", pReportData->device_mode);
return -EPERM;
}
//We need only valid points...
if ( pReportData->fingers > TINNO_TOUCH_TRACK_IDS ){
CTP_DBG("fingers is %d\n", pReportData->fingers);
return -EAGAIN;
}
// For processing gestures.
if (pReportData->gesture >= 0xF0 && pReportData->gesture <= 0xF3) {
//fts_6x06_parase_keys(ts, pReportData);
}
iSearchDeep = 0;
#ifdef FTS_SUPPORT_TRACK_ID
for ( i = 0; i < TINNO_TOUCH_TRACK_IDS; i++ ){
iSearchDeep += ((pReportData->xy_data[i].event_flag != FTS_EF_RESERVED)?1:0);
}
#else
if (pReportData->fingers >= ts->last_fingers ){
iSearchDeep = pReportData->fingers;
}else{
iSearchDeep = ts->last_fingers;
}
ts->last_fingers = pReportData->fingers;
#endif
if ( iSearchDeep ) {
#ifdef FTS_SUPPORT_TRACK_ID
for ( i=0; i < TINNO_TOUCH_TRACK_IDS; i++ ){
#else
for ( i=0; i < iSearchDeep; i++ ){
#endif
if (pReportData->xy_data[i].event_flag != FTS_EF_RESERVED) {
#ifdef FTS_SUPPORT_TRACK_ID
iTouchID = pReportData->xy_data[i].touch_id;
if ( iTouchID >= TINNO_TOUCH_TRACK_IDS )
{
//CTP_DBG("i: Invalied Track ID(%d)\n!", i, iTouchID);
iInvalidTrackIDs++;
continue;
}
#else
iTouchID = i;
#endif
touch_point[iTouchID].flag = pReportData->xy_data[i].event_flag;
touch_point[iTouchID].x = pReportData->xy_data[i].x_h << 8 | pReportData->xy_data[i].x_l;
touch_point[iTouchID].y = pReportData->xy_data[i].y_h << 8 | pReportData->xy_data[i].y_l;
touch_point[iTouchID].pressure = pReportData->xy_data[i].pressure;
#ifdef TPD_FIRST_FIRWARE
ft_map_coordinate(&(touch_point[iTouchID].x), &(touch_point[iTouchID].y));
#endif
}else{
//CTP_DBG("We got a invalied point, we take it the same as a up event!");
//CTP_DBG("As it has no valid track ID, we assume it's order is the same as it's layout in the memory!");
//touch_point[i].flag = FTS_EF_RESERVED;
}
}
if ( TINNO_TOUCH_TRACK_IDS == iInvalidTrackIDs ){
CTP_DBG("All points are Invalied, Ignore the interrupt!\n");
return -EAGAIN;
}
}
CTP_DBG("p0_flag=0x%x x0=0x%03x y0=0x%03x pressure0=0x%03x "
"p1_flag=0x%x x1=0x%03x y1=0x%03x pressure1=0x%03x "
"gesture = 0x%x fingers=0x%x",
touch_point[0].flag, touch_point[0].x, touch_point[0].y, touch_point[0].pressure,
touch_point[1].flag, touch_point[1].x, touch_point[1].y, touch_point[1].pressure,
pReportData->gesture, pReportData->fingers);
return 0;
};
//BEGIN <touch panel> <DATE20130831> <tp proximity> zhangxiaofei
#if defined TPD_PROXIMITY
int tpd_read_ps(void)
{
tpd_proximity_detect;
return 0;
}
static int tpd_get_ps_value(void)
{
return tpd_proximity_detect;
}
static int tpd_enable_ps(int enable)
{
u8 state;
int ret = -1;
i2c_smbus_read_i2c_block_data(g_pts->client, TPD_PROXIMITY_ENABLE_REG, 1, &state);
printk("[proxi_5206]read: 999 0xb0's value is 0x%02X\n", state);
if (enable){
state |= 0x01;
tpd_proximity_flag = 1;
TPD_PROXIMITY_DBG("[proxi_5206]ps function is on\n");
}else{
state &= 0x00;
tpd_proximity_flag = 0;
TPD_PROXIMITY_DBG("[proxi_5206]ps function is off\n");
}
ret = i2c_smbus_write_i2c_block_data(g_pts->client, TPD_PROXIMITY_ENABLE_REG, 1, &state);
TPD_PROXIMITY_DBG("[proxi_5206]write: 0xB0's value is 0x%02X\n", state);
return 0;
}
int tpd_ps_operate(void* self, uint32_t command, void* buff_in, int size_in,
void* buff_out, int size_out, int* actualout)
{
int err = 0;
int value;
hwm_sensor_data *sensor_data;
TPD_DEBUG("[proxi_5206]command = 0x%02X\n", command);
switch (command)
{
case SENSOR_DELAY:
if((buff_in == NULL) || (size_in < sizeof(int)))
{
APS_ERR("Set delay parameter error!\n");
err = -EINVAL;
}
// Do nothing
break;
case SENSOR_ENABLE:
if((buff_in == NULL) || (size_in < sizeof(int)))
{
APS_ERR("Enable sensor parameter error!\n");
err = -EINVAL;
}
else
{
value = *(int *)buff_in;
if(value)
{
if((tpd_enable_ps(1) != 0))
{
APS_ERR("enable ps fail: %d\n", err);
return -1;
}
}
else
{
if((tpd_enable_ps(0) != 0))
{
APS_ERR("disable ps fail: %d\n", err);
return -1;
}
}
}
break;
case SENSOR_GET_DATA:
if((buff_out == NULL) || (size_out< sizeof(hwm_sensor_data)))
{
APS_ERR("get sensor data parameter error!\n");
err = -EINVAL;
}
else
{
sensor_data = (hwm_sensor_data *)buff_out;
if((err = tpd_read_ps()))
{
err = -1;;
}
else
{
sensor_data->values[0] = tpd_get_ps_value();
TPD_PROXIMITY_DBG("huang sensor_data->values[0] 1082 = %d\n", sensor_data->values[0]);
sensor_data->value_divide = 1;
sensor_data->status = SENSOR_STATUS_ACCURACY_MEDIUM;
}
}
break;
default:
APS_ERR("proxmy sensor operate function no this parameter %d!\n", command);
err = -1;
break;
}
return err;
}
#endif
//END <touch panel> <DATE20130831> <tp proximity> zhangxiaofei
static int touch_event_handler(void *para)
{
int i;
tinno_ts_point touch_point[TINNO_TOUCH_TRACK_IDS];
struct sched_param param = { .sched_priority = RTPM_PRIO_TPD };
tinno_ts_data *ts = (tinno_ts_data *)para;
sched_setscheduler(current, SCHED_RR, &param);
//BEGIN <touch panel> <DATE20130831> <tp proximity> zhangxiaofei
#if defined TPD_PROXIMITY
int err;
hwm_sensor_data sensor_data;
u8 proximity_status;
u8 state;
#endif
//END <touch panel> <DATE20130831> <tp proximity> zhangxiaofei
do {
set_current_state(TASK_INTERRUPTIBLE);
wait_event_interruptible(waiter, tpd_flag!=0);
tpd_flag = 0;
memset(touch_point, FTS_INVALID_DATA, sizeof(touch_point));
set_current_state(TASK_RUNNING);
//BEGIN <touch panel> <DATE20130831> <tp proximity> zhangxiaofei
#if defined TPD_PROXIMITY
if (tpd_proximity_flag == 1)
{
i2c_smbus_read_i2c_block_data(g_pts->client, TPD_PROXIMITY_ENABLE_REG, 1, &state);
TPD_PROXIMITY_DBG("proxi_5206 0xB0 state value is 1131 0x%02X\n", state);
if(!(state&0x01))
{
tpd_enable_ps(1);
}
i2c_smbus_read_i2c_block_data(g_pts->client, 0x01, 1, &proximity_status);
TPD_PROXIMITY_DBG("proxi_5206 0x01 value is 1139 0x%02X\n", proximity_status);
if (proximity_status == TPD_PROXIMITY_CLOSE_VALUE)
{
tpd_proximity_detect = 0;
}
else if(proximity_status == TPD_PROXIMITY_FARAWAY_VALUE)
{
tpd_proximity_detect = 1;
}
TPD_PROXIMITY_DBG("tpd_proximity_detect 1149 = %d\n", tpd_proximity_detect);
if ((err = tpd_read_ps()))
{
TPD_PROXIMITY_DBG("proxi_5206 read ps data 1156: %d\n", err);
}
sensor_data.values[0] = tpd_get_ps_value();
sensor_data.value_divide = 1;
sensor_data.status = SENSOR_STATUS_ACCURACY_MEDIUM;
if ((err = hwmsen_get_interrupt_data(ID_PROXIMITY, &sensor_data)))
{
TPD_PROXIMITY_DBG(" proxi_5206 call hwmsen_get_interrupt_data failed= %d\n", err);
}
}
#endif
//END <touch panel> <DATE20130831> <tp proximity> zhangxiaofei
//BEGIN <add changing flag> <DATE20130330> <add changing flag> zhangxiaofei
if(g_tp_charger_flag != g_pre_tp_charger_flag){
g_pre_tp_charger_flag = g_tp_charger_flag;
fts_ft6x06_switch_charger_status(g_tp_charger_flag);
}
//END <add changing flag> <DATE20130330> <add changing flag> zhangxiaofei
if (!tpd_touchinfo(ts, &touch_point)) {
//report muti point then
for ( i=0; i < TINNO_TOUCH_TRACK_IDS; i++ ){
if ( FTS_INVALID_DATA != touch_point[i].x ){
if ( FTS_EF_UP == touch_point[i].flag ){
if( test_bit(i, &ts->fingers_flag) ){
tpd_up(ts, ts->touch_point_pre[i].x, ts->touch_point_pre[i].y,
touch_point[i].pressure, i);
}else{
CTP_DBG("This is a invalid up event.(%d)", i);
}
}else{//FTS_EF_CONTACT or FTS_EF_DOWN
if ( test_bit(i, &ts->fingers_flag)
&& (FTS_EF_DOWN == touch_point[i].flag) ){
CTP_DBG("Ignore a invalid down event.(%d)", i);
continue;
}
tpd_down(ts, touch_point[i].x, touch_point[i].y,
touch_point[i].pressure, i);
}
}else if ( test_bit(i, &ts->fingers_flag) ){
CTP_DBG("Complete a invalid down or move event.(%d)", i);
tpd_up(ts, ts->touch_point_pre[i].x, ts->touch_point_pre[i].y,
touch_point[i].pressure, i);
}
}
input_sync(tpd->dev);
}
mt_eint_unmask(CUST_EINT_TOUCH_PANEL_NUM);
}while(!kthread_should_stop());
mt_eint_mask(CUST_EINT_TOUCH_PANEL_NUM);
return 0;
}
static int tpd_detect (struct i2c_client *client, struct i2c_board_info *info)
{
strcpy(info->type, TPD_DEVICE);
return 0;
}
static void tpd_eint_interrupt_handler(void)
{
if ( 0 == tpd_load_status ){
return;
}
tpd_flag = 1;
wake_up_interruptible(&waiter);
}
void fts_6x06_hw_reset(void)
{
mt_set_gpio_out(GPIO_CTP_RST_PIN, GPIO_OUT_ONE);
msleep(3);//mdelay(1);
mt_set_gpio_out(GPIO_CTP_RST_PIN, GPIO_OUT_ZERO);
msleep(3);//mdelay(15);
mt_set_gpio_out(GPIO_CTP_RST_PIN, GPIO_OUT_ONE);
msleep(40);//mdelay(1);
}
static void fts_6x06_hw_init(void)
{
hwPowerOn(touch_ssb_data.power_id, VOL_2800, "TP");
hwPowerOn(MT6323_POWER_LDO_VGP1, VOL_2800, "TP");
hwPowerOn(MT6323_POWER_LDO_VGP2, VOL_2800, "TP");
hwPowerOn(MT6323_POWER_LDO_VGP3, VOL_2800, "TP");
hwPowerOn(MT65XX_POWER_LDO_VGP1, VOL_2800, "TP");
hwPowerOn(MT65XX_POWER_LDO_VGP2, VOL_2800, "TP");
hwPowerOn(MT65XX_POWER_LDO_VGP3, VOL_2800, "TP");
msleep(2);
//Reset CTP
mt_set_gpio_mode(GPIO_CTP_RST_PIN, GPIO_CTP_RST_PIN_M_GPIO);
mt_set_gpio_dir(GPIO_CTP_RST_PIN, GPIO_DIR_OUT);
mt_set_gpio_out(GPIO_CTP_RST_PIN, GPIO_OUT_ONE);
msleep(30);
}
static char *fts_get_vendor_name(int vendor_id)
{
switch(vendor_id){
case FTS_CTP_VENDOR_BYD: return "BYD"; break;
case FTS_CTP_VENDOR_TRULY: return "TRULY"; break;
case FTS_CTP_VENDOR_NANBO: return "NANBO"; break;
case FTS_CTP_VENDOR_BAOMING: return "BAOMING"; break;
case FTS_CTP_VENDOR_JIEMIAN: return "JIEMIAN"; break;
case FTS_CTP_VENDOR_YEJI: return "YEJI"; break;
case FTS_CTP_VENDOR_HUARUICHUANG: return "HUARUICHUANG";break;
case FTS_CTP_VENDOR_DEFAULT: return "DEFAULT"; break;
default: return "UNKNOWN"; break;
}
return "UNKNOWN";
}
//BEGIN<touch panel><date20131021><tp auto update>yinhuiyong
#ifdef FTS_AUTO_TP_UPGRADE
static struct task_struct * focaltech_update_thread;
static int update_firmware_thread(void *priv)
{
TPD_DEBUG("[Focaltech] enter update_firmware_thread\n");
//fts_ctpm_fw_upgrade_with_i_file();
if( 0 == memcmp(tpd_desc, "HUARUICHUANG", 12))
{
ft6x06_tp_upgrade(ftbin_HRC, sizeof(ftbin_HRC));
}else if(0 == memcmp(tpd_desc, "YEJI",4))
{
ft6x06_tp_upgrade(ftbin_YEJI, sizeof(ftbin_YEJI));
}else if(0 == memcmp(tpd_desc, "JIEMIAN",7))
{
ft6x06_tp_upgrade(ftbin_JIEMIAN, sizeof(ftbin_JIEMIAN));
}
kthread_should_stop();
return NULL;
}
int focaltech_auto_upgrade(void)
{
int err;
focaltech_update_thread = kthread_run(update_firmware_thread, 0, TPD_DEVICE);
if (IS_ERR(focaltech_update_thread)) {
err = PTR_ERR(focaltech_update_thread);
TPD_DEBUG(TPD_DEVICE " failed to create update_firmware_thread thread: %d\n", err);
}
return err;
}
#endif
//END<touch panel><date20131021><tp auto update>yinhuiyong
static int __devinit tpd_probe(struct i2c_client *client, const struct i2c_device_id *id)
{
int retval = TPD_OK;
int panel_version = 0;
int panel_vendor = 0;
int iRetry = 3;
tinno_ts_data *ts;
int ret = 0;
if ( tpd_load_status ){
CTP_DBG("Already probed a TP, needn't to probe any more!");
return -1;
}
if (!i2c_check_functionality(client->adapter, I2C_FUNC_I2C)) {
dev_err(&client->dev,"need I2C_FUNC_I2C");
ret = -ENODEV;
goto err_check_functionality_failed;
}
ts = kzalloc(sizeof(*ts), GFP_KERNEL);
if (ts == NULL) {
ret = -ENOMEM;
goto err_alloc_data_failed;
}
CTP_DBG("TPD enter tpd_probe ts=0x%p, TPD_RES_X=%d, TPD_RES_Y=%d, addr=0x%x\n", ts, TPD_RES_X, TPD_RES_Y, client->addr);
memset(ts, 0, sizeof(*ts));
g_pts = ts;
client->timing = I2C_MASTER_CLOCK;
ts->client = client;
ts->start_reg = 0x00;
atomic_set( &ts->ts_sleepState, 0 );
mutex_init(&ts->mutex);
i2c_set_clientdata(client, ts);
fts_6x06_hw_init();
msleep(120);
fts_iic_init(ts);
if ( fts_6x06_isp_init(ts) ){
goto err_isp_register;
}
while (iRetry) {
ret = ft6x06_get_vendor_version(ts, &panel_vendor, &panel_version);
if ( panel_version < 0 || panel_vendor<0 || ret<0 ){
TPD_DMESG("Product version is %d\n", panel_version);
fts_6x06_hw_reset();
}else{
break;
}
iRetry--;
msleep(15);
}
if ( panel_version < 0 || panel_vendor<0 || ret<0 ){
goto err_get_version;
}
if(touch_ssb_data.use_tpd_button == 1)
tinno_update_tp_button_dim(panel_vendor);
#ifdef CONFIG_TOUCHSCREEN_FT5X05_DISABLE_KEY_WHEN_SLIDE
if ( fts_keys_init(ts) ){
fts_keys_deinit();
goto err_get_version;
}
#endif
mt_set_gpio_mode(GPIO_CTP_EINT_PIN, GPIO_CTP_EINT_PIN_M_EINT);
mt_set_gpio_dir(GPIO_CTP_EINT_PIN, GPIO_DIR_IN);
mt_set_gpio_pull_enable(GPIO_CTP_EINT_PIN, GPIO_PULL_ENABLE);
mt_set_gpio_pull_select(GPIO_CTP_EINT_PIN, GPIO_PULL_UP);
mt_eint_registration(CUST_EINT_TOUCH_PANEL_NUM, CUST_EINT_TOUCH_PANEL_TYPE, tpd_eint_interrupt_handler, 1);
ts->thread = kthread_run(touch_event_handler, ts, TPD_DEVICE);
if (IS_ERR(ts->thread)){
retval = PTR_ERR(ts->thread);
TPD_DMESG(TPD_DEVICE " failed to create kernel thread: %d\n", retval);
goto err_start_touch_kthread;
}
tpd_load_status = 1;
mt_eint_unmask(CUST_EINT_TOUCH_PANEL_NUM);
CTP_DBG("Touch Panel Device(%s) Probe PASS\n", fts_get_vendor_name(panel_vendor));
//BEGIN <tp> <DATE20130507> <tp version> zhangxiaofei
sprintf(tpd_desc, "%s", fts_get_vendor_name(panel_vendor));
tpd_fw_version = panel_version;
//END <tp> <DATE20130507> <tp version> zhangxiaofei
//LINE<tp><DATE20130619><add for focaltech debug>zhangxiaofei
#ifdef FTS_CTL_IIC
if (ft_rw_iic_drv_init(client) < 0)
dev_err(&client->dev, "%s:[FTS] create fts control iic driver failed\n",
__func__);
#endif
//BEGIN <touch panel> <DATE20130831> <tp proximity> zhangxiaofei
#if defined TPD_PROXIMITY
struct hwmsen_object obj_ps;
int err=0;
obj_ps.polling = 0;//interrupt mode
obj_ps.sensor_operate = tpd_ps_operate;
if((err = hwmsen_attach(ID_PROXIMITY, &obj_ps)))
{
APS_ERR("proxi_fts attach fail = %d\n", err);
}
else
{
APS_ERR("proxi_fts attach ok = %d\n", err);
}
#endif
//END <touch panel> <DATE20130831> <tp proximity> zhangxiaofei
//BEGIN<touch panel><date20131028><tp auto update>yinhuiyong
#ifdef FTS_AUTO_TP_UPGRADE
focaltech_auto_upgrade();
#endif
//END<touch panel><date20131028><tp auto update>yinhuiyong
return 0;
err_start_touch_kthread:
mt_eint_mask(CUST_EINT_TOUCH_PANEL_NUM);
err_get_version:
err_isp_register:
#ifdef CONFIG_TOUCHSCREEN_POWER_DOWN_WHEN_SLEEP
hwPowerDown(MT65XX_POWER_LDO_VGP2, "touch");
#endif
fts_6x06_isp_exit();
mutex_destroy(&ts->mutex);
g_pts = NULL;
kfree(ts);
err_alloc_data_failed:
err_check_functionality_failed:
CTP_DBG("Touch Panel Device Probe FAIL\n");
return -1;
}
static int __devexit tpd_remove(struct i2c_client *client)
{
CTP_DBG("TPD removed\n");
//LINE<tp><DATE20130619><add for focaltech debug>zhangxiaofei
#ifdef FTS_CTL_IIC
ft_rw_iic_drv_exit();
#endif
return 0;
}
static int tpd_local_init(void)
{
TPD_DMESG("Focaltech FT6x06 I2C Touchscreen Driver (Built %s @ %s)\n", __DATE__, __TIME__);
if(i2c_add_driver(&tpd_i2c_driver)!=0)
{
TPD_DMESG("unable to add i2c driver.\n");
return -1;
}
if(touch_ssb_data.use_tpd_button == 1)
tinno_update_tp_button_dim(FTS_CTP_VENDOR_NANBO);
TPD_DMESG("end %s, %d\n", __FUNCTION__, __LINE__);
tpd_type_cap = 1;
return 0;
}
static void tpd_resume(struct early_suspend *h)
{
char data;
#ifdef TPD_PROXIMITY
if (tpd_proximity_flag == 1)
{
if(tpd_proximity_flag_one == 1)
{
tpd_proximity_flag_one = 0;
TPD_DMESG(TPD_DEVICE " tpd_proximity_flag_one \n");
return;
}
}
#endif
if ( g_pts ){
CTP_DBG("TPD wake up\n");
if (atomic_read(&g_pts->isp_opened)){
CTP_DBG("isp is already opened.");
return;
}
#ifdef CONFIG_TOUCHSCREEN_POWER_DOWN_WHEN_SLEEP
fts_6x06_hw_init();
#else
//BEGIN <tp> <DATE20130507> <tp resume> zhangxiaofei
// reset ctp
mt_set_gpio_mode(GPIO_CTP_RST_PIN, GPIO_CTP_RST_PIN_M_GPIO);
mt_set_gpio_dir(GPIO_CTP_RST_PIN, GPIO_DIR_OUT);
mt_set_gpio_out(GPIO_CTP_RST_PIN, GPIO_OUT_ZERO);
msleep(10);
mt_set_gpio_mode(GPIO_CTP_RST_PIN, GPIO_CTP_RST_PIN_M_GPIO);
mt_set_gpio_dir(GPIO_CTP_RST_PIN, GPIO_DIR_OUT);
mt_set_gpio_out(GPIO_CTP_RST_PIN, GPIO_OUT_ONE);
msleep(200);//add this line
CTP_DBG("TPD wake up done\n");
//END <tp> <DATE20130507> <tp resume> zhangxiaofei
#endif//CONFIG_TOUCHSCREEN_POWER_DOWN_WHEN_SLEEP
mt_eint_unmask(CUST_EINT_TOUCH_PANEL_NUM);
atomic_set( &g_pts->ts_sleepState, 0 );
}
}
//Clear the unfinished touch event, simulate a up event if there this a pen down or move event.
void ft6x06_complete_unfinished_event( void )
{
int i = 0;
for ( i=0; i < TINNO_TOUCH_TRACK_IDS; i++ ){
if ( test_bit(i, &g_pts->fingers_flag) ){
tpd_up(g_pts, g_pts->touch_point_pre[i].x, g_pts->touch_point_pre[i].y,
g_pts->touch_point_pre[i].pressure, i);
}
}
input_sync(tpd->dev);
}
static void tpd_suspend(struct early_suspend *h)
{
int ret = 0;
int iRetry = 5;
const char data = 0x3;
//NEGIN <touch panel> <DATE20130831> <tp proximity> zhangxiaofei
#ifdef TPD_PROXIMITY
if (tpd_proximity_flag == 1)
{
tpd_proximity_flag_one = 1;
return;
}
#endif
//END <touch panel> <DATE20130831> <tp proximity> zhangxiaofei
if ( g_pts ){
CTP_DBG("TPD enter sleep\n");
if (atomic_read(&g_pts->isp_opened)){
CTP_DBG("isp is already opened.");
return;
}
mt_eint_mask(CUST_EINT_TOUCH_PANEL_NUM);
#ifdef CONFIG_TOUCHSCREEN_FT5X05_DISABLE_KEY_WHEN_SLIDE
fts_6x06_key_cancel();
#endif
#ifdef CONFIG_TOUCHSCREEN_POWER_DOWN_WHEN_SLEEP
mt_set_gpio_mode(GPIO_CTP_RST_PIN, GPIO_CTP_RST_PIN_M_GPIO);
mt_set_gpio_dir(GPIO_CTP_RST_PIN, GPIO_DIR_OUT);
mt_set_gpio_out(GPIO_CTP_RST_PIN, GPIO_OUT_ZERO);
msleep(2);
#else //!CONFIG_TOUCHSCREEN_POWER_DOWN_WHEN_SLEEP
//make sure the WakeUp is high before it enter sleep mode,
//otherwise the touch can't be resumed.
//mt_set_gpio_mode(GPIO_CTP_EN_PIN, GPIO_CTP_EN_PIN_M_GPIO);
//mt_set_gpio_dir(GPIO_CTP_EN_PIN, GPIO_DIR_OUT);
//mt_set_gpio_out(GPIO_CTP_EN_PIN, GPIO_OUT_ONE);
//msleep(1);
while (iRetry) {
ret = i2c_smbus_write_i2c_block_data(g_pts->client, 0xA5, 1, &data); //TP enter sleep mode
if ( ret < 0 ){
TPD_DMESG("Enter sleep mode is %d\n", ret);
msleep(2);
//fts_6x06_hw_init();
}else{
break;
}
iRetry--;
msleep(100);
}
#endif//CONFIG_TOUCHSCREEN_POWER_DOWN_WHEN_SLEEP
atomic_set( &g_pts->ts_sleepState, 1 );
}
}
//BEGIN <touch panel> <DATE20130909> <touch panel version info> zhangxiaofei
extern int get_fw_version_ext(void);
//extern char tpd_desc[50];
int ft6x06_tpd_get_fw_version( void )
{
return get_fw_version_ext();
}
void ft6x06_tpd_get_fw_vendor_name(char * fw_vendor_name)
{
sprintf(fw_vendor_name, "%s", tpd_desc);
}
//END <touch panel> <DATE20130909> <touch panel version info> zhangxiaofei
static struct tpd_driver_t tpd_device_driver = {
.tpd_device_name = "FT6x06",
.tpd_local_init = tpd_local_init,
.suspend = tpd_suspend,
.resume = tpd_resume,
#ifdef TPD_HAVE_BUTTON
.tpd_have_button = 1,
#else
.tpd_have_button = 0,
#endif
};
/* called when loaded into kernel */
static int __init tpd_driver_init(void)
{
int err = 0;
char name[20] = "ft6x06";
printk("MediaTek FT6x06 touch panel driver init\n");
err = tpd_ssb_data_match(name, &touch_ssb_data);
if(err != 0){
printk("touch tpd_ssb_data_match error\n");
return -1;
}
printk("ft6x06 touch_ssb_data:: name:(%s), endflag:0x%x, i2c_number:0x%x, i2c_addr:0x%x,power_id:%d, use_tpd_button:%d\n",
touch_ssb_data.identifier,
touch_ssb_data.endflag,
touch_ssb_data.i2c_number,
touch_ssb_data.i2c_addr,
touch_ssb_data.power_id,
touch_ssb_data.use_tpd_button
);
ft6x06_i2c_tpd[0].addr = touch_ssb_data.i2c_addr;
i2c_register_board_info(touch_ssb_data.i2c_number, &ft6x06_i2c_tpd, sizeof(ft6x06_i2c_tpd)/sizeof(ft6x06_i2c_tpd[0]));
//add for ssb support
tpd_device_driver.tpd_have_button = touch_ssb_data.use_tpd_button;
if(tpd_driver_add(&tpd_device_driver) < 0)
TPD_DMESG("add FT6x06 driver failed\n");
return 0;
}
/* should never be called */
static void __exit tpd_driver_exit(void)
{
TPD_DMESG("MediaTek FT6x06 touch panel driver exit\n");
tpd_driver_remove(&tpd_device_driver);
}
module_init(tpd_driver_init);
module_exit(tpd_driver_exit);