kernel_samsung_a53x/drivers/input/touchscreen/stm/fts5cu56a/fts_ts.c
2024-06-15 16:02:09 -03:00

4369 lines
124 KiB
C
Executable file

/******************** (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.
*******************************************************************************/
#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/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
#include "fts_ts.h"
#if defined(CONFIG_INPUT_SEC_SECURE_TOUCH)
#include <linux/pm_runtime.h>
#include <linux/atomic.h>
#endif
#ifdef CONFIG_OF
#ifndef USE_OPEN_CLOSE
#define USE_OPEN_CLOSE
#undef CONFIG_PM
#endif
#endif
#ifdef USE_OPEN_CLOSE
static int fts_input_open(struct input_dev *dev);
static void fts_input_close(struct input_dev *dev);
#ifdef USE_OPEN_DWORK
static void fts_open_work(struct work_struct *work);
#endif
#endif
static int fts_stop_device(struct fts_ts_info *info);
static int fts_start_device(struct fts_ts_info *info);
static 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);
static void fts_fw_reset_work(struct work_struct *work);
//static void fts_lfd_ctrl_work(struct work_struct *work);
//static void fts_lfd_ctrl(struct fts_ts_info *info, int touch_count);
#if defined(CONFIG_TOUCHSCREEN_DUMP_MODE)
#include <linux/input/sec_tsp_dumpkey.h>
static void tsp_dump(void);
static void fts_sponge_dump_flush(struct fts_ts_info *info, int dump_area);
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
int fts_systemreset(struct fts_ts_info *info, unsigned int msec);
#if defined(CONFIG_INPUT_SEC_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, (0664),
fts_secure_touch_enable_show,
fts_secure_touch_enable_store),
__ATTR(secure_touch, (0444),
fts_secure_touch_show,
NULL),
};
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: already disabled, pending:%d\n",
__func__, atomic_read(&info->st_pending_irqs));
break;
}
fts_delay(200);
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_all(&info->st_powerdown);
complete(&info->st_interrupt);
input_info(true, &info->client->dev, "%s: disabled\n", __func__);
break;
case 1:
if (info->reset_is_on_going) {
input_err(true, &info->client->dev, "%s: reset is on going becuse i2c fail\n",
__func__);
return -EBUSY;
}
if (atomic_read(&info->st_enabled)) {
input_err(true, &info->client->dev, "%s: already enabled, pending:%d\n",
__func__, atomic_read(&info->st_pending_irqs));
err = -EBUSY;
break;
}
fts_delay(200);
/* synchronize_irq -> disable_irq + enable_irq
* concern about timing issue.
*/
fts_interrupt_set(info, INT_DISABLE);
/* 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;
fts_interrupt_set(info, INT_ENABLE);
break;
}
reinit_completion(&info->st_powerdown);
reinit_completion(&info->st_interrupt);
atomic_set(&info->st_enabled, 1);
atomic_set(&info->st_pending_irqs, 0);
fts_interrupt_set(info, INT_ENABLE);
input_info(true, &info->client->dev, "%s: enabled\n", __func__);
break;
default:
input_err(true, &info->client->dev, "%s: unsupported value: %lu\n", __func__, value);
err = -EINVAL;
break;
}
#ifdef CONFIG_TOUCHSCREEN_DUAL_FOLDABLE
if (info->ss_drv)
info->ss_drv->is_running = value;
#endif
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)
{
init_completion(&info->st_powerdown);
init_completion(&info->st_interrupt);
}
static void fts_secure_touch_stop(struct fts_ts_info *info, int blocking)
{
mutex_lock(&info->st_lock);
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);
}
mutex_unlock(&info->st_lock);
}
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,
u8 *reg, u16 num_com)
{
struct i2c_msg xfer_msg[2];
int ret;
int retry = FTS_TS_I2C_RETRY_CNT;
u8 *buff;
if (!info->resume_done.done) {
ret = wait_for_completion_interruptible_timeout(&info->resume_done, msecs_to_jiffies(500));
if (ret <= 0) {
input_err(true, &info->client->dev, "%s: resume is not handled:%d\n", __func__, ret);
return -EIO;
}
}
if (info->fts_power_state == FTS_POWER_STATE_POWERDOWN) {
input_err(true, &info->client->dev, "%s: Sensor stopped\n", __func__);
goto exit;
}
#ifdef CONFIG_INPUT_SEC_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
buff = kzalloc(num_com, GFP_KERNEL);
if (!buff)
return -ENOMEM;
memcpy(buff, reg, num_com);
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 = buff;
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 SEC_TSP_FACTORY_TEST
if (info->debug_string & FTS_DEBUG_SEND_UEVENT)
sec_cmd_send_event_to_user(&info->sec, NULL, "RESULT=I2C");
#endif
#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
}
if (info->debug_string & FTS_DEBUG_PRINT_I2C_WRITE_CMD) {
int i;
pr_info("sec_input: i2c_cmd: W: ");
for (i = 0; i < num_com; i++)
pr_cont("%02X ", buff[i]);
pr_cont("\n");
}
kfree(buff);
return ret;
exit:
return 0;
}
int fts_read_reg(struct fts_ts_info *info, u8 *reg, int cnum,
u8 *buf, int num)
{
struct i2c_msg xfer_msg[2];
int ret;
int retry = FTS_TS_I2C_RETRY_CNT;
u8 *buff;
u8 *msg_buff;
if (!info->resume_done.done) {
ret = wait_for_completion_interruptible_timeout(&info->resume_done, msecs_to_jiffies(500));
if (ret <= 0) {
input_err(true, &info->client->dev, "%s: resume is not handled:%d\n", __func__, ret);
return -EIO;
}
}
if (info->fts_power_state == FTS_POWER_STATE_POWERDOWN) {
input_err(true, &info->client->dev, "%s: Sensor stopped\n", __func__);
goto exit;
}
#ifdef CONFIG_INPUT_SEC_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
msg_buff = kzalloc(cnum, GFP_KERNEL);
if (!msg_buff)
return -ENOMEM;
memcpy(msg_buff, reg, cnum);
buff = kzalloc(num, GFP_KERNEL);
if (!buff) {
kfree(msg_buff);
return -ENOMEM;
}
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 = msg_buff;
xfer_msg[1].addr = info->client->addr;
xfer_msg[1].len = num;
xfer_msg[1].flags = I2C_M_RD;
xfer_msg[1].buf = buff;
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__);
#ifdef SEC_TSP_FACTORY_TEST
if (info->debug_string & FTS_DEBUG_SEND_UEVENT)
sec_cmd_send_event_to_user(&info->sec, NULL, "RESULT=I2C");
#endif
#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
}
if (info->debug_string & FTS_DEBUG_PRINT_I2C_READ_CMD) {
int i;
pr_info("sec_input: i2c_cmd: R: ");
for (i = 0; i < cnum; i++)
pr_cont("%02X ", msg_buff[i]);
pr_cont("|");
for (i = 0; i < num; i++)
pr_cont("%02X ", buff[i]);
pr_cont("\n");
}
memcpy(buf, buff, num);
kfree(msg_buff);
kfree(buff);
return ret;
exit:
return 0;
}
#ifdef FTS_SUPPORT_SPONGELIB
#ifdef CONFIG_SEC_FACTORY
static void fts_disable_sponge(struct fts_ts_info *info)
{
u8 regAdd[3] = {FTS_CMD_SET_FUNCTION_ONOFF, FTS_FUNCTION_ENABLE_SPONGE_LIB, 0x00};
int ret = 0;
ret = fts_write_reg(info, &regAdd[0], 3);
input_info(true, &info->client->dev, "%s: Sponge Library Disabled, ret = %d\n", __func__, ret);
}
#endif
static int fts_read_from_sponge(struct fts_ts_info *info,
u16 offset, u8 *data, int length)
{
u8 sponge_reg[3];
u8 *buf;
int rtn;
#ifdef CONFIG_INPUT_SEC_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
offset += FTS_CMD_SPONGE_ACCESS;
sponge_reg[0] = 0xAA;
sponge_reg[1] = (offset >> 8) & 0xFF;
sponge_reg[2] = offset & 0xFF;
buf = kzalloc(length, GFP_KERNEL);
if (buf == NULL)
return -ENOMEM;
rtn = fts_read_reg(info, sponge_reg, 3, buf, length);
if (rtn >= 0)
memcpy(data, &buf[0], length);
else
input_err(true, &info->client->dev, "%s: failed\n", __func__);
kfree(buf);
return rtn;
}
/*
* int fts_write_to_sponge(struct fts_ts_info *, u16 *, u8 *, int)
* send command or write specific value to the sponge area.
* sponge area means guest image or display lab firmware.. etc..
*/
static int fts_write_to_sponge(struct fts_ts_info *info,
u16 offset, u8 *data, int length)
{
u8 *regAdd;
int ret = 0;
if (info->fts_power_state == FTS_POWER_STATE_POWERDOWN) {
input_err(true, &info->client->dev, "%s: Sensor stopped\n", __func__);
return 0;
}
#ifdef CONFIG_INPUT_SEC_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(3 + length, GFP_KERNEL);
offset += FTS_CMD_SPONGE_ACCESS;
regAdd[0] = FTS_CMD_SPONGE_READ_WRITE_CMD;
regAdd[1] = (offset >> 8) & 0xFF;
regAdd[2] = offset & 0xFF;
memcpy(&regAdd[3], &data[0], length);
ret = fts_write_reg(info, &regAdd[0], 3 + length);
if (ret <= 0) {
input_err(true, &info->client->dev,
"%s: sponge command is failed. ret: %d\n", __func__, ret);
}
// Notify Command
regAdd[0] = FTS_CMD_SPONGE_NOTIFY_CMD;
regAdd[1] = (offset >> 8) & 0xFF;
regAdd[2] = offset & 0xFF;
ret = fts_write_reg(info, &regAdd[0], 3);
if (ret <= 0) {
input_err(true, &info->client->dev,
"%s: sponge notify is failed.\n", __func__);
kfree(regAdd);
return ret;
}
input_info(true, &info->client->dev,
"%s: sponge notify is OK[0x%02X].\n", __func__, *data);
kfree(regAdd);
return ret;
}
#ifdef FTS_SUPPORT_SPONGELIB
int fts_check_custom_library(struct fts_ts_info *info)
{
struct fts_sponge_information *sponge_info;
u8 regAdd[3] = { 0xA4, 0x06, 0x91 };
u8 data[sizeof(struct fts_sponge_information)] = { 0 };
int ret = -1;
fts_interrupt_set(info, INT_DISABLE);
ret = fts_write_reg(info, &regAdd[0], 3);
if (ret < 0)
goto out;
fts_interrupt_set(info, INT_ENABLE);
regAdd[0] = 0xA6;
regAdd[1] = 0x00;
regAdd[2] = 0x00;
ret = fts_read_reg(info, &regAdd[0], 3, &data[0], sizeof(struct fts_sponge_information));
if (ret <= 0) {
input_err(true, &info->client->dev, "%s: failed. ret: %d\n", __func__, ret);
goto out;
}
sponge_info = (struct fts_sponge_information *) &data[0];
input_info(true, &info->client->dev,
"%s: (%d) model name %s\n",
__func__, ret, sponge_info->sponge_model_name);
info->use_sponge = true;
if (info->use_sponge) {
ret = fts_write_to_sponge(info, FTS_CMD_SPONGE_OFFSET_MODE,
&info->lowpower_flag, sizeof(info->lowpower_flag));
if (ret < 0) {
input_err(true, &info->client->dev, "%s: failed to write lowpower flag. ret: %d\n", __func__, ret);
goto out;
}
memset(regAdd, 0x00, 3);
ret = fts_read_from_sponge(info, FTS_CMD_SPONGE_FOD_INFO, regAdd, 3);
if (ret < 0) {
input_err(true, &info->client->dev, "%s: failed to read FOD INFO. ret: %d\n", __func__, ret);
goto out;
}
info->fod_x = regAdd[0];
info->fod_y = regAdd[1];
info->fod_vi_size = regAdd[2];
input_info(true, &info->client->dev, "%s: fod_x:%d, fod_y:%d, fod_vi_size:%d\n",
__func__, info->fod_x, info->fod_y, info->fod_vi_size);
/* read dump info */
ret = fts_read_from_sponge(info, FTS_CMD_SPONGE_LP_DUMP, regAdd, 2);
if (ret < 0) {
input_err(true, &info->client->dev, "%s: Failed to read dump_data. ret: %d\n", __func__, ret);
goto out;
}
if (!info->fod_vi_data) {
info->fod_vi_data = devm_kzalloc(&info->client->dev, info->fod_vi_size, GFP_KERNEL);
if (!info->fod_vi_data)
return -ENOMEM;
}
info->sponge_inf_dump = (regAdd[0] & FTS_SPONGE_DUMP_INF_MASK) >> FTS_SPONGE_DUMP_INF_SHIFT;
info->sponge_dump_format = regAdd[0] & FTS_SPONGE_DUMP_EVENT_MASK;
info->sponge_dump_event = regAdd[1];
info->sponge_dump_border = FTS_CMD_SPONGE_LP_DUMP_EVENT
+ (info->sponge_dump_format * info->sponge_dump_event);
info->sponge_dump_border_lsb = info->sponge_dump_border & 0xFF;
info->sponge_dump_border_msb = (info->sponge_dump_border & 0xFF00) >> 8;
input_info(true, &info->client->dev, "%s sponge_dump format %x event %x\n",
__func__, info->sponge_dump_format, info->sponge_dump_event);
}
out:
input_err(true, &info->client->dev, "%s: use %s\n",
__func__, info->use_sponge ? "SPONGE" : "VENDOR");
return ret;
}
#endif
int fts_set_press_property(struct fts_ts_info *info)
{
int ret = 0;
if (!info->board->support_fod)
return 0;
ret = info->fts_write_to_sponge(info, FTS_CMD_SPONGE_PRESS_PROPERTY,
&info->press_prop, sizeof(info->press_prop));
if (ret < 0)
input_err(true, &info->client->dev, "%s: failed. ret: %d\n", __func__, ret);
input_info(true, &info->client->dev, "%s: %d\n", __func__, info->press_prop);
return ret;
}
#endif
void fts_delay(unsigned int ms)
{
if (ms < 20)
usleep_range(ms * 1000, ms * 1000);
else
msleep(ms);
}
void fts_command(struct fts_ts_info *info, u8 cmd, bool checkEcho)
{
u8 regAdd = 0;
int ret = 0;
fts_interrupt_set(info, INT_DISABLE);
regAdd = cmd;
ret = fts_write_reg(info, &regAdd, 1);
if (ret < 0)
input_err(true, &info->client->dev,
"%s: failed to write command(0x%02X), ret = %d\n", __func__, cmd, ret);
fts_interrupt_set(info, INT_ENABLE);
}
int fts_set_scanmode(struct fts_ts_info *info, u8 scan_mode)
{
u8 regAdd[3] = { 0xA0, 0x00, scan_mode };
int rc;
fts_interrupt_set(info, INT_DISABLE);
rc = fts_write_reg(info, &regAdd[0], 3);
if (rc < 0) {
input_info(true, &info->client->dev, "%s: timeout, ret = %d\n", __func__, rc);
fts_interrupt_set(info, INT_ENABLE);
return rc;
}
fts_interrupt_set(info, INT_ENABLE);
input_info(true, &info->client->dev, "%s: 0x%02X\n", __func__, scan_mode);
return 0;
}
int fts_set_opmode(struct fts_ts_info *info, u8 mode)
{
int ret;
u8 regAdd[2] = {FTS_CMD_SET_GET_OPMODE, mode};
ret = fts_write_reg(info, &regAdd[0], 2);
if (ret <= 0)
input_err(true, &info->client->dev, "%s: Failed to write opmode", __func__);
fts_delay(5);
if (info->lowpower_flag) {
regAdd[0] = FTS_CMD_WRITE_WAKEUP_GESTURE;
regAdd[1] = 0x02;
ret = fts_write_reg(info, &regAdd[0], 2);
if (ret <= 0)
input_err(true, &info->client->dev, "%s: Failed to send lowpower flag command", __func__);
}
return ret;
}
void fts_set_fod_finger_merge(struct fts_ts_info *info)
{
int ret;
u8 regAdd[2] = {FTS_CMD_SET_FOD_FINGER_MERGE, 0};
if (!info->board->support_fod)
return;
if (info->lowpower_flag & FTS_MODE_PRESS)
regAdd[1] = 1;
else
regAdd[1] = 0;
mutex_lock(&info->sponge_mutex);
input_info(true, &info->client->dev, "%s: %d\n", __func__, regAdd[1]);
ret = fts_write_reg(info, regAdd, 2);
if (ret < 0)
input_err(true, &info->client->dev, "%s: failed\n", __func__);
mutex_unlock(&info->sponge_mutex);
}
static void fts_set_cover_type(struct fts_ts_info *info, bool enable)
{
int ret;
u8 regAdd[3] = {0};
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:
case FTS_VIEW_WALLET:
case FTS_FLIP_WALLET:
case FTS_LED_COVER:
case FTS_MONTBLANC_COVER:
case FTS_CLEAR_FLIP_COVER:
case FTS_QWERTY_KEYBOARD_EUR:
case FTS_QWERTY_KEYBOARD_KOR:
case FTS_MINI_SVIEW_WALLET_COVER:
info->cover_cmd = (u8)info->cover_type;
break;
case FTS_CHARGER_COVER:
case FTS_COVER_NOTHING1:
case FTS_COVER_NOTHING2:
default:
info->cover_cmd = 0;
input_err(true, &info->client->dev, "%s: not change touch state, %d\n",
__func__, info->cover_type);
break;
}
if (enable) {
regAdd[0] = FTS_CMD_SET_GET_COVERTYPE;
regAdd[1] = info->cover_cmd;
ret = fts_write_reg(info, &regAdd[0], 2);
if (ret < 0) {
input_err(true, &info->client->dev, "%s: Failed to send covertype command: %d",
__func__, info->cover_cmd);
}
info->touch_functions = info->touch_functions | FTS_TOUCHTYPE_BIT_COVER |
FTS_TOUCHTYPE_DEFAULT_ENABLE;
} else {
info->touch_functions = (info->touch_functions & (~FTS_TOUCHTYPE_BIT_COVER)) |
FTS_TOUCHTYPE_DEFAULT_ENABLE;
}
regAdd[0] = FTS_CMD_SET_GET_TOUCHTYPE;
regAdd[1] = (u8)(info->touch_functions & 0xFF);
regAdd[2] = (u8)(info->touch_functions >> 8);
ret = fts_write_reg(info, &regAdd[0], 3);
if (ret < 0) {
input_err(true, &info->client->dev, "%s: Failed to send touch type command: 0x%02X%02X",
__func__, regAdd[1], regAdd[2]);
}
}
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 */
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;
}
#ifdef SEC_TSP_FACTORY_TEST
if (mode)
fts_set_grip_data_to_ic(info, mode);
#endif
}
static void fts_wirelesscharger_mode(struct fts_ts_info *info)
{
u8 regAdd[2] = {FTS_CMD_SET_GET_CHARGER_MODE, (u8)info->charger_mode};
int ret;
input_info(true, &info->client->dev, "%s: Set charger mode CMD[%2X]\n", __func__, regAdd[1]);
ret = fts_write_reg(info, regAdd, 2);
if (ret < 0)
input_err(true, &info->client->dev, "%s: failed. ret: %d\n", __func__, ret);
}
/*
static int fts_set_sip_mode(struct fts_ts_info *info, u8 enable)
{
u8 regAdd[3];
int ret;
regAdd[0] = FTS_CMD_SET_FUNCTION_ONOFF;
regAdd[1] = FTS_FUNCTION_ENABLE_SIP_MODE;
if (enable)
regAdd[2] = 0x01;
else
regAdd[2] = 0x00;
info->sip_mode = enable;
input_info(true, &info->client->dev, "%s: %s\n", __func__, enable ? "enable" : "disable");
ret = fts_write_reg(info, regAdd, 3);
if (ret < 0)
input_err(true, &info->client->dev, "%s: failed. ret: %d\n", __func__, ret);
return ret;
}
*/
void fts_change_scan_rate(struct fts_ts_info *info, u8 rate)
{
u8 regAdd[2] = {FTS_CMD_SET_GET_REPORT_RATE, rate};
int ret = 0;
ret = fts_write_reg(info, &regAdd[0], 2);
input_dbg(true, &info->client->dev, "%s: scan rate (%d Hz), ret = %d\n", __func__, regAdd[1], ret);
}
void fts_interrupt_set(struct fts_ts_info *info, int enable)
{
struct irq_desc *desc = irq_to_desc(info->irq);
mutex_lock(&info->irq_mutex);
if (enable) {
while (desc->depth > 0)
enable_irq(info->irq);
} else {
disable_irq(info->irq);
}
input_info(true, &info->client->dev, "%s: %s\n", __func__, enable ? "Enable" : "Disable");
mutex_unlock(&info->irq_mutex);
}
void fts_ic_interrupt_set(struct fts_ts_info *info, int enable)
{
u8 regAdd[3] = { 0xA4, 0x01, 0x00 };
if (enable)
regAdd[2] = 0x01;
else
regAdd[2] = 0x00;
fts_write_reg(info, &regAdd[0], 3);
fts_delay(10);
}
static int fts_read_chip_id(struct fts_ts_info *info)
{
u8 regAdd = FTS_READ_DEVICE_ID;
u8 val[5] = {0};
int ret;
ret = fts_read_reg(info, &regAdd, 1, &val[0], 5);
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: %c %c %02X %02X %02X\n",
__func__, val[0], val[1], val[2], val[3], val[4]);
if ((val[2] != FTS_ID0) && (val[3] != FTS_ID1))
return -FTS_ERROR_INVALID_CHIP_ID;
return ret;
}
static int fts_read_chip_id_hw(struct fts_ts_info *info)
{
u8 regAdd[5] = { 0xFA, 0x20, 0x00, 0x00, 0x00 };
u8 val[8] = {0};
int ret;
ret = fts_read_reg(info, regAdd, 5, &val[0], 8);
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\n",
__func__, val[0], val[1], val[2], val[3], val[4], val[5], val[6], val[7]);
if ((val[0] = FTS_ID1) && (val[1] == FTS_ID0))
return FTS_NOT_ERROR;
return -FTS_ERROR_INVALID_CHIP_ID;
}
int fts_osc_trim_recovery(struct fts_ts_info *info)
{
u8 regAdd[3];
int rc;
input_info(true, &info->client->dev, "%s\n", __func__);
fts_interrupt_set(info, INT_DISABLE);
/* OSC trim error recovery command. */
regAdd[0] = 0xA4;
regAdd[1] = 0x00;
regAdd[2] = 0x05;
rc = fts_write_reg(info, &regAdd[0], 3);
if (rc < 0) {
rc = -FTS_ERROR_BROKEN_OSC_TRIM;
goto out;
}
/* save panel configuration area */
regAdd[0] = 0xA4;
regAdd[1] = 0x05;
regAdd[2] = 0x04;
rc = fts_write_reg(info, &regAdd[0], 3);
if (rc < 0) {
rc = -FTS_ERROR_BROKEN_OSC_TRIM;
goto out;
}
fts_delay(500);
rc = fts_systemreset(info, 0);
fts_delay(50);
out:
fts_interrupt_set(info, INT_ENABLE);
return rc;
}
static int fts_wait_for_ready(struct fts_ts_info *info)
{
struct fts_event_status *p_event_status;
int rc;
u8 regAdd[3];
u8 data[FTS_EVENT_SIZE];
int retry = 0;
int err_cnt = 0;
mutex_lock(&info->wait_for);
fts_interrupt_set(info, INT_DISABLE);
memset(data, 0x0, FTS_EVENT_SIZE);
regAdd[0] = FTS_READ_ONE_EVENT;
rc = -1;
while (fts_read_reg(info, &regAdd[0], 1, (u8 *)data, FTS_EVENT_SIZE) > 0) {
p_event_status = (struct fts_event_status *) &data[0];
if ((p_event_status->stype == FTS_EVENT_STATUSTYPE_INFORMATION) &&
(p_event_status->status_id == FTS_INFO_READY_STATUS)) {
rc = 0;
break;
}
if (data[0] == FTS_EVENT_ERROR_REPORT) {
input_err(true, &info->client->dev,
"%s: Err detected %02X, %02X, %02X, %02X, %02X, %02X, %02X, %02X\n",
__func__, data[0], data[1], data[2], data[3],
data[4], data[5], data[6], data[7]);
// check if config / cx / panel configuration area is corrupted
if (((data[1] >= 0x20) && (data[1] <= 0x23)) || ((data[1] >= 0xA0) && (data[1] <= 0xA8))) {
rc = -FTS_ERROR_FW_CORRUPTION;
info->checksum_result = 1;
info->fw_corruption = true;
input_err(true, &info->client->dev, "%s: flash corruption\n", __func__);
break;
}
/*
* F3 24 / 25 / 29 / 2A / 2D / 34: the flash about OSC TRIM value is broken.
*/
if (data[1] == 0x24 || data[1] == 0x25 || data[1] == 0x29 || data[1] == 0x2A || data[1] == 0x2D || data[1] == 0x34) {
input_err(true, &info->client->dev, "%s: osc trim is broken\n", __func__);
rc = -FTS_ERROR_BROKEN_OSC_TRIM;
info->fw_corruption = true;
break;
}
if (err_cnt++ > 32) {
rc = -FTS_ERROR_EVENT_ID;
break;
}
continue;
}
if (retry++ > FTS_RETRY_COUNT) {
rc = -FTS_ERROR_TIMEOUT;
if (data[0] == 0 && data[1] == 0 && data[2] == 0)
rc = -FTS_ERROR_TIMEOUT_ZERO;
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(20);
}
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]);
fts_interrupt_set(info, INT_ENABLE);
mutex_unlock(&info->wait_for);
return rc;
}
int fts_systemreset(struct fts_ts_info *info, unsigned int msec)
{
u8 regAdd[6] = { 0xFA, 0x20, 0x00, 0x00, 0x24, 0x81 };
int rc;
input_info(true, &info->client->dev, "%s\n", __func__);
fts_interrupt_set(info, INT_DISABLE);
fts_write_reg(info, &regAdd[0], 6);
fts_delay(msec + 10);
rc = fts_wait_for_ready(info);
fts_release_all_finger(info);
fts_interrupt_set(info, INT_ENABLE);
return rc;
}
int fts_fw_corruption_check(struct fts_ts_info *info)
{
u8 regAdd[6] = { 0xFA, 0x20, 0x00, 0x00, 0x24, 0x81 };
u8 val = 0;
int rc;
fts_interrupt_set(info, INT_DISABLE);
/* fts_systemreset */
rc = fts_write_reg(info, &regAdd[0], 6);
if (rc < 0) {
rc = -FTS_I2C_ERROR;
goto out;
}
fts_delay(10);
/* Firmware Corruption Check */
regAdd[0] = 0xFA;
regAdd[1] = 0x20;
regAdd[2] = 0x00;
regAdd[3] = 0x00;
regAdd[4] = 0x78;
rc = fts_read_reg(info, regAdd, 5, &val, 1);
if (rc < 0) {
rc = -FTS_I2C_ERROR;
goto out;
}
if (val & 0x03) { // Check if crc error
input_err(true, &info->client->dev, "%s: firmware corruption. CRC status:%02X\n",
__func__, val & 0x03);
rc = -FTS_ERROR_FW_CORRUPTION;
} else {
rc = 0;
}
out:
fts_interrupt_set(info, INT_ENABLE);
return rc;
}
int fts_get_sysinfo_data(struct fts_ts_info *info, u8 sysinfo_addr, u8 read_cnt, u8 *data)
{
int ret;
int rc = 0;
u8 *buff = NULL;
u8 regAdd[3] = { 0xA4, 0x06, 0x01 }; // request system information
fts_interrupt_set(info, INT_DISABLE);
ret = fts_write_reg(info, &regAdd[0], 3);
if (ret < 0) {
input_err(true, &info->client->dev, "%s: timeout wait for event\n", __func__);
rc = -1;
goto ERROR;
}
regAdd[0] = 0xA6;
regAdd[1] = 0x00;
regAdd[2] = sysinfo_addr;
buff = kzalloc(read_cnt, GFP_KERNEL);
if (!buff) {
rc = -2;
goto ERROR;
}
ret = fts_read_reg(info, &regAdd[0], 3, &buff[0], read_cnt);
if (ret <= 0) {
input_err(true, &info->client->dev, "%s: failed. ret: %d\n",
__func__, ret);
kfree(buff);
rc = -3;
goto ERROR;
}
memcpy(data, &buff[0], read_cnt);
kfree(buff);
ERROR:
fts_interrupt_set(info, INT_ENABLE);
return rc;
}
int fts_get_version_info(struct fts_ts_info *info)
{
int rc;
u8 regAdd = FTS_READ_FW_VERSION;
u8 data[FTS_VERSION_SIZE] = { 0 };
memset(data, 0x0, FTS_VERSION_SIZE);
rc = fts_read_reg(info, &regAdd, 1, (u8 *)data, FTS_VERSION_SIZE);
info->fw_version_of_ic = (data[0] << 8) + data[1];
info->config_version_of_ic = (data[2] << 8) + data[3];
info->fw_main_version_of_ic = data[4] + (data[5] << 8);
info->project_id_of_ic = data[6];
info->ic_name_of_ic = data[7];
info->module_version_of_ic = data[8];
input_info(true, &info->client->dev,
"%s: [IC] Firmware Ver: 0x%04X, Config Ver: 0x%04X, Main Ver: 0x%04X\n",
__func__, info->fw_version_of_ic,
info->config_version_of_ic, info->fw_main_version_of_ic);
input_info(true, &info->client->dev,
"%s: [IC] Project ID: 0x%02X, IC Name: 0x%02X, Module Ver: 0x%02X\n",
__func__, info->project_id_of_ic,
info->ic_name_of_ic, info->module_version_of_ic);
return rc;
}
int fts_fix_active_mode(struct fts_ts_info *info, bool enable)
{
u8 regAdd[3] = {0xA0, 0x00, 0x00};
int ret;
if (enable) {
regAdd[1] = 0x03;
regAdd[2] = 0x00;
} else {
regAdd[1] = 0x00;
regAdd[2] = 0x01;
}
ret = fts_write_reg(info, &regAdd[0], 3);
if (ret < 0)
input_info(true, &info->client->dev, "%s: err: %d\n", __func__, ret);
else
input_info(true, &info->client->dev, "%s: %s\n", __func__,
enable ? "fix" : "release");
fts_delay(10);
return ret;
}
/* Added for samsung dependent codes such as Factory test,
* Touch booster, Related debug sysfs.
*/
#include "fts_sec.c"
struct fts_ts_info *g_fts_info;
static ssize_t fts_tsp_cmoffset_all_read(struct file *file, char __user *buf,
size_t len, loff_t *offset)
{
struct fts_ts_info *info;
static ssize_t retlen = 0;
ssize_t retlen_sdc = 0, retlen_sub = 0, retlen_main = 0;
ssize_t count;
loff_t pos = *offset;
#ifdef CONFIG_SEC_FACTORY
int ret;
#endif
if (!g_fts_info) {
pr_err("%s %s: dev is null\n", SECLOG, __func__);
return 0;
}
info = g_fts_info;
if (pos == 0) {
#ifdef CONFIG_SEC_FACTORY
ret = fts_get_cmoffset_dump(info, info->cmoffset_sdc_proc, OFFSET_FW_SDC);
if (ret < 0)
input_err(true, &info->client->dev,
"%s: SDC fail use boot time value\n", __func__);
ret = fts_get_cmoffset_dump(info, info->cmoffset_sub_proc, OFFSET_FW_SUB);
if (ret < 0)
input_err(true, &info->client->dev,
"%s: SUB fail use boot time value\n", __func__);
ret = fts_get_cmoffset_dump(info, info->cmoffset_main_proc, OFFSET_FW_MAIN);
if (ret < 0)
input_err(true, &info->client->dev,
"%s: MAIN fail use boot time value\n", __func__);
#endif
retlen_sdc = strlen(info->cmoffset_sdc_proc);
retlen_sub = strlen(info->cmoffset_sub_proc);
retlen_main = strlen(info->cmoffset_main_proc);
info->cmoffset_all_proc = kzalloc(info->proc_cmoffset_all_size, GFP_KERNEL);
if (!info->cmoffset_all_proc){
input_err(true, &info->client->dev, "%s: kzalloc fail (cmoffset_all_proc)\n", __func__);
return 0;
}
strlcat(info->cmoffset_all_proc, info->cmoffset_sdc_proc, info->proc_cmoffset_all_size);
strlcat(info->cmoffset_all_proc, info->cmoffset_sub_proc, info->proc_cmoffset_all_size);
strlcat(info->cmoffset_all_proc, info->cmoffset_main_proc, info->proc_cmoffset_all_size);
retlen = strlen(info->cmoffset_all_proc);
input_info(true, &info->client->dev, "%s: retlen[%ld], retlen_sdc[%ld], retlen_sub[%ld], retlen_main[%ld]\n",
__func__, retlen, retlen_sdc, retlen_sub, retlen_main);
}
if (pos >= retlen)
return 0;
count = min(len, (size_t)(retlen - pos));
input_info(true, &info->client->dev, "%s: total:%ld count:%ld\n", __func__, retlen, count);
if (copy_to_user(buf, info->cmoffset_all_proc + pos, count)) {
input_err(true, &info->client->dev, "%s: copy_to_user error!\n", __func__);
return -EFAULT;
}
*offset += count;
if (count < len) {
input_info(true, &info->client->dev, "%s: print all & free cmoffset_all_proc\n", __func__);
if (info->cmoffset_all_proc)
kfree(info->cmoffset_all_proc);
retlen = 0;
}
return count;
}
static ssize_t fts_tsp_cmoffset_read(struct file *file, char __user *buf,
size_t len, loff_t *offset)
{
pr_info("[sec_input] %s called offset:%d\n", __func__, (int)*offset);
return fts_tsp_cmoffset_all_read(file, buf, len, offset);
}
static const struct file_operations tsp_cmoffset_all_file_ops = {
.owner = THIS_MODULE,
.read = fts_tsp_cmoffset_read,
.llseek = generic_file_llseek,
};
static void fts_init_proc(struct fts_ts_info *info)
{
struct proc_dir_entry *entry_cmoffset_all;
info->proc_cmoffset_size = (512 * 512 * 4 + 100) * 2;
info->proc_cmoffset_all_size = info->proc_cmoffset_size * 3; /* sdc sub main */
info->cmoffset_sdc_proc = kzalloc(info->proc_cmoffset_size, GFP_KERNEL);
if (!info->cmoffset_sdc_proc)
return;
info->cmoffset_sub_proc = kzalloc(info->proc_cmoffset_size, GFP_KERNEL);
if (!info->cmoffset_sub_proc)
goto err_alloc_sub;
info->cmoffset_main_proc = kzalloc(info->proc_cmoffset_size, GFP_KERNEL);
if (!info->cmoffset_main_proc)
goto err_alloc_main;
entry_cmoffset_all = proc_create("tsp_cmoffset_all", S_IFREG | S_IRUGO, NULL, (struct proc_ops *)&tsp_cmoffset_all_file_ops);
if (!entry_cmoffset_all) {
input_err(true, &info->client->dev, "%s: failed to create /proc/tsp_cmoffset_all\n", __func__);
goto err_cmoffset_proc_create;
}
proc_set_size(entry_cmoffset_all, info->proc_cmoffset_all_size);
g_fts_info = info;
input_info(true, &info->client->dev, "%s: done\n", __func__);
return;
err_cmoffset_proc_create:
kfree(info->cmoffset_main_proc);
err_alloc_main:
kfree(info->cmoffset_sub_proc);
err_alloc_sub:
kfree(info->cmoffset_sdc_proc);
info->cmoffset_sdc_proc = NULL;
info->cmoffset_sub_proc = NULL;
info->cmoffset_main_proc = NULL;
input_err(true, &info->client->dev, "%s: failed\n", __func__);
}
static int fts_init(struct fts_ts_info *info)
{
u8 retry = 3;
u8 regAdd[8] = { 0 };
int rc;
u8 data[FTS_EVENT_SIZE] = { 0 };
rc = fts_read_reg(info, &regAdd[0], 1, (u8 *)data, FTS_EVENT_SIZE);
if (rc == -ENOTCONN) {
return rc;
}
do {
rc = fts_fw_corruption_check(info);
if (rc == -FTS_ERROR_FW_CORRUPTION) {
info->checksum_result = 1;
break;
} else if (rc < 0) {
goto reset;
}
rc = fts_wait_for_ready(info);
reset:
if (rc < 0) {
if (rc == -FTS_ERROR_BROKEN_OSC_TRIM) {
break;
} else if (info->checksum_result) {
break;
} else if (rc == -FTS_ERROR_TIMEOUT_ZERO) {
rc = fts_read_chip_id_hw(info);
if (rc == FTS_NOT_ERROR) {
info->checksum_result = 1;
input_err(true, &info->client->dev, "%s: config corruption\n", __func__);
break;
}
}
fts_reset(info, 20);
} else {
break;
}
} while (--retry);
fts_get_version_info(info);
if (rc == -FTS_ERROR_BROKEN_OSC_TRIM) {
rc = fts_osc_trim_recovery(info);
if (rc < 0)
input_err(true, &info->client->dev, "%s: Failed to recover osc trim\n", __func__);
}
if (0/*!info->checksum_result && rc < 0*/) {
input_err(true, &info->client->dev, "%s: Failed to system reset\n", __func__);
return FTS_ERROR_TIMEOUT;
}
if (info->checksum_result) {
info->fw_version_of_ic = 0;
info->config_version_of_ic = 0;
info->fw_main_version_of_ic = 0;
}
input_err(true, &info->client->dev, "%s: read chip : hw\n", __func__);
rc = fts_read_chip_id_hw(info);
input_err(true, &info->client->dev, "%s: read chip : sw\n", __func__);
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;
}
/* remove fwu file. no fw update!!!
rc = fts_fw_update_on_probe(info);
if (rc < 0) {
input_err(true, &info->client->dev, "%s: Failed to firmware update\n",
__func__);
return FTS_ERROR_FW_UPDATE_FAIL;
}
*/
#ifdef SEC_TSP_FACTORY_TEST
rc = fts_get_channel_info(info);
if (rc < 0) {
input_err(true, &info->client->dev, "%s: read failed rc = %d\n", __func__, rc);
return 1;
}
info->pFrame = kzalloc(info->SenseChannelLength * info->ForceChannelLength * 2 + 1, GFP_KERNEL);
if (!info->pFrame)
return 1;
info->cx_data = kzalloc(info->SenseChannelLength * info->ForceChannelLength + 1, GFP_KERNEL);
if (!info->cx_data) {
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->pFrame);
return 1;
}
#if defined(FTS_SUPPORT_SPONGELIB) && defined(CONFIG_SEC_FACTORY)
if (info->use_sponge)
fts_disable_sponge(info);
#endif
#endif
/* fts driver set functional feature */
info->touch_count = 0;
info->flip_enable = false;
info->mainscr_disable = false;
info->deepsleep_mode = false;
info->touch_opmode = FTS_OPMODE_NORMAL;
info->charger_mode = FTS_BIT_CHARGER_MODE_NORMAL;
info->lowpower_flag = 0x00;
#ifdef TCLM_CONCEPT
info->tdata->external_factory = false;
#endif
info->touch_functions = FTS_TOUCHTYPE_DEFAULT_ENABLE;
regAdd[0] = FTS_CMD_SET_GET_TOUCHTYPE;
regAdd[1] = (u8)(info->touch_functions & 0xFF);
regAdd[2] = (u8)(info->touch_functions >> 8);
fts_write_reg(info, &regAdd[0], 3);
fts_delay(10);
fts_command(info, FTS_CMD_FORCE_CALIBRATION, true);
fts_command(info, FTS_CMD_CLEAR_ALL_EVENT, true);
info->scan_mode = FTS_SCAN_MODE_DEFAULT;
fts_set_scanmode(info, info->scan_mode);
info->lp_dump = kzalloc(FTS_SPONGE_LP_DUMP_DATA_FORMAT_10_LEN * FTS_SPONGE_LP_DUMP_LENGTH, GFP_KERNEL);
input_info(true, &info->client->dev, "%s: Initialized\n", __func__);
return 0;
}
int fts_set_temp(struct fts_ts_info *info, bool bforced)
{
int ret = 0;
u8 temp_data = 0;
u8 regAdd[2] = {0x3C, 0x00};
if (!info->psy)
info->psy = power_supply_get_by_name("battery");
if (!info->psy) {
input_err(true, &info->client->dev, "%s: Cannot find power supply\n", __func__);
return -1;
}
ret = power_supply_get_property(info->psy, POWER_SUPPLY_PROP_TEMP, &info->psy_value);
if (ret < 0) {
input_err(true, &info->client->dev, "%s: Couldn't get aicl settled value ret=%d\n", __func__, ret);
return ret;
}
temp_data = (u8)(info->psy_value.intval / 10);
if (bforced || info->tsp_temp_data != temp_data) {
regAdd[1] = temp_data;
ret = fts_write_reg(info, &regAdd[0], 2);
if (ret < 0) {
input_err(true, &info->client->dev, "%s: Failed to write\n", __func__);
return ret;
}
info->tsp_temp_data = temp_data;
input_info(true, &info->client->dev, "%s set temperature:%d\n", __func__, (s8)temp_data);
} else {
input_dbg(true, &info->client->dev, "%s skip temperature:%d\n", __func__, (s8)temp_data);
}
return ret;
}
static void fts_print_info(struct fts_ts_info *info)
{
struct irq_desc *desc = irq_to_desc(info->irq);
if (!info)
return;
if (!info->client)
return;
info->print_info_cnt_open++;
if (info->print_info_cnt_open > 0xfff0)
info->print_info_cnt_open = 0;
if (info->touch_count == 0)
info->print_info_cnt_release++;
input_info(true, &info->client->dev,
"noise:%x,%x tc:%d iq:%d depth:%d lp:%x flip:%d wet:%d selfie:%d // v:%04X cal:%02X,C%02XT%04X.%4s%s Cal_flag:%s,%d // #%d %d\n",
info->touch_noise_status, info->touch_noise_reason, info->touch_count,
gpio_get_value(info->board->irq_gpio), desc->depth,
info->lowpower_flag, info->flip_status_current, info->wet_mode, info->rear_selfie_mode,
(info->module_version_of_ic << 8) | (info->fw_main_version_of_ic & 0xFF),
info->test_result.data[0],
#ifdef TCLM_CONCEPT
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" : " ",
(info->tdata->nvdata.cal_fail_falg == SEC_CAL_PASS) ? "Success" : "Fail",
info->tdata->nvdata.cal_fail_cnt,
#else
0, 0, " ", " ", " ", 0,
#endif
info->print_info_cnt_open, info->print_info_cnt_release);
}
static void fts_print_info_work(struct work_struct *work)
{
struct fts_ts_info *info = container_of(work, struct fts_ts_info,
work_print_info.work);
fts_print_info(info);
/*
if (info->sec.cmd_is_running) {
input_err(true, &info->client->dev, "%s: skip sec_ts_set_temp, cmd running\n", __func__);
} else {
if (info->touch_count) {
info->tsp_temp_data_skip = true;
input_err(true, &info->client->dev, "%s: skip sec_ts_set_temp, t_cnt(%d)\n", __func__, info->touch_count);
} else {
info->tsp_temp_data_skip = false;
fts_set_temp(info, false);
}
}
*/
schedule_delayed_work(&info->work_print_info, msecs_to_jiffies(TOUCH_PRINT_INFO_DWORK_TIME));
}
/************************************************************
* 720 * 1480 : <48 96 60> indicator: 24dp navigator:48dp edge:60px dpi=320
* 1080 * 2220 : 4096 * 4096 : <133 266 341> (approximately value)
************************************************************/
static void location_detect(struct fts_ts_info *info, char *loc, int x, int y)
{
int i;
for (i = 0; i < FTS_TS_LOCATION_DETECT_SIZE; ++i)
loc[i] = 0;
if (x < info->board->area_edge)
strcat(loc, "E.");
else if (x < (info->board->max_x - info->board->area_edge))
strcat(loc, "C.");
else
strcat(loc, "e.");
if (y < info->board->area_indicator)
strcat(loc, "S");
else if (y < (info->board->max_y - info->board->area_navigation))
strcat(loc, "C");
else
strcat(loc, "N");
}
static const char finger_mode[10] = {'N', '1', '2', 'G', '4', 'P'};
static u8 fts_event_handler_type_b(struct fts_ts_info *info)
{
u8 regAdd;
int left_event_count = 0;
int EventNum = 0;
u8 TouchID = 0, event_id = 0;
u8 data[FTS_FIFO_MAX * FTS_EVENT_SIZE] = {0};
u8 *event_buff;
struct fts_event_coordinate *p_event_coord;
struct fts_gesture_status *p_gesture_status;
struct fts_event_status *p_event_status;
u8 prev_action = 0;
char location[FTS_TS_LOCATION_DETECT_SIZE] = { 0 };
regAdd = FTS_READ_ONE_EVENT;
fts_read_reg(info, &regAdd, 1, (u8 *)&data[0 * FTS_EVENT_SIZE], FTS_EVENT_SIZE);
left_event_count = (data[7] & 0x1F);
if (left_event_count >= FTS_FIFO_MAX)
left_event_count = FTS_FIFO_MAX - 1;
if (left_event_count > 0) {
regAdd = FTS_READ_ALL_EVENT;
fts_read_reg(info, &regAdd, 1, (u8 *)&data[1 * FTS_EVENT_SIZE], FTS_EVENT_SIZE * (left_event_count));
}
do {
/* for event debugging */
if (info->debug_string & 0x1)
input_info(true, &info->client->dev, "[%d] %02X %02X %02X %02X %02X %02X %02X %02X\n",
EventNum, data[EventNum * FTS_EVENT_SIZE+0], 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]);
event_buff = (u8 *) &data[EventNum * FTS_EVENT_SIZE];
event_id = event_buff[0] & 0x3;
switch (event_id) {
case FTS_STATUS_EVENT:
p_event_status = (struct fts_event_status *)event_buff;
if (p_event_status->stype > 0)
input_info(true, &info->client->dev, "%s: STATUS %02X %02X %02X %02X %02X %02X %02X %02X\n",
__func__, event_buff[0], event_buff[1], event_buff[2],
event_buff[3], event_buff[4], event_buff[5],
event_buff[6], event_buff[7]);
if ((p_event_status->stype == FTS_EVENT_STATUSTYPE_ERROR) &&
(p_event_status->status_id == FTS_ERR_EVENT_QUEUE_FULL)) {
input_err(true, &info->client->dev, "%s: IC Event Queue is full\n", __func__);
fts_release_all_finger(info);
}
if ((p_event_status->stype == FTS_EVENT_STATUSTYPE_ERROR) &&
(p_event_status->status_id == FTS_ERR_EVENT_ESD)) {
input_err(true, &info->client->dev, "%s: ESD detected. run reset\n", __func__);
if (!info->reset_is_on_going)
schedule_delayed_work(&info->reset_work, msecs_to_jiffies(10));
}
if ((p_event_status->stype == FTS_EVENT_STATUSTYPE_INFORMATION) &&
(p_event_status->status_id == FTS_INFO_READY_STATUS)) {
if (p_event_status->status_data_1 == 0x10) {
input_err(true, &info->client->dev, "%s: IC Reset\n", __func__);
/*
if (!info->reset_is_on_going)
schedule_delayed_work(&info->reset_work, msecs_to_jiffies(10));
*/
}
}
if ((p_event_status->stype == FTS_EVENT_STATUSTYPE_INFORMATION) &&
(p_event_status->status_id == FTS_INFO_WET_MODE)) {
info->wet_mode = p_event_status->status_data_1;
input_info(true, &info->client->dev, "%s: WET MODE %s[%d]\n",
__func__, info->wet_mode == 0 ? "OFF" : "ON",
p_event_status->status_data_1);
if (info->wet_mode)
info->wet_count++;
}
if ((p_event_status->stype == FTS_EVENT_STATUSTYPE_INFORMATION) &&
(p_event_status->status_id == FTS_INFO_NOISE_MODE)) {
info->touch_noise_status = (p_event_status->status_data_1 >> 4);
info->touch_noise_reason = (p_event_status->status_data_1 & 0x0F);
input_info(true, &info->client->dev, "%s: NOISE MODE %s[%02X]\n",
__func__, info->touch_noise_status == 0 ? "OFF" : "ON",
p_event_status->status_data_1);
if (info->touch_noise_status)
info->noise_count++;
}
if ((p_event_status->stype == FTS_EVENT_STATUSTYPE_INFORMATION) &&
(p_event_status->status_id == FTS_INFO_XENOSENSOR_DETECT)) {
info->xenosensor_detect = (p_event_status->status_data_5 & 0x80);
info->xenosensor_x = (p_event_status->status_data_1 << 4) | ((p_event_status->status_data_3 & 0xf0) >> 4);
info->xenosensor_y = (p_event_status->status_data_2 << 4) | (p_event_status->status_data_3 & 0x0f);
input_info(true, &info->client->dev, "%s: XENOSENSOR DETECT [%d] (%d,%d)\n",
__func__, info->xenosensor_detect, info->xenosensor_x, info->xenosensor_y);
}
break;
case FTS_COORDINATE_EVENT:
if (info->fts_power_state < FTS_POWER_STATE_ACTIVE) {
input_info(true, &info->client->dev, "%s: opmode is not normal\n", __func__);
break;
}
p_event_coord = (struct fts_event_coordinate *) event_buff;
TouchID = p_event_coord->tid;
if (TouchID >= FINGER_MAX) {
input_err(true, &info->client->dev,
"%s: tid(%d) is out of supported max finger number\n",
__func__, TouchID);
break;
}
info->finger[TouchID].prev_ttype = info->finger[TouchID].ttype;
prev_action = info->finger[TouchID].action;
info->finger[TouchID].id = TouchID;
info->finger[TouchID].action = p_event_coord->tchsta;
info->finger[TouchID].x = (p_event_coord->x_11_4 << 4) | (p_event_coord->x_3_0);
info->finger[TouchID].y = (p_event_coord->y_11_4 << 4) | (p_event_coord->y_3_0);
info->finger[TouchID].z = p_event_coord->z & 0x3F;
info->finger[TouchID].ttype = p_event_coord->ttype_3_2 << 2 |
p_event_coord->ttype_1_0 << 0;
info->finger[TouchID].major = p_event_coord->major;
info->finger[TouchID].minor = p_event_coord->minor;
info->finger[TouchID].max_energy = p_event_coord->max_energy;
if (info->finger[TouchID].max_energy) {
info->finger[TouchID].max_energy_x = info->finger[TouchID].x;
info->finger[TouchID].max_energy_y = info->finger[TouchID].y;
}
if (!info->finger[TouchID].palm &&
info->finger[TouchID].ttype == FTS_EVENT_TOUCHTYPE_PALM)
info->finger[TouchID].palm_count++;
info->finger[TouchID].palm = (info->finger[TouchID].ttype == FTS_EVENT_TOUCHTYPE_PALM);
info->finger[TouchID].left_event = p_event_coord->left_event;
info->finger[TouchID].noise_level = p_event_coord->noise_level;
info->finger[TouchID].max_strength = max(info->finger[TouchID].max_strength, p_event_coord->max_strength);
info->finger[TouchID].hover_id_num = max(info->finger[TouchID].hover_id_num, (u8)p_event_coord->hover_id_num);
if (info->finger[TouchID].z <= 0)
info->finger[TouchID].z = 1;
if ((info->finger[TouchID].ttype == FTS_EVENT_TOUCHTYPE_NORMAL) ||
(info->finger[TouchID].ttype == FTS_EVENT_TOUCHTYPE_PALM) ||
(info->finger[TouchID].ttype == FTS_EVENT_TOUCHTYPE_WET) ||
(info->finger[TouchID].ttype == FTS_EVENT_TOUCHTYPE_GLOVE)) {
location_detect(info, location, info->finger[TouchID].x, info->finger[TouchID].y);
if (info->finger[TouchID].action == FTS_COORDINATE_ACTION_RELEASE) {
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)
info->touch_count--;
if (info->touch_count == 0) {
input_report_key(info->input_dev, BTN_TOUCH, 0);
input_report_key(info->input_dev, BTN_TOOL_FINGER, 0);
info->check_multi = 0;
info->print_info_cnt_release = 0;
}
#if !defined(CONFIG_SAMSUNG_PRODUCT_SHIP)
input_info(true, &info->client->dev,
"[R] tID:%d loc:%s dd:%d,%d mc:%d tc:%d lx:%d ly:%d mx:%d my:%d p:%d nlvl:%d maxS:%d hid:%d\n",
TouchID, location,
info->finger[TouchID].x - info->finger[TouchID].p_x,
info->finger[TouchID].y - info->finger[TouchID].p_y,
info->finger[TouchID].mcount, info->touch_count,
info->finger[TouchID].x, info->finger[TouchID].y,
info->finger[TouchID].max_energy_x, info->finger[TouchID].max_energy_y,
info->finger[TouchID].palm_count, info->finger[TouchID].noise_level,
info->finger[TouchID].max_strength, info->finger[TouchID].hover_id_num);
#else
input_info(true, &info->client->dev,
"[R] tID:%d loc:%s dd:%d,%d mp:%d,%d mc:%d tc:%d p:%d nlvl:%d maxS:%d hid:%d\n",
TouchID, location,
info->finger[TouchID].x - info->finger[TouchID].p_x,
info->finger[TouchID].y - info->finger[TouchID].p_y,
info->finger[TouchID].max_energy_x - info->finger[TouchID].p_x,
info->finger[TouchID].max_energy_y - info->finger[TouchID].p_y,
info->finger[TouchID].mcount, info->touch_count,
info->finger[TouchID].palm_count, info->finger[TouchID].noise_level,
info->finger[TouchID].max_strength, info->finger[TouchID].hover_id_num);
#endif
info->finger[TouchID].action = FTS_COORDINATE_ACTION_NONE;
info->finger[TouchID].mcount = 0;
info->finger[TouchID].palm_count = 0;
info->finger[TouchID].noise_level = 0;
info->finger[TouchID].max_strength = 0;
info->finger[TouchID].hover_id_num = 0;
} else if (info->finger[TouchID].action == FTS_COORDINATE_ACTION_PRESS) {
info->touch_count++;
info->all_finger_count++;
info->finger[TouchID].p_x = info->finger[TouchID].x;
info->finger[TouchID].p_y = info->finger[TouchID].y;
input_mt_slot(info->input_dev, TouchID);
input_mt_report_slot_state(info->input_dev, MT_TOOL_FINGER, 1);
input_report_key(info->input_dev, BTN_TOUCH, 1);
input_report_key(info->input_dev, BTN_TOOL_FINGER, 1);
input_report_abs(info->input_dev, ABS_MT_POSITION_X, info->finger[TouchID].x);
input_report_abs(info->input_dev, ABS_MT_POSITION_Y, info->finger[TouchID].y);
input_report_abs(info->input_dev, ABS_MT_TOUCH_MAJOR,
info->finger[TouchID].major);
input_report_abs(info->input_dev, ABS_MT_TOUCH_MINOR,
info->finger[TouchID].minor);
if (info->board->support_mt_pressure)
input_report_abs(info->input_dev, ABS_MT_PRESSURE,
info->finger[TouchID].z);
if ((info->touch_count > 4) && (info->check_multi == 0)) {
info->check_multi = 1;
info->multi_count++;
}
#if !defined(CONFIG_SAMSUNG_PRODUCT_SHIP)
input_info(true, &info->client->dev,
"[P] tID:%d.%d x:%d y:%d z:%d major:%d minor:%d loc:%s tc:%d type:%d p:%d nlvl:%d maxS:%d hid:%d\n",
TouchID, (info->input_dev->mt->trkid - 1) & TRKID_MAX,
info->finger[TouchID].x, info->finger[TouchID].y,
info->finger[TouchID].z,
info->finger[TouchID].major, info->finger[TouchID].minor,
location, info->touch_count, info->finger[TouchID].ttype,
info->finger[TouchID].palm_count, info->finger[TouchID].noise_level,
info->finger[TouchID].max_strength, info->finger[TouchID].hover_id_num);
#else
input_info(true, &info->client->dev,
"[P] tID:%d.%d z:%d major:%d minor:%d loc:%s tc:%d type:%d p:%d nlvl:%d maxS:%d hid:%d\n",
TouchID, (info->input_dev->mt->trkid - 1) & TRKID_MAX,
info->finger[TouchID].z,
info->finger[TouchID].major, info->finger[TouchID].minor,
location, info->touch_count, info->finger[TouchID].ttype,
info->finger[TouchID].palm_count, info->finger[TouchID].noise_level,
info->finger[TouchID].max_strength, info->finger[TouchID].hover_id_num);
#endif
} else if (info->finger[TouchID].action == FTS_COORDINATE_ACTION_MOVE) {
if (info->touch_count == 0) {
input_err(true, &info->client->dev, "%s: touch count 0\n", __func__);
fts_release_all_finger(info);
break;
}
if (prev_action == FTS_COORDINATE_ACTION_NONE) {
input_err(true, &info->client->dev,
"%s: previous state is released but point is moved\n",
__func__);
break;
}
input_mt_slot(info->input_dev, TouchID);
input_mt_report_slot_state(info->input_dev, MT_TOOL_FINGER, 1);
input_report_key(info->input_dev, BTN_TOUCH, 1);
input_report_key(info->input_dev, BTN_TOOL_FINGER, 1);
input_report_abs(info->input_dev, ABS_MT_POSITION_X, info->finger[TouchID].x);
input_report_abs(info->input_dev, ABS_MT_POSITION_Y, info->finger[TouchID].y);
input_report_abs(info->input_dev, ABS_MT_TOUCH_MAJOR,
info->finger[TouchID].major);
input_report_abs(info->input_dev, ABS_MT_TOUCH_MINOR,
info->finger[TouchID].minor);
if (info->board->support_mt_pressure)
input_report_abs(info->input_dev, ABS_MT_PRESSURE,
info->finger[TouchID].z);
info->finger[TouchID].mcount++;
} else {
input_dbg(true, &info->client->dev,
"%s: do not support coordinate action(%d)\n",
__func__, info->finger[TouchID].action);
}
if (info->finger[TouchID].ttype != info->finger[TouchID].prev_ttype) {
input_info(true, &info->client->dev, "%s: tID:%d ttype(%c->%c) : %s\n",
__func__, info->finger[TouchID].id,
finger_mode[info->finger[TouchID].prev_ttype],
finger_mode[info->finger[TouchID].ttype],
info->finger[TouchID].action == FTS_COORDINATE_ACTION_PRESS ? "P" :
info->finger[TouchID].action == FTS_COORDINATE_ACTION_MOVE ? "M" : "R");
}
} else {
input_dbg(true, &info->client->dev,
"%s: do not support coordinate type(%d)\n",
__func__, info->finger[TouchID].ttype);
}
break;
case FTS_GESTURE_EVENT:
p_gesture_status = (struct fts_gesture_status *)event_buff;
input_info(true, &info->client->dev, "%s: [GESTURE] type:%X sf:%X id:%X | %X, %X, %X, %X\n",
__func__, p_gesture_status->stype, p_gesture_status->sf, p_gesture_status->gesture_id,
p_gesture_status->gesture_data_1, p_gesture_status->gesture_data_2,
p_gesture_status->gesture_data_3, p_gesture_status->gesture_data_4);
#ifdef FTS_SUPPORT_SPONGELIB
if (p_gesture_status->sf == FTS_GESTURE_SAMSUNG_FEATURE) {
switch (p_gesture_status->stype) {
case FTS_SPONGE_EVENT_SWIPE_UP:
info->scrub_id = SPONGE_EVENT_TYPE_SPAY;
input_info(true, &info->client->dev, "%s: SPAY\n", __func__);
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);
break;
case FTS_SPONGE_EVENT_DOUBLETAP:
if (p_gesture_status->gesture_id == FTS_SPONGE_EVENT_GESTURE_ID_AOD) {
info->scrub_id = SPONGE_EVENT_TYPE_AOD_DOUBLETAB;
info->scrub_x = (p_gesture_status->gesture_data_1 << 4) | (p_gesture_status->gesture_data_3 >> 4);
info->scrub_y = (p_gesture_status->gesture_data_2 << 4) | (p_gesture_status->gesture_data_3 & 0x0F);
input_info(true, &info->client->dev, "%s: AOD\n", __func__);
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);
} else if (p_gesture_status->gesture_id == FTS_SPONGE_EVENT_GESTURE_ID_DOUBLETAP_TO_WAKEUP) {
input_report_key(info->input_dev, KEY_WAKEUP, 1);
input_sync(info->input_dev);
input_report_key(info->input_dev, KEY_WAKEUP, 0);
input_info(true, &info->client->dev, "%s: DOUBLE TAP TO WAKEUP\n", __func__);
}
break;
case FTS_SPONGE_EVENT_SINGLETAP:
info->scrub_id = SPONGE_EVENT_TYPE_SINGLE_TAP;
info->scrub_x = (p_gesture_status->gesture_data_1 << 4) | (p_gesture_status->gesture_data_3 >> 4);
info->scrub_y = (p_gesture_status->gesture_data_2 << 4) | (p_gesture_status->gesture_data_3 & 0x0F);
input_info(true, &info->client->dev, "%s: SINGLE TAP\n", __func__);
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);
break;
#ifdef CONFIG_TOUCHSCREEN_DUMP_MODE
case FTS_SPONGE_EVENT_DUMPFLUSH:
if (info->sponge_inf_dump) {
if (info->fts_power_state == FTS_POWER_STATE_LOWPOWER) {
if (p_gesture_status->gesture_id == FTS_SPONGE_DUMP_0)
fts_sponge_dump_flush(info, FTS_SPONGE_DUMP_0);
if (p_gesture_status->gesture_id == FTS_SPONGE_DUMP_1)
fts_sponge_dump_flush(info, FTS_SPONGE_DUMP_1);
} else {
info->sponge_dump_delayed_flag = true;
info->sponge_dump_delayed_area = p_gesture_status->gesture_id;
}
}
break;
#endif
case FTS_SPONGE_EVENT_PRESS:
if (p_gesture_status->gesture_id == FTS_SPONGE_EVENT_GESTURE_ID_FOD_LONG ||
p_gesture_status->gesture_id == FTS_SPONGE_EVENT_GESTURE_ID_FOD_NORMAL) {
info->scrub_id = SPONGE_EVENT_TYPE_FOD;
input_info(true, &info->client->dev, "%s: FOD %sPRESS\n",
__func__, p_gesture_status->gesture_id ? "" : "LONG");
} else if (p_gesture_status->gesture_id == FTS_SPONGE_EVENT_GESTURE_ID_FOD_RELEASE) {
info->scrub_id = SPONGE_EVENT_TYPE_FOD_RELEASE;
input_info(true, &info->client->dev, "%s: FOD RELEASE\n", __func__);
} else if (p_gesture_status->gesture_id == FTS_SPONGE_EVENT_GESTURE_ID_FOD_OUT) {
info->scrub_id = SPONGE_EVENT_TYPE_FOD_OUT;
input_info(true, &info->client->dev, "%s: FOD OUT\n", __func__);
} else if (p_gesture_status->gesture_id == FTS_SPONGE_EVENT_GESTURE_ID_FOD_VI) {
if (info->lowpower_flag & FTS_MODE_PRESS) {
int ret;
ret = fts_read_from_sponge(info, FTS_CMD_SPONGE_FOD_POSITION, info->fod_vi_data, info->fod_vi_size);
if (ret < 0) {
input_info(true, &info->client->dev, "%s: failed to read FOD VI: ret: %d\n", __func__, ret);
} else {
input_info(true, &info->client->dev, "%s: FOD VI\n", __func__);
}
} else {
input_info(true, &info->client->dev, "%s: FOD not enabled: 0x%X\n", __func__, info->lowpower_flag);
}
break;
} else {
input_info(true, &info->client->dev, "%s: invalid id %d\n",
__func__, p_gesture_status->gesture_id);
break;
}
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);
break;
}
}
#endif
break;
case FTS_VENDOR_EVENT: // just print message for debugging
if (event_buff[1] == 0x01) { // echo event
input_info(true, &info->client->dev,
"%s: echo event %02X %02X %02X %02X %02X %02X %02X %02X %02X %02X %02X %02X %02X %02X %02X %02X\n",
__func__, event_buff[0], event_buff[1], event_buff[2], event_buff[3], event_buff[4], event_buff[5],
event_buff[6], event_buff[7], event_buff[8], event_buff[9], event_buff[10], event_buff[11],
event_buff[12], event_buff[13], event_buff[14], event_buff[15]);
} else {
input_info(true, &info->client->dev,
"%s: %02X %02X %02X %02X %02X %02X %02X %02X %02X %02X %02X %02X %02X %02X %02X %02X\n",
__func__, event_buff[0], event_buff[1], event_buff[2], event_buff[3], event_buff[4], event_buff[5],
event_buff[6], event_buff[7], event_buff[8], event_buff[9], event_buff[10], event_buff[11],
event_buff[12], event_buff[13], event_buff[14], event_buff[15]);
}
break;
default:
input_info(true, &info->client->dev,
"%s: unknown event %02X %02X %02X %02X %02X %02X %02X %02X %02X %02X %02X %02X %02X %02X %02X %02X\n",
__func__, event_buff[0], event_buff[1], event_buff[2], event_buff[3], event_buff[4], event_buff[5],
event_buff[6], event_buff[7], event_buff[8], event_buff[9], event_buff[10], event_buff[11],
event_buff[12], event_buff[13], event_buff[14], event_buff[15]);
break;
}
EventNum++;
left_event_count--;
} while (left_event_count >= 0);
input_sync(info->input_dev);
if(info->touch_count == 0 && info->tsp_temp_data_skip){
info->tsp_temp_data_skip = false;
fts_set_temp(info, false);
input_err(true, &info->client->dev, "%s: fts_set_temp, no touch\n", __func__);
}
// fts_lfd_ctrl(info, info->touch_count);
return 0;
}
/*
static void fts_lfd_ctrl(struct fts_ts_info *info, int touch_count)
{
if (info->lfd_ctrl_delay > 0) {
if (touch_count > 0) {
cancel_delayed_work_sync(&info->work_lfd_ctrl);
info->lfd_ctrl = FTS_LFD_CTRL_LOCK;
schedule_work(&info->work_lfd_ctrl.work);
} else {
cancel_delayed_work_sync(&info->work_lfd_ctrl);
info->lfd_ctrl = FTS_LFD_CTRL_UNLOCK;
schedule_delayed_work(&info->work_lfd_ctrl, msecs_to_jiffies(info->lfd_ctrl_delay));
}
}
}
*/
#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);
u8 regAdd[8] = {0};
u8 data;
u8 wiredCharger;
regAdd[0] = FTS_CMD_SET_GET_CHARGER_MODE;
fts_read_reg(info, &regAdd[0], 1, &data, 1);
if (ta_status == 0x01 || ta_status == 0x03) {
wiredCharger = data | FTS_BIT_CHARGER_MODE_WIRE_CHARGER;
info->TA_Pluged = true;
input_info(true, &info->client->dev,
"%s: device_control : CHARGER CONNECTED, ta_status : %x\n",
__func__, ta_status);
} else {
wiredCharger = data & (~FTS_BIT_CHARGER_MODE_WIRE_CHARGER);
info->TA_Pluged = false;
input_info(true, &info->client->dev,
"%s: device_control : CHARGER DISCONNECTED, ta_status : %x\n",
__func__, ta_status);
}
regAdd[1] = wiredCharger;
fts_write_reg(info, &regAdd[0], 2);
}
#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;
int ret;
#if defined(CONFIG_INPUT_SEC_SECURE_TOUCH)
if (fts_filter_interrupt(info) == IRQ_HANDLED) {
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) {
input_dbg(true, &info->client->dev, "%s: run LPM interrupt handler\n", __func__);
__pm_wakeup_event(info->wakelock, msecs_to_jiffies(500));
/* waiting for blsp block resuming, if not occurs i2c error */
ret = wait_for_completion_interruptible_timeout(&info->resume_done, msecs_to_jiffies(500));
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__, jiffies_to_msecs(ret));
/* run lpm interrupt handler */
}
mutex_lock(&info->eventlock);
ret = fts_event_handler_type_b(info);
mutex_unlock(&info->eventlock);
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, IRQF_TRIGGER_LOW | IRQF_ONESHOT,
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) {
fts_interrupt_set(info, INT_DISABLE);
free_irq(info->irq, info);
info->irq_enabled = false;
}
}
return retval;
}
#ifdef CONFIG_TOUCHSCREEN_DUAL_FOLDABLE
static int fts_notifier_call(struct notifier_block *n,
unsigned long data, void *v)
{
struct fts_ts_info *info = container_of(n, struct fts_ts_info, nb);
input_dbg(true, &info->client->dev, "%s: %lu\n", __func__, data);
return 0;
}
static void fts_switching_work(struct work_struct *work)
{
struct fts_ts_info *info = container_of(work, struct fts_ts_info,
switching_work.work);
if (info == NULL) {
input_err(true, NULL, "%s: tsp info is null\n", __func__);
return;
}
if (info->flip_status != info->flip_status_current) {
if (!info->info_work_done) {
input_err(true, &info->client->dev, "%s: info_work is not done yet\n", __func__);
info->change_flip_status = 1;
return;
}
info->change_flip_status = 0;
mutex_lock(&info->switching_mutex);
info->flip_status = info->flip_status_current;
if (info->flip_status == 0) {
/* open : main_tsp on */
} else {
/* close : main_tsp off */
#if defined(CONFIG_INPUT_SEC_SECURE_TOUCH)
fts_secure_touch_stop(info, 1);
#endif
}
fts_chk_tsp_ic_status(info, FTS_STATE_CHK_POS_HALL);
mutex_unlock(&info->switching_mutex);
} else if (info->board->support_flex_mode) {
input_info(true, &info->client->dev, "%s support_flex_mode\n", __func__);
mutex_lock(&info->switching_mutex);
fts_chk_tsp_ic_status(info, FTS_STATE_CHK_POS_SYSFS);
sec_input_notify(&info->nb, NOTIFIER_MAIN_TOUCH_ON, NULL);
mutex_unlock(&info->switching_mutex);
}
}
#ifdef CONFIG_FOLDER_HALL
static int fts_hall_ic_notify(struct notifier_block *nb,
unsigned long flip_cover, void *v)
{
struct fts_ts_info *info = container_of(nb, struct fts_ts_info,
hall_ic_nb);
if (info == NULL) {
input_err(true, NULL, "%s: tsp info is null\n", __func__);
return 0;
}
input_info(true, &info->client->dev, "%s: %s\n", __func__,
flip_cover ? "close" : "open");
cancel_delayed_work(&info->switching_work);
info->flip_status_current = flip_cover;
schedule_work(&info->switching_work.work);
if (info->xenosensor_detect && info->flip_status_current) {
info->xenosensor_detect_count++;
info->xenosensor_detect_x = info->xenosensor_x;
info->xenosensor_detect_y = info->xenosensor_y;
time64_to_tm(ktime_get_real_seconds(), -sys_tz.tz_minuteswest * 60, &info->xenosensor_time);
input_info(true, &info->client->dev, "%s: xenosensor_detect_count %d\n", __func__, info->xenosensor_detect_count);
}
return 0;
}
#endif
#endif
#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
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;
static bool enabled;
static bool boot_on = true;
int retval = 0;
if (enabled == on)
return retval;
if (info->regulator_dvdd == NULL) {
info->regulator_dvdd = devm_regulator_get(&info->client->dev, "dvdd");
if (IS_ERR_OR_NULL(info->regulator_dvdd)) {
input_err(true, dev, "%s: Failed to get regulator: dvdd\n", __func__);
goto out;
}
}
if (info->regulator_avdd == NULL) {
info->regulator_avdd = devm_regulator_get(&info->client->dev, "avdd");
if (IS_ERR_OR_NULL(info->regulator_avdd)) {
input_err(true, dev, "%s: Failed to get regulator dvdd\n", __func__);
goto out;
}
}
if (on) {
if (!regulator_is_enabled(info->regulator_avdd) || boot_on) {
retval = regulator_enable(info->regulator_avdd);
if (retval) {
input_err(true, dev, "%s: Failed to enable avdd: %d\n", __func__, retval);
goto out;
}
} else {
input_err(true, dev, "%s: avdd is already enabled\n", __func__);
}
fts_delay(1);
if (!regulator_is_enabled(info->regulator_dvdd) || boot_on) {
retval = regulator_enable(info->regulator_dvdd);
if (retval) {
input_err(true, dev, "%s: Failed to enable vdd: %d\n", __func__, retval);
regulator_disable(info->regulator_avdd);
goto out;
}
} else {
input_err(true, dev, "%s: dvdd is already enabled\n", __func__);
}
if (!IS_ERR_OR_NULL(pdata->pins_default)) {
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 {
if (regulator_is_enabled(info->regulator_dvdd)) {
retval = regulator_disable(info->regulator_dvdd);
if (retval) {
input_err(true, dev, "%s: Failed to disable dvdd: %d\n", __func__, retval);
goto out;
}
} else {
input_err(true, dev, "%s: dvdd is already disabled\n", __func__);
}
if (regulator_is_enabled(info->regulator_avdd)) {
retval = regulator_disable(info->regulator_avdd);
if (retval) {
input_err(true, dev, "%s: Failed to disable avdd: %d\n", __func__, retval);
regulator_enable(info->regulator_dvdd);
goto out;
}
} else {
input_err(true, dev, "%s: avdd is already disabled\n", __func__);
}
if (!IS_ERR_OR_NULL(pdata->pins_sleep)) {
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(info->regulator_avdd) ? "on" : "off",
regulator_is_enabled(info->regulator_dvdd) ? "on" : "off");
out:
// regulator_put(regulator_dvdd);
// regulator_put(regulator_avdd);
boot_on = false;
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;
int lcdtype = 0;
#if defined(CONFIG_EXYNOS_DECON_FB)
int connected;
#endif
u32 px_zone[3] = { 0 };
int count = 0, i;
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;
}
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_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 (of_property_read_bool(np, "stm,support_gesture"))
pdata->support_sidegesture = true;
pdata->support_dex = of_property_read_bool(np, "support_dex_mode");
pdata->support_ear_detect = of_property_read_bool(np, "support_ear_detect");
pdata->sync_reportrate_120 = of_property_read_bool(np, "sync-reportrate-120");
pdata->support_open_short_test = of_property_read_bool(np, "support_open_short_test");
pdata->support_mis_calibration_test = of_property_read_bool(np, "support_mis_calibration_test");
pdata->support_sram_test = of_property_read_bool(np, "support_sram_test");
pdata->support_hall_ic = of_property_read_bool(np, "support_hall_ic");
pdata->support_flex_mode = of_property_read_bool(np, "support_flex_mode");
of_property_read_u32(np, "stm,bringup", &pdata->bringup);
pdata->enable_settings_aot = of_property_read_bool(np, "stm,enable_settings_aot");
pdata->support_fod = of_property_read_bool(np, "stm,support_fod");
pdata->hw_i2c_reset = of_property_read_bool(np, "stm,hw_i2c_reset");
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,device_num", &pdata->device_num))
input_err(true, dev, "%s: Failed to get device_num property\n", __func__);
pdata->chip_on_board = of_property_read_bool(np, "stm,chip_on_board");
#ifdef CONFIG_INPUT_SEC_SECURE_TOUCH
of_property_read_u32(np, "stm,ss_touch_num", &pdata->ss_touch_num);
input_info(true, dev, "%s: ss_touch_num:%d\n", __func__, pdata->ss_touch_num);
#endif
#if defined(CONFIG_DISPLAY_SAMSUNG)
lcdtype = get_lcd_attached("GET");
if (lcdtype == 0xFFFFFF) {
input_err(true, &client->dev, "%s: lcd is not attached\n", __func__);
if (!pdata->chip_on_board)
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__);
if (!pdata->chip_on_board)
return -EINVAL;
}
if (!connected) {
input_err(true, &client->dev, "%s: lcd is disconnected\n", __func__);
if (!pdata->chip_on_board)
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__);
if (!pdata->chip_on_board)
return -EINVAL;
}
#endif
input_info(true, &client->dev, "%s: lcdtype 0x%08X\n", __func__, lcdtype);
count = of_property_count_strings(np, "stm,firmware_name");
if (count <= 0) {
pdata->firmware_name = NULL;
} else {
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 {
u8 lcd_id_num = of_property_count_u32_elems(np, "stm,select_lcdid");
if ((lcd_id_num != count) || (lcd_id_num <= 0)) {
of_property_read_string_index(np, "stm,firmware_name", 0, &pdata->firmware_name);
} else {
u32 lcd_id[5];
of_property_read_u32_array(np, "stm,select_lcdid", lcd_id, lcd_id_num);
for (i = 0; i < lcd_id_num; i++) {
if (lcd_id[i] == ((lcdtype >> 16) & 0xFF)) {
of_property_read_string_index(np, "stm,firmware_name", i, &pdata->firmware_name);
break;
}
}
if (!pdata->firmware_name)
pdata->bringup = 1;
else if (strlen(pdata->firmware_name) == 0)
pdata->bringup = 1;
}
}
}
pdata->panel_revision = ((lcdtype >> 8) & 0xFF) >> 4;
if (of_property_read_u32_array(np, "stm,area-size", px_zone, 3)) {
input_info(true, &client->dev, "%s: Failed to get zone's size\n", __func__);
pdata->area_indicator = 48;
pdata->area_navigation = 96;
pdata->area_edge = 60;
} else {
pdata->area_indicator = px_zone[0];
pdata->area_navigation = px_zone[1];
pdata->area_edge = px_zone[2];
}
input_info(true, dev, "%s : zone's size - indicator:%d, navigation:%d, edge:%d\n",
__func__, pdata->area_indicator, pdata->area_navigation ,pdata->area_edge);
input_err(true, dev,
"%s: irq :%d, max[x,y]: [%d,%d], "
"panel_revision: %d, gesture: %d, device_num: %d, dex: %d, aot: %d%s\n",
__func__, pdata->irq_gpio, pdata->max_x, pdata->max_y,
pdata->panel_revision,
pdata->support_sidegesture, pdata->device_num, pdata->support_dex, pdata->enable_settings_aot,
pdata->chip_on_board ? ", COB type" : "");
return retval;
}
#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__);
}
tdata->support_tclm_test = of_property_read_bool(np, "support_tclm_test");
input_err(true, &client->dev, "%s: tclm_level %d, sec_afe_base %04X\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)
return -ENOMEM;
info->client = client;
info->board = pdata;
info->irq = client->irq;
info->irq_enabled = 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_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_SPONGELIB
info->fts_read_from_sponge = fts_read_from_sponge;
info->fts_write_to_sponge = fts_write_to_sponge;
#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;
}
#endif
#ifdef TCLM_CONCEPT
sec_tclm_initialize(info->tdata);
info->tdata->client = info->client;
info->tdata->tclm_read = fts_tclm_data_read;
info->tdata->tclm_write = fts_tclm_data_write;
info->tdata->tclm_execute_force_calibration = fts_tclm_execute_force_calibration;
info->tdata->tclm_parse_dt = sec_tclm_parse_dt;
#endif
#ifdef USE_OPEN_DWORK
INIT_DELAYED_WORK(&info->open_work, fts_open_work);
#endif
INIT_DELAYED_WORK(&info->reset_work, fts_reset_work);
INIT_DELAYED_WORK(&info->work_read_info, fts_read_info_work);
INIT_DELAYED_WORK(&info->work_print_info, fts_print_info_work);
// INIT_DELAYED_WORK(&info->work_lfd_ctrl, fts_lfd_ctrl_work);
info->lfd_ctrl_delay = 500;
if (pdata->hw_i2c_reset)
INIT_DELAYED_WORK(&info->fw_reset_work, fts_fw_reset_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;
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_WAKEUP, 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);
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);
}
static void fts_set_input_prop_proximity(struct fts_ts_info *info, struct input_dev *dev)
{
char phys[64] = { 0 };
snprintf(phys, sizeof(phys), "%s/input1", dev->name);
dev->phys = phys;
dev->id.bustype = BUS_I2C;
dev->dev.parent = &info->client->dev;
set_bit(EV_SYN, dev->evbit);
input_set_drvdata(dev, info);
}
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;
}
#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->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;
}
}
if (info->board->support_ear_detect) {
info->input_dev_proximity = input_allocate_device();
if (!info->input_dev_proximity) {
input_err(true, &info->client->dev, "%s: Failed to alloc input_dev: proximity\n", __func__);
goto err_input_proximity_allocate_device;
}
}
mutex_init(&info->device_mutex);
mutex_init(&info->i2c_mutex);
mutex_init(&info->irq_mutex);
mutex_init(&info->eventlock);
mutex_init(&info->status_mutex);
mutex_init(&info->wait_for);
#if defined(CONFIG_INPUT_SEC_SECURE_TOUCH)
mutex_init(&info->st_lock);
#endif
mutex_init(&info->sponge_mutex);
init_completion(&info->resume_done);
complete_all(&info->resume_done);
retval = fts_init(info);
if (retval) {
input_err(true, &info->client->dev, "%s: fts_init fail!\n", __func__);
goto err_fts_init;
}
fts_init_proc(info);
mutex_lock(&info->device_mutex);
info->reinit_done = true;
mutex_unlock(&info->device_mutex);
info->wakelock = wakeup_source_register(&info->client->dev, "tsp_wakelock");
if (info->board->support_dex) {
info->input_dev_pad->name = "sec_touchpad";
fts_set_input_prop(info, info->input_dev_pad, INPUT_PROP_POINTER);
}
if (info->board->support_ear_detect) {
info->input_dev_proximity->name = "sec_touchproximity";
fts_set_input_prop_proximity(info, info->input_dev_proximity);
}
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;
}
}
if (info->board->support_ear_detect) {
retval = input_register_device(info->input_dev_proximity);
if (retval) {
input_err(true, &info->client->dev, "%s: Unable to register %s input device\n",
__func__, info->input_dev_proximity->name);
goto err_register_input_dev_proximity;
}
}
for (i = 0; i < FINGER_MAX; i++) {
info->finger[i].action = FTS_COORDINATE_ACTION_NONE;
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 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
#ifdef CONFIG_TOUCHSCREEN_DUAL_FOLDABLE
retval = sec_cmd_init(&info->sec, ft_commands,
ARRAY_SIZE(ft_commands), SEC_CLASS_DEVT_TSP1);
#else
retval = sec_cmd_init(&info->sec, ft_commands,
ARRAY_SIZE(ft_commands), SEC_CLASS_DEVT_TSP);
#endif
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_input_link;
}
#endif
#if defined(CONFIG_INPUT_SEC_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);
#ifdef FTS_SUPPORT_SPONGELIB
fts_check_custom_library(info);
#endif
#ifdef CONFIG_TOUCHSCREEN_DUAL_FOLDABLE
mutex_init(&info->switching_mutex);
INIT_DELAYED_WORK(&info->switching_work, fts_switching_work);
sec_input_register_notify(&info->nb, fts_notifier_call, 1);
info->flip_status = -1;
info->flip_status_current = FTS_STATUS_UNFOLDING; // default : 0 unfolding
#ifdef CONFIG_FOLDER_HALL
/* Hall IC notify priority -> ftn -> register */
info->hall_ic_nb.priority = 1;
info->hall_ic_nb.notifier_call = fts_hall_ic_notify;
if (info->board->support_hall_ic) {
hall_ic_register_notify(&info->hall_ic_nb);
input_info(true, &info->client->dev, "%s: hall ic register\n", __func__);
}
#endif
#endif
schedule_delayed_work(&info->work_read_info, msecs_to_jiffies(100));
info->info_work_done = true;
#if defined(CONFIG_TOUCHSCREEN_DUMP_MODE)
#if defined(CONFIG_TOUCHSCREEN_DUAL_FOLDABLE)
tsp_callbacks[FTS_STATUS_UNFOLDING].inform_dump = tsp_dump;
#else
dump_callbacks.inform_dump = tsp_dump;
#endif
INIT_DELAYED_WORK(&info->debug_work, dump_tsp_rawdata);
p_debug_work = &info->debug_work;
#endif
#ifdef CONFIG_INPUT_SEC_SECURE_TOUCH
info->ss_drv = sec_secure_touch_register(info, info->board->ss_touch_num, &info->input_dev->dev.kobj);
#endif
info->psy = power_supply_get_by_name("battery");
if (!info->psy)
input_err(true, &info->client->dev, "%s: Cannot find power supply\n", __func__);
info->probe_done = true;
input_err(true, &info->client->dev, "%s: done\n", __func__);
return 0;
#ifdef SEC_TSP_FACTORY_TEST
//sysfs_remove_link(&info->sec.fac_dev->kobj, "input");
err_input_link:
sysfs_remove_group(&info->sec.fac_dev->kobj,
&sec_touch_factory_attr_group);
err_sysfs:
#ifdef CONFIG_TOUCHSCREEN_DUAL_FOLDABLE
sec_cmd_exit(&info->sec, SEC_CLASS_DEVT_TSP1);
#else
sec_cmd_exit(&info->sec, SEC_CLASS_DEVT_TSP);
#endif
err_sec_cmd:
#endif
if (info->irq_enabled)
fts_irq_enable(info, false);
err_enable_irq:
if (info->board->support_ear_detect) {
input_unregister_device(info->input_dev_proximity);
info->input_dev_proximity = NULL;
}
err_register_input_dev_proximity:
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:
wakeup_source_destroy(info->wakelock);
#ifdef SEC_TSP_FACTORY_TEST
kfree(info->ito_result);
kfree(info->cx_data);
kfree(info->pFrame);
#endif
err_fts_init:
#if defined(CONFIG_INPUT_SEC_SECURE_TOUCH)
mutex_destroy(&info->st_lock);
#endif
mutex_destroy(&info->device_mutex);
mutex_destroy(&info->i2c_mutex);
mutex_destroy(&info->status_mutex);
if (info->board->support_ear_detect) {
if (info->input_dev_proximity)
input_free_device(info->input_dev_proximity);
}
err_input_proximity_allocate_device:
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);
g_fts_info = NULL;
kfree(info);
err_get_drv_data:
err_setup_drv_data:
input_err(true, &client->dev, "%s: failed(%d)\n", __func__, retval);
return retval;
}
static int fts_remove(struct i2c_client *client)
{
struct fts_ts_info *info = i2c_get_clientdata(client);
#if defined(CONFIG_INPUT_SEC_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);
#ifdef CONFIG_TOUCHSCREEN_DUAL_FOLDABLE
cancel_delayed_work_sync(&info->switching_work);
#ifdef CONFIG_FOLDER_HALL
hall_ic_unregister_notify(&info->hall_ic_nb);
#endif
#endif
if (info->board->hw_i2c_reset)
cancel_delayed_work_sync(&info->fw_reset_work);
cancel_delayed_work_sync(&info->work_print_info);
cancel_delayed_work_sync(&info->work_read_info);
cancel_delayed_work_sync(&info->reset_work);
// cancel_delayed_work_sync(&info->work_lfd_ctrl);
wakeup_source_destroy(info->wakelock);
#if defined(CONFIG_INPUT_SEC_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
sysfs_remove_link(&info->sec.fac_dev->kobj, "input");
sysfs_remove_group(&info->sec.fac_dev->kobj,
&sec_touch_factory_attr_group);
#ifdef CONFIG_TOUCHSCREEN_DUAL_FOLDABLE
sec_cmd_exit(&info->sec, SEC_CLASS_DEVT_TSP1);
#else
sec_cmd_exit(&info->sec, SEC_CLASS_DEVT_TSP);
#endif
kfree(info->ito_result);
kfree(info->cx_data);
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);
g_fts_info = NULL;
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__);
#if defined(CONFIG_INPUT_SEC_SECURE_TOUCH)
fts_secure_touch_stop(info, 1);
#endif
mutex_lock(&info->device_mutex);
#ifdef USE_OPEN_DWORK
schedule_delayed_work(&info->open_work,
msecs_to_jiffies(TOUCH_OPEN_DWORK_TIME));
#else
#ifdef CONFIG_TOUCHSCREEN_DUAL_FOLDABLE
cancel_delayed_work_sync(&info->switching_work);
mutex_lock(&info->switching_mutex);
#endif
if (info->fts_power_state == FTS_POWER_STATE_POWERDOWN) {
retval = fts_start_device(info);
if (retval < 0) {
input_err(true, &info->client->dev,
"%s: Failed to start device\n", __func__);
#ifdef CONFIG_TOUCHSCREEN_DUAL_FOLDABLE
mutex_unlock(&info->switching_mutex);
#endif
mutex_unlock(&info->device_mutex);
goto out;
}
} else { /* FTS_POWER_STATE_LOWPOWER */
fts_set_lowpowermode(info, TO_TOUCH_MODE);
}
info->fts_power_state = FTS_POWER_STATE_ACTIVE;
fts_set_hsync_scanmode(info, FTS_CMD_NPM_SYNC_SCAN);
if (info->fix_active_mode)
fts_fix_active_mode(info, true);
#endif
fts_set_temp(info, true);
#ifdef CONFIG_TOUCHSCREEN_DUAL_FOLDABLE
mutex_unlock(&info->switching_mutex);
#endif
mutex_unlock(&info->device_mutex);
out:
cancel_delayed_work(&info->work_print_info);
info->print_info_cnt_open = 0;
info->print_info_cnt_release = 0;
schedule_work(&info->work_print_info.work);
info->flip_status_prev = info->flip_status_current;
#ifdef CONFIG_TOUCHSCREEN_DUAL_FOLDABLE
if (!info->board->support_hall_ic || info->board->support_flex_mode)
sec_input_notify(&info->nb, NOTIFIER_MAIN_TOUCH_ON, NULL);
#endif
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__);
if (info->touch_aging_mode) {
info->touch_aging_mode = false;
fts_reinit(info, false);
}
#if defined(CONFIG_INPUT_SEC_SECURE_TOUCH)
fts_secure_touch_stop(info, 1);
#endif
mutex_lock(&info->device_mutex);
#ifdef TCLM_CONCEPT
sec_tclm_debug_info(info->tdata);
#endif
#ifdef USE_OPEN_DWORK
cancel_delayed_work(&info->open_work);
#endif
cancel_delayed_work(&info->reset_work);
cancel_delayed_work(&info->work_print_info);
fts_print_info(info);
#ifdef CONFIG_TOUCHSCREEN_DUAL_FOLDABLE
cancel_delayed_work_sync(&info->switching_work);
mutex_lock(&info->switching_mutex);
#endif
if (info->flip_status_current || info->rear_selfie_mode) {
fts_stop_device(info);
} else {
if (info->lowpower_flag || info->ed_enable || info->pocket_mode) {
if (info->fix_active_mode)
fts_fix_active_mode(info, false);
fts_set_lowpowermode(info, TO_LOWPOWER_MODE);
} else {
fts_stop_device(info);
}
}
#ifdef CONFIG_TOUCHSCREEN_DUAL_FOLDABLE
mutex_unlock(&info->switching_mutex);
#endif
info->prox_power_off = 0;
info->fw_corruption = false;
mutex_unlock(&info->device_mutex);
#ifdef CONFIG_TOUCHSCREEN_DUAL_FOLDABLE
if (!info->board->support_hall_ic)
sec_input_notify(&info->nb, NOTIFIER_MAIN_TOUCH_OFF, NULL);
#endif
}
#endif
#if 0 //def CONFIG_SEC_FACTORY
static void fts_reinit_fac(struct fts_ts_info *info)
{
u8 regAdd[3] = {0};
info->touch_count = 0;
fts_command(info, FTS_CMD_CLEAR_ALL_EVENT, true);
info->scan_mode = FTS_SCAN_MODE_DEFAULT;
fts_set_scanmode(info, info->scan_mode);
if (info->flip_enable)
fts_set_cover_type(info, true);
#ifdef CONFIG_GLOVE_TOUCH
/* enable glove touch when flip cover is closed */
if (info->glove_enabled) {
info->touch_functions = info->touch_functions | FTS_TOUCHTYPE_BIT_GLOVE;
regAdd[0] = FTS_CMD_SET_GET_TOUCHTYPE;
regAdd[1] = (u8)(info->touch_functions & 0xFF);
regAdd[2] = (u8)(info->touch_functions >> 8);
fts_write_reg(info, &regAdd[0], 3);
}
#endif
fts_command(info, FTS_CMD_FORCE_CALIBRATION, true);
input_info(true, &info->client->dev, "%s\n", __func__);
}
#endif
void fts_reinit(struct fts_ts_info *info, bool delay)
{
u8 regAdd[3] = {0};
#ifdef FTS_SUPPORT_TA_MODE
u8 data;
u8 wiredCharger;
#endif
u8 retry = 3;
int rc = 0;
if (delay) {
rc = fts_wait_for_ready(info);
if (rc < 0) {
input_err(true, &info->client->dev, "%s: Failed to wait for ready\n", __func__);
goto out;
}
rc = fts_read_chip_id(info);
if (rc < 0) {
input_err(true, &info->client->dev, "%s: Failed to read chip id\n", __func__);
goto out;
}
}
do {
rc = fts_systemreset(info, 0);
if (rc < 0)
fts_reset(info, 20);
else
break;
} while (--retry);
if (retry == 0) {
input_err(true, &info->client->dev, "%s: Failed to system reset\n", __func__);
goto out;
}
#if defined(FTS_SUPPORT_SPONGELIB) && defined(CONFIG_SEC_FACTORY)
if (info->use_sponge)
fts_disable_sponge(info);
#endif
fts_command(info, FTS_CMD_CLEAR_ALL_EVENT, true);
info->touch_functions = FTS_TOUCHTYPE_DEFAULT_ENABLE;
if (info->flip_enable)
fts_set_cover_type(info, true);
#ifdef CONFIG_GLOVE_TOUCH
if (info->glove_enabled)
info->touch_functions = (info->touch_functions | FTS_TOUCHTYPE_BIT_GLOVE);
#endif
regAdd[0] = FTS_CMD_SET_GET_TOUCHTYPE;
regAdd[1] = (u8)(info->touch_functions & 0xFF);
regAdd[2] = (u8)(info->touch_functions >> 8);
fts_write_reg(info, &regAdd[0], 3);
#ifdef FTS_SUPPORT_TA_MODE
regAdd[0] = FTS_CMD_SET_GET_CHARGER_MODE;
if (info->TA_Pluged) {
fts_read_reg(info, &regAdd[0], 1, &data, 1);
wiredCharger = data | FTS_BIT_CHARGER_MODE_WIRE_CHARGER;
regAdd[1] = wiredCharger;
fts_write_reg(info, &regAdd[0], 2);
}
#endif
#ifdef SEC_TSP_FACTORY_TEST
/* need to add new command (dex mode ~ touchable area) */
fts_set_external_noise_mode(info, EXT_NOISE_MODE_MAX);
fts_set_fod_rect(info);
#endif
if (info->brush_mode) {
u8 regAdd[3] = {FTS_CMD_SET_FUNCTION_ONOFF, FTS_FUNCTION_ENABLE_BRUSH_MODE, info->brush_mode};
input_info(true, &info->client->dev, "%s: set brush mode\n", __func__);
if (fts_write_reg(info, regAdd, 3) < 0)
input_err(true, &info->client->dev, "%s: brush_enable failed\n", __func__);
}
if (info->touchable_area) {
u8 regAdd[3] = {FTS_CMD_SET_FUNCTION_ONOFF, FTS_FUNCTION_SET_TOUCHABLE_AREA, info->touchable_area};
input_info(true, &info->client->dev, "%s: set 16:9 mode\n", __func__);
if (fts_write_reg(info, regAdd, 3) < 0)
input_err(true, &info->client->dev, "%s: set_touchable_area failed\n", __func__);
}
/* because edge and dead zone will recover soon */
#if defined(CONFIG_TOUCHSCREEN_DUAL_FOLDABLE)
fts_set_grip_type(info, GRIP_ALL_DATA);
#else
fts_set_grip_type(info, ONLY_EDGE_HANDLER);
#endif
fts_delay(50);
out:
info->touch_count = 0;
#if defined(CONFIG_TOUCHSCREEN_DUAL_FOLDABLE) && defined(CONFIG_SEC_FACTORY)
if (info->flip_status_current == FTS_STATUS_FOLDING)
fts_set_hsync_scanmode(info, FTS_CMD_LPM_ASYNC_SCAN);
#endif
fts_set_scanmode(info, info->scan_mode);
}
void fts_locked_release_all_finger(struct fts_ts_info *info)
{
mutex_lock(&info->eventlock);
fts_release_all_finger(info);
mutex_unlock(&info->eventlock);
};
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].action == FTS_COORDINATE_ACTION_PRESS) ||
(info->finger[i].action == FTS_COORDINATE_ACTION_MOVE)) {
input_info(true, &info->client->dev,
"[RA] tID:%d mc:%d tc:%d\n",
i, info->finger[i].mcount, info->touch_count);
}
info->finger[i].action = FTS_COORDINATE_ACTION_NONE;
info->finger[i].mcount = 0;
info->finger[i].palm_count = 0;
info->finger[i].noise_level = 0;
info->finger[i].max_strength = 0;
info->finger[i].hover_id_num = 0;
}
info->touch_count = 0;
input_report_key(info->input_dev, BTN_TOUCH, 0);
input_report_key(info->input_dev, BTN_TOOL_FINGER, 0);
if (info->board->support_ear_detect) {
input_sync(info->input_dev_proximity);
}
input_sync(info->input_dev);
info->check_multi = 0;
// cancel_delayed_work_sync(&info->work_lfd_ctrl);
// info->lfd_ctrl = FTS_LFD_CTRL_UNLOCK;
// schedule_work(&info->work_lfd_ctrl.work);
}
#ifdef 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);
if (info->rawdata_read_lock == 1) {
input_err(true, &info->client->dev, "%s: ignored ## already checking..\n", __func__);
return;
}
if (info->fts_power_state == FTS_POWER_STATE_POWERDOWN) {
input_err(true, &info->client->dev, "%s: ignored ## IC is power off\n", __func__);
return;
}
//fts_run_rawdata_read_all(info);
}
static 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));
}
static void fts_sponge_dump_flush(struct fts_ts_info *info, int dump_area)
{
int i, ret;
unsigned char *sec_spg_dat;
u16 addr;
input_info(true, &info->client->dev, "%s: ++\n", __func__);
sec_spg_dat = vmalloc(FTS_MAX_SPONGE_DUMP_BUFFER);
if (!sec_spg_dat) {
input_err(true, &info->client->dev, "%s : Failed!!\n", __func__);
return;
}
memset(sec_spg_dat, 0, FTS_MAX_SPONGE_DUMP_BUFFER);
input_info(true, &info->client->dev, "%s: dump area=%d\n", __func__, dump_area);
/* check dump area */
if (dump_area == 0)
addr = FTS_CMD_SPONGE_LP_DUMP_EVENT;
else
addr = info->sponge_dump_border;
if ((info->sponge_dump_event * info->sponge_dump_format) > FTS_MAX_SPONGE_DUMP_BUFFER) {
input_err(true, &info->client->dev, "%s: wrong sponge dump read size (%d)\n",
__func__, info->sponge_dump_event * info->sponge_dump_format);
vfree(sec_spg_dat);
return;
}
/* dump all events at once */
ret = fts_read_from_sponge(info, addr, sec_spg_dat, info->sponge_dump_event * info->sponge_dump_format);
if (ret < 0) {
input_err(true, &info->client->dev, "%s: Failed to read sponge\n", __func__);
vfree(sec_spg_dat);
return;
}
for (i = 0 ; i < info->sponge_dump_event ; i++) {
int e_offset = i * info->sponge_dump_format;
char buff[30] = {0, };
u16 edata[5];
edata[0] = (sec_spg_dat[1 + e_offset] & 0xFF) << 8 | (sec_spg_dat[0 + e_offset] & 0xFF);
edata[1] = (sec_spg_dat[3 + e_offset] & 0xFF) << 8 | (sec_spg_dat[2 + e_offset] & 0xFF);
edata[2] = (sec_spg_dat[5 + e_offset] & 0xFF) << 8 | (sec_spg_dat[4 + e_offset] & 0xFF);
edata[3] = (sec_spg_dat[7 + e_offset] & 0xFF) << 8 | (sec_spg_dat[6 + e_offset] & 0xFF);
edata[4] = (sec_spg_dat[9 + e_offset] & 0xFF) << 8 | (sec_spg_dat[8 + e_offset] & 0xFF);
if (edata[0] || edata[1] || edata[2] || edata[3] || edata[4]) {
snprintf(buff, sizeof(buff), "%03d: %04x%04x%04x%04x%04x\n",
i + (info->sponge_dump_event * dump_area),
edata[0], edata[1], edata[2], edata[3], edata[4]);
sec_tsp_sponge_log(buff);
}
}
vfree(sec_spg_dat);
input_info(true, &info->client->dev, "%s: --\n", __func__);
}
#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);
fts_interrupt_set(info, INT_DISABLE);
if (info->board->power)
info->board->power(info, false);
fts_delay(ms);
if (info->board->power)
info->board->power(info, true);
fts_delay(5);
fts_interrupt_set(info, INT_ENABLE);
}
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 SEC_TSP_FACTORY_TEST
if (info->debug_string & FTS_DEBUG_SEND_UEVENT)
sec_cmd_send_event_to_user(&info->sec, NULL, "RESULT=RESET");
#endif
#ifdef CONFIG_INPUT_SEC_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;
__pm_stay_awake(info->wakelock);
fts_stop_device(info);
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) {
u8 data[8] = { 0 };
input_err(true, &info->client->dev, "%s: call input_close[0x%X]\n", __func__, info->lowpower_flag);
if (info->lowpower_flag) {
if (info->fix_active_mode)
fts_fix_active_mode(info, false);
fts_set_lowpowermode(info, TO_LOWPOWER_MODE);
if ((info->lowpower_flag & FTS_MODE_AOD) && info->use_sponge) {
int i;
for (i = 0; i < 4; i++) {
data[i * 2] = info->rect_data[i] & 0xFF;
data[i * 2 + 1] = (info->rect_data[i] >> 8) & 0xFF;
}
#ifdef FTS_SUPPORT_SPONGELIB
info->fts_write_to_sponge(info, FTS_CMD_SPONGE_OFFSET_AOD_RECT,
data, sizeof(data));
#endif
}
} else {
fts_stop_device(info);
}
}
*/
#ifdef FTS_SUPPORT_SPONGELIB
fts_set_press_property(info);
#endif
fts_set_fod_finger_merge(info);
info->reset_is_on_going = false;
__pm_relax(info->wakelock);
}
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 SEC_TSP_FACTORY_TEST
int ret;
#endif
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
#ifdef SEC_TSP_FACTORY_TEST
ret = fts_get_tsp_test_result(info);
if (ret < 0)
input_err(true, &info->client->dev, "%s: failed to get result\n",
__func__);
input_raw_info_d(true, &info->client->dev, "%s: fac test result %02X\n",
__func__, info->test_result.data[0]);
fts_run_rawdata_read_all(info);
#endif
input_info(true, &info->client->dev, "%s: read cm data in tsp ic\n", __func__);
#ifdef CONFIG_SEC_FACTORY
fts_get_cmoffset_dump(info, info->cmoffset_sdc_proc, OFFSET_FW_SDC);
fts_get_cmoffset_dump(info, info->cmoffset_sub_proc, OFFSET_FW_SUB);
fts_get_cmoffset_dump(info, info->cmoffset_main_proc, OFFSET_FW_MAIN);
#endif
info->info_work_done = true;
if (info->shutdown_is_on_going) {
input_info(true, &info->client->dev, "%s done, do not run work\n", __func__);
return;
}
schedule_work(&info->work_print_info.work);
#ifdef CONFIG_TOUCHSCREEN_DUAL_FOLDABLE
if (info->change_flip_status) {
input_info(true, &info->client->dev, "%s: re-try switching after reading info\n", __func__);
schedule_work(&info->switching_work.work);
}
#endif
/* sec_touchscreen is opened by KGSL(handler registered) in probe time
* so,
*/
#ifdef CONFIG_TOUCHSCREEN_DUAL_FOLDABLE
if (!info->board->support_hall_ic)
sec_input_notify(&info->nb, NOTIFIER_MAIN_TOUCH_ON, NULL);
#endif
input_info(true, &info->client->dev, "%s done\n", __func__);
}
static void fts_fw_reset_work(struct work_struct *work)
{
struct fts_ts_info *info = container_of(work, struct fts_ts_info,
fw_reset_work.work);
u8 data[3];
int ret;
if (info->reset_is_on_going) {
input_err(true, &info->client->dev, "%s: reset is on going, attempt 3 sec after\n", __func__);
schedule_delayed_work(&info->fw_reset_work, msecs_to_jiffies(3000));
return;
}
data[0] = 0xC1;
data[1] = 0x13;
data[2] = info->fw_reset_cmd;
ret = fts_write_reg(info, &data[0], 3);
if (ret < 0) {
input_err(true, &info->client->dev, "%s: Failed to write command: %d\n", __func__, data[2]);
} else {
input_err(true, &info->client->dev, "%s: send fw reset enable command: %d\n", __func__, data[2]);
}
if (info->fw_reset_cmd == 0x01) {
schedule_delayed_work(&info->fw_reset_work, msecs_to_jiffies(3000));
}
}
/* limit LFD during touch
* LFD LOCK : limit to Display LFD mode
*/
/*
static void fts_lfd_ctrl_work(struct work_struct *work)
{
struct fts_ts_info *info = container_of(work, struct fts_ts_info,
work_lfd_ctrl.work);
struct sec_input_notify_data data;
data.dual_policy = MAIN_TOUCHSCREEN;
if (info->lfd_ctrl == info->lfd_ctrl_prev)
return;
if (info->lfd_ctrl == FTS_LFD_CTRL_LOCK) {
sec_input_notify(&info->nb, NOTIFIER_LCD_VRR_LFD_LOCK_REQUEST, &data);
} else if (info->lfd_ctrl == FTS_LFD_CTRL_UNLOCK) {
sec_input_notify(&info->nb, NOTIFIER_LCD_VRR_LFD_LOCK_RELEASE, &data);
} else {
input_err(true, &info->client->dev, "%s: not work\n", __func__);
}
info->lfd_ctrl_prev = info->lfd_ctrl;
}
*/
void fts_chk_tsp_ic_status(struct fts_ts_info *info, int call_pos)
{
mutex_lock(&info->status_mutex);
input_info(true, &info->client->dev,
"%s: START: pos[%d] power_state[0x%X] lowpower_flag[0x%X] %sfolding\n",
__func__, call_pos, info->fts_power_state, info->lowpower_flag,
info->flip_status_current ? "": "un");
if (call_pos == FTS_STATE_CHK_POS_OPEN) {
input_dbg(true, &info->client->dev, "%s: OPEN : Nothing\n", __func__);
} else if (call_pos == FTS_STATE_CHK_POS_CLOSE) {
input_dbg(true, &info->client->dev, "%s: CLOSE: Nothing\n", __func__);
} else if (call_pos == FTS_STATE_CHK_POS_HALL) {
if (info->fts_power_state == FTS_POWER_STATE_LOWPOWER && info->flip_status_current == FTS_STATUS_FOLDING) {
input_info(true, &info->client->dev, "%s: HALL : TSP IC LP => IC OFF\n", __func__);
fts_stop_device(info);
} else {
input_info(true, &info->client->dev, "%s: HALL : Nothing\n", __func__);
}
/*
} else if (call_pos == FTS_STATE_CHK_POS_SYSFS) {
if (info->rear_selfie_mode) {
if (info->fts_power_state == FTS_POWER_STATE_LOWPOWER) {
input_info(true, &info->client->dev, "%s: SYSFS: Rear Selfie mode => TSP IC OFF\n",
__func__);
fts_stop_device(info);
} else {
input_info(true, &info->client->dev, "%s: SYSFS: Rear Selfie mode nothing!\n", __func__);
}
} else if (info->flip_status_current == FTS_STATUS_UNFOLDING) {
if (info->fts_power_state == FTS_POWER_STATE_POWERDOWN && info->lowpower_flag) {
input_info(true, &info->client->dev, "%s: SYSFS: TSP IC OFF => LP mode[0x%X]\n",
__func__, info->lowpower_flag);
fts_start_device(info);
fts_set_lowpowermode(info, TO_LOWPOWER_MODE);
} else if (info->fts_power_state == FTS_POWER_STATE_LOWPOWER && info->lowpower_flag == 0) {
input_info(true, &info->client->dev, "%s: SYSFS: LP mode [0x0] => TSP IC OFF\n",
__func__);
fts_stop_device(info);
} else {
input_info(true, &info->client->dev, "%s: SYSFS: set lowpower_flag again[0x%X]\n",
__func__, info->lowpower_flag);
info->fts_write_to_sponge(info, FTS_CMD_SPONGE_OFFSET_MODE,
&info->lowpower_flag, sizeof(info->lowpower_flag));
}
} else {
input_info(true, &info->client->dev, "%s: SYSFS: folding nothing[0x%X]\n", __func__, info->lowpower_flag);
}
*/
} else {
input_info(true, &info->client->dev, "%s: ETC : nothing!\n", __func__);
}
input_info(true, &info->client->dev, "%s: END : pos[%d] power_state[0x%X] lowpower_flag[0x%X]\n",
__func__, call_pos, info->fts_power_state, info->lowpower_flag);
mutex_unlock(&info->status_mutex);
}
/* optional reg : SEC_TS_CMD_LPM_AOD_OFF_ON(0x9B) */
/* 0 : Async base scan (default on lp mode) */
/* 1 : sync base scan */
int fts_set_hsync_scanmode(struct fts_ts_info *info, u8 mode)
{
int ret = 0;
u8 regAdd[2] = {FTS_CMD_SET_LPM_AOD_OFF_ON, 0};
input_info(true, &info->client->dev, "%s: mode:%x\n", __func__, mode);
regAdd[1] = mode;
ret = fts_write_reg(info, &regAdd[0], 2);
if (ret <= 0)
input_err(true, &info->client->dev, "%s: Failed to send command: %x/%x",
__func__, regAdd[0], regAdd[1]);
return ret;
}
#ifdef FTS_SUPPORT_SPONGELIB
void fts_set_utc_sponge(struct fts_ts_info *info)
{
struct timespec64 current_time;
u8 data[4];
do_gettimeofday(&current_time);
data[0] = (u8)(current_time.tv_sec >> 0 & 0xFF);
data[1] = (u8)(current_time.tv_sec >> 8 & 0xFF);
data[2] = (u8)(current_time.tv_sec >> 16 & 0xFF);
data[3] = (u8)(current_time.tv_sec >> 24& 0xFF);
fts_write_to_sponge(info, FTS_CMD_SPONGE_OFFSET_UTC, data, sizeof(data));
}
#endif
int fts_set_lowpowermode(struct fts_ts_info *info, u8 mode)
{
int ret = 0;
#ifdef FTS_SUPPORT_SPONGELIB
u8 d_lowpower_flag;
#endif
input_info(true, &info->client->dev, "%s: %s(%X)\n", __func__,
mode == TO_LOWPOWER_MODE ? "ENTER" : "EXIT", info->lowpower_flag);
if (info->fts_power_state == FTS_POWER_STATE_POWERDOWN) {
input_err(true, &info->client->dev, "%s: power off error!\n", __func__);
goto out;
}
if (mode == TO_LOWPOWER_MODE) {
if (device_may_wakeup(&info->client->dev))
enable_irq_wake(info->irq);
#ifdef SEC_TSP_FACTORY_TEST
if (info->read_ito)
fts_panel_ito_test(info, OPEN_SHORT_CRACK_TEST);
#endif
info->read_ito = false;
fts_set_opmode(info, FTS_OPMODE_LOWPOWER);
info->fts_power_state = FTS_POWER_STATE_LOWPOWER;
fts_locked_release_all_finger(info);
#ifdef FTS_SUPPORT_SPONGELIB
if (info->use_sponge) {
fts_set_utc_sponge(info);
#ifdef CONFIG_TOUCHSCREEN_DUMP_MODE
if (info->sponge_inf_dump) {
if (info->sponge_dump_delayed_flag) {
fts_sponge_dump_flush(info, info->sponge_dump_delayed_area);
info->sponge_dump_delayed_flag = false;
input_info(true, &info->client->dev, "%s : Sponge dump flush delayed work have proceed\n", __func__);
}
}
#endif
if (info->prox_power_off)
d_lowpower_flag = info->lowpower_flag & ~FTS_MODE_DOUBLETAP_WAKEUP;
else
d_lowpower_flag = info->lowpower_flag;
info->fts_write_to_sponge(info, FTS_CMD_SPONGE_OFFSET_MODE,
&d_lowpower_flag, sizeof(d_lowpower_flag));
}
#endif
} else {
ret = fts_set_opmode(info, FTS_OPMODE_NORMAL);
if (ret < 0) {
info->reinit_done = false;
fts_reinit(info, false);
info->reinit_done = true;
}
info->fts_power_state = FTS_POWER_STATE_ACTIVE;
if (device_may_wakeup(&info->client->dev))
disable_irq_wake(info->irq);
info->lp_dump_readmore = 1;
}
out:
return 0;
}
static int fts_stop_device(struct fts_ts_info *info)
{
input_info(true, &info->client->dev, "%s\n", __func__);
if (info->fts_power_state == FTS_POWER_STATE_POWERDOWN) {
input_err(true, &info->client->dev, "%s: already power off\n", __func__);
goto out;
}
#ifdef FTS_SUPPORT_SPONGELIB
#ifdef CONFIG_TOUCHSCREEN_DUAL_FOLDABLE
if (info->sec.fac_dev)
get_lp_dump(info->sec.fac_dev, NULL, NULL);
#endif
#endif
if (info->board->hw_i2c_reset)
cancel_delayed_work_sync(&info->fw_reset_work);
fts_command(info, FTS_CMD_SENSE_OFF, false);
fts_interrupt_set(info, INT_DISABLE);
fts_release_all_finger(info);
info->hover_enabled = false;
info->hover_ready = false;
info->touch_noise_status = 0;
info->touch_noise_reason = 0;
info->wet_mode = 0;
info->fts_power_state = FTS_POWER_STATE_POWERDOWN;
if (info->board->power)
info->board->power(info, false);
out:
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" : "");
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);
if (info->board->power)
info->board->power(info, true);
fts_delay(20);
info->reinit_done = false;
info->fts_power_state = FTS_POWER_STATE_ACTIVE;
fts_reinit(info, false);//true);
info->reinit_done = true;
if (info->board->support_ear_detect) {
u8 data[2];
int ret;
if (info->ed_enable) {
data[0] = FTS_CMD_SET_EAR_DETECT;
data[1] = info->ed_enable;
ret = fts_write_reg(info, data, 2);
input_info(true, &info->client->dev, "%s: set ear detect %s, ret = %d\n",
__func__, info->ed_enable ? "enable" : "disable", ret);
}
if (info->pocket_mode) {
data[0] = FTS_CMD_SET_POCKET_MODE;
data[1] = info->pocket_mode;
ret = fts_write_reg(info, data, 2);
input_info(true, &info->client->dev, "%s: set pocket mode %s, ret = %d\n",
__func__, info->pocket_mode ? "enable" : "disable", ret);
}
}
#ifdef FTS_SUPPORT_SPONGELIB
#ifndef CONFIG_SEC_FACTORY
if (info->lowpower_flag)
#endif
info->fts_write_to_sponge(info, FTS_CMD_SPONGE_OFFSET_MODE,
&info->lowpower_flag, sizeof(info->lowpower_flag));
#endif
out:
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 0//def USE_OPEN_CLOSE
if (info->input_dev) {
int retval = mutex_lock_interruptible(&info->input_dev->mutex);
if (retval) {
input_err(true, &info->client->dev,
"%s : mutex error\n", __func__);
goto out;
}
if (!info->input_dev->disabled) {
info->input_dev->disabled = true;
if (info->input_dev->users && info->input_dev->close) {
input_err(true, &info->client->dev,
"%s called without input_close\n",
__func__);
info->input_dev->close(info->input_dev);
}
info->input_dev->users = 0;
}
mutex_unlock(&info->input_dev->mutex);
}
out:
#endif
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__);
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__);
if (info->lowpower_flag) {
fts_set_lowpowermode(info, TO_LOWPOWER_MODE);
} else {
fts_stop_device(info);
}
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__);
if (info->lowpower_flag) {
fts_set_lowpowermode(info, TO_TOUCH_MODE);
} else {
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 const 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)
{
pr_info("[sec_input] %s\n", __func__);
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);