| /* |
| * |
| * FocalTech fts TouchScreen driver. |
| * |
| * Copyright (c) 2010-2015, Focaltech 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. |
| * |
| * VERSION DATE AUTHOR |
| * 1.0 2014-09 mshl |
| * |
| */ |
| |
| /******************************************************************************* |
| * |
| * File Name: focaltech.c |
| * |
| * Author: mshl |
| * |
| * Created: 2014-09 |
| * |
| * Modify by mshl on 2015-10-26 |
| * |
| * Abstract: |
| * |
| * Reference: |
| * |
| *******************************************************************************/ |
| /******************************************************************************* |
| * Included header files |
| *******************************************************************************/ |
| //user defined include header files |
| #include "focaltech_core.h" |
| #if GTP_ESD_PROTECT |
| #include "focaltech_esd_protection.h" |
| #endif |
| |
| #if defined(CONFIG_FB) |
| #include <linux/notifier.h> |
| #include <linux/fb.h> |
| |
| #elif defined(CONFIG_HAS_EARLYSUSPEND) |
| #include <linux/earlysuspend.h> |
| /* Early-suspend level */ |
| #define FTS_SUSPEND_LEVEL 1 |
| #endif |
| #ifdef GESTURE_CONTROL |
| #include "../../../base/base.h" |
| #endif |
| //#include "../lct_tp_fm_info.h" |
| //#include "../lct_ctp_upgrade.h" |
| |
| /******************************************************************************* |
| * Private constant and macro definitions using #define |
| *******************************************************************************/ |
| #define FTS_META_REGS 3 |
| #define FTS_ONE_TCH_LEN 6 |
| #define FTS_TCH_LEN(x) (FTS_META_REGS + FTS_ONE_TCH_LEN * x) |
| |
| #define FTS_PRESS 0x7F |
| #define FTS_MAX_ID 0x0F |
| #define FTS_TOUCH_X_H_POS 3 |
| #define FTS_TOUCH_X_L_POS 4 |
| #define FTS_TOUCH_Y_H_POS 5 |
| #define FTS_TOUCH_Y_L_POS 6 |
| #define FTS_TOUCH_PRE_POS 7 |
| #define FTS_TOUCH_AREA_POS 8 |
| #define FTS_TOUCH_POINT_NUM 2 |
| #define FTS_TOUCH_EVENT_POS 3 |
| #define FTS_TOUCH_ID_POS 5 |
| |
| #define FTS_TOUCH_DOWN 0 |
| #define FTS_TOUCH_UP 1 |
| #define FTS_TOUCH_CONTACT 2 |
| |
| #define POINT_READ_BUF (3 + FTS_ONE_TCH_LEN * FTS_MAX_POINTS) |
| |
| /*register address*/ |
| #define FTS_REG_DEV_MODE 0x00 |
| #define FTS_DEV_MODE_REG_CAL 0x02 |
| |
| #define FTS_REG_PMODE 0xA5 |
| |
| #define FTS_REG_POINT_RATE 0x88 |
| #define FTS_REG_THGROUP 0x80 |
| |
| /* power register bits*/ |
| #define FTS_PMODE_ACTIVE 0x00 |
| #define FTS_PMODE_MONITOR 0x01 |
| #define FTS_PMODE_STANDBY 0x02 |
| #define FTS_PMODE_HIBERNATE 0x03 |
| |
| #define FTS_STATUS_NUM_TP_MASK 0x0F |
| |
| #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 |
| |
| #define FTS_COORDS_ARR_SIZE 4 |
| #define MAX_BUTTONS 4 |
| |
| #define FTS_8BIT_SHIFT 8 |
| #define FTS_4BIT_SHIFT 4 |
| |
| /* psensor register address*/ |
| #define FTS_REG_PSENSOR_ENABLE 0xB0 |
| #define FTS_REG_PSENSOR_STATUS 0x01 |
| |
| /* psensor register bits*/ |
| #define FTS_PSENSOR_ENABLE_MASK 0x01 |
| #define FTS_PSENSOR_STATUS_NEAR 0xC0 |
| #define FTS_PSENSOR_STATUS_FAR 0xE0 |
| #define FTS_PSENSOR_FAR_TO_NEAR 0 |
| #define FTS_PSENSOR_NEAR_TO_FAR 1 |
| #define FTS_PSENSOR_ORIGINAL_STATE_FAR 1 |
| #define FTS_PSENSOR_WAKEUP_TIMEOUT 500 |
| |
| #define PINCTRL_STATE_ACTIVE "pmx_ts_active" |
| #define PINCTRL_STATE_SUSPEND "pmx_ts_suspend" |
| #define PINCTRL_STATE_RELEASE "pmx_ts_release" |
| |
| /******************************************************************************* |
| * Private enumerations, structures and unions using typedef |
| *******************************************************************************/ |
| |
| /******************************************************************************* |
| * Static variables |
| *******************************************************************************/ |
| |
| |
| /******************************************************************************* |
| * 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; |
| |
| static unsigned int buf_count_add=0; |
| static unsigned int buf_count_neg=0; |
| |
| #if FTS_GESTRUE_EN |
| struct kobject *touchscreen_dev_kobj=NULL; |
| #endif |
| unsigned int gesture_switch; |
| static u8 fts_fw_version; |
| EXPORT_SYMBOL_GPL(gesture_switch); |
| |
| u8 buf_touch_data[30*POINT_READ_BUF] = { 0 }; |
| |
| #ifdef CONFIG_TOUCHSCREEN_FTS_PSENSOR |
| static struct sensors_classdev __maybe_unused sensors_proximity_cdev = { |
| .name = "fts-proximity", |
| .vendor = "FocalTech", |
| .version = 1, |
| .handle = SENSORS_PROXIMITY_HANDLE, |
| .type = SENSOR_TYPE_PROXIMITY, |
| .max_range = "5.0", |
| .resolution = "5.0", |
| .sensor_power = "0.1", |
| .min_delay = 0, |
| .fifo_reserved_event_count = 0, |
| .fifo_max_event_count = 0, |
| .enabled = 0, |
| .delay_msec = 200, |
| .sensors_enable = NULL, |
| .sensors_poll_delay = NULL, |
| }; |
| #endif |
| |
| /******************************************************************************* |
| * Static function prototypes |
| *******************************************************************************/ |
| static int fts_ts_start(struct device *dev); |
| static int fts_ts_stop(struct device *dev); |
| |
| static int fts_read_Touchdata(struct fts_ts_data *data); |
| static void fts_report_value(struct fts_ts_data *data); |
| |
| |
| #ifdef CONFIG_TOUCHSCREEN_FTS_PSENSOR |
| /******************************************************************************* |
| * Name: fts_psensor_support_enabled |
| * Brief: |
| * Input: |
| * Output: |
| * Return: |
| *******************************************************************************/ |
| static inline bool fts_psensor_support_enabled(void) |
| { |
| return config_enabled(CONFIG_TOUCHSCREEN_FTS_PSENSOR); |
| } |
| #endif |
| |
| /******************************************************************************* |
| * Name: fts_i2c_read |
| * Brief: |
| * Input: |
| * Output: |
| * Return: |
| *******************************************************************************/ |
| int fts_i2c_read(struct i2c_client *client, char *writebuf, int writelen, char *readbuf, int readlen) |
| { |
| int ret; |
| #if GTP_ESD_PROTECT |
| int i; |
| |
| for(i = 0; i < 3; i++) |
| { |
| ret = fts_esd_protection_notice(); |
| if(0 == ret) |
| break; // can use I2C |
| else |
| { |
| //printk("[focal] fts_esd_protection_notice return :%d \n", ret); |
| continue; |
| } |
| } |
| /* |
| if(3 == i) |
| { |
| printk("[focal] ESD are still use I2C. \n"); |
| }*/ |
| #endif |
| |
| mutex_lock(&i2c_rw_access); |
| |
| if(readlen > 0) |
| { |
| if (writelen > 0) { |
| struct i2c_msg msgs[] = { |
| { |
| .addr = client->addr, |
| .flags = 0, |
| .len = writelen, |
| .buf = writebuf, |
| }, |
| { |
| .addr = client->addr, |
| .flags = I2C_M_RD, |
| .len = readlen, |
| .buf = readbuf, |
| }, |
| }; |
| ret = i2c_transfer(client->adapter, msgs, 2); |
| if (ret < 0) |
| dev_err(&client->dev, "%s: i2c read error.\n", __func__); |
| } else { |
| struct i2c_msg msgs[] = { |
| { |
| .addr = client->addr, |
| .flags = I2C_M_RD, |
| .len = readlen, |
| .buf = readbuf, |
| }, |
| }; |
| ret = i2c_transfer(client->adapter, msgs, 1); |
| if (ret < 0) |
| dev_err(&client->dev, "%s:i2c read error.\n", __func__); |
| } |
| } |
| |
| mutex_unlock(&i2c_rw_access); |
| |
| return ret; |
| } |
| |
| /******************************************************************************* |
| * Name: fts_i2c_write |
| * Brief: |
| * Input: |
| * Output: |
| * Return: |
| *******************************************************************************/ |
| int fts_i2c_write(struct i2c_client *client, char *writebuf, int writelen) |
| { |
| int ret; |
| |
| struct i2c_msg msgs[] = { |
| { |
| .addr = client->addr, |
| .flags = 0, |
| .len = writelen, |
| .buf = writebuf, |
| }, |
| }; |
| |
| #if GTP_ESD_PROTECT |
| fts_esd_protection_notice(); |
| #endif |
| |
| mutex_lock(&i2c_rw_access); |
| |
| if(writelen > 0) |
| { |
| ret = i2c_transfer(client->adapter, msgs, 1); |
| if (ret < 0) |
| dev_err(&client->dev, "%s: i2c write error.\n", __func__); |
| } |
| |
| mutex_unlock(&i2c_rw_access); |
| |
| return ret; |
| } |
| |
| /******************************************************************************* |
| * Name: fts_write_reg |
| * Brief: |
| * Input: |
| * Output: |
| * Return: |
| *******************************************************************************/ |
| int fts_write_reg(struct i2c_client *client, u8 addr, const u8 val) |
| { |
| u8 buf[2] = {0}; |
| |
| buf[0] = addr; |
| buf[1] = val; |
| |
| return fts_i2c_write(client, buf, sizeof(buf)); |
| } |
| |
| /******************************************************************************* |
| * Name: fts_read_reg |
| * Brief: |
| * Input: |
| * Output: |
| * Return: |
| *******************************************************************************/ |
| int fts_read_reg(struct i2c_client *client, u8 addr, u8 *val) |
| { |
| return fts_i2c_read(client, &addr, 1, val, 1); |
| } |
| |
| #ifdef CONFIG_TOUCHSCREEN_FTS_PSENSOR |
| /******************************************************************************* |
| * Name: fts_psensor_enable |
| * Brief: |
| * Input: |
| * Output: |
| * Return: |
| *******************************************************************************/ |
| static void fts_psensor_enable(struct fts_ts_data *data, int enable) |
| { |
| u8 state; |
| int ret = -1; |
| |
| if (data->client == NULL) |
| return; |
| |
| fts_read_reg(data->client, FTS_REG_PSENSOR_ENABLE, &state); |
| if (enable) |
| state |= FTS_PSENSOR_ENABLE_MASK; |
| else |
| state &= ~FTS_PSENSOR_ENABLE_MASK; |
| |
| ret = fts_write_reg(data->client, FTS_REG_PSENSOR_ENABLE, state); |
| if (ret < 0) |
| dev_err(&data->client->dev, |
| "write psensor switch command failed\n"); |
| return; |
| } |
| |
| /******************************************************************************* |
| * Name: fts_psensor_enable_set |
| * Brief: |
| * Input: |
| * Output: |
| * Return: |
| *******************************************************************************/ |
| static int fts_psensor_enable_set(struct sensors_classdev *sensors_cdev, |
| unsigned int enable) |
| { |
| struct fts_psensor_platform_data *psensor_pdata = |
| container_of(sensors_cdev, |
| struct fts_psensor_platform_data, ps_cdev); |
| struct fts_ts_data *data = psensor_pdata->data; |
| struct input_dev *input_dev = data->psensor_pdata->input_psensor_dev; |
| |
| mutex_lock(&input_dev->mutex); |
| fts_psensor_enable(data, enable); |
| psensor_pdata->tp_psensor_data = FTS_PSENSOR_ORIGINAL_STATE_FAR; |
| if (enable) |
| psensor_pdata->tp_psensor_opened = 1; |
| else |
| psensor_pdata->tp_psensor_opened = 0; |
| mutex_unlock(&input_dev->mutex); |
| return enable; |
| } |
| |
| /******************************************************************************* |
| * Name: fts_read_tp_psensor_data |
| * Brief: |
| * Input: |
| * Output: |
| * Return: |
| *******************************************************************************/ |
| static int fts_read_tp_psensor_data(struct fts_ts_data *data) |
| { |
| u8 psensor_status; |
| char tmp; |
| int ret = 1; |
| |
| fts_read_reg(data->client, |
| FTS_REG_PSENSOR_STATUS, &psensor_status); |
| |
| tmp = data->psensor_pdata->tp_psensor_data; |
| if (psensor_status == FTS_PSENSOR_STATUS_NEAR) |
| data->psensor_pdata->tp_psensor_data = |
| FTS_PSENSOR_FAR_TO_NEAR; |
| else if (psensor_status == FTS_PSENSOR_STATUS_FAR) |
| data->psensor_pdata->tp_psensor_data = |
| FTS_PSENSOR_NEAR_TO_FAR; |
| |
| if (tmp != data->psensor_pdata->tp_psensor_data) { |
| dev_dbg(&data->client->dev, |
| "%s sensor data changed\n", __func__); |
| ret = 0; |
| } |
| return ret; |
| } |
| #else |
| /******************************************************************************* |
| * Name: fts_psensor_enable_set |
| * Brief: |
| * Input: |
| * Output: |
| * Return: |
| *******************************************************************************/ |
| /* |
| static int fts_psensor_enable_set(struct sensors_classdev *sensors_cdev, |
| unsigned int enable) |
| { |
| return enable; |
| } |
| */ |
| |
| /******************************************************************************* |
| * Name: fts_read_tp_psensor_data |
| * Brief: |
| * Input: |
| * Output: |
| * Return: |
| *******************************************************************************/ |
| /* |
| static int fts_read_tp_psensor_data(struct fts_ts_data *data) |
| { |
| return 0; |
| } |
| */ |
| #endif |
| |
| /******************************************************************************* |
| * Name: fts_ts_interrupt |
| * Brief: |
| * Input: |
| * Output: |
| * Return: |
| *******************************************************************************/ |
| static irqreturn_t fts_ts_interrupt(int irq, void *dev_id) |
| { |
| int ret; |
| struct fts_ts_data *fts_ts = dev_id; |
| |
| pr_debug("%s: ---start\n",__func__); |
| |
| if (!fts_ts) { |
| pr_err("%s: Invalid fts_ts\n", __func__); |
| return IRQ_HANDLED; |
| } |
| ret = fts_read_Touchdata(fts_wq_data); |
| if (ret == 0) |
| fts_report_value(fts_wq_data); |
| |
| pr_debug("%s: ---end\n",__func__); |
| |
| return IRQ_HANDLED; |
| } |
| |
| /******************************************************************************* |
| * Name: fts_read_Touchdata |
| * Brief: |
| * Input: |
| * Output: |
| * Return: |
| *******************************************************************************/ |
| static int fts_read_Touchdata(struct fts_ts_data *data) |
| { |
| #ifdef CONFIG_TOUCHSCREEN_FTS_PSENSOR |
| int rc = 0; |
| #endif |
| |
| #if FTS_GESTRUE_EN |
| u8 state; |
| #endif |
| |
| u8 buf[POINT_READ_BUF] = { 0 }; |
| int ret = -1; |
| |
| |
| #if FTS_GESTRUE_EN |
| if(gesture_switch){ |
| if(data->suspended) |
| { |
| fts_read_reg(data->client, 0xd0, &state); |
| if(state ==1) |
| { |
| fts_read_Gestruedata(); |
| return 1; |
| } |
| } |
| } |
| #endif |
| |
| #ifdef CONFIG_TOUCHSCREEN_FTS_PSENSOR |
| if (fts_psensor_support_enabled() && data->pdata->psensor_support && |
| data->psensor_pdata->tp_psensor_opened) { |
| rc = fts_read_tp_psensor_data(data); |
| if (!rc) { |
| if (data->suspended) |
| pm_wakeup_event(&data->client->dev, |
| FTS_PSENSOR_WAKEUP_TIMEOUT); |
| input_report_abs(data->psensor_pdata->input_psensor_dev, |
| ABS_DISTANCE, |
| data->psensor_pdata->tp_psensor_data); |
| input_sync(data->psensor_pdata->input_psensor_dev); |
| if (data->suspended) |
| return 1; |
| } |
| if (data->suspended) |
| return 1; |
| } |
| #endif |
| |
| ret = fts_i2c_read(data->client, buf, 1, buf, POINT_READ_BUF); |
| if (ret < 0) { |
| dev_err(&data->client->dev, "%s read touchdata failed.\n", __func__); |
| return ret; |
| } |
| |
| buf_count_add++; |
| memcpy( buf_touch_data+(((buf_count_add-1)%30)*POINT_READ_BUF), buf, sizeof(u8)*POINT_READ_BUF ); |
| |
| 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; |
| int i; |
| int uppoint = 0; |
| int touchs = 0; |
| u8 pointid = FTS_MAX_ID; |
| |
| u8 buf[POINT_READ_BUF] = { 0 }; |
| |
| buf_count_neg++; |
| |
| memcpy( buf,buf_touch_data+(((buf_count_neg-1)%30)*POINT_READ_BUF), sizeof(u8)*POINT_READ_BUF ); |
| |
| |
| memset(event, 0, sizeof(struct ts_event)); |
| |
| event->point_num=buf[FTS_TOUCH_POINT_NUM] & 0x0F; |
| event->touch_point = 0; |
| for (i = 0; i < FTS_MAX_POINTS; i++) { |
| pointid = (buf[FTS_TOUCH_ID_POS + FTS_ONE_TCH_LEN * i]) >> 4; |
| if (pointid >= FTS_MAX_ID) |
| break; |
| else |
| 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; |
| 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)) |
| return; |
| } |
| |
| 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) |
| { |
| // lct.lixiaojun add for button 2016-04-23 |
| //pr_info("lxj--down: x= %d, y =%d\n",x,y); |
| if (event->au16_y[i] > 1920){ |
| if(event->au16_x[i]==200) |
| input_report_key(data->input_dev, KEY_MENU, 1); |
| if(event->au16_x[i]==600) |
| input_report_key(data->input_dev, KEY_HOMEPAGE, 1); |
| if(event->au16_x[i]==800) |
| input_report_key(data->input_dev, KEY_BACK, 1); |
| }else{ |
| input_mt_report_slot_state(data->input_dev, MT_TOOL_FINGER, true); |
| input_report_abs(data->input_dev, ABS_MT_TOUCH_MAJOR, event->area[i]); |
| input_report_abs(data->input_dev, ABS_MT_PRESSURE, event->pressure[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]); |
| } |
| }else{ |
| uppoint++; |
| //pr_info("lxj--up: x= %d, y =%d\n",x,y); |
| if (event->au16_y[i] > 1920){ |
| if(event->au16_x[i]==200) |
| input_report_key(data->input_dev, KEY_MENU, 0); |
| if(event->au16_x[i]==600) |
| input_report_key(data->input_dev, KEY_HOMEPAGE, 0); |
| if(event->au16_x[i]==800) |
| input_report_key(data->input_dev, KEY_BACK, 0); |
| }else{ |
| input_mt_report_slot_state(data->input_dev, MT_TOOL_FINGER, false); |
| data->touchs &= ~BIT(event->au8_finger_id[i]); |
| } |
| } |
| } |
| |
| if(unlikely(data->touchs ^ touchs)) |
| { |
| for(i = 0; i < FTS_MAX_POINTS; i++) |
| { |
| if(BIT(i) & (data->touchs ^ touchs)) |
| { |
| input_mt_slot(data->input_dev, i); |
| input_mt_report_slot_state(data->input_dev, MT_TOOL_FINGER, false); |
| } |
| } |
| } |
| data->touchs = touchs; |
| if(event->touch_point == uppoint) |
| { |
| 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); |
| } |
| |
| |
| /******************************************************************************* |
| * Name: fts_gpio_configure |
| * Brief: |
| * Input: |
| * Output: |
| * Return: |
| *******************************************************************************/ |
| static int fts_gpio_configure(struct fts_ts_data *data, bool on) |
| { |
| int err = 0; |
| static int first_in = 1; |
| |
| if (on) { |
| if (gpio_is_valid(data->pdata->irq_gpio)) { |
| err = gpio_request(data->pdata->irq_gpio, |
| "fts_irq_gpio"); |
| if (err) { |
| dev_err(&data->client->dev, |
| "irq gpio request failed"); |
| goto err_irq_gpio_req; |
| } |
| |
| err = gpio_direction_input(data->pdata->irq_gpio); |
| if (err) { |
| dev_err(&data->client->dev, |
| "set_direction for irq gpio failed\n"); |
| goto err_irq_gpio_dir; |
| } |
| } |
| |
| if (gpio_is_valid(data->pdata->reset_gpio)) { |
| if(first_in){ |
| first_in = 0; |
| err = gpio_request(data->pdata->reset_gpio, |
| "fts_reset_gpio"); |
| if (err) { |
| dev_err(&data->client->dev, |
| "reset gpio request failed"); |
| goto err_irq_gpio_dir; |
| } |
| } |
| |
| err = gpio_direction_output(data->pdata->reset_gpio, 0); |
| if (err) { |
| dev_err(&data->client->dev, |
| "set_direction for reset gpio failed\n"); |
| goto err_reset_gpio_dir; |
| } |
| msleep(data->pdata->hard_rst_dly); |
| gpio_set_value_cansleep(data->pdata->reset_gpio, 1); |
| msleep(200); |
| } |
| |
| return 0; |
| } else { |
| if (gpio_is_valid(data->pdata->irq_gpio)) |
| gpio_free(data->pdata->irq_gpio); |
| if (gpio_is_valid(data->pdata->reset_gpio)) { |
| /* |
| * This is intended to save leakage current |
| * only. Even if the call(gpio_direction_input) |
| * fails, only leakage current will be more but |
| * functionality will not be affected. |
| */ |
| /* |
| err = gpio_direction_input(data->pdata->reset_gpio); |
| if (err) { |
| dev_err(&data->client->dev, |
| "unable to set direction for gpio " |
| "[%d]\n", data->pdata->irq_gpio); |
| } |
| gpio_free(data->pdata->reset_gpio); |
| */ |
| } |
| |
| 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: |
| return err; |
| } |
| |
| /******************************************************************************* |
| * Name: fts_power_on |
| * Brief: |
| * Input: |
| * Output: |
| * Return: |
| *******************************************************************************/ |
| static int fts_power_on(struct fts_ts_data *data, bool on) |
| { |
| int rc = 0; |
| |
| if (!on) |
| { |
| goto power_off; |
| } |
| /* |
| rc = regulator_enable(data->vdd); |
| if (rc) { |
| dev_err(&data->client->dev, "Regulator vdd enable failed rc=%d\n", rc); |
| return rc; |
| } |
| */ |
| |
| rc = gpio_direction_output(data->pdata->power_gpio, 1); |
| |
| |
| |
| rc = regulator_enable(data->vcc_i2c); |
| if (rc) { |
| dev_err(&data->client->dev, "Regulator vcc_i2c enable failed rc=%d\n", rc); |
| } |
| |
| return rc; |
| |
| |
| power_off: |
| rc = gpio_direction_output(data->pdata->power_gpio, 0); |
| if (rc) { |
| dev_err(&data->client->dev, " vdd disable failed rc=%d\n", rc); |
| return rc; |
| } |
| |
| rc = regulator_disable(data->vcc_i2c); |
| if (rc) { |
| dev_err(&data->client->dev, "Regulator vcc_i2c disable failed rc=%d\n", rc); |
| //rc = regulator_enable(data->vdd); |
| gpio_direction_output(data->pdata->power_gpio, 1); |
| if (rc) { |
| dev_err(&data->client->dev, |
| "Regulator vdd enable failed rc=%d\n", rc); |
| } |
| } |
| |
| return rc; |
| } |
| |
| /******************************************************************************* |
| * Name: fts_power_init |
| * Brief: |
| * Input: |
| * Output: |
| * Return: |
| *******************************************************************************/ |
| static int fts_power_init(struct fts_ts_data *data, bool on) |
| { |
| int rc; |
| |
| if (!on) |
| { |
| dev_err(&data->client->dev, "fts_power_init false \n"); |
| } |
| |
| /* |
| data->vdd = regulator_get(&data->client->dev, "vdd"); |
| if (IS_ERR(data->vdd)) { |
| rc = PTR_ERR(data->vdd); |
| dev_err(&data->client->dev, "Regulator get failed vdd rc=%d\n", rc); |
| } |
| |
| if (regulator_count_voltages(data->vdd) > 0) { |
| rc = regulator_set_voltage(data->vdd, FTS_VTG_MIN_UV, FTS_VTG_MAX_UV); |
| if (rc) { |
| dev_err(&data->client->dev, "Regulator set_vtg failed vdd rc=%d\n", rc); |
| goto reg_vdd_put; |
| } |
| } |
| |
| */ |
| |
| |
| if (gpio_is_valid(data->pdata->power_gpio)) |
| { |
| rc = gpio_request(data->pdata->power_gpio, "focaltech,power-gpio"); |
| |
| if (rc < 0) |
| dev_err(&data->client->dev, "%s: request 3v3_en pin failed\n", __func__); |
| //rc = gpio_direction_output(data->pdata->power_gpio, 1); |
| |
| } |
| |
| |
| data->vcc_i2c = regulator_get(&data->client->dev, "vcc_i2c"); |
| if (IS_ERR(data->vcc_i2c)) { |
| rc = PTR_ERR(data->vcc_i2c); |
| dev_err(&data->client->dev, "Regulator get failed vcc_i2c rc=%d\n", 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) { |
| dev_err(&data->client->dev, "Regulator set_vtg failed vcc_i2c rc=%d\n", rc); |
| goto reg_vcc_i2c_put; |
| } |
| } |
| |
| 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); |
| return rc; |
| } |
| |
| /******************************************************************************* |
| * Name: fts_ts_pinctrl_init |
| * Brief: |
| * Input: |
| * Output: |
| * Return: |
| *******************************************************************************/ |
| #ifdef MSM_NEW_VER |
| static int fts_ts_pinctrl_init(struct fts_ts_data *fts_data) |
| { |
| int retval; |
| |
| /* Get pinctrl if target uses pinctrl */ |
| fts_data->ts_pinctrl = devm_pinctrl_get(&(fts_data->client->dev)); |
| if (IS_ERR_OR_NULL(fts_data->ts_pinctrl)) { |
| retval = PTR_ERR(fts_data->ts_pinctrl); |
| dev_dbg(&fts_data->client->dev, |
| "Target does not use pinctrl %d\n", retval); |
| goto err_pinctrl_get; |
| } |
| |
| fts_data->pinctrl_state_active |
| = pinctrl_lookup_state(fts_data->ts_pinctrl, |
| PINCTRL_STATE_ACTIVE); |
| if (IS_ERR_OR_NULL(fts_data->pinctrl_state_active)) { |
| retval = PTR_ERR(fts_data->pinctrl_state_active); |
| dev_err(&fts_data->client->dev, |
| "Can not lookup %s pinstate %d\n", |
| PINCTRL_STATE_ACTIVE, retval); |
| goto err_pinctrl_lookup; |
| } |
| |
| fts_data->pinctrl_state_suspend |
| = pinctrl_lookup_state(fts_data->ts_pinctrl, |
| PINCTRL_STATE_SUSPEND); |
| if (IS_ERR_OR_NULL(fts_data->pinctrl_state_suspend)) { |
| retval = PTR_ERR(fts_data->pinctrl_state_suspend); |
| dev_err(&fts_data->client->dev, |
| "Can not lookup %s pinstate %d\n", |
| PINCTRL_STATE_SUSPEND, retval); |
| goto err_pinctrl_lookup; |
| } |
| |
| fts_data->pinctrl_state_release |
| = pinctrl_lookup_state(fts_data->ts_pinctrl, |
| PINCTRL_STATE_RELEASE); |
| if (IS_ERR_OR_NULL(fts_data->pinctrl_state_release)) { |
| retval = PTR_ERR(fts_data->pinctrl_state_release); |
| dev_dbg(&fts_data->client->dev, |
| "Can not lookup %s pinstate %d\n", |
| PINCTRL_STATE_RELEASE, retval); |
| } |
| |
| return 0; |
| |
| err_pinctrl_lookup: |
| devm_pinctrl_put(fts_data->ts_pinctrl); |
| err_pinctrl_get: |
| fts_data->ts_pinctrl = NULL; |
| return retval; |
| } |
| #endif |
| |
| #ifdef CONFIG_PM |
| /******************************************************************************* |
| * Name: fts_ts_start |
| * Brief: |
| * Input: |
| * Output: |
| * Return: |
| *******************************************************************************/ |
| static int fts_ts_start(struct device *dev) |
| { |
| struct fts_ts_data *data = dev_get_drvdata(dev); |
| int err; |
| |
| if (data->pdata->power_on) { |
| err = data->pdata->power_on(true); |
| if (err) { |
| dev_err(dev, "power on failed"); |
| return err; |
| } |
| } else { |
| err = fts_power_on(data, true); |
| if (err) { |
| dev_err(dev, "power on failed"); |
| return err; |
| } |
| } |
| |
| #ifdef MSM_NEW_VER |
| if (data->ts_pinctrl) { |
| err = pinctrl_select_state(data->ts_pinctrl, |
| data->pinctrl_state_active); |
| if (err < 0) |
| dev_err(dev, "Cannot get active pinctrl state\n"); |
| } |
| #endif |
| |
| err = fts_gpio_configure(data, true); |
| if (err < 0) { |
| dev_err(&data->client->dev, |
| "failed to put gpios in resue state\n"); |
| goto err_gpio_configuration; |
| } |
| /* |
| if (gpio_is_valid(data->pdata->reset_gpio)) { |
| gpio_set_value_cansleep(data->pdata->reset_gpio, 0); |
| msleep(data->pdata->hard_rst_dly); |
| gpio_set_value_cansleep(data->pdata->reset_gpio, 1); |
| } |
| |
| msleep(data->pdata->soft_rst_dly); |
| */ |
| enable_irq(data->client->irq); |
| data->suspended = false; |
| |
| return 0; |
| |
| err_gpio_configuration: |
| #ifdef MSM_NEW_VER |
| if (data->ts_pinctrl) { |
| err = pinctrl_select_state(data->ts_pinctrl, |
| data->pinctrl_state_suspend); |
| if (err < 0) |
| dev_err(dev, "Cannot get suspend pinctrl state\n"); |
| } |
| #endif |
| |
| if (data->pdata->power_on) { |
| err = data->pdata->power_on(false); |
| if (err) |
| dev_err(dev, "power off failed"); |
| } else { |
| err = fts_power_on(data, false); |
| if (err) |
| dev_err(dev, "power off failed"); |
| } |
| return err; |
| } |
| |
| /******************************************************************************* |
| * Name: fts_ts_stop |
| * Brief: |
| * Input: |
| * Output: |
| * Return: |
| *******************************************************************************/ |
| static int fts_ts_stop(struct device *dev) |
| { |
| struct fts_ts_data *data = dev_get_drvdata(dev); |
| char txbuf[2]; |
| int i, err; |
| |
| disable_irq(data->client->irq); |
| |
| /* release all touches */ |
| for (i = 0; i < data->pdata->num_max_touches; i++) { |
| input_mt_slot(data->input_dev, i); |
| input_mt_report_slot_state(data->input_dev, MT_TOOL_FINGER, 0); |
| } |
| input_mt_report_pointer_emulation(data->input_dev, false); |
| input_sync(data->input_dev); |
| |
| if (gpio_is_valid(data->pdata->reset_gpio)) { |
| txbuf[0] = FTS_REG_PMODE; |
| txbuf[1] = FTS_PMODE_HIBERNATE; |
| fts_i2c_write(data->client, txbuf, sizeof(txbuf)); |
| } |
| |
| if (data->pdata->power_on) { |
| err = data->pdata->power_on(false); |
| if (err) { |
| dev_err(dev, "power off failed"); |
| goto pwr_off_fail; |
| } |
| } else { |
| err = fts_power_on(data, false); |
| if (err) { |
| dev_err(dev, "power off failed"); |
| goto pwr_off_fail; |
| } |
| } |
| |
| #ifdef MSM_NEW_VER |
| if (data->ts_pinctrl) { |
| err = pinctrl_select_state(data->ts_pinctrl, |
| data->pinctrl_state_suspend); |
| if (err < 0) |
| dev_err(dev, "Cannot get suspend pinctrl state\n"); |
| } |
| #endif |
| |
| err = fts_gpio_configure(data, false); |
| if (err < 0) { |
| dev_err(&data->client->dev, |
| "failed to put gpios in suspend state\n"); |
| goto gpio_configure_fail; |
| } |
| |
| data->suspended = true; |
| |
| return 0; |
| |
| gpio_configure_fail: |
| #ifdef MSM_NEW_VER |
| if (data->ts_pinctrl) { |
| err = pinctrl_select_state(data->ts_pinctrl, |
| data->pinctrl_state_active); |
| if (err < 0) |
| dev_err(dev, "Cannot get active pinctrl state\n"); |
| } |
| #endif |
| |
| if (data->pdata->power_on) { |
| err = data->pdata->power_on(true); |
| if (err) |
| dev_err(dev, "power on failed"); |
| } else { |
| err = fts_power_on(data, true); |
| if (err) |
| dev_err(dev, "power on failed"); |
| } |
| pwr_off_fail: |
| if (gpio_is_valid(data->pdata->reset_gpio)) { |
| gpio_set_value_cansleep(data->pdata->reset_gpio, 0); |
| msleep(data->pdata->hard_rst_dly); |
| gpio_set_value_cansleep(data->pdata->reset_gpio, 1); |
| } |
| enable_irq(data->client->irq); |
| return err; |
| } |
| |
| /******************************************************************************* |
| * Name: fts_ts_suspend |
| * Brief: |
| * Input: |
| * Output: |
| * Return: |
| *******************************************************************************/ |
| int fts_ts_suspend(struct device *dev) |
| { |
| struct fts_ts_data *data = dev_get_drvdata(dev); |
| int err = 0; |
| |
| if (data->loading_fw) { |
| dev_info(dev, "Firmware loading in process...\n"); |
| return 0; |
| } |
| |
| if (data->suspended) { |
| dev_info(dev, "Already in suspend state\n"); |
| return 0; |
| } |
| |
| #if FTS_GESTRUE_EN |
| if(gesture_switch){ |
| fts_write_reg(fts_i2c_client, 0xd0, 0x01); |
| if (fts_updateinfo_curr.CHIP_ID==0x54 || fts_updateinfo_curr.CHIP_ID==0x58 || fts_updateinfo_curr.CHIP_ID==0x86 || fts_updateinfo_curr.CHIP_ID==0x87) |
| { |
| fts_write_reg(fts_i2c_client, 0xd1, 0xff); |
| fts_write_reg(fts_i2c_client, 0xd2, 0xff); |
| fts_write_reg(fts_i2c_client, 0xd5, 0xff); |
| fts_write_reg(fts_i2c_client, 0xd6, 0xff); |
| fts_write_reg(fts_i2c_client, 0xd7, 0xff); |
| fts_write_reg(fts_i2c_client, 0xd8, 0xff); |
| } |
| |
| err = enable_irq_wake(data->client->irq); |
| if (err) |
| dev_err(&data->client->dev, |
| "%s: set_irq_wake failed\n", __func__); |
| |
| data->suspended = true; |
| |
| return 0; |
| } |
| #endif |
| |
| #if GTP_ESD_PROTECT |
| fts_esd_protection_suspend(); |
| #endif |
| |
| #ifdef CONFIG_TOUCHSCREEN_FTS_PSENSOR |
| if (fts_psensor_support_enabled() && data->pdata->psensor_support && |
| device_may_wakeup(dev) && |
| data->psensor_pdata->tp_psensor_opened) { |
| |
| err = enable_irq_wake(data->client->irq); |
| if (err) |
| dev_err(&data->client->dev, |
| "%s: set_irq_wake failed\n", __func__); |
| data->suspended = true; |
| return err; |
| } |
| #endif |
| |
| return fts_ts_stop(dev); |
| } |
| |
| #ifdef CONFIG_TOUCHSCREEN_FT8716 |
| int ft5x06_ts_suspend_ext(void) |
| { |
| struct fts_ts_data *fts_data = fts_wq_data; |
| |
| if(!fts_data){ |
| pr_err("fts_ts_data is NULL!\n"); |
| return -1; |
| } |
| |
| return fts_ts_suspend(&fts_data->client->dev); |
| } |
| |
| EXPORT_SYMBOL(ft5x06_ts_suspend_ext); |
| #endif |
| |
| /******************************************************************************* |
| * Name: fts_ts_resume |
| * Brief: |
| * Input: |
| * Output: |
| * Return: |
| *******************************************************************************/ |
| int fts_ts_resume(struct device *dev) |
| { |
| int err; |
| struct fts_ts_data *data = dev_get_drvdata(dev); |
| |
| if (!data->suspended) { |
| dev_dbg(dev, "Already in awake state\n"); |
| return 0; |
| } |
| |
| #if FTS_GESTRUE_EN |
| if(gesture_switch){ |
| if (gpio_is_valid(data->pdata->reset_gpio)) { |
| gpio_set_value_cansleep(data->pdata->reset_gpio, 0); |
| msleep(data->pdata->hard_rst_dly); |
| gpio_set_value_cansleep(data->pdata->reset_gpio, 1); |
| } |
| |
| msleep(200); |
| |
| err = disable_irq_wake(data->client->irq); |
| if (err) |
| dev_err(&data->client->dev, |
| "%s: disable_irq_wake failed\n", |
| __func__); |
| |
| data->suspended = false; |
| return 0; |
| } |
| |
| #endif |
| |
| #ifdef CONFIG_TOUCHSCREEN_FTS_PSENSOR |
| if (fts_psensor_support_enabled() && data->pdata->psensor_support && |
| device_may_wakeup(dev) && |
| data->psensor_pdata->tp_psensor_opened) { |
| err = disable_irq_wake(data->client->irq); |
| if (err) |
| dev_err(&data->client->dev, |
| "%s: disable_irq_wake failed\n", |
| __func__); |
| data->suspended = false; |
| return err; |
| } |
| #endif |
| |
| err = fts_ts_start(dev); |
| if (err < 0) |
| return err; |
| |
| #if GTP_ESD_PROTECT |
| fts_esd_protection_resume(); |
| #endif |
| |
| return 0; |
| } |
| |
| static const struct dev_pm_ops fts_ts_pm_ops = { |
| #if (!defined(CONFIG_FB) && !defined(CONFIG_HAS_EARLYSUSPEND)) |
| .suspend = fts_ts_suspend, |
| .resume = fts_ts_resume, |
| #endif |
| }; |
| #else |
| /******************************************************************************* |
| * Name: fts_ts_suspend |
| * Brief: |
| * Input: |
| * Output: |
| * Return: |
| *******************************************************************************/ |
| static int fts_ts_suspend(struct device *dev) |
| { |
| return 0; |
| } |
| /******************************************************************************* |
| * Name: fts_ts_resume |
| * Brief: |
| * Input: |
| * Output: |
| * Return: |
| *******************************************************************************/ |
| static int fts_ts_resume(struct device *dev) |
| { |
| return 0; |
| } |
| #endif |
| |
| #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 |
| |
| |
| #ifdef CONFIG_OF |
| /******************************************************************************* |
| * 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) { |
| dev_err(dev, "invalid %s\n", name); |
| return -EINVAL; |
| } |
| |
| rc = of_property_read_u32_array(np, name, coords, coords_size); |
| if (rc && (rc != -EINVAL)) { |
| dev_err(dev, "Unable to read %s\n", name); |
| return rc; |
| } |
| |
| if (!strcmp(name, "focaltech,panel-coords")) { |
| pdata->panel_minx = coords[0]; |
| pdata->panel_miny = coords[1]; |
| pdata->panel_maxx = coords[2]; |
| pdata->panel_maxy = coords[3]; |
| } else 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 { |
| dev_err(dev, "unsupported property %s\n", 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; |
| struct property *prop; |
| u32 temp_val, num_buttons; |
| u32 button_map[MAX_BUTTONS]; |
| |
| pdata->name = "focaltech"; |
| rc = of_property_read_string(np, "focaltech,name", &pdata->name); |
| if (rc && (rc != -EINVAL)) { |
| dev_err(dev, "Unable to read name\n"); |
| } |
| |
| rc = fts_get_dt_coords(dev, "focaltech,panel-coords", pdata); |
| if (rc && (rc != -EINVAL)) |
| dev_err(dev, "Unable to get panel-coords \n"); |
| |
| rc = fts_get_dt_coords(dev, "focaltech,display-coords", pdata); |
| if (rc) |
| dev_err(dev, "Unable to get display-coords \n"); |
| |
| pdata->i2c_pull_up = of_property_read_bool(np, "focaltech,i2c-pull-up"); |
| |
| pdata->no_force_update = of_property_read_bool(np, "focaltech,no-force-update"); |
| |
| /* power_gpio,irq gpio info*/ |
| |
| pdata->power_gpio = of_get_named_gpio_flags(np, "focaltech,power-gpio", |
| 0, &pdata->power_gpio_flags); |
| if (pdata->power_gpio < 0) |
| return pdata->power_gpio; |
| |
| /* 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) |
| { |
| dev_err(dev, "Unable to get reset_gpio \n"); |
| } |
| |
| pdata->irq_gpio = of_get_named_gpio_flags(np, "focaltech,irq-gpio", 0, &pdata->irq_gpio_flags); |
| if (pdata->irq_gpio < 0) |
| { |
| dev_err(dev, "Unable to get irq_gpio \n"); |
| } |
| |
| pdata->fw_name = "ft_fw.bin"; |
| rc = of_property_read_string(np, "focaltech,fw-name", &pdata->fw_name); |
| if (rc && (rc != -EINVAL)) { |
| dev_err(dev, "Unable to read fw name\n"); |
| } |
| |
| rc = of_property_read_u32(np, "focaltech,group-id", &temp_val); |
| if (!rc) |
| pdata->group_id = temp_val; |
| else |
| dev_err(dev, "Unable to get group-id \n"); |
| |
| rc = of_property_read_u32(np, "focaltech,hard-reset-delay-ms", &temp_val); |
| if (!rc) |
| pdata->hard_rst_dly = temp_val; |
| else |
| dev_err(dev, "Unable to get hard-reset-delay-ms \n"); |
| |
| rc = of_property_read_u32(np, "focaltech,soft-reset-delay-ms", &temp_val); |
| if (!rc) |
| pdata->soft_rst_dly = temp_val; |
| else |
| dev_err(dev, "Unable to get soft-reset-delay-ms \n"); |
| |
| rc = of_property_read_u32(np, "focaltech,num-max-touches", &temp_val); |
| if (!rc) |
| pdata->num_max_touches = temp_val; |
| else |
| dev_err(dev, "Unable to num-max-touches \n"); |
| |
| rc = of_property_read_u32(np, "focaltech,fw-delay-aa-ms", &temp_val); |
| if (rc && (rc != -EINVAL)) { |
| dev_err(dev, "Unable to read fw delay aa\n"); |
| } else if (rc != -EINVAL) |
| pdata->info.delay_aa = temp_val; |
| |
| rc = of_property_read_u32(np, "focaltech,fw-delay-55-ms", &temp_val); |
| if (rc && (rc != -EINVAL)) { |
| dev_err(dev, "Unable to read fw delay 55\n"); |
| } else if (rc != -EINVAL) |
| pdata->info.delay_55 = temp_val; |
| |
| rc = of_property_read_u32(np, "focaltech,fw-upgrade-id1", &temp_val); |
| if (rc && (rc != -EINVAL)) { |
| dev_err(dev, "Unable to read fw upgrade id1\n"); |
| } else if (rc != -EINVAL) |
| pdata->info.upgrade_id_1 = temp_val; |
| |
| rc = of_property_read_u32(np, "focaltech,fw-upgrade-id2", &temp_val); |
| if (rc && (rc != -EINVAL)) { |
| dev_err(dev, "Unable to read fw upgrade id2\n"); |
| } else if (rc != -EINVAL) |
| pdata->info.upgrade_id_2 = temp_val; |
| |
| rc = of_property_read_u32(np, "focaltech,fw-delay-readid-ms", &temp_val); |
| if (rc && (rc != -EINVAL)) { |
| dev_err(dev, "Unable to read fw delay read id-ms \n"); |
| } else if (rc != -EINVAL) |
| pdata->info.delay_readid = temp_val; |
| |
| rc = of_property_read_u32(np, "focaltech,fw-delay-era-flsh-ms", &temp_val); |
| if (rc && (rc != -EINVAL)) { |
| dev_err(dev, "Unable to read fw delay erase flash\n"); |
| } else if (rc != -EINVAL) |
| pdata->info.delay_erase_flash = temp_val; |
| |
| pdata->info.AUTO_CLB = of_property_read_bool(np, "focaltech,fw-auto-cal"); |
| |
| pdata->fw_vkey_support = of_property_read_bool(np, "focaltech,fw-vkey-support"); |
| |
| pdata->ignore_id_check = of_property_read_bool(np, "focaltech,ignore-id-check"); |
| |
| pdata->psensor_support = of_property_read_bool(np, |
| "focaltech,psensor-support"); |
| |
| rc = of_property_read_u32(np, "focaltech,family-id", &temp_val); |
| if (!rc) |
| pdata->family_id = temp_val; |
| else |
| dev_err(dev, "Unable to read family-id \n"); |
| |
| prop = of_find_property(np, "focaltech,button-map", NULL); |
| if (prop) { |
| num_buttons = prop->length / sizeof(temp_val); |
| if (num_buttons > MAX_BUTTONS) |
| dev_err(dev, "Num_buttons is more than MAX_BUTTONS\n"); |
| rc = of_property_read_u32_array(np, "focaltech,button-map", button_map, num_buttons); |
| if (rc) { |
| dev_err(dev, "Unable to read key codes\n"); |
| } |
| } |
| |
| return 0; |
| } |
| #else |
| /******************************************************************************* |
| * Name: fts_parse_dt |
| * Brief: |
| * Input: |
| * Output: |
| * Return: |
| *******************************************************************************/ |
| static int fts_parse_dt(struct device *dev, struct fts_ts_platform_data *pdata) |
| { |
| return -ENODEV; |
| } |
| #endif |
| |
| /******************************************************************************* |
| * Name: fts_debug_addr_is_valid |
| * Brief: |
| * Input: |
| * Output: |
| * Return: |
| *******************************************************************************/ |
| static bool fts_debug_addr_is_valid(int addr) |
| { |
| if (addr < 0 || addr > 0xFF) { |
| pr_err("FT reg address is invalid: 0x%x\n", addr); |
| return false; |
| } |
| |
| return true; |
| } |
| |
| /******************************************************************************* |
| * Name: fts_debug_data_set |
| * Brief: |
| * Input: |
| * Output: |
| * Return: |
| *******************************************************************************/ |
| static int fts_debug_data_set(void *_data, u64 val) |
| { |
| struct fts_ts_data *data = _data; |
| |
| mutex_lock(&data->input_dev->mutex); |
| |
| if (fts_debug_addr_is_valid(data->addr)) |
| dev_info(&data->client->dev, |
| "Writing into FT registers not supported\n"); |
| |
| mutex_unlock(&data->input_dev->mutex); |
| |
| return 0; |
| } |
| |
| /******************************************************************************* |
| * Name: fts_debug_data_get |
| * Brief: |
| * Input: |
| * Output: |
| * Return: |
| *******************************************************************************/ |
| static int fts_debug_data_get(void *_data, u64 *val) |
| { |
| struct fts_ts_data *data = _data; |
| int rc; |
| u8 reg; |
| |
| mutex_lock(&data->input_dev->mutex); |
| |
| if (fts_debug_addr_is_valid(data->addr)) { |
| rc = fts_read_reg(data->client, data->addr, ®); |
| if (rc < 0) |
| dev_err(&data->client->dev, |
| "FT read register 0x%x failed (%d)\n", |
| data->addr, rc); |
| else |
| *val = reg; |
| } |
| |
| mutex_unlock(&data->input_dev->mutex); |
| |
| return 0; |
| } |
| |
| DEFINE_SIMPLE_ATTRIBUTE(debug_data_fops, fts_debug_data_get, fts_debug_data_set, "0x%02llX\n"); |
| |
| /******************************************************************************* |
| * Name: fts_debug_addr_set |
| * Brief: |
| * Input: |
| * Output: |
| * Return: |
| *******************************************************************************/ |
| static int fts_debug_addr_set(void *_data, u64 val) |
| { |
| struct fts_ts_data *data = _data; |
| |
| if (fts_debug_addr_is_valid(val)) { |
| mutex_lock(&data->input_dev->mutex); |
| data->addr = val; |
| mutex_unlock(&data->input_dev->mutex); |
| } |
| |
| return 0; |
| } |
| |
| /******************************************************************************* |
| * Name: fts_debug_addr_get |
| * Brief: |
| * Input: |
| * Output: |
| * Return: |
| *******************************************************************************/ |
| static int fts_debug_addr_get(void *_data, u64 *val) |
| { |
| struct fts_ts_data *data = _data; |
| |
| mutex_lock(&data->input_dev->mutex); |
| |
| if (fts_debug_addr_is_valid(data->addr)) |
| *val = data->addr; |
| |
| mutex_unlock(&data->input_dev->mutex); |
| |
| return 0; |
| } |
| |
| DEFINE_SIMPLE_ATTRIBUTE(debug_addr_fops, fts_debug_addr_get, fts_debug_addr_set, "0x%02llX\n"); |
| |
| /******************************************************************************* |
| * Name: fts_debug_suspend_set |
| * Brief: |
| * Input: |
| * Output: |
| * Return: |
| *******************************************************************************/ |
| static int fts_debug_suspend_set(void *_data, u64 val) |
| { |
| struct fts_ts_data *data = _data; |
| |
| mutex_lock(&data->input_dev->mutex); |
| |
| if (val) |
| fts_ts_suspend(&data->client->dev); |
| else |
| fts_ts_resume(&data->client->dev); |
| |
| mutex_unlock(&data->input_dev->mutex); |
| |
| return 0; |
| } |
| |
| /******************************************************************************* |
| * Name: fts_debug_suspend_get |
| * Brief: |
| * Input: |
| * Output: |
| * Return: |
| *******************************************************************************/ |
| static int fts_debug_suspend_get(void *_data, u64 *val) |
| { |
| struct fts_ts_data *data = _data; |
| |
| mutex_lock(&data->input_dev->mutex); |
| *val = data->suspended; |
| mutex_unlock(&data->input_dev->mutex); |
| |
| return 0; |
| } |
| |
| DEFINE_SIMPLE_ATTRIBUTE(debug_suspend_fops, fts_debug_suspend_get, fts_debug_suspend_set, "%lld\n"); |
| |
| /******************************************************************************* |
| * Name: fts_debug_dump_info |
| * Brief: |
| * Input: |
| * Output: |
| * Return: |
| *******************************************************************************/ |
| int fts_debug_dump_info(struct seq_file *m, void *v) |
| { |
| struct fts_ts_data *data = m->private; |
| |
| seq_printf(m, "%s\n", data->ts_info); |
| |
| return 0; |
| } |
| |
| /******************************************************************************* |
| * Name: debugfs_dump_info_open |
| * Brief: |
| * Input: |
| * Output: |
| * Return: |
| *******************************************************************************/ |
| static int debugfs_dump_info_open(struct inode *inode, struct file *file) |
| { |
| return single_open(file, fts_debug_dump_info, inode->i_private); |
| } |
| |
| static const struct file_operations debug_dump_info_fops = { |
| .owner = THIS_MODULE, |
| .open = debugfs_dump_info_open, |
| .read = seq_read, |
| .release = single_release, |
| }; |
| |
| |
| #ifdef GESTURE_CONTROL |
| static ssize_t fts_ts_gesture_ctrl_store(struct kobject *kobj, struct kobj_attribute *attr,const char *buf, size_t count) |
| { |
| unsigned int gctl=0; |
| |
| if (sscanf(buf,"%d",&gctl) != 1) |
| { |
| printk("%s:sscanf fail gctl=%d\n", __func__,gctl); |
| return -1; |
| } |
| |
| gesture_switch = gctl ? 1 : 0; |
| return count; |
| } |
| static ssize_t fts_ts_gesture_ctrl_show(struct kobject *kobj, struct kobj_attribute *attr, char *buf) |
| { |
| int ret; |
| u8 state=0; |
| |
| fts_read_reg(fts_i2c_client, 0xd0, &state); |
| ret=sprintf(buf, "%s=%d:%s=0x%x\n","gesture_switch",gesture_switch,"[0xd0]",(int)state); |
| |
| return ret; |
| } |
| |
| static struct kobj_attribute fts_ts_gesture_ctrl_attr = { |
| .attr = { |
| .name = "gesture_ctrl", |
| .mode = S_IRWXUGO, |
| }, |
| |
| .store = &fts_ts_gesture_ctrl_store, |
| .show = &fts_ts_gesture_ctrl_show, |
| }; |
| #endif |
| |
| static ssize_t fts_ts_firmware_version_show(struct kobject *kobj, struct kobj_attribute *attr, char *buf) |
| { |
| int ret; |
| |
| ret=sprintf(buf, "Focaltech:FT8716:0x%02x\n",fts_fw_version); |
| |
| return ret; |
| } |
| |
| static struct kobj_attribute fts_ts_firmware_version_attr = { |
| .attr = { |
| .name = "firmware_version", |
| .mode = S_IRUGO, |
| }, |
| |
| .show = &fts_ts_firmware_version_show, |
| }; |
| |
| static ssize_t fts_ts_firmware_update_store(struct kobject *kobj, struct kobj_attribute *attr,const char *buf, size_t count) |
| { |
| char fwname[128]; |
| struct i2c_client *client = fts_i2c_client;//container_of(dev, struct i2c_client, dev); |
| memset(fwname, 0, sizeof(fwname)); |
| sprintf(fwname, "%s", buf); |
| fwname[count-1] = '\0'; |
| |
| mutex_lock(&fts_input_dev->mutex); |
| |
| disable_irq(client->irq); |
| fts_ctpm_fw_upgrade_with_app_file(client, fwname); |
| enable_irq(client->irq); |
| |
| mutex_unlock(&fts_input_dev->mutex); |
| |
| return count; |
| } |
| |
| static struct kobj_attribute fts_ts_firmware_update_attr = { |
| .attr = { |
| .name = "firmware_update", |
| .mode = S_IWUGO, |
| }, |
| |
| .store = &fts_ts_firmware_update_store, |
| }; |
| |
| static struct attribute * fts_ts_properties_attrs[] = { |
| &fts_ts_firmware_version_attr.attr, |
| //&ctp_vendor_attr.attr, |
| &fts_ts_firmware_update_attr.attr, |
| #ifdef GESTURE_CONTROL |
| &fts_ts_gesture_ctrl_attr.attr, |
| #endif |
| NULL |
| }; |
| |
| static struct attribute_group fts_ts_attr_group = { |
| .attrs = fts_ts_properties_attrs, |
| }; |
| |
| static int fts_ts_create_ctp_node(void) |
| { |
| int ret; |
| struct kobject *virtual_dir=NULL; |
| struct kobject *touchscreen_dir=NULL; |
| struct kobject *touchscreen_dev_dir=NULL; |
| |
| virtual_dir = virtual_device_parent(NULL); |
| if(!virtual_dir) |
| { |
| printk("Get virtual dir failed\n"); |
| return -ENOMEM; |
| } |
| |
| touchscreen_dir=kobject_create_and_add("touchscreen",virtual_dir); |
| if(!touchscreen_dir) |
| { |
| printk("Create touchscreen dir failed\n"); |
| return -ENOMEM; |
| } |
| |
| touchscreen_dev_dir=kobject_create_and_add("touchscreen_dev",touchscreen_dir); |
| if(!touchscreen_dev_dir) |
| { |
| printk("Create touchscreen_dev dir failed\n"); |
| return -ENOMEM; |
| } |
| |
| #if FTS_GESTRUE_EN |
| touchscreen_dev_kobj = touchscreen_dev_dir; |
| #endif |
| ret=sysfs_create_group(touchscreen_dev_dir, &fts_ts_attr_group); |
| if(ret) |
| { |
| printk("create fts_ts_attr_group error\n"); |
| } |
| |
| return 0; |
| } |
| |
| /******************************************************************************* |
| * Name: fts_ts_probe |
| * Brief: |
| * Input: |
| * Output: |
| * Return: |
| *******************************************************************************/ |
| unsigned int lcm_id = 1; |
| static int fts_ts_probe(struct i2c_client *client, const struct i2c_device_id *id) |
| { |
| |
| struct fts_ts_platform_data *pdata; |
| |
| #ifdef CONFIG_TOUCHSCREEN_FTS_PSENSOR |
| struct fts_psensor_platform_data *psensor_pdata; |
| struct input_dev *psensor_input_dev; |
| #endif |
| |
| struct fts_ts_data *data; |
| struct input_dev *input_dev; |
| |
| struct dentry *temp; |
| u8 reg_value=0; |
| u8 reg_addr; |
| int err, len; |
| char fw_version[64]; |
| |
| if(lcm_id != 1) |
| return -1; |
| |
| if (client->dev.of_node) { |
| pdata = devm_kzalloc(&client->dev, |
| sizeof(struct fts_ts_platform_data), GFP_KERNEL); |
| if (!pdata) { |
| dev_err(&client->dev, "Failed to allocate memory\n"); |
| return -ENOMEM; |
| } |
| |
| err = fts_parse_dt(&client->dev, pdata); |
| if (err) { |
| dev_err(&client->dev, "DT parsing failed\n"); |
| } |
| } else |
| pdata = client->dev.platform_data; |
| |
| if (!pdata) { |
| dev_err(&client->dev, "Invalid pdata\n"); |
| return -EINVAL; |
| } |
| |
| if (!i2c_check_functionality(client->adapter, I2C_FUNC_I2C)) { |
| dev_err(&client->dev, "I2C not supported\n"); |
| return -ENODEV; |
| } |
| |
| data = devm_kzalloc(&client->dev, sizeof(struct fts_ts_data), GFP_KERNEL); |
| if (!data) { |
| dev_err(&client->dev, "Not enough memory\n"); |
| return -ENOMEM; |
| } |
| |
| |
| fts_wq_data = data; |
| |
| if (pdata->fw_name) { |
| len = strlen(pdata->fw_name); |
| if (len > FTS_FW_NAME_MAX_LEN - 1) { |
| dev_err(&client->dev, "Invalid firmware name\n"); |
| return -EINVAL; |
| } |
| |
| strlcpy(data->fw_name, pdata->fw_name, len + 1); |
| } |
| |
| data->tch_data_len = FTS_TCH_LEN(pdata->num_max_touches); |
| data->tch_data = devm_kzalloc(&client->dev, data->tch_data_len, GFP_KERNEL); |
| |
| if (!data->tch_data) { |
| dev_err(&client->dev, "Not enough memory\n"); |
| return -ENOMEM; |
| } |
| |
| input_dev = input_allocate_device(); |
| if (!input_dev) { |
| dev_err(&client->dev, "failed to allocate input device\n"); |
| return -ENOMEM; |
| } |
| |
| |
| data->input_dev = input_dev; |
| data->client = client; |
| data->pdata = pdata; |
| |
| input_dev->name = "fts_ts"; |
| 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); |
| __set_bit(EV_ABS, input_dev->evbit); |
| __set_bit(BTN_TOUCH, input_dev->keybit); |
| // lct.lixiaojun add for button 2016-04-23 |
| __set_bit(KEY_BACK, input_dev->keybit); |
| __set_bit(KEY_HOMEPAGE, input_dev->keybit); |
| __set_bit(KEY_MENU, input_dev->keybit); |
| __set_bit(INPUT_PROP_DIRECT, input_dev->propbit); |
| |
| input_mt_init_slots(input_dev, pdata->num_max_touches,0); |
| //input_mt_init_slots(input_dev, pdata->num_max_touches); |
| 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, 0x0f, 0, 0); |
| input_set_abs_params(input_dev, ABS_MT_PRESSURE, 0, 0xff, 0, 0); |
| |
| |
| err = input_register_device(input_dev); |
| if (err) { |
| dev_err(&client->dev, "Input device registration failed\n"); |
| goto free_inputdev; |
| } |
| |
| if (pdata->power_init) { |
| err = pdata->power_init(true); |
| if (err) { |
| dev_err(&client->dev, "pdata->power_init power init failed"); |
| goto unreg_inputdev; |
| } |
| } else { |
| |
| err = fts_power_init(data, true); |
| |
| if (err) { |
| dev_err(&client->dev, "fts_power_init power init failed"); |
| goto unreg_inputdev; |
| } |
| } |
| |
| #ifdef GESTURE_CONTROL |
| |
| fts_ts_create_ctp_node(); |
| |
| #endif |
| |
| if (pdata->power_on) { |
| err = pdata->power_on(true); |
| if (err) { |
| dev_err(&client->dev, "power on failed"); |
| goto pwr_deinit; |
| } |
| } else { |
| |
| err = fts_power_on(data, true); |
| |
| if (err) { |
| dev_err(&client->dev, "power on failed"); |
| goto pwr_deinit; |
| } |
| } |
| |
| |
| #ifdef MSM_NEW_VER |
| err = fts_ts_pinctrl_init(data); |
| if (!err && data->ts_pinctrl) { |
| /* |
| * Pinctrl handle is optional. If pinctrl handle is found |
| * let pins to be configured in active state. If not |
| * found continue further without error. |
| */ |
| err = pinctrl_select_state(data->ts_pinctrl, |
| data->pinctrl_state_active); |
| if (err < 0) { |
| dev_err(&client->dev, |
| "failed to select pin to active state"); |
| } |
| } |
| #endif |
| |
| |
| err = fts_gpio_configure(data, true); |
| if (err < 0) { |
| dev_err(&client->dev, |
| "Failed to configure the gpios\n"); |
| goto err_gpio_req; |
| } |
| |
| /* make sure CTP already finish startup process */ |
| msleep(data->pdata->soft_rst_dly); |
| |
| |
| /* check the controller id */ |
| reg_addr = FTS_REG_ID; |
| err = fts_i2c_read(client, ®_addr, 1, ®_value, 1); |
| if (err < 0) { |
| dev_err(&client->dev, "version read failed"); |
| } |
| |
| dev_info(&client->dev, "Device ID = 0x%x\n", reg_value); |
| |
| if ((pdata->family_id != reg_value) && (!pdata->ignore_id_check)) { |
| dev_err(&client->dev, "%s:Unsupported controller\n", __func__); |
| goto free_gpio; |
| } |
| |
| |
| data->family_id = pdata->family_id; |
| |
| fts_i2c_client = client; |
| fts_input_dev = input_dev; |
| |
| fts_get_upgrade_array(); |
| |
| err = request_threaded_irq(client->irq, NULL, fts_ts_interrupt, |
| pdata->irqflags | IRQF_ONESHOT | IRQF_TRIGGER_FALLING, |
| client->dev.driver->name, data); |
| if (err) { |
| dev_err(&client->dev, "request irq failed\n"); |
| goto free_gpio; |
| } |
| |
| disable_irq(client->irq); |
| |
| |
| |
| #ifdef CONFIG_TOUCHSCREEN_FTS_PSENSOR |
| if (fts_psensor_support_enabled() && data->pdata->psensor_support) { |
| device_init_wakeup(&client->dev, 1); |
| psensor_pdata = devm_kzalloc(&client->dev, |
| sizeof(struct fts_psensor_platform_data), |
| GFP_KERNEL); |
| if (!psensor_pdata) { |
| dev_err(&client->dev, "Failed to allocate memory\n"); |
| goto irq_free; |
| } |
| data->psensor_pdata = psensor_pdata; |
| |
| psensor_input_dev = input_allocate_device(); |
| if (!psensor_input_dev) { |
| dev_err(&data->client->dev, |
| "Failed to allocate device\n"); |
| goto free_psensor_pdata; |
| } |
| |
| __set_bit(EV_ABS, psensor_input_dev->evbit); |
| input_set_abs_params(psensor_input_dev, |
| ABS_DISTANCE, 0, 1, 0, 0); |
| psensor_input_dev->name = "proximity"; |
| psensor_input_dev->id.bustype = BUS_I2C; |
| psensor_input_dev->dev.parent = &data->client->dev; |
| data->psensor_pdata->input_psensor_dev = psensor_input_dev; |
| |
| err = input_register_device(psensor_input_dev); |
| if (err) { |
| dev_err(&data->client->dev, |
| "Unable to register device, err=%d\n", err); |
| goto free_psensor_input_dev; |
| } |
| |
| psensor_pdata->ps_cdev = sensors_proximity_cdev; |
| psensor_pdata->ps_cdev.sensors_enable = |
| fts_psensor_enable_set; |
| psensor_pdata->data = data; |
| |
| err = sensors_classdev_register(&client->dev, |
| &psensor_pdata->ps_cdev); |
| if (err) |
| goto unregister_psensor_input_device; |
| } |
| #endif |
| |
| data->dir = debugfs_create_dir(FTS_DEBUG_DIR_NAME, NULL); |
| if (data->dir == NULL || IS_ERR(data->dir)) { |
| pr_err("debugfs_create_dir failed(%ld)\n", PTR_ERR(data->dir)); |
| err = PTR_ERR(data->dir); |
| } |
| |
| temp = debugfs_create_file("addr", S_IRUSR | S_IWUSR, data->dir, data, |
| &debug_addr_fops); |
| if (temp == NULL || IS_ERR(temp)) { |
| pr_err("debugfs_create_file failed: rc=%ld\n", PTR_ERR(temp)); |
| err = PTR_ERR(temp); |
| goto free_debug_dir; |
| } |
| |
| temp = debugfs_create_file("data", S_IRUSR | S_IWUSR, data->dir, data, |
| &debug_data_fops); |
| if (temp == NULL || IS_ERR(temp)) { |
| pr_err("debugfs_create_file failed: rc=%ld\n", PTR_ERR(temp)); |
| err = PTR_ERR(temp); |
| goto free_debug_dir; |
| } |
| |
| temp = debugfs_create_file("suspend", S_IRUSR | S_IWUSR, data->dir, |
| data, &debug_suspend_fops); |
| if (temp == NULL || IS_ERR(temp)) { |
| pr_err("debugfs_create_file failed: rc=%ld\n", PTR_ERR(temp)); |
| err = PTR_ERR(temp); |
| goto free_debug_dir; |
| } |
| |
| temp = debugfs_create_file("dump_info", S_IRUSR | S_IWUSR, data->dir, |
| data, &debug_dump_info_fops); |
| if (temp == NULL || IS_ERR(temp)) { |
| pr_err("debugfs_create_file failed: rc=%ld\n", PTR_ERR(temp)); |
| err = PTR_ERR(temp); |
| goto free_debug_dir; |
| } |
| |
| data->ts_info = devm_kzalloc(&client->dev, FTS_INFO_MAX_LEN, GFP_KERNEL); |
| if (!data->ts_info) { |
| dev_err(&client->dev, "Not enough memory\n"); |
| goto free_debug_dir; |
| } |
| |
| /*get some register information */ |
| reg_addr = FTS_REG_POINT_RATE; |
| fts_i2c_read(client, ®_addr, 1, ®_value, 1); |
| if (err < 0) |
| dev_err(&client->dev, "report rate read failed"); |
| |
| dev_info(&client->dev, "report rate = %dHz\n", reg_value * 10); |
| |
| reg_addr = FTS_REG_THGROUP; |
| err = fts_i2c_read(client, ®_addr, 1, ®_value, 1); |
| if (err < 0) |
| dev_err(&client->dev, "threshold read failed"); |
| |
| dev_dbg(&client->dev, "touch threshold = %d\n", reg_value * 4); |
| |
| |
| #ifdef FTS_APK_DEBUG |
| fts_create_apk_debug_channel(client); |
| #endif |
| |
| #ifdef FTS_SYSFS_DEBUG |
| fts_create_sysfs(client); |
| #endif |
| |
| |
| #ifdef FTS_CTL_IIC |
| if (fts_rw_iic_drv_init(client) < 0) |
| { |
| dev_err(&client->dev, "%s:[FTS] create fts control iic driver failed\n", __func__); |
| } |
| #endif |
| |
| |
| #if FTS_GESTRUE_EN |
| fts_Gesture_init(input_dev); |
| // if (fts_updateinfo_curr.CHIP_ID != 0x54 && fts_updateinfo_curr.CHIP_ID != 0x58 && fts_updateinfo_curr.CHIP_ID != 0x86 && fts_updateinfo_curr.CHIP_ID != 0x87) |
| // { |
| // init_para(720,1280,0,0,0); |
| // } |
| #endif |
| |
| |
| #ifdef FTS_AUTO_UPGRADE |
| printk("********************Enter CTP Auto Upgrade********************\n"); |
| fts_ctpm_auto_upgrade(client); |
| |
| |
| #endif |
| |
| |
| fts_update_fw_ver(data); |
| fts_update_fw_vendor_id(data); |
| |
| printk("%s, Firmware version = 0x%02x.%d.%d, fw_vendor_id=0x%02x\n", __func__, |
| data->fw_ver[0], data->fw_ver[1], data->fw_ver[2], data->fw_vendor_id); |
| fts_fw_version = data->fw_ver[0]; |
| memset(fw_version, 0, sizeof(fw_version)); |
| sprintf(fw_version, "[FW]0x%x, [IC]FT8716", data->fw_ver[0]); |
| //init_tp_fm_info(0, fw_version, "FocalTech"); |
| FTS_STORE_TS_INFO(data->ts_info, data->family_id, data->pdata->name, |
| data->pdata->num_max_touches, data->pdata->group_id, |
| data->pdata->fw_vkey_support ? "yes" : "no", |
| data->pdata->fw_name, data->fw_ver[0], |
| data->fw_ver[1], data->fw_ver[2]); |
| |
| |
| #if defined(CONFIG_FB) |
| data->fb_notif.notifier_call = fb_notifier_callback; |
| |
| err = fb_register_client(&data->fb_notif); |
| |
| if (err) |
| dev_err(&client->dev, "Unable to register fb_notifier: %d\n", 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 |
| |
| enable_irq(client->irq); |
| |
| #if GTP_ESD_PROTECT |
| fts_esd_protection_init(); |
| #endif |
| |
| return 0; |
| |
| free_debug_dir: |
| debugfs_remove_recursive(data->dir); |
| |
| #ifdef CONFIG_TOUCHSCREEN_FTS_PSENSOR |
| unregister_psensor_input_device: |
| if (fts_psensor_support_enabled() && data->pdata->psensor_support) |
| input_unregister_device(data->psensor_pdata->input_psensor_dev); |
| free_psensor_input_dev: |
| if (fts_psensor_support_enabled() && data->pdata->psensor_support) |
| input_free_device(data->psensor_pdata->input_psensor_dev); |
| free_psensor_pdata: |
| if (fts_psensor_support_enabled() && data->pdata->psensor_support) { |
| devm_kfree(&client->dev, psensor_pdata); |
| data->psensor_pdata = NULL; |
| } |
| irq_free: |
| if ((fts_psensor_support_enabled() && |
| data->pdata->psensor_support)) |
| device_init_wakeup(&client->dev, 0); |
| free_irq(client->irq, data); |
| #endif |
| |
| free_gpio: |
| if (gpio_is_valid(pdata->power_gpio)) |
| gpio_free(pdata->power_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); |
| |
| err_gpio_req: |
| #ifdef MSM_NEW_VER |
| if (data->ts_pinctrl) { |
| if (IS_ERR_OR_NULL(data->pinctrl_state_release)) { |
| devm_pinctrl_put(data->ts_pinctrl); |
| data->ts_pinctrl = NULL; |
| } else { |
| err = pinctrl_select_state(data->ts_pinctrl, |
| data->pinctrl_state_release); |
| if (err) |
| pr_err("failed to select relase pinctrl state\n"); |
| } |
| } |
| #endif |
| |
| if (pdata->power_on) |
| pdata->power_on(false); |
| else |
| fts_power_on(data, false); |
| pwr_deinit: |
| if (pdata->power_init) |
| pdata->power_init(false); |
| else |
| fts_power_init(data, false); |
| unreg_inputdev: |
| input_unregister_device(input_dev); |
| input_dev = NULL; |
| free_inputdev: |
| input_free_device(input_dev); |
| 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); |
| |
| cancel_work_sync(&data->touch_event_work); |
| |
| debugfs_remove_recursive(data->dir); |
| |
| #ifdef CONFIG_TOUCHSCREEN_FTS_PSENSOR |
| if (fts_psensor_support_enabled() && data->pdata->psensor_support) { |
| |
| device_init_wakeup(&client->dev, 0); |
| sensors_classdev_unregister(&data->psensor_pdata->ps_cdev); |
| input_unregister_device(data->psensor_pdata->input_psensor_dev); |
| devm_kfree(&client->dev, data->psensor_pdata); |
| data->psensor_pdata = NULL; |
| } |
| #endif |
| |
| #ifdef FTS_APK_DEBUG |
| fts_release_apk_debug_channel(); |
| #endif |
| |
| #ifdef FTS_SYSFS_DEBUG |
| fts_remove_sysfs(fts_i2c_client); |
| #endif |
| |
| |
| #ifdef FTS_CTL_IIC |
| fts_rw_iic_drv_exit(); |
| #endif |
| |
| |
| #if defined(CONFIG_FB) |
| if (fb_unregister_client(&data->fb_notif)) |
| dev_err(&client->dev, "Error occurred while unregistering fb_notifier.\n"); |
| #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); |
| |
| if (data->pdata->power_on) |
| data->pdata->power_on(false); |
| else |
| fts_power_on(data, false); |
| |
| if (data->pdata->power_init) |
| data->pdata->power_init(false); |
| else |
| fts_power_init(data, false); |
| |
| input_unregister_device(data->input_dev); |
| |
| return 0; |
| } |
| |
| static const struct i2c_device_id fts_ts_id[] = { |
| {"fts_ts", 0}, |
| {}, |
| }; |
| |
| MODULE_DEVICE_TABLE(i2c, fts_ts_id); |
| |
| #ifdef CONFIG_OF |
| static struct of_device_id fts_match_table[] = { |
| { .compatible = "focaltech,fts",}, |
| { }, |
| }; |
| #else |
| #define fts_match_table NULL |
| #endif |
| |
| static struct i2c_driver fts_ts_driver = { |
| .probe = fts_ts_probe, |
| .remove = fts_ts_remove, |
| .driver = { |
| .name = "fts_ts", |
| .owner = THIS_MODULE, |
| .of_match_table = fts_match_table, |
| #ifdef CONFIG_PM |
| .pm = &fts_ts_pm_ops, |
| #endif |
| }, |
| .id_table = fts_ts_id, |
| }; |
| |
| /******************************************************************************* |
| * Name: fts_ts_init |
| * Brief: |
| * Input: |
| * Output: |
| * Return: |
| *******************************************************************************/ |
| static int __init fts_ts_init(void) |
| { |
| return i2c_add_driver(&fts_ts_driver); |
| } |
| |
| /******************************************************************************* |
| * 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_DESCRIPTION("FocalTech fts TouchScreen driver"); |
| MODULE_LICENSE("GPL v2"); |