From a5b633b984a35442de41b14ed26e8c54a5f40fa2 Mon Sep 17 00:00:00 2001 From: Mohan Pallaka Date: Fri, 7 Jun 2013 17:47:42 +0530 Subject: [PATCH 1/9] input: ft5x06_ts: Add debugfs support Add debugfs support to provide addr and data entries to read registers from controller. Writing into the registers is not supported as it might corrupt controller. Also add suspend entry to dynamically test suspend and resume. This patch is propagated from 3.18 kernel 'commit 0804395c18d8 ("input: ft5x06_ts: Add debugfs support")' Change-Id: If61bbbefdbf624f62b07c2a96d28c8338bd948c1 Signed-off-by: Mohan Pallaka Signed-off-by: Shantanu Jain --- drivers/input/touchscreen/ft5x06_ts.c | 172 ++++++++++++++++++++++++++ 1 file changed, 172 insertions(+) diff --git a/drivers/input/touchscreen/ft5x06_ts.c b/drivers/input/touchscreen/ft5x06_ts.c index 45ce036b1693..2d8933c113a9 100644 --- a/drivers/input/touchscreen/ft5x06_ts.c +++ b/drivers/input/touchscreen/ft5x06_ts.c @@ -27,6 +27,7 @@ #include #include #include +#include #include #if defined(CONFIG_FB) @@ -167,6 +168,8 @@ #define FT_REG_CAL 0x00 #define FT_CAL_MASK 0x70 +#define FT_DEBUG_DIR_NAME "ft_debug" + struct ts_event { u16 x[CFG_MAX_TOUCH_POINTS]; /* x coordinate */ u16 y[CFG_MAX_TOUCH_POINTS]; /* y coordinate */ @@ -196,6 +199,9 @@ struct ft5x06_ts_data { char fw_name[FT_FW_NAME_MAX_LEN]; bool loading_fw; u8 family_id; + struct dentry *dir; + u16 addr; + bool suspended; #if defined(CONFIG_FB) struct notifier_block fb_notif; #elif defined(CONFIG_HAS_EARLYSUSPEND) @@ -475,6 +481,16 @@ static int ft5x06_ts_suspend(struct device *dev) struct ft5x06_ts_data *data = dev_get_drvdata(dev); char txbuf[2]; + 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; + } + disable_irq(data->client->irq); if (gpio_is_valid(data->pdata->reset_gpio)) { @@ -483,6 +499,8 @@ static int ft5x06_ts_suspend(struct device *dev) ft5x06_i2c_write(data->client, txbuf, sizeof(txbuf)); } + data->suspended = true; + return 0; } @@ -490,6 +508,11 @@ static int ft5x06_ts_resume(struct device *dev) { struct ft5x06_ts_data *data = dev_get_drvdata(dev); + if (!data->suspended) { + dev_info(dev, "Already in awake state\n"); + return 0; + } + if (gpio_is_valid(data->pdata->reset_gpio)) { gpio_set_value_cansleep(data->pdata->reset_gpio, 0); msleep(FT_RESET_DLY); @@ -497,6 +520,8 @@ static int ft5x06_ts_resume(struct device *dev) } enable_irq(data->client->irq); + data->suspended = false; + return 0; } @@ -882,6 +907,117 @@ static ssize_t ft5x06_fw_name_store(struct device *dev, static DEVICE_ATTR(fw_name, 0664, ft5x06_fw_name_show, ft5x06_fw_name_store); +static bool ft5x06_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; +} + +static int ft5x06_debug_data_set(void *_data, u64 val) +{ + struct ft5x06_ts_data *data = _data; + + mutex_lock(&data->input_dev->mutex); + + if (ft5x06_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; +} + +static int ft5x06_debug_data_get(void *_data, u64 *val) +{ + struct ft5x06_ts_data *data = _data; + int rc; + u8 reg; + + mutex_lock(&data->input_dev->mutex); + + if (ft5x06_debug_addr_is_valid(data->addr)) { + rc = ft5x0x_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, ft5x06_debug_data_get, + ft5x06_debug_data_set, "0x%02llX\n"); + +static int ft5x06_debug_addr_set(void *_data, u64 val) +{ + struct ft5x06_ts_data *data = _data; + + if (ft5x06_debug_addr_is_valid(val)) { + mutex_lock(&data->input_dev->mutex); + data->addr = val; + mutex_unlock(&data->input_dev->mutex); + } + + return 0; +} + +static int ft5x06_debug_addr_get(void *_data, u64 *val) +{ + struct ft5x06_ts_data *data = _data; + + mutex_lock(&data->input_dev->mutex); + + if (ft5x06_debug_addr_is_valid(data->addr)) + *val = data->addr; + + mutex_unlock(&data->input_dev->mutex); + + return 0; +} + +DEFINE_SIMPLE_ATTRIBUTE(debug_addr_fops, ft5x06_debug_addr_get, + ft5x06_debug_addr_set, "0x%02llX\n"); + +static int ft5x06_debug_suspend_set(void *_data, u64 val) +{ + struct ft5x06_ts_data *data = _data; + + mutex_lock(&data->input_dev->mutex); + + if (val) + ft5x06_ts_suspend(&data->client->dev); + else + ft5x06_ts_resume(&data->client->dev); + + mutex_unlock(&data->input_dev->mutex); + + return 0; +} + +static int ft5x06_debug_suspend_get(void *_data, u64 *val) +{ + struct ft5x06_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, ft5x06_debug_suspend_get, + ft5x06_debug_suspend_set, "%lld\n"); + #ifdef CONFIG_OF static int ft5x06_get_dt_coords(struct device *dev, char *name, struct ft5x06_ts_platform_data *pdata) @@ -997,6 +1133,7 @@ static int ft5x06_ts_probe(struct i2c_client *client, struct ft5x06_ts_platform_data *pdata; struct ft5x06_ts_data *data; struct input_dev *input_dev; + struct dentry *dir, *temp; u8 reg_value; u8 reg_addr; int err; @@ -1192,6 +1329,36 @@ static int ft5x06_ts_probe(struct i2c_client *client, goto free_update_fw_sys; } + dir = debugfs_create_dir(FT_DEBUG_DIR_NAME, NULL); + if (dir == NULL || IS_ERR(dir)) { + pr_err("debugfs_create_dir failed: rc=%ld\n", PTR_ERR(dir)); + err = PTR_ERR(dir); + goto free_force_update_fw_sys; + } + + temp = debugfs_create_file("addr", S_IRUSR | S_IWUSR, 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, 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, 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; + } #if defined(CONFIG_FB) data->fb_notif.notifier_call = fb_notifier_callback; @@ -1211,6 +1378,10 @@ static int ft5x06_ts_probe(struct i2c_client *client, return 0; +free_debug_dir: + debugfs_remove_recursive(data->dir); +free_force_update_fw_sys: + device_remove_file(&client->dev, &dev_attr_force_update_fw); free_update_fw_sys: device_remove_file(&client->dev, &dev_attr_update_fw); free_fw_name_sys: @@ -1247,6 +1418,7 @@ static int ft5x06_ts_remove(struct i2c_client *client) { struct ft5x06_ts_data *data = i2c_get_clientdata(client); + debugfs_remove_recursive(data->dir); device_remove_file(&client->dev, &dev_attr_force_update_fw); device_remove_file(&client->dev, &dev_attr_update_fw); device_remove_file(&client->dev, &dev_attr_fw_name); From 19bdb8e2d1b5cf11f7b3838ba6bc38f3930e081c Mon Sep 17 00:00:00 2001 From: Mohan Pallaka Date: Fri, 14 Jun 2013 17:12:24 +0530 Subject: [PATCH 2/9] input: ft5x06_ts: Add support for protocol B Focaltech touchscreen chips can use MT protocol B because they can assign unique id to ABS_MT_TRACKING_ID from finger id provided by hardware. This patch is propagated from 3.18 kernel 'commit 836a4745d978 ("input: ft5x06_ts: Add support for protocol B")' Change-Id: I7f236d819eb805934e12faedaf84407b9de95c0e Signed-off-by: Mohan Pallaka Signed-off-by: Shantanu Jain --- drivers/input/touchscreen/ft5x06_ts.c | 31 ++++++++++++++++----------- 1 file changed, 18 insertions(+), 13 deletions(-) diff --git a/drivers/input/touchscreen/ft5x06_ts.c b/drivers/input/touchscreen/ft5x06_ts.c index 2d8933c113a9..2ab6b7da26b1 100644 --- a/drivers/input/touchscreen/ft5x06_ts.c +++ b/drivers/input/touchscreen/ft5x06_ts.c @@ -18,6 +18,7 @@ #include #include +#include #include #include #include @@ -298,17 +299,22 @@ static void ft5x06_report_value(struct ft5x06_ts_data *data) event->pressure = 0; } - input_report_abs(data->input_dev, ABS_MT_POSITION_X, - event->x[i]); - input_report_abs(data->input_dev, ABS_MT_POSITION_Y, - event->y[i]); - input_report_abs(data->input_dev, ABS_MT_PRESSURE, - event->pressure); - input_report_abs(data->input_dev, ABS_MT_TRACKING_ID, - event->finger_id[i]); - input_report_abs(data->input_dev, ABS_MT_TOUCH_MAJOR, - event->pressure); - input_mt_sync(data->input_dev); + input_mt_slot(data->input_dev, i); + input_mt_report_slot_state(data->input_dev, MT_TOOL_FINGER, + !!event->pressure); + + if (event->pressure == FT_PRESS) { + input_report_abs(data->input_dev, ABS_MT_POSITION_X, + event->x[i]); + input_report_abs(data->input_dev, ABS_MT_POSITION_Y, + event->y[i]); + input_report_abs(data->input_dev, ABS_MT_PRESSURE, + event->pressure); + input_report_abs(data->input_dev, ABS_MT_TRACKING_ID, + event->finger_id[i]); + input_report_abs(data->input_dev, ABS_MT_TOUCH_MAJOR, + event->pressure); + } } input_report_key(data->input_dev, BTN_TOUCH, !!fingerdown); @@ -1187,12 +1193,11 @@ 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, CFG_MAX_TOUCH_POINTS); 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_TRACKING_ID, 0, - CFG_MAX_TOUCH_POINTS, 0, 0); input_set_abs_params(input_dev, ABS_MT_TOUCH_MAJOR, 0, FT_PRESS, 0, 0); input_set_abs_params(input_dev, ABS_MT_PRESSURE, 0, FT_PRESS, 0, 0); From c72652e02fc4a7ea084810d37918ae6331542ea0 Mon Sep 17 00:00:00 2001 From: Mohan Pallaka Date: Fri, 14 Jun 2013 17:50:10 +0530 Subject: [PATCH 3/9] input: ft5x06_ts: Add debugfs entry for dump info Add a debugfs entry to display information about controller to the user. Also have cleared the checkpatch errors on 3.18 This patch is propagated from 3.18 kernel 'commit 71e8bfde1da8 ("input: ft5x06_ts: Add debugfs entry for dump info")' Change-Id: I075501fefe1984533139ecca65af92f3c0ab8b97 Signed-off-by: Mohan Pallaka Signed-off-by: Sudhakar Manapati --- drivers/input/touchscreen/ft5x06_ts.c | 110 ++++++++++++++++++-------- 1 file changed, 78 insertions(+), 32 deletions(-) diff --git a/drivers/input/touchscreen/ft5x06_ts.c b/drivers/input/touchscreen/ft5x06_ts.c index 2ab6b7da26b1..8e4d146186f7 100644 --- a/drivers/input/touchscreen/ft5x06_ts.c +++ b/drivers/input/touchscreen/ft5x06_ts.c @@ -169,7 +169,15 @@ #define FT_REG_CAL 0x00 #define FT_CAL_MASK 0x70 -#define FT_DEBUG_DIR_NAME "ft_debug" +#define FT_INFO_MAX_LEN 200 + +#define FT_STORE_TS_INFO(buf, id, fw_ver) \ + snprintf(buf, FT_INFO_MAX_LEN, \ + "controller\t= focaltech\n" \ + "model\t\t= 0x%x\n" \ + "fw_ver\t\t= 0x%x\n", id, fw_ver) + +#define FT_DEBUG_DIR_NAME "ts_debug" struct ts_event { u16 x[CFG_MAX_TOUCH_POINTS]; /* x coordinate */ @@ -203,6 +211,7 @@ struct ft5x06_ts_data { struct dentry *dir; u16 addr; bool suspended; + char *ts_info; #if defined(CONFIG_FB) struct notifier_block fb_notif; #elif defined(CONFIG_HAS_EARLYSUSPEND) @@ -816,6 +825,7 @@ static int ft5x06_fw_upgrade(struct device *dev, bool force) rc = -EIO; } + FT_STORE_TS_INFO(data->ts_info, data->family_id, FT_FW_FILE_VER(fw)); rel_fw: release_firmware(fw); return rc; @@ -1024,6 +1034,27 @@ static int ft5x06_debug_suspend_get(void *_data, u64 *val) DEFINE_SIMPLE_ATTRIBUTE(debug_suspend_fops, ft5x06_debug_suspend_get, ft5x06_debug_suspend_set, "%lld\n"); +static int ft5x06_debug_dump_info(struct seq_file *m, void *v) +{ + struct ft5x06_ts_data *data = m->private; + + seq_printf(m, "%s\n", data->ts_info); + + return 0; +} + +static int debugfs_dump_info_open(struct inode *inode, struct file *file) +{ + return single_open(file, ft5x06_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 CONFIG_OF static int ft5x06_get_dt_coords(struct device *dev, char *name, struct ft5x06_ts_platform_data *pdata) @@ -1139,7 +1170,7 @@ static int ft5x06_ts_probe(struct i2c_client *client, struct ft5x06_ts_platform_data *pdata; struct ft5x06_ts_data *data; struct input_dev *input_dev; - struct dentry *dir, *temp; + struct dentry *temp; u8 reg_value; u8 reg_addr; int err; @@ -1286,28 +1317,6 @@ static int ft5x06_ts_probe(struct i2c_client *client, data->family_id = reg_value; - /* get some register information */ - reg_addr = FT_REG_FW_VER; - err = ft5x06_i2c_read(client, ®_addr, 1, ®_value, 1); - if (err < 0) - dev_err(&client->dev, "version read failed"); - - dev_info(&client->dev, "Firmware version = 0x%x\n", reg_value); - - reg_addr = FT_REG_POINT_RATE; - ft5x06_i2c_read(client, ®_addr, 1, ®_value, 1); - if (err < 0) - dev_err(&client->dev, "report rate read failed"); - - dev_dbg(&client->dev, "report rate = %dHz\n", reg_value * 10); - - reg_addr = FT_REG_THGROUP; - err = ft5x06_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); - err = request_threaded_irq(client->irq, NULL, ft5x06_ts_interrupt, pdata->irqflags, client->dev.driver->name, data); @@ -1334,14 +1343,14 @@ static int ft5x06_ts_probe(struct i2c_client *client, goto free_update_fw_sys; } - dir = debugfs_create_dir(FT_DEBUG_DIR_NAME, NULL); - if (dir == NULL || IS_ERR(dir)) { - pr_err("debugfs_create_dir failed: rc=%ld\n", PTR_ERR(dir)); - err = PTR_ERR(dir); + data->dir = debugfs_create_dir(FT_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); goto free_force_update_fw_sys; } - temp = debugfs_create_file("addr", S_IRUSR | S_IWUSR, dir, data, + 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)); @@ -1349,7 +1358,7 @@ static int ft5x06_ts_probe(struct i2c_client *client, goto free_debug_dir; } - temp = debugfs_create_file("data", S_IRUSR | S_IWUSR, dir, data, + 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)); @@ -1357,14 +1366,50 @@ static int ft5x06_ts_probe(struct i2c_client *client, goto free_debug_dir; } - temp = debugfs_create_file("suspend", S_IRUSR | S_IWUSR, dir, data, - &debug_suspend_fops); + 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 = kzalloc(FT_INFO_MAX_LEN, GFP_KERNEL); + if (!data->ts_info) + goto free_debug_dir; + + /*get some register information */ + reg_addr = FT_REG_POINT_RATE; + ft5x06_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 = FT_REG_THGROUP; + err = ft5x06_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); + + reg_addr = FT_REG_FW_VER; + err = ft5x06_i2c_read(client, ®_addr, 1, ®_value, 1); + if (err < 0) + dev_err(&client->dev, "version read failed"); + + dev_info(&client->dev, "Firmware version = 0x%x\n", reg_value); + + FT_STORE_TS_INFO(data->ts_info, data->family_id, reg_value); + #if defined(CONFIG_FB) data->fb_notif.notifier_call = fb_notifier_callback; @@ -1453,6 +1498,7 @@ static int ft5x06_ts_remove(struct i2c_client *client) ft5x06_power_init(data, false); input_unregister_device(data->input_dev); + kfree(data->ts_info); kfree(data); return 0; From 849d4f3c1c389abc019511467a48594ba82298bc Mon Sep 17 00:00:00 2001 From: Figo Wang Date: Fri, 28 Jun 2013 19:49:49 +0800 Subject: [PATCH 4/9] input: touchpanel: Release all touches during suspend Release all the touches before going to suspend to avoid sticky touches and correct multitouch ID error. This patch is propagated from 3.18 kernel 'commit 8a123ff1f64b ("input: touchpanel: Release all touches during suspend")' Change-Id: I5dd84d44478291e16fd577aad5cf06503e44fbde Signed-off-by: Figo Wang --- drivers/input/touchscreen/ft5x06_ts.c | 17 ++++++++++------- 1 file changed, 10 insertions(+), 7 deletions(-) diff --git a/drivers/input/touchscreen/ft5x06_ts.c b/drivers/input/touchscreen/ft5x06_ts.c index 8e4d146186f7..53d66b2ac924 100644 --- a/drivers/input/touchscreen/ft5x06_ts.c +++ b/drivers/input/touchscreen/ft5x06_ts.c @@ -308,7 +308,7 @@ static void ft5x06_report_value(struct ft5x06_ts_data *data) event->pressure = 0; } - input_mt_slot(data->input_dev, i); + input_mt_slot(data->input_dev, event->finger_id[i]); input_mt_report_slot_state(data->input_dev, MT_TOOL_FINGER, !!event->pressure); @@ -317,10 +317,6 @@ static void ft5x06_report_value(struct ft5x06_ts_data *data) event->x[i]); input_report_abs(data->input_dev, ABS_MT_POSITION_Y, event->y[i]); - input_report_abs(data->input_dev, ABS_MT_PRESSURE, - event->pressure); - input_report_abs(data->input_dev, ABS_MT_TRACKING_ID, - event->finger_id[i]); input_report_abs(data->input_dev, ABS_MT_TOUCH_MAJOR, event->pressure); } @@ -494,7 +490,7 @@ pwr_deinit: static int ft5x06_ts_suspend(struct device *dev) { struct ft5x06_ts_data *data = dev_get_drvdata(dev); - char txbuf[2]; + char txbuf[2], i; if (data->loading_fw) { dev_info(dev, "Firmware loading in process...\n"); @@ -508,6 +504,14 @@ static int ft5x06_ts_suspend(struct device *dev) disable_irq(data->client->irq); + /* release all touches */ + for (i = 0; i < CFG_MAX_TOUCH_POINTS; i++) { + 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_sync(data->input_dev); + if (gpio_is_valid(data->pdata->reset_gpio)) { txbuf[0] = FT_REG_PMODE; txbuf[1] = FT_PMODE_HIBERNATE; @@ -1230,7 +1234,6 @@ static int ft5x06_ts_probe(struct i2c_client *client, 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, FT_PRESS, 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 faca745b0bd0c8a569dcede5cc11ab5a5162b6e7 Mon Sep 17 00:00:00 2001 From: Shantanu Jain Date: Mon, 8 Jul 2013 19:11:07 +0530 Subject: [PATCH 5/9] input: ft5x06_ts: Handle error if i2c read fails Handle error if i2c read fails and undo other probe initializations. This patch is propagated from 3.18 kernel 'commit 0123456789ab ("input: ft5x06_ts: Handle error if i2c read fails")' Change-Id: I26180b057b45f4dad123dd3581e5c5f2b33a32aa Signed-off-by: Shantanu Jain --- 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 53d66b2ac924..3d9361de9836 100644 --- a/drivers/input/touchscreen/ft5x06_ts.c +++ b/drivers/input/touchscreen/ft5x06_ts.c @@ -1308,7 +1308,7 @@ 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"); - return err; + goto free_reset_gpio; } dev_info(&client->dev, "Device ID = 0x%x\n", reg_value); From e6d2a8e4bbf1e8068274adedbcd8f35e7aae6884 Mon Sep 17 00:00:00 2001 From: Mohan Pallaka Date: Mon, 15 Jul 2013 16:17:46 +0530 Subject: [PATCH 6/9] input: ft5x06_ts: Disable regulators in suspend Disable/enable the regulators in suspend/resume to have better power savings. This patch is propagated from 3.18 kernel 'commit 550c106ea1a3 ("input: ft5x06_ts: Disable regulators in suspend")' Change-Id: I8aa7e941f20040955d6cc177e70ed38dbd28af8c Signed-off-by: Mohan Pallaka --- drivers/input/touchscreen/ft5x06_ts.c | 39 +++++++++++++++++++++++++++ 1 file changed, 39 insertions(+) diff --git a/drivers/input/touchscreen/ft5x06_ts.c b/drivers/input/touchscreen/ft5x06_ts.c index 3d9361de9836..b55fea10cb30 100644 --- a/drivers/input/touchscreen/ft5x06_ts.c +++ b/drivers/input/touchscreen/ft5x06_ts.c @@ -491,6 +491,7 @@ static int ft5x06_ts_suspend(struct device *dev) { struct ft5x06_ts_data *data = dev_get_drvdata(dev); char txbuf[2], i; + int err; if (data->loading_fw) { dev_info(dev, "Firmware loading in process...\n"); @@ -518,20 +519,58 @@ static int ft5x06_ts_suspend(struct device *dev) ft5x06_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 = ft5x06_power_on(data, false); + if (err) { + dev_err(dev, "power off failed"); + goto pwr_off_fail; + } + } + data->suspended = true; return 0; + +pwr_off_fail: + if (gpio_is_valid(data->pdata->reset_gpio)) { + gpio_set_value_cansleep(data->pdata->reset_gpio, 0); + msleep(FT_RESET_DLY); + gpio_set_value_cansleep(data->pdata->reset_gpio, 1); + } + enable_irq(data->client->irq); + return err; } static int ft5x06_ts_resume(struct device *dev) { struct ft5x06_ts_data *data = dev_get_drvdata(dev); + int err; if (!data->suspended) { dev_info(dev, "Already in awake state\n"); return 0; } + if (data->pdata->power_on) { + err = data->pdata->power_on(true); + if (err) { + dev_err(dev, "power on failed"); + return err; + } + } else { + err = ft5x06_power_on(data, true); + if (err) { + dev_err(dev, "power on failed"); + return err; + } + } + if (gpio_is_valid(data->pdata->reset_gpio)) { gpio_set_value_cansleep(data->pdata->reset_gpio, 0); msleep(FT_RESET_DLY); From 8f5d6d8fd0e762c0db47b3a4a995b261f6ce3dc6 Mon Sep 17 00:00:00 2001 From: Figo Wang Date: Thu, 18 Jul 2013 15:15:03 +0800 Subject: [PATCH 7/9] input: touchpanel: fix the number of touch events detection Focaltech controllers can reports the number of touch points by read "TD_STATUS" register. Read this register to avoid always looping through maximum number of touches supported. This patch is propagated from 3.18 kernel 'commit 4c142d16fced ("input: touchpanel: fix the number of touch events detection")' Change-Id: I8ab111b56b2074cb52cb021d37e9e6505c8b7c72 Signed-off-by: Figo Wang Signed-off-by: Bingzhe Cai --- drivers/input/touchscreen/ft5x06_ts.c | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/drivers/input/touchscreen/ft5x06_ts.c b/drivers/input/touchscreen/ft5x06_ts.c index b55fea10cb30..2f30e9234708 100644 --- a/drivers/input/touchscreen/ft5x06_ts.c +++ b/drivers/input/touchscreen/ft5x06_ts.c @@ -82,6 +82,7 @@ #define FT_ERASE_PANEL_REG 0x63 #define FT_FW_START_REG 0xBF +#define FT_STATUS_NUM_TP_MASK 0x0F #define FT_VTG_MIN_UV 2600000 #define FT_VTG_MAX_UV 3300000 @@ -329,6 +330,7 @@ static void ft5x06_report_value(struct ft5x06_ts_data *data) static int ft5x06_handle_touchdata(struct ft5x06_ts_data *data) { struct ts_event *event = &data->event; + int num_points; int ret, i; u8 buf[POINT_READ_BUF] = { 0 }; u8 pointid = FT_MAX_ID; @@ -342,7 +344,9 @@ static int ft5x06_handle_touchdata(struct ft5x06_ts_data *data) memset(event, 0, sizeof(struct ts_event)); event->touch_point = 0; - for (i = 0; i < CFG_MAX_TOUCH_POINTS; i++) { + num_points = buf[2] & FT_STATUS_NUM_TP_MASK; + + for (i = 0; i < num_points; i++) { pointid = (buf[FT_TOUCH_ID_POS + FT_TOUCH_STEP * i]) >> 4; if (pointid >= FT_MAX_ID) break; From cb040c2f612803398eed9271c3aea2ade4aec39d Mon Sep 17 00:00:00 2001 From: Mohan Pallaka Date: Tue, 23 Jul 2013 15:35:28 +0530 Subject: [PATCH 8/9] input: ft5x06_ts: Add firmware upgrade support for ft6x06 Focaltech 6x06 controller series uses a different reset register compared to earlier supported controllers. Use appropriate reset registers based on controller's id. This patch is propagated from 3.18 kernel 'commit 88dd4c27a3f6 ("input: ft5x06_ts: Add firmware upgrade support for ft6x06")' Change-Id: Ia72fa9c256f9e6e2db79388b0152f4d6724ec457 Signed-off-by: Mohan Pallaka --- drivers/input/touchscreen/ft5x06_ts.c | 40 +++++++++++++++++++++------ 1 file changed, 32 insertions(+), 8 deletions(-) diff --git a/drivers/input/touchscreen/ft5x06_ts.c b/drivers/input/touchscreen/ft5x06_ts.c index 2f30e9234708..99f86224999d 100644 --- a/drivers/input/touchscreen/ft5x06_ts.c +++ b/drivers/input/touchscreen/ft5x06_ts.c @@ -76,7 +76,8 @@ #define FT_PMODE_HIBERNATE 0x03 #define FT_FACTORYMODE_VALUE 0x40 #define FT_WORKMODE_VALUE 0x00 -#define FT_RST_CMD_REG 0xFC +#define FT_RST_CMD_REG1 0xFC +#define FT_RST_CMD_REG2 0xBC #define FT_READ_ID_REG 0x90 #define FT_ERASE_APP_REG 0x61 #define FT_ERASE_PANEL_REG 0x63 @@ -98,6 +99,7 @@ #define FT5316_ID 0x0A #define FT5306I_ID 0x55 +#define FT6X06_ID 0x06 #define FT_UPGRADE_AA 0xAA #define FT_UPGRADE_55 0x55 @@ -134,6 +136,14 @@ #define FT6208_UPGRADE_READID_DELAY 10 #define FT6208_UPGRADE_EARSE_DELAY 2000 +/*upgrade config of FT6x06*/ +#define FT6X06_UPGRADE_AA_DELAY 100 +#define FT6X06_UPGRADE_55_DELAY 30 +#define FT6X06_UPGRADE_ID_1 0x79 +#define FT6X06_UPGRADE_ID_2 0x08 +#define FT6X06_UPGRADE_READID_DELAY 10 +#define FT6X06_UPGRADE_EARSE_DELAY 2000 + #define FT_UPGRADE_INFO(x, y) do { \ x->delay_55 = y##_UPGRADE_55_DELAY; \ x->delay_aa = y##_UPGRADE_AA_DELAY; \ @@ -162,7 +172,7 @@ #define FT_FW_LAST_PKT 0x6ffa #define FT_EARSE_DLY_MS 100 -#define FT_UPGRADE_LOOP 3 +#define FT_UPGRADE_LOOP 10 #define FT_CAL_START 0x04 #define FT_CAL_FIN 0x00 #define FT_CAL_STORE 0x05 @@ -580,6 +590,9 @@ static int ft5x06_ts_resume(struct device *dev) msleep(FT_RESET_DLY); gpio_set_value_cansleep(data->pdata->reset_gpio, 1); } + + msleep(FT_STARTUP_DLY); + enable_irq(data->client->irq); data->suspended = false; @@ -679,6 +692,9 @@ static int ft5x06_get_upgrade_info(u8 family_id, struct upgrade_info *info) case FT5316_ID: FT_UPGRADE_INFO(info, FT5316); break; + case FT6X06_ID: + FT_UPGRADE_INFO(info, FT6X06); + break; default: return -EINVAL; } @@ -691,6 +707,7 @@ static int ft5x06_fw_upgrade_start(struct i2c_client *client, { struct ft5x06_ts_data *ts_data = i2c_get_clientdata(client); struct upgrade_info info; + 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; @@ -703,22 +720,27 @@ static int ft5x06_fw_upgrade_start(struct i2c_client *client, return -EINVAL; } - for (i = 0; i < FT_UPGRADE_LOOP; i++) { - /* reset - write 0xaa and 0x55 to register 0xfc */ - ft5x0x_write_reg(client, FT_RST_CMD_REG, FT_UPGRADE_AA); + for (i = 0, j = 0; i < FT_UPGRADE_LOOP; i++) { + /* reset - write 0xaa and 0x55 to reset register */ + if (ts_data->family_id == FT6X06_ID) + reset_reg = FT_RST_CMD_REG2; + else + reset_reg = FT_RST_CMD_REG1; + + ft5x0x_write_reg(client, reset_reg, FT_UPGRADE_AA); msleep(info.delay_aa); - ft5x0x_write_reg(client, FT_RST_CMD_REG, FT_UPGRADE_55); + ft5x0x_write_reg(client, reset_reg, FT_UPGRADE_55); msleep(info.delay_55); /* Enter upgrade mode */ w_buf[0] = FT_UPGRADE_55; w_buf[1] = FT_UPGRADE_AA; do { - i++; + j++; rc = ft5x06_i2c_write(client, w_buf, 2); msleep(FT_RETRY_DLY); - } while (rc <= 0 && i < FT_MAX_TRIES); + } while (rc <= 0 && j < FT_MAX_TRIES); /* check READ_ID */ msleep(info.delay_readid); @@ -821,6 +843,8 @@ static int ft5x06_fw_upgrade_start(struct i2c_client *client, ft5x06_i2c_write(client, w_buf, 1); msleep(FT_STARTUP_DLY); + dev_info(&client->dev, "Firmware upgrade successful\n"); + return 0; } From 72436be454654749d7c64676543ae6e7a579b042 Mon Sep 17 00:00:00 2001 From: Mohan Pallaka Date: Tue, 13 Aug 2013 16:11:47 +0530 Subject: [PATCH 9/9] input: ft5x06_ts: Parse board specific parameters from pdata To support all models supported by focaltech push necessary parameters to platform data. This patch is propagated from msm-3.18 kernel 'commit 473f8cd84f93 ("input: ft5x06_ts: Parse board specific parameters from pdata")' Also fix the below checkpatch warnings on 3.18 Kernel. WARNING: Possible unnecessary 'out of memory' message + if (!data) { + dev_err(&client->dev, "Not enough memory\n"); WARNING: Possible unnecessary 'out of memory' message + if (!data->ts_info) { + dev_err(&client->dev, "Not enough memory\n"); Change-Id: I00b0e8ba7cfc8d8fbd48aa910bd3d0010283ed00 Signed-off-by: Mohan Pallaka Signed-off-by: Sudhakar Manapati --- .../bindings/input/touchscreen/ft5x06-ts.txt | 52 ++- drivers/input/touchscreen/ft5x06_ts.c | 429 +++++++++--------- include/linux/input/ft5x06_ts.h | 17 + 3 files changed, 270 insertions(+), 228 deletions(-) diff --git a/Documentation/devicetree/bindings/input/touchscreen/ft5x06-ts.txt b/Documentation/devicetree/bindings/input/touchscreen/ft5x06-ts.txt index 1c7c2c5ea99f..b5e12a1951dd 100644 --- a/Documentation/devicetree/bindings/input/touchscreen/ft5x06-ts.txt +++ b/Documentation/devicetree/bindings/input/touchscreen/ft5x06-ts.txt @@ -22,7 +22,11 @@ Required properties: - focaltech,reset-gpio : reset gpio to control the reset of chip. - focaltech,display-coords : display coordinates in pixels. It is a four tuple consisting of min x, min y, max x and - max y values. + max y values + - focaltech,name : name of the controller + - 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 Optional properties: @@ -32,22 +36,44 @@ Optional properties: - focaltech,i2c-pull-up : to specify pull up is required. - focaltech,no-force-update : to specify force update is allowed. - focaltech,button-map : button map of key codes. The number - of key codes depend on panel. - + of key codes depend on panel + - focaltech,fw-name : specify the firmware file name + - focaltech,fw-delay-aa-ms : specify the "aa" delay in ms for firmware upgrade + - focaltech,fw-delay-55-ms : specify the "55" delay in ms 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 + - focaltech,fw-auto-cal : specify whether calibration is needed after firmware upgrade Example: - i2c@f9924000 { - ft5x06_ts@38 { + i2c@f9923000{ + focaltech@38{ compatible = "focaltech,5x06"; reg = <0x38>; interrupt-parent = <&msmgpio>; - interrupts = <61 0x2>; - vdd-supply = <&pm8941_l22>; - vcc_i2c-supply = <&pm8941_s3>; - focaltech,reset-gpio = <&msmgpio 60 0x00>; - focaltech,irq-gpio = <&msmgpio 61 0x00>; - focaltech,panel-coords = <0 0 480 800>; + interrupts = <1 0x2>; + vdd-supply = <&pm8110_l19>; + vcc_i2c-supply = <&pm8110_l14>; + focaltech,name = "ft6x06"; + focaltech,family-id = <0x06>; + focaltech,reset-gpio = <&msmgpio 0 0x00>; + focaltech,irq-gpio = <&msmgpio 1 0x00>; focaltech,display-coords = <0 0 480 800>; - focaltech,button-map= <158 102 139 217>; - focaltech,family-id = <0x0a>; + focaltech,panel-coords = <0 0 480 800>; + focaltech,button-map= <139 102 158>; + focaltech,no-force-update; + focaltech,i2c-pull-up; + focaltech,group-id = <1>; + focaltech,hard-reset-delay = <20>; + focaltech,soft-reset-delay = <150>; + focaltech,num-max-touches = <2>; + focaltech,fw-name = "ft_8610_qrd_fw.bin"; + focaltech,fw-delay-aa-ms = <100>; + focaltech,fw-delay-55-ms = <30>; + focaltech,fw-upgrade-id1 = <0x79>; + focaltech,fw-upgrade-id2 = <0x08>; + focaltech,fw-delay-readid-ms = <10>; + focaltech,fw-delay-era-flsh-ms = <2000>; + focaltech,fw-auto-cal; }; }; diff --git a/drivers/input/touchscreen/ft5x06_ts.c b/drivers/input/touchscreen/ft5x06_ts.c index 99f86224999d..794ba264abdf 100644 --- a/drivers/input/touchscreen/ft5x06_ts.c +++ b/drivers/input/touchscreen/ft5x06_ts.c @@ -41,22 +41,23 @@ #define FT_SUSPEND_LEVEL 1 #endif -#define CFG_MAX_TOUCH_POINTS 5 +#define FT_DRIVER_VERSION 0x01 -#define FT_STARTUP_DLY 150 -#define FT_RESET_DLY 20 +#define FT_META_REGS 3 +#define FT_ONE_TCH_LEN 6 +#define FT_TCH_LEN(x) (FT_META_REGS + FT_ONE_TCH_LEN * x) #define FT_PRESS 0x7F #define FT_MAX_ID 0x0F -#define FT_TOUCH_STEP 6 #define FT_TOUCH_X_H_POS 3 #define FT_TOUCH_X_L_POS 4 #define FT_TOUCH_Y_H_POS 5 #define FT_TOUCH_Y_L_POS 6 +#define FT_TD_STATUS 2 #define FT_TOUCH_EVENT_POS 3 #define FT_TOUCH_ID_POS 5 - -#define POINT_READ_BUF (3 + FT_TOUCH_STEP * CFG_MAX_TOUCH_POINTS) +#define FT_TOUCH_DOWN 0 +#define FT_TOUCH_CONTACT 2 /* register address*/ #define FT_REG_DEV_MODE 0x00 @@ -104,55 +105,6 @@ #define FT_UPGRADE_AA 0xAA #define FT_UPGRADE_55 0x55 -/* upgrade config of FT5606 */ -#define FT5606_UPGRADE_AA_DELAY 50 -#define FT5606_UPGRADE_55_DELAY 10 -#define FT5606_UPGRADE_ID_1 0x79 -#define FT5606_UPGRADE_ID_2 0x06 -#define FT5606_UPGRADE_READID_DELAY 100 -#define FT5606_UPGRADE_EARSE_DELAY 2000 - -/* upgrade config of FT5316 */ -#define FT5316_UPGRADE_AA_DELAY 50 -#define FT5316_UPGRADE_55_DELAY 30 -#define FT5316_UPGRADE_ID_1 0x79 -#define FT5316_UPGRADE_ID_2 0x07 -#define FT5316_UPGRADE_READID_DELAY 1 -#define FT5316_UPGRADE_EARSE_DELAY 1500 - -/* upgrade config of FT5x06(x=2,3,4) */ -#define FT5X06_UPGRADE_AA_DELAY 50 -#define FT5X06_UPGRADE_55_DELAY 30 -#define FT5X06_UPGRADE_ID_1 0x79 -#define FT5X06_UPGRADE_ID_2 0x03 -#define FT5X06_UPGRADE_READID_DELAY 1 -#define FT5X06_UPGRADE_EARSE_DELAY 2000 - -/* upgrade config of FT6208 */ -#define FT6208_UPGRADE_AA_DELAY 60 -#define FT6208_UPGRADE_55_DELAY 10 -#define FT6208_UPGRADE_ID_1 0x79 -#define FT6208_UPGRADE_ID_2 0x05 -#define FT6208_UPGRADE_READID_DELAY 10 -#define FT6208_UPGRADE_EARSE_DELAY 2000 - -/*upgrade config of FT6x06*/ -#define FT6X06_UPGRADE_AA_DELAY 100 -#define FT6X06_UPGRADE_55_DELAY 30 -#define FT6X06_UPGRADE_ID_1 0x79 -#define FT6X06_UPGRADE_ID_2 0x08 -#define FT6X06_UPGRADE_READID_DELAY 10 -#define FT6X06_UPGRADE_EARSE_DELAY 2000 - -#define FT_UPGRADE_INFO(x, y) do { \ - x->delay_55 = y##_UPGRADE_55_DELAY; \ - x->delay_aa = y##_UPGRADE_AA_DELAY; \ - x->upgrade_id_1 = y##_UPGRADE_ID_1; \ - x->upgrade_id_2 = y##_UPGRADE_ID_2; \ - x->delay_readid = y##_UPGRADE_READID_DELAY; \ - x->delay_earse_flash = y##_UPGRADE_EARSE_DELAY; \ - } while (0) - #define FT_FW_MIN_SIZE 8 #define FT_FW_MAX_SIZE 32768 #define FT_FW_FILE_VER(x) ((x)->data[(x)->size - 2]) @@ -180,39 +132,26 @@ #define FT_REG_CAL 0x00 #define FT_CAL_MASK 0x70 -#define FT_INFO_MAX_LEN 200 +#define FT_INFO_MAX_LEN 512 -#define FT_STORE_TS_INFO(buf, id, fw_ver) \ +#define FT_STORE_TS_INFO(buf, id, name, max_tch, group_id, fw_name, fw_ver) \ snprintf(buf, FT_INFO_MAX_LEN, \ "controller\t= focaltech\n" \ "model\t\t= 0x%x\n" \ - "fw_ver\t\t= 0x%x\n", id, fw_ver) + "name\t\t= %s\n" \ + "max_touches\t= %d\n" \ + "drv_ver\t\t= 0x%x\n" \ + "group_id\t= 0x%x\n" \ + "fw_name\t\t= %s\n" \ + "fw_ver\t\t= 0x%x\n", id, name, \ + max_tch, FT_DRIVER_VERSION, group_id, \ + fw_name, fw_ver) #define FT_DEBUG_DIR_NAME "ts_debug" -struct ts_event { - u16 x[CFG_MAX_TOUCH_POINTS]; /* x coordinate */ - u16 y[CFG_MAX_TOUCH_POINTS]; /* y coordinate */ - /* touch event: 0 -- down; 1-- contact; 2 -- contact */ - u8 touch_event[CFG_MAX_TOUCH_POINTS]; - u8 finger_id[CFG_MAX_TOUCH_POINTS]; /* touch ID */ - u16 pressure; - u8 touch_point; -}; - -struct upgrade_info { - u16 delay_aa; /* delay of write FT_UPGRADE_AA */ - u16 delay_55; /* delay of write FT_UPGRADE_55 */ - u8 upgrade_id_1; /* upgrade id 1 */ - u8 upgrade_id_2; /* upgrade id 2 */ - u16 delay_readid; /* delay of read id */ - u16 delay_earse_flash; /* delay of earse flash */ -}; - struct ft5x06_ts_data { struct i2c_client *client; struct input_dev *input_dev; - struct ts_event event; const struct ft5x06_ts_platform_data *pdata; struct regulator *vdd; struct regulator *vcc_i2c; @@ -223,6 +162,8 @@ struct ft5x06_ts_data { u16 addr; bool suspended; char *ts_info; + u8 *tch_data; + u32 tch_data_len; #if defined(CONFIG_FB) struct notifier_block fb_notif; #elif defined(CONFIG_HAS_EARLYSUSPEND) @@ -305,89 +246,65 @@ 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_report_value(struct ft5x06_ts_data *data) -{ - struct ts_event *event = &data->event; - int i; - int fingerdown = 0; - - for (i = 0; i < event->touch_point; i++) { - if (event->touch_event[i] == 0 || event->touch_event[i] == 2) { - event->pressure = FT_PRESS; - fingerdown++; - } else { - event->pressure = 0; - } - - input_mt_slot(data->input_dev, event->finger_id[i]); - input_mt_report_slot_state(data->input_dev, MT_TOOL_FINGER, - !!event->pressure); - - if (event->pressure == FT_PRESS) { - input_report_abs(data->input_dev, ABS_MT_POSITION_X, - event->x[i]); - input_report_abs(data->input_dev, ABS_MT_POSITION_Y, - event->y[i]); - input_report_abs(data->input_dev, ABS_MT_TOUCH_MAJOR, - event->pressure); - } - } - - input_report_key(data->input_dev, BTN_TOUCH, !!fingerdown); - input_sync(data->input_dev); -} - -static int ft5x06_handle_touchdata(struct ft5x06_ts_data *data) -{ - struct ts_event *event = &data->event; - int num_points; - int ret, i; - u8 buf[POINT_READ_BUF] = { 0 }; - u8 pointid = FT_MAX_ID; - - ret = ft5x06_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; - } - memset(event, 0, sizeof(struct ts_event)); - - event->touch_point = 0; - num_points = buf[2] & FT_STATUS_NUM_TP_MASK; - - for (i = 0; i < num_points; i++) { - pointid = (buf[FT_TOUCH_ID_POS + FT_TOUCH_STEP * i]) >> 4; - if (pointid >= FT_MAX_ID) - break; - - event->touch_point++; - - event->x[i] = - (s16) (buf[FT_TOUCH_X_H_POS + FT_TOUCH_STEP * i] & 0x0F) << - 8 | (s16) buf[FT_TOUCH_X_L_POS + FT_TOUCH_STEP * i]; - event->y[i] = - (s16) (buf[FT_TOUCH_Y_H_POS + FT_TOUCH_STEP * i] & 0x0F) << - 8 | (s16) buf[FT_TOUCH_Y_L_POS + FT_TOUCH_STEP * i]; - event->touch_event[i] = - buf[FT_TOUCH_EVENT_POS + FT_TOUCH_STEP * i] >> 6; - event->finger_id[i] = - (buf[FT_TOUCH_ID_POS + FT_TOUCH_STEP * i]) >> 4; - } - - ft5x06_report_value(data); - - return 0; -} - static irqreturn_t ft5x06_ts_interrupt(int irq, void *dev_id) { struct ft5x06_ts_data *data = dev_id; - int rc; + struct input_dev *ip_dev; + int rc, i; + u32 id, x, y, pressure, status, num_touches; + u8 reg = 0x00, *buf; + bool update_input = false; - rc = ft5x06_handle_touchdata(data); - if (rc) - pr_err("%s: handling touchdata failed\n", __func__); + if (!data) { + pr_err("%s: Invalid data\n", __func__); + return IRQ_HANDLED; + } + + ip_dev = data->input_dev; + buf = data->tch_data; + + 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; + } + + for (i = 0; i < data->pdata->num_max_touches; i++) { + id = (buf[FT_TOUCH_ID_POS + FT_ONE_TCH_LEN * i]) >> 4; + if (id >= FT_MAX_ID) + break; + + update_input = true; + + x = (buf[FT_TOUCH_X_H_POS + FT_ONE_TCH_LEN * i] & 0x0F) << 8 | + (buf[FT_TOUCH_X_L_POS + FT_ONE_TCH_LEN * i]); + y = (buf[FT_TOUCH_Y_H_POS + FT_ONE_TCH_LEN * i] & 0x0F) << 8 | + (buf[FT_TOUCH_Y_L_POS + FT_ONE_TCH_LEN * i]); + + status = buf[FT_TOUCH_EVENT_POS + FT_ONE_TCH_LEN * i] >> 6; + + num_touches = buf[FT_TD_STATUS] & FT_STATUS_NUM_TP_MASK; + + /* invalid combination */ + if (!num_touches && !status && !id) + break; + + 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); + } + + if (update_input) { + input_mt_report_pointer_emulation(ip_dev, false); + input_sync(ip_dev); + } return IRQ_HANDLED; } @@ -520,7 +437,7 @@ static int ft5x06_ts_suspend(struct device *dev) disable_irq(data->client->irq); /* release all touches */ - for (i = 0; i < CFG_MAX_TOUCH_POINTS; i++) { + 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); } @@ -554,7 +471,7 @@ static int ft5x06_ts_suspend(struct device *dev) pwr_off_fail: if (gpio_is_valid(data->pdata->reset_gpio)) { gpio_set_value_cansleep(data->pdata->reset_gpio, 0); - msleep(FT_RESET_DLY); + msleep(data->pdata->hard_rst_dly); gpio_set_value_cansleep(data->pdata->reset_gpio, 1); } enable_irq(data->client->irq); @@ -567,7 +484,7 @@ static int ft5x06_ts_resume(struct device *dev) int err; if (!data->suspended) { - dev_info(dev, "Already in awake state\n"); + dev_dbg(dev, "Already in awake state\n"); return 0; } @@ -587,11 +504,11 @@ static int ft5x06_ts_resume(struct device *dev) if (gpio_is_valid(data->pdata->reset_gpio)) { gpio_set_value_cansleep(data->pdata->reset_gpio, 0); - msleep(FT_RESET_DLY); + msleep(data->pdata->hard_rst_dly); gpio_set_value_cansleep(data->pdata->reset_gpio, 1); } - msleep(FT_STARTUP_DLY); + msleep(data->pdata->soft_rst_dly); enable_irq(data->client->irq); @@ -650,16 +567,17 @@ static const struct dev_pm_ops ft5x06_ts_pm_ops = { static int ft5x06_auto_cal(struct i2c_client *client) { + struct ft5x06_ts_data *data = i2c_get_clientdata(client); u8 temp = 0, i; /* set to factory mode */ - msleep(2 * FT_STARTUP_DLY); + msleep(2 * data->pdata->soft_rst_dly); ft5x0x_write_reg(client, FT_REG_DEV_MODE, FT_FACTORYMODE_VALUE); - msleep(FT_STARTUP_DLY); + msleep(data->pdata->soft_rst_dly); /* start calibration */ ft5x0x_write_reg(client, FT_DEV_MODE_REG_CAL, FT_CAL_START); - msleep(2 * FT_STARTUP_DLY); + msleep(2 * data->pdata->soft_rst_dly); for (i = 0; i < FT_CAL_RETRY; i++) { ft5x0x_read_reg(client, FT_REG_CAL, &temp); /* return to normal mode, calibration finish */ @@ -667,37 +585,18 @@ static int ft5x06_auto_cal(struct i2c_client *client) break; } - /* calibration OK */ - msleep(2 * FT_STARTUP_DLY); + /*calibration OK */ + msleep(2 * data->pdata->soft_rst_dly); ft5x0x_write_reg(client, FT_REG_DEV_MODE, FT_FACTORYMODE_VALUE); - msleep(FT_STARTUP_DLY); + msleep(data->pdata->soft_rst_dly); /* store calibration data */ ft5x0x_write_reg(client, FT_DEV_MODE_REG_CAL, FT_CAL_STORE); - msleep(2 * FT_STARTUP_DLY); + msleep(2 * data->pdata->soft_rst_dly); /* set to normal mode */ ft5x0x_write_reg(client, FT_REG_DEV_MODE, FT_WORKMODE_VALUE); - msleep(2 * FT_STARTUP_DLY); - - return 0; -} - -static int ft5x06_get_upgrade_info(u8 family_id, struct upgrade_info *info) -{ - switch (family_id) { - case FT5306I_ID: - FT_UPGRADE_INFO(info, FT5X06); - break; - case FT5316_ID: - FT_UPGRADE_INFO(info, FT5316); - break; - case FT6X06_ID: - FT_UPGRADE_INFO(info, FT6X06); - break; - default: - return -EINVAL; - } + msleep(2 * data->pdata->soft_rst_dly); return 0; } @@ -706,7 +605,7 @@ static int ft5x06_fw_upgrade_start(struct i2c_client *client, const u8 *data, u32 data_len) { struct ft5x06_ts_data *ts_data = i2c_get_clientdata(client); - struct upgrade_info info; + struct fw_upgrade_info info = ts_data->pdata->info; 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]; @@ -714,12 +613,6 @@ static int ft5x06_fw_upgrade_start(struct i2c_client *client, u32 pkt_num, pkt_len; u8 fw_ecc; - rc = ft5x06_get_upgrade_info(ts_data->family_id, &info); - if (rc < 0) { - dev_err(&client->dev, "Cannot get upgrade information!\n"); - return -EINVAL; - } - for (i = 0, j = 0; i < FT_UPGRADE_LOOP; i++) { /* reset - write 0xaa and 0x55 to reset register */ if (ts_data->family_id == FT6X06_ID) @@ -766,7 +659,7 @@ static int ft5x06_fw_upgrade_start(struct i2c_client *client, /* erase app and panel paramenter area */ w_buf[0] = FT_ERASE_APP_REG; ft5x06_i2c_write(client, w_buf, 1); - msleep(info.delay_earse_flash); + msleep(info.delay_erase_flash); w_buf[0] = FT_ERASE_PANEL_REG; ft5x06_i2c_write(client, w_buf, 1); @@ -841,7 +734,7 @@ static int ft5x06_fw_upgrade_start(struct i2c_client *client, /* reset */ w_buf[0] = FT_REG_RESET_FW; ft5x06_i2c_write(client, w_buf, 1); - msleep(FT_STARTUP_DLY); + msleep(ts_data->pdata->soft_rst_dly); dev_info(&client->dev, "Firmware upgrade successful\n"); @@ -889,14 +782,16 @@ static int ft5x06_fw_upgrade(struct device *dev, bool force) rc = ft5x06_fw_upgrade_start(data->client, fw->data, fw->size); if (rc < 0) dev_err(dev, "update failed (%d)\n", rc); - else + else if (data->pdata->info.auto_cal) ft5x06_auto_cal(data->client); } else { dev_err(dev, "FW format error\n"); rc = -EIO; } - FT_STORE_TS_INFO(data->ts_info, data->family_id, FT_FW_FILE_VER(fw)); + FT_STORE_TS_INFO(data->ts_info, data->family_id, data->pdata->name, + data->pdata->num_max_touches, data->pdata->group_id, + data->pdata->fw_name, FT_FW_FILE_VER(fw)); rel_fw: release_firmware(fw); return rc; @@ -1180,6 +1075,13 @@ static int ft5x06_parse_dt(struct device *dev, 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"); + return rc; + } + rc = ft5x06_get_dt_coords(dev, "focaltech,panel-coords", pdata); if (rc && (rc != -EINVAL)) return rc; @@ -1204,6 +1106,86 @@ static int ft5x06_parse_dt(struct device *dev, if (pdata->irq_gpio < 0) return pdata->irq_gpio; + 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"); + return rc; + } + + rc = of_property_read_u32(np, "focaltech,group-id", &temp_val); + if (!rc) + pdata->group_id = temp_val; + else + return rc; + + rc = of_property_read_u32(np, "focaltech,hard-reset-delay-ms", + &temp_val); + if (!rc) + pdata->hard_rst_dly = temp_val; + else + return rc; + + rc = of_property_read_u32(np, "focaltech,soft-reset-delay-ms", + &temp_val); + if (!rc) + pdata->soft_rst_dly = temp_val; + else + return rc; + + rc = of_property_read_u32(np, "focaltech,num-max-touches", &temp_val); + if (!rc) + pdata->num_max_touches = temp_val; + else + return rc; + + 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"); + return rc; + } 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"); + return rc; + } 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"); + return rc; + } 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"); + return rc; + } 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\n"); + return rc; + } 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"); + return rc; + } else if (rc != -EINVAL) + pdata->info.delay_erase_flash = temp_val; + + pdata->info.auto_cal = of_property_read_bool(np, + "focaltech,fw-auto-cal"); + rc = of_property_read_u32(np, "focaltech,family-id", &temp_val); if (!rc) pdata->family_id = temp_val; @@ -1244,7 +1226,7 @@ static int ft5x06_ts_probe(struct i2c_client *client, struct dentry *temp; u8 reg_value; u8 reg_addr; - int err; + int err, len; if (client->dev.of_node) { pdata = devm_kzalloc(&client->dev, @@ -1253,8 +1235,10 @@ static int ft5x06_ts_probe(struct i2c_client *client, return -ENOMEM; err = ft5x06_parse_dt(&client->dev, pdata); - if (err) + if (err) { + dev_err(&client->dev, "DT parsing failed\n"); return err; + } } else pdata = client->dev.platform_data; @@ -1268,15 +1252,31 @@ static int ft5x06_ts_probe(struct i2c_client *client, return -ENODEV; } - data = kzalloc(sizeof(struct ft5x06_ts_data), GFP_KERNEL); + data = devm_kzalloc(&client->dev, + sizeof(struct ft5x06_ts_data), GFP_KERNEL); + if (!data) + return -ENOMEM; + + if (pdata->fw_name) { + len = strlen(pdata->fw_name); + if (len > FT_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 = FT_TCH_LEN(pdata->num_max_touches); + data->tch_data = devm_kzalloc(&client->dev, + data->tch_data_len, GFP_KERNEL); if (!data) return -ENOMEM; input_dev = input_allocate_device(); if (!input_dev) { - err = -ENOMEM; dev_err(&client->dev, "failed to allocate input device\n"); - goto free_mem; + return -ENOMEM; } data->input_dev = input_dev; @@ -1295,12 +1295,12 @@ 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, CFG_MAX_TOUCH_POINTS); + 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, FT_PRESS, 0, 0); + input_set_abs_params(input_dev, ABS_MT_PRESSURE, 0, FT_PRESS, 0, 0); err = input_register_device(input_dev); if (err) { @@ -1363,12 +1363,12 @@ static int ft5x06_ts_probe(struct i2c_client *client, "set_direction for reset gpio failed\n"); goto free_reset_gpio; } - msleep(FT_RESET_DLY); + msleep(data->pdata->hard_rst_dly); gpio_set_value_cansleep(data->pdata->reset_gpio, 1); } /* make sure CTP already finish startup process */ - msleep(FT_STARTUP_DLY); + msleep(data->pdata->soft_rst_dly); /* check the controller id */ reg_addr = FT_REG_ID; @@ -1452,7 +1452,8 @@ static int ft5x06_ts_probe(struct i2c_client *client, goto free_debug_dir; } - data->ts_info = kzalloc(FT_INFO_MAX_LEN, GFP_KERNEL); + data->ts_info = devm_kzalloc(&client->dev, + FT_INFO_MAX_LEN, GFP_KERNEL); if (!data->ts_info) goto free_debug_dir; @@ -1478,7 +1479,9 @@ static int ft5x06_ts_probe(struct i2c_client *client, dev_info(&client->dev, "Firmware version = 0x%x\n", reg_value); - FT_STORE_TS_INFO(data->ts_info, data->family_id, reg_value); + FT_STORE_TS_INFO(data->ts_info, data->family_id, data->pdata->name, + data->pdata->num_max_touches, data->pdata->group_id, + data->pdata->fw_name, reg_value); #if defined(CONFIG_FB) data->fb_notif.notifier_call = fb_notifier_callback; @@ -1529,8 +1532,6 @@ unreg_inputdev: input_dev = NULL; free_inputdev: input_free_device(input_dev); -free_mem: - kfree(data); return err; } @@ -1568,8 +1569,6 @@ static int ft5x06_ts_remove(struct i2c_client *client) ft5x06_power_init(data, false); input_unregister_device(data->input_dev); - kfree(data->ts_info); - kfree(data); return 0; } diff --git a/include/linux/input/ft5x06_ts.h b/include/linux/input/ft5x06_ts.h index 08ccbc9bd71c..89e3143eda77 100644 --- a/include/linux/input/ft5x06_ts.h +++ b/include/linux/input/ft5x06_ts.h @@ -23,7 +23,20 @@ #define FT5X36_ID 0x14 #define FT6X06_ID 0x06 +struct fw_upgrade_info { + bool auto_cal; + u16 delay_aa; + u16 delay_55; + u8 upgrade_id_1; + u8 upgrade_id_2; + u16 delay_readid; + u16 delay_erase_flash; +}; + struct ft5x06_ts_platform_data { + struct fw_upgrade_info info; + const char *name; + const char *fw_name; u32 irqflags; u32 irq_gpio; u32 irq_gpio_flags; @@ -38,6 +51,10 @@ struct ft5x06_ts_platform_data { u32 panel_miny; u32 panel_maxx; u32 panel_maxy; + u32 group_id; + u32 hard_rst_dly; + u32 soft_rst_dly; + u32 num_max_touches; bool no_force_update; bool i2c_pull_up; int (*power_init)(bool);