android_kernel_samsung_hero.../drivers/input/touchscreen/stm/fts_ts.c
2016-08-17 16:41:52 +08:00

3523 lines
92 KiB
C

/******************** (C) COPYRIGHT 2012 STMicroelectronics ********************
*
* File Name : fts.c
* Authors : AMS(Analog Mems Sensor) Team
* Description : FTS Capacitive touch screen controller (FingerTipS)
*
********************************************************************************
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation.
*
* THE PRESENT SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES
* OR CONDITIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED, FOR THE SOLE
* PURPOSE TO SUPPORT YOUR APPLICATION DEVELOPMENT.
* AS A RESULT, STMICROELECTRONICS SHALL NOT BE HELD LIABLE FOR ANY DIRECT,
* INDIRECT OR CONSEQUENTIAL DAMAGES WITH RESPECT TO ANY CLAIMS ARISING FROM THE
* CONTENT OF SUCH SOFTWARE AND/OR THE USE MADE BY CUSTOMERS OF THE CODING
* INFORMATION CONTAINED HEREIN IN CONNECTION WITH THEIR PRODUCTS.
*
* THIS SOFTWARE IS SPECIFICALLY DESIGNED FOR EXCLUSIVE USE WITH ST PARTS.
********************************************************************************
* REVISON HISTORY
* DATE | DESCRIPTION
* 03/09/2012| First Release
* 08/11/2012| Code migration
* 23/01/2013| SEC Factory Test
* 29/01/2013| Support Hover Events
* 08/04/2013| SEC Factory Test Add more - hover_enable, glove_mode, clear_cover_mode, fast_glove_mode
* 09/04/2013| Support Blob Information
*******************************************************************************/
#include <linux/init.h>
#include <linux/errno.h>
#include <linux/platform_device.h>
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/slab.h>
#include <linux/input.h>
#include <linux/interrupt.h>
#include <linux/serio.h>
#include <linux/init.h>
#include <linux/pm.h>
#include <linux/delay.h>
#include <linux/ctype.h>
#include <linux/gpio.h>
#include <linux/i2c.h>
#include <linux/i2c-dev.h>
#include <linux/fs.h>
#include <linux/uaccess.h>
#include <linux/power_supply.h>
#include <linux/firmware.h>
#include <linux/regulator/consumer.h>
#include <linux/input/mt.h>
#ifdef CONFIG_OF
#include <linux/of_gpio.h>
#endif
#include "fts_ts.h"
#if defined(CONFIG_SECURE_TOUCH)
#include <linux/pm_runtime.h>
#include <linux/errno.h>
#include <linux/atomic.h>
/*#include <asm/system.h>*/
#include <soc/qcom/scm.h>
enum subsystem {
TZ = 1,
APSS = 3
};
#define TZ_BLSP_MODIFY_OWNERSHIP_ID 3
#endif
#ifdef FTS_SUPPORT_TOUCH_KEY
static struct fts_touchkey fts_touchkeys[] = {
{
.value = 0x01,
.keycode = KEY_RECENT,
.name = "recent",
},
{
.value = 0x02,
.keycode = KEY_BACK,
.name = "back",
},
};
#endif
bool tsp_init_done = false;
EXPORT_SYMBOL(tsp_init_done);
#ifdef USE_OPEN_CLOSE
static int fts_input_open(struct input_dev *dev);
static void fts_input_close(struct input_dev *dev);
#ifdef USE_OPEN_DWORK
static void fts_open_work(struct work_struct *work);
#endif
#endif
#if defined(USE_RESET_WORK_EXIT_LOWPOWERMODE) || defined(FTS_SUPPORT_ESD_CHECK)
static void fts_reset_work(struct work_struct *work);
#endif
static void fts_recovery_cx(struct fts_ts_info *info);
static int fts_stop_device(struct fts_ts_info *info);
static int fts_start_device(struct fts_ts_info *info);
static void fts_release_all_finger(struct fts_ts_info *info);
static void fts_delay(unsigned int ms);
#if defined(CONFIG_SECURE_TOUCH)
static void fts_secure_touch_notify(struct fts_ts_info *info);
static irqreturn_t fts_filter_interrupt(struct fts_ts_info *info);
static irqreturn_t fts_interrupt_handler(int irq, void *handle);
static ssize_t fts_secure_touch_enable_show(struct device *dev,
struct device_attribute *attr, char *buf);
static ssize_t fts_secure_touch_enable_store(struct device *dev,
struct device_attribute *attr, const char *buf, size_t count);
static ssize_t fts_secure_touch_show(struct device *dev,
struct device_attribute *attr, char *buf);
static ssize_t secure_ownership_show(struct device *dev,
struct device_attribute *attr, char *buf);
static struct device_attribute attrs[] = {
__ATTR(secure_touch_enable, (S_IRUGO | S_IWUSR | S_IWGRP),
fts_secure_touch_enable_show,
fts_secure_touch_enable_store),
__ATTR(secure_touch, (S_IRUGO),
fts_secure_touch_show,
NULL),
__ATTR(secure_ownership, (S_IRUGO),
secure_ownership_show,
NULL),
};
static int fts_change_pipe_owner(struct fts_ts_info *info, enum subsystem subsystem)
{
/* scm call disciptor */
struct scm_desc desc;
int ret = 0;
/* number of arguments */
desc.arginfo = SCM_ARGS(2);
/* BLSPID (1 - 12) */
desc.args[0] = info->client->adapter->nr - 1;
/* Owner if TZ or APSS */
desc.args[1] = subsystem;
ret = scm_call2(SCM_SIP_FNID(SCM_SVC_TZ, TZ_BLSP_MODIFY_OWNERSHIP_ID), &desc);
dev_err(&info->client->dev, "%s: return1: %d\n", __func__, ret);
if (ret)
return ret;
dev_err(&info->client->dev, "%s: return2: %llu\n", __func__, desc.ret[0]);
return desc.ret[0];
}
static int fts_secure_touch_clk_prepare_enable(struct fts_ts_info *info)
{
int ret;
if (!info->iface_clk || !info->core_clk) {
dev_err(&info->client->dev,
"%s: error clk. iface:%d, core:%d\n", __func__,
IS_ERR_OR_NULL(info->iface_clk), IS_ERR_OR_NULL(info->core_clk));
return -ENODEV;
}
ret = clk_prepare_enable(info->iface_clk);
if (ret) {
dev_err(&info->client->dev,
"%s: error on clk_prepare_enable(iface_clk):%d\n", __func__, ret);
return ret;
}
ret = clk_prepare_enable(info->core_clk);
if (ret) {
clk_disable_unprepare(info->iface_clk);
dev_err(&info->client->dev,
"%s: error clk_prepare_enable(core_clk):%d\n", __func__, ret);
return ret;
}
return ret;
}
static void fts_secure_touch_clk_disable_unprepare(
struct fts_ts_info *info)
{
clk_disable_unprepare(info->core_clk);
clk_disable_unprepare(info->iface_clk);
}
static ssize_t fts_secure_touch_enable_show(struct device *dev,
struct device_attribute *attr, char *buf)
{
struct fts_ts_info *info = dev_get_drvdata(dev);
return scnprintf(buf, PAGE_SIZE, "%d", atomic_read(&info->st_enabled));
}
/*
* Accept only "0" and "1" valid values.
* "0" will reset the st_enabled flag, then wake up the reading process.
* The bus driver is notified via pm_runtime that it is not required to stay
* awake anymore.
* It will also make sure the queue of events is emptied in the controller,
* in case a touch happened in between the secure touch being disabled and
* the local ISR being ungated.
* "1" will set the st_enabled flag and clear the st_pending_irqs flag.
* The bus driver is requested via pm_runtime to stay awake.
*/
static ssize_t fts_secure_touch_enable_store(struct device *dev,
struct device_attribute *attr,
const char *buf, size_t count)
{
struct fts_ts_info *info = dev_get_drvdata(dev);
unsigned long value;
int err = 0;
if (count > 2)
return -EINVAL;
err = kstrtoul(buf, 10, &value);
if (err != 0)
return err;
err = count;
switch (value) {
case 0:
if (atomic_read(&info->st_enabled) == 0)
break;
dev_err(&info->client->dev, "%s: SECURETOUCH_secure_touch_enable : case 0", __func__);
fts_change_pipe_owner(info, APSS);
// mutex_lock(&info->i2c_mutex);
fts_secure_touch_clk_disable_unprepare(info);
dev_err(&info->client->dev, "%s: SECURETOUCH_secure_touch_enable_reset", __func__);
pm_runtime_put_sync(info->client->adapter->dev.parent);
// mutex_unlock(&info->i2c_mutex);
atomic_set(&info->st_enabled, 0);
fts_secure_touch_notify(info);
fts_delay(10);
fts_interrupt_handler(info->client->irq, info);
complete(&info->st_powerdown);
#ifdef ST_INT_COMPLETE
complete(&info->st_interrupt);
#endif
break;
case 1:
if (atomic_read(&info->st_enabled)) {
err = -EBUSY;
break;
}
synchronize_irq(info->client->irq);
/* Release All Finger */
fts_release_all_finger(info);
dev_err(&info->client->dev, "%s: SECURETOUCH_secure_touch_enable : case 1", __func__);
// mutex_lock(&info->i2c_mutex);
if (pm_runtime_get_sync(info->client->adapter->dev.parent) < 0) {
dev_err(&info->client->dev, "pm_runtime_get failed\n");
err = -EIO;
break;
}
if (fts_secure_touch_clk_prepare_enable(info) < 0) {
dev_err(&info->client->dev, "clk_prepare_enable failed\n");
pm_runtime_put_sync(info->client->adapter->dev.parent);
err = -EIO;
break;
}
// mutex_unlock(&info->i2c_mutex);
dev_err(&info->client->dev, "%s: SECURETOUCH_secure_touch_enable_set", __func__);
fts_change_pipe_owner(info, TZ);
dev_err(&info->client->dev, "%s: 1", __func__);
reinit_completion(&info->st_powerdown);
#ifdef ST_INT_COMPLETE
reinit_completion(&info->st_interrupt);
#endif
atomic_set(&info->st_enabled, 1);
dev_err(&info->client->dev, "%s: 2", __func__);
// synchronize_irq(info->client->irq);
atomic_set(&info->st_pending_irqs, 0);
break;
default:
dev_err(&info->client->dev, "unsupported value: %lu\n", value);
err = -EINVAL;
break;
}
dev_err(&info->client->dev, "%s: 3", __func__);
return err;
}
static ssize_t fts_secure_touch_show(struct device *dev,
struct device_attribute *attr, char *buf)
{
struct fts_ts_info *info = dev_get_drvdata(dev);
int val = 0;
dev_err(&info->client->dev, "%s: SECURETOUCH_fts_secure_touch_show &info->st_pending_irqs=%d ",
__func__, atomic_read(&info->st_pending_irqs));
if (atomic_read(&info->st_enabled) == 0)
return -EBADF;
if (atomic_cmpxchg(&info->st_pending_irqs, -1, 0) == -1)
return -EINVAL;
if (atomic_cmpxchg(&info->st_pending_irqs, 1, 0) == 1)
val = 1;
dev_err(&info->client->dev, "%s %d: SECURETOUCH_fts_secure_touch_show", __func__, val);
dev_err(&info->client->dev, "%s: SECURETOUCH_fts_secure_touch_show &info->st_pending_irqs=%d ",
__func__, atomic_read(&info->st_pending_irqs));
#ifdef ST_INT_COMPLETE
complete(&info->st_interrupt);
#endif
return scnprintf(buf, PAGE_SIZE, "%u", val);
}
static ssize_t secure_ownership_show(struct device *dev,
struct device_attribute *attr, char *buf)
{
return snprintf(buf, PAGE_SIZE, "1");
}
#endif
static int fts_write_reg(struct fts_ts_info *info,
unsigned char *reg, unsigned short num_com)
{
struct i2c_msg xfer_msg[2];
struct i2c_client *client = info->client;
int ret;
if (info->touch_stopped) {
dev_err(&info->client->dev, "%s: Sensor stopped\n", __func__);
goto exit;
}
mutex_lock(&info->i2c_mutex);
xfer_msg[0].addr = client->addr;
xfer_msg[0].len = num_com;
xfer_msg[0].flags = 0;
xfer_msg[0].buf = reg;
ret = i2c_transfer(client->adapter, xfer_msg, 1);
if (ret < 0)
dev_err(&client->dev, "%s failed. ret: %d, addr: %x\n",
__func__, ret, xfer_msg[0].addr);
mutex_unlock(&info->i2c_mutex);
return ret;
exit:
return 0;
}
static int fts_read_reg(struct fts_ts_info *info, unsigned char *reg, int cnum,
unsigned char *buf, int num)
{
struct i2c_msg xfer_msg[2];
struct i2c_client *client = info->client;
int ret;
int retry = 3;
if (info->touch_stopped) {
dev_err(&info->client->dev, "%s: Sensor stopped\n", __func__);
goto exit;
}
mutex_lock(&info->i2c_mutex);
xfer_msg[0].addr = client->addr;
xfer_msg[0].len = cnum;
xfer_msg[0].flags = 0;
xfer_msg[0].buf = reg;
xfer_msg[1].addr = client->addr;
xfer_msg[1].len = num;
xfer_msg[1].flags = I2C_M_RD;
xfer_msg[1].buf = buf;
do {
ret = i2c_transfer(client->adapter, xfer_msg, 2);
if (ret < 0) {
dev_err(&client->dev, "%s failed(%d). ret:%d, addr:%x\n",
__func__, retry, ret, xfer_msg[0].addr);
usleep_range(10 * 1000, 10 * 1000);
} else {
break;
}
} while (--retry > 0);
mutex_unlock(&info->i2c_mutex);
return ret;
exit:
return 0;
}
/*
* int fts_read_to_string(struct fts_ts_info *, unsigned short *reg, unsigned char *data, int length)
* read specfic value from the string area.
* string area means Display Lab algorithm
*/
static int fts_read_from_string(struct fts_ts_info *info,
unsigned short *reg, unsigned char *data, int length)
{
unsigned char string_reg[3];
unsigned char *buf;
int ret = 0;
if (!info->dt_data->support_string_lib) {
dev_err(&info->client->dev, "%s: Did not support String lib\n", __func__);
return 0;
}
string_reg[0] = 0xD0;
string_reg[1] = (*reg >> 8) & 0xFF;
string_reg[2] = *reg & 0xFF;
if (info->digital_rev == FTS_DIGITAL_REV_1) {
ret = fts_read_reg(info, string_reg, 3, data, length);
} else {
int ret;
buf = kzalloc(length + 1, GFP_KERNEL);
if (buf == NULL) {
tsp_debug_info(true, &info->client->dev,
"%s: kzalloc error.\n", __func__);
return -ENOMEM;
}
ret = fts_read_reg(info, string_reg, 3, buf, length + 1);
if (ret >= 0)
memcpy(data, &buf[1], length);
kfree(buf);
}
return ret;
}
/*
* int fts_write_to_string(struct fts_ts_info *, unsigned short *, unsigned char *, int)
* send command or write specfic value to the string area.
* string area means Display Lab algorithm
*/
static int fts_write_to_string(struct fts_ts_info *info,
unsigned short *reg, unsigned char *data, int length)
{
struct i2c_msg xfer_msg[3];
unsigned char *regAdd;
int ret;
if (info->touch_stopped) {
dev_err(&info->client->dev, "%s: Sensor stopped\n", __func__);
return 0;
}
if (!info->dt_data->support_string_lib) {
dev_err(&info->client->dev, "%s: Did not support String lib\n", __func__);
return 0;
}
regAdd = kzalloc(length + 6, GFP_KERNEL);
if (regAdd == NULL) {
tsp_debug_info(true, &info->client->dev,
"%s: kzalloc error.\n", __func__);
return -ENOMEM;
}
mutex_lock(&info->i2c_mutex);
/* msg[0], length 3*/
regAdd[0] = 0xb3;
regAdd[1] = 0x20;
regAdd[2] = 0x01;
xfer_msg[0].addr = info->client->addr;
xfer_msg[0].len = 3;
xfer_msg[0].flags = 0;
xfer_msg[0].buf = &regAdd[0];
/* msg[0], length 3*/
/* msg[1], length 4*/
regAdd[3] = 0xb1;
regAdd[4] = (*reg >> 8) & 0xFF;
regAdd[5] = *reg & 0xFF;
memcpy(&regAdd[6], data, length);
/*regAdd[3] : B1 address, [4], [5] : String Address, [6]...: data */
xfer_msg[1].addr = info->client->addr;
xfer_msg[1].len = 3 + length;
xfer_msg[1].flags = 0;
xfer_msg[1].buf = &regAdd[3];
/* msg[1], length 4*/
ret = i2c_transfer(info->client->adapter, xfer_msg, 2);
if (ret == 2) {
dev_info(&info->client->dev,
"%s: string command is OK.\n", __func__);
regAdd[0] = FTS_CMD_NOTIFY;
regAdd[1] = *reg & 0xFF;
regAdd[2] = (*reg >> 8) & 0xFF;
xfer_msg[0].addr = info->client->addr;
xfer_msg[0].len = 3;
xfer_msg[0].flags = 0;
xfer_msg[0].buf = regAdd;
ret = i2c_transfer(info->client->adapter, xfer_msg, 1);
if (ret != 1)
dev_info(&info->client->dev,
"%s: string notify is failed.\n", __func__);
else
dev_info(&info->client->dev,
"%s: string notify is OK[%X].\n", __func__, *data);
} else {
dev_info(&info->client->dev,
"%s: string command is failed. ret: %d\n", __func__, ret);
}
mutex_unlock(&info->i2c_mutex);
kfree(regAdd);
return ret;
}
static void fts_delay(unsigned int ms)
{
if (ms < 20)
usleep_range(ms * 1000, ms * 1000);
else
msleep(ms);
}
static int fts_command(struct fts_ts_info *info, unsigned char cmd)
{
unsigned char regAdd = 0;
int ret;
regAdd = cmd;
ret = fts_write_reg(info, &regAdd, 1);
if (ret < 0)
dev_err(&info->client->dev, "%s failed. ret: %d\n",
__func__, ret);
else
dev_info(&info->client->dev, "%s: cmd: %02X, ret: %d\n",
__func__, cmd, ret);
return ret;
}
static void fts_enable_feature(struct fts_ts_info *info, unsigned char cmd, int enable)
{
unsigned char regAdd[2] = {FTS_CMS_ENABLE_FEATURE, 0x00};
int ret = 0;
if (!enable)
regAdd[0] = FTS_CMS_DISABLE_FEATURE;
regAdd[1] = cmd;
ret = fts_write_reg(info, &regAdd[0], 2);
tsp_debug_info(true, &info->client->dev,
"FTS %s Feature (%02X %02X) , ret = %d\n",
enable ? "Enable" : "Disable", regAdd[0], regAdd[1], ret);
}
/* Cover Type
* 0 : Flip Cover
* 1 : S View Cover
* 2 : N/A
* 3 : S View Wireless Cover
* 4 : N/A
* 5 : S Charger Cover
* 6 : S View Wallet Cover
* 7 : LED Cover
* 100 : Montblanc
*/
static void fts_set_cover_type(struct fts_ts_info *info, bool enable)
{
dev_info(&info->client->dev, "%s: %d\n", __func__, info->cover_type);
switch (info->cover_type) {
case FTS_VIEW_WIRELESS:
case FTS_VIEW_COVER:
fts_enable_feature(info, FTS_FEATURE_COVER_GLASS, enable);
break;
case FTS_VIEW_WALLET:
fts_enable_feature(info, FTS_FEATURE_COVER_WALLET, enable);
break;
case FTS_FLIP_WALLET:
case FTS_LED_COVER:
case FTS_MONTBLANC_COVER:
fts_enable_feature(info, FTS_FEATURE_COVER_LED, enable);
break;
case FTS_CLEAR_FLIP_COVER :
fts_enable_feature(info, FTS_FEATURE_COVER_CLEAR_FLIP, enable);
break;
case FTS_QWERTY_KEYBOARD_EUR :
case FTS_QWERTY_KEYBOARD_KOR :
fts_enable_feature(info, 0x0D, enable);
break;
case FTS_CHARGER_COVER:
case FTS_COVER_NOTHING1:
case FTS_COVER_NOTHING2:
default:
dev_err(&info->client->dev, "%s: not chage touch state, %d\n",
__func__, info->cover_type);
break;
}
info->flip_state = enable;
}
static int fts_change_scan_rate(struct fts_ts_info *info, unsigned char cmd)
{
unsigned char regAdd[2] = {0xC3, 0x00};
int ret = 0;
regAdd[1] = cmd;
ret = fts_write_reg(info, &regAdd[0], 2);
if (ret < 0)
dev_err(&info->client->dev, "%s failed. ret: %d\n",
__func__, ret);
else
dev_info(&info->client->dev, "%s: cmd: %02X, ret: %d\n",
__func__, cmd, ret);
return ret;
}
static int fts_systemreset(struct fts_ts_info *info)
{
unsigned char regAdd[4] = {0xB6, 0x00, 0x23, 0x01};
int ret;
if (info->stm_ver == STM_VER7) {
regAdd[2] = 0x28;
regAdd[3] = 0x80;
}
ret = fts_write_reg(info, regAdd, 4);
if (ret < 0)
dev_err(&info->client->dev, "%s failed. ret: %d\n",
__func__, ret);
else
dev_info(&info->client->dev, "%s: ret: %d\n",
__func__, ret);
fts_delay(10);
return ret;
}
int fts_interrupt_set(struct fts_ts_info *info, int enable)
{
unsigned char regAdd[4] = {0xB6, 0x00, 0x1C, enable};
int ret;
if (info->stm_ver == STM_VER7) {
regAdd[2] = 0x2C;
if (enable)
regAdd[3] = INT_ENABLE_D3;
else
regAdd[3] = INT_DISABLE_D3;
}
ret = fts_write_reg(info, regAdd, 4);
if (ret < 0)
dev_err(&info->client->dev, "%s failed. ret: %d\n",
__func__, ret);
else
dev_info(&info->client->dev, "%s: %s. ret: %d\n",
__func__, enable ? "enable" : "disable", ret);
return ret;
}
static int fts_check_stm_ver(struct fts_ts_info *info)
{
unsigned char regAdd[3] = {0xB6, 0x00, 0x07};
unsigned char val[7] = {0};
int ret;
ret = fts_read_reg(info, regAdd, 3, val, 7);
if (ret < 0) {
dev_err(&info->client->dev, "%s failed. ret: %d\n",
__func__, ret);
return ret;
}
if ((val[1] == FTS_ID0) && (val[2] == FTS_ID2)) {
dev_err(&info->client->dev, "%s FTS4/5\n", __func__);
return STM_VER5;
}
regAdd[2] = 0x04;
ret = fts_read_reg(info, regAdd, 3, val, 7);
if (ret < 0) {
dev_err(&info->client->dev, "%s failed. ret: %d\n",
__func__, ret);
return ret;
}
if ((val[1] == FTS_7_ID0) && (val[2]) == FTS_7_ID1) {
tsp_debug_info(true, &info->client->dev,
"FTS 7 series Chip ID : %02X %02X\n", val[1], val[2]);
return STM_VER7;
}
return 0;
}
/*
* static int fts_read_chip_id(struct fts_ts_info *info)
* :
*/
static int fts_read_chip_id(struct fts_ts_info *info)
{
unsigned char regAdd[3] = {0xB6, 0x00, 0x07};
unsigned char val[7] = {0};
int ret;
if (info->stm_ver == STM_VER7) {
regAdd[2] = 0x04;
}
ret = fts_read_reg(info, regAdd, 3, val, 7);
if (ret < 0) {
dev_err(&info->client->dev, "%s failed. ret: %d\n",
__func__, ret);
return ret;
}
dev_info(&info->client->dev, "%s: %02X %02X %02X %02X %02X %02X\n",
__func__, val[1], val[2], val[3],
val[4], val[5], val[6]);
/* FTS7BD50C is different with FTS5 and FTS4 series */
if (info->stm_ver == STM_VER7) {
if ((val[1] == FTS_7_ID0) && (val[2]) == FTS_7_ID1) {
tsp_debug_info(true, &info->client->dev,
"FTS 7 series Chip ID : %02X %02X\n", val[1], val[2]);
info->digital_rev = FTS_DIGITAL_REV_3;
return 0;
} else {
tsp_debug_err(true, &info->client->dev,
"[FAIL] FTS 7 series Chip ID : %02X, %02X %02X\n", val[0], val[1], val[2]);
}
}
if (val[1] == FTS_ID0 && val[2] == FTS_ID2) {
if (val[4] == 0x00 && val[5] == 0x00) {
/* 00 39 6C 03 00 00 ==> Cx Corruption */
fts_recovery_cx(info);
} else {
tsp_debug_info(true, &info->client->dev,
"FTS Chip ID : %02X %02X\n", val[1], val[2]);
}
}
if (val[1] != FTS_ID0) {
dev_err(&info->client->dev, "%s: invalid chip id, %02X\n",
__func__, val[1]);
return -FTS_ERROR_INVALID_CHIP_ID;
}
if (val[2] == FTS_ID1) {
info->digital_rev = FTS_DIGITAL_REV_1;
} else if (val[2] == FTS_ID2) {
info->digital_rev = FTS_DIGITAL_REV_2;
} else {
dev_err(&info->client->dev, "%s: invalid chip version id, %02X\n",
__func__, val[2]);
return -FTS_ERROR_INVALID_CHIP_VERSION_ID;
}
if ((val[5] == 0) && (val[6] == 0)) {
dev_err(&info->client->dev, "%s: invalid version, need firmup\n", __func__);
}
return ret;
}
static int fts_wait_for_ready(struct fts_ts_info *info)
{
unsigned char regAdd = READ_ONE_EVENT;
unsigned char data[FTS_EVENT_SIZE] = {0, };
int retry = 0, err_cnt = 0, ret;
do {
ret = fts_read_reg(info, &regAdd, 1, data, FTS_EVENT_SIZE);
if (data[0] == EVENTID_CONTROLLER_READY) {
ret = 0;
break;
}
if (data[0] == EVENTID_ERROR) {
if (err_cnt++ > 32) {
ret = -FTS_ERROR_EVENT_ID;
break;
}
continue;
}
if (ret < 0 && retry == 3) {
dev_err(&info->client->dev, "%s: i2c error\n", __func__);
ret = -FTS_ERROR_TIMEOUT;
#ifdef FTS_SUPPORT_ESD_CHECK
if (info->lowpower_mode) {
schedule_delayed_work(&info->reset_work, msecs_to_jiffies(10));
}
#endif
break;
}
if (retry++ > 30) {
dev_err(&info->client->dev, "%s: time out\n", __func__);
ret = -FTS_ERROR_TIMEOUT;
#ifdef FTS_SUPPORT_ESD_CHECK
if (info->lowpower_mode) {
schedule_delayed_work(&info->reset_work, msecs_to_jiffies(10));
}
#endif
break;
}
fts_delay(10);
} while (ret);
dev_info(&info->client->dev,
"%s: %02X, %02X, %02X, %02X, %02X, %02X, %02X, %02X\n",
__func__, data[0], data[1], data[2], data[3],
data[4], data[5], data[6], data[7]);
return ret;
}
static int fts_get_version_info(struct fts_ts_info *info)
{
unsigned char regAdd[3] = {0, 0, 0};
unsigned char data[FTS_EVENT_SIZE] = {0, };
int retry = 0, ret;
ret = fts_command(info, FTS_CMD_RELEASEINFO);
if (ret < 0) {
dev_err(&info->client->dev, "%s: failed\n", __func__);
return ret;
}
regAdd[0] = READ_ONE_EVENT;
while (fts_read_reg(info, regAdd, 1, data, FTS_EVENT_SIZE)) {
if (data[0] == EVENTID_INTERNAL_RELEASE_INFO) {
info->fw_version_of_ic = ((data[3] << 8) | data[4]);
info->config_version_of_ic = ((data[6] << 8) | data[5]);
info->ic_product_id = data[2];
} else if (data[0] == EVENTID_EXTERNAL_RELEASE_INFO) {
info->fw_main_version_of_ic = ((data[1] << 8) | data[2]);
break;
}
if (retry++ > 30) {
dev_err(&info->client->dev, "%s: time out\n", __func__);
ret = -FTS_ERROR_TIMEOUT;
break;
}
}
dev_info(&info->client->dev,
"%s: ID: 0x%02X, Firmware: 0x%04X, Config: 0x%04X, Main: 0x%04X\n",
__func__, info->ic_product_id, info->fw_version_of_ic,
info->config_version_of_ic, info->fw_main_version_of_ic);
return ret;
}
#ifdef FTS_SUPPORT_NOISE_PARAM
int fts_get_noise_param_address(struct fts_ts_info *info)
{
unsigned char regAdd[3];
unsigned char rData[3] = {0, };
int ret = -1;
int ii;
regAdd[0] = 0xd0;
regAdd[1] = 0x00;
regAdd[2] = 0x40;
if (info->digital_rev == FTS_DIGITAL_REV_1) {
ret = fts_read_reg(info, regAdd, 3, (unsigned char *)info->noise_param->pAddr, 2);
} else if (info->digital_rev == FTS_DIGITAL_REV_2) {
ret = fts_read_reg(info, regAdd, 3, rData, 3);
info->noise_param->pAddr[0] = (rData[2] << 8) | rData[1];
}
if (ret < 0) {
dev_err(&info->client->dev, "%s: failed, ret: %d\n", __func__, ret);
return ret;
}
for (ii = 1; ii < MAX_NOISE_PARAM; ii++)
info->noise_param->pAddr[ii] = info->noise_param->pAddr[0] + (ii * 2);
for (ii = 0; ii < MAX_NOISE_PARAM; ii++)
dev_info(&info->client->dev, "%s: [%d] address: 0x%04X\n",
__func__, ii, info->noise_param->pAddr[ii]);
return ret;
}
static int fts_get_noise_param(struct fts_ts_info *info)
{
int rc;
unsigned char regAdd[3];
unsigned char data[MAX_NOISE_PARAM * 2];
struct fts_noise_param *noise_param;
int i;
unsigned char buf[3];
noise_param = (struct fts_noise_param *)&info->noise_param;
memset(data, 0x0, MAX_NOISE_PARAM * 2);
for (i = 0; i < MAX_NOISE_PARAM; i++) {
regAdd[0] = 0xb3;
regAdd[1] = 0x00;
regAdd[2] = 0x10;
fts_write_reg(info, regAdd, 3);
regAdd[0] = 0xb1;
regAdd[1] = (info->noise_param->pAddr[i] >> 8) & 0xff;
regAdd[2] = info->noise_param->pAddr[i] & 0xff;
rc = fts_read_reg(info, regAdd, 3, buf, 3);
info->noise_param->pData[i] = (buf[2] << 8) | buf[1];
}
for (i = 0; i < MAX_NOISE_PARAM; i++)
dev_info(&info->client->dev, "%s: [%d] address: 0x%04X data: 0x%04X\n",
__func__, i, info->noise_param->pAddr[i],
info->noise_param->pData[i]);
return rc;
}
static int fts_set_noise_param(struct fts_ts_info *info)
{
int i;
unsigned char regAdd[5];
for (i = 0; i < MAX_NOISE_PARAM; i++) {
regAdd[0] = 0xb3;
regAdd[1] = 0x00;
regAdd[2] = 0x10;
fts_write_reg(info, regAdd, 3);
regAdd[0] = 0xb1;
regAdd[1] = (info->noise_param->pAddr[i] >> 8) & 0xff;
regAdd[2] = info->noise_param->pAddr[i] & 0xff;
regAdd[3] = info->noise_param->pData[i] & 0xff;
regAdd[4] = (info->noise_param->pData[i] >> 8) & 0xff;
fts_write_reg(info, regAdd, 5);
}
for (i = 0; i < MAX_NOISE_PARAM; i++)
dev_info(&info->client->dev, "%s: [%d] address: 0x%04X, data: 0x%04X\n",
__func__, i, info->noise_param->pAddr[i],
info->noise_param->pData[i]);
return 0;
}
#endif // FTS_SUPPORT_NOISE_PARAM
#ifdef FTS_SUPPORT_TOUCH_KEY
static void fts_release_all_key(struct fts_ts_info *info)
{
input_report_key(info->input_dev, KEY_RECENT, KEY_RELEASE);
input_report_key(info->input_dev, KEY_BACK, KEY_RELEASE);
tsp_debug_info(true, &info->client->dev, "[TSP_KEY] %s\n", __func__);
#ifdef TKEY_BOOSTER
if ((info->tsp_keystatus & TOUCH_KEY_RECENT)
|| (info->tsp_keystatus & TOUCH_KEY_BACK)) {
if (info->tkey_booster && info->tkey_booster->dvfs_set)
info->tkey_booster->dvfs_set(info->tkey_booster, 0);
} else {
if (info->tkey_booster && info->tkey_booster->dvfs_set)
info->tkey_booster->dvfs_set(info->tkey_booster, 2);
}
#endif
}
#endif
#if defined(CONFIG_TOUCHSCREEN_DUMP_MODE)
#include <linux/qcom/sec_debug.h>
extern struct tsp_dump_callbacks dump_callbacks;
static struct delayed_work * p_ghost_check;
extern void fts_ts_run_rawdata_all(struct fts_ts_info *info);
static void fts_ts_check_rawdata(struct work_struct *work)
{
struct fts_ts_info *info = container_of(work, struct fts_ts_info, ghost_check.work);
if (info->tsp_dump_lock == 1) {
tsp_debug_err(true, &info->client->dev, "%s, ignored ## already checking..\n", __func__);
return;
}
if (info->power_state == STATE_POWEROFF) {
tsp_debug_err(true, &info->client->dev, "%s, ignored ## IC is power off\n", __func__);
return;
}
info->tsp_dump_lock = 1;
tsp_debug_err(true, &info->client->dev, "%s, start ##\n", __func__);
fts_ts_run_rawdata_all((void *)info);
msleep(100);
tsp_debug_err(true, &info->client->dev, "%s, done ##\n", __func__);
info->tsp_dump_lock = 0;
}
static void dump_tsp_log(void)
{
printk(KERN_ERR "fts_ts %s: start \n", __func__);
#if defined(CONFIG_SAMSUNG_LPM_MODE)
if (poweroff_charging) {
printk(KERN_ERR "fts_ts %s, ignored ## lpm charging Mode!!\n", __func__);
return;
}
#endif
if (p_ghost_check == NULL) {
printk(KERN_ERR "fts_ts %s, ignored ## tsp probe fail!!\n", __func__);
return;
}
schedule_delayed_work(p_ghost_check, msecs_to_jiffies(100));
}
#endif
/* Added for samsung dependent codes such as Factory test,
* Touch booster, Related debug sysfs.
*/
#include "fts_sec.c"
static int fts_init(struct fts_ts_info *info)
{
unsigned char val[16] = {0, };
unsigned char regAdd[8];
int ret;
int retry = 0;
fts_systemreset(info);
ret = fts_wait_for_ready(info);
if (ret == -FTS_ERROR_EVENT_ID) {
info->fw_version_of_ic = 0;
info->config_version_of_ic = 0;
info->fw_main_version_of_ic = 0;
}/* else {
fts_get_version_info(info);
}*/
info->stm_ver = fts_check_stm_ver(info);
ret = fts_read_chip_id(info);
if (ret < 0) {
return ret;
}
fts_get_version_info(info);
/* get_tsp_test_result to backup after fw_update */
if (info->stm_ver == STM_VER7) {
do {
ret = fts_get_tsp_test_result(info);
retry++;
} while ((ret < 0) && (retry < 5));
}
ret = fts_fw_update_on_probe(info);
if (ret < 0)
dev_err(info->dev, "%s: Failed to firmware update\n",
__func__);
if (info->stm_ver != STM_VER7)
fts_command(info, SLEEPOUT);
#ifdef SEC_TSP_FACTORY_TEST
ret = fts_get_channel_info(info);
if (ret < 0) {
dev_info(&info->client->dev, "%s: failed get channel info\n", __func__);
return ret;
}
if (info->SenseChannelLength && info->ForceChannelLength) {
info->pFrame =
kzalloc(info->SenseChannelLength * info->ForceChannelLength * 2,
GFP_KERNEL);
info->cx_data = kzalloc(info->SenseChannelLength * info->ForceChannelLength, GFP_KERNEL);
if (!info->pFrame || !info->cx_data) {
dev_err(&info->client->dev, "%s: cx_data kzalloc Failed\n", __func__);
return -ENOMEM;
}
}
#endif
fts_command(info, FLUSHBUFFER);
fts_delay(10);
fts_command(info, SENSEON);
#ifdef FTS_SUPPORT_TOUCH_KEY
if (info->dt_data->support_mskey)
fts_command(info, FTS_CMD_KEY_SENSE_ON);
#endif
#ifdef FTS_SUPPORT_NOISE_PARAM
fts_get_noise_param_address(info);
#endif
/* fts driver set functional feature */
info->touch_count = 0;
#ifdef FTS_SUPPORT_HOVER
info->hover_enabled = false;
info->hover_ready = false;
#endif
info->flip_enable = false;
info->mainscr_disable = false;
info->deepsleep_mode = false;
info->lowpower_mode = false;
info->lowpower_flag = 0x00;
info->power_state = STATE_ACTIVE;
#ifdef FTS_SUPPORT_TOUCH_KEY
info->tsp_keystatus = 0x00;
#endif
#ifdef FTS_SUPPORT_2NDSCREEN_FLAG
info->SIDE_Flag = 0;
info->previous_SIDE_value = 0;
#endif
fts_command(info, FORCECALIBRATION);
fts_interrupt_set(info, INT_ENABLE);
fts_irq_enable(info, true);
regAdd[0] = READ_STATUS;
ret = fts_read_reg(info, regAdd, 1, (unsigned char *)val, 4);
dev_info(&info->client->dev, "FTS ReadStatus(0x84) : %02X %02X %02X %02X\n", val[0],
val[1], val[2], val[3]);
dev_info(&info->client->dev, "FTS Initialized\n");
return 0;
}
static void fts_debug_msg_event_handler(struct fts_ts_info *info,
unsigned char data[])
{
dev_err(&info->client->dev,
"%s: %02X %02X %02X %02X %02X %02X %02X %02X\n",
__func__, data[0], data[1], data[2], data[3],
data[4], data[5], data[6], data[7]);
}
#if defined(CONFIG_SAMSUNG_PRODUCT_SHIP)
static char location_detect(struct fts_ts_info *info, int coord, bool flag)
{
/* flag ? coord = Y : coord = X */
int x_devide = info->dt_data->max_x / 3;
int y_devide = info->dt_data->max_y / 3;
if (flag) {
if (coord < y_devide)
return 'H';
else if (coord < y_devide * 2)
return 'M';
else
return 'L';
} else {
if (coord < x_devide)
return '0';
else if (coord < x_devide * 2)
return '1';
else
return '2';
}
return 'E';
}
#endif
static unsigned char fts_event_handler_type_b(struct fts_ts_info *info,
unsigned char data[],
unsigned char LeftEvent)
{
unsigned char EventNum = 0;
unsigned char NumTouches = 0;
unsigned char TouchID = 0, EventID = 0, status = 0;
unsigned char LastLeftEvent = 0;
int x = 0, y = 0, z = 0;
int bw = 0, bh = 0, palm = 0, sumsize = 0;
unsigned short string_addr;
unsigned char string;
#ifdef FTS_SUPPORT_2NDSCREEN_FLAG
u8 currentSideFlag = 0;
#endif
#ifdef FTS_USE_SIDE_SCROLL_FLAG
int scroll_flag = 0;
int scroll_thr = 0;
#endif
#ifdef FTS_SUPPORT_SIDE_GESTURE
static int longpress_release[FINGER_MAX] = {0, };
#endif
unsigned char tool_type = MT_TOOL_FINGER;
#ifdef USE_STYLUS_PEN
bool finger_type = MT_TOOL_FINGER;
#endif
for (EventNum = 0; EventNum < LeftEvent; EventNum++) {
#if 0
dev_info(&info->client->dev, "%d %2x %2x %2x %2x %2x %2x %2x %2x\n",
EventNum,
data[EventNum * FTS_EVENT_SIZE],
data[EventNum * FTS_EVENT_SIZE + 1],
data[EventNum * FTS_EVENT_SIZE + 2],
data[EventNum * FTS_EVENT_SIZE + 3],
data[EventNum * FTS_EVENT_SIZE + 4],
data[EventNum * FTS_EVENT_SIZE + 5],
data[EventNum * FTS_EVENT_SIZE + 6],
data[EventNum * FTS_EVENT_SIZE + 7]);
#endif
EventID = data[EventNum * FTS_EVENT_SIZE] & 0x0F;
if ((EventID >= 3) && (EventID <= 5)) {
LastLeftEvent = 0;
NumTouches = 1;
TouchID = (data[EventNum * FTS_EVENT_SIZE] >> 4) & 0x0F;
} else {
LastLeftEvent =
data[7 + EventNum * FTS_EVENT_SIZE] & 0x0F;
NumTouches =
(data[1 + EventNum * FTS_EVENT_SIZE] & 0xF0) >> 4;
TouchID = data[1 + EventNum * FTS_EVENT_SIZE] & 0x0F;
EventID = data[EventNum * FTS_EVENT_SIZE] & 0xFF;
status = data[1 + EventNum * FTS_EVENT_SIZE] & 0xFF;
}
#ifdef FTS_SUPPORT_HOVER
if (info->hover_present &&
(EventID != EVENTID_HOVER_ENTER_POINTER) &&
(EventID != EVENTID_HOVER_MOTION_POINTER)) {
dev_info(&info->client->dev,
"[HR] tID:%d Ver[%02X%04X%01X%01X%01X] --\n",
TouchID,
info->panel_revision, info->fw_main_version_of_ic,
info->flip_enable, info->glove_enabled, info->mainscr_disable);
info->finger[TouchID].mcount = 0;
input_mt_slot(info->input_dev, 0);
input_mt_report_slot_state(info->input_dev,
MT_TOOL_FINGER, 0);
info->hover_present = false;
}
#endif
switch (EventID) {
case EVENTID_NO_EVENT:
dev_info(&info->client->dev, "%s: No Event\n", __func__);
break;
#ifdef FTS_SUPPORT_TOUCH_KEY
case EVENTID_MSKEY:
if (info->dt_data->support_mskey) {
unsigned char input_keys;
input_keys = data[2 + EventNum * FTS_EVENT_SIZE];
if (input_keys == 0x00)
fts_release_all_key(info);
else {
unsigned char change_keys;
unsigned char key_state;
unsigned char key_recent;
unsigned char key_back;
key_recent = TOUCH_KEY_RECENT;
key_back = TOUCH_KEY_BACK;
change_keys = input_keys ^ info->tsp_keystatus;
if (change_keys & key_recent) {
key_state = input_keys & key_recent;
input_report_key(info->input_dev, KEY_RECENT, key_state != 0 ? KEY_PRESS : KEY_RELEASE);
tsp_debug_info(true, &info->client->dev, "[TSP_KEY] RECENT %s\n", key_state != 0 ? "P" : "R");
}
if (change_keys & key_back) {
key_state = input_keys & key_back;
input_report_key(info->input_dev, KEY_BACK, key_state != 0 ? KEY_PRESS : KEY_RELEASE);
tsp_debug_info(true, &info->client->dev, "[TSP_KEY] BACK %s\n" , key_state != 0 ? "P" : "R");
}
#ifdef TKEY_BOOSTER
if (info->tkey_booster && info->tkey_booster->dvfs_set)
info->tkey_booster->dvfs_set(info->tkey_booster, !!key_state);
#endif
input_sync(info->input_dev);
}
info->tsp_keystatus = input_keys;
}
break;
#endif
case EVENTID_SIDE_TOUCH:
case EVENTID_SIDE_TOUCH_DEBUG: {
#if defined(FTS_SUPPORT_SIDE_GESTURE) || defined(FTS_SUPPORT_ESD_CHECK)
unsigned char event_type = data[1 + EventNum * FTS_EVENT_SIZE];
#endif
#ifdef FTS_SUPPORT_SIDE_GESTURE
if ((event_type == FTS_SIDEGESTURE_EVENT_SINGLE_STROKE) ||
(event_type == FTS_SIDEGESTURE_EVENT_DOUBLE_STROKE) ||
(event_type == FTS_SIDEGESTURE_EVENT_INNER_STROKE)) {
if (info->dt_data->support_sidegesture) {
int direction, distance;
direction = data[2 + EventNum * FTS_EVENT_SIZE];
distance = *(int *)&data[3 + EventNum * FTS_EVENT_SIZE];
if (direction)
input_report_key(info->input_dev, KEY_SIDE_GESTURE_RIGHT, 1);
else
input_report_key(info->input_dev, KEY_SIDE_GESTURE_LEFT, 1);
tsp_debug_info(true, &info->client->dev,
"%s: [Gesture] %02X %02X %02X %02X %02X %02X %02X %02X\n",
__func__, data[0], data[1], data[2], data[3],
data[4], data[5], data[6], data[7]);
input_sync(info->input_dev);
usleep_range(1000, 1000);
if (direction)
input_report_key(info->input_dev, KEY_SIDE_GESTURE_RIGHT, 0);
else
input_report_key(info->input_dev, KEY_SIDE_GESTURE_LEFT, 0);
} else {
fts_debug_msg_event_handler(info, &data[EventNum * FTS_EVENT_SIZE]);
}
}
else if (event_type == FTS_SIDETOUCH_EVENT_LONG_PRESS) {
int sideLongPressfingerID = 0;
sideLongPressfingerID = data[2 + EventNum * FTS_EVENT_SIZE];
//Todo : event processing
longpress_release[sideLongPressfingerID - 1] = 1;
tsp_debug_info(true, &info->client->dev,
"%s: [Side Long Press]id:%d %02X %02X %02X %02X %02X %02X %02X %02X\n",
__func__, sideLongPressfingerID, data[0], data[1],
data[2], data[3], data[4], data[5], data[6], data[7]);
}
else
#endif
{
#ifdef FTS_SUPPORT_ESD_CHECK
if (event_type == FTS_SIDETOUCH_EVENT_REBOOT_BY_ESD) {
tsp_debug_info(true, &info->client->dev,
"%s: [ESD error] %02X %02X %02X %02X %02X %02X %02X %02X\n",
__func__, data[0], data[1], data[2], data[3],
data[4], data[5], data[6], data[7]);
schedule_delayed_work(&info->reset_work, msecs_to_jiffies(10));
}
else
#endif
fts_debug_msg_event_handler(info, &data[EventNum * FTS_EVENT_SIZE]);
}
break;}
case EVENTID_ERROR:
/* Get Auto tune fail event */
if (data[1 + EventNum * FTS_EVENT_SIZE] == 0x08) {
if (data[2 + EventNum * FTS_EVENT_SIZE] == 0x00)
dev_info(&info->client->dev, "[FTS] Fail Mutual Auto tune\n");
else if (data[2 + EventNum * FTS_EVENT_SIZE] == 0x01)
dev_info(&info->client->dev, "[FTS] Fail Self Auto tune\n");
} else if (data[1 + EventNum * FTS_EVENT_SIZE] == 0x09) {
tsp_debug_info(true, &info->client->dev, "[FTS] Fail detect SYNC\n");
}
break;
#ifdef FTS_SUPPORT_HOVER
case EVENTID_HOVER_ENTER_POINTER:
case EVENTID_HOVER_MOTION_POINTER:
info->hover_present = true;
x = ((data[4 + EventNum * FTS_EVENT_SIZE] & 0xF0) >> 4)
| ((data[2 + EventNum * FTS_EVENT_SIZE]) << 4);
y = ((data[4 + EventNum * FTS_EVENT_SIZE] & 0x0F) |
((data[3 + EventNum * FTS_EVENT_SIZE]) << 4));
z = data[5 + EventNum * FTS_EVENT_SIZE];
input_mt_slot(info->input_dev, 0);
input_mt_report_slot_state(info->input_dev,
MT_TOOL_FINGER, 1);
input_report_key(info->input_dev, BTN_TOUCH, 0);
input_report_key(info->input_dev, BTN_TOOL_FINGER, 1);
#ifdef FTS_SUPPORT_2NDSCREEN_FLAG
info->SIDE_Flag = 0;
info->previous_SIDE_value = 0;
input_report_key(info->input_dev, BTN_SUBSCREEN_FLAG, 0);
#endif
input_report_abs(info->input_dev, ABS_MT_POSITION_X, x);
input_report_abs(info->input_dev, ABS_MT_POSITION_Y, y);
input_report_abs(info->input_dev, ABS_MT_DISTANCE, 255 - z);
break;
case EVENTID_HOVER_LEAVE_POINTER:
info->hover_present = false;
input_mt_slot(info->input_dev, 0);
input_mt_report_slot_state(info->input_dev,
MT_TOOL_FINGER, 0);
break;
#endif
case EVENTID_ENTER_POINTER:
if (info->power_state & STATE_LOWPOWER_SUSPEND)
continue;
info->touch_count++;
case EVENTID_MOTION_POINTER:
if (info->power_state & STATE_LOWPOWER_SUSPEND) {
dev_err(&info->client->dev, "%s %d: low power mode\n", __func__, __LINE__);
fts_release_all_finger(info);
continue;
}
if (info->touch_count == 0) {
dev_err(&info->client->dev, "%s %d: count 0, set as Enter_pointer\n", __func__, __LINE__);
EventID = EVENTID_ENTER_POINTER;
info->touch_count++;
}
if ((EventID == EVENTID_MOTION_POINTER) &&
(info->finger[TouchID].state == EVENTID_LEAVE_POINTER)) {
dev_err(&info->client->dev, "%s: state leave but point is moved.\n", __func__);
continue;
}
x = data[1 + EventNum * FTS_EVENT_SIZE] +
((data[2 + EventNum * FTS_EVENT_SIZE] &
0x0f) << 8);
y = ((data[2 + EventNum * FTS_EVENT_SIZE] &
0xf0) >> 4) + (data[3 +
EventNum *
FTS_EVENT_SIZE] << 4);
bw = data[4 + EventNum * FTS_EVENT_SIZE];
bh = data[5 + EventNum * FTS_EVENT_SIZE];
palm = (data[6 + EventNum * FTS_EVENT_SIZE] >> 7) & 0x01;
sumsize = (data[6 + EventNum * FTS_EVENT_SIZE] & 0x7f) << 1;
#ifdef FTS_SUPPORT_2NDSCREEN_FLAG
currentSideFlag = (data[7 + EventNum * FTS_EVENT_SIZE] >> 7) & 0x01;
z = data[7 + EventNum * FTS_EVENT_SIZE] & 0x7f;
#else
z = data[7 + EventNum * FTS_EVENT_SIZE];
#endif
input_mt_slot(info->input_dev, TouchID);
#ifdef USE_STYLUS_PEN
/* TODO : need to read finger type in TSP IC. arrange specific register for this. */
if (info->use_stylus)
tool_type = finger_type;
input_mt_report_slot_state(info->input_dev,
tool_type, 1);
#else
input_mt_report_slot_state(info->input_dev,
tool_type,
1 + (palm << 1));
#endif
input_report_key(info->input_dev, BTN_TOUCH, 1);
input_report_key(info->input_dev,
BTN_TOOL_FINGER, 1);
input_report_abs(info->input_dev,
ABS_MT_POSITION_X, x);
input_report_abs(info->input_dev,
ABS_MT_POSITION_Y, y);
input_report_abs(info->input_dev,
ABS_MT_TOUCH_MAJOR, max(bw, bh));
input_report_abs(info->input_dev,
ABS_MT_TOUCH_MINOR, min(bw, bh));
input_report_abs(info->input_dev, ABS_MT_PALM,
palm);
#if defined(FTS_SUPPORT_SIDE_GESTURE)
input_report_abs(info->input_dev, ABS_MT_GRIP, 0);
#endif
#if defined(CONFIG_SEC_FACTORY)
if (z == 0)
input_report_abs(info->input_dev,
ABS_MT_PRESSURE, 0x01);
else
input_report_abs(info->input_dev,
ABS_MT_PRESSURE, z & 0xFF);
#endif
info->finger[TouchID].lx = x;
info->finger[TouchID].ly = y;
break;
case EVENTID_LEAVE_POINTER:
if (info->power_state & STATE_LOWPOWER_SUSPEND)
continue;
if (info->touch_count <= 0) {
dev_err(&info->client->dev, "%s %d: count 0\n", __func__, __LINE__);
fts_release_all_finger(info);
continue;
}
info->touch_count--;
input_mt_slot(info->input_dev, TouchID);
#if defined(FTS_SUPPORT_SIDE_GESTURE)
if (longpress_release[TouchID] == 1) {
input_report_abs(info->input_dev, ABS_MT_GRIP, 1);
dev_info(&info->client->dev, "[FTS] GRIP [%d] %s\n",
TouchID, longpress_release[TouchID] ? "LONGPRESS" : "RELEASE");
longpress_release[TouchID] = 0;
input_sync(info->input_dev);
}
#endif
input_mt_report_slot_state(info->input_dev,
MT_TOOL_FINGER, 0);
if (info->touch_count == 0) {
/* Clear BTN_TOUCH when All touch are released */
input_report_key(info->input_dev, BTN_TOUCH, 0);
input_report_key(info->input_dev, BTN_TOOL_FINGER, 0);
#ifdef FTS_USE_SIDE_SCROLL_FLAG
input_report_key(info->input_dev, BTN_R_FLICK_FLAG, 0);
input_report_key(info->input_dev, BTN_L_FLICK_FLAG, 0);
#endif
#ifdef FTS_SUPPORT_2NDSCREEN_FLAG
info->SIDE_Flag = 0;
info->previous_SIDE_value = 0;
input_report_key(info->input_dev, BTN_SUBSCREEN_FLAG, 0);
#endif
#ifdef FTS_SUPPORT_SIDE_GESTURE
if (info->dt_data->support_sidegesture) {
input_report_key(info->input_dev, KEY_SIDE_GESTURE, 0);
input_report_key(info->input_dev, KEY_SIDE_GESTURE_RIGHT, 0);
input_report_key(info->input_dev, KEY_SIDE_GESTURE_LEFT, 0);
}
#endif
}
break;
case EVENTID_STATUS_EVENT:
if (status == STATUS_EVENT_GLOVE_MODE) {
#ifdef CONFIG_GLOVE_TOUCH
int tm;
if (data[2 + EventNum * FTS_EVENT_SIZE] == 0x01)
info->touch_mode = FTS_TM_GLOVE;
else
info->touch_mode = FTS_TM_NORMAL;
tm = info->touch_mode;
input_report_switch(info->input_dev, SW_GLOVE, tm);
dev_info(&info->client->dev, "[FTS] GLOVE %s\n", tm ? "PRESS" : "RELEASE");
#endif
} else if (status == STATUS_EVENT_RAW_DATA_READY) {
unsigned char regAdd[4] = {0xB0, 0x01, 0x29, 0x01};
fts_write_reg(info, &regAdd[0], 4);
#ifdef FTS_SUPPORT_HOVER
info->hover_ready = true;
#endif
dev_info(&info->client->dev,
"[FTS] Received the Hover Raw Data Ready Event\n");
} else if (status == STATUS_EVENT_FORCE_CAL_MUTUAL) {
tsp_debug_err(true, &info->client->dev,
"[FTS] Received Force Calibration Mutual only Event\n");
} else if (status == STATUS_EVENT_FORCE_CAL_SELF) {
tsp_debug_err(true, &info->client->dev,
"[FTS] Received Force Calibration Self only Event\n");
} else if (status == STATUS_EVENT_WATERMODE_ON) {
tsp_debug_err(true, &info->client->dev,
"[FTS] Received Water Mode On Event\n");
} else if (status == STATUS_EVENT_WATERMODE_OFF) {
tsp_debug_err(true, &info->client->dev,
"[FTS] Received Water Mode Off Event\n");
} else if (status == STATUS_EVENT_MUTUAL_CAL_FRAME_CHECK) {
tsp_debug_err(true, &info->client->dev,
"[FTS] Received Mutual Calib Frame Check Event\n");
} else if (status == STATUS_EVENT_SELF_CAL_FRAME_CHECK) {
tsp_debug_err(true, &info->client->dev,
"[FTS] Received Self Calib Frame Check Event\n");
} else {
fts_debug_msg_event_handler(info,
&data[EventNum *
FTS_EVENT_SIZE]);
}
break;
#ifdef SEC_TSP_FACTORY_TEST
case EVENTID_RESULT_READ_REGISTER:
fts_debug_msg_event_handler(info, &data[EventNum * FTS_EVENT_SIZE]);
break;
#endif
#ifdef FTS_USE_SIDE_SCROLL_FLAG
case EVENTID_SIDE_SCROLL_FLAG:
scroll_flag = data[3 + EventNum * FTS_EVENT_SIZE];
scroll_thr = data[6 + EventNum * FTS_EVENT_SIZE];
dev_info(&info->client->dev,
"[TB] side scroll flag: event: %02X, thr: %02X\n",
scroll_flag, scroll_thr);
// TODO : Report function call this area
if (scroll_flag == 1) {
input_report_key(info->input_dev, BTN_R_FLICK_FLAG, 1);
input_report_key(info->input_dev, BTN_L_FLICK_FLAG, 0);
input_sync(info->input_dev);
} else if (scroll_flag == 2) {
input_report_key(info->input_dev, BTN_R_FLICK_FLAG, 0);
input_report_key(info->input_dev, BTN_L_FLICK_FLAG, 1);
input_sync(info->input_dev);
}
break;
#endif
case EVENTID_FROM_STRING:
string_addr = FTS_CMD_STRING_ACCESS + 1;
fts_read_from_string(info, &string_addr, &string, sizeof(string));
dev_info(&info->client->dev,
"%s: [String] %02X %02X %02X %02X %02X %02X %02X %02X || %04X: %02X\n",
__func__, data[0], data[1], data[2], data[3],
data[4], data[5], data[6], data[7],
string_addr, string);
switch (string) {
case FTS_STRING_EVENT_AOD_TRIGGER:
tsp_debug_info(true, &info->client->dev, "%s: AOD[%X]\n", __func__, string);
input_report_key(info->input_dev, KEY_BLACK_UI_GESTURE, 1);
info->scrub_id = (string << 3) & 0xFF;
break;
/* case FTS_STRING_EVENT_WATCH_STATUS:
case FTS_STRING_EVENT_FAST_ACCESS:
case FTS_STRING_EVENT_DIRECT_INDICATOR:
tsp_debug_info(true, &info->client->dev, "%s: SCRUB[%X]\n", __func__, string_data[1]);
input_report_key(info->input_dev, KEY_BLACK_UI_GESTURE, 1);
info->scrub_id = (string_data[1] >> 2) & 0x3;
info->scrub_x = string_data[2] | (string_data[3] << 8);
info->scrub_y = string_data[4] | (string_data[5] << 8);
break;*/
case FTS_STRING_EVENT_SPAY:
case FTS_STRING_EVENT_SPAY1:
case FTS_STRING_EVENT_SPAY2:
tsp_debug_info(true, &info->client->dev, "%s: SPAY[%X]\n", __func__, string);
input_report_key(info->input_dev, KEY_BLACK_UI_GESTURE, 1);
info->scrub_id = (string >> 2) & 0xF;
break;
/* case FTS_STRING_EVENT_EDGE_SWIPE_RIGHT:
case FTS_STRING_EVENT_EDGE_SWIPE_LEFT:
tsp_debug_info(true, &info->client->dev, "%s: Edge swipe[%X]\n", __func__, string_data[1]);
input_report_key(info->input_dev, KEY_BLACK_UI_GESTURE, 1);
info->scrub_id = (string_data[1] >> 2) & 0xFF;
break;*/
default:
tsp_debug_info(true, &info->client->dev, "%s: no event:%X\n", __func__, string);
break;
}
input_sync(info->input_dev);
input_report_key(info->input_dev, KEY_BLACK_UI_GESTURE, 0);
break;
default:
fts_debug_msg_event_handler(info, &data[EventNum * FTS_EVENT_SIZE]);
continue;
}
#ifdef FTS_SUPPORT_2NDSCREEN_FLAG
if (currentSideFlag != info->previous_SIDE_value) {
dev_info(&info->client->dev, "[TB] 2nd screen flag was changed, old:%d c:%d f:%d\n",
info->previous_SIDE_value, currentSideFlag, info->SIDE_Flag);
info->SIDE_Flag = currentSideFlag;
// TODO : Report function call this area
input_report_key(info->input_dev, BTN_SUBSCREEN_FLAG, !(!(info->SIDE_Flag)));
}
info->previous_SIDE_value = currentSideFlag;
#endif
#if !defined(CONFIG_SAMSUNG_PRODUCT_SHIP)
if (EventID == EVENTID_ENTER_POINTER)
dev_err(&info->client->dev,
"[P] tID:%d x:%d y:%d w:%d h:%d z:%d s:%d p:%d tc:%d tm:%d\n",
TouchID, x, y, bw, bh, z, sumsize, palm, info->touch_count, info->touch_mode);
#ifdef FTS_SUPPORT_HOVER
else if (EventID == EVENTID_HOVER_ENTER_POINTER)
dev_err(&info->client->dev,
"[HP] tID:%d x:%d y:%d z:%d\n",
TouchID, x, y, z);
#endif
else if (EventID == EVENTID_LEAVE_POINTER) {
dev_err(&info->client->dev,
"[R] tID:%d mc: %d tc:%d lx:%d ly:%d Ver[%02X%04X%01X%01X%01X] NV[%X]\n",
TouchID, info->finger[TouchID].mcount, info->touch_count,
info->finger[TouchID].lx, info->finger[TouchID].ly,
info->panel_revision, info->fw_main_version_of_ic,
info->flip_enable, info->glove_enabled, info->mainscr_disable,
info->test_result.data[0]);
info->finger[TouchID].mcount = 0;
}
#else /* defined(CONFIG_SAMSUNG_PRODUCT_SHIP) */
if (EventID == EVENTID_ENTER_POINTER)
dev_err(&info->client->dev,
"[P] tID:%d loc:%c%c tc:%d tm:%d\n",
TouchID,
location_detect(info, y, 1), location_detect(info, x, 0),
info->touch_count, info->touch_mode);
#ifdef FTS_SUPPORT_HOVER
else if (EventID == EVENTID_HOVER_ENTER_POINTER)
dev_err(&info->client->dev,
"[HP] tID:%d\n", TouchID);
#endif
else if (EventID == EVENTID_LEAVE_POINTER) {
dev_err(&info->client->dev,
"[R] tID:%d loc:%c%c mc: %d tc:%d Ver[%02X%04X%01X%01X%01X] NV[%X]\n",
TouchID,
location_detect(info, info->finger[TouchID].ly, 1),
location_detect(info, info->finger[TouchID].lx, 0),
info->finger[TouchID].mcount, info->touch_count,
info->panel_revision, info->fw_main_version_of_ic,
info->flip_enable, info->glove_enabled, info->mainscr_disable,
info->test_result.data[0]);
info->finger[TouchID].mcount = 0;
}
#endif
#ifdef FTS_SUPPORT_HOVER
else if (EventID == EVENTID_HOVER_LEAVE_POINTER) {
if (info->hover_present) {
dev_info(&info->client->dev,
"[HR] tID:%d Ver[%02X%04X%01X%01X] ++\n",
TouchID,
info->panel_revision, info->fw_main_version_of_ic,
info->flip_enable, info->glove_enabled);
info->finger[TouchID].mcount = 0;
info->hover_present = false;
}
}
#endif
else if (EventID == EVENTID_MOTION_POINTER) {
info->finger[TouchID].mcount++;
}
if ((EventID == EVENTID_ENTER_POINTER) ||
(EventID == EVENTID_MOTION_POINTER) ||
(EventID == EVENTID_LEAVE_POINTER))
info->finger[TouchID].state = EventID;
}
input_sync(info->input_dev);
//TEST
#ifdef TSP_BOOSTER
if (EventID == EVENTID_ENTER_POINTER || EventID == EVENTID_LEAVE_POINTER)
if (info->tsp_booster && info->tsp_booster->dvfs_set)
info->tsp_booster->dvfs_set(info->tsp_booster, info->touch_count);
#endif
return LastLeftEvent;
}
#ifdef FTS_SUPPORT_TA_MODE
static void fts_ta_cb(struct fts_callbacks *cb, int ta_status)
{
struct fts_ts_info *info =
container_of(cb, struct fts_ts_info, callbacks);
if (ta_status == 0x01 || ta_status == 0x03) {
fts_command(info, FTS_CMD_CHARGER_PLUGGED);
info->TA_Pluged = true;
dev_info(&info->client->dev,
"%s: device_control : CHARGER CONNECTED, ta_status : %x\n",
__func__, ta_status);
} else {
fts_command(info, FTS_CMD_CHARGER_UNPLUGGED);
info->TA_Pluged = false;
dev_info(&info->client->dev,
"%s: device_control : CHARGER DISCONNECTED, ta_status : %x\n",
__func__, ta_status);
}
}
#endif
#if defined(CONFIG_SECURE_TOUCH)
static void fts_secure_touch_notify(struct fts_ts_info *info)
{
dev_err(&info->client->dev, "%s: SECURETOUCH_NOTIFY", __func__);
sysfs_notify(&info->input_dev->dev.kobj, NULL, "secure_touch");
}
static irqreturn_t fts_filter_interrupt(struct fts_ts_info *info)
{
/*
dev_err(&info->client->dev, "%s: SECURETOUCH_cmpxchg st_pending_irqs=%d",
__func__, atomic_read(&info->st_pending_irqs));
*/
if (atomic_read(&info->st_enabled)) {
if (atomic_cmpxchg(&info->st_pending_irqs, 0, 1) == 0) {
dev_err(&info->client->dev, "%s: SECURETOUCH_cmpxchg", __func__);
dev_err(&info->client->dev, "%s: SECURETOUCH_cmpxchg st_pending_irqs=%d",
__func__, atomic_read(&info->st_pending_irqs));
fts_secure_touch_notify(info);
}
dev_err(&info->client->dev, "%s: SECURETOUCH_FILTER_INTR", __func__);
return IRQ_HANDLED;
}
/*
dev_err(&info->client->dev, "%s: SECURETOUCH_FILTER_INTR_irq-none", __func__);
*/
return IRQ_NONE;
}
#endif
/**
* fts_interrupt_handler()
*
* Called by the kernel when an interrupt occurs (when the sensor
* asserts the attention irq).
*
* This function is the ISR thread and handles the acquisition
* and the reporting of finger data when the presence of fingers
* is detected.
*/
static irqreturn_t fts_interrupt_handler(int irq, void *handle)
{
struct fts_ts_info *info = handle;
unsigned char regAdd[4] = {0xb6, 0x00, 0x45, READ_ALL_EVENT};
unsigned short evtcount = 0;
int ret;
if(info->stm_ver == STM_VER7){
regAdd[2] = 0x23;
}
#if defined(CONFIG_SECURE_TOUCH)
if (IRQ_HANDLED == fts_filter_interrupt(info)) {
#ifdef ST_INT_COMPLETE
ret = wait_for_completion_interruptible_timeout((&info->st_interrupt),
msecs_to_jiffies(10 * MSEC_PER_SEC));
dev_err(&info->client->dev, "%s: SECURETOUCH_IRQ_HANDLED, ret:%d",
__func__, ret);
#else
dev_err(&info->client->dev, "%s: SECURETOUCH_IRQ_HANDLED",
__func__);
#endif
goto out;
}
#endif
evtcount = 0;
if (info->lowpower_mode) {
if (info->power_state == STATE_LOWPOWER_SUSPEND) {
wake_lock_timeout(&info->wakelock, 4 * HZ);
dev_dbg(&info->client->dev, "%s: PM_SUSPEND\n", __func__);
ret = wait_for_completion_interruptible_timeout((&info->resume_done),
msecs_to_jiffies(1 * MSEC_PER_SEC));
dev_dbg(&info->client->dev, "%s: PM_RESUMED, ret:%d\n", __func__, ret);
if (ret == 0)
return IRQ_HANDLED;
}
}
fts_read_reg(info, &regAdd[0], 3, (unsigned char *)&evtcount, 2);
if(info->stm_ver == STM_VER7){
evtcount = evtcount >> 8;
evtcount = evtcount / 2;
//evtcount = evtcount >> 9;
} else {
evtcount = evtcount >> 10;
}
if (evtcount > FTS_FIFO_MAX)
evtcount = FTS_FIFO_MAX;
if (evtcount > 0) {
memset(info->data, 0x0, FTS_EVENT_SIZE * evtcount);
fts_read_reg(info, &regAdd[3], 1, (unsigned char *)info->data,
FTS_EVENT_SIZE * evtcount);
fts_event_handler_type_b(info, info->data, evtcount);
} else {
dev_info(&info->client->dev, "%s: No event[%02X]\n",
__func__, evtcount);
}
#if defined(CONFIG_SECURE_TOUCH)
out:
#endif
return IRQ_HANDLED;
}
void fts_irq_enable(struct fts_ts_info *info, bool enable)
{
if (!info->irq)
return;
if (enable) {
if (info->irq_enabled)
return;
enable_irq(info->irq);
info->irq_enabled = true;
} else {
if (!info->irq_enabled)
return;
disable_irq(info->irq);
info->irq_enabled = false;
}
}
#ifdef FTS_SUPPORT_TOUCH_KEY
static int fts_led_power_ctrl(struct fts_ts_info *info, bool on)
{
int ret = 0;
dev_info(&info->client->dev, "%s: vdd_keyled %s ++\n", __func__, on ? "ON" : "OFF");
if (!info->keyled_vreg) {
dev_info(&info->client->dev, "%s: failed vdd_keyled is null\n", __func__);
} else {
if (on) {
if (regulator_is_enabled(info->keyled_vreg)) {
dev_info(&info->client->dev, "vdd_keyled already enabled\n");
} else {
ret = regulator_enable(info->keyled_vreg);
if (ret) {
dev_err(&info->client->dev, "vdd_keyled enable failed rc=%d\n", ret);
return 2;
}
}
} else {
if (regulator_is_enabled(info->keyled_vreg)) {
ret = regulator_disable(info->keyled_vreg);
if (ret) {
dev_err(&info->client->dev, "vdd_keyled disable failed rc=%d\n", ret);
return 3;
}
} else {
dev_info(&info->client->dev, "vdd_keyled already disabled\n");
}
}
dev_info(&info->client->dev, "%s: vdd_keyled %s --\n", __func__, regulator_is_enabled(info->keyled_vreg) ? "ON" : "OFF");
}
return ret;
}
#endif
static int fts_power_ctrl(struct i2c_client *client, bool enable)
{
struct device *dev = &client->dev;
int retval;
static struct regulator *avdd, *vddo;
if (!avdd) {
avdd = devm_regulator_get(dev, "avdd");
if (IS_ERR(avdd)) {
dev_err(dev, "%s: could not get avdd, rc = %ld\n",
__func__, PTR_ERR(avdd));
avdd = NULL;
return -ENODEV;
}
retval = regulator_set_voltage(avdd, 3300000, 3300000);
if (retval)
dev_err(dev, "%s: unable to set avdd voltage to 3.3V\n",
__func__);
dev_err(dev, "%s: is enabled %s\n", __func__, regulator_is_enabled(avdd) ? "TRUE" : "FALSE");
}
if (!vddo) {
vddo = devm_regulator_get(dev, "vddo");
if (IS_ERR(vddo)) {
dev_err(dev, "%s: could not get vddo, rc = %ld\n",
__func__, PTR_ERR(vddo));
vddo = NULL;
return -ENODEV;
}
retval = regulator_set_voltage(vddo, 1800000, 1800000);
if (retval)
dev_err(dev, "%s: unable to set vddo voltage to 1.8V\n",
__func__);
dev_err(dev, "%s: is enabled %s\n", __func__, regulator_is_enabled(vddo) ? "TRUE" : "FALSE");
}
if (enable) {
if (0/*regulator_is_enabled(avdd)*/) {
dev_err(dev, "%s: avdd is already enabled\n", __func__);
} else {
retval = regulator_enable(avdd);
if (retval) {
dev_err(dev, "%s: Fail to enable regulator avdd[%d]\n",
__func__, retval);
goto err;
}
dev_info(dev, "%s: avdd is enabled[OK]\n", __func__);
}
if (0/*regulator_is_enabled(vddo)*/) {
dev_err(dev, "%s: vddo is already enabled\n", __func__);
} else {
retval = regulator_enable(vddo);
if (retval) {
dev_err(dev, "%s: Fail to enable regulator vddo[%d]\n",
__func__, retval);
goto err;
}
dev_info(dev, "%s: vddo is enabled[OK]\n", __func__);
}
} else {
if (regulator_is_enabled(vddo)) {
retval = regulator_disable(vddo);
if (retval) {
dev_err(dev, "%s: Fail to disable regulator vddo[%d]\n",
__func__, retval);
goto err;
}
dev_info(dev, "%s: vddo is disabled[OK]\n", __func__);
} else {
dev_err(dev, "%s: vddo is already disabled\n", __func__);
}
if (regulator_is_enabled(avdd)) {
retval = regulator_disable(avdd);
if (retval) {
dev_err(dev, "%s: Fail to disable regulator avdd[%d]\n",
__func__, retval);
goto err;
}
dev_info(dev, "%s: avdd is disabled[OK]\n", __func__);
} else {
dev_err(dev, "%s: avdd is already disabled\n", __func__);
}
}
return 0;
err:
return retval;
}
#if defined(CONFIG_SECURE_TOUCH)
static void fts_secure_touch_init(struct fts_ts_info *info)
{
int ret;
dev_err(&info->client->dev, "%s: SECURETOUCH_INIT\n", __func__);
init_completion(&info->st_powerdown);
#ifdef ST_INT_COMPLETE
init_completion(&info->st_interrupt);
#endif
info->core_clk = clk_get(&info->client->dev, "core_clk");
if (IS_ERR(info->core_clk)) {
ret = PTR_ERR(info->core_clk);
dev_err(&info->client->dev, "%s: error on clk_get(core_clk):%d\n",
__func__, ret);
return;
}
info->iface_clk = clk_get(&info->client->dev, "iface_clk");
if (IS_ERR(info->iface_clk)) {
ret = PTR_ERR(info->core_clk);
dev_err(&info->client->dev, "%s: error on clk_get(iface_clk):%d\n",
__func__, ret);
goto err_iface_clk;
}
return;
err_iface_clk:
clk_put(info->core_clk);
info->core_clk = NULL;
}
#endif
#ifdef CONFIG_OF
static int fts_parse_dt(struct device *dev,
struct fts_device_tree_data *dt_data)
{
struct device_node *np = dev->of_node;
int rc;
int tsp_id, tsp_id2;
u32 coords[2];
/* irq gpio info */
dt_data->irq_gpio = of_get_named_gpio(np, "stm,irq-gpio", 0);
rc = of_property_read_u32_array(np, "stm,tsp-coords", coords, 2);
if (rc < 0) {
dev_info(dev, "%s: Unable to read stm,tsp-coords\n", __func__);
return rc;
}
dt_data->max_x = coords[0];
dt_data->max_y = coords[1];
/* tsp_id, tsp_id2 */
dt_data->tsp_id = of_get_named_gpio(np, "stm,tsp-id", 0);
dt_data->tsp_id2 = of_get_named_gpio(np, "stm,tsp-id2", 0);
/* check STM/LSI IC in HERO2 */
if (gpio_is_valid(dt_data->tsp_id) && gpio_is_valid(dt_data->tsp_id2)) {
rc = gpio_request(dt_data->tsp_id, "tsp_id");
if (rc)
pr_err("%s: unable to request tsp_id [%d], ret=%d\n",
__func__, dt_data->tsp_id, rc);
rc = gpio_request(dt_data->tsp_id2, "tsp_id2");
if (rc)
pr_err("%s: unable to request tsp_id [%d], ret=%d\n",
__func__, dt_data->tsp_id2, rc);
tsp_id = gpio_get_value(dt_data->tsp_id);
tsp_id2 = gpio_get_value(dt_data->tsp_id2);
/* tsp_id : 0 STM, 1 LSI
* tsp_id2 : 0 ALPS, 1 ILSIN */
if (tsp_id) {
dev_err(dev, "%s: Return error to use LSI IC\n", __func__);
return -ENODEV;
}
if (tsp_id2) {
dev_err(dev, "%s: Hero2 ILSIN module\n", __func__);
} else {
dev_err(dev, "%s: Hero2 ALPS module\n", __func__);
}
} else {
tsp_id2 = 0;
}
rc = of_property_read_string_index(np, "stm,firmware_name", tsp_id2, &dt_data->firmware_name);
if (rc) {
dt_data->firmware_name = NULL;
}
#ifdef FTS_SUPPORT_TOUCH_KEY
dt_data->support_mskey = of_property_read_bool(np, "stm,support_mskey");
dt_data->num_touchkey = 2;
dt_data->touchkey = fts_touchkeys;
if (dt_data->support_mskey)
dev_info(dev, "%s: Support touchkey\n", __func__);
#endif
/* project info */
rc = of_property_read_string(np, "stm,tsp-project", &dt_data->project);
if (rc < 0) {
dev_info(dev, "%s: Unable to read stm,tsp-project\n", __func__);
dt_data->project = "0";
}
/* model info */
rc = of_property_read_string(np, "stm,tsp-model", &dt_data->model);
if (rc < 0) {
dev_info(dev, "%s: Unable to read stm,tsp-model\n", __func__);
dt_data->model = FTS_DEVICE_NAME;
}
of_property_read_u32(np, "stm,support_gesture", &dt_data->support_sidegesture);
rc = of_property_read_u32(np, "stm,grip_area", &dt_data->grip_area);
if (rc < 0)
dt_data->grip_area = -1;
dt_data->support_string_lib = of_property_read_bool(np, "stm,string-lib");
rc = of_property_read_u32(np, "stm,bringup", &dt_data->bringup);
if (rc < 0)
dt_data->bringup = 0;
dev_err(dev, "%s: tsp_int= %d, X= %d, Y= %d, grip_area= %d, project= %s, gesture[%d], string[%d], bringup[%d], tsp_id=%d, tsp_id2=%d, fw_name=%s\n",
__func__, dt_data->irq_gpio,
dt_data->max_x, dt_data->max_y, dt_data->grip_area, dt_data->project,
dt_data->support_sidegesture, dt_data->support_string_lib, dt_data->bringup,
dt_data->tsp_id, dt_data->tsp_id2, dt_data->firmware_name);
return 0;
}
#else
static int fts_parse_dt(struct device *dev,
struct fts_device_tree_data *dt_data)
{
return -ENODEV;
}
#endif
static int fts_pinctrl_init(struct fts_ts_info *info)
{
struct i2c_client *client = info->client;
int ret;
/* Get pinctrl if target uses pinctrl */
info->pinctrl = devm_pinctrl_get(&client->dev);
if (IS_ERR(info->pinctrl)) {
if (PTR_ERR(info->pinctrl) == -EPROBE_DEFER) {
ret = -ENODEV;
return ret;
}
dev_err(&client->dev, "%s: Target does not use pinctrl\n", __func__);
info->pinctrl = NULL;
}
return 0;
}
static int fts_pinctrl_configure(struct fts_ts_info *info,
bool active)
{
struct pinctrl_state *set_state_i2c;
int retval;
if (!info->pinctrl) {
return 0;
}
dev_info(&info->client->dev, "%s: %s\n", __func__, active ? "ACTIVE" : "SUSPEND");
if (active) {
set_state_i2c =
pinctrl_lookup_state(info->pinctrl,
"tsp_active");
if (IS_ERR(set_state_i2c)) {
dev_err(&info->client->dev, "%s: cannot get active state\n", __func__);
return PTR_ERR(set_state_i2c);
}
} else {
set_state_i2c =
pinctrl_lookup_state(info->pinctrl,
"tsp_suspend");
if (IS_ERR(set_state_i2c)) {
dev_err(&info->client->dev, "%s: cannot get sleep state\n", __func__);
return PTR_ERR(set_state_i2c);
}
}
retval = pinctrl_select_state(info->pinctrl, set_state_i2c);
if (retval) {
dev_err(&info->client->dev, "%s: cannot set %s state\n",
__func__, active ? "active" : "suspend");
return retval;
}
gpio_direction_input(info->dt_data->irq_gpio);
return 0;
}
static void fts_request_gpio(struct fts_ts_info *info)
{
int ret;
dev_info(&info->client->dev, "%s\n", __func__);
ret = gpio_request(info->dt_data->irq_gpio, "stm,irq_gpio");
if (ret) {
pr_err("%s: unable to request irq_gpio [%d], ret=%d\n",
__func__, info->dt_data->irq_gpio, ret);
return;
}
#ifdef FTS_SUPPORT_TOUCH_KEY
if (info->dt_data->support_mskey) {
dev_info(&info->client->dev, "%s: vdd_keyled setting, %d\n", __func__, __LINE__);
info->keyled_vreg = regulator_get(&info->client->dev, "vdd_keyled");
if (IS_ERR(info->keyled_vreg)) {
pr_err("%s: failed vdd_keyled request error \n", __func__);
info->keyled_vreg = NULL;
return;
}
ret = regulator_set_voltage(info->keyled_vreg, 3300000, 3300000);
if (ret)
pr_err("%s: vdd_keyled 3.3V setting error\n", __func__);
}
#endif
}
static int fts_probe(struct i2c_client *client, const struct i2c_device_id *idp)
{
int retval;
struct fts_ts_info *info = NULL;
struct fts_device_tree_data *dt_data = NULL;
static char fts_ts_phys[64] = { 0 };
int i = 0;
int temp;
if (tsp_init_done) {
pr_err("%s: tsp already init done\n", __func__);
return -ENODEV;
}
tsp_debug_info(true, &client->dev, "%s\n", __func__);
if (!i2c_check_functionality(client->adapter, I2C_FUNC_I2C)) {
dev_err(&client->dev, "FTS err = EIO!\n");
return -EIO;
}
retval = fts_power_ctrl(client, true);
if (retval) {
dev_err(&client->dev,
"%s: Failed to power on: %d\n", __func__, retval);
goto err_power_on;
}
if (client->dev.of_node) {
dt_data = devm_kzalloc(&client->dev,
sizeof(struct fts_device_tree_data),
GFP_KERNEL);
if (!dt_data) {
dev_err(&client->dev, "Failed to allocate memory\n");
retval = -ENOMEM;
goto err_pdata;
}
retval = fts_parse_dt(&client->dev, dt_data);
if (retval)
goto err_pdata;
} else {
dt_data = client->dev.platform_data;
dev_err(&client->dev, "%s: TSP failed to align dtsi\n", __func__);
}
info = kzalloc(sizeof(struct fts_ts_info), GFP_KERNEL);
if (!info) {
dev_err(&client->dev, "FTS err = ENOMEM!\n");
retval = -ENOMEM;
goto err_pdata;
}
#ifdef FTS_SUPPORT_NOISE_PARAM
info->noise_param = kzalloc(sizeof(struct fts_noise_param), GFP_KERNEL);
if (!info->noise_param) {
dev_err(&info->client->dev, "%s: Failed to set noise param mem\n",
__func__);
retval = -ENOMEM;
goto err_alloc_noise_param;
}
#endif
info->client = client;
info->dt_data = dt_data;
#ifdef USE_OPEN_DWORK
INIT_DELAYED_WORK(&info->open_work, fts_open_work);
#endif
info->delay_time = 300;
#if defined(USE_RESET_WORK_EXIT_LOWPOWERMODE) || defined(FTS_SUPPORT_ESD_CHECK)
INIT_DELAYED_WORK(&info->reset_work, fts_reset_work);
#endif
fts_request_gpio(info);
retval = fts_pinctrl_init(info);
if (retval < 0) {
dev_err(&info->client->dev, "%s: Failed to get pinctrl\n",
__func__);
goto err_init_pinctrl;
}
fts_pinctrl_configure(info, true);
#ifdef TSP_BOOSTER
info->tsp_booster = input_booster_allocate(INPUT_BOOSTER_ID_TSP);
if (!info->tsp_booster) {
dev_err(&client->dev,
"%s: Failed to alloc mem for tsp_booster\n", __func__);
goto err_get_tsp_booster;
}
#endif
#ifdef TKEY_BOOSTER
info->tkey_booster = input_booster_allocate(INPUT_BOOSTER_ID_TKEY);
if (!info->tkey_booster) {
dev_err(&client->dev,
"%s: Failed to alloc mem for tkey_booster\n", __func__);
goto err_get_tkey_booster;
}
#endif
info->dev = &info->client->dev;
info->input_dev = input_allocate_device();
if (!info->input_dev) {
dev_info(&info->client->dev, "FTS err = ENOMEM!\n");
retval = -ENOMEM;
goto err_input_allocate_device;
}
info->input_dev->dev.parent = &client->dev;
info->input_dev->name = "sec_touchscreen";
snprintf(fts_ts_phys, sizeof(fts_ts_phys), "%s/input0",
info->input_dev->name);
info->input_dev->phys = fts_ts_phys;
info->input_dev->id.bustype = BUS_I2C;
info->irq = client->irq = gpio_to_irq(info->dt_data->irq_gpio);
dev_err(&info->client->dev, "%s: # irq : %d, gpio_to_irq[%d]\n",
__func__, info->irq, info->dt_data->irq_gpio);
temp = get_lcd_attached("GET");
info->lcd_id[2] = temp & 0xFF;
info->lcd_id[1] = (temp >> 8) & 0xFF;
info->lcd_id[0] = (temp >> 16) & 0xFF;
info->panel_revision = info->lcd_id[1] >> 4;
dev_err(&info->client->dev, "%s: lcd id : %06X[%02X][%02X][%02X]\n",
__func__, temp, info->lcd_id[0],
info->lcd_id[1], info->lcd_id[2]);
info->panel_revision = (info->lcd_id[1] >> 4) & 0xF;
info->irq_enabled = false;
info->touch_stopped = false;
info->stop_device = fts_stop_device;
info->start_device = fts_start_device;
info->fts_delay = fts_delay;
info->fts_command = fts_command;
info->fts_read_reg = fts_read_reg;
info->fts_write_reg = fts_write_reg;
info->fts_systemreset = fts_systemreset;
info->fts_get_version_info = fts_get_version_info;
info->fts_wait_for_ready = fts_wait_for_ready;
info->fts_power_ctrl = fts_power_ctrl;
info->fts_write_to_string = fts_write_to_string;
info->fts_read_from_string = fts_read_from_string;
info->fts_change_scan_rate = fts_change_scan_rate;
#ifdef FTS_SUPPORT_TOUCH_KEY
if (info->dt_data->support_mskey)
info->led_power = fts_led_power_ctrl;
#endif
#ifdef FTS_SUPPORT_NOISE_PARAM
info->fts_get_noise_param_address = fts_get_noise_param_address;
#endif
#ifdef USE_OPEN_CLOSE
info->input_dev->open = fts_input_open;
info->input_dev->close = fts_input_close;
#endif
#ifdef CONFIG_GLOVE_TOUCH
input_set_capability(info->input_dev, EV_SW, SW_GLOVE);
#endif
set_bit(EV_SYN, info->input_dev->evbit);
set_bit(EV_KEY, info->input_dev->evbit);
set_bit(EV_ABS, info->input_dev->evbit);
#ifdef INPUT_PROP_DIRECT
set_bit(INPUT_PROP_DIRECT, info->input_dev->propbit);
#endif
set_bit(BTN_TOUCH, info->input_dev->keybit);
set_bit(BTN_TOOL_FINGER, info->input_dev->keybit);
#ifdef FTS_SUPPORT_2NDSCREEN_FLAG
set_bit(BTN_SUBSCREEN_FLAG, info->input_dev->keybit);
#endif
#ifdef FTS_USE_SIDE_SCROLL_FLAG
set_bit(BTN_R_FLICK_FLAG, info->input_dev->keybit);
set_bit(BTN_L_FLICK_FLAG, info->input_dev->keybit);
#endif
#ifdef USE_STYLUS
input_set_abs_params(info->input_dev,
ABS_MT_TOOL_TYPE, 0,
MT_TOOL_MAX, 0, 0);
#endif
set_bit(KEY_BLACK_UI_GESTURE, info->input_dev->keybit);
#ifdef FTS_SUPPORT_TOUCH_KEY
if (info->dt_data->support_mskey) {
for (i = 0 ; i < info->dt_data->num_touchkey ; i++)
set_bit(info->dt_data->touchkey[i].keycode, info->input_dev->keybit);
set_bit(EV_LED, info->input_dev->evbit);
set_bit(LED_MISC, info->input_dev->ledbit);
}
#endif
#ifdef FTS_SUPPORT_SIDE_GESTURE
if (info->dt_data->support_sidegesture) {
set_bit(KEY_SIDE_GESTURE, info->input_dev->keybit);
set_bit(KEY_SIDE_GESTURE_RIGHT, info->input_dev->keybit);
set_bit(KEY_SIDE_GESTURE_LEFT, info->input_dev->keybit);
}
#endif
input_mt_init_slots(info->input_dev, FINGER_MAX, INPUT_MT_DIRECT);
input_set_abs_params(info->input_dev, ABS_MT_POSITION_X,
0, info->dt_data->max_x, 0, 0);
input_set_abs_params(info->input_dev, ABS_MT_POSITION_Y,
0, info->dt_data->max_y, 0, 0);
#if defined(CONFIG_SEC_FACTORY)
input_set_abs_params(info->input_dev, ABS_MT_PRESSURE,
0, 0xFF, 0, 0);
#endif
for (i = 0; i < FINGER_MAX; i++) {
info->finger[i].state = EVENTID_LEAVE_POINTER;
info->finger[i].mcount = 0;
}
mutex_init(&(info->device_mutex));
mutex_init(&info->i2c_mutex);
wake_lock_init(&info->wakelock, WAKE_LOCK_SUSPEND, "fts_wakelock");
init_completion(&info->resume_done);
irq_set_status_flags(info->irq, IRQ_NOAUTOEN);
retval = request_threaded_irq(info->irq, NULL,
fts_interrupt_handler, IRQF_TRIGGER_LOW | IRQF_ONESHOT,
FTS_TS_DRV_NAME, info);
if (retval < 0) {
dev_info(&info->client->dev,
"%s: Failed to create irq thread %d\n",
__func__, retval);
goto err_request_irq;
}
retval = fts_init(info);
if (retval < 0) {
dev_err(&info->client->dev, "FTS fts_init fail!\n");
goto err_fts_init;
}
info->reinit_done = true;
/* temporary code : revXX use old pannel */
if (info->stm_ver != STM_VER7) {
input_set_abs_params(info->input_dev, ABS_MT_POSITION_X,
0, 4096 - 1, 0, 0);
input_set_abs_params(info->input_dev, ABS_MT_POSITION_Y,
0, 4096 - 1, 0, 0);
}
input_set_abs_params(info->input_dev, ABS_MT_TOUCH_MAJOR,
0, 255, 0, 0);
input_set_abs_params(info->input_dev, ABS_MT_TOUCH_MINOR,
0, 255, 0, 0);
input_set_abs_params(info->input_dev, ABS_MT_PALM, 0, 1, 0, 0);
#if defined(FTS_SUPPORT_SIDE_GESTURE)
input_set_abs_params(info->input_dev, ABS_MT_GRIP, 0, 1, 0, 0);
#endif
input_set_abs_params(info->input_dev, ABS_MT_DISTANCE,
0, 255, 0, 0);
input_set_drvdata(info->input_dev, info);
i2c_set_clientdata(client, info);
retval = input_register_device(info->input_dev);
if (retval) {
dev_err(&info->client->dev, "FTS input_register_device fail!\n");
goto err_register_input;
}
#ifdef FTS_SUPPORT_TA_MODE
info->register_cb = info->register_cb;
info->callbacks.inform_charger = fts_ta_cb;
if (info->register_cb)
info->register_cb(&info->callbacks);
#endif
#ifdef SEC_TSP_FACTORY_TEST
retval = sec_cmd_init(&info->sec, stm_ft_cmds,
ARRAY_SIZE(stm_ft_cmds), SEC_CLASS_DEVT_TSP);
if (retval < 0) {
tsp_debug_err(true, &info->client->dev,
"%s: Failed to sec_cmd_init\n", __func__);
return -ENODEV;
}
retval = sysfs_create_group(&info->sec.fac_dev->kobj,
&sec_touch_factory_attr_group);
if (retval < 0) {
tsp_debug_err(true, &info->client->dev,
"%s: FTS Failed to create sysfs group\n", __func__);
goto err_sysfs;
}
#endif
retval = sysfs_create_link(&info->sec.fac_dev->kobj,
&info->input_dev->dev.kobj, "input");
if (retval < 0) {
tsp_debug_err(true, &info->client->dev,
"%s: Failed to create input symbolic link\n",
__func__);
}
#ifdef FTS_SUPPORT_TOUCH_KEY
if (info->dt_data->support_mskey) {
info->fac_dev_tk = device_create(sec_class, NULL, 0, info, "sec_touchkey");
if (IS_ERR(info->fac_dev_tk))
tsp_debug_err(true, &info->client->dev, "Failed to create device for the touchkey sysfs\n");
else {
dev_set_drvdata(info->fac_dev_tk, info);
retval = sysfs_create_group(&info->fac_dev_tk->kobj,
&sec_touchkey_factory_attr_group);
if (retval < 0) {
tsp_debug_err(true, &info->client->dev, "FTS Failed to create sysfs group\n");
} else {
retval = sysfs_create_link(&info->fac_dev_tk->kobj,
&info->input_dev->dev.kobj, "input");
if (retval < 0)
tsp_debug_err(true, &info->client->dev,
"%s: Failed to create link\n", __func__);
}
}
}
#endif
#if defined(CONFIG_SECURE_TOUCH)
for (i = 0; i < (int)ARRAY_SIZE(attrs); i++) {
retval = sysfs_create_file(&info->input_dev->dev.kobj,
&attrs[i].attr);
if (retval < 0) {
dev_err(&info->client->dev,
"%s: Failed to create sysfs attributes\n",
__func__);
}
}
fts_secure_touch_init(info);
#endif
device_init_wakeup(&client->dev, 1);
#ifdef CONFIG_TOUCHSCREEN_DUMP_MODE
dump_callbacks.inform_dump = dump_tsp_log;
INIT_DELAYED_WORK(&info->ghost_check, fts_ts_check_rawdata);
p_ghost_check = &info->ghost_check;
#endif
tsp_init_done = true;
dev_err(&info->client->dev, "%s done\n", __func__);
return 0;
#ifdef SEC_TSP_FACTORY_TEST
sysfs_remove_group(&info->sec.fac_dev->kobj,
&sec_touch_factory_attr_group);
err_sysfs:
sec_cmd_exit(&info->sec, SEC_CLASS_DEVT_TSP);
#endif
input_unregister_device(info->input_dev);
info->input_dev = NULL;
err_register_input:
if (info->input_dev)
input_free_device(info->input_dev);
err_fts_init:
#ifdef SEC_TSP_FACTORY_TEST
kfree(info->cx_data);
kfree(info->pFrame);
#endif
fts_irq_enable(info, false);
irq_clear_status_flags(info->irq, IRQ_NOAUTOEN);
free_irq(info->irq, info);
err_request_irq:
wake_lock_destroy(&info->wakelock);
err_input_allocate_device:
#ifdef TSP_BOOSTER
input_booster_free(info->tsp_booster);
info->tsp_booster = NULL;
err_get_tsp_booster:
#endif
#ifdef TKEY_BOOSTER
input_booster_free(info->tkey_booster);
info->tkey_booster = NULL;
err_get_tkey_booster:
#endif
#ifdef CONFIG_TOUCHSCREEN_DUMP_MODE
p_ghost_check = NULL;
#endif
info->pinctrl = NULL;
err_init_pinctrl:
gpio_free(info->dt_data->irq_gpio);
#ifdef FTS_SUPPORT_NOISE_PARAM
kfree(info->noise_param);
err_alloc_noise_param:
#endif
kfree(info);
err_pdata:
if (dt_data) {
if (gpio_is_valid(dt_data->tsp_id))
gpio_free(dt_data->tsp_id);
if (gpio_is_valid(dt_data->tsp_id2))
gpio_free(dt_data->tsp_id2);
}
fts_power_ctrl(client, false);
err_power_on:
pr_err("[TSP] %s failed, rc=%d\n", __func__, retval);
return retval;
}
static int fts_remove(struct i2c_client *client)
{
struct fts_ts_info *info = i2c_get_clientdata(client);
#if defined(CONFIG_SECURE_TOUCH)
int i = 0;
#endif
dev_info(&info->client->dev, "FTS removed\n");
fts_interrupt_set(info, INT_DISABLE);
fts_command(info, FLUSHBUFFER);
fts_irq_enable(info, false);
free_irq(info->irq, info);
#ifdef CONFIG_TOUCHSCREEN_DUMP_MODE
p_ghost_check = NULL;
#endif
#if defined(CONFIG_SECURE_TOUCH)
for (i = 0; i < (int)ARRAY_SIZE(attrs); i++) {
sysfs_remove_file(&info->input_dev->dev.kobj,
&attrs[i].attr);
}
#endif
input_mt_destroy_slots(info->input_dev);
#ifdef SEC_TSP_FACTORY_TEST
sysfs_remove_group(&info->sec.fac_dev->kobj,
&sec_touch_factory_attr_group);
sec_cmd_exit(&info->sec, SEC_CLASS_DEVT_TSP);
kfree(info->cx_data);
kfree(info->pFrame);
#endif
wake_lock_destroy(&info->wakelock);
input_unregister_device(info->input_dev);
info->input_dev = NULL;
#ifdef TSP_BOOSTER
input_booster_free(info->tsp_booster);
info->tsp_booster = NULL;
#endif
#ifdef TKEY_BOOSTER
input_booster_free(info->tkey_booster);
info->tkey_booster = NULL;
#endif
fts_power_ctrl(client, false);
#ifdef FTS_SUPPORT_TOUCH_KEY
if (info->dt_data->support_mskey)
fts_led_power_ctrl(info, false);
#endif
kfree(info);
return 0;
}
static void fts_shutdown(struct i2c_client *client)
{
struct fts_ts_info *info = i2c_get_clientdata(client);
dev_info(&info->client->dev, "%s\n", __func__);
fts_remove(client);
}
static void fts_recovery_cx(struct fts_ts_info *info)
{
unsigned char regAdd[4] = {0};
unsigned char buf[8] = {0};
unsigned char cnt = 100;
regAdd[0] = 0xB6;
regAdd[1] = 0x00;
regAdd[2] = 0x1E;
regAdd[3] = 0x08;
/* Loading FW to PRAM without CRC Check */
fts_write_reg(info,&regAdd[0], 4);
fts_delay(30);
fts_command(info,CX_TUNNING);
fts_delay(300);
fts_command(info,FTS_CMD_SAVE_CX_TUNING);
fts_delay(200);
do {
int ret;
regAdd[0] = READ_ONE_EVENT;
ret = fts_read_reg(info, regAdd, 1, &buf[0], FTS_EVENT_SIZE);
fts_delay(10);
if (cnt-- == 0) break;
} while (buf[0] != 0x16 || buf[1] != 0x04);
if (info->stm_ver != STM_VER7) {
fts_command(info, SLEEPOUT);
fts_delay(50);
}
fts_command(info, FLUSHBUFFER);
fts_delay(10);
fts_command(info, SENSEON);
fts_delay(50);
#ifdef FTS_SUPPORT_TOUCH_KEY
if (info->dt_data->support_mskey)
fts_command(info, FTS_CMD_KEY_SENSE_ON);
#endif
}
#ifdef USE_OPEN_CLOSE
#ifdef USE_OPEN_DWORK
static void fts_open_work(struct work_struct *work)
{
int retval;
struct fts_ts_info *info = container_of(work, struct fts_ts_info,
open_work.work);
dev_info(&info->client->dev, "%s\n", __func__);
retval = fts_start_device(info);
if (retval < 0)
dev_err(&info->client->dev,
"%s: Failed to start device\n", __func__);
}
#endif
static int fts_input_open(struct input_dev *dev)
{
struct fts_ts_info *info = input_get_drvdata(dev);
dev_info(&info->client->dev, "%s\n", __func__);
#ifdef USE_OPEN_DWORK
schedule_delayed_work(&info->open_work,
msecs_to_jiffies(TOUCH_OPEN_DWORK_TIME));
#else
fts_start_device(info);
#endif
#ifdef FTS_SUPPORT_HOVER
/* Hover feature is not using anymore */
tsp_debug_err(true, &info->client->dev, "FTS cmd after wakeup : h%d\n",
info->retry_hover_enable_after_wakeup);
if(info->retry_hover_enable_after_wakeup == 1){
unsigned char regAdd[4] = {0xB0, 0x01, 0x29, 0x41};
fts_write_reg(info, &regAdd[0], 4);
fts_command(info, FTS_CMD_HOVER_ON);
info->hover_ready = false;
info->hover_enabled = true;
}
#endif
return 0;
}
static void fts_input_close(struct input_dev *dev)
{
struct fts_ts_info *info = input_get_drvdata(dev);
dev_info(&info->client->dev, "%s\n", __func__);
#ifdef USE_OPEN_DWORK
cancel_delayed_work(&info->open_work);
#endif
fts_stop_device(info);
#ifdef FTS_SUPPORT_HOVER
info->retry_hover_enable_after_wakeup = 0;
#endif
}
#endif
#ifdef CONFIG_SEC_FACTORY
#include <linux/uaccess.h>
#define LCD_LDI_FILE_PATH "/sys/class/lcd/panel/window_type"
static int fts_get_panel_revision(struct fts_ts_info *info)
{
int iRet = 0;
mm_segment_t old_fs;
struct file *window_type;
unsigned char lcdtype[4] = {0,};
old_fs = get_fs();
set_fs(KERNEL_DS);
window_type = filp_open(LCD_LDI_FILE_PATH, O_RDONLY, 0666);
if (IS_ERR(window_type)) {
iRet = PTR_ERR(window_type);
if (iRet != -ENOENT)
dev_err(&info->client->dev, "%s: window_type file open fail\n", __func__);
set_fs(old_fs);
goto exit;
}
iRet = window_type->f_op->read(window_type, (u8 *)lcdtype, sizeof(u8) * 4, &window_type->f_pos);
if (iRet != (sizeof(u8) * 4)) {
dev_err(&info->client->dev, "%s: Can't read the lcd ldi data\n", __func__);
iRet = -EIO;
}
/* The variable of lcdtype has ASCII values(40 81 45) at 0x08 OCTA,
* so if someone need a TSP panel revision then to read third parameter.*/
info->panel_revision = lcdtype[3] & 0x0F;
dev_info(&info->client->dev,
"%s: update panel_revision 0x%02X\n", __func__, info->panel_revision);
filp_close(window_type, current->files);
set_fs(old_fs);
exit:
return iRet;
}
static void fts_reinit_fac(struct fts_ts_info *info)
{
int rc;
info->touch_count = 0;
if (info->stm_ver != STM_VER7) {
fts_command(info, SLEEPOUT);
fts_delay(50);
}
fts_command(info, FLUSHBUFFER);
fts_delay(10);
fts_command(info, SENSEON);
fts_delay(50);
#ifdef FTS_SUPPORT_TOUCH_KEY
if (info->dt_data->support_mskey)
info->fts_command(info, FTS_CMD_KEY_SENSE_ON);
#endif
#ifdef FTS_SUPPORT_NOISE_PARAM
fts_get_noise_param_address(info);
#endif
if (info->flip_enable)
fts_set_cover_type(info, true);
#ifdef CONFIG_GLOVE_TOUCH
/* enable glove touch when flip cover is closed */
if (info->fast_glove_enabled)
fts_command(info, FTS_CMD_SET_FAST_GLOVE_MODE);
else if (info->glove_enabled)
fts_command(info, FTS_CMD_GLOVE_MODE_ON);
#endif
rc = fts_get_channel_info(info);
if (rc >= 0) {
dev_info(&info->client->dev, "FTS Sense(%02d) Force(%02d)\n",
info->SenseChannelLength, info->ForceChannelLength);
} else {
dev_info(&info->client->dev, "FTS read failed rc = %d\n", rc);
dev_info(&info->client->dev, "FTS Initialise Failed\n");
return;
}
if (info->pFrame)
kfree(info->pFrame);
info->pFrame = kzalloc(info->SenseChannelLength * info->ForceChannelLength * 2, GFP_KERNEL);
if (info->cx_data)
kfree(info->cx_data);
info->cx_data = kzalloc(info->SenseChannelLength * info->ForceChannelLength, GFP_KERNEL);
if (!info->pFrame || !info->cx_data) {
dev_info(&info->client->dev, "mem alloc failed\n");
return;
}
fts_command(info, FORCECALIBRATION);
fts_interrupt_set(info, INT_ENABLE);
dev_info(&info->client->dev, "FTS Re-Initialised\n");
}
#endif
static void fts_reinit(struct fts_ts_info *info)
{
if (!info->lowpower_mode) {
fts_wait_for_ready(info);
fts_read_chip_id(info);
}
fts_systemreset(info);
fts_wait_for_ready(info);
#ifdef CONFIG_SEC_FACTORY
/* Read firmware version from IC when every power up IC.
* During Factory process touch panel can be changed manually.
*/
{
unsigned short orig_fw_main_version_of_ic = info->fw_main_version_of_ic;
fts_get_panel_revision(info);
fts_get_version_info(info);
if (info->fw_main_version_of_ic != orig_fw_main_version_of_ic) {
fts_fw_init(info);
fts_reinit_fac(info);
return;
}
}
#endif
#ifdef FTS_SUPPORT_NOISE_PARAM
fts_set_noise_param(info);
#endif
if (info->stm_ver != STM_VER7) {
fts_command(info, SLEEPOUT);
fts_delay(50);
}
fts_command(info, FLUSHBUFFER);
fts_delay(10);
fts_command(info, SENSEON);
fts_delay(50);
#ifdef FTS_SUPPORT_TOUCH_KEY
if (info->dt_data->support_mskey)
info->fts_command(info, FTS_CMD_KEY_SENSE_ON);
#endif
if (info->flip_enable)
fts_set_cover_type(info, true);
#ifdef CONFIG_GLOVE_TOUCH
/* enable glove touch when flip cover is closed */
if (info->fast_glove_enabled)
fts_command(info, FTS_CMD_SET_FAST_GLOVE_MODE);
else if (info->glove_enabled)
fts_command(info, FTS_CMD_GLOVE_MODE_ON);
#endif
#ifdef FTS_SUPPORT_TA_MODE
if (info->TA_Pluged)
fts_command(info, FTS_CMD_CHARGER_PLUGGED);
#endif
info->touch_count = 0;
#ifdef FTS_SUPPORT_2NDSCREEN_FLAG
info->SIDE_Flag = 0;
info->previous_SIDE_value = 0;
#endif
fts_interrupt_set(info, INT_ENABLE);
}
static void fts_release_all_finger(struct fts_ts_info *info)
{
int i;
if (!tsp_init_done)
return;
for (i = 0; i < FINGER_MAX; i++) {
input_mt_slot(info->input_dev, i);
input_mt_report_slot_state(info->input_dev, MT_TOOL_FINGER, 0);
if ((info->finger[i].state == EVENTID_ENTER_POINTER) ||
(info->finger[i].state == EVENTID_MOTION_POINTER)) {
info->touch_count--;
if (info->touch_count < 0)
info->touch_count = 0;
dev_info(&info->client->dev,
"[R] tID:%d mc: %d tc:%d Ver[%02X%04X%01X%01X%01X]\n",
i, info->finger[i].mcount, info->touch_count,
info->panel_revision, info->fw_main_version_of_ic,
info->flip_enable, info->glove_enabled, info->mainscr_disable);
}
info->finger[i].state = EVENTID_LEAVE_POINTER;
info->finger[i].mcount = 0;
}
input_report_key(info->input_dev, BTN_TOUCH, 0);
input_report_key(info->input_dev, BTN_TOOL_FINGER, 0);
#ifdef FTS_SUPPORT_2NDSCREEN_FLAG
info->SIDE_Flag = 0;
info->previous_SIDE_value = 0;
input_report_key(info->input_dev, BTN_SUBSCREEN_FLAG, 0);
#endif
#ifdef FTS_USE_SIDE_SCROLL_FLAG
input_report_key(info->input_dev, BTN_R_FLICK_FLAG, 0);
input_report_key(info->input_dev, BTN_L_FLICK_FLAG, 0);
#endif
#ifdef CONFIG_GLOVE_TOUCH
input_report_switch(info->input_dev, SW_GLOVE, false);
info->touch_mode = FTS_TM_NORMAL;
#endif
input_report_key(info->input_dev, KEY_BLACK_UI_GESTURE, 0);
#ifdef FTS_SUPPORT_SIDE_GESTURE
if (info->dt_data->support_sidegesture) {
input_report_key(info->input_dev, KEY_SIDE_GESTURE, 0);
input_report_key(info->input_dev, KEY_SIDE_GESTURE_RIGHT, 0);
input_report_key(info->input_dev, KEY_SIDE_GESTURE_LEFT, 0);
}
#endif
input_sync(info->input_dev);
input_mt_slot(info->input_dev, 0);
#ifdef TSP_BOOSTER
if (info->tsp_booster && info->tsp_booster->dvfs_set)
info->tsp_booster->dvfs_set(info->tsp_booster, -1);
#endif
}
#if defined(CONFIG_SECURE_TOUCH)
static void fts_secure_touch_stop(struct fts_ts_info *info, int blocking)
{
dev_err(&info->client->dev, "%s: SECURETOUCH_STOP\n", __func__);
if (atomic_read(&info->st_enabled)) {
atomic_set(&info->st_pending_irqs, -1);
fts_secure_touch_notify(info);
if (blocking)
wait_for_completion_interruptible(&info->st_powerdown);
}
}
#endif
static int fts_stop_device(struct fts_ts_info *info)
{
dev_info(&info->client->dev, "%s %s\n",
__func__, info->lowpower_mode ? "enter low power mode" : "");
#if defined(CONFIG_SECURE_TOUCH)
fts_secure_touch_stop(info, 1);
#endif
mutex_lock(&info->device_mutex);
if (info->touch_stopped) {
dev_err(&info->client->dev, "%s already power off\n", __func__);
goto out;
}
if (info->lowpower_mode) {
#ifdef FTS_ADDED_RESETCODE_IN_LPLM
info->mainscr_disable = false;
info->edge_grip_mode = false;
#else // clear cmd list.
#ifdef FTS_SUPPORT_MAINSCREEN_DISBLE
dev_info(&info->client->dev, "%s mainscreen disebla flag:%d, clear 0\n", __func__, info->mainscr_disable);
set_mainscreen_disable_cmd((void *)info, 0);
#endif
if (info->edge_grip_mode == false) {
dev_info(&info->client->dev, "%s edge grip enable flag:%d, clear 1\n", __func__, info->edge_grip_mode);
longpress_grip_enable_mode(info, 1); // default
grip_check_enable_mode(info, 1); // default
}
#endif
dev_info(&info->client->dev, "%s lowpower flag:%d\n", __func__, info->lowpower_flag);
info->power_state = STATE_LOWPOWER_RESUME;
if (info->stm_ver != STM_VER7) {
if (info->wakeful_edge_side == EDGEWAKE_LEFT) {
fts_enable_feature(info, 0x0F, true); /* left - C1, 0F*/
} else {
fts_enable_feature(info, 0x0F, false); /* right - C2, 0F */
}
fts_delay(10);
}
fts_command(info, FLUSHBUFFER);
fts_delay(10);
#ifdef FTS_SUPPORT_SIDE_GESTURE
if (info->dt_data->support_sidegesture) {
fts_enable_feature(info, FTS_FEATURE_SIDE_GESTURE, true);
fts_delay(20);
}
#endif
if (info->dt_data->support_string_lib && info->fts_mode)
fts_enable_feature(info, 0x0B, true);
fts_command(info, FTS_CMD_LOWPOWER_MODE);
if (device_may_wakeup(&info->client->dev))
enable_irq_wake(info->client->irq);
fts_command(info, FLUSHBUFFER);
fts_delay(10);
fts_release_all_finger(info);
#ifdef FTS_SUPPORT_TOUCH_KEY
if (info->dt_data->support_mskey)
fts_release_all_key(info);
#endif
#ifdef FTS_SUPPORT_NOISE_PARAM
fts_get_noise_param(info);
#endif
} else {
fts_interrupt_set(info, INT_DISABLE);
fts_irq_enable(info, false);
fts_command(info, FLUSHBUFFER);
fts_delay(10);
fts_command(info, SLEEPIN);
fts_release_all_finger(info);
#ifdef FTS_SUPPORT_TOUCH_KEY
if (info->dt_data->support_mskey)
fts_release_all_key(info);
#endif
#ifdef FTS_SUPPORT_NOISE_PARAM
fts_get_noise_param(info);
#endif
info->touch_stopped = true;
mutex_lock(&info->i2c_mutex);
fts_power_ctrl(info->client, false);
mutex_unlock(&info->i2c_mutex);
info->power_state = STATE_POWEROFF;
}
fts_pinctrl_configure(info, false);
out:
mutex_unlock(&info->device_mutex);
return 0;
}
static int fts_start_device(struct fts_ts_info *info)
{
dev_info(&info->client->dev, "%s %s\n",
__func__, info->lowpower_mode ? "exit low power mode" : "");
#if defined(CONFIG_SECURE_TOUCH)
fts_secure_touch_stop(info, 1);
#endif
mutex_lock(&info->device_mutex);
if (!info->touch_stopped && !info->lowpower_mode) {
dev_err(&info->client->dev, "%s already power on\n", __func__);
goto out;
}
fts_release_all_finger(info);
#ifdef FTS_SUPPORT_TOUCH_KEY
if (info->dt_data->support_mskey)
fts_release_all_key(info);
#endif
fts_pinctrl_configure(info, true);
info->power_state = STATE_ACTIVE;
if (info->lowpower_mode) {
/* low power mode command is sent after LCD OFF. turn on touch power @ LCD ON */
if (info->touch_stopped)
goto tsp_power_on;
if (device_may_wakeup(&info->client->dev))
disable_irq_wake(info->client->irq);
#ifdef USE_RESET_WORK_EXIT_LOWPOWERMODE
schedule_work(&info->reset_work.work);
goto out;
#endif
fts_irq_enable(info, false);
info->reinit_done = false;
fts_reinit(info);
info->reinit_done = true;
fts_irq_enable(info, true);
} else {
tsp_power_on:
fts_power_ctrl(info->client, true);
fts_delay(10);
info->touch_stopped = false;
info->reinit_done = false;
fts_reinit(info);
info->reinit_done = true;
if (info->flip_state != info->flip_enable) {
dev_err(&info->client->dev, "%s: not equal cover state.(%d, %d)\n",
__func__, info->flip_state, info->flip_enable);
fts_set_cover_type(info, info->flip_enable);
}
fts_irq_enable(info, true);
}
if (info->dt_data->support_string_lib && info->fts_mode) {
unsigned short string_addr = FTS_CMD_STRING_ACCESS;
int ret;
ret = info->fts_write_to_string(info, &string_addr, &info->fts_mode, sizeof(info->fts_mode));
if (ret < 0)
dev_err(&info->client->dev, "%s: failed. ret: %d\n", __func__, ret);
}
out:
mutex_unlock(&info->device_mutex);
return 0;
}
#if defined(USE_RESET_WORK_EXIT_LOWPOWERMODE) || defined(FTS_SUPPORT_ESD_CHECK)
static void fts_reset_work(struct work_struct *work)
{
struct fts_ts_info *info = container_of(work, struct fts_ts_info,
reset_work.work);
unsigned char orig_state;
bool orig_lowpower;
orig_state = info->power_state;
orig_lowpower = info->lowpower_mode;
info->lowpower_mode = FTS_DISABLE;
dev_info(&info->client->dev, "%s, reset IC off, lpm:%d\n", __func__, orig_lowpower);
fts_stop_device(info);
msleep(100);
info->lowpower_mode = orig_lowpower;
dev_dbg(&info->client->dev, "%s, reset IC on\n", __func__);
if (fts_start_device(info) < 0) {
dev_err(&info->client->dev,
"%s: Failed to start device\n", __func__);
}
if (orig_state & STATE_LOWPOWER)
fts_stop_device(info);
}
#endif
#ifdef CONFIG_PM
static int fts_ts_suspend(struct device *dev)
{
struct fts_ts_info *info = dev_get_drvdata(dev);
if (info->lowpower_mode)
reinit_completion(&info->resume_done);
info->power_state = STATE_LOWPOWER_SUSPEND;
return 0;
}
static int fts_ts_resume(struct device *dev)
{
struct fts_ts_info *info = dev_get_drvdata(dev);
if (info->lowpower_mode)
complete_all(&info->resume_done);
info->power_state = STATE_LOWPOWER_RESUME;
return 0;
}
static const struct dev_pm_ops fts_ts_pm_ops = {
.suspend = fts_ts_suspend,
.resume = fts_ts_resume,
};
#endif
static const struct i2c_device_id fts_device_id[] = {
{FTS_TS_DRV_NAME, 0},
{}
};
#ifdef CONFIG_OF
static struct of_device_id fts_match_table[] = {
{ .compatible = "stm,fts_ts",},
{ },
};
#else
#define fts_match_table NULL
#endif
static struct i2c_driver fts_i2c_driver = {
.driver = {
.name = FTS_TS_DRV_NAME,
.owner = THIS_MODULE,
#ifdef CONFIG_OF
.of_match_table = fts_match_table,
#endif
#ifdef CONFIG_PM
.pm = &fts_ts_pm_ops,
#endif
},
.probe = fts_probe,
.remove = fts_remove,
.shutdown = fts_shutdown,
.id_table = fts_device_id,
};
static int __init fts_driver_init(void)
{
pr_err("%s\n", __func__);
#ifdef CONFIG_SAMSUNG_LPM_MODE
if (poweroff_charging) {
pr_err("%s : LPM Charging Mode!!\n", __func__);
return 0;
}
#endif
return i2c_add_driver(&fts_i2c_driver);
}
static void __exit fts_driver_exit(void)
{
i2c_del_driver(&fts_i2c_driver);
}
MODULE_DESCRIPTION("STMicroelectronics MultiTouch IC Driver");
MODULE_AUTHOR("STMicroelectronics, Inc.");
MODULE_LICENSE("GPL v2");
module_init(fts_driver_init);
module_exit(fts_driver_exit);