Merge "input: touchscreen: add touch to wake feature in ITEtech driver"

This commit is contained in:
Linux Build Service Account 2016-08-01 03:01:55 -07:00 committed by Gerrit - the friendly Code Review server
commit 8560f5d5ea
3 changed files with 416 additions and 71 deletions

View file

@ -0,0 +1,39 @@
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
- ite,wakeup : boolean, use this to support touch-to-wake feature.
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>;
ite,wakeup;
};
};

View file

@ -120,6 +120,7 @@ intercontrol Inter Control Group
invensense InvenSense Inc. invensense InvenSense Inc.
isee ISEE 2007 S.L. isee ISEE 2007 S.L.
isil Intersil isil Intersil
ite ITE Tech. Inc.
jedec JEDEC Solid State Technology Association jedec JEDEC Solid State Technology Association
karo Ka-Ro electronics GmbH karo Ka-Ro electronics GmbH
keymile Keymile GmbH keymile Keymile GmbH

View file

@ -22,12 +22,16 @@
#include <linux/firmware.h> #include <linux/firmware.h>
#include <linux/gpio.h> #include <linux/gpio.h>
#include <linux/slab.h> #include <linux/slab.h>
#include <linux/wakelock.h> #include <linux/regulator/consumer.h>
#include <linux/of_gpio.h>
#include <linux/fb.h>
#include <linux/debugfs.h>
#define MAX_BUFFER_SIZE 144 #define MAX_BUFFER_SIZE 144
#define DEVICE_NAME "IT7260" #define DEVICE_NAME "IT7260"
#define SCREEN_X_RESOLUTION 320 #define SCREEN_X_RESOLUTION 320
#define SCREEN_Y_RESOLUTION 320 #define SCREEN_Y_RESOLUTION 320
#define DEBUGFS_DIR_NAME "ts_debug"
/* all commands writes go to this idx */ /* all commands writes go to this idx */
#define BUF_COMMAND 0x20 #define BUF_COMMAND 0x20
@ -98,6 +102,11 @@
/* use this to include integers in commands */ /* use this to include integers in commands */
#define CMD_UINT16(v) ((uint8_t)(v)) , ((uint8_t)((v) >> 8)) #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 { struct FingerData {
uint8_t xLo; uint8_t xLo;
@ -128,24 +137,65 @@ struct PointData {
#define FD_PRESSURE_HIGH 0x08 #define FD_PRESSURE_HIGH 0x08
#define FD_PRESSURE_HEAVY 0x0F #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;
bool wakeup;
};
struct IT7260_ts_data { struct IT7260_ts_data {
struct i2c_client *client; struct i2c_client *client;
struct input_dev *input_dev; struct input_dev *input_dev;
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
struct dentry *dir;
}; };
static int8_t fwUploadResult; static int8_t fwUploadResult;
static int8_t calibrationWasSuccessful; static int8_t calibrationWasSuccessful;
static bool devicePresent; static bool devicePresent;
static DEFINE_MUTEX(sleepModeMutex);
static bool chipAwake;
static bool hadFingerDown; static bool hadFingerDown;
static bool isDeviceSuspend;
static struct input_dev *input_dev; static struct input_dev *input_dev;
static struct IT7260_ts_data *gl_ts; static struct IT7260_ts_data *gl_ts;
#define LOGE(...) pr_err(DEVICE_NAME ": " __VA_ARGS__) #define LOGE(...) pr_err(DEVICE_NAME ": " __VA_ARGS__)
#define LOGI(...) printk(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 = gl_ts->suspended;
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 */ /* internal use func - does not make sure chip is ready before read */
static bool i2cReadNoReadyCheck(uint8_t bufferIndex, uint8_t *dataBuffer, static bool i2cReadNoReadyCheck(uint8_t bufferIndex, uint8_t *dataBuffer,
uint16_t dataLength) uint16_t dataLength)
@ -397,6 +447,21 @@ static bool chipGetVersions(uint8_t *verFw, uint8_t *verCfg, bool logIt)
return ret; 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, static ssize_t sysfsUpgradeStore(struct device *dev,
struct device_attribute *attr, const char *buf, size_t count) struct device_attribute *attr, const char *buf, size_t count)
{ {
@ -569,50 +634,39 @@ static ssize_t sysfsSleepShow(struct device *dev,
* leaking a byte of kernel data (by claiming to return a byte but not * 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 * writing to buf. To fix this now we actually return the sleep status
*/ */
if (!mutex_lock_interruptible(&sleepModeMutex)) { *buf = gl_ts->suspended ? '1' : '0';
*buf = chipAwake ? '1' : '0'; return 1;
mutex_unlock(&sleepModeMutex);
return 1;
} else {
return -EINTR;
}
} }
static ssize_t sysfsSleepStore(struct device *dev, static ssize_t sysfsSleepStore(struct device *dev,
struct device_attribute *attr, const char *buf, size_t count) struct device_attribute *attr, const char *buf, size_t count)
{ {
static const uint8_t cmdGoSleep[] = {CMD_PWR_CTL, int go_to_sleep, ret;
0x00, PWR_CTL_SLEEP_MODE};
int goToSleepVal, ret;
bool goToWake;
uint8_t dummy;
ret = kstrtoint(buf, 10, &goToSleepVal); ret = kstrtoint(buf, 10, &goToSleepVal);
/* convert to bool of proper polarity */
goToWake = !goToSleepVal;
if (!mutex_lock_interruptible(&sleepModeMutex)) { /* (gl_ts->suspended == true && goToSleepVal > 0) means
if ((chipAwake && goToWake) || (!chipAwake && !goToWake)) * device is already suspended and you want it to be in sleep,
LOGE("duplicate request to %s chip\n", * (gl_ts->suspended == false && goToSleepVal == 0) means
goToWake ? "wake" : "sleep"); * device is already active and you also want it to be active.
else if (goToWake) { */
i2cReadNoReadyCheck(BUF_QUERY, &dummy, sizeof(dummy)); if ((gl_ts->suspended && go_to_sleep > 0) ||
enable_irq(gl_ts->client->irq); (!gl_ts->suspended && go_to_sleep == 0))
LOGI("touch is going to wake!\n"); dev_err(dev, "duplicate request to %s chip\n",
} else { go_to_sleep ? "sleep" : "wake");
disable_irq(gl_ts->client->irq); else if (go_to_sleep) {
i2cWriteNoReadyCheck(BUF_COMMAND, cmdGoSleep, disable_irq(gl_ts->client->irq);
sizeof(cmdGoSleep)); IT7260_ts_chipLowPowerMode(true);
LOGI("touch is going to sleep...\n"); dev_dbg(dev, "touch is going to sleep...\n");
}
chipAwake = goToWake;
mutex_unlock(&sleepModeMutex);
return count;
} else { } else {
return -EINTR; IT7260_ts_chipLowPowerMode(false);
enable_irq(gl_ts->client->irq);
dev_dbg(dev, "touch is going to wake!\n");
} }
} gl_ts->suspended = go_to_sleep;
return count;
}
static DEVICE_ATTR(status, S_IRUGO|S_IWUSR|S_IWGRP, static DEVICE_ATTR(status, S_IRUGO|S_IWUSR|S_IWGRP,
sysfsStatusShow, sysfsStatusStore); sysfsStatusShow, sysfsStatusStore);
@ -667,6 +721,13 @@ void sendCalibrationCmd(void)
} }
EXPORT_SYMBOL(sendCalibrationCmd); 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, static void readFingerData(uint16_t *xP, uint16_t *yP, uint8_t *pressureP,
const struct FingerData *fd) const struct FingerData *fd)
{ {
@ -684,60 +745,79 @@ static void readFingerData(uint16_t *xP, uint16_t *yP, uint8_t *pressureP,
*pressureP = fd->pressure & FD_PRESSURE_BITS; *pressureP = fd->pressure & FD_PRESSURE_BITS;
} }
static void readTouchDataPoint(void) static irqreturn_t IT7260_ts_threaded_handler(int irq, void *devid)
{ {
struct PointData pointData; struct PointData pointData;
uint8_t devStatus; uint8_t devStatus;
uint8_t pressure = FD_PRESSURE_NONE; uint8_t pressure = FD_PRESSURE_NONE;
uint16_t x, y; 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 */ /* verify there is point data to read & it is readable and valid */
i2cReadNoReadyCheck(BUF_QUERY, &devStatus, sizeof(devStatus)); i2cReadNoReadyCheck(BUF_QUERY, &devStatus, sizeof(devStatus));
if (!((devStatus & PT_INFO_BITS) & PT_INFO_YES)) { if (!((devStatus & PT_INFO_BITS) & PT_INFO_YES)) {
pr_err("readTouchDataPoint() called when no data available (0x%02X)\n", return IRQ_HANDLED;
devStatus);
return;
} }
if (!i2cReadNoReadyCheck(BUF_POINT_INFO, (void *)&pointData, if (!i2cReadNoReadyCheck(BUF_POINT_INFO, (void *)&pointData,
sizeof(pointData))) { sizeof(pointData))) {
pr_err("readTouchDataPoint() failed to read point data buffer\n"); dev_err(&gl_ts->client->dev,
return; "readTouchDataPoint() failed to read point data buffer\n");
return IRQ_HANDLED;
} }
if ((pointData.flags & PD_FLAGS_DATA_TYPE_BITS) != if ((pointData.flags & PD_FLAGS_DATA_TYPE_BITS) !=
PD_FLAGS_DATA_TYPE_TOUCH) { 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); pointData.flags);
return; return IRQ_HANDLED;
} }
if ((pointData.flags & PD_FLAGS_HAVE_FINGERS) & 1) if ((pointData.flags & PD_FLAGS_HAVE_FINGERS) & 1)
readFingerData(&x, &y, &pressure, pointData.fd); readFingerData(&x, &y, &pressure, pointData.fd);
if (pressure >= FD_PRESSURE_LIGHT) { if (pressure >= FD_PRESSURE_LIGHT) {
if (!hadFingerDown) if (!hadFingerDown)
hadFingerDown = true; hadFingerDown = true;
readFingerData(&x, &y, &pressure, pointData.fd); 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_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); input_sync(gl_ts->input_dev);
} else if (hadFingerDown) { } else if (hadFingerDown) {
hadFingerDown = false; hadFingerDown = false;
input_report_key(gl_ts->input_dev, BTN_TOUCH, 0); input_report_key(gl_ts->input_dev, BTN_TOUCH, 0);
input_mt_sync(gl_ts->input_dev);
input_sync(gl_ts->input_dev); input_sync(gl_ts->input_dev);
} }
return IRQ_HANDLED;
} }
static irqreturn_t IT7260_ts_threaded_handler(int irq, void *devid) static void IT7260_ts_work_func(struct work_struct *work)
{ {
readTouchDataPoint(); pm_relax(&gl_ts->client->dev);
return IRQ_HANDLED;
} }
static bool chipIdentifyIT7260(void) static bool chipIdentifyIT7260(void)
@ -782,9 +862,11 @@ static int IT7260_ts_probe(struct i2c_client *client,
const struct i2c_device_id *id) const struct i2c_device_id *id)
{ {
static const uint8_t cmdStart[] = {CMD_UNKNOWN_7}; static const uint8_t cmdStart[] = {CMD_UNKNOWN_7};
struct IT7260_i2c_platform_data *pdata; struct IT7260_ts_platform_data *pdata;
uint8_t rsp[2]; uint8_t rsp[2];
int ret = -1; int ret = -1;
int rc;
struct dentry *temp;
if (!i2c_check_functionality(client->adapter, I2C_FUNC_I2C)) { if (!i2c_check_functionality(client->adapter, I2C_FUNC_I2C)) {
LOGE("need I2C_FUNC_I2C\n"); LOGE("need I2C_FUNC_I2C\n");
@ -805,13 +887,100 @@ static int IT7260_ts_probe(struct i2c_client *client,
gl_ts->client = client; gl_ts->client = client;
i2c_set_clientdata(client, gl_ts); 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)) { if (sysfs_create_group(&(client->dev.kobj), &it7260_attrstatus_group)) {
dev_err(&client->dev, "failed to register sysfs #1\n"); dev_err(&client->dev, "failed to register sysfs #1\n");
goto err_sysfs_grp_create_1; 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;
}
pdata->wakeup = of_property_read_bool(client->dev.of_node,
"ite,wakeup");
if (!chipIdentifyIT7260()) { if (!chipIdentifyIT7260()) {
LOGI("chipIdentifyIT7260 FAIL"); LOGI("chipIdentifyIT7260 FAIL");
goto err_ident_fail_or_input_alloc; goto err_ident_fail_or_input_alloc;
@ -836,10 +1005,18 @@ static int IT7260_ts_probe(struct i2c_client *client,
set_bit(INPUT_PROP_DIRECT,input_dev->propbit); set_bit(INPUT_PROP_DIRECT,input_dev->propbit);
set_bit(BTN_TOUCH, input_dev->keybit); set_bit(BTN_TOUCH, input_dev->keybit);
set_bit(KEY_SLEEP,input_dev->keybit); set_bit(KEY_SLEEP,input_dev->keybit);
set_bit(KEY_WAKEUP,input_dev->keybit);
set_bit(KEY_POWER,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_MT_POSITION_X, 0,
input_set_abs_params(input_dev, ABS_Y, 0, SCREEN_Y_RESOLUTION, 0, 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 (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)) { if (input_register_device(input_dev)) {
LOGE("failed to register input device\n"); LOGE("failed to register input device\n");
@ -856,6 +1033,15 @@ static int IT7260_ts_probe(struct i2c_client *client,
dev_err(&client->dev, "failed to register sysfs #2\n"); dev_err(&client->dev, "failed to register sysfs #2\n");
goto err_sysfs_grp_create_2; 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; devicePresent = true;
@ -864,8 +1050,36 @@ static int IT7260_ts_probe(struct i2c_client *client,
i2cReadNoReadyCheck(BUF_RESPONSE, rsp, sizeof(rsp)); i2cReadNoReadyCheck(BUF_RESPONSE, rsp, sizeof(rsp));
mdelay(10); 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; 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: err_sysfs_grp_create_2:
free_irq(client->irq, gl_ts); free_irq(client->irq, gl_ts);
@ -874,6 +1088,10 @@ err_irq_reg:
input_dev = NULL; input_dev = NULL;
err_input_register: err_input_register:
if (pdata->wakeup) {
cancel_work_sync(&gl_ts->work_pm_relax);
device_init_wakeup(&client->dev, false);
}
if (input_dev) if (input_dev)
input_free_device(input_dev); input_free_device(input_dev);
@ -889,10 +1107,108 @@ err_out:
static int IT7260_ts_remove(struct i2c_client *client) 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);
if (gl_ts->pdata->wakeup) {
cancel_work_sync(&gl_ts->work_pm_relax);
device_init_wakeup(&client->dev, false);
}
devicePresent = false; devicePresent = false;
return 0; 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->client->dev));
else if (*blank == FB_BLANK_POWERDOWN ||
*blank == FB_BLANK_VSYNC_SUSPEND)
IT7260_ts_suspend(&(gl_ts->client->dev));
}
}
return 0;
}
#endif
#ifdef CONFIG_PM
static int IT7260_ts_resume(struct device *dev)
{
if (!gl_ts->suspended) {
dev_info(dev, "Already in resume state\n");
return 0;
}
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);
gl_ts->suspended = false;
return 0;
}
static int IT7260_ts_suspend(struct device *dev)
{
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 low power mode */
IT7260_ts_chipLowPowerMode(true);
IT7260_ts_release_all();
gl_ts->suspended = 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[] = { static const struct i2c_device_id IT7260_ts_id[] = {
{ DEVICE_NAME, 0}, { DEVICE_NAME, 0},
{} {}
@ -901,33 +1217,22 @@ static const struct i2c_device_id IT7260_ts_id[] = {
MODULE_DEVICE_TABLE(i2c, IT7260_ts_id); MODULE_DEVICE_TABLE(i2c, IT7260_ts_id);
static const struct of_device_id IT7260_match_table[] = { static const struct of_device_id IT7260_match_table[] = {
{ .compatible = "ITE,IT7260_ts",}, { .compatible = "ite,it7260_ts",},
{}, {},
}; };
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 = { static struct i2c_driver IT7260_ts_driver = {
.driver = { .driver = {
.owner = THIS_MODULE, .owner = THIS_MODULE,
.name = DEVICE_NAME, .name = DEVICE_NAME,
.of_match_table = IT7260_match_table, .of_match_table = IT7260_match_table,
#ifdef CONFIG_PM
.pm = &IT7260_ts_dev_pm_ops,
#endif
}, },
.probe = IT7260_ts_probe, .probe = IT7260_ts_probe,
.remove = IT7260_ts_remove, .remove = IT7260_ts_remove,
.id_table = IT7260_ts_id, .id_table = IT7260_ts_id,
.resume = IT7260_ts_resume,
.suspend = IT7260_ts_suspend,
}; };
module_i2c_driver(IT7260_ts_driver); module_i2c_driver(IT7260_ts_driver);