From 6145f7f6717524be587bcc8ccd7490ed498cd2c8 Mon Sep 17 00:00:00 2001 From: Shantanu Jain Date: Thu, 14 May 2015 15:08:58 +0530 Subject: 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 --- drivers/input/touchscreen/it7258_ts_i2c.c | 99 +++++++++++++++++++++++++------ 1 file changed, 80 insertions(+), 19 deletions(-) (limited to 'drivers/input') 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; } -- cgit v1.2.3