diff options
Diffstat (limited to 'drivers/input/touchscreen/ist30xxb/ist30xxb.c')
| -rwxr-xr-x | drivers/input/touchscreen/ist30xxb/ist30xxb.c | 1261 |
1 files changed, 1261 insertions, 0 deletions
diff --git a/drivers/input/touchscreen/ist30xxb/ist30xxb.c b/drivers/input/touchscreen/ist30xxb/ist30xxb.c new file mode 100755 index 00000000..7af978bc --- /dev/null +++ b/drivers/input/touchscreen/ist30xxb/ist30xxb.c @@ -0,0 +1,1261 @@ +/* + * Copyright (C) 2010,Imagis Technology Co. Ltd. All Rights Reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * 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. + * + */ + +#include <linux/version.h> +#include <linux/kernel.h> +#include <linux/module.h> +#include <linux/input.h> +#include <linux/interrupt.h> +#include <linux/i2c.h> +#include <linux/delay.h> +#include <linux/slab.h> + +#include <linux/platform_device.h> +#include <linux/device.h> +#include <linux/mutex.h> +#include <linux/mutex.h> +#include <mach/gpio.h> + +#include <linux/input/mt.h> + +#include <linux/input/ist30xxb.h> + +#ifdef CONFIG_OF +#include <linux/of_gpio.h> +#include <linux/of_device.h> +#endif +#include "ist30xxb_sec.h" +#include "ist30xxb_update.h" + +#if IST30XX_DEBUG +#include "ist30xxb_misc.h" +#endif +#if IST30XX_TRACKING_MODE +#include "ist30xxb_tracking.h" +#endif + +#define MAX_ERR_CNT (100) + +#if IST30XX_USE_KEY +int ist30xx_key_code[] = { 0, KEY_MENU, KEY_BACK }; +#endif + +DEFINE_MUTEX(ist30xx_mutex); + +volatile bool ist30xx_irq_working = false; +static bool ist30xx_initialized = 0; + +struct ist30xx_data *ts_data; +EXPORT_SYMBOL(ts_data); + +static struct delayed_work work_reset_check; +static struct delayed_work work_noise_protect, work_debug_algorithm; + +#if IST30XX_DETECT_TA +static int ist30xx_ta_status = -1; +#endif +#if IST30XX_DETECT_CALLER +static int ist30xx_call_status = -1; +#endif +#if IST30XX_INTERNAL_BIN && IST30XX_UPDATE_BY_WORKQUEUE +static struct delayed_work work_fw_update; +#endif + + +#if IST30XX_EVENT_MODE +# if IST30XX_NOISE_MODE +int ist30xx_scan_retry = 0; +# endif +u32 event_ms = 0; + +static struct timer_list idle_timer; +static struct timespec t_current; // ns +int timer_period_ms = 500; // 0.5sec +# define EVENT_TIMER_INTERVAL (HZ / 2) // 0.5sec +#endif // IST30XX_EVENT_MODE + +#if IST30XX_DEBUG +extern TSP_INFO ist30xx_tsp_info; +extern TKEY_INFO ist30xx_tkey_info; +#endif + +int ist30xx_dbg_level = IST30XX_DEBUG_LEVEL; +void tsp_printk(int level, const char *fmt, ...) +{ + struct va_format vaf; + va_list args; + int r; + + if (ist30xx_dbg_level < level) + return; + + va_start(args, fmt); + + vaf.fmt = fmt; + vaf.va = &args; + + r = printk("%s %pV", IST30XX_DEBUG_TAG, &vaf); + + va_end(args); +} + +#if IST30XX_EVENT_MODE +long get_milli_second(void) +{ + ktime_get_ts(&t_current); + + return t_current.tv_sec * 1000 + t_current.tv_nsec / 1000000; +} +#endif // IST30XX_EVENT_MODE + + +void ist30xx_disable_irq(struct ist30xx_data *data) +{ + if (data->irq_enabled) { + ist30xx_tracking(TRACK_INTR_ENABLE); + disable_irq(data->client->irq); + #if PRINT_TSP_LOG + printk("[TSP] ist30xx_disable_irq : %d\n", data->client->irq); + #endif + data->irq_enabled = 0; +#if IST30XX_EVENT_MODE + data->status.event_mode = false; +#endif + } +} + +void ist30xx_enable_irq(struct ist30xx_data *data) +{ + if (!data->irq_enabled) { + ist30xx_tracking(TRACK_INTR_DISABLE); + enable_irq(data->client->irq); + printk("[TSP] ist30xx_enable_irq : %d\n", data->client->irq); + msleep(10); + data->irq_enabled = 1; +#if IST30XX_EVENT_MODE + data->status.event_mode = true; +#endif + } +} + + +int ist30xx_max_error_cnt = MAX_ERR_CNT; +int ist30xx_error_cnt = 0; +static void ist30xx_request_reset(void) +{ + #if PRINT_TSP_LOG + printk("[TSP] %s()\n", __func__); + #endif + ist30xx_error_cnt++; + if (ist30xx_error_cnt >= ist30xx_max_error_cnt) { + #if PRINT_TSP_LOG + printk("[TSP] %s()\n", __func__); + #endif + schedule_delayed_work(&work_reset_check, 0); + ist30xx_error_cnt = 0; + } + +} + + +void ist30xx_start(struct ist30xx_data *data) +{ +#if IST30XX_DETECT_TA + if (ist30xx_ta_status > -1) { + ist30xx_write_cmd(data->client, CMD_SET_TA_MODE, ist30xx_ta_status); + #if PRINT_TSP_LOG + tsp_info("%s(), ta_mode : %d\n", __func__, ist30xx_ta_status); + #endif + } +#endif +#if IST30XX_DETECT_CALLER + if (ist30xx_call_status > -1) { + ist30xx_write_cmd(data->client, CMD_SET_CALL_MODE, ist30xx_call_status); + #if PRINT_TSP_LOG + tsp_info("%s(), call_mode : %d\n", __func__, ist30xx_call_status); + #endif + } +#endif + + ist30xx_cmd_start_scan(data->client); +} + + +int ist30xx_get_ver_info(struct ist30xx_data *data) +{ + int ret; + + data->fw.prev_core_ver = data->fw.core_ver; + data->fw.prev_param_ver = data->fw.param_ver; + data->fw.core_ver = data->fw.param_ver = 0; + + ret = ist30xx_read_cmd(data->client, CMD_GET_FW_VER, &data->fw.core_ver); + if (ret) + return ret; + + ret = ist30xx_read_cmd(data->client, CMD_GET_PARAM_VER, &data->fw.param_ver); + if (ret) + return ret; + + tsp_info("IC version read core: %x, param: %x\n", + data->fw.core_ver, data->fw.param_ver); + + return 0; +} + + +int ist30xx_init_touch_driver(struct ist30xx_data *data) +{ + int ret = 0; + + mutex_lock(&ist30xx_mutex); + ist30xx_disable_irq(data); + + ret = ist30xx_cmd_run_device(data->client); + if (ret) + goto init_touch_end; + + ist30xx_get_ver_info(data); + +init_touch_end: + ist30xx_start(data); + + ist30xx_enable_irq(data); + mutex_unlock(&ist30xx_mutex); + + return ret; +} + + +#if IST30XX_DEBUG +void ist30xx_print_info(void) +{ + TSP_INFO *tsp = &ist30xx_tsp_info; + TKEY_INFO *tkey = &ist30xx_tkey_info; + tsp_debug("*** TSP/TKEY info ***\n"); + tsp_debug("tscn dir swap: %d, flip x: %d, y: %d\n", + tsp->dir.swap_xy, tsp->dir.flip_x, tsp->dir.flip_y); + tsp_debug("tscn ch_num tx: %d, rx: %d\n", + tsp->ch_num.tx, tsp->ch_num.rx); + tsp_debug("tscn width: %d, height: %d\n", + tsp->width, tsp->height); + tsp_debug("tkey enable: %d, key num: %d, axis rx: %d \n", + tkey->enable, tkey->key_num, tkey->axis_rx); + tsp_debug("tkey ch_num[0] %d, [1] %d, [2] %d, [3] %d, [4] %d\n", + tkey->ch_num[0], tkey->ch_num[1], tkey->ch_num[2], + tkey->ch_num[3], tkey->ch_num[4]); +} +#endif + +#define CALIB_MSG_MASK (0xF0000FFF) +#define CALIB_MSG_VALID (0x80000CAB) +int ist30xx_get_info(struct ist30xx_data *data) +{ + int ret; + u32 calib_msg; + + mutex_lock(&ist30xx_mutex); + ist30xx_disable_irq(data); + +#if IST30XX_INTERNAL_BIN +# if IST30XX_DEBUG + ist30xx_get_tsp_info(data); + ist30xx_get_tkey_info(data); +#endif // IST30XX_DEBUG +#else + ret = ist30xx_write_cmd(data->client, CMD_RUN_DEVICE, 0); + if (ret) + goto get_info_end; + msleep(10); + + ret = ist30xx_get_ver_info(data); + if (ret) + goto get_info_end; + +# if IST30XX_DEBUG + ret = ist30xx_tsp_update_info(); + if (ret) + goto get_info_end; + + ret = ist30xx_tkey_update_info(); + if (ret) + goto get_info_end; +# endif // IST30XX_DEBUG +#endif // IST30XX_INTERNAL_BIN + +#if IST30XX_DEBUG + ist30xx_print_info(); + data->max_fingers = ist30xx_tsp_info.finger_num; + data->max_keys = ist30xx_tkey_info.key_num; +#endif // IST30XX_DEBUG + + ret = ist30xx_read_cmd(ts_data->client, CMD_GET_CALIB_RESULT, &calib_msg); + if (ret == 0) { + tsp_info("calib status: 0x%08x\n", calib_msg); + ist30xx_tracking(calib_msg); + tsp_info("CALIB_MSG_MASK = 0x%08x\n",(calib_msg & CALIB_MSG_MASK)); + tsp_info("CALIB_MSG_VALID = 0x%08x\n",CALIB_MSG_VALID); + tsp_info("CALIB_TOSTATUS = 0x%08x\n",CALIB_TO_STATUS(calib_msg)); + if ((calib_msg & CALIB_MSG_MASK) != CALIB_MSG_VALID || + CALIB_TO_STATUS(calib_msg) > 0) { +// ist30xx_calibrate(IST30XX_FW_UPDATE_RETRY); + ist30xx_calibrate(1); + ist30xx_cmd_run_device(data->client); + } + } + +#if (IST30XX_EVENT_MODE && IST30XX_CHECK_CALIB) + if (!data->status.update) { + ret = ist30xx_cmd_check_calib(data->client); + if (!ret) { + data->status.calib = 1; + event_ms = (u32)get_milli_second(); + data->status.event_mode = true; + } + } +#else + ist30xx_start(ts_data); +#endif + +#if !(IST30XX_INTERNAL_BIN) +get_info_end: +#endif + if (ret == 0) + ist30xx_enable_irq(data); + mutex_unlock(&ist30xx_mutex); + + return ret; +} + + +#define PRESS_MSG_MASK (0x01) +#define MULTI_MSG_MASK (0x02) +#define PRESS_MSG_KEY (0x6) + +#define TOUCH_DOWN_MESSAGE ("p") +#define TOUCH_UP_MESSAGE ("r") +#define TOUCH_MOVE_MESSAGE (" ") +bool tsp_touched[IST30XX_MAX_MT_FINGERS] = { 0, }; + +void print_tsp_event(finger_info *finger) +{ + int idx = finger->bit_field.id - 1; + int press = finger->bit_field.udmg & PRESS_MSG_MASK; + + if (press == PRESS_MSG_MASK) { + if (tsp_touched[idx] == 0) { // touch down + #if PRINT_TSP_LOG + tsp_info("%s%d (%d, %d)\n", + TOUCH_DOWN_MESSAGE, finger->bit_field.id, + finger->bit_field.x, finger->bit_field.y); + #endif + tsp_touched[idx] = 1; + } else { + // touch move + #if PRINT_TSP_LOG + tsp_debug("%s%d (%d, %d)\n", + TOUCH_MOVE_MESSAGE, finger->bit_field.id, + finger->bit_field.x, finger->bit_field.y); + #endif + } + } else { + if (tsp_touched[idx] == 1) { // touch up + #if PRINT_TSP_LOG + tsp_info("%s%d (%d, %d)\n", + TOUCH_UP_MESSAGE, finger->bit_field.id, + finger->bit_field.x, finger->bit_field.y); + #endif + tsp_touched[idx] = 0; + } + } +} + + +static void release_finger(finger_info *finger) +{ + input_mt_slot(ts_data->input_dev, finger->bit_field.id - 1); + input_mt_report_slot_state(ts_data->input_dev, MT_TOOL_FINGER, false); + + ist30xx_tracking(TRACK_POS_FINGER + finger->bit_field.id); +#if PRINT_TSP_LOG + tsp_info("%s() %d(%d, %d)\n", __func__, + finger->bit_field.id, finger->bit_field.x, finger->bit_field.y); +#endif + finger->bit_field.udmg &= ~(PRESS_MSG_MASK); +#if PRINT_TSP_LOG + print_tsp_event(finger); +#endif + finger->bit_field.id = 0; + + input_sync(ts_data->input_dev); +} + + +#define CANCEL_KEY (0xff) +#define RELEASE_KEY (0) +static void release_key(finger_info *key, u8 key_status) +{ + int id = key->bit_field.id; + + input_report_key(ts_data->input_dev, ist30xx_key_code[id], key_status); + + ist30xx_tracking(TRACK_POS_KEY + key->bit_field.id); +#if PRINT_TSP_LOG + tsp_debug("%s() key%d, event: %d, status: %d\n", __func__, + id, key->bit_field.w, key_status); +#endif + key->bit_field.id = 0; + + input_sync(ts_data->input_dev); +} + +static void clear_input_data(struct ist30xx_data *data) +{ + int i; + finger_info *fingers = (finger_info *)data->prev_fingers; + finger_info *keys = (finger_info *)data->prev_keys; + + for (i = 0; i < data->num_fingers; i++) { + if (fingers[i].bit_field.id == 0) + continue; + + if (fingers[i].bit_field.udmg & PRESS_MSG_MASK) + release_finger(&fingers[i]); + } + + for (i = 0; i < data->num_keys; i++) { + if (keys[i].bit_field.id == 0) + continue; + + if (keys[i].bit_field.w == PRESS_MSG_KEY) + release_key(&keys[i], RELEASE_KEY); + } +} + +static int check_report_data(struct ist30xx_data *data, int finger_counts, int key_counts) +{ + int i, j; + bool valid_id; + finger_info *fingers = (finger_info *)data->fingers; + finger_info *prev_fingers = (finger_info *)data->prev_fingers; + + /* current finger info */ + for (i = 0; i < finger_counts; i++) { + if ((fingers[i].bit_field.id == 0) || + (fingers[i].bit_field.id > data->max_fingers) || + (fingers[i].bit_field.x > IST30XX_MAX_X) || + (fingers[i].bit_field.y > IST30XX_MAX_Y)) { + tsp_warn("Invalid touch data - %d: %d(%d, %d)\n", i, + fingers[i].bit_field.id, + fingers[i].bit_field.x, + fingers[i].bit_field.y); + + fingers[i].bit_field.id = 0; + ist30xx_tracking(TRACK_POS_UNKNOWN); + return -EPERM; + } else{ + /*tsp_debug("***touch data - %d: %d(%d, %d)\n", i, + fingers[i].bit_field.id, + fingers[i].bit_field.x, + fingers[i].bit_field.y);*/ + } + + } + + /* previous finger info */ + if (data->num_fingers >= finger_counts) { + for (i = 0; i < data->max_fingers; i++) { // prev_fingers + if (prev_fingers[i].bit_field.id != 0 && + (prev_fingers[i].bit_field.udmg & PRESS_MSG_MASK)) { + valid_id = false; + for (j = 0; j < data->max_fingers; j++) { // fingers + if ((prev_fingers[i].bit_field.id) == + (fingers[j].bit_field.id)) { + valid_id = true; + break; + } + } + if (valid_id == false) + release_finger(&prev_fingers[i]); + } + } + } + + return 0; +} + +bool finger_on_screen(void) +{ + int i; + + for (i = 0; i < IST30XX_MAX_MT_FINGERS; i++) + if (tsp_touched[i]) return true; + + return false; +} + +int key_press = 0; +int key_id = 0; +u32 key_sensitivity = 0; +static void report_input_data(struct ist30xx_data *data, int finger_counts, int key_counts) +{ + int i, press, count; + finger_info *fingers = (finger_info *)data->fingers; + + memset(data->prev_fingers, 0, sizeof(data->prev_fingers)); + + for (i = 0, count = 0; i < finger_counts; i++) { + press = fingers[i].bit_field.udmg & PRESS_MSG_MASK; + #if PRINT_TSP_LOG + print_tsp_event(&fingers[i]); + #endif + input_mt_slot(data->input_dev, fingers[i].bit_field.id - 1); + input_mt_report_slot_state(data->input_dev, MT_TOOL_FINGER, + (press ? true : false)); + if (press) { + input_report_abs(data->input_dev, ABS_MT_POSITION_X, + fingers[i].bit_field.x); + input_report_abs(data->input_dev, ABS_MT_POSITION_Y, + fingers[i].bit_field.y); + input_report_abs(data->input_dev, ABS_MT_TOUCH_MAJOR, + fingers[i].bit_field.w); + } + + data->prev_fingers[i] = fingers[i]; + count++; + } + +#if IST30XX_USE_KEY + for (i = finger_counts; i < finger_counts + key_counts; i++) { + key_id = fingers[i].bit_field.id; + key_press = (fingers[i].bit_field.w == PRESS_MSG_KEY) ? 1 : 0; + key_sensitivity = fingers[i].bit_field.y; +#if PRINT_TSP_LOG + tsp_debug("key(%08x) id: %d, press: %d, sensitivity: %d\n", + fingers[i].full_field, key_id, key_press, fingers[i].bit_field.y); +#endif + input_report_key(data->input_dev, ist30xx_key_code[key_id], key_press); + + data->prev_keys[key_id - 1] = fingers[i]; + + count++; + } +#endif // IST30XX_USE_KEY + + if (count > 0) + input_sync(data->input_dev); + + data->num_fingers = finger_counts; + data->num_keys = key_counts; + ist30xx_error_cnt = 0; + ist30xx_scan_retry = 0; +} + +/* + * CMD : CMD_GET_COORD + * [31:30] [29:26] [25:16] [15:10] [9:0] + * Multi(1st) UDMG Rsvd. NumOfKey Rsvd. NumOfFinger + * Single & UDMG ID X Area Y + * Multi(2nd) + * + * UDMG [31] 0/1 : single/multi + * UDMG [30] 0/1 : unpress/press + */ +static irqreturn_t ist30xx_irq_thread(int irq, void *ptr) +{ + int i, ret; + int key_cnt, finger_cnt, read_cnt; + struct ist30xx_data *data = ts_data; + u32 msg[IST30XX_MAX_MT_FINGERS]; + + ist30xx_irq_working = true; + //printk("[TSP] %s\n", __func__); + if (!data->irq_enabled) + goto irq_end; + + if (get_milli_second() - event_ms < 5) // Noise detect + goto irq_end; + + memset(msg, 0, sizeof(msg)); + + ret = ist30xx_get_position(data->client, msg, 1); + if (ret) + goto irq_err; + #if PRINT_TSP_LOG + tsp_verb("intr msg: 0x%08x\n", *msg); + #endif + if (*msg == 0xE11CE970) + goto irq_ic_err; + + if (*msg == 0 || *msg == 0xFFFFFFFF || *msg == 0x2FFF03FF || + *msg == 0x30003000 || *msg == 0x300B300B) + goto irq_err; + +#if IST30XX_EVENT_MODE + event_ms = (u32)get_milli_second(); +# if IST30XX_TRACKING_MODE + ist30xx_put_track(event_ms, *msg); +# endif +#endif // IST30XX_EVENT_MODE + + if ((*msg & CALIB_MSG_MASK) == CALIB_MSG_VALID) { + data->status.calib_msg = *msg; + tsp_info("calib status: 0x%08x\n", data->status.calib_msg); + + goto irq_end; + } + + memset(data->fingers, 0, sizeof(data->fingers)); + + key_cnt = 0; + finger_cnt = 1; + read_cnt = 1; + data->fingers[0].full_field = *msg; + + if (data->fingers[0].bit_field.udmg & MULTI_MSG_MASK) { + key_cnt = data->fingers[0].bit_field.x; + finger_cnt = data->fingers[0].bit_field.y; + read_cnt = finger_cnt + key_cnt; + + if (finger_cnt > data->max_fingers || + key_cnt > data->max_keys) { + tsp_warn("Invalid touch count - finger: %d(%d), key: %d(%d)\n", + finger_cnt, data->max_fingers, + key_cnt, data->max_keys); + goto irq_err; + } + +#if I2C_BURST_MODE + ret = ist30xx_get_position(data->client, msg, read_cnt); + if (ret) + goto irq_err; + + for (i = 0; i < read_cnt; i++) + data->fingers[i].full_field = msg[i]; +#else + for (i = 0; i < read_cnt; i++) { + ret = ist30xx_get_position(data->client, &msg[i], 1); + if (ret) + goto irq_err; + + data->fingers[i].full_field = msg[i]; + } +#endif // I2C_BURST_MODE + +#if IST30XX_EVENT_MODE && IST30XX_TRACKING_MODE + for (i = 0; i < read_cnt; i++) + ist30xx_put_track(event_ms, msg[i]); +#endif + } + + if (check_report_data(data, finger_cnt, key_cnt)) + goto irq_end; + + if (read_cnt > 0) + report_input_data(data, finger_cnt, key_cnt); + + goto irq_end; + +irq_err: + tsp_err("intr msg[0]: 0x%08x, ret: %d\n", *msg, ret); + ist30xx_request_reset(); +irq_end: + ist30xx_irq_working = false; + //printk("[TSP] %s : %d\n", __func__, __LINE__); +#if IST30XX_EVENT_MODE + event_ms = (u32)get_milli_second(); +#endif + return IRQ_HANDLED; + +irq_ic_err: + tsp_err("Occured IC exception\n"); + schedule_delayed_work(&work_reset_check, 0); + ist30xx_irq_working = false; + //printk("[TSP] %s : %d\n", __func__, __LINE__); +#if IST30XX_EVENT_MODE + event_ms = (u32)get_milli_second(); +#endif + return IRQ_HANDLED; +} + + +#ifdef CONFIG_HAS_EARLYSUSPEND +#define ist30xx_suspend NULL +#define ist30xx_resume NULL +static void ist30xx_early_suspend(struct early_suspend *h) +{ + struct ist30xx_data *data = container_of(h, struct ist30xx_data, + early_suspend); + + mutex_lock(&ist30xx_mutex); + ist30xx_disable_irq(data); + ist30xx_internal_suspend(data); + clear_input_data(data); + mutex_unlock(&ist30xx_mutex); +} +static void ist30xx_late_resume(struct early_suspend *h) +{ + struct ist30xx_data *data = container_of(h, struct ist30xx_data, + early_suspend); + + mutex_lock(&ist30xx_mutex); + ist30xx_internal_resume(data); + ist30xx_start(data); + ist30xx_enable_irq(data); + mutex_unlock(&ist30xx_mutex); +} +#else +static int ist30xx_suspend(struct device *dev) +{ + struct i2c_client *client = to_i2c_client(dev); + struct ist30xx_data *data = i2c_get_clientdata(client); + + return ist30xx_internal_suspend(data); +} +static int ist30xx_resume(struct device *dev) +{ + struct i2c_client *client = to_i2c_client(dev); + struct ist30xx_data *data = i2c_get_clientdata(client); + + return ist30xx_internal_resume(data); +} +#endif // CONFIG_HAS_EARLYSUSPEND + + +void ist30xx_set_ta_mode(bool charging) +{ + tsp_info("%s(), charging = %d\n", __func__, charging); +#if IST30XX_DETECT_TA + if (charging == ist30xx_ta_status) + return; + + if (ist30xx_ta_status == -1) { + ist30xx_ta_status = charging ? 1 : 0; + return; + } + + ist30xx_ta_status = charging ? 1 : 0; + + tsp_info("%s(), status = %d\n", __func__, ist30xx_ta_status); + + schedule_delayed_work(&work_reset_check, 0); +#endif +} +EXPORT_SYMBOL(ist30xx_set_ta_mode); + +void ist30xx_set_call_mode(int mode) +{ + tsp_info("%s(), mode = %d\n", __func__, mode); + +#if IST30XX_DETECT_CALLER + if (mode == ist30xx_ta_status) + return; + + if (ist30xx_ta_status == -1) { + ist30xx_ta_status = mode ? 1 : 0; + return; + } + + ist30xx_call_status = mode; + + tsp_info("%s(), call = %d\n", __func__, ist30xx_call_status); + + schedule_delayed_work(&work_reset_check, 0); +#endif +} +EXPORT_SYMBOL(ist30xx_set_call_mode); + + +void charger_enable(int enable) +{ + bool charging = enable ? true : false; + + ist30xx_set_ta_mode(charging); +} + +static void reset_work_func(struct work_struct *work) +{ + if ((ts_data == NULL) || (ts_data->client == NULL)) + return; + + tsp_info("Request reset function\n"); + + if ((ist30xx_initialized == 1) && (ts_data->status.power == 1) && + (ts_data->status.update != 1) && (ts_data->status.calib != 1)) { + mutex_lock(&ist30xx_mutex); + ist30xx_disable_irq(ts_data); + + clear_input_data(ts_data); + + ist30xx_cmd_run_device(ts_data->client); + + ist30xx_start(ts_data); + + ist30xx_enable_irq(ts_data); + mutex_unlock(&ist30xx_mutex); + } +} +#if 0 +#if IST30XX_INTERNAL_BIN && IST30XX_UPDATE_BY_WORKQUEUE +static void fw_update_func(struct work_struct *work) +{ + if ((ts_data == NULL) || (ts_data->client == NULL)) + return; + + tsp_info("FW update function\n"); + + if (ist30xx_auto_bin_update(ts_data)) + ist30xx_disable_irq(ts_data); +} +#endif // IST30XX_INTERNAL_BIN && IST30XX_UPDATE_BY_WORKQUEUE +#endif + +#if IST30XX_EVENT_MODE +u32 ist30xx_max_scan_retry = 2; +u32 ist30xx_scan_count = 0; +u32 ist30xx_algr_addr = 0, ist30xx_algr_size = 0; + +#define SCAN_STATUS_MAGIC (0x3C000000) +#define SCAN_STATUS_MASK (0xFF000000) +#define FINGER_CNT_MASK (0x00F00000) +#define SCAN_CNT_MASK (0x000FFFFF) +#define GET_FINGER_CNT(k) ((k & FINGER_CNT_MASK) >> 20) +#define GET_SCAN_CNT(k) (k & SCAN_CNT_MASK) + +static void noise_work_func(struct work_struct *work) +{ +# if IST30XX_NOISE_MODE + int i, ret; + u32 scan_status = 0; + + ret = ist30xx_read_cmd(ts_data->client, IST30XXB_MEM_COUNT, &scan_status); + if (ret) { + tsp_warn("Mem scan count read fail!\n"); + goto retry_timer; + } + #if PRINT_TSP_LOG + tsp_verb("scan status: 0x%x\n", scan_status); + #endif + if ((scan_status & SCAN_STATUS_MASK) != SCAN_STATUS_MAGIC) { + tsp_warn("Scan status is not corrected!\n"); + goto retry_timer; + } else { + /* Status of IC is idle */ + if (GET_FINGER_CNT(scan_status) == 0) { + for (i = 0; i < IST30XX_MAX_MT_FINGERS; i++) { + if (ts_data->prev_fingers[i].bit_field.id == 0) + continue; + + if (ts_data->prev_fingers[i].bit_field.udmg & PRESS_MSG_MASK) { + tsp_warn("prev_fingers: 0x%08x\n", + ts_data->prev_fingers[i].full_field); + release_finger(&ts_data->prev_fingers[i]); + } + } + + for (i = 0; i < IST30XX_MAX_MT_FINGERS; i++) { + if (ts_data->prev_keys[i].bit_field.id == 0) + continue; + + if (ts_data->prev_keys[i].bit_field.w == PRESS_MSG_KEY) { + tsp_warn("prev_keys: 0x%08x\n", + ts_data->prev_keys[i].full_field); + release_key(&ts_data->prev_keys[i], RELEASE_KEY); + } + } + } + + scan_status &= SCAN_CNT_MASK; + + /* Status of IC is lock-up */ + if (scan_status == ist30xx_scan_count) { + tsp_warn("TSP IC is not responded!\n"); + goto retry_timer; + } + } + + ist30xx_scan_count = scan_status; + + return; + +retry_timer: + tsp_warn("Retry scan status!\n"); + + if (ist30xx_scan_retry++ == ist30xx_max_scan_retry) { + schedule_delayed_work(&work_reset_check, 0); + ist30xx_scan_retry = 0; + } +# endif // IST30XX_NOISE_MODE +} + +static void debug_work_func(struct work_struct *work) +{ +# if IST30XX_ALGORITHM_MODE + int ret = -EPERM; + ALGR_INFO algr; + u32 *buf32 = (u32 *)&algr; + + ret = ist30xxb_burst_read(ts_data->client, + ist30xx_algr_addr, (u32 *)&algr, ist30xx_algr_size); + if (ret) { + tsp_warn("Algorithm mem addr read fail!\n"); + return; + } + + tsp_debug(" 0x%08x 0x%08x 0x%08x 0x%08x 0x&08x\n", + buf32[0], buf32[1], buf32[2], buf32[3], buf32[4]); + + tsp_debug(" Scanstatus: %x\n", algr.scan_status); + tsp_debug(" TouchCnt: %d\n", algr.touch_cnt); + tsp_debug(" IntlTouchCnt: %d\n", algr.intl_touch_cnt); + tsp_debug(" StatusFlag: %d\n", algr.status_flag); + tsp_debug(" RAWPeakMax: %d\n", algr.raw_peak_max); + tsp_debug(" RAWPeakMin: %d\n", algr.raw_peak_min); + tsp_debug(" FLTPeakMax: %d\n", algr.flt_peak_max); + tsp_debug(" AdptThreshold: %d\n", algr.adpt_threshold); + tsp_debug(" KeyRawData0: %d\n", algr.key_raw_data[0]); + tsp_debug(" KeyRawData1: %d\n", algr.key_raw_data[1]); +# endif // IST30XX_ALGORITHM_MODE + } + +void timer_handler(unsigned long data) +{ + u32 ms = 0; + struct ist30xx_status *status = &ts_data->status; + + if (ist30xx_irq_working) + goto restart_timer; + + if (status->event_mode) { + if ((status->power == 1) && (status->update != 1)) { + ms = (u32)get_milli_second(); + if (status->calib == 1) { + if (ms - event_ms >= 3000) { // 3second + tsp_debug("calibration timeout over 3sec\n"); + schedule_delayed_work(&work_reset_check, 0); + status->calib = 0; + } + } else if (status->noise_mode) { + schedule_delayed_work(&work_noise_protect, 0); + } + +# if IST30XX_ALGORITHM_MODE + if ((ist30xx_algr_addr >= IST30XXB_ACCESS_ADDR) && + (ist30xx_algr_size > 0)) + schedule_delayed_work(&work_debug_algorithm, 0); + +# endif // IST30XX_ALGORITHM_MODE + } + } + +restart_timer: + mod_timer(&idle_timer, get_jiffies_64() + (HZ * timer_period_ms / 1000)); +} +#endif // IST30XX_EVENT_MODE + +extern int sec_touch_sysfs(struct ist30xx_data *data); +extern int sec_fac_cmd_init(struct ist30xx_data *data); + +static int ist30xx_probe(struct i2c_client * client, + const struct i2c_device_id * id) +{ + int ret; + int retry = 3; + struct ist30xx_data *data; + struct input_dev *input_dev; + int ist_gpio = 0; + struct tsp_dev_info *pdata = client->dev.platform_data; + printk("[TSP] %s(), the i2c addr=0x%x", __func__, client->addr); + + data = kzalloc(sizeof(*data), GFP_KERNEL); + if (!data) + return -ENOMEM; + +#ifdef CONFIG_OF + + struct device_node *np = client->dev.of_node; + + printk("[TSP] the client->dev.of_node=0x%x\n", client->dev.of_node); + + if (client->dev.of_node){ + ist_gpio = of_get_gpio(np, 0); + printk("[TSP] gpio id=%d", ist_gpio); + } +#endif + + input_dev = input_allocate_device(); + if (!input_dev) { + ret = -ENOMEM; + tsp_err("%s(), input_allocate_device failed (%d)\n", __func__, ret); + goto err_alloc_dev; + } + + data->max_fingers = data->max_keys = IST30XX_MAX_MT_FINGERS; + data->irq_enabled = 1; + data->client = client; + data->input_dev = input_dev; + i2c_set_clientdata(client, data); + + input_mt_init_slots(input_dev, IST30XX_MAX_MT_FINGERS, 0); + + input_dev->name = "ist30xx_ts_input"; + input_dev->id.bustype = BUS_I2C; + input_dev->dev.parent = &client->dev; + + set_bit(EV_ABS, input_dev->evbit); + set_bit(INPUT_PROP_DIRECT, input_dev->propbit); + + input_set_abs_params(input_dev, ABS_MT_POSITION_X, 0, IST30XX_MAX_X, 0, 0); + input_set_abs_params(input_dev, ABS_MT_POSITION_Y, 0, IST30XX_MAX_Y, 0, 0); + input_set_abs_params(input_dev, ABS_MT_TOUCH_MAJOR, 0, IST30XX_MAX_W, 0, 0); + +#if IST30XX_USE_KEY + { + int i; + set_bit(EV_KEY, input_dev->evbit); + set_bit(EV_SYN, input_dev->evbit); + for (i = 1; i < ARRAY_SIZE(ist30xx_key_code); i++) + set_bit(ist30xx_key_code[i], input_dev->keybit); + } +#endif + + input_set_drvdata(input_dev, data); + ret = input_register_device(input_dev); + if (ret) { + input_free_device(input_dev); + goto err_reg_dev; + } + + ts_data = data; + ist30xx_power_on(); + ret = ist30xx_init_system(); + if (ret) { + dev_err(&client->dev, "chip initialization failed\n"); + goto err_init_drv; + } + + ret = ist30xx_init_update_sysfs(); + if (ret) + goto err_init_drv; + +#if IST30XX_DEBUG + ret = ist30xx_init_misc_sysfs(); + if (ret) + goto err_init_drv; +#endif +#if 0 +# if IST30XX_FACTORY_TEST + ret = sec_fac_cmd_init(data); + if (ret) + goto err_init_drv; + ret = sec_touch_sysfs(data); + if (ret) + goto err_init_drv; +#endif + +#if IST30XX_TRACKING_MODE + ret = ist30xx_init_tracking_sysfs(); + if (ret) + goto err_init_drv; +#endif +#endif + +#ifndef CONFIG_OF + ist_gpio = pdata->gpio; +#endif + /* configure touchscreen interrupt gpio */ + ret = gpio_request(ist_gpio, "ist30xx_irq_gpio"); + printk("[TSP] pdata->gpio : %d\n", ist_gpio); + if (ret) { + tsp_err("unable to request gpio.(%s)\r\n",input_dev->name); + goto err_init_drv; + } + + client->irq = gpio_to_irq(ist_gpio); + + printk("[TSP] client->irq : %d\n", client->irq); + ret = request_threaded_irq(client->irq, NULL, ist30xx_irq_thread, + IRQF_TRIGGER_FALLING | IRQF_ONESHOT, "ist30xx_ts", data); + if (ret) + goto err_irq; + + ist30xx_disable_irq(data); + + while (data->chip_id != IST30XXB_CHIP_ID) { + ist30xx_read_cmd(data->client, IST30XXB_REG_CHIPID, &data->chip_id); + data->chip_id = (data->chip_id == 0x3000B ? IST30XXB_CHIP_ID : 0); + + if (retry-- == 0) + break;//goto err_irq; + } + + retry = 3; + while (retry-- > 0) { + ret = ist30xx_read_cmd(data->client, IST30XXB_REG_TSPTYPE, + &data->tsp_type); + if (ret) continue; + + tsp_debug("tsptype: %x\n", data->tsp_type); + data->tsp_type = IST30XXB_PARSE_TSPTYPE(data->tsp_type); + + if (ret == 0) + break; + } + if (retry == 0) + data->tsp_type = TSP_TYPE_UNKNOWN; + + tsp_info("TSP IC: %x, TSP Vendor: %x\n", data->chip_id, data->tsp_type); + +#if IST30XX_EVENT_MODE + data->status.event_mode = false; +#endif +#if IST30XX_INTERNAL_BIN +# if IST30XX_UPDATE_BY_WORKQUEUE + INIT_DELAYED_WORK(&work_fw_update, fw_update_func); + schedule_delayed_work(&work_fw_update, IST30XX_UPDATE_DELAY); +# else + ret = ist30xx_auto_bin_update(data); //sinjan.kumar + if (ret != 0) + goto err_irq; +# endif +#endif // IST30XX_INTERNAL_BIN + + ret = ist30xx_get_info(data); + tsp_info("Get info: %s\n", (ret == 0 ? "success" : "fail")); + + INIT_DELAYED_WORK(&work_reset_check, reset_work_func); + INIT_DELAYED_WORK(&work_noise_protect, noise_work_func); + INIT_DELAYED_WORK(&work_debug_algorithm, debug_work_func); + +#if IST30XX_EVENT_MODE + init_timer(&idle_timer); + idle_timer.function = timer_handler; + idle_timer.expires = jiffies_64 + (EVENT_TIMER_INTERVAL); + + mod_timer(&idle_timer, get_jiffies_64() + EVENT_TIMER_INTERVAL); +#endif + + +#ifdef CONFIG_HAS_EARLYSUSPEND + data->early_suspend.level = EARLY_SUSPEND_LEVEL_BLANK_SCREEN + 1; + data->early_suspend.suspend = ist30xx_early_suspend; + data->early_suspend.resume = ist30xx_late_resume; + register_early_suspend(&data->early_suspend); +#endif + + ist30xx_initialized = 1; + printk("[TSP] Probe end\n"); + return 0; + +err_irq: + tsp_debug("ChipID: %x\n", data->chip_id); + ist30xx_disable_irq(data); + free_irq(client->irq, data); +err_init_drv: +#if IST30XX_EVENT_MODE + data->status.event_mode = false; +#endif + tsp_err("Error, ist30xx init driver\n"); + ist30xx_power_off(); + input_unregister_device(input_dev); + return 0; + +err_reg_dev: +err_alloc_dev: + tsp_err("Error, ist30xx mem free\n"); + kfree(data); + return 0; +} + + +static int ist30xx_remove(struct i2c_client *client) +{ + struct ist30xx_data *data = i2c_get_clientdata(client); + +#ifdef CONFIG_HAS_EARLYSUSPEND + unregister_early_suspend(&data->early_suspend); +#endif + + free_irq(client->irq, data); + ist30xx_power_off(); + + input_unregister_device(data->input_dev); + kfree(data); + + return 0; +} + + +static struct i2c_device_id ist30xx_idtable[] = { + { IST30XX_DEV_NAME, 0 }, + {}, +}; + + +MODULE_DEVICE_TABLE(i2c, ist30xx_idtable); + +#ifdef CONFIG_HAS_EARLYSUSPEND +static const struct dev_pm_ops ist30xx_pm_ops = { + .suspend = ist30xx_suspend, + .resume = ist30xx_resume, +}; +#endif + +#ifdef CONFIG_OF +static const struct of_device_id ist30xxb_ts_of_match[] = { + { .compatible = "Imagis,IST30XXB", }, + { } +}; +#endif + +static struct i2c_driver ist30xx_i2c_driver = { + .id_table = ist30xx_idtable, + .probe = ist30xx_probe, + .remove = ist30xx_remove, + .driver = { + .owner = THIS_MODULE, + .name = IST30XX_DEV_NAME, +#ifdef CONFIG_HAS_EARLYSUSPEND + .pm = &ist30xx_pm_ops, +#endif +#ifdef CONFIG_OF + .of_match_table = ist30xxb_ts_of_match, +#endif + + }, +}; + + +static int __init ist30xx_init(void) +{ +// #if PRINT_TSP_LOG + printk("[TSP] ist30xx_init\n"); +// #endif + + + return i2c_add_driver(&ist30xx_i2c_driver); +} + + +static void __exit ist30xx_exit(void) +{ + i2c_del_driver(&ist30xx_i2c_driver); +} + +module_init(ist30xx_init); +module_exit(ist30xx_exit); + +MODULE_DESCRIPTION("Imagis IST30XX touch driver"); +MODULE_LICENSE("GPL"); |
