aboutsummaryrefslogtreecommitdiff
path: root/drivers/input
diff options
context:
space:
mode:
authorLinus Torvalds <torvalds@linux-foundation.org>2011-03-19 22:27:06 -0700
committerLinus Torvalds <torvalds@linux-foundation.org>2011-03-19 22:27:06 -0700
commita952baa034ae7c2e4a66932005cbc7ebbccfe28d (patch)
treeff5abe0c77f5b129946300677d9b57b00d926a1e /drivers/input
parent5bab188a316718a26346cdb25c4cc6b319f8f907 (diff)
parent97eb3f24352ec6632c2127b35d8087d2a809a9b9 (diff)
Merge branch 'for-linus' of git://git.kernel.org/pub/scm/linux/kernel/git/dtor/input
* 'for-linus' of git://git.kernel.org/pub/scm/linux/kernel/git/dtor/input: (64 commits) Input: tsc2005 - remove 'disable' sysfs attribute Input: tsc2005 - add open/close Input: tsc2005 - handle read errors from SPI layer Input: tsc2005 - do not rearm timer in hardirq handler Input: tsc2005 - don't use work for 'pen up' handling Input: tsc2005 - do not use 0 in place of NULL Input: tsc2005 - use true/false for boolean variables Input: tsc2005 - hide selftest attribute if we can't reset Input: tsc2005 - rework driver initialization code Input: tsc2005 - set up bus type in input device Input: tsc2005 - set up parent device Input: tsc2005 - clear driver data after unbinding Input: tsc2005 - add module description Input: tsc2005 - remove driver banner message Input: tsc2005 - remove incorrect module alias Input: tsc2005 - convert to using dev_pm_ops Input: tsc2005 - use spi_get/set_drvdata() Input: introduce tsc2005 driver Input: xen-kbdfront - move to drivers/input/misc Input: xen-kbdfront - add grant reference for shared page ...
Diffstat (limited to 'drivers/input')
-rw-r--r--drivers/input/Kconfig10
-rw-r--r--drivers/input/Makefile2
-rw-r--r--drivers/input/evdev.c10
-rw-r--r--drivers/input/input-polldev.c4
-rw-r--r--drivers/input/input.c55
-rw-r--r--drivers/input/keyboard/Kconfig10
-rw-r--r--drivers/input/keyboard/Makefile1
-rw-r--r--drivers/input/keyboard/lm8323.c15
-rw-r--r--drivers/input/keyboard/max7359_keypad.c17
-rw-r--r--drivers/input/keyboard/mcs_touchkey.c57
-rw-r--r--drivers/input/keyboard/omap4-keypad.c74
-rw-r--r--drivers/input/keyboard/qt1070.c276
-rw-r--r--drivers/input/keyboard/tc3589x-keypad.c22
-rw-r--r--drivers/input/keyboard/tca6416-keypad.c28
-rw-r--r--drivers/input/misc/Kconfig13
-rw-r--r--drivers/input/misc/Makefile1
-rw-r--r--drivers/input/misc/ad714x-i2c.c17
-rw-r--r--drivers/input/misc/ad714x-spi.c17
-rw-r--r--drivers/input/misc/adxl34x-i2c.c16
-rw-r--r--drivers/input/misc/adxl34x-spi.c20
-rw-r--r--drivers/input/misc/ati_remote2.c4
-rw-r--r--drivers/input/misc/uinput.c48
-rw-r--r--drivers/input/misc/xen-kbdfront.c (renamed from drivers/input/xen-kbdfront.c)83
-rw-r--r--drivers/input/mouse/bcm5974.c28
-rw-r--r--drivers/input/mouse/synaptics_i2c.c16
-rw-r--r--drivers/input/sparse-keymap.c4
-rw-r--r--drivers/input/tablet/wacom_sys.c12
-rw-r--r--drivers/input/tablet/wacom_wac.c247
-rw-r--r--drivers/input/tablet/wacom_wac.h6
-rw-r--r--drivers/input/touchscreen/Kconfig45
-rw-r--r--drivers/input/touchscreen/Makefile4
-rw-r--r--drivers/input/touchscreen/ad7877.c19
-rw-r--r--drivers/input/touchscreen/ad7879-spi.c17
-rw-r--r--drivers/input/touchscreen/ads7846.c16
-rw-r--r--drivers/input/touchscreen/atmel_mxt_ts.c1211
-rw-r--r--drivers/input/touchscreen/qt602240_ts.c1406
-rw-r--r--drivers/input/touchscreen/tsc2005.c756
-rw-r--r--drivers/input/touchscreen/wm831x-ts.c368
38 files changed, 3123 insertions, 1832 deletions
diff --git a/drivers/input/Kconfig b/drivers/input/Kconfig
index 1903c0f5b92..23e82e46656 100644
--- a/drivers/input/Kconfig
+++ b/drivers/input/Kconfig
@@ -161,16 +161,6 @@ config INPUT_APMPOWER
To compile this driver as a module, choose M here: the
module will be called apm-power.
-config XEN_KBDDEV_FRONTEND
- tristate "Xen virtual keyboard and mouse support"
- depends on XEN_FBDEV_FRONTEND
- default y
- select XEN_XENBUS_FRONTEND
- help
- This driver implements the front-end of the Xen virtual
- keyboard and mouse device driver. It communicates with a back-end
- in another domain.
-
comment "Input Device Drivers"
source "drivers/input/keyboard/Kconfig"
diff --git a/drivers/input/Makefile b/drivers/input/Makefile
index 09614ce7496..0c789490e0b 100644
--- a/drivers/input/Makefile
+++ b/drivers/input/Makefile
@@ -24,5 +24,3 @@ obj-$(CONFIG_INPUT_TOUCHSCREEN) += touchscreen/
obj-$(CONFIG_INPUT_MISC) += misc/
obj-$(CONFIG_INPUT_APMPOWER) += apm-power.o
-
-obj-$(CONFIG_XEN_KBDDEV_FRONTEND) += xen-kbdfront.o
diff --git a/drivers/input/evdev.c b/drivers/input/evdev.c
index c8471a2552e..7f42d3a454d 100644
--- a/drivers/input/evdev.c
+++ b/drivers/input/evdev.c
@@ -321,6 +321,9 @@ static ssize_t evdev_write(struct file *file, const char __user *buffer,
struct input_event event;
int retval;
+ if (count < input_event_size())
+ return -EINVAL;
+
retval = mutex_lock_interruptible(&evdev->mutex);
if (retval)
return retval;
@@ -330,17 +333,16 @@ static ssize_t evdev_write(struct file *file, const char __user *buffer,
goto out;
}
- while (retval < count) {
-
+ do {
if (input_event_from_user(buffer + retval, &event)) {
retval = -EFAULT;
goto out;
}
+ retval += input_event_size();
input_inject_event(&evdev->handle,
event.type, event.code, event.value);
- retval += input_event_size();
- }
+ } while (retval + input_event_size() <= count);
out:
mutex_unlock(&evdev->mutex);
diff --git a/drivers/input/input-polldev.c b/drivers/input/input-polldev.c
index 0559e309bac..3037842a60d 100644
--- a/drivers/input/input-polldev.c
+++ b/drivers/input/input-polldev.c
@@ -192,7 +192,7 @@ static struct attribute_group input_polldev_attribute_group = {
};
/**
- * input_allocate_polled_device - allocated memory polled device
+ * input_allocate_polled_device - allocate memory for polled device
*
* The function allocates memory for a polled device and also
* for an input device associated with this polled device.
@@ -239,7 +239,7 @@ EXPORT_SYMBOL(input_free_polled_device);
* with input layer. The device should be allocated with call to
* input_allocate_polled_device(). Callers should also set up poll()
* method and set up capabilities (id, name, phys, bits) of the
- * corresponing input_dev structure.
+ * corresponding input_dev structure.
*/
int input_register_polled_device(struct input_polled_dev *dev)
{
diff --git a/drivers/input/input.c b/drivers/input/input.c
index 11905b6a302..d6e8bd8a851 100644
--- a/drivers/input/input.c
+++ b/drivers/input/input.c
@@ -791,22 +791,9 @@ int input_get_keycode(struct input_dev *dev, struct input_keymap_entry *ke)
int retval;
spin_lock_irqsave(&dev->event_lock, flags);
-
- if (dev->getkeycode) {
- /*
- * Support for legacy drivers, that don't implement the new
- * ioctls
- */
- u32 scancode = ke->index;
-
- memcpy(ke->scancode, &scancode, sizeof(scancode));
- ke->len = sizeof(scancode);
- retval = dev->getkeycode(dev, scancode, &ke->keycode);
- } else {
- retval = dev->getkeycode_new(dev, ke);
- }
-
+ retval = dev->getkeycode(dev, ke);
spin_unlock_irqrestore(&dev->event_lock, flags);
+
return retval;
}
EXPORT_SYMBOL(input_get_keycode);
@@ -831,35 +818,7 @@ int input_set_keycode(struct input_dev *dev,
spin_lock_irqsave(&dev->event_lock, flags);
- if (dev->setkeycode) {
- /*
- * Support for legacy drivers, that don't implement the new
- * ioctls
- */
- unsigned int scancode;
-
- retval = input_scancode_to_scalar(ke, &scancode);
- if (retval)
- goto out;
-
- /*
- * We need to know the old scancode, in order to generate a
- * keyup effect, if the set operation happens successfully
- */
- if (!dev->getkeycode) {
- retval = -EINVAL;
- goto out;
- }
-
- retval = dev->getkeycode(dev, scancode, &old_keycode);
- if (retval)
- goto out;
-
- retval = dev->setkeycode(dev, scancode, ke->keycode);
- } else {
- retval = dev->setkeycode_new(dev, ke, &old_keycode);
- }
-
+ retval = dev->setkeycode(dev, ke, &old_keycode);
if (retval)
goto out;
@@ -1846,11 +1805,11 @@ int input_register_device(struct input_dev *dev)
dev->rep[REP_PERIOD] = 33;
}
- if (!dev->getkeycode && !dev->getkeycode_new)
- dev->getkeycode_new = input_default_getkeycode;
+ if (!dev->getkeycode)
+ dev->getkeycode = input_default_getkeycode;
- if (!dev->setkeycode && !dev->setkeycode_new)
- dev->setkeycode_new = input_default_setkeycode;
+ if (!dev->setkeycode)
+ dev->setkeycode = input_default_setkeycode;
dev_set_name(&dev->dev, "input%ld",
(unsigned long) atomic_inc_return(&input_no) - 1);
diff --git a/drivers/input/keyboard/Kconfig b/drivers/input/keyboard/Kconfig
index c7a92028f45..b16bed038f7 100644
--- a/drivers/input/keyboard/Kconfig
+++ b/drivers/input/keyboard/Kconfig
@@ -112,6 +112,16 @@ config KEYBOARD_ATKBD_RDI_KEYCODES
right-hand column will be interpreted as the key shown in the
left-hand column.
+config KEYBOARD_QT1070
+ tristate "Atmel AT42QT1070 Touch Sensor Chip"
+ depends on I2C
+ help
+ Say Y here if you want to use Atmel AT42QT1070 QTouch
+ Sensor chip as input device.
+
+ To compile this driver as a module, choose M here:
+ the module will be called qt1070
+
config KEYBOARD_QT2160
tristate "Atmel AT42QT2160 Touch Sensor Chip"
depends on I2C && EXPERIMENTAL
diff --git a/drivers/input/keyboard/Makefile b/drivers/input/keyboard/Makefile
index 468c627a284..878e6c20deb 100644
--- a/drivers/input/keyboard/Makefile
+++ b/drivers/input/keyboard/Makefile
@@ -34,6 +34,7 @@ obj-$(CONFIG_KEYBOARD_OMAP4) += omap4-keypad.o
obj-$(CONFIG_KEYBOARD_OPENCORES) += opencores-kbd.o
obj-$(CONFIG_KEYBOARD_PXA27x) += pxa27x_keypad.o
obj-$(CONFIG_KEYBOARD_PXA930_ROTARY) += pxa930_rotary.o
+obj-$(CONFIG_KEYBOARD_QT1070) += qt1070.o
obj-$(CONFIG_KEYBOARD_QT2160) += qt2160.o
obj-$(CONFIG_KEYBOARD_SAMSUNG) += samsung-keypad.o
obj-$(CONFIG_KEYBOARD_SH_KEYSC) += sh_keysc.o
diff --git a/drivers/input/keyboard/lm8323.c b/drivers/input/keyboard/lm8323.c
index f7c2a166576..b732870ecc8 100644
--- a/drivers/input/keyboard/lm8323.c
+++ b/drivers/input/keyboard/lm8323.c
@@ -30,6 +30,7 @@
#include <linux/delay.h>
#include <linux/input.h>
#include <linux/leds.h>
+#include <linux/pm.h>
#include <linux/i2c/lm8323.h>
#include <linux/slab.h>
@@ -802,8 +803,9 @@ static int __devexit lm8323_remove(struct i2c_client *client)
* We don't need to explicitly suspend the chip, as it already switches off
* when there's no activity.
*/
-static int lm8323_suspend(struct i2c_client *client, pm_message_t mesg)
+static int lm8323_suspend(struct device *dev)
{
+ struct i2c_client *client = to_i2c_client(dev);
struct lm8323_chip *lm = i2c_get_clientdata(client);
int i;
@@ -821,8 +823,9 @@ static int lm8323_suspend(struct i2c_client *client, pm_message_t mesg)
return 0;
}
-static int lm8323_resume(struct i2c_client *client)
+static int lm8323_resume(struct device *dev)
{
+ struct i2c_client *client = to_i2c_client(dev);
struct lm8323_chip *lm = i2c_get_clientdata(client);
int i;
@@ -839,11 +842,10 @@ static int lm8323_resume(struct i2c_client *client)
return 0;
}
-#else
-#define lm8323_suspend NULL
-#define lm8323_resume NULL
#endif
+static SIMPLE_DEV_PM_OPS(lm8323_pm_ops, lm8323_suspend, lm8323_resume);
+
static const struct i2c_device_id lm8323_id[] = {
{ "lm8323", 0 },
{ }
@@ -852,11 +854,10 @@ static const struct i2c_device_id lm8323_id[] = {
static struct i2c_driver lm8323_i2c_driver = {
.driver = {
.name = "lm8323",
+ .pm = &lm8323_pm_ops,
},
.probe = lm8323_probe,
.remove = __devexit_p(lm8323_remove),
- .suspend = lm8323_suspend,
- .resume = lm8323_resume,
.id_table = lm8323_id,
};
MODULE_DEVICE_TABLE(i2c, lm8323_id);
diff --git a/drivers/input/keyboard/max7359_keypad.c b/drivers/input/keyboard/max7359_keypad.c
index 9091ff5ea80..5afe35ad24d 100644
--- a/drivers/input/keyboard/max7359_keypad.c
+++ b/drivers/input/keyboard/max7359_keypad.c
@@ -17,6 +17,7 @@
#include <linux/i2c.h>
#include <linux/slab.h>
#include <linux/interrupt.h>
+#include <linux/pm.h>
#include <linux/input.h>
#include <linux/input/matrix_keypad.h>
@@ -271,8 +272,10 @@ static int __devexit max7359_remove(struct i2c_client *client)
}
#ifdef CONFIG_PM
-static int max7359_suspend(struct i2c_client *client, pm_message_t mesg)
+static int max7359_suspend(struct device *dev)
{
+ struct i2c_client *client = to_i2c_client(dev);
+
max7359_fall_deepsleep(client);
if (device_may_wakeup(&client->dev))
@@ -281,8 +284,10 @@ static int max7359_suspend(struct i2c_client *client, pm_message_t mesg)
return 0;
}
-static int max7359_resume(struct i2c_client *client)
+static int max7359_resume(struct device *dev)
{
+ struct i2c_client *client = to_i2c_client(dev);
+
if (device_may_wakeup(&client->dev))
disable_irq_wake(client->irq);
@@ -291,11 +296,10 @@ static int max7359_resume(struct i2c_client *client)
return 0;
}
-#else
-#define max7359_suspend NULL
-#define max7359_resume NULL
#endif
+static SIMPLE_DEV_PM_OPS(max7359_pm, max7359_suspend, max7359_resume);
+
static const struct i2c_device_id max7359_ids[] = {
{ "max7359", 0 },
{ }
@@ -305,11 +309,10 @@ MODULE_DEVICE_TABLE(i2c, max7359_ids);
static struct i2c_driver max7359_i2c_driver = {
.driver = {
.name = "max7359",
+ .pm = &max7359_pm,
},
.probe = max7359_probe,
.remove = __devexit_p(max7359_remove),
- .suspend = max7359_suspend,
- .resume = max7359_resume,
.id_table = max7359_ids,
};
diff --git a/drivers/input/keyboard/mcs_touchkey.c b/drivers/input/keyboard/mcs_touchkey.c
index 63b849d7e90..af1aab324a4 100644
--- a/drivers/input/keyboard/mcs_touchkey.c
+++ b/drivers/input/keyboard/mcs_touchkey.c
@@ -1,5 +1,5 @@
/*
- * mcs_touchkey.c - Touchkey driver for MELFAS MCS5000/5080 controller
+ * Touchkey driver for MELFAS MCS5000/5080 controller
*
* Copyright (C) 2010 Samsung Electronics Co.Ltd
* Author: HeungJun Kim <riverful.kim@samsung.com>
@@ -19,6 +19,7 @@
#include <linux/input.h>
#include <linux/irq.h>
#include <linux/slab.h>
+#include <linux/pm.h>
/* MCS5000 Touchkey */
#define MCS5000_TOUCHKEY_STATUS 0x04
@@ -45,6 +46,8 @@ struct mcs_touchkey_chip {
};
struct mcs_touchkey_data {
+ void (*poweron)(bool);
+
struct i2c_client *client;
struct input_dev *input_dev;
struct mcs_touchkey_chip chip;
@@ -169,6 +172,11 @@ static int __devinit mcs_touchkey_probe(struct i2c_client *client,
if (pdata->cfg_pin)
pdata->cfg_pin();
+ if (pdata->poweron) {
+ data->poweron = pdata->poweron;
+ data->poweron(true);
+ }
+
error = request_threaded_irq(client->irq, NULL, mcs_touchkey_interrupt,
IRQF_TRIGGER_FALLING, client->dev.driver->name, data);
if (error) {
@@ -196,12 +204,57 @@ static int __devexit mcs_touchkey_remove(struct i2c_client *client)
struct mcs_touchkey_data *data = i2c_get_clientdata(client);
free_irq(client->irq, data);
+ if (data->poweron)
+ data->poweron(false);
input_unregister_device(data->input_dev);
kfree(data);
return 0;
}
+static void mcs_touchkey_shutdown(struct i2c_client *client)
+{
+ struct mcs_touchkey_data *data = i2c_get_clientdata(client);
+
+ if (data->poweron)
+ data->poweron(false);
+}
+
+#ifdef CONFIG_PM_SLEEP
+static int mcs_touchkey_suspend(struct device *dev)
+{
+ struct mcs_touchkey_data *data = dev_get_drvdata(dev);
+ struct i2c_client *client = data->client;
+
+ /* Disable the work */
+ disable_irq(client->irq);
+
+ /* Finally turn off the power */
+ if (data->poweron)
+ data->poweron(false);
+
+ return 0;
+}
+
+static int mcs_touchkey_resume(struct device *dev)
+{
+ struct mcs_touchkey_data *data = dev_get_drvdata(dev);
+ struct i2c_client *client = data->client;
+
+ /* Enable the device first */
+ if (data->poweron)
+ data->poweron(true);
+
+ /* Enable irq again */
+ enable_irq(client->irq);
+
+ return 0;
+}
+#endif
+
+static SIMPLE_DEV_PM_OPS(mcs_touchkey_pm_ops,
+ mcs_touchkey_suspend, mcs_touchkey_resume);
+
static const struct i2c_device_id mcs_touchkey_id[] = {
{ "mcs5000_touchkey", MCS5000_TOUCHKEY },
{ "mcs5080_touchkey", MCS5080_TOUCHKEY },
@@ -213,9 +266,11 @@ static struct i2c_driver mcs_touchkey_driver = {
.driver = {
.name = "mcs_touchkey",
.owner = THIS_MODULE,
+ .pm = &mcs_touchkey_pm_ops,
},
.probe = mcs_touchkey_probe,
.remove = __devexit_p(mcs_touchkey_remove),
+ .shutdown = mcs_touchkey_shutdown,
.id_table = mcs_touchkey_id,
};
diff --git a/drivers/input/keyboard/omap4-keypad.c b/drivers/input/keyboard/omap4-keypad.c
index 45bd0977d00..c51a3c4a7fe 100644
--- a/drivers/input/keyboard/omap4-keypad.c
+++ b/drivers/input/keyboard/omap4-keypad.c
@@ -29,6 +29,7 @@
#include <linux/io.h>
#include <linux/input.h>
#include <linux/slab.h>
+#include <linux/pm_runtime.h>
#include <plat/omap4-keypad.h>
@@ -80,20 +81,6 @@ struct omap4_keypad {
unsigned short keymap[];
};
-static void __devinit omap4_keypad_config(struct omap4_keypad *keypad_data)
-{
- __raw_writel(OMAP4_VAL_FUNCTIONALCFG,
- keypad_data->base + OMAP4_KBD_CTRL);
- __raw_writel(OMAP4_VAL_DEBOUNCINGTIME,
- keypad_data->base + OMAP4_KBD_DEBOUNCINGTIME);
- __raw_writel(OMAP4_VAL_IRQDISABLE,
- keypad_data->base + OMAP4_KBD_IRQSTATUS);
- __raw_writel(OMAP4_DEF_IRQENABLE_EVENTEN | OMAP4_DEF_IRQENABLE_LONGKEY,
- keypad_data->base + OMAP4_KBD_IRQENABLE);
- __raw_writel(OMAP4_DEF_WUP_EVENT_ENA | OMAP4_DEF_WUP_LONG_KEY_ENA,
- keypad_data->base + OMAP4_KBD_WAKEUPENABLE);
-}
-
/* Interrupt handler */
static irqreturn_t omap4_keypad_interrupt(int irq, void *dev_id)
{
@@ -144,6 +131,49 @@ static irqreturn_t omap4_keypad_interrupt(int irq, void *dev_id)
return IRQ_HANDLED;
}
+static int omap4_keypad_open(struct input_dev *input)
+{
+ struct omap4_keypad *keypad_data = input_get_drvdata(input);
+
+ pm_runtime_get_sync(input->dev.parent);
+
+ disable_irq(keypad_data->irq);
+
+ __raw_writel(OMAP4_VAL_FUNCTIONALCFG,
+ keypad_data->base + OMAP4_KBD_CTRL);
+ __raw_writel(OMAP4_VAL_DEBOUNCINGTIME,
+ keypad_data->base + OMAP4_KBD_DEBOUNCINGTIME);
+ __raw_writel(OMAP4_VAL_IRQDISABLE,
+ keypad_data->base + OMAP4_KBD_IRQSTATUS);
+ __raw_writel(OMAP4_DEF_IRQENABLE_EVENTEN | OMAP4_DEF_IRQENABLE_LONGKEY,
+ keypad_data->base + OMAP4_KBD_IRQENABLE);
+ __raw_writel(OMAP4_DEF_WUP_EVENT_ENA | OMAP4_DEF_WUP_LONG_KEY_ENA,
+ keypad_data->base + OMAP4_KBD_WAKEUPENABLE);
+
+ enable_irq(keypad_data->irq);
+
+ return 0;
+}
+
+static void omap4_keypad_close(struct input_dev *input)
+{
+ struct omap4_keypad *keypad_data = input_get_drvdata(input);
+
+ disable_irq(keypad_data->irq);
+
+ /* Disable interrupts */
+ __raw_writel(OMAP4_VAL_IRQDISABLE,
+ keypad_data->base + OMAP4_KBD_IRQENABLE);
+
+ /* clear pending interrupts */
+ __raw_writel(__raw_readl(keypad_data->base + OMAP4_KBD_IRQSTATUS),
+ keypad_data->base + OMAP4_KBD_IRQSTATUS);
+
+ enable_irq(keypad_data->irq);
+
+ pm_runtime_put_sync(input->dev.parent);
+}
+
static int __devinit omap4_keypad_probe(struct platform_device *pdev)
{
const struct omap4_keypad_platform_data *pdata;
@@ -225,6 +255,9 @@ static int __devinit omap4_keypad_probe(struct platform_device *pdev)
input_dev->id.product = 0x0001;
input_dev->id.version = 0x0001;
+ input_dev->open = omap4_keypad_open;
+ input_dev->close = omap4_keypad_close;
+
input_dev->keycode = keypad_data->keymap;
input_dev->keycodesize = sizeof(keypad_data->keymap[0]);
input_dev->keycodemax = max_keys;
@@ -239,8 +272,6 @@ static int __devinit omap4_keypad_probe(struct platform_device *pdev)
matrix_keypad_build_keymap(pdata->keymap_data, row_shift,
input_dev->keycode, input_dev->keybit);
- omap4_keypad_config(keypad_data);
-
error = request_irq(keypad_data->irq, omap4_keypad_interrupt,
IRQF_TRIGGER_RISING,
"omap4-keypad", keypad_data);
@@ -249,17 +280,19 @@ static int __devinit omap4_keypad_probe(struct platform_device *pdev)
goto err_free_input;
}
+ pm_runtime_enable(&pdev->dev);
+
error = input_register_device(keypad_data->input);
if (error < 0) {
dev_err(&pdev->dev, "failed to register input device\n");
- goto err_free_irq;
+ goto err_pm_disable;
}
-
platform_set_drvdata(pdev, keypad_data);
return 0;
-err_free_irq:
+err_pm_disable:
+ pm_runtime_disable(&pdev->dev);
free_irq(keypad_data->irq, keypad_data);
err_free_input:
input_free_device(input_dev);
@@ -278,6 +311,9 @@ static int __devexit omap4_keypad_remove(struct platform_device *pdev)
struct resource *res;
free_irq(keypad_data->irq, keypad_data);
+
+ pm_runtime_disable(&pdev->dev);
+
input_unregister_device(keypad_data->input);
iounmap(keypad_data->base);
diff --git a/drivers/input/keyboard/qt1070.c b/drivers/input/keyboard/qt1070.c
new file mode 100644
index 00000000000..fba8404c729
--- /dev/null
+++ b/drivers/input/keyboard/qt1070.c
@@ -0,0 +1,276 @@
+/*
+ * Atmel AT42QT1070 QTouch Sensor Controller
+ *
+ * Copyright (C) 2011 Atmel
+ *
+ * Authors: Bo Shen <voice.shen@atmel.com>
+ *
+ * Base on AT42QT2160 driver by:
+ * Raphael Derosso Pereira <raphaelpereira@gmail.com>
+ * Copyright (C) 2009
+ *
+ * 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; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/i2c.h>
+#include <linux/input.h>
+#include <linux/slab.h>
+#include <linux/irq.h>
+#include <linux/interrupt.h>
+#include <linux/jiffies.h>
+#include <linux/delay.h>
+
+/* Address for each register */
+#define CHIP_ID 0x00
+#define QT1070_CHIP_ID 0x2E
+
+#define FW_VERSION 0x01
+#define QT1070_FW_VERSION 0x15
+
+#define DET_STATUS 0x02
+
+#define KEY_STATUS 0x03
+
+/* Calibrate */
+#define CALIBRATE_CMD 0x38
+#define QT1070_CAL_TIME 200
+
+/* Reset */
+#define RESET 0x39
+#define QT1070_RESET_TIME 255
+
+/* AT42QT1070 support up to 7 keys */
+static const unsigned short qt1070_key2code[] = {
+ KEY_0, KEY_1, KEY_2, KEY_3,
+ KEY_4, KEY_5, KEY_6,
+};
+
+struct qt1070_data {
+ struct i2c_client *client;
+ struct input_dev *input;
+ unsigned int irq;
+ unsigned short keycodes[ARRAY_SIZE(qt1070_key2code)];
+ u8 last_keys;
+};
+
+static int qt1070_read(struct i2c_client *client, u8 reg)
+{
+ int ret;
+
+ ret = i2c_smbus_read_byte_data(client, reg);
+ if (ret < 0)
+ dev_err(&client->dev,
+ "can not read register, returned %d\n", ret);
+
+ return ret;
+}
+
+static int qt1070_write(struct i2c_client *client, u8 reg, u8 data)
+{
+ int ret;
+
+ ret = i2c_smbus_write_byte_data(client, reg, data);
+ if (ret < 0)
+ dev_err(&client->dev,
+ "can not write register, returned %d\n", ret);
+
+ return ret;
+}
+
+static bool __devinit qt1070_identify(struct i2c_client *client)
+{
+ int id, ver;
+
+ /* Read Chip ID */
+ id = qt1070_read(client, CHIP_ID);
+ if (id != QT1070_CHIP_ID) {
+ dev_err(&client->dev, "ID %d not supported\n", id);
+ return false;
+ }
+
+ /* Read firmware version */
+ ver = qt1070_read(client, FW_VERSION);
+ if (ver < 0) {
+ dev_err(&client->dev, "could not read the firmware version\n");
+ return false;
+ }
+
+ dev_info(&client->dev, "AT42QT1070 firmware version %x\n", ver);
+
+ return true;
+}
+
+static irqreturn_t qt1070_interrupt(int irq, void *dev_id)
+{
+ struct qt1070_data *data = dev_id;
+ struct i2c_client *client = data->client;
+ struct input_dev *input = data->input;
+ int i;
+ u8 new_keys, keyval, mask = 0x01;
+
+ /* Read the detected status register, thus clearing interrupt */
+ qt1070_read(client, DET_STATUS);
+
+ /* Read which key changed */
+ new_keys = qt1070_read(client, KEY_STATUS);
+
+ for (i = 0; i < ARRAY_SIZE(qt1070_key2code); i++) {
+ keyval = new_keys & mask;
+ if ((data->last_keys & mask) != keyval)
+ input_report_key(input, data->keycodes[i], keyval);
+ mask <<= 1;
+ }
+ input_sync(input);
+
+ data->last_keys = new_keys;
+ return IRQ_HANDLED;
+}
+
+static int __devinit qt1070_probe(struct i2c_client *client,
+ const struct i2c_device_id *id)
+{
+ struct qt1070_data *data;
+ struct input_dev *input;
+ int i;
+ int err;
+
+ err = i2c_check_functionality(client->adapter, I2C_FUNC_SMBUS_BYTE);
+ if (!err) {
+ dev_err(&client->dev, "%s adapter not supported\n",
+ dev_driver_string(&client->adapter->dev));
+ return -ENODEV;
+ }
+
+ if (!client->irq) {
+ dev_err(&client->dev, "please assign the irq to this device\n");
+ return -EINVAL;
+ }
+
+ /* Identify the qt1070 chip */
+ if (!qt1070_identify(client))
+ return -ENODEV;
+
+ data = kzalloc(sizeof(struct qt1070_data), GFP_KERNEL);
+ input = input_allocate_device();
+ if (!data || !input) {
+ dev_err(&client->dev, "insufficient memory\n");
+ err = -ENOMEM;
+ goto err_free_mem;
+ }
+
+ data->client = client;
+ data->input = input;
+ data->irq = client->irq;
+
+ input->name = "AT42QT1070 QTouch Sensor";
+ input->dev.parent = &client->dev;
+ input->id.bustype = BUS_I2C;
+
+ /* Add the keycode */
+ input->keycode = data->keycodes;
+ input->keycodesize = sizeof(data->keycodes[0]);
+ input->keycodemax = ARRAY_SIZE(qt1070_key2code);
+
+ __set_bit(EV_KEY, input->evbit);
+
+ for (i = 0; i < ARRAY_SIZE(qt1070_key2code); i++) {
+ data->keycodes[i] = qt1070_key2code[i];
+ __set_bit(qt1070_key2code[i], input->keybit);
+ }
+
+ /* Calibrate device */
+ qt1070_write(client, CALIBRATE_CMD, 1);
+ msleep(QT1070_CAL_TIME);
+
+ /* Soft reset */
+ qt1070_write(client, RESET, 1);
+ msleep(QT1070_RESET_TIME);
+
+ err = request_threaded_irq(client->irq, NULL, qt1070_interrupt,
+ IRQF_TRIGGER_NONE, client->dev.driver->name, data);
+ if (err) {
+ dev_err(&client->dev, "fail to request irq\n");
+ goto err_free_mem;
+ }
+
+ /* Register the input device */
+ err = input_register_device(data->input);
+ if (err) {
+ dev_err(&client->dev, "Failed to register input device\n");
+ goto err_free_irq;
+ }
+
+ i2c_set_clientdata(client, data);
+
+ /* Read to clear the chang line */
+ qt1070_read(client, DET_STATUS);
+
+ return 0;
+
+err_free_irq:
+ free_irq(client->irq, data);
+err_free_mem:
+ input_free_device(input);
+ kfree(data);
+ return err;
+}
+
+static int __devexit qt1070_remove(struct i2c_client *client)
+{
+ struct qt1070_data *data = i2c_get_clientdata(client);
+
+ /* Release IRQ */
+ free_irq(client->irq, data);
+
+ input_unregister_device(data->input);
+ kfree(data);
+
+ i2c_set_clientdata(client, NULL);
+
+ return 0;
+}
+
+static const struct i2c_device_id qt1070_id[] = {
+ { "qt1070", 0 },
+ { },
+};
+
+static struct i2c_driver qt1070_driver = {
+ .driver = {
+ .name = "qt1070",
+ .owner = THIS_MODULE,
+ },
+ .id_table = qt1070_id,
+ .probe = qt1070_probe,
+ .remove = __devexit_p(qt1070_remove),
+};
+
+static int __init qt1070_init(void)
+{
+ return i2c_add_driver(&qt1070_driver);
+}
+module_init(qt1070_init);
+
+static void __exit qt1070_exit(void)
+{
+ i2c_del_driver(&qt1070_driver);
+}
+module_exit(qt1070_exit);
+
+MODULE_AUTHOR("Bo Shen <voice.shen@atmel.com>");
+MODULE_DESCRIPTION("Driver for AT42QT1070 QTouch sensor");
+MODULE_LICENSE("GPL");
diff --git a/drivers/input/keyboard/tc3589x-keypad.c b/drivers/input/keyboard/tc3589x-keypad.c
index dbbe761778d..99122f59e98 100644
--- a/drivers/input/keyboard/tc3589x-keypad.c
+++ b/drivers/input/keyboard/tc3589x-keypad.c
@@ -402,7 +402,7 @@ static int __devexit tc3589x_keypad_remove(struct platform_device *pdev)
return 0;
}
-#ifdef CONFIG_PM
+#ifdef CONFIG_PM_SLEEP
static int tc3589x_keypad_suspend(struct device *dev)
{
struct platform_device *pdev = to_platform_device(dev);
@@ -439,19 +439,19 @@ static int tc3589x_keypad_resume(struct device *dev)
return 0;
}
-
-static const SIMPLE_DEV_PM_OPS(tc3589x_keypad_dev_pm_ops,
- tc3589x_keypad_suspend, tc3589x_keypad_resume);
#endif
+static SIMPLE_DEV_PM_OPS(tc3589x_keypad_dev_pm_ops,
+ tc3589x_keypad_suspend, tc3589x_keypad_resume);
+
static struct platform_driver tc3589x_keypad_driver = {
- .driver.name = "tc3589x-keypad",
- .driver.owner = THIS_MODULE,
-#ifdef CONFIG_PM
- .driver.pm = &tc3589x_keypad_dev_pm_ops,
-#endif
- .probe = tc3589x_keypad_probe,
- .remove = __devexit_p(tc3589x_keypad_remove),
+ .driver = {
+ .name = "tc3589x-keypad",
+ .owner = THIS_MODULE,
+ .pm = &tc3589x_keypad_dev_pm_ops,
+ },
+ .probe = tc3589x_keypad_probe,
+ .remove = __devexit_p(tc3589x_keypad_remove),
};
static int __init tc3589x_keypad_init(void)
diff --git a/drivers/input/keyboard/tca6416-keypad.c b/drivers/input/keyboard/tca6416-keypad.c
index 800fbccf1f0..3afea3f8971 100644
--- a/drivers/input/keyboard/tca6416-keypad.c
+++ b/drivers/input/keyboard/tca6416-keypad.c
@@ -297,6 +297,7 @@ static int __devinit tca6416_keypad_probe(struct i2c_client *client,
}
i2c_set_clientdata(client, chip);
+ device_init_wakeup(&client->dev, 1);
return 0;
@@ -326,10 +327,37 @@ static int __devexit tca6416_keypad_remove(struct i2c_client *client)
return 0;
}
+#ifdef CONFIG_PM_SLEEP
+static int tca6416_keypad_suspend(struct device *dev)
+{
+ struct i2c_client *client = to_i2c_client(dev);
+ struct tca6416_keypad_chip *chip = i2c_get_clientdata(client);
+
+ if (device_may_wakeup(dev))
+ enable_irq_wake(chip->irqnum);
+
+ return 0;
+}
+
+static int tca6416_keypad_resume(struct device *dev)
+{
+ struct i2c_client *client = to_i2c_client(dev);
+ struct tca6416_keypad_chip *chip = i2c_get_clientdata(client);
+
+ if (device_may_wakeup(dev))
+ disable_irq_wake(chip->irqnum);
+
+ return 0;
+}
+#endif
+
+static SIMPLE_DEV_PM_OPS(tca6416_keypad_dev_pm_ops,
+ tca6416_keypad_suspend, tca6416_keypad_resume);
static struct i2c_driver tca6416_keypad_driver = {
.driver = {
.name = "tca6416-keypad",
+ .pm = &tca6416_keypad_dev_pm_ops,
},
.probe = tca6416_keypad_probe,
.remove = __devexit_p(tca6416_keypad_remove),
diff --git a/drivers/input/misc/Kconfig b/drivers/input/misc/Kconfig
index b0c6772851a..f9cf0881b0e 100644
--- a/drivers/input/misc/Kconfig
+++ b/drivers/input/misc/Kconfig
@@ -454,4 +454,17 @@ config INPUT_CMA3000_I2C
To compile this driver as a module, choose M here: the
module will be called cma3000_d0x_i2c.
+config INPUT_XEN_KBDDEV_FRONTEND
+ tristate "Xen virtual keyboard and mouse support"
+ depends on XEN_FBDEV_FRONTEND
+ default y
+ select XEN_XENBUS_FRONTEND
+ help
+ This driver implements the front-end of the Xen virtual
+ keyboard and mouse device driver. It communicates with a back-end
+ in another domain.
+
+ To compile this driver as a module, choose M here: the
+ module will be called xen-kbdfront.
+
endif
diff --git a/drivers/input/misc/Makefile b/drivers/input/misc/Makefile
index 9b4797112c9..e3f7984e627 100644
--- a/drivers/input/misc/Makefile
+++ b/drivers/input/misc/Makefile
@@ -42,5 +42,6 @@ obj-$(CONFIG_INPUT_TWL4030_VIBRA) += twl4030-vibra.o
obj-$(CONFIG_INPUT_UINPUT) += uinput.o
obj-$(CONFIG_INPUT_WISTRON_BTNS) += wistron_btns.o
obj-$(CONFIG_INPUT_WM831X_ON) += wm831x-on.o
+obj-$(CONFIG_INPUT_XEN_KBDDEV_FRONTEND) += xen-kbdfront.o
obj-$(CONFIG_INPUT_YEALINK) += yealink.o
diff --git a/drivers/input/misc/ad714x-i2c.c b/drivers/input/misc/ad714x-i2c.c
index 2bef8fa56c9..e21deb1baa8 100644
--- a/drivers/input/misc/ad714x-i2c.c
+++ b/drivers/input/misc/ad714x-i2c.c
@@ -10,23 +10,23 @@
#include <linux/i2c.h>
#include <linux/module.h>
#include <linux/types.h>
+#include <linux/pm.h>
#include "ad714x.h"
#ifdef CONFIG_PM
-static int ad714x_i2c_suspend(struct i2c_client *client, pm_message_t message)
+static int ad714x_i2c_suspend(struct device *dev)
{
- return ad714x_disable(i2c_get_clientdata(client));
+ return ad714x_disable(i2c_get_clientdata(to_i2c_client(dev)));
}
-static int ad714x_i2c_resume(struct i2c_client *client)
+static int ad714x_i2c_resume(struct device *dev)
{
- return ad714x_enable(i2c_get_clientdata(client));
+ return ad714x_enable(i2c_get_clientdata(to_i2c_client(dev)));
}
-#else
-# define ad714x_i2c_suspend NULL
-# define ad714x_i2c_resume NULL
#endif
+static SIMPLE_DEV_PM_OPS(ad714x_i2c_pm, ad714x_i2c_suspend, ad714x_i2c_resume);
+
static int ad714x_i2c_write(struct device *dev, unsigned short reg,
unsigned short data)
{
@@ -114,11 +114,10 @@ MODULE_DEVICE_TABLE(i2c, ad714x_id);
static struct i2c_driver ad714x_i2c_driver = {
.driver = {
.name = "ad714x_captouch",
+ .pm = &ad714x_i2c_pm,
},
.probe = ad714x_i2c_probe,
.remove = __devexit_p(ad714x_i2c_remove),
- .suspend = ad714x_i2c_suspend,
- .resume = ad714x_i2c_resume,
.id_table = ad714x_id,
};
diff --git a/drivers/input/misc/ad714x-spi.c b/drivers/input/misc/ad714x-spi.c
index 7f8dedfd1bf..4120dd54930 100644
--- a/drivers/input/misc/ad714x-spi.c
+++ b/drivers/input/misc/ad714x-spi.c
@@ -9,6 +9,7 @@
#include <linux/input.h> /* BUS_I2C */
#include <linux/module.h>
#include <linux/spi/spi.h>
+#include <linux/pm.h>
#include <linux/types.h>
#include "ad714x.h"
@@ -16,20 +17,19 @@
#define AD714x_SPI_READ BIT(10)
#ifdef CONFIG_PM
-static int ad714x_spi_suspend(struct spi_device *spi, pm_message_t message)
+static int ad714x_spi_suspend(struct device *dev)
{
- return ad714x_disable(spi_get_drvdata(spi));
+ return ad714x_disable(spi_get_drvdata(to_spi_device(dev)));
}
-static int ad714x_spi_resume(struct spi_device *spi)
+static int ad714x_spi_resume(struct device *dev)
{
- return ad714x_enable(spi_get_drvdata(spi));
+ return ad714x_enable(spi_get_drvdata(to_spi_device(dev)));
}
-#else
-# define ad714x_spi_suspend NULL
-# define ad714x_spi_resume NULL
#endif
+static SIMPLE_DEV_PM_OPS(ad714x_spi_pm, ad714x_spi_suspend, ad714x_spi_resume);
+
static int ad714x_spi_read(struct device *dev, unsigned short reg,
unsigned short *data)
{
@@ -79,11 +79,10 @@ static struct spi_driver ad714x_spi_driver = {
.driver = {
.name = "ad714x_captouch",
.owner = THIS_MODULE,
+ .pm = &ad714x_spi_pm,
},
.probe = ad714x_spi_probe,
.remove = __devexit_p(ad714x_spi_remove),
- .suspend = ad714x_spi_suspend,
- .resume = ad714x_spi_resume,
};
static __init int ad714x_spi_init(void)
diff --git a/drivers/input/misc/adxl34x-i2c.c b/drivers/input/misc/adxl34x-i2c.c
index 0779724af7e..ccacf2bb06a 100644
--- a/drivers/input/misc/adxl34x-i2c.c
+++ b/drivers/input/misc/adxl34x-i2c.c
@@ -11,6 +11,7 @@
#include <linux/i2c.h>
#include <linux/module.h>
#include <linux/types.h>
+#include <linux/pm.h>
#include "adxl34x.h"
static int adxl34x_smbus_read(struct device *dev, unsigned char reg)
@@ -105,8 +106,9 @@ static int __devexit adxl34x_i2c_remove(struct i2c_client *client)
}
#ifdef CONFIG_PM
-static int adxl34x_i2c_suspend(struct i2c_client *client, pm_message_t message)
+static int adxl34x_i2c_suspend(struct device *dev)
{
+ struct i2c_client *client = to_i2c_client(dev);
struct adxl34x *ac = i2c_get_clientdata(client);
adxl34x_suspend(ac);
@@ -114,19 +116,20 @@ static int adxl34x_i2c_suspend(struct i2c_client *client, pm_message_t message)
return 0;
}
-static int adxl34x_i2c_resume(struct i2c_client *client)
+static int adxl34x_i2c_resume(struct device *dev)
{
+ struct i2c_client *client = to_i2c_client(dev);
struct adxl34x *ac = i2c_get_clientdata(client);
adxl34x_resume(ac);
return 0;
}
-#else
-# define adxl34x_i2c_suspend NULL
-# define adxl34x_i2c_resume NULL
#endif
+static SIMPLE_DEV_PM_OPS(adxl34x_i2c_pm, adxl34x_i2c_suspend,
+ adxl34x_i2c_resume);
+
static const struct i2c_device_id adxl34x_id[] = {
{ "adxl34x", 0 },
{ }
@@ -138,11 +141,10 @@ static struct i2c_driver adxl34x_driver = {
.driver = {
.name = "adxl34x",
.owner = THIS_MODULE,
+ .pm = &adxl34x_i2c_pm,
},
.probe = adxl34x_i2c_probe,
.remove = __devexit_p(adxl34x_i2c_remove),
- .suspend = adxl34x_i2c_suspend,
- .resume = adxl34x_i2c_resume,
.id_table = adxl34x_id,
};
diff --git a/drivers/input/misc/adxl34x-spi.c b/drivers/input/misc/adxl34x-spi.c
index 782de9e8982..f29de22fdda 100644
--- a/drivers/input/misc/adxl34x-spi.c
+++ b/drivers/input/misc/adxl34x-spi.c
@@ -10,6 +10,7 @@
#include <linux/input.h> /* BUS_SPI */
#include <linux/module.h>
#include <linux/spi/spi.h>
+#include <linux/pm.h>
#include <linux/types.h>
#include "adxl34x.h"
@@ -57,7 +58,7 @@ static int adxl34x_spi_read_block(struct device *dev,
return (status < 0) ? status : 0;
}
-static const struct adxl34x_bus_ops adx134x_spi_bops = {
+static const struct adxl34x_bus_ops adxl34x_spi_bops = {
.bustype = BUS_SPI,
.write = adxl34x_spi_write,
.read = adxl34x_spi_read,
@@ -76,7 +77,7 @@ static int __devinit adxl34x_spi_probe(struct spi_device *spi)
ac = adxl34x_probe(&spi->dev, spi->irq,
spi->max_speed_hz > MAX_FREQ_NO_FIFODELAY,
- &adx134x_spi_bops);
+ &adxl34x_spi_bops);
if (IS_ERR(ac))
return PTR_ERR(ac);
@@ -94,8 +95,9 @@ static int __devexit adxl34x_spi_remove(struct spi_device *spi)
}
#ifdef CONFIG_PM
-static int adxl34x_spi_suspend(struct spi_device *spi, pm_message_t message)
+static int adxl34x_spi_suspend(struct device *dev)
{
+ struct spi_device *spi = to_spi_device(dev);
struct adxl34x *ac = dev_get_drvdata(&spi->dev);
adxl34x_suspend(ac);
@@ -103,29 +105,29 @@ static int adxl34x_spi_suspend(struct spi_device *spi, pm_message_t message)
return 0;
}
-static int adxl34x_spi_resume(struct spi_device *spi)
+static int adxl34x_spi_resume(struct device *dev)
{
+ struct spi_device *spi = to_spi_device(dev);
struct adxl34x *ac = dev_get_drvdata(&spi->dev);
adxl34x_resume(ac);
return 0;
}
-#else
-# define adxl34x_spi_suspend NULL
-# define adxl34x_spi_resume NULL
#endif
+static SIMPLE_DEV_PM_OPS(adxl34x_spi_pm, adxl34x_spi_suspend,
+ adxl34x_spi_resume);
+
static struct spi_driver adxl34x_driver = {
.driver = {
.name = "adxl34x",
.bus = &spi_bus_type,
.owner = THIS_MODULE,
+ .pm = &adxl34x_spi_pm,
},
.probe = adxl34x_spi_probe,
.remove = __devexit_p(adxl34x_spi_remove),
- .suspend = adxl34x_spi_suspend,
- .resume = adxl34x_spi_resume,
};
static int __init adxl34x_spi_init(void)
diff --git a/drivers/input/misc/ati_remote2.c b/drivers/input/misc/ati_remote2.c
index 0b0e9be6354..9ccdb82d869 100644
--- a/drivers/input/misc/ati_remote2.c
+++ b/drivers/input/misc/ati_remote2.c
@@ -612,8 +612,8 @@ static int ati_remote2_input_init(struct ati_remote2 *ar2)
idev->open = ati_remote2_open;
idev->close = ati_remote2_close;
- idev->getkeycode_new = ati_remote2_getkeycode;
- idev->setkeycode_new = ati_remote2_setkeycode;
+ idev->getkeycode = ati_remote2_getkeycode;
+ idev->setkeycode = ati_remote2_setkeycode;
idev->name = ar2->name;
idev->phys = ar2->phys;
diff --git a/drivers/input/misc/uinput.c b/drivers/input/misc/uinput.c
index 82542a1c109..364bdf43a38 100644
--- a/drivers/input/misc/uinput.c
+++ b/drivers/input/misc/uinput.c
@@ -347,8 +347,7 @@ static int uinput_setup_device(struct uinput_device *udev, const char __user *bu
{
struct uinput_user_dev *user_dev;
struct input_dev *dev;
- char *name;
- int i, size;
+ int i;
int retval;
if (count != sizeof(struct uinput_user_dev))
@@ -362,30 +361,25 @@ static int uinput_setup_device(struct uinput_device *udev, const char __user *bu
dev = udev->dev;
- user_dev = kmalloc(sizeof(struct uinput_user_dev), GFP_KERNEL);
- if (!user_dev)
- return -ENOMEM;
-
- if (copy_from_user(user_dev, buffer, sizeof(struct uinput_user_dev))) {
- retval = -EFAULT;
- goto exit;
- }
+ user_dev = memdup_user(buffer, sizeof(struct uinput_user_dev));
+ if (IS_ERR(user_dev))
+ return PTR_ERR(user_dev);
udev->ff_effects_max = user_dev->ff_effects_max;
- size = strnlen(user_dev->name, UINPUT_MAX_NAME_SIZE) + 1;
- if (!size) {
+ /* Ensure name is filled in */
+ if (!user_dev->name[0]) {
retval = -EINVAL;
goto exit;
}
kfree(dev->name);
- dev->name = name = kmalloc(size, GFP_KERNEL);
- if (!name) {
+ dev->name = kstrndup(user_dev->name, UINPUT_MAX_NAME_SIZE,
+ GFP_KERNEL);
+ if (!dev->name) {
retval = -ENOMEM;
goto exit;
}
- strlcpy(name, user_dev->name, size);
dev->id.bustype = user_dev->id.bustype;
dev->id.vendor = user_dev->id.vendor;
@@ -622,7 +616,6 @@ static long uinput_ioctl_handler(struct file *file, unsigned int cmd,
struct uinput_ff_upload ff_up;
struct uinput_ff_erase ff_erase;
struct uinput_request *req;
- int length;
char *phys;
retval = mutex_lock_interruptible(&udev->mutex);
@@ -689,24 +682,15 @@ static long uinput_ioctl_handler(struct file *file, unsigned int cmd,
retval = -EINVAL;
goto out;
}
- length = strnlen_user(p, 1024);
- if (length <= 0) {
- retval = -EFAULT;
- break;
+
+ phys = strndup_user(p, 1024);
+ if (IS_ERR(phys)) {
+ retval = PTR_ERR(phys);
+ goto out;
}
+
kfree(udev->dev->phys);
- udev->dev->phys = phys = kmalloc(length, GFP_KERNEL);
- if (!phys) {
- retval = -ENOMEM;
- break;
- }
- if (copy_from_user(phys, p, length)) {
- udev->dev->phys = NULL;
- kfree(phys);
- retval = -EFAULT;
- break;
- }
- phys[length - 1] = '\0';
+ udev->dev->phys = phys;
break;
case UI_BEGIN_FF_UPLOAD:
diff --git a/drivers/input/xen-kbdfront.c b/drivers/input/misc/xen-kbdfront.c
index 7f85a862ad1..7077f9bf5ea 100644
--- a/drivers/input/xen-kbdfront.c
+++ b/drivers/input/misc/xen-kbdfront.c
@@ -11,12 +11,6 @@
* more details.
*/
-/*
- * TODO:
- *
- * Switch to grant tables together with xen-fbfront.c.
- */
-
#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
#include <linux/kernel.h>
@@ -30,6 +24,8 @@
#include <xen/xen.h>
#include <xen/events.h>
#include <xen/page.h>
+#include <xen/grant_table.h>
+#include <xen/interface/grant_table.h>
#include <xen/interface/io/fbif.h>
#include <xen/interface/io/kbdif.h>
#include <xen/xenbus.h>
@@ -38,6 +34,7 @@ struct xenkbd_info {
struct input_dev *kbd;
struct input_dev *ptr;
struct xenkbd_page *page;
+ int gref;
int irq;
struct xenbus_device *xbdev;
char phys[32];
@@ -110,7 +107,7 @@ static irqreturn_t input_handler(int rq, void *dev_id)
static int __devinit xenkbd_probe(struct xenbus_device *dev,
const struct xenbus_device_id *id)
{
- int ret, i;
+ int ret, i, abs;
struct xenkbd_info *info;
struct input_dev *kbd, *ptr;
@@ -122,12 +119,18 @@ static int __devinit xenkbd_probe(struct xenbus_device *dev,
dev_set_drvdata(&dev->dev, info);
info->xbdev = dev;
info->irq = -1;
+ info->gref = -1;
snprintf(info->phys, sizeof(info->phys), "xenbus/%s", dev->nodename);
info->page = (void *)__get_free_page(GFP_KERNEL | __GFP_ZERO);
if (!info->page)
goto error_nomem;
+ if (xenbus_scanf(XBT_NIL, dev->otherend, "feature-abs-pointer", "%d", &abs) < 0)
+ abs = 0;
+ if (abs)
+ xenbus_printf(XBT_NIL, dev->nodename, "request-abs-pointer", "1");
+
/* keyboard */
kbd = input_allocate_device();
if (!kbd)
@@ -137,11 +140,12 @@ static int __devinit xenkbd_probe(struct xenbus_device *dev,
kbd->id.bustype = BUS_PCI;
kbd->id.vendor = 0x5853;
kbd->id.product = 0xffff;
- kbd->evbit[0] = BIT(EV_KEY);
+
+ __set_bit(EV_KEY, kbd->evbit);
for (i = KEY_ESC; i < KEY_UNKNOWN; i++)
- set_bit(i, kbd->keybit);
+ __set_bit(i, kbd->keybit);
for (i = KEY_OK; i < KEY_MAX; i++)
- set_bit(i, kbd->keybit);
+ __set_bit(i, kbd->keybit);
ret = input_register_device(kbd);
if (ret) {
@@ -160,12 +164,20 @@ static int __devinit xenkbd_probe(struct xenbus_device *dev,
ptr->id.bustype = BUS_PCI;
ptr->id.vendor = 0x5853;
ptr->id.product = 0xfffe;
- ptr->evbit[0] = BIT(EV_KEY) | BIT(EV_REL) | BIT(EV_ABS);
+
+ if (abs) {
+ __set_bit(EV_ABS, ptr->evbit);
+ input_set_abs_params(ptr, ABS_X, 0, XENFB_WIDTH, 0, 0);
+ input_set_abs_params(ptr, ABS_Y, 0, XENFB_HEIGHT, 0, 0);
+ } else {
+ input_set_capability(ptr, EV_REL, REL_X);
+ input_set_capability(ptr, EV_REL, REL_Y);
+ }
+ input_set_capability(ptr, EV_REL, REL_WHEEL);
+
+ __set_bit(EV_KEY, ptr->evbit);
for (i = BTN_LEFT; i <= BTN_TASK; i++)
- set_bit(i, ptr->keybit);
- ptr->relbit[0] = BIT(REL_X) | BIT(REL_Y) | BIT(REL_WHEEL);
- input_set_abs_params(ptr, ABS_X, 0, XENFB_WIDTH, 0, 0);
- input_set_abs_params(ptr, ABS_Y, 0, XENFB_HEIGHT, 0, 0);
+ __set_bit(i, ptr->keybit);
ret = input_register_device(ptr);
if (ret) {
@@ -218,15 +230,20 @@ static int xenkbd_connect_backend(struct xenbus_device *dev,
int ret, evtchn;
struct xenbus_transaction xbt;
+ ret = gnttab_grant_foreign_access(dev->otherend_id,
+ virt_to_mfn(info->page), 0);
+ if (ret < 0)
+ return ret;
+ info->gref = ret;
+
ret = xenbus_alloc_evtchn(dev, &evtchn);
if (ret)
- return ret;
+ goto error_grant;
ret = bind_evtchn_to_irqhandler(evtchn, input_handler,
0, dev->devicetype, info);
if (ret < 0) {
- xenbus_free_evtchn(dev, evtchn);
xenbus_dev_fatal(dev, ret, "bind_evtchn_to_irqhandler");
- return ret;
+ goto error_evtchan;
}
info->irq = ret;
@@ -234,12 +251,15 @@ static int xenkbd_connect_backend(struct xenbus_device *dev,
ret = xenbus_transaction_start(&xbt);
if (ret) {
xenbus_dev_fatal(dev, ret, "starting transaction");
- return ret;
+ goto error_irqh;
}
ret = xenbus_printf(xbt, dev->nodename, "page-ref", "%lu",
virt_to_mfn(info->page));
if (ret)
goto error_xenbus;
+ ret = xenbus_printf(xbt, dev->nodename, "page-gref", "%u", info->gref);
+ if (ret)
+ goto error_xenbus;
ret = xenbus_printf(xbt, dev->nodename, "event-channel", "%u",
evtchn);
if (ret)
@@ -249,7 +269,7 @@ static int xenkbd_connect_backend(struct xenbus_device *dev,
if (ret == -EAGAIN)
goto again;
xenbus_dev_fatal(dev, ret, "completing transaction");
- return ret;
+ goto error_irqh;
}
xenbus_switch_state(dev, XenbusStateInitialised);
@@ -258,6 +278,14 @@ static int xenkbd_connect_backend(struct xenbus_device *dev,
error_xenbus:
xenbus_transaction_end(xbt, 1);
xenbus_dev_fatal(dev, ret, "writing xenstore");
+ error_irqh:
+ unbind_from_irqhandler(info->irq, info);
+ info->irq = -1;
+ error_evtchan:
+ xenbus_free_evtchn(dev, evtchn);
+ error_grant:
+ gnttab_end_foreign_access_ref(info->gref, 0);
+ info->gref = -1;
return ret;
}
@@ -266,13 +294,16 @@ static void xenkbd_disconnect_backend(struct xenkbd_info *info)
if (info->irq >= 0)
unbind_from_irqhandler(info->irq, info);
info->irq = -1;
+ if (info->gref >= 0)
+ gnttab_end_foreign_access_ref(info->gref, 0);
+ info->gref = -1;
}
static void xenkbd_backend_changed(struct xenbus_device *dev,
enum xenbus_state backend_state)
{
struct xenkbd_info *info = dev_get_drvdata(&dev->dev);
- int ret, val;
+ int val;
switch (backend_state) {
case XenbusStateInitialising:
@@ -285,16 +316,6 @@ static void xenkbd_backend_changed(struct xenbus_device *dev,
case XenbusStateInitWait:
InitWait:
- ret = xenbus_scanf(XBT_NIL, info->xbdev->otherend,
- "feature-abs-pointer", "%d", &val);
- if (ret < 0)
- val = 0;
- if (val) {
- ret = xenbus_printf(XBT_NIL, info->xbdev->nodename,
- "request-abs-pointer", "1");
- if (ret)
- pr_warning("can't request abs-pointer\n");
- }
xenbus_switch_state(dev, XenbusStateConnected);
break;
diff --git a/drivers/input/mouse/bcm5974.c b/drivers/input/mouse/bcm5974.c
index ee82851afe3..3aead91bacc 100644
--- a/drivers/input/mouse/bcm5974.c
+++ b/drivers/input/mouse/bcm5974.c
@@ -63,6 +63,10 @@
#define USB_DEVICE_ID_APPLE_WELLSPRING4A_ANSI 0x0242
#define USB_DEVICE_ID_APPLE_WELLSPRING4A_ISO 0x0243
#define USB_DEVICE_ID_APPLE_WELLSPRING4A_JIS 0x0244
+/* Macbook8 (unibody, March 2011) */
+#define USB_DEVICE_ID_APPLE_WELLSPRING5_ANSI 0x0245
+#define USB_DEVICE_ID_APPLE_WELLSPRING5_ISO 0x0246
+#define USB_DEVICE_ID_APPLE_WELLSPRING5_JIS 0x0247
#define BCM5974_DEVICE(prod) { \
.match_flags = (USB_DEVICE_ID_MATCH_DEVICE | \
@@ -96,6 +100,10 @@ static const struct usb_device_id bcm5974_table[] = {
BCM5974_DEVICE(USB_DEVICE_ID_APPLE_WELLSPRING4A_ANSI),
BCM5974_DEVICE(USB_DEVICE_ID_APPLE_WELLSPRING4A_ISO),
BCM5974_DEVICE(USB_DEVICE_ID_APPLE_WELLSPRING4A_JIS),
+ /* MacbookPro8 */
+ BCM5974_DEVICE(USB_DEVICE_ID_APPLE_WELLSPRING5_ANSI),
+ BCM5974_DEVICE(USB_DEVICE_ID_APPLE_WELLSPRING5_ISO),
+ BCM5974_DEVICE(USB_DEVICE_ID_APPLE_WELLSPRING5_JIS),
/* Terminating entry */
{}
};
@@ -274,6 +282,18 @@ static const struct bcm5974_config bcm5974_config_table[] = {
{ DIM_X, DIM_X / SN_COORD, -4616, 5112 },
{ DIM_Y, DIM_Y / SN_COORD, -142, 5234 }
},
+ {
+ USB_DEVICE_ID_APPLE_WELLSPRING5_ANSI,
+ USB_DEVICE_ID_APPLE_WELLSPRING5_ISO,
+ USB_DEVICE_ID_APPLE_WELLSPRING5_JIS,
+ HAS_INTEGRATED_BUTTON,
+ 0x84, sizeof(struct bt_data),
+ 0x81, TYPE2, FINGER_TYPE2, FINGER_TYPE2 + SIZEOF_ALL_FINGERS,
+ { DIM_PRESSURE, DIM_PRESSURE / SN_PRESSURE, 0, 300 },
+ { DIM_WIDTH, DIM_WIDTH / SN_WIDTH, 0, 2048 },
+ { DIM_X, DIM_X / SN_COORD, -4415, 5050 },
+ { DIM_Y, DIM_Y / SN_COORD, -55, 6680 }
+ },
{}
};
@@ -430,10 +450,6 @@ static int report_tp_state(struct bcm5974 *dev, int size)
ptest = int2bound(&c->p, raw_p);
origin = raw2int(f->origin);
- /* set the integrated button if applicable */
- if (c->tp_type == TYPE2)
- ibt = raw2int(dev->tp_data[BUTTON_TYPE2]);
-
/* while tracking finger still valid, count all fingers */
if (ptest > PRESSURE_LOW && origin) {
abs_p = ptest;
@@ -452,6 +468,10 @@ static int report_tp_state(struct bcm5974 *dev, int size)
}
}
+ /* set the integrated button if applicable */
+ if (c->tp_type == TYPE2)
+ ibt = raw2int(dev->tp_data[BUTTON_TYPE2]);
+
if (dev->fingers < nmin)
dev->fingers = nmin;
if (dev->fingers > nmax)
diff --git a/drivers/input/mouse/synaptics_i2c.c b/drivers/input/mouse/synaptics_i2c.c
index 0ae62f0bcb3..f6aa26d305e 100644
--- a/drivers/input/mouse/synaptics_i2c.c
+++ b/drivers/input/mouse/synaptics_i2c.c
@@ -18,6 +18,7 @@
#include <linux/delay.h>
#include <linux/workqueue.h>
#include <linux/slab.h>
+#include <linux/pm.h>
#define DRIVER_NAME "synaptics_i2c"
/* maximum product id is 15 characters */
@@ -619,8 +620,9 @@ static int __devexit synaptics_i2c_remove(struct i2c_client *client)
}
#ifdef CONFIG_PM
-static int synaptics_i2c_suspend(struct i2c_client *client, pm_message_t mesg)
+static int synaptics_i2c_suspend(struct device *dev)
{
+ struct i2c_client *client = to_i2c_client(dev);
struct synaptics_i2c *touch = i2c_get_clientdata(client);
cancel_delayed_work_sync(&touch->dwork);
@@ -631,9 +633,10 @@ static int synaptics_i2c_suspend(struct i2c_client *client, pm_message_t mesg)
return 0;
}
-static int synaptics_i2c_resume(struct i2c_client *client)
+static int synaptics_i2c_resume(struct device *dev)
{
int ret;
+ struct i2c_client *client = to_i2c_client(dev);
struct synaptics_i2c *touch = i2c_get_clientdata(client);
ret = synaptics_i2c_reset_config(client);
@@ -645,11 +648,11 @@ static int synaptics_i2c_resume(struct i2c_client *client)
return 0;
}
-#else
-#define synaptics_i2c_suspend NULL
-#define synaptics_i2c_resume NULL
#endif
+static SIMPLE_DEV_PM_OPS(synaptics_i2c_pm, synaptics_i2c_suspend,
+ synaptics_i2c_resume);
+
static const struct i2c_device_id synaptics_i2c_id_table[] = {
{ "synaptics_i2c", 0 },
{ },
@@ -660,13 +663,12 @@ static struct i2c_driver synaptics_i2c_driver = {
.driver = {
.name = DRIVER_NAME,
.owner = THIS_MODULE,
+ .pm = &synaptics_i2c_pm,
},
.probe = synaptics_i2c_probe,
.remove = __devexit_p(synaptics_i2c_remove),
- .suspend = synaptics_i2c_suspend,
- .resume = synaptics_i2c_resume,
.id_table = synaptics_i2c_id_table,
};
diff --git a/drivers/input/sparse-keymap.c b/drivers/input/sparse-keymap.c
index 7729e547ba6..337bf51bc98 100644
--- a/drivers/input/sparse-keymap.c
+++ b/drivers/input/sparse-keymap.c
@@ -210,8 +210,8 @@ int sparse_keymap_setup(struct input_dev *dev,
dev->keycode = map;
dev->keycodemax = map_size;
- dev->getkeycode_new = sparse_keymap_getkeycode;
- dev->setkeycode_new = sparse_keymap_setkeycode;
+ dev->getkeycode = sparse_keymap_getkeycode;
+ dev->setkeycode = sparse_keymap_setkeycode;
return 0;
diff --git a/drivers/input/tablet/wacom_sys.c b/drivers/input/tablet/wacom_sys.c
index cf8fb9f5d4a..449c0a46dba 100644
--- a/drivers/input/tablet/wacom_sys.c
+++ b/drivers/input/tablet/wacom_sys.c
@@ -193,16 +193,16 @@ static int wacom_parse_hid(struct usb_interface *intf, struct hid_descriptor *hi
case HID_USAGE_X:
if (usage == WCM_DESKTOP) {
if (finger) {
- features->device_type = BTN_TOOL_DOUBLETAP;
+ features->device_type = BTN_TOOL_FINGER;
if (features->type == TABLETPC2FG) {
/* need to reset back */
features->pktlen = WACOM_PKGLEN_TPC2FG;
- features->device_type = BTN_TOOL_TRIPLETAP;
+ features->device_type = BTN_TOOL_DOUBLETAP;
}
if (features->type == BAMBOO_PT) {
/* need to reset back */
features->pktlen = WACOM_PKGLEN_BBTOUCH;
- features->device_type = BTN_TOOL_TRIPLETAP;
+ features->device_type = BTN_TOOL_DOUBLETAP;
features->x_phy =
get_unaligned_le16(&report[i + 5]);
features->x_max =
@@ -241,11 +241,11 @@ static int wacom_parse_hid(struct usb_interface *intf, struct hid_descriptor *hi
case HID_USAGE_Y:
if (usage == WCM_DESKTOP) {
if (finger) {
- features->device_type = BTN_TOOL_DOUBLETAP;
+ features->device_type = BTN_TOOL_FINGER;
if (features->type == TABLETPC2FG) {
/* need to reset back */
features->pktlen = WACOM_PKGLEN_TPC2FG;
- features->device_type = BTN_TOOL_TRIPLETAP;
+ features->device_type = BTN_TOOL_DOUBLETAP;
features->y_max =
get_unaligned_le16(&report[i + 3]);
features->y_phy =
@@ -254,7 +254,7 @@ static int wacom_parse_hid(struct usb_interface *intf, struct hid_descriptor *hi
} else if (features->type == BAMBOO_PT) {
/* need to reset back */
features->pktlen = WACOM_PKGLEN_BBTOUCH;
- features->device_type = BTN_TOOL_TRIPLETAP;
+ features->device_type = BTN_TOOL_DOUBLETAP;
features->y_phy =
get_unaligned_le16(&report[i + 3]);
features->y_max =
diff --git a/drivers/input/tablet/wacom_wac.c b/drivers/input/tablet/wacom_wac.c
index 367fa82a607..5597637cfd4 100644
--- a/drivers/input/tablet/wacom_wac.c
+++ b/drivers/input/tablet/wacom_wac.c
@@ -675,169 +675,87 @@ static int wacom_intuos_irq(struct wacom_wac *wacom)
return 1;
}
-
-static void wacom_tpc_finger_in(struct wacom_wac *wacom, char *data, int idx)
+static int wacom_tpc_mt_touch(struct wacom_wac *wacom)
{
struct input_dev *input = wacom->input;
- int finger = idx + 1;
- int x = le16_to_cpup((__le16 *)&data[finger * 2]) & 0x7fff;
- int y = le16_to_cpup((__le16 *)&data[4 + finger * 2]) & 0x7fff;
+ unsigned char *data = wacom->data;
+ int contact_with_no_pen_down_count = 0;
+ int i;
- /*
- * Work around input core suppressing "duplicate" events since
- * we are abusing ABS_X/ABS_Y to transmit multi-finger data.
- * This should go away once we switch to true multitouch
- * protocol.
- */
- if (wacom->last_finger != finger) {
- if (x == input_abs_get_val(input, ABS_X))
- x++;
+ for (i = 0; i < 2; i++) {
+ int p = data[1] & (1 << i);
+ bool touch = p && !wacom->shared->stylus_in_proximity;
+
+ input_mt_slot(input, i);
+ input_mt_report_slot_state(input, MT_TOOL_FINGER, touch);
+ if (touch) {
+ int x = le16_to_cpup((__le16 *)&data[i * 2 + 2]) & 0x7fff;
+ int y = le16_to_cpup((__le16 *)&data[i * 2 + 6]) & 0x7fff;
- if (y == input_abs_get_val(input, ABS_Y))
- y++;
+ input_report_abs(input, ABS_MT_POSITION_X, x);
+ input_report_abs(input, ABS_MT_POSITION_Y, y);
+ contact_with_no_pen_down_count++;
+ }
}
- input_report_abs(input, ABS_X, x);
- input_report_abs(input, ABS_Y, y);
- input_report_abs(input, ABS_MISC, wacom->id[0]);
- input_report_key(input, wacom->tool[finger], 1);
- if (!idx)
- input_report_key(input, BTN_TOUCH, 1);
- input_event(input, EV_MSC, MSC_SERIAL, finger);
- input_sync(input);
+ /* keep touch state for pen event */
+ wacom->shared->touch_down = (contact_with_no_pen_down_count > 0);
- wacom->last_finger = finger;
-}
+ input_mt_report_pointer_emulation(input, true);
-static void wacom_tpc_touch_out(struct wacom_wac *wacom, int idx)
-{
- struct input_dev *input = wacom->input;
- int finger = idx + 1;
-
- input_report_abs(input, ABS_X, 0);
- input_report_abs(input, ABS_Y, 0);
- input_report_abs(input, ABS_MISC, 0);
- input_report_key(input, wacom->tool[finger], 0);
- if (!idx)
- input_report_key(input, BTN_TOUCH, 0);
- input_event(input, EV_MSC, MSC_SERIAL, finger);
- input_sync(input);
+ return 1;
}
-static void wacom_tpc_touch_in(struct wacom_wac *wacom, size_t len)
+static int wacom_tpc_single_touch(struct wacom_wac *wacom, size_t len)
{
char *data = wacom->data;
struct input_dev *input = wacom->input;
+ bool prox;
+ int x = 0, y = 0;
- wacom->tool[1] = BTN_TOOL_DOUBLETAP;
- wacom->id[0] = TOUCH_DEVICE_ID;
- wacom->tool[2] = BTN_TOOL_TRIPLETAP;
-
- if (len != WACOM_PKGLEN_TPC1FG) {
-
- switch (data[0]) {
+ if (!wacom->shared->stylus_in_proximity) {
+ if (len == WACOM_PKGLEN_TPC1FG) {
+ prox = data[0] & 0x01;
+ x = get_unaligned_le16(&data[1]);
+ y = get_unaligned_le16(&data[3]);
+ } else { /* with capacity */
+ prox = data[1] & 0x01;
+ x = le16_to_cpup((__le16 *)&data[2]);
+ y = le16_to_cpup((__le16 *)&data[4]);
+ }
+ } else
+ /* force touch out when pen is in prox */
+ prox = 0;
- case WACOM_REPORT_TPC1FG:
- input_report_abs(input, ABS_X, le16_to_cpup((__le16 *)&data[2]));
- input_report_abs(input, ABS_Y, le16_to_cpup((__le16 *)&data[4]));
- input_report_abs(input, ABS_PRESSURE, le16_to_cpup((__le16 *)&data[6]));
- input_report_key(input, BTN_TOUCH, le16_to_cpup((__le16 *)&data[6]));
- input_report_abs(input, ABS_MISC, wacom->id[0]);
- input_report_key(input, wacom->tool[1], 1);
- input_sync(input);
- break;
+ if (prox) {
+ input_report_abs(input, ABS_X, x);
+ input_report_abs(input, ABS_Y, y);
+ }
+ input_report_key(input, BTN_TOUCH, prox);
- case WACOM_REPORT_TPC2FG:
- if (data[1] & 0x01)
- wacom_tpc_finger_in(wacom, data, 0);
- else if (wacom->id[1] & 0x01)
- wacom_tpc_touch_out(wacom, 0);
+ /* keep touch state for pen events */
+ wacom->shared->touch_down = prox;
- if (data[1] & 0x02)
- wacom_tpc_finger_in(wacom, data, 1);
- else if (wacom->id[1] & 0x02)
- wacom_tpc_touch_out(wacom, 1);
- break;
- }
- } else {
- input_report_abs(input, ABS_X, get_unaligned_le16(&data[1]));
- input_report_abs(input, ABS_Y, get_unaligned_le16(&data[3]));
- input_report_key(input, BTN_TOUCH, 1);
- input_report_abs(input, ABS_MISC, wacom->id[1]);
- input_report_key(input, wacom->tool[1], 1);
- input_sync(input);
- }
+ return 1;
}
-static int wacom_tpc_irq(struct wacom_wac *wacom, size_t len)
+static int wacom_tpc_pen(struct wacom_wac *wacom)
{
struct wacom_features *features = &wacom->features;
char *data = wacom->data;
struct input_dev *input = wacom->input;
- int prox = 0, pressure;
- int retval = 0;
+ int pressure;
+ bool prox = data[1] & 0x20;
- dbg("wacom_tpc_irq: received report #%d", data[0]);
-
- if (len == WACOM_PKGLEN_TPC1FG || /* single touch */
- data[0] == WACOM_REPORT_TPC1FG || /* single touch */
- data[0] == WACOM_REPORT_TPC2FG) { /* 2FG touch */
-
- if (wacom->shared->stylus_in_proximity) {
- if (wacom->id[1] & 0x01)
- wacom_tpc_touch_out(wacom, 0);
-
- if (wacom->id[1] & 0x02)
- wacom_tpc_touch_out(wacom, 1);
-
- wacom->id[1] = 0;
- return 0;
- }
-
- if (len == WACOM_PKGLEN_TPC1FG) { /* with touch */
- prox = data[0] & 0x01;
- } else { /* with capacity */
- if (data[0] == WACOM_REPORT_TPC1FG)
- /* single touch */
- prox = data[1] & 0x01;
- else
- /* 2FG touch data */
- prox = data[1] & 0x03;
- }
-
- if (prox) {
- if (!wacom->id[1])
- wacom->last_finger = 1;
- wacom_tpc_touch_in(wacom, len);
- } else {
- if (data[0] == WACOM_REPORT_TPC2FG) {
- /* 2FGT out-prox */
- if (wacom->id[1] & 0x01)
- wacom_tpc_touch_out(wacom, 0);
+ if (!wacom->shared->stylus_in_proximity) /* first in prox */
+ /* Going into proximity select tool */
+ wacom->tool[0] = (data[1] & 0x0c) ? BTN_TOOL_RUBBER : BTN_TOOL_PEN;
- if (wacom->id[1] & 0x02)
- wacom_tpc_touch_out(wacom, 1);
- } else
- /* one finger touch */
- wacom_tpc_touch_out(wacom, 0);
+ /* keep pen state for touch events */
+ wacom->shared->stylus_in_proximity = prox;
- wacom->id[0] = 0;
- }
- /* keep prox bit to send proper out-prox event */
- wacom->id[1] = prox;
- } else if (data[0] == WACOM_REPORT_PENABLED) { /* Penabled */
- prox = data[1] & 0x20;
-
- if (!wacom->shared->stylus_in_proximity) { /* first in prox */
- /* Going into proximity select tool */
- wacom->tool[0] = (data[1] & 0x0c) ? BTN_TOOL_RUBBER : BTN_TOOL_PEN;
- if (wacom->tool[0] == BTN_TOOL_PEN)
- wacom->id[0] = STYLUS_DEVICE_ID;
- else
- wacom->id[0] = ERASER_DEVICE_ID;
-
- wacom->shared->stylus_in_proximity = true;
- }
+ /* send pen events only when touch is up or forced out */
+ if (!wacom->shared->touch_down) {
input_report_key(input, BTN_STYLUS, data[1] & 0x02);
input_report_key(input, BTN_STYLUS2, data[1] & 0x10);
input_report_abs(input, ABS_X, le16_to_cpup((__le16 *)&data[2]));
@@ -847,15 +765,27 @@ static int wacom_tpc_irq(struct wacom_wac *wacom, size_t len)
pressure = features->pressure_max + pressure + 1;
input_report_abs(input, ABS_PRESSURE, pressure);
input_report_key(input, BTN_TOUCH, data[1] & 0x05);
- if (!prox) { /* out-prox */
- wacom->id[0] = 0;
- wacom->shared->stylus_in_proximity = false;
- }
input_report_key(input, wacom->tool[0], prox);
- input_report_abs(input, ABS_MISC, wacom->id[0]);
- retval = 1;
+ return 1;
}
- return retval;
+
+ return 0;
+}
+
+static int wacom_tpc_irq(struct wacom_wac *wacom, size_t len)
+{
+ char *data = wacom->data;
+
+ dbg("wacom_tpc_irq: received report #%d", data[0]);
+
+ if (len == WACOM_PKGLEN_TPC1FG || data[0] == WACOM_REPORT_TPC1FG)
+ return wacom_tpc_single_touch(wacom, len);
+ else if (data[0] == WACOM_REPORT_TPC2FG)
+ return wacom_tpc_mt_touch(wacom);
+ else if (data[0] == WACOM_REPORT_PENABLED)
+ return wacom_tpc_pen(wacom);
+
+ return 0;
}
static int wacom_bpt_touch(struct wacom_wac *wacom)
@@ -1078,7 +1008,7 @@ void wacom_setup_device_quirks(struct wacom_features *features)
{
/* touch device found but size is not defined. use default */
- if (features->device_type == BTN_TOOL_DOUBLETAP && !features->x_max) {
+ if (features->device_type == BTN_TOOL_FINGER && !features->x_max) {
features->x_max = 1023;
features->y_max = 1023;
}
@@ -1090,7 +1020,7 @@ void wacom_setup_device_quirks(struct wacom_features *features)
/* quirks for bamboo touch */
if (features->type == BAMBOO_PT &&
- features->device_type == BTN_TOOL_TRIPLETAP) {
+ features->device_type == BTN_TOOL_DOUBLETAP) {
features->x_max <<= 5;
features->y_max <<= 5;
features->x_fuzz <<= 5;
@@ -1226,27 +1156,30 @@ void wacom_setup_input_capabilities(struct input_dev *input_dev,
break;
case TABLETPC2FG:
- if (features->device_type == BTN_TOOL_TRIPLETAP) {
- __set_bit(BTN_TOOL_TRIPLETAP, input_dev->keybit);
- input_set_capability(input_dev, EV_MSC, MSC_SERIAL);
+ if (features->device_type == BTN_TOOL_DOUBLETAP) {
+
+ input_mt_init_slots(input_dev, 2);
+ input_set_abs_params(input_dev, ABS_MT_TOOL_TYPE,
+ 0, MT_TOOL_MAX, 0, 0);
+ input_set_abs_params(input_dev, ABS_MT_POSITION_X,
+ 0, features->x_max, 0, 0);
+ input_set_abs_params(input_dev, ABS_MT_POSITION_Y,
+ 0, features->y_max, 0, 0);
}
/* fall through */
case TABLETPC:
- if (features->device_type == BTN_TOOL_DOUBLETAP ||
- features->device_type == BTN_TOOL_TRIPLETAP) {
+ __clear_bit(ABS_MISC, input_dev->absbit);
+
+ if (features->device_type != BTN_TOOL_PEN) {
input_abs_set_res(input_dev, ABS_X,
wacom_calculate_touch_res(features->x_max,
features->x_phy));
input_abs_set_res(input_dev, ABS_Y,
wacom_calculate_touch_res(features->y_max,
features->y_phy));
- __set_bit(BTN_TOOL_DOUBLETAP, input_dev->keybit);
- }
-
- if (features->device_type != BTN_TOOL_PEN)
break; /* no need to process stylus stuff */
-
+ }
/* fall through */
case PL:
@@ -1264,7 +1197,7 @@ void wacom_setup_input_capabilities(struct input_dev *input_dev,
case BAMBOO_PT:
__clear_bit(ABS_MISC, input_dev->absbit);
- if (features->device_type == BTN_TOOL_TRIPLETAP) {
+ if (features->device_type == BTN_TOOL_DOUBLETAP) {
__set_bit(BTN_LEFT, input_dev->keybit);
__set_bit(BTN_FORWARD, input_dev->keybit);
__set_bit(BTN_BACK, input_dev->keybit);
diff --git a/drivers/input/tablet/wacom_wac.h b/drivers/input/tablet/wacom_wac.h
index b1310ec9720..835f756b150 100644
--- a/drivers/input/tablet/wacom_wac.h
+++ b/drivers/input/tablet/wacom_wac.h
@@ -88,15 +88,15 @@ struct wacom_features {
struct wacom_shared {
bool stylus_in_proximity;
+ bool touch_down;
};
struct wacom_wac {
char name[64];
unsigned char *data;
- int tool[3];
- int id[3];
+ int tool[2];
+ int id[2];
__u32 serial[2];
- int last_finger;
struct wacom_features features;
struct wacom_shared *shared;
struct input_dev *input;
diff --git a/drivers/input/touchscreen/Kconfig b/drivers/input/touchscreen/Kconfig
index 61834ae282e..112ec55f293 100644
--- a/drivers/input/touchscreen/Kconfig
+++ b/drivers/input/touchscreen/Kconfig
@@ -86,6 +86,18 @@ config TOUCHSCREEN_AD7879_SPI
To compile this driver as a module, choose M here: the
module will be called ad7879-spi.
+config TOUCHSCREEN_ATMEL_MXT
+ tristate "Atmel mXT I2C Touchscreen"
+ depends on I2C
+ help
+ Say Y here if you have Atmel mXT series I2C touchscreen,
+ such as AT42QT602240/ATMXT224, connected to your system.
+
+ If unsure, say N.
+
+ To compile this driver as a module, choose M here: the
+ module will be called atmel_mxt_ts.
+
config TOUCHSCREEN_BITSY
tristate "Compaq iPAQ H3600 (Bitsy) touchscreen"
depends on SA1100_BITSY
@@ -339,18 +351,6 @@ config TOUCHSCREEN_PENMOUNT
To compile this driver as a module, choose M here: the
module will be called penmount.
-config TOUCHSCREEN_QT602240
- tristate "QT602240 I2C Touchscreen"
- depends on I2C
- help
- Say Y here if you have the AT42QT602240/ATMXT224 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 qt602240_ts.
-
config TOUCHSCREEN_MIGOR
tristate "Renesas MIGO-R touchscreen"
depends on SH_MIGOR && I2C
@@ -423,6 +423,16 @@ config TOUCHSCREEN_UCB1400
To compile this driver as a module, choose M here: the
module will be called ucb1400_ts.
+config TOUCHSCREEN_WM831X
+ tristate "Support for WM831x touchscreen controllers"
+ depends on MFD_WM831X
+ help
+ This enables support for the touchscreen controller on the WM831x
+ series of PMICs.
+
+ To compile this driver as a module, choose M here: the
+ module will be called wm831x-ts.
+
config TOUCHSCREEN_WM97XX
tristate "Support for WM97xx AC97 touchscreen controllers"
depends on AC97_BUS
@@ -629,6 +639,17 @@ config TOUCHSCREEN_TOUCHIT213
To compile this driver as a module, choose M here: the
module will be called touchit213.
+config TOUCHSCREEN_TSC2005
+ tristate "TSC2005 based touchscreens"
+ depends on SPI_MASTER
+ help
+ Say Y here if you have a TSC2005 based touchscreen.
+
+ If unsure, say N.
+
+ To compile this driver as a module, choose M here: the
+ module will be called tsc2005.
+
config TOUCHSCREEN_TSC2007
tristate "TSC2007 based touchscreens"
depends on I2C
diff --git a/drivers/input/touchscreen/Makefile b/drivers/input/touchscreen/Makefile
index 718bcc81495..ca94098d4c9 100644
--- a/drivers/input/touchscreen/Makefile
+++ b/drivers/input/touchscreen/Makefile
@@ -12,6 +12,7 @@ obj-$(CONFIG_TOUCHSCREEN_AD7879) += ad7879.o
obj-$(CONFIG_TOUCHSCREEN_AD7879_I2C) += ad7879-i2c.o
obj-$(CONFIG_TOUCHSCREEN_AD7879_SPI) += ad7879-spi.o
obj-$(CONFIG_TOUCHSCREEN_ADS7846) += ads7846.o
+obj-$(CONFIG_TOUCHSCREEN_ATMEL_MXT) += atmel_mxt_ts.o
obj-$(CONFIG_TOUCHSCREEN_ATMEL_TSADCC) += atmel_tsadcc.o
obj-$(CONFIG_TOUCHSCREEN_BITSY) += h3600_ts_input.o
obj-$(CONFIG_TOUCHSCREEN_BU21013) += bu21013_ts.o
@@ -37,7 +38,6 @@ obj-$(CONFIG_TOUCHSCREEN_HTCPEN) += htcpen.o
obj-$(CONFIG_TOUCHSCREEN_USB_COMPOSITE) += usbtouchscreen.o
obj-$(CONFIG_TOUCHSCREEN_PCAP) += pcap_ts.o
obj-$(CONFIG_TOUCHSCREEN_PENMOUNT) += penmount.o
-obj-$(CONFIG_TOUCHSCREEN_QT602240) += qt602240_ts.o
obj-$(CONFIG_TOUCHSCREEN_S3C2410) += s3c2410_ts.o
obj-$(CONFIG_TOUCHSCREEN_ST1232) += st1232.o
obj-$(CONFIG_TOUCHSCREEN_STMPE) += stmpe-ts.o
@@ -45,9 +45,11 @@ obj-$(CONFIG_TOUCHSCREEN_TNETV107X) += tnetv107x-ts.o
obj-$(CONFIG_TOUCHSCREEN_TOUCHIT213) += touchit213.o
obj-$(CONFIG_TOUCHSCREEN_TOUCHRIGHT) += touchright.o
obj-$(CONFIG_TOUCHSCREEN_TOUCHWIN) += touchwin.o
+obj-$(CONFIG_TOUCHSCREEN_TSC2005) += tsc2005.o
obj-$(CONFIG_TOUCHSCREEN_TSC2007) += tsc2007.o
obj-$(CONFIG_TOUCHSCREEN_UCB1400) += ucb1400_ts.o
obj-$(CONFIG_TOUCHSCREEN_WACOM_W8001) += wacom_w8001.o
+obj-$(CONFIG_TOUCHSCREEN_WM831X) += wm831x-ts.o
obj-$(CONFIG_TOUCHSCREEN_WM97XX) += wm97xx-ts.o
wm97xx-ts-$(CONFIG_TOUCHSCREEN_WM9705) += wm9705.o
wm97xx-ts-$(CONFIG_TOUCHSCREEN_WM9712) += wm9712.o
diff --git a/drivers/input/touchscreen/ad7877.c b/drivers/input/touchscreen/ad7877.c
index a1952fcc083..714d4e0f9f9 100644
--- a/drivers/input/touchscreen/ad7877.c
+++ b/drivers/input/touchscreen/ad7877.c
@@ -41,6 +41,7 @@
#include <linux/delay.h>
#include <linux/input.h>
#include <linux/interrupt.h>
+#include <linux/pm.h>
#include <linux/slab.h>
#include <linux/spi/spi.h>
#include <linux/spi/ad7877.h>
@@ -826,39 +827,37 @@ static int __devexit ad7877_remove(struct spi_device *spi)
return 0;
}
-#ifdef CONFIG_PM
-static int ad7877_suspend(struct spi_device *spi, pm_message_t message)
+#ifdef CONFIG_PM_SLEEP
+static int ad7877_suspend(struct device *dev)
{
- struct ad7877 *ts = dev_get_drvdata(&spi->dev);
+ struct ad7877 *ts = dev_get_drvdata(dev);
ad7877_disable(ts);
return 0;
}
-static int ad7877_resume(struct spi_device *spi)
+static int ad7877_resume(struct device *dev)
{
- struct ad7877 *ts = dev_get_drvdata(&spi->dev);
+ struct ad7877 *ts = dev_get_drvdata(dev);
ad7877_enable(ts);
return 0;
}
-#else
-#define ad7877_suspend NULL
-#define ad7877_resume NULL
#endif
+static SIMPLE_DEV_PM_OPS(ad7877_pm, ad7877_suspend, ad7877_resume);
+
static struct spi_driver ad7877_driver = {
.driver = {
.name = "ad7877",
.bus = &spi_bus_type,
.owner = THIS_MODULE,
+ .pm = &ad7877_pm,
},
.probe = ad7877_probe,
.remove = __devexit_p(ad7877_remove),
- .suspend = ad7877_suspend,
- .resume = ad7877_resume,
};
static int __init ad7877_init(void)
diff --git a/drivers/input/touchscreen/ad7879-spi.c b/drivers/input/touchscreen/ad7879-spi.c
index 59c6e68c432..ddf732f3caf 100644
--- a/drivers/input/touchscreen/ad7879-spi.c
+++ b/drivers/input/touchscreen/ad7879-spi.c
@@ -7,6 +7,7 @@
*/
#include <linux/input.h> /* BUS_SPI */
+#include <linux/pm.h>
#include <linux/spi/spi.h>
#include "ad7879.h"
@@ -20,9 +21,10 @@
#define AD7879_WRITECMD(reg) (AD7879_CMD(reg))
#define AD7879_READCMD(reg) (AD7879_CMD(reg) | AD7879_CMD_READ)
-#ifdef CONFIG_PM
-static int ad7879_spi_suspend(struct spi_device *spi, pm_message_t message)
+#ifdef CONFIG_PM_SLEEP
+static int ad7879_spi_suspend(struct device *dev)
{
+ struct spi_device *spi = to_spi_device(dev);
struct ad7879 *ts = spi_get_drvdata(spi);
ad7879_suspend(ts);
@@ -30,19 +32,19 @@ static int ad7879_spi_suspend(struct spi_device *spi, pm_message_t message)
return 0;
}
-static int ad7879_spi_resume(struct spi_device *spi)
+static int ad7879_spi_resume(struct device *dev)
{
+ struct spi_device *spi = to_spi_device(dev);
struct ad7879 *ts = spi_get_drvdata(spi);
ad7879_resume(ts);
return 0;
}
-#else
-# define ad7879_spi_suspend NULL
-# define ad7879_spi_resume NULL
#endif
+static SIMPLE_DEV_PM_OPS(ad7879_spi_pm, ad7879_spi_suspend, ad7879_spi_resume);
+
/*
* ad7879_read/write are only used for initial setup and for sysfs controls.
* The main traffic is done in ad7879_collect().
@@ -173,11 +175,10 @@ static struct spi_driver ad7879_spi_driver = {
.name = "ad7879",
.bus = &spi_bus_type,
.owner = THIS_MODULE,
+ .pm = &ad7879_spi_pm,
},
.probe = ad7879_spi_probe,
.remove = __devexit_p(ad7879_spi_remove),
- .suspend = ad7879_spi_suspend,
- .resume = ad7879_spi_resume,
};
static int __init ad7879_spi_init(void)
diff --git a/drivers/input/touchscreen/ads7846.c b/drivers/input/touchscreen/ads7846.c
index 4bf2316e328..c24946f5125 100644
--- a/drivers/input/touchscreen/ads7846.c
+++ b/drivers/input/touchscreen/ads7846.c
@@ -26,6 +26,7 @@
#include <linux/input.h>
#include <linux/interrupt.h>
#include <linux/slab.h>
+#include <linux/pm.h>
#include <linux/gpio.h>
#include <linux/spi/spi.h>
#include <linux/spi/ads7846.h>
@@ -892,9 +893,10 @@ static irqreturn_t ads7846_irq(int irq, void *handle)
return IRQ_HANDLED;
}
-static int ads7846_suspend(struct spi_device *spi, pm_message_t message)
+#ifdef CONFIG_PM_SLEEP
+static int ads7846_suspend(struct device *dev)
{
- struct ads7846 *ts = dev_get_drvdata(&spi->dev);
+ struct ads7846 *ts = dev_get_drvdata(dev);
mutex_lock(&ts->lock);
@@ -914,9 +916,9 @@ static int ads7846_suspend(struct spi_device *spi, pm_message_t message)
return 0;
}
-static int ads7846_resume(struct spi_device *spi)
+static int ads7846_resume(struct device *dev)
{
- struct ads7846 *ts = dev_get_drvdata(&spi->dev);
+ struct ads7846 *ts = dev_get_drvdata(dev);
mutex_lock(&ts->lock);
@@ -935,6 +937,9 @@ static int ads7846_resume(struct spi_device *spi)
return 0;
}
+#endif
+
+static SIMPLE_DEV_PM_OPS(ads7846_pm, ads7846_suspend, ads7846_resume);
static int __devinit ads7846_setup_pendown(struct spi_device *spi, struct ads7846 *ts)
{
@@ -1408,11 +1413,10 @@ static struct spi_driver ads7846_driver = {
.name = "ads7846",
.bus = &spi_bus_type,
.owner = THIS_MODULE,
+ .pm = &ads7846_pm,
},
.probe = ads7846_probe,
.remove = __devexit_p(ads7846_remove),
- .suspend = ads7846_suspend,
- .resume = ads7846_resume,
};
static int __init ads7846_init(void)
diff --git a/drivers/input/touchscreen/atmel_mxt_ts.c b/drivers/input/touchscreen/atmel_mxt_ts.c
new file mode 100644
index 00000000000..4012436633b
--- /dev/null
+++ b/drivers/input/touchscreen/atmel_mxt_ts.c
@@ -0,0 +1,1211 @@
+/*
+ * Atmel maXTouch Touchscreen driver
+ *
+ * Copyright (C) 2010 Samsung Electronics Co.Ltd
+ * Author: Joonyoung Shim <jy0922.shim@samsung.com>
+ *
+ * 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; either version 2 of the License, or (at your
+ * option) any later version.
+ *
+ */
+
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/delay.h>
+#include <linux/firmware.h>
+#include <linux/i2c.h>
+#include <linux/i2c/atmel_mxt_ts.h>
+#include <linux/input.h>
+#include <linux/interrupt.h>
+#include <linux/slab.h>
+
+/* Version */
+#define MXT_VER_20 20
+#define MXT_VER_21 21
+#define MXT_VER_22 22
+
+/* Slave addresses */
+#define MXT_APP_LOW 0x4a
+#define MXT_APP_HIGH 0x4b
+#define MXT_BOOT_LOW 0x24
+#define MXT_BOOT_HIGH 0x25
+
+/* Firmware */
+#define MXT_FW_NAME "maxtouch.fw"
+
+/* Registers */
+#define MXT_FAMILY_ID 0x00
+#define MXT_VARIANT_ID 0x01
+#define MXT_VERSION 0x02
+#define MXT_BUILD 0x03
+#define MXT_MATRIX_X_SIZE 0x04
+#define MXT_MATRIX_Y_SIZE 0x05
+#define MXT_OBJECT_NUM 0x06
+#define MXT_OBJECT_START 0x07
+
+#define MXT_OBJECT_SIZE 6
+
+/* Object types */
+#define MXT_DEBUG_DIAGNOSTIC 37
+#define MXT_GEN_MESSAGE 5
+#define MXT_GEN_COMMAND 6
+#define MXT_GEN_POWER 7
+#define MXT_GEN_ACQUIRE 8
+#define MXT_TOUCH_MULTI 9
+#define MXT_TOUCH_KEYARRAY 15
+#define MXT_TOUCH_PROXIMITY 23
+#define MXT_PROCI_GRIPFACE 20
+#define MXT_PROCG_NOISE 22
+#define MXT_PROCI_ONETOUCH 24
+#define MXT_PROCI_TWOTOUCH 27
+#define MXT_PROCI_GRIP 40
+#define MXT_PROCI_PALM 41
+#define MXT_SPT_COMMSCONFIG 18
+#define MXT_SPT_GPIOPWM 19
+#define MXT_SPT_SELFTEST 25
+#define MXT_SPT_CTECONFIG 28
+#define MXT_SPT_USERDATA 38
+#define MXT_SPT_DIGITIZER 43
+#define MXT_SPT_MESSAGECOUNT 44
+
+/* MXT_GEN_COMMAND field */
+#define MXT_COMMAND_RESET 0
+#define MXT_COMMAND_BACKUPNV 1
+#define MXT_COMMAND_CALIBRATE 2
+#define MXT_COMMAND_REPORTALL 3
+#define MXT_COMMAND_DIAGNOSTIC 5
+
+/* MXT_GEN_POWER field */
+#define MXT_POWER_IDLEACQINT 0
+#define MXT_POWER_ACTVACQINT 1
+#define MXT_POWER_ACTV2IDLETO 2
+
+/* MXT_GEN_ACQUIRE field */
+#define MXT_ACQUIRE_CHRGTIME 0
+#define MXT_ACQUIRE_TCHDRIFT 2
+#define MXT_ACQUIRE_DRIFTST 3
+#define MXT_ACQUIRE_TCHAUTOCAL 4
+#define MXT_ACQUIRE_SYNC 5
+#define MXT_ACQUIRE_ATCHCALST 6
+#define MXT_ACQUIRE_ATCHCALSTHR 7
+
+/* MXT_TOUCH_MULTI field */
+#define MXT_TOUCH_CTRL 0
+#define MXT_TOUCH_XORIGIN 1
+#define MXT_TOUCH_YORIGIN 2
+#define MXT_TOUCH_XSIZE 3
+#define MXT_TOUCH_YSIZE 4
+#define MXT_TOUCH_BLEN 6
+#define MXT_TOUCH_TCHTHR 7
+#define MXT_TOUCH_TCHDI 8
+#define MXT_TOUCH_ORIENT 9
+#define MXT_TOUCH_MOVHYSTI 11
+#define MXT_TOUCH_MOVHYSTN 12
+#define MXT_TOUCH_NUMTOUCH 14
+#define MXT_TOUCH_MRGHYST 15
+#define MXT_TOUCH_MRGTHR 16
+#define MXT_TOUCH_AMPHYST 17
+#define MXT_TOUCH_XRANGE_LSB 18
+#define MXT_TOUCH_XRANGE_MSB 19
+#define MXT_TOUCH_YRANGE_LSB 20
+#define MXT_TOUCH_YRANGE_MSB 21
+#define MXT_TOUCH_XLOCLIP 22
+#define MXT_TOUCH_XHICLIP 23
+#define MXT_TOUCH_YLOCLIP 24
+#define MXT_TOUCH_YHICLIP 25
+#define MXT_TOUCH_XEDGECTRL 26
+#define MXT_TOUCH_XEDGEDIST 27
+#define MXT_TOUCH_YEDGECTRL 28
+#define MXT_TOUCH_YEDGEDIST 29
+#define MXT_TOUCH_JUMPLIMIT 30
+
+/* MXT_PROCI_GRIPFACE field */
+#define MXT_GRIPFACE_CTRL 0
+#define MXT_GRIPFACE_XLOGRIP 1
+#define MXT_GRIPFACE_XHIGRIP 2
+#define MXT_GRIPFACE_YLOGRIP 3
+#define MXT_GRIPFACE_YHIGRIP 4
+#define MXT_GRIPFACE_MAXTCHS 5
+#define MXT_GRIPFACE_SZTHR1 7
+#define MXT_GRIPFACE_SZTHR2 8
+#define MXT_GRIPFACE_SHPTHR1 9
+#define MXT_GRIPFACE_SHPTHR2 10
+#define MXT_GRIPFACE_SUPEXTTO 11
+
+/* MXT_PROCI_NOISE field */
+#define MXT_NOISE_CTRL 0
+#define MXT_NOISE_OUTFLEN 1
+#define MXT_NOISE_GCAFUL_LSB 3
+#define MXT_NOISE_GCAFUL_MSB 4
+#define MXT_NOISE_GCAFLL_LSB 5
+#define MXT_NOISE_GCAFLL_MSB 6
+#define MXT_NOISE_ACTVGCAFVALID 7
+#define MXT_NOISE_NOISETHR 8
+#define MXT_NOISE_FREQHOPSCALE 10
+#define MXT_NOISE_FREQ0 11
+#define MXT_NOISE_FREQ1 12
+#define MXT_NOISE_FREQ2 13
+#define MXT_NOISE_FREQ3 14
+#define MXT_NOISE_FREQ4 15
+#define MXT_NOISE_IDLEGCAFVALID 16
+
+/* MXT_SPT_COMMSCONFIG */
+#define MXT_COMMS_CTRL 0
+#define MXT_COMMS_CMD 1
+
+/* MXT_SPT_CTECONFIG field */
+#define MXT_CTE_CTRL 0
+#define MXT_CTE_CMD 1
+#define MXT_CTE_MODE 2
+#define MXT_CTE_IDLEGCAFDEPTH 3
+#define MXT_CTE_ACTVGCAFDEPTH 4
+#define MXT_CTE_VOLTAGE 5
+
+#define MXT_VOLTAGE_DEFAULT 2700000
+#define MXT_VOLTAGE_STEP 10000
+
+/* Define for MXT_GEN_COMMAND */
+#define MXT_BOOT_VALUE 0xa5
+#define MXT_BACKUP_VALUE 0x55
+#define MXT_BACKUP_TIME 25 /* msec */
+#define MXT_RESET_TIME 65 /* msec */
+
+#define MXT_FWRESET_TIME 175 /* msec */
+
+/* Command to unlock bootloader */
+#define MXT_UNLOCK_CMD_MSB 0xaa
+#define MXT_UNLOCK_CMD_LSB 0xdc
+
+/* Bootloader mode status */
+#define MXT_WAITING_BOOTLOAD_CMD 0xc0 /* valid 7 6 bit only */
+#define MXT_WAITING_FRAME_DATA 0x80 /* valid 7 6 bit only */
+#define MXT_FRAME_CRC_CHECK 0x02
+#define MXT_FRAME_CRC_FAIL 0x03
+#define MXT_FRAME_CRC_PASS 0x04
+#define MXT_APP_CRC_FAIL 0x40 /* valid 7 8 bit only */
+#define MXT_BOOT_STATUS_MASK 0x3f
+
+/* Touch status */
+#define MXT_SUPPRESS (1 << 1)
+#define MXT_AMP (1 << 2)
+#define MXT_VECTOR (1 << 3)
+#define MXT_MOVE (1 << 4)
+#define MXT_RELEASE (1 << 5)
+#define MXT_PRESS (1 << 6)
+#define MXT_DETECT (1 << 7)
+
+/* Touchscreen absolute values */
+#define MXT_MAX_XC 0x3ff
+#define MXT_MAX_YC 0x3ff
+#define MXT_MAX_AREA 0xff
+
+#define MXT_MAX_FINGER 10
+
+struct mxt_info {
+ u8 family_id;
+ u8 variant_id;
+ u8 version;
+ u8 build;
+ u8 matrix_xsize;
+ u8 matrix_ysize;
+ u8 object_num;
+};
+
+struct mxt_object {
+ u8 type;
+ u16 start_address;
+ u8 size;
+ u8 instances;
+ u8 num_report_ids;
+
+ /* to map object and message */
+ u8 max_reportid;
+};
+
+struct mxt_message {
+ u8 reportid;
+ u8 message[7];
+ u8 checksum;
+};
+
+struct mxt_finger {
+ int status;
+ int x;
+ int y;
+ int area;
+};
+
+/* Each client has this additional data */
+struct mxt_data {
+ struct i2c_client *client;
+ struct input_dev *input_dev;
+ const struct mxt_platform_data *pdata;
+ struct mxt_object *object_table;
+ struct mxt_info info;
+ struct mxt_finger finger[MXT_MAX_FINGER];
+ unsigned int irq;
+};
+
+static bool mxt_object_readable(unsigned int type)
+{
+ switch (type) {
+ case MXT_GEN_MESSAGE:
+ case MXT_GEN_COMMAND:
+ case MXT_GEN_POWER:
+ case MXT_GEN_ACQUIRE:
+ case MXT_TOUCH_MULTI:
+ case MXT_TOUCH_KEYARRAY:
+ case MXT_TOUCH_PROXIMITY:
+ case MXT_PROCI_GRIPFACE:
+ case MXT_PROCG_NOISE:
+ case MXT_PROCI_ONETOUCH:
+ case MXT_PROCI_TWOTOUCH:
+ case MXT_PROCI_GRIP:
+ case MXT_PROCI_PALM:
+ case MXT_SPT_COMMSCONFIG:
+ case MXT_SPT_GPIOPWM:
+ case MXT_SPT_SELFTEST:
+ case MXT_SPT_CTECONFIG:
+ case MXT_SPT_USERDATA:
+ return true;
+ default:
+ return false;
+ }
+}
+
+static bool mxt_object_writable(unsigned int type)
+{
+ switch (type) {
+ case MXT_GEN_COMMAND:
+ case MXT_GEN_POWER:
+ case MXT_GEN_ACQUIRE:
+ case MXT_TOUCH_MULTI:
+ case MXT_TOUCH_KEYARRAY:
+ case MXT_TOUCH_PROXIMITY:
+ case MXT_PROCI_GRIPFACE:
+ case MXT_PROCG_NOISE:
+ case MXT_PROCI_ONETOUCH:
+ case MXT_PROCI_TWOTOUCH:
+ case MXT_PROCI_GRIP:
+ case MXT_PROCI_PALM:
+ case MXT_SPT_GPIOPWM:
+ case MXT_SPT_SELFTEST:
+ case MXT_SPT_CTECONFIG:
+ return true;
+ default:
+ return false;
+ }
+}
+
+static void mxt_dump_message(struct device *dev,
+ struct mxt_message *message)
+{
+ dev_dbg(dev, "reportid:\t0x%x\n", message->reportid);
+ dev_dbg(dev, "message1:\t0x%x\n", message->message[0]);
+ dev_dbg(dev, "message2:\t0x%x\n", message->message[1]);
+ dev_dbg(dev, "message3:\t0x%x\n", message->message[2]);
+ dev_dbg(dev, "message4:\t0x%x\n", message->message[3]);
+ dev_dbg(dev, "message5:\t0x%x\n", message->message[4]);
+ dev_dbg(dev, "message6:\t0x%x\n", message->message[5]);
+ dev_dbg(dev, "message7:\t0x%x\n", message->message[6]);
+ dev_dbg(dev, "checksum:\t0x%x\n", message->checksum);
+}
+
+static int mxt_check_bootloader(struct i2c_client *client,
+ unsigned int state)
+{
+ u8 val;
+
+recheck:
+ if (i2c_master_recv(client, &val, 1) != 1) {
+ dev_err(&client->dev, "%s: i2c recv failed\n", __func__);
+ return -EIO;
+ }
+
+ switch (state) {
+ case MXT_WAITING_BOOTLOAD_CMD:
+ case MXT_WAITING_FRAME_DATA:
+ val &= ~MXT_BOOT_STATUS_MASK;
+ break;
+ case MXT_FRAME_CRC_PASS:
+ if (val == MXT_FRAME_CRC_CHECK)
+ goto recheck;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ if (val != state) {
+ dev_err(&client->dev, "Unvalid bootloader mode state\n");
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+static int mxt_unlock_bootloader(struct i2c_client *client)
+{
+ u8 buf[2];
+
+ buf[0] = MXT_UNLOCK_CMD_LSB;
+ buf[1] = MXT_UNLOCK_CMD_MSB;
+
+ if (i2c_master_send(client, buf, 2) != 2) {
+ dev_err(&client->dev, "%s: i2c send failed\n", __func__);
+ return -EIO;
+ }
+
+ return 0;
+}
+
+static int mxt_fw_write(struct i2c_client *client,
+ const u8 *data, unsigned int frame_size)
+{
+ if (i2c_master_send(client, data, frame_size) != frame_size) {
+ dev_err(&client->dev, "%s: i2c send failed\n", __func__);
+ return -EIO;
+ }
+
+ return 0;
+}
+
+static int __mxt_read_reg(struct i2c_client *client,
+ u16 reg, u16 len, void *val)
+{
+ struct i2c_msg xfer[2];
+ u8 buf[2];
+
+ buf[0] = reg & 0xff;
+ buf[1] = (reg >> 8) & 0xff;
+
+ /* Write register */
+ xfer[0].addr = client->addr;
+ xfer[0].flags = 0;
+ xfer[0].len = 2;
+ xfer[0].buf = buf;
+
+ /* Read data */
+ xfer[1].addr = client->addr;
+ xfer[1].flags = I2C_M_RD;
+ xfer[1].len = len;
+ xfer[1].buf = val;
+
+ if (i2c_transfer(client->adapter, xfer, 2) != 2) {
+ dev_err(&client->dev, "%s: i2c transfer failed\n", __func__);
+ return -EIO;
+ }
+
+ return 0;
+}
+
+static int mxt_read_reg(struct i2c_client *client, u16 reg, u8 *val)
+{
+ return __mxt_read_reg(client, reg, 1, val);
+}
+
+static int mxt_write_reg(struct i2c_client *client, u16 reg, u8 val)
+{
+ u8 buf[3];
+
+ buf[0] = reg & 0xff;
+ buf[1] = (reg >> 8) & 0xff;
+ buf[2] = val;
+
+ if (i2c_master_send(client, buf, 3) != 3) {
+ dev_err(&client->dev, "%s: i2c send failed\n", __func__);
+ return -EIO;
+ }
+
+ return 0;
+}
+
+static int mxt_read_object_table(struct i2c_client *client,
+ u16 reg, u8 *object_buf)
+{
+ return __mxt_read_reg(client, reg, MXT_OBJECT_SIZE,
+ object_buf);
+}
+
+static struct mxt_object *
+mxt_get_object(struct mxt_data *data, u8 type)
+{
+ struct mxt_object *object;
+ int i;
+
+ for (i = 0; i < data->info.object_num; i++) {
+ object = data->object_table + i;
+ if (object->type == type)
+ return object;
+ }
+
+ dev_err(&data->client->dev, "Invalid object type\n");
+ return NULL;
+}
+
+static int mxt_read_message(struct mxt_data *data,
+ struct mxt_message *message)
+{
+ struct mxt_object *object;
+ u16 reg;
+
+ object = mxt_get_object(data, MXT_GEN_MESSAGE);
+ if (!object)
+ return -EINVAL;
+
+ reg = object->start_address;
+ return __mxt_read_reg(data->client, reg,
+ sizeof(struct mxt_message), message);
+}
+
+static int mxt_read_object(struct mxt_data *data,
+ u8 type, u8 offset, u8 *val)
+{
+ struct mxt_object *object;
+ u16 reg;
+
+ object = mxt_get_object(data, type);
+ if (!object)
+ return -EINVAL;
+
+ reg = object->start_address;
+ return __mxt_read_reg(data->client, reg + offset, 1, val);
+}
+
+static int mxt_write_object(struct mxt_data *data,
+ u8 type, u8 offset, u8 val)
+{
+ struct mxt_object *object;
+ u16 reg;
+
+ object = mxt_get_object(data, type);
+ if (!object)
+ return -EINVAL;
+
+ reg = object->start_address;
+ return mxt_write_reg(data->client, reg + offset, val);
+}
+
+static void mxt_input_report(struct mxt_data *data, int single_id)
+{
+ struct mxt_finger *finger = data->finger;
+ struct input_dev *input_dev = data->input_dev;
+ int status = finger[single_id].status;
+ int finger_num = 0;
+ int id;
+
+ for (id = 0; id < MXT_MAX_FINGER; id++) {
+ if (!finger[id].status)
+ continue;
+
+ input_report_abs(input_dev, ABS_MT_TOUCH_MAJOR,
+ finger[id].status != MXT_RELEASE ?
+ finger[id].area : 0);
+ input_report_abs(input_dev, ABS_MT_POSITION_X,
+ finger[id].x);
+ input_report_abs(input_dev, ABS_MT_POSITION_Y,
+ finger[id].y);
+ input_mt_sync(input_dev);
+
+ if (finger[id].status == MXT_RELEASE)
+ finger[id].status = 0;
+ else
+ finger_num++;
+ }
+
+ input_report_key(input_dev, BTN_TOUCH, finger_num > 0);
+
+ if (status != MXT_RELEASE) {
+ input_report_abs(input_dev, ABS_X, finger[single_id].x);
+ input_report_abs(input_dev, ABS_Y, finger[single_id].y);
+ }
+
+ input_sync(input_dev);
+}
+
+static void mxt_input_touchevent(struct mxt_data *data,
+ struct mxt_message *message, int id)
+{
+ struct mxt_finger *finger = data->finger;
+ struct device *dev = &data->client->dev;
+ u8 status = message->message[0];
+ int x;
+ int y;
+ int area;
+
+ /* Check the touch is present on the screen */
+ if (!(status & MXT_DETECT)) {
+ if (status & MXT_RELEASE) {
+ dev_dbg(dev, "[%d] released\n", id);
+
+ finger[id].status = MXT_RELEASE;
+ mxt_input_report(data, id);
+ }
+ return;
+ }
+
+ /* Check only AMP detection */
+ if (!(status & (MXT_PRESS | MXT_MOVE)))
+ return;
+
+ x = (message->message[1] << 2) | ((message->message[3] & ~0x3f) >> 6);
+ y = (message->message[2] << 2) | ((message->message[3] & ~0xf3) >> 2);
+ area = message->message[4];
+
+ dev_dbg(dev, "[%d] %s x: %d, y: %d, area: %d\n", id,
+ status & MXT_MOVE ? "moved" : "pressed",
+ x, y, area);
+
+ finger[id].status = status & MXT_MOVE ?
+ MXT_MOVE : MXT_PRESS;
+ finger[id].x = x;
+ finger[id].y = y;
+ finger[id].area = area;
+
+ mxt_input_report(data, id);
+}
+
+static irqreturn_t mxt_interrupt(int irq, void *dev_id)
+{
+ struct mxt_data *data = dev_id;
+ struct mxt_message message;
+ struct mxt_object *object;
+ struct device *dev = &data->client->dev;
+ int id;
+ u8 reportid;
+ u8 max_reportid;
+ u8 min_reportid;
+
+ do {
+ if (mxt_read_message(data, &message)) {
+ dev_err(dev, "Failed to read message\n");
+ goto end;
+ }
+
+ reportid = message.reportid;
+
+ /* whether reportid is thing of MXT_TOUCH_MULTI */
+ object = mxt_get_object(data, MXT_TOUCH_MULTI);
+ if (!object)
+ goto end;
+
+ max_reportid = object->max_reportid;
+ min_reportid = max_reportid - object->num_report_ids + 1;
+ id = reportid - min_reportid;
+
+ if (reportid >= min_reportid && reportid <= max_reportid)
+ mxt_input_touchevent(data, &message, id);
+ else
+ mxt_dump_message(dev, &message);
+ } while (reportid != 0xff);
+
+end:
+ return IRQ_HANDLED;
+}
+
+static int mxt_check_reg_init(struct mxt_data *data)
+{
+ const struct mxt_platform_data *pdata = data->pdata;
+ struct mxt_object *object;
+ struct device *dev = &data->client->dev;
+ int index = 0;
+ int i, j, config_offset;
+
+ if (!pdata->config) {
+ dev_dbg(dev, "No cfg data defined, skipping reg init\n");
+ return 0;
+ }
+
+ for (i = 0; i < data->info.object_num; i++) {
+ object = data->object_table + i;
+
+ if (!mxt_object_writable(object->type))
+ continue;
+
+ for (j = 0; j < object->size + 1; j++) {
+ config_offset = index + j;
+ if (config_offset > pdata->config_length) {
+ dev_err(dev, "Not enough config data!\n");
+ return -EINVAL;
+ }
+ mxt_write_object(data, object->type, j,
+ pdata->config[config_offset]);
+ }
+ index += object->size + 1;
+ }
+
+ return 0;
+}
+
+static int mxt_make_highchg(struct mxt_data *data)
+{
+ struct device *dev = &data->client->dev;
+ struct mxt_message message;
+ int count = 10;
+ int error;
+
+ /* Read dummy message to make high CHG pin */
+ do {
+ error = mxt_read_message(data, &message);
+ if (error)
+ return error;
+ } while (message.reportid != 0xff && --count);
+
+ if (!count) {
+ dev_err(dev, "CHG pin isn't cleared\n");
+ return -EBUSY;
+ }
+
+ return 0;
+}
+
+static void mxt_handle_pdata(struct mxt_data *data)
+{
+ const struct mxt_platform_data *pdata = data->pdata;
+ u8 voltage;
+
+ /* Set touchscreen lines */
+ mxt_write_object(data, MXT_TOUCH_MULTI, MXT_TOUCH_XSIZE,
+ pdata->x_line);
+ mxt_write_object(data, MXT_TOUCH_MULTI, MXT_TOUCH_YSIZE,
+ pdata->y_line);
+
+ /* Set touchscreen orient */
+ mxt_write_object(data, MXT_TOUCH_MULTI, MXT_TOUCH_ORIENT,
+ pdata->orient);
+
+ /* Set touchscreen burst length */
+ mxt_write_object(data, MXT_TOUCH_MULTI,
+ MXT_TOUCH_BLEN, pdata->blen);
+
+ /* Set touchscreen threshold */
+ mxt_write_object(data, MXT_TOUCH_MULTI,
+ MXT_TOUCH_TCHTHR, pdata->threshold);
+
+ /* Set touchscreen resolution */
+ mxt_write_object(data, MXT_TOUCH_MULTI,
+ MXT_TOUCH_XRANGE_LSB, (pdata->x_size - 1) & 0xff);
+ mxt_write_object(data, MXT_TOUCH_MULTI,
+ MXT_TOUCH_XRANGE_MSB, (pdata->x_size - 1) >> 8);
+ mxt_write_object(data, MXT_TOUCH_MULTI,
+ MXT_TOUCH_YRANGE_LSB, (pdata->y_size - 1) & 0xff);
+ mxt_write_object(data, MXT_TOUCH_MULTI,
+ MXT_TOUCH_YRANGE_MSB, (pdata->y_size - 1) >> 8);
+
+ /* Set touchscreen voltage */
+ if (pdata->voltage) {
+ if (pdata->voltage < MXT_VOLTAGE_DEFAULT) {
+ voltage = (MXT_VOLTAGE_DEFAULT - pdata->voltage) /
+ MXT_VOLTAGE_STEP;
+ voltage = 0xff - voltage + 1;
+ } else
+ voltage = (pdata->voltage - MXT_VOLTAGE_DEFAULT) /
+ MXT_VOLTAGE_STEP;
+
+ mxt_write_object(data, MXT_SPT_CTECONFIG,
+ MXT_CTE_VOLTAGE, voltage);
+ }
+}
+
+static int mxt_get_info(struct mxt_data *data)
+{
+ struct i2c_client *client = data->client;
+ struct mxt_info *info = &data->info;
+ int error;
+ u8 val;
+
+ error = mxt_read_reg(client, MXT_FAMILY_ID, &val);
+ if (error)
+ return error;
+ info->family_id = val;
+
+ error = mxt_read_reg(client, MXT_VARIANT_ID, &val);
+ if (error)
+ return error;
+ info->variant_id = val;
+
+ error = mxt_read_reg(client, MXT_VERSION, &val);
+ if (error)
+ return error;
+ info->version = val;
+
+ error = mxt_read_reg(client, MXT_BUILD, &val);
+ if (error)
+ return error;
+ info->build = val;
+
+ error = mxt_read_reg(client, MXT_OBJECT_NUM, &val);
+ if (error)
+ return error;
+ info->object_num = val;
+
+ return 0;
+}
+
+static int mxt_get_object_table(struct mxt_data *data)
+{
+ int error;
+ int i;
+ u16 reg;
+ u8 reportid = 0;
+ u8 buf[MXT_OBJECT_SIZE];
+
+ for (i = 0; i < data->info.object_num; i++) {
+ struct mxt_object *object = data->object_table + i;
+
+ reg = MXT_OBJECT_START + MXT_OBJECT_SIZE * i;
+ error = mxt_read_object_table(data->client, reg, buf);
+ if (error)
+ return error;
+
+ object->type = buf[0];
+ object->start_address = (buf[2] << 8) | buf[1];
+ object->size = buf[3];
+ object->instances = buf[4];
+ object->num_report_ids = buf[5];
+
+ if (object->num_report_ids) {
+ reportid += object->num_report_ids *
+ (object->instances + 1);
+ object->max_reportid = reportid;
+ }
+ }
+
+ return 0;
+}
+
+static int mxt_initialize(struct mxt_data *data)
+{
+ struct i2c_client *client = data->client;
+ struct mxt_info *info = &data->info;
+ int error;
+ u8 val;
+
+ error = mxt_get_info(data);
+ if (error)
+ return error;
+
+ data->object_table = kcalloc(info->object_num,
+ sizeof(struct mxt_object),
+ GFP_KERNEL);
+ if (!data->object_table) {
+ dev_err(&client->dev, "Failed to allocate memory\n");
+ return -ENOMEM;
+ }
+
+ /* Get object table information */
+ error = mxt_get_object_table(data);
+ if (error)
+ return error;
+
+ /* Check register init values */
+ error = mxt_check_reg_init(data);
+ if (error)
+ return error;
+
+ error = mxt_make_highchg(data);
+ if (error)
+ return error;
+
+ mxt_handle_pdata(data);
+
+ /* Backup to memory */
+ mxt_write_object(data, MXT_GEN_COMMAND,
+ MXT_COMMAND_BACKUPNV,
+ MXT_BACKUP_VALUE);
+ msleep(MXT_BACKUP_TIME);
+
+ /* Soft reset */
+ mxt_write_object(data, MXT_GEN_COMMAND,
+ MXT_COMMAND_RESET, 1);
+ msleep(MXT_RESET_TIME);
+
+ /* Update matrix size at info struct */
+ error = mxt_read_reg(client, MXT_MATRIX_X_SIZE, &val);
+ if (error)
+ return error;
+ info->matrix_xsize = val;
+
+ error = mxt_read_reg(client, MXT_MATRIX_Y_SIZE, &val);
+ if (error)
+ return error;
+ info->matrix_ysize = val;
+
+ dev_info(&client->dev,
+ "Family ID: %d Variant ID: %d Version: %d Build: %d\n",
+ info->family_id, info->variant_id, info->version,
+ info->build);
+
+ dev_info(&client->dev,
+ "Matrix X Size: %d Matrix Y Size: %d Object Num: %d\n",
+ info->matrix_xsize, info->matrix_ysize,
+ info->object_num);
+
+ return 0;
+}
+
+static ssize_t mxt_object_show(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ struct mxt_data *data = dev_get_drvdata(dev);
+ struct mxt_object *object;
+ int count = 0;
+ int i, j;
+ int error;
+ u8 val;
+
+ for (i = 0; i < data->info.object_num; i++) {
+ object = data->object_table + i;
+
+ count += sprintf(buf + count,
+ "Object Table Element %d(Type %d)\n",
+ i + 1, object->type);
+
+ if (!mxt_object_readable(object->type)) {
+ count += sprintf(buf + count, "\n");
+ continue;
+ }
+
+ for (j = 0; j < object->size + 1; j++) {
+ error = mxt_read_object(data,
+ object->type, j, &val);
+ if (error)
+ return error;
+
+ count += sprintf(buf + count,
+ " Byte %d: 0x%x (%d)\n", j, val, val);
+ }
+
+ count += sprintf(buf + count, "\n");
+ }
+
+ return count;
+}
+
+static int mxt_load_fw(struct device *dev, const char *fn)
+{
+ struct mxt_data *data = dev_get_drvdata(dev);
+ struct i2c_client *client = data->client;
+ const struct firmware *fw = NULL;
+ unsigned int frame_size;
+ unsigned int pos = 0;
+ int ret;
+
+ ret = request_firmware(&fw, fn, dev);
+ if (ret) {
+ dev_err(dev, "Unable to open firmware %s\n", fn);
+ return ret;
+ }
+
+ /* Change to the bootloader mode */
+ mxt_write_object(data, MXT_GEN_COMMAND,
+ MXT_COMMAND_RESET, MXT_BOOT_VALUE);
+ msleep(MXT_RESET_TIME);
+
+ /* Change to slave address of bootloader */
+ if (client->addr == MXT_APP_LOW)
+ client->addr = MXT_BOOT_LOW;
+ else
+ client->addr = MXT_BOOT_HIGH;
+
+ ret = mxt_check_bootloader(client, MXT_WAITING_BOOTLOAD_CMD);
+ if (ret)
+ goto out;
+
+ /* Unlock bootloader */
+ mxt_unlock_bootloader(client);
+
+ while (pos < fw->size) {
+ ret = mxt_check_bootloader(client,
+ MXT_WAITING_FRAME_DATA);
+ if (ret)
+ goto out;
+
+ frame_size = ((*(fw->data + pos) << 8) | *(fw->data + pos + 1));
+
+ /* We should add 2 at frame size as the the firmware data is not
+ * included the CRC bytes.
+ */
+ frame_size += 2;
+
+ /* Write one frame to device */
+ mxt_fw_write(client, fw->data + pos, frame_size);
+
+ ret = mxt_check_bootloader(client,
+ MXT_FRAME_CRC_PASS);
+ if (ret)
+ goto out;
+
+ pos += frame_size;
+
+ dev_dbg(dev, "Updated %d bytes / %zd bytes\n", pos, fw->size);
+ }
+
+out:
+ release_firmware(fw);
+
+ /* Change to slave address of application */
+ if (client->addr == MXT_BOOT_LOW)
+ client->addr = MXT_APP_LOW;
+ else
+ client->addr = MXT_APP_HIGH;
+
+ return ret;
+}
+
+static ssize_t mxt_update_fw_store(struct device *dev,
+ struct device_attribute *attr,
+ const char *buf, size_t count)
+{
+ struct mxt_data *data = dev_get_drvdata(dev);
+ int error;
+
+ disable_irq(data->irq);
+
+ error = mxt_load_fw(dev, MXT_FW_NAME);
+ if (error) {
+ dev_err(dev, "The firmware update failed(%d)\n", error);
+ count = error;
+ } else {
+ dev_dbg(dev, "The firmware update succeeded\n");
+
+ /* Wait for reset */
+ msleep(MXT_FWRESET_TIME);
+
+ kfree(data->object_table);
+ data->object_table = NULL;
+
+ mxt_initialize(data);
+ }
+
+ enable_irq(data->irq);
+
+ return count;
+}
+
+static DEVICE_ATTR(object, 0444, mxt_object_show, NULL);
+static DEVICE_ATTR(update_fw, 0664, NULL, mxt_update_fw_store);
+
+static struct attribute *mxt_attrs[] = {
+ &dev_attr_object.attr,
+ &dev_attr_update_fw.attr,
+ NULL
+};
+
+static const struct attribute_group mxt_attr_group = {
+ .attrs = mxt_attrs,
+};
+
+static void mxt_start(struct mxt_data *data)
+{
+ /* Touch enable */
+ mxt_write_object(data,
+ MXT_TOUCH_MULTI, MXT_TOUCH_CTRL, 0x83);
+}
+
+static void mxt_stop(struct mxt_data *data)
+{
+ /* Touch disable */
+ mxt_write_object(data,
+ MXT_TOUCH_MULTI, MXT_TOUCH_CTRL, 0);
+}
+
+static int mxt_input_open(struct input_dev *dev)
+{
+ struct mxt_data *data = input_get_drvdata(dev);
+
+ mxt_start(data);
+
+ return 0;
+}
+
+static void mxt_input_close(struct input_dev *dev)
+{
+ struct mxt_data *data = input_get_drvdata(dev);
+
+ mxt_stop(data);
+}
+
+static int __devinit mxt_probe(struct i2c_client *client,
+ const struct i2c_device_id *id)
+{
+ const struct mxt_platform_data *pdata = client->dev.platform_data;
+ struct mxt_data *data;
+ struct input_dev *input_dev;
+ int error;
+
+ if (!pdata)
+ return -EINVAL;
+
+ data = kzalloc(sizeof(struct mxt_data), GFP_KERNEL);
+ input_dev = input_allocate_device();
+ if (!data || !input_dev) {
+ dev_err(&client->dev, "Failed to allocate memory\n");
+ error = -ENOMEM;
+ goto err_free_mem;
+ }
+
+ input_dev->name = "Atmel maXTouch Touchscreen";
+ input_dev->id.bustype = BUS_I2C;
+ input_dev->dev.parent = &client->dev;
+ input_dev->open = mxt_input_open;
+ input_dev->close = mxt_input_close;
+
+ __set_bit(EV_ABS, input_dev->evbit);
+ __set_bit(EV_KEY, input_dev->evbit);
+ __set_bit(BTN_TOUCH, input_dev->keybit);
+
+ /* For single touch */
+ input_set_abs_params(input_dev, ABS_X,
+ 0, MXT_MAX_XC, 0, 0);
+ input_set_abs_params(input_dev, ABS_Y,
+ 0, MXT_MAX_YC, 0, 0);
+
+ /* For multi touch */
+ input_set_abs_params(input_dev, ABS_MT_TOUCH_MAJOR,
+ 0, MXT_MAX_AREA, 0, 0);
+ input_set_abs_params(input_dev, ABS_MT_POSITION_X,
+ 0, MXT_MAX_XC, 0, 0);
+ input_set_abs_params(input_dev, ABS_MT_POSITION_Y,
+ 0, MXT_MAX_YC, 0, 0);
+
+ input_set_drvdata(input_dev, data);
+
+ data->client = client;
+ data->input_dev = input_dev;
+ data->pdata = pdata;
+ data->irq = client->irq;
+
+ i2c_set_clientdata(client, data);
+
+ error = mxt_initialize(data);
+ if (error)
+ goto err_free_object;
+
+ error = request_threaded_irq(client->irq, NULL, mxt_interrupt,
+ pdata->irqflags, client->dev.driver->name, data);
+ if (error) {
+ dev_err(&client->dev, "Failed to register interrupt\n");
+ goto err_free_object;
+ }
+
+ error = input_register_device(input_dev);
+ if (error)
+ goto err_free_irq;
+
+ error = sysfs_create_group(&client->dev.kobj, &mxt_attr_group);
+ if (error)
+ goto err_unregister_device;
+
+ return 0;
+
+err_unregister_device:
+ input_unregister_device(input_dev);
+ input_dev = NULL;
+err_free_irq:
+ free_irq(client->irq, data);
+err_free_object:
+ kfree(data->object_table);
+err_free_mem:
+ input_free_device(input_dev);
+ kfree(data);
+ return error;
+}
+
+static int __devexit mxt_remove(struct i2c_client *client)
+{
+ struct mxt_data *data = i2c_get_clientdata(client);
+
+ sysfs_remove_group(&client->dev.kobj, &mxt_attr_group);
+ free_irq(data->irq, data);
+ input_unregister_device(data->input_dev);
+ kfree(data->object_table);
+ kfree(data);
+
+ return 0;
+}
+
+#ifdef CONFIG_PM
+static int mxt_suspend(struct device *dev)
+{
+ struct i2c_client *client = to_i2c_client(dev);
+ struct mxt_data *data = i2c_get_clientdata(client);
+ struct input_dev *input_dev = data->input_dev;
+
+ mutex_lock(&input_dev->mutex);
+
+ if (input_dev->users)
+ mxt_stop(data);
+
+ mutex_unlock(&input_dev->mutex);
+
+ return 0;
+}
+
+static int mxt_resume(struct device *dev)
+{
+ struct i2c_client *client = to_i2c_client(dev);
+ struct mxt_data *data = i2c_get_clientdata(client);
+ struct input_dev *input_dev = data->input_dev;
+
+ /* Soft reset */
+ mxt_write_object(data, MXT_GEN_COMMAND,
+ MXT_COMMAND_RESET, 1);
+
+ msleep(MXT_RESET_TIME);
+
+ mutex_lock(&input_dev->mutex);
+
+ if (input_dev->users)
+ mxt_start(data);
+
+ mutex_unlock(&input_dev->mutex);
+
+ return 0;
+}
+
+static const struct dev_pm_ops mxt_pm_ops = {
+ .suspend = mxt_suspend,
+ .resume = mxt_resume,
+};
+#endif
+
+static const struct i2c_device_id mxt_id[] = {
+ { "qt602240_ts", 0 },
+ { "atmel_mxt_ts", 0 },
+ { "mXT224", 0 },
+ { }
+};
+MODULE_DEVICE_TABLE(i2c, mxt_id);
+
+static struct i2c_driver mxt_driver = {
+ .driver = {
+ .name = "atmel_mxt_ts",
+ .owner = THIS_MODULE,
+#ifdef CONFIG_PM
+ .pm = &mxt_pm_ops,
+#endif
+ },
+ .probe = mxt_probe,
+ .remove = __devexit_p(mxt_remove),
+ .id_table = mxt_id,
+};
+
+static int __init mxt_init(void)
+{
+ return i2c_add_driver(&mxt_driver);
+}
+
+static void __exit mxt_exit(void)
+{
+ i2c_del_driver(&mxt_driver);
+}
+
+module_init(mxt_init);
+module_exit(mxt_exit);
+
+/* Module information */
+MODULE_AUTHOR("Joonyoung Shim <jy0922.shim@samsung.com>");
+MODULE_DESCRIPTION("Atmel maXTouch Touchscreen driver");
+MODULE_LICENSE("GPL");
diff --git a/drivers/input/touchscreen/qt602240_ts.c b/drivers/input/touchscreen/qt602240_ts.c
deleted file mode 100644
index 4dcb0e872f6..00000000000
--- a/drivers/input/touchscreen/qt602240_ts.c
+++ /dev/null
@@ -1,1406 +0,0 @@
-/*
- * AT42QT602240/ATMXT224 Touchscreen driver
- *
- * Copyright (C) 2010 Samsung Electronics Co.Ltd
- * Author: Joonyoung Shim <jy0922.shim@samsung.com>
- *
- * 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; either version 2 of the License, or (at your
- * option) any later version.
- *
- */
-
-#include <linux/module.h>
-#include <linux/init.h>
-#include <linux/delay.h>
-#include <linux/firmware.h>
-#include <linux/i2c.h>
-#include <linux/i2c/qt602240_ts.h>
-#include <linux/input.h>
-#include <linux/interrupt.h>
-#include <linux/slab.h>
-
-/* Version */
-#define QT602240_VER_20 20
-#define QT602240_VER_21 21
-#define QT602240_VER_22 22
-
-/* Slave addresses */
-#define QT602240_APP_LOW 0x4a
-#define QT602240_APP_HIGH 0x4b
-#define QT602240_BOOT_LOW 0x24
-#define QT602240_BOOT_HIGH 0x25
-
-/* Firmware */
-#define QT602240_FW_NAME "qt602240.fw"
-
-/* Registers */
-#define QT602240_FAMILY_ID 0x00
-#define QT602240_VARIANT_ID 0x01
-#define QT602240_VERSION 0x02
-#define QT602240_BUILD 0x03
-#define QT602240_MATRIX_X_SIZE 0x04
-#define QT602240_MATRIX_Y_SIZE 0x05
-#define QT602240_OBJECT_NUM 0x06
-#define QT602240_OBJECT_START 0x07
-
-#define QT602240_OBJECT_SIZE 6
-
-/* Object types */
-#define QT602240_DEBUG_DIAGNOSTIC 37
-#define QT602240_GEN_MESSAGE 5
-#define QT602240_GEN_COMMAND 6
-#define QT602240_GEN_POWER 7
-#define QT602240_GEN_ACQUIRE 8
-#define QT602240_TOUCH_MULTI 9
-#define QT602240_TOUCH_KEYARRAY 15
-#define QT602240_TOUCH_PROXIMITY 23
-#define QT602240_PROCI_GRIPFACE 20
-#define QT602240_PROCG_NOISE 22
-#define QT602240_PROCI_ONETOUCH 24
-#define QT602240_PROCI_TWOTOUCH 27
-#define QT602240_SPT_COMMSCONFIG 18 /* firmware ver 21 over */
-#define QT602240_SPT_GPIOPWM 19
-#define QT602240_SPT_SELFTEST 25
-#define QT602240_SPT_CTECONFIG 28
-#define QT602240_SPT_USERDATA 38 /* firmware ver 21 over */
-
-/* QT602240_GEN_COMMAND field */
-#define QT602240_COMMAND_RESET 0
-#define QT602240_COMMAND_BACKUPNV 1
-#define QT602240_COMMAND_CALIBRATE 2
-#define QT602240_COMMAND_REPORTALL 3
-#define QT602240_COMMAND_DIAGNOSTIC 5
-
-/* QT602240_GEN_POWER field */
-#define QT602240_POWER_IDLEACQINT 0
-#define QT602240_POWER_ACTVACQINT 1
-#define QT602240_POWER_ACTV2IDLETO 2
-
-/* QT602240_GEN_ACQUIRE field */
-#define QT602240_ACQUIRE_CHRGTIME 0
-#define QT602240_ACQUIRE_TCHDRIFT 2
-#define QT602240_ACQUIRE_DRIFTST 3
-#define QT602240_ACQUIRE_TCHAUTOCAL 4
-#define QT602240_ACQUIRE_SYNC 5
-#define QT602240_ACQUIRE_ATCHCALST 6
-#define QT602240_ACQUIRE_ATCHCALSTHR 7
-
-/* QT602240_TOUCH_MULTI field */
-#define QT602240_TOUCH_CTRL 0
-#define QT602240_TOUCH_XORIGIN 1
-#define QT602240_TOUCH_YORIGIN 2
-#define QT602240_TOUCH_XSIZE 3
-#define QT602240_TOUCH_YSIZE 4
-#define QT602240_TOUCH_BLEN 6
-#define QT602240_TOUCH_TCHTHR 7
-#define QT602240_TOUCH_TCHDI 8
-#define QT602240_TOUCH_ORIENT 9
-#define QT602240_TOUCH_MOVHYSTI 11
-#define QT602240_TOUCH_MOVHYSTN 12
-#define QT602240_TOUCH_NUMTOUCH 14
-#define QT602240_TOUCH_MRGHYST 15
-#define QT602240_TOUCH_MRGTHR 16
-#define QT602240_TOUCH_AMPHYST 17
-#define QT602240_TOUCH_XRANGE_LSB 18
-#define QT602240_TOUCH_XRANGE_MSB 19
-#define QT602240_TOUCH_YRANGE_LSB 20
-#define QT602240_TOUCH_YRANGE_MSB 21
-#define QT602240_TOUCH_XLOCLIP 22
-#define QT602240_TOUCH_XHICLIP 23
-#define QT602240_TOUCH_YLOCLIP 24
-#define QT602240_TOUCH_YHICLIP 25
-#define QT602240_TOUCH_XEDGECTRL 26
-#define QT602240_TOUCH_XEDGEDIST 27
-#define QT602240_TOUCH_YEDGECTRL 28
-#define QT602240_TOUCH_YEDGEDIST 29
-#define QT602240_TOUCH_JUMPLIMIT 30 /* firmware ver 22 over */
-
-/* QT602240_PROCI_GRIPFACE field */
-#define QT602240_GRIPFACE_CTRL 0
-#define QT602240_GRIPFACE_XLOGRIP 1
-#define QT602240_GRIPFACE_XHIGRIP 2
-#define QT602240_GRIPFACE_YLOGRIP 3
-#define QT602240_GRIPFACE_YHIGRIP 4
-#define QT602240_GRIPFACE_MAXTCHS 5
-#define QT602240_GRIPFACE_SZTHR1 7
-#define QT602240_GRIPFACE_SZTHR2 8
-#define QT602240_GRIPFACE_SHPTHR1 9
-#define QT602240_GRIPFACE_SHPTHR2 10
-#define QT602240_GRIPFACE_SUPEXTTO 11
-
-/* QT602240_PROCI_NOISE field */
-#define QT602240_NOISE_CTRL 0
-#define QT602240_NOISE_OUTFLEN 1
-#define QT602240_NOISE_GCAFUL_LSB 3
-#define QT602240_NOISE_GCAFUL_MSB 4
-#define QT602240_NOISE_GCAFLL_LSB 5
-#define QT602240_NOISE_GCAFLL_MSB 6
-#define QT602240_NOISE_ACTVGCAFVALID 7
-#define QT602240_NOISE_NOISETHR 8
-#define QT602240_NOISE_FREQHOPSCALE 10
-#define QT602240_NOISE_FREQ0 11
-#define QT602240_NOISE_FREQ1 12
-#define QT602240_NOISE_FREQ2 13
-#define QT602240_NOISE_FREQ3 14
-#define QT602240_NOISE_FREQ4 15
-#define QT602240_NOISE_IDLEGCAFVALID 16
-
-/* QT602240_SPT_COMMSCONFIG */
-#define QT602240_COMMS_CTRL 0
-#define QT602240_COMMS_CMD 1
-
-/* QT602240_SPT_CTECONFIG field */
-#define QT602240_CTE_CTRL 0
-#define QT602240_CTE_CMD 1
-#define QT602240_CTE_MODE 2
-#define QT602240_CTE_IDLEGCAFDEPTH 3
-#define QT602240_CTE_ACTVGCAFDEPTH 4
-#define QT602240_CTE_VOLTAGE 5 /* firmware ver 21 over */
-
-#define QT602240_VOLTAGE_DEFAULT 2700000
-#define QT602240_VOLTAGE_STEP 10000
-
-/* Define for QT602240_GEN_COMMAND */
-#define QT602240_BOOT_VALUE 0xa5
-#define QT602240_BACKUP_VALUE 0x55
-#define QT602240_BACKUP_TIME 25 /* msec */
-#define QT602240_RESET_TIME 65 /* msec */
-
-#define QT602240_FWRESET_TIME 175 /* msec */
-
-/* Command to unlock bootloader */
-#define QT602240_UNLOCK_CMD_MSB 0xaa
-#define QT602240_UNLOCK_CMD_LSB 0xdc
-
-/* Bootloader mode status */
-#define QT602240_WAITING_BOOTLOAD_CMD 0xc0 /* valid 7 6 bit only */
-#define QT602240_WAITING_FRAME_DATA 0x80 /* valid 7 6 bit only */
-#define QT602240_FRAME_CRC_CHECK 0x02
-#define QT602240_FRAME_CRC_FAIL 0x03
-#define QT602240_FRAME_CRC_PASS 0x04
-#define QT602240_APP_CRC_FAIL 0x40 /* valid 7 8 bit only */
-#define QT602240_BOOT_STATUS_MASK 0x3f
-
-/* Touch status */
-#define QT602240_SUPPRESS (1 << 1)
-#define QT602240_AMP (1 << 2)
-#define QT602240_VECTOR (1 << 3)
-#define QT602240_MOVE (1 << 4)
-#define QT602240_RELEASE (1 << 5)
-#define QT602240_PRESS (1 << 6)
-#define QT602240_DETECT (1 << 7)
-
-/* Touchscreen absolute values */
-#define QT602240_MAX_XC 0x3ff
-#define QT602240_MAX_YC 0x3ff
-#define QT602240_MAX_AREA 0xff
-
-#define QT602240_MAX_FINGER 10
-
-/* Initial register values recommended from chip vendor */
-static const u8 init_vals_ver_20[] = {
- /* QT602240_GEN_COMMAND(6) */
- 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
- /* QT602240_GEN_POWER(7) */
- 0x20, 0xff, 0x32,
- /* QT602240_GEN_ACQUIRE(8) */
- 0x08, 0x05, 0x05, 0x00, 0x00, 0x00, 0x05, 0x14,
- /* QT602240_TOUCH_MULTI(9) */
- 0x00, 0x00, 0x00, 0x11, 0x0a, 0x00, 0x00, 0x00, 0x02, 0x00,
- 0x00, 0x01, 0x01, 0x0e, 0x0a, 0x0a, 0x0a, 0x0a, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x88, 0x64,
- /* QT602240_TOUCH_KEYARRAY(15) */
- 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
- 0x00,
- /* QT602240_SPT_GPIOPWM(19) */
- 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
- 0x00, 0x00,
- /* QT602240_PROCI_GRIPFACE(20) */
- 0x00, 0x64, 0x64, 0x64, 0x64, 0x00, 0x00, 0x1e, 0x14, 0x04,
- 0x1e, 0x00,
- /* QT602240_PROCG_NOISE(22) */
- 0x05, 0x00, 0x00, 0x19, 0x00, 0xe7, 0xff, 0x04, 0x32, 0x00,
- 0x01, 0x0a, 0x0f, 0x14, 0x00, 0x00, 0xe8,
- /* QT602240_TOUCH_PROXIMITY(23) */
- 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x00,
- /* QT602240_PROCI_ONETOUCH(24) */
- 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
- /* QT602240_SPT_SELFTEST(25) */
- 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x00,
- /* QT602240_PROCI_TWOTOUCH(27) */
- 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
- /* QT602240_SPT_CTECONFIG(28) */
- 0x00, 0x00, 0x00, 0x04, 0x08,
-};
-
-static const u8 init_vals_ver_21[] = {
- /* QT602240_GEN_COMMAND(6) */
- 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
- /* QT602240_GEN_POWER(7) */
- 0x20, 0xff, 0x32,
- /* QT602240_GEN_ACQUIRE(8) */
- 0x0a, 0x00, 0x05, 0x00, 0x00, 0x00, 0x09, 0x23,
- /* QT602240_TOUCH_MULTI(9) */
- 0x00, 0x00, 0x00, 0x13, 0x0b, 0x00, 0x00, 0x00, 0x02, 0x00,
- 0x00, 0x01, 0x01, 0x0e, 0x0a, 0x0a, 0x0a, 0x0a, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
- /* QT602240_TOUCH_KEYARRAY(15) */
- 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
- 0x00,
- /* QT602240_SPT_GPIOPWM(19) */
- 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
- /* QT602240_PROCI_GRIPFACE(20) */
- 0x07, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x50, 0x28, 0x04,
- 0x0f, 0x0a,
- /* QT602240_PROCG_NOISE(22) */
- 0x05, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0x23, 0x00,
- 0x00, 0x05, 0x0f, 0x19, 0x23, 0x2d, 0x03,
- /* QT602240_TOUCH_PROXIMITY(23) */
- 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x00,
- /* QT602240_PROCI_ONETOUCH(24) */
- 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
- /* QT602240_SPT_SELFTEST(25) */
- 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x00,
- /* QT602240_PROCI_TWOTOUCH(27) */
- 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
- /* QT602240_SPT_CTECONFIG(28) */
- 0x00, 0x00, 0x00, 0x08, 0x10, 0x00,
-};
-
-static const u8 init_vals_ver_22[] = {
- /* QT602240_GEN_COMMAND(6) */
- 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
- /* QT602240_GEN_POWER(7) */
- 0x20, 0xff, 0x32,
- /* QT602240_GEN_ACQUIRE(8) */
- 0x0a, 0x00, 0x05, 0x00, 0x00, 0x00, 0x09, 0x23,
- /* QT602240_TOUCH_MULTI(9) */
- 0x00, 0x00, 0x00, 0x13, 0x0b, 0x00, 0x00, 0x00, 0x02, 0x00,
- 0x00, 0x01, 0x01, 0x0e, 0x0a, 0x0a, 0x0a, 0x0a, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
- 0x00,
- /* QT602240_TOUCH_KEYARRAY(15) */
- 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
- 0x00,
- /* QT602240_SPT_GPIOPWM(19) */
- 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
- /* QT602240_PROCI_GRIPFACE(20) */
- 0x07, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x50, 0x28, 0x04,
- 0x0f, 0x0a,
- /* QT602240_PROCG_NOISE(22) */
- 0x05, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0x23, 0x00,
- 0x00, 0x05, 0x0f, 0x19, 0x23, 0x2d, 0x03,
- /* QT602240_TOUCH_PROXIMITY(23) */
- 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x00, 0x00,
- /* QT602240_PROCI_ONETOUCH(24) */
- 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
- /* QT602240_SPT_SELFTEST(25) */
- 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x00,
- /* QT602240_PROCI_TWOTOUCH(27) */
- 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
- /* QT602240_SPT_CTECONFIG(28) */
- 0x00, 0x00, 0x00, 0x08, 0x10, 0x00,
-};
-
-struct qt602240_info {
- u8 family_id;
- u8 variant_id;
- u8 version;
- u8 build;
- u8 matrix_xsize;
- u8 matrix_ysize;
- u8 object_num;
-};
-
-struct qt602240_object {
- u8 type;
- u16 start_address;
- u8 size;
- u8 instances;
- u8 num_report_ids;
-
- /* to map object and message */
- u8 max_reportid;
-};
-
-struct qt602240_message {
- u8 reportid;
- u8 message[7];
- u8 checksum;
-};
-
-struct qt602240_finger {
- int status;
- int x;
- int y;
- int area;
-};
-
-/* Each client has this additional data */
-struct qt602240_data {
- struct i2c_client *client;
- struct input_dev *input_dev;
- const struct qt602240_platform_data *pdata;
- struct qt602240_object *object_table;
- struct qt602240_info info;
- struct qt602240_finger finger[QT602240_MAX_FINGER];
- unsigned int irq;
-};
-
-static bool qt602240_object_readable(unsigned int type)
-{
- switch (type) {
- case QT602240_GEN_MESSAGE:
- case QT602240_GEN_COMMAND:
- case QT602240_GEN_POWER:
- case QT602240_GEN_ACQUIRE:
- case QT602240_TOUCH_MULTI:
- case QT602240_TOUCH_KEYARRAY:
- case QT602240_TOUCH_PROXIMITY:
- case QT602240_PROCI_GRIPFACE:
- case QT602240_PROCG_NOISE:
- case QT602240_PROCI_ONETOUCH:
- case QT602240_PROCI_TWOTOUCH:
- case QT602240_SPT_COMMSCONFIG:
- case QT602240_SPT_GPIOPWM:
- case QT602240_SPT_SELFTEST:
- case QT602240_SPT_CTECONFIG:
- case QT602240_SPT_USERDATA:
- return true;
- default:
- return false;
- }
-}
-
-static bool qt602240_object_writable(unsigned int type)
-{
- switch (type) {
- case QT602240_GEN_COMMAND:
- case QT602240_GEN_POWER:
- case QT602240_GEN_ACQUIRE:
- case QT602240_TOUCH_MULTI:
- case QT602240_TOUCH_KEYARRAY:
- case QT602240_TOUCH_PROXIMITY:
- case QT602240_PROCI_GRIPFACE:
- case QT602240_PROCG_NOISE:
- case QT602240_PROCI_ONETOUCH:
- case QT602240_PROCI_TWOTOUCH:
- case QT602240_SPT_GPIOPWM:
- case QT602240_SPT_SELFTEST:
- case QT602240_SPT_CTECONFIG:
- return true;
- default:
- return false;
- }
-}
-
-static void qt602240_dump_message(struct device *dev,
- struct qt602240_message *message)
-{
- dev_dbg(dev, "reportid:\t0x%x\n", message->reportid);
- dev_dbg(dev, "message1:\t0x%x\n", message->message[0]);
- dev_dbg(dev, "message2:\t0x%x\n", message->message[1]);
- dev_dbg(dev, "message3:\t0x%x\n", message->message[2]);
- dev_dbg(dev, "message4:\t0x%x\n", message->message[3]);
- dev_dbg(dev, "message5:\t0x%x\n", message->message[4]);
- dev_dbg(dev, "message6:\t0x%x\n", message->message[5]);
- dev_dbg(dev, "message7:\t0x%x\n", message->message[6]);
- dev_dbg(dev, "checksum:\t0x%x\n", message->checksum);
-}
-
-static int qt602240_check_bootloader(struct i2c_client *client,
- unsigned int state)
-{
- u8 val;
-
-recheck:
- if (i2c_master_recv(client, &val, 1) != 1) {
- dev_err(&client->dev, "%s: i2c recv failed\n", __func__);
- return -EIO;
- }
-
- switch (state) {
- case QT602240_WAITING_BOOTLOAD_CMD:
- case QT602240_WAITING_FRAME_DATA:
- val &= ~QT602240_BOOT_STATUS_MASK;
- break;
- case QT602240_FRAME_CRC_PASS:
- if (val == QT602240_FRAME_CRC_CHECK)
- goto recheck;
- break;
- default:
- return -EINVAL;
- }
-
- if (val != state) {
- dev_err(&client->dev, "Unvalid bootloader mode state\n");
- return -EINVAL;
- }
-
- return 0;
-}
-
-static int qt602240_unlock_bootloader(struct i2c_client *client)
-{
- u8 buf[2];
-
- buf[0] = QT602240_UNLOCK_CMD_LSB;
- buf[1] = QT602240_UNLOCK_CMD_MSB;
-
- if (i2c_master_send(client, buf, 2) != 2) {
- dev_err(&client->dev, "%s: i2c send failed\n", __func__);
- return -EIO;
- }
-
- return 0;
-}
-
-static int qt602240_fw_write(struct i2c_client *client,
- const u8 *data, unsigned int frame_size)
-{
- if (i2c_master_send(client, data, frame_size) != frame_size) {
- dev_err(&client->dev, "%s: i2c send failed\n", __func__);
- return -EIO;
- }
-
- return 0;
-}
-
-static int __qt602240_read_reg(struct i2c_client *client,
- u16 reg, u16 len, void *val)
-{
- struct i2c_msg xfer[2];
- u8 buf[2];
-
- buf[0] = reg & 0xff;
- buf[1] = (reg >> 8) & 0xff;
-
- /* Write register */
- xfer[0].addr = client->addr;
- xfer[0].flags = 0;
- xfer[0].len = 2;
- xfer[0].buf = buf;
-
- /* Read data */
- xfer[1].addr = client->addr;
- xfer[1].flags = I2C_M_RD;
- xfer[1].len = len;
- xfer[1].buf = val;
-
- if (i2c_transfer(client->adapter, xfer, 2) != 2) {
- dev_err(&client->dev, "%s: i2c transfer failed\n", __func__);
- return -EIO;
- }
-
- return 0;
-}
-
-static int qt602240_read_reg(struct i2c_client *client, u16 reg, u8 *val)
-{
- return __qt602240_read_reg(client, reg, 1, val);
-}
-
-static int qt602240_write_reg(struct i2c_client *client, u16 reg, u8 val)
-{
- u8 buf[3];
-
- buf[0] = reg & 0xff;
- buf[1] = (reg >> 8) & 0xff;
- buf[2] = val;
-
- if (i2c_master_send(client, buf, 3) != 3) {
- dev_err(&client->dev, "%s: i2c send failed\n", __func__);
- return -EIO;
- }
-
- return 0;
-}
-
-static int qt602240_read_object_table(struct i2c_client *client,
- u16 reg, u8 *object_buf)
-{
- return __qt602240_read_reg(client, reg, QT602240_OBJECT_SIZE,
- object_buf);
-}
-
-static struct qt602240_object *
-qt602240_get_object(struct qt602240_data *data, u8 type)
-{
- struct qt602240_object *object;
- int i;
-
- for (i = 0; i < data->info.object_num; i++) {
- object = data->object_table + i;
- if (object->type == type)
- return object;
- }
-
- dev_err(&data->client->dev, "Invalid object type\n");
- return NULL;
-}
-
-static int qt602240_read_message(struct qt602240_data *data,
- struct qt602240_message *message)
-{
- struct qt602240_object *object;
- u16 reg;
-
- object = qt602240_get_object(data, QT602240_GEN_MESSAGE);
- if (!object)
- return -EINVAL;
-
- reg = object->start_address;
- return __qt602240_read_reg(data->client, reg,
- sizeof(struct qt602240_message), message);
-}
-
-static int qt602240_read_object(struct qt602240_data *data,
- u8 type, u8 offset, u8 *val)
-{
- struct qt602240_object *object;
- u16 reg;
-
- object = qt602240_get_object(data, type);
- if (!object)
- return -EINVAL;
-
- reg = object->start_address;
- return __qt602240_read_reg(data->client, reg + offset, 1, val);
-}
-
-static int qt602240_write_object(struct qt602240_data *data,
- u8 type, u8 offset, u8 val)
-{
- struct qt602240_object *object;
- u16 reg;
-
- object = qt602240_get_object(data, type);
- if (!object)
- return -EINVAL;
-
- reg = object->start_address;
- return qt602240_write_reg(data->client, reg + offset, val);
-}
-
-static void qt602240_input_report(struct qt602240_data *data, int single_id)
-{
- struct qt602240_finger *finger = data->finger;
- struct input_dev *input_dev = data->input_dev;
- int status = finger[single_id].status;
- int finger_num = 0;
- int id;
-
- for (id = 0; id < QT602240_MAX_FINGER; id++) {
- if (!finger[id].status)
- continue;
-
- input_report_abs(input_dev, ABS_MT_TOUCH_MAJOR,
- finger[id].status != QT602240_RELEASE ?
- finger[id].area : 0);
- input_report_abs(input_dev, ABS_MT_POSITION_X,
- finger[id].x);
- input_report_abs(input_dev, ABS_MT_POSITION_Y,
- finger[id].y);
- input_mt_sync(input_dev);
-
- if (finger[id].status == QT602240_RELEASE)
- finger[id].status = 0;
- else
- finger_num++;
- }
-
- input_report_key(input_dev, BTN_TOUCH, finger_num > 0);
-
- if (status != QT602240_RELEASE) {
- input_report_abs(input_dev, ABS_X, finger[single_id].x);
- input_report_abs(input_dev, ABS_Y, finger[single_id].y);
- }
-
- input_sync(input_dev);
-}
-
-static void qt602240_input_touchevent(struct qt602240_data *data,
- struct qt602240_message *message, int id)
-{
- struct qt602240_finger *finger = data->finger;
- struct device *dev = &data->client->dev;
- u8 status = message->message[0];
- int x;
- int y;
- int area;
-
- /* Check the touch is present on the screen */
- if (!(status & QT602240_DETECT)) {
- if (status & QT602240_RELEASE) {
- dev_dbg(dev, "[%d] released\n", id);
-
- finger[id].status = QT602240_RELEASE;
- qt602240_input_report(data, id);
- }
- return;
- }
-
- /* Check only AMP detection */
- if (!(status & (QT602240_PRESS | QT602240_MOVE)))
- return;
-
- x = (message->message[1] << 2) | ((message->message[3] & ~0x3f) >> 6);
- y = (message->message[2] << 2) | ((message->message[3] & ~0xf3) >> 2);
- area = message->message[4];
-
- dev_dbg(dev, "[%d] %s x: %d, y: %d, area: %d\n", id,
- status & QT602240_MOVE ? "moved" : "pressed",
- x, y, area);
-
- finger[id].status = status & QT602240_MOVE ?
- QT602240_MOVE : QT602240_PRESS;
- finger[id].x = x;
- finger[id].y = y;
- finger[id].area = area;
-
- qt602240_input_report(data, id);
-}
-
-static irqreturn_t qt602240_interrupt(int irq, void *dev_id)
-{
- struct qt602240_data *data = dev_id;
- struct qt602240_message message;
- struct qt602240_object *object;
- struct device *dev = &data->client->dev;
- int id;
- u8 reportid;
- u8 max_reportid;
- u8 min_reportid;
-
- do {
- if (qt602240_read_message(data, &message)) {
- dev_err(dev, "Failed to read message\n");
- goto end;
- }
-
- reportid = message.reportid;
-
- /* whether reportid is thing of QT602240_TOUCH_MULTI */
- object = qt602240_get_object(data, QT602240_TOUCH_MULTI);
- if (!object)
- goto end;
-
- max_reportid = object->max_reportid;
- min_reportid = max_reportid - object->num_report_ids + 1;
- id = reportid - min_reportid;
-
- if (reportid >= min_reportid && reportid <= max_reportid)
- qt602240_input_touchevent(data, &message, id);
- else
- qt602240_dump_message(dev, &message);
- } while (reportid != 0xff);
-
-end:
- return IRQ_HANDLED;
-}
-
-static int qt602240_check_reg_init(struct qt602240_data *data)
-{
- struct qt602240_object *object;
- struct device *dev = &data->client->dev;
- int index = 0;
- int i, j;
- u8 version = data->info.version;
- u8 *init_vals;
-
- switch (version) {
- case QT602240_VER_20:
- init_vals = (u8 *)init_vals_ver_20;
- break;
- case QT602240_VER_21:
- init_vals = (u8 *)init_vals_ver_21;
- break;
- case QT602240_VER_22:
- init_vals = (u8 *)init_vals_ver_22;
- break;
- default:
- dev_err(dev, "Firmware version %d doesn't support\n", version);
- return -EINVAL;
- }
-
- for (i = 0; i < data->info.object_num; i++) {
- object = data->object_table + i;
-
- if (!qt602240_object_writable(object->type))
- continue;
-
- for (j = 0; j < object->size + 1; j++)
- qt602240_write_object(data, object->type, j,
- init_vals[index + j]);
-
- index += object->size + 1;
- }
-
- return 0;
-}
-
-static int qt602240_check_matrix_size(struct qt602240_data *data)
-{
- const struct qt602240_platform_data *pdata = data->pdata;
- struct device *dev = &data->client->dev;
- int mode = -1;
- int error;
- u8 val;
-
- dev_dbg(dev, "Number of X lines: %d\n", pdata->x_line);
- dev_dbg(dev, "Number of Y lines: %d\n", pdata->y_line);
-
- switch (pdata->x_line) {
- case 0 ... 15:
- if (pdata->y_line <= 14)
- mode = 0;
- break;
- case 16:
- if (pdata->y_line <= 12)
- mode = 1;
- if (pdata->y_line == 13 || pdata->y_line == 14)
- mode = 0;
- break;
- case 17:
- if (pdata->y_line <= 11)
- mode = 2;
- if (pdata->y_line == 12 || pdata->y_line == 13)
- mode = 1;
- break;
- case 18:
- if (pdata->y_line <= 10)
- mode = 3;
- if (pdata->y_line == 11 || pdata->y_line == 12)
- mode = 2;
- break;
- case 19:
- if (pdata->y_line <= 9)
- mode = 4;
- if (pdata->y_line == 10 || pdata->y_line == 11)
- mode = 3;
- break;
- case 20:
- mode = 4;
- }
-
- if (mode < 0) {
- dev_err(dev, "Invalid X/Y lines\n");
- return -EINVAL;
- }
-
- error = qt602240_read_object(data, QT602240_SPT_CTECONFIG,
- QT602240_CTE_MODE, &val);
- if (error)
- return error;
-
- if (mode == val)
- return 0;
-
- /* Change the CTE configuration */
- qt602240_write_object(data, QT602240_SPT_CTECONFIG,
- QT602240_CTE_CTRL, 1);
- qt602240_write_object(data, QT602240_SPT_CTECONFIG,
- QT602240_CTE_MODE, mode);
- qt602240_write_object(data, QT602240_SPT_CTECONFIG,
- QT602240_CTE_CTRL, 0);
-
- return 0;
-}
-
-static int qt602240_make_highchg(struct qt602240_data *data)
-{
- struct device *dev = &data->client->dev;
- int count = 10;
- int error;
- u8 val;
-
- /* Read dummy message to make high CHG pin */
- do {
- error = qt602240_read_object(data, QT602240_GEN_MESSAGE, 0, &val);
- if (error)
- return error;
- } while ((val != 0xff) && --count);
-
- if (!count) {
- dev_err(dev, "CHG pin isn't cleared\n");
- return -EBUSY;
- }
-
- return 0;
-}
-
-static void qt602240_handle_pdata(struct qt602240_data *data)
-{
- const struct qt602240_platform_data *pdata = data->pdata;
- u8 voltage;
-
- /* Set touchscreen lines */
- qt602240_write_object(data, QT602240_TOUCH_MULTI, QT602240_TOUCH_XSIZE,
- pdata->x_line);
- qt602240_write_object(data, QT602240_TOUCH_MULTI, QT602240_TOUCH_YSIZE,
- pdata->y_line);
-
- /* Set touchscreen orient */
- qt602240_write_object(data, QT602240_TOUCH_MULTI, QT602240_TOUCH_ORIENT,
- pdata->orient);
-
- /* Set touchscreen burst length */
- qt602240_write_object(data, QT602240_TOUCH_MULTI,
- QT602240_TOUCH_BLEN, pdata->blen);
-
- /* Set touchscreen threshold */
- qt602240_write_object(data, QT602240_TOUCH_MULTI,
- QT602240_TOUCH_TCHTHR, pdata->threshold);
-
- /* Set touchscreen resolution */
- qt602240_write_object(data, QT602240_TOUCH_MULTI,
- QT602240_TOUCH_XRANGE_LSB, (pdata->x_size - 1) & 0xff);
- qt602240_write_object(data, QT602240_TOUCH_MULTI,
- QT602240_TOUCH_XRANGE_MSB, (pdata->x_size - 1) >> 8);
- qt602240_write_object(data, QT602240_TOUCH_MULTI,
- QT602240_TOUCH_YRANGE_LSB, (pdata->y_size - 1) & 0xff);
- qt602240_write_object(data, QT602240_TOUCH_MULTI,
- QT602240_TOUCH_YRANGE_MSB, (pdata->y_size - 1) >> 8);
-
- /* Set touchscreen voltage */
- if (data->info.version >= QT602240_VER_21 && pdata->voltage) {
- if (pdata->voltage < QT602240_VOLTAGE_DEFAULT) {
- voltage = (QT602240_VOLTAGE_DEFAULT - pdata->voltage) /
- QT602240_VOLTAGE_STEP;
- voltage = 0xff - voltage + 1;
- } else
- voltage = (pdata->voltage - QT602240_VOLTAGE_DEFAULT) /
- QT602240_VOLTAGE_STEP;
-
- qt602240_write_object(data, QT602240_SPT_CTECONFIG,
- QT602240_CTE_VOLTAGE, voltage);
- }
-}
-
-static int qt602240_get_info(struct qt602240_data *data)
-{
- struct i2c_client *client = data->client;
- struct qt602240_info *info = &data->info;
- int error;
- u8 val;
-
- error = qt602240_read_reg(client, QT602240_FAMILY_ID, &val);
- if (error)
- return error;
- info->family_id = val;
-
- error = qt602240_read_reg(client, QT602240_VARIANT_ID, &val);
- if (error)
- return error;
- info->variant_id = val;
-
- error = qt602240_read_reg(client, QT602240_VERSION, &val);
- if (error)
- return error;
- info->version = val;
-
- error = qt602240_read_reg(client, QT602240_BUILD, &val);
- if (error)
- return error;
- info->build = val;
-
- error = qt602240_read_reg(client, QT602240_OBJECT_NUM, &val);
- if (error)
- return error;
- info->object_num = val;
-
- return 0;
-}
-
-static int qt602240_get_object_table(struct qt602240_data *data)
-{
- int error;
- int i;
- u16 reg;
- u8 reportid = 0;
- u8 buf[QT602240_OBJECT_SIZE];
-
- for (i = 0; i < data->info.object_num; i++) {
- struct qt602240_object *object = data->object_table + i;
-
- reg = QT602240_OBJECT_START + QT602240_OBJECT_SIZE * i;
- error = qt602240_read_object_table(data->client, reg, buf);
- if (error)
- return error;
-
- object->type = buf[0];
- object->start_address = (buf[2] << 8) | buf[1];
- object->size = buf[3];
- object->instances = buf[4];
- object->num_report_ids = buf[5];
-
- if (object->num_report_ids) {
- reportid += object->num_report_ids *
- (object->instances + 1);
- object->max_reportid = reportid;
- }
- }
-
- return 0;
-}
-
-static int qt602240_initialize(struct qt602240_data *data)
-{
- struct i2c_client *client = data->client;
- struct qt602240_info *info = &data->info;
- int error;
- u8 val;
-
- error = qt602240_get_info(data);
- if (error)
- return error;
-
- data->object_table = kcalloc(info->object_num,
- sizeof(struct qt602240_object),
- GFP_KERNEL);
- if (!data->object_table) {
- dev_err(&client->dev, "Failed to allocate memory\n");
- return -ENOMEM;
- }
-
- /* Get object table information */
- error = qt602240_get_object_table(data);
- if (error)
- return error;
-
- /* Check register init values */
- error = qt602240_check_reg_init(data);
- if (error)
- return error;
-
- /* Check X/Y matrix size */
- error = qt602240_check_matrix_size(data);
- if (error)
- return error;
-
- error = qt602240_make_highchg(data);
- if (error)
- return error;
-
- qt602240_handle_pdata(data);
-
- /* Backup to memory */
- qt602240_write_object(data, QT602240_GEN_COMMAND,
- QT602240_COMMAND_BACKUPNV,
- QT602240_BACKUP_VALUE);
- msleep(QT602240_BACKUP_TIME);
-
- /* Soft reset */
- qt602240_write_object(data, QT602240_GEN_COMMAND,
- QT602240_COMMAND_RESET, 1);
- msleep(QT602240_RESET_TIME);
-
- /* Update matrix size at info struct */
- error = qt602240_read_reg(client, QT602240_MATRIX_X_SIZE, &val);
- if (error)
- return error;
- info->matrix_xsize = val;
-
- error = qt602240_read_reg(client, QT602240_MATRIX_Y_SIZE, &val);
- if (error)
- return error;
- info->matrix_ysize = val;
-
- dev_info(&client->dev,
- "Family ID: %d Variant ID: %d Version: %d Build: %d\n",
- info->family_id, info->variant_id, info->version,
- info->build);
-
- dev_info(&client->dev,
- "Matrix X Size: %d Matrix Y Size: %d Object Num: %d\n",
- info->matrix_xsize, info->matrix_ysize,
- info->object_num);
-
- return 0;
-}
-
-static ssize_t qt602240_object_show(struct device *dev,
- struct device_attribute *attr, char *buf)
-{
- struct qt602240_data *data = dev_get_drvdata(dev);
- struct qt602240_object *object;
- int count = 0;
- int i, j;
- int error;
- u8 val;
-
- for (i = 0; i < data->info.object_num; i++) {
- object = data->object_table + i;
-
- count += sprintf(buf + count,
- "Object Table Element %d(Type %d)\n",
- i + 1, object->type);
-
- if (!qt602240_object_readable(object->type)) {
- count += sprintf(buf + count, "\n");
- continue;
- }
-
- for (j = 0; j < object->size + 1; j++) {
- error = qt602240_read_object(data,
- object->type, j, &val);
- if (error)
- return error;
-
- count += sprintf(buf + count,
- " Byte %d: 0x%x (%d)\n", j, val, val);
- }
-
- count += sprintf(buf + count, "\n");
- }
-
- return count;
-}
-
-static int qt602240_load_fw(struct device *dev, const char *fn)
-{
- struct qt602240_data *data = dev_get_drvdata(dev);
- struct i2c_client *client = data->client;
- const struct firmware *fw = NULL;
- unsigned int frame_size;
- unsigned int pos = 0;
- int ret;
-
- ret = request_firmware(&fw, fn, dev);
- if (ret) {
- dev_err(dev, "Unable to open firmware %s\n", fn);
- return ret;
- }
-
- /* Change to the bootloader mode */
- qt602240_write_object(data, QT602240_GEN_COMMAND,
- QT602240_COMMAND_RESET, QT602240_BOOT_VALUE);
- msleep(QT602240_RESET_TIME);
-
- /* Change to slave address of bootloader */
- if (client->addr == QT602240_APP_LOW)
- client->addr = QT602240_BOOT_LOW;
- else
- client->addr = QT602240_BOOT_HIGH;
-
- ret = qt602240_check_bootloader(client, QT602240_WAITING_BOOTLOAD_CMD);
- if (ret)
- goto out;
-
- /* Unlock bootloader */
- qt602240_unlock_bootloader(client);
-
- while (pos < fw->size) {
- ret = qt602240_check_bootloader(client,
- QT602240_WAITING_FRAME_DATA);
- if (ret)
- goto out;
-
- frame_size = ((*(fw->data + pos) << 8) | *(fw->data + pos + 1));
-
- /* We should add 2 at frame size as the the firmware data is not
- * included the CRC bytes.
- */
- frame_size += 2;
-
- /* Write one frame to device */
- qt602240_fw_write(client, fw->data + pos, frame_size);
-
- ret = qt602240_check_bootloader(client,
- QT602240_FRAME_CRC_PASS);
- if (ret)
- goto out;
-
- pos += frame_size;
-
- dev_dbg(dev, "Updated %d bytes / %zd bytes\n", pos, fw->size);
- }
-
-out:
- release_firmware(fw);
-
- /* Change to slave address of application */
- if (client->addr == QT602240_BOOT_LOW)
- client->addr = QT602240_APP_LOW;
- else
- client->addr = QT602240_APP_HIGH;
-
- return ret;
-}
-
-static ssize_t qt602240_update_fw_store(struct device *dev,
- struct device_attribute *attr,
- const char *buf, size_t count)
-{
- struct qt602240_data *data = dev_get_drvdata(dev);
- unsigned int version;
- int error;
-
- if (sscanf(buf, "%u", &version) != 1) {
- dev_err(dev, "Invalid values\n");
- return -EINVAL;
- }
-
- if (data->info.version < QT602240_VER_21 || version < QT602240_VER_21) {
- dev_err(dev, "FW update supported starting with version 21\n");
- return -EINVAL;
- }
-
- disable_irq(data->irq);
-
- error = qt602240_load_fw(dev, QT602240_FW_NAME);
- if (error) {
- dev_err(dev, "The firmware update failed(%d)\n", error);
- count = error;
- } else {
- dev_dbg(dev, "The firmware update succeeded\n");
-
- /* Wait for reset */
- msleep(QT602240_FWRESET_TIME);
-
- kfree(data->object_table);
- data->object_table = NULL;
-
- qt602240_initialize(data);
- }
-
- enable_irq(data->irq);
-
- return count;
-}
-
-static DEVICE_ATTR(object, 0444, qt602240_object_show, NULL);
-static DEVICE_ATTR(update_fw, 0664, NULL, qt602240_update_fw_store);
-
-static struct attribute *qt602240_attrs[] = {
- &dev_attr_object.attr,
- &dev_attr_update_fw.attr,
- NULL
-};
-
-static const struct attribute_group qt602240_attr_group = {
- .attrs = qt602240_attrs,
-};
-
-static void qt602240_start(struct qt602240_data *data)
-{
- /* Touch enable */
- qt602240_write_object(data,
- QT602240_TOUCH_MULTI, QT602240_TOUCH_CTRL, 0x83);
-}
-
-static void qt602240_stop(struct qt602240_data *data)
-{
- /* Touch disable */
- qt602240_write_object(data,
- QT602240_TOUCH_MULTI, QT602240_TOUCH_CTRL, 0);
-}
-
-static int qt602240_input_open(struct input_dev *dev)
-{
- struct qt602240_data *data = input_get_drvdata(dev);
-
- qt602240_start(data);
-
- return 0;
-}
-
-static void qt602240_input_close(struct input_dev *dev)
-{
- struct qt602240_data *data = input_get_drvdata(dev);
-
- qt602240_stop(data);
-}
-
-static int __devinit qt602240_probe(struct i2c_client *client,
- const struct i2c_device_id *id)
-{
- struct qt602240_data *data;
- struct input_dev *input_dev;
- int error;
-
- if (!client->dev.platform_data)
- return -EINVAL;
-
- data = kzalloc(sizeof(struct qt602240_data), GFP_KERNEL);
- input_dev = input_allocate_device();
- if (!data || !input_dev) {
- dev_err(&client->dev, "Failed to allocate memory\n");
- error = -ENOMEM;
- goto err_free_mem;
- }
-
- input_dev->name = "AT42QT602240/ATMXT224 Touchscreen";
- input_dev->id.bustype = BUS_I2C;
- input_dev->dev.parent = &client->dev;
- input_dev->open = qt602240_input_open;
- input_dev->close = qt602240_input_close;
-
- __set_bit(EV_ABS, input_dev->evbit);
- __set_bit(EV_KEY, input_dev->evbit);
- __set_bit(BTN_TOUCH, input_dev->keybit);
-
- /* For single touch */
- input_set_abs_params(input_dev, ABS_X,
- 0, QT602240_MAX_XC, 0, 0);
- input_set_abs_params(input_dev, ABS_Y,
- 0, QT602240_MAX_YC, 0, 0);
-
- /* For multi touch */
- input_set_abs_params(input_dev, ABS_MT_TOUCH_MAJOR,
- 0, QT602240_MAX_AREA, 0, 0);
- input_set_abs_params(input_dev, ABS_MT_POSITION_X,
- 0, QT602240_MAX_XC, 0, 0);
- input_set_abs_params(input_dev, ABS_MT_POSITION_Y,
- 0, QT602240_MAX_YC, 0, 0);
-
- input_set_drvdata(input_dev, data);
-
- data->client = client;
- data->input_dev = input_dev;
- data->pdata = client->dev.platform_data;
- data->irq = client->irq;
-
- i2c_set_clientdata(client, data);
-
- error = qt602240_initialize(data);
- if (error)
- goto err_free_object;
-
- error = request_threaded_irq(client->irq, NULL, qt602240_interrupt,
- IRQF_TRIGGER_FALLING, client->dev.driver->name, data);
- if (error) {
- dev_err(&client->dev, "Failed to register interrupt\n");
- goto err_free_object;
- }
-
- error = input_register_device(input_dev);
- if (error)
- goto err_free_irq;
-
- error = sysfs_create_group(&client->dev.kobj, &qt602240_attr_group);
- if (error)
- goto err_unregister_device;
-
- return 0;
-
-err_unregister_device:
- input_unregister_device(input_dev);
- input_dev = NULL;
-err_free_irq:
- free_irq(client->irq, data);
-err_free_object:
- kfree(data->object_table);
-err_free_mem:
- input_free_device(input_dev);
- kfree(data);
- return error;
-}
-
-static int __devexit qt602240_remove(struct i2c_client *client)
-{
- struct qt602240_data *data = i2c_get_clientdata(client);
-
- sysfs_remove_group(&client->dev.kobj, &qt602240_attr_group);
- free_irq(data->irq, data);
- input_unregister_device(data->input_dev);
- kfree(data->object_table);
- kfree(data);
-
- return 0;
-}
-
-#ifdef CONFIG_PM
-static int qt602240_suspend(struct device *dev)
-{
- struct i2c_client *client = to_i2c_client(dev);
- struct qt602240_data *data = i2c_get_clientdata(client);
- struct input_dev *input_dev = data->input_dev;
-
- mutex_lock(&input_dev->mutex);
-
- if (input_dev->users)
- qt602240_stop(data);
-
- mutex_unlock(&input_dev->mutex);
-
- return 0;
-}
-
-static int qt602240_resume(struct device *dev)
-{
- struct i2c_client *client = to_i2c_client(dev);
- struct qt602240_data *data = i2c_get_clientdata(client);
- struct input_dev *input_dev = data->input_dev;
-
- /* Soft reset */
- qt602240_write_object(data, QT602240_GEN_COMMAND,
- QT602240_COMMAND_RESET, 1);
-
- msleep(QT602240_RESET_TIME);
-
- mutex_lock(&input_dev->mutex);
-
- if (input_dev->users)
- qt602240_start(data);
-
- mutex_unlock(&input_dev->mutex);
-
- return 0;
-}
-
-static const struct dev_pm_ops qt602240_pm_ops = {
- .suspend = qt602240_suspend,
- .resume = qt602240_resume,
-};
-#endif
-
-static const struct i2c_device_id qt602240_id[] = {
- { "qt602240_ts", 0 },
- { }
-};
-MODULE_DEVICE_TABLE(i2c, qt602240_id);
-
-static struct i2c_driver qt602240_driver = {
- .driver = {
- .name = "qt602240_ts",
- .owner = THIS_MODULE,
-#ifdef CONFIG_PM
- .pm = &qt602240_pm_ops,
-#endif
- },
- .probe = qt602240_probe,
- .remove = __devexit_p(qt602240_remove),
- .id_table = qt602240_id,
-};
-
-static int __init qt602240_init(void)
-{
- return i2c_add_driver(&qt602240_driver);
-}
-
-static void __exit qt602240_exit(void)
-{
- i2c_del_driver(&qt602240_driver);
-}
-
-module_init(qt602240_init);
-module_exit(qt602240_exit);
-
-/* Module information */
-MODULE_AUTHOR("Joonyoung Shim <jy0922.shim@samsung.com>");
-MODULE_DESCRIPTION("AT42QT602240/ATMXT224 Touchscreen driver");
-MODULE_LICENSE("GPL");
diff --git a/drivers/input/touchscreen/tsc2005.c b/drivers/input/touchscreen/tsc2005.c
new file mode 100644
index 00000000000..87420616efa
--- /dev/null
+++ b/drivers/input/touchscreen/tsc2005.c
@@ -0,0 +1,756 @@
+/*
+ * TSC2005 touchscreen driver
+ *
+ * Copyright (C) 2006-2010 Nokia Corporation
+ *
+ * Author: Lauri Leukkunen <lauri.leukkunen@nokia.com>
+ * based on TSC2301 driver by Klaus K. Pedersen <klaus.k.pedersen@nokia.com>
+ *
+ * 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; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+ */
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/input.h>
+#include <linux/interrupt.h>
+#include <linux/delay.h>
+#include <linux/pm.h>
+#include <linux/spi/spi.h>
+#include <linux/spi/tsc2005.h>
+
+/*
+ * The touchscreen interface operates as follows:
+ *
+ * 1) Pen is pressed against the touchscreen.
+ * 2) TSC2005 performs AD conversion.
+ * 3) After the conversion is done TSC2005 drives DAV line down.
+ * 4) GPIO IRQ is received and tsc2005_irq_thread() is scheduled.
+ * 5) tsc2005_irq_thread() queues up an spi transfer to fetch the x, y, z1, z2
+ * values.
+ * 6) tsc2005_irq_thread() reports coordinates to input layer and sets up
+ * tsc2005_penup_timer() to be called after TSC2005_PENUP_TIME_MS (40ms).
+ * 7) When the penup timer expires, there have not been touch or DAV interrupts
+ * during the last 40ms which means the pen has been lifted.
+ *
+ * ESD recovery via a hardware reset is done if the TSC2005 doesn't respond
+ * after a configurable period (in ms) of activity. If esd_timeout is 0, the
+ * watchdog is disabled.
+ */
+
+/* control byte 1 */
+#define TSC2005_CMD 0x80
+#define TSC2005_CMD_NORMAL 0x00
+#define TSC2005_CMD_STOP 0x01
+#define TSC2005_CMD_12BIT 0x04
+
+/* control byte 0 */
+#define TSC2005_REG_READ 0x0001
+#define TSC2005_REG_PND0 0x0002
+#define TSC2005_REG_X 0x0000
+#define TSC2005_REG_Y 0x0008
+#define TSC2005_REG_Z1 0x0010
+#define TSC2005_REG_Z2 0x0018
+#define TSC2005_REG_TEMP_HIGH 0x0050
+#define TSC2005_REG_CFR0 0x0060
+#define TSC2005_REG_CFR1 0x0068
+#define TSC2005_REG_CFR2 0x0070
+
+/* configuration register 0 */
+#define TSC2005_CFR0_PRECHARGE_276US 0x0040
+#define TSC2005_CFR0_STABTIME_1MS 0x0300
+#define TSC2005_CFR0_CLOCK_1MHZ 0x1000
+#define TSC2005_CFR0_RESOLUTION12 0x2000
+#define TSC2005_CFR0_PENMODE 0x8000
+#define TSC2005_CFR0_INITVALUE (TSC2005_CFR0_STABTIME_1MS | \
+ TSC2005_CFR0_CLOCK_1MHZ | \
+ TSC2005_CFR0_RESOLUTION12 | \
+ TSC2005_CFR0_PRECHARGE_276US | \
+ TSC2005_CFR0_PENMODE)
+
+/* bits common to both read and write of configuration register 0 */
+#define TSC2005_CFR0_RW_MASK 0x3fff
+
+/* configuration register 1 */
+#define TSC2005_CFR1_BATCHDELAY_4MS 0x0003
+#define TSC2005_CFR1_INITVALUE TSC2005_CFR1_BATCHDELAY_4MS
+
+/* configuration register 2 */
+#define TSC2005_CFR2_MAVE_Z 0x0004
+#define TSC2005_CFR2_MAVE_Y 0x0008
+#define TSC2005_CFR2_MAVE_X 0x0010
+#define TSC2005_CFR2_AVG_7 0x0800
+#define TSC2005_CFR2_MEDIUM_15 0x3000
+#define TSC2005_CFR2_INITVALUE (TSC2005_CFR2_MAVE_X | \
+ TSC2005_CFR2_MAVE_Y | \
+ TSC2005_CFR2_MAVE_Z | \
+ TSC2005_CFR2_MEDIUM_15 | \
+ TSC2005_CFR2_AVG_7)
+
+#define MAX_12BIT 0xfff
+#define TSC2005_SPI_MAX_SPEED_HZ 10000000
+#define TSC2005_PENUP_TIME_MS 40
+
+struct tsc2005_spi_rd {
+ struct spi_transfer spi_xfer;
+ u32 spi_tx;
+ u32 spi_rx;
+};
+
+struct tsc2005 {
+ struct spi_device *spi;
+
+ struct spi_message spi_read_msg;
+ struct tsc2005_spi_rd spi_x;
+ struct tsc2005_spi_rd spi_y;
+ struct tsc2005_spi_rd spi_z1;
+ struct tsc2005_spi_rd spi_z2;
+
+ struct input_dev *idev;
+ char phys[32];
+
+ struct mutex mutex;
+
+ /* raw copy of previous x,y,z */
+ int in_x;
+ int in_y;
+ int in_z1;
+ int in_z2;
+
+ spinlock_t lock;
+ struct timer_list penup_timer;
+
+ unsigned int esd_timeout;
+ struct delayed_work esd_work;
+ unsigned long last_valid_interrupt;
+
+ unsigned int x_plate_ohm;
+
+ bool opened;
+ bool suspended;
+
+ bool pen_down;
+
+ void (*set_reset)(bool enable);
+};
+
+static int tsc2005_cmd(struct tsc2005 *ts, u8 cmd)
+{
+ u8 tx = TSC2005_CMD | TSC2005_CMD_12BIT | cmd;
+ struct spi_transfer xfer = {
+ .tx_buf = &tx,
+ .len = 1,
+ .bits_per_word = 8,
+ };
+ struct spi_message msg;
+ int error;
+
+ spi_message_init(&msg);
+ spi_message_add_tail(&xfer, &msg);
+
+ error = spi_sync(ts->spi, &msg);
+ if (error) {
+ dev_err(&ts->spi->dev, "%s: failed, command: %x, error: %d\n",
+ __func__, cmd, error);
+ return error;
+ }
+
+ return 0;
+}
+
+static int tsc2005_write(struct tsc2005 *ts, u8 reg, u16 value)
+{
+ u32 tx = ((reg | TSC2005_REG_PND0) << 16) | value;
+ struct spi_transfer xfer = {
+ .tx_buf = &tx,
+ .len = 4,
+ .bits_per_word = 24,
+ };
+ struct spi_message msg;
+ int error;
+
+ spi_message_init(&msg);
+ spi_message_add_tail(&xfer, &msg);
+
+ error = spi_sync(ts->spi, &msg);
+ if (error) {
+ dev_err(&ts->spi->dev,
+ "%s: failed, register: %x, value: %x, error: %d\n",
+ __func__, reg, value, error);
+ return error;
+ }
+
+ return 0;
+}
+
+static void tsc2005_setup_read(struct tsc2005_spi_rd *rd, u8 reg, bool last)
+{
+ memset(rd, 0, sizeof(*rd));
+
+ rd->spi_tx = (reg | TSC2005_REG_READ) << 16;
+ rd->spi_xfer.tx_buf = &rd->spi_tx;
+ rd->spi_xfer.rx_buf = &rd->spi_rx;
+ rd->spi_xfer.len = 4;
+ rd->spi_xfer.bits_per_word = 24;
+ rd->spi_xfer.cs_change = !last;
+}
+
+static int tsc2005_read(struct tsc2005 *ts, u8 reg, u16 *value)
+{
+ struct tsc2005_spi_rd spi_rd;
+ struct spi_message msg;
+ int error;
+
+ tsc2005_setup_read(&spi_rd, reg, true);
+
+ spi_message_init(&msg);
+ spi_message_add_tail(&spi_rd.spi_xfer, &msg);
+
+ error = spi_sync(ts->spi, &msg);
+ if (error)
+ return error;
+
+ *value = spi_rd.spi_rx;
+ return 0;
+}
+
+static void tsc2005_update_pen_state(struct tsc2005 *ts,
+ int x, int y, int pressure)
+{
+ if (pressure) {
+ input_report_abs(ts->idev, ABS_X, x);
+ input_report_abs(ts->idev, ABS_Y, y);
+ input_report_abs(ts->idev, ABS_PRESSURE, pressure);
+ if (!ts->pen_down) {
+ input_report_key(ts->idev, BTN_TOUCH, !!pressure);
+ ts->pen_down = true;
+ }
+ } else {
+ input_report_abs(ts->idev, ABS_PRESSURE, 0);
+ if (ts->pen_down) {
+ input_report_key(ts->idev, BTN_TOUCH, 0);
+ ts->pen_down = false;
+ }
+ }
+ input_sync(ts->idev);
+ dev_dbg(&ts->spi->dev, "point(%4d,%4d), pressure (%4d)\n", x, y,
+ pressure);
+}
+
+static irqreturn_t tsc2005_irq_thread(int irq, void *_ts)
+{
+ struct tsc2005 *ts = _ts;
+ unsigned long flags;
+ unsigned int pressure;
+ u32 x, y;
+ u32 z1, z2;
+ int error;
+
+ /* read the coordinates */
+ error = spi_sync(ts->spi, &ts->spi_read_msg);
+ if (unlikely(error))
+ goto out;
+
+ x = ts->spi_x.spi_rx;
+ y = ts->spi_y.spi_rx;
+ z1 = ts->spi_z1.spi_rx;
+ z2 = ts->spi_z2.spi_rx;
+
+ /* validate position */
+ if (unlikely(x > MAX_12BIT || y > MAX_12BIT))
+ goto out;
+
+ /* Skip reading if the pressure components are out of range */
+ if (unlikely(z1 == 0 || z2 > MAX_12BIT || z1 >= z2))
+ goto out;
+
+ /*
+ * Skip point if this is a pen down with the exact same values as
+ * the value before pen-up - that implies SPI fed us stale data
+ */
+ if (!ts->pen_down &&
+ ts->in_x == x && ts->in_y == y &&
+ ts->in_z1 == z1 && ts->in_z2 == z2) {
+ goto out;
+ }
+
+ /*
+ * At this point we are happy we have a valid and useful reading.
+ * Remember it for later comparisons. We may now begin downsampling.
+ */
+ ts->in_x = x;
+ ts->in_y = y;
+ ts->in_z1 = z1;
+ ts->in_z2 = z2;
+
+ /* Compute touch pressure resistance using equation #1 */
+ pressure = x * (z2 - z1) / z1;
+ pressure = pressure * ts->x_plate_ohm / 4096;
+ if (unlikely(pressure > MAX_12BIT))
+ goto out;
+
+ spin_lock_irqsave(&ts->lock, flags);
+
+ tsc2005_update_pen_state(ts, x, y, pressure);
+ mod_timer(&ts->penup_timer,
+ jiffies + msecs_to_jiffies(TSC2005_PENUP_TIME_MS));
+
+ spin_unlock_irqrestore(&ts->lock, flags);
+
+ ts->last_valid_interrupt = jiffies;
+out:
+ return IRQ_HANDLED;
+}
+
+static void tsc2005_penup_timer(unsigned long data)
+{
+ struct tsc2005 *ts = (struct tsc2005 *)data;
+ unsigned long flags;
+
+ spin_lock_irqsave(&ts->lock, flags);
+ tsc2005_update_pen_state(ts, 0, 0, 0);
+ spin_unlock_irqrestore(&ts->lock, flags);
+}
+
+static void tsc2005_start_scan(struct tsc2005 *ts)
+{
+ tsc2005_write(ts, TSC2005_REG_CFR0, TSC2005_CFR0_INITVALUE);
+ tsc2005_write(ts, TSC2005_REG_CFR1, TSC2005_CFR1_INITVALUE);
+ tsc2005_write(ts, TSC2005_REG_CFR2, TSC2005_CFR2_INITVALUE);
+ tsc2005_cmd(ts, TSC2005_CMD_NORMAL);
+}
+
+static void tsc2005_stop_scan(struct tsc2005 *ts)
+{
+ tsc2005_cmd(ts, TSC2005_CMD_STOP);
+}
+
+/* must be called with ts->mutex held */
+static void __tsc2005_disable(struct tsc2005 *ts)
+{
+ tsc2005_stop_scan(ts);
+
+ disable_irq(ts->spi->irq);
+ del_timer_sync(&ts->penup_timer);
+
+ cancel_delayed_work_sync(&ts->esd_work);
+
+ enable_irq(ts->spi->irq);
+}
+
+/* must be called with ts->mutex held */
+static void __tsc2005_enable(struct tsc2005 *ts)
+{
+ tsc2005_start_scan(ts);
+
+ if (ts->esd_timeout && ts->set_reset) {
+ ts->last_valid_interrupt = jiffies;
+ schedule_delayed_work(&ts->esd_work,
+ round_jiffies(jiffies +
+ msecs_to_jiffies(ts->esd_timeout)));
+ }
+
+}
+
+static ssize_t tsc2005_selftest_show(struct device *dev,
+ struct device_attribute *attr,
+ char *buf)
+{
+ struct spi_device *spi = to_spi_device(dev);
+ struct tsc2005 *ts = spi_get_drvdata(spi);
+ u16 temp_high;
+ u16 temp_high_orig;
+ u16 temp_high_test;
+ bool success = true;
+ int error;
+
+ mutex_lock(&ts->mutex);
+
+ /*
+ * Test TSC2005 communications via temp high register.
+ */
+ __tsc2005_disable(ts);
+
+ error = tsc2005_read(ts, TSC2005_REG_TEMP_HIGH, &temp_high_orig);
+ if (error) {
+ dev_warn(dev, "selftest failed: read error %d\n", error);
+ success = false;
+ goto out;
+ }
+
+ temp_high_test = (temp_high_orig - 1) & MAX_12BIT;
+
+ error = tsc2005_write(ts, TSC2005_REG_TEMP_HIGH, temp_high_test);
+ if (error) {
+ dev_warn(dev, "selftest failed: write error %d\n", error);
+ success = false;
+ goto out;
+ }
+
+ error = tsc2005_read(ts, TSC2005_REG_TEMP_HIGH, &temp_high);
+ if (error) {
+ dev_warn(dev, "selftest failed: read error %d after write\n",
+ error);
+ success = false;
+ goto out;
+ }
+
+ if (temp_high != temp_high_test) {
+ dev_warn(dev, "selftest failed: %d != %d\n",
+ temp_high, temp_high_test);
+ success = false;
+ }
+
+ /* hardware reset */
+ ts->set_reset(false);
+ usleep_range(100, 500); /* only 10us required */
+ ts->set_reset(true);
+
+ if (!success)
+ goto out;
+
+ /* test that the reset really happened */
+ error = tsc2005_read(ts, TSC2005_REG_TEMP_HIGH, &temp_high);
+ if (error) {
+ dev_warn(dev, "selftest failed: read error %d after reset\n",
+ error);
+ success = false;
+ goto out;
+ }
+
+ if (temp_high != temp_high_orig) {
+ dev_warn(dev, "selftest failed after reset: %d != %d\n",
+ temp_high, temp_high_orig);
+ success = false;
+ }
+
+out:
+ __tsc2005_enable(ts);
+ mutex_unlock(&ts->mutex);
+
+ return sprintf(buf, "%d\n", success);
+}
+
+static DEVICE_ATTR(selftest, S_IRUGO, tsc2005_selftest_show, NULL);
+
+static struct attribute *tsc2005_attrs[] = {
+ &dev_attr_selftest.attr,
+ NULL
+};
+
+static mode_t tsc2005_attr_is_visible(struct kobject *kobj,
+ struct attribute *attr, int n)
+{
+ struct device *dev = container_of(kobj, struct device, kobj);
+ struct spi_device *spi = to_spi_device(dev);
+ struct tsc2005 *ts = spi_get_drvdata(spi);
+ mode_t mode = attr->mode;
+
+ if (attr == &dev_attr_selftest.attr) {
+ if (!ts->set_reset)
+ mode = 0;
+ }
+
+ return mode;
+}
+
+static const struct attribute_group tsc2005_attr_group = {
+ .is_visible = tsc2005_attr_is_visible,
+ .attrs = tsc2005_attrs,
+};
+
+static void tsc2005_esd_work(struct work_struct *work)
+{
+ struct tsc2005 *ts = container_of(work, struct tsc2005, esd_work.work);
+ int error;
+ u16 r;
+
+ mutex_lock(&ts->mutex);
+
+ if (time_is_after_jiffies(ts->last_valid_interrupt +
+ msecs_to_jiffies(ts->esd_timeout)))
+ goto out;
+
+ /* We should be able to read register without disabling interrupts. */
+ error = tsc2005_read(ts, TSC2005_REG_CFR0, &r);
+ if (!error &&
+ !((r ^ TSC2005_CFR0_INITVALUE) & TSC2005_CFR0_RW_MASK)) {
+ goto out;
+ }
+
+ /*
+ * If we could not read our known value from configuration register 0
+ * then we should reset the controller as if from power-up and start
+ * scanning again.
+ */
+ dev_info(&ts->spi->dev, "TSC2005 not responding - resetting\n");
+
+ disable_irq(ts->spi->irq);
+ del_timer_sync(&ts->penup_timer);
+
+ tsc2005_update_pen_state(ts, 0, 0, 0);
+
+ ts->set_reset(false);
+ usleep_range(100, 500); /* only 10us required */
+ ts->set_reset(true);
+
+ enable_irq(ts->spi->irq);
+ tsc2005_start_scan(ts);
+
+out:
+ /* re-arm the watchdog */
+ schedule_delayed_work(&ts->esd_work,
+ round_jiffies(jiffies +
+ msecs_to_jiffies(ts->esd_timeout)));
+ mutex_unlock(&ts->mutex);
+}
+
+static int tsc2005_open(struct input_dev *input)
+{
+ struct tsc2005 *ts = input_get_drvdata(input);
+
+ mutex_lock(&ts->mutex);
+
+ if (!ts->suspended)
+ __tsc2005_enable(ts);
+
+ ts->opened = true;
+
+ mutex_unlock(&ts->mutex);
+
+ return 0;
+}
+
+static void tsc2005_close(struct input_dev *input)
+{
+ struct tsc2005 *ts = input_get_drvdata(input);
+
+ mutex_lock(&ts->mutex);
+
+ if (!ts->suspended)
+ __tsc2005_disable(ts);
+
+ ts->opened = false;
+
+ mutex_unlock(&ts->mutex);
+}
+
+static void __devinit tsc2005_setup_spi_xfer(struct tsc2005 *ts)
+{
+ tsc2005_setup_read(&ts->spi_x, TSC2005_REG_X, false);
+ tsc2005_setup_read(&ts->spi_y, TSC2005_REG_Y, false);
+ tsc2005_setup_read(&ts->spi_z1, TSC2005_REG_Z1, false);
+ tsc2005_setup_read(&ts->spi_z2, TSC2005_REG_Z2, true);
+
+ spi_message_init(&ts->spi_read_msg);
+ spi_message_add_tail(&ts->spi_x.spi_xfer, &ts->spi_read_msg);
+ spi_message_add_tail(&ts->spi_y.spi_xfer, &ts->spi_read_msg);
+ spi_message_add_tail(&ts->spi_z1.spi_xfer, &ts->spi_read_msg);
+ spi_message_add_tail(&ts->spi_z2.spi_xfer, &ts->spi_read_msg);
+}
+
+static int __devinit tsc2005_probe(struct spi_device *spi)
+{
+ const struct tsc2005_platform_data *pdata = spi->dev.platform_data;
+ struct tsc2005 *ts;
+ struct input_dev *input_dev;
+ unsigned int max_x, max_y, max_p;
+ unsigned int fudge_x, fudge_y, fudge_p;
+ int error;
+
+ if (!pdata) {
+ dev_dbg(&spi->dev, "no platform data\n");
+ return -ENODEV;
+ }
+
+ fudge_x = pdata->ts_x_fudge ? : 4;
+ fudge_y = pdata->ts_y_fudge ? : 8;
+ fudge_p = pdata->ts_pressure_fudge ? : 2;
+ max_x = pdata->ts_x_max ? : MAX_12BIT;
+ max_y = pdata->ts_y_max ? : MAX_12BIT;
+ max_p = pdata->ts_pressure_max ? : MAX_12BIT;
+
+ if (spi->irq <= 0) {
+ dev_dbg(&spi->dev, "no irq\n");
+ return -ENODEV;
+ }
+
+ spi->mode = SPI_MODE_0;
+ spi->bits_per_word = 8;
+ if (!spi->max_speed_hz)
+ spi->max_speed_hz = TSC2005_SPI_MAX_SPEED_HZ;
+
+ error = spi_setup(spi);
+ if (error)
+ return error;
+
+ ts = kzalloc(sizeof(*ts), GFP_KERNEL);
+ input_dev = input_allocate_device();
+ if (!ts || !input_dev) {
+ error = -ENOMEM;
+ goto err_free_mem;
+ }
+
+ ts->spi = spi;
+ ts->idev = input_dev;
+
+ ts->x_plate_ohm = pdata->ts_x_plate_ohm ? : 280;
+ ts->esd_timeout = pdata->esd_timeout_ms;
+ ts->set_reset = pdata->set_reset;
+
+ mutex_init(&ts->mutex);
+
+ spin_lock_init(&ts->lock);
+ setup_timer(&ts->penup_timer, tsc2005_penup_timer, (unsigned long)ts);
+
+ INIT_DELAYED_WORK(&ts->esd_work, tsc2005_esd_work);
+
+ tsc2005_setup_spi_xfer(ts);
+
+ snprintf(ts->phys, sizeof(ts->phys),
+ "%s/input-ts", dev_name(&spi->dev));
+
+ input_dev->name = "TSC2005 touchscreen";
+ input_dev->phys = ts->phys;
+ input_dev->id.bustype = BUS_SPI;
+ input_dev->dev.parent = &spi->dev;
+ input_dev->evbit[0] = BIT(EV_ABS) | BIT(EV_KEY);
+ input_dev->keybit[BIT_WORD(BTN_TOUCH)] = BIT_MASK(BTN_TOUCH);
+
+ input_set_abs_params(input_dev, ABS_X, 0, max_x, fudge_x, 0);
+ input_set_abs_params(input_dev, ABS_Y, 0, max_y, fudge_y, 0);
+ input_set_abs_params(input_dev, ABS_PRESSURE, 0, max_p, fudge_p, 0);
+
+ input_dev->open = tsc2005_open;
+ input_dev->close = tsc2005_close;
+
+ input_set_drvdata(input_dev, ts);
+
+ /* Ensure the touchscreen is off */
+ tsc2005_stop_scan(ts);
+
+ error = request_threaded_irq(spi->irq, NULL, tsc2005_irq_thread,
+ IRQF_TRIGGER_RISING, "tsc2005", ts);
+ if (error) {
+ dev_err(&spi->dev, "Failed to request irq, err: %d\n", error);
+ goto err_free_mem;
+ }
+
+ spi_set_drvdata(spi, ts);
+ error = sysfs_create_group(&spi->dev.kobj, &tsc2005_attr_group);
+ if (error) {
+ dev_err(&spi->dev,
+ "Failed to create sysfs attributes, err: %d\n", error);
+ goto err_clear_drvdata;
+ }
+
+ error = input_register_device(ts->idev);
+ if (error) {
+ dev_err(&spi->dev,
+ "Failed to register input device, err: %d\n", error);
+ goto err_remove_sysfs;
+ }
+
+ set_irq_wake(spi->irq, 1);
+ return 0;
+
+err_remove_sysfs:
+ sysfs_remove_group(&spi->dev.kobj, &tsc2005_attr_group);
+err_clear_drvdata:
+ spi_set_drvdata(spi, NULL);
+ free_irq(spi->irq, ts);
+err_free_mem:
+ input_free_device(input_dev);
+ kfree(ts);
+ return error;
+}
+
+static int __devexit tsc2005_remove(struct spi_device *spi)
+{
+ struct tsc2005 *ts = spi_get_drvdata(spi);
+
+ sysfs_remove_group(&ts->spi->dev.kobj, &tsc2005_attr_group);
+
+ free_irq(ts->spi->irq, ts);
+ input_unregister_device(ts->idev);
+ kfree(ts);
+
+ spi_set_drvdata(spi, NULL);
+ return 0;
+}
+
+#ifdef CONFIG_PM_SLEEP
+static int tsc2005_suspend(struct device *dev)
+{
+ struct spi_device *spi = to_spi_device(dev);
+ struct tsc2005 *ts = spi_get_drvdata(spi);
+
+ mutex_lock(&ts->mutex);
+
+ if (!ts->suspended && ts->opened)
+ __tsc2005_disable(ts);
+
+ ts->suspended = true;
+
+ mutex_unlock(&ts->mutex);
+
+ return 0;
+}
+
+static int tsc2005_resume(struct device *dev)
+{
+ struct spi_device *spi = to_spi_device(dev);
+ struct tsc2005 *ts = spi_get_drvdata(spi);
+
+ mutex_lock(&ts->mutex);
+
+ if (ts->suspended && ts->opened)
+ __tsc2005_enable(ts);
+
+ ts->suspended = false;
+
+ mutex_unlock(&ts->mutex);
+
+ return 0;
+}
+#endif
+
+static SIMPLE_DEV_PM_OPS(tsc2005_pm_ops, tsc2005_suspend, tsc2005_resume);
+
+static struct spi_driver tsc2005_driver = {
+ .driver = {
+ .name = "tsc2005",
+ .owner = THIS_MODULE,
+ .pm = &tsc2005_pm_ops,
+ },
+ .probe = tsc2005_probe,
+ .remove = __devexit_p(tsc2005_remove),
+};
+
+static int __init tsc2005_init(void)
+{
+ return spi_register_driver(&tsc2005_driver);
+}
+module_init(tsc2005_init);
+
+static void __exit tsc2005_exit(void)
+{
+ spi_unregister_driver(&tsc2005_driver);
+}
+module_exit(tsc2005_exit);
+
+MODULE_AUTHOR("Lauri Leukkunen <lauri.leukkunen@nokia.com>");
+MODULE_DESCRIPTION("TSC2005 Touchscreen Driver");
+MODULE_LICENSE("GPL");
diff --git a/drivers/input/touchscreen/wm831x-ts.c b/drivers/input/touchscreen/wm831x-ts.c
new file mode 100644
index 00000000000..6ae054f8e0a
--- /dev/null
+++ b/drivers/input/touchscreen/wm831x-ts.c
@@ -0,0 +1,368 @@
+/*
+ * Touchscreen driver for WM831x PMICs
+ *
+ * Copyright 2011 Wolfson Microelectronics plc.
+ * Author: Mark Brown <broonie@opensource.wolfsonmicro.com>
+ *
+ * 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; either version 2 of the License, or (at your
+ * option) any later version.
+ */
+
+#include <linux/module.h>
+#include <linux/moduleparam.h>
+#include <linux/kernel.h>
+#include <linux/init.h>
+#include <linux/string.h>
+#include <linux/pm.h>
+#include <linux/input.h>
+#include <linux/interrupt.h>
+#include <linux/io.h>
+#include <linux/mfd/wm831x/core.h>
+#include <linux/mfd/wm831x/irq.h>
+#include <linux/mfd/wm831x/pdata.h>
+#include <linux/platform_device.h>
+#include <linux/slab.h>
+#include <linux/types.h>
+
+/*
+ * R16424 (0x4028) - Touch Control 1
+ */
+#define WM831X_TCH_ENA 0x8000 /* TCH_ENA */
+#define WM831X_TCH_CVT_ENA 0x4000 /* TCH_CVT_ENA */
+#define WM831X_TCH_SLPENA 0x1000 /* TCH_SLPENA */
+#define WM831X_TCH_Z_ENA 0x0400 /* TCH_Z_ENA */
+#define WM831X_TCH_Y_ENA 0x0200 /* TCH_Y_ENA */
+#define WM831X_TCH_X_ENA 0x0100 /* TCH_X_ENA */
+#define WM831X_TCH_DELAY_MASK 0x00E0 /* TCH_DELAY - [7:5] */
+#define WM831X_TCH_DELAY_SHIFT 5 /* TCH_DELAY - [7:5] */
+#define WM831X_TCH_DELAY_WIDTH 3 /* TCH_DELAY - [7:5] */
+#define WM831X_TCH_RATE_MASK 0x001F /* TCH_RATE - [4:0] */
+#define WM831X_TCH_RATE_SHIFT 0 /* TCH_RATE - [4:0] */
+#define WM831X_TCH_RATE_WIDTH 5 /* TCH_RATE - [4:0] */
+
+/*
+ * R16425 (0x4029) - Touch Control 2
+ */
+#define WM831X_TCH_PD_WK 0x2000 /* TCH_PD_WK */
+#define WM831X_TCH_5WIRE 0x1000 /* TCH_5WIRE */
+#define WM831X_TCH_PDONLY 0x0800 /* TCH_PDONLY */
+#define WM831X_TCH_ISEL 0x0100 /* TCH_ISEL */
+#define WM831X_TCH_RPU_MASK 0x000F /* TCH_RPU - [3:0] */
+#define WM831X_TCH_RPU_SHIFT 0 /* TCH_RPU - [3:0] */
+#define WM831X_TCH_RPU_WIDTH 4 /* TCH_RPU - [3:0] */
+
+/*
+ * R16426-8 (0x402A-C) - Touch Data X/Y/X
+ */
+#define WM831X_TCH_PD 0x8000 /* TCH_PD1 */
+#define WM831X_TCH_DATA_MASK 0x0FFF /* TCH_DATA - [11:0] */
+#define WM831X_TCH_DATA_SHIFT 0 /* TCH_DATA - [11:0] */
+#define WM831X_TCH_DATA_WIDTH 12 /* TCH_DATA - [11:0] */
+
+struct wm831x_ts {
+ struct input_dev *input_dev;
+ struct wm831x *wm831x;
+ unsigned int data_irq;
+ unsigned int pd_irq;
+ bool pressure;
+ bool pen_down;
+};
+
+static irqreturn_t wm831x_ts_data_irq(int irq, void *irq_data)
+{
+ struct wm831x_ts *wm831x_ts = irq_data;
+ struct wm831x *wm831x = wm831x_ts->wm831x;
+ static int data_types[] = { ABS_X, ABS_Y, ABS_PRESSURE };
+ u16 data[3];
+ int count;
+ int i, ret;
+
+ if (wm831x_ts->pressure)
+ count = 3;
+ else
+ count = 2;
+
+ wm831x_set_bits(wm831x, WM831X_INTERRUPT_STATUS_1,
+ WM831X_TCHDATA_EINT, WM831X_TCHDATA_EINT);
+
+ ret = wm831x_bulk_read(wm831x, WM831X_TOUCH_DATA_X, count,
+ data);
+ if (ret != 0) {
+ dev_err(wm831x->dev, "Failed to read touch data: %d\n",
+ ret);
+ return IRQ_NONE;
+ }
+
+ /*
+ * We get a pen down reading on every reading, report pen up if any
+ * individual reading does so.
+ */
+ wm831x_ts->pen_down = true;
+ for (i = 0; i < count; i++) {
+ if (!(data[i] & WM831X_TCH_PD)) {
+ wm831x_ts->pen_down = false;
+ continue;
+ }
+ input_report_abs(wm831x_ts->input_dev, data_types[i],
+ data[i] & WM831X_TCH_DATA_MASK);
+ }
+
+ if (!wm831x_ts->pen_down) {
+ disable_irq_nosync(wm831x_ts->data_irq);
+
+ /* Don't need data any more */
+ wm831x_set_bits(wm831x, WM831X_TOUCH_CONTROL_1,
+ WM831X_TCH_X_ENA | WM831X_TCH_Y_ENA |
+ WM831X_TCH_Z_ENA, 0);
+
+ /* Flush any final samples that arrived while reading */
+ wm831x_set_bits(wm831x, WM831X_INTERRUPT_STATUS_1,
+ WM831X_TCHDATA_EINT, WM831X_TCHDATA_EINT);
+
+ wm831x_bulk_read(wm831x, WM831X_TOUCH_DATA_X, count, data);
+
+ if (wm831x_ts->pressure)
+ input_report_abs(wm831x_ts->input_dev,
+ ABS_PRESSURE, 0);
+
+ input_report_key(wm831x_ts->input_dev, BTN_TOUCH, 0);
+ }
+
+ input_sync(wm831x_ts->input_dev);
+
+ return IRQ_HANDLED;
+}
+
+static irqreturn_t wm831x_ts_pen_down_irq(int irq, void *irq_data)
+{
+ struct wm831x_ts *wm831x_ts = irq_data;
+ struct wm831x *wm831x = wm831x_ts->wm831x;
+ int ena = 0;
+
+ /* Start collecting data */
+ if (wm831x_ts->pressure)
+ ena |= WM831X_TCH_Z_ENA;
+
+ wm831x_set_bits(wm831x, WM831X_TOUCH_CONTROL_1,
+ WM831X_TCH_X_ENA | WM831X_TCH_Y_ENA | WM831X_TCH_Z_ENA,
+ WM831X_TCH_X_ENA | WM831X_TCH_Y_ENA | ena);
+
+ input_report_key(wm831x_ts->input_dev, BTN_TOUCH, 1);
+ input_sync(wm831x_ts->input_dev);
+
+ wm831x_set_bits(wm831x, WM831X_INTERRUPT_STATUS_1,
+ WM831X_TCHPD_EINT, WM831X_TCHPD_EINT);
+
+ wm831x_ts->pen_down = true;
+ enable_irq(wm831x_ts->data_irq);
+
+ return IRQ_HANDLED;
+}
+
+static int wm831x_ts_input_open(struct input_dev *idev)
+{
+ struct wm831x_ts *wm831x_ts = input_get_drvdata(idev);
+ struct wm831x *wm831x = wm831x_ts->wm831x;
+
+ wm831x_set_bits(wm831x, WM831X_TOUCH_CONTROL_1,
+ WM831X_TCH_ENA | WM831X_TCH_CVT_ENA |
+ WM831X_TCH_X_ENA | WM831X_TCH_Y_ENA |
+ WM831X_TCH_Z_ENA, WM831X_TCH_ENA);
+
+ wm831x_set_bits(wm831x, WM831X_TOUCH_CONTROL_1,
+ WM831X_TCH_CVT_ENA, WM831X_TCH_CVT_ENA);
+
+ return 0;
+}
+
+static void wm831x_ts_input_close(struct input_dev *idev)
+{
+ struct wm831x_ts *wm831x_ts = input_get_drvdata(idev);
+ struct wm831x *wm831x = wm831x_ts->wm831x;
+
+ wm831x_set_bits(wm831x, WM831X_TOUCH_CONTROL_1,
+ WM831X_TCH_ENA | WM831X_TCH_CVT_ENA |
+ WM831X_TCH_X_ENA | WM831X_TCH_Y_ENA |
+ WM831X_TCH_Z_ENA, 0);
+
+ if (wm831x_ts->pen_down)
+ disable_irq(wm831x_ts->data_irq);
+}
+
+static __devinit int wm831x_ts_probe(struct platform_device *pdev)
+{
+ struct wm831x_ts *wm831x_ts;
+ struct wm831x *wm831x = dev_get_drvdata(pdev->dev.parent);
+ struct wm831x_pdata *core_pdata = dev_get_platdata(pdev->dev.parent);
+ struct wm831x_touch_pdata *pdata = NULL;
+ struct input_dev *input_dev;
+ int error;
+
+ if (core_pdata)
+ pdata = core_pdata->touch;
+
+ wm831x_ts = kzalloc(sizeof(struct wm831x_ts), GFP_KERNEL);
+ input_dev = input_allocate_device();
+ if (!wm831x_ts || !input_dev) {
+ error = -ENOMEM;
+ goto err_alloc;
+ }
+
+ wm831x_ts->wm831x = wm831x;
+ wm831x_ts->input_dev = input_dev;
+
+ /*
+ * If we have a direct IRQ use it, otherwise use the interrupt
+ * from the WM831x IRQ controller.
+ */
+ if (pdata && pdata->data_irq)
+ wm831x_ts->data_irq = pdata->data_irq;
+ else
+ wm831x_ts->data_irq = platform_get_irq_byname(pdev, "TCHDATA");
+
+ if (pdata && pdata->pd_irq)
+ wm831x_ts->pd_irq = pdata->pd_irq;
+ else
+ wm831x_ts->pd_irq = platform_get_irq_byname(pdev, "TCHPD");
+
+ if (pdata)
+ wm831x_ts->pressure = pdata->pressure;
+ else
+ wm831x_ts->pressure = true;
+
+ /* Five wire touchscreens can't report pressure */
+ if (pdata && pdata->fivewire) {
+ wm831x_set_bits(wm831x, WM831X_TOUCH_CONTROL_2,
+ WM831X_TCH_5WIRE, WM831X_TCH_5WIRE);
+
+ /* Pressure measurements are not possible for five wire mode */
+ WARN_ON(pdata->pressure && pdata->fivewire);
+ wm831x_ts->pressure = false;
+ } else {
+ wm831x_set_bits(wm831x, WM831X_TOUCH_CONTROL_2,
+ WM831X_TCH_5WIRE, 0);
+ }
+
+ if (pdata) {
+ switch (pdata->isel) {
+ default:
+ dev_err(&pdev->dev, "Unsupported ISEL setting: %d\n",
+ pdata->isel);
+ /* Fall through */
+ case 200:
+ case 0:
+ wm831x_set_bits(wm831x, WM831X_TOUCH_CONTROL_2,
+ WM831X_TCH_ISEL, 0);
+ break;
+ case 400:
+ wm831x_set_bits(wm831x, WM831X_TOUCH_CONTROL_2,
+ WM831X_TCH_ISEL, WM831X_TCH_ISEL);
+ break;
+ }
+ }
+
+ wm831x_set_bits(wm831x, WM831X_TOUCH_CONTROL_2,
+ WM831X_TCH_PDONLY, 0);
+
+ /* Default to 96 samples/sec */
+ wm831x_set_bits(wm831x, WM831X_TOUCH_CONTROL_1,
+ WM831X_TCH_RATE_MASK, 6);
+
+ error = request_threaded_irq(wm831x_ts->data_irq,
+ NULL, wm831x_ts_data_irq,
+ IRQF_ONESHOT,
+ "Touchscreen data", wm831x_ts);
+ if (error) {
+ dev_err(&pdev->dev, "Failed to request data IRQ %d: %d\n",
+ wm831x_ts->data_irq, error);
+ goto err_alloc;
+ }
+ disable_irq(wm831x_ts->data_irq);
+
+ error = request_threaded_irq(wm831x_ts->pd_irq,
+ NULL, wm831x_ts_pen_down_irq,
+ IRQF_ONESHOT,
+ "Touchscreen pen down", wm831x_ts);
+ if (error) {
+ dev_err(&pdev->dev, "Failed to request pen down IRQ %d: %d\n",
+ wm831x_ts->pd_irq, error);
+ goto err_data_irq;
+ }
+
+ /* set up touch configuration */
+ input_dev->name = "WM831x touchscreen";
+ input_dev->phys = "wm831x";
+ input_dev->open = wm831x_ts_input_open;
+ input_dev->close = wm831x_ts_input_close;
+
+ __set_bit(EV_ABS, input_dev->evbit);
+ __set_bit(EV_KEY, input_dev->evbit);
+ __set_bit(BTN_TOUCH, input_dev->keybit);
+
+ input_set_abs_params(input_dev, ABS_X, 0, 4095, 5, 0);
+ input_set_abs_params(input_dev, ABS_Y, 0, 4095, 5, 0);
+ if (wm831x_ts->pressure)
+ input_set_abs_params(input_dev, ABS_PRESSURE, 0, 4095, 5, 0);
+
+ input_set_drvdata(input_dev, wm831x_ts);
+ input_dev->dev.parent = &pdev->dev;
+
+ error = input_register_device(input_dev);
+ if (error)
+ goto err_pd_irq;
+
+ platform_set_drvdata(pdev, wm831x_ts);
+ return 0;
+
+err_pd_irq:
+ free_irq(wm831x_ts->pd_irq, wm831x_ts);
+err_data_irq:
+ free_irq(wm831x_ts->data_irq, wm831x_ts);
+err_alloc:
+ input_free_device(input_dev);
+ kfree(wm831x_ts);
+
+ return error;
+}
+
+static __devexit int wm831x_ts_remove(struct platform_device *pdev)
+{
+ struct wm831x_ts *wm831x_ts = platform_get_drvdata(pdev);
+
+ free_irq(wm831x_ts->pd_irq, wm831x_ts);
+ free_irq(wm831x_ts->data_irq, wm831x_ts);
+ input_unregister_device(wm831x_ts->input_dev);
+ kfree(wm831x_ts);
+
+ platform_set_drvdata(pdev, NULL);
+ return 0;
+}
+
+static struct platform_driver wm831x_ts_driver = {
+ .driver = {
+ .name = "wm831x-touch",
+ .owner = THIS_MODULE,
+ },
+ .probe = wm831x_ts_probe,
+ .remove = __devexit_p(wm831x_ts_remove),
+};
+
+static int __init wm831x_ts_init(void)
+{
+ return platform_driver_register(&wm831x_ts_driver);
+}
+module_init(wm831x_ts_init);
+
+static void __exit wm831x_ts_exit(void)
+{
+ platform_driver_unregister(&wm831x_ts_driver);
+}
+module_exit(wm831x_ts_exit);
+
+/* Module information */
+MODULE_AUTHOR("Mark Brown <broonie@opensource.wolfsonmicro.com>");
+MODULE_DESCRIPTION("WM831x PMIC touchscreen driver");
+MODULE_LICENSE("GPL");
+MODULE_ALIAS("platform:wm831x-touch");