blob: 77c70cca847f7ba960e151f0786f2fa368b5ada2 [file] [log] [blame]
/*
*
* FocalTech TouchScreen driver.
*
* Copyright (c) 2010-2016, FocalTech Systems, Ltd., all rights reserved.
*
* This software is licensed under the terms of the GNU General Public
* License version 2, as published by the Free Software Foundation, and
* may be copied, distributed, and modified under those terms.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
*/
/*****************************************************************************
*
* File Name: focaltech_core.c
*
* Author: Focaltech Driver Team
*
* Created: 2016-08-08
*
* Abstract:
*
* Reference:
*
*****************************************************************************/
/*****************************************************************************
* Included header files
*****************************************************************************/
#include "focaltech_core.h"
#if defined(CONFIG_FB)
#include <linux/notifier.h>
#include <linux/fb.h>
#elif defined(CONFIG_HAS_EARLYSUSPEND)
#include <linux/earlysuspend.h>
#define FTS_SUSPEND_LEVEL 1 /* Early-suspend level */
#endif
/*****************************************************************************
* Private constant and macro definitions using #define
*****************************************************************************/
#define FTS_DRIVER_NAME "fts_ts"
#define INTERVAL_READ_REG 20
#define TIMEOUT_READ_REG 300
#if FTS_POWER_SOURCE_CUST_EN
#define FTS_VTG_MIN_UV 2600000
#define FTS_VTG_MAX_UV 3300000
#define FTS_I2C_VTG_MIN_UV 1800000
#define FTS_I2C_VTG_MAX_UV 1800000
#endif
#define FTS_READ_TOUCH_BUFFER_DIVIDED 0
/*****************************************************************************
* Global variable or extern global variabls/functions
******************************************************************************/
struct i2c_client *fts_i2c_client;
struct fts_ts_data *fts_wq_data;
struct input_dev *fts_input_dev;
#if FTS_DEBUG_EN
int g_show_log = 1;
#else
int g_show_log;
#endif
#if (FTS_DEBUG_EN && (FTS_DEBUG_LEVEL == 2))
char g_sz_debug[1024] = { 0 };
#endif
/*****************************************************************************
* Static function prototypes
*****************************************************************************/
static void fts_release_all_finger(void);
static int fts_ts_suspend(struct device *dev);
static int fts_ts_resume(struct device *dev);
static bool fts_chip_idc(struct fts_ts_data *data)
{
return ((data->pdata->fts_chip_type & FLAGBIT(FLAG_IDC_BIT)) ==
FLAGBIT(FLAG_IDC_BIT));
}
/*****************************************************************************
* Name: fts_wait_tp_to_valid
* Brief: Read chip id until TP FW become valid,
* need call when reset/power on/resume...
* 1. Read Chip ID per INTERVAL_READ_REG(20ms)
* 2. Timeout: TIMEOUT_READ_REG(300ms)
* Input:
* Output:
* Return: 0 - Get correct Device ID
*****************************************************************************/
int fts_wait_tp_to_valid(struct i2c_client *client)
{
int ret = 0;
int cnt = 0;
u8 reg_value = 0;
do {
ret = fts_i2c_read_reg(client, FTS_REG_CHIP_ID, &reg_value);
if (ret < 0)
FTS_INFO("TP Not Ready, ReadData = 0x%x", reg_value);
else {
FTS_INFO("TP Ready, Device ID = 0x%x", reg_value);
return 0;
}
cnt++;
msleep(INTERVAL_READ_REG);
} while ((cnt * INTERVAL_READ_REG) < TIMEOUT_READ_REG);
/* error: not get correct reg data */
return -1;
}
/*****************************************************************************
* Name: fts_recover_state
* Brief: Need execute this function when reset
* Input:
* Output:
* Return:
*****************************************************************************/
void fts_tp_state_recovery(struct i2c_client *client)
{
/* wait tp stable */
fts_wait_tp_to_valid(client);
/* recover TP charger state 0x8B */
/* recover TP glove state 0xC0 */
/* recover TP cover state 0xC1 */
fts_ex_mode_recovery(client);
/* recover TP gesture state 0xD0 */
#if FTS_GESTURE_EN
fts_gesture_recovery(client);
#endif
fts_release_all_finger();
}
/*****************************************************************************
* Name: fts_reset_proc
* Brief: Execute reset operation
* Input: hdelayms - delay time unit:ms
* Output:
* Return:
*****************************************************************************/
int fts_reset_proc(int hdelayms)
{
gpio_direction_output(fts_wq_data->pdata->reset_gpio, 0);
msleep(20);
gpio_direction_output(fts_wq_data->pdata->reset_gpio, 1);
msleep(hdelayms);
return 0;
}
/*****************************************************************************
* Name: fts_irq_disable
* Brief: disable irq
* Input:
* sync:
* Output:
* Return:
*****************************************************************************/
void fts_irq_disable(void)
{
unsigned long irqflags;
spin_lock_irqsave(&fts_wq_data->irq_lock, irqflags);
if (!fts_wq_data->irq_disable) {
disable_irq_nosync(fts_wq_data->client->irq);
fts_wq_data->irq_disable = 1;
}
spin_unlock_irqrestore(&fts_wq_data->irq_lock, irqflags);
}
/*****************************************************************************
* Name: fts_irq_enable
* Brief: enable irq
* Input:
* Output:
* Return:
*****************************************************************************/
void fts_irq_enable(void)
{
unsigned long irqflags = 0;
spin_lock_irqsave(&fts_wq_data->irq_lock, irqflags);
if (fts_wq_data->irq_disable) {
enable_irq(fts_wq_data->client->irq);
fts_wq_data->irq_disable = 0;
}
spin_unlock_irqrestore(&fts_wq_data->irq_lock, irqflags);
}
/*****************************************************************************
* Name: fts_input_dev_init
* Brief: input dev init
* Input:
* Output:
* Return:
*****************************************************************************/
static int fts_input_dev_init(struct i2c_client *client,
struct fts_ts_data *data,
struct input_dev *input_dev,
struct fts_ts_platform_data *pdata)
{
int err, len;
FTS_FUNC_ENTER();
/* Init and register Input device */
input_dev->name = FTS_DRIVER_NAME;
input_dev->id.bustype = BUS_I2C;
input_dev->dev.parent = &client->dev;
input_set_drvdata(input_dev, data);
i2c_set_clientdata(client, data);
__set_bit(EV_KEY, input_dev->evbit);
if (data->pdata->have_key) {
FTS_DEBUG("set key capabilities");
for (len = 0; len < data->pdata->key_number; len++) {
input_set_capability(input_dev, EV_KEY,
data->pdata->keys[len]);
}
}
__set_bit(EV_ABS, input_dev->evbit);
__set_bit(BTN_TOUCH, input_dev->keybit);
__set_bit(INPUT_PROP_DIRECT, input_dev->propbit);
#if FTS_MT_PROTOCOL_B_EN
input_mt_init_slots(input_dev, pdata->max_touch_number,
INPUT_MT_DIRECT);
#else
input_set_abs_params(input_dev, ABS_MT_TRACKING_ID, 0, 0x0f, 0, 0);
#endif
input_set_abs_params(input_dev, ABS_MT_POSITION_X, pdata->x_min,
pdata->x_max, 0, 0);
input_set_abs_params(input_dev, ABS_MT_POSITION_Y, pdata->y_min,
pdata->y_max, 0, 0);
input_set_abs_params(input_dev, ABS_MT_TOUCH_MAJOR, 0, 0xFF, 0, 0);
#if FTS_REPORT_PRESSURE_EN
input_set_abs_params(input_dev, ABS_MT_PRESSURE, 0, 0xFF, 0, 0);
#endif
err = input_register_device(input_dev);
if (err) {
FTS_ERROR("Input device registration failed");
goto free_inputdev;
}
FTS_FUNC_EXIT();
return 0;
free_inputdev:
input_free_device(input_dev);
FTS_FUNC_EXIT();
return err;
}
/*****************************************************************************
* Power Control
*****************************************************************************/
#if FTS_POWER_SOURCE_CUST_EN
static int fts_power_source_init(struct fts_ts_data *data)
{
int rc;
FTS_FUNC_ENTER();
data->vdd = regulator_get(&data->client->dev, "vdd");
if (IS_ERR(data->vdd)) {
rc = PTR_ERR(data->vdd);
FTS_ERROR("Regulator get failed vdd rc=%d", rc);
}
if (regulator_count_voltages(data->vdd) > 0) {
rc = regulator_set_voltage(data->vdd, FTS_VTG_MIN_UV,
FTS_VTG_MAX_UV);
if (rc) {
FTS_ERROR("Regulator set_vtg failed vdd rc=%d", rc);
goto reg_vdd_put;
}
}
data->vcc_i2c = regulator_get(&data->client->dev, "vcc_i2c");
if (IS_ERR(data->vcc_i2c)) {
rc = PTR_ERR(data->vcc_i2c);
FTS_ERROR("Regulator get failed vcc_i2c rc=%d", rc);
goto reg_vdd_set_vtg;
}
if (regulator_count_voltages(data->vcc_i2c) > 0) {
rc = regulator_set_voltage(data->vcc_i2c, FTS_I2C_VTG_MIN_UV,
FTS_I2C_VTG_MAX_UV);
if (rc) {
FTS_ERROR("Regulator set_vtg failed vcc_i2c rc=%d", rc);
goto reg_vcc_i2c_put;
}
}
FTS_FUNC_EXIT();
return 0;
reg_vcc_i2c_put:
regulator_put(data->vcc_i2c);
reg_vdd_set_vtg:
if (regulator_count_voltages(data->vdd) > 0)
regulator_set_voltage(data->vdd, 0, FTS_VTG_MAX_UV);
reg_vdd_put:
regulator_put(data->vdd);
FTS_FUNC_EXIT();
return rc;
}
static int fts_power_source_ctrl(struct fts_ts_data *data, int enable)
{
int rc;
FTS_FUNC_ENTER();
if (enable) {
rc = regulator_enable(data->vdd);
if (rc)
FTS_ERROR("Regulator vdd enable failed rc=%d", rc);
rc = regulator_enable(data->vcc_i2c);
if (rc)
FTS_ERROR("Regulator vcc_i2c enable failed rc=%d", rc);
} else {
rc = regulator_disable(data->vdd);
if (rc)
FTS_ERROR("Regulator vdd disable failed rc=%d", rc);
rc = regulator_disable(data->vcc_i2c);
if (rc)
FTS_ERROR("Regulator vcc_i2c disable failed rc=%d", rc);
}
FTS_FUNC_EXIT();
return 0;
}
#endif
/*****************************************************************************
* Reprot related
*****************************************************************************/
/*****************************************************************************
* Name: fts_release_all_finger
* Brief:
* Input:
* Output:
* Return:
*****************************************************************************/
static void fts_release_all_finger(void)
{
#if FTS_MT_PROTOCOL_B_EN
unsigned int finger_count = 0;
for (finger_count = 0;
finger_count < fts_wq_data->pdata->max_touch_number;
finger_count++) {
input_mt_slot(fts_input_dev, finger_count);
input_mt_report_slot_state(fts_input_dev, MT_TOOL_FINGER,
false);
}
#else
input_mt_sync(fts_input_dev);
#endif
input_report_key(fts_input_dev, BTN_TOUCH, 0);
input_sync(fts_input_dev);
}
#if (FTS_DEBUG_EN && (FTS_DEBUG_LEVEL == 2))
static void fts_show_touch_buffer(u8 *buf, int point_num)
{
int len = point_num * FTS_ONE_TCH_LEN;
int count = 0;
int i;
memset(g_sz_debug, 0, 1024);
if (len > (POINT_READ_BUF - 3))
len = POINT_READ_BUF - 3;
else if (len == 0) {
len += FTS_ONE_TCH_LEN;
count += sprintf(g_sz_debug, "%02X,%02X,%02X", buf[0], buf[1], buf[2]);
for (i = 0; i < len; i++)
count += sprintf(g_sz_debug + count, ",%02X", buf[i + 3]);
FTS_DEBUG("buffer: %s", g_sz_debug);
}
#endif
static int fts_input_dev_report_key_event(struct ts_event *event,
struct fts_ts_data *data)
{
int i;
if (data->pdata->have_key) {
if ((1 == event->touch_point || 1 == event->point_num)
&& (event->au16_y[0] == data->pdata->key_y_coord)) {
if (event->point_num == 0) {
FTS_DEBUG("Keys All Up!");
for (i = 0; i < data->pdata->key_number; i++) {
input_report_key(data->input_dev,
data->pdata->keys[i],
0);
}
} else {
for (i = 0; i < data->pdata->key_number; i++) {
if (event->au16_x[0] >
(data->pdata->key_x_coords[i] -
FTS_KEY_WIDTH)
&& event->au16_x[0] <
(data->pdata->key_x_coords[i] +
FTS_KEY_WIDTH)) {
if (event->au8_touch_event[i] ==
0
|| event->au8_touch_event[i]
== 2) {
input_report_key
(data->input_dev,
data->pdata->keys
[i], 1);
FTS_DEBUG
("Key%d(%d, %d) DOWN!",
i,
event->au16_x[0],
event->au16_y[0]);
} else {
input_report_key
(data->input_dev,
data->pdata->keys
[i], 0);
FTS_DEBUG
("Key%d(%d, %d) Up!",
i,
event->au16_x[0],
event->au16_y[0]);
}
break;
}
}
}
input_sync(data->input_dev);
return 0;
}
}
return -1;
}
#if FTS_MT_PROTOCOL_B_EN
static int fts_input_dev_report_b(struct ts_event *event,
struct fts_ts_data *data)
{
int i = 0;
int uppoint = 0;
int touchs = 0;
for (i = 0; i < event->touch_point; i++) {
input_mt_slot(data->input_dev, event->au8_finger_id[i]);
if (event->au8_touch_event[i] == FTS_TOUCH_DOWN
|| event->au8_touch_event[i] == FTS_TOUCH_CONTACT) {
input_mt_report_slot_state(data->input_dev,
MT_TOOL_FINGER, true);
#if FTS_REPORT_PRESSURE_EN
#if FTS_FORCE_TOUCH_EN
if (event->pressure[i] <= 0) {
FTS_ERROR("[B]Illegal pressure: %d",
event->pressure[i]);
event->pressure[i] = 1;
}
#else
event->pressure[i] = 0x3f;
#endif
input_report_abs(data->input_dev, ABS_MT_PRESSURE,
event->pressure[i]);
#endif
if (event->area[i] <= 0) {
FTS_ERROR("[B]Illegal touch-major: %d",
event->area[i]);
event->area[i] = 1;
}
input_report_abs(data->input_dev, ABS_MT_TOUCH_MAJOR,
event->area[i]);
input_report_abs(data->input_dev, ABS_MT_POSITION_X,
event->au16_x[i]);
input_report_abs(data->input_dev, ABS_MT_POSITION_Y,
event->au16_y[i]);
touchs |= BIT(event->au8_finger_id[i]);
data->touchs |= BIT(event->au8_finger_id[i]);
#if FTS_REPORT_PRESSURE_EN
FTS_DEBUG("[B]P%d(%d, %d)[p:%d,tm:%d] DOWN!",
event->au8_finger_id[i], event->au16_x[i],
event->au16_y[i], event->pressure[i],
event->area[i]);
#else
FTS_DEBUG("[B]P%d(%d, %d)[tm:%d] DOWN!",
event->au8_finger_id[i], event->au16_x[i],
event->au16_y[i], event->area[i]);
#endif
} else {
uppoint++;
input_mt_report_slot_state(data->input_dev,
MT_TOOL_FINGER, false);
#if FTS_REPORT_PRESSURE_EN
input_report_abs(data->input_dev, ABS_MT_PRESSURE, 0);
#endif
data->touchs &= ~BIT(event->au8_finger_id[i]);
FTS_DEBUG("[B]P%d UP!", event->au8_finger_id[i]);
}
}
if (unlikely(data->touchs ^ touchs)) {
for (i = 0; i < data->pdata->max_touch_number; i++) {
if (BIT(i) & (data->touchs ^ touchs)) {
FTS_DEBUG("[B]P%d UP!", i);
input_mt_slot(data->input_dev, i);
input_mt_report_slot_state(data->input_dev,
MT_TOOL_FINGER,
false);
#if FTS_REPORT_PRESSURE_EN
input_report_abs(data->input_dev,
ABS_MT_PRESSURE, 0);
#endif
}
}
}
data->touchs = touchs;
if (event->touch_point == uppoint) {
FTS_DEBUG("Points All Up!");
input_report_key(data->input_dev, BTN_TOUCH, 0);
} else {
input_report_key(data->input_dev, BTN_TOUCH,
event->touch_point > 0);
}
input_sync(data->input_dev);
return 0;
}
#else
static int fts_input_dev_report_a(struct ts_event *event,
struct fts_ts_data *data)
{
int i = 0;
int uppoint = 0;
int touchs = 0;
for (i = 0; i < event->touch_point; i++) {
if (event->au8_touch_event[i] == FTS_TOUCH_DOWN
|| event->au8_touch_event[i] == FTS_TOUCH_CONTACT) {
input_report_abs(data->input_dev, ABS_MT_TRACKING_ID,
event->au8_finger_id[i]);
#if FTS_REPORT_PRESSURE_EN
#if FTS_FORCE_TOUCH_EN
if (event->pressure[i] <= 0) {
FTS_ERROR("[B]Illegal pressure: %d",
event->pressure[i]);
event->pressure[i] = 1;
}
#else
event->pressure[i] = 0x3f;
#endif
input_report_abs(data->input_dev, ABS_MT_PRESSURE,
event->pressure[i]);
#endif
if (event->area[i] <= 0) {
FTS_ERROR("[B]Illegal touch-major: %d",
event->area[i]);
event->area[i] = 1;
}
input_report_abs(data->input_dev, ABS_MT_TOUCH_MAJOR,
event->area[i]);
input_report_abs(data->input_dev, ABS_MT_POSITION_X,
event->au16_x[i]);
input_report_abs(data->input_dev, ABS_MT_POSITION_Y,
event->au16_y[i]);
input_mt_sync(data->input_dev);
#if FTS_REPORT_PRESSURE_EN
FTS_DEBUG("[B]P%d(%d, %d)[p:%d,tm:%d] DOWN!",
event->au8_finger_id[i], event->au16_x[i],
event->au16_y[i], event->pressure[i],
event->area[i]);
#else
FTS_DEBUG("[B]P%d(%d, %d)[tm:%d] DOWN!",
event->au8_finger_id[i], event->au16_x[i],
event->au16_y[i], event->area[i]);
#endif
} else {
uppoint++;
}
}
data->touchs = touchs;
if (event->touch_point == uppoint) {
FTS_DEBUG("Points All Up!");
input_report_key(data->input_dev, BTN_TOUCH, 0);
input_mt_sync(data->input_dev);
} else {
input_report_key(data->input_dev, BTN_TOUCH,
event->touch_point > 0);
}
input_sync(data->input_dev);
return 0;
}
#endif
/*****************************************************************************
* Name: fts_read_touchdata
* Brief:
* Input:
* Output:
* Return:
*****************************************************************************/
static int fts_read_touchdata(struct fts_ts_data *data)
{
u8 buf[POINT_READ_BUF] = { 0 };
u8 pointid = FTS_MAX_ID;
int ret = -1;
int i;
struct ts_event *event = &(data->event);
#if FTS_GESTURE_EN
{
u8 state;
if (data->suspended) {
fts_i2c_read_reg(data->client, FTS_REG_GESTURE_EN,
&state);
if (state == 1) {
fts_gesture_readdata(data->client);
return 1;
}
}
}
#endif
#if FTS_PSENSOR_EN
if ((fts_sensor_read_data(data) != 0) && (data->suspended == 1))
return 1;
#endif
#if FTS_READ_TOUCH_BUFFER_DIVIDED
memset(buf, 0xFF, POINT_READ_BUF);
memset(event, 0, sizeof(struct ts_event));
buf[0] = 0x00;
ret = fts_i2c_read(data->client, buf, 1, buf, (3 + FTS_ONE_TCH_LEN));
if (ret < 0) {
FTS_ERROR("%s read touchdata failed.", __func__);
return ret;
}
event->touch_point = 0;
event->point_num = buf[FTS_TOUCH_POINT_NUM] & 0x0F;
if (event->point_num > data->pdata->max_touch_number)
event->point_num = data->pdata->max_touch_number;
if (event->point_num > 1) {
buf[9] = 0x09;
fts_i2c_read(data->client, buf + 9, 1, buf + 9,
(event->point_num - 1) * FTS_ONE_TCH_LEN);
}
#else
ret = fts_i2c_read(data->client, buf, 1, buf, POINT_READ_BUF);
if (ret < 0) {
FTS_ERROR("[B]Read touchdata failed, ret: %d", ret);
return ret;
}
#if FTS_POINT_REPORT_CHECK_EN
fts_point_report_check_queue_work();
#endif
memset(event, 0, sizeof(struct ts_event));
event->point_num = buf[FTS_TOUCH_POINT_NUM] & 0x0F;
if (event->point_num > data->pdata->max_touch_number)
event->point_num = data->pdata->max_touch_number;
event->touch_point = 0;
#endif
#if (FTS_DEBUG_EN && (FTS_DEBUG_LEVEL == 2))
fts_show_touch_buffer(buf, event->point_num);
#endif
for (i = 0; i < data->pdata->max_touch_number; i++) {
pointid = (buf[FTS_TOUCH_ID_POS + FTS_ONE_TCH_LEN * i]) >> 4;
if (pointid >= FTS_MAX_ID)
break;
event->touch_point++;
event->au16_x[i] =
(s16) (buf[FTS_TOUCH_X_H_POS + FTS_ONE_TCH_LEN * i] & 0x0F)
<< 8 | (s16) buf[FTS_TOUCH_X_L_POS + FTS_ONE_TCH_LEN * i];
event->au16_y[i] =
(s16) (buf[FTS_TOUCH_Y_H_POS + FTS_ONE_TCH_LEN * i] & 0x0F)
<< 8 | (s16) buf[FTS_TOUCH_Y_L_POS + FTS_ONE_TCH_LEN * i];
event->au8_touch_event[i] =
buf[FTS_TOUCH_EVENT_POS + FTS_ONE_TCH_LEN * i] >> 6;
if (data->pdata->swap)
swap(event->au16_x[i], event->au16_y[i]);
if (data->pdata->scaling_down_half) {
event->au16_x[i] = event->au16_x[i] >> 1;
event->au16_y[i] = event->au16_y[i] >> 1;
}
event->au8_finger_id[i] =
(buf[FTS_TOUCH_ID_POS + FTS_ONE_TCH_LEN * i]) >> 4;
event->area[i] =
(buf[FTS_TOUCH_AREA_POS + FTS_ONE_TCH_LEN * i]) >> 4;
event->pressure[i] =
(s16) buf[FTS_TOUCH_PRE_POS + FTS_ONE_TCH_LEN * i];
if (0 == event->area[i])
event->area[i] = 0x09;
if (0 == event->pressure[i])
event->pressure[i] = 0x3f;
if ((event->au8_touch_event[i] == 0
|| event->au8_touch_event[i] == 2)
&& (event->point_num == 0))
break;
}
return 0;
}
/*****************************************************************************
* Name: fts_report_value
* Brief:
* Input:
* Output:
* Return:
*****************************************************************************/
static void fts_report_value(struct fts_ts_data *data)
{
struct ts_event *event = &data->event;
FTS_DEBUG("point number: %d, touch point: %d", event->point_num,
event->touch_point);
if (0 == fts_input_dev_report_key_event(event, data))
return;
#if FTS_MT_PROTOCOL_B_EN
fts_input_dev_report_b(event, data);
#else
fts_input_dev_report_a(event, data);
#endif
return;
}
/*****************************************************************************
* Name: fts_ts_interrupt
* Brief:
* Input:
* Output:
* Return:
*****************************************************************************/
static irqreturn_t fts_ts_interrupt(int irq, void *dev_id)
{
struct fts_ts_data *fts_ts = dev_id;
int ret = -1;
if (!fts_ts) {
FTS_ERROR("[INTR]: Invalid fts_ts");
return IRQ_HANDLED;
}
#if FTS_ESDCHECK_EN
fts_esdcheck_set_intr(1);
#endif
ret = fts_read_touchdata(fts_wq_data);
if (ret == 0)
fts_report_value(fts_wq_data);
#if FTS_ESDCHECK_EN
fts_esdcheck_set_intr(0);
#endif
return IRQ_HANDLED;
}
/*****************************************************************************
* Name: fts_gpio_configure
* Brief: Configure IRQ&RESET GPIO
* Input:
* Output:
* Return:
*****************************************************************************/
static int fts_gpio_configure(struct fts_ts_data *data)
{
int err = 0;
FTS_FUNC_ENTER();
/* request irq gpio */
if (gpio_is_valid(data->pdata->irq_gpio)) {
err = gpio_request(data->pdata->irq_gpio, "fts_irq_gpio");
if (err) {
FTS_ERROR("[GPIO]irq gpio request failed");
goto err_irq_gpio_req;
}
err = gpio_direction_input(data->pdata->irq_gpio);
if (err) {
FTS_ERROR("[GPIO]set_direction for irq gpio failed");
goto err_irq_gpio_dir;
}
}
/* request reset gpio */
if (gpio_is_valid(data->pdata->reset_gpio)) {
err = gpio_request(data->pdata->reset_gpio, "fts_reset_gpio");
if (err) {
FTS_ERROR("[GPIO]reset gpio request failed");
goto err_irq_gpio_dir;
}
err = gpio_direction_output(data->pdata->reset_gpio, 1);
if (err) {
FTS_ERROR("[GPIO]set_direction for reset gpio failed");
goto err_reset_gpio_dir;
}
}
FTS_FUNC_EXIT();
return 0;
err_reset_gpio_dir:
if (gpio_is_valid(data->pdata->reset_gpio))
gpio_free(data->pdata->reset_gpio);
err_irq_gpio_dir:
if (gpio_is_valid(data->pdata->irq_gpio))
gpio_free(data->pdata->irq_gpio);
err_irq_gpio_req:
FTS_FUNC_EXIT();
return err;
}
/*****************************************************************************
* Name: fts_get_dt_coords
* Brief:
* Input:
* Output:
* Return:
*****************************************************************************/
static int fts_get_dt_coords(struct device *dev, char *name,
struct fts_ts_platform_data *pdata)
{
u32 coords[FTS_COORDS_ARR_SIZE];
struct property *prop;
struct device_node *np = dev->of_node;
int coords_size, rc;
prop = of_find_property(np, name, NULL);
if (!prop)
return -EINVAL;
if (!prop->value)
return -ENODATA;
coords_size = prop->length / sizeof(u32);
if (coords_size != FTS_COORDS_ARR_SIZE) {
FTS_ERROR("invalid %s", name);
return -EINVAL;
}
rc = of_property_read_u32_array(np, name, coords, coords_size);
if (rc && (rc != -EINVAL)) {
FTS_ERROR("Unable to read %s", name);
return rc;
}
pr_err("x_max: %d", pdata->x_max);
pr_err("y_max: %d", pdata->y_max);
pr_err("coords[2]: %d", coords[2]);
pr_err("coords[3]: %d", coords[3]);
if (!strcmp(name, "focaltech,display-coords")) {
pdata->x_min = coords[0];
pdata->y_min = coords[1];
pdata->x_max = coords[2];
pdata->y_max = coords[3];
pr_err("x_max: %d", pdata->x_max);
pr_err("y_max: %d", pdata->y_max);
pr_err("coords[2]: %d", coords[2]);
pr_err("coords[3]: %d", coords[3]);
} else {
FTS_ERROR("unsupported property %s", name);
pr_err("x_max: %d", pdata->x_max);
return -EINVAL;
}
return 0;
}
/*****************************************************************************
* Name: fts_parse_dt
* Brief:
* Input:
* Output:
* Return:
*****************************************************************************/
static int fts_parse_dt(struct device *dev, struct fts_ts_platform_data *pdata)
{
int rc;
struct device_node *np = dev->of_node;
u32 temp_val;
FTS_FUNC_ENTER();
pdata->fts_chip_type = _FT5416;
rc = of_property_read_u32(np, "focaltech,panel-type",
&pdata->fts_chip_type);
if (rc)
FTS_ERROR("Panel type is undefined, use default panel FT5416");
rc = fts_get_dt_coords(dev, "focaltech,display-coords", pdata);
if (rc)
FTS_ERROR("Unable to get display-coords");
/* key */
pdata->have_key = of_property_read_bool(np, "focaltech,have-key");
if (pdata->have_key) {
rc = of_property_read_u32(np, "focaltech,key-number",
&pdata->key_number);
if (rc)
FTS_ERROR("Key number undefined!");
rc = of_property_read_u32_array(np, "focaltech,keys",
pdata->keys, pdata->key_number);
if (rc)
FTS_ERROR("Keys undefined!");
rc = of_property_read_u32(np, "focaltech,key-y-coord",
&pdata->key_y_coord);
if (rc)
FTS_ERROR("Key Y Coord undefined!");
rc = of_property_read_u32_array(np, "focaltech,key-x-coords",
pdata->key_x_coords,
pdata->key_number);
if (rc)
FTS_ERROR("Key X Coords undefined!");
FTS_DEBUG("%d: (%d, %d, %d), [%d, %d, %d][%d]",
pdata->key_number, pdata->keys[0], pdata->keys[1],
pdata->keys[2], pdata->key_x_coords[0],
pdata->key_x_coords[1], pdata->key_x_coords[2],
pdata->key_y_coord);
}
/* reset, irq gpio info */
pdata->reset_gpio =
of_get_named_gpio_flags(np, "focaltech,reset-gpio", 0,
&pdata->reset_gpio_flags);
if (pdata->reset_gpio < 0)
FTS_ERROR("Unable to get reset_gpio");
pdata->irq_gpio =
of_get_named_gpio_flags(np, "focaltech,irq-gpio", 0,
&pdata->irq_gpio_flags);
if (pdata->irq_gpio < 0)
FTS_ERROR("Unable to get irq_gpio");
rc = of_property_read_u32(np, "focaltech,max-touch-number", &temp_val);
if (!rc) {
pdata->max_touch_number = temp_val;
FTS_DEBUG("max_touch_number=%d", pdata->max_touch_number);
} else {
FTS_ERROR("Unable to get max-touch-number");
pdata->max_touch_number = FTS_MAX_POINTS;
}
pdata->swap = of_property_read_bool(np, "focaltech,swap-xy");
pdata->scaling_down_half = of_property_read_bool(np,
"focaltech,scaling-down-half");
FTS_FUNC_EXIT();
return 0;
}
#if defined(CONFIG_FB)
/*****************************************************************************
* Name: fb_notifier_callback
* Brief:
* Input:
* Output:
* Return:
*****************************************************************************/
static int fb_notifier_callback(struct notifier_block *self,
unsigned long event, void *data)
{
struct fb_event *evdata = data;
int *blank;
struct fts_ts_data *fts_data =
container_of(self, struct fts_ts_data, fb_notif);
if (evdata && evdata->data && event == FB_EVENT_BLANK && fts_data
&& fts_data->client) {
blank = evdata->data;
if (*blank == FB_BLANK_UNBLANK)
fts_ts_resume(&fts_data->client->dev);
else if (*blank == FB_BLANK_POWERDOWN)
fts_ts_suspend(&fts_data->client->dev);
}
return 0;
}
#elif defined(CONFIG_HAS_EARLYSUSPEND)
/*****************************************************************************
* Name: fts_ts_early_suspend
* Brief:
* Input:
* Output:
* Return:
*****************************************************************************/
static void fts_ts_early_suspend(struct early_suspend *handler)
{
struct fts_ts_data *data = container_of(handler,
struct fts_ts_data,
early_suspend);
fts_ts_suspend(&data->client->dev);
}
/*****************************************************************************
* Name: fts_ts_late_resume
* Brief:
* Input:
* Output:
* Return:
*****************************************************************************/
static void fts_ts_late_resume(struct early_suspend *handler)
{
struct fts_ts_data *data = container_of(handler,
struct fts_ts_data,
early_suspend);
fts_ts_resume(&data->client->dev);
}
#endif
/*****************************************************************************
* Name: fts_ts_probe
* Brief:
* Input:
* Output:
* Return:
*****************************************************************************/
static int fts_ts_probe(struct i2c_client *client,
const struct i2c_device_id *id)
{
struct fts_ts_platform_data *pdata;
struct fts_ts_data *data;
struct input_dev *input_dev;
int err;
FTS_FUNC_ENTER();
/* 1. Get Platform data */
if (client->dev.of_node) {
pdata =
devm_kzalloc(&client->dev,
sizeof(struct fts_ts_platform_data),
GFP_KERNEL);
if (!pdata) {
FTS_ERROR("[MEMORY]Failed to allocate memory");
FTS_FUNC_EXIT();
return -ENOMEM;
}
err = fts_parse_dt(&client->dev, pdata);
if (err)
FTS_ERROR("[DTS]DT parsing failed");
} else
pdata = client->dev.platform_data;
if (!pdata) {
FTS_ERROR("Invalid pdata");
FTS_FUNC_EXIT();
return -EINVAL;
}
if (!i2c_check_functionality(client->adapter, I2C_FUNC_I2C)) {
FTS_ERROR("I2C not supported");
FTS_FUNC_EXIT();
return -ENODEV;
}
data =
devm_kzalloc(&client->dev, sizeof(struct fts_ts_data), GFP_KERNEL);
if (!data) {
FTS_ERROR("[MEMORY]Failed to allocate memory");
FTS_FUNC_EXIT();
return -ENOMEM;
}
input_dev = input_allocate_device();
if (!input_dev) {
FTS_ERROR("[INPUT]Failed to allocate input device");
FTS_FUNC_EXIT();
return -ENOMEM;
}
data->input_dev = input_dev;
data->client = client;
data->pdata = pdata;
fts_wq_data = data;
fts_i2c_client = client;
fts_input_dev = input_dev;
spin_lock_init(&fts_wq_data->irq_lock);
fts_input_dev_init(client, data, input_dev, pdata);
#if FTS_POWER_SOURCE_CUST_EN
fts_power_source_init(data);
fts_power_source_ctrl(data, 1);
#endif
err = fts_gpio_configure(data);
if (err < 0) {
FTS_ERROR("[GPIO]Failed to configure the gpios");
goto free_gpio;
}
fts_reset_proc(200);
fts_wait_tp_to_valid(client);
err =
request_threaded_irq(client->irq, NULL, fts_ts_interrupt,
pdata->irq_gpio_flags | IRQF_ONESHOT |
IRQF_TRIGGER_FALLING, client->dev.driver->name,
data);
if (err) {
FTS_ERROR("Request irq failed!");
goto free_gpio;
}
fts_irq_disable();
#if FTS_PSENSOR_EN
if (fts_sensor_init(data) != 0) {
FTS_ERROR("fts_sensor_init failed!");
FTS_FUNC_EXIT();
return 0;
}
#endif
#if FTS_POINT_REPORT_CHECK_EN
fts_point_report_check_init();
#endif
fts_ex_mode_init(client);
#if FTS_GESTURE_EN
fts_gesture_init(input_dev, client);
#endif
#if FTS_ESDCHECK_EN
fts_esdcheck_init();
#endif
fts_irq_enable();
#if FTS_TEST_EN
fts_test_init(client);
#endif
#if defined(CONFIG_FB)
data->fb_notif.notifier_call = fb_notifier_callback;
err = fb_register_client(&data->fb_notif);
if (err)
FTS_ERROR("[FB]Unable to register fb_notifier: %d", err);
#elif defined(CONFIG_HAS_EARLYSUSPEND)
data->early_suspend.level =
EARLY_SUSPEND_LEVEL_BLANK_SCREEN + FTS_SUSPEND_LEVEL;
data->early_suspend.suspend = fts_ts_early_suspend;
data->early_suspend.resume = fts_ts_late_resume;
register_early_suspend(&data->early_suspend);
#endif
FTS_FUNC_EXIT();
return 0;
free_gpio:
if (gpio_is_valid(pdata->reset_gpio))
gpio_free(pdata->reset_gpio);
if (gpio_is_valid(pdata->irq_gpio))
gpio_free(pdata->irq_gpio);
return err;
}
/*****************************************************************************
* Name: fts_ts_remove
* Brief:
* Input:
* Output:
* Return:
*****************************************************************************/
static int fts_ts_remove(struct i2c_client *client)
{
struct fts_ts_data *data = i2c_get_clientdata(client);
FTS_FUNC_ENTER();
cancel_work_sync(&data->touch_event_work);
#if FTS_PSENSOR_EN
fts_sensor_remove(data);
#endif
#if FTS_POINT_REPORT_CHECK_EN
fts_point_report_check_exit();
#endif
fts_ex_mode_exit(client);
#if defined(CONFIG_FB)
if (fb_unregister_client(&data->fb_notif))
FTS_ERROR("Error occurred while unregistering fb_notifier.");
#elif defined(CONFIG_HAS_EARLYSUSPEND)
unregister_early_suspend(&data->early_suspend);
#endif
free_irq(client->irq, data);
if (gpio_is_valid(data->pdata->reset_gpio))
gpio_free(data->pdata->reset_gpio);
if (gpio_is_valid(data->pdata->irq_gpio))
gpio_free(data->pdata->irq_gpio);
input_unregister_device(data->input_dev);
#if FTS_TEST_EN
fts_test_exit(client);
#endif
#if FTS_ESDCHECK_EN
fts_esdcheck_exit();
#endif
FTS_FUNC_EXIT();
return 0;
}
/*****************************************************************************
* Name: fts_ts_suspend
* Brief:
* Input:
* Output:
* Return:
*****************************************************************************/
static int fts_ts_suspend(struct device *dev)
{
struct fts_ts_data *data = dev_get_drvdata(dev);
int retval = 0;
FTS_FUNC_ENTER();
if (data->suspended) {
FTS_INFO("Already in suspend state");
FTS_FUNC_EXIT();
return -1;
}
fts_release_all_finger();
#if FTS_GESTURE_EN
retval = fts_gesture_suspend(data->client);
if (retval == 0) {
/* Enter into gesture mode(suspend) */
retval = enable_irq_wake(fts_wq_data->client->irq);
if (retval)
FTS_ERROR("%s: set_irq_wake failed", __func__);
data->suspended = true;
FTS_FUNC_EXIT();
return 0;
}
#endif
#if FTS_PSENSOR_EN
if (fts_sensor_suspend(data) != 0) {
enable_irq_wake(data->client->irq);
data->suspended = true;
return 0;
}
#endif
#if FTS_ESDCHECK_EN
fts_esdcheck_suspend();
#endif
fts_irq_disable();
/* TP enter sleep mode */
retval =
fts_i2c_write_reg(data->client, FTS_REG_POWER_MODE,
FTS_REG_POWER_MODE_SLEEP_VALUE);
if (retval < 0)
FTS_ERROR("Set TP to sleep mode fail, ret=%d!", retval);
data->suspended = true;
FTS_FUNC_EXIT();
return 0;
}
/*****************************************************************************
* Name: fts_ts_resume
* Brief:
* Input:
* Output:
* Return:
*****************************************************************************/
static int fts_ts_resume(struct device *dev)
{
struct fts_ts_data *data = dev_get_drvdata(dev);
FTS_FUNC_ENTER();
if (!data->suspended) {
FTS_DEBUG("Already in awake state");
FTS_FUNC_EXIT();
return -1;
}
if (!fts_chip_idc(data))
fts_reset_proc(200);
fts_tp_state_recovery(data->client);
#if FTS_GESTURE_EN
if (fts_gesture_resume(data->client) == 0) {
int err;
err = disable_irq_wake(data->client->irq);
if (err)
FTS_ERROR("%s: disable_irq_wake failed", __func__);
data->suspended = false;
FTS_FUNC_EXIT();
return 0;
}
#endif
#if FTS_PSENSOR_EN
if (fts_sensor_resume(data) != 0) {
disable_irq_wake(data->client->irq);
data->suspended = false;
FTS_FUNC_EXIT();
return 0;
}
#endif
data->suspended = false;
fts_irq_enable();
#if FTS_ESDCHECK_EN
fts_esdcheck_resume();
#endif
FTS_FUNC_EXIT();
return 0;
}
/*****************************************************************************
* I2C Driver
*****************************************************************************/
static const struct i2c_device_id fts_ts_id[] = {
{FTS_DRIVER_NAME, 0},
{},
};
MODULE_DEVICE_TABLE(i2c, fts_ts_id);
static struct of_device_id fts_match_table[] = {
{.compatible = "focaltech,fts",},
{},
};
static struct i2c_driver fts_ts_driver = {
.probe = fts_ts_probe,
.remove = fts_ts_remove,
.driver = {
.name = FTS_DRIVER_NAME,
.owner = THIS_MODULE,
.of_match_table = fts_match_table,
},
.id_table = fts_ts_id,
};
/*****************************************************************************
* Name: fts_ts_init
* Brief:
* Input:
* Output:
* Return:
*****************************************************************************/
static int __init fts_ts_init(void)
{
int ret = 0;
FTS_FUNC_ENTER();
ret = i2c_add_driver(&fts_ts_driver);
if (ret != 0)
FTS_ERROR("Focaltech touch screen driver init failed!");
FTS_FUNC_EXIT();
return ret;
}
/*****************************************************************************
* Name: fts_ts_exit
* Brief:
* Input:
* Output:
* Return:
*****************************************************************************/
static void __exit fts_ts_exit(void)
{
i2c_del_driver(&fts_ts_driver);
}
module_init(fts_ts_init);
module_exit(fts_ts_exit);
MODULE_AUTHOR("FocalTech Driver Team");
MODULE_DESCRIPTION("FocalTech Touchscreen Driver");
MODULE_LICENSE("GPL v2");