sdk-hwV1.3/lichee/linux-4.9/drivers/input/touchscreen/tlsc6x/tlsc6x_main.c

2135 lines
48 KiB
C

/*
* This software is licensed under the terms of the GNU General Public
* License version 2, as published by the Free Software Foundation, and
* may be copied, distributed, and modified under those terms.
* 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.
* * VERSION DATE AUTHOR Note
*
*/
#include <linux/firmware.h>
#include <linux/platform_device.h>
#include <linux/delay.h>
#include <linux/module.h>
#include <linux/interrupt.h>
#include <linux/slab.h>
#include <linux/i2c.h>
#include <linux/input.h>
#include <asm/uaccess.h>
#include <linux/gpio.h>
#include <linux/regulator/consumer.h>
/* #include <soc/sprd/regulator.h> */
#include <linux/input/mt.h>
#include <linux/of_device.h>
#include <linux/of_address.h>
#include <linux/of_gpio.h>
#include <linux/sched.h>
#include <linux/kthread.h>
#include <linux/completion.h>
#include <linux/err.h>
#include <linux/proc_fs.h>
#include <linux/file.h>
#include <linux/fs.h>
#include <linux/kernel.h>
#include <linux/string.h>
#include <asm/unistd.h>
#include <asm/io.h>
#include <linux/pm_runtime.h>
#include <linux/types.h>
#include <linux/fb.h>
#if defined(CONFIG_ADF)
#include <linux/notifier.h>
//#include <video/adf_notifier.h>
#include <linux/fb.h>
#elif defined(CONFIG_HAS_EARLYSUSPEND)
#include <linux/earlysuspend.h>
#endif
#include <linux/fs.h>
#include <linux/miscdevice.h>
#include <linux/ioctl.h>
#include <linux/wakelock.h>
#include <linux/suspend.h>
#include <linux/irq.h>
#include <linux/string.h>
#include "tlsc6x_main.h"
#ifdef CONFIG_HWINFO
#include <linux/hwinfo.h>
static struct hwinfo *tp_hwinfo;
static char g_ft_tp_firmware[20];
static char *g_tp_devinfo = "CHSC6448_VGA";
static char *g_tp_devinfo1 = "CHSC6448_HD";
unsigned short version_cfg[102];
unsigned int cfg_ver;
int cfg_version = -1;
extern int tlsc6x_get_running_cfg(unsigned short *ptcfg);
#endif
#define TOUCH_VIRTUAL_KEYS
#define MULTI_PROTOCOL_TYPE_B 0
#define TS_MAX_FINGER 2
#define MAX_CHIP_ID (10)
#define TS_NAME "tlsc6x_ts"
unsigned char tlsc6x_chip_name[MAX_CHIP_ID][20] = { "null", "tlsc6206a",
"0x6306", "tlsc6206", "tlsc6324", "tlsc6332", "tlsc6440", "tlsc6432",
"tlsc6424", "tlsc6448"
};
int g_is_telink_comp;
struct tlsc6x_data *g_tp_drvdata;
static struct i2c_client *this_client;
static struct wake_lock tlsc6x_wakelock;
#ifdef TLSC_TPD_PROXIMITY
static int tlsc6x_prox_ctrl(int enable);
#endif
static int tlsc6x_do_suspend(void);
static int tlsc6x_do_resume(void);
DEFINE_MUTEX(i2c_rw_access);
#if defined(CONFIG_ADF)
static int tlsc6x_adf_suspend(void);
static int tlsc6x_adf_resume(void);
#elif defined(CONFIG_HAS_EARLYSUSPEND)
static void tlsc6x_ts_suspend(struct early_suspend *handler);
static void tlsc6x_ts_resume(struct early_suspend *handler);
#endif
#ifdef TLSC_ESD_HELPER_EN
static int tpd_esd_flag;
static struct hrtimer tpd_esd_kthread_timer;
static DECLARE_WAIT_QUEUE_HEAD(tpd_esd_waiter);
#endif
#ifdef TLSC_TPD_PROXIMITY
unsigned char tpd_prox_old_state;
static int tpd_prox_active;
static struct class *sprd_tpd_class;
static struct device *sprd_ps_cmd_dev;
#endif
#ifdef TOUCH_VIRTUAL_KEYS
static ssize_t virtual_keys_show(struct kobject *kobj,
struct kobj_attribute *attr, char *buf)
{
struct tlsc6x_data *data = i2c_get_clientdata(this_client);
struct tlsc6x_platform_data *pdata = data->platform_data;
return snprintf(buf, PAGE_SIZE,
"%s:%s:%d:%d:%d:%d:%s:%s:%d:%d:%d:%d:%s:%s:%d:%d:%d:%d\n",
__stringify(EV_KEY), __stringify(KEY_APPSELECT),
pdata->virtualkeys[0], pdata->virtualkeys[1],
pdata->virtualkeys[2], pdata->virtualkeys[3],
__stringify(EV_KEY), __stringify(KEY_HOMEPAGE),
pdata->virtualkeys[4], pdata->virtualkeys[5],
pdata->virtualkeys[6], pdata->virtualkeys[7],
__stringify(EV_KEY), __stringify(KEY_BACK),
pdata->virtualkeys[8], pdata->virtualkeys[9],
pdata->virtualkeys[10], pdata->virtualkeys[11]);
}
static struct kobj_attribute virtual_keys_attr = {
.attr = {.name = "XXXXXXXXXXXXXXXXXXXXXXXX", .mode = 0444,}, .show =
&virtual_keys_show,
};
static struct attribute *properties_attrs[] = { &virtual_keys_attr.attr,
NULL
};
static struct attribute_group properties_attr_group = {.attrs =
properties_attrs,
};
static void tlsc6x_virtual_keys_init(void)
{
int ret = 0;
struct kobject *properties_kobj;
TLSC_FUNC_ENTER();
//properties_kobj = kobject_create_and_add("board_properties", NULL);
properties_kobj = kobject_create_and_add("board_properties",
&g_tp_drvdata->input_dev->dev.
kobj);
if (properties_kobj) {
ret =
sysfs_create_group(properties_kobj, &properties_attr_group);
}
if (!properties_kobj || ret) {
tlsc_err("failed to create board_properties\n");
}
}
#endif
/*
iic access interface
*/
int tlsc6x_i2c_read_sub(struct i2c_client *client, char *writebuf, int writelen,
char *readbuf, int readlen)
{
int ret = 0;
if (client == NULL) {
tlsc_err("[IIC][%s]i2c_client==NULL!\n", __func__);
return -EINVAL;
}
if (readlen > 0) {
if (writelen > 0) {
struct i2c_msg msgs[] = { {.addr = client->addr, .flags =
0, .len = writelen, .buf =
writebuf,}, {.addr =
client->addr,
.flags =
I2C_M_RD, .len =
readlen, .buf =
readbuf,},
};
ret = i2c_transfer(client->adapter, msgs, 1);
if (ret < 0) {
tlsc_err
("[IIC]: i2c_transfer(1) error, addr= 0x%x!!\n",
writebuf[0]);
tlsc_err
("[IIC]: i2c_transfer(1) error, ret=%d, rlen=%d, wlen=%d!!\n",
ret, readlen, writelen);
} else {
ret =
i2c_transfer(client->adapter, &msgs[1], 1);
if (ret < 0) {
tlsc_err
("[IIC]: i2c_transfer(2) error, addr= 0x%x!!\n",
writebuf[0]);
tlsc_err
("[IIC]: i2c_transfer(2) error, ret=%d, rlen=%d, wlen=%d!!\n",
ret, readlen, writelen);
}
}
} else {
struct i2c_msg msgs[] = { {.addr = client->addr, .flags =
I2C_M_RD,
.len = readlen, .buf =
readbuf,},
};
ret = i2c_transfer(client->adapter, msgs, 1);
if (ret < 0) {
tlsc_err
("[IIC]: i2c_transfer(read) error, ret=%d, rlen=%d, wlen=%d!!",
ret, readlen, writelen);
}
}
}
return ret;
}
/* fail : <0 */
int tlsc6x_i2c_read(struct i2c_client *client, char *writebuf, int writelen,
char *readbuf, int readlen)
{
int ret = 0;
/* lock in this function so we can do direct mode iic transfer in debug fun */
mutex_lock(&i2c_rw_access);
ret = tlsc6x_i2c_read_sub(client, writebuf, writelen, readbuf, readlen);
mutex_unlock(&i2c_rw_access);
return ret;
}
/* fail : <0 */
int tlsc6x_i2c_write_sub(struct i2c_client *client, char *writebuf,
int writelen)
{
int ret = 0;
if (client == NULL) {
tlsc_err("[IIC][%s]i2c_client==NULL!\n", __func__);
return -EINVAL;
}
if (writelen > 0) {
struct i2c_msg msgs[] = { {.addr = client->addr, .flags =
0, .len = writelen, .buf = writebuf,},
};
ret = i2c_transfer(client->adapter, msgs, 1);
if (ret < 0) {
tlsc_err("[IIC]: i2c_transfer(write) error, ret=%d!!\n",
ret);
}
}
return ret;
}
/* fail : <0 */
int tlsc6x_i2c_write(struct i2c_client *client, char *writebuf, int writelen)
{
int ret = 0;
mutex_lock(&i2c_rw_access);
ret = tlsc6x_i2c_write_sub(client, writebuf, writelen);
mutex_unlock(&i2c_rw_access);
return ret;
}
/* fail : <0 */
int tlsc6x_write_reg(struct i2c_client *client, u8 regaddr, u8 regvalue)
{
unsigned char buf[2] = { 0 };
buf[0] = regaddr;
buf[1] = regvalue;
return tlsc6x_i2c_write(client, buf, sizeof(buf));
}
/* fail : <0 */
int tlsc6x_read_reg(struct i2c_client *client, u8 regaddr, u8 *regvalue)
{
return tlsc6x_i2c_read(client, &regaddr, 1, regvalue, 1);
}
static void tlsc6x_clear_report_data(struct tlsc6x_data *drvdata)
{
int i;
for (i = 0; i < TS_MAX_FINGER; i++) {
#if MULTI_PROTOCOL_TYPE_B
input_mt_slot(drvdata->input_dev, i);
input_mt_report_slot_state(drvdata->input_dev, MT_TOOL_FINGER,
false);
#endif
}
input_report_key(drvdata->input_dev, BTN_TOUCH, 0);
#if !MULTI_PROTOCOL_TYPE_B
input_mt_sync(drvdata->input_dev);
#endif
input_sync(drvdata->input_dev);
}
void tlsc6x_irq_disable(void)
{
TLSC_FUNC_ENTER();
if (!g_tp_drvdata->irq_disabled) {
disable_irq_nosync(this_client->irq);
g_tp_drvdata->irq_disabled = true;
}
}
void tlsc6x_irq_enable(void)
{
TLSC_FUNC_ENTER();
if (g_tp_drvdata->irq_disabled) {
enable_irq(this_client->irq);
g_tp_drvdata->irq_disabled = false;
}
}
static int tlsc6x_update_data(void)
{
struct tlsc6x_data *data = i2c_get_clientdata(this_client);
struct ts_event *event = &data->event;
u8 buf[20] = { 0 };
int ret = -1;
int i;
u16 x, y;
u8 tlsc_pressure, tlsc_size;
ret = tlsc6x_i2c_read(this_client, buf, 1, buf, 18);
if (ret < 0) {
tlsc_err("%s read_data i2c_rxdata failed: %d\n", __func__, ret);
return ret;
}
memset(event, 0, sizeof(struct ts_event));
event->touch_point = buf[2] & 0x07;
#ifdef TLSC_TPD_PROXIMITY
if (tpd_prox_active && (event->touch_point == 0)) {
if (((buf[1] == 0xc0) || (buf[1] == 0xe0))
&& (tpd_prox_old_state != buf[1])) {
input_report_abs(g_tp_drvdata->ps_input_dev,
ABS_DISTANCE,
(buf[1] == 0xc0) ? 0 : 1);
input_mt_sync(g_tp_drvdata->ps_input_dev);
input_sync(g_tp_drvdata->ps_input_dev);
}
tpd_prox_old_state = buf[1];
}
#endif
for (i = 0; i < TS_MAX_FINGER; i++) {
if ((buf[6 * i + 3] & 0xc0) == 0xc0) {
continue;
}
x = (s16) (buf[6 * i + 3] & 0x0F) << 8 | (s16) buf[6 * i + 4];
y = (s16) (buf[6 * i + 5] & 0x0F) << 8 | (s16) buf[6 * i + 6];
#ifdef CONFIG_OF
if (data->platform_data) {
if (data->platform_data->exchange_x_y_flag)
swap(x, y);
if (data->platform_data->revert_x_flag)
x = data->platform_data->x_res_max - x;
if (data->platform_data->revert_y_flag)
y = data->platform_data->y_res_max - y;
}
#endif
tlsc_pressure = buf[6 * i + 7];
if (tlsc_pressure > 127) {
tlsc_pressure = 127;
}
tlsc_size = (buf[6 * i + 8] >> 4) & 0x0F;
if ((buf[6 * i + 3] & 0x40) == 0x0) {
#if MULTI_PROTOCOL_TYPE_B
input_mt_slot(data->input_dev, buf[6 * i + 5] >> 4);
input_mt_report_slot_state(data->input_dev,
MT_TOOL_FINGER, true);
#else
input_report_abs(data->input_dev, ABS_MT_TRACKING_ID,
buf[6 * i + 5] >> 4);
#endif
if (y == 1500) {
if (x == 40) {
input_report_key(data->input_dev,
KEY_MENU, 1);
}
if (x == 80) {
input_report_key(data->input_dev,
KEY_HOMEPAGE, 1);
}
if (x == 120) {
input_report_key(data->input_dev,
KEY_BACK, 1);
}
} else {
// input_report_abs(data->input_dev, ABS_MT_POSITION_X, 319 - y);
// input_report_abs(data->input_dev, ABS_MT_POSITION_Y, x);
input_report_abs(data->input_dev,
ABS_MT_POSITION_X, x);
input_report_abs(data->input_dev,
ABS_MT_POSITION_Y, y);
input_report_abs(data->input_dev,
ABS_MT_PRESSURE, 15);
input_report_abs(data->input_dev,
ABS_MT_TOUCH_MAJOR, tlsc_size);
input_report_key(data->input_dev, BTN_TOUCH, 1);
}
#if !MULTI_PROTOCOL_TYPE_B
input_mt_sync(data->input_dev);
#endif
} else {
#if MULTI_PROTOCOL_TYPE_B
input_mt_slot(data->input_dev, buf[6 * i + 5] >> 4);
input_mt_report_slot_state(data->input_dev,
MT_TOOL_FINGER, false);
#endif
}
}
if (event->touch_point == 0) {
if (y == 1500) {
if (x == 40) {
input_report_key(data->input_dev, KEY_MENU, 0);
}
if (x == 80) {
input_report_key(data->input_dev, KEY_HOMEPAGE,
0);
}
if (x == 120) {
input_report_key(data->input_dev, KEY_BACK, 0);
}
}
tlsc6x_clear_report_data(data);
}
input_sync(data->input_dev);
return 0;
}
static irqreturn_t touch_event_thread_handler(int irq, void *devid)
{
tlsc6x_update_data();
return IRQ_HANDLED;
}
void tlsc6x_tpd_reset_force(void)
{
struct tlsc6x_platform_data *pdata = g_tp_drvdata->platform_data;
TLSC_FUNC_ENTER();
gpio_direction_output(pdata->reset_gpio_number, 1);
usleep_range(10000, 11000);
gpio_set_value(pdata->reset_gpio_number, 0);
msleep(20);
gpio_set_value(pdata->reset_gpio_number, 1);
msleep(30);
}
static void tlsc6x_tpd_reset(void)
{
TLSC_FUNC_ENTER();
if (g_tp_drvdata->needKeepRamCode) {
return;
}
tlsc6x_tpd_reset_force();
}
static unsigned char real_suspend_flag;
#if 1
static int tlsc6x_do_suspend(void)
{
TLSC_FUNC_ENTER();
#ifdef TLSC_ESD_HELPER_EN
hrtimer_cancel(&tpd_esd_kthread_timer);
#endif
#ifdef TLSC_TPD_PROXIMITY
if (tpd_prox_active) {
real_suspend_flag = 0;
return 0;
}
#endif
tlsc6x_irq_disable();
#ifdef TLSC_GESTRUE
tlsc6x_enter_gest_mode(0xffffff);
enable_irq_wake(this_client->irq);
irq_set_irq_type(this_client->irq,
IRQF_TRIGGER_LOW | IRQF_NO_SUSPEND | IRQF_ONESHOT);
tlsc6x_irq_enable();
#else
if (tlsc6x_write_reg(this_client, 0xa5, 0x03) < 0) {
tlsc_err("tlsc6x error::setup suspend fail!\n");
}
#endif
real_suspend_flag = 1;
tlsc6x_clear_report_data(g_tp_drvdata);
return 0;
}
static int tlsc6x_do_resume(void)
{
TLSC_FUNC_ENTER();
#ifdef TLSC_ESD_HELPER_EN
hrtimer_start(&tpd_esd_kthread_timer, ktime_set(3, 0),
HRTIMER_MODE_REL);
#endif
#ifdef TLSC_TPD_PROXIMITY
if (tpd_prox_active && (real_suspend_flag == 0)) {
return 0;
}
#endif
queue_work(g_tp_drvdata->tp_resume_workqueue,
&g_tp_drvdata->resume_work);
return 0;
}
#endif
#if defined(CONFIG_ADF)
static int tlsc6x_adf_suspend(void)
{
TLSC_FUNC_ENTER();
return tlsc6x_do_suspend();
}
static int tlsc6x_adf_resume(void)
{
TLSC_FUNC_ENTER();
return tlsc6x_do_resume();
}
#elif defined(CONFIG_HAS_EARLYSUSPEND)
static void tlsc6x_ts_suspend(struct early_suspend *handler)
{
TLSC_FUNC_ENTER();
tlsc6x_do_suspend();
}
static void tlsc6x_ts_resume(struct early_suspend *handler)
{
TLSC_FUNC_ENTER();
tlsc6x_do_resume();
}
#endif
static void tlsc6x_resume_work(struct work_struct *work)
{
TLSC_FUNC_ENTER();
tlsc6x_tpd_reset();
if (g_tp_drvdata->needKeepRamCode) { /* need wakeup cmd in this mode */
tlsc6x_write_reg(this_client, 0xa5, 0x00);
}
tlsc6x_clear_report_data(g_tp_drvdata);
#ifdef TLSC_GESTRUE
disable_irq_wake(this_client->irq);
irq_set_irq_type(this_client->irq,
IRQF_TRIGGER_FALLING | IRQF_ONESHOT | IRQF_NO_SUSPEND);
#endif
tlsc6x_irq_enable();
real_suspend_flag = 0;
}
#if defined(CONFIG_ADF)
/*
* touchscreen's suspend and resume state should rely on screen state,
* as fb_notifier and early_suspend are all disabled on our platform,
* we can only use adf_event now
*/
static int ts_adf_event_handler(struct notifier_block *nb, unsigned long action,
void *data)
{
struct fb_event *event = data;
int adf_event_data;
// if (action != ADF_EVENT_BLANK) {
if (action != FB_EVENT_BLANK) {
return NOTIFY_DONE;
}
adf_event_data = *(int *)event->data;
tlsc_info("receive adf event with adf_event_data=%d", adf_event_data);
switch (adf_event_data) {
// case DRM_MODE_DPMS_ON:
case FB_BLANK_UNBLANK:
tlsc6x_adf_resume();
break;
// case DRM_MODE_DPMS_OFF:
case FB_BLANK_POWERDOWN:
tlsc6x_adf_suspend();
break;
default:
tlsc_info
("receive adf event with error data, adf_event_data=%d",
adf_event_data);
break;
}
return NOTIFY_OK;
}
#endif
static int tlsc6x_hw_init(struct tlsc6x_data *drvdata)
{
struct regulator *reg_vdd;
struct i2c_client *client = drvdata->client;
struct tlsc6x_platform_data *pdata = drvdata->platform_data;
// int ret = 0;
TLSC_FUNC_ENTER();
if (gpio_request(pdata->irq_gpio_number, NULL) < 0) {
goto OUT;
}
if (gpio_request(pdata->reset_gpio_number, NULL) < 0) {
goto OUT;
}
gpio_direction_output(pdata->reset_gpio_number, 1);
gpio_direction_input(pdata->irq_gpio_number);
drvdata->reg_vdd = NULL;
if (pdata->vdd_name != NULL) {
// reg_vdd = regulator_get(&client->dev, pdata->vdd_name);
reg_vdd = regulator_get(&client->dev, "vdd");
if (!WARN(IS_ERR(reg_vdd),
"tlsc6x_hw_init regulator: failed to get %s.\n",
pdata->vdd_name)) {
regulator_set_voltage(reg_vdd, 2800000, 2800000);
if (regulator_enable(reg_vdd)) {
tlsc_info
("tlsc6x_hw_init:regulator_enable return none zero\n");
}
if (regulator_is_enabled(reg_vdd) == 0) {
tlsc_err
("tlsc6x_hw_init:regulator_enable fail\n");
}
drvdata->reg_vdd = reg_vdd;
}
}
msleep(100);
tlsc6x_tpd_reset();
return 0;
OUT: return -EPERM;
}
#ifdef CONFIG_OF
static struct tlsc6x_platform_data *tlsc6x_parse_dt(struct device *dev)
{
int ret;
struct tlsc6x_platform_data *pdata;
struct device_node *np = dev->of_node;
TLSC_FUNC_ENTER();
pdata = kzalloc(sizeof(*pdata), GFP_KERNEL);
if (!pdata) {
dev_err(dev, "Could not allocate struct tlsc6x_platform_data");
return NULL;
}
pdata->reset_gpio_number = of_get_named_gpio_flags(np, "reset-gpio",
0,
&pdata->
reset_gpio_flags);
if (pdata->reset_gpio_number < 0) {
dev_err(dev, "fail to get reset_gpio_number\n");
goto fail;
}
pdata->irq_gpio_number = of_get_named_gpio_flags(np, "irq-gpio",
0,
&pdata->
irq_gpio_flags);
if (pdata->irq_gpio_number < 0) {
dev_err(dev, "fail to get irq_gpio_number\n");
goto fail;
}
ret = of_property_read_string(np, "vdd-supply", &pdata->vdd_name);
if (ret) {
dev_err(dev, "fail to get vdd_name\n");
/* goto fail; */
}
#ifdef TOUCH_VIRTUAL_KEYS
ret =
of_property_read_u32_array(np, "virtualkeys", pdata->virtualkeys,
12);
if (ret) {
dev_err(dev, "fail to get virtualkeys\n");
/* goto fail; */
}
#endif
ret = of_property_read_u32(np, "TP_MAX_X", &pdata->x_res_max);
if (ret) {
dev_err(dev, "fail to get TP_MAX_X\n");
goto fail;
}
ret = of_property_read_u32(np, "TP_MAX_Y", &pdata->y_res_max);
if (ret) {
dev_err(dev, "fail to get TP_MAX_Y\n");
goto fail;
}
ret = of_property_read_u32(np, "revert_x_flag", &pdata->revert_x_flag);
if (ret) {
dev_err(dev, "fail to get revert_x_flag\n");
goto fail;
}
ret = of_property_read_u32(np, "revert_y_flag", &pdata->revert_y_flag);
if (ret) {
dev_err(dev, "fail to get revert_y_flag\n");
goto fail;
}
ret =
of_property_read_u32(np, "exchange_x_y_flag",
&pdata->exchange_x_y_flag);
if (ret) {
dev_err(dev, "fail to get exchange_x_y_flag\n");
goto fail;
}
return pdata;
fail:
kfree(pdata);
return NULL;
}
#endif
/* file interface - write*/
int tlsc6x_fif_write(char *fname, u8 *pdata, u16 len)
{
int ret = 0;
loff_t pos = 0;
static struct file *pfile;
mm_segment_t old_fs = KERNEL_DS;
pfile = filp_open(fname, O_TRUNC | O_CREAT | O_RDWR, 0644);
if (IS_ERR(pfile)) {
ret = -EFAULT;
tlsc_err("tlsc6x tlsc6x_fif_write:open error!\n");
} else {
tlsc_info("tlsc6x tlsc6x_fif_write:start write!\n");
old_fs = get_fs();
set_fs(KERNEL_DS);
ret =
(int)vfs_write(pfile, (__force const char __user *)pdata,
(size_t) len, &pos);
vfs_fsync(pfile, 0);
filp_close(pfile, NULL);
set_fs(old_fs);
}
return ret;
}
#if (defined TPD_AUTO_UPGRADE_PATH) || (defined TLSC_APK_DEBUG)
extern int tlsx6x_update_running_cfg(u16 *ptcfg);
extern int tlsx6x_update_burn_cfg(u16 *ptcfg);
extern int tlsc6x_load_ext_binlib(u8 *pcode, u16 len);
extern int tlsc6x_update_f_combboot(u8 *pdata, u16 len);
int auto_upd_busy;
/* 0:success */
/* 1: no file OR open fail */
/* 2: wrong file size OR read error */
/* -1:op-fial */
int tlsc6x_proc_cfg_update(u8 *dir, int behave)
{
int ret = 1;
u8 *pbt_buf = NULL;
u32 fileSize;
mm_segment_t old_fs;
static struct file *file;
TLSC_FUNC_ENTER();
tlsc_info("tlsc6x proc-file:%s\n", dir);
file = filp_open(dir, O_RDONLY, 0);
if (IS_ERR(file)) {
tlsc_err("tlsc6x proc-file:open error!\n");
} else {
ret = 2;
old_fs = get_fs();
set_fs(KERNEL_DS);
fileSize = file->f_op->llseek(file, 0, SEEK_END);
tlsc_info("tlsc6x proc-file, size:%d\n", fileSize);
pbt_buf = kmalloc(fileSize, GFP_KERNEL);
file->f_op->llseek(file, 0, SEEK_SET);
if (fileSize
== file->f_op->read(file, (char *)pbt_buf, fileSize,
&file->f_pos)) {
tlsc_info("tlsc6x proc-file, read ok1!\n");
ret = 3;
}
if (ret == 3) {
auto_upd_busy = 1;
tlsc6x_irq_disable();
msleep(1000);
wake_lock_timeout(&tlsc6x_wakelock,
msecs_to_jiffies(2000));
if (behave == 0) {
if (fileSize == 204) {
ret = tlsx6x_update_running_cfg((u16 *)
pbt_buf);
} else if (fileSize > 0x400) {
tlsc6x_load_ext_binlib((u8 *) pbt_buf,
(u16) fileSize);
}
} else if (behave == 1) {
if (fileSize == 204) {
ret = tlsx6x_update_burn_cfg((u16 *)
pbt_buf);
} else if (fileSize > 0x400) {
ret = tlsc6x_update_f_combboot((u8 *)
pbt_buf,
(u16)
fileSize);
}
tlsc6x_tpd_reset();
}
tlsc6x_irq_enable();
auto_upd_busy = 0;
}
filp_close(file, NULL);
set_fs(old_fs);
kfree(pbt_buf);
}
return ret;
}
#endif
#ifdef TLSC_APK_DEBUG
unsigned char proc_out_len;
unsigned char proc_out_buf[256];
unsigned char debug_type;
unsigned char iic_reg[2];
unsigned char sync_flag_addr[3];
unsigned char sync_buf_addr[2];
unsigned char reg_len;
static struct proc_dir_entry *tlsc6x_proc_entry;
static int debug_read(char *writebuf, int writelen, char *readbuf, int readlen)
{
int ret = 0;
mutex_lock(&i2c_rw_access);
tlsc6x_set_dd_mode_sub();
ret = tlsc6x_i2c_read_sub(this_client, writebuf, writelen, readbuf,
readlen);
tlsc6x_set_nor_mode_sub();
mutex_unlock(&i2c_rw_access);
if (ret > 0) {
ret = readlen;
}
return ret;
}
static int debug_write(char *writebuf, int writelen)
{
int ret = 0;
mutex_lock(&i2c_rw_access);
tlsc6x_set_dd_mode_sub();
ret = tlsc6x_i2c_write_sub(this_client, writebuf, writelen);
tlsc6x_set_nor_mode_sub();
mutex_unlock(&i2c_rw_access);
if (ret > 0) {
ret = writelen;
}
return ret;
}
static int debug_read_sync(char *writebuf, int writelen, char *readbuf,
int readlen)
{
int ret = 0;
int retryTime;
mutex_lock(&i2c_rw_access);
tlsc6x_set_dd_mode_sub();
sync_flag_addr[2] = 1;
ret = tlsc6x_i2c_write_sub(this_client, sync_flag_addr, 3);
retryTime = 100;
do {
ret = tlsc6x_i2c_read_sub(this_client, sync_flag_addr, 2,
&sync_flag_addr[2], 1);
if (ret < 0) {
mutex_unlock(&i2c_rw_access);
return ret;
}
retryTime--;
} while (retryTime > 0 && sync_flag_addr[2] == 1);
if (retryTime == 0 && sync_flag_addr[2] == 1) {
mutex_unlock(&i2c_rw_access);
return -EFAULT;
}
if (ret >= 0) {
/* read data */
ret =
tlsc6x_i2c_read_sub(this_client, sync_buf_addr, 2, readbuf,
readlen);
}
tlsc6x_set_nor_mode_sub();
mutex_unlock(&i2c_rw_access);
if (ret > 0) {
ret = readlen;
}
return ret;
}
static int tlsc6x_rawdata_test_3535allch(u8 *buf, int len)
{
int ret;
int retryTime;
u8 writebuf[4];
buf[len] = '\0';
ret = 0;
tlsc6x_irq_disable();
g_tp_drvdata->esdHelperFreeze = 1;
tlsc6x_tpd_reset();
if (tlsc6x_proc_cfg_update(&buf[2], 0)) {
ret = -EIO;
}
msleep(30);
mutex_lock(&i2c_rw_access);
//write addr
writebuf[0] = 0x9F;
writebuf[1] = 0x20;
writebuf[2] = 48;
writebuf[3] = 0xFF;
ret = tlsc6x_i2c_write_sub(this_client, writebuf, 4);
writebuf[0] = 0x9F;
writebuf[1] = 0x24;
writebuf[2] = 1;
ret = tlsc6x_i2c_write_sub(this_client, writebuf, 3);
retryTime = 100;
do {
ret =
tlsc6x_i2c_read_sub(this_client, writebuf, 2, &writebuf[2],
1);
if (ret < 0) {
break;
}
retryTime--;
msleep(30);
} while (retryTime > 0 && writebuf[2] == 1);
if (ret >= 0) {
writebuf[0] = 0x9F;
writebuf[1] = 0x26;
ret =
tlsc6x_i2c_read_sub(this_client, writebuf, 2, proc_out_buf,
96);
if (ret >= 0) {
proc_out_len = 96;
}
}
mutex_unlock(&i2c_rw_access);
tlsc6x_tpd_reset();
g_tp_drvdata->esdHelperFreeze = 0;
tlsc6x_irq_enable();
return ret;
}
static ssize_t tlsc6x_proc_write(struct file *filp, const char __user *buff,
size_t len, loff_t *data)
{
int ret;
int buflen = len;
unsigned char local_buf[256];
if (buflen > 255) {
return -EFAULT;
}
if (copy_from_user(local_buf, buff, buflen)) {
tlsc_err("%s:copy from user error\n", __func__);
return -EFAULT;
}
ret = 0;
debug_type = local_buf[0];
/* format:cmd+para+data0+data1+data2... */
switch (local_buf[0]) {
case 0: /* cfg version */
proc_out_len = 4;
proc_out_buf[0] = g_tlsc6x_cfg_ver;
proc_out_buf[1] = g_tlsc6x_cfg_ver >> 8;
proc_out_buf[2] = g_tlsc6x_cfg_ver >> 16;
proc_out_buf[3] = g_tlsc6x_cfg_ver >> 24;
break;
case 1:
local_buf[buflen] = '\0';
if (tlsc6x_proc_cfg_update(&local_buf[2], 0) < 0) {
len = -EIO;
}
break;
case 2:
local_buf[buflen] = '\0';
if (tlsc6x_proc_cfg_update(&local_buf[2], 1) < 0) {
len = -EIO;
}
break;
case 3:
ret = debug_write(&local_buf[1], len - 1);
break;
case 4: /* read */
reg_len = local_buf[1];
iic_reg[0] = local_buf[2];
iic_reg[1] = local_buf[3];
break;
case 5: /* read with sync */
ret = debug_write(&local_buf[1], 4); /* write size */
if (ret >= 0) {
ret = debug_write(&local_buf[5], 4); /* write addr */
}
sync_flag_addr[0] = local_buf[9];
sync_flag_addr[1] = local_buf[10];
sync_buf_addr[0] = local_buf[11];
sync_buf_addr[1] = local_buf[12];
break;
case 8: // Force reset ic
tlsc6x_tpd_reset_force();
break;
case 9: // raw test
ret = tlsc6x_rawdata_test_3535allch(local_buf, buflen);
break;
case 14: /* e, esd control */
g_tp_drvdata->esdHelperFreeze = (int)local_buf[1];
break;
default:
break;
}
if (ret < 0) {
len = ret;
}
return len;
}
static ssize_t tlsc6x_proc_read(struct file *filp, char __user *page,
size_t len, loff_t *pos)
{
int ret = 0;
if (*pos != 0) {
return 0;
}
switch (debug_type) {
case 0: /* version information */
proc_out_len = 4;
proc_out_buf[0] = g_tlsc6x_cfg_ver;
proc_out_buf[1] = g_tlsc6x_cfg_ver >> 8;
proc_out_buf[2] = g_tlsc6x_cfg_ver >> 16;
proc_out_buf[3] = g_tlsc6x_cfg_ver >> 24;
if (copy_to_user(page, proc_out_buf, proc_out_len)) {
ret = -EFAULT;
} else {
ret = proc_out_len;
}
break;
case 1:
break;
case 2:
break;
case 3:
break;
case 4:
len = debug_read(iic_reg, reg_len, proc_out_buf, len);
if (len > 0) {
if (copy_to_user(page, proc_out_buf, len)) {
ret = -EFAULT;
} else {
ret = len;
}
} else {
ret = len;
}
break;
case 5:
len = debug_read_sync(iic_reg, reg_len, proc_out_buf, len);
if (len > 0) {
if (copy_to_user(page, proc_out_buf, len)) {
ret = -EFAULT;
} else {
ret = len;
}
} else {
ret = len;
}
break;
case 9:
if (proc_out_buf > 0) {
if (copy_to_user(page, proc_out_buf, len)) {
ret = -EFAULT;
} else {
ret = proc_out_len;
}
}
break;
default:
break;
}
if (ret > 0) {
*pos += ret;
}
return ret;
}
static struct file_operations tlsc6x_proc_ops = {.owner = THIS_MODULE, .read =
tlsc6x_proc_read, .write = tlsc6x_proc_write,
};
void tlsc6x_release_apk_debug_channel(void)
{
if (tlsc6x_proc_entry) {
remove_proc_entry("tlsc6x-debug", NULL);
}
}
int tlsc6x_create_apk_debug_channel(struct i2c_client *client)
{
tlsc6x_proc_entry =
proc_create("tlsc6x-debug", 0x0644, NULL, &tlsc6x_proc_ops);
if (tlsc6x_proc_entry == NULL) {
dev_err(&client->dev, "Couldn't create proc entry!\n");
return -ENOMEM;
}
dev_info(&client->dev, "Create proc entry success!\n");
return 0;
}
#endif
extern unsigned int g_mccode;
static ssize_t show_tlsc_version(struct device *dev,
struct device_attribute *attr, char *buf)
{
u8 reg[2];
u8 readBuf[4];
char *ptr = buf;
u8 vender_id;
mutex_lock(&i2c_rw_access);
tlsc6x_set_dd_mode_sub();
reg[0] = 0x80;
reg[1] = 0x04;
tlsc6x_i2c_read_sub(this_client, reg, 2, readBuf, 2);
ptr += sprintf(ptr, "The boot version is %04X.\n",
(readBuf[0] + (readBuf[1] << 8)));
if (g_mccode == 0) {
reg[0] = 0xD6;
reg[1] = 0xE0;
} else {
reg[0] = 0x9E;
reg[1] = 0x00;
}
tlsc6x_i2c_read_sub(this_client, reg, 2, readBuf, 4);
ptr += sprintf(ptr, "The config version is %d.\n", readBuf[3] >> 2);
vender_id = readBuf[1] >> 1;
ptr +=
sprintf(ptr, "The vender id is %d, the vender name is ", vender_id);
switch (vender_id) {
case 1:
ptr += sprintf(ptr, "xufang");
break;
case 2:
ptr += sprintf(ptr, "xuri");
break;
case 3:
ptr += sprintf(ptr, "yuye");
break;
case 4:
ptr += sprintf(ptr, "tianyi");
break;
case 5:
ptr += sprintf(ptr, "minglang");
break;
case 6:
ptr += sprintf(ptr, "duoxinda");
break;
case 7:
ptr += sprintf(ptr, "zhenhua");
break;
case 8:
ptr += sprintf(ptr, "jitegao");
break;
case 9:
ptr += sprintf(ptr, "guangjishengtai");
break;
case 10:
ptr += sprintf(ptr, "shengguang");
break;
case 11:
ptr += sprintf(ptr, "xintiantong");
break;
case 12:
ptr += sprintf(ptr, "xinyou");
break;
case 13:
ptr += sprintf(ptr, "yanqi");
break;
case 14:
ptr += sprintf(ptr, "zhongcheng");
break;
case 15:
ptr += sprintf(ptr, "xinmaoxin");
break;
case 16:
ptr += sprintf(ptr, "zhenzhixin");
break;
case 17:
ptr += sprintf(ptr, "helitai");
break;
case 18:
ptr += sprintf(ptr, "huaxin");
break;
case 19:
ptr += sprintf(ptr, "lihaojie");
break;
case 20:
ptr += sprintf(ptr, "jiandong");
break;
case 21:
ptr += sprintf(ptr, "xinpengda");
break;
case 22:
ptr += sprintf(ptr, "jiake");
break;
case 23:
ptr += sprintf(ptr, "yijian");
break;
case 24:
ptr += sprintf(ptr, "yixing");
break;
case 25:
ptr += sprintf(ptr, "zhongguangdian");
break;
case 26:
ptr += sprintf(ptr, "hongzhan");
break;
case 27:
ptr += sprintf(ptr, "huaxingda");
break;
case 28:
ptr += sprintf(ptr, "dongjianhuanyu");
break;
case 29:
ptr += sprintf(ptr, "dawosi");
break;
case 30:
ptr += sprintf(ptr, "dacheng");
break;
case 31:
ptr += sprintf(ptr, "mingwangda");
break;
case 32:
ptr += sprintf(ptr, "huangze");
break;
case 33:
ptr += sprintf(ptr, "jinxinxiang");
break;
case 34:
ptr += sprintf(ptr, "gaoge");
break;
case 35:
ptr += sprintf(ptr, "zhihui");
break;
case 36:
ptr += sprintf(ptr, "miaochu");
break;
case 37:
ptr += sprintf(ptr, "qicai");
break;
case 38:
ptr += sprintf(ptr, "zhenghai");
break;
case 39:
ptr += sprintf(ptr, "hongfazhan");
break;
case 40:
ptr += sprintf(ptr, "lianchuang");
break;
case 41:
ptr += sprintf(ptr, "saihua");
break;
case 42:
ptr += sprintf(ptr, "keleli");
break;
case 43:
ptr += sprintf(ptr, "weiyi");
break;
case 44:
ptr += sprintf(ptr, "futuo");
break;
default:
ptr += sprintf(ptr, "unknown");
break;
}
ptr += sprintf(ptr, ".\n");
tlsc6x_set_nor_mode_sub();
mutex_unlock(&i2c_rw_access);
return (ptr - buf);
}
static ssize_t store_tlsc_version(struct device *dev,
struct device_attribute *attr,
const char *buf, size_t count)
{
return -EPERM;
}
static ssize_t show_tlsc_info(struct device *dev, struct device_attribute *attr,
char *buf)
{
char *ptr = buf;
ptr += sprintf(ptr, "Max finger number is %0d.\n", TS_MAX_FINGER);
ptr += sprintf(ptr, "Int irq is %d.\n", this_client->irq);
ptr +=
sprintf(ptr, "I2c address is 0x%02X(0x%02X).\n", this_client->addr,
(this_client->addr) << 1);
return (ptr - buf);
}
static ssize_t store_tlsc_info(struct device *dev,
struct device_attribute *attr, const char *buf,
size_t count)
{
return -EPERM;
}
static ssize_t show_tlsc_ps(struct device *dev, struct device_attribute *attr,
char *buf)
{
char *ptr = buf;
#ifdef TLSC_TPD_PROXIMITY
ptr += sprintf(ptr, "%d\n", tpd_prox_active);
#else
ptr += sprintf(ptr, "No proximity function.\n");
#endif
return (ptr - buf);
}
static ssize_t store_tlsc_ps(struct device *dev, struct device_attribute *attr,
const char *buf, size_t count)
{
#ifdef TLSC_TPD_PROXIMITY
if (buf[0] == '0') {
tlsc6x_prox_ctrl(0);
//ps off
} else if (buf[0] == '1') {
//ps on
tlsc6x_prox_ctrl(1);
}
#endif
return count;
}
static ssize_t show_tlsc_gesture(struct device *dev,
struct device_attribute *attr, char *buf)
{
char *ptr = buf;
#ifdef TLSC_GESTRUE
ptr += sprintf(ptr, "%d\n", gesture_enable);
#else
ptr += sprintf(ptr, "No gesture function.\n");
#endif
return (ptr - buf);
}
static ssize_t store_tlsc_gesture(struct device *dev,
struct device_attribute *attr,
const char *buf, size_t count)
{
#ifdef TLSC_GESTRUE
if (buf[0] == '0') {
//gesture off
gesture_enable = 0;
} else if (buf[0] == '1') {
//gesture on
gesture_enable = 1;
}
#endif
return count;
}
//static int debugResult=0;
u8 readFlashbuf[204];
static ssize_t show_tlsc_debug_flash(struct device *dev,
struct device_attribute *attr, char *buf)
{
char *ptr = buf;
int i;
for (i = 0; i < 204; i++) {
ptr += sprintf(ptr, "%d,", readFlashbuf[i]);
}
ptr += sprintf(ptr, "\n");
return (ptr - buf);
}
extern int tlsc6x_download_ramcode(u8 *pcode, u16 len);
extern int tlsc6x_write_burn_space(u8 *psrc, u16 adr, u16 len);
extern int tlsc6x_read_burn_space(u8 *pdes, u16 adr, u16 len);
extern int tlsc6x_set_nor_mode(void);
extern unsigned char fw_fcode_burn[2024];
int writeFlash(u8 *buf, u16 addr, int len)
{
//int ret=0;
auto_upd_busy = 1;
tlsc6x_irq_disable();
if (tlsc6x_download_ramcode(fw_fcode_burn, sizeof(fw_fcode_burn))) {
tlsc_err("Tlsc6x:write flash error:ram-code error!\n");
return -EPERM;
}
if (tlsc6x_write_burn_space((unsigned char *)buf, addr, len)) {
tlsc_err("Tlsc6x:write flash fail!\n");
return -EPERM;
}
tlsc6x_set_nor_mode();
tlsc6x_tpd_reset();
auto_upd_busy = 0;
tlsc6x_irq_enable();
return 0;
}
int readFlash(u16 addr, int len)
{
//int ret=0;
auto_upd_busy = 1;
tlsc6x_irq_disable();
if (tlsc6x_download_ramcode(fw_fcode_burn, sizeof(fw_fcode_burn))) {
tlsc_err("Tlsc6x:write flash error:ram-code error!\n");
return -EPERM;
}
if (tlsc6x_read_burn_space((unsigned char *)readFlashbuf, addr, len)) {
tlsc_err("Tlsc6x:write flash fail!\n");
return -EPERM;
}
tlsc6x_set_nor_mode();
tlsc6x_tpd_reset();
auto_upd_busy = 0;
tlsc6x_irq_enable();
return 0;
}
static ssize_t store_tlsc_debug_flash(struct device *dev,
struct device_attribute *attr,
const char *buf, size_t count)
{
u8 wBuf[16] = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 };
if (buf[0] == '0') {
//gesture off
} else if (buf[0] == '1') {
//gesture on
tlsc_info("not support!\n");
} else if (buf[0] == '2') {
//gesture on
writeFlash(wBuf, 0, 16);
} else if (buf[0] == '3') {
//gesture on
writeFlash(wBuf, 0x8000, 16);
} else if (buf[0] == '4') {
//gesture on
readFlash(0xF000, 204);
} else if (buf[0] == '5') {
//gesture on
readFlash(0, 204);
} else if (buf[0] == '6') {
//gesture on
readFlash(0x8000, 204);
}
return count;
}
static DEVICE_ATTR(tlsc_version, 0664, show_tlsc_version, store_tlsc_version);
static DEVICE_ATTR(tlsc_tp_info, 0664, show_tlsc_info, store_tlsc_info);
static DEVICE_ATTR(tlsc_ps_ctl, 0664, show_tlsc_ps, store_tlsc_ps);
static DEVICE_ATTR(tlsc_gs_ctl, 0664, show_tlsc_gesture, store_tlsc_gesture);
static DEVICE_ATTR(tlsc_flash_ctl, 0664, show_tlsc_debug_flash,
store_tlsc_debug_flash);
static struct attribute *tlsc_attrs[] = { &dev_attr_tlsc_version.attr,
&dev_attr_tlsc_tp_info.attr, &dev_attr_tlsc_ps_ctl.attr,
&dev_attr_tlsc_gs_ctl.attr, &dev_attr_tlsc_flash_ctl.attr,
NULL, // Can not delete this line!!! The phone will reset.
};
static struct attribute_group tlsc_attr_group = {.attrs = tlsc_attrs, };
#ifdef TLSC_ESD_HELPER_EN
unsigned char g_tlsc6x_esdtar;
static int tlsc6x_power_on(struct regulator *p_reg)
{
int err = 0;
int retry = 0;
TLSC_FUNC_ENTER();
if (p_reg == NULL) {
return -EPERM;
}
while (retry++ < 30) {
err = regulator_enable(p_reg);
if (regulator_is_enabled(p_reg)) {
break;
}
}
return err;
}
static int tlsc6x_power_off(struct regulator *p_reg)
{
int err = 0;
int retry = 0;
TLSC_FUNC_ENTER();
if (p_reg == NULL) {
return -EPERM;
}
while (retry++ < 30) {
err = regulator_disable(p_reg);
if (regulator_is_enabled(p_reg) == 0) {
break;
}
}
return err;
}
static int esd_check_work(void)
{
int ret = -1;
u8 test_val = 0;
TLSC_FUNC_ENTER();
if (g_tp_drvdata->esdHelperFreeze) {
return 1;
}
ret = tlsc6x_read_reg(this_client, 0xa3, &test_val);
if (ret < 0) { //maybe confused by some noise,so retry is make sense.
msleep(10);
tlsc6x_read_reg(this_client, 0xa3, &test_val);
ret = tlsc6x_read_reg(this_client, 0xa3, &test_val);
}
if ((ret >= 0) && (g_tlsc6x_esdtar != 0)) {
if (g_tlsc6x_esdtar != test_val) {
ret = -1;
}
}
if (ret < 0) {
/* re-power-on */
tlsc6x_power_off(g_tp_drvdata->reg_vdd);
msleep(20);
tlsc6x_power_on(g_tp_drvdata->reg_vdd);
tlsc6x_tpd_reset();
if (tlsc6x_read_reg(this_client, 0xa3, &g_tlsc6x_esdtar) < 0) {
g_tlsc6x_esdtar = 0x00;
}
}
return ret;
}
static int esd_checker_handler(void *unused)
{
ktime_t ktime;
if (tlsc6x_read_reg(this_client, 0xa3, &g_tlsc6x_esdtar) < 0) {
g_tlsc6x_esdtar = 0x00;
}
do {
wait_event_interruptible(tpd_esd_waiter, tpd_esd_flag != 0);
tpd_esd_flag = 0;
ktime = ktime_set(4, 0);
hrtimer_start(&tpd_esd_kthread_timer, ktime, HRTIMER_MODE_REL);
if (g_tp_drvdata->esdHelperFreeze) {
continue;
}
#if (defined TPD_AUTO_UPGRADE_PATH) || (defined TLSC_APK_DEBUG)
if (auto_upd_busy) {
continue;
}
#endif
esd_check_work();
} while (!kthread_should_stop());
return 0;
}
enum hrtimer_restart tpd_esd_kthread_hrtimer_func(struct hrtimer *timer)
{
tpd_esd_flag = 1;
wake_up_interruptible(&tpd_esd_waiter);
return HRTIMER_NORESTART;
}
#endif
#ifdef TLSC_TPD_PROXIMITY
static int tlsc6x_prox_ctrl(int enable)
{
TLSC_FUNC_ENTER();
if (enable == 1) {
tpd_prox_active = 1;
tlsc6x_write_reg(this_client, 0xb0, 0x01);
} else if (enable == 0) {
tpd_prox_active = 0;
tlsc6x_write_reg(this_client, 0xb0, 0x00);
}
tpd_prox_old_state = 0xe0; /* default is far away */
return 1;
}
static ssize_t tlsc6x_prox_enable_show(struct device *dev,
struct device_attribute *attr, char *buf)
{
return snprintf(buf, PAGE_SIZE, "ps enable %d\n", tpd_prox_active);
}
static ssize_t tlsc6x_prox_enable_store(struct device *dev,
struct device_attribute *attr,
const char *buf, size_t count)
{
unsigned int enable;
int ret = 0;
TLSC_FUNC_ENTER();
ret = kstrtouint(buf, 0, &enable);
if (ret)
return -EINVAL;
enable = (enable > 0) ? 1 : 0;
tlsc6x_prox_ctrl(enable);
return count;
}
static DEVICE_ATTR(proximity, 0664, tlsc6x_prox_enable_show,
tlsc6x_prox_enable_store);
/* default cmd interface(refer to sensor HAL):"/sys/class/sprd-tpd/device/proximity" */
static void tlsc6x_prox_cmd_path_init(void)
{
sprd_tpd_class = class_create(THIS_MODULE, "sprd-tpd");
if (IS_ERR(sprd_tpd_class)) {
dev_err(&this_client->dev,
"tlsc6x error::create sprd-tpd fail\n");
} else {
sprd_ps_cmd_dev =
device_create(sprd_tpd_class, NULL, 0, NULL, "device");
if (IS_ERR(sprd_ps_cmd_dev)) {
dev_err(&this_client->dev,
"tlsc6x error::create ges&ps cmd-io fail\n");
} else {
/* sys/class/sprd-tpd/device/proximity */
if (device_create_file
(sprd_ps_cmd_dev, &dev_attr_proximity) < 0) {
dev_err(&this_client->dev,
" tlsc6x error::create device file fail(%s)!\n",
dev_attr_proximity.attr.name);
}
}
}
}
#endif
static int tlsc6x_request_irq_work(void)
{
int ret = 0;
this_client->irq =
gpio_to_irq(g_tp_drvdata->platform_data->irq_gpio_number);
tlsc_info("The irq node num is %d", this_client->irq);
ret = request_threaded_irq(this_client->irq,
NULL, touch_event_thread_handler,
IRQF_TRIGGER_FALLING | IRQF_ONESHOT |
IRQF_NO_SUSPEND, "tlsc6x_tpd_irq", NULL);
if (ret < 0) {
tlsc_err("Request irq thread error!");
return ret;
}
return ret;
}
ssize_t GTprocTpInfoRead(struct file *pFile, char __user *pBuffer,
size_t nCount, loff_t *pPos)
{
u8 nLength;
nLength = sprintf(pBuffer, "TLSC%s\n", TS_NAME);
*pPos += nLength;
return nLength;
}
ssize_t GTprocTpInfoWrite(struct file *pFile, const char __user *pBuffer,
size_t nCount, loff_t *pPos)
{
return nCount;
}
static const struct file_operations GTProcTpInfo = {.read = GTprocTpInfoRead,
.write = GTprocTpInfoWrite,
};
static struct proc_dir_entry *GTProcTpInfoEntry;
int i2c_correspond(void)
{
u8 test_val = 0;
int ret = tlsc6x_read_reg(this_client, 0xa3, &test_val);
if (ret < 0)
return 1;
else
return 0;
}
static int tlsc6x_probe(struct i2c_client *client,
const struct i2c_device_id *id)
{
int ret = -1;
int err = 0;
int reset_count;
struct input_dev *input_dev;
struct tlsc6x_platform_data *pdata = NULL;
TLSC_FUNC_ENTER();
if (!i2c_check_functionality(client->adapter, I2C_FUNC_I2C)) {
err = -ENODEV;
goto exit_alloc_platform_data_failed;
}
#ifdef CONFIG_OF /* NOTE:THIS IS MUST!!! */
if (client->dev.of_node) {
pdata = tlsc6x_parse_dt(&client->dev);
if (pdata) {
client->dev.platform_data = pdata;
}
}
#endif
if (pdata == NULL) {
err = -ENOMEM;
tlsc_err("%s: no platform data!!!\n", __func__);
goto exit_alloc_platform_data_failed;
}
g_tp_drvdata = kzalloc(sizeof(*g_tp_drvdata), GFP_KERNEL); /* auto clear */
if (!g_tp_drvdata) {
err = -ENOMEM;
goto exit_alloc_data_failed;
}
this_client = client;
g_tp_drvdata->client = client;
g_tp_drvdata->platform_data = pdata;
err = tlsc6x_hw_init(g_tp_drvdata);
if (err < 0) {
goto exit_gpio_request_failed;
}
i2c_set_clientdata(client, g_tp_drvdata);
reset_count = 0;
g_is_telink_comp = 0;
while (++reset_count <= 3) {
tlsc6x_tpd_reset();
g_is_telink_comp = tlsc6x_tp_dect(client);
if (g_is_telink_comp) {
break;
}
}
g_tp_drvdata->needKeepRamCode = g_needKeepRamCode;
if (g_is_telink_comp) {
tlsc6x_tpd_reset();
} else {
tlsc_err("tlsc6x:%s, no tlsc6x!\n", __func__);
err = -ENODEV;
goto exit_chip_check_failed;
}
input_dev = input_allocate_device();
if (!input_dev) {
err = -ENOMEM;
dev_err(&client->dev,
"tlsc6x error::failed to allocate input device\n");
goto exit_input_dev_alloc_failed;
}
g_tp_drvdata->input_dev = input_dev;
__set_bit(ABS_MT_TOUCH_MAJOR, input_dev->absbit);
__set_bit(ABS_MT_POSITION_X, input_dev->absbit);
__set_bit(ABS_MT_POSITION_Y, input_dev->absbit);
__set_bit(ABS_MT_WIDTH_MAJOR, input_dev->absbit);
__set_bit(KEY_MENU, input_dev->keybit);
__set_bit(KEY_BACK, input_dev->keybit);
__set_bit(KEY_HOMEPAGE, input_dev->keybit);
__set_bit(BTN_TOUCH, input_dev->keybit);
__set_bit(INPUT_PROP_DIRECT, input_dev->propbit);
#if MULTI_PROTOCOL_TYPE_B
input_mt_init_slots(input_dev, TS_MAX_FINGER, 0);
#else
input_set_abs_params(input_dev, ABS_MT_TRACKING_ID, 0, 255, 0, 0);
#endif
#ifndef CONFIG_OF
if ((g_tlsc6x_cfg_ver & 0xffffff) == 0x0e3610) {
pdata->x_res_max = 480;
pdata->y_res_max = 854;
} else if ((g_tlsc6x_cfg_ver & 0xffffff) == 0x0e3614) {
pdata->x_res_max = 720;
pdata->y_res_max = 1280;
} else {
pdata->x_res_max = 720;
pdata->y_res_max = 1280;
}
#endif
input_set_abs_params(input_dev, ABS_MT_POSITION_X, 0, pdata->x_res_max,
0, 0);
input_set_abs_params(input_dev, ABS_MT_POSITION_Y, 0, pdata->y_res_max,
0, 0);
input_set_abs_params(input_dev, ABS_MT_TOUCH_MAJOR, 0, 15, 0, 0);
input_set_abs_params(input_dev, ABS_MT_WIDTH_MAJOR, 0, 15, 0, 0);
input_set_abs_params(input_dev, ABS_MT_PRESSURE, 0, 127, 0, 0);
input_set_abs_params(input_dev, ABS_DISTANCE, 0, 1, 0, 0); /* give this capability aways */
set_bit(EV_ABS, input_dev->evbit);
set_bit(EV_KEY, input_dev->evbit);
input_dev->name = "tlsc6x_touch";
err = input_register_device(input_dev);
if (err) {
dev_err(&client->dev,
"tlsc6x error::failed to register input device: %s\n",
dev_name(&client->dev));
goto exit_input_register_device_failed;
}
#ifdef TLSC_TPD_PROXIMITY
tlsc6x_prox_cmd_path_init();
g_tp_drvdata->ps_input_dev = input_allocate_device();
if (!g_tp_drvdata->ps_input_dev) {
err = -ENOMEM;
dev_err(&client->dev,
"tlsc6x error::failed to allocate ps-input device\n");
goto exit_input_register_device_failed;
}
g_tp_drvdata->ps_input_dev->name = "proximity_tp";
set_bit(EV_ABS, g_tp_drvdata->ps_input_dev->evbit);
input_set_capability(g_tp_drvdata->ps_input_dev, EV_ABS, ABS_DISTANCE);
input_set_abs_params(g_tp_drvdata->ps_input_dev, ABS_DISTANCE, 0, 1, 0,
0);
err = input_register_device(g_tp_drvdata->ps_input_dev);
if (err) {
dev_err(&client->dev,
"tlsc6x error::failed to register ps-input device: %s\n",
dev_name(&client->dev));
goto exit_input_register_device_failed;
}
#endif
#ifdef TOUCH_VIRTUAL_KEYS
tlsc6x_virtual_keys_init();
#endif
INIT_WORK(&g_tp_drvdata->resume_work, tlsc6x_resume_work);
g_tp_drvdata->tp_resume_workqueue =
create_singlethread_workqueue("tlsc6x_resume_work");
if (!g_tp_drvdata->tp_resume_workqueue) {
err = -ESRCH;
goto exit_input_register_device_failed;
}
wake_lock_init(&tlsc6x_wakelock, WAKE_LOCK_SUSPEND, "tlsc6x_wakelock");
err = tlsc6x_request_irq_work();
if (err < 0) {
dev_err(&client->dev, "tlsc6x error::request irq failed %d\n",
err);
goto exit_irq_request_failed;
}
#if defined(CONFIG_ADF)
g_tp_drvdata->fb_notif.notifier_call = ts_adf_event_handler;
ret = fb_register_client(&g_tp_drvdata->fb_notif);
if (ret) {
dev_err(&client->dev,
"tlsc6x error::unable to register fb_notifier: %d",
ret);
}
#elif defined(CONFIG_HAS_EARLYSUSPEND)
g_tp_drvdata->early_suspend.level =
EARLY_SUSPEND_LEVEL_BLANK_SCREEN + 1;
g_tp_drvdata->early_suspend.suspend = tlsc6x_ts_suspend;
g_tp_drvdata->early_suspend.resume = tlsc6x_ts_resume;
register_early_suspend(&g_tp_drvdata->early_suspend);
#endif
#ifdef TLSC_APK_DEBUG
tlsc6x_create_apk_debug_channel(client);
#endif
err = sysfs_create_group(&client->dev.kobj, &tlsc_attr_group);
if (err < 0) {
tlsc_err("Can not create sysfs group!");
}
//tlsc6x_tptest_init(client);
#ifdef TLSC_ESD_HELPER_EN
{ /* esd issue: i2c monitor thread */
ktime_t ktime = ktime_set(30, 0);
hrtimer_init(&tpd_esd_kthread_timer, CLOCK_MONOTONIC,
HRTIMER_MODE_REL);
tpd_esd_kthread_timer.function = tpd_esd_kthread_hrtimer_func;
hrtimer_start(&tpd_esd_kthread_timer, ktime, HRTIMER_MODE_REL);
kthread_run(esd_checker_handler, 0, "tlsc6x_esd_helper");
}
#endif
GTProcTpInfoEntry = proc_create("tp_info", 0444, NULL, &GTProcTpInfo);
#if 0
if (tp_status_register(i2c_correspond)) {
printk("tp_status_register failed\n");
} else
printk("tp_status_register success\n");
#endif
return 0;
exit_irq_request_failed:input_unregister_device(input_dev);
exit_input_register_device_failed:input_free_device(input_dev);
exit_input_dev_alloc_failed: exit_chip_check_failed:gpio_free
(pdata->
irq_gpio_number);
gpio_free(pdata->reset_gpio_number);
exit_gpio_request_failed:kfree(g_tp_drvdata);
exit_alloc_data_failed:if (pdata != NULL) {
kfree(pdata);
}
g_tp_drvdata = NULL;
i2c_set_clientdata(client, g_tp_drvdata);
exit_alloc_platform_data_failed:return err;
}
static int tlsc6x_remove(struct i2c_client *client)
{
struct tlsc6x_data *drvdata = i2c_get_clientdata(client);
TLSC_FUNC_ENTER();
#ifdef TLSC_APK_DEBUG
tlsc6x_release_apk_debug_channel();
#endif
#ifdef TLSC_ESD_HELPER_EN
hrtimer_cancel(&tpd_esd_kthread_timer);
#endif
if (drvdata == NULL) {
return 0;
}
#if defined(CONFIG_ADF)
fb_unregister_client(&g_tp_drvdata->fb_notif);
#elif defined(CONFIG_HAS_EARLYSUSPEND)
unregister_early_suspend(&drvdata->early_suspend);
#endif
free_irq(client->irq, drvdata);
input_unregister_device(drvdata->input_dev);
input_free_device(drvdata->input_dev);
#ifdef CONFIG_HAS_EARLYSUSPEND
cancel_work_sync(&drvdata->resume_work);
destroy_workqueue(drvdata->tp_resume_workqueue);
#endif
kfree(drvdata);
drvdata = NULL;
i2c_set_clientdata(client, drvdata);
return 0;
}
static const struct i2c_device_id tlsc6x_id[] = { {TS_NAME, 0}, {} };
MODULE_DEVICE_TABLE(i2c, tlsc6x_id);
static const struct of_device_id tlsc6x_of_match[] = { {.compatible =
"tlsc6x,tlsc6x_ts",}, {}
};
MODULE_DEVICE_TABLE(of, tlsc6x_of_match);
static struct i2c_driver tlsc6x_driver = {.probe = tlsc6x_probe, .remove =
tlsc6x_remove, .id_table = tlsc6x_id, .driver = {.name =
TS_NAME, .owner =
THIS_MODULE, .
of_match_table =
tlsc6x_of_match,},
};
static int __init tlsc6x_init(void)
{
tlsc_info("%s: ++\n", __func__);
return i2c_add_driver(&tlsc6x_driver);
}
static void __exit tlsc6x_exit(void)
{
i2c_del_driver(&tlsc6x_driver);
}
module_init(tlsc6x_init);
module_exit(tlsc6x_exit);
MODULE_DESCRIPTION("Chipsemi touchscreen driver");
MODULE_LICENSE("GPL");