blob: b75865131f917cf439880c50755c16117b5bdc81 [file] [log] [blame]
/*
* 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/regulator/consumer.h>
#include <linux/firmware.h>
#include <linux/string.h>
#ifdef CONFIG_HAS_EARLYSUSPEND
#include <linux/earlysuspend.h>
#endif
#include "ist30xx.h"
#include "ist30xx_update.h"
#include <linux/input/mt.h>
#if IST30XX_DEBUG
#include "ist30xx_misc.h"
#endif
#define MAX_ERR_CNT (100)
static char IsfwUpdate[20] = {0};
#define FW_DOWNLOADING "Downloading"
#define FW_DOWNLOAD_COMPLETE "Complete"
#define FW_DOWNLOAD_FAIL "FAIL"
#define TSP_BUF_SIZE (1024)
#if IST30XX_USE_KEY
int ist30xx_key_code[] = { 0, KEY_MENU, KEY_BACK};
#endif
#if IST30XX_DETECT_TA
static int ist30xx_ta_status = -1;
#endif
DEFINE_MUTEX(ist30xx_mutex);
struct ist30xx_data *ts_data;
static struct delayed_work work_reset_check;
static struct regulator *touch_regulator;
int ist30xx_zvalue;
int ist30xx_zvalue_menu;
int ist30xx_zvalue_back;
#define TOUCH_ON 1
#define TOUCH_OFF 0
struct device *sec_touchscreen;
EXPORT_SYMBOL(sec_touchscreen);
struct device *sec_touchkey;
EXPORT_SYMBOL(sec_touchkey);
extern struct class *sec_class;
/*struct device *sec_touchscreen_dev;*/
/*struct device *sec_touchkey_dev;*/
#define TSP_CMD(name, func) .cmd_name = name, .cmd_func = func
struct tsp_cmd {
struct list_head list;
const char *cmd_name;
void (*cmd_func)(void *device_data);
};
extern int Zinitix_Connected;
int Ist30xx_Connected;
EXPORT_SYMBOL(Ist30xx_Connected);
static int tsp_check;
static uint8_t buf_firmware[3];
static ssize_t phone_firmware_show(struct device *dev,
struct device_attribute *attr, char *buf);
static ssize_t part_firmware_show(struct device *dev,
struct device_attribute *attr, char *buf);
static ssize_t threshold_firmware_show(struct device *dev,
struct device_attribute *attr, char *buf);
static ssize_t firmware_update(struct device *dev,
struct device_attribute *attr, char *buf);
static ssize_t firmware_update_status(struct device *dev,
struct device_attribute *attr, char *buf);
static ssize_t menu_sensitivity_show(struct device *dev,
struct device_attribute *attr, char *buf);
static ssize_t back_sensitivity_show(struct device *dev,
struct device_attribute *attr, char *buf);
static ssize_t touchkey_threshold_show(struct device *dev,
struct device_attribute *attr, char *buf);
static DEVICE_ATTR(tsp_firm_version_phone,
S_IRUGO | S_IWUSR | S_IWOTH | S_IXOTH,
phone_firmware_show, NULL);
static DEVICE_ATTR(tsp_firm_version_panel,
S_IRUGO | S_IWUSR | S_IWOTH | S_IXOTH,
part_firmware_show, NULL);
static DEVICE_ATTR(tsp_threshold,
S_IRUGO | S_IWUSR | S_IWOTH | S_IXOTH,
threshold_firmware_show, NULL);
static DEVICE_ATTR(tsp_firm_update,
S_IRUGO | S_IWUSR | S_IWOTH | S_IXOTH,
firmware_update, firmware_update);
static DEVICE_ATTR(tsp_firm_update_status,
S_IRUGO | S_IWUSR | S_IWGRP | S_IXOTH,
firmware_update_status, NULL);
static DEVICE_ATTR(touchkey_menu,
S_IRUGO | S_IWUSR | S_IWGRP | S_IXOTH,
menu_sensitivity_show, NULL);
static DEVICE_ATTR(touchkey_back,
S_IRUGO | S_IWUSR | S_IWGRP | S_IXOTH,
back_sensitivity_show, NULL);
static DEVICE_ATTR(touchkey_threshold,
S_IRUGO | S_IWUSR | S_IWGRP | S_IXOTH,
touchkey_threshold_show, NULL);
/*static void fw_update(void *device_data);*/
static void get_fw_ver_bin(void *device_data);
static void get_fw_ver_ic(void *device_data);
/*static void get_config_ver(void *device_data);*/
/*static void get_threshold(void *device_data);*/
/*static void module_off_master(void *device_data);*/
/*static void module_on_master(void *device_data);*/
/*static void get_chip_vendor(void *device_data);*/
/*static void get_chip_name(void *device_data);*/
/*static void get_reference(void *device_data);*/
/*static void get_cm_abs(void *device_data);*/
/*static void get_cm_delta(void *device_data);*/
/*static void get_intensity(void *device_data);*/
static void get_x_num(void *device_data);
static void get_y_num(void *device_data);
/*static void run_reference_read(void *device_data);*/
/*static void run_cm_abs_read(void *device_data);*/
/*static void run_cm_delta_read(void *device_data);*/
/*static void run_intensity_read(void *device_data);*/
static void not_support_cmd(void *device_data);
/*static int check_delta_value(struct melfas_ts_data *ts);*/
struct tsp_cmd tsp_cmds[] = {
{TSP_CMD("fw_update", not_support_cmd),},
{TSP_CMD("get_fw_ver_bin", get_fw_ver_bin),},
{TSP_CMD("get_fw_ver_ic", get_fw_ver_ic),},
{TSP_CMD("get_config_ver", not_support_cmd),},
{TSP_CMD("get_threshold", not_support_cmd),},
{TSP_CMD("module_off_master", not_support_cmd),},
{TSP_CMD("module_on_master", not_support_cmd),},
{TSP_CMD("module_off_slave", not_support_cmd),},
{TSP_CMD("module_on_slave", not_support_cmd),},
{TSP_CMD("get_chip_vendor", not_support_cmd),},
{TSP_CMD("get_chip_name", not_support_cmd),},
{TSP_CMD("run_rawcap_read", not_support_cmd),},
{TSP_CMD("get_x_num", get_x_num),},
{TSP_CMD("get_y_num", get_y_num),},
{TSP_CMD("get_reference", not_support_cmd),},
{TSP_CMD("get_cm_abs", not_support_cmd),},
{TSP_CMD("get_cm_delta", not_support_cmd),},
{TSP_CMD("get_intensity", not_support_cmd),},
{TSP_CMD("run_reference_read", not_support_cmd),},
{TSP_CMD("run_cm_abs_read", not_support_cmd),},
{TSP_CMD("run_cm_delta_read", not_support_cmd),},
{TSP_CMD("run_intensity_read", not_support_cmd),},
{TSP_CMD("not_support_cmd", not_support_cmd),},
};
static void set_default_result(struct ist30xx_data *ts)
{
char delim = ':';
memset(ts->cmd_result, 0x00, ARRAY_SIZE(ts->cmd_result));
memcpy(ts->cmd_result, ts->cmd, strlen(ts->cmd));
strncat(ts->cmd_result, &delim, 1);
}
static void set_cmd_result(struct ist30xx_data *ts, char *buff, int len)
{
strncat(ts->cmd_result, buff, len);
}
static void not_support_cmd(void *device_data)
{
struct ist30xx_data *ts = (struct ist30xx_data *)device_data;
char buff[16] = {0};
set_default_result(ts);
snprintf(buff, sizeof(buff), "%s", "NA");
set_cmd_result(ts, buff, strnlen(buff, sizeof(buff)));
ts->cmd_state = 4;
dev_info(&ts->client->dev, "%s: \"%s(%d)\"\n", __func__,
buff, strnlen(buff, sizeof(buff)));
return;
}
static ssize_t show_close_tsp_test(struct device *dev,
struct device_attribute *attr, char *buf)
{
return snprintf(buf, TSP_BUF_SIZE, "%u\n", 0);
}
static ssize_t show_cmd_status(struct device *dev,
struct device_attribute *devattr, char *buf)
{
struct ist30xx_data *info = dev_get_drvdata(dev);
char buff[16] = {0};
dev_info(&info->client->dev, "tsp cmd: status:%d\n",
info->cmd_state);
if (info->cmd_state == 0)
snprintf(buff, sizeof(buff), "WAITING");
else if (info->cmd_state == 1)
snprintf(buff, sizeof(buff), "RUNNING");
else if (info->cmd_state == 2)
snprintf(buff, sizeof(buff), "OK");
else if (info->cmd_state == 3)
snprintf(buff, sizeof(buff), "FAIL");
else if (info->cmd_state == 4)
snprintf(buff, sizeof(buff), "NOT_APPLICABLE");
return snprintf(buf, TSP_BUF_SIZE, "%s\n", buff);
}
static ssize_t show_cmd_result(struct device *dev, struct device_attribute
*devattr, char *buf)
{
struct ist30xx_data *info = dev_get_drvdata(dev);
dev_info(&info->client->dev, "tsp cmd: result: %s\n", info->cmd_result);
mutex_lock(&info->cmd_lock);
info->cmd_is_running = false;
mutex_unlock(&info->cmd_lock);
info->cmd_state = 0;
return snprintf(buf, TSP_BUF_SIZE, "%s\n", info->cmd_result);
}
static ssize_t store_cmd(struct device *dev, struct device_attribute
*devattr, const char *buf, size_t count)
{
struct ist30xx_data *info = dev_get_drvdata(dev);
struct i2c_client *client = info->client;
char *cur, *start, *end;
char buff[TSP_CMD_STR_LEN] = {0};
int len, i;
struct tsp_cmd *tsp_cmd_ptr = NULL;
char delim = ',';
bool cmd_found = false;
int param_cnt = 0;
int ret;
if (info->cmd_is_running == true) {
dev_err(&info->client->dev, "tsp_cmd: other cmd is running.\n");
goto err_out;
}
/* check lock */
mutex_lock(&info->cmd_lock);
info->cmd_is_running = true;
mutex_unlock(&info->cmd_lock);
info->cmd_state = 1;
for (i = 0; i < ARRAY_SIZE(info->cmd_param); i++)
info->cmd_param[i] = 0;
len = (int)count;
if (*(buf + len - 1) == '\n')
len--;
memset(info->cmd, 0x00, ARRAY_SIZE(info->cmd));
memcpy(info->cmd, buf, len);
cur = strchr(buf, (int)delim);
if (cur)
memcpy(buff, buf, cur - buf);
else
memcpy(buff, buf, len);
/* find command */
list_for_each_entry(tsp_cmd_ptr, &info->cmd_list_head, list) {
if (!strcmp(buff, tsp_cmd_ptr->cmd_name)) {
cmd_found = true;
break;
}
}
/* set not_support_cmd */
if (!cmd_found) {
list_for_each_entry(tsp_cmd_ptr, &info->cmd_list_head, list) {
if (!strcmp("not_support_cmd", tsp_cmd_ptr->cmd_name))
break;
}
}
/* parsing parameters */
if (cur && cmd_found) {
cur++;
start = cur;
memset(buff, 0x00, ARRAY_SIZE(buff));
do {
if (*cur == delim || cur - buf == len) {
end = cur;
memcpy(buff, start, end - start);
*(buff + strlen(buff)) = '\0';
ret = kstrtoint(buff, 10,\
info->cmd_param + param_cnt);
start = cur + 1;
memset(buff, 0x00, ARRAY_SIZE(buff));
param_cnt++;
}
cur++;
} while (cur - buf <= len);
}
dev_info(&client->dev, "cmd = %s\n", tsp_cmd_ptr->cmd_name);
for (i = 0; i < param_cnt; i++)
dev_info(&client->dev, "cmd param %d= %d\n", i,
info->cmd_param[i]);
tsp_cmd_ptr->cmd_func(info);
err_out:
return count;
}
static void get_x_num(void *device_data)
{
struct ist30xx_data *ts = (struct ist30xx_data *)device_data;
char buff[16] = {0};
int val;
int exciting_ch;
set_default_result(ts);
exciting_ch = 14;
val = exciting_ch;
if (val < 0) {
snprintf(buff, sizeof(buff), "%s", "NG");
set_cmd_result(ts, buff, strnlen(buff, sizeof(buff)));
ts->cmd_state = 3;
dev_info(&ts->client->dev,
"%s: fail to read num of x (%d).\n", __func__, val);
return ;
}
snprintf(buff, sizeof(buff), "%u", val);
set_cmd_result(ts, buff, strnlen(buff, sizeof(buff)));
ts->cmd_state = 2;
dev_info(&ts->client->dev, "%s: %s(%d)\n", __func__, buff,
strnlen(buff, sizeof(buff)));
}
static void get_y_num(void *device_data)
{
struct ist30xx_data *ts = (struct ist30xx_data *)device_data;
char buff[16] = {0};
int val;
int sensing_ch;
set_default_result(ts);
sensing_ch = 16;
val = sensing_ch;
if (val < 0) {
snprintf(buff, sizeof(buff), "%s", "NG");
set_cmd_result(ts, buff, strnlen(buff, sizeof(buff)));
ts->cmd_state = 3;
dev_info(&ts->client->dev,
"%s: fail to read num of y (%d).\n", __func__, val);
return ;
}
snprintf(buff, sizeof(buff), "%u", val);
set_cmd_result(ts, buff, strnlen(buff, sizeof(buff)));
ts->cmd_state = 2;
dev_info(&ts->client->dev, "%s: %s(%d)\n", __func__, buff,
strnlen(buff, sizeof(buff)));
}
static void get_fw_ver_bin(void *device_data)
{
struct ist30xx_data *info = (struct ist30xx_data *)device_data;
char buff[16] = {0};
u32 val = 0;
printk(KERN_INFO "[TSP] %s, %d\n",
__func__, __LINE__);
set_default_result(info);
val = ist30xx_get_ic_fw_ver();
snprintf(buff, sizeof(buff), "IM00%04x", val);
set_cmd_result(info, buff, strnlen(buff, sizeof(buff)));
info->cmd_state = 2;
dev_info(&info->client->dev, "%s: %s(%d)\n", __func__,
buff, strnlen(buff, sizeof(buff)));
}
static void get_fw_ver_ic(void *device_data)
{
struct ist30xx_data *info = (struct ist30xx_data *)device_data;
char buff[16] = {0};
int ret, part_fw_ver;
set_default_result(info);
ist30xx_disable_irq(ts_data);
ret = ist30xx_cmd_run_device(ts_data->client);
ret = ist30xx_read_cmd(ts_data->client,
CMD_GET_PARAM_VER, &ts_data->param_ver);
part_fw_ver = ts_data->param_ver;
ist30xx_start(ts_data);
ist30xx_enable_irq(ts_data);
snprintf(buff, sizeof(buff), "IM00%02x%02x",
(part_fw_ver>>8)&0xFF, part_fw_ver&0xFF);
set_cmd_result(info, buff, strnlen(buff, sizeof(buff)));
info->cmd_state = 2;
dev_info(&info->client->dev, "%s: %s(%d)\n", __func__,
buff, strnlen(buff, sizeof(buff)));
}
static ssize_t phone_firmware_show(struct device *dev,
struct device_attribute *attr, char *buf)
{
int phone_fw_ver = 0;
u32 val = 0;
printk(KERN_INFO "[TSP] %s\n", __func__);
val = ist30xx_get_ic_fw_ver();
return sprintf(buf, "IM00%04x", val);
}
static ssize_t part_firmware_show(struct device *dev,
struct device_attribute *attr, char *buf)
{
int ret, part_fw_ver;
printk(KERN_INFO "[TSP] %s\n", __func__);
ist30xx_disable_irq(ts_data);
ret = ist30xx_cmd_run_device(ts_data->client);
ret = ist30xx_read_cmd(ts_data->client,
CMD_GET_PARAM_VER, &ts_data->param_ver);
part_fw_ver = ts_data->param_ver;
ist30xx_start(ts_data);
ist30xx_enable_irq(ts_data);
printk(KERN_INFO "[TSP] %s : %x\n",
__func__, part_fw_ver);
return sprintf(buf, "IM00%02x%02x",
(part_fw_ver>>8)&0xFF, part_fw_ver&0xFF);
}
static ssize_t threshold_firmware_show(struct device *dev,
struct device_attribute *attr, char *buf)
{
int threshord = 30;
printk(KERN_INFO "[TSP] %s\n", __func__);
return sprintf(buf, "%d", threshord);
}
static ssize_t menu_sensitivity_show(struct device *dev,
struct device_attribute *attr, char *buf)
{
printk(KERN_INFO "[TSP] %s\n", __func__, ist30xx_zvalue_menu);
return sprintf(buf, "%d\n", ist30xx_zvalue_menu);
}
static ssize_t back_sensitivity_show(struct device *dev,
struct device_attribute *attr, char *buf)
{
printk(KERN_INFO "[TSP] %s\n", __func__, ist30xx_zvalue_back);
return sprintf(buf, "%d\n", ist30xx_zvalue_back);
}
static ssize_t touchkey_threshold_show(struct device *dev,
struct device_attribute *attr, char *buf)
{
int threshold = 30;
printk(KERN_INFO "[TSP] touch tkey threshold: %d\n", threshold);
return snprintf(buf, sizeof(int), "%d\n", threshold);
}
static ssize_t firmware_update(struct device *dev,
struct device_attribute *attr, char *buf)
{
bool ret;
printk(KERN_INFO "[TSP] %s\n", __func__);
sprintf(IsfwUpdate, "%s\n", FW_DOWNLOADING);
ret = ist30xx_force_fw_update(ts_data);
ret = ist30xx_force_param_update(ts_data);
if (ret == true) {
sprintf(IsfwUpdate, "%s\n", FW_DOWNLOAD_COMPLETE);
return sprintf(buf, "%d", 1);
} else {
sprintf(IsfwUpdate, "%s\n", FW_DOWNLOAD_FAIL);
return sprintf(buf, "%d", -1);
}
}
static ssize_t firmware_update_status(struct device *dev,
struct device_attribute *attr, char *buf)
{
printk(KERN_INFO "[TSP] %s\n", __func__);
return sprintf(buf, "%s\n", IsfwUpdate);
}
void ist30xx_disable_irq(struct ist30xx_data *data)
{
if (data->irq_enabled) {
disable_irq(data->client->irq);
data->irq_enabled = 0;
}
}
void ist30xx_enable_irq(struct ist30xx_data *data)
{
if (!data->irq_enabled) {
enable_irq(data->client->irq);
msleep(50);
data->irq_enabled = 1;
}
}
int ist30xx_max_error_cnt = MAX_ERR_CNT;
int ist30xx_error_cnt;
static void ist30xx_request_reset(void)
{
ist30xx_error_cnt++;
if (ist30xx_error_cnt >= ist30xx_max_error_cnt) {
schedule_delayed_work(&work_reset_check, 0);
DMSG("[ TSP ] ist30xx_request_reset!\n");
ist30xx_error_cnt = 0;
}
}
void ts_power_enable(int en)
{
int ret;
printk(KERN_ERR "%s %s\n", __func__, (en) ? "on" : "off");
if (touch_regulator == NULL) {
touch_regulator = regulator_get(NULL, "gpldo2_uc");
if (IS_ERR(touch_regulator))
printk(KERN_ERR "can not get VTOUCH_3.3V\n");
printk(KERN_INFO "touch_regulator= %d\n", touch_regulator);
}
if (en == 1) {
ret = regulator_set_voltage(touch_regulator,
2000000, 2000000);
/*@Fixed me, HW*/
if (ret < 0)
printk(KERN_INFO
"[TSP] regulator_set_voltage ret = %d\n",
ret);
ret = regulator_enable(touch_regulator);
if (ret < 0)
printk(KERN_INFO
"[TSP] regulator_enable ret = %d\n",
ret);
} else {
ret = regulator_disable(touch_regulator);
if (ret < 0)
printk(KERN_INFO "regulator_disable ret = %d\n", ret);
}
}
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);
DMSG("[ TSP ] ist30xx_start, ta_mode : %d\n",
ist30xx_ta_status);
}
#endif
ist30xx_cmd_start_scan(data->client);
}
static int ist30xx_get_ver_info(struct ist30xx_data *data)
{
int ret;
data->fw.pre_ver = data->fw.ver;
data->fw.ver = 0;
ret = ist30xx_read_cmd(data->client,
CMD_GET_CHIP_ID, &data->chip_id);
if (ret)
return -EIO;
ret = ist30xx_read_cmd(data->client,
CMD_GET_FW_VER, &data->fw.ver);
if (ret)
return -EIO;
ret = ist30xx_read_cmd(data->client,
CMD_GET_PARAM_VER, &data->param_ver);
if (ret)
return -EIO;
DMSG("[ TSP ] Chip ID : %x F/W: %x Param: %x\n",
data->chip_id, data->fw.ver, data->param_ver);
if ((data->chip_id != IST30XX_CHIP_ID) &&
(data->chip_id != IST30XXA_CHIP_ID))
return -EPERM;
return 0;
}
int ist30xx_init_touch_driver(struct ist30xx_data *data)
{
int ret;
ist30xx_disable_irq(data);
ret = ist30xx_cmd_run_device(data->client);
if (ret)
return ret;
ret = ist30xx_get_ver_info(data);
if (ret)
return ret;
ist30xx_start(data);
ist30xx_enable_irq(data);
return 0;
}
#define PRESS_MSG_MASK (0x01)
#define MULTI_MSG_MASK (0x02)
#define PRESS_MSG_KEY (0x6)
#define CALIB_MSG_MASK (0xF0000FFF)
#define CALIB_MSG_VALID (0x80000CAB)
static void clear_input_data(struct ist30xx_data *data)
{
int i, pressure, count;
for (i = 0, count = 0; i < IST30XX_MAX_MT_FINGERS; i++) {
if (data->fingers[i].bit_field.id == 0)
continue;
pressure = (data->fingers[i].bit_field.udmg & PRESS_MSG_MASK);
if (pressure) {
#if (LINUX_VERSION_CODE > KERNEL_VERSION(3, 0, 0))
input_mt_slot(data->input_dev,
data->fingers[i].bit_field.id - 1);
input_mt_report_slot_state(data->input_dev,
MT_TOOL_FINGER, false);
#else
input_report_abs(data->input_dev, ABS_MT_POSITION_X,
data->fingers[i].bit_field.x);
input_report_abs(data->input_dev, ABS_MT_POSITION_Y,
data->fingers[i].bit_field.y);
input_report_abs(data->input_dev, ABS_MT_TOUCH_MAJOR,
0);
input_report_abs(data->input_dev, ABS_MT_WIDTH_MAJOR,
0);
input_mt_sync(data->input_dev);
#endif
data->fingers[i].bit_field.id = 0;
count++;
}
}
if (count > 0)
input_sync(data->input_dev);
}
static int touch_press;
static void report_input_data(struct ist30xx_data *data,
int finger_counts, int key_counts)
{
int i, pressure, count;
for (i = 0, count = 0; i < finger_counts; i++) {
if ((data->fingers[i].bit_field.id == 0) ||
(data->fingers[i].bit_field.id > IST30XX_MAX_MT_FINGERS) ||
(data->fingers[i].bit_field.x > IST30XX_MAX_X) ||
(data->fingers[i].bit_field.y > IST30XX_MAX_Y)) {
DMSG("[ TSP ] Error, [%d][%d] - [%d][%d]\n", i,
data->fingers[i].bit_field.id,
data->fingers[i].bit_field.x,
data->fingers[i].bit_field.y);
data->fingers[i].bit_field.id = 0;
ist30xx_request_reset();
continue;
}
pressure = data->fingers[i].bit_field.udmg & PRESS_MSG_MASK;
if (touch_press == 0) {
DMSG(
"[ TSP ] [%d][%d][%d] x, y, z = %03d, %03d, %04d\n",
i, data->fingers[i].bit_field.id, pressure,
data->fingers[i].bit_field.x,
data->fingers[i].bit_field.y,
data->fingers[i].bit_field.w << 5);
}
input_mt_slot(data->input_dev,
data->fingers[i].bit_field.id - 1);
input_mt_report_slot_state(data->input_dev, MT_TOOL_FINGER,
(pressure ? true : false));
if (pressure) {
input_report_abs(data->input_dev, ABS_MT_POSITION_X,
data->fingers[i].bit_field.x);
input_report_abs(data->input_dev, ABS_MT_POSITION_Y,
data->fingers[i].bit_field.y);
touch_press = 1;
} else {
ist30xx_zvalue = 0;
DMSG("[ TSP ] [%d][%d][%d] x, y, z = %03d, %03d, %04d\n", i,
data->fingers[i].bit_field.id, pressure,
data->fingers[i].bit_field.x,
data->fingers[i].bit_field.y,
data->fingers[i].bit_field.w << 5);
touch_press = 0;
}
count++;
ist30xx_error_cnt = 0;
}
#if IST30XX_USE_KEY
for (i = finger_counts; i < finger_counts + key_counts; i++) {
int press, id;
press = (data->fingers[i].bit_field.w == PRESS_MSG_KEY) ? 1 : 0;
id = data->fingers[i].bit_field.id;
DMSG("[ TSP ] [%08x] id: %d, event: %d, cdc: %d, anr: %d\n",
data->fingers[i].full_field, id,
data->fingers[i].bit_field.w,
data->fingers[i].bit_field.x,
data->fingers[i].bit_field.y);
if (id == 1) {
if (!press)
ist30xx_zvalue_menu = 0;
else
ist30xx_zvalue_menu = ist30xx_zvalue;
} else if (id == 2) {
if (!press)
ist30xx_zvalue_back = 0;
else
ist30xx_zvalue_back = ist30xx_zvalue;
}
input_report_key(data->input_dev, ist30xx_key_code[id], press);
count++;
}
#endif
if (count > 0)
input_sync(data->input_dev);
}
/*
* 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
*/
bool get_zvalue_mode;
static irqreturn_t ist30xx_irq_thread(int irq, void *ptr)
{
int i, ret;
int key_cnt, finger_cnt, read_cnt;
struct ist30xx_data *data = ptr;
u32 msg[IST30XX_MAX_MT_FINGERS];
if (!data->irq_enabled)
return IRQ_HANDLED;
memset(msg, 0, IST30XX_MAX_MT_FINGERS);
ret = ist30xx_get_position(data->client, msg, 1);
if (ret)
goto irq_err;
/*DMSG("[ TSP ] intr thread msg: 0x%08x\n", *msg);*/
if (msg[0] == 0)
return IRQ_HANDLED;
if ((msg[0] & CALIB_MSG_MASK) == CALIB_MSG_VALID) {
data->status.calib = msg[0];
return IRQ_HANDLED;
}
for (i = 0; i < IST30XX_MAX_MT_FINGERS; i++)
data->fingers[i].full_field = 0;
key_cnt = 0;
finger_cnt = 1;
read_cnt = 1;
data->fingers[0].full_field = msg[0];
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 (read_cnt > IST30XX_MAX_MT_FINGERS)
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
}
if (read_cnt > 0)
report_input_data(data, finger_cnt, key_cnt);
if (get_zvalue_mode == true) {
u32 zvalue = 0;
ret = ist30xx_get_position(data->client, &zvalue, 1);
if (ret)
goto irq_err;
if (zvalue > 0)
ist30xx_zvalue = zvalue;
/*DMSG("[ TSP ] [%08x] key raw data: %d\n",
zvalue, ist30xx_zvalue);*/
}
return IRQ_HANDLED;
irq_err:
pr_err("[ TSP ] intr msg[0]: 0x%08x, ret: %d\n", msg[0], ret);
ist30xx_request_reset();
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_enable_irq(data);
mutex_unlock(&ist30xx_mutex);
ist30xx_start(data);
}
#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
void ist30xx_set_ta_mode(bool charging)
{
#if IST30XX_DETECT_TA
if ((ist30xx_ta_status == -1) || (charging == ist30xx_ta_status))
return;
ist30xx_ta_status = charging ? 1 : 0;
schedule_delayed_work(&work_reset_check, 0);
#endif
}
EXPORT_SYMBOL(ist30xx_set_ta_mode);
static void reset_work_func(struct work_struct *work)
{
if ((ts_data == NULL) || (ts_data->client == NULL))
return;
DMSG("[ TSP ] Request reset function\n");
if ((ts_data->status.power == 1) && (ts_data->status.fw_update != 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);
}
}
static DEVICE_ATTR(close_tsp_test, S_IRUGO, show_close_tsp_test, NULL);
static DEVICE_ATTR(cmd, S_IWUSR | S_IWGRP, NULL, store_cmd);
static DEVICE_ATTR(cmd_status, S_IRUGO, show_cmd_status, NULL);
static DEVICE_ATTR(cmd_result, S_IRUGO, show_cmd_result, NULL);
static struct attribute *sec_touch_facotry_attributes[] = {
&dev_attr_close_tsp_test.attr,
&dev_attr_cmd.attr,
&dev_attr_cmd_status.attr,
&dev_attr_cmd_result.attr,
NULL,
};
static struct attribute_group sec_touch_factory_attr_group = {
.attrs = sec_touch_facotry_attributes,
};
int Ist30xx_ts_check(void)
{
int ret, i;
uint8_t buf_tmp[3] = {0, 0, 0};
int retry = 3;
buf_firmware[0] = 0xAA;
buf_firmware[1] = 0x10;
printk(KERN_INFO "[TSP] Ist30xx_ts_check start.\n");
ret = i2c_master_send(ts_data->client, &buf_firmware, 2);
/*i2c read retry*/
if (ret <= 0) {
for (i = 0; i < retry; i++) {
ret = i2c_master_send(ts_data->client,
&buf_firmware, 2);
if (ret > 0)
break;
}
printk(KERN_ERR "%s : i2c_master_send fail [%d]\n",
__func__, ret);
tsp_check = 1;
}
if (ret <= 0) {
printk(KERN_INFO "[TSP][Ist30xx][%s] %s\n",
__func__, "Ist30xx Failed i2c");
Ist30xx_Connected = 0;
ret = 0;
} else {
printk(KERN_INFO "[TSP][Ist30xx][%s] %s\n",
__func__, "Passed Ist30xx i2c");
Ist30xx_Connected = 1;
if (tsp_check == 0) {
ret = i2c_master_recv(ts_data->client,
&buf_firmware, 3);
if (ret < 0) {
printk(KERN_ERR "%s : i2c_master_recv fail[%d]\n",
__func__, ret);
tsp_check = 1;
}
}
/*printk("[TSP][Silabs][%s][SlaveAddress
: 0x%x][VendorID : 0x%x]
[HW : 0x%x] [SW : 0x%x]\n", __func__,ts_global->client->addr,
buf_firmware[0], buf_firmware[1], buf_firmware[2]);*/
ret = 1;
}
return ret;
}
static int ist30xx_probe(struct i2c_client *client,
const struct i2c_device_id *id)
{
int i, ret;
struct ist30xx_data *data;
struct input_dev *input_dev;
struct ist30xx_data *touch_dev;
struct device *fac_dev_ts;
DMSG("[ TSP ] %s() ,the i2c addr=0x%x", __func__, client->addr);
printk(KERN_INFO "[TSP] ist30xx_probe\n");
if (Zinitix_Connected == 1) {
printk(KERN_INFO "[TSP][Ist30xx][%s] %s\n",
__func__, "Zinitix TSP already detected !!");
return -ENXIO;
}
data = kzalloc(sizeof(*data), GFP_KERNEL);
if (!data)
return -ENOMEM;
input_dev = input_allocate_device();
if (!input_dev) {
ret = -ENOMEM;
pr_err("%s: input_allocate_device failed (%d)\n",
__func__, ret);
goto err_alloc_dev;
}
data->num_fingers = 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);
set_bit(EV_LED, input_dev->evbit);
set_bit(LED_MISC, input_dev->ledbit);
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);
#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;
}
#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
ts_data = data;
ret = Ist30xx_ts_check();
if (ret <= 0) {
i2c_release_client(client);
ts_power_enable(0);
ret = -ENXIO;
goto err_input_dev_alloc_failed;
}
ts_power_enable(0);
ret = ist30xx_init_system();
if (ret) {
dev_err(&client->dev, "chip initialization failed\n");
goto err_init_drv;
}
if (ret)
goto err_irq;
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
ret = request_threaded_irq(client->irq, NULL, ist30xx_irq_thread,
IRQF_TRIGGER_FALLING | \
IRQF_ONESHOT, "ist30xx_ts", data);
# if IST30XX_INTERNAL_BIN
ist30xx_disable_irq(data);
ist30xx_cmd_run_device(data->client);
ist30xx_get_ver_info(data);
ret = ist30xx_auto_fw_update(data);
if (ret)
goto err_irq;
ret = ist30xx_auto_param_update(data);
if (ret)
goto err_irq;
ist30xx_start(data);
ist30xx_enable_irq(data);
# endif
# if SEC_FACTORY_TEST
ist30xx_sec_sysfs();
if (ret)
goto err_init_drv;
# endif
INIT_DELAYED_WORK(&work_reset_check, reset_work_func);
/* sys fs */
sec_touchscreen = device_create(sec_class, NULL,
0, NULL, "sec_touchscreen");
if (IS_ERR(sec_touchscreen)) {
dev_err(&client->dev,
"Failed to create device for the sysfs1\n");
ret = -ENODEV;
}
if (device_create_file(sec_touchscreen,
&dev_attr_tsp_firm_version_phone) < 0)
pr_err("Failed to create device file(%s)!\n",
dev_attr_tsp_firm_version_phone.attr.name);
if (device_create_file(sec_touchscreen,
&dev_attr_tsp_firm_version_panel) < 0)
pr_err("Failed to create device file(%s)!\n",
dev_attr_tsp_firm_version_panel.attr.name);
if (device_create_file(sec_touchscreen,
&dev_attr_tsp_threshold) < 0)
pr_err("Failed to create device file(%s)!\n",
dev_attr_tsp_threshold.attr.name);
if (device_create_file(sec_touchscreen,
&dev_attr_tsp_firm_update) < 0)
pr_err("Failed to create device file(%s)!\n",
dev_attr_tsp_firm_update.attr.name);
if (device_create_file(sec_touchscreen,
&dev_attr_tsp_firm_update_status) < 0)
pr_err("[TSP] Failed to create device file(%s)!\n",
dev_attr_tsp_firm_update_status.attr.name);
sec_touchkey = device_create(sec_class, NULL, 0, NULL, "sec_touchkey");
if (IS_ERR(sec_touchkey)) {
dev_err(&client->dev, "Failed to create device for the sysfs1\n");
ret = -ENODEV;
}
if (device_create_file(sec_touchkey,
&dev_attr_touchkey_menu) < 0)
pr_err("Failed to create device file(%s)!\n",
dev_attr_touchkey_menu.attr.name);
if (device_create_file(sec_touchkey, &dev_attr_touchkey_back) < 0)
pr_err("Failed to create device file(%s)!\n",
dev_attr_touchkey_back.attr.name);
touch_dev = kzalloc(sizeof(struct ist30xx_data), GFP_KERNEL);
if (!touch_dev) {
printk(KERN_ERR "unabled to allocate touch data \r\n");
ret = -ENOMEM;
goto err_reg_dev;
}
touch_dev->client = client;
i2c_set_clientdata(client, touch_dev);
INIT_LIST_HEAD(&touch_dev->cmd_list_head);
for (i = 0; i < ARRAY_SIZE(tsp_cmds); i++)
list_add_tail(&tsp_cmds[i].list,
&touch_dev->cmd_list_head);
mutex_init(&touch_dev->cmd_lock);
touch_dev->cmd_is_running = false;
fac_dev_ts = device_create(sec_class, NULL, 0, touch_dev, "tsp");
if (IS_ERR(fac_dev_ts))
printk(KERN_ERR "Failed to create device for the sysfs\n");
ret = sysfs_create_group(&fac_dev_ts->kobj,
&sec_touch_factory_attr_group);
if (ret)
dev_err(&client->dev, "Failed to create sysfs group\n");
#if IST30XX_DETECT_TA
ist30xx_ta_status = 0;
#endif
return 0;
err_irq:
ist30xx_disable_irq(data);
err_init_drv:
pr_err("[ TSP ] Error, ist30xx init driver\n");
ist30xx_power_off();
input_unregister_device(input_dev);
return 0;
err_input_dev_alloc_failed:
printk(KERN_ERR "ist30xx-ts: err_input_dev_alloc failed\n");
err_reg_dev:
err_alloc_dev:
pr_err("[ TSP ] 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
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
},
};
static int __init ist30xx_init(void)
{
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");