From 0530539198f4b2be7128c457a35306d28839b766 Mon Sep 17 00:00:00 2001 From: Bingzhe Cai Date: Fri, 25 Oct 2013 00:28:21 +0800 Subject: [PATCH 01/15] input: touchscreen: ft5x06: remove unbalanced touch event Touchscreen driver may generate unbalanced input events when enter suspend state, these unbalanced event will make input system always wait for symmetrical input event and can not handle input event properly. PRESSURE event is removed to keep input events symmetrical. This patch is propagated from msm-3.18 kernel 'commit 15725c248511 ("input: touchscreen: ft5x06: remove unbalanced touch event")' CRs-Fixed: 566134 Change-Id: I3f6fda3fb5d0a717ae943a9113be89623c96ce61 Signed-off-by: Bingzhe Cai --- drivers/input/touchscreen/ft5x06_ts.c | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) diff --git a/drivers/input/touchscreen/ft5x06_ts.c b/drivers/input/touchscreen/ft5x06_ts.c index 4f5b5b4ecd7f..9dd358515210 100644 --- a/drivers/input/touchscreen/ft5x06_ts.c +++ b/drivers/input/touchscreen/ft5x06_ts.c @@ -288,7 +288,7 @@ static irqreturn_t ft5x06_ts_interrupt(int irq, void *dev_id) struct ft5x06_ts_data *data = dev_id; struct input_dev *ip_dev; int rc, i; - u32 id, x, y, pressure, status, num_touches; + u32 id, x, y, status, num_touches; u8 reg = 0x00, *buf; bool update_input = false; @@ -329,11 +329,9 @@ static irqreturn_t ft5x06_ts_interrupt(int irq, void *dev_id) input_mt_slot(ip_dev, id); if (status == FT_TOUCH_DOWN || status == FT_TOUCH_CONTACT) { - pressure = FT_PRESS; input_mt_report_slot_state(ip_dev, MT_TOOL_FINGER, 1); input_report_abs(ip_dev, ABS_MT_POSITION_X, x); input_report_abs(ip_dev, ABS_MT_POSITION_Y, y); - input_report_abs(ip_dev, ABS_MT_PRESSURE, pressure); } else { input_mt_report_slot_state(ip_dev, MT_TOOL_FINGER, 0); } @@ -479,7 +477,7 @@ static int ft5x06_ts_suspend(struct device *dev) input_mt_slot(data->input_dev, i); input_mt_report_slot_state(data->input_dev, MT_TOOL_FINGER, 0); } - input_report_key(data->input_dev, BTN_TOUCH, 0); + input_mt_report_pointer_emulation(data->input_dev, false); input_sync(data->input_dev); if (gpio_is_valid(data->pdata->reset_gpio)) { @@ -1365,7 +1363,6 @@ static int ft5x06_ts_probe(struct i2c_client *client, 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_PRESSURE, 0, FT_PRESS, 0, 0); err = input_register_device(input_dev); if (err) { From f51bd56922a88c1b119ce815abce880674d5c27f Mon Sep 17 00:00:00 2001 From: Bingzhe Cai Date: Mon, 28 Oct 2013 19:30:27 +0800 Subject: [PATCH 02/15] input: touchscreen: ft5x06: add FT5336 firmware update support FT5336 use new firmware bootloader, current driver cannot update its firmware. Driver needs update to support this new firmware bootloader. This patch is propagated from 3.18 kernel 'commit 740d7b108461 ("input: touchscreen: ft5x06: add FT5336 firmware update support")' CRs-Fixed: 1046961 Change-Id: I5c210097a6b3daedce52c4dda806c698cda63968 Signed-off-by: Bingzhe Cai --- drivers/input/touchscreen/ft5x06_ts.c | 132 +++++++++++++++++++++----- 1 file changed, 109 insertions(+), 23 deletions(-) diff --git a/drivers/input/touchscreen/ft5x06_ts.c b/drivers/input/touchscreen/ft5x06_ts.c index 9dd358515210..1ee983e62509 100644 --- a/drivers/input/touchscreen/ft5x06_ts.c +++ b/drivers/input/touchscreen/ft5x06_ts.c @@ -131,8 +131,9 @@ #define FT_FW_PKT_DLY_MS 20 #define FT_FW_LAST_PKT 0x6ffa #define FT_EARSE_DLY_MS 100 +#define FT_55_AA_DLY_NS 5000 -#define FT_UPGRADE_LOOP 10 +#define FT_UPGRADE_LOOP 30 #define FT_CAL_START 0x04 #define FT_CAL_FIN 0x00 #define FT_CAL_STORE 0x05 @@ -142,6 +143,30 @@ #define FT_INFO_MAX_LEN 512 +#define FT_BLOADER_SIZE_OFF 12 +#define FT_BLOADER_NEW_SIZE 30 +#define FT_DATA_LEN_OFF_OLD_FW 8 +#define FT_DATA_LEN_OFF_NEW_FW 14 +#define FT_FINISHING_PKT_LEN_OLD_FW 6 +#define FT_FINISHING_PKT_LEN_NEW_FW 12 +#define FT_MAGIC_BLOADER_Z7 0x7bfa +#define FT_MAGIC_BLOADER_LZ4 0x6ffa +#define FT_MAGIC_BLOADER_GZF_30 0x7ff4 +#define FT_MAGIC_BLOADER_GZF 0x7bf4 + +enum { + FT_BLOADER_VERSION_LZ4 = 0, + FT_BLOADER_VERSION_Z7 = 1, + FT_BLOADER_VERSION_GZF = 2, +}; + +enum { + FT_FT5336_FAMILY_ID_0x11 = 0x11, + FT_FT5336_FAMILY_ID_0x12 = 0x12, + FT_FT5336_FAMILY_ID_0x13 = 0x13, + FT_FT5336_FAMILY_ID_0x14 = 0x14, +}; + #define FT_STORE_TS_INFO(buf, id, name, max_tch, group_id, fw_vkey_support, \ fw_name, fw_maj, fw_min, fw_sub_min) \ snprintf(buf, FT_INFO_MAX_LEN, \ @@ -645,11 +670,20 @@ static int ft5x06_fw_upgrade_start(struct i2c_client *client, u8 reset_reg; u8 w_buf[FT_MAX_WR_BUF] = {0}, r_buf[FT_MAX_RD_BUF] = {0}; u8 pkt_buf[FT_FW_PKT_LEN + FT_FW_PKT_META_LEN]; - int rc, i, j, temp; + int i, j, temp; u32 pkt_num, pkt_len; + u8 is_5336_new_bootloader = false; + u8 is_5336_fwsize_30 = false; u8 fw_ecc; + /* determine firmware size */ + if (*(data + data_len - FT_BLOADER_SIZE_OFF) == FT_BLOADER_NEW_SIZE) + is_5336_fwsize_30 = true; + else + is_5336_fwsize_30 = false; + for (i = 0, j = 0; i < FT_UPGRADE_LOOP; i++) { + msleep(FT_EARSE_DLY_MS); /* reset - write 0xaa and 0x55 to reset register */ if (ts_data->family_id == FT6X06_ID) reset_reg = FT_RST_CMD_REG2; @@ -660,16 +694,17 @@ static int ft5x06_fw_upgrade_start(struct i2c_client *client, msleep(info.delay_aa); ft5x0x_write_reg(client, reset_reg, FT_UPGRADE_55); - msleep(info.delay_55); + if (i <= (FT_UPGRADE_LOOP / 2)) + msleep(info.delay_55 + i * 3); + else + msleep(info.delay_55 - (i - (FT_UPGRADE_LOOP / 2)) * 2); /* Enter upgrade mode */ w_buf[0] = FT_UPGRADE_55; - w_buf[1] = FT_UPGRADE_AA; - do { - j++; - rc = ft5x06_i2c_write(client, w_buf, 2); - msleep(FT_RETRY_DLY); - } while (rc <= 0 && j < FT_MAX_TRIES); + ft5x06_i2c_write(client, w_buf, 1); + usleep(FT_55_AA_DLY_NS); + w_buf[0] = FT_UPGRADE_AA; + ft5x06_i2c_write(client, w_buf, 1); /* check READ_ID */ msleep(info.delay_readid); @@ -692,17 +727,40 @@ static int ft5x06_fw_upgrade_start(struct i2c_client *client, return -EIO; } + w_buf[0] = 0xcd; + ft5x06_i2c_read(client, w_buf, 1, r_buf, 1); + + if (r_buf[0] <= 4) + is_5336_new_bootloader = FT_BLOADER_VERSION_LZ4; + else if (r_buf[0] == 7) + is_5336_new_bootloader = FT_BLOADER_VERSION_Z7; + else if (r_buf[0] >= 0x0f && + ((ts_data->family_id == FT_FT5336_FAMILY_ID_0x11) || + (ts_data->family_id == FT_FT5336_FAMILY_ID_0x12) || + (ts_data->family_id == FT_FT5336_FAMILY_ID_0x13) || + (ts_data->family_id == FT_FT5336_FAMILY_ID_0x14))) + is_5336_new_bootloader = FT_BLOADER_VERSION_GZF; + else + is_5336_new_bootloader = FT_BLOADER_VERSION_LZ4; + /* erase app and panel paramenter area */ w_buf[0] = FT_ERASE_APP_REG; ft5x06_i2c_write(client, w_buf, 1); msleep(info.delay_erase_flash); - w_buf[0] = FT_ERASE_PANEL_REG; - ft5x06_i2c_write(client, w_buf, 1); + if (is_5336_fwsize_30) { + w_buf[0] = FT_ERASE_PANEL_REG; + ft5x06_i2c_write(client, w_buf, 1); + } msleep(FT_EARSE_DLY_MS); /* program firmware */ - data_len = data_len - 8; + if (is_5336_new_bootloader == FT_BLOADER_VERSION_LZ4 + || is_5336_new_bootloader == FT_BLOADER_VERSION_Z7) + data_len = data_len - FT_DATA_LEN_OFF_OLD_FW; + else + data_len = data_len - FT_DATA_LEN_OFF_NEW_FW; + pkt_num = (data_len) / FT_FW_PKT_LEN; pkt_len = FT_FW_PKT_LEN; pkt_buf[0] = FT_FW_START_REG; @@ -745,17 +803,45 @@ static int ft5x06_fw_upgrade_start(struct i2c_client *client, } /* send the finishing packet */ - for (i = 0; i < 6; i++) { - temp = FT_FW_LAST_PKT + i; - pkt_buf[2] = (u8) (temp >> 8); - pkt_buf[3] = (u8) temp; - temp = 1; - pkt_buf[4] = (u8) (temp >> 8); - pkt_buf[5] = (u8) temp; - pkt_buf[6] = data[data_len + i]; - fw_ecc ^= pkt_buf[6]; - ft5x06_i2c_write(client, pkt_buf, temp + FT_FW_PKT_META_LEN); - msleep(FT_FW_PKT_DLY_MS); + if (is_5336_new_bootloader == FT_BLOADER_VERSION_LZ4 || + is_5336_new_bootloader == FT_BLOADER_VERSION_Z7) { + for (i = 0; i < FT_FINISHING_PKT_LEN_OLD_FW; i++) { + if (is_5336_new_bootloader == FT_BLOADER_VERSION_Z7) + temp = FT_MAGIC_BLOADER_Z7 + i; + else if (is_5336_new_bootloader == + FT_BLOADER_VERSION_LZ4) + temp = FT_MAGIC_BLOADER_LZ4 + i; + pkt_buf[2] = (u8)(temp >> 8); + pkt_buf[3] = (u8)temp; + temp = 1; + pkt_buf[4] = (u8)(temp >> 8); + pkt_buf[5] = (u8)temp; + pkt_buf[6] = data[data_len + i]; + fw_ecc ^= pkt_buf[6]; + + ft5x06_i2c_write(client, + pkt_buf, temp + FT_FW_PKT_META_LEN); + msleep(FT_FW_PKT_DLY_MS); + } + } else if (is_5336_new_bootloader == FT_BLOADER_VERSION_GZF) { + for (i = 0; i < FT_FINISHING_PKT_LEN_NEW_FW; i++) { + if (is_5336_fwsize_30) + temp = FT_MAGIC_BLOADER_GZF_30 + i; + else + temp = FT_MAGIC_BLOADER_GZF + i; + pkt_buf[2] = (u8)(temp >> 8); + pkt_buf[3] = (u8)temp; + temp = 1; + pkt_buf[4] = (u8)(temp >> 8); + pkt_buf[5] = (u8)temp; + pkt_buf[6] = data[data_len + i]; + fw_ecc ^= pkt_buf[6]; + + ft5x06_i2c_write(client, + pkt_buf, temp + FT_FW_PKT_META_LEN); + msleep(FT_FW_PKT_DLY_MS); + + } } /* verify checksum */ From 6b5ef7a5537f83fc38caebca0b69c778669cf603 Mon Sep 17 00:00:00 2001 From: Bingzhe Cai Date: Mon, 4 Nov 2013 15:03:53 +0800 Subject: [PATCH 03/15] input: touchscreen: ft5x06: fix firmware force update issue Force firmware update cannot work when touchscreen controller cannot report correct family ID. If touchscreen is does not work in correct state and it reports wrong family ID, it cannot be recovered by force update, this change use family ID from device tree instead of read from controller register to fix this issue. This patch is propagated from 3.18 kernel 'commit a85789530620 ("input: touchscreen: ft5x06: fix firmware force update issue")' Change-Id: I85d24bfc68a7777053c15f28a84027260c68cebd Signed-off-by: Bingzhe Cai --- drivers/input/touchscreen/ft5x06_ts.c | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/drivers/input/touchscreen/ft5x06_ts.c b/drivers/input/touchscreen/ft5x06_ts.c index 1ee983e62509..0b435886897e 100644 --- a/drivers/input/touchscreen/ft5x06_ts.c +++ b/drivers/input/touchscreen/ft5x06_ts.c @@ -717,7 +717,9 @@ static int ft5x06_fw_upgrade_start(struct i2c_client *client, if (r_buf[0] != info.upgrade_id_1 || r_buf[1] != info.upgrade_id_2) { - dev_err(&client->dev, "Upgrade ID mismatch(%d)\n", i); + dev_err(&client->dev, "Upgrade ID mismatch(%d), IC=0x%x 0x%x, info=0x%x 0x%x\n", + i, r_buf[0], r_buf[1], + info.upgrade_id_1, info.upgrade_id_2); } else break; } @@ -743,6 +745,10 @@ static int ft5x06_fw_upgrade_start(struct i2c_client *client, else is_5336_new_bootloader = FT_BLOADER_VERSION_LZ4; + dev_dbg(&client->dev, "bootloader type=%d, r_buf=0x%x, family_id=0x%x\n", + is_5336_new_bootloader, r_buf[0], ts_data->family_id); + /* is_5336_new_bootloader = FT_BLOADER_VERSION_GZF; */ + /* erase app and panel paramenter area */ w_buf[0] = FT_ERASE_APP_REG; ft5x06_i2c_write(client, w_buf, 1); @@ -1533,7 +1539,7 @@ static int ft5x06_ts_probe(struct i2c_client *client, goto free_reset_gpio; } - data->family_id = reg_value; + data->family_id = pdata->family_id; err = request_threaded_irq(client->irq, NULL, ft5x06_ts_interrupt, pdata->irqflags, From 13954554dc40a65d3ee7c8155193a1bfd5ef7b6d Mon Sep 17 00:00:00 2001 From: Shantanu Jain Date: Tue, 24 Dec 2013 16:59:41 +0530 Subject: [PATCH 04/15] input: touchscreen: request threaded-only IRQs with IRQF_ONESHOT Threaded IRQs without primary handler need to be requested with IRQF_ONESHOT, otherwise the request will fail. This patch adds the IRQF_ONESHOT to the focaltech touchscreen driver where it is missing. This patch is propagated from 3.18 kernel 'commit 4e8b58e99bf7 ("input: touchscreen: request threaded-only IRQs with IRQF_ONESHOT")' Change-Id: I429f48126bd5d28c6dbf6ba846175d5e643dd436 Signed-off-by: Shantanu Jain --- drivers/input/touchscreen/ft5x06_ts.c | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/drivers/input/touchscreen/ft5x06_ts.c b/drivers/input/touchscreen/ft5x06_ts.c index 0b435886897e..1719ae2bd5a7 100644 --- a/drivers/input/touchscreen/ft5x06_ts.c +++ b/drivers/input/touchscreen/ft5x06_ts.c @@ -1542,8 +1542,9 @@ static int ft5x06_ts_probe(struct i2c_client *client, data->family_id = pdata->family_id; err = request_threaded_irq(client->irq, NULL, - ft5x06_ts_interrupt, pdata->irqflags, - client->dev.driver->name, data); + ft5x06_ts_interrupt, + pdata->irqflags | IRQF_ONESHOT, + client->dev.driver->name, data); if (err) { dev_err(&client->dev, "request irq failed\n"); goto free_reset_gpio; From af3203fa14b9c7fac5fe5d93bc10d84ceb693438 Mon Sep 17 00:00:00 2001 From: Chun Zhang Date: Fri, 31 Jan 2014 13:41:37 -0800 Subject: [PATCH 05/15] input: ft5x06: add support when CONFIG_PM is not defined When CONFIG_PM is not defined, there are some compilation errors, because it introduces undeclared touch suspend and resume function. This patch is propagated from 3.18 kernel 'commit a144428c6e02 ("input: ft5x06: add support when CONFIG_PM is not defined")' Change-Id: I869bd41270ea240c671e5478126a61d4ff2088dc Signed-off-by: Chun Zhang --- drivers/input/touchscreen/ft5x06_ts.c | 30 +++++++++++++++++++-------- 1 file changed, 21 insertions(+), 9 deletions(-) diff --git a/drivers/input/touchscreen/ft5x06_ts.c b/drivers/input/touchscreen/ft5x06_ts.c index 1719ae2bd5a7..4d046f690a57 100644 --- a/drivers/input/touchscreen/ft5x06_ts.c +++ b/drivers/input/touchscreen/ft5x06_ts.c @@ -578,6 +578,26 @@ static int ft5x06_ts_resume(struct device *dev) return 0; } +static const struct dev_pm_ops ft5x06_ts_pm_ops = { +#if (!defined(CONFIG_FB) && !defined(CONFIG_HAS_EARLYSUSPEND)) + .suspend = ft5x06_ts_suspend, + .resume = ft5x06_ts_resume, +#endif +}; + +#else +static int ft5x06_ts_suspend(struct device *dev) +{ + return 0; +} + +static int ft5x06_ts_resume(struct device *dev) +{ + return 0; +} + +#endif + #if defined(CONFIG_FB) static int fb_notifier_callback(struct notifier_block *self, unsigned long event, void *data) @@ -618,14 +638,6 @@ static void ft5x06_ts_late_resume(struct early_suspend *handler) } #endif -static const struct dev_pm_ops ft5x06_ts_pm_ops = { -#if (!defined(CONFIG_FB) && !defined(CONFIG_HAS_EARLYSUSPEND)) - .suspend = ft5x06_ts_suspend, - .resume = ft5x06_ts_resume, -#endif -}; -#endif - static int ft5x06_auto_cal(struct i2c_client *client) { struct ft5x06_ts_data *data = i2c_get_clientdata(client); @@ -885,7 +897,7 @@ static int ft5x06_fw_upgrade(struct device *dev, bool force) } if (fw->size < FT_FW_MIN_SIZE || fw->size > FT_FW_MAX_SIZE) { - dev_err(dev, "Invalid firmware size (%d)\n", fw->size); + dev_err(dev, "Invalid firmware size (%zu)\n", fw->size); rc = -EIO; goto rel_fw; } From a80c7960f1e098e5de43eae1a420a5576cd7a73a Mon Sep 17 00:00:00 2001 From: Sudhakar Manapati Date: Wed, 12 Aug 2015 12:32:27 +0530 Subject: [PATCH 06/15] input: ft5x06_ts: remove deprecated apis in 3.18 kernel This patch removes the usage of deprecated apis like usleep(), __devexit_p, etc. This is a propagated patch from 3.18 kernel 'commit 20482b216b8a ("input: ft5x06_ts: remove deprecated apis in 3.18 kernel")' Change-Id: I77cedabdb234a643f96427f051038faad31bfdcf Signed-off-by: Sudhakar Manapati --- drivers/input/touchscreen/ft5x06_ts.c | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/drivers/input/touchscreen/ft5x06_ts.c b/drivers/input/touchscreen/ft5x06_ts.c index 4d046f690a57..c0e756fc59f9 100644 --- a/drivers/input/touchscreen/ft5x06_ts.c +++ b/drivers/input/touchscreen/ft5x06_ts.c @@ -405,7 +405,11 @@ power_off: if (rc) { dev_err(&data->client->dev, "Regulator vcc_i2c disable failed rc=%d\n", rc); - regulator_enable(data->vdd); + rc = regulator_enable(data->vdd); + if (rc) { + dev_err(&data->client->dev, + "Regulator vdd enable failed rc=%d\n", rc); + } } return rc; @@ -714,7 +718,7 @@ static int ft5x06_fw_upgrade_start(struct i2c_client *client, /* Enter upgrade mode */ w_buf[0] = FT_UPGRADE_55; ft5x06_i2c_write(client, w_buf, 1); - usleep(FT_55_AA_DLY_NS); + usleep_range(FT_55_AA_DLY_NS, FT_55_AA_DLY_NS + 1); w_buf[0] = FT_UPGRADE_AA; ft5x06_i2c_write(client, w_buf, 1); @@ -1462,7 +1466,7 @@ static int ft5x06_ts_probe(struct i2c_client *client, __set_bit(BTN_TOUCH, input_dev->keybit); __set_bit(INPUT_PROP_DIRECT, input_dev->propbit); - input_mt_init_slots(input_dev, pdata->num_max_touches); + input_mt_init_slots(input_dev, pdata->num_max_touches, 0); 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, @@ -1755,7 +1759,7 @@ static const struct of_device_id ft5x06_match_table[] = { static struct i2c_driver ft5x06_ts_driver = { .probe = ft5x06_ts_probe, - .remove = __devexit_p(ft5x06_ts_remove), + .remove = ft5x06_ts_remove, .driver = { .name = "ft5x06_ts", .owner = THIS_MODULE, From 19cda0c39a6eb40af171c58bc42ff9beea3d0d0a Mon Sep 17 00:00:00 2001 From: Mao Li Date: Thu, 15 May 2014 17:36:00 +0800 Subject: [PATCH 07/15] input: ft5x06_ts: add support for firmware update for CTP FT6X36 The firmware image format is changed in CTP FT6436. Also vendor id and version id are read from fw image from a different offset as compared to previous controllers. The driver is updated to support the new image format. This patch is propagated from msm-3.18 kernel 'commit 6146c68f81b4 ("input: ft5x06_ts: add support for firmware update for CTP FT6X36")' Change-Id: I465fdfa73e56e8fc4bbfdde980c558e4715d6d08 Signed-off-by: Mao Li Signed-off-by: Sudhakar Manapati --- drivers/input/touchscreen/ft5x06_ts.c | 32 ++++++++++++++++++++++----- include/linux/input/ft5x06_ts.h | 1 + 2 files changed, 27 insertions(+), 6 deletions(-) diff --git a/drivers/input/touchscreen/ft5x06_ts.c b/drivers/input/touchscreen/ft5x06_ts.c index c0e756fc59f9..421021ab0a4e 100644 --- a/drivers/input/touchscreen/ft5x06_ts.c +++ b/drivers/input/touchscreen/ft5x06_ts.c @@ -104,6 +104,7 @@ #define FT5316_ID 0x0A #define FT5306I_ID 0x55 #define FT6X06_ID 0x06 +#define FT6X36_ID 0x36 #define FT_UPGRADE_AA 0xAA #define FT_UPGRADE_55 0x55 @@ -116,10 +117,22 @@ #define FT_FW_FILE_MIN_VER(x) 0 #define FT_FW_FILE_SUB_MIN_VER(x) 0 -#define FT_FW_CHECK(x) \ +#define FT_FW_FILE_MAJ_VER_FT6X36(x) ((x)->data[0x10a]) +#define FT_FW_FILE_VENDOR_ID_FT6X36(x) ((x)->data[0x108]) + +/** +* Application data verification will be run before upgrade flow. +* Firmware image stores some flags with negative and positive value +* in corresponding addresses, we need pick them out do some check to +* make sure the application data is valid. +*/ +#define FT_FW_CHECK(x, ts_data) \ + (ts_data->family_id == FT6X36_ID ? \ + (((x)->data[0x104] ^ (x)->data[0x105]) == 0xFF \ + && ((x)->data[0x106] ^ (x)->data[0x107]) == 0xFF) : \ (((x)->data[(x)->size - 8] ^ (x)->data[(x)->size - 6]) == 0xFF \ - && (((x)->data[(x)->size - 7] ^ (x)->data[(x)->size - 5]) == 0xFF \ - && (((x)->data[(x)->size - 3] ^ (x)->data[(x)->size - 4]) == 0xFF))) + && ((x)->data[(x)->size - 7] ^ (x)->data[(x)->size - 5]) == 0xFF \ + && ((x)->data[(x)->size - 3] ^ (x)->data[(x)->size - 4]) == 0xFF)) #define FT_MAX_TRIES 5 #define FT_RETRY_DLY 20 @@ -701,7 +714,8 @@ static int ft5x06_fw_upgrade_start(struct i2c_client *client, for (i = 0, j = 0; i < FT_UPGRADE_LOOP; i++) { msleep(FT_EARSE_DLY_MS); /* reset - write 0xaa and 0x55 to reset register */ - if (ts_data->family_id == FT6X06_ID) + if (ts_data->family_id == FT6X06_ID + || ts_data->family_id == FT6X36_ID) reset_reg = FT_RST_CMD_REG2; else reset_reg = FT_RST_CMD_REG1; @@ -906,7 +920,13 @@ static int ft5x06_fw_upgrade(struct device *dev, bool force) goto rel_fw; } - fw_file_maj = FT_FW_FILE_MAJ_VER(fw); + if (data->family_id == FT6X36_ID) { + fw_file_maj = FT_FW_FILE_MAJ_VER_FT6X36(fw); + fw_file_vendor_id = FT_FW_FILE_VENDOR_ID_FT6X36(fw); + } else { + fw_file_maj = FT_FW_FILE_MAJ_VER(fw); + fw_file_vendor_id = FT_FW_FILE_VENDOR_ID(fw); + } fw_file_min = FT_FW_FILE_MIN_VER(fw); fw_file_sub_min = FT_FW_FILE_SUB_MIN_VER(fw); @@ -934,7 +954,7 @@ static int ft5x06_fw_upgrade(struct device *dev, bool force) } /* start firmware upgrade */ - if (FT_FW_CHECK(fw)) { + if (FT_FW_CHECK(fw, data)) { rc = ft5x06_fw_upgrade_start(data->client, fw->data, fw->size); if (rc < 0) dev_err(dev, "update failed (%d). try later...\n", rc); diff --git a/include/linux/input/ft5x06_ts.h b/include/linux/input/ft5x06_ts.h index 1340737070f7..5242f96707ac 100644 --- a/include/linux/input/ft5x06_ts.h +++ b/include/linux/input/ft5x06_ts.h @@ -22,6 +22,7 @@ #define FT5X16_ID 0x0A #define FT5X36_ID 0x14 #define FT6X06_ID 0x06 +#define FT6X36_ID 0x36 struct fw_upgrade_info { bool auto_cal; From fa14a65666673b967f88b33f744c87f40a76256d Mon Sep 17 00:00:00 2001 From: Sudhakar Manapati Date: Wed, 19 Mar 2014 17:38:12 +0530 Subject: [PATCH 08/15] input: touchscreen: change the focaltech firmware upgrade method upgarde firmware on the touch controller when the new firmware version is geater than the current firmware version. Update the firmware version id after successful firmware update. skip firmware update when device is in suspend state. This patch is propagated from msm-3.18 kernel 'commit 8399308e570e ("input: touchscreen: change the focaltech firmware upgrade method")' CRs-Fixed: 1046961 Change-Id: Ic462f6483887a3654665852e58ae9891de9f5eff Signed-off-by: Sarada Prasanna Garnayak Signed-off-by: Sudhakar Manapati --- drivers/input/touchscreen/ft5x06_ts.c | 21 +++++++++------------ 1 file changed, 9 insertions(+), 12 deletions(-) diff --git a/drivers/input/touchscreen/ft5x06_ts.c b/drivers/input/touchscreen/ft5x06_ts.c index 421021ab0a4e..010da1bdcce1 100644 --- a/drivers/input/touchscreen/ft5x06_ts.c +++ b/drivers/input/touchscreen/ft5x06_ts.c @@ -69,7 +69,6 @@ #define FT_REG_THGROUP 0x80 #define FT_REG_ECC 0xCC #define FT_REG_RESET_FW 0x07 -#define FT_REG_FW_MAJ_VER 0xB1 #define FT_REG_FW_MIN_VER 0xB2 #define FT_REG_FW_SUB_MIN_VER 0xB3 @@ -302,7 +301,7 @@ static void ft5x06_update_fw_ver(struct ft5x06_ts_data *data) u8 reg_addr; int err; - reg_addr = FT_REG_FW_MAJ_VER; + reg_addr = FT_REG_FW_VER; err = ft5x06_i2c_read(client, ®_addr, 1, &data->fw_ver[0], 1); if (err < 0) dev_err(&client->dev, "fw major version read failed"); @@ -907,6 +906,11 @@ static int ft5x06_fw_upgrade(struct device *dev, bool force) u8 fw_file_maj, fw_file_min, fw_file_sub_min; bool fw_upgrade = false; + if (data->suspended) { + dev_err(dev, "Device is in suspend state: Exit FW upgrade\n"); + return -EBUSY; + } + rc = request_firmware(&fw, data->fw_name, dev); if (rc < 0) { dev_err(dev, "Request firmware failed - %s (%d)\n", @@ -935,17 +939,10 @@ static int ft5x06_fw_upgrade(struct device *dev, bool force) dev_info(dev, "New firmware: %d.%d.%d", fw_file_maj, fw_file_min, fw_file_sub_min); - if (force) { + if (force) + fw_upgrade = true; + else if (data->fw_ver[0] < fw_file_maj) fw_upgrade = true; - } else if (data->fw_ver[0] == fw_file_maj) { - if (data->fw_ver[1] < fw_file_min) - fw_upgrade = true; - else if (data->fw_ver[2] < fw_file_sub_min) - fw_upgrade = true; - else - dev_info(dev, "No need to upgrade\n"); - } else - dev_info(dev, "Firmware versions do not match\n"); if (!fw_upgrade) { dev_info(dev, "Exiting fw upgrade...\n"); From 405643a77ee764fa0dddacaa9dc4b50b76794c5f Mon Sep 17 00:00:00 2001 From: Mao Li Date: Tue, 8 Apr 2014 19:58:04 +0800 Subject: [PATCH 09/15] input: ft5x06_ts: check vendor id before upgrade FT firmware Upgrade the firmware only if same vendor id is found both in existing fw and new fw to be upgraded. This patch is propagated from 3.18 kernel 'commit dba19fc86781 ("input: ft5x06_ts: check vendor id before upgrade FT firmware")' Change-Id: Idfc50da45891a1475ac6b35d80c7d725607cbf81 Signed-off-by: Mao Li --- drivers/input/touchscreen/ft5x06_ts.c | 22 ++++++++++++++++++++-- 1 file changed, 20 insertions(+), 2 deletions(-) diff --git a/drivers/input/touchscreen/ft5x06_ts.c b/drivers/input/touchscreen/ft5x06_ts.c index 010da1bdcce1..0f74e10a0bbc 100644 --- a/drivers/input/touchscreen/ft5x06_ts.c +++ b/drivers/input/touchscreen/ft5x06_ts.c @@ -65,6 +65,7 @@ #define FT_REG_ID 0xA3 #define FT_REG_PMODE 0xA5 #define FT_REG_FW_VER 0xA6 +#define FT_REG_FW_VENDOR_ID 0xA8 #define FT_REG_POINT_RATE 0x88 #define FT_REG_THGROUP 0x80 #define FT_REG_ECC 0xCC @@ -115,6 +116,7 @@ #define FT_FW_FILE_MAJ_VER(x) ((x)->data[(x)->size - 2]) #define FT_FW_FILE_MIN_VER(x) 0 #define FT_FW_FILE_SUB_MIN_VER(x) 0 +#define FT_FW_FILE_VENDOR_ID(x) ((x)->data[(x)->size - 1]) #define FT_FW_FILE_MAJ_VER_FT6X36(x) ((x)->data[0x10a]) #define FT_FW_FILE_VENDOR_ID_FT6X36(x) ((x)->data[0x108]) @@ -213,6 +215,7 @@ struct ft5x06_ts_data { u8 *tch_data; u32 tch_data_len; u8 fw_ver[3]; + u8 fw_vendor_id; #if defined(CONFIG_FB) struct notifier_block fb_notif; #elif defined(CONFIG_HAS_EARLYSUSPEND) @@ -295,6 +298,18 @@ static int ft5x0x_read_reg(struct i2c_client *client, u8 addr, u8 *val) return ft5x06_i2c_read(client, &addr, 1, val, 1); } +static void ft5x06_update_fw_vendor_id(struct ft5x06_ts_data *data) +{ + struct i2c_client *client = data->client; + u8 reg_addr; + int err; + + reg_addr = FT_REG_FW_VENDOR_ID; + err = ft5x06_i2c_read(client, ®_addr, 1, &data->fw_vendor_id, 1); + if (err < 0) + dev_err(&client->dev, "fw vendor id read failed"); +} + static void ft5x06_update_fw_ver(struct ft5x06_ts_data *data) { struct i2c_client *client = data->client; @@ -903,7 +918,7 @@ static int ft5x06_fw_upgrade(struct device *dev, bool force) struct ft5x06_ts_data *data = dev_get_drvdata(dev); const struct firmware *fw = NULL; int rc; - u8 fw_file_maj, fw_file_min, fw_file_sub_min; + u8 fw_file_maj, fw_file_min, fw_file_sub_min, fw_file_vendor_id; bool fw_upgrade = false; if (data->suspended) { @@ -933,6 +948,7 @@ static int ft5x06_fw_upgrade(struct device *dev, bool force) } fw_file_min = FT_FW_FILE_MIN_VER(fw); fw_file_sub_min = FT_FW_FILE_SUB_MIN_VER(fw); + fw_file_vendor_id = FT_FW_FILE_VENDOR_ID(fw); dev_info(dev, "Current firmware: %d.%d.%d", data->fw_ver[0], data->fw_ver[1], data->fw_ver[2]); @@ -941,7 +957,8 @@ static int ft5x06_fw_upgrade(struct device *dev, bool force) if (force) fw_upgrade = true; - else if (data->fw_ver[0] < fw_file_maj) + else if ((data->fw_ver[0] < fw_file_maj) && + data->fw_vendor_id == fw_file_vendor_id) fw_upgrade = true; if (!fw_upgrade) { @@ -1661,6 +1678,7 @@ static int ft5x06_ts_probe(struct i2c_client *client, dev_dbg(&client->dev, "touch threshold = %d\n", reg_value * 4); ft5x06_update_fw_ver(data); + ft5x06_update_fw_vendor_id(data); FT_STORE_TS_INFO(data->ts_info, data->family_id, data->pdata->name, data->pdata->num_max_touches, data->pdata->group_id, From 888157c601b1c063b40852510afc316a1003cba3 Mon Sep 17 00:00:00 2001 From: Mao Li Date: Wed, 2 Apr 2014 11:06:42 +0800 Subject: [PATCH 10/15] input: ft5x06_ts: add support for pinctrl framework Migrate the Focaltech driver to use pinctrl framework for GPIO configuration so that driver is compatible with targets that use and targets that don't use pinctrl framework. This patch is propagated from 3.18 kernel 'commit 266d1fe0f630 ("input: ft5x06_ts: add support for pinctrl framework")' Change-Id: I84fc2a6d30f831b4b054780b107ce105614855ea Signed-off-by: Mao Li --- .../bindings/input/touchscreen/ft5x06-ts.txt | 19 ++++ drivers/input/touchscreen/ft5x06_ts.c | 102 ++++++++++++++++++ 2 files changed, 121 insertions(+) diff --git a/Documentation/devicetree/bindings/input/touchscreen/ft5x06-ts.txt b/Documentation/devicetree/bindings/input/touchscreen/ft5x06-ts.txt index 1d04a7e365e1..0d6a6990fc78 100644 --- a/Documentation/devicetree/bindings/input/touchscreen/ft5x06-ts.txt +++ b/Documentation/devicetree/bindings/input/touchscreen/ft5x06-ts.txt @@ -27,6 +27,22 @@ Required properties: - focaltech,group-id : group id of this device - focaltech,hard-reset-delay-ms : hard reset delay in ms - focaltech,soft-reset-delay-ms : soft reset delay in ms + - focaltech,fw-delay-aa-ms : specify the delay in ms after programming 0xaa + register for firmware upgrade + - focaltech,fw-delay-55-ms : specify the delay in ms after programming 0x55 + register for firmware upgrade + - focaltech,fw-upgrade-id1 : specify the upgrade id1 for firmware upgrade + - focaltech,fw-upgrade-id2 : specify the upgrade id2 for firmware upgrade + - focaltech,fw-delay-readid-ms : specify the read id delay in ms for firmware upgrade + - focaltech,fw-delay-era-flsh-ms : specify the erase flash delay in ms for firmware upgrade + - pinctrl-names : This should be defined if a target uses pinctrl framework. + See "pinctrl" in Documentation/devicetree/bindings/pinctrl/msm-pinctrl.txt. + Specify the names of the configs that pinctrl can install in driver. + Following are the pinctrl configs that can be installed: + "pmx_ts_active" : Active configuration of pins, this should specify active + config defined in pin groups of interrupt and reset gpio. + "pmx_ts_suspend" : Disabled configuration of pins, this should specify sleep + config defined in pin groups of interrupt and reset gpio. Optional properties: @@ -57,6 +73,9 @@ Example: interrupts = <1 0x2>; vdd-supply = <&pm8110_l19>; vcc_i2c-supply = <&pm8110_l14>; + pinctrl-names = "pmx_ts_active","pmx_ts_suspend"; + pinctrl-0 = <&ts_int_active &ts_reset_active>; + pinctrl-1 = <&ts_int_suspend &ts_reset_suspend>; focaltech,name = "ft6x06"; focaltech,family-id = <0x06>; focaltech,reset-gpio = <&msmgpio 0 0x00>; diff --git a/drivers/input/touchscreen/ft5x06_ts.c b/drivers/input/touchscreen/ft5x06_ts.c index 0f74e10a0bbc..72142ed333fd 100644 --- a/drivers/input/touchscreen/ft5x06_ts.c +++ b/drivers/input/touchscreen/ft5x06_ts.c @@ -221,6 +221,9 @@ struct ft5x06_ts_data { #elif defined(CONFIG_HAS_EARLYSUSPEND) struct early_suspend early_suspend; #endif + struct pinctrl *ts_pinctrl; + struct pinctrl_state *gpio_state_active; + struct pinctrl_state *gpio_state_suspend; }; static int ft5x06_i2c_read(struct i2c_client *client, char *writebuf, @@ -509,6 +512,71 @@ pwr_deinit: return 0; } +static int ft5x06_ts_pinctrl_init(struct ft5x06_ts_data *ft5x06_data) +{ + int retval; + + /* Get pinctrl if target uses pinctrl */ + ft5x06_data->ts_pinctrl = devm_pinctrl_get(&(ft5x06_data->client->dev)); + if (IS_ERR_OR_NULL(ft5x06_data->ts_pinctrl)) { + dev_dbg(&ft5x06_data->client->dev, + "Target does not use pinctrl\n"); + retval = PTR_ERR(ft5x06_data->ts_pinctrl); + ft5x06_data->ts_pinctrl = NULL; + return retval; + } + + ft5x06_data->gpio_state_active + = pinctrl_lookup_state(ft5x06_data->ts_pinctrl, + "pmx_ts_active"); + if (IS_ERR_OR_NULL(ft5x06_data->gpio_state_active)) { + dev_dbg(&ft5x06_data->client->dev, + "Can not get ts default pinstate\n"); + retval = PTR_ERR(ft5x06_data->gpio_state_active); + ft5x06_data->ts_pinctrl = NULL; + return retval; + } + + ft5x06_data->gpio_state_suspend + = pinctrl_lookup_state(ft5x06_data->ts_pinctrl, + "pmx_ts_suspend"); + if (IS_ERR_OR_NULL(ft5x06_data->gpio_state_suspend)) { + dev_err(&ft5x06_data->client->dev, + "Can not get ts sleep pinstate\n"); + retval = PTR_ERR(ft5x06_data->gpio_state_suspend); + ft5x06_data->ts_pinctrl = NULL; + return retval; + } + + return 0; +} + +static int ft5x06_ts_pinctrl_select(struct ft5x06_ts_data *ft5x06_data, + bool on) +{ + struct pinctrl_state *pins_state; + int ret; + + pins_state = on ? ft5x06_data->gpio_state_active + : ft5x06_data->gpio_state_suspend; + if (!IS_ERR_OR_NULL(pins_state)) { + ret = pinctrl_select_state(ft5x06_data->ts_pinctrl, pins_state); + if (ret) { + dev_err(&ft5x06_data->client->dev, + "can not set %s pins\n", + on ? "pmx_ts_active" : "pmx_ts_suspend"); + return ret; + } + } else { + dev_err(&ft5x06_data->client->dev, + "not a valid '%s' pinstate\n", + on ? "pmx_ts_active" : "pmx_ts_suspend"); + } + + return 0; +} + + #ifdef CONFIG_PM static int ft5x06_ts_suspend(struct device *dev) { @@ -525,6 +593,11 @@ static int ft5x06_ts_suspend(struct device *dev) dev_info(dev, "Already in suspend state\n"); return 0; } + if (data->ts_pinctrl) { + err = ft5x06_ts_pinctrl_select(data, false); + if (err < 0) + dev_err(dev, "Cannot get idle pinctrl state\n"); + } disable_irq(data->client->irq); @@ -593,6 +666,11 @@ static int ft5x06_ts_resume(struct device *dev) return err; } } + if (data->ts_pinctrl) { + err = ft5x06_ts_pinctrl_select(data, true); + if (err < 0) + dev_err(dev, "Cannot get default pinctrl state\n"); + } if (gpio_is_valid(data->pdata->reset_gpio)) { gpio_set_value_cansleep(data->pdata->reset_gpio, 0); @@ -1540,6 +1618,13 @@ static int ft5x06_ts_probe(struct i2c_client *client, } } + err = ft5x06_ts_pinctrl_init(data); + if (!err && data->ts_pinctrl) { + err = ft5x06_ts_pinctrl_select(data, true); + if (err < 0) + goto pwr_off; + } + if (gpio_is_valid(pdata->irq_gpio)) { err = gpio_request(pdata->irq_gpio, "ft5x06_irq_gpio"); if (err) { @@ -1717,9 +1802,19 @@ irq_free: free_reset_gpio: if (gpio_is_valid(pdata->reset_gpio)) gpio_free(pdata->reset_gpio); + if (data->ts_pinctrl) { + err = ft5x06_ts_pinctrl_select(data, false); + if (err < 0) + pr_err("Cannot get idle pinctrl state\n"); + } free_irq_gpio: if (gpio_is_valid(pdata->irq_gpio)) gpio_free(pdata->irq_gpio); + if (data->ts_pinctrl) { + err = ft5x06_ts_pinctrl_select(data, false); + if (err < 0) + pr_err("Cannot get idle pinctrl state\n"); + } pwr_off: if (pdata->power_on) pdata->power_on(false); @@ -1741,6 +1836,7 @@ free_inputdev: static int ft5x06_ts_remove(struct i2c_client *client) { struct ft5x06_ts_data *data = i2c_get_clientdata(client); + int retval; debugfs_remove_recursive(data->dir); device_remove_file(&client->dev, &dev_attr_force_update_fw); @@ -1761,6 +1857,12 @@ static int ft5x06_ts_remove(struct i2c_client *client) if (gpio_is_valid(data->pdata->irq_gpio)) gpio_free(data->pdata->irq_gpio); + if (data->ts_pinctrl) { + retval = ft5x06_ts_pinctrl_select(data, false); + if (retval < 0) + pr_err("Cannot get idle pinctrl state\n"); + } + if (data->pdata->power_on) data->pdata->power_on(false); else From cbb13b31eb274830cb44c1780faee1178209db4a Mon Sep 17 00:00:00 2001 From: Mao Li Date: Tue, 10 Jun 2014 19:54:13 +0800 Subject: [PATCH 11/15] input: ft5x06_ts: fix the Focaltech CTP current leakage issue Focaltech touch controller does not go to sleep in suspend because the pinctrl operation inadvertently resets the touch controller. Remove the pinctrl operation in suspend and resume callback to prevent this spurious reset. This patch is propagated from msm-3.18 kernel. 'commit 7e2ea02b16de ("input: ft5x06_ts: fix the Focaltech CTP current leakage issue")' CRs-Fixed: 1046961 Change-Id: Ifa02c0287195d7ce55fb2d8ac49ec1c8ae5baec9 Signed-off-by: Mao Li --- drivers/input/touchscreen/ft5x06_ts.c | 10 ---------- 1 file changed, 10 deletions(-) diff --git a/drivers/input/touchscreen/ft5x06_ts.c b/drivers/input/touchscreen/ft5x06_ts.c index 72142ed333fd..43dbed51d8ee 100644 --- a/drivers/input/touchscreen/ft5x06_ts.c +++ b/drivers/input/touchscreen/ft5x06_ts.c @@ -593,11 +593,6 @@ static int ft5x06_ts_suspend(struct device *dev) dev_info(dev, "Already in suspend state\n"); return 0; } - if (data->ts_pinctrl) { - err = ft5x06_ts_pinctrl_select(data, false); - if (err < 0) - dev_err(dev, "Cannot get idle pinctrl state\n"); - } disable_irq(data->client->irq); @@ -666,11 +661,6 @@ static int ft5x06_ts_resume(struct device *dev) return err; } } - if (data->ts_pinctrl) { - err = ft5x06_ts_pinctrl_select(data, true); - if (err < 0) - dev_err(dev, "Cannot get default pinctrl state\n"); - } if (gpio_is_valid(data->pdata->reset_gpio)) { gpio_set_value_cansleep(data->pdata->reset_gpio, 0); From 14632a5cfb4d2acc40856748f91b07687eff9884 Mon Sep 17 00:00:00 2001 From: Shantanu Jain Date: Mon, 21 Jul 2014 18:00:00 +0530 Subject: [PATCH 12/15] input: focaltech: release pinctrl resources on probe failure devm_pinctrl_get() does not release the ownership of mux function of pins in the associated pin-group whenever a failure occurs in driver probe routine. i.e struct pin_desc's mux_owner field is still marked as being in use after a failure occurs in probe. As a result of this, if another driver tries to acquire the ownership of same pin, it gets an error while applying that setting. To fix this, explicitly release the mux function ownership of the the pin, by adding a new pin-group in pinctrl DT and a new pinctrl state in touch device's DT node. This new pin-group does not have a function setting (qcom,pin-func property). This new state is explicitly activated during a probe failure and driver remove routine to release the mux function ownership. The patch also reorganizes the pinctrl related code in driver. This patch is propagated from msm-3.18 kernel. 'commit f09a0560dde9 ("input: focaltech: release pinctrl resources on probe failure")' Change-Id: I16a97fefc64dd171deb800b481aa74a797c9ad55 Signed-off-by: Shantanu Jain --- .../bindings/input/touchscreen/ft5x06-ts.txt | 5 +- drivers/input/touchscreen/ft5x06_ts.c | 137 ++++++++++-------- 2 files changed, 77 insertions(+), 65 deletions(-) diff --git a/Documentation/devicetree/bindings/input/touchscreen/ft5x06-ts.txt b/Documentation/devicetree/bindings/input/touchscreen/ft5x06-ts.txt index 0d6a6990fc78..ccf11ec97c4e 100644 --- a/Documentation/devicetree/bindings/input/touchscreen/ft5x06-ts.txt +++ b/Documentation/devicetree/bindings/input/touchscreen/ft5x06-ts.txt @@ -43,6 +43,8 @@ Required properties: config defined in pin groups of interrupt and reset gpio. "pmx_ts_suspend" : Disabled configuration of pins, this should specify sleep config defined in pin groups of interrupt and reset gpio. + "pmx_ts_release" : Release configuration of pins, this should specify + release config defined in pin groups of interrupt and reset gpio. Optional properties: @@ -73,9 +75,10 @@ Example: interrupts = <1 0x2>; vdd-supply = <&pm8110_l19>; vcc_i2c-supply = <&pm8110_l14>; - pinctrl-names = "pmx_ts_active","pmx_ts_suspend"; + pinctrl-names = "pmx_ts_active","pmx_ts_suspend","pmx_ts_release"; pinctrl-0 = <&ts_int_active &ts_reset_active>; pinctrl-1 = <&ts_int_suspend &ts_reset_suspend>; + pinctrl-2 = <&ts_release>; focaltech,name = "ft6x06"; focaltech,family-id = <0x06>; focaltech,reset-gpio = <&msmgpio 0 0x00>; diff --git a/drivers/input/touchscreen/ft5x06_ts.c b/drivers/input/touchscreen/ft5x06_ts.c index 43dbed51d8ee..48ceadaafd81 100644 --- a/drivers/input/touchscreen/ft5x06_ts.c +++ b/drivers/input/touchscreen/ft5x06_ts.c @@ -168,6 +168,10 @@ #define FT_MAGIC_BLOADER_GZF_30 0x7ff4 #define FT_MAGIC_BLOADER_GZF 0x7bf4 +#define PINCTRL_STATE_ACTIVE "pmx_ts_active" +#define PINCTRL_STATE_SUSPEND "pmx_ts_suspend" +#define PINCTRL_STATE_RELEASE "pmx_ts_release" + enum { FT_BLOADER_VERSION_LZ4 = 0, FT_BLOADER_VERSION_Z7 = 1, @@ -222,8 +226,9 @@ struct ft5x06_ts_data { struct early_suspend early_suspend; #endif struct pinctrl *ts_pinctrl; - struct pinctrl_state *gpio_state_active; - struct pinctrl_state *gpio_state_suspend; + struct pinctrl_state *pinctrl_state_active; + struct pinctrl_state *pinctrl_state_suspend; + struct pinctrl_state *pinctrl_state_release; }; static int ft5x06_i2c_read(struct i2c_client *client, char *writebuf, @@ -519,64 +524,53 @@ static int ft5x06_ts_pinctrl_init(struct ft5x06_ts_data *ft5x06_data) /* Get pinctrl if target uses pinctrl */ ft5x06_data->ts_pinctrl = devm_pinctrl_get(&(ft5x06_data->client->dev)); if (IS_ERR_OR_NULL(ft5x06_data->ts_pinctrl)) { - dev_dbg(&ft5x06_data->client->dev, - "Target does not use pinctrl\n"); retval = PTR_ERR(ft5x06_data->ts_pinctrl); - ft5x06_data->ts_pinctrl = NULL; - return retval; - } - - ft5x06_data->gpio_state_active - = pinctrl_lookup_state(ft5x06_data->ts_pinctrl, - "pmx_ts_active"); - if (IS_ERR_OR_NULL(ft5x06_data->gpio_state_active)) { dev_dbg(&ft5x06_data->client->dev, - "Can not get ts default pinstate\n"); - retval = PTR_ERR(ft5x06_data->gpio_state_active); - ft5x06_data->ts_pinctrl = NULL; - return retval; + "Target does not use pinctrl %d\n", retval); + goto err_pinctrl_get; } - ft5x06_data->gpio_state_suspend + ft5x06_data->pinctrl_state_active = pinctrl_lookup_state(ft5x06_data->ts_pinctrl, - "pmx_ts_suspend"); - if (IS_ERR_OR_NULL(ft5x06_data->gpio_state_suspend)) { + PINCTRL_STATE_ACTIVE); + if (IS_ERR_OR_NULL(ft5x06_data->pinctrl_state_active)) { + retval = PTR_ERR(ft5x06_data->pinctrl_state_active); dev_err(&ft5x06_data->client->dev, - "Can not get ts sleep pinstate\n"); - retval = PTR_ERR(ft5x06_data->gpio_state_suspend); - ft5x06_data->ts_pinctrl = NULL; - return retval; + "Can not lookup %s pinstate %d\n", + PINCTRL_STATE_ACTIVE, retval); + goto err_pinctrl_lookup; + } + + ft5x06_data->pinctrl_state_suspend + = pinctrl_lookup_state(ft5x06_data->ts_pinctrl, + PINCTRL_STATE_SUSPEND); + if (IS_ERR_OR_NULL(ft5x06_data->pinctrl_state_suspend)) { + retval = PTR_ERR(ft5x06_data->pinctrl_state_suspend); + dev_err(&ft5x06_data->client->dev, + "Can not lookup %s pinstate %d\n", + PINCTRL_STATE_SUSPEND, retval); + goto err_pinctrl_lookup; + } + + ft5x06_data->pinctrl_state_release + = pinctrl_lookup_state(ft5x06_data->ts_pinctrl, + PINCTRL_STATE_RELEASE); + if (IS_ERR_OR_NULL(ft5x06_data->pinctrl_state_release)) { + retval = PTR_ERR(ft5x06_data->pinctrl_state_release); + dev_dbg(&ft5x06_data->client->dev, + "Can not lookup %s pinstate %d\n", + PINCTRL_STATE_RELEASE, retval); } return 0; + +err_pinctrl_lookup: + devm_pinctrl_put(ft5x06_data->ts_pinctrl); +err_pinctrl_get: + ft5x06_data->ts_pinctrl = NULL; + return retval; } -static int ft5x06_ts_pinctrl_select(struct ft5x06_ts_data *ft5x06_data, - bool on) -{ - struct pinctrl_state *pins_state; - int ret; - - pins_state = on ? ft5x06_data->gpio_state_active - : ft5x06_data->gpio_state_suspend; - if (!IS_ERR_OR_NULL(pins_state)) { - ret = pinctrl_select_state(ft5x06_data->ts_pinctrl, pins_state); - if (ret) { - dev_err(&ft5x06_data->client->dev, - "can not set %s pins\n", - on ? "pmx_ts_active" : "pmx_ts_suspend"); - return ret; - } - } else { - dev_err(&ft5x06_data->client->dev, - "not a valid '%s' pinstate\n", - on ? "pmx_ts_active" : "pmx_ts_suspend"); - } - - return 0; -} - - #ifdef CONFIG_PM static int ft5x06_ts_suspend(struct device *dev) { @@ -1610,16 +1604,22 @@ static int ft5x06_ts_probe(struct i2c_client *client, err = ft5x06_ts_pinctrl_init(data); if (!err && data->ts_pinctrl) { - err = ft5x06_ts_pinctrl_select(data, true); - if (err < 0) - goto pwr_off; + 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"); + goto pinctrl_deinit; + } + } else { + goto pwr_off; } if (gpio_is_valid(pdata->irq_gpio)) { err = gpio_request(pdata->irq_gpio, "ft5x06_irq_gpio"); if (err) { dev_err(&client->dev, "irq gpio request failed"); - goto pwr_off; + goto err_gpio_req; } err = gpio_direction_input(pdata->irq_gpio); if (err) { @@ -1792,18 +1792,21 @@ irq_free: free_reset_gpio: if (gpio_is_valid(pdata->reset_gpio)) gpio_free(pdata->reset_gpio); - if (data->ts_pinctrl) { - err = ft5x06_ts_pinctrl_select(data, false); - if (err < 0) - pr_err("Cannot get idle pinctrl state\n"); - } free_irq_gpio: if (gpio_is_valid(pdata->irq_gpio)) gpio_free(pdata->irq_gpio); +err_gpio_req: +pinctrl_deinit: if (data->ts_pinctrl) { - err = ft5x06_ts_pinctrl_select(data, false); - if (err < 0) - pr_err("Cannot get idle pinctrl state\n"); + 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"); + } } pwr_off: if (pdata->power_on) @@ -1848,9 +1851,15 @@ static int ft5x06_ts_remove(struct i2c_client *client) gpio_free(data->pdata->irq_gpio); if (data->ts_pinctrl) { - retval = ft5x06_ts_pinctrl_select(data, false); - if (retval < 0) - pr_err("Cannot get idle pinctrl state\n"); + if (IS_ERR_OR_NULL(data->pinctrl_state_release)) { + devm_pinctrl_put(data->ts_pinctrl); + data->ts_pinctrl = NULL; + } else { + retval = pinctrl_select_state(data->ts_pinctrl, + data->pinctrl_state_release); + if (retval < 0) + pr_err("failed to select release pinctrl state\n"); + } } if (data->pdata->power_on) From 0d7758f93a87bd2c422178eb5019b2ac8acd2fba Mon Sep 17 00:00:00 2001 From: Sudhakar Manapati Date: Mon, 1 Sep 2014 12:53:03 +0530 Subject: [PATCH 13/15] input: ft5x06_ts: pinctrl and gpio config in suspend/resume path When device switch to suspend state the gpio and pinctrl need to be configured properly to prevent the touch controller current leakage in device suspend state. The pinctrl must be configure first before the gpio configuration. Select the pinctrl sleep state and after that in gpio configuration part free both IRQ and reset gpio, and also set direction of reset gpio to be input. If the gpio and pinctrl is not in proper state the touch controller will not switch to the deep sleep state and it will start consume current during the device suspend state. To prevent this leakage current issue, pinctrl and gpio must be configured properly. This patch is propagated from msm-3.18 kernel. 'commit 494eae39f2a4 ("input: ft5x06_ts: pinctrl and gpio config in suspend/resume path")' Also cleared checkpatch errors on msm-3.18 kernel. Change-Id: I0ec72bbbf12320ad22608522d1250614c6686fe3 Signed-off-by: Sarada Prasanna Garnayak Signed-off-by: Sudhakar Manapati --- drivers/input/touchscreen/ft5x06_ts.c | 188 ++++++++++++++++++++------ 1 file changed, 149 insertions(+), 39 deletions(-) diff --git a/drivers/input/touchscreen/ft5x06_ts.c b/drivers/input/touchscreen/ft5x06_ts.c index 48ceadaafd81..e1de4663c0f2 100644 --- a/drivers/input/touchscreen/ft5x06_ts.c +++ b/drivers/input/touchscreen/ft5x06_ts.c @@ -405,6 +405,79 @@ static irqreturn_t ft5x06_ts_interrupt(int irq, void *dev_id) return IRQ_HANDLED; } +static int ft5x06_gpio_configure(struct ft5x06_ts_data *data, bool on) +{ + int err = 0; + + if (on) { + if (gpio_is_valid(data->pdata->irq_gpio)) { + err = gpio_request(data->pdata->irq_gpio, + "ft5x06_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)) { + err = gpio_request(data->pdata->reset_gpio, + "ft5x06_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); + } + + return 0; + } + 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; +} + static int ft5x06_power_on(struct ft5x06_ts_data *data, bool on) { int rc; @@ -618,10 +691,40 @@ static int ft5x06_ts_suspend(struct device *dev) } } + 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"); + } + + err = ft5x06_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: + 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"); + } + if (data->pdata->power_on) { + err = data->pdata->power_on(true); + if (err) + dev_err(dev, "power on failed"); + } else { + err = ft5x06_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); @@ -656,6 +759,20 @@ static int ft5x06_ts_resume(struct device *dev) } } + 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"); + } + + err = ft5x06_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); @@ -669,6 +786,24 @@ static int ft5x06_ts_resume(struct device *dev) data->suspended = false; return 0; + +err_gpio_configuration: + 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"); + } + if (data->pdata->power_on) { + err = data->pdata->power_on(false); + if (err) + dev_err(dev, "power off failed"); + } else { + err = ft5x06_power_on(data, false); + if (err) + dev_err(dev, "power off failed"); + } + return err; } static const struct dev_pm_ops ft5x06_ts_pm_ops = { @@ -1604,46 +1739,24 @@ static int ft5x06_ts_probe(struct i2c_client *client, err = ft5x06_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"); - goto pinctrl_deinit; - } - } else { - goto pwr_off; - } - - if (gpio_is_valid(pdata->irq_gpio)) { - err = gpio_request(pdata->irq_gpio, "ft5x06_irq_gpio"); - if (err) { - dev_err(&client->dev, "irq gpio request failed"); - goto err_gpio_req; - } - err = gpio_direction_input(pdata->irq_gpio); - if (err) { - dev_err(&client->dev, - "set_direction for irq gpio failed\n"); - goto free_irq_gpio; } } - if (gpio_is_valid(pdata->reset_gpio)) { - err = gpio_request(pdata->reset_gpio, "ft5x06_reset_gpio"); - if (err) { - dev_err(&client->dev, "reset gpio request failed"); - goto free_irq_gpio; - } - - err = gpio_direction_output(pdata->reset_gpio, 0); - if (err) { - dev_err(&client->dev, - "set_direction for reset gpio failed\n"); - goto free_reset_gpio; - } - msleep(data->pdata->hard_rst_dly); - gpio_set_value_cansleep(data->pdata->reset_gpio, 1); + err = ft5x06_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 */ @@ -1654,14 +1767,14 @@ static int ft5x06_ts_probe(struct i2c_client *client, err = ft5x06_i2c_read(client, ®_addr, 1, ®_value, 1); if (err < 0) { dev_err(&client->dev, "version read failed"); - goto free_reset_gpio; + goto free_gpio; } 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_reset_gpio; + goto free_gpio; } data->family_id = pdata->family_id; @@ -1672,7 +1785,7 @@ static int ft5x06_ts_probe(struct i2c_client *client, client->dev.driver->name, data); if (err) { dev_err(&client->dev, "request irq failed\n"); - goto free_reset_gpio; + goto free_gpio; } err = device_create_file(&client->dev, &dev_attr_fw_name); @@ -1789,14 +1902,12 @@ free_fw_name_sys: device_remove_file(&client->dev, &dev_attr_fw_name); irq_free: free_irq(client->irq, data); -free_reset_gpio: +free_gpio: if (gpio_is_valid(pdata->reset_gpio)) gpio_free(pdata->reset_gpio); -free_irq_gpio: if (gpio_is_valid(pdata->irq_gpio)) gpio_free(pdata->irq_gpio); err_gpio_req: -pinctrl_deinit: if (data->ts_pinctrl) { if (IS_ERR_OR_NULL(data->pinctrl_state_release)) { devm_pinctrl_put(data->ts_pinctrl); @@ -1808,7 +1919,6 @@ pinctrl_deinit: pr_err("failed to select relase pinctrl state\n"); } } -pwr_off: if (pdata->power_on) pdata->power_on(false); else From 49aaf48f4768e07a02fc62cfc2520afb941217cd Mon Sep 17 00:00:00 2001 From: Sudhakar Manapati Date: Wed, 3 Sep 2014 17:49:52 +0530 Subject: [PATCH 14/15] input: ft5x06_ts: add NULL pointer check Add null pointer exception check for pointers which may be NULL after memory allocation failure and may be deferenced. This patch is propagated from msm-3.18 kernel. 'commit 5c6aa1bb97a3 ("input: ft5x06_ts: add NULL pointer check")' Change-Id: Ied34d548bff27f8f6da9f4c81896e4505cdce218 Signed-off-by: Sarada Prasanna Garnayak Signed-off-by: Sudhakar Manapati --- drivers/input/touchscreen/ft5x06_ts.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/input/touchscreen/ft5x06_ts.c b/drivers/input/touchscreen/ft5x06_ts.c index e1de4663c0f2..b3aaf78a0496 100644 --- a/drivers/input/touchscreen/ft5x06_ts.c +++ b/drivers/input/touchscreen/ft5x06_ts.c @@ -1672,7 +1672,7 @@ static int ft5x06_ts_probe(struct i2c_client *client, data->tch_data_len = FT_TCH_LEN(pdata->num_max_touches); data->tch_data = devm_kzalloc(&client->dev, data->tch_data_len, GFP_KERNEL); - if (!data) + if (!data->tch_data) return -ENOMEM; input_dev = input_allocate_device(); From fd296540aeaf0e8c0de20bec92ef66e8ecbdc9b9 Mon Sep 17 00:00:00 2001 From: Mao Li Date: Tue, 21 Oct 2014 06:42:56 -0400 Subject: [PATCH 15/15] input: ft5x06_ts: add proximity feature support Focaltech's CTP FT6436 is able to behave like a proximity sensor. Enable the driver support this new feature. Also cleared the chekpatch warning on 3.18 kernel. Change-Id: I7a6ec3a387536c512637b0bd8dab95e7cceca212 Signed-off-by: Mao Li Signed-off-by: Sudhakar Manapati Signed-off-by: Abinaya P --- .../bindings/input/touchscreen/ft5x06-ts.txt | 6 + drivers/input/touchscreen/Kconfig | 9 + drivers/input/touchscreen/ft5x06_ts.c | 249 +++++++++++++++++- include/linux/input/ft5x06_ts.h | 9 + 4 files changed, 266 insertions(+), 7 deletions(-) diff --git a/Documentation/devicetree/bindings/input/touchscreen/ft5x06-ts.txt b/Documentation/devicetree/bindings/input/touchscreen/ft5x06-ts.txt index ccf11ec97c4e..c852394254ff 100644 --- a/Documentation/devicetree/bindings/input/touchscreen/ft5x06-ts.txt +++ b/Documentation/devicetree/bindings/input/touchscreen/ft5x06-ts.txt @@ -65,6 +65,11 @@ Optional properties: - focaltech,fw-auto-cal : specify whether calibration is needed after firmware upgrade - focaltech,fw-vkey-support : specify if virtual keys are supported through firmware - focaltech,ignore-id-check : specify ignore family-id check + - focaltech,panel-coords : panel coordinates for the chip in pixels. + It is a four tuple consisting of min x, + min y, max x and max y values + - focaltech,fw-name : specify the firmware file name + - focaltech,psensor-support : specify whether support the proximity sensor Example: i2c@f9923000{ @@ -100,5 +105,6 @@ Example: focaltech,fw-delay-readid-ms = <10>; focaltech,fw-delay-era-flsh-ms = <2000>; focaltech,fw-auto-cal; + focaltech,psensor-support; }; }; diff --git a/drivers/input/touchscreen/Kconfig b/drivers/input/touchscreen/Kconfig index 69028bd45fdd..7c4a7c7c16e7 100644 --- a/drivers/input/touchscreen/Kconfig +++ b/drivers/input/touchscreen/Kconfig @@ -1110,6 +1110,15 @@ config TOUCHSCREEN_COLIBRI_VF50 To compile this driver as a module, choose M here: the module will be called colibri_vf50_ts. +config TOUCHSCREEN_FT5X06_PSENSOR + tristate "FocalTech proximity feature support" + depends on TOUCHSCREEN_FT5X06 && SENSORS + help + Say Y here if you want to support ft5x06's proximity + feature. + + If unsure, say N. + config TOUCHSCREEN_MSTAR21XX tristate "Mstar touchscreens" depends on I2C diff --git a/drivers/input/touchscreen/ft5x06_ts.c b/drivers/input/touchscreen/ft5x06_ts.c index b3aaf78a0496..beb86c9dea11 100644 --- a/drivers/input/touchscreen/ft5x06_ts.c +++ b/drivers/input/touchscreen/ft5x06_ts.c @@ -29,6 +29,7 @@ #include #include #include +#include #include #if defined(CONFIG_FB) @@ -73,7 +74,20 @@ #define FT_REG_FW_MIN_VER 0xB2 #define FT_REG_FW_SUB_MIN_VER 0xB3 -/* power register bits */ +/* psensor register address*/ +#define FT_REG_PSENSOR_ENABLE 0xB0 +#define FT_REG_PSENSOR_STATUS 0x01 + +/* psensor register bits*/ +#define FT_PSENSOR_ENABLE_MASK 0x01 +#define FT_PSENSOR_STATUS_NEAR 0xC0 +#define FT_PSENSOR_STATUS_FAR 0xE0 +#define FT_PSENSOR_FAR_TO_NEAR 0 +#define FT_PSENSOR_NEAR_TO_FAR 1 +#define FT_PSENSOR_ORIGINAL_STATE_FAR 1 +#define FT_PSENSOR_WAKEUP_TIMEOUT 100 + +/* power register bits*/ #define FT_PMODE_ACTIVE 0x00 #define FT_PMODE_MONITOR 0x01 #define FT_PMODE_STANDBY 0x02 @@ -207,6 +221,7 @@ struct ft5x06_ts_data { struct i2c_client *client; struct input_dev *input_dev; const struct ft5x06_ts_platform_data *pdata; + struct ft5x06_psensor_platform_data *psensor_pdata; struct regulator *vdd; struct regulator *vcc_i2c; char fw_name[FT_FW_NAME_MAX_LEN]; @@ -231,6 +246,29 @@ struct ft5x06_ts_data { struct pinctrl_state *pinctrl_state_release; }; +static struct sensors_classdev __maybe_unused sensors_proximity_cdev = { + .name = "ft5x06-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, +}; + +static inline bool ft5x06_psensor_support_enabled(void) +{ + return config_enabled(CONFIG_TOUCHSCREEN_FT5X06_PSENSOR); +} + static int ft5x06_i2c_read(struct i2c_client *client, char *writebuf, int writelen, char *readbuf, int readlen) { @@ -306,6 +344,84 @@ static int ft5x0x_read_reg(struct i2c_client *client, u8 addr, u8 *val) return ft5x06_i2c_read(client, &addr, 1, val, 1); } +#ifdef CONFIG_TOUCHSCREEN_FT5X06_PSENSOR +static void ft5x06_psensor_enable(struct ft5x06_ts_data *data, int enable) +{ + u8 state; + int ret = -1; + + if (data->client == NULL) + return; + + ft5x0x_read_reg(data->client, FT_REG_PSENSOR_ENABLE, &state); + if (enable) + state |= FT_PSENSOR_ENABLE_MASK; + else + state &= ~FT_PSENSOR_ENABLE_MASK; + + ret = ft5x0x_write_reg(data->client, FT_REG_PSENSOR_ENABLE, state); + if (ret < 0) + dev_err(&data->client->dev, + "write psensor switch command failed\n"); +} + +static int ft5x06_psensor_enable_set(struct sensors_classdev *sensors_cdev, + unsigned int enable) +{ + struct ft5x06_psensor_platform_data *psensor_pdata = + container_of(sensors_cdev, + struct ft5x06_psensor_platform_data, ps_cdev); + struct ft5x06_ts_data *data = psensor_pdata->data; + struct input_dev *input_dev = data->psensor_pdata->input_psensor_dev; + + mutex_lock(&input_dev->mutex); + ft5x06_psensor_enable(data, enable); + psensor_pdata->tp_psensor_data = FT_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; +} + +static int ft5x06_read_tp_psensor_data(struct ft5x06_ts_data *data) +{ + u8 psensor_status; + char tmp; + int ret = 0; + + ft5x0x_read_reg(data->client, + FT_REG_PSENSOR_STATUS, &psensor_status); + + tmp = data->psensor_pdata->tp_psensor_data; + if (psensor_status == FT_PSENSOR_STATUS_NEAR) + data->psensor_pdata->tp_psensor_data = + FT_PSENSOR_FAR_TO_NEAR; + else if (psensor_status == FT_PSENSOR_STATUS_FAR) + data->psensor_pdata->tp_psensor_data = + FT_PSENSOR_NEAR_TO_FAR; + + if (tmp != data->psensor_pdata->tp_psensor_data) { + dev_info(&data->client->dev, + "%s sensor data changed\n", __func__); + ret = 1; + } + return ret; +} +#else +static int ft5x06_psensor_enable_set(struct sensors_classdev *sensors_cdev, + unsigned int enable) +{ + return enable; +} + +static int ft5x06_read_tp_psensor_data(struct ft5x06_ts_data *data) +{ + return 0; +} +#endif + static void ft5x06_update_fw_vendor_id(struct ft5x06_ts_data *data) { struct i2c_client *client = data->client; @@ -349,7 +465,7 @@ static irqreturn_t ft5x06_ts_interrupt(int irq, void *dev_id) struct input_dev *ip_dev; int rc, i; u32 id, x, y, status, num_touches; - u8 reg = 0x00, *buf; + u8 reg, *buf; bool update_input = false; if (!data) { @@ -360,8 +476,31 @@ static irqreturn_t ft5x06_ts_interrupt(int irq, void *dev_id) ip_dev = data->input_dev; buf = data->tch_data; - rc = ft5x06_i2c_read(data->client, ®, 1, - buf, data->tch_data_len); + if (ft5x06_psensor_support_enabled() && data->pdata->psensor_support && + data->psensor_pdata->tp_psensor_opened) { + rc = ft5x06_read_tp_psensor_data(data); + if (rc) { + if (data->suspended) + pm_wakeup_event(&data->client->dev, + FT_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 IRQ_HANDLED; + } + if (data->suspended) + return IRQ_HANDLED; + } + + /** + * Read touch data start from register FT_REG_DEV_MODE. + * The touch x/y value start from FT_TOUCH_X_H/L_POS and + * FT_TOUCH_Y_H/L_POS in buf. + */ + reg = FT_REG_DEV_MODE; + rc = ft5x06_i2c_read(data->client, ®, 1, buf, data->tch_data_len); if (rc < 0) { dev_err(&data->client->dev, "%s: read data fail\n", __func__); return IRQ_HANDLED; @@ -661,6 +800,16 @@ static int ft5x06_ts_suspend(struct device *dev) return 0; } + if (ft5x06_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; + } disable_irq(data->client->irq); /* release all touches */ @@ -745,6 +894,18 @@ static int ft5x06_ts_resume(struct device *dev) return 0; } + if (ft5x06_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; + } + if (data->pdata->power_on) { err = data->pdata->power_on(true); if (err) { @@ -1588,6 +1749,8 @@ static int ft5x06_parse_dt(struct device *dev, 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; @@ -1623,8 +1786,10 @@ static int ft5x06_ts_probe(struct i2c_client *client, const struct i2c_device_id *id) { struct ft5x06_ts_platform_data *pdata; + struct ft5x06_psensor_platform_data *psensor_pdata; struct ft5x06_ts_data *data; struct input_dev *input_dev; + struct input_dev *psensor_input_dev; struct dentry *temp; u8 reg_value; u8 reg_addr; @@ -1781,17 +1946,61 @@ static int ft5x06_ts_probe(struct i2c_client *client, err = request_threaded_irq(client->irq, NULL, ft5x06_ts_interrupt, - pdata->irqflags | IRQF_ONESHOT, + IRQF_ONESHOT, client->dev.driver->name, data); if (err) { dev_err(&client->dev, "request irq failed\n"); goto free_gpio; } + if (ft5x06_psensor_support_enabled() && data->pdata->psensor_support) { + device_init_wakeup(&client->dev, 1); + psensor_pdata = devm_kzalloc(&client->dev, + sizeof(struct ft5x06_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 = + ft5x06_psensor_enable_set; + psensor_pdata->data = data; + + err = sensors_classdev_register(&client->dev, + &psensor_pdata->ps_cdev); + if (err) + goto unregister_psensor_input_device; + } + err = device_create_file(&client->dev, &dev_attr_fw_name); if (err) { dev_err(&client->dev, "sys file creation failed\n"); - goto irq_free; + goto free_psensor_class_sysfs; } err = device_create_file(&client->dev, &dev_attr_update_fw); @@ -1900,7 +2109,23 @@ free_update_fw_sys: device_remove_file(&client->dev, &dev_attr_update_fw); free_fw_name_sys: device_remove_file(&client->dev, &dev_attr_fw_name); +free_psensor_class_sysfs: + if (ft5x06_psensor_support_enabled() && data->pdata->psensor_support) + sensors_classdev_unregister(&psensor_pdata->ps_cdev); +unregister_psensor_input_device: + if (ft5x06_psensor_support_enabled() && data->pdata->psensor_support) + input_unregister_device(data->psensor_pdata->input_psensor_dev); +free_psensor_input_dev: + if (ft5x06_psensor_support_enabled() && data->pdata->psensor_support) + input_free_device(data->psensor_pdata->input_psensor_dev); +free_psensor_pdata: + if (ft5x06_psensor_support_enabled() && data->pdata->psensor_support) { + devm_kfree(&client->dev, psensor_pdata); + data->psensor_pdata = NULL; + } irq_free: + if (ft5x06_psensor_support_enabled() && data->pdata->psensor_support) + device_init_wakeup(&client->dev, 0); free_irq(client->irq, data); free_gpio: if (gpio_is_valid(pdata->reset_gpio)) @@ -1930,9 +2155,9 @@ pwr_deinit: ft5x06_power_init(data, false); unreg_inputdev: input_unregister_device(input_dev); - input_dev = NULL; free_inputdev: input_free_device(input_dev); + input_dev = NULL; return err; } @@ -1941,6 +2166,16 @@ static int ft5x06_ts_remove(struct i2c_client *client) struct ft5x06_ts_data *data = i2c_get_clientdata(client); int retval; + if (ft5x06_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); + input_free_device(data->psensor_pdata->input_psensor_dev); + devm_kfree(&client->dev, data->psensor_pdata); + data->psensor_pdata = NULL; + } + debugfs_remove_recursive(data->dir); device_remove_file(&client->dev, &dev_attr_force_update_fw); device_remove_file(&client->dev, &dev_attr_update_fw); diff --git a/include/linux/input/ft5x06_ts.h b/include/linux/input/ft5x06_ts.h index 5242f96707ac..bd37af71fe0d 100644 --- a/include/linux/input/ft5x06_ts.h +++ b/include/linux/input/ft5x06_ts.h @@ -34,6 +34,14 @@ struct fw_upgrade_info { u16 delay_erase_flash; }; +struct ft5x06_psensor_platform_data { + struct input_dev *input_psensor_dev; + struct sensors_classdev ps_cdev; + int tp_psensor_opened; + char tp_psensor_data; /* 0 near, 1 far */ + struct ft5x06_ts_data *data; +}; + struct ft5x06_ts_platform_data { struct fw_upgrade_info info; const char *name; @@ -60,6 +68,7 @@ struct ft5x06_ts_platform_data { bool no_force_update; bool i2c_pull_up; bool ignore_id_check; + bool psensor_support; int (*power_init)(bool); int (*power_on)(bool); };