From 70dede533eb3d6864d10df6308c0a0df41e78d76 Mon Sep 17 00:00:00 2001 From: Abinaya P Date: Thu, 7 May 2015 16:12:24 +0530 Subject: [PATCH 1/5] input: it7258_ts_i2c: add DT parsing support for touch driver Add device tree support for ITE Tech touch controller driver. Change-Id: I2671b7c30b0f2ed1d7ad788f29b7f900a2a1cdcc Signed-off-by: Shantanu Jain Signed-off-by: Abinaya P --- .../input/touchscreen/it7258_ts_i2c.txt | 37 ++++++ .../devicetree/bindings/vendor-prefixes.txt | 1 + drivers/input/touchscreen/it7258_ts_i2c.c | 110 +++++++++++++++++- 3 files changed, 144 insertions(+), 4 deletions(-) create mode 100644 Documentation/devicetree/bindings/input/touchscreen/it7258_ts_i2c.txt diff --git a/Documentation/devicetree/bindings/input/touchscreen/it7258_ts_i2c.txt b/Documentation/devicetree/bindings/input/touchscreen/it7258_ts_i2c.txt new file mode 100644 index 000000000000..0c41e9e31d9b --- /dev/null +++ b/Documentation/devicetree/bindings/input/touchscreen/it7258_ts_i2c.txt @@ -0,0 +1,37 @@ +ITE Tech. touch controller + +The ITE Tech. touch controller is connected to host processor +via i2c. The controller generates interrupts when the user +touches the panel. The host controller is expected to read +the touch coordinates over i2c and pass the coordinates to +the rest of the system. + +Required properties: + + - compatible : should be "ite,it7260_ts" + - reg : i2c slave address of the device + - interrupt-parent : parent of interrupt + - interrupts : touch sample interrupt to indicate presence or release + of fingers on the panel. + - ite,irq-gpio : irq gpio which is to provide interrupts to host, + same as "interrupts" node. It will also + contain active low or active high information. + - ite,reset-gpio : reset gpio to control the reset of chip + +Optional properties: + - avdd-supply : Analog power supply needed to power device + - vdd-supply : Power source required to pull up i2c bus + +Example: + i2c@f9927000 { + it7260@46 { + compatible = "ite,it7260_ts"; + reg = <0x46>; + interrupt-parent = <&msmgpio>; + interrupts = <17 0x2>; + avdd-supply = <&pm8226_l19>; + vdd-supply = <&pm8226_lvs1>; + ite,reset-gpio = <&msmgpio 16 0x00>; + ite,irq-gpio = <&msmgpio 17 0x2008>; + }; + }; diff --git a/Documentation/devicetree/bindings/vendor-prefixes.txt b/Documentation/devicetree/bindings/vendor-prefixes.txt index 8570e6a6bfa4..4097b7cd6454 100644 --- a/Documentation/devicetree/bindings/vendor-prefixes.txt +++ b/Documentation/devicetree/bindings/vendor-prefixes.txt @@ -120,6 +120,7 @@ intercontrol Inter Control Group invensense InvenSense Inc. isee ISEE 2007 S.L. isil Intersil +ite ITE Tech. Inc. jedec JEDEC Solid State Technology Association karo Ka-Ro electronics GmbH keymile Keymile GmbH diff --git a/drivers/input/touchscreen/it7258_ts_i2c.c b/drivers/input/touchscreen/it7258_ts_i2c.c index 773ece9eb1d4..edd0b049f097 100644 --- a/drivers/input/touchscreen/it7258_ts_i2c.c +++ b/drivers/input/touchscreen/it7258_ts_i2c.c @@ -22,7 +22,8 @@ #include #include #include -#include +#include +#include #define MAX_BUFFER_SIZE 144 #define DEVICE_NAME "IT7260" @@ -128,9 +129,25 @@ struct PointData { #define FD_PRESSURE_HIGH 0x08 #define FD_PRESSURE_HEAVY 0x0F +#define IT_VTG_MIN_UV 1800000 +#define IT_VTG_MAX_UV 1800000 +#define IT_I2C_VTG_MIN_UV 2600000 +#define IT_I2C_VTG_MAX_UV 3300000 + +struct IT7260_ts_platform_data { + u32 irqflags; + u32 irq_gpio; + u32 irq_gpio_flags; + u32 reset_gpio; + u32 reset_gpio_flags; +}; + struct IT7260_ts_data { struct i2c_client *client; struct input_dev *input_dev; + const struct IT7260_ts_platform_data *pdata; + struct regulator *vdd; + struct regulator *avdd; }; static int8_t fwUploadResult; @@ -782,9 +799,10 @@ static int IT7260_ts_probe(struct i2c_client *client, const struct i2c_device_id *id) { static const uint8_t cmdStart[] = {CMD_UNKNOWN_7}; - struct IT7260_i2c_platform_data *pdata; + struct IT7260_ts_platform_data *pdata; uint8_t rsp[2]; int ret = -1; + int rc; if (!i2c_check_functionality(client->adapter, I2C_FUNC_I2C)) { LOGE("need I2C_FUNC_I2C\n"); @@ -805,13 +823,97 @@ static int IT7260_ts_probe(struct i2c_client *client, gl_ts->client = client; i2c_set_clientdata(client, gl_ts); - pdata = client->dev.platform_data; + if (client->dev.platform_data == NULL) + return -ENODEV; + + if (client->dev.of_node) { + pdata = devm_kzalloc(&client->dev, sizeof(*pdata), GFP_KERNEL); + if (!pdata) + return -ENOMEM; + } else + pdata = client->dev.platform_data; + + if (!pdata) + return -ENOMEM; + + gl_ts->pdata = pdata; if (sysfs_create_group(&(client->dev.kobj), &it7260_attrstatus_group)) { dev_err(&client->dev, "failed to register sysfs #1\n"); goto err_sysfs_grp_create_1; } + gl_ts->vdd = regulator_get(&gl_ts->client->dev, "vdd"); + if (IS_ERR(gl_ts->vdd)) { + dev_err(&gl_ts->client->dev, + "Regulator get failed vdd\n"); + gl_ts->vdd = NULL; + } else { + rc = regulator_set_voltage(gl_ts->vdd, + IT_VTG_MIN_UV, IT_VTG_MAX_UV); + if (rc) + dev_err(&gl_ts->client->dev, + "Regulator set_vtg failed vdd\n"); + } + + gl_ts->avdd = regulator_get(&gl_ts->client->dev, "avdd"); + if (IS_ERR(gl_ts->avdd)) { + dev_err(&gl_ts->client->dev, + "Regulator get failed avdd\n"); + gl_ts->avdd = NULL; + } else { + rc = regulator_set_voltage(gl_ts->avdd, IT_I2C_VTG_MIN_UV, + IT_I2C_VTG_MAX_UV); + if (rc) + dev_err(&gl_ts->client->dev, + "Regulator get failed avdd\n"); + } + + if (gl_ts->vdd) { + rc = regulator_enable(gl_ts->vdd); + if (rc) { + dev_err(&gl_ts->client->dev, + "Regulator vdd enable failed rc=%d\n", rc); + return rc; + } + } + + if (gl_ts->avdd) { + rc = regulator_enable(gl_ts->avdd); + if (rc) { + dev_err(&gl_ts->client->dev, + "Regulator avdd enable failed rc=%d\n", rc); + return rc; + } + } + + /* reset gpio info */ + pdata->reset_gpio = of_get_named_gpio_flags(client->dev.of_node, + "ite,reset-gpio", 0, + &pdata->reset_gpio_flags); + if (gpio_is_valid(pdata->reset_gpio)) { + if (gpio_request(pdata->reset_gpio, "ite_reset_gpio")) + dev_err(&gl_ts->client->dev, + "gpio_request failed for reset GPIO\n"); + if (gpio_direction_output(pdata->reset_gpio, 0)) + dev_err(&gl_ts->client->dev, + "gpio_direction_output for reset GPIO\n"); + dev_dbg(&gl_ts->client->dev, "Reset GPIO %d\n", + pdata->reset_gpio); + } else { + return pdata->reset_gpio; + } + + /* irq gpio info */ + pdata->irq_gpio = of_get_named_gpio_flags(client->dev.of_node, + "ite,irq-gpio", 0, &pdata->irq_gpio_flags); + if (gpio_is_valid(pdata->irq_gpio)) { + dev_dbg(&gl_ts->client->dev, "IRQ GPIO %d, IRQ # %d\n", + pdata->irq_gpio, gpio_to_irq(pdata->irq_gpio)); + } else { + return pdata->irq_gpio; + } + if (!chipIdentifyIT7260()) { LOGI("chipIdentifyIT7260 FAIL"); goto err_ident_fail_or_input_alloc; @@ -901,7 +1003,7 @@ static const struct i2c_device_id IT7260_ts_id[] = { MODULE_DEVICE_TABLE(i2c, IT7260_ts_id); static const struct of_device_id IT7260_match_table[] = { - { .compatible = "ITE,IT7260_ts",}, + { .compatible = "ite,it7260_ts",}, {}, }; From a444cab639abe96fbaf42f495d72c40a3816a799 Mon Sep 17 00:00:00 2001 From: Shantanu Jain Date: Thu, 14 May 2015 14:17:38 +0530 Subject: [PATCH 2/5] input: touchscreen: add suspend-resume and fb support Add suspend-resume and fb notification support for ITE tech touchscreen driver. Change-Id: I9a601412fb9a3935c0eadad5157bb1fd819b29dd Signed-off-by: Shantanu Jain --- drivers/input/touchscreen/it7258_ts_i2c.c | 183 ++++++++++++++++------ 1 file changed, 136 insertions(+), 47 deletions(-) diff --git a/drivers/input/touchscreen/it7258_ts_i2c.c b/drivers/input/touchscreen/it7258_ts_i2c.c index edd0b049f097..4cf915787ff8 100644 --- a/drivers/input/touchscreen/it7258_ts_i2c.c +++ b/drivers/input/touchscreen/it7258_ts_i2c.c @@ -24,6 +24,7 @@ #include #include #include +#include #define MAX_BUFFER_SIZE 144 #define DEVICE_NAME "IT7260" @@ -99,6 +100,11 @@ /* use this to include integers in commands */ #define CMD_UINT16(v) ((uint8_t)(v)) , ((uint8_t)((v) >> 8)) +/* Function declarations */ +static int fb_notifier_callback(struct notifier_block *self, + unsigned long event, void *data); +static int IT7260_ts_resume(struct device *dev); +static int IT7260_ts_suspend(struct device *dev); struct FingerData { uint8_t xLo; @@ -148,13 +154,14 @@ struct IT7260_ts_data { const struct IT7260_ts_platform_data *pdata; struct regulator *vdd; struct regulator *avdd; +#ifdef CONFIG_FB + struct notifier_block fb_notif; +#endif }; static int8_t fwUploadResult; static int8_t calibrationWasSuccessful; static bool devicePresent; -static DEFINE_MUTEX(sleepModeMutex); -static bool chipAwake; static bool hadFingerDown; static bool isDeviceSuspend; static struct input_dev *input_dev; @@ -414,6 +421,21 @@ static bool chipGetVersions(uint8_t *verFw, uint8_t *verCfg, bool logIt) return ret; } +static int IT7260_ts_chipLowPowerMode(bool low) +{ + static const uint8_t cmdGoSleep[] = {CMD_PWR_CTL, + 0x00, PWR_CTL_SLEEP_MODE}; + uint8_t dummy; + + if (low) + i2cWriteNoReadyCheck(BUF_COMMAND, cmdGoSleep, + sizeof(cmdGoSleep)); + else + i2cReadNoReadyCheck(BUF_QUERY, &dummy, sizeof(dummy)); + + return 0; +} + static ssize_t sysfsUpgradeStore(struct device *dev, struct device_attribute *attr, const char *buf, size_t count) { @@ -586,48 +608,33 @@ static ssize_t sysfsSleepShow(struct device *dev, * leaking a byte of kernel data (by claiming to return a byte but not * writing to buf. To fix this now we actually return the sleep status */ - if (!mutex_lock_interruptible(&sleepModeMutex)) { - *buf = chipAwake ? '1' : '0'; - mutex_unlock(&sleepModeMutex); - return 1; - } else { - return -EINTR; - } + *buf = isDeviceSuspend ? '1' : '0'; + return 1; } static ssize_t sysfsSleepStore(struct device *dev, struct device_attribute *attr, const char *buf, size_t count) { - static const uint8_t cmdGoSleep[] = {CMD_PWR_CTL, - 0x00, PWR_CTL_SLEEP_MODE}; int goToSleepVal, ret; - bool goToWake; - uint8_t dummy; ret = kstrtoint(buf, 10, &goToSleepVal); - /* convert to bool of proper polarity */ - goToWake = !goToSleepVal; - if (!mutex_lock_interruptible(&sleepModeMutex)) { - if ((chipAwake && goToWake) || (!chipAwake && !goToWake)) - LOGE("duplicate request to %s chip\n", - goToWake ? "wake" : "sleep"); - else if (goToWake) { - i2cReadNoReadyCheck(BUF_QUERY, &dummy, sizeof(dummy)); - enable_irq(gl_ts->client->irq); - LOGI("touch is going to wake!\n"); - } else { - disable_irq(gl_ts->client->irq); - i2cWriteNoReadyCheck(BUF_COMMAND, cmdGoSleep, - sizeof(cmdGoSleep)); - LOGI("touch is going to sleep...\n"); - } - chipAwake = goToWake; - mutex_unlock(&sleepModeMutex); - return count; + if ((isDeviceSuspend && goToSleepVal > 0) || (!isDeviceSuspend && + goToSleepVal == 0)) + dev_err(dev, "duplicate request to %s chip\n", + goToSleepVal ? "sleep" : "wake"); + else if (goToSleepVal) { + disable_irq(gl_ts->client->irq); + IT7260_ts_chipLowPowerMode(true); + dev_dbg(dev, "touch is going to sleep...\n"); } else { - return -EINTR; + IT7260_ts_chipLowPowerMode(false); + enable_irq(gl_ts->client->irq); + dev_dbg(dev, "touch is going to wake!\n"); } + isDeviceSuspend = goToSleepVal; + + return count; } @@ -684,6 +691,13 @@ void sendCalibrationCmd(void) } EXPORT_SYMBOL(sendCalibrationCmd); +static void IT7260_ts_release_all(void) +{ + input_report_key(gl_ts->input_dev, BTN_TOUCH, 0); + input_mt_sync(gl_ts->input_dev); + input_sync(gl_ts->input_dev); +} + static void readFingerData(uint16_t *xP, uint16_t *yP, uint8_t *pressureP, const struct FingerData *fd) { @@ -942,6 +956,7 @@ static int IT7260_ts_probe(struct i2c_client *client, set_bit(KEY_POWER,input_dev->keybit); input_set_abs_params(input_dev, ABS_X, 0, SCREEN_X_RESOLUTION, 0, 0); input_set_abs_params(input_dev, ABS_Y, 0, SCREEN_Y_RESOLUTION, 0, 0); + input_set_drvdata(gl_ts->input_dev, gl_ts); if (input_register_device(input_dev)) { LOGE("failed to register input device\n"); @@ -958,6 +973,15 @@ static int IT7260_ts_probe(struct i2c_client *client, dev_err(&client->dev, "failed to register sysfs #2\n"); goto err_sysfs_grp_create_2; } + +#if defined(CONFIG_FB) + gl_ts->fb_notif.notifier_call = fb_notifier_callback; + + ret = fb_register_client(&gl_ts->fb_notif); + if (ret) + dev_err(&client->dev, "Unable to register fb_notifier: %d\n", + ret); +#endif devicePresent = true; @@ -991,10 +1015,86 @@ err_out: static int IT7260_ts_remove(struct i2c_client *client) { +#if defined(CONFIG_FB) + if (fb_unregister_client(&gl_ts->fb_notif)) + dev_err(&client->dev, "Error occurred while unregistering fb_notifier.\n"); +#endif devicePresent = false; return 0; } +#if defined(CONFIG_FB) +static int fb_notifier_callback(struct notifier_block *self, + unsigned long event, void *data) +{ + struct fb_event *evdata = data; + int *blank; + + if (evdata && evdata->data && gl_ts && gl_ts->client) { + if (event == FB_EVENT_BLANK) { + blank = evdata->data; + if (*blank == FB_BLANK_UNBLANK) + IT7260_ts_resume(&(gl_ts->input_dev->dev)); + else if (*blank == FB_BLANK_POWERDOWN || + *blank == FB_BLANK_VSYNC_SUSPEND) + IT7260_ts_suspend(&(gl_ts->input_dev->dev)); + } + } + + return 0; +} +#endif + +#ifdef CONFIG_PM +static int IT7260_ts_resume(struct device *dev) +{ + if (!isDeviceSuspend) { + dev_info(dev, "Already in resume state\n"); + return 0; + } + + /* put the device in active powr mode */ + IT7260_ts_chipLowPowerMode(false); + + enable_irq(gl_ts->client->irq); + isDeviceSuspend = false; + return 0; +} + +static int IT7260_ts_suspend(struct device *dev) +{ + if (isDeviceSuspend) { + dev_info(dev, "Already in suspend state\n"); + return 0; + } + + disable_irq(gl_ts->client->irq); + + /* put the device in active powr mode */ + IT7260_ts_chipLowPowerMode(true); + + IT7260_ts_release_all(); + isDeviceSuspend = true; + + return 0; +} + +static const struct dev_pm_ops IT7260_ts_dev_pm_ops = { + .suspend = IT7260_ts_suspend, + .resume = IT7260_ts_resume, +}; +#else +static int IT7260_ts_resume(struct device *dev) +{ + return 0; +} + +static int IT7260_ts_suspend(struct device *dev) +{ + return 0; +} +#endif + static const struct i2c_device_id IT7260_ts_id[] = { { DEVICE_NAME, 0}, {} @@ -1007,29 +1107,18 @@ static const struct of_device_id IT7260_match_table[] = { {}, }; -static int IT7260_ts_resume(struct i2c_client *i2cdev) -{ - isDeviceSuspend = false; - return 0; -} - -static int IT7260_ts_suspend(struct i2c_client *i2cdev, pm_message_t pmesg) -{ - isDeviceSuspend = true; - return 0; -} - static struct i2c_driver IT7260_ts_driver = { .driver = { .owner = THIS_MODULE, .name = DEVICE_NAME, .of_match_table = IT7260_match_table, +#ifdef CONFIG_PM + .pm = &IT7260_ts_dev_pm_ops, +#endif }, .probe = IT7260_ts_probe, .remove = IT7260_ts_remove, .id_table = IT7260_ts_id, - .resume = IT7260_ts_resume, - .suspend = IT7260_ts_suspend, }; module_i2c_driver(IT7260_ts_driver); From 59a666fb58df2df86746e8e920e8789cbf899ec3 Mon Sep 17 00:00:00 2001 From: Himanshu Aggarwal Date: Tue, 12 May 2015 18:57:58 +0530 Subject: [PATCH 3/5] input: touchscreen: add protocol A support to ITE tech driver Add multi-touch protocol A support to ITE tech touchscreen driver. Change-Id: I49bc7e3eaa3b266fe92bb209f56ae5a1b40675e0 Signed-off-by: Himanshu Aggarwal Signed-off-by: Shantanu Jain --- drivers/input/touchscreen/it7258_ts_i2c.c | 35 +++++++++++------------ 1 file changed, 17 insertions(+), 18 deletions(-) diff --git a/drivers/input/touchscreen/it7258_ts_i2c.c b/drivers/input/touchscreen/it7258_ts_i2c.c index 4cf915787ff8..6c1e3a7a4b4d 100644 --- a/drivers/input/touchscreen/it7258_ts_i2c.c +++ b/drivers/input/touchscreen/it7258_ts_i2c.c @@ -715,7 +715,7 @@ static void readFingerData(uint16_t *xP, uint16_t *yP, uint8_t *pressureP, *pressureP = fd->pressure & FD_PRESSURE_BITS; } -static void readTouchDataPoint(void) +static irqreturn_t IT7260_ts_threaded_handler(int irq, void *devid) { struct PointData pointData; uint8_t devStatus; @@ -725,49 +725,46 @@ static void readTouchDataPoint(void) /* verify there is point data to read & it is readable and valid */ i2cReadNoReadyCheck(BUF_QUERY, &devStatus, sizeof(devStatus)); if (!((devStatus & PT_INFO_BITS) & PT_INFO_YES)) { - pr_err("readTouchDataPoint() called when no data available (0x%02X)\n", - devStatus); - return; + return IRQ_HANDLED; } if (!i2cReadNoReadyCheck(BUF_POINT_INFO, (void *)&pointData, sizeof(pointData))) { - pr_err("readTouchDataPoint() failed to read point data buffer\n"); - return; + dev_err(&gl_ts->client->dev, + "readTouchDataPoint() failed to read point data buffer\n"); + return IRQ_HANDLED; } if ((pointData.flags & PD_FLAGS_DATA_TYPE_BITS) != PD_FLAGS_DATA_TYPE_TOUCH) { - pr_err("readTouchDataPoint() dropping non-point data of type 0x%02X\n", + dev_err(&gl_ts->client->dev, + "readTouchDataPoint() dropping non-point data of type 0x%02X\n", pointData.flags); - return; + return IRQ_HANDLED; } if ((pointData.flags & PD_FLAGS_HAVE_FINGERS) & 1) readFingerData(&x, &y, &pressure, pointData.fd); if (pressure >= FD_PRESSURE_LIGHT) { - if (!hadFingerDown) hadFingerDown = true; readFingerData(&x, &y, &pressure, pointData.fd); - input_report_abs(gl_ts->input_dev, ABS_X, x); - input_report_abs(gl_ts->input_dev, ABS_Y, y); input_report_key(gl_ts->input_dev, BTN_TOUCH, 1); + input_report_abs(gl_ts->input_dev, ABS_MT_POSITION_X, x); + input_report_abs(gl_ts->input_dev, ABS_MT_POSITION_Y, y); + input_mt_sync(gl_ts->input_dev); input_sync(gl_ts->input_dev); + } else if (hadFingerDown) { hadFingerDown = false; input_report_key(gl_ts->input_dev, BTN_TOUCH, 0); + input_mt_sync(gl_ts->input_dev); input_sync(gl_ts->input_dev); } -} - -static irqreturn_t IT7260_ts_threaded_handler(int irq, void *devid) -{ - readTouchDataPoint(); return IRQ_HANDLED; } @@ -954,8 +951,10 @@ static int IT7260_ts_probe(struct i2c_client *client, set_bit(KEY_SLEEP,input_dev->keybit); set_bit(KEY_WAKEUP,input_dev->keybit); set_bit(KEY_POWER,input_dev->keybit); - input_set_abs_params(input_dev, ABS_X, 0, SCREEN_X_RESOLUTION, 0, 0); - input_set_abs_params(input_dev, ABS_Y, 0, SCREEN_Y_RESOLUTION, 0, 0); + input_set_abs_params(input_dev, ABS_MT_POSITION_X, 0, + SCREEN_X_RESOLUTION, 0, 0); + input_set_abs_params(input_dev, ABS_MT_POSITION_Y, 0, + SCREEN_Y_RESOLUTION, 0, 0); input_set_drvdata(gl_ts->input_dev, gl_ts); if (input_register_device(input_dev)) { From 8b724a99b7bc7128556e51205761433df602bd83 Mon Sep 17 00:00:00 2001 From: Shantanu Jain Date: Fri, 15 May 2015 18:54:18 +0530 Subject: [PATCH 4/5] input: it7258_ts_i2c: add debugfs support for suspend/resume Add debugfs support for suspend and resume test for ITE tech touchscreen driver. Change-Id: I5a3d55c7c8e4b8f594fd7924c61ac1e5b5ad1965 Signed-off-by: Shantanu Jain --- drivers/input/touchscreen/it7258_ts_i2c.c | 54 +++++++++++++++++++++++ 1 file changed, 54 insertions(+) diff --git a/drivers/input/touchscreen/it7258_ts_i2c.c b/drivers/input/touchscreen/it7258_ts_i2c.c index 6c1e3a7a4b4d..c2b77472446b 100644 --- a/drivers/input/touchscreen/it7258_ts_i2c.c +++ b/drivers/input/touchscreen/it7258_ts_i2c.c @@ -25,11 +25,13 @@ #include #include #include +#include #define MAX_BUFFER_SIZE 144 #define DEVICE_NAME "IT7260" #define SCREEN_X_RESOLUTION 320 #define SCREEN_Y_RESOLUTION 320 +#define DEBUGFS_DIR_NAME "ts_debug" /* all commands writes go to this idx */ #define BUF_COMMAND 0x20 @@ -157,6 +159,7 @@ struct IT7260_ts_data { #ifdef CONFIG_FB struct notifier_block fb_notif; #endif + struct dentry *dir; }; static int8_t fwUploadResult; @@ -170,6 +173,26 @@ static struct IT7260_ts_data *gl_ts; #define LOGE(...) pr_err(DEVICE_NAME ": " __VA_ARGS__) #define LOGI(...) printk(DEVICE_NAME ": " __VA_ARGS__) +static int IT7260_debug_suspend_set(void *_data, u64 val) +{ + if (val) + IT7260_ts_suspend(&gl_ts->client->dev); + else + IT7260_ts_resume(&gl_ts->client->dev); + + return 0; +} + +static int IT7260_debug_suspend_get(void *_data, u64 *val) +{ + *val = isDeviceSuspend; + + return 0; +} + +DEFINE_SIMPLE_ATTRIBUTE(debug_suspend_fops, IT7260_debug_suspend_get, + IT7260_debug_suspend_set, "%lld\n"); + /* internal use func - does not make sure chip is ready before read */ static bool i2cReadNoReadyCheck(uint8_t bufferIndex, uint8_t *dataBuffer, uint16_t dataLength) @@ -814,6 +837,7 @@ static int IT7260_ts_probe(struct i2c_client *client, uint8_t rsp[2]; int ret = -1; int rc; + struct dentry *temp; if (!i2c_check_functionality(client->adapter, I2C_FUNC_I2C)) { LOGE("need I2C_FUNC_I2C\n"); @@ -989,8 +1013,36 @@ static int IT7260_ts_probe(struct i2c_client *client, i2cReadNoReadyCheck(BUF_RESPONSE, rsp, sizeof(rsp)); mdelay(10); + gl_ts->dir = debugfs_create_dir(DEBUGFS_DIR_NAME, NULL); + if (gl_ts->dir == NULL || IS_ERR(gl_ts->dir)) { + dev_err(&client->dev, + "%s: Failed to create debugfs directory, rc = %ld\n", + __func__, PTR_ERR(gl_ts->dir)); + ret = PTR_ERR(gl_ts->dir); + goto err_create_debugfs_dir; + } + + temp = debugfs_create_file("suspend", S_IRUSR | S_IWUSR, gl_ts->dir, + gl_ts, &debug_suspend_fops); + if (temp == NULL || IS_ERR(temp)) { + dev_err(&client->dev, + "%s: Failed to create suspend debugfs file, rc = %ld\n", + __func__, PTR_ERR(temp)); + ret = PTR_ERR(temp); + goto err_create_debugfs_file; + } + return 0; +err_create_debugfs_file: + debugfs_remove_recursive(gl_ts->dir); +err_create_debugfs_dir: +#if defined(CONFIG_FB) + if (fb_unregister_client(&gl_ts->fb_notif)) + dev_err(&client->dev, "Error occurred while unregistering fb_notifier.\n"); +#endif + sysfs_remove_group(&(client->dev.kobj), &it7260_attr_group); + err_sysfs_grp_create_2: free_irq(client->irq, gl_ts); @@ -1014,10 +1066,12 @@ err_out: static int IT7260_ts_remove(struct i2c_client *client) { + debugfs_remove_recursive(gl_ts->dir); #if defined(CONFIG_FB) if (fb_unregister_client(&gl_ts->fb_notif)) dev_err(&client->dev, "Error occurred while unregistering fb_notifier.\n"); #endif + sysfs_remove_group(&(client->dev.kobj), &it7260_attr_group); devicePresent = false; return 0; } From 6145f7f6717524be587bcc8ccd7490ed498cd2c8 Mon Sep 17 00:00:00 2001 From: Shantanu Jain Date: Thu, 14 May 2015 15:08:58 +0530 Subject: [PATCH 5/5] input: touchscreen: add touch to wake feature in ITEtech driver Add touch to wake feature in ITE tech touchscreen driver. The touchscreen interrupt is configured as wakeable interrupt, so TS can be used to wake the device from suspend state. Change-Id: I8da53ab4f03237b8652cd5891eadbffa752d72d3 Signed-off-by: Shantanu Jain --- .../input/touchscreen/it7258_ts_i2c.txt | 2 + drivers/input/touchscreen/it7258_ts_i2c.c | 99 +++++++++++++++---- 2 files changed, 82 insertions(+), 19 deletions(-) diff --git a/Documentation/devicetree/bindings/input/touchscreen/it7258_ts_i2c.txt b/Documentation/devicetree/bindings/input/touchscreen/it7258_ts_i2c.txt index 0c41e9e31d9b..ac59a0950f8a 100644 --- a/Documentation/devicetree/bindings/input/touchscreen/it7258_ts_i2c.txt +++ b/Documentation/devicetree/bindings/input/touchscreen/it7258_ts_i2c.txt @@ -21,6 +21,7 @@ Required properties: Optional properties: - avdd-supply : Analog power supply needed to power device - vdd-supply : Power source required to pull up i2c bus + - ite,wakeup : boolean, use this to support touch-to-wake feature. Example: i2c@f9927000 { @@ -33,5 +34,6 @@ Example: vdd-supply = <&pm8226_lvs1>; ite,reset-gpio = <&msmgpio 16 0x00>; ite,irq-gpio = <&msmgpio 17 0x2008>; + ite,wakeup; }; }; diff --git a/drivers/input/touchscreen/it7258_ts_i2c.c b/drivers/input/touchscreen/it7258_ts_i2c.c index c2b77472446b..61028e1b1d88 100644 --- a/drivers/input/touchscreen/it7258_ts_i2c.c +++ b/drivers/input/touchscreen/it7258_ts_i2c.c @@ -148,6 +148,7 @@ struct IT7260_ts_platform_data { u32 irq_gpio_flags; u32 reset_gpio; u32 reset_gpio_flags; + bool wakeup; }; struct IT7260_ts_data { @@ -156,6 +157,9 @@ struct IT7260_ts_data { const struct IT7260_ts_platform_data *pdata; struct regulator *vdd; struct regulator *avdd; + bool device_needs_wakeup; + bool suspended; + struct work_struct work_pm_relax; #ifdef CONFIG_FB struct notifier_block fb_notif; #endif @@ -166,7 +170,6 @@ static int8_t fwUploadResult; static int8_t calibrationWasSuccessful; static bool devicePresent; static bool hadFingerDown; -static bool isDeviceSuspend; static struct input_dev *input_dev; static struct IT7260_ts_data *gl_ts; @@ -185,7 +188,7 @@ static int IT7260_debug_suspend_set(void *_data, u64 val) static int IT7260_debug_suspend_get(void *_data, u64 *val) { - *val = isDeviceSuspend; + *val = gl_ts->suspended; return 0; } @@ -631,22 +634,27 @@ static ssize_t sysfsSleepShow(struct device *dev, * leaking a byte of kernel data (by claiming to return a byte but not * writing to buf. To fix this now we actually return the sleep status */ - *buf = isDeviceSuspend ? '1' : '0'; + *buf = gl_ts->suspended ? '1' : '0'; return 1; } static ssize_t sysfsSleepStore(struct device *dev, struct device_attribute *attr, const char *buf, size_t count) { - int goToSleepVal, ret; + int go_to_sleep, ret; ret = kstrtoint(buf, 10, &goToSleepVal); - if ((isDeviceSuspend && goToSleepVal > 0) || (!isDeviceSuspend && - goToSleepVal == 0)) + /* (gl_ts->suspended == true && goToSleepVal > 0) means + * device is already suspended and you want it to be in sleep, + * (gl_ts->suspended == false && goToSleepVal == 0) means + * device is already active and you also want it to be active. + */ + if ((gl_ts->suspended && go_to_sleep > 0) || + (!gl_ts->suspended && go_to_sleep == 0)) dev_err(dev, "duplicate request to %s chip\n", - goToSleepVal ? "sleep" : "wake"); - else if (goToSleepVal) { + go_to_sleep ? "sleep" : "wake"); + else if (go_to_sleep) { disable_irq(gl_ts->client->irq); IT7260_ts_chipLowPowerMode(true); dev_dbg(dev, "touch is going to sleep...\n"); @@ -655,12 +663,11 @@ static ssize_t sysfsSleepStore(struct device *dev, enable_irq(gl_ts->client->irq); dev_dbg(dev, "touch is going to wake!\n"); } - isDeviceSuspend = goToSleepVal; + gl_ts->suspended = go_to_sleep; return count; } - static DEVICE_ATTR(status, S_IRUGO|S_IWUSR|S_IWGRP, sysfsStatusShow, sysfsStatusStore); static DEVICE_ATTR(version, S_IRUGO|S_IWUSR|S_IWGRP, @@ -745,6 +752,23 @@ static irqreturn_t IT7260_ts_threaded_handler(int irq, void *devid) uint8_t pressure = FD_PRESSURE_NONE; uint16_t x, y; + /* This code adds the touch-to-wake functioanlity to the ITE tech + * driver. When the device is in suspend driver, it sends the + * KEY_WAKEUP event to wake the device. The pm_stay_awake() call + * tells the pm core to stay awake until the CPU cores up already. The + * schedule_work() call schedule a work that tells the pm core to relax + * once the CPU cores are up. + */ + if (gl_ts->device_needs_wakeup) { + pm_stay_awake(&gl_ts->client->dev); + gl_ts->device_needs_wakeup = false; + input_report_key(gl_ts->input_dev, KEY_WAKEUP, 1); + input_sync(gl_ts->input_dev); + input_report_key(gl_ts->input_dev, KEY_WAKEUP, 0); + input_sync(gl_ts->input_dev); + schedule_work(&gl_ts->work_pm_relax); + } + /* verify there is point data to read & it is readable and valid */ i2cReadNoReadyCheck(BUF_QUERY, &devStatus, sizeof(devStatus)); if (!((devStatus & PT_INFO_BITS) & PT_INFO_YES)) { @@ -791,6 +815,11 @@ static irqreturn_t IT7260_ts_threaded_handler(int irq, void *devid) return IRQ_HANDLED; } +static void IT7260_ts_work_func(struct work_struct *work) +{ + pm_relax(&gl_ts->client->dev); +} + static bool chipIdentifyIT7260(void) { static const uint8_t cmdIdent[] = {CMD_IDENT_CHIP}; @@ -949,6 +978,9 @@ static int IT7260_ts_probe(struct i2c_client *client, return pdata->irq_gpio; } + pdata->wakeup = of_property_read_bool(client->dev.of_node, + "ite,wakeup"); + if (!chipIdentifyIT7260()) { LOGI("chipIdentifyIT7260 FAIL"); goto err_ident_fail_or_input_alloc; @@ -973,7 +1005,6 @@ static int IT7260_ts_probe(struct i2c_client *client, set_bit(INPUT_PROP_DIRECT,input_dev->propbit); set_bit(BTN_TOUCH, input_dev->keybit); set_bit(KEY_SLEEP,input_dev->keybit); - set_bit(KEY_WAKEUP,input_dev->keybit); set_bit(KEY_POWER,input_dev->keybit); input_set_abs_params(input_dev, ABS_MT_POSITION_X, 0, SCREEN_X_RESOLUTION, 0, 0); @@ -981,6 +1012,12 @@ static int IT7260_ts_probe(struct i2c_client *client, SCREEN_Y_RESOLUTION, 0, 0); input_set_drvdata(gl_ts->input_dev, gl_ts); + if (pdata->wakeup) { + set_bit(KEY_WAKEUP, gl_ts->input_dev->keybit); + INIT_WORK(&gl_ts->work_pm_relax, IT7260_ts_work_func); + device_init_wakeup(&client->dev, pdata->wakeup); + } + if (input_register_device(input_dev)) { LOGE("failed to register input device\n"); goto err_input_register; @@ -1051,6 +1088,10 @@ err_irq_reg: input_dev = NULL; err_input_register: + if (pdata->wakeup) { + cancel_work_sync(&gl_ts->work_pm_relax); + device_init_wakeup(&client->dev, false); + } if (input_dev) input_free_device(input_dev); @@ -1072,6 +1113,10 @@ static int IT7260_ts_remove(struct i2c_client *client) dev_err(&client->dev, "Error occurred while unregistering fb_notifier.\n"); #endif sysfs_remove_group(&(client->dev.kobj), &it7260_attr_group); + if (gl_ts->pdata->wakeup) { + cancel_work_sync(&gl_ts->work_pm_relax); + device_init_wakeup(&client->dev, false); + } devicePresent = false; return 0; } @@ -1087,10 +1132,10 @@ static int fb_notifier_callback(struct notifier_block *self, if (event == FB_EVENT_BLANK) { blank = evdata->data; if (*blank == FB_BLANK_UNBLANK) - IT7260_ts_resume(&(gl_ts->input_dev->dev)); + IT7260_ts_resume(&(gl_ts->client->dev)); else if (*blank == FB_BLANK_POWERDOWN || *blank == FB_BLANK_VSYNC_SUSPEND) - IT7260_ts_suspend(&(gl_ts->input_dev->dev)); + IT7260_ts_suspend(&(gl_ts->client->dev)); } } @@ -1101,33 +1146,49 @@ static int fb_notifier_callback(struct notifier_block *self, #ifdef CONFIG_PM static int IT7260_ts_resume(struct device *dev) { - if (!isDeviceSuspend) { + if (!gl_ts->suspended) { dev_info(dev, "Already in resume state\n"); return 0; } - /* put the device in active powr mode */ + if (device_may_wakeup(dev)) { + if (gl_ts->device_needs_wakeup) { + gl_ts->device_needs_wakeup = false; + disable_irq_wake(gl_ts->client->irq); + } + return 0; + } + + /* put the device in active power mode */ IT7260_ts_chipLowPowerMode(false); enable_irq(gl_ts->client->irq); - isDeviceSuspend = false; + gl_ts->suspended = false; return 0; } static int IT7260_ts_suspend(struct device *dev) { - if (isDeviceSuspend) { + if (gl_ts->suspended) { dev_info(dev, "Already in suspend state\n"); return 0; } + if (device_may_wakeup(dev)) { + if (!gl_ts->device_needs_wakeup) { + gl_ts->device_needs_wakeup = true; + enable_irq_wake(gl_ts->client->irq); + } + return 0; + } + disable_irq(gl_ts->client->irq); - /* put the device in active powr mode */ + /* put the device in low power mode */ IT7260_ts_chipLowPowerMode(true); IT7260_ts_release_all(); - isDeviceSuspend = true; + gl_ts->suspended = true; return 0; }