summaryrefslogtreecommitdiff
path: root/drivers/input/touchscreen/focaltech_touch/focaltech_core.c
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/input/touchscreen/focaltech_touch/focaltech_core.c')
-rw-r--r--drivers/input/touchscreen/focaltech_touch/focaltech_core.c1420
1 files changed, 1420 insertions, 0 deletions
diff --git a/drivers/input/touchscreen/focaltech_touch/focaltech_core.c b/drivers/input/touchscreen/focaltech_touch/focaltech_core.c
new file mode 100644
index 000000000000..0e3da4229968
--- /dev/null
+++ b/drivers/input/touchscreen/focaltech_touch/focaltech_core.c
@@ -0,0 +1,1420 @@
+/*
+ *
+ * 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);
+
+/*****************************************************************************
+* 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(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]);
+
+ 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;
+ }
+
+ 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];
+ } else {
+ FTS_ERROR("unsupported property %s", name);
+ 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();
+
+ 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");
+
+ 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)
+ fts_reset_proc(200);
+#endif
+
+ 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");