blob: f7e527a46de29d9d263bae972e306b44eefe7bbc [file] [log] [blame]
/******************** (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>
#include <linux/input/mt.h>
#ifdef CONFIG_SEC_SYSFS
#include <linux/sec_sysfs.h>
#endif
#ifdef CONFIG_TRUSTONIC_TRUSTED_UI
#include <linux/t-base-tui.h>
#endif
#include "fts_ts.h"
#if defined(CONFIG_SECURE_TOUCH)
#include <linux/clk.h>
#include <linux/pm_runtime.h>
#include <linux/errno.h>
#include <linux/atomic.h>
#include <soc/qcom/scm.h>
/*#include <asm/system.h>*/
enum subsystem {
TZ = 1,
APSS = 3
};
#define TZ_BLSP_MODIFY_OWNERSHIP_ID 3
#endif
#ifdef CONFIG_OF
#ifndef USE_OPEN_CLOSE
#define USE_OPEN_CLOSE
#undef CONFIG_PM
#endif
#endif
#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, bool lpmode);
static int fts_start_device(struct fts_ts_info *info);
static void fts_reset(struct fts_ts_info *info, unsigned int ms);
static void fts_reset_work(struct work_struct *work);
static void fts_read_info_work(struct work_struct *work);
#if defined(CONFIG_TOUCHSCREEN_DUMP_MODE)
static void dump_tsp_rawdata(struct work_struct *work);
struct delayed_work *p_debug_work;
#endif
#if (!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
static int fts_wait_for_ready(struct fts_ts_info *info);
#if defined(CONFIG_SECURE_TOUCH)
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 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),
};
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);
if (ret) {
input_err(true, &info->client->dev, "%s: return: %d\n", __func__, ret);
return ret;
}
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) {
input_err(true, &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) {
input_err(true, &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);
input_err(true, &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) {
input_err(true, &info->client->dev,
"%s: cmd length is over (%s,%d)!!\n",
__func__, buf, (int)strlen(buf));
return -EINVAL;
}
err = kstrtoul(buf, 10, &value);
if (err != 0) {
input_err(true, &info->client->dev, "%s: failed to read:%d\n",
__func__, err);
return err;
}
err = count;
switch (value) {
case 0:
if (atomic_read(&info->st_enabled) == 0) {
input_err(true, &info->client->dev, "%s: secure_touch is not enabled, pending:%d\n",
__func__, atomic_read(&info->st_pending_irqs));
break;
}
fts_change_pipe_owner(info, APSS);
fts_secure_touch_clk_disable_unprepare(info);
pm_runtime_put_sync(info->client->adapter->dev.parent);
atomic_set(&info->st_enabled, 0);
sysfs_notify(&info->input_dev->dev.kobj, NULL, "secure_touch");
fts_delay(10);
fts_interrupt_handler(info->client->irq, info);
complete(&info->st_powerdown);
complete(&info->st_interrupt);
input_info(true, &info->client->dev, "%s: secure_touch is disabled\n", __func__);
break;
case 1:
if (info->reset_is_on_going) {
input_err(true, &info->client->dev, "%s: reset is on goning becuse i2c fail\n",
__func__);
return -EBUSY;
}
if (atomic_read(&info->st_enabled)) {
input_err(true, &info->client->dev, "%s: secure_touch is already enabled, pending:%d\n",
__func__, atomic_read(&info->st_pending_irqs));
err = -EBUSY;
break;
}
/* syncronize_irq -> disable_irq + enable_irq
* concern about timing issue.
*/
disable_irq(info->client->irq);
/* Release All Finger */
fts_release_all_finger(info);
if (pm_runtime_get_sync(info->client->adapter->dev.parent) < 0) {
input_err(true, &info->client->dev, "%s: pm_runtime_get failed\n", __func__);
err = -EIO;
enable_irq(info->client->irq);
break;
}
if (fts_secure_touch_clk_prepare_enable(info) < 0) {
input_err(true, &info->client->dev, "%s: clk_prepare_enable failed\n", __func__);
pm_runtime_put_sync(info->client->adapter->dev.parent);
err = -EIO;
enable_irq(info->client->irq);
break;
}
fts_change_pipe_owner(info, TZ);
reinit_completion(&info->st_powerdown);
reinit_completion(&info->st_interrupt);
atomic_set(&info->st_enabled, 1);
atomic_set(&info->st_pending_irqs, 0);
enable_irq(info->client->irq);
input_info(true, &info->client->dev, "%s: secure_touch is enabled\n", __func__);
break;
default:
input_err(true, &info->client->dev, "%s: unsupported value: %lu\n", __func__, value);
err = -EINVAL;
break;
}
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;
if (atomic_read(&info->st_enabled) == 0) {
input_err(true, &info->client->dev, "%s: secure_touch is not enabled, st_pending_irqs: %d\n",
__func__, atomic_read(&info->st_pending_irqs));
return -EBADF;
}
if (atomic_cmpxchg(&info->st_pending_irqs, -1, 0) == -1) {
input_err(true, &info->client->dev, "%s: st_pending_irqs: %d\n",
__func__, atomic_read(&info->st_pending_irqs));
return -EINVAL;
}
if (atomic_cmpxchg(&info->st_pending_irqs, 1, 0) == 1)
val = 1;
input_info(true, &info->client->dev, "%s: st_pending_irqs: %d, val: %d\n",
__func__, atomic_read(&info->st_pending_irqs), val);
complete(&info->st_interrupt);
return scnprintf(buf, PAGE_SIZE, "%u", val);
}
static void fts_secure_touch_init(struct fts_ts_info *info)
{
int ret;
init_completion(&info->st_powerdown);
init_completion(&info->st_interrupt);
info->core_clk = clk_get(&info->client->adapter->dev, "core_clk");
if (IS_ERR(info->core_clk)) {
ret = PTR_ERR(info->core_clk);
input_err(true, &info->client->dev, "%s: error on clk_get(core_clk):%d\n",
__func__, ret);
return;
}
info->iface_clk = clk_get(&info->client->adapter->dev, "iface_clk");
if (IS_ERR(info->iface_clk)) {
ret = PTR_ERR(info->core_clk);
input_err(true, &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;
}
static void fts_secure_touch_stop(struct fts_ts_info *info, int blocking)
{
if (atomic_read(&info->st_enabled)) {
atomic_set(&info->st_pending_irqs, -1);
sysfs_notify(&info->input_dev->dev.kobj, NULL, "secure_touch");
if (blocking)
wait_for_completion_interruptible(&info->st_powerdown);
}
}
static irqreturn_t fts_filter_interrupt(struct fts_ts_info *info)
{
if (atomic_read(&info->st_enabled)) {
if (atomic_cmpxchg(&info->st_pending_irqs, 0, 1) == 0)
sysfs_notify(&info->input_dev->dev.kobj, NULL, "secure_touch");
else
input_info(true, &info->client->dev, "%s: st_pending_irqs: %d\n",
__func__, atomic_read(&info->st_pending_irqs));
return IRQ_HANDLED;
}
return IRQ_NONE;
}
#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;
int retry = FTS_TS_I2C_RETRY_CNT;
if (info->touch_stopped) {
input_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()) {
input_err(true, &info->client->dev,
"%s: TSP no accessible from Linux, TUI is enabled!\n", __func__);
return -EIO;
}
#endif
#ifdef CONFIG_SECURE_TOUCH
if (atomic_read(&info->st_enabled)) {
input_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;
do {
ret = i2c_transfer(info->client->adapter, xfer_msg, 1);
if (ret < 0) {
info->comm_err_count++;
input_err(true, &info->client->dev,
"%s failed(%d). ret:%d, addr:%x, cnt:%d\n",
__func__, retry, ret, xfer_msg[0].addr, info->comm_err_count);
usleep_range(10 * 1000, 10 * 1000);
} else {
break;
}
} while (--retry > 0);
mutex_unlock(&info->i2c_mutex);
if (retry == 0) {
input_err(true, &info->client->dev, "%s: I2C read over retry limit\n", __func__);
ret = -EIO;
#ifdef USE_POR_AFTER_I2C_RETRY
if (info->probe_done && !info->reset_is_on_going)
schedule_delayed_work(&info->reset_work, msecs_to_jiffies(10));
#endif
}
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;
int retry = FTS_TS_I2C_RETRY_CNT;
if (info->touch_stopped) {
input_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()) {
input_err(true, &info->client->dev,
"%s: TSP no accessible from Linux, TUI is enabled!\n", __func__);
return -EIO;
}
#endif
#ifdef CONFIG_SECURE_TOUCH
if (atomic_read(&info->st_enabled)) {
input_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;
do {
ret = i2c_transfer(info->client->adapter, xfer_msg, 2);
if (ret < 0) {
info->comm_err_count++;
input_err(true, &info->client->dev,
"%s failed(%d). ret:%d, addr:%x, cnt:%d\n",
__func__, retry, ret, xfer_msg[0].addr, info->comm_err_count);
usleep_range(10 * 1000, 10 * 1000);
} else {
break;
}
} while (--retry > 0);
mutex_unlock(&info->i2c_mutex);
if (retry == 0) {
input_err(true, &info->client->dev, "%s: I2C read over retry limit\n", __func__);
ret = -EIO;
#ifdef USE_POR_AFTER_I2C_RETRY
if (info->probe_done && !info->reset_is_on_going)
schedule_delayed_work(&info->reset_work, msecs_to_jiffies(10));
#endif
}
return ret;
exit:
return 0;
}
#ifdef FTS_SUPPORT_STRINGLIB
#ifdef CONFIG_SEC_FACTORY
static void fts_disable_string(struct fts_ts_info *info)
{
unsigned char regAdd[4] = {0xb0, 0x01, 0x10, 0x8f};
int ret = 0;
ret = fts_write_reg(info, &regAdd[0], 4);
input_info(true, &info->client->dev, "%s: String Library Disabled, ret = %d\n", __func__, ret);
}
#endif
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 rtn;
#ifdef CONFIG_TRUSTONIC_TRUSTED_UI
if (TRUSTEDUI_MODE_INPUT_SECURED & trustedui_get_current_mode()) {
input_err(true, &info->client->dev,
"%s: TSP no accessible from Linux, TUI is enabled!\n", __func__);
return -EIO;
}
#endif
#ifdef CONFIG_SECURE_TOUCH
if (atomic_read(&info->st_enabled)) {
input_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;
buf = kzalloc(length + 1, GFP_KERNEL);
if (buf == NULL) {
input_info(true, &info->client->dev,
"%s: kzalloc error.\n", __func__);
return -ENOMEM;
}
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 display lab 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) {
input_err(true, &info->client->dev, "%s: Sensor stopped\n", __func__);
return 0;
}
#ifdef CONFIG_TRUSTONIC_TRUSTED_UI
if (TRUSTEDUI_MODE_INPUT_SECURED & trustedui_get_current_mode()) {
input_err(true, &info->client->dev,
"%s: TSP no accessible from Linux, TUI is enabled!\n", __func__);
return -EIO;
}
#endif
#ifdef CONFIG_SECURE_TOUCH
if (atomic_read(&info->st_enabled)) {
input_err(true, &info->client->dev,
"%s: TSP no accessible from Linux, TUI is enabled!\n", __func__);
return -EIO;
}
#endif
regAdd = kzalloc(length + 6, GFP_KERNEL);
if (regAdd == NULL) {
input_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) {
input_dbg(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)
input_err(true, &info->client->dev,
"%s: string notify is failed.\n", __func__);
else
input_info(true, &info->client->dev,
"%s: string notify is OK[0x%02X].\n", __func__, *data);
} else {
input_err(true, &info->client->dev,
"%s: string command is failed. ret: %d\n", __func__, ret);
}
mutex_unlock(&info->i2c_mutex);
kfree(regAdd);
return ret;
}
int fts_check_custom_library(struct fts_ts_info *info)
{
u8 regAdd[3] = {0xD0, 0x00, 0x00};
u8 data[15] = { 0 };
int ret = -1;
unsigned short addr = FTS_CMD_STRING_ACCESS;
regAdd[2] = 0x7A;
ret = fts_read_reg(info, regAdd, 3, data, 15);
if (ret < 0) {
input_err(true, &info->client->dev, "%s: failed. ret: %d\n", __func__, ret);
goto out;
}
regAdd[1] = data[2];
regAdd[2] = data[1];
ret = fts_read_reg(info, regAdd, 3, data, 5);
if (ret < 0) {
input_err(true, &info->client->dev, "%s: failed. ret: %d\n", __func__, ret);
goto out;
}
input_info(true, &info->client->dev,
"%s: (%d) %02X%02X %c%c%c%c\n",
__func__, ret, regAdd[1], regAdd[2], data[1], data[2], data[3], data[4]);
/* compare model name with device tree */
if (info->board->model_name)
ret = strncmp(&data[1], info->board->model_name, 4);
if (ret == 0)
info->use_sponge = true;
else
info->use_sponge = false;
if (info->use_sponge) {
ret = fts_write_to_string(info, &addr, &info->lowpower_flag, sizeof(info->lowpower_flag));
if (ret < 0)
input_err(true, &info->client->dev, "%s: failed. ret: %d\n", __func__, ret);
}
out:
input_err(true, &info->client->dev, "%s: use %s\n",
__func__, info->use_sponge ? "SPONGE" : "VENDOR");
return ret;
}
#endif
void fts_delay(unsigned int ms)
{
if (ms < 20)
usleep_range(ms * 1000, ms * 1000);
else
msleep(ms);
}
int fts_command(struct fts_ts_info *info, unsigned char cmd)
{
unsigned char regAdd = 0;
int ret = 0;
regAdd = cmd;
ret = fts_write_reg(info, &regAdd, 1);
input_info(true, &info->client->dev, "%s: (%02X), ret = %d\n", __func__, cmd, ret);
return 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, &regAdd[0], 2);
input_info(true, &info->client->dev, "%s: %s (%02X %02X), ret = %d\n", __func__,
(enable) ? "Enable":"Disable", regAdd[0], regAdd[1], ret);
}
static void fts_set_cover_type(struct fts_ts_info *info, bool enable)
{
bool enabled = enable;
input_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_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:
enabled = false;
input_err(true, &info->client->dev, "%s: not change touch state, %d\n",
__func__, info->cover_type);
break;
}
#ifdef FTS_SUPPORT_PRESSURE_SENSOR
if (enable && enabled)
fts_command(info, FTS_CMD_PRESSURE_SENSE_OFF);
else
fts_command(info, FTS_CMD_PRESSURE_SENSE_ON);
#endif
}
void fts_set_grip_type(struct fts_ts_info *info, u8 set_type)
{
u8 mode = G_NONE;
input_info(true, &info->client->dev, "%s: re-init grip(%d), edh:%d, edg:%d, lan:%d\n", __func__,\
set_type, info->grip_edgehandler_direction, info->grip_edge_range, info->grip_landscape_mode);
/* edge handler */
if (info->grip_edgehandler_direction != 0)
mode |= G_SET_EDGE_HANDLER;
if (set_type == GRIP_ALL_DATA) {
/* edge */
if (info->grip_edge_range != 60)
mode |= G_SET_EDGE_ZONE;
/* dead zone */
if (info->grip_landscape_mode == 1) /* default 0 mode, 32 */
mode |= G_SET_LANDSCAPE_MODE;
else
mode |= G_SET_NORMAL_MODE;
}
if (mode)
fts_set_grip_data_to_ic(info, mode);
}
static void fts_wirelesscharger_mode(struct fts_ts_info *info)
{
unsigned char regAdd[2] = {0xC2, 0x10};
int ret;
if (info->wirelesscharger_mode == 0)
regAdd[0] = 0xC2;
else
regAdd[0] = 0xC1;
input_info(true, &info->client->dev, "%s: Set W-Charger Status CMD[%2X]\n", __func__, regAdd[0]);
ret = fts_write_reg(info, regAdd, 2);
if (ret < 0)
input_err(true, &info->client->dev, "%s: failed. ret: %d\n", __func__, ret);
}
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, &regAdd[0], 2);
input_dbg(true, &info->client->dev, "%s: %s (%02X %02X), ret = %d\n", __func__,
(cmd == FTS_CMD_FAST_SCAN) ? "90Hz" : (cmd == FTS_CMD_SLOW_SCAN) ? "60Hz" : "30Hz",
regAdd[0], regAdd[1], ret);
}
int fts_systemreset(struct fts_ts_info *info, unsigned int delay)
{
unsigned char regAdd[4] = { 0xB6, 0x00, 0x28, 0x80 };
int rc;
#ifdef FTS_SUPPORT_STRINGLIB
int ret;
unsigned short addr = FTS_CMD_STRING_ACCESS;
#endif
fts_write_reg(info, &regAdd[0], 4);
fts_delay(delay);
rc = fts_wait_for_ready(info);
#ifndef CONFIG_SEC_FACTORY
if (info->lowpower_flag)
#endif
{
#ifdef FTS_SUPPORT_STRINGLIB
ret = info->fts_write_to_string(info, &addr, &info->lowpower_flag, sizeof(info->lowpower_flag));
if (ret < 0)
input_err(true, &info->client->dev, "%s: failed. ret: %d\n", __func__, ret);
#endif
}
/* do not consider about sponge read/write fail after syste reset routine. */
return rc;
}
int fts_interrupt_set(struct fts_ts_info *info, int enable)
{
unsigned char regAdd[4] = { 0xB6, 0x00, 0x2C, enable };
if (enable) {
regAdd[3] = INT_ENABLE_D3;
input_info(true, &info->client->dev, "%s: Enable\n", __func__);
} else {
regAdd[3] = INT_DISABLE_D3;
input_info(true, &info->client->dev, "%s: Disable\n", __func__);
}
return fts_write_reg(info, &regAdd[0], 4);
}
static int fts_read_chip_id(struct fts_ts_info *info)
{
unsigned char regAdd[3] = {0xB6, 0x00, 0x04};
unsigned char val[7] = {0};
int ret;
ret = fts_read_reg(info, regAdd, 3, (unsigned char *)val, 7);
if (ret < 0) {
input_err(true, &info->client->dev, "%s: failed. ret: %d\n",
__func__, ret);
return ret;
}
input_info(true, &info->client->dev, "%s: %02X%02X%02X = %02X %02X %02X %02X %02X %02X\n", __func__,
regAdd[0], regAdd[1], regAdd[2], val[1], val[2], val[3], val[4], val[5], val[6]);
if (val[1] == FTS_ID0 && val[2] == FTS_ID1)
input_info(true, &info->client->dev, "%s: %02X %02X\n", __func__, val[1], val[2]);
else
return -FTS_ERROR_INVALID_CHIP_ID;
return ret;
}
int fts_read_analog_chip_id(struct fts_ts_info *info, unsigned char id)
{
unsigned char regAdd[6] = {0xB3, 0x20, 0x07, 0x00, 0x00, 0x00};
unsigned char val[4] = {0};
int ret = -1;
switch(id) {
case ANALOG_ID_FTS8:
ret = fts_write_reg(info, &regAdd[0], 3);
if (ret < 0) {
input_err(true, &info->client->dev, "%s: failed. ret: %d\n",
__func__, ret);
return ret;
}
regAdd[0] = 0xB1;
regAdd[1] = 0x00;
regAdd[2] = 0x84;
ret = fts_write_reg(info, &regAdd[0], 6);
if (ret < 0) {
input_err(true, &info->client->dev, "%s: failed. ret: %d\n",
__func__, ret);
return ret;
}
regAdd[0] = 0xB1;
regAdd[1] = 0x00;
regAdd[2] = 0x04;
ret = fts_read_reg(info, regAdd, 3, (unsigned char *)val, 4);
if (ret < 0) {
input_err(true, &info->client->dev, "%s: failed. ret: %d\n",
__func__, ret);
return ret;
}
input_info(true, &info->client->dev, "%s: %02X%02X%02X = %02X %02X %02X\n", __func__,
regAdd[0], regAdd[1], regAdd[2], val[1], val[2], val[3]);
if (val[1] == id) {
input_info(true, &info->client->dev, "%s: FTS8 %02X %02X\n", __func__, val[1], val[2]);
ret = 1;
}
break;
case ANALOG_ID_FTS9:
regAdd[0] = 0xB6;
regAdd[1] = 0x00;
regAdd[2] = 0x89;
regAdd[3] = 0x14;
ret = fts_write_reg(info, &regAdd[0], 4);
if (ret < 0) {
input_err(true, &info->client->dev, "%s: failed. ret: %d\n",
__func__, ret);
return ret;
}
regAdd[0] = 0xB3;
regAdd[1] = 0x20;
regAdd[2] = 0x03;
ret = fts_write_reg(info, &regAdd[0], 3);
if (ret < 0) {
input_err(true, &info->client->dev, "%s: failed. ret: %d\n",
__func__, ret);
return ret;
}
regAdd[0] = 0xB1;
regAdd[1] = 0xF8;
regAdd[2] = 0x00;
ret = fts_read_reg(info, regAdd, 3, (unsigned char *)val, 4);
if (ret < 0) {
input_err(true, &info->client->dev, "%s: failed. ret: %d\n",
__func__, ret);
return ret;
}
input_info(true, &info->client->dev, "%s: %02X%02X%02X = %02X %02X %02X\n", __func__,
regAdd[0], regAdd[1], regAdd[2], val[1], val[2], val[3]);
if (val[1] == id) {
input_info(true, &info->client->dev, "%s: FTS9 %02X %02X\n", __func__, val[1], val[2]);
ret = 0;
}
break;
default:
break;
}
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, &regAdd, 1, (unsigned char *)data, FTS_EVENT_SIZE)) {
if (data[0] == EVENTID_CONTROLLER_READY) {
rc = 0;
break;
}
if (data[0] == EVENTID_ERROR) {
if (data[1] == EVENTID_ERROR_FLASH_CORRUPTION) {
rc = -FTS_ERROR_EVENT_ID;
info->boot_crc_check_fail = FTS_BOOT_CRC_FAIL;
input_err(true, &info->client->dev, "%s: flash corruption:%02X,%02X,%02X\n",
__func__, data[0], data[1], data[2]);
break;
}
if (err_cnt++ > 32) {
rc = -FTS_ERROR_EVENT_ID;
break;
}
continue;
}
if (retry++ > FTS_RETRY_COUNT) {
rc = -FTS_ERROR_TIMEOUT;
input_err(true, &info->client->dev, "%s: Time Over\n", __func__);
if (info->fts_power_state == FTS_POWER_STATE_LOWPOWER)
schedule_delayed_work(&info->reset_work, msecs_to_jiffies(10));
break;
}
fts_delay(50);
}
input_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_sysinfo_data(struct fts_ts_info *info, unsigned char sysinfo_addr, unsigned char read_cnt, unsigned char *data)
{
int ret;
unsigned char regAdd[3] = { 0xD0, 0x00, sysinfo_addr };
unsigned char *buff;
buff = kzalloc(read_cnt + 1, GFP_KERNEL);
if (!buff) {
input_err(true, &info->client->dev, "%s: kzalloc error.\n", __func__);
return -ENOMEM;
}
ret = fts_read_reg(info, &regAdd[0], 3, buff, read_cnt + 1);
if (ret <= 0) {
input_err(true, &info->client->dev, "%s: failed. ret: %d\n",
__func__, ret);
kfree(buff);
return ret;
}
memcpy(data, &buff[1], read_cnt);
kfree(buff);
return ret;
}
int fts_get_version_info(struct fts_ts_info *info)
{
int rc;
unsigned char regAdd[3];
unsigned char data[FTS_EVENT_SIZE] = { 0 };
int retry = 0;
fts_interrupt_set(info, INT_DISABLE);
fts_delay(10);
fts_command(info, FTS_CMD_RELEASEINFO);
fts_delay(5);
memset(data, 0x0, FTS_EVENT_SIZE);
regAdd[0] = READ_ONE_EVENT;
rc = -1;
while (fts_read_reg(info, &regAdd[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) {
info->fw_version_of_ic = 0;
info->config_version_of_ic = 0;
info->ic_product_id = 0;
info->fw_main_version_of_ic = 0;
rc = -FTS_ERROR_TIMEOUT;
input_err(true, &info->client->dev, "%s: Time Over\n", __func__);
break;
}
fts_delay(80);
}
input_info(true, &info->client->dev,
"%s: [IC] product id: 0x%02X, Firmware Ver: 0x%04X, Config Ver: 0x%04X, Main Ver: 0x%04X\n", __func__,
info->ic_product_id, info->fw_version_of_ic,
info->config_version_of_ic, info->fw_main_version_of_ic);
fts_interrupt_set(info, INT_ENABLE);
return rc;
}
/* warmboot crc check() is default disable in FTS8. Enable after power on */
int fts_set_warmboot_crc_enable(struct fts_ts_info *info)
{
int ret;
unsigned char regAdd[4] = {0xB6, 0x00, 0x1E, 0x20};
ret = fts_write_reg(info, &regAdd[0], 4);
if (ret <= 0)
input_err(true, &info->client->dev, "%s: failed\n", __func__);
fts_delay(10);
return ret;
}
#ifdef FTS_SUPPORT_NOISE_PARAM
int fts_get_noise_param_address(struct fts_ts_info *info)
{
int rc;
unsigned char regAdd[3];
unsigned char data[6] = { 0 };
struct fts_noise_param *noise_param;
noise_param = (struct fts_noise_param *)&info->noise_param;
regAdd[0] = 0xD0;
regAdd[1] = 0x00;
regAdd[2] = FTS_SI_NOISE_PARAM_ADDR;
rc = fts_read_reg(info, regAdd, 3, (unsigned char *)data, 4);
noise_param->pAddr = (unsigned short)(data[1] + (data[2] << 8));
return rc;
}
static int fts_get_noise_param(struct fts_ts_info *info)
{
int rc;
unsigned char regAdd[3];
unsigned char data[6] = { 0 };
struct fts_noise_param *noise_param;
noise_param = (struct fts_noise_param *)&info->noise_param;
regAdd[0] = 0xB3;
regAdd[1] = 0x00;
regAdd[2] = 0x10;
rc = fts_write_reg(info, regAdd, 3);
regAdd[0] = 0xB1;
regAdd[1] = (noise_param->pAddr >> 8) & 0xff;
regAdd[2] = noise_param->pAddr & 0xff;
rc = fts_read_reg(info, regAdd, 3, data, 5);
noise_param->bestRIdx = data[1];
noise_param->mtNoiseLvl = data[2];
noise_param->sstNoiseLvl = data[3];
noise_param->bestRMutual = data[4];
return rc;
}
static int fts_set_noise_param(struct fts_ts_info *info)
{
int rc;
unsigned char regAdd[7];
struct fts_noise_param *noise_param;
noise_param = (struct fts_noise_param *)&info->noise_param;
regAdd[0] = 0xB3;
regAdd[1] = 0x00;
regAdd[2] = 0x10;
rc = fts_write_reg(info, regAdd, 3);
regAdd[0] = 0xB1;
regAdd[1] = (noise_param->pAddr >> 8) & 0xff;
regAdd[2] = noise_param->pAddr & 0xff;
regAdd[3] = noise_param->bestRIdx;
regAdd[4] = noise_param->mtNoiseLvl;
regAdd[5] = noise_param->sstNoiseLvl;
regAdd[6] = noise_param->bestRMutual;
rc = fts_write_reg(info, regAdd, 7);
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);
input_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);
input_info(true, &info->client->dev, "[TSP_KEY] back R!\n");
}
input_sync(info->input_dev);
info->tsp_keystatus = TOUCH_KEY_NULL;
}
}
#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 retry = 3;
unsigned char val[16] = { 0 };
unsigned char regAdd[8] = { 0 };
int rc;
do {
fts_set_warmboot_crc_enable(info);
rc = fts_systemreset(info, 10);
/* below register address is used only fts8cd56. */
regAdd[0] = 0xB6;
regAdd[1] = 0x00;
regAdd[2] = 0x08;
fts_read_reg(info, regAdd, 3, (unsigned char *)val, 3);
if (!val[1] && !val[2]) {
input_info(true, &info->client->dev, "%s: corrunption:%02X%02X\n",
__func__, val[1], val[2]);
info->boot_crc_check_fail = FTS_BOOT_CRC_FAIL;
}
memset(regAdd, 0x00, 8);
/* about fts_systemreset() */
if (rc != FTS_NOT_ERROR) {
info->boot_crc_check_fail = FTS_BOOT_CRC_FAIL;
fts_reset(info, 20);
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);
break;
}
} while (retry--);
if (retry == 0) {
input_err(true, &info->client->dev, "%s: Failed to system reset\n", __func__);
return FTS_ERROR_TIMEOUT;
}
rc = fts_read_chip_id(info);
if (rc < 0) {
fts_reset(info, 500); /* Delay to discharge the IC from ESD or On-state.*/
input_err(true, &info->client->dev, "%s: Reset caused by chip id error\n", __func__);
rc = fts_read_chip_id(info);
if (rc < 0)
return 1;
}
rc = fts_fw_update_on_probe(info);
if (rc < 0)
input_err(true, &info->client->dev, "%s: Failed to firmware update\n",
__func__);
#ifdef SEC_TSP_FACTORY_TEST
rc = fts_get_channel_info(info);
if (rc >= 0) {
input_info(true, &info->client->dev, "%s: Sense(%02d) Force(%02d)\n", __func__,
info->SenseChannelLength, info->ForceChannelLength);
} else {
input_err(true, &info->client->dev, "%s: read failed rc = %d\n", __func__, rc);
return 1;
}
info->pFrame = kzalloc(info->SenseChannelLength * info->ForceChannelLength * 2, GFP_KERNEL);
if (info->pFrame == NULL) {
input_err(true, &info->client->dev, "%s: pFrame kzalloc Failed\n", __func__);
return 1;
}
info->miscal_ref_raw = kzalloc(info->SenseChannelLength * info->ForceChannelLength * 2, GFP_KERNEL);
if (!info->miscal_ref_raw) {
kfree(info->pFrame);
return 1;
}
info->cx_data = kzalloc(info->SenseChannelLength * info->ForceChannelLength, GFP_KERNEL);
if (!info->cx_data) {
input_err(true, &info->client->dev, "%s: cx_data kzalloc Failed\n", __func__);
kfree(info->miscal_ref_raw);
kfree(info->pFrame);
return 1;
}
info->ito_result = kzalloc(FTS_ITO_RESULT_PRINT_SIZE, GFP_KERNEL);
if (!info->ito_result) {
kfree(info->cx_data);
kfree(info->miscal_ref_raw);
kfree(info->pFrame);
return 1;
}
#if defined(FTS_SUPPORT_STRINGLIB) && defined(CONFIG_SEC_FACTORY)
fts_disable_string(info);
#endif
#endif
fts_command(info, FLUSHBUFFER);
fts_delay(10);
fts_command(info, SENSEON);
#ifdef FTS_SUPPORT_PRESSURE_SENSOR
fts_command(info, FTS_CMD_PRESSURE_SENSE_ON);
#endif
#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;
#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->wirelesscharger_mode = false;
if (info->board->use_pressure)
info->lowpower_flag |= FTS_MODE_PRESSURE;
else
info->lowpower_flag = 0x00;
info->tdata->external_factory = false;
#ifdef FTS_SUPPORT_TOUCH_KEY
info->tsp_keystatus = 0x00;
#endif
fts_command(info, FORCECALIBRATION);
fts_interrupt_set(info, INT_ENABLE);
memset(val, 0x0, 4);
regAdd[0] = READ_STATUS;
fts_read_reg(info, regAdd, 1, (unsigned char *)val, 4);
input_info(true, &info->client->dev, "%s: ReadStatus(0x84) : %02X %02X %02X %02X\n", __func__, val[0],
val[1], val[2], val[3]);
memset(val, 0x0, 4);
fts_get_sysinfo_data(info, FTS_SI_REPORT_PRESSURE_RAW_DATA, 2, val);
if (val[0] == 1)
info->report_pressure = true;
memset(val, 0x0, 4);
rc = fts_get_sysinfo_data(info, FTS_SI_POSITION_RESOLUTION_Y, 4, val);
if (rc < 0) {
input_err(true, &info->client->dev, "%s: no not read resolution information\n", __func__);
} else {
/*
info->board->max_y = (val[0] | (val[1] << 8));
info->board->max_x = (val[2] | (val[3] << 8));
*/
input_info(true, &info->client->dev, "%s: resolution:(IC)%02X%02X%02X%02X, (DT)x:%d,y:%d\n",
__func__, val[0], val[1], val[2], val[3], info->board->max_x, info->board->max_y);
}
input_info(true, &info->client->dev, "%s: Initialized\n", __func__);
return 0;
}
static void fts_debug_msg_event_handler(struct fts_ts_info *info,
unsigned char data[])
{
#ifdef CONFIG_SAMSUNG_PRODUCT_SHIP
input_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]);
#else
input_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]);
#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 TouchID = 0, EventID = 0, status = 0, event_type = 0;
unsigned char LastLeftEvent = 0;
int x = 0, y = 0, z = 0;
int bw = 0, bh = 0, palm = 0;
int custom = 0;
#ifdef FTS_SUPPORT_STRINGLIB
unsigned short string_addr;
unsigned char string_event_id;
unsigned char string_data[5];
#endif
for (EventNum = 0; EventNum < LeftEvent; EventNum++) {
/* for event debugging */
if (info->debug_string & 0x1)
input_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]);
EventID = data[EventNum * FTS_EVENT_SIZE] & 0x0F;
if ((EventID >= 3) && (EventID <= 5)) {
LastLeftEvent = 0;
TouchID = (data[EventNum * FTS_EVENT_SIZE] >> 4) & 0x0F;
if (info->fts_power_state != FTS_POWER_STATE_ACTIVE) {
input_err(true, &info->client->dev, "%s: skip id %d in low power mode\n", __func__, EventID);
fts_release_all_finger(info);
continue;
}
} else {
LastLeftEvent = data[7 + EventNum * FTS_EVENT_SIZE] & 0x0F;
TouchID = data[1 + EventNum * FTS_EVENT_SIZE] & 0x0F;
EventID = data[EventNum * FTS_EVENT_SIZE] & 0xFF;
status = data[1 + EventNum * FTS_EVENT_SIZE] & 0xFF;
}
event_type = data[1 + EventNum * FTS_EVENT_SIZE];
switch (EventID) {
case EVENTID_NO_EVENT:
break;
case EVENTID_GESTURE_WAKEUP:
input_info(true, &info->client->dev, "%s: GESTURE_WAKEUP detected![EventID=%x]\n", __func__, EventID);
/* defined in Istor & JF-synaptics */
info->scrub_id = 0x07;
input_report_key(info->input_dev, KEY_BLACK_UI_GESTURE, 1);
input_sync(info->input_dev);
input_report_key(info->input_dev, KEY_BLACK_UI_GESTURE, 0);
input_sync(info->input_dev);
break;
case EVENTID_PRESSURE:
if (event_type == 0x01)
info->scrub_id = SPECIAL_EVENT_TYPE_PRESSURE_TOUCHED;
else
info->scrub_id = SPECIAL_EVENT_TYPE_PRESSURE_RELEASED;
info->scrub_x = (data[4] & 0xFF) << 8 | (data[3] & 0xFF);
info->scrub_y = (data[6] & 0xFF) << 8 | (data[5] & 0xFF);
input_report_key(info->input_dev, KEY_BLACK_UI_GESTURE, 1);
input_sync(info->input_dev);
input_report_key(info->input_dev, KEY_BLACK_UI_GESTURE, 0);
input_info(true, &info->client->dev,
"%s: [PRESSURE] %02X %02X %02X %02X %02X %02X %02X %02X\n", __func__,
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]);
break;
case EVENTID_GESTURE_HOME:
info->scrub_x = (data[4] & 0xFF) << 8 | (data[3] & 0xFF);
info->scrub_y = (data[6] & 0xFF) << 8 | (data[5] & 0xFF);
if (event_type == 0x01) {
input_report_key(info->input_dev, KEY_HOMEPAGE, 1);
info->scrub_id = SPECIAL_EVENT_TYPE_AOD_HOMEKEY_PRESS;
}
else {
input_report_key(info->input_dev, KEY_HOMEPAGE, 0);
info->scrub_id = SPECIAL_EVENT_TYPE_AOD_HOMEKEY_RELEASE;
}
input_report_key(info->input_dev, KEY_BLACK_UI_GESTURE, 1);
input_sync(info->input_dev);
input_report_key(info->input_dev, KEY_BLACK_UI_GESTURE, 0);
input_info(true, &info->client->dev,
"%s: [HOME key] %02X %02X %02X %02X %02X %02X %02X %02X\n", __func__,
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]);
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);
input_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);
input_info(true, &info->client->dev, "[TSP_KEY] BACK %s\n", key_state != 0 ? "P" : "R");
}
input_sync(info->input_dev);
}
info->tsp_keystatus = input_keys;
}
break;
#endif
case EVENTID_DEBUG:
if (info->board->support_sidegesture) {
if ((event_type == FTS_SIDEGESTURE_EVENT_SINGLE_STROKE) ||
(event_type == FTS_SIDEGESTURE_EVENT_DOUBLE_STROKE) ||
(event_type == FTS_SIDEGESTURE_EVENT_INNER_STROKE)) {
int direction = data[2 + EventNum * FTS_EVENT_SIZE];
// int 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);
input_info(true, &info->client->dev,
"%s: [Gesture] %02X %02X %02X %02X %02X %02X %02X %02X\n", __func__,
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]);
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 {
fts_debug_msg_event_handler(info,
&data[EventNum * FTS_EVENT_SIZE]);
}
break;
case EVENTID_ERROR:
if (data[1 + EventNum * FTS_EVENT_SIZE] == 0x08) {
/* Get Auto tune fail event */
if (data[2 + EventNum * FTS_EVENT_SIZE] == 0x00)
input_info(true, &info->client->dev, "%s: Fail Mutual Auto tune\n", __func__);
else if (data[2 + EventNum * FTS_EVENT_SIZE] == 0x01)
input_info(true, &info->client->dev, "%s: Fail Self Auto tune\n", __func__);
} else if (data[1 + EventNum * FTS_EVENT_SIZE] == 0x09) {
/* Get detect SYNC fail event */
input_info(true, &info->client->dev, "%s: Fail detect SYNC\n", __func__);
}
break;
#ifdef FTS_SUPPORT_HOVER
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);
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;
#endif
case EVENTID_ENTER_POINTER:
info->touch_count++;
info->all_finger_count++;
info->max_z_value = max((unsigned int)z, info->max_z_value);
info->min_z_value = min((unsigned int)z, info->min_z_value);
info->sum_z_value += (unsigned int)z;
do_gettimeofday(&info->time_pressed[TouchID]);
if ((info->touch_count > 4) && (info->check_multi == 0)) {
info->check_multi = 1;
info->multi_count++;
}
case EVENTID_MOTION_POINTER:
if (info->touch_count == 0) {
input_err(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)) {
input_err(true, &info->client->dev, "%s: state leave but point is moved.\n", __func__);
break;
}
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;
z = data[7 + EventNum * FTS_EVENT_SIZE];
input_report_key(info->input_dev, BTN_TOUCH, 1);
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_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));
if (info->brush_mode)
custom = ((z & 0xFF) << 1) | palm;
else
custom = (BRUSH_Z_DATA << 1) | palm;
input_report_abs(info->input_dev, ABS_MT_CUSTOM, custom);
if (info->board->support_mt_pressure) {
int pressure = z & 0xFF;
#if defined(FTS_SUPPORT_PRESSURE_SENSOR) && !defined(CONFIG_SEC_FACTORY)
pressure = fts_read_pressure_data(info);
#endif
if (pressure <= 0)
pressure = 1;
if ((info->finger[TouchID].lx != x) || (info->finger[TouchID].ly != y))
input_report_abs(info->input_dev, ABS_MT_PRESSURE, pressure);
else
input_report_abs(info->input_dev, ABS_MT_PRESSURE, info->finger[TouchID].lp);
info->finger[TouchID].lp = pressure;
}
info->finger[TouchID].lx = x;
info->finger[TouchID].ly = y;
break;
case EVENTID_LEAVE_POINTER:
if (info->touch_count <= 0) {
input_err(true, &info->client->dev, "%s: count 0\n", __func__);
fts_release_all_finger(info);
break;
}
do_gettimeofday(&info->time_released[TouchID]);
if (info->time_longest < (info->time_released[TouchID].tv_sec - info->time_pressed[TouchID].tv_sec))
info->time_longest = (info->time_released[TouchID].tv_sec - info->time_pressed[TouchID].tv_sec);
info->touch_count--;
input_mt_slot(info->input_dev, TouchID);
if (info->board->support_mt_pressure)
input_report_abs(info->input_dev, ABS_MT_PRESSURE, 0);
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);
info->check_multi = 0;
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);
}
}
break;
case EVENTID_STATUS_EVENT:
fts_debug_msg_event_handler(info, &data[EventNum * FTS_EVENT_SIZE]);
if(status == STATUS_EVENT_ESD_DETECT) {
input_err(true, &info->client->dev,
"%s: ESD detected! %02X %02X %02X %02X %02X %02X %02X %02X\n", __func__,
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]);
schedule_delayed_work(&info->reset_work, msecs_to_jiffies(10));
} 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
input_info(true, &info->client->dev, "%s: Received the Hover Raw Data Ready Event\n", __func__);
} else if (status == STATUS_EVENT_GLOVE_MODE) {
#ifdef CONFIG_GLOVE_TOUCH
if (data[2 + EventNum * FTS_EVENT_SIZE] == 0x01)
info->touch_mode = FTS_TM_GLOVE;
else
info->touch_mode = FTS_TM_NORMAL;
input_report_switch(info->input_dev, SW_GLOVE, info->touch_mode);
#endif
} else if (status == STATUS_EVENT_FORCE_CAL_TRIGGER) {
input_info(true, &info->client->dev, "%s: Received Force Calibration trigger Event\n", __func__);
} else if (status == STATUS_EVENT_FORCE_CAL_DONE_D3) {
input_info(true, &info->client->dev, "%s: Received Force Calibration finished Event\n", __func__);
} else if (status == STATUS_EVENT_WETMODE) {
if (data[2 + EventNum * FTS_EVENT_SIZE] == 0x01) {
input_info(true, &info->client->dev, "%s: Received Wet Mode On Event\n", __func__);
info->wet_mode = true;
info->wet_count++;
}
else {
input_info(true, &info->client->dev, "%s: Received Wet Mode Off Event\n", __func__);
info->wet_mode = false;
}
}
break;
#ifdef SEC_TSP_FACTORY_TEST
case EVENTID_RESULT_READ_REGISTER:
break;
#endif
#ifdef FTS_SUPPORT_STRINGLIB
case EVENTID_FROM_STRING:
string_addr = FTS_CMD_STRING_ACCESS + 1;
fts_read_from_string(info, &string_addr, &string_event_id, sizeof(string_event_id));
input_dbg(true, &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_event_id);
switch (string_event_id) {
case FTS_STRING_EVENT_AOD_TRIGGER:
string_addr = FTS_CMD_STRING_ACCESS + 10;
fts_read_from_string(info, &string_addr, string_data, sizeof(string_data));
input_report_key(info->input_dev, KEY_BLACK_UI_GESTURE, 1);
info->scrub_id = SPECIAL_EVENT_TYPE_AOD_DOUBLETAB;
info->scrub_x = (string_data[1] & 0xFF) << 8 | (string_data[0] & 0xFF);
info->scrub_y = (string_data[3] & 0xFF) << 8 | (string_data[2] & 0xFF);
#ifdef CONFIG_SAMSUNG_PRODUCT_SHIP
input_info(true, &info->client->dev, "%s: AOD[%d]\n", __func__, info->scrub_id);
#else
input_info(true, &info->client->dev, "%s: AOD[%d %d %d]\n", __func__,
info->scrub_id, info->scrub_x, info->scrub_y);
#endif
info->all_aod_tap_count++;
break;
case FTS_STRING_EVENT_SPAY:
input_report_key(info->input_dev, KEY_BLACK_UI_GESTURE, 1);
info->scrub_id = SPECIAL_EVENT_TYPE_SPAY;
input_info(true, &info->client->dev, "%s: SPAY[%d]\n", __func__, info->scrub_id);
break;
case FTS_STRING_EVENT_PRESSURE_TOUCHED:
case FTS_STRING_EVENT_PRESSURE_RELEASED:
string_addr = FTS_CMD_STRING_ACCESS + 82;
fts_read_from_string(info, &string_addr, string_data, sizeof(string_data));
if (info->fts_power_state == FTS_POWER_STATE_ACTIVE) {
if (string_event_id & FTS_STRING_EVENT_PRESSURE_TOUCHED) {
info->all_force_count++;
if (info->scrub_id == SPECIAL_EVENT_TYPE_PRESSURE_TOUCHED) {
input_info(true, &info->client->dev, "%s: Skip duplicate PRESSURE[%d]\n", __func__, info->scrub_id);
break;
} else {
info->scrub_id = SPECIAL_EVENT_TYPE_PRESSURE_TOUCHED;
}
} else {
if (info->scrub_id == SPECIAL_EVENT_TYPE_PRESSURE_TOUCHED) {
info->scrub_id = SPECIAL_EVENT_TYPE_PRESSURE_RELEASED;
}
else if (info->scrub_id == SPECIAL_EVENT_TYPE_AOD_HOMEKEY_PRESS) {
input_report_key(info->input_dev, KEY_HOMEPAGE, 0);
info->scrub_id = SPECIAL_EVENT_TYPE_AOD_HOMEKEY_RELEASE;
} else {
info->scrub_id = 0;
break;
}
}
} else {
if (info->scrub_id == SPECIAL_EVENT_TYPE_AOD_HOMEKEY_PRESS) {
input_report_key(info->input_dev, KEY_HOMEPAGE, 0);
info->scrub_id = SPECIAL_EVENT_TYPE_AOD_HOMEKEY_RELEASE;
} else {
input_report_key(info->input_dev, KEY_HOMEPAGE, 1);
info->scrub_id = SPECIAL_EVENT_TYPE_AOD_HOMEKEY_PRESS;
info->all_force_count++;
}
}
info->scrub_x = (string_data[1] & 0xFF) << 8 | (string_data[0] & 0xFF);
info->scrub_y = (string_data[3] & 0xFF) << 8 | (string_data[2] & 0xFF);
input_report_key(info->input_dev, KEY_BLACK_UI_GESTURE, 1);
#ifdef CONFIG_SAMSUNG_PRODUCT_SHIP
input_info(true, &info->client->dev, "%s: PRESSURE[%d]\n", __func__, info->scrub_id);
#else
input_info(true, &info->client->dev, "%s: PRESSURE[%d %d %d]\n", __func__,
info->scrub_id, info->scrub_x, info->scrub_y);
#endif
break;
default:
input_info(true, &info->client->dev,
"%s: [No event] %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_event_id);
break;
}
input_sync(info->input_dev);
input_report_key(info->input_dev, KEY_BLACK_UI_GESTURE, 0);
break;
case EVENTID_FROM_SPONGE:
input_dbg(true, &info->client->dev, "%s: [Sponge] %2x %2x %2x %2x %2x %2x %2x %2x\n",
__func__, 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]);
switch (event_type) {
case FTS_STRING_EVENT_AOD_TRIGGER:
input_report_key(info->input_dev, KEY_BLACK_UI_GESTURE, 1);
info->scrub_id = SPECIAL_EVENT_TYPE_AOD_DOUBLETAB;
info->scrub_x = (data[4] & 0xF0) << 4 | (data[3] & 0xFF);
info->scrub_y = (data[4] & 0x0F) << 8 | (data[2] & 0xFF);
#ifdef CONFIG_SAMSUNG_PRODUCT_SHIP
input_info(true, &info->client->dev, "%s: AOD[%d]\n", __func__, info->scrub_id);
#else
input_info(true, &info->client->dev, "%s: AOD[%d %d %d]\n", __func__,
info->scrub_id, info->scrub_x, info->scrub_y);
#endif
info->all_aod_tap_count++;
break;
case FTS_STRING_EVENT_SPAY:
input_report_key(info->input_dev, KEY_BLACK_UI_GESTURE, 1);
info->scrub_id = SPECIAL_EVENT_TYPE_SPAY;
input_info(true, &info->client->dev, "%s: SPAY[%d]\n", __func__, info->scrub_id);
info->all_spay_count++;
break;
case FTS_STRING_EVENT_PRESSURE_TOUCHED:
case FTS_STRING_EVENT_PRESSURE_RELEASED:
if (info->fts_power_state == FTS_POWER_STATE_ACTIVE) {
if (event_type & FTS_STRING_EVENT_PRESSURE_TOUCHED) {
info->all_force_count++;
info->scrub_id = SPECIAL_EVENT_TYPE_PRESSURE_TOUCHED;
} else {
if (info->scrub_id == SPECIAL_EVENT_TYPE_AOD_HOMEKEY_PRESS) {
input_report_key(info->input_dev, KEY_HOMEPAGE, 0);
info->scrub_id = SPECIAL_EVENT_TYPE_AOD_HOMEKEY_RELEASE;
} else {
info->scrub_id = SPECIAL_EVENT_TYPE_PRESSURE_RELEASED;
}
}
input_report_key(info->input_dev, KEY_BLACK_UI_GESTURE, 1);
} else {
if (event_type & FTS_STRING_EVENT_PRESSURE_RELEASED) {
input_report_key(info->input_dev, KEY_HOMEPAGE, 0);
input_report_key(info->input_dev, KEY_BLACK_UI_GESTURE, 1);
info->scrub_id = SPECIAL_EVENT_TYPE_AOD_HOMEKEY_RELEASE_NO_HAPTIC;
input_sync(info->input_dev);
haptic_homekey_release();
} else {
input_report_key(info->input_dev, KEY_HOMEPAGE, 1);
input_sync(info->input_dev);
info->scrub_id = SPECIAL_EVENT_TYPE_AOD_HOMEKEY_PRESS;
haptic_homekey_press();
info->all_force_count++;
}
}
info->scrub_x = (data[4] & 0xF0) << 4 | (data[3] & 0xFF);
info->scrub_y = (data[4] & 0x0F) << 8 | (data[2] & 0xFF);
#ifdef CONFIG_SAMSUNG_PRODUCT_SHIP
input_info(true, &info->client->dev, "%s: PRESSURE[%d]\n", __func__, info->scrub_id);
#else
input_info(true, &info->client->dev, "%s: PRESSURE[%d %d %d]\n", __func__,
info->scrub_id, info->scrub_x, info->scrub_y);
#endif
break;
default:
input_info(true, &info->client->dev, "%s: [No event] %2x %2x %2x %2x %2x %2x %2x %2x\n",
__func__, 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]);
break;
}
input_sync(info->input_dev);
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;
}
#if !defined(CONFIG_SAMSUNG_PRODUCT_SHIP)
if (EventID == EVENTID_ENTER_POINTER)
input_info(true, &info->client->dev,
"%s[P] tID:%d x:%d y:%d w:%d h:%d z:%d p:%d tc:%d tm:%d\n",
info->dex_name,
TouchID, x, y, bw, bh, z, palm, info->touch_count, info->touch_mode);
#ifdef FTS_SUPPORT_HOVER
else if (EventID == EVENTID_HOVER_ENTER_POINTER)
input_dbg(true, &info->client->dev,
"[HP] tID:%d x:%d y:%d z:%d\n",
TouchID, x, y, z);
#endif
#else
if (EventID == EVENTID_ENTER_POINTER)
input_info(true, &info->client->dev,
"%s[P] tID:%d tc:%d tm:%d\n",
info->dex_name,
TouchID, info->touch_count, info->touch_mode);
#ifdef FTS_SUPPORT_HOVER
else if (EventID == EVENTID_HOVER_ENTER_POINTER)
input_dbg(true, &info->client->dev,
"[HP] tID:%d\n", TouchID);
#endif
#endif
else if (EventID == EVENTID_LEAVE_POINTER) {
#if !defined(CONFIG_SAMSUNG_PRODUCT_SHIP)
input_info(true, &info->client->dev,
"%s[R] tID:%d mc:%d tc:%d lx:%d ly:%d Ver[%02X%04X|%01X] C%02XT%04X.%4s%s\n",
info->dex_name,
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->tdata->nvdata.cal_count, info->tdata->nvdata.tune_fix_ver,
info->tdata->tclm_string[info->tdata->nvdata.cal_position].f_name,
(info->tdata->tclm_level == TCLM_LEVEL_LOCKDOWN) ? ".L" : " ");
#else
input_info(true, &info->client->dev,
"%s[R] tID:%d mc:%d tc:%d Ver[%02X%04X|%01X] C%02XT%04X.%4s%s\n",
info->dex_name,
TouchID, info->finger[TouchID].mcount, info->touch_count,
info->panel_revision, info->fw_main_version_of_ic,
info->flip_enable,
info->tdata->nvdata.cal_count, info->tdata->nvdata.tune_fix_ver,
info->tdata->tclm_string[info->tdata->nvdata.cal_position].f_name,
(info->tdata->tclm_level == TCLM_LEVEL_LOCKDOWN) ? ".L" : " ");
#endif
info->finger[TouchID].mcount = 0;
}
#ifdef FTS_SUPPORT_HOVER
else if (EventID == EVENTID_HOVER_LEAVE_POINTER) {
input_dbg(true, &info->client->dev,
"[HR] tID:%d Ver[%02X%04X|%01X]\n",
TouchID,
info->panel_revision, info->fw_main_version_of_ic,
info->flip_enable);
info->finger[TouchID].mcount = 0;
}
#endif
else if (EventID == EVENTID_MOTION_POINTER && info->fts_power_state == FTS_POWER_STATE_ACTIVE)
info->finger[TouchID].mcount++;
if (((EventID == EVENTID_ENTER_POINTER) ||
(EventID == EVENTID_MOTION_POINTER) ||
(EventID == EVENTID_LEAVE_POINTER)) && info->fts_power_state == FTS_POWER_STATE_ACTIVE)
info->finger[TouchID].state = EventID;
}
input_sync(info->input_dev);
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;
input_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;
input_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, 0x23, READ_ALL_EVENT};
unsigned short evtcount = 0;
#if defined(CONFIG_SECURE_TOUCH)
if (fts_filter_interrupt(info) == IRQ_HANDLED) {
int ret;
ret = wait_for_completion_interruptible_timeout((&info->st_interrupt),
msecs_to_jiffies(10 * MSEC_PER_SEC));
return IRQ_HANDLED;
}
#endif
/* in LPM, waiting blsp block resume */
if (info->fts_power_state == FTS_POWER_STATE_LOWPOWER) {
int ret;
input_dbg(true, &info->client->dev, "%s: run LPM interrupt handler\n", __func__);
wake_lock_timeout(&info->wakelock, msecs_to_jiffies(3 * MSEC_PER_SEC));
/* waiting for blsp block resuming, if not occurs i2c error */
ret = wait_for_completion_interruptible_timeout(&info->resume_done, msecs_to_jiffies(3 * MSEC_PER_SEC));
if (ret == 0) {
input_err(true, &info->client->dev, "%s: LPM: pm resume is not handled\n", __func__);
return IRQ_NONE;
}
if (ret < 0) {
input_err(true, &info->client->dev, "%s: LPM: -ERESTARTSYS if interrupted, %d\n", __func__, ret);
return IRQ_NONE;
}
input_info(true, &info->client->dev, "%s: run LPM interrupt handler, %d\n", __func__, ret);
/* run lpm interrupt handler */
}
evtcount = 0;
fts_read_reg(info, &regAdd[0], 3, (unsigned char *)&evtcount, 2);
evtcount = evtcount >> 8;
evtcount = evtcount / 2;
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);
}
return IRQ_HANDLED;
}
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) {
input_err(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("%s: %s %s: ta:%d\n", FTS_TS_DRV_NAME, SECLOG, __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;
int retval = 0;
if (info->tsk_led_enabled == on)
return retval;
regulator_tk_led = regulator_get(NULL, pdata->regulator_tk_led);
if (IS_ERR_OR_NULL(regulator_tk_led)) {
input_err(true, dev, "%s: Failed to get %s regulator.\n",
__func__, pdata->regulator_tk_led);
goto out;
}
input_info(true, dev, "%s: %s\n", __func__, on ? "on" : "off");
if (on) {
retval = regulator_enable(regulator_tk_led);
if (retval) {
input_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);
}
info->tsk_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)) {
input_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)) {
input_err(true, dev, "%s: Failed to get %s regulator\n",
__func__, pdata->regulator_avdd);
goto out;
}
if (on) {
retval = regulator_enable(regulator_avdd);
if (retval) {
input_err(true, dev, "%s: Failed to enable avdd: %d\n", __func__, retval);
regulator_disable(regulator_avdd);
goto out;
}
fts_delay(1);
retval = regulator_enable(regulator_dvdd);
if (retval) {
input_err(true, dev, "%s: Failed to enable vdd: %d\n", __func__, retval);
regulator_disable(regulator_dvdd);
regulator_disable(regulator_avdd);
goto out;
}
retval = pinctrl_select_state(pdata->pinctrl, pdata->pins_default);
if (retval < 0)
input_err(true, dev, "%s: Failed to configure tsp_attn pin\n", __func__);
fts_delay(5);
} else {
regulator_disable(regulator_dvdd);
regulator_disable(regulator_avdd);
retval = pinctrl_select_state(pdata->pinctrl, pdata->pins_sleep);
if (retval < 0)
input_err(true, dev, "%s: Failed to configure tsp_attn pin\n", __func__);
}
enabled = on;
input_err(true, dev, "%s: %s: avdd:%s, dvdd:%s\n", __func__, on ? "on" : "off",
regulator_is_enabled(regulator_avdd) ? "on" : "off",
regulator_is_enabled(regulator_dvdd) ? "on" : "off");
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];
u32 ic_match_value;
int retval = 0;
#if !defined(CONFIG_EXYNOS_DECON_LCD)
int lcdtype = 0;
#endif
#if defined(CONFIG_EXYNOS_DECON_FB)
int connected;
#endif
pdata->tsp_icid = of_get_named_gpio(np, "stm,tsp-icid_gpio", 0);
if (gpio_is_valid(pdata->tsp_icid)) {
input_info(true, dev, "%s: TSP_ICID : %d\n", __func__, gpio_get_value(pdata->tsp_icid));
if (of_property_read_u32(np, "stm,icid_match_value", &ic_match_value)) {
input_err(true, dev, "%s: Failed to get icid match value\n", __func__);
return -EINVAL;
} else {
input_err(true, dev, "%s: IC matched value : %d\n", __func__, ic_match_value);
}
if (gpio_get_value(pdata->tsp_icid) != ic_match_value) {
input_err(true, dev, "%s: Do not match TSP_ICID\n", __func__);
return -EINVAL;
}
} else {
input_err(true, dev, "%s: Failed to get tsp-icid gpio\n", __func__);
}
if (gpio_is_valid(pdata->tsp_icid)) {
retval = gpio_request(pdata->tsp_icid, "TSP_ICID");
if (retval)
input_err(true, dev, "%s: Unable to request tsp_icid [%d]\n", __func__, pdata->tsp_icid);
}
pdata->tsp_id = of_get_named_gpio(np, "stm,tsp-id_gpio", 0);
if (gpio_is_valid(pdata->tsp_id))
input_info(true, dev, "%s: TSP_ID : %d\n", __func__, gpio_get_value(pdata->tsp_id));
else
input_err(true, dev, "%s: Failed to get tsp-id gpio\n", __func__);
if (gpio_is_valid(pdata->tsp_id)) {
retval = gpio_request(pdata->tsp_id, "TSP_ID");
if (retval)
input_err(true, dev, "%s: Unable to request tsp_id [%d]\n", __func__, pdata->tsp_id);
}
pdata->device_id = of_get_named_gpio(np, "stm,device_gpio", 0);
if (gpio_is_valid(pdata->device_id))
input_info(true, dev, "%s: Device ID : %d\n", __func__, gpio_get_value(pdata->device_id));
else
input_err(true, dev, "%s: skipped to get device-id gpio\n", __func__);
pdata->irq_gpio = of_get_named_gpio(np, "stm,irq_gpio", 0);
if (gpio_is_valid(pdata->irq_gpio)) {
retval = gpio_request_one(pdata->irq_gpio, GPIOF_DIR_IN, "stm,tsp_int");
if (retval) {
input_err(true, dev, "%s: Unable to request tsp_int [%d]\n", __func__, pdata->irq_gpio);
return -EINVAL;
}
} else {
input_err(true, dev, "%s: Failed to get irq gpio\n", __func__);
return -EINVAL;
}
client->irq = gpio_to_irq(pdata->irq_gpio);
if (of_property_read_u32(np, "stm,irq_type", &pdata->irq_type)) {
input_err(true, dev, "%s: Failed to get irq_type property\n", __func__);
return -EINVAL;
}
if (of_property_read_u32_array(np, "stm,max_coords", coords, 2)) {
input_err(true, dev, "%s: Failed to get max_coords property\n", __func__);
return -EINVAL;
}
pdata->max_x = coords[0];
pdata->max_y = coords[1];
if (of_property_read_string(np, "stm,regulator_dvdd", &pdata->regulator_dvdd)) {
input_err(true, dev, "%s: Failed to get regulator_dvdd name property\n", __func__);
return -EINVAL;
}
if (of_property_read_string(np, "stm,regulator_avdd", &pdata->regulator_avdd)) {
input_err(true, dev, "%s: Failed to get regulator_avdd name property\n", __func__);
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
*/
if (gpio_is_valid(pdata->tsp_id))
of_property_read_string_index(np, "stm,firmware_name", gpio_get_value(pdata->tsp_id), &pdata->firmware_name);
else
of_property_read_string_index(np, "stm,firmware_name", 0, &pdata->firmware_name);
if (of_property_read_string_index(np, "stm,project_name", 0, &pdata->project_name))
input_err(true, dev, "%s: skipped to get project_name property\n", __func__);
if (of_property_read_string_index(np, "stm,project_name", 1, &pdata->model_name))
input_err(true, dev, "%s: skipped to get model_name property\n", __func__);
if (of_property_read_bool(np, "stm,support_gesture"))
pdata->support_sidegesture = true;
pdata->support_dex = of_property_read_bool(np, "support_dex_mode");
of_property_read_u32(np, "stm,bringup", &pdata->bringup);
if (of_property_read_u32(np, "stm,use_pressure", &pdata->use_pressure) < 0)
pdata->use_pressure = 0;
pdata->support_hover = false;
pdata->support_glove = false;
#ifdef CONFIG_SEC_FACTORY
pdata->support_mt_pressure = true;
#endif
#ifdef FTS_SUPPORT_TA_MODE
pdata->register_cb = fts_tsp_register_callback;
#endif
if (of_property_read_u32(np, "stm,factory_item_version", &pdata->item_version) < 0)
pdata->item_version = 0;
#ifdef FTS_SUPPORT_TOUCH_KEY
if (of_property_read_u32(np, "stm,num_touchkey", &pdata->num_touchkey))
input_err(true, dev, "%s: skipped to get num_touchkey property\n", __func__);
else {
pdata->support_mskey = true;
pdata->touchkey = fts_touchkeys;
if (of_property_read_string(np, "stm,regulator_tk_led", &pdata->regulator_tk_led))
input_err(true, dev, "%s: skipped to get regulator_tk_led name property\n", __func__);
else
pdata->led_power = fts_led_power_ctrl;
}
#endif
if (of_property_read_u32(np, "stm,device_num", &pdata->device_num))
input_err(true, dev, "%s: Failed to get device_num property\n", __func__);
#if defined(CONFIG_FB_MSM_MDSS_SAMSUNG)
lcdtype = get_lcd_attached("GET");
if (lcdtype == 0xFFFFFF) {
input_err(true, &client->dev, "%s: lcd is not attached\n", __func__);
return -ENODEV;
}
#endif
#if defined(CONFIG_EXYNOS_DECON_LCD)
if (lcdtype == 0) {
input_err(true, &client->dev, "%s: lcd is not attached\n", __func__);
return -ENODEV;
}
#endif
#if defined(CONFIG_EXYNOS_DECON_FB)
connected = get_lcd_info("connected");
if (connected < 0) {
input_err(true, dev, "%s: Failed to get lcd info\n", __func__);
return -EINVAL;
}
if (!connected) {
input_err(true, &client->dev, "%s: lcd is disconnected\n", __func__);
return -ENODEV;
}
input_info(true, &client->dev, "%s: lcd is connected\n", __func__);
lcdtype = get_lcd_info("id");
if (lcdtype < 0) {
input_err(true, dev, "%s: Failed to get lcd info\n", __func__);
return -EINVAL;
}
#endif
input_info(true, &client->dev, "%s: lcdtype 0x%08X\n", __func__, lcdtype);
pdata->panel_revision = ((lcdtype >> 8) & 0xFF) >> 4;
input_err(true, dev,
"%s: irq :%d, irq_type: 0x%04x, max[x,y]: [%d,%d], project/model_name: %s/%s, panel_revision: %d, gesture: %d, device_num: %d, dex: %d\n",
__func__, pdata->irq_gpio, pdata->irq_type, pdata->max_x, pdata->max_y,
pdata->project_name, pdata->model_name, pdata->panel_revision,
pdata->support_sidegesture, pdata->device_num, pdata->support_dex);
return retval;
}
#endif
#if defined(CONFIG_TOUCHSCREEN_DUMP_MODE)
#include <linux/sec_debug.h>
extern struct tsp_dump_callbacks dump_callbacks;
void tsp_dump(void);
#endif
#ifdef TCLM_CONCEPT
static void sec_tclm_parse_dt(struct i2c_client *client, struct sec_tclm_data *tdata)
{
struct device *dev = &client->dev;
struct device_node *np = dev->of_node;
if (of_property_read_u32(np, "stm,tclm_level", &tdata->tclm_level) < 0) {
tdata->tclm_level = 0;
input_err(true, dev, "%s: Failed to get tclm_level property\n", __func__);
}
if (of_property_read_u32(np, "stm,afe_base", &tdata->afe_base) < 0) {
tdata->afe_base = 0;
input_err(true, dev, "%s: Failed to get afe_base property\n", __func__);
}
input_err(true, &client->dev, "%s: tclm_level %d, sec_afe_base %d\n", __func__, tdata->tclm_level, tdata->afe_base);
}
#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;
#ifdef TCLM_CONCEPT
struct sec_tclm_data *tdata = NULL;
#endif
/* parse dt */
if (client->dev.of_node) {
pdata = devm_kzalloc(&client->dev,
sizeof(struct fts_i2c_platform_data), GFP_KERNEL);
if (!pdata) {
input_err(true, &client->dev, "%s: Failed to allocate platform data\n", __func__);
return -ENOMEM;
}
client->dev.platform_data = pdata;
retval = fts_parse_dt(client);
if (retval) {
input_err(true, &client->dev, "%s: Failed to parse dt\n", __func__);
return retval;
}
#ifdef TCLM_CONCEPT
tdata = devm_kzalloc(&client->dev,
sizeof(struct sec_tclm_data), GFP_KERNEL);
if (!tdata)
return -ENOMEM;
sec_tclm_parse_dt(client, tdata);
#endif
} else {
pdata = client->dev.platform_data;
}
if (!pdata) {
input_err(true, &client->dev, "%s: No platform data found\n", __func__);
return -EINVAL;
}
if (!pdata->power) {
input_err(true, &client->dev, "%s: No power contorl found\n", __func__);
return -EINVAL;
}
pdata->pinctrl = devm_pinctrl_get(&client->dev);
if (IS_ERR(pdata->pinctrl)) {
input_err(true, &client->dev, "%s: could not get pinctrl\n", __func__);
return PTR_ERR(pdata->pinctrl);
}
pdata->pins_default = pinctrl_lookup_state(pdata->pinctrl, "on_state");
if (IS_ERR(pdata->pins_default))
input_err(true, &client->dev, "%s: could not get default pinstate\n", __func__);
pdata->pins_sleep = pinctrl_lookup_state(pdata->pinctrl, "off_state");
if (IS_ERR(pdata->pins_sleep))
input_err(true, &client->dev, "%s: could not get sleep pinstate\n", __func__);
info = kzalloc(sizeof(struct fts_ts_info), GFP_KERNEL);
if (!info) {
input_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_get_sysinfo_data = fts_get_sysinfo_data;
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 TCLM_CONCEPT
info->tdata = tdata;
if (!info->tdata) {
input_err(true, &client->dev, "%s: No tclm data found\n", __func__);
kfree(info);
return -EINVAL;
}
sec_tclm_initialize(info->tdata);
info->tdata->client = info->client;
info->tdata->tclm_read = sec_tclm_data_read;
info->tdata->tclm_write = sec_tclm_data_write;
info->tdata->tclm_execute_force_calibration = sec_tclm_execute_force_calibration;
#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);
INIT_DELAYED_WORK(&info->work_read_info, fts_read_info_work);
if (info->board->support_hover)
input_info(true, &info->client->dev, "%s: Support Hover Event\n", __func__);
else
input_info(true, &info->client->dev, "%s: Not support Hover Event\n", __func__);
i2c_set_clientdata(client, info);
if (pdata->get_ddi_type) {
info->ddi_type = pdata->get_ddi_type();
input_info(true, &client->dev, "%s: DDI Type is %s[%d]\n",
__func__, info->ddi_type ? "MAGNA" : "SDC", info->ddi_type);
}
return retval;
}
static void fts_set_input_prop(struct fts_ts_info *info, struct input_dev *dev, u8 propbit)
{
static char fts_ts_phys[64] = { 0 };
dev->dev.parent = &info->client->dev;
snprintf(fts_ts_phys, sizeof(fts_ts_phys), "%s/input1",
dev->name);
dev->phys = fts_ts_phys;
dev->id.bustype = BUS_I2C;
#ifdef CONFIG_GLOVE_TOUCH
input_set_capability(dev, EV_SW, SW_GLOVE);
#endif
set_bit(EV_SYN, dev->evbit);
set_bit(EV_KEY, dev->evbit);
set_bit(EV_ABS, dev->evbit);
set_bit(propbit, dev->propbit);
set_bit(BTN_TOUCH, dev->keybit);
set_bit(BTN_TOOL_FINGER, dev->keybit);
set_bit(KEY_BLACK_UI_GESTURE, dev->keybit);
set_bit(KEY_HOMEPAGE, dev->keybit);
#ifdef FTS_SUPPORT_TOUCH_KEY
if (info->board->support_mskey) {
int i;
for (i = 0 ; i < info->board->num_touchkey ; i++)
set_bit(info->board->touchkey[i].keycode, dev->keybit);
set_bit(EV_LED, dev->evbit);
set_bit(LED_MISC, dev->ledbit);
}
#endif
if (info->board->support_sidegesture) {
set_bit(KEY_SIDE_GESTURE, dev->keybit);
set_bit(KEY_SIDE_GESTURE_RIGHT, dev->keybit);
set_bit(KEY_SIDE_GESTURE_LEFT, dev->keybit);
}
input_set_abs_params(dev, ABS_MT_POSITION_X,
0, info->board->max_x, 0, 0);
input_set_abs_params(dev, ABS_MT_POSITION_Y,
0, info->board->max_y, 0, 0);
#ifdef CONFIG_SEC_FACTORY
input_set_abs_params(dev, ABS_MT_PRESSURE, 0, 255, 0, 0);
#else
if (info->board->support_mt_pressure)
input_set_abs_params(dev, ABS_MT_PRESSURE, 0, 10000, 0, 0);
#endif
input_set_abs_params(dev, ABS_MT_TOUCH_MAJOR,
0, 255, 0, 0);
input_set_abs_params(dev, ABS_MT_TOUCH_MINOR,
0, 255, 0, 0);
input_set_abs_params(dev, ABS_MT_CUSTOM, 0, 0xFFFF, 0, 0);
if (info->board->support_hover)
input_set_abs_params(dev, ABS_MT_DISTANCE, 0, 255, 0, 0);
if (propbit == INPUT_PROP_POINTER)
input_mt_init_slots(dev, FINGER_MAX, INPUT_MT_POINTER);
else
input_mt_init_slots(dev, FINGER_MAX, INPUT_MT_DIRECT);
input_set_drvdata(dev, info);
}
unsigned int bootmode;
static int __init get_bootmode(char *arg)
{
get_option(&arg, &bootmode);
return 0;
}
early_param("bootmode", get_bootmode);
static int fts_probe(struct i2c_client *client, const struct i2c_device_id *idp)
{
int retval;
struct fts_ts_info *info = NULL;
int i = 0;
input_info(true, &client->dev, "%s: FTS Driver [70%s]\n", __func__,
FTS_TS_DRV_VERSION);
if (!i2c_check_functionality(client->adapter, I2C_FUNC_I2C)) {
input_err(true, &client->dev, "%s: FTS err = EIO!\n", __func__);
return -EIO;
}
if (bootmode == 2) {
input_err(true, &client->dev, "%s : Do not load driver due to : device entered recovery mode %d\n",
__func__, bootmode);
return -ENODEV;
}
#ifdef CONFIG_BATTERY_SAMSUNG
if (lpcharge == 1) {
input_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) {
input_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) {
input_err(true, &client->dev, "%s: Failed to get driver data\n", __func__);
retval = -ENODEV;
goto err_get_drv_data;
}
i2c_set_clientdata(client, info);
info->probe_done = false;
if (info->board->power)
info->board->power(info, true);
info->fts_power_state = FTS_POWER_STATE_ACTIVE;
info->dev = &info->client->dev;
info->input_dev = input_allocate_device();
if (!info->input_dev) {
input_err(true, &info->client->dev, "%s: Failed to alloc input_dev\n", __func__);
retval = -ENOMEM;
goto err_input_allocate_device;
}
if (info->board->support_dex) {
info->input_dev_pad = input_allocate_device();
if (!info->input_dev_pad) {
input_err(true, &info->client->dev, "%s: Failed to alloc input_dev\n", __func__);
retval = -ENOMEM;
goto err_input_pad_allocate_device;
}
}
mutex_init(&info->device_mutex);
mutex_init(&info->i2c_mutex);
retval = fts_init(info);
if (retval) {
input_err(true, &info->client->dev, "%s: fts_init fail!\n", __func__);
goto err_fts_init;
}
mutex_lock(&info->device_mutex);
info->reinit_done = true;
mutex_unlock(&info->device_mutex);
info->max_z_value = 0;
info->min_z_value = 0xFFFFFFFF;
info->sum_z_value = 0;
wake_lock_init(&info->wakelock, WAKE_LOCK_SUSPEND, "tsp_wakelock");
init_completion(&info->resume_done);
complete_all(&info->resume_done);
if (info->board->support_dex) {
info->input_dev_pad->name = "sec_touchpad";
fts_set_input_prop(info, info->input_dev_pad, INPUT_PROP_POINTER);
}
info->dex_name = "";
if (info->board->device_num == 0)
info->input_dev->name = "sec_touchscreen";
else if (info->board->device_num == 2)
info->input_dev->name = "sec_touchscreen2";
else
info->input_dev->name = "sec_touchscreen";
fts_set_input_prop(info, info->input_dev, INPUT_PROP_DIRECT);
#ifdef USE_OPEN_CLOSE
info->input_dev->open = fts_input_open;
info->input_dev->close = fts_input_close;
#endif
info->input_dev_touch = info->input_dev;
retval = input_register_device(info->input_dev);
if (retval) {
input_err(true, &info->client->dev, "%s: input_register_device fail!\n", __func__);
goto err_register_input;
}
if (info->board->support_dex) {
retval = input_register_device(info->input_dev_pad);
if (retval) {
input_err(true, &info->client->dev, "%s: input_register_device fail!\n", __func__);
goto err_register_input_pad;
}
}
for (i = 0; i < FINGER_MAX; i++) {
info->finger[i].state = EVENTID_LEAVE_POINTER;
info->finger[i].mcount = 0;
}
retval = fts_irq_enable(info, true);
if (retval < 0) {
input_err(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);
input_info(true, &client->dev, "%s[%d] called!\n", __func__, info->irq);
#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
retval = sec_cmd_init(&info->sec, ft_commands,
ARRAY_SIZE(ft_commands), SEC_CLASS_DEVT_TSP);
if (retval < 0) {
input_err(true, &info->client->dev,
"%s: Failed to sec_cmd_init\n", __func__);
retval = -ENODEV;
goto err_sec_cmd;
}
retval = sysfs_create_group(&info->sec.fac_dev->kobj,
&sec_touch_factory_attr_group);
if (retval < 0) {
input_err(true, &info->client->dev, "%s: Failed to create sysfs group\n", __func__);
goto err_sysfs;
}
retval = sysfs_create_link(&info->sec.fac_dev->kobj,
&info->input_dev->dev.kobj, "input");
if (retval < 0) {
input_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) {
#ifdef CONFIG_SEC_SYSFS
info->fac_dev_tk = sec_device_create(info, "sec_touchkey");
#else
info->fac_dev_tk = device_create(sec_class, NULL, 11, info, "sec_touchkey");
#endif
if (IS_ERR(info->fac_dev_tk))
input_err(true, &info->client->dev, "%s: Failed to create device for the touchkey sysfs\n", __func__);
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)
input_err(true, &info->client->dev, "%s: Failed to create sysfs group\n", __func__);
else {
retval = sysfs_create_link(&info->fac_dev_tk->kobj,
&info->input_dev->dev.kobj, "input");
if (retval < 0)
input_err(true, &info->client->dev,
"%s: Failed to create link\n", __func__);
}
}
}
#endif
#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) {
input_err(true, &info->client->dev,
"%s: Failed to create sysfs attributes\n",
__func__);
}
}
fts_secure_touch_init(info);
#endif
device_init_wakeup(&client->dev, true);
info->probe_done = true;
#ifdef FTS_SUPPORT_STRINGLIB
fts_check_custom_library(info);
#endif
schedule_delayed_work(&info->work_read_info, msecs_to_jiffies(5 * MSEC_PER_SEC));
#if defined(CONFIG_TOUCHSCREEN_DUMP_MODE)
dump_callbacks.inform_dump = tsp_dump;
INIT_DELAYED_WORK(&info->debug_work, dump_tsp_rawdata);
p_debug_work = &info->debug_work;
#endif
input_err(true, &info->client->dev, "%s: done\n", __func__);
input_log_fix();
return 0;
#ifdef SEC_TSP_FACTORY_TEST
err_sysfs:
sec_cmd_exit(&info->sec, SEC_CLASS_DEVT_TSP);
err_sec_cmd:
#endif
if (info->irq_enabled)
fts_irq_enable(info, false);
err_enable_irq:
if (info->board->support_dex) {
input_unregister_device(info->input_dev_pad);
info->input_dev_pad = NULL;
}
err_register_input_pad:
input_unregister_device(info->input_dev);
info->input_dev = NULL;
info->input_dev_touch = NULL;
err_register_input:
wake_lock_destroy(&info->wakelock);
#ifdef SEC_TSP_FACTORY_TEST
kfree(info->ito_result);
if (info->cx_data)
kfree(info->cx_data);
kfree(info->miscal_ref_raw);
kfree(info->pFrame);
#endif
err_fts_init:
mutex_destroy(&info->device_mutex);
mutex_destroy(&info->i2c_mutex);
if (info->board->support_dex) {
if (info->input_dev_pad)
input_free_device(info->input_dev_pad);
}
err_input_pad_allocate_device:
if (info->input_dev)
input_free_device(info->input_dev);
err_input_allocate_device:
if (info->board->power)
info->board->power(info, false);
if (gpio_is_valid(info->board->irq_gpio))
gpio_free(info->board->irq_gpio);
kfree(info);
err_get_drv_data:
err_setup_drv_data:
input_err(true, &client->dev, "%s: failed(%d)\n", __func__, retval);
input_log_fix();
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
input_info(true, &info->client->dev, "%s\n", __func__);
info->shutdown_is_on_going = true;
disable_irq_nosync(info->client->irq);
free_irq(info->client->irq, info);
cancel_delayed_work_sync(&info->work_read_info);
flush_delayed_work(&info->work_read_info);
cancel_delayed_work_sync(&info->reset_work);
flush_delayed_work(&info->reset_work);
wake_lock_destroy(&info->wakelock);
#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
#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);
#ifdef CONFIG_SEC_SYSFS
sec_device_destroy(info->fac_dev_tk->devt);
#else
device_destroy(sec_class, 11);
#endif
}
#endif
sysfs_remove_link(&info->sec.fac_dev->kobj, "input");
sysfs_remove_group(&info->sec.fac_dev->kobj,
&sec_touch_factory_attr_group);
sec_cmd_exit(&info->sec, SEC_CLASS_DEVT_TSP);
kfree(info->ito_result);
if (info->cx_data)
kfree(info->cx_data);
kfree(info->miscal_ref_raw);
kfree(info->pFrame);
#endif
if (info->board->support_dex) {
input_mt_destroy_slots(info->input_dev_pad);
input_unregister_device(info->input_dev_pad);
}
info->input_dev_pad = NULL;
info->input_dev = info->input_dev_touch;
input_mt_destroy_slots(info->input_dev);
input_unregister_device(info->input_dev);
info->input_dev = NULL;
info->input_dev_touch = NULL;
if (info->board->power)
info->board->power(info, false);
#ifdef FTS_SUPPORT_TOUCH_KEY
if (info->board->led_power)
info->board->led_power(info, false);
#endif
info->shutdown_is_on_going = 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);
input_info(true, &info->client->dev, "%s\n", __func__);
retval = fts_start_device(info);
if (retval < 0)
input_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;
if (!info->probe_done) {
input_dbg(true, &info->client->dev, "%s: not probe\n", __func__);
goto out;
}
if (!info->info_work_done) {
input_err(true, &info->client->dev, "%s not finished info work\n", __func__);
goto out;
}
input_dbg(false, &info->client->dev, "%s\n", __func__);
#ifdef CONFIG_TRUSTONIC_TRUSTED_UI
if(TRUSTEDUI_MODE_TUI_SESSION & trustedui_get_current_mode()){
input_err(true, &info->client->dev, "%s TUI cancel event call!\n", __func__);
msleep(100);
tui_force_close(1);
msleep(200);
if(TRUSTEDUI_MODE_TUI_SESSION & trustedui_get_current_mode()){
input_err(true, &info->client->dev, "%s TUI flag force clear!\n", __func__);
trustedui_clear_mask(TRUSTEDUI_MODE_VIDEO_SECURED|TRUSTEDUI_MODE_INPUT_SECURED);
trustedui_set_mode(TRUSTEDUI_MODE_OFF);
}
}
#endif
#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) {
input_err(true, &info->client->dev,
"%s: Failed to start device\n", __func__);
goto out;
}
#endif
out:
return 0;
}
static void fts_input_close(struct input_dev *dev)
{
struct fts_ts_info *info = input_get_drvdata(dev);
if (!info->probe_done || info->shutdown_is_on_going) {
input_dbg(false, &info->client->dev, "%s: not probe\n", __func__);
return;
}
if (!info->info_work_done) {
input_err(true, &info->client->dev, "%s not finished info work\n", __func__);
return;
}
input_dbg(false, &info->client->dev, "%s\n", __func__);
#ifdef CONFIG_TRUSTONIC_TRUSTED_UI
if(TRUSTEDUI_MODE_TUI_SESSION & trustedui_get_current_mode()){
input_err(true, &info->client->dev, "%s TUI cancel event call!\n", __func__);
msleep(100);
tui_force_close(1);
msleep(200);
if(TRUSTEDUI_MODE_TUI_SESSION & trustedui_get_current_mode()){
input_err(true, &info->client->dev, "%s TUI flag force clear!\n", __func__);
trustedui_clear_mask(TRUSTEDUI_MODE_VIDEO_SECURED|TRUSTEDUI_MODE_INPUT_SECURED);
trustedui_set_mode(TRUSTEDUI_MODE_OFF);
}
}
#endif
#ifdef USE_OPEN_DWORK
cancel_delayed_work(&info->open_work);
#endif
cancel_delayed_work(&info->reset_work);
#ifndef CONFIG_SEC_FACTORY
if (info->board->use_pressure)
info->lowpower_flag |= FTS_MODE_PRESSURE;
#endif
fts_stop_device(info, info->lowpower_flag);
#ifdef FTS_SUPPORT_HOVER
info->retry_hover_enable_after_wakeup = 0;
#endif
}
#endif
#if 0//def CONFIG_SEC_FACTORY
static void fts_reinit_fac(struct fts_ts_info *info)
{
info->touch_count = 0;
fts_command(info, FLUSHBUFFER);
fts_delay(10);
fts_command(info, SENSEON);
#ifdef FTS_SUPPORT_PRESSURE_SENSOR
fts_command(info, FTS_CMD_PRESSURE_SENSE_ON);
#endif
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);
#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_ON);
#endif
fts_command(info, FORCECALIBRATION);
fts_interrupt_set(info, INT_ENABLE);
input_info(true, &info->client->dev, "%s\n", __func__);
}
#endif
void fts_reinit(struct fts_ts_info *info)
{
fts_systemreset(info, 10);
fts_read_chip_id(info);
#ifdef FTS_SUPPORT_NOISE_PARAM
fts_set_noise_param(info);
#endif
#if defined(FTS_SUPPORT_STRINGLIB) && defined(CONFIG_SEC_FACTORY)
fts_disable_string(info);
#endif
fts_command(info, FLUSHBUFFER);
fts_delay(10);
fts_command(info, SENSEON);
#ifdef FTS_SUPPORT_PRESSURE_SENSOR
fts_command(info, FTS_CMD_PRESSURE_SENSE_ON);
#endif
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_glove_enabled)
fts_command(info, FTS_CMD_SET_FAST_GLOVE_MODE);
else if (info->glove_enabled)
fts_command(info, FTS_CMD_GLOVE_ON);
#ifdef FTS_SUPPORT_TA_MODE
if (info->TA_Pluged)
fts_command(info, FTS_CMD_CHARGER_PLUGGED);
#endif
if (info->dex_mode) {
unsigned char regAdd[3] = {0xC1, 0x13, info->dex_mode};
input_info(true, &info->client->dev, "%s: set dex mode\n", __func__);
if (fts_write_reg(info, regAdd, 3) < 0)
input_err(true, &info->client->dev, "%s: dex_enable failed\n", __func__);
}
if (info->brush_mode) {
unsigned char regAdd[2] = {0xC1, 0x14};
input_info(true, &info->client->dev, "%s: set brush mode\n", __func__);
if (fts_write_reg(info, regAdd, 2) < 0)
input_err(true, &info->client->dev, "%s: brush_enable failed\n", __func__);
}
if (info->touchable_area) {
unsigned char regAdd[2] = {0xC1, 0x15};
input_info(true, &info->client->dev, "%s: set 16:9 mode\n", __func__);
if (fts_write_reg(info, regAdd, 2) < 0)
input_err(true, &info->client->dev, "%s: set_touchable_area failed\n", __func__);
}
/* because edge and dead zone will recover soon */
fts_set_grip_type(info, ONLY_EDGE_HANDLER);
info->touch_count = 0;
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);
if (info->board->support_mt_pressure)
input_report_abs(info->input_dev, ABS_MT_PRESSURE, 0);
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)) {
input_info(true, &info->client->dev,
"[RA] tID:%d mc:%d tc:%d Ver[%02X%04X|%01X]\n",
i, info->finger[i].mcount, info->touch_count,
info->panel_revision, info->fw_main_version_of_ic,
info->flip_enable);
do_gettimeofday(&info->time_released[i]);
if (info->time_longest < (info->time_released[i].tv_sec - info->time_pressed[i].tv_sec))
info->time_longest = (info->time_released[i].tv_sec - info->time_pressed[i].tv_sec);
}
info->finger[i].state = EVENTID_LEAVE_POINTER;
info->finger[i].mcount = 0;
}
info->touch_count = 0;
input_report_key(info->input_dev, BTN_TOUCH, 0);
input_report_key(info->input_dev, BTN_TOOL_FINGER, 0);
#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_HOMEPAGE, 0);
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);
}
input_sync(info->input_dev);
info->check_multi = 0;
}
#if 0/*def CONFIG_TRUSTONIC_TRUSTED_UI*/
void trustedui_mode_on(void)
{
input_info(true, &tui_tsp_info->client->dev, "%s, release all finger..", __func__);
fts_release_all_finger(tui_tsp_info);
}
#endif
#if defined(CONFIG_TOUCHSCREEN_DUMP_MODE)
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)
input_err(true, &info->client->dev, "%s: ## checking.. ignored.\n", __func__);
info->rawdata_read_lock = true;
input_info(true, &info->client->dev, "%s: ## run CX data ##, %d\n", __func__, __LINE__);
run_cx_data_read((void *)&info->sec);
for (i = 0; i < 5; i++) {
input_info(true, &info->client->dev, "%s: ## run Raw Cap data ##, %d\n", __func__, __LINE__);
run_rawcap_read((void *)&info->sec);
input_info(true, &info->client->dev, "%s: ## run Delta ##, %d\n", __func__, __LINE__);
run_delta_read((void *)&info->sec);
fts_delay(50);
}
input_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;
pr_err("%s: %s %s: start\n", FTS_TS_DRV_NAME, SECLOG, __func__);
schedule_delayed_work(p_debug_work, msecs_to_jiffies(100));
}
#endif
static void fts_reset(struct fts_ts_info *info, unsigned int ms)
{
input_info(true, &info->client->dev, "%s: Recover IC, discharge time:%d\n", __func__, ms);
if (info->board->power)
info->board->power(info, false);
fts_delay(ms);
if (info->board->power)
info->board->power(info, true);
fts_delay(5);
}
static void fts_reset_work(struct work_struct *work)
{
struct fts_ts_info *info = container_of(work, struct fts_ts_info,
reset_work.work);
#ifdef CONFIG_SECURE_TOUCH
if (atomic_read(&info->st_enabled)) {
input_err(true, &info->client->dev, "%s: secure touch enabled \n",
__func__);
return;
}
#endif
input_info(true, &info->client->dev, "%s: Call Power-Off to recover IC\n", __func__);
info->reset_is_on_going = true;
fts_stop_device(info, false);
msleep(100); /* Delay to discharge the IC from ESD or On-state.*/
if (fts_start_device(info) < 0)
input_err(true, &info->client->dev, "%s: Failed to start device\n", __func__);
if (info->input_dev_touch->disabled) {
input_err(true, &info->client->dev , "%s: call input_close\n", __func__);
fts_stop_device(info, info->lowpower_flag);
if ((info->lowpower_flag & FTS_MODE_AOD) && info->use_sponge) {
int i, ret = -1;
u8 data[8] = {0, };
#ifdef FTS_SUPPORT_STRINGLIB
unsigned short addr = FTS_CMD_STRING_ACCESS + 2;
#endif
for (i = 0; i < 4; i++) {
data[i * 2] = info->rect_data[i] & 0xFF;
data[i * 2 + 1] = (info->rect_data[i] >> 8) & 0xFF;
}
disable_irq(info->client->irq);
#ifdef FTS_SUPPORT_STRINGLIB
ret = info->fts_write_to_string(info, &addr, data, sizeof(data));
#endif
if (ret < 0)
input_err(true, &info->client->dev, "%s: failed. ret: %d\n", __func__, ret);
enable_irq(info->client->irq);
}
}
info->reset_is_on_going = false;
}
static void fts_read_info_work(struct work_struct *work)
{
struct fts_ts_info *info = container_of(work, struct fts_ts_info,
work_read_info.work);
#ifdef FTS_SUPPORT_PRESSURE_SENSOR
unsigned char *data = NULL;
unsigned char index = 0;
#endif
short minval = 0x7FFF;
short maxval = 0x8000;
int ret;
input_info(true, &info->client->dev, "%s\n", __func__);
#ifdef TCLM_CONCEPT
ret = sec_tclm_check_cal_case(info->tdata);
input_info(true, &info->client->dev, "%s: sec_tclm_check_cal_case ret: %d \n", __func__, ret);
#endif
if (info->boot_crc_check_fail == FTS_BOOT_CRC_FAIL) {
info->nv_crc_fail_count++;
if (info->nv_crc_fail_count > 0xFE)
info->nv_crc_fail_count = 0xFE;
}
ret = fts_get_tsp_test_result(info);
if (ret < 0)
input_err(true, &info->client->dev, "%s: failed to get result\n",
__func__);
#ifdef FTS_SUPPORT_PRESSURE_SENSOR
ret = get_nvm_data(info, GROUP_INDEX, &index);
/*
* index is 0, 0xFF : cleared, do not calibrated
* index is 1 : Ass'y
* index is 2 : Rear
* index is 3 : BackGlass
*/
data = kzalloc(nvm_data[PRESSURE_STRENGTH].length, GFP_KERNEL);
if (!data)
goto err_no_mem;;
ret = get_nvm_data(info, PRESSURE_STRENGTH, data);
if (ret <= 0)
goto err_data;
if ((index > 0) && (index <= 4)) {
/* calibrated strength is saved by (index - 1) in flash memory */
info->pressure_left = (short)(data[(index - 1) * 8 + 0] | ((data[(index - 1) * 8 + 1] << 8) & 0xFF00));
info->pressure_center = (short)(data[(index - 1) * 8 + 2] | ((data[(index - 1) * 8 + 3] << 8) & 0xFF00));
info->pressure_right = (short)(data[(index - 1) * 8 + 4] | ((data[(index - 1) * 8 + 5] << 8) & 0xFF00));
input_raw_info(true, &info->client->dev, "%s: [pressure][index:%d]: %d, %d, %d\n",
__func__, index, info->pressure_left, info->pressure_center, info->pressure_right);
} else if (index == 0) {
input_raw_info(true, &info->client->dev, "%s: [pressure] do not calibrated\n", __func__);
} else {
input_raw_info(true, &info->client->dev, "%s: [pressure]: invalid index: %d\n",
__func__, index);
}
fts_panel_ito_test(info, OPEN_SHORT_CRACK_TEST);
input_raw_info(true, &info->client->dev, "%s: [ito] %02X, %02X, %02X, %02X\n",
__func__, info->ito_test[0], info->ito_test[1], info->ito_test[2], info->ito_test[3]);
fts_read_frame(info, TYPE_BASELINE_DATA, &minval, &maxval);
kfree(data);
input_log_fix();
info->info_work_done = true;
return;
err_data:
kfree(data);
err_no_mem:
input_info(true, &info->client->dev, "%s: failed\n", __func__);
input_log_fix();
info->info_work_done = true;
return;
#else
fts_read_frame(info, TYPE_BASELINE_DATA, &minval, &maxval);
info->info_work_done = true;
return;
#endif
}
static int fts_stop_device(struct fts_ts_info *info, bool lpmode)
{
input_info(true, &info->client->dev, "%s\n", __func__);
#if defined(CONFIG_SECURE_TOUCH)
fts_secure_touch_stop(info, 1);
#endif
mutex_lock(&info->device_mutex);
#ifdef TCLM_CONCEPT
sec_tclm_debug_info(info->tdata);
#endif
if (info->touch_stopped) {
input_err(true, &info->client->dev, "%s: already power off\n", __func__);
goto out;
}
if (lpmode) {
input_info(true, &info->client->dev, "%s: lowpower flag:%d\n", __func__, info->lowpower_flag);
if (info->board->support_sidegesture) {
fts_enable_feature(info, FTS_FEATURE_SIDE_GUSTURE, true);
fts_delay(20);
}
if (device_may_wakeup(&info->client->dev))
enable_irq_wake(info->irq);
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
fts_enable_feature(info, FTS_FEATURE_LPM_FUNCTION, true);
fts_command(info, FTS_CMD_LOWPOWER_MODE);
info->fts_power_state = FTS_POWER_STATE_LOWPOWER;
#ifdef FTS_SUPPORT_STRINGLIB
#ifndef CONFIG_SEC_FACTORY
if (info->lowpower_flag)
#endif
{
unsigned short addr = FTS_CMD_STRING_ACCESS;
int ret;
ret = info->fts_write_to_string(info, &addr, &info->lowpower_flag, sizeof(info->lowpower_flag));
if (ret < 0)
input_err(true, &info->client->dev, "%s: failed. ret: %d\n", __func__, ret);
}
#endif
} else {
fts_interrupt_set(info, INT_DISABLE);
disable_irq(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
info->touch_stopped = true;
info->hover_enabled = false;
info->hover_ready = false;
info->fts_power_state = FTS_POWER_STATE_POWERDOWN;
if (info->board->power)
info->board->power(info, false);
}
out:
mutex_unlock(&info->device_mutex);
return 0;
}
static int fts_start_device(struct fts_ts_info *info)
{
input_info(true, &info->client->dev, "%s%s\n",
__func__, info->fts_power_state ? ": exit low power mode TP" : "");
#if defined(CONFIG_SECURE_TOUCH)
fts_secure_touch_stop(info, 1);
#endif
mutex_lock(&info->device_mutex);
if (info->fts_power_state == FTS_POWER_STATE_ACTIVE) {
input_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->fts_power_state == FTS_POWER_STATE_POWERDOWN) {
if (info->board->power)
info->board->power(info, true);
info->touch_stopped = false;
fts_set_warmboot_crc_enable(info);
info->reinit_done = false;
fts_reinit(info);
info->reinit_done = true;
} else { /* FTS_POWER_STATE_LOWPOWER */
fts_interrupt_set(info, INT_DISABLE);
disable_irq(info->irq);
fts_command(info, FTS_CMD_NORMAL_MODE);
if (fts_fw_wait_for_event(info, STATUS_EVENT_FORCE_CAL_DONE_D3)) {
info->reinit_done = false;
fts_reinit(info);
info->reinit_done = true;
} else {
fts_interrupt_set(info, INT_ENABLE);
}
if (device_may_wakeup(&info->client->dev))
disable_irq_wake(info->irq);
}
info->fts_power_state = FTS_POWER_STATE_ACTIVE;
enable_irq(info->irq);
#ifdef FTS_SUPPORT_STRINGLIB
#ifndef CONFIG_SEC_FACTORY
if (info->lowpower_flag)
#endif
{
unsigned short addr = FTS_CMD_STRING_ACCESS;
int ret;
ret = info->fts_write_to_string(info, &addr, &info->lowpower_flag, sizeof(info->lowpower_flag));
if (ret < 0)
input_err(true, &info->client->dev, "%s: failed. ret: %d\n", __func__, ret);
}
#endif
out:
mutex_unlock(&info->device_mutex);
#ifdef FTS_SUPPORT_HOVER
input_info(true, &info->client->dev, "%s: cmd after wakeup : h%d\n", __func__, 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_enabled = true;
}
#endif
fts_wirelesscharger_mode(info);
return 0;
}
static void fts_shutdown(struct i2c_client *client)
{
struct fts_ts_info *info = i2c_get_clientdata(client);
input_info(true, &info->client->dev, "%s\n", __func__);
fts_remove(client);
}
#ifdef CONFIG_PM
static int fts_pm_suspend(struct device *dev)
{
struct fts_ts_info *info = dev_get_drvdata(dev);
input_dbg(true, &info->client->dev, "%s\n", __func__);
if (info->fts_power_state > FTS_POWER_STATE_POWERDOWN)
reinit_completion(&info->resume_done);
return 0;
}
static int fts_pm_resume(struct device *dev)
{
struct fts_ts_info *info = dev_get_drvdata(dev);
input_dbg(true, &info->client->dev, "%s\n", __func__);
if (info->fts_power_state > FTS_POWER_STATE_POWERDOWN)
complete_all(&info->resume_done);
return 0;
}
#endif
#if (!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);
input_dbg(true, &info->client->dev, "%s\n", __func__);
fts_stop_device(info, info->lowpower_flag);
return 0;
}
static int fts_resume(struct i2c_client *client)
{
struct fts_ts_info *info = i2c_get_clientdata(client);
input_dbg(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_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);