mirror of
https://github.com/AetherDroid/android_kernel_samsung_on5xelte.git
synced 2025-09-08 09:08:05 -04:00
2815 lines
78 KiB
C
2815 lines
78 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/of_gpio.h>
|
|
|
|
#ifdef CONFIG_TRUSTONIC_TRUSTED_UI
|
|
#include <linux/trustedui.h>
|
|
#endif
|
|
|
|
#define SCAILE_FOR_LSI
|
|
|
|
#ifdef CONFIG_OF
|
|
#ifndef USE_OPEN_CLOSE
|
|
#define USE_OPEN_CLOSE
|
|
#undef CONFIG_HAS_EARLYSUSPEND
|
|
#undef CONFIG_PM
|
|
#endif
|
|
#endif
|
|
|
|
#ifdef CONFIG_HAS_EARLYSUSPEND
|
|
#include <linux/earlysuspend.h>
|
|
#endif
|
|
#include <linux/input/mt.h>
|
|
#include "fts_ts.h"
|
|
|
|
static struct i2c_driver fts_i2c_driver;
|
|
|
|
#ifdef FTS_SUPPORT_TOUCH_KEY
|
|
struct fts_touchkey fts_touchkeys[] = {
|
|
{
|
|
.value = 0x01,
|
|
.keycode = KEY_RECENT,
|
|
.name = "recent",
|
|
},
|
|
{
|
|
.value = 0x02,
|
|
.keycode = KEY_BACK,
|
|
.name = "back",
|
|
},
|
|
};
|
|
#endif
|
|
|
|
#ifdef CONFIG_GLOVE_TOUCH
|
|
enum TOUCH_MODE {
|
|
FTS_TM_NORMAL = 0,
|
|
FTS_TM_GLOVE,
|
|
};
|
|
#endif
|
|
|
|
#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
|
|
|
|
static int fts_stop_device(struct fts_ts_info *info);
|
|
static int fts_start_device(struct fts_ts_info *info);
|
|
static int fts_irq_enable(struct fts_ts_info *info, bool enable);
|
|
static void fts_reset_work(struct work_struct *work);
|
|
void fts_release_all_finger(struct fts_ts_info *info);
|
|
|
|
#ifdef CONFIG_SEC_DEBUG_TSP_LOG
|
|
static void dump_tsp_rawdata(struct work_struct *work);
|
|
struct delayed_work * p_debug_work;
|
|
#endif
|
|
|
|
#if (!defined(CONFIG_HAS_EARLYSUSPEND)) && (!defined(CONFIG_PM)) && !defined(USE_OPEN_CLOSE)
|
|
static int fts_suspend(struct i2c_client *client, pm_message_t mesg);
|
|
static int fts_resume(struct i2c_client *client);
|
|
#endif
|
|
|
|
#ifdef CONFIG_HAS_EARLYSUSPEND
|
|
static void fts_early_suspend(struct early_suspend *h)
|
|
{
|
|
struct fts_ts_info *info;
|
|
info = container_of(h, struct fts_ts_info, early_suspend);
|
|
fts_suspend(info->client, PMSG_SUSPEND);
|
|
}
|
|
|
|
static void fts_late_resume(struct early_suspend *h)
|
|
{
|
|
struct fts_ts_info *info;
|
|
info = container_of(h, struct fts_ts_info, early_suspend);
|
|
fts_resume(info->client);
|
|
}
|
|
#endif
|
|
|
|
int fts_write_reg(struct fts_ts_info *info,
|
|
unsigned char *reg, unsigned short num_com)
|
|
{
|
|
struct i2c_msg xfer_msg[2];
|
|
int ret;
|
|
|
|
if (info->touch_stopped) {
|
|
tsp_debug_err(true, &info->client->dev, "%s: Sensor stopped\n", __func__);
|
|
goto exit;
|
|
}
|
|
|
|
#ifdef CONFIG_TRUSTONIC_TRUSTED_UI
|
|
if (TRUSTEDUI_MODE_INPUT_SECURED & trustedui_get_current_mode()) {
|
|
tsp_debug_err(true, &info->client->dev,
|
|
"%s TSP no accessible from Linux, TUI is enabled!\n", __func__);
|
|
return -EIO;
|
|
}
|
|
#endif
|
|
|
|
mutex_lock(&info->i2c_mutex);
|
|
|
|
xfer_msg[0].addr = info->client->addr;
|
|
xfer_msg[0].len = num_com;
|
|
xfer_msg[0].flags = 0;
|
|
xfer_msg[0].buf = reg;
|
|
|
|
ret = i2c_transfer(info->client->adapter, xfer_msg, 1);
|
|
|
|
mutex_unlock(&info->i2c_mutex);
|
|
return ret;
|
|
|
|
exit:
|
|
return 0;
|
|
}
|
|
|
|
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];
|
|
int ret;
|
|
|
|
if (info->touch_stopped) {
|
|
tsp_debug_err(true, &info->client->dev, "%s: Sensor stopped\n", __func__);
|
|
goto exit;
|
|
}
|
|
|
|
#ifdef CONFIG_TRUSTONIC_TRUSTED_UI
|
|
if (TRUSTEDUI_MODE_INPUT_SECURED & trustedui_get_current_mode()) {
|
|
tsp_debug_err(true, &info->client->dev,
|
|
"%s TSP no accessible from Linux, TUI is enabled!\n", __func__);
|
|
return -EIO;
|
|
}
|
|
#endif
|
|
|
|
mutex_lock(&info->i2c_mutex);
|
|
|
|
xfer_msg[0].addr = info->client->addr;
|
|
xfer_msg[0].len = cnum;
|
|
xfer_msg[0].flags = 0;
|
|
xfer_msg[0].buf = reg;
|
|
|
|
xfer_msg[1].addr = info->client->addr;
|
|
xfer_msg[1].len = num;
|
|
xfer_msg[1].flags = I2C_M_RD;
|
|
xfer_msg[1].buf = buf;
|
|
ret = i2c_transfer(info->client->adapter, xfer_msg, 2);
|
|
|
|
mutex_unlock(&info->i2c_mutex);
|
|
return ret;
|
|
|
|
exit:
|
|
return 0;
|
|
}
|
|
|
|
#ifdef FTS_SUPPORT_STRINGLIB
|
|
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;
|
|
|
|
#ifdef CONFIG_TRUSTONIC_TRUSTED_UI
|
|
if (TRUSTEDUI_MODE_INPUT_SECURED & trustedui_get_current_mode()) {
|
|
tsp_debug_err(true, &info->client->dev,
|
|
"%s TSP no accessible from Linux, TUI is enabled!\n", __func__);
|
|
return -EIO;
|
|
}
|
|
#endif
|
|
|
|
string_reg[0] = 0xD0;
|
|
string_reg[1] = (*reg >> 8) & 0xFF;
|
|
string_reg[2] = *reg & 0xFF;
|
|
|
|
if (info->digital_rev == FTS_DIGITAL_REV_1) {
|
|
return fts_read_reg(info, string_reg, 3, data, length);
|
|
} else {
|
|
int rtn;
|
|
buf = kzalloc(length + 1, GFP_KERNEL);
|
|
if (buf == NULL) {
|
|
tsp_debug_info(true, &info->client->dev,
|
|
"%s: kzalloc error.\n", __func__);
|
|
return -1;
|
|
}
|
|
|
|
rtn = fts_read_reg(info, string_reg, 3, buf, length + 1);
|
|
if (rtn >= 0)
|
|
memcpy(data, &buf[1], length);
|
|
|
|
kfree(buf);
|
|
return rtn;
|
|
}
|
|
}
|
|
/*
|
|
* 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 guest image or brane firmware.. etc..
|
|
*/
|
|
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) {
|
|
tsp_debug_err(true, &info->client->dev, "%s: Sensor stopped\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 -1;
|
|
}
|
|
|
|
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 = ®Add[0];
|
|
/* msg[0], length 3*/
|
|
|
|
/* msg[1], length 4*/
|
|
regAdd[3] = 0xb1;
|
|
regAdd[4] = (*reg >> 8) & 0xFF;
|
|
regAdd[5] = *reg & 0xFF;
|
|
|
|
memcpy(®Add[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 = ®Add[3];
|
|
/* msg[1], length 4*/
|
|
|
|
ret = i2c_transfer(info->client->adapter, xfer_msg, 2);
|
|
if (ret == 2) {
|
|
tsp_debug_info(true, &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)
|
|
tsp_debug_info(true, &info->client->dev,
|
|
"%s: string notify is failed.\n", __func__);
|
|
else
|
|
tsp_debug_info(true, &info->client->dev,
|
|
"%s: string notify is OK[%X].\n", __func__, *data);
|
|
|
|
} else
|
|
tsp_debug_info(true, &info->client->dev,
|
|
"%s: string command is failed. ret: %d\n", __func__, ret);
|
|
|
|
mutex_unlock(&info->i2c_mutex);
|
|
kfree(regAdd);
|
|
|
|
return ret;
|
|
}
|
|
#endif
|
|
|
|
static void fts_delay(unsigned int ms)
|
|
{
|
|
if (ms < 20)
|
|
usleep_range(ms * 1000, ms * 1000);
|
|
else
|
|
msleep(ms);
|
|
}
|
|
|
|
void fts_command(struct fts_ts_info *info, unsigned char cmd)
|
|
{
|
|
unsigned char regAdd = 0;
|
|
int ret = 0;
|
|
|
|
regAdd = cmd;
|
|
ret = fts_write_reg(info, ®Add, 1);
|
|
tsp_debug_info(true, &info->client->dev, "FTS Command (%02X) , ret = %d \n", cmd, ret);
|
|
}
|
|
|
|
void fts_enable_feature(struct fts_ts_info *info, unsigned char cmd, int enable)
|
|
{
|
|
unsigned char regAdd[2] = {0xC1, 0x00};
|
|
int ret = 0;
|
|
|
|
if (!enable)
|
|
regAdd[0] = 0xC2;
|
|
regAdd[1] = cmd;
|
|
ret = fts_write_reg(info, ®Add[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);
|
|
}
|
|
|
|
static void fts_set_cover_type(struct fts_ts_info *info, bool enable)
|
|
{
|
|
tsp_debug_info(true, &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_CHARGER_COVER:
|
|
case FTS_COVER_NOTHING1:
|
|
case FTS_COVER_NOTHING2:
|
|
default:
|
|
tsp_debug_err(true, &info->client->dev, "%s: not change touch state, %d\n",
|
|
__func__, info->cover_type);
|
|
break;
|
|
}
|
|
}
|
|
|
|
void 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, ®Add[0], 2);
|
|
|
|
tsp_debug_dbg(true, &info->client->dev, "FTS %s Scan Rate (%02X %02X) , ret = %d \n",
|
|
(cmd == FTS_CMD_FAST_SCAN) ? "90Hz" : (cmd == FTS_CMD_SLOW_SCAN) ? "60Hz" : "30Hz",
|
|
regAdd[0], regAdd[1], ret);
|
|
}
|
|
|
|
void fts_systemreset(struct fts_ts_info *info)
|
|
{
|
|
unsigned char regAdd[4] = { 0xB6, 0x00, 0x23, 0x01 };
|
|
tsp_debug_info(true, &info->client->dev, "FTS SystemReset\n");
|
|
fts_write_reg(info, ®Add[0], 4);
|
|
fts_delay(10);
|
|
}
|
|
|
|
static void fts_interrupt_set(struct fts_ts_info *info, int enable)
|
|
{
|
|
unsigned char regAdd[4] = { 0xB6, 0x00, 0x1C, enable };
|
|
|
|
if (enable)
|
|
tsp_debug_info(true, &info->client->dev, "FTS INT Enable\n");
|
|
else
|
|
tsp_debug_info(true, &info->client->dev, "FTS INT Disable\n");
|
|
|
|
fts_write_reg(info, ®Add[0], 4);
|
|
}
|
|
|
|
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;
|
|
|
|
ret = fts_read_reg(info, regAdd, 3, (unsigned char *)val, 7);
|
|
if (ret < 0) {
|
|
tsp_debug_err(true, &info->client->dev, "%s failed. ret: %d\n",
|
|
__func__, ret);
|
|
return ret;
|
|
}
|
|
|
|
tsp_debug_info(true, &info->client->dev, "FTS %02X%02X%02X = %02X %02X %02X %02X %02X %02X\n",
|
|
regAdd[0], regAdd[1], regAdd[2], val[1], val[2], val[3], val[4], val[5], val[6]);
|
|
|
|
if (val[1] != FTS_ID0)
|
|
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
|
|
return -FTS_ERROR_INVALID_CHIP_VERSION_ID;
|
|
|
|
return ret;
|
|
}
|
|
|
|
static int fts_wait_for_ready(struct fts_ts_info *info)
|
|
{
|
|
int rc;
|
|
unsigned char regAdd;
|
|
unsigned char data[FTS_EVENT_SIZE];
|
|
int retry = 0;
|
|
int err_cnt=0;
|
|
|
|
memset(data, 0x0, FTS_EVENT_SIZE);
|
|
|
|
regAdd = READ_ONE_EVENT;
|
|
rc = -1;
|
|
while (fts_read_reg(info, ®Add, 1, (unsigned char *)data, FTS_EVENT_SIZE)) {
|
|
if (data[0] == EVENTID_CONTROLLER_READY) {
|
|
rc = 0;
|
|
break;
|
|
}
|
|
|
|
if (data[0] == EVENTID_ERROR) {
|
|
if (err_cnt++ > 32) {
|
|
rc = -FTS_ERROR_EVENT_ID;
|
|
break;
|
|
}
|
|
continue;
|
|
}
|
|
|
|
if (retry++ > FTS_RETRY_COUNT) {
|
|
rc = -FTS_ERROR_TIMEOUT;
|
|
tsp_debug_err(true, &info->client->dev, "%s: Time Over\n", __func__);
|
|
if (info->lowpower_mode) {
|
|
schedule_delayed_work(&info->reset_work, msecs_to_jiffies(10));
|
|
}
|
|
break;
|
|
}
|
|
fts_delay(20);
|
|
}
|
|
|
|
tsp_debug_info(true, &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 rc;
|
|
}
|
|
|
|
int fts_get_version_info(struct fts_ts_info *info)
|
|
{
|
|
int rc;
|
|
unsigned char regAdd[3];
|
|
unsigned char data[FTS_EVENT_SIZE];
|
|
int retry = 0;
|
|
|
|
fts_command(info, FTS_CMD_RELEASEINFO);
|
|
|
|
memset(data, 0x0, FTS_EVENT_SIZE);
|
|
|
|
regAdd[0] = READ_ONE_EVENT;
|
|
rc = -1;
|
|
while (fts_read_reg(info, ®Add[0], 1, (unsigned char *)data, FTS_EVENT_SIZE)) {
|
|
if (data[0] == EVENTID_INTERNAL_RELEASE_INFO) {
|
|
// Internal release Information
|
|
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) {
|
|
// External release Information
|
|
info->fw_main_version_of_ic = (data[1] << 8) + data[2];
|
|
rc = 0;
|
|
break;
|
|
}
|
|
|
|
if (retry++ > FTS_RETRY_COUNT) {
|
|
rc = -FTS_ERROR_TIMEOUT;
|
|
tsp_debug_err(true, &info->client->dev, "%s: Time Over\n", __func__);
|
|
break;
|
|
}
|
|
}
|
|
|
|
tsp_debug_info(true, &info->client->dev,
|
|
"IC product id : 0x%02X "
|
|
"IC Firmware Version : 0x%04X "
|
|
"IC Config Version : 0x%04X "
|
|
"IC Main Version : 0x%04X\n",
|
|
info->ic_product_id,
|
|
info->fw_version_of_ic,
|
|
info->config_version_of_ic,
|
|
info->fw_main_version_of_ic);
|
|
|
|
return rc;
|
|
}
|
|
|
|
#ifdef FTS_SUPPORT_NOISE_PARAM
|
|
int fts_get_noise_param_address(struct fts_ts_info *info)
|
|
{
|
|
int rc;
|
|
unsigned char regAdd[3];
|
|
unsigned char rData[3];
|
|
struct fts_noise_param *noise_param;
|
|
int i;
|
|
|
|
noise_param = (struct fts_noise_param *)&info->noise_param;
|
|
|
|
regAdd[0] = 0xd0;
|
|
regAdd[1] = 0x00;
|
|
regAdd[2] = 32 * 2;
|
|
|
|
if (info->digital_rev == FTS_DIGITAL_REV_1)
|
|
rc = fts_read_reg(info, regAdd, 3, (unsigned char *)noise_param->pAddr, 2);
|
|
else {
|
|
rc = fts_read_reg(info, regAdd, 3, (unsigned char *)rData, 3);
|
|
noise_param->pAddr[0] = rData[1] + (rData[2]<<8);
|
|
}
|
|
|
|
for (i = 1; i < MAX_NOISE_PARAM; i++) {
|
|
noise_param->pAddr[i] = noise_param->pAddr[0] + i * 2;
|
|
}
|
|
|
|
for (i = 0; i < MAX_NOISE_PARAM; i++) {
|
|
tsp_debug_dbg(true, &info->client->dev, "Get Noise Param%d Address = 0x%4x\n", i,
|
|
noise_param->pAddr[i]);
|
|
}
|
|
|
|
return rc;
|
|
}
|
|
|
|
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] = (noise_param->pAddr[i] >> 8) & 0xff;
|
|
regAdd[2] = noise_param->pAddr[i] & 0xff;
|
|
rc = fts_read_reg(info, regAdd, 3, &buf[0], 3);
|
|
|
|
noise_param->pData[i] = buf[1]+(buf[2]<<8);
|
|
//tsp_debug_info(true, &info->client->dev, "0x%2x%2x%2x 0x%2x 0x%2x\n", regAdd[0],regAdd[1],regAdd[2], buf[1], buf[2]);
|
|
}
|
|
|
|
for (i = 0; i < MAX_NOISE_PARAM; i++) {
|
|
tsp_debug_dbg(true, &info->client->dev, "Get Noise Param%d Address [ 0x%04x ] = 0x%04x\n", i,
|
|
noise_param->pAddr[i], noise_param->pData[i]);
|
|
}
|
|
|
|
return rc;
|
|
}
|
|
|
|
static int fts_set_noise_param(struct fts_ts_info *info)
|
|
{
|
|
int i;
|
|
unsigned char regAdd[5];
|
|
struct fts_noise_param *noise_param;
|
|
|
|
noise_param = (struct fts_noise_param *)&info->noise_param;
|
|
|
|
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] = (noise_param->pAddr[i] >> 8) & 0xff;
|
|
regAdd[2] = noise_param->pAddr[i] & 0xff;
|
|
regAdd[3] = noise_param->pData[i] & 0xff;
|
|
regAdd[4] = (noise_param->pData[i] >> 8) & 0xff;
|
|
fts_write_reg(info, regAdd, 5);
|
|
}
|
|
|
|
for (i = 0; i < MAX_NOISE_PARAM; i++) {
|
|
tsp_debug_dbg(true, &info->client->dev, "Set Noise Param%d Address [ 0x%04x ] = 0x%04x\n", i,
|
|
noise_param->pAddr[i], noise_param->pData[i]);
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
#endif
|
|
|
|
#ifdef FTS_SUPPORT_TOUCH_KEY
|
|
void fts_release_all_key(struct fts_ts_info *info)
|
|
{
|
|
unsigned char key_recent = TOUCH_KEY_RECENT;
|
|
unsigned char key_back = TOUCH_KEY_BACK;
|
|
|
|
if (info->board->support_mskey && info->tsp_keystatus != TOUCH_KEY_NULL) {
|
|
if (info->tsp_keystatus & key_recent) {
|
|
input_report_key(info->input_dev, KEY_RECENT, KEY_RELEASE);
|
|
tsp_debug_info(true, &info->client->dev, "[TSP_KEY] Recent R!\n");
|
|
}
|
|
|
|
if (info->tsp_keystatus & key_back) {
|
|
input_report_key(info->input_dev, KEY_BACK, KEY_RELEASE);
|
|
tsp_debug_info(true, &info->client->dev, "[TSP_KEY] back R!\n");
|
|
}
|
|
|
|
input_sync(info->input_dev);
|
|
|
|
info->tsp_keystatus = TOUCH_KEY_NULL;
|
|
#if defined (CONFIG_INPUT_BOOSTER)
|
|
input_booster_send_event(BOOSTER_DEVICE_TOUCHKEY, BOOSTER_MODE_OFF);
|
|
#endif
|
|
}
|
|
}
|
|
#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];
|
|
unsigned char regAdd[8];
|
|
int rc;
|
|
|
|
fts_systemreset(info);
|
|
|
|
rc = fts_wait_for_ready(info);
|
|
if (rc == -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);
|
|
|
|
rc = fts_read_chip_id(info);
|
|
if (rc < 0)
|
|
return 1;
|
|
|
|
rc = fts_fw_update_on_probe(info);
|
|
if (rc < 0)
|
|
tsp_debug_err(true, &info->client->dev, "%s: Failed to firmware update\n",
|
|
__func__);
|
|
|
|
fts_command(info, SLEEPOUT);
|
|
|
|
fts_command(info, SENSEON);
|
|
|
|
#ifdef FTS_SUPPORT_TOUCH_KEY
|
|
if (info->board->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;
|
|
info->hover_enabled = false;
|
|
info->hover_ready = false;
|
|
info->flip_enable = false;
|
|
info->mainscr_disable = false;
|
|
|
|
info->deepsleep_mode = false;
|
|
info->lowpower_mode = false;
|
|
info->lowpower_flag = 0x00;
|
|
info->fts_power_state = 0;
|
|
|
|
#ifdef FTS_SUPPORT_TOUCH_KEY
|
|
info->tsp_keystatus = 0x00;
|
|
#endif
|
|
|
|
#ifdef FTS_SUPPORT_2NDSCREEN
|
|
if (info->board->support_2ndscreen) {
|
|
info->SIDE_Flag = 0;
|
|
info->previous_SIDE_value = 0;
|
|
info->run_autotune = true;
|
|
}
|
|
#endif
|
|
|
|
#ifdef SEC_TSP_FACTORY_TEST
|
|
rc = fts_get_channel_info(info);
|
|
if (rc >= 0) {
|
|
tsp_debug_info(true, &info->client->dev, "FTS Sense(%02d) Force(%02d)\n",
|
|
info->SenseChannelLength, info->ForceChannelLength);
|
|
} else {
|
|
tsp_debug_info(true, &info->client->dev, "FTS read failed rc = %d\n", rc);
|
|
tsp_debug_info(true, &info->client->dev, "FTS Initialise Failed\n");
|
|
return 1;
|
|
}
|
|
|
|
info->pFrame =
|
|
kzalloc(info->SenseChannelLength * info->ForceChannelLength * 2,
|
|
GFP_KERNEL);
|
|
if (info->pFrame == NULL) {
|
|
tsp_debug_info(true, &info->client->dev, "FTS pFrame kzalloc Failed\n");
|
|
return 1;
|
|
}
|
|
|
|
info->cx_data = kzalloc(info->SenseChannelLength * info->ForceChannelLength, GFP_KERNEL);
|
|
if (!info->cx_data)
|
|
tsp_debug_err(true, &info->client->dev, "%s: cx_data kzalloc Failed\n", __func__);
|
|
#endif
|
|
|
|
fts_command(info, FORCECALIBRATION);
|
|
fts_command(info, FLUSHBUFFER);
|
|
|
|
fts_interrupt_set(info, INT_ENABLE);
|
|
|
|
memset(val, 0x0, 4);
|
|
regAdd[0] = READ_STATUS;
|
|
fts_read_reg(info, regAdd, 1, (unsigned char *)val, 4);
|
|
tsp_debug_info(true, &info->client->dev, "FTS ReadStatus(0x84) : %02X %02X %02X %02X\n", val[0],
|
|
val[1], val[2], val[3]);
|
|
|
|
tsp_debug_info(true, &info->client->dev, "FTS Initialized\n");
|
|
|
|
return 0;
|
|
}
|
|
|
|
static void fts_debug_msg_event_handler(struct fts_ts_info *info,
|
|
unsigned char data[])
|
|
{
|
|
tsp_debug_dbg(true, &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]);
|
|
}
|
|
|
|
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;
|
|
#if defined (CONFIG_INPUT_BOOSTER)
|
|
bool booster_restart = false;
|
|
#endif
|
|
#ifdef FTS_SUPPORT_2NDSCREEN
|
|
u8 currentSideFlag = 0;
|
|
#endif
|
|
#ifdef FTS_SUPPORT_SIDE_GESTURE
|
|
static int longpress_release[FINGER_MAX] = {0, };
|
|
#endif
|
|
#ifdef FTS_SUPPORT_STRINGLIB
|
|
unsigned short string_addr;
|
|
unsigned char string_data[10] = {0, };
|
|
unsigned char string_clear = 0;
|
|
#endif
|
|
|
|
for (EventNum = 0; EventNum < LeftEvent; EventNum++) {
|
|
#if 0
|
|
tsp_debug_info(true, &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;
|
|
}
|
|
|
|
switch (EventID) {
|
|
case EVENTID_NO_EVENT:
|
|
break;
|
|
|
|
#ifdef FTS_SUPPORT_TOUCH_KEY
|
|
case EVENTID_MSKEY:
|
|
if (info->board->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 = TOUCH_KEY_RECENT;
|
|
unsigned char 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");
|
|
}
|
|
|
|
#if defined (CONFIG_INPUT_BOOSTER)
|
|
if ((change_keys & key_recent)||(change_keys & key_back)) {
|
|
input_booster_send_event(BOOSTER_DEVICE_TOUCHKEY, (key_state != 0 ? KEY_PRESS : KEY_RELEASE));
|
|
}
|
|
#endif
|
|
input_sync(info->input_dev);
|
|
}
|
|
|
|
info->tsp_keystatus = input_keys;
|
|
}
|
|
break;
|
|
#endif
|
|
#ifdef FTS_SUPPORT_SIDE_GESTURE
|
|
case EVENTID_SIDE_TOUCH:
|
|
case EVENTID_SIDE_TOUCH_DEBUG:
|
|
if (info->board->support_sidegesture) {
|
|
unsigned char event_type = data[1 + EventNum * FTS_EVENT_SIZE];
|
|
|
|
if ((event_type == FTS_SIDEGESTURE_EVENT_SINGLE_STROKE) ||
|
|
(event_type == FTS_SIDEGESTURE_EVENT_DOUBLE_STROKE) ||
|
|
(event_type == FTS_SIDEGESTURE_EVENT_INNER_STROKE)) {
|
|
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);
|
|
fts_delay(1);
|
|
|
|
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 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 if(event_type == FTS_SIDETOUCH_EVENT_REBOOT_BY_ESD) {
|
|
tsp_debug_info(true, &info->client->dev,
|
|
"%s: ESD detected! %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 {
|
|
fts_debug_msg_event_handler(info,
|
|
&data[EventNum *
|
|
FTS_EVENT_SIZE]);
|
|
}
|
|
} else {
|
|
unsigned char event_type = data[1 + EventNum * FTS_EVENT_SIZE];
|
|
if(event_type == FTS_SIDETOUCH_EVENT_REBOOT_BY_ESD) {
|
|
tsp_debug_info(true, &info->client->dev,
|
|
"%s: ESD detected! %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 {
|
|
fts_debug_msg_event_handler(info,
|
|
&data[EventNum *
|
|
FTS_EVENT_SIZE]);
|
|
}
|
|
|
|
}
|
|
break;
|
|
#endif
|
|
case EVENTID_ERROR:
|
|
if (data[1 + EventNum * FTS_EVENT_SIZE] == 0x08) { // Get Auto tune fail event
|
|
if (data[2 + EventNum * FTS_EVENT_SIZE] == 0x00) {
|
|
tsp_debug_info(true, &info->client->dev, "[FTS] Fail Mutual Auto tune\n");
|
|
}
|
|
else if (data[2 + EventNum * FTS_EVENT_SIZE] == 0x01) {
|
|
tsp_debug_info(true, &info->client->dev, "[FTS] Fail Self Auto tune\n");
|
|
}
|
|
} else if (data[1 + EventNum * FTS_EVENT_SIZE] == 0x09) // Get detect SYNC fail event
|
|
tsp_debug_info(true, &info->client->dev, "[FTS] Fail detect SYNC\n");
|
|
break;
|
|
|
|
case EVENTID_HOVER_ENTER_POINTER:
|
|
case EVENTID_HOVER_MOTION_POINTER:
|
|
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
|
|
if (info->board->support_2ndscreen) {
|
|
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:
|
|
input_mt_slot(info->input_dev, 0);
|
|
input_mt_report_slot_state(info->input_dev,
|
|
MT_TOOL_FINGER, 0);
|
|
break;
|
|
|
|
case EVENTID_ENTER_POINTER:
|
|
if (info->fts_power_state == FTS_POWER_STATE_LOWPOWER)
|
|
break;
|
|
|
|
info->touch_count++;
|
|
#if defined (CONFIG_INPUT_BOOSTER)
|
|
booster_restart = true;
|
|
#endif
|
|
case EVENTID_MOTION_POINTER:
|
|
if (info->fts_power_state == FTS_POWER_STATE_LOWPOWER) {
|
|
tsp_debug_info(true, &info->client->dev, "%s: low power mode\n", __func__);
|
|
fts_release_all_finger(info);
|
|
break;
|
|
}
|
|
|
|
if (info->touch_count == 0) {
|
|
tsp_debug_info(true, &info->client->dev, "%s: count 0\n", __func__);
|
|
fts_release_all_finger(info);
|
|
break;
|
|
}
|
|
|
|
if ((EventID == EVENTID_MOTION_POINTER) &&
|
|
(info->finger[TouchID].state == EVENTID_LEAVE_POINTER)) {
|
|
tsp_debug_info(true, &info->client->dev, "%s: state leave but point is moved.\n", __func__);
|
|
break;
|
|
}
|
|
|
|
if (info->fts_power_state == FTS_POWER_STATE_LOWPOWER)
|
|
break;
|
|
#ifdef SCAILE_FOR_LSI
|
|
if (!strncmp(info->board->model_name, "espresso", 8)) {
|
|
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);
|
|
} else {
|
|
x = (data[1 + EventNum * FTS_EVENT_SIZE] +
|
|
((data[2 + EventNum * FTS_EVENT_SIZE] &
|
|
0x0f) << 8)) * info->board->max_x / 4096;
|
|
y = (((data[2 + EventNum * FTS_EVENT_SIZE] &
|
|
0xf0) >> 4) + (data[3 + EventNum *
|
|
FTS_EVENT_SIZE] << 4)) * info->board->max_y / 4096;
|
|
}
|
|
#else
|
|
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);
|
|
|
|
#endif
|
|
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;
|
|
|
|
if ((info->touch_count == 1) && (sumsize < 40))
|
|
sumsize = 39;
|
|
#ifdef FTS_SUPPORT_2NDSCREEN
|
|
if (info->board->support_2ndscreen) {
|
|
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];
|
|
#else
|
|
z = data[7 + EventNum * FTS_EVENT_SIZE];
|
|
#endif
|
|
input_mt_slot(info->input_dev, TouchID);
|
|
input_mt_report_slot_state(info->input_dev,
|
|
MT_TOOL_FINGER,
|
|
1 + (palm << 1));
|
|
|
|
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));
|
|
#ifdef FTS_SUPPORT_SEC_SWIPE
|
|
input_report_abs(info->input_dev, ABS_MT_PALM,
|
|
palm);
|
|
#endif
|
|
#if defined(FTS_SUPPORT_SIDE_GESTURE)
|
|
if (info->board->support_sidegesture)
|
|
input_report_abs(info->input_dev, ABS_MT_GRIP, 0);
|
|
#endif
|
|
|
|
info->finger[TouchID].lx = x;
|
|
info->finger[TouchID].ly = y;
|
|
|
|
break;
|
|
|
|
case EVENTID_LEAVE_POINTER:
|
|
if (info->fts_power_state == FTS_POWER_STATE_LOWPOWER)
|
|
break;
|
|
|
|
if (info->touch_count <= 0) {
|
|
tsp_debug_info(true, &info->client->dev, "%s: count 0\n", __func__);
|
|
fts_release_all_finger(info);
|
|
break;
|
|
}
|
|
|
|
info->touch_count--;
|
|
|
|
input_mt_slot(info->input_dev, TouchID);
|
|
|
|
#if defined(FTS_SUPPORT_SIDE_GESTURE)
|
|
if (info->board->support_sidegesture) {
|
|
if (longpress_release[TouchID] == 1) {
|
|
input_report_abs(info->input_dev, ABS_MT_GRIP, 1);
|
|
tsp_debug_info(true, &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_SUPPORT_SIDE_SCROLL
|
|
if (info->board->support_sidescroll) {
|
|
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
|
|
if (info->board->support_2ndscreen) {
|
|
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->board->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);
|
|
#endif
|
|
} else if (status == STATUS_EVENT_RAW_DATA_READY) {
|
|
unsigned char regAdd[4] = {0xB0, 0x01, 0x29, 0x01};
|
|
fts_write_reg(info, ®Add[0], 4);
|
|
|
|
info->hover_ready = true;
|
|
|
|
tsp_debug_info(true, &info->client->dev, "[FTS] Received the Hover Raw Data Ready Event\n");
|
|
} else if (status == STATUS_EVENT_FORCE_CAL_MUTUAL) {
|
|
tsp_debug_dbg(true, &info->client->dev, "[FTS] Received Force Calibration Mutual only Event\n");
|
|
} else if (status == STATUS_EVENT_FORCE_CAL_SELF) {
|
|
tsp_debug_dbg(true, &info->client->dev, "[FTS] Received Force Calibration Self only Event\n");
|
|
} else if (status == STATUS_EVENT_WATERMODE_ON) {
|
|
tsp_debug_info(true, &info->client->dev, "[FTS] Received Water Mode On Event\n");
|
|
} else if (status == STATUS_EVENT_WATERMODE_OFF) {
|
|
tsp_debug_info(true, &info->client->dev, "[FTS] Received Water Mode Off Event\n");
|
|
} else if (status == STATUS_EVENT_MUTUAL_CAL_FRAME_CHECK) {
|
|
tsp_debug_info(true, &info->client->dev, "[FTS] Received Mutual Calib Frame Check Event\n");
|
|
} else if (status == STATUS_EVENT_SELF_CAL_FRAME_CHECK) {
|
|
tsp_debug_info(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:
|
|
procedure_cmd_event(info, &data[EventNum * FTS_EVENT_SIZE]);
|
|
break;
|
|
#endif
|
|
|
|
#ifdef FTS_SUPPORT_SIDE_SCROLL
|
|
case EVENTID_SIDE_SCROLL:
|
|
if (info->board->support_sidescroll) {
|
|
int scroll_flag = data[3 + EventNum * FTS_EVENT_SIZE];
|
|
int scroll_thr = data[6 + EventNum * FTS_EVENT_SIZE];
|
|
tsp_debug_info(true, &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
|
|
#ifdef FTS_SUPPORT_STRINGLIB
|
|
case EVENTID_FROM_STRING:
|
|
string_addr = FTS_CMD_STRING_ACCESS;
|
|
fts_read_from_string(info, &string_addr, string_data, 6);
|
|
tsp_debug_info(true, &info->client->dev,
|
|
"%s: [String] %02X %02X %02X %02X %02X %02X %02X %02X || %04X: %02X, %02X\n",
|
|
__func__, data[0], data[1], data[2], data[3],
|
|
data[4], data[5], data[6], data[7],
|
|
string_addr, string_data[0], string_data[1]);
|
|
|
|
switch(string_data[1]) {
|
|
case FTS_STRING_EVENT_REAR_CAM:
|
|
tsp_debug_info(true, &info->client->dev, "%s: REAR_CAM\n", __func__);
|
|
break;
|
|
case FTS_STRING_EVENT_FRONT_CAM:
|
|
tsp_debug_info(true, &info->client->dev, "%s: FRONT_CAM\n", __func__);
|
|
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:
|
|
dev_info(&info->client->dev, "%s: SPAY[%X]\n", __func__, string_data[1]);
|
|
input_report_key(info->input_dev, KEY_BLACK_UI_GESTURE, 1);
|
|
info->scrub_id = (string_data[1] >> 2) & 0xF;
|
|
break;
|
|
default:
|
|
tsp_debug_info(true, &info->client->dev, "%s: no event:%X\n", __func__, string_data[1]);
|
|
break;
|
|
}
|
|
|
|
input_sync(info->input_dev);
|
|
|
|
if (!info->debug_string) {
|
|
/* request by display lab, buf clear */
|
|
string_clear |= string_data[1];
|
|
|
|
string_addr = FTS_CMD_STRING_ACCESS + 1;
|
|
string_data[1] &= ~(string_clear);
|
|
|
|
tsp_debug_info(true, &info->client->dev,
|
|
"%s: clear bit : string_data[1]: %X, string_clear: %X\n",
|
|
__func__, string_data[1], string_clear);
|
|
|
|
fts_write_to_string(info, &string_addr, &string_data[1], 1);
|
|
} else {
|
|
tsp_debug_info(true, &info->client->dev,
|
|
"%s: do not clear string_bit\n",
|
|
__func__);
|
|
}
|
|
|
|
input_report_key(info->input_dev, KEY_BLACK_UI_GESTURE, 0);
|
|
|
|
break;
|
|
#endif
|
|
|
|
default:
|
|
fts_debug_msg_event_handler(info,
|
|
&data[EventNum *
|
|
FTS_EVENT_SIZE]);
|
|
continue;
|
|
}
|
|
#ifdef FTS_SUPPORT_2NDSCREEN
|
|
if (info->board->support_2ndscreen) {
|
|
if (currentSideFlag != info->previous_SIDE_value) {
|
|
tsp_debug_info(true, &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)
|
|
tsp_debug_info(true, &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);
|
|
else if (EventID == EVENTID_HOVER_ENTER_POINTER)
|
|
tsp_debug_dbg(true, &info->client->dev,
|
|
"[HP] tID:%d x:%d y:%d z:%d\n",
|
|
TouchID, x, y, z);
|
|
#else
|
|
if (EventID == EVENTID_ENTER_POINTER)
|
|
tsp_debug_info(true, &info->client->dev,
|
|
"[P] tID:%d tc:%d tm:%d\n",
|
|
TouchID, info->touch_count, info->touch_mode);
|
|
else if (EventID == EVENTID_HOVER_ENTER_POINTER)
|
|
tsp_debug_dbg(true, &info->client->dev,
|
|
"[HP] tID:%d\n", TouchID);
|
|
#endif
|
|
else if (EventID == EVENTID_LEAVE_POINTER) {
|
|
#if !defined(CONFIG_SAMSUNG_PRODUCT_SHIP)
|
|
if (strncmp(info->board->model_name, "G925", 4) == 0) {
|
|
tsp_debug_info(true, &info->client->dev,
|
|
"[R] tID:%d mc: %d tc:%d lx: %d ly: %d Ver[%01X%01X%04X%01X%01X%01X]\n",
|
|
TouchID, info->finger[TouchID].mcount, info->touch_count,
|
|
info->finger[TouchID].lx, info->finger[TouchID].ly,
|
|
info->tspid_val,info->tspid2_val, info->fw_main_version_of_ic,
|
|
info->flip_enable, info->mshover_enabled, info->mainscr_disable);
|
|
}else{
|
|
tsp_debug_info(true, &info->client->dev,
|
|
"[R] tID:%d mc: %d tc:%d lx: %d ly: %d Ver[%02X%04X%01X%01X%01X]\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->mshover_enabled, info->mainscr_disable);
|
|
}
|
|
#else
|
|
if (strncmp(info->board->model_name, "G925", 4) == 0) {
|
|
|
|
tsp_debug_info(true, &info->client->dev,
|
|
"[R] tID:%d mc: %d tc:%d Ver[%01X%01X%04X%01X%01X%01X]\n",
|
|
TouchID, info->finger[TouchID].mcount, info->touch_count,
|
|
info->tspid_val,info->tspid2_val, info->fw_main_version_of_ic,
|
|
info->flip_enable, info->mshover_enabled, info->mainscr_disable);
|
|
}else{
|
|
tsp_debug_info(true, &info->client->dev,
|
|
"[R] tID:%d mc: %d tc:%d Ver[%02X%04X%01X%01X%01X]\n",
|
|
TouchID, info->finger[TouchID].mcount, info->touch_count,
|
|
info->panel_revision, info->fw_main_version_of_ic,
|
|
info->flip_enable, info->mshover_enabled, info->mainscr_disable);
|
|
}
|
|
#endif
|
|
info->finger[TouchID].mcount = 0;
|
|
} else if (EventID == EVENTID_HOVER_LEAVE_POINTER) {
|
|
tsp_debug_dbg(true, &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->mshover_enabled);
|
|
info->finger[TouchID].mcount = 0;
|
|
} 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);
|
|
|
|
#if defined (CONFIG_INPUT_BOOSTER)
|
|
if ((EventID == EVENTID_ENTER_POINTER)
|
|
|| (EventID == EVENTID_LEAVE_POINTER)) {
|
|
if (booster_restart)
|
|
input_booster_send_event(BOOSTER_DEVICE_TOUCH, BOOSTER_MODE_ON);
|
|
if (!info->touch_count)
|
|
input_booster_send_event(BOOSTER_DEVICE_TOUCH, BOOSTER_MODE_OFF);
|
|
}
|
|
#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;
|
|
tsp_debug_info(true, &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;
|
|
tsp_debug_info(true, &info->client->dev,
|
|
"%s: device_control : CHARGER DISCONNECTED, ta_status : %x\n",
|
|
__func__, ta_status);
|
|
}
|
|
}
|
|
#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;
|
|
#ifdef FTS_SUPPORT_SIDE_GESTURE
|
|
if ((info->board->support_sidegesture) &&
|
|
(info->fts_power_state == FTS_POWER_STATE_LOWPOWER)) {
|
|
pm_wakeup_event(info->input_dev->dev.parent, 1000);
|
|
}
|
|
#endif
|
|
evtcount = 0;
|
|
fts_read_reg(info, ®Add[0], 3, (unsigned char *)&evtcount, 2);
|
|
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, ®Add[3], 1, (unsigned char *)info->data,
|
|
FTS_EVENT_SIZE * evtcount);
|
|
fts_event_handler_type_b(info, info->data, evtcount);
|
|
}
|
|
#ifdef FTS_SUPPORT_SIDE_GESTURE
|
|
if ((info->board->support_sidegesture) &&
|
|
(info->fts_power_state == FTS_POWER_STATE_LOWPOWER))
|
|
pm_relax(info->input_dev->dev.parent);
|
|
#endif
|
|
|
|
return IRQ_HANDLED;
|
|
}
|
|
|
|
static int fts_irq_enable(struct fts_ts_info *info,
|
|
bool enable)
|
|
{
|
|
int retval = 0;
|
|
|
|
if (enable) {
|
|
if (info->irq_enabled)
|
|
return retval;
|
|
|
|
retval = request_threaded_irq(info->irq, NULL,
|
|
fts_interrupt_handler, info->board->irq_type,
|
|
FTS_TS_DRV_NAME, info);
|
|
if (retval < 0) {
|
|
tsp_debug_info(true, &info->client->dev,
|
|
"%s: Failed to create irq thread %d\n",
|
|
__func__, retval);
|
|
return retval;
|
|
}
|
|
|
|
info->irq_enabled = true;
|
|
} else {
|
|
if (info->irq_enabled) {
|
|
disable_irq(info->irq);
|
|
free_irq(info->irq, info);
|
|
info->irq_enabled = false;
|
|
}
|
|
}
|
|
|
|
return retval;
|
|
}
|
|
|
|
#ifdef CONFIG_OF
|
|
#ifdef FTS_SUPPORT_TA_MODE
|
|
struct fts_callbacks *fts_charger_callbacks;
|
|
void tsp_charger_infom(bool en)
|
|
{
|
|
|
|
pr_err("[TSP]%s: ta:%d\n", __func__, en);
|
|
|
|
if (fts_charger_callbacks && fts_charger_callbacks->inform_charger)
|
|
fts_charger_callbacks->inform_charger(fts_charger_callbacks, en);
|
|
}
|
|
static void fts_tsp_register_callback(void *cb)
|
|
{
|
|
fts_charger_callbacks = cb;
|
|
}
|
|
#endif
|
|
|
|
#ifdef FTS_SUPPORT_TOUCH_KEY
|
|
static int fts_led_power_ctrl(void *data, bool on)
|
|
{
|
|
struct fts_ts_info *info = (struct fts_ts_info *)data;
|
|
const struct fts_i2c_platform_data *pdata = info->board;
|
|
struct device *dev = &info->client->dev;
|
|
struct regulator *regulator_tk_led = NULL;
|
|
static bool enabled;
|
|
int retval = 0;
|
|
|
|
if (enabled == on)
|
|
return retval;
|
|
|
|
regulator_tk_led = regulator_get(NULL, pdata->regulator_tk_led);
|
|
if (IS_ERR_OR_NULL(regulator_tk_led)) {
|
|
tsp_debug_err(true, dev, "%s: Failed to get %s regulator.\n",
|
|
__func__, pdata->regulator_tk_led);
|
|
goto out;
|
|
}
|
|
|
|
tsp_debug_info(true, dev, "%s: %s\n", __func__, on ? "on" : "off");
|
|
|
|
if (on) {
|
|
retval = regulator_enable(regulator_tk_led);
|
|
if (retval) {
|
|
tsp_debug_err(true, dev, "%s: Failed to enable led%d\n", __func__, retval);
|
|
goto out;
|
|
}
|
|
} else {
|
|
if (regulator_is_enabled(regulator_tk_led))
|
|
regulator_disable(regulator_tk_led);
|
|
}
|
|
|
|
enabled = on;
|
|
out:
|
|
regulator_put(regulator_tk_led);
|
|
|
|
return retval;
|
|
}
|
|
#endif
|
|
|
|
static int fts_power_ctrl(void *data, bool on)
|
|
{
|
|
struct fts_ts_info *info = (struct fts_ts_info *)data;
|
|
const struct fts_i2c_platform_data *pdata = info->board;
|
|
struct device *dev = &info->client->dev;
|
|
struct regulator *regulator_dvdd = NULL;
|
|
struct regulator *regulator_avdd = NULL;
|
|
static bool enabled;
|
|
int retval = 0;
|
|
|
|
if (enabled == on)
|
|
return retval;
|
|
|
|
regulator_dvdd = regulator_get(NULL, pdata->regulator_dvdd);
|
|
if (IS_ERR_OR_NULL(regulator_dvdd)) {
|
|
tsp_debug_err(true, dev, "%s: Failed to get %s regulator.\n",
|
|
__func__, pdata->regulator_dvdd);
|
|
goto out;
|
|
}
|
|
|
|
regulator_avdd = regulator_get(NULL, pdata->regulator_avdd);
|
|
if (IS_ERR_OR_NULL(regulator_avdd)) {
|
|
tsp_debug_err(true, dev, "%s: Failed to get %s regulator.\n",
|
|
__func__, pdata->regulator_avdd);
|
|
goto out;
|
|
}
|
|
|
|
tsp_debug_info(true, dev, "%s: %s\n", __func__, on ? "on" : "off");
|
|
|
|
if (on) {
|
|
retval = regulator_enable(regulator_avdd);
|
|
if (retval) {
|
|
tsp_debug_err(true, dev, "%s: Failed to enable avdd: %d\n", __func__, retval);
|
|
goto out;
|
|
}
|
|
retval = regulator_enable(regulator_dvdd);
|
|
if (retval) {
|
|
tsp_debug_err(true, dev, "%s: Failed to enable vdd: %d\n", __func__, retval);
|
|
goto out;
|
|
}
|
|
|
|
retval = pinctrl_select_state(pdata->pinctrl, pdata->pins_default);
|
|
if (retval < 0)
|
|
tsp_debug_err(true, dev, "%s: Failed to configure tsp_attn pin\n", __func__);
|
|
|
|
fts_delay(5);
|
|
} else {
|
|
if (regulator_is_enabled(regulator_dvdd))
|
|
regulator_disable(regulator_dvdd);
|
|
if (regulator_is_enabled(regulator_avdd))
|
|
regulator_disable(regulator_avdd);
|
|
|
|
retval = pinctrl_select_state(pdata->pinctrl, pdata->pins_sleep);
|
|
if (retval < 0)
|
|
tsp_debug_err(true, dev, "%s: Failed to configure tsp_attn pin\n", __func__);
|
|
}
|
|
|
|
enabled = on;
|
|
out:
|
|
regulator_put(regulator_dvdd);
|
|
regulator_put(regulator_avdd);
|
|
|
|
return retval;
|
|
}
|
|
|
|
static int fts_parse_dt(struct i2c_client *client)
|
|
{
|
|
struct device *dev = &client->dev;
|
|
struct fts_i2c_platform_data *pdata = dev->platform_data;
|
|
struct device_node *np = dev->of_node;
|
|
u32 coords[2], lines[2];
|
|
int retval = 0;
|
|
|
|
pdata->tspid = of_get_named_gpio(np, "stm,tspid_gpio", 0);
|
|
if (gpio_is_valid(pdata->tspid))
|
|
tsp_debug_info(true, dev, "TSP_ID : %d\n", gpio_get_value(pdata->tspid));
|
|
else
|
|
tsp_debug_err(true, dev, "Failed to get tspid gpio\n");
|
|
|
|
pdata->tspid2 = of_get_named_gpio(np, "stm,tspid2_gpio", 0);
|
|
if (gpio_is_valid(pdata->tspid2))
|
|
tsp_debug_info(true, dev, "TSP_ID2 : %d\n", gpio_get_value(pdata->tspid2));
|
|
else
|
|
tsp_debug_err(true, dev, "Failed to get tspid2 gpio\n");
|
|
|
|
pdata->gpio = of_get_named_gpio(np, "stm,irq_gpio", 0);
|
|
if (gpio_is_valid(pdata->gpio)) {
|
|
retval = gpio_request_one(pdata->gpio, GPIOF_DIR_IN, "stm,tsp_int");
|
|
if (retval) {
|
|
tsp_debug_err(true, dev, "Unable to request tsp_int [%d]\n", pdata->gpio);
|
|
return -EINVAL;
|
|
}
|
|
} else {
|
|
tsp_debug_err(true, dev, "Failed to get irq gpio\n");
|
|
return -EINVAL;
|
|
}
|
|
client->irq = gpio_to_irq(pdata->gpio);
|
|
|
|
if (of_property_read_u32(np, "stm,irq_type", &pdata->irq_type)) {
|
|
tsp_debug_err(true, dev, "Failed to get irq_type property\n");
|
|
return -EINVAL;
|
|
}
|
|
|
|
if (of_property_read_u32(np, "stm,grip_area", &pdata->grip_area))
|
|
tsp_debug_err(true, dev, "Failed to get grip_area property\n");
|
|
|
|
if (of_property_read_u32_array(np, "stm,max_coords", coords, 2)) {
|
|
tsp_debug_err(true, dev, "Failed to get max_coords property\n");
|
|
return -EINVAL;
|
|
}
|
|
pdata->max_x = coords[0];
|
|
pdata->max_y = coords[1];
|
|
#ifdef CONFIG_SEC_FACTORY
|
|
if ((lcdtype == S6E3HF2_WQXGA_ID1) || (lcdtype == S6E3HF2_WQXGA_ID2)) {
|
|
pdata->max_x = 1439;
|
|
pdata->max_y = 2559;
|
|
pdata->grip_area = 160;
|
|
}
|
|
#endif
|
|
if (of_property_read_u32_array(np, "stm,num_lines", lines, 2))
|
|
tsp_debug_dbg(true, dev, "skipped to get num_lines property\n");
|
|
else {
|
|
pdata->SenseChannelLength = lines[0];
|
|
pdata->ForceChannelLength = lines[1];
|
|
tsp_debug_info(true, dev, "num_of[rx,tx]: [%d,%d]\n",
|
|
pdata->SenseChannelLength, pdata->ForceChannelLength);
|
|
}
|
|
|
|
if (of_property_read_string(np, "stm,regulator_dvdd", &pdata->regulator_dvdd)) {
|
|
tsp_debug_err(true, dev, "Failed to get regulator_dvdd name property\n");
|
|
return -EINVAL;
|
|
}
|
|
if (of_property_read_string(np, "stm,regulator_avdd", &pdata->regulator_avdd)) {
|
|
tsp_debug_err(true, dev, "Failed to get regulator_avdd name property\n");
|
|
return -EINVAL;
|
|
}
|
|
pdata->power = fts_power_ctrl;
|
|
|
|
/* Optional parmeters(those values are not mandatory)
|
|
* do not return error value even if fail to get the value
|
|
*/
|
|
of_property_read_string(np, "stm,firmware_name", &pdata->firmware_name);
|
|
|
|
if (of_property_read_string_index(np, "stm,project_name", 0, &pdata->project_name))
|
|
tsp_debug_dbg(true, dev, "skipped to get project_name property\n");
|
|
if (of_property_read_string_index(np, "stm,project_name", 1, &pdata->model_name))
|
|
tsp_debug_dbg(true, dev, "skipped to get model_name property\n");
|
|
|
|
pdata->max_width = 28;
|
|
pdata->support_hover = true;
|
|
pdata->support_mshover = true;
|
|
#ifdef FTS_SUPPORT_TA_MODE
|
|
pdata->register_cb = fts_tsp_register_callback;
|
|
#endif
|
|
|
|
#ifdef FTS_SUPPORT_TOUCH_KEY
|
|
if (of_property_read_u32(np, "stm,num_touchkey", &pdata->num_touchkey))
|
|
tsp_debug_dbg(true, dev, "skipped to get num_touchkey property\n");
|
|
else {
|
|
#ifdef FTS_SUPPORT_SIDE_GESTURE
|
|
pdata->support_sidegesture = true;
|
|
#endif
|
|
pdata->support_mskey = true;
|
|
pdata->touchkey = fts_touchkeys;
|
|
|
|
if (of_property_read_string(np, "stm,regulator_tk_led", &pdata->regulator_tk_led))
|
|
tsp_debug_dbg(true, dev, "skipped to get regulator_tk_led name property\n");
|
|
else
|
|
pdata->led_power = fts_led_power_ctrl;
|
|
}
|
|
#endif
|
|
#ifdef CONFIG_SEC_FACTORY
|
|
pdata->panel_revision = (lcdtype & 0xF000) >> 12;
|
|
#endif
|
|
tsp_debug_info(true, dev,
|
|
"irq :%d, irq_type: 0x%04x, max[x,y]: [%d,%d], grip_area :%d, project/model_name: %s/%s, panel_revision: %d\n",
|
|
pdata->gpio, pdata->irq_type, pdata->max_x, pdata->max_y, pdata->grip_area,
|
|
pdata->project_name, pdata->model_name,
|
|
pdata->panel_revision);
|
|
|
|
return retval;
|
|
}
|
|
#endif
|
|
|
|
static int fts_setup_drv_data(struct i2c_client *client)
|
|
{
|
|
int retval = 0;
|
|
struct fts_i2c_platform_data *pdata;
|
|
struct fts_ts_info *info;
|
|
|
|
/* parse dt */
|
|
if (client->dev.of_node) {
|
|
pdata = devm_kzalloc(&client->dev,
|
|
sizeof(struct fts_i2c_platform_data), GFP_KERNEL);
|
|
|
|
if (!pdata) {
|
|
tsp_debug_err(true, &client->dev, "Failed to allocate platform data\n");
|
|
return -ENOMEM;
|
|
}
|
|
|
|
client->dev.platform_data = pdata;
|
|
retval = fts_parse_dt(client);
|
|
if (retval) {
|
|
tsp_debug_err(true, &client->dev, "Failed to parse dt\n");
|
|
return retval;
|
|
}
|
|
} else {
|
|
pdata = client->dev.platform_data;
|
|
}
|
|
|
|
if (!pdata) {
|
|
tsp_debug_err(true, &client->dev, "No platform data found\n");
|
|
return -EINVAL;
|
|
}
|
|
if (!pdata->power) {
|
|
tsp_debug_err(true, &client->dev, "No power contorl found\n");
|
|
return -EINVAL;
|
|
}
|
|
|
|
pdata->pinctrl = devm_pinctrl_get(&client->dev);
|
|
if (IS_ERR(pdata->pinctrl)) {
|
|
tsp_debug_err(true, &client->dev, "could not get pinctrl\n");
|
|
return PTR_ERR(pdata->pinctrl);
|
|
}
|
|
|
|
pdata->pins_default = pinctrl_lookup_state(pdata->pinctrl, "on_state");
|
|
if (IS_ERR(pdata->pins_default))
|
|
tsp_debug_err(true, &client->dev, "could not get default pinstate\n");
|
|
|
|
pdata->pins_sleep = pinctrl_lookup_state(pdata->pinctrl, "off_state");
|
|
if (IS_ERR(pdata->pins_sleep))
|
|
tsp_debug_err(true, &client->dev, "could not get sleep pinstate\n");
|
|
|
|
info = kzalloc(sizeof(struct fts_ts_info), GFP_KERNEL);
|
|
if (!info) {
|
|
tsp_debug_err(true, &client->dev,
|
|
"%s: Failed to alloc mem for info\n",
|
|
__func__);
|
|
return -ENOMEM;
|
|
}
|
|
|
|
info->client = client;
|
|
info->board = pdata;
|
|
info->irq = client->irq;
|
|
info->irq_type = info->board->irq_type;
|
|
info->irq_enabled = false;
|
|
info->touch_stopped = false;
|
|
info->panel_revision = info->board->panel_revision;
|
|
info->stop_device = fts_stop_device;
|
|
info->start_device = fts_start_device;
|
|
info->fts_command = fts_command;
|
|
info->fts_enable_feature = fts_enable_feature;
|
|
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;
|
|
#ifdef FTS_SUPPORT_STRINGLIB
|
|
info->fts_read_from_string = fts_read_from_string;
|
|
info->fts_write_to_string = fts_write_to_string;
|
|
#endif
|
|
#ifdef FTS_SUPPORT_NOISE_PARAM
|
|
info->fts_get_noise_param_address = fts_get_noise_param_address;
|
|
#endif
|
|
|
|
#ifdef USE_OPEN_DWORK
|
|
INIT_DELAYED_WORK(&info->open_work, fts_open_work);
|
|
#endif
|
|
info->delay_time = 300;
|
|
INIT_DELAYED_WORK(&info->reset_work, fts_reset_work);
|
|
|
|
#ifdef CONFIG_SEC_DEBUG_TSP_LOG
|
|
INIT_DELAYED_WORK(&info->debug_work, dump_tsp_rawdata);
|
|
p_debug_work = &info->debug_work;
|
|
#endif
|
|
#ifdef SEC_TSP_FACTORY_TEST
|
|
INIT_DELAYED_WORK(&info->cover_cmd_work, clear_cover_cmd_work);
|
|
#endif
|
|
|
|
if (info->board->support_hover)
|
|
tsp_debug_info(true, &info->client->dev, "FTS Support Hover Event \n");
|
|
else
|
|
tsp_debug_info(true, &info->client->dev, "FTS Not support Hover Event \n");
|
|
|
|
i2c_set_clientdata(client, info);
|
|
|
|
if (pdata->get_ddi_type) {
|
|
info->ddi_type = pdata->get_ddi_type();
|
|
tsp_debug_info(true, &client->dev, "%s: DDI Type is %s[%d]\n",
|
|
__func__, info->ddi_type ? "MAGNA" : "SDC", info->ddi_type);
|
|
}
|
|
|
|
return retval;
|
|
}
|
|
#ifdef CONFIG_BATTERY_SAMSUNG
|
|
extern unsigned int lpcharge;
|
|
#endif
|
|
static int fts_probe(struct i2c_client *client, const struct i2c_device_id *idp)
|
|
{
|
|
int retval;
|
|
struct fts_ts_info *info = NULL;
|
|
static char fts_ts_phys[64] = { 0 };
|
|
int i = 0;
|
|
#ifdef SEC_TSP_FACTORY_TEST
|
|
long j = 0;
|
|
#endif
|
|
tsp_debug_info(true, &client->dev, "FTS Driver [12%s]\n", FTS_TS_DRV_VERSION);
|
|
|
|
if (!i2c_check_functionality(client->adapter, I2C_FUNC_I2C)) {
|
|
tsp_debug_err(true, &client->dev, "FTS err = EIO!\n");
|
|
return -EIO;
|
|
}
|
|
#ifdef CONFIG_BATTERY_SAMSUNG
|
|
if (lpcharge == 1) {
|
|
tsp_debug_err(true, &client->dev, "%s : Do not load driver due to : lpm %d\n",
|
|
__func__, lpcharge);
|
|
return -ENODEV;
|
|
}
|
|
#endif
|
|
/* Build up driver data */
|
|
retval = fts_setup_drv_data(client);
|
|
if (retval < 0) {
|
|
tsp_debug_err(true, &client->dev, "%s: Failed to set up driver data\n", __func__);
|
|
goto err_setup_drv_data;
|
|
}
|
|
|
|
info = (struct fts_ts_info *)i2c_get_clientdata(client);
|
|
if (!info) {
|
|
tsp_debug_err(true, &client->dev, "%s: Failed to get driver data\n", __func__);
|
|
retval = -ENODEV;
|
|
goto err_get_drv_data;
|
|
}
|
|
|
|
if (info->board->power)
|
|
info->board->power(info, true);
|
|
|
|
info->dev = &info->client->dev;
|
|
info->input_dev = input_allocate_device();
|
|
if (!info->input_dev) {
|
|
tsp_debug_info(true, &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/input1",
|
|
info->input_dev->name);
|
|
info->input_dev->phys = fts_ts_phys;
|
|
info->input_dev->id.bustype = BUS_I2C;
|
|
|
|
#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
|
|
if (info->board->support_2ndscreen)
|
|
set_bit(BTN_SUBSCREEN_FLAG, info->input_dev->keybit);
|
|
#endif
|
|
#ifdef FTS_SUPPORT_SIDE_SCROLL
|
|
if (info->board->support_sidescroll) {
|
|
set_bit(BTN_R_FLICK_FLAG, info->input_dev->keybit);
|
|
set_bit(BTN_L_FLICK_FLAG, info->input_dev->keybit);
|
|
}
|
|
#endif
|
|
#ifdef FTS_SUPPORT_STRINGLIB
|
|
set_bit(KEY_BLACK_UI_GESTURE, info->input_dev->keybit);
|
|
#endif
|
|
#ifdef FTS_SUPPORT_TOUCH_KEY
|
|
if (info->board->support_mskey) {
|
|
for (i = 0 ; i < info->board->num_touchkey ; i++)
|
|
set_bit(info->board->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->board->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->board->max_x, 0, 0);
|
|
input_set_abs_params(info->input_dev, ABS_MT_POSITION_Y,
|
|
0, info->board->max_y, 0, 0);
|
|
|
|
mutex_init(&info->lock);
|
|
mutex_init(&info->device_mutex);
|
|
mutex_init(&info->i2c_mutex);
|
|
|
|
info->enabled = false;
|
|
mutex_lock(&info->lock);
|
|
retval = fts_init(info);
|
|
info->reinit_done = true;
|
|
mutex_unlock(&info->lock);
|
|
if (retval) {
|
|
tsp_debug_err(true, &info->client->dev, "FTS fts_init fail!\n");
|
|
goto err_fts_init;
|
|
}
|
|
|
|
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);
|
|
#ifdef FTS_SUPPORT_SEC_SWIPE
|
|
input_set_abs_params(info->input_dev, ABS_MT_PALM, 0, 1, 0, 0);
|
|
#endif
|
|
#if defined(FTS_SUPPORT_SIDE_GESTURE)
|
|
if (info->board->support_sidegesture)
|
|
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) {
|
|
tsp_debug_err(true, &info->client->dev, "FTS input_register_device fail!\n");
|
|
goto err_register_input;
|
|
}
|
|
|
|
for (i = 0; i < FINGER_MAX; i++) {
|
|
info->finger[i].state = EVENTID_LEAVE_POINTER;
|
|
info->finger[i].mcount = 0;
|
|
}
|
|
|
|
info->enabled = true;
|
|
|
|
retval = fts_irq_enable(info, true);
|
|
if (retval < 0) {
|
|
tsp_debug_info(true, &info->client->dev,
|
|
"%s: Failed to enable attention interrupt\n",
|
|
__func__);
|
|
goto err_enable_irq;
|
|
}
|
|
|
|
#ifdef CONFIG_TRUSTONIC_TRUSTED_UI
|
|
trustedui_set_tsp_irq(info->irq);
|
|
tsp_debug_info(true, &client->dev, "%s[%d] called!\n",
|
|
__func__, info->irq);
|
|
#endif
|
|
|
|
#ifdef CONFIG_HAS_EARLYSUSPEND
|
|
info->early_suspend.level = EARLY_SUSPEND_LEVEL_BLANK_SCREEN + 1;
|
|
info->early_suspend.suspend = fts_early_suspend;
|
|
info->early_sfts_start_deviceuspend.resume = fts_late_resume;
|
|
register_early_suspend(&info->early_suspend);
|
|
#endif
|
|
|
|
#ifdef FTS_SUPPORT_TA_MODE
|
|
info->register_cb = info->board->register_cb;
|
|
|
|
info->callbacks.inform_charger = fts_ta_cb;
|
|
if (info->register_cb)
|
|
info->register_cb(&info->callbacks);
|
|
#endif
|
|
|
|
#ifdef SEC_TSP_FACTORY_TEST
|
|
INIT_LIST_HEAD(&info->cmd_list_head);
|
|
|
|
info->cmd_buffer_size = 0;
|
|
for (j = 0; j < ARRAY_SIZE(ft_commands); j++) {
|
|
list_add_tail(&ft_commands[j].list, &info->cmd_list_head);
|
|
if(ft_commands[j].cmd_name)
|
|
info->cmd_buffer_size += strlen(ft_commands[j].cmd_name) + 1;
|
|
}
|
|
|
|
mutex_init(&info->cmd_lock);
|
|
info->cmd_is_running = false;
|
|
|
|
info->fac_dev_ts = sec_device_create(info, "tsp");
|
|
if (IS_ERR(info->fac_dev_ts)) {
|
|
tsp_debug_err(true, &info->client->dev, "FTS Failed to create device for the sysfs\n");
|
|
retval = -ENOENT;
|
|
goto err_sysfs;
|
|
}
|
|
|
|
dev_set_drvdata(info->fac_dev_ts, info);
|
|
|
|
retval = sysfs_create_group(&info->fac_dev_ts->kobj,
|
|
&sec_touch_factory_attr_group);
|
|
if (retval < 0) {
|
|
tsp_debug_err(true, &info->client->dev, "FTS Failed to create sysfs group\n");
|
|
goto err_sysfs;
|
|
}
|
|
|
|
retval = sysfs_create_link(&info->fac_dev_ts->kobj,
|
|
&info->input_dev->dev.kobj, "input");
|
|
|
|
if (retval < 0) {
|
|
tsp_debug_err(true, &info->client->dev,
|
|
"%s: Failed to create link\n", __func__);
|
|
goto err_sysfs;
|
|
}
|
|
|
|
#ifdef FTS_SUPPORT_TOUCH_KEY
|
|
if (info->board->support_mskey) {
|
|
info->fac_dev_tk = sec_device_create(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
|
|
#endif
|
|
|
|
device_init_wakeup(&client->dev, true);
|
|
|
|
return 0;
|
|
|
|
#ifdef SEC_TSP_FACTORY_TEST
|
|
err_sysfs:
|
|
if (info->irq_enabled)
|
|
fts_irq_enable(info, false);
|
|
mutex_destroy(&info->cmd_lock);
|
|
#endif
|
|
|
|
err_enable_irq:
|
|
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:
|
|
mutex_destroy(&info->lock);
|
|
mutex_destroy(&info->device_mutex);
|
|
mutex_destroy(&info->i2c_mutex);
|
|
err_input_allocate_device:
|
|
info->board->power(info, false);
|
|
kfree(info);
|
|
err_get_drv_data:
|
|
err_setup_drv_data:
|
|
return retval;
|
|
}
|
|
|
|
static int fts_remove(struct i2c_client *client)
|
|
{
|
|
struct fts_ts_info *info = i2c_get_clientdata(client);
|
|
|
|
tsp_debug_info(true, &info->client->dev, "FTS removed \n");
|
|
|
|
#ifdef CONFIG_HAS_EARLYSUSPEND
|
|
unregister_early_suspend(&info->early_suspend);
|
|
#endif
|
|
|
|
fts_interrupt_set(info, INT_DISABLE);
|
|
fts_command(info, FLUSHBUFFER);
|
|
|
|
fts_irq_enable(info, false);
|
|
|
|
input_mt_destroy_slots(info->input_dev);
|
|
|
|
#ifdef SEC_TSP_FACTORY_TEST
|
|
#ifdef FTS_SUPPORT_TOUCH_KEY
|
|
if (info->board->support_mskey) {
|
|
sysfs_remove_link(&info->fac_dev_tk->kobj, "input");
|
|
sysfs_remove_group(&info->fac_dev_tk->kobj,
|
|
&sec_touchkey_factory_attr_group);
|
|
sec_device_destroy(info->fac_dev_tk->devt);
|
|
}
|
|
#endif
|
|
sysfs_remove_link(&info->fac_dev_ts->kobj, "input");
|
|
sysfs_remove_group(&info->fac_dev_ts->kobj,
|
|
&sec_touch_factory_attr_group);
|
|
sec_device_destroy(info->fac_dev_ts->devt);
|
|
|
|
list_del(&info->cmd_list_head);
|
|
|
|
mutex_destroy(&info->cmd_lock);
|
|
|
|
if (info->cx_data)
|
|
kfree(info->cx_data);
|
|
|
|
if (info->pFrame)
|
|
kfree(info->pFrame);
|
|
#endif
|
|
|
|
mutex_destroy(&info->lock);
|
|
|
|
input_unregister_device(info->input_dev);
|
|
info->input_dev = NULL;
|
|
|
|
info->board->power(info, false);
|
|
|
|
kfree(info);
|
|
|
|
return 0;
|
|
}
|
|
|
|
#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);
|
|
|
|
tsp_debug_info(true, &info->client->dev, "%s\n", __func__);
|
|
|
|
retval = fts_start_device(info);
|
|
if (retval < 0)
|
|
tsp_debug_err(true, &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);
|
|
int retval;
|
|
|
|
tsp_debug_dbg(false, &info->client->dev, "%s\n", __func__);
|
|
|
|
#ifdef USE_OPEN_DWORK
|
|
schedule_delayed_work(&info->open_work,
|
|
msecs_to_jiffies(TOUCH_OPEN_DWORK_TIME));
|
|
#else
|
|
retval = fts_start_device(info);
|
|
if (retval < 0){
|
|
tsp_debug_err(true, &info->client->dev,
|
|
"%s: Failed to start device\n", __func__);
|
|
goto out;
|
|
}
|
|
#endif
|
|
|
|
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, ®Add[0], 4);
|
|
fts_command(info, FTS_CMD_HOVER_ON);
|
|
info->hover_enabled = true;
|
|
}
|
|
|
|
out:
|
|
return 0;
|
|
}
|
|
|
|
static void fts_input_close(struct input_dev *dev)
|
|
{
|
|
struct fts_ts_info *info = input_get_drvdata(dev);
|
|
|
|
tsp_debug_dbg(false, &info->client->dev, "%s\n", __func__);
|
|
|
|
#ifdef USE_OPEN_DWORK
|
|
cancel_delayed_work(&info->open_work);
|
|
#endif
|
|
|
|
fts_stop_device(info);
|
|
|
|
info->retry_hover_enable_after_wakeup = 0;
|
|
}
|
|
#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)
|
|
tsp_debug_err(true, &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)) {
|
|
tsp_debug_err(true, &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.*/
|
|
#ifdef CONFIG_SEC_FACTORY
|
|
info->panel_revision = lcdtype[3] & 0x0F;
|
|
tsp_debug_info(true, &info->client->dev,
|
|
"%s: update panel_revision 0x%02X\n", __func__, info->panel_revision);
|
|
#endif
|
|
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;
|
|
|
|
fts_command(info, SLEEPOUT);
|
|
fts_delay(50);
|
|
|
|
fts_command(info, SENSEON);
|
|
fts_delay(50);
|
|
|
|
#ifdef FTS_SUPPORT_TOUCH_KEY
|
|
if (info->board->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);
|
|
|
|
/* enable glove touch when flip cover is closed */
|
|
if (info->fast_mshover_enabled)
|
|
fts_command(info, FTS_CMD_SET_FAST_GLOVE_MODE);
|
|
else if (info->mshover_enabled)
|
|
fts_command(info, FTS_CMD_MSHOVER_ON);
|
|
|
|
rc = fts_get_channel_info(info);
|
|
if (rc >= 0) {
|
|
tsp_debug_info(true, &info->client->dev, "FTS Sense(%02d) Force(%02d)\n",
|
|
info->SenseChannelLength, info->ForceChannelLength);
|
|
} else {
|
|
tsp_debug_info(true, &info->client->dev, "FTS read failed rc = %d\n", rc);
|
|
tsp_debug_info(true, &info->client->dev, "FTS Initialise Failed\n");
|
|
return;
|
|
}
|
|
|
|
info->pFrame =
|
|
kzalloc(info->SenseChannelLength * info->ForceChannelLength * 2,
|
|
GFP_KERNEL);
|
|
if (info->pFrame == NULL) {
|
|
tsp_debug_info(true, &info->client->dev, "FTS pFrame kzalloc Failed\n");
|
|
return;
|
|
}
|
|
|
|
info->cx_data = kzalloc(info->SenseChannelLength * info->ForceChannelLength, GFP_KERNEL);
|
|
if (!info->cx_data)
|
|
tsp_debug_err(true, &info->client->dev, "%s: cx_data kzalloc Failed\n", __func__);
|
|
|
|
fts_command(info, FORCECALIBRATION);
|
|
fts_command(info, FLUSHBUFFER);
|
|
|
|
fts_interrupt_set(info, INT_ENABLE);
|
|
|
|
tsp_debug_info(true, &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
|
|
|
|
fts_command(info, SLEEPOUT);
|
|
fts_delay(50);
|
|
|
|
fts_command(info, SENSEON);
|
|
fts_delay(50);
|
|
|
|
#ifdef FTS_SUPPORT_TOUCH_KEY
|
|
if (info->board->support_mskey)
|
|
info->fts_command(info, FTS_CMD_KEY_SENSE_ON);
|
|
#endif
|
|
|
|
if (info->flip_enable)
|
|
fts_set_cover_type(info, true);
|
|
else if (info->fast_mshover_enabled)
|
|
fts_command(info, FTS_CMD_SET_FAST_GLOVE_MODE);
|
|
else if (info->mshover_enabled)
|
|
fts_command(info, FTS_CMD_MSHOVER_ON);
|
|
|
|
#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
|
|
if (info->board->support_2ndscreen) {
|
|
info->SIDE_Flag = 0;
|
|
info->previous_SIDE_value = 0;
|
|
}
|
|
#endif
|
|
|
|
fts_command(info, FLUSHBUFFER);
|
|
fts_interrupt_set(info, INT_ENABLE);
|
|
}
|
|
|
|
void fts_release_all_finger(struct fts_ts_info *info)
|
|
{
|
|
int i;
|
|
|
|
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;
|
|
|
|
tsp_debug_info(true, &info->client->dev,
|
|
"[RA] 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->mshover_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
|
|
if (info->board->support_2ndscreen) {
|
|
info->SIDE_Flag = 0;
|
|
info->previous_SIDE_value = 0;
|
|
input_report_key(info->input_dev, BTN_SUBSCREEN_FLAG, 0);
|
|
}
|
|
#endif
|
|
#ifdef FTS_SUPPORT_SIDE_SCROLL
|
|
if (info->board->support_sidescroll) {
|
|
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
|
|
#ifdef FTS_SUPPORT_STRINGLIB
|
|
input_report_key(info->input_dev, KEY_BLACK_UI_GESTURE, 0);
|
|
#endif
|
|
#ifdef FTS_SUPPORT_SIDE_GESTURE
|
|
if (info->board->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
|
|
|
|
#ifdef CONFIG_INPUT_BOOSTER
|
|
input_booster_send_event(BOOSTER_DEVICE_TOUCH, BOOSTER_MODE_FORCE_OFF);
|
|
#endif
|
|
|
|
input_sync(info->input_dev);
|
|
}
|
|
|
|
#ifdef CONFIG_SEC_DEBUG_TSP_LOG
|
|
static void dump_tsp_rawdata(struct work_struct *work)
|
|
{
|
|
struct fts_ts_info *info = container_of(work, struct fts_ts_info,
|
|
debug_work.work);
|
|
int i;
|
|
|
|
if (info->rawdata_read_lock == true)
|
|
tsp_debug_err(true, &info->client->dev, "%s, ## checking.. ignored.\n", __func__);
|
|
|
|
info->rawdata_read_lock = true;
|
|
tsp_debug_info(true, &info->client->dev, "%s, ## run CX data ##, %d\n", __func__, __LINE__);
|
|
run_cx_data_read((void *)info);
|
|
|
|
for (i = 0; i < 5; i++) {
|
|
tsp_debug_info(true, &info->client->dev, "%s, ## run Raw Cap data ##, %d\n", __func__, __LINE__);
|
|
run_rawcap_read((void *)info);
|
|
|
|
tsp_debug_info(true, &info->client->dev, "%s, ## run Delta ##, %d\n", __func__, __LINE__);
|
|
run_delta_read((void *)info);
|
|
fts_delay(50);
|
|
}
|
|
tsp_debug_info(true, &info->client->dev, "%s, ## Done ##, %d\n", __func__, __LINE__);
|
|
|
|
info->rawdata_read_lock = false;
|
|
}
|
|
|
|
void tsp_dump(void)
|
|
{
|
|
#ifdef CONFIG_BATTERY_SAMSUNG
|
|
if (lpcharge)
|
|
return;
|
|
#endif
|
|
if (!p_debug_work)
|
|
return;
|
|
|
|
printk(KERN_ERR "FTS %s: start \n", __func__);
|
|
schedule_delayed_work(p_debug_work, msecs_to_jiffies(100));
|
|
}
|
|
#endif
|
|
|
|
static void fts_reset_work(struct work_struct *work)
|
|
{
|
|
struct fts_ts_info *info = container_of(work, struct fts_ts_info,
|
|
reset_work.work);
|
|
bool temp_lpm;
|
|
|
|
temp_lpm = info->lowpower_mode;
|
|
/* Reset-routine must go to power off state */
|
|
info->lowpower_mode = 0;
|
|
|
|
tsp_debug_info(true, &info->client->dev,"%s, Call Power-Off to recover IC, lpm:%d\n", __func__, temp_lpm);
|
|
fts_stop_device(info);
|
|
|
|
msleep(100); /* Delay to discharge the IC from ESD or On-state.*/
|
|
if (fts_start_device(info) < 0) {
|
|
tsp_debug_err(true, &info->client->dev, "%s: Failed to start device\n", __func__);
|
|
}
|
|
|
|
info->lowpower_mode = temp_lpm;
|
|
}
|
|
|
|
|
|
static int fts_stop_device(struct fts_ts_info *info)
|
|
{
|
|
tsp_debug_info(true, &info->client->dev, "%s\n", __func__);
|
|
|
|
mutex_lock(&info->device_mutex);
|
|
|
|
if (info->touch_stopped) {
|
|
tsp_debug_err(true, &info->client->dev, "%s already power off\n", __func__);
|
|
goto out;
|
|
}
|
|
|
|
if (info->lowpower_mode) {
|
|
#ifdef FTS_SUPPORT_SIDE_GESTURE
|
|
if (info->board->support_sidegesture) {
|
|
tsp_debug_info(true, &info->client->dev, "%s mainscreen disable flag:%d, clear\n", __func__, info->mainscr_disable);
|
|
set_mainscreen_disable_cmd((void *)info, 0);
|
|
}
|
|
#endif
|
|
tsp_debug_info(true, &info->client->dev, "%s lowpower flag:%d\n", __func__, info->lowpower_flag);
|
|
|
|
info->fts_power_state = FTS_POWER_STATE_LOWPOWER;
|
|
|
|
fts_command(info, FLUSHBUFFER);
|
|
|
|
#ifdef FTS_SUPPORT_SIDE_GESTURE
|
|
if (info->board->support_sidegesture) {
|
|
fts_enable_feature(info, FTS_FEATURE_SIDE_GUSTURE, true);
|
|
fts_delay(20);
|
|
}
|
|
#endif
|
|
fts_command(info, FTS_CMD_LOWPOWER_MODE);
|
|
|
|
if (device_may_wakeup(&info->client->dev))
|
|
enable_irq_wake(info->irq);
|
|
|
|
fts_command(info, FLUSHBUFFER);
|
|
|
|
fts_release_all_finger(info);
|
|
#ifdef FTS_SUPPORT_TOUCH_KEY
|
|
fts_release_all_key(info);
|
|
#endif
|
|
#ifdef FTS_SUPPORT_NOISE_PARAM
|
|
fts_get_noise_param(info);
|
|
#endif
|
|
} else {
|
|
fts_interrupt_set(info, INT_DISABLE);
|
|
disable_irq(info->irq);
|
|
|
|
fts_command(info, FLUSHBUFFER);
|
|
fts_command(info, SLEEPIN);
|
|
fts_release_all_finger(info);
|
|
#ifdef FTS_SUPPORT_TOUCH_KEY
|
|
fts_release_all_key(info);
|
|
#endif
|
|
#ifdef FTS_SUPPORT_NOISE_PARAM
|
|
fts_get_noise_param(info);
|
|
#endif
|
|
info->touch_stopped = true;
|
|
info->hover_enabled = false;
|
|
info->hover_ready = false;
|
|
|
|
if (info->board->power)
|
|
info->board->power(info, false);
|
|
|
|
info->fts_power_state = FTS_POWER_STATE_POWERDOWN;
|
|
}
|
|
out:
|
|
mutex_unlock(&info->device_mutex);
|
|
return 0;
|
|
}
|
|
|
|
static int fts_start_device(struct fts_ts_info *info)
|
|
{
|
|
tsp_debug_info(true, &info->client->dev, "%s %s\n",
|
|
__func__, info->lowpower_mode ? "exit low power mode" : "");
|
|
|
|
mutex_lock(&info->device_mutex);
|
|
|
|
if (!info->touch_stopped && !info->lowpower_mode) {
|
|
tsp_debug_err(true, &info->client->dev, "%s already power on\n", __func__);
|
|
goto out;
|
|
}
|
|
|
|
fts_release_all_finger(info);
|
|
#ifdef FTS_SUPPORT_TOUCH_KEY
|
|
fts_release_all_key(info);
|
|
#endif
|
|
|
|
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;
|
|
|
|
disable_irq(info->irq);
|
|
|
|
info->reinit_done = false;
|
|
fts_reinit(info);
|
|
info->reinit_done = true;
|
|
|
|
enable_irq(info->irq);
|
|
|
|
if (device_may_wakeup(&info->client->dev))
|
|
disable_irq_wake(info->irq);
|
|
} else {
|
|
tsp_power_on:
|
|
if (info->board->power)
|
|
info->board->power(info, true);
|
|
info->touch_stopped = false;
|
|
info->reinit_done = false;
|
|
|
|
fts_reinit(info);
|
|
info->reinit_done = true;
|
|
|
|
enable_irq(info->irq);
|
|
}
|
|
|
|
#ifdef FTS_SUPPORT_STRINGLIB
|
|
if (info->fts_mode) {
|
|
unsigned short addr = FTS_CMD_STRING_ACCESS;
|
|
int ret;
|
|
|
|
ret = info->fts_write_to_string(info, &addr, &info->fts_mode, sizeof(info->fts_mode));
|
|
if (ret < 0)
|
|
tsp_debug_err(true, &info->client->dev, "%s: failed. ret: %d\n", __func__, ret);
|
|
}
|
|
#endif
|
|
if (strncmp(info->board->model_name, "G925", 4) == 0) {
|
|
info->tspid_val= gpio_get_value(info->board->tspid);
|
|
info->tspid2_val= gpio_get_value(info->board->tspid2);
|
|
}
|
|
|
|
out:
|
|
mutex_unlock(&info->device_mutex);
|
|
|
|
info->fts_power_state = FTS_POWER_STATE_ACTIVE;
|
|
|
|
return 0;
|
|
}
|
|
|
|
static void fts_shutdown(struct i2c_client *client)
|
|
{
|
|
struct fts_ts_info *info = i2c_get_clientdata(client);
|
|
|
|
tsp_debug_info(true, &info->client->dev, "FTS %s called!\n", __func__);
|
|
|
|
if (info->lowpower_mode) {
|
|
info->lowpower_mode = 0;
|
|
tsp_debug_info(true, &info->client->dev, "FTS lowpower_mode off!\n");
|
|
}
|
|
|
|
fts_stop_device(info);
|
|
#ifdef FTS_SUPPORT_TOUCH_KEY
|
|
if (info->board->led_power)
|
|
info->board->led_power(info, false);
|
|
#endif
|
|
}
|
|
|
|
#ifdef CONFIG_PM
|
|
static int fts_pm_suspend(struct device *dev)
|
|
{
|
|
struct fts_ts_info *info = dev_get_drvdata(dev);
|
|
|
|
tsp_debug_err(true, &info->client->dev, "%s\n", __func__);;
|
|
|
|
mutex_lock(&info->input_dev->mutex);
|
|
|
|
if (info->input_dev->users)
|
|
fts_stop_device(info);
|
|
|
|
mutex_unlock(&info->input_dev->mutex);
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int fts_pm_resume(struct device *dev)
|
|
{
|
|
struct fts_ts_info *info = dev_get_drvdata(dev);
|
|
|
|
tsp_debug_err(true, &info->client->dev, "%s\n", __func__);
|
|
|
|
mutex_lock(&info->input_dev->mutex);
|
|
|
|
if (info->input_dev->users)
|
|
fts_start_device(info);
|
|
|
|
mutex_unlock(&info->input_dev->mutex);
|
|
|
|
return 0;
|
|
}
|
|
#endif
|
|
|
|
#if (!defined(CONFIG_HAS_EARLYSUSPEND)) && (!defined(CONFIG_PM)) && !defined(USE_OPEN_CLOSE)
|
|
static int fts_suspend(struct i2c_client *client, pm_message_t mesg)
|
|
{
|
|
struct fts_ts_info *info = i2c_get_clientdata(client);
|
|
|
|
tsp_debug_info(true, &info->client->dev, "%s\n", __func__);
|
|
|
|
fts_stop_device(info);
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int fts_resume(struct i2c_client *client)
|
|
{
|
|
|
|
struct fts_ts_info *info = i2c_get_clientdata(client);
|
|
|
|
tsp_debug_info(true, &info->client->dev, "%s\n", __func__);
|
|
|
|
fts_start_device(info);
|
|
|
|
return 0;
|
|
}
|
|
#endif
|
|
|
|
static const struct i2c_device_id fts_device_id[] = {
|
|
{FTS_TS_DRV_NAME, 0},
|
|
{}
|
|
};
|
|
|
|
#ifdef CONFIG_PM
|
|
static const struct dev_pm_ops fts_dev_pm_ops = {
|
|
.suspend = fts_pm_suspend,
|
|
.resume = fts_pm_resume,
|
|
};
|
|
#endif
|
|
|
|
#ifdef CONFIG_OF
|
|
static struct of_device_id fts_match_table[] = {
|
|
{ .compatible = "stm,fts_touch",},
|
|
{ },
|
|
};
|
|
#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_dev_pm_ops,
|
|
#endif
|
|
},
|
|
.probe = fts_probe,
|
|
.remove = fts_remove,
|
|
.shutdown = fts_shutdown,
|
|
#if (!defined(CONFIG_HAS_EARLYSUSPEND)) && (!defined(CONFIG_PM)) && !defined(USE_OPEN_CLOSE)
|
|
.suspend = fts_suspend,
|
|
.resume = fts_resume,
|
|
#endif
|
|
.id_table = fts_device_id,
|
|
};
|
|
|
|
static int __init fts_driver_init(void)
|
|
{
|
|
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);
|