diff options
author | Linus Torvalds <torvalds@linux-foundation.org> | 2014-12-17 10:06:02 -0800 |
---|---|---|
committer | Linus Torvalds <torvalds@linux-foundation.org> | 2014-12-17 10:06:02 -0800 |
commit | d797da41b2aceed5daa8cd2eee92cd74b2a0c652 (patch) | |
tree | f4de7daf5a81f425c057dd5a65a0e1b8056de5a9 /drivers/input/touchscreen | |
parent | d6666be6f0c43efb9475d1d35fbef9f8be61b7b1 (diff) | |
parent | f20c86cd75f1c8c728dafd0218645ff3c5e8545d (diff) |
Merge branch 'for-linus' of git://git.kernel.org/pub/scm/linux/kernel/git/dtor/input
Pull input subsystem updates from Dmitry Torokhov:
"Two new drivers for Elan hardware (for I2C touchpad and touchscreen
found in several Chromebooks and other devices), a driver for Goodix
touch panel, and small fixes to Cypress I2C trackpad and other input
drivers.
Also we switched to use __maybe_unused instead of gating suspend/
resume code with #ifdef guards to get better compile coverage"
* 'for-linus' of git://git.kernel.org/pub/scm/linux/kernel/git/dtor/input: (27 commits)
Input: gpio_keys - fix warning regarding uninitialized 'button' variable
Input: add support for Elan eKTH I2C touchscreens
Input: gpio_keys - fix warning regarding uninitialized 'irq' variable
Input: cyapa - use 'error' for error codes
Input: cyapa - fix resuming the device
Input: gpio_keys - add device tree support for interrupt only keys
Input: amikbd - allocate temporary keymap buffer on the stack
Input: amikbd - fix build if !CONFIG_HW_CONSOLE
Input: lm8323 - missing error check in lm8323_set_disable()
Input: initialize device counter variables with -1
Input: initialize input_no to -1 to avoid subtraction
Input: i8042 - do not try to load on Intel NUC D54250WYK
Input: atkbd - correct MSC_SCAN events for force_release keys
Input: cyapa - switch to using managed resources
Input: lifebook - use "static inline" instead of "inline" in lifebook.h
Input: touchscreen - use __maybe_unused instead of ifdef around suspend/resume
Input: mouse - use __maybe_unused instead of ifdef around suspend/resume
Input: misc - use __maybe_unused instead of ifdef around suspend/resume
Input: cap11xx - support for irq-active-high option
Input: cap11xx - add support for various cap11xx devices
...
Diffstat (limited to 'drivers/input/touchscreen')
23 files changed, 1731 insertions, 78 deletions
diff --git a/drivers/input/touchscreen/Kconfig b/drivers/input/touchscreen/Kconfig index e1d8003d01f8..58917525126e 100644 --- a/drivers/input/touchscreen/Kconfig +++ b/drivers/input/touchscreen/Kconfig @@ -295,6 +295,19 @@ config TOUCHSCREEN_FUJITSU To compile this driver as a module, choose M here: the module will be called fujitsu-ts. +config TOUCHSCREEN_GOODIX + tristate "Goodix I2C touchscreen" + depends on I2C && ACPI + help + Say Y here if you have the Goodix touchscreen (such as one + installed in Onda v975w tablets) connected to your + system. + + If unsure, say N. + + To compile this driver as a module, choose M here: the + module will be called goodix. + config TOUCHSCREEN_ILI210X tristate "Ilitek ILI210X based touchscreen" depends on I2C @@ -334,6 +347,18 @@ config TOUCHSCREEN_GUNZE To compile this driver as a module, choose M here: the module will be called gunze. +config TOUCHSCREEN_ELAN + tristate "Elan eKTH I2C touchscreen" + depends on I2C + help + Say Y here if you have an Elan eKTH I2C touchscreen + connected to your system. + + If unsure, say N. + + To compile this driver as a module, choose M here: the + module will be called elants_i2c. + config TOUCHSCREEN_ELO tristate "Elo serial touchscreens" select SERIO diff --git a/drivers/input/touchscreen/Makefile b/drivers/input/touchscreen/Makefile index 090e61cc9171..0242fea2102a 100644 --- a/drivers/input/touchscreen/Makefile +++ b/drivers/input/touchscreen/Makefile @@ -31,9 +31,11 @@ obj-$(CONFIG_TOUCHSCREEN_EDT_FT5X06) += edt-ft5x06.o obj-$(CONFIG_TOUCHSCREEN_HAMPSHIRE) += hampshire.o obj-$(CONFIG_TOUCHSCREEN_GUNZE) += gunze.o obj-$(CONFIG_TOUCHSCREEN_EETI) += eeti_ts.o +obj-$(CONFIG_TOUCHSCREEN_ELAN) += elants_i2c.o obj-$(CONFIG_TOUCHSCREEN_ELO) += elo.o obj-$(CONFIG_TOUCHSCREEN_EGALAX) += egalax_ts.o obj-$(CONFIG_TOUCHSCREEN_FUJITSU) += fujitsu_ts.o +obj-$(CONFIG_TOUCHSCREEN_GOODIX) += goodix.o obj-$(CONFIG_TOUCHSCREEN_ILI210X) += ili210x.o obj-$(CONFIG_TOUCHSCREEN_INEXIO) += inexio.o obj-$(CONFIG_TOUCHSCREEN_INTEL_MID) += intel-mid-touch.o diff --git a/drivers/input/touchscreen/ad7877.c b/drivers/input/touchscreen/ad7877.c index 523865daa1d3..da4e5bb5e045 100644 --- a/drivers/input/touchscreen/ad7877.c +++ b/drivers/input/touchscreen/ad7877.c @@ -820,8 +820,7 @@ static int ad7877_remove(struct spi_device *spi) return 0; } -#ifdef CONFIG_PM_SLEEP -static int ad7877_suspend(struct device *dev) +static int __maybe_unused ad7877_suspend(struct device *dev) { struct ad7877 *ts = dev_get_drvdata(dev); @@ -830,7 +829,7 @@ static int ad7877_suspend(struct device *dev) return 0; } -static int ad7877_resume(struct device *dev) +static int __maybe_unused ad7877_resume(struct device *dev) { struct ad7877 *ts = dev_get_drvdata(dev); @@ -838,7 +837,6 @@ static int ad7877_resume(struct device *dev) return 0; } -#endif static SIMPLE_DEV_PM_OPS(ad7877_pm, ad7877_suspend, ad7877_resume); diff --git a/drivers/input/touchscreen/ad7879.c b/drivers/input/touchscreen/ad7879.c index 1eb9d3c20886..fec66ad80513 100644 --- a/drivers/input/touchscreen/ad7879.c +++ b/drivers/input/touchscreen/ad7879.c @@ -284,8 +284,7 @@ static void ad7879_close(struct input_dev* input) __ad7879_disable(ts); } -#ifdef CONFIG_PM_SLEEP -static int ad7879_suspend(struct device *dev) +static int __maybe_unused ad7879_suspend(struct device *dev) { struct ad7879 *ts = dev_get_drvdata(dev); @@ -301,7 +300,7 @@ static int ad7879_suspend(struct device *dev) return 0; } -static int ad7879_resume(struct device *dev) +static int __maybe_unused ad7879_resume(struct device *dev) { struct ad7879 *ts = dev_get_drvdata(dev); @@ -316,7 +315,6 @@ static int ad7879_resume(struct device *dev) return 0; } -#endif SIMPLE_DEV_PM_OPS(ad7879_pm_ops, ad7879_suspend, ad7879_resume); EXPORT_SYMBOL(ad7879_pm_ops); diff --git a/drivers/input/touchscreen/ads7846.c b/drivers/input/touchscreen/ads7846.c index e57ba52bf484..e4eb8a6c658f 100644 --- a/drivers/input/touchscreen/ads7846.c +++ b/drivers/input/touchscreen/ads7846.c @@ -883,8 +883,7 @@ static irqreturn_t ads7846_irq(int irq, void *handle) return IRQ_HANDLED; } -#ifdef CONFIG_PM_SLEEP -static int ads7846_suspend(struct device *dev) +static int __maybe_unused ads7846_suspend(struct device *dev) { struct ads7846 *ts = dev_get_drvdata(dev); @@ -906,7 +905,7 @@ static int ads7846_suspend(struct device *dev) return 0; } -static int ads7846_resume(struct device *dev) +static int __maybe_unused ads7846_resume(struct device *dev) { struct ads7846 *ts = dev_get_drvdata(dev); @@ -927,7 +926,6 @@ static int ads7846_resume(struct device *dev) return 0; } -#endif static SIMPLE_DEV_PM_OPS(ads7846_pm, ads7846_suspend, ads7846_resume); diff --git a/drivers/input/touchscreen/atmel_mxt_ts.c b/drivers/input/touchscreen/atmel_mxt_ts.c index aaacf8bfa61f..bb070206223c 100644 --- a/drivers/input/touchscreen/atmel_mxt_ts.c +++ b/drivers/input/touchscreen/atmel_mxt_ts.c @@ -2244,8 +2244,7 @@ static int mxt_remove(struct i2c_client *client) return 0; } -#ifdef CONFIG_PM_SLEEP -static int mxt_suspend(struct device *dev) +static int __maybe_unused mxt_suspend(struct device *dev) { struct i2c_client *client = to_i2c_client(dev); struct mxt_data *data = i2c_get_clientdata(client); @@ -2261,7 +2260,7 @@ static int mxt_suspend(struct device *dev) return 0; } -static int mxt_resume(struct device *dev) +static int __maybe_unused mxt_resume(struct device *dev) { struct i2c_client *client = to_i2c_client(dev); struct mxt_data *data = i2c_get_clientdata(client); @@ -2276,7 +2275,6 @@ static int mxt_resume(struct device *dev) return 0; } -#endif static SIMPLE_DEV_PM_OPS(mxt_pm_ops, mxt_suspend, mxt_resume); diff --git a/drivers/input/touchscreen/auo-pixcir-ts.c b/drivers/input/touchscreen/auo-pixcir-ts.c index 7f3c94787787..40e02dd5b2f9 100644 --- a/drivers/input/touchscreen/auo-pixcir-ts.c +++ b/drivers/input/touchscreen/auo-pixcir-ts.c @@ -417,8 +417,7 @@ static void auo_pixcir_input_close(struct input_dev *dev) return; } -#ifdef CONFIG_PM_SLEEP -static int auo_pixcir_suspend(struct device *dev) +static int __maybe_unused auo_pixcir_suspend(struct device *dev) { struct i2c_client *client = to_i2c_client(dev); struct auo_pixcir_ts *ts = i2c_get_clientdata(client); @@ -450,7 +449,7 @@ unlock: return ret; } -static int auo_pixcir_resume(struct device *dev) +static int __maybe_unused auo_pixcir_resume(struct device *dev) { struct i2c_client *client = to_i2c_client(dev); struct auo_pixcir_ts *ts = i2c_get_clientdata(client); @@ -479,7 +478,6 @@ unlock: return ret; } -#endif static SIMPLE_DEV_PM_OPS(auo_pixcir_pm_ops, auo_pixcir_suspend, auo_pixcir_resume); diff --git a/drivers/input/touchscreen/cy8ctmg110_ts.c b/drivers/input/touchscreen/cy8ctmg110_ts.c index 5bf1aeeea825..f2119ee0e21b 100644 --- a/drivers/input/touchscreen/cy8ctmg110_ts.c +++ b/drivers/input/touchscreen/cy8ctmg110_ts.c @@ -291,8 +291,7 @@ err_free_mem: return err; } -#ifdef CONFIG_PM_SLEEP -static int cy8ctmg110_suspend(struct device *dev) +static int __maybe_unused cy8ctmg110_suspend(struct device *dev) { struct i2c_client *client = to_i2c_client(dev); struct cy8ctmg110 *ts = i2c_get_clientdata(client); @@ -306,7 +305,7 @@ static int cy8ctmg110_suspend(struct device *dev) return 0; } -static int cy8ctmg110_resume(struct device *dev) +static int __maybe_unused cy8ctmg110_resume(struct device *dev) { struct i2c_client *client = to_i2c_client(dev); struct cy8ctmg110 *ts = i2c_get_clientdata(client); @@ -319,7 +318,6 @@ static int cy8ctmg110_resume(struct device *dev) } return 0; } -#endif static SIMPLE_DEV_PM_OPS(cy8ctmg110_pm, cy8ctmg110_suspend, cy8ctmg110_resume); diff --git a/drivers/input/touchscreen/cyttsp_core.c b/drivers/input/touchscreen/cyttsp_core.c index eee656f77a2e..5b74e8b84e79 100644 --- a/drivers/input/touchscreen/cyttsp_core.c +++ b/drivers/input/touchscreen/cyttsp_core.c @@ -472,8 +472,7 @@ static int cyttsp_disable(struct cyttsp *ts) return 0; } -#ifdef CONFIG_PM_SLEEP -static int cyttsp_suspend(struct device *dev) +static int __maybe_unused cyttsp_suspend(struct device *dev) { struct cyttsp *ts = dev_get_drvdata(dev); int retval = 0; @@ -491,7 +490,7 @@ static int cyttsp_suspend(struct device *dev) return retval; } -static int cyttsp_resume(struct device *dev) +static int __maybe_unused cyttsp_resume(struct device *dev) { struct cyttsp *ts = dev_get_drvdata(dev); @@ -507,8 +506,6 @@ static int cyttsp_resume(struct device *dev) return 0; } -#endif - SIMPLE_DEV_PM_OPS(cyttsp_pm_ops, cyttsp_suspend, cyttsp_resume); EXPORT_SYMBOL_GPL(cyttsp_pm_ops); diff --git a/drivers/input/touchscreen/edt-ft5x06.c b/drivers/input/touchscreen/edt-ft5x06.c index ee3434f1e949..3793fcc7e5db 100644 --- a/drivers/input/touchscreen/edt-ft5x06.c +++ b/drivers/input/touchscreen/edt-ft5x06.c @@ -1092,8 +1092,7 @@ static int edt_ft5x06_ts_remove(struct i2c_client *client) return 0; } -#ifdef CONFIG_PM_SLEEP -static int edt_ft5x06_ts_suspend(struct device *dev) +static int __maybe_unused edt_ft5x06_ts_suspend(struct device *dev) { struct i2c_client *client = to_i2c_client(dev); @@ -1103,7 +1102,7 @@ static int edt_ft5x06_ts_suspend(struct device *dev) return 0; } -static int edt_ft5x06_ts_resume(struct device *dev) +static int __maybe_unused edt_ft5x06_ts_resume(struct device *dev) { struct i2c_client *client = to_i2c_client(dev); @@ -1112,7 +1111,6 @@ static int edt_ft5x06_ts_resume(struct device *dev) return 0; } -#endif static SIMPLE_DEV_PM_OPS(edt_ft5x06_ts_pm_ops, edt_ft5x06_ts_suspend, edt_ft5x06_ts_resume); diff --git a/drivers/input/touchscreen/eeti_ts.c b/drivers/input/touchscreen/eeti_ts.c index b1884ddd7a84..09be6ced7151 100644 --- a/drivers/input/touchscreen/eeti_ts.c +++ b/drivers/input/touchscreen/eeti_ts.c @@ -264,8 +264,7 @@ static int eeti_ts_remove(struct i2c_client *client) return 0; } -#ifdef CONFIG_PM_SLEEP -static int eeti_ts_suspend(struct device *dev) +static int __maybe_unused eeti_ts_suspend(struct device *dev) { struct i2c_client *client = to_i2c_client(dev); struct eeti_ts_priv *priv = i2c_get_clientdata(client); @@ -284,7 +283,7 @@ static int eeti_ts_suspend(struct device *dev) return 0; } -static int eeti_ts_resume(struct device *dev) +static int __maybe_unused eeti_ts_resume(struct device *dev) { struct i2c_client *client = to_i2c_client(dev); struct eeti_ts_priv *priv = i2c_get_clientdata(client); @@ -302,7 +301,6 @@ static int eeti_ts_resume(struct device *dev) return 0; } -#endif static SIMPLE_DEV_PM_OPS(eeti_ts_pm, eeti_ts_suspend, eeti_ts_resume); diff --git a/drivers/input/touchscreen/egalax_ts.c b/drivers/input/touchscreen/egalax_ts.c index c8057847d71d..4c56299284ef 100644 --- a/drivers/input/touchscreen/egalax_ts.c +++ b/drivers/input/touchscreen/egalax_ts.c @@ -239,8 +239,7 @@ static const struct i2c_device_id egalax_ts_id[] = { }; MODULE_DEVICE_TABLE(i2c, egalax_ts_id); -#ifdef CONFIG_PM_SLEEP -static int egalax_ts_suspend(struct device *dev) +static int __maybe_unused egalax_ts_suspend(struct device *dev) { static const u8 suspend_cmd[MAX_I2C_DATA_LEN] = { 0x3, 0x6, 0xa, 0x3, 0x36, 0x3f, 0x2, 0, 0, 0 @@ -252,13 +251,12 @@ static int egalax_ts_suspend(struct device *dev) return ret > 0 ? 0 : ret; } -static int egalax_ts_resume(struct device *dev) +static int __maybe_unused egalax_ts_resume(struct device *dev) { struct i2c_client *client = to_i2c_client(dev); return egalax_wake_up_device(client); } -#endif static SIMPLE_DEV_PM_OPS(egalax_ts_pm_ops, egalax_ts_suspend, egalax_ts_resume); diff --git a/drivers/input/touchscreen/elants_i2c.c b/drivers/input/touchscreen/elants_i2c.c new file mode 100644 index 000000000000..a510f7ef9b66 --- /dev/null +++ b/drivers/input/touchscreen/elants_i2c.c @@ -0,0 +1,1271 @@ +/* + * Elan Microelectronics touch panels with I2C interface + * + * Copyright (C) 2014 Elan Microelectronics Corporation. + * Scott Liu <scott.liu@emc.com.tw> + * + * This code is partly based on hid-multitouch.c: + * + * Copyright (c) 2010-2012 Stephane Chatty <chatty@enac.fr> + * Copyright (c) 2010-2012 Benjamin Tissoires <benjamin.tissoires@gmail.com> + * Copyright (c) 2010-2012 Ecole Nationale de l'Aviation Civile, France + * + * + * This code is partly based on i2c-hid.c: + * + * Copyright (c) 2012 Benjamin Tissoires <benjamin.tissoires@gmail.com> + * Copyright (c) 2012 Ecole Nationale de l'Aviation Civile, France + * Copyright (c) 2012 Red Hat, Inc + */ + +/* + * This software is licensed under the terms of the GNU General Public + * License version 2, as published by the Free Software Foundation, and + * may be copied, distributed, and modified under those terms. + */ + +#include <linux/module.h> +#include <linux/input.h> +#include <linux/interrupt.h> +#include <linux/platform_device.h> +#include <linux/async.h> +#include <linux/i2c.h> +#include <linux/delay.h> +#include <linux/uaccess.h> +#include <linux/buffer_head.h> +#include <linux/version.h> +#include <linux/slab.h> +#include <linux/firmware.h> +#include <linux/version.h> +#include <linux/input/mt.h> +#include <linux/acpi.h> +#include <linux/of.h> +#include <asm/unaligned.h> + +/* Device, Driver information */ +#define DEVICE_NAME "elants_i2c" +#define DRV_VERSION "1.0.9" + +/* Convert from rows or columns into resolution */ +#define ELAN_TS_RESOLUTION(n, m) (((n) - 1) * (m)) + +/* FW header data */ +#define HEADER_SIZE 4 +#define FW_HDR_TYPE 0 +#define FW_HDR_COUNT 1 +#define FW_HDR_LENGTH 2 + +/* Buffer mode Queue Header information */ +#define QUEUE_HEADER_SINGLE 0x62 +#define QUEUE_HEADER_NORMAL 0X63 +#define QUEUE_HEADER_WAIT 0x64 + +/* Command header definition */ +#define CMD_HEADER_WRITE 0x54 +#define CMD_HEADER_READ 0x53 +#define CMD_HEADER_6B_READ 0x5B +#define CMD_HEADER_RESP 0x52 +#define CMD_HEADER_6B_RESP 0x9B +#define CMD_HEADER_HELLO 0x55 +#define CMD_HEADER_REK 0x66 + +/* FW position data */ +#define PACKET_SIZE 55 +#define MAX_CONTACT_NUM 10 +#define FW_POS_HEADER 0 +#define FW_POS_STATE 1 +#define FW_POS_TOTAL 2 +#define FW_POS_XY 3 +#define FW_POS_CHECKSUM 34 +#define FW_POS_WIDTH 35 +#define FW_POS_PRESSURE 45 + +#define HEADER_REPORT_10_FINGER 0x62 + +/* Header (4 bytes) plus 3 fill 10-finger packets */ +#define MAX_PACKET_SIZE 169 + +#define BOOT_TIME_DELAY_MS 50 + +/* FW read command, 0x53 0x?? 0x0, 0x01 */ +#define E_ELAN_INFO_FW_VER 0x00 +#define E_ELAN_INFO_BC_VER 0x10 +#define E_ELAN_INFO_TEST_VER 0xE0 +#define E_ELAN_INFO_FW_ID 0xF0 +#define E_INFO_OSR 0xD6 +#define E_INFO_PHY_SCAN 0xD7 +#define E_INFO_PHY_DRIVER 0xD8 + +#define MAX_RETRIES 3 +#define MAX_FW_UPDATE_RETRIES 30 + +#define ELAN_FW_PAGESIZE 132 +#define ELAN_FW_FILENAME "elants_i2c.bin" + +/* calibration timeout definition */ +#define ELAN_CALI_TIMEOUT_MSEC 10000 + +enum elants_state { + ELAN_STATE_NORMAL, + ELAN_WAIT_QUEUE_HEADER, + ELAN_WAIT_RECALIBRATION, +}; + +enum elants_iap_mode { + ELAN_IAP_OPERATIONAL, + ELAN_IAP_RECOVERY, +}; + +/* struct elants_data - represents state of Elan touchscreen device */ +struct elants_data { + struct i2c_client *client; + struct input_dev *input; + + u16 fw_version; + u8 test_version; + u8 solution_version; + u8 bc_version; + u8 iap_version; + u16 hw_version; + unsigned int x_res; /* resolution in units/mm */ + unsigned int y_res; + unsigned int x_max; + unsigned int y_max; + + enum elants_state state; + enum elants_iap_mode iap_mode; + + /* Guards against concurrent access to the device via sysfs */ + struct mutex sysfs_mutex; + + u8 cmd_resp[HEADER_SIZE]; + struct completion cmd_done; + + u8 buf[MAX_PACKET_SIZE]; + + bool wake_irq_enabled; +}; + +static int elants_i2c_send(struct i2c_client *client, + const void *data, size_t size) +{ + int ret; + + ret = i2c_master_send(client, data, size); + if (ret == size) + return 0; + + if (ret >= 0) + ret = -EIO; + + dev_err(&client->dev, "%s failed (%*ph): %d\n", + __func__, (int)size, data, ret); + + return ret; +} + +static int elants_i2c_read(struct i2c_client *client, void *data, size_t size) +{ + int ret; + + ret = i2c_master_recv(client, data, size); + if (ret == size) + return 0; + + if (ret >= 0) + ret = -EIO; + + dev_err(&client->dev, "%s failed: %d\n", __func__, ret); + + return ret; +} + +static int elants_i2c_execute_command(struct i2c_client *client, + const u8 *cmd, size_t cmd_size, + u8 *resp, size_t resp_size) +{ + struct i2c_msg msgs[2]; + int ret; + u8 expected_response; + + switch (cmd[0]) { + case CMD_HEADER_READ: + expected_response = CMD_HEADER_RESP; + break; + + case CMD_HEADER_6B_READ: + expected_response = CMD_HEADER_6B_RESP; + break; + + default: + dev_err(&client->dev, "%s: invalid command %*ph\n", + __func__, (int)cmd_size, cmd); + return -EINVAL; + } + + msgs[0].addr = client->addr; + msgs[0].flags = client->flags & I2C_M_TEN; + msgs[0].len = cmd_size; + msgs[0].buf = (u8 *)cmd; + + msgs[1].addr = client->addr; + msgs[1].flags = client->flags & I2C_M_TEN; + msgs[1].flags |= I2C_M_RD; + msgs[1].len = resp_size; + msgs[1].buf = resp; + + ret = i2c_transfer(client->adapter, msgs, ARRAY_SIZE(msgs)); + if (ret < 0) + return ret; + + if (ret != ARRAY_SIZE(msgs) || resp[FW_HDR_TYPE] != expected_response) + return -EIO; + + return 0; +} + +static int elants_i2c_calibrate(struct elants_data *ts) +{ + struct i2c_client *client = ts->client; + int ret, error; + static const u8 w_flashkey[] = { 0x54, 0xC0, 0xE1, 0x5A }; + static const u8 rek[] = { 0x54, 0x29, 0x00, 0x01 }; + static const u8 rek_resp[] = { CMD_HEADER_REK, 0x66, 0x66, 0x66 }; + + disable_irq(client->irq); + + ts->state = ELAN_WAIT_RECALIBRATION; + reinit_completion(&ts->cmd_done); + + elants_i2c_send(client, w_flashkey, sizeof(w_flashkey)); + elants_i2c_send(client, rek, sizeof(rek)); + + enable_irq(client->irq); + + ret = wait_for_completion_interruptible_timeout(&ts->cmd_done, + msecs_to_jiffies(ELAN_CALI_TIMEOUT_MSEC)); + + ts->state = ELAN_STATE_NORMAL; + + if (ret <= 0) { + error = ret < 0 ? ret : -ETIMEDOUT; + dev_err(&client->dev, + "error while waiting for calibration to complete: %d\n", + error); + return error; + } + + if (memcmp(rek_resp, ts->cmd_resp, sizeof(rek_resp))) { + dev_err(&client->dev, + "unexpected calibration response: %*ph\n", + (int)sizeof(ts->cmd_resp), ts->cmd_resp); + return -EINVAL; + } + + return 0; +} + +static int elants_i2c_sw_reset(struct i2c_client *client) +{ + const u8 soft_rst_cmd[] = { 0x77, 0x77, 0x77, 0x77 }; + int error; + + error = elants_i2c_send(client, soft_rst_cmd, + sizeof(soft_rst_cmd)); + if (error) { + dev_err(&client->dev, "software reset failed: %d\n", error); + return error; + } + + /* + * We should wait at least 10 msec (but no more than 40) before + * sending fastboot or IAP command to the device. + */ + msleep(30); + + return 0; +} + +static u16 elants_i2c_parse_version(u8 *buf) +{ + return get_unaligned_be32(buf) >> 4; +} + +static int elants_i2c_query_fw_id(struct elants_data *ts) +{ + struct i2c_client *client = ts->client; + int error, retry_cnt; + const u8 cmd[] = { CMD_HEADER_READ, E_ELAN_INFO_FW_ID, 0x00, 0x01 }; + u8 resp[HEADER_SIZE]; + + for (retry_cnt = 0; retry_cnt < MAX_RETRIES; retry_cnt++) { + error = elants_i2c_execute_command(client, cmd, sizeof(cmd), + resp, sizeof(resp)); + if (!error) { + ts->hw_version = elants_i2c_parse_version(resp); + if (ts->hw_version != 0xffff) + return 0; + } + + dev_dbg(&client->dev, "read fw id error=%d, buf=%*phC\n", + error, (int)sizeof(resp), resp); + } + + dev_err(&client->dev, + "Failed to read fw id or fw id is invalid\n"); + + return -EINVAL; +} + +static int elants_i2c_query_fw_version(struct elants_data *ts) +{ + struct i2c_client *client = ts->client; + int error, retry_cnt; + const u8 cmd[] = { CMD_HEADER_READ, E_ELAN_INFO_FW_VER, 0x00, 0x01 }; + u8 resp[HEADER_SIZE]; + + for (retry_cnt = 0; retry_cnt < MAX_RETRIES; retry_cnt++) { + error = elants_i2c_execute_command(client, cmd, sizeof(cmd), + resp, sizeof(resp)); + if (!error) { + ts->fw_version = elants_i2c_parse_version(resp); + if (ts->fw_version != 0x0000 && + ts->fw_version != 0xffff) + return 0; + } + + dev_dbg(&client->dev, "read fw version error=%d, buf=%*phC\n", + error, (int)sizeof(resp), resp); + } + + dev_err(&client->dev, + "Failed to read fw version or fw version is invalid\n"); + + return -EINVAL; +} + +static int elants_i2c_query_test_version(struct elants_data *ts) +{ + struct i2c_client *client = ts->client; + int error, retry_cnt; + u16 version; + const u8 cmd[] = { CMD_HEADER_READ, E_ELAN_INFO_TEST_VER, 0x00, 0x01 }; + u8 resp[HEADER_SIZE]; + + for (retry_cnt = 0; retry_cnt < MAX_RETRIES; retry_cnt++) { + error = elants_i2c_execute_command(client, cmd, sizeof(cmd), + resp, sizeof(resp)); + if (!error) { + version = elants_i2c_parse_version(resp); + ts->test_version = version >> 8; + ts->solution_version = version & 0xff; + + return 0; + } + + dev_dbg(&client->dev, + "read test version error rc=%d, buf=%*phC\n", + error, (int)sizeof(resp), resp); + } + + dev_err(&client->dev, "Failed to read test version\n"); + + return -EINVAL; +} + +static int elants_i2c_query_bc_version(struct elants_data *ts) +{ + struct i2c_client *client = ts->client; + const u8 cmd[] = { CMD_HEADER_READ, E_ELAN_INFO_BC_VER, 0x00, 0x01 }; + u8 resp[HEADER_SIZE]; + u16 version; + int error; + + error = elants_i2c_execute_command(client, cmd, sizeof(cmd), + resp, sizeof(resp)); + if (error) { + dev_err(&client->dev, + "read BC version error=%d, buf=%*phC\n", + error, (int)sizeof(resp), resp); + return error; + } + + version = elants_i2c_parse_version(resp); + ts->bc_version = version >> 8; + ts->iap_version = version & 0xff; + + return 0; +} + +static int elants_i2c_query_ts_info(struct elants_data *ts) +{ + struct i2c_client *client = ts->client; + int error; + u8 resp[17]; + u16 phy_x, phy_y, rows, cols, osr; + const u8 get_resolution_cmd[] = { + CMD_HEADER_6B_READ, 0x00, 0x00, 0x00, 0x00, 0x00 + }; + const u8 get_osr_cmd[] = { + CMD_HEADER_READ, E_INFO_OSR, 0x00, 0x01 + }; + const u8 get_physical_scan_cmd[] = { + CMD_HEADER_READ, E_INFO_PHY_SCAN, 0x00, 0x01 + }; + const u8 get_physical_drive_cmd[] = { + CMD_HEADER_READ, E_INFO_PHY_DRIVER, 0x00, 0x01 + }; + + /* Get trace number */ + error = elants_i2c_execute_command(client, + get_resolution_cmd, + sizeof(get_resolution_cmd), + resp, sizeof(resp)); + if (error) { + dev_err(&client->dev, "get resolution command failed: %d\n", + error); + return error; + } + + rows = resp[2] + resp[6] + resp[10]; + cols = resp[3] + resp[7] + resp[11]; + + /* Process mm_to_pixel information */ + error = elants_i2c_execute_command(client, + get_osr_cmd, sizeof(get_osr_cmd), + resp, sizeof(resp)); + if (error) { + dev_err(&client->dev, "get osr command failed: %d\n", + error); + return error; + } + + osr = resp[3]; + + error = elants_i2c_execute_command(client, + get_physical_scan_cmd, + sizeof(get_physical_scan_cmd), + resp, sizeof(resp)); + if (error) { + dev_err(&client->dev, "get physical scan command failed: %d\n", + error); + return error; + } + + phy_x = get_unaligned_be16(&resp[2]); + + error = elants_i2c_execute_command(client, + get_physical_drive_cmd, + sizeof(get_physical_drive_cmd), + resp, sizeof(resp)); + if (error) { + dev_err(&client->dev, "get physical drive command failed: %d\n", + error); + return error; + } + + phy_y = get_unaligned_be16(&resp[2]); + + dev_dbg(&client->dev, "phy_x=%d, phy_y=%d\n", phy_x, phy_y); + + if (rows == 0 || cols == 0 || osr == 0) { + dev_warn(&client->dev, + "invalid trace number data: %d, %d, %d\n", + rows, cols, osr); + } else { + /* translate trace number to TS resolution */ + ts->x_max = ELAN_TS_RESOLUTION(rows, osr); + ts->x_res = DIV_ROUND_CLOSEST(ts->x_max, phy_x); + ts->y_max = ELAN_TS_RESOLUTION(cols, osr); + ts->y_res = DIV_ROUND_CLOSEST(ts->y_max, phy_y); + } + + return 0; +} + +static int elants_i2c_fastboot(struct i2c_client *client) +{ + const u8 boot_cmd[] = { 0x4D, 0x61, 0x69, 0x6E }; + int error; + + error = elants_i2c_send(client, boot_cmd, sizeof(boot_cmd)); + if (error) { + dev_err(&client->dev, "boot failed: %d\n", error); + return error; + } + + dev_dbg(&client->dev, "boot success -- 0x%x\n", client->addr); + return 0; +} + +static int elants_i2c_initialize(struct elants_data *ts) +{ + struct i2c_client *client = ts->client; + int error, retry_cnt; + const u8 hello_packet[] = { 0x55, 0x55, 0x55, 0x55 }; + const u8 recov_packet[] = { 0x55, 0x55, 0x80, 0x80 }; + u8 buf[HEADER_SIZE]; + + for (retry_cnt = 0; retry_cnt < MAX_RETRIES; retry_cnt++) { + error = elants_i2c_sw_reset(client); + if (error) { + /* Continue initializing if it's the last try */ + if (retry_cnt < MAX_RETRIES - 1) + continue; + } + + error = elants_i2c_fastboot(client); + if (error) { + /* Continue initializing if it's the last try */ + if (retry_cnt < MAX_RETRIES - 1) + continue; + } + + /* Wait for Hello packet */ + msleep(BOOT_TIME_DELAY_MS); + + error = elants_i2c_read(client, buf, sizeof(buf)); + if (error) { + dev_err(&client->dev, + "failed to read 'hello' packet: %d\n", error); + } else if (!memcmp(buf, hello_packet, sizeof(hello_packet))) { + ts->iap_mode = ELAN_IAP_OPERATIONAL; + break; + } else if (!memcmp(buf, recov_packet, sizeof(recov_packet))) { + /* + * Setting error code will mark device + * in recovery mode below. + */ + error = -EIO; + break; + } else { + error = -EINVAL; + dev_err(&client->dev, + "invalid 'hello' packet: %*ph\n", + (int)sizeof(buf), buf); + } + } + + if (!error) + error = elants_i2c_query_fw_id(ts); + if (!error) + error = elants_i2c_query_fw_version(ts); + + if (error) { + ts->iap_mode = ELAN_IAP_RECOVERY; + } else { + elants_i2c_query_test_version(ts); + elants_i2c_query_bc_version(ts); + elants_i2c_query_ts_info(ts); + } + + return 0; +} + +/* + * Firmware update interface. + */ + +static int elants_i2c_fw_write_page(struct i2c_client *client, + const void *page) +{ + const u8 ack_ok[] = { 0xaa, 0xaa }; + u8 buf[2]; + int retry; + int error; + + for (retry = 0; retry < MAX_FW_UPDATE_RETRIES; retry++) { + error = elants_i2c_send(client, page, ELAN_FW_PAGESIZE); + if (error) { + dev_err(&client->dev, + "IAP Write Page failed: %d\n", error); + continue; + } + + error = elants_i2c_read(client, buf, 2); + if (error) { + dev_err(&client->dev, + "IAP Ack read failed: %d\n", error); + return error; + } + + if (!memcmp(buf, ack_ok, sizeof(ack_ok))) + return 0; + + error = -EIO; + dev_err(&client->dev, + "IAP Get Ack Error [%02x:%02x]\n", + buf[0], buf[1]); + } + + return error; +} + +static int elants_i2c_do_update_firmware(struct i2c_client *client, + const struct firmware *fw, + bool force) +{ + const u8 enter_iap[] = { 0x45, 0x49, 0x41, 0x50 }; + const u8 enter_iap2[] = { 0x54, 0x00, 0x12, 0x34 }; + const u8 iap_ack[] = { 0x55, 0xaa, 0x33, 0xcc }; + u8 buf[HEADER_SIZE]; + u16 send_id; + int page, n_fw_pages; + int error; + + /* Recovery mode detection! */ + if (force) { + dev_dbg(&client->dev, "Recovery mode procedure\n"); + error = elants_i2c_send(client, enter_iap2, sizeof(enter_iap2)); + } else { + /* Start IAP Procedure */ + dev_dbg(&client->dev, "Normal IAP procedure\n"); + elants_i2c_sw_reset(client); + + error = elants_i2c_send(client, enter_iap, sizeof(enter_iap)); + } + + if (error) { + dev_err(&client->dev, "failed to enter IAP mode: %d\n", error); + return error; + } + + msleep(20); + + /* check IAP state */ + error = elants_i2c_read(client, buf, 4); + if (error) { + dev_err(&client->dev, + "failed to read IAP acknowledgement: %d\n", + error); + return error; + } + + if (memcmp(buf, iap_ack, sizeof(iap_ack))) { + dev_err(&client->dev, + "failed to enter IAP: %*ph (expected %*ph)\n", + (int)sizeof(buf), buf, (int)sizeof(iap_ack), iap_ack); + return -EIO; + } + + dev_info(&client->dev, "successfully entered IAP mode"); + + send_id = client->addr; + error = elants_i2c_send(client, &send_id, 1); + if (error) { + dev_err(&client->dev, "sending dummy byte failed: %d\n", + error); + return error; + } + + /* Clear the last page of Master */ + error = elants_i2c_send(client, fw->data, ELAN_FW_PAGESIZE); + if (error) { + dev_err(&client->dev, "clearing of the last page failed: %d\n", + error); + return error; + } + + error = elants_i2c_read(client, buf, 2); + if (error) { + dev_err(&client->dev, + "failed to read ACK for clearing the last page: %d\n", + error); + return error; + } + + n_fw_pages = fw->size / ELAN_FW_PAGESIZE; + dev_dbg(&client->dev, "IAP Pages = %d\n", n_fw_pages); + + for (page = 0; page < n_fw_pages; page++) { + error = elants_i2c_fw_write_page(client, + fw->data + page * ELAN_FW_PAGESIZE); + if (error) { + dev_err(&client->dev, + "failed to write FW page %d: %d\n", + page, error); + return error; + } + } + + /* Old iap needs to wait 200ms for WDT and rest is for hello packets */ + msleep(300); + + dev_info(&client->dev, "firmware update completed\n"); + return 0; +} + +static int elants_i2c_fw_update(struct elants_data *ts) +{ + struct i2c_client *client = ts->client; + const struct firmware *fw; + int error; + + error = request_firmware(&fw, ELAN_FW_FILENAME, &client->dev); + if (error) { + dev_err(&client->dev, "failed to request firmware %s: %d\n", + ELAN_FW_FILENAME, error); + return error; + } + + if (fw->size % ELAN_FW_PAGESIZE) { + dev_err(&client->dev, "invalid firmware length: %zu\n", + fw->size); + error = -EINVAL; + goto out; + } + + disable_irq(client->irq); + + error = elants_i2c_do_update_firmware(client, fw, + ts->iap_mode == ELAN_IAP_RECOVERY); + if (error) { + dev_err(&client->dev, "firmware update failed: %d\n", error); + ts->iap_mode = ELAN_IAP_RECOVERY; + goto out_enable_irq; + } + + error = elants_i2c_initialize(ts); + if (error) { + dev_err(&client->dev, + "failed to initialize device after firmware update: %d\n", + error); + ts->iap_mode = ELAN_IAP_RECOVERY; + goto out_enable_irq; + } + + ts->iap_mode = ELAN_IAP_OPERATIONAL; + +out_enable_irq: + ts->state = ELAN_STATE_NORMAL; + enable_irq(client->irq); + msleep(100); + + if (!error) + elants_i2c_calibrate(ts); +out: + release_firmware(fw); + return error; +} + +/* + * Event reporting. + */ + +static void elants_i2c_mt_event(struct elants_data *ts, u8 *buf) +{ + struct input_dev *input = ts->input; + unsigned int n_fingers; + u16 finger_state; + int i; + + n_fingers = buf[FW_POS_STATE + 1] & 0x0f; + finger_state = ((buf[FW_POS_STATE + 1] & 0x30) << 4) | + buf[FW_POS_STATE]; + + dev_dbg(&ts->client->dev, + "n_fingers: %u, state: %04x\n", n_fingers, finger_state); + + for (i = 0; i < MAX_CONTACT_NUM && n_fingers; i++) { + if (finger_state & 1) { + unsigned int x, y, p, w; + u8 *pos; + + pos = &buf[FW_POS_XY + i * 3]; + x = (((u16)pos[0] & 0xf0) << 4) | pos[1]; + y = (((u16)pos[0] & 0x0f) << 8) | pos[2]; + p = buf[FW_POS_PRESSURE + i]; + w = buf[FW_POS_WIDTH + i]; + + dev_dbg(&ts->client->dev, "i=%d x=%d y=%d p=%d w=%d\n", + i, x, y, p, w); + + input_mt_slot(input, i); + input_mt_report_slot_state(input, MT_TOOL_FINGER, true); + input_event(input, EV_ABS, ABS_MT_POSITION_X, x); + input_event(input, EV_ABS, ABS_MT_POSITION_Y, y); + input_event(input, EV_ABS, ABS_MT_PRESSURE, p); + input_event(input, EV_ABS, ABS_MT_TOUCH_MAJOR, w); + + n_fingers--; + } + + finger_state >>= 1; + } + + input_mt_sync_frame(input); + input_sync(input); +} + +static u8 elants_i2c_calculate_checksum(u8 *buf) +{ + u8 checksum = 0; + u8 i; + + for (i = 0; i < FW_POS_CHECKSUM; i++) + checksum += buf[i]; + + return checksum; +} + +static void elants_i2c_event(struct elants_data *ts, u8 *buf) +{ + u8 checksum = elants_i2c_calculate_checksum(buf); + + if (unlikely(buf[FW_POS_CHECKSUM] != checksum)) + dev_warn(&ts->client->dev, + "%s: invalid checksum for packet %02x: %02x vs. %02x\n", + __func__, buf[FW_POS_HEADER], + checksum, buf[FW_POS_CHECKSUM]); + else if (unlikely(buf[FW_POS_HEADER] != HEADER_REPORT_10_FINGER)) + dev_warn(&ts->client->dev, + "%s: unknown packet type: %02x\n", + __func__, buf[FW_POS_HEADER]); + else + elants_i2c_mt_event(ts, buf); +} + +static irqreturn_t elants_i2c_irq(int irq, void *_dev) +{ + const u8 wait_packet[] = { 0x64, 0x64, 0x64, 0x64 }; + struct elants_data *ts = _dev; + struct i2c_client *client = ts->client; + int report_count, report_len; + int i; + int len; + + len = i2c_master_recv(client, ts->buf, sizeof(ts->buf)); + if (len < 0) { + dev_err(&client->dev, "%s: failed to read data: %d\n", + __func__, len); + goto out; + } + + dev_dbg(&client->dev, "%s: packet %*ph\n", + __func__, HEADER_SIZE, ts->buf); + + switch (ts->state) { + case ELAN_WAIT_RECALIBRATION: + if (ts->buf[FW_HDR_TYPE] == CMD_HEADER_REK) { + memcpy(ts->cmd_resp, ts->buf, sizeof(ts->cmd_resp)); + complete(&ts->cmd_done); + ts->state = ELAN_STATE_NORMAL; + } + break; + + case ELAN_WAIT_QUEUE_HEADER: + if (ts->buf[FW_HDR_TYPE] != QUEUE_HEADER_NORMAL) + break; + + ts->state = ELAN_STATE_NORMAL; + /* fall through */ + + case ELAN_STATE_NORMAL: + + switch (ts->buf[FW_HDR_TYPE]) { + case CMD_HEADER_HELLO: + case CMD_HEADER_RESP: + case CMD_HEADER_REK: + break; + + case QUEUE_HEADER_WAIT: + if (memcmp(ts->buf, wait_packet, sizeof(wait_packet))) { + dev_err(&client->dev, + "invalid wait packet %*ph\n", + HEADER_SIZE, ts->buf); + } else { + ts->state = ELAN_WAIT_QUEUE_HEADER; + udelay(30); + } + break; + + case QUEUE_HEADER_SINGLE: + elants_i2c_event(ts, &ts->buf[HEADER_SIZE]); + break; + + case QUEUE_HEADER_NORMAL: + report_count = ts->buf[FW_HDR_COUNT]; + if (report_count > 3) { + dev_err(&client->dev, + "too large report count: %*ph\n", + HEADER_SIZE, ts->buf); + break; + } + + report_len = ts->buf[FW_HDR_LENGTH] / report_count; + if (report_len != PACKET_SIZE) { + dev_err(&client->dev, + "mismatching report length: %*ph\n", + HEADER_SIZE, ts->buf); + break; + } + + for (i = 0; i < report_count; i++) { + u8 *buf = ts->buf + HEADER_SIZE + + i * PACKET_SIZE; + elants_i2c_event(ts, buf); + } + break; + + default: + dev_err(&client->dev, "unknown packet %*ph\n", + HEADER_SIZE, ts->buf); + break; + } + break; + } + +out: + return IRQ_HANDLED; +} + +/* + * sysfs interface + */ +static ssize_t calibrate_store(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t count) +{ + struct i2c_client *client = to_i2c_client(dev); + struct elants_data *ts = i2c_get_clientdata(client); + int error; + + error = mutex_lock_interruptible(&ts->sysfs_mutex); + if (error) + return error; + + error = elants_i2c_calibrate(ts); + + mutex_unlock(&ts->sysfs_mutex); + return error ?: count; +} + +static ssize_t write_update_fw(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t count) +{ + struct i2c_client *client = to_i2c_client(dev); + struct elants_data *ts = i2c_get_clientdata(client); + int error; + + error = mutex_lock_interruptible(&ts->sysfs_mutex); + if (error) + return error; + + error = elants_i2c_fw_update(ts); + dev_dbg(dev, "firmware update result: %d\n", error); + + mutex_unlock(&ts->sysfs_mutex); + return error ?: count; +} + +static ssize_t show_iap_mode(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct i2c_client *client = to_i2c_client(dev); + struct elants_data *ts = i2c_get_clientdata(client); + + return sprintf(buf, "%s\n", + ts->iap_mode == ELAN_IAP_OPERATIONAL ? + "Normal" : "Recovery"); +} + +static DEVICE_ATTR(calibrate, S_IWUSR, NULL, calibrate_store); +static DEVICE_ATTR(iap_mode, S_IRUGO, show_iap_mode, NULL); +static DEVICE_ATTR(update_fw, S_IWUSR, NULL, write_update_fw); + +struct elants_version_attribute { + struct device_attribute dattr; + size_t field_offset; + size_t field_size; +}; + +#define __ELANTS_FIELD_SIZE(_field) \ + sizeof(((struct elants_data *)NULL)->_field) +#define __ELANTS_VERIFY_SIZE(_field) \ + (BUILD_BUG_ON_ZERO(__ELANTS_FIELD_SIZE(_field) > 2) + \ + __ELANTS_FIELD_SIZE(_field)) +#define ELANTS_VERSION_ATTR(_field) \ + struct elants_version_attribute elants_ver_attr_##_field = { \ + .dattr = __ATTR(_field, S_IRUGO, \ + elants_version_attribute_show, NULL), \ + .field_offset = offsetof(struct elants_data, _field), \ + .field_size = __ELANTS_VERIFY_SIZE(_field), \ + } + +static ssize_t elants_version_attribute_show(struct device *dev, + struct device_attribute *dattr, + char *buf) +{ + struct i2c_client *client = to_i2c_client(dev); + struct elants_data *ts = i2c_get_clientdata(client); + struct elants_version_attribute *attr = + container_of(dattr, struct elants_version_attribute, dattr); + u8 *field = (u8 *)((char *)ts + attr->field_offset); + unsigned int fmt_size; + unsigned int val; + + if (attr->field_size == 1) { + val = *field; + fmt_size = 2; /* 2 HEX digits */ + } else { + val = *(u16 *)field; + fmt_size = 4; /* 4 HEX digits */ + } + + return sprintf(buf, "%0*x\n", fmt_size, val); +} + +static ELANTS_VERSION_ATTR(fw_version); +static ELANTS_VERSION_ATTR(hw_version); +static ELANTS_VERSION_ATTR(test_version); +static ELANTS_VERSION_ATTR(solution_version); +static ELANTS_VERSION_ATTR(bc_version); +static ELANTS_VERSION_ATTR(iap_version); + +static struct attribute *elants_attributes[] = { + &dev_attr_calibrate.attr, + &dev_attr_update_fw.attr, + &dev_attr_iap_mode.attr, + + &elants_ver_attr_fw_version.dattr.attr, + &elants_ver_attr_hw_version.dattr.attr, + &elants_ver_attr_test_version.dattr.attr, + &elants_ver_attr_solution_version.dattr.attr, + &elants_ver_attr_bc_version.dattr.attr, + &elants_ver_attr_iap_version.dattr.attr, + NULL +}; + +static struct attribute_group elants_attribute_group = { + .attrs = elants_attributes, +}; + +static void elants_i2c_remove_sysfs_group(void *_data) +{ + struct elants_data *ts = _data; + + sysfs_remove_group(&ts->client->dev.kobj, &elants_attribute_group); +} + +static int elants_i2c_probe(struct i2c_client *client, + const struct i2c_device_id *id) +{ + union i2c_smbus_data dummy; + struct elants_data *ts; + unsigned long irqflags; + int error; + + if (!i2c_check_functionality(client->adapter, I2C_FUNC_I2C)) { + dev_err(&client->dev, + "%s: i2c check functionality error\n", DEVICE_NAME); + return -ENXIO; + } + + /* Make sure there is something at this address */ + if (i2c_smbus_xfer(client->adapter, client->addr, 0, + I2C_SMBUS_READ, 0, I2C_SMBUS_BYTE, &dummy) < 0) { + dev_err(&client->dev, "nothing at this address\n"); + return -ENXIO; + } + + ts = devm_kzalloc(&client->dev, sizeof(struct elants_data), GFP_KERNEL); + if (!ts) + return -ENOMEM; + + mutex_init(&ts->sysfs_mutex); + init_completion(&ts->cmd_done); + + ts->client = client; + i2c_set_clientdata(client, ts); + + error = elants_i2c_initialize(ts); + if (error) { + dev_err(&client->dev, "failed to initialize: %d\n", error); + return error; + } + + ts->input = devm_input_allocate_device(&client->dev); + if (!ts->input) { + dev_err(&client->dev, "Failed to allocate input device\n"); + return -ENOMEM; + } + + ts->input->name = "Elan Touchscreen"; + ts->input->id.bustype = BUS_I2C; + + __set_bit(BTN_TOUCH, ts->input->keybit); + __set_bit(EV_ABS, ts->input->evbit); + __set_bit(EV_KEY, ts->input->evbit); + + /* Single touch input params setup */ + input_set_abs_params(ts->input, ABS_X, 0, ts->x_max, 0, 0); + input_set_abs_params(ts->input, ABS_Y, 0, ts->y_max, 0, 0); + input_set_abs_params(ts->input, ABS_PRESSURE, 0, 255, 0, 0); + input_abs_set_res(ts->input, ABS_X, ts->x_res); + input_abs_set_res(ts->input, ABS_Y, ts->y_res); + + /* Multitouch input params setup */ + error = input_mt_init_slots(ts->input, MAX_CONTACT_NUM, + INPUT_MT_DIRECT | INPUT_MT_DROP_UNUSED); + if (error) { + dev_err(&client->dev, + "failed to initialize MT slots: %d\n", error); + return error; + } + + input_set_abs_params(ts->input, ABS_MT_POSITION_X, 0, ts->x_max, 0, 0); + input_set_abs_params(ts->input, ABS_MT_POSITION_Y, 0, ts->y_max, 0, 0); + input_set_abs_params(ts->input, ABS_MT_TOUCH_MAJOR, 0, 255, 0, 0); + input_set_abs_params(ts->input, ABS_MT_PRESSURE, 0, 255, 0, 0); + input_abs_set_res(ts->input, ABS_MT_POSITION_X, ts->x_res); + input_abs_set_res(ts->input, ABS_MT_POSITION_Y, ts->y_res); + + input_set_drvdata(ts->input, ts); + + error = input_register_device(ts->input); + if (error) { + dev_err(&client->dev, + "unable to register input device: %d\n", error); + return error; + } + + /* + * Systems using device tree should set up interrupt via DTS, + * the rest will use the default falling edge interrupts. + */ + irqflags = client->dev.of_node ? 0 : IRQF_TRIGGER_FALLING; + + error = devm_request_threaded_irq(&client->dev, client->irq, + NULL, elants_i2c_irq, + irqflags | IRQF_ONESHOT, + client->name, ts); + if (error) { + dev_err(&client->dev, "Failed to register interrupt\n"); + return error; + } + + /* + * Systems using device tree should set up wakeup via DTS, + * the rest will configure device as wakeup source by default. + */ + if (!client->dev.of_node) + device_init_wakeup(&client->dev, true); + + error = sysfs_create_group(&client->dev.kobj, &elants_attribute_group); + if (error) { + dev_err(&client->dev, "failed to create sysfs attributes: %d\n", + error); + return error; + } + + error = devm_add_action(&client->dev, + elants_i2c_remove_sysfs_group, ts); + if (error) { + elants_i2c_remove_sysfs_group(ts); + dev_err(&client->dev, + "Failed to add sysfs cleanup action: %d\n", + error); + return error; + } + + return 0; +} + +static int __maybe_unused elants_i2c_suspend(struct device *dev) +{ + struct i2c_client *client = to_i2c_client(dev); + struct elants_data *ts = i2c_get_clientdata(client); + const u8 set_sleep_cmd[] = { 0x54, 0x50, 0x00, 0x01 }; + int retry_cnt; + int error; + + /* Command not support in IAP recovery mode */ + if (ts->iap_mode != ELAN_IAP_OPERATIONAL) + return -EBUSY; + + disable_irq(client->irq); + + for (retry_cnt = 0; retry_cnt < MAX_RETRIES; retry_cnt++) { + error = elants_i2c_send(client, set_sleep_cmd, + sizeof(set_sleep_cmd)); + if (!error) + break; + + dev_err(&client->dev, "suspend command failed: %d\n", error); + } + + if (device_may_wakeup(dev)) + ts->wake_irq_enabled = (enable_irq_wake(client->irq) == 0); + + return 0; +} + +static int __maybe_unused elants_i2c_resume(struct device *dev) +{ + struct i2c_client *client = to_i2c_client(dev); + struct elants_data *ts = i2c_get_clientdata(client); + const u8 set_active_cmd[] = { 0x54, 0x58, 0x00, 0x01 }; + int retry_cnt; + int error; + + if (device_may_wakeup(dev) && ts->wake_irq_enabled) + disable_irq_wake(client->irq); + + for (retry_cnt = 0; retry_cnt < MAX_RETRIES; retry_cnt++) { + error = elants_i2c_send(client, set_active_cmd, + sizeof(set_active_cmd)); + if (!error) + break; + + dev_err(&client->dev, "resume command failed: %d\n", error); + } + + ts->state = ELAN_STATE_NORMAL; + enable_irq(client->irq); + + return 0; +} + +static SIMPLE_DEV_PM_OPS(elants_i2c_pm_ops, + elants_i2c_suspend, elants_i2c_resume); + +static const struct i2c_device_id elants_i2c_id[] = { + { DEVICE_NAME, 0 }, + { } +}; +MODULE_DEVICE_TABLE(i2c, elants_i2c_id); + +#ifdef CONFIG_ACPI +static const struct acpi_device_id elants_acpi_id[] = { + { "ELAN0001", 0 }, + { } +}; +MODULE_DEVICE_TABLE(acpi, elants_acpi_id); +#endif + +#ifdef CONFIG_OF +static const struct of_device_id elants_of_match[] = { + { .compatible = "elan,ekth3500" }, + { /* sentinel */ } +}; +MODULE_DEVICE_TABLE(of, elants_of_match); +#endif + +static struct i2c_driver elants_i2c_driver = { + .probe = elants_i2c_probe, + .id_table = elants_i2c_id, + .driver = { + .name = DEVICE_NAME, + .owner = THIS_MODULE, + .pm = &elants_i2c_pm_ops, + .acpi_match_table = ACPI_PTR(elants_acpi_id), + .of_match_table = of_match_ptr(elants_of_match), + }, +}; +module_i2c_driver(elants_i2c_driver); + +MODULE_AUTHOR("Scott Liu <scott.liu@emc.com.tw>"); +MODULE_DESCRIPTION("Elan I2c Touchscreen driver"); +MODULE_VERSION(DRV_VERSION); +MODULE_LICENSE("GPL"); diff --git a/drivers/input/touchscreen/goodix.c b/drivers/input/touchscreen/goodix.c new file mode 100644 index 000000000000..ca196689f025 --- /dev/null +++ b/drivers/input/touchscreen/goodix.c @@ -0,0 +1,395 @@ +/* + * Driver for Goodix Touchscreens + * + * Copyright (c) 2014 Red Hat Inc. + * + * This code is based on gt9xx.c authored by andrew@goodix.com: + * + * 2010 - 2012 Goodix Technology. + */ + +/* + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the Free + * Software Foundation; version 2 of the License. + */ + +#include <linux/kernel.h> +#include <linux/i2c.h> +#include <linux/input.h> +#include <linux/input/mt.h> +#include <linux/module.h> +#include <linux/delay.h> +#include <linux/irq.h> +#include <linux/interrupt.h> +#include <linux/slab.h> +#include <asm/unaligned.h> + +struct goodix_ts_data { + struct i2c_client *client; + struct input_dev *input_dev; + int abs_x_max; + int abs_y_max; + unsigned int max_touch_num; + unsigned int int_trigger_type; +}; + +#define GOODIX_MAX_HEIGHT 4096 +#define GOODIX_MAX_WIDTH 4096 +#define GOODIX_INT_TRIGGER 1 +#define GOODIX_CONTACT_SIZE 8 +#define GOODIX_MAX_CONTACTS 10 + +#define GOODIX_CONFIG_MAX_LENGTH 240 + +/* Register defines */ +#define GOODIX_READ_COOR_ADDR 0x814E +#define GOODIX_REG_CONFIG_DATA 0x8047 +#define GOODIX_REG_VERSION 0x8140 + +#define RESOLUTION_LOC 1 +#define TRIGGER_LOC 6 + +static const unsigned long goodix_irq_flags[] = { + IRQ_TYPE_EDGE_RISING, + IRQ_TYPE_EDGE_FALLING, + IRQ_TYPE_LEVEL_LOW, + IRQ_TYPE_LEVEL_HIGH, +}; + +/** + * goodix_i2c_read - read data from a register of the i2c slave device. + * + * @client: i2c device. + * @reg: the register to read from. + * @buf: raw write data buffer. + * @len: length of the buffer to write + */ +static int goodix_i2c_read(struct i2c_client *client, + u16 reg, u8 *buf, int len) +{ + struct i2c_msg msgs[2]; + u16 wbuf = cpu_to_be16(reg); + int ret; + + msgs[0].flags = 0; + msgs[0].addr = client->addr; + msgs[0].len = 2; + msgs[0].buf = (u8 *) &wbuf; + + msgs[1].flags = I2C_M_RD; + msgs[1].addr = client->addr; + msgs[1].len = len; + msgs[1].buf = buf; + + ret = i2c_transfer(client->adapter, msgs, 2); + return ret < 0 ? ret : (ret != ARRAY_SIZE(msgs) ? -EIO : 0); +} + +static int goodix_ts_read_input_report(struct goodix_ts_data *ts, u8 *data) +{ + int touch_num; + int error; + + error = goodix_i2c_read(ts->client, GOODIX_READ_COOR_ADDR, data, + GOODIX_CONTACT_SIZE + 1); + if (error) { + dev_err(&ts->client->dev, "I2C transfer error: %d\n", error); + return error; + } + + touch_num = data[0] & 0x0f; + if (touch_num > GOODIX_MAX_CONTACTS) + return -EPROTO; + + if (touch_num > 1) { + data += 1 + GOODIX_CONTACT_SIZE; + error = goodix_i2c_read(ts->client, + GOODIX_READ_COOR_ADDR + + 1 + GOODIX_CONTACT_SIZE, + data, + GOODIX_CONTACT_SIZE * (touch_num - 1)); + if (error) + return error; + } + + return touch_num; +} + +static void goodix_ts_report_touch(struct goodix_ts_data *ts, u8 *coor_data) +{ + int id = coor_data[0] & 0x0F; + int input_x = get_unaligned_le16(&coor_data[1]); + int input_y = get_unaligned_le16(&coor_data[3]); + int input_w = get_unaligned_le16(&coor_data[5]); + + input_mt_slot(ts->input_dev, id); + input_mt_report_slot_state(ts->input_dev, MT_TOOL_FINGER, true); + input_report_abs(ts->input_dev, ABS_MT_POSITION_X, input_x); + input_report_abs(ts->input_dev, ABS_MT_POSITION_Y, input_y); + input_report_abs(ts->input_dev, ABS_MT_TOUCH_MAJOR, input_w); + input_report_abs(ts->input_dev, ABS_MT_WIDTH_MAJOR, input_w); +} + +/** + * goodix_process_events - Process incoming events + * + * @ts: our goodix_ts_data pointer + * + * Called when the IRQ is triggered. Read the current device state, and push + * the input events to the user space. + */ +static void goodix_process_events(struct goodix_ts_data *ts) +{ + u8 point_data[1 + GOODIX_CONTACT_SIZE * GOODIX_MAX_CONTACTS]; + int touch_num; + int i; + + touch_num = goodix_ts_read_input_report(ts, point_data); + if (touch_num < 0) + return; + + for (i = 0; i < touch_num; i++) + goodix_ts_report_touch(ts, + &point_data[1 + GOODIX_CONTACT_SIZE * i]); + + input_mt_sync_frame(ts->input_dev); + input_sync(ts->input_dev); +} + +/** + * goodix_ts_irq_handler - The IRQ handler + * + * @irq: interrupt number. + * @dev_id: private data pointer. + */ +static irqreturn_t goodix_ts_irq_handler(int irq, void *dev_id) +{ + static const u8 end_cmd[] = { + GOODIX_READ_COOR_ADDR >> 8, + GOODIX_READ_COOR_ADDR & 0xff, + 0 + }; + struct goodix_ts_data *ts = dev_id; + + goodix_process_events(ts); + + if (i2c_master_send(ts->client, end_cmd, sizeof(end_cmd)) < 0) + dev_err(&ts->client->dev, "I2C write end_cmd error\n"); + + return IRQ_HANDLED; +} + +/** + * goodix_read_config - Read the embedded configuration of the panel + * + * @ts: our goodix_ts_data pointer + * + * Must be called during probe + */ +static void goodix_read_config(struct goodix_ts_data *ts) +{ + u8 config[GOODIX_CONFIG_MAX_LENGTH]; + int error; + + error = goodix_i2c_read(ts->client, GOODIX_REG_CONFIG_DATA, + config, + GOODIX_CONFIG_MAX_LENGTH); + if (error) { + dev_warn(&ts->client->dev, + "Error reading config (%d), using defaults\n", + error); + ts->abs_x_max = GOODIX_MAX_WIDTH; + ts->abs_y_max = GOODIX_MAX_HEIGHT; + ts->int_trigger_type = GOODIX_INT_TRIGGER; + return; + } + + ts->abs_x_max = get_unaligned_le16(&config[RESOLUTION_LOC]); + ts->abs_y_max = get_unaligned_le16(&config[RESOLUTION_LOC + 2]); + ts->int_trigger_type = (config[TRIGGER_LOC]) & 0x03; + if (!ts->abs_x_max || !ts->abs_y_max) { + dev_err(&ts->client->dev, + "Invalid config, using defaults\n"); + ts->abs_x_max = GOODIX_MAX_WIDTH; + ts->abs_y_max = GOODIX_MAX_HEIGHT; + } +} + + +/** + * goodix_read_version - Read goodix touchscreen version + * + * @client: the i2c client + * @version: output buffer containing the version on success + */ +static int goodix_read_version(struct i2c_client *client, u16 *version) +{ + int error; + u8 buf[6]; + + error = goodix_i2c_read(client, GOODIX_REG_VERSION, buf, sizeof(buf)); + if (error) { + dev_err(&client->dev, "read version failed: %d\n", error); + return error; + } + + if (version) + *version = get_unaligned_le16(&buf[4]); + + dev_info(&client->dev, "IC VERSION: %6ph\n", buf); + + return 0; +} + +/** + * goodix_i2c_test - I2C test function to check if the device answers. + * + * @client: the i2c client + */ +static int goodix_i2c_test(struct i2c_client *client) +{ + int retry = 0; + int error; + u8 test; + + while (retry++ < 2) { + error = goodix_i2c_read(client, GOODIX_REG_CONFIG_DATA, + &test, 1); + if (!error) + return 0; + + dev_err(&client->dev, "i2c test failed attempt %d: %d\n", + retry, error); + msleep(20); + } + + return error; +} + +/** + * goodix_request_input_dev - Allocate, populate and register the input device + * + * @ts: our goodix_ts_data pointer + * + * Must be called during probe + */ +static int goodix_request_input_dev(struct goodix_ts_data *ts) +{ + int error; + + ts->input_dev = devm_input_allocate_device(&ts->client->dev); + if (!ts->input_dev) { + dev_err(&ts->client->dev, "Failed to allocate input device."); + return -ENOMEM; + } + + ts->input_dev->evbit[0] = BIT_MASK(EV_SYN) | + BIT_MASK(EV_KEY) | + BIT_MASK(EV_ABS); + + input_set_abs_params(ts->input_dev, ABS_MT_POSITION_X, 0, + ts->abs_x_max, 0, 0); + input_set_abs_params(ts->input_dev, ABS_MT_POSITION_Y, 0, + ts->abs_y_max, 0, 0); + input_set_abs_params(ts->input_dev, ABS_MT_WIDTH_MAJOR, 0, 255, 0, 0); + input_set_abs_params(ts->input_dev, ABS_MT_TOUCH_MAJOR, 0, 255, 0, 0); + + input_mt_init_slots(ts->input_dev, GOODIX_MAX_CONTACTS, + INPUT_MT_DIRECT | INPUT_MT_DROP_UNUSED); + + ts->input_dev->name = "Goodix Capacitive TouchScreen"; + ts->input_dev->phys = "input/ts"; + ts->input_dev->id.bustype = BUS_I2C; + ts->input_dev->id.vendor = 0x0416; + ts->input_dev->id.product = 0x1001; + ts->input_dev->id.version = 10427; + + error = input_register_device(ts->input_dev); + if (error) { + dev_err(&ts->client->dev, + "Failed to register input device: %d", error); + return error; + } + + return 0; +} + +static int goodix_ts_probe(struct i2c_client *client, + const struct i2c_device_id *id) +{ + struct goodix_ts_data *ts; + unsigned long irq_flags; + int error; + u16 version_info; + + dev_dbg(&client->dev, "I2C Address: 0x%02x\n", client->addr); + + if (!i2c_check_functionality(client->adapter, I2C_FUNC_I2C)) { + dev_err(&client->dev, "I2C check functionality failed.\n"); + return -ENXIO; + } + + ts = devm_kzalloc(&client->dev, sizeof(*ts), GFP_KERNEL); + if (!ts) + return -ENOMEM; + + ts->client = client; + i2c_set_clientdata(client, ts); + + error = goodix_i2c_test(client); + if (error) { + dev_err(&client->dev, "I2C communication failure: %d\n", error); + return error; + } + + error = goodix_read_version(client, &version_info); + if (error) { + dev_err(&client->dev, "Read version failed.\n"); + return error; + } + + goodix_read_config(ts); + + error = goodix_request_input_dev(ts); + if (error) + return error; + + irq_flags = goodix_irq_flags[ts->int_trigger_type] | IRQF_ONESHOT; + error = devm_request_threaded_irq(&ts->client->dev, client->irq, + NULL, goodix_ts_irq_handler, + irq_flags, client->name, ts); + if (error) { + dev_err(&client->dev, "request IRQ failed: %d\n", error); + return error; + } + + return 0; +} + +static const struct i2c_device_id goodix_ts_id[] = { + { "GDIX1001:00", 0 }, + { } +}; + +static const struct acpi_device_id goodix_acpi_match[] = { + { "GDIX1001", 0 }, + { } +}; +MODULE_DEVICE_TABLE(acpi, goodix_acpi_match); + +static struct i2c_driver goodix_ts_driver = { + .probe = goodix_ts_probe, + .id_table = goodix_ts_id, + .driver = { + .name = "Goodix-TS", + .owner = THIS_MODULE, + .acpi_match_table = goodix_acpi_match, + }, +}; +module_i2c_driver(goodix_ts_driver); + +MODULE_AUTHOR("Benjamin Tissoires <benjamin.tissoires@gmail.com>"); +MODULE_AUTHOR("Bastien Nocera <hadess@hadess.net>"); +MODULE_DESCRIPTION("Goodix touchscreen driver"); +MODULE_LICENSE("GPL v2"); diff --git a/drivers/input/touchscreen/ili210x.c b/drivers/input/touchscreen/ili210x.c index 2a5089139818..da6dc819c846 100644 --- a/drivers/input/touchscreen/ili210x.c +++ b/drivers/input/touchscreen/ili210x.c @@ -311,8 +311,7 @@ static int ili210x_i2c_remove(struct i2c_client *client) return 0; } -#ifdef CONFIG_PM_SLEEP -static int ili210x_i2c_suspend(struct device *dev) +static int __maybe_unused ili210x_i2c_suspend(struct device *dev) { struct i2c_client *client = to_i2c_client(dev); @@ -322,7 +321,7 @@ static int ili210x_i2c_suspend(struct device *dev) return 0; } -static int ili210x_i2c_resume(struct device *dev) +static int __maybe_unused ili210x_i2c_resume(struct device *dev) { struct i2c_client *client = to_i2c_client(dev); @@ -331,7 +330,6 @@ static int ili210x_i2c_resume(struct device *dev) return 0; } -#endif static SIMPLE_DEV_PM_OPS(ili210x_i2c_pm, ili210x_i2c_suspend, ili210x_i2c_resume); diff --git a/drivers/input/touchscreen/ipaq-micro-ts.c b/drivers/input/touchscreen/ipaq-micro-ts.c index 62c8976e616f..33c134820ef9 100644 --- a/drivers/input/touchscreen/ipaq-micro-ts.c +++ b/drivers/input/touchscreen/ipaq-micro-ts.c @@ -122,8 +122,7 @@ static int micro_ts_probe(struct platform_device *pdev) return 0; } -#ifdef CONFIG_PM_SLEEP -static int micro_ts_suspend(struct device *dev) +static int __maybe_unused micro_ts_suspend(struct device *dev) { struct touchscreen_data *ts = dev_get_drvdata(dev); @@ -132,7 +131,7 @@ static int micro_ts_suspend(struct device *dev) return 0; } -static int micro_ts_resume(struct device *dev) +static int __maybe_unused micro_ts_resume(struct device *dev) { struct touchscreen_data *ts = dev_get_drvdata(dev); struct input_dev *input = ts->input; @@ -146,7 +145,6 @@ static int micro_ts_resume(struct device *dev) return 0; } -#endif static const struct dev_pm_ops micro_ts_dev_pm_ops = { SET_SYSTEM_SLEEP_PM_OPS(micro_ts_suspend, micro_ts_resume) diff --git a/drivers/input/touchscreen/mms114.c b/drivers/input/touchscreen/mms114.c index 372bbf7658fe..67c0d31613d8 100644 --- a/drivers/input/touchscreen/mms114.c +++ b/drivers/input/touchscreen/mms114.c @@ -515,8 +515,7 @@ static int mms114_probe(struct i2c_client *client, return 0; } -#ifdef CONFIG_PM_SLEEP -static int mms114_suspend(struct device *dev) +static int __maybe_unused mms114_suspend(struct device *dev) { struct i2c_client *client = to_i2c_client(dev); struct mms114_data *data = i2c_get_clientdata(client); @@ -540,7 +539,7 @@ static int mms114_suspend(struct device *dev) return 0; } -static int mms114_resume(struct device *dev) +static int __maybe_unused mms114_resume(struct device *dev) { struct i2c_client *client = to_i2c_client(dev); struct mms114_data *data = i2c_get_clientdata(client); @@ -559,7 +558,6 @@ static int mms114_resume(struct device *dev) return 0; } -#endif static SIMPLE_DEV_PM_OPS(mms114_pm_ops, mms114_suspend, mms114_resume); diff --git a/drivers/input/touchscreen/pixcir_i2c_ts.c b/drivers/input/touchscreen/pixcir_i2c_ts.c index fc49c75317d1..4fb5537fdd42 100644 --- a/drivers/input/touchscreen/pixcir_i2c_ts.c +++ b/drivers/input/touchscreen/pixcir_i2c_ts.c @@ -347,8 +347,7 @@ static void pixcir_input_close(struct input_dev *dev) pixcir_stop(ts); } -#ifdef CONFIG_PM_SLEEP -static int pixcir_i2c_ts_suspend(struct device *dev) +static int __maybe_unused pixcir_i2c_ts_suspend(struct device *dev) { struct i2c_client *client = to_i2c_client(dev); struct pixcir_i2c_ts_data *ts = i2c_get_clientdata(client); @@ -377,7 +376,7 @@ unlock: return ret; } -static int pixcir_i2c_ts_resume(struct device *dev) +static int __maybe_unused pixcir_i2c_ts_resume(struct device *dev) { struct i2c_client *client = to_i2c_client(dev); struct pixcir_i2c_ts_data *ts = i2c_get_clientdata(client); @@ -405,7 +404,6 @@ unlock: return ret; } -#endif static SIMPLE_DEV_PM_OPS(pixcir_dev_pm_ops, pixcir_i2c_ts_suspend, pixcir_i2c_ts_resume); diff --git a/drivers/input/touchscreen/st1232.c b/drivers/input/touchscreen/st1232.c index 3c0f57efe7b1..697e26e52d54 100644 --- a/drivers/input/touchscreen/st1232.c +++ b/drivers/input/touchscreen/st1232.c @@ -243,8 +243,7 @@ static int st1232_ts_remove(struct i2c_client *client) return 0; } -#ifdef CONFIG_PM_SLEEP -static int st1232_ts_suspend(struct device *dev) +static int __maybe_unused st1232_ts_suspend(struct device *dev) { struct i2c_client *client = to_i2c_client(dev); struct st1232_ts_data *ts = i2c_get_clientdata(client); @@ -259,7 +258,7 @@ static int st1232_ts_suspend(struct device *dev) return 0; } -static int st1232_ts_resume(struct device *dev) +static int __maybe_unused st1232_ts_resume(struct device *dev) { struct i2c_client *client = to_i2c_client(dev); struct st1232_ts_data *ts = i2c_get_clientdata(client); @@ -274,8 +273,6 @@ static int st1232_ts_resume(struct device *dev) return 0; } -#endif - static SIMPLE_DEV_PM_OPS(st1232_ts_pm_ops, st1232_ts_suspend, st1232_ts_resume); diff --git a/drivers/input/touchscreen/tsc2005.c b/drivers/input/touchscreen/tsc2005.c index 52380b68ebdf..72657c579430 100644 --- a/drivers/input/touchscreen/tsc2005.c +++ b/drivers/input/touchscreen/tsc2005.c @@ -773,8 +773,7 @@ static int tsc2005_remove(struct spi_device *spi) return 0; } -#ifdef CONFIG_PM_SLEEP -static int tsc2005_suspend(struct device *dev) +static int __maybe_unused tsc2005_suspend(struct device *dev) { struct spi_device *spi = to_spi_device(dev); struct tsc2005 *ts = spi_get_drvdata(spi); @@ -791,7 +790,7 @@ static int tsc2005_suspend(struct device *dev) return 0; } -static int tsc2005_resume(struct device *dev) +static int __maybe_unused tsc2005_resume(struct device *dev) { struct spi_device *spi = to_spi_device(dev); struct tsc2005 *ts = spi_get_drvdata(spi); @@ -807,7 +806,6 @@ static int tsc2005_resume(struct device *dev) return 0; } -#endif static SIMPLE_DEV_PM_OPS(tsc2005_pm_ops, tsc2005_suspend, tsc2005_resume); diff --git a/drivers/input/touchscreen/ucb1400_ts.c b/drivers/input/touchscreen/ucb1400_ts.c index 0eca00da584b..c1e23cfc6155 100644 --- a/drivers/input/touchscreen/ucb1400_ts.c +++ b/drivers/input/touchscreen/ucb1400_ts.c @@ -406,8 +406,7 @@ static int ucb1400_ts_remove(struct platform_device *pdev) return 0; } -#ifdef CONFIG_PM_SLEEP -static int ucb1400_ts_suspend(struct device *dev) +static int __maybe_unused ucb1400_ts_suspend(struct device *dev) { struct ucb1400_ts *ucb = dev_get_platdata(dev); struct input_dev *idev = ucb->ts_idev; @@ -421,7 +420,7 @@ static int ucb1400_ts_suspend(struct device *dev) return 0; } -static int ucb1400_ts_resume(struct device *dev) +static int __maybe_unused ucb1400_ts_resume(struct device *dev) { struct ucb1400_ts *ucb = dev_get_platdata(dev); struct input_dev *idev = ucb->ts_idev; @@ -434,7 +433,6 @@ static int ucb1400_ts_resume(struct device *dev) mutex_unlock(&idev->mutex); return 0; } -#endif static SIMPLE_DEV_PM_OPS(ucb1400_ts_pm_ops, ucb1400_ts_suspend, ucb1400_ts_resume); diff --git a/drivers/input/touchscreen/wacom_i2c.c b/drivers/input/touchscreen/wacom_i2c.c index 7ccaa1b12b05..32f8ac003936 100644 --- a/drivers/input/touchscreen/wacom_i2c.c +++ b/drivers/input/touchscreen/wacom_i2c.c @@ -242,8 +242,7 @@ static int wacom_i2c_remove(struct i2c_client *client) return 0; } -#ifdef CONFIG_PM_SLEEP -static int wacom_i2c_suspend(struct device *dev) +static int __maybe_unused wacom_i2c_suspend(struct device *dev) { struct i2c_client *client = to_i2c_client(dev); @@ -252,7 +251,7 @@ static int wacom_i2c_suspend(struct device *dev) return 0; } -static int wacom_i2c_resume(struct device *dev) +static int __maybe_unused wacom_i2c_resume(struct device *dev) { struct i2c_client *client = to_i2c_client(dev); @@ -260,7 +259,6 @@ static int wacom_i2c_resume(struct device *dev) return 0; } -#endif static SIMPLE_DEV_PM_OPS(wacom_i2c_pm, wacom_i2c_suspend, wacom_i2c_resume); diff --git a/drivers/input/touchscreen/zforce_ts.c b/drivers/input/touchscreen/zforce_ts.c index 8ba48f5eff7b..19880c7385e3 100644 --- a/drivers/input/touchscreen/zforce_ts.c +++ b/drivers/input/touchscreen/zforce_ts.c @@ -602,8 +602,7 @@ static void zforce_input_close(struct input_dev *dev) return; } -#ifdef CONFIG_PM_SLEEP -static int zforce_suspend(struct device *dev) +static int __maybe_unused zforce_suspend(struct device *dev) { struct i2c_client *client = to_i2c_client(dev); struct zforce_ts *ts = i2c_get_clientdata(client); @@ -648,7 +647,7 @@ unlock: return ret; } -static int zforce_resume(struct device *dev) +static int __maybe_unused zforce_resume(struct device *dev) { struct i2c_client *client = to_i2c_client(dev); struct zforce_ts *ts = i2c_get_clientdata(client); @@ -685,7 +684,6 @@ unlock: return ret; } -#endif static SIMPLE_DEV_PM_OPS(zforce_pm_ops, zforce_suspend, zforce_resume); |