aboutsummaryrefslogtreecommitdiff
path: root/drivers/input
diff options
context:
space:
mode:
authorDmitry Torokhov <dmitry.torokhov@gmail.com>2011-03-18 23:38:50 -0700
committerDmitry Torokhov <dmitry.torokhov@gmail.com>2011-03-18 23:38:50 -0700
commit97eb3f24352ec6632c2127b35d8087d2a809a9b9 (patch)
tree722948059bbd325bbca232269490124231df80d4 /drivers/input
parent439581ec07fa9cf3f519dd461a2cf41cfd3adcb4 (diff)
parentdef179c271ac9b5020deca798470521f14d11edd (diff)
Merge branch 'next' into for-linus
Diffstat (limited to 'drivers/input')
-rw-r--r--drivers/input/Kconfig16
-rw-r--r--drivers/input/Makefile2
-rw-r--r--drivers/input/gameport/gameport.c2
-rw-r--r--drivers/input/input-polldev.c4
-rw-r--r--drivers/input/input.c56
-rw-r--r--drivers/input/keyboard/Kconfig24
-rw-r--r--drivers/input/keyboard/Makefile2
-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/omap-keypad.c41
-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.c472
-rw-r--r--drivers/input/keyboard/tca6416-keypad.c28
-rw-r--r--drivers/input/misc/Kconfig31
-rw-r--r--drivers/input/misc/Makefile2
-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/winbond-cir.c1608
-rw-r--r--drivers/input/misc/xen-kbdfront.c (renamed from drivers/input/xen-kbdfront.c)83
-rw-r--r--drivers/input/mouse/Kconfig10
-rw-r--r--drivers/input/mouse/bcm5974.c60
-rw-r--r--drivers/input/mouse/synaptics_i2c.c16
-rw-r--r--drivers/input/serio/Kconfig9
-rw-r--r--drivers/input/serio/gscps2.c2
-rw-r--r--drivers/input/serio/serio_raw.c1
-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/Kconfig75
-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
44 files changed, 3675 insertions, 3496 deletions
diff --git a/drivers/input/Kconfig b/drivers/input/Kconfig
index 07c2cd43109..23e82e46656 100644
--- a/drivers/input/Kconfig
+++ b/drivers/input/Kconfig
@@ -6,7 +6,7 @@ menu "Input device support"
depends on !S390
config INPUT
- tristate "Generic input layer (needed for keyboard, mouse, ...)" if EMBEDDED
+ tristate "Generic input layer (needed for keyboard, mouse, ...)" if EXPERT
default y
help
Say Y here if you have any input device (mouse, keyboard, tablet,
@@ -67,7 +67,7 @@ config INPUT_SPARSEKMAP
comment "Userland interfaces"
config INPUT_MOUSEDEV
- tristate "Mouse interface" if EMBEDDED
+ tristate "Mouse interface" if EXPERT
default y
help
Say Y here if you want your mouse to be accessible as char devices
@@ -150,7 +150,7 @@ config INPUT_EVBUG
module will be called evbug.
config INPUT_APMPOWER
- tristate "Input Power Event -> APM Bridge" if EMBEDDED
+ tristate "Input Power Event -> APM Bridge" if EXPERT
depends on INPUT && APM_EMULATION
help
Say Y here if you want suspend key events to trigger a user
@@ -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/gameport/gameport.c b/drivers/input/gameport/gameport.c
index ce57ede6e98..5b8f59d6c3e 100644
--- a/drivers/input/gameport/gameport.c
+++ b/drivers/input/gameport/gameport.c
@@ -121,7 +121,7 @@ static int gameport_measure_speed(struct gameport *gameport)
}
gameport_close(gameport);
- return (cpu_data(raw_smp_processor_id()).loops_per_jiffy *
+ return (this_cpu_read(cpu_info.loops_per_jiffy) *
(unsigned long)HZ / (1000 / 50)) / (tx < 1 ? 1 : tx);
#else
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 b8894a059f8..d6e8bd8a851 100644
--- a/drivers/input/input.c
+++ b/drivers/input/input.c
@@ -26,7 +26,6 @@
#include <linux/device.h>
#include <linux/mutex.h>
#include <linux/rcupdate.h>
-#include <linux/smp_lock.h>
#include "input-compat.h"
MODULE_AUTHOR("Vojtech Pavlik <vojtech@suse.cz>");
@@ -792,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);
@@ -832,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;
@@ -1847,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 b7dca74eb9b..b16bed038f7 100644
--- a/drivers/input/keyboard/Kconfig
+++ b/drivers/input/keyboard/Kconfig
@@ -2,7 +2,7 @@
# Input core configuration
#
menuconfig INPUT_KEYBOARD
- bool "Keyboards" if EMBEDDED || !X86
+ bool "Keyboards" if EXPERT || !X86
default y
help
Say Y here, and a list of supported keyboards will be displayed.
@@ -57,7 +57,7 @@ config KEYBOARD_ATARI
module will be called atakbd.
config KEYBOARD_ATKBD
- tristate "AT keyboard" if EMBEDDED || !X86
+ tristate "AT keyboard" if EXPERT || !X86
default y
select SERIO
select SERIO_LIBPS2
@@ -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
@@ -468,6 +478,16 @@ config KEYBOARD_SPEAR
To compile this driver as a module, choose M here: the
module will be called spear-keboard.
+config KEYBOARD_TC3589X
+ tristate "TC3589X Keypad support"
+ depends on MFD_TC3589X
+ help
+ Say Y here if you want to use the keypad controller on
+ TC35892/3 I/O expander.
+
+ To compile this driver as a module, choose M here: the
+ module will be called tc3589x-keypad.
+
config KEYBOARD_TNETV107X
tristate "TI TNETV107X keypad support"
depends on ARCH_DAVINCI_TNETV107X
diff --git a/drivers/input/keyboard/Makefile b/drivers/input/keyboard/Makefile
index 3456b931e25..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
@@ -41,6 +42,7 @@ obj-$(CONFIG_KEYBOARD_SPEAR) += spear-keyboard.o
obj-$(CONFIG_KEYBOARD_STMPE) += stmpe-keypad.o
obj-$(CONFIG_KEYBOARD_STOWAWAY) += stowaway.o
obj-$(CONFIG_KEYBOARD_SUNKBD) += sunkbd.o
+obj-$(CONFIG_KEYBOARD_TC3589X) += tc3589x-keypad.o
obj-$(CONFIG_KEYBOARD_TEGRA) += tegra-kbc.o
obj-$(CONFIG_KEYBOARD_TNETV107X) += tnetv107x-keypad.o
obj-$(CONFIG_KEYBOARD_TWL4030) += twl4030_keypad.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/omap-keypad.c b/drivers/input/keyboard/omap-keypad.c
index a72e61ddca9..0e2a19cb43d 100644
--- a/drivers/input/keyboard/omap-keypad.c
+++ b/drivers/input/keyboard/omap-keypad.c
@@ -65,7 +65,6 @@ struct omap_kp {
static DECLARE_TASKLET_DISABLED(kp_tasklet, omap_kp_tasklet, 0);
-static int *keymap;
static unsigned int *row_gpios;
static unsigned int *col_gpios;
@@ -162,20 +161,11 @@ static void omap_kp_scan_keypad(struct omap_kp *omap_kp, unsigned char *state)
}
}
-static inline int omap_kp_find_key(int col, int row)
-{
- int i, key;
-
- key = KEY(col, row, 0);
- for (i = 0; keymap[i] != 0; i++)
- if ((keymap[i] & 0xff000000) == key)
- return keymap[i] & 0x00ffffff;
- return -1;
-}
-
static void omap_kp_tasklet(unsigned long data)
{
struct omap_kp *omap_kp_data = (struct omap_kp *) data;
+ unsigned short *keycodes = omap_kp_data->input->keycode;
+ unsigned int row_shift = get_count_order(omap_kp_data->cols);
unsigned char new_state[8], changed, key_down = 0;
int col, row;
int spurious = 0;
@@ -199,7 +189,7 @@ static void omap_kp_tasklet(unsigned long data)
row, (new_state[col] & (1 << row)) ?
"pressed" : "released");
#else
- key = omap_kp_find_key(col, row);
+ key = keycodes[MATRIX_SCAN_CODE(row, col, row_shift)];
if (key < 0) {
printk(KERN_WARNING
"omap-keypad: Spurious key event %d-%d\n",
@@ -298,13 +288,18 @@ static int __devinit omap_kp_probe(struct platform_device *pdev)
struct input_dev *input_dev;
struct omap_kp_platform_data *pdata = pdev->dev.platform_data;
int i, col_idx, row_idx, irq_idx, ret;
+ unsigned int row_shift, keycodemax;
- if (!pdata->rows || !pdata->cols || !pdata->keymap) {
- printk(KERN_ERR "No rows, cols or keymap from pdata\n");
+ if (!pdata->rows || !pdata->cols || !pdata->keymap_data) {
+ printk(KERN_ERR "No rows, cols or keymap_data from pdata\n");
return -EINVAL;
}
- omap_kp = kzalloc(sizeof(struct omap_kp), GFP_KERNEL);
+ row_shift = get_count_order(pdata->cols);
+ keycodemax = pdata->rows << row_shift;
+
+ omap_kp = kzalloc(sizeof(struct omap_kp) +
+ keycodemax * sizeof(unsigned short), GFP_KERNEL);
input_dev = input_allocate_device();
if (!omap_kp || !input_dev) {
kfree(omap_kp);
@@ -320,7 +315,9 @@ static int __devinit omap_kp_probe(struct platform_device *pdev)
if (!cpu_is_omap24xx())
omap_writew(1, OMAP1_MPUIO_BASE + OMAP_MPUIO_KBD_MASKIT);
- keymap = pdata->keymap;
+ input_dev->keycode = &omap_kp[1];
+ input_dev->keycodesize = sizeof(unsigned short);
+ input_dev->keycodemax = keycodemax;
if (pdata->rep)
__set_bit(EV_REP, input_dev->evbit);
@@ -374,8 +371,8 @@ static int __devinit omap_kp_probe(struct platform_device *pdev)
/* setup input device */
__set_bit(EV_KEY, input_dev->evbit);
- for (i = 0; keymap[i] != 0; i++)
- __set_bit(keymap[i] & KEY_MAX, input_dev->keybit);
+ matrix_keypad_build_keymap(pdata->keymap_data, row_shift,
+ input_dev->keycode, input_dev->keybit);
input_dev->name = "omap-keypad";
input_dev->phys = "omap-keypad/input0";
input_dev->dev.parent = &pdev->dev;
@@ -416,7 +413,7 @@ static int __devinit omap_kp_probe(struct platform_device *pdev)
return 0;
err5:
for (i = irq_idx - 1; i >=0; i--)
- free_irq(row_gpios[i], 0);
+ free_irq(row_gpios[i], NULL);
err4:
input_unregister_device(omap_kp->input);
input_dev = NULL;
@@ -447,11 +444,11 @@ static int __devexit omap_kp_remove(struct platform_device *pdev)
gpio_free(col_gpios[i]);
for (i = 0; i < omap_kp->rows; i++) {
gpio_free(row_gpios[i]);
- free_irq(gpio_to_irq(row_gpios[i]), 0);
+ free_irq(gpio_to_irq(row_gpios[i]), NULL);
}
} else {
omap_writew(1, OMAP1_MPUIO_BASE + OMAP_MPUIO_KBD_MASKIT);
- free_irq(omap_kp->irq, 0);
+ free_irq(omap_kp->irq, NULL);
}
del_timer_sync(&omap_kp->timer);
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
new file mode 100644
index 00000000000..99122f59e98
--- /dev/null
+++ b/drivers/input/keyboard/tc3589x-keypad.c
@@ -0,0 +1,472 @@
+/*
+ * Copyright (C) ST-Ericsson SA 2010
+ *
+ * Author: Jayeeta Banerjee <jayeeta.banerjee@stericsson.com>
+ * Author: Sundar Iyer <sundar.iyer@stericsson.com>
+ *
+ * License Terms: GNU General Public License, version 2
+ *
+ * TC35893 MFD Keypad Controller driver
+ */
+
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/interrupt.h>
+#include <linux/input.h>
+#include <linux/platform_device.h>
+#include <linux/input/matrix_keypad.h>
+#include <linux/i2c.h>
+#include <linux/slab.h>
+#include <linux/mfd/tc3589x.h>
+
+/* Maximum supported keypad matrix row/columns size */
+#define TC3589x_MAX_KPROW 8
+#define TC3589x_MAX_KPCOL 12
+
+/* keypad related Constants */
+#define TC3589x_MAX_DEBOUNCE_SETTLE 0xFF
+#define DEDICATED_KEY_VAL 0xFF
+
+/* Pull up/down masks */
+#define TC3589x_NO_PULL_MASK 0x0
+#define TC3589x_PULL_DOWN_MASK 0x1
+#define TC3589x_PULL_UP_MASK 0x2
+#define TC3589x_PULLUP_ALL_MASK 0xAA
+#define TC3589x_IO_PULL_VAL(index, mask) ((mask)<<((index)%4)*2))
+
+/* Bit masks for IOCFG register */
+#define IOCFG_BALLCFG 0x01
+#define IOCFG_IG 0x08
+
+#define KP_EVCODE_COL_MASK 0x0F
+#define KP_EVCODE_ROW_MASK 0x70
+#define KP_RELEASE_EVT_MASK 0x80
+
+#define KP_ROW_SHIFT 4
+
+#define KP_NO_VALID_KEY_MASK 0x7F
+
+/* bit masks for RESTCTRL register */
+#define TC3589x_KBDRST 0x2
+#define TC3589x_IRQRST 0x10
+#define TC3589x_RESET_ALL 0x1B
+
+/* KBDMFS register bit mask */
+#define TC3589x_KBDMFS_EN 0x1
+
+/* CLKEN register bitmask */
+#define KPD_CLK_EN 0x1
+
+/* RSTINTCLR register bit mask */
+#define IRQ_CLEAR 0x1
+
+/* bit masks for keyboard interrupts*/
+#define TC3589x_EVT_LOSS_INT 0x8
+#define TC3589x_EVT_INT 0x4
+#define TC3589x_KBD_LOSS_INT 0x2
+#define TC3589x_KBD_INT 0x1
+
+/* bit masks for keyboard interrupt clear*/
+#define TC3589x_EVT_INT_CLR 0x2
+#define TC3589x_KBD_INT_CLR 0x1
+
+#define TC3589x_KBD_KEYMAP_SIZE 64
+
+/**
+ * struct tc_keypad - data structure used by keypad driver
+ * @input: pointer to input device object
+ * @board: keypad platform device
+ * @krow: number of rows
+ * @kcol: number of coloumns
+ * @keymap: matrix scan code table for keycodes
+ */
+struct tc_keypad {
+ struct tc3589x *tc3589x;
+ struct input_dev *input;
+ const struct tc3589x_keypad_platform_data *board;
+ unsigned int krow;
+ unsigned int kcol;
+ unsigned short keymap[TC3589x_KBD_KEYMAP_SIZE];
+ bool keypad_stopped;
+};
+
+static int __devinit tc3589x_keypad_init_key_hardware(struct tc_keypad *keypad)
+{
+ int ret;
+ struct tc3589x *tc3589x = keypad->tc3589x;
+ u8 settle_time = keypad->board->settle_time;
+ u8 dbounce_period = keypad->board->debounce_period;
+ u8 rows = keypad->board->krow & 0xf; /* mask out the nibble */
+ u8 column = keypad->board->kcol & 0xf; /* mask out the nibble */
+
+ /* validate platform configurations */
+ if (keypad->board->kcol > TC3589x_MAX_KPCOL ||
+ keypad->board->krow > TC3589x_MAX_KPROW ||
+ keypad->board->debounce_period > TC3589x_MAX_DEBOUNCE_SETTLE ||
+ keypad->board->settle_time > TC3589x_MAX_DEBOUNCE_SETTLE)
+ return -EINVAL;
+
+ /* configure KBDSIZE 4 LSbits for cols and 4 MSbits for rows */
+ ret = tc3589x_reg_write(tc3589x, TC3589x_KBDSIZE,
+ (rows << KP_ROW_SHIFT) | column);
+ if (ret < 0)
+ return ret;
+
+ /* configure dedicated key config, no dedicated key selected */
+ ret = tc3589x_reg_write(tc3589x, TC3589x_KBCFG_LSB, DEDICATED_KEY_VAL);
+ if (ret < 0)
+ return ret;
+
+ ret = tc3589x_reg_write(tc3589x, TC3589x_KBCFG_MSB, DEDICATED_KEY_VAL);
+ if (ret < 0)
+ return ret;
+
+ /* Configure settle time */
+ ret = tc3589x_reg_write(tc3589x, TC3589x_KBDSETTLE_REG, settle_time);
+ if (ret < 0)
+ return ret;
+
+ /* Configure debounce time */
+ ret = tc3589x_reg_write(tc3589x, TC3589x_KBDBOUNCE, dbounce_period);
+ if (ret < 0)
+ return ret;
+
+ /* Start of initialise keypad GPIOs */
+ ret = tc3589x_set_bits(tc3589x, TC3589x_IOCFG, 0x0, IOCFG_IG);
+ if (ret < 0)
+ return ret;
+
+ /* Configure pull-up resistors for all row GPIOs */
+ ret = tc3589x_reg_write(tc3589x, TC3589x_IOPULLCFG0_LSB,
+ TC3589x_PULLUP_ALL_MASK);
+ if (ret < 0)
+ return ret;
+
+ ret = tc3589x_reg_write(tc3589x, TC3589x_IOPULLCFG0_MSB,
+ TC3589x_PULLUP_ALL_MASK);
+ if (ret < 0)
+ return ret;
+
+ /* Configure pull-up resistors for all column GPIOs */
+ ret = tc3589x_reg_write(tc3589x, TC3589x_IOPULLCFG1_LSB,
+ TC3589x_PULLUP_ALL_MASK);
+ if (ret < 0)
+ return ret;
+
+ ret = tc3589x_reg_write(tc3589x, TC3589x_IOPULLCFG1_MSB,
+ TC3589x_PULLUP_ALL_MASK);
+ if (ret < 0)
+ return ret;
+
+ ret = tc3589x_reg_write(tc3589x, TC3589x_IOPULLCFG2_LSB,
+ TC3589x_PULLUP_ALL_MASK);
+
+ return ret;
+}
+
+#define TC35893_DATA_REGS 4
+#define TC35893_KEYCODE_FIFO_EMPTY 0x7f
+#define TC35893_KEYCODE_FIFO_CLEAR 0xff
+#define TC35893_KEYPAD_ROW_SHIFT 0x3
+
+static irqreturn_t tc3589x_keypad_irq(int irq, void *dev)
+{
+ struct tc_keypad *keypad = dev;
+ struct tc3589x *tc3589x = keypad->tc3589x;
+ u8 i, row_index, col_index, kbd_code, up;
+ u8 code;
+
+ for (i = 0; i < TC35893_DATA_REGS * 2; i++) {
+ kbd_code = tc3589x_reg_read(tc3589x, TC3589x_EVTCODE_FIFO);
+
+ /* loop till fifo is empty and no more keys are pressed */
+ if (kbd_code == TC35893_KEYCODE_FIFO_EMPTY ||
+ kbd_code == TC35893_KEYCODE_FIFO_CLEAR)
+ continue;
+
+ /* valid key is found */
+ col_index = kbd_code & KP_EVCODE_COL_MASK;
+ row_index = (kbd_code & KP_EVCODE_ROW_MASK) >> KP_ROW_SHIFT;
+ code = MATRIX_SCAN_CODE(row_index, col_index,
+ TC35893_KEYPAD_ROW_SHIFT);
+ up = kbd_code & KP_RELEASE_EVT_MASK;
+
+ input_event(keypad->input, EV_MSC, MSC_SCAN, code);
+ input_report_key(keypad->input, keypad->keymap[code], !up);
+ input_sync(keypad->input);
+ }
+
+ /* clear IRQ */
+ tc3589x_set_bits(tc3589x, TC3589x_KBDIC,
+ 0x0, TC3589x_EVT_INT_CLR | TC3589x_KBD_INT_CLR);
+ /* enable IRQ */
+ tc3589x_set_bits(tc3589x, TC3589x_KBDMSK,
+ 0x0, TC3589x_EVT_LOSS_INT | TC3589x_EVT_INT);
+
+ return IRQ_HANDLED;
+}
+
+static int tc3589x_keypad_enable(struct tc_keypad *keypad)
+{
+ struct tc3589x *tc3589x = keypad->tc3589x;
+ int ret;
+
+ /* pull the keypad module out of reset */
+ ret = tc3589x_set_bits(tc3589x, TC3589x_RSTCTRL, TC3589x_KBDRST, 0x0);
+ if (ret < 0)
+ return ret;
+
+ /* configure KBDMFS */
+ ret = tc3589x_set_bits(tc3589x, TC3589x_KBDMFS, 0x0, TC3589x_KBDMFS_EN);
+ if (ret < 0)
+ return ret;
+
+ /* enable the keypad clock */
+ ret = tc3589x_set_bits(tc3589x, TC3589x_CLKEN, 0x0, KPD_CLK_EN);
+ if (ret < 0)
+ return ret;
+
+ /* clear pending IRQs */
+ ret = tc3589x_set_bits(tc3589x, TC3589x_RSTINTCLR, 0x0, 0x1);
+ if (ret < 0)
+ return ret;
+
+ /* enable the IRQs */
+ ret = tc3589x_set_bits(tc3589x, TC3589x_KBDMSK, 0x0,
+ TC3589x_EVT_LOSS_INT | TC3589x_EVT_INT);
+ if (ret < 0)
+ return ret;
+
+ keypad->keypad_stopped = false;
+
+ return ret;
+}
+
+static int tc3589x_keypad_disable(struct tc_keypad *keypad)
+{
+ struct tc3589x *tc3589x = keypad->tc3589x;
+ int ret;
+
+ /* clear IRQ */
+ ret = tc3589x_set_bits(tc3589x, TC3589x_KBDIC,
+ 0x0, TC3589x_EVT_INT_CLR | TC3589x_KBD_INT_CLR);
+ if (ret < 0)
+ return ret;
+
+ /* disable all interrupts */
+ ret = tc3589x_set_bits(tc3589x, TC3589x_KBDMSK,
+ ~(TC3589x_EVT_LOSS_INT | TC3589x_EVT_INT), 0x0);
+ if (ret < 0)
+ return ret;
+
+ /* disable the keypad module */
+ ret = tc3589x_set_bits(tc3589x, TC3589x_CLKEN, 0x1, 0x0);
+ if (ret < 0)
+ return ret;
+
+ /* put the keypad module into reset */
+ ret = tc3589x_set_bits(tc3589x, TC3589x_RSTCTRL, TC3589x_KBDRST, 0x1);
+
+ keypad->keypad_stopped = true;
+
+ return ret;
+}
+
+static int tc3589x_keypad_open(struct input_dev *input)
+{
+ int error;
+ struct tc_keypad *keypad = input_get_drvdata(input);
+
+ /* enable the keypad module */
+ error = tc3589x_keypad_enable(keypad);
+ if (error < 0) {
+ dev_err(&input->dev, "failed to enable keypad module\n");
+ return error;
+ }
+
+ error = tc3589x_keypad_init_key_hardware(keypad);
+ if (error < 0) {
+ dev_err(&input->dev, "failed to configure keypad module\n");
+ return error;
+ }
+
+ return 0;
+}
+
+static void tc3589x_keypad_close(struct input_dev *input)
+{
+ struct tc_keypad *keypad = input_get_drvdata(input);
+
+ /* disable the keypad module */
+ tc3589x_keypad_disable(keypad);
+}
+
+static int __devinit tc3589x_keypad_probe(struct platform_device *pdev)
+{
+ struct tc3589x *tc3589x = dev_get_drvdata(pdev->dev.parent);
+ struct tc_keypad *keypad;
+ struct input_dev *input;
+ const struct tc3589x_keypad_platform_data *plat;
+ int error, irq;
+
+ plat = tc3589x->pdata->keypad;
+ if (!plat) {
+ dev_err(&pdev->dev, "invalid keypad platform data\n");
+ return -EINVAL;
+ }
+
+ irq = platform_get_irq(pdev, 0);
+ if (irq < 0)
+ return irq;
+
+ keypad = kzalloc(sizeof(struct tc_keypad), GFP_KERNEL);
+ input = input_allocate_device();
+ if (!keypad || !input) {
+ dev_err(&pdev->dev, "failed to allocate keypad memory\n");
+ error = -ENOMEM;
+ goto err_free_mem;
+ }
+
+ keypad->board = plat;
+ keypad->input = input;
+ keypad->tc3589x = tc3589x;
+
+ input->id.bustype = BUS_I2C;
+ input->name = pdev->name;
+ input->dev.parent = &pdev->dev;
+
+ input->keycode = keypad->keymap;
+ input->keycodesize = sizeof(keypad->keymap[0]);
+ input->keycodemax = ARRAY_SIZE(keypad->keymap);
+
+ input->open = tc3589x_keypad_open;
+ input->close = tc3589x_keypad_close;
+
+ input_set_drvdata(input, keypad);
+
+ input_set_capability(input, EV_MSC, MSC_SCAN);
+
+ __set_bit(EV_KEY, input->evbit);
+ if (!plat->no_autorepeat)
+ __set_bit(EV_REP, input->evbit);
+
+ matrix_keypad_build_keymap(plat->keymap_data, 0x3,
+ input->keycode, input->keybit);
+
+ error = request_threaded_irq(irq, NULL,
+ tc3589x_keypad_irq, plat->irqtype,
+ "tc3589x-keypad", keypad);
+ if (error < 0) {
+ dev_err(&pdev->dev,
+ "Could not allocate irq %d,error %d\n",
+ irq, error);
+ goto err_free_mem;
+ }
+
+ error = input_register_device(input);
+ if (error) {
+ dev_err(&pdev->dev, "Could not register input device\n");
+ goto err_free_irq;
+ }
+
+ /* let platform decide if keypad is a wakeup source or not */
+ device_init_wakeup(&pdev->dev, plat->enable_wakeup);
+ device_set_wakeup_capable(&pdev->dev, plat->enable_wakeup);
+
+ platform_set_drvdata(pdev, keypad);
+
+ return 0;
+
+err_free_irq:
+ free_irq(irq, keypad);
+err_free_mem:
+ input_free_device(input);
+ kfree(keypad);
+ return error;
+}
+
+static int __devexit tc3589x_keypad_remove(struct platform_device *pdev)
+{
+ struct tc_keypad *keypad = platform_get_drvdata(pdev);
+ int irq = platform_get_irq(pdev, 0);
+
+ if (!keypad->keypad_stopped)
+ tc3589x_keypad_disable(keypad);
+
+ free_irq(irq, keypad);
+
+ input_unregister_device(keypad->input);
+
+ kfree(keypad);
+
+ return 0;
+}
+
+#ifdef CONFIG_PM_SLEEP
+static int tc3589x_keypad_suspend(struct device *dev)
+{
+ struct platform_device *pdev = to_platform_device(dev);
+ struct tc_keypad *keypad = platform_get_drvdata(pdev);
+ int irq = platform_get_irq(pdev, 0);
+
+ /* keypad is already off; we do nothing */
+ if (keypad->keypad_stopped)
+ return 0;
+
+ /* if device is not a wakeup source, disable it for powersave */
+ if (!device_may_wakeup(&pdev->dev))
+ tc3589x_keypad_disable(keypad);
+ else
+ enable_irq_wake(irq);
+
+ return 0;
+}
+
+static int tc3589x_keypad_resume(struct device *dev)
+{
+ struct platform_device *pdev = to_platform_device(dev);
+ struct tc_keypad *keypad = platform_get_drvdata(pdev);
+ int irq = platform_get_irq(pdev, 0);
+
+ if (!keypad->keypad_stopped)
+ return 0;
+
+ /* enable the device to resume normal operations */
+ if (!device_may_wakeup(&pdev->dev))
+ tc3589x_keypad_enable(keypad);
+ else
+ disable_irq_wake(irq);
+
+ return 0;
+}
+#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",
+ .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)
+{
+ return platform_driver_register(&tc3589x_keypad_driver);
+}
+module_init(tc3589x_keypad_init);
+
+static void __exit tc3589x_keypad_exit(void)
+{
+ return platform_driver_unregister(&tc3589x_keypad_driver);
+}
+module_exit(tc3589x_keypad_exit);
+
+MODULE_LICENSE("GPL v2");
+MODULE_AUTHOR("Jayeeta Banerjee/Sundar Iyer");
+MODULE_DESCRIPTION("TC35893 Keypad Driver");
+MODULE_ALIAS("platform:tc3589x-keypad");
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 f0d90172a65..f9cf0881b0e 100644
--- a/drivers/input/misc/Kconfig
+++ b/drivers/input/misc/Kconfig
@@ -294,24 +294,6 @@ config INPUT_SGI_BTNS
To compile this driver as a module, choose M here: the
module will be called sgi_btns.
-config INPUT_WINBOND_CIR
- tristate "Winbond IR remote control"
- depends on X86 && PNP
- select NEW_LEDS
- select LEDS_CLASS
- select LEDS_TRIGGERS
- select BITREVERSE
- help
- Say Y here if you want to use the IR remote functionality found
- in some Winbond SuperI/O chips. Currently only the WPCD376I
- chip is supported (included in some Intel Media series motherboards).
-
- IR Receive and wake-on-IR from suspend and power-off is currently
- supported.
-
- To compile this driver as a module, choose M here: the module will be
- called winbond_cir.
-
config HP_SDC_RTC
tristate "HP SDC Real Time Clock"
depends on (GSC || HP300) && SERIO
@@ -472,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 35bcfe46555..e3f7984e627 100644
--- a/drivers/input/misc/Makefile
+++ b/drivers/input/misc/Makefile
@@ -40,8 +40,8 @@ obj-$(CONFIG_INPUT_SPARCSPKR) += sparcspkr.o
obj-$(CONFIG_INPUT_TWL4030_PWRBUTTON) += twl4030-pwrbutton.o
obj-$(CONFIG_INPUT_TWL4030_VIBRA) += twl4030-vibra.o
obj-$(CONFIG_INPUT_UINPUT) += uinput.o
-obj-$(CONFIG_INPUT_WINBOND_CIR) += winbond-cir.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/misc/winbond-cir.c b/drivers/input/misc/winbond-cir.c
deleted file mode 100644
index 64f1de7960c..00000000000
--- a/drivers/input/misc/winbond-cir.c
+++ /dev/null
@@ -1,1608 +0,0 @@
-/*
- * winbond-cir.c - Driver for the Consumer IR functionality of Winbond
- * SuperI/O chips.
- *
- * Currently supports the Winbond WPCD376i chip (PNP id WEC1022), but
- * could probably support others (Winbond WEC102X, NatSemi, etc)
- * with minor modifications.
- *
- * Original Author: David Härdeman <david@hardeman.nu>
- * Copyright (C) 2009 David Härdeman <david@hardeman.nu>
- *
- * Dedicated to Matilda, my newborn daughter, without whose loving attention
- * this driver would have been finished in half the time and with a fraction
- * of the bugs.
- *
- * Written using:
- * o Winbond WPCD376I datasheet helpfully provided by Jesse Barnes at Intel
- * o NatSemi PC87338/PC97338 datasheet (for the serial port stuff)
- * o DSDT dumps
- *
- * Supported features:
- * o RC6
- * o Wake-On-CIR functionality
- *
- * To do:
- * o Test NEC and RC5
- *
- * Left as an exercise for the reader:
- * o Learning (I have neither the hardware, nor the need)
- * o IR Transmit (ibid)
- *
- * 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/module.h>
-#include <linux/pnp.h>
-#include <linux/interrupt.h>
-#include <linux/timer.h>
-#include <linux/input.h>
-#include <linux/leds.h>
-#include <linux/list.h>
-#include <linux/spinlock.h>
-#include <linux/pci_ids.h>
-#include <linux/io.h>
-#include <linux/bitrev.h>
-#include <linux/bitops.h>
-#include <linux/slab.h>
-
-#define DRVNAME "winbond-cir"
-
-/* CEIR Wake-Up Registers, relative to data->wbase */
-#define WBCIR_REG_WCEIR_CTL 0x03 /* CEIR Receiver Control */
-#define WBCIR_REG_WCEIR_STS 0x04 /* CEIR Receiver Status */
-#define WBCIR_REG_WCEIR_EV_EN 0x05 /* CEIR Receiver Event Enable */
-#define WBCIR_REG_WCEIR_CNTL 0x06 /* CEIR Receiver Counter Low */
-#define WBCIR_REG_WCEIR_CNTH 0x07 /* CEIR Receiver Counter High */
-#define WBCIR_REG_WCEIR_INDEX 0x08 /* CEIR Receiver Index */
-#define WBCIR_REG_WCEIR_DATA 0x09 /* CEIR Receiver Data */
-#define WBCIR_REG_WCEIR_CSL 0x0A /* CEIR Re. Compare Strlen */
-#define WBCIR_REG_WCEIR_CFG1 0x0B /* CEIR Re. Configuration 1 */
-#define WBCIR_REG_WCEIR_CFG2 0x0C /* CEIR Re. Configuration 2 */
-
-/* CEIR Enhanced Functionality Registers, relative to data->ebase */
-#define WBCIR_REG_ECEIR_CTS 0x00 /* Enhanced IR Control Status */
-#define WBCIR_REG_ECEIR_CCTL 0x01 /* Infrared Counter Control */
-#define WBCIR_REG_ECEIR_CNT_LO 0x02 /* Infrared Counter LSB */
-#define WBCIR_REG_ECEIR_CNT_HI 0x03 /* Infrared Counter MSB */
-#define WBCIR_REG_ECEIR_IREM 0x04 /* Infrared Emitter Status */
-
-/* SP3 Banked Registers, relative to data->sbase */
-#define WBCIR_REG_SP3_BSR 0x03 /* Bank Select, all banks */
- /* Bank 0 */
-#define WBCIR_REG_SP3_RXDATA 0x00 /* FIFO RX data (r) */
-#define WBCIR_REG_SP3_TXDATA 0x00 /* FIFO TX data (w) */
-#define WBCIR_REG_SP3_IER 0x01 /* Interrupt Enable */
-#define WBCIR_REG_SP3_EIR 0x02 /* Event Identification (r) */
-#define WBCIR_REG_SP3_FCR 0x02 /* FIFO Control (w) */
-#define WBCIR_REG_SP3_MCR 0x04 /* Mode Control */
-#define WBCIR_REG_SP3_LSR 0x05 /* Link Status */
-#define WBCIR_REG_SP3_MSR 0x06 /* Modem Status */
-#define WBCIR_REG_SP3_ASCR 0x07 /* Aux Status and Control */
- /* Bank 2 */
-#define WBCIR_REG_SP3_BGDL 0x00 /* Baud Divisor LSB */
-#define WBCIR_REG_SP3_BGDH 0x01 /* Baud Divisor MSB */
-#define WBCIR_REG_SP3_EXCR1 0x02 /* Extended Control 1 */
-#define WBCIR_REG_SP3_EXCR2 0x04 /* Extended Control 2 */
-#define WBCIR_REG_SP3_TXFLV 0x06 /* TX FIFO Level */
-#define WBCIR_REG_SP3_RXFLV 0x07 /* RX FIFO Level */
- /* Bank 3 */
-#define WBCIR_REG_SP3_MRID 0x00 /* Module Identification */
-#define WBCIR_REG_SP3_SH_LCR 0x01 /* LCR Shadow */
-#define WBCIR_REG_SP3_SH_FCR 0x02 /* FCR Shadow */
- /* Bank 4 */
-#define WBCIR_REG_SP3_IRCR1 0x02 /* Infrared Control 1 */
- /* Bank 5 */
-#define WBCIR_REG_SP3_IRCR2 0x04 /* Infrared Control 2 */
- /* Bank 6 */
-#define WBCIR_REG_SP3_IRCR3 0x00 /* Infrared Control 3 */
-#define WBCIR_REG_SP3_SIR_PW 0x02 /* SIR Pulse Width */
- /* Bank 7 */
-#define WBCIR_REG_SP3_IRRXDC 0x00 /* IR RX Demod Control */
-#define WBCIR_REG_SP3_IRTXMC 0x01 /* IR TX Mod Control */
-#define WBCIR_REG_SP3_RCCFG 0x02 /* CEIR Config */
-#define WBCIR_REG_SP3_IRCFG1 0x04 /* Infrared Config 1 */
-#define WBCIR_REG_SP3_IRCFG4 0x07 /* Infrared Config 4 */
-
-/*
- * Magic values follow
- */
-
-/* No interrupts for WBCIR_REG_SP3_IER and WBCIR_REG_SP3_EIR */
-#define WBCIR_IRQ_NONE 0x00
-/* RX data bit for WBCIR_REG_SP3_IER and WBCIR_REG_SP3_EIR */
-#define WBCIR_IRQ_RX 0x01
-/* Over/Under-flow bit for WBCIR_REG_SP3_IER and WBCIR_REG_SP3_EIR */
-#define WBCIR_IRQ_ERR 0x04
-/* Led enable/disable bit for WBCIR_REG_ECEIR_CTS */
-#define WBCIR_LED_ENABLE 0x80
-/* RX data available bit for WBCIR_REG_SP3_LSR */
-#define WBCIR_RX_AVAIL 0x01
-/* RX disable bit for WBCIR_REG_SP3_ASCR */
-#define WBCIR_RX_DISABLE 0x20
-/* Extended mode enable bit for WBCIR_REG_SP3_EXCR1 */
-#define WBCIR_EXT_ENABLE 0x01
-/* Select compare register in WBCIR_REG_WCEIR_INDEX (bits 5 & 6) */
-#define WBCIR_REGSEL_COMPARE 0x10
-/* Select mask register in WBCIR_REG_WCEIR_INDEX (bits 5 & 6) */
-#define WBCIR_REGSEL_MASK 0x20
-/* Starting address of selected register in WBCIR_REG_WCEIR_INDEX */
-#define WBCIR_REG_ADDR0 0x00
-
-/* Valid banks for the SP3 UART */
-enum wbcir_bank {
- WBCIR_BANK_0 = 0x00,
- WBCIR_BANK_1 = 0x80,
- WBCIR_BANK_2 = 0xE0,
- WBCIR_BANK_3 = 0xE4,
- WBCIR_BANK_4 = 0xE8,
- WBCIR_BANK_5 = 0xEC,
- WBCIR_BANK_6 = 0xF0,
- WBCIR_BANK_7 = 0xF4,
-};
-
-/* Supported IR Protocols */
-enum wbcir_protocol {
- IR_PROTOCOL_RC5 = 0x0,
- IR_PROTOCOL_NEC = 0x1,
- IR_PROTOCOL_RC6 = 0x2,
-};
-
-/* Misc */
-#define WBCIR_NAME "Winbond CIR"
-#define WBCIR_ID_FAMILY 0xF1 /* Family ID for the WPCD376I */
-#define WBCIR_ID_CHIP 0x04 /* Chip ID for the WPCD376I */
-#define IR_KEYPRESS_TIMEOUT 250 /* FIXME: should be per-protocol? */
-#define INVALID_SCANCODE 0x7FFFFFFF /* Invalid with all protos */
-#define WAKEUP_IOMEM_LEN 0x10 /* Wake-Up I/O Reg Len */
-#define EHFUNC_IOMEM_LEN 0x10 /* Enhanced Func I/O Reg Len */
-#define SP_IOMEM_LEN 0x08 /* Serial Port 3 (IR) Reg Len */
-#define WBCIR_MAX_IDLE_BYTES 10
-
-static DEFINE_SPINLOCK(wbcir_lock);
-static DEFINE_RWLOCK(keytable_lock);
-
-struct wbcir_key {
- u32 scancode;
- unsigned int keycode;
-};
-
-struct wbcir_keyentry {
- struct wbcir_key key;
- struct list_head list;
-};
-
-static struct wbcir_key rc6_def_keymap[] = {
- { 0x800F0400, KEY_NUMERIC_0 },
- { 0x800F0401, KEY_NUMERIC_1 },
- { 0x800F0402, KEY_NUMERIC_2 },
- { 0x800F0403, KEY_NUMERIC_3 },
- { 0x800F0404, KEY_NUMERIC_4 },
- { 0x800F0405, KEY_NUMERIC_5 },
- { 0x800F0406, KEY_NUMERIC_6 },
- { 0x800F0407, KEY_NUMERIC_7 },
- { 0x800F0408, KEY_NUMERIC_8 },
- { 0x800F0409, KEY_NUMERIC_9 },
- { 0x800F041D, KEY_NUMERIC_STAR },
- { 0x800F041C, KEY_NUMERIC_POUND },
- { 0x800F0410, KEY_VOLUMEUP },
- { 0x800F0411, KEY_VOLUMEDOWN },
- { 0x800F0412, KEY_CHANNELUP },
- { 0x800F0413, KEY_CHANNELDOWN },
- { 0x800F040E, KEY_MUTE },
- { 0x800F040D, KEY_VENDOR }, /* Vista Logo Key */
- { 0x800F041E, KEY_UP },
- { 0x800F041F, KEY_DOWN },
- { 0x800F0420, KEY_LEFT },
- { 0x800F0421, KEY_RIGHT },
- { 0x800F0422, KEY_OK },
- { 0x800F0423, KEY_ESC },
- { 0x800F040F, KEY_INFO },
- { 0x800F040A, KEY_CLEAR },
- { 0x800F040B, KEY_ENTER },
- { 0x800F045B, KEY_RED },
- { 0x800F045C, KEY_GREEN },
- { 0x800F045D, KEY_YELLOW },
- { 0x800F045E, KEY_BLUE },
- { 0x800F045A, KEY_TEXT },
- { 0x800F0427, KEY_SWITCHVIDEOMODE },
- { 0x800F040C, KEY_POWER },
- { 0x800F0450, KEY_RADIO },
- { 0x800F0448, KEY_PVR },
- { 0x800F0447, KEY_AUDIO },
- { 0x800F0426, KEY_EPG },
- { 0x800F0449, KEY_CAMERA },
- { 0x800F0425, KEY_TV },
- { 0x800F044A, KEY_VIDEO },
- { 0x800F0424, KEY_DVD },
- { 0x800F0416, KEY_PLAY },
- { 0x800F0418, KEY_PAUSE },
- { 0x800F0419, KEY_STOP },
- { 0x800F0414, KEY_FASTFORWARD },
- { 0x800F041A, KEY_NEXT },
- { 0x800F041B, KEY_PREVIOUS },
- { 0x800F0415, KEY_REWIND },
- { 0x800F0417, KEY_RECORD },
-};
-
-/* Registers and other state is protected by wbcir_lock */
-struct wbcir_data {
- unsigned long wbase; /* Wake-Up Baseaddr */
- unsigned long ebase; /* Enhanced Func. Baseaddr */
- unsigned long sbase; /* Serial Port Baseaddr */
- unsigned int irq; /* Serial Port IRQ */
-
- struct input_dev *input_dev;
- struct timer_list timer_keyup;
- struct led_trigger *rxtrigger;
- struct led_trigger *txtrigger;
- struct led_classdev led;
-
- u32 last_scancode;
- unsigned int last_keycode;
- u8 last_toggle;
- u8 keypressed;
- unsigned long keyup_jiffies;
- unsigned int idle_count;
-
- /* RX irdata and parsing state */
- unsigned long irdata[30];
- unsigned int irdata_count;
- unsigned int irdata_idle;
- unsigned int irdata_off;
- unsigned int irdata_error;
-
- /* Protected by keytable_lock */
- struct list_head keytable;
-};
-
-static enum wbcir_protocol protocol = IR_PROTOCOL_RC6;
-module_param(protocol, uint, 0444);
-MODULE_PARM_DESC(protocol, "IR protocol to use "
- "(0 = RC5, 1 = NEC, 2 = RC6A, default)");
-
-static int invert; /* default = 0 */
-module_param(invert, bool, 0444);
-MODULE_PARM_DESC(invert, "Invert the signal from the IR receiver");
-
-static unsigned int wake_sc = 0x800F040C;
-module_param(wake_sc, uint, 0644);
-MODULE_PARM_DESC(wake_sc, "Scancode of the power-on IR command");
-
-static unsigned int wake_rc6mode = 6;
-module_param(wake_rc6mode, uint, 0644);
-MODULE_PARM_DESC(wake_rc6mode, "RC6 mode for the power-on command "
- "(0 = 0, 6 = 6A, default)");
-
-
-
-/*****************************************************************************
- *
- * UTILITY FUNCTIONS
- *
- *****************************************************************************/
-
-/* Caller needs to hold wbcir_lock */
-static void
-wbcir_set_bits(unsigned long addr, u8 bits, u8 mask)
-{
- u8 val;
-
- val = inb(addr);
- val = ((val & ~mask) | (bits & mask));
- outb(val, addr);
-}
-
-/* Selects the register bank for the serial port */
-static inline void
-wbcir_select_bank(struct wbcir_data *data, enum wbcir_bank bank)
-{
- outb(bank, data->sbase + WBCIR_REG_SP3_BSR);
-}
-
-static enum led_brightness
-wbcir_led_brightness_get(struct led_classdev *led_cdev)
-{
- struct wbcir_data *data = container_of(led_cdev,
- struct wbcir_data,
- led);
-
- if (inb(data->ebase + WBCIR_REG_ECEIR_CTS) & WBCIR_LED_ENABLE)
- return LED_FULL;
- else
- return LED_OFF;
-}
-
-static void
-wbcir_led_brightness_set(struct led_classdev *led_cdev,
- enum led_brightness brightness)
-{
- struct wbcir_data *data = container_of(led_cdev,
- struct wbcir_data,
- led);
-
- wbcir_set_bits(data->ebase + WBCIR_REG_ECEIR_CTS,
- brightness == LED_OFF ? 0x00 : WBCIR_LED_ENABLE,
- WBCIR_LED_ENABLE);
-}
-
-/* Manchester encodes bits to RC6 message cells (see wbcir_parse_rc6) */
-static u8
-wbcir_to_rc6cells(u8 val)
-{
- u8 coded = 0x00;
- int i;
-
- val &= 0x0F;
- for (i = 0; i < 4; i++) {
- if (val & 0x01)
- coded |= 0x02 << (i * 2);
- else
- coded |= 0x01 << (i * 2);
- val >>= 1;
- }
-
- return coded;
-}
-
-
-
-/*****************************************************************************
- *
- * INPUT FUNCTIONS
- *
- *****************************************************************************/
-
-static unsigned int
-wbcir_do_getkeycode(struct wbcir_data *data, u32 scancode)
-{
- struct wbcir_keyentry *keyentry;
- unsigned int keycode = KEY_RESERVED;
- unsigned long flags;
-
- read_lock_irqsave(&keytable_lock, flags);
-
- list_for_each_entry(keyentry, &data->keytable, list) {
- if (keyentry->key.scancode == scancode) {
- keycode = keyentry->key.keycode;
- break;
- }
- }
-
- read_unlock_irqrestore(&keytable_lock, flags);
- return keycode;
-}
-
-static int
-wbcir_getkeycode(struct input_dev *dev,
- unsigned int scancode, unsigned int *keycode)
-{
- struct wbcir_data *data = input_get_drvdata(dev);
-
- *keycode = wbcir_do_getkeycode(data, scancode);
- return 0;
-}
-
-static int
-wbcir_setkeycode(struct input_dev *dev,
- unsigned int scancode, unsigned int keycode)
-{
- struct wbcir_data *data = input_get_drvdata(dev);
- struct wbcir_keyentry *keyentry;
- struct wbcir_keyentry *new_keyentry;
- unsigned long flags;
- unsigned int old_keycode = KEY_RESERVED;
-
- new_keyentry = kmalloc(sizeof(*new_keyentry), GFP_KERNEL);
- if (!new_keyentry)
- return -ENOMEM;
-
- write_lock_irqsave(&keytable_lock, flags);
-
- list_for_each_entry(keyentry, &data->keytable, list) {
- if (keyentry->key.scancode != scancode)
- continue;
-
- old_keycode = keyentry->key.keycode;
- keyentry->key.keycode = keycode;
-
- if (keyentry->key.keycode == KEY_RESERVED) {
- list_del(&keyentry->list);
- kfree(keyentry);
- }
-
- break;
- }
-
- set_bit(keycode, dev->keybit);
-
- if (old_keycode == KEY_RESERVED) {
- new_keyentry->key.scancode = scancode;
- new_keyentry->key.keycode = keycode;
- list_add(&new_keyentry->list, &data->keytable);
- } else {
- kfree(new_keyentry);
- clear_bit(old_keycode, dev->keybit);
- list_for_each_entry(keyentry, &data->keytable, list) {
- if (keyentry->key.keycode == old_keycode) {
- set_bit(old_keycode, dev->keybit);
- break;
- }
- }
- }
-
- write_unlock_irqrestore(&keytable_lock, flags);
- return 0;
-}
-
-/*
- * Timer function to report keyup event some time after keydown is
- * reported by the ISR.
- */
-static void
-wbcir_keyup(unsigned long cookie)
-{
- struct wbcir_data *data = (struct wbcir_data *)cookie;
- unsigned long flags;
-
- /*
- * data->keyup_jiffies is used to prevent a race condition if a
- * hardware interrupt occurs at this point and the keyup timer
- * event is moved further into the future as a result.
- *
- * The timer will then be reactivated and this function called
- * again in the future. We need to exit gracefully in that case
- * to allow the input subsystem to do its auto-repeat magic or
- * a keyup event might follow immediately after the keydown.
- */
-
- spin_lock_irqsave(&wbcir_lock, flags);
-
- if (time_is_after_eq_jiffies(data->keyup_jiffies) && data->keypressed) {
- data->keypressed = 0;
- led_trigger_event(data->rxtrigger, LED_OFF);
- input_report_key(data->input_dev, data->last_keycode, 0);
- input_sync(data->input_dev);
- }
-
- spin_unlock_irqrestore(&wbcir_lock, flags);
-}
-
-static void
-wbcir_keydown(struct wbcir_data *data, u32 scancode, u8 toggle)
-{
- unsigned int keycode;
-
- /* Repeat? */
- if (data->last_scancode == scancode &&
- data->last_toggle == toggle &&
- data->keypressed)
- goto set_timer;
- data->last_scancode = scancode;
-
- /* Do we need to release an old keypress? */
- if (data->keypressed) {
- input_report_key(data->input_dev, data->last_keycode, 0);
- input_sync(data->input_dev);
- data->keypressed = 0;
- }
-
- /* Report scancode */
- input_event(data->input_dev, EV_MSC, MSC_SCAN, (int)scancode);
-
- /* Do we know this scancode? */
- keycode = wbcir_do_getkeycode(data, scancode);
- if (keycode == KEY_RESERVED)
- goto set_timer;
-
- /* Register a keypress */
- input_report_key(data->input_dev, keycode, 1);
- data->keypressed = 1;
- data->last_keycode = keycode;
- data->last_toggle = toggle;
-
-set_timer:
- input_sync(data->input_dev);
- led_trigger_event(data->rxtrigger,
- data->keypressed ? LED_FULL : LED_OFF);
- data->keyup_jiffies = jiffies + msecs_to_jiffies(IR_KEYPRESS_TIMEOUT);
- mod_timer(&data->timer_keyup, data->keyup_jiffies);
-}
-
-
-
-/*****************************************************************************
- *
- * IR PARSING FUNCTIONS
- *
- *****************************************************************************/
-
-/* Resets all irdata */
-static void
-wbcir_reset_irdata(struct wbcir_data *data)
-{
- memset(data->irdata, 0, sizeof(data->irdata));
- data->irdata_count = 0;
- data->irdata_off = 0;
- data->irdata_error = 0;
- data->idle_count = 0;
-}
-
-/* Adds one bit of irdata */
-static void
-add_irdata_bit(struct wbcir_data *data, int set)
-{
- if (data->irdata_count >= sizeof(data->irdata) * 8) {
- data->irdata_error = 1;
- return;
- }
-
- if (set)
- __set_bit(data->irdata_count, data->irdata);
- data->irdata_count++;
-}
-
-/* Gets count bits of irdata */
-static u16
-get_bits(struct wbcir_data *data, int count)
-{
- u16 val = 0x0;
-
- if (data->irdata_count - data->irdata_off < count) {
- data->irdata_error = 1;
- return 0x0;
- }
-
- while (count > 0) {
- val <<= 1;
- if (test_bit(data->irdata_off, data->irdata))
- val |= 0x1;
- count--;
- data->irdata_off++;
- }
-
- return val;
-}
-
-/* Reads 16 cells and converts them to a byte */
-static u8
-wbcir_rc6cells_to_byte(struct wbcir_data *data)
-{
- u16 raw = get_bits(data, 16);
- u8 val = 0x00;
- int bit;
-
- for (bit = 0; bit < 8; bit++) {
- switch (raw & 0x03) {
- case 0x01:
- break;
- case 0x02:
- val |= (0x01 << bit);
- break;
- default:
- data->irdata_error = 1;
- break;
- }
- raw >>= 2;
- }
-
- return val;
-}
-
-/* Decodes a number of bits from raw RC5 data */
-static u8
-wbcir_get_rc5bits(struct wbcir_data *data, unsigned int count)
-{
- u16 raw = get_bits(data, count * 2);
- u8 val = 0x00;
- int bit;
-
- for (bit = 0; bit < count; bit++) {
- switch (raw & 0x03) {
- case 0x01:
- val |= (0x01 << bit);
- break;
- case 0x02:
- break;
- default:
- data->irdata_error = 1;
- break;
- }
- raw >>= 2;
- }
-
- return val;
-}
-
-static void
-wbcir_parse_rc6(struct device *dev, struct wbcir_data *data)
-{
- /*
- * Normal bits are manchester coded as follows:
- * cell0 + cell1 = logic "0"
- * cell1 + cell0 = logic "1"
- *
- * The IR pulse has the following components:
- *
- * Leader - 6 * cell1 - discarded
- * Gap - 2 * cell0 - discarded
- * Start bit - Normal Coding - always "1"
- * Mode Bit 2 - 0 - Normal Coding
- * Toggle bit - Normal Coding with double bit time,
- * e.g. cell0 + cell0 + cell1 + cell1
- * means logic "0".
- *
- * The rest depends on the mode, the following modes are known:
- *
- * MODE 0:
- * Address Bit 7 - 0 - Normal Coding
- * Command Bit 7 - 0 - Normal Coding
- *
- * MODE 6:
- * The above Toggle Bit is used as a submode bit, 0 = A, 1 = B.
- * Submode B is for pointing devices, only remotes using submode A
- * are supported.
- *
- * Customer range bit - 0 => Customer = 7 bits, 0...127
- * 1 => Customer = 15 bits, 32768...65535
- * Customer Bits - Normal Coding
- *
- * Customer codes are allocated by Philips. The rest of the bits
- * are customer dependent. The following is commonly used (and the
- * only supported config):
- *
- * Toggle Bit - Normal Coding
- * Address Bit 6 - 0 - Normal Coding
- * Command Bit 7 - 0 - Normal Coding
- *
- * All modes are followed by at least 6 * cell0.
- *
- * MODE 0 msglen:
- * 1 * 2 (start bit) + 3 * 2 (mode) + 2 * 2 (toggle) +
- * 8 * 2 (address) + 8 * 2 (command) =
- * 44 cells
- *
- * MODE 6A msglen:
- * 1 * 2 (start bit) + 3 * 2 (mode) + 2 * 2 (submode) +
- * 1 * 2 (customer range bit) + 7/15 * 2 (customer bits) +
- * 1 * 2 (toggle bit) + 7 * 2 (address) + 8 * 2 (command) =
- * 60 - 76 cells
- */
- u8 mode;
- u8 toggle;
- u16 customer = 0x0;
- u8 address;
- u8 command;
- u32 scancode;
-
- /* Leader mark */
- while (get_bits(data, 1) && !data->irdata_error)
- /* Do nothing */;
-
- /* Leader space */
- if (get_bits(data, 1)) {
- dev_dbg(dev, "RC6 - Invalid leader space\n");
- return;
- }
-
- /* Start bit */
- if (get_bits(data, 2) != 0x02) {
- dev_dbg(dev, "RC6 - Invalid start bit\n");
- return;
- }
-
- /* Mode */
- mode = get_bits(data, 6);
- switch (mode) {
- case 0x15: /* 010101 = b000 */
- mode = 0;
- break;
- case 0x29: /* 101001 = b110 */
- mode = 6;
- break;
- default:
- dev_dbg(dev, "RC6 - Invalid mode\n");
- return;
- }
-
- /* Toggle bit / Submode bit */
- toggle = get_bits(data, 4);
- switch (toggle) {
- case 0x03:
- toggle = 0;
- break;
- case 0x0C:
- toggle = 1;
- break;
- default:
- dev_dbg(dev, "RC6 - Toggle bit error\n");
- break;
- }
-
- /* Customer */
- if (mode == 6) {
- if (toggle != 0) {
- dev_dbg(dev, "RC6B - Not Supported\n");
- return;
- }
-
- customer = wbcir_rc6cells_to_byte(data);
-
- if (customer & 0x80) {
- /* 15 bit customer value */
- customer <<= 8;
- customer |= wbcir_rc6cells_to_byte(data);
- }
- }
-
- /* Address */
- address = wbcir_rc6cells_to_byte(data);
- if (mode == 6) {
- toggle = address >> 7;
- address &= 0x7F;
- }
-
- /* Command */
- command = wbcir_rc6cells_to_byte(data);
-
- /* Create scancode */
- scancode = command;
- scancode |= address << 8;
- scancode |= customer << 16;
-
- /* Last sanity check */
- if (data->irdata_error) {
- dev_dbg(dev, "RC6 - Cell error(s)\n");
- return;
- }
-
- dev_dbg(dev, "IR-RC6 ad 0x%02X cm 0x%02X cu 0x%04X "
- "toggle %u mode %u scan 0x%08X\n",
- address,
- command,
- customer,
- (unsigned int)toggle,
- (unsigned int)mode,
- scancode);
-
- wbcir_keydown(data, scancode, toggle);
-}
-
-static void
-wbcir_parse_rc5(struct device *dev, struct wbcir_data *data)
-{
- /*
- * Bits are manchester coded as follows:
- * cell1 + cell0 = logic "0"
- * cell0 + cell1 = logic "1"
- * (i.e. the reverse of RC6)
- *
- * Start bit 1 - "1" - discarded
- * Start bit 2 - Must be inverted to get command bit 6
- * Toggle bit
- * Address Bit 4 - 0
- * Command Bit 5 - 0
- */
- u8 toggle;
- u8 address;
- u8 command;
- u32 scancode;
-
- /* Start bit 1 */
- if (!get_bits(data, 1)) {
- dev_dbg(dev, "RC5 - Invalid start bit\n");
- return;
- }
-
- /* Start bit 2 */
- if (!wbcir_get_rc5bits(data, 1))
- command = 0x40;
- else
- command = 0x00;
-
- toggle = wbcir_get_rc5bits(data, 1);
- address = wbcir_get_rc5bits(data, 5);
- command |= wbcir_get_rc5bits(data, 6);
- scancode = address << 7 | command;
-
- /* Last sanity check */
- if (data->irdata_error) {
- dev_dbg(dev, "RC5 - Invalid message\n");
- return;
- }
-
- dev_dbg(dev, "IR-RC5 ad %u cm %u t %u s %u\n",
- (unsigned int)address,
- (unsigned int)command,
- (unsigned int)toggle,
- (unsigned int)scancode);
-
- wbcir_keydown(data, scancode, toggle);
-}
-
-static void
-wbcir_parse_nec(struct device *dev, struct wbcir_data *data)
-{
- /*
- * Each bit represents 560 us.
- *
- * Leader - 9 ms burst
- * Gap - 4.5 ms silence
- * Address1 bit 0 - 7 - Address 1
- * Address2 bit 0 - 7 - Address 2
- * Command1 bit 0 - 7 - Command 1
- * Command2 bit 0 - 7 - Command 2
- *
- * Note the bit order!
- *
- * With the old NEC protocol, Address2 was the inverse of Address1
- * and Command2 was the inverse of Command1 and were used as
- * an error check.
- *
- * With NEC extended, Address1 is the LSB of the Address and
- * Address2 is the MSB, Command parsing remains unchanged.
- *
- * A repeat message is coded as:
- * Leader - 9 ms burst
- * Gap - 2.25 ms silence
- * Repeat - 560 us active
- */
- u8 address1;
- u8 address2;
- u8 command1;
- u8 command2;
- u16 address;
- u32 scancode;
-
- /* Leader mark */
- while (get_bits(data, 1) && !data->irdata_error)
- /* Do nothing */;
-
- /* Leader space */
- if (get_bits(data, 4)) {
- dev_dbg(dev, "NEC - Invalid leader space\n");
- return;
- }
-
- /* Repeat? */
- if (get_bits(data, 1)) {
- if (!data->keypressed) {
- dev_dbg(dev, "NEC - Stray repeat message\n");
- return;
- }
-
- dev_dbg(dev, "IR-NEC repeat s %u\n",
- (unsigned int)data->last_scancode);
-
- wbcir_keydown(data, data->last_scancode, data->last_toggle);
- return;
- }
-
- /* Remaining leader space */
- if (get_bits(data, 3)) {
- dev_dbg(dev, "NEC - Invalid leader space\n");
- return;
- }
-
- address1 = bitrev8(get_bits(data, 8));
- address2 = bitrev8(get_bits(data, 8));
- command1 = bitrev8(get_bits(data, 8));
- command2 = bitrev8(get_bits(data, 8));
-
- /* Sanity check */
- if (data->irdata_error) {
- dev_dbg(dev, "NEC - Invalid message\n");
- return;
- }
-
- /* Check command validity */
- if (command1 != ~command2) {
- dev_dbg(dev, "NEC - Command bytes mismatch\n");
- return;
- }
-
- /* Check for extended NEC protocol */
- address = address1;
- if (address1 != ~address2)
- address |= address2 << 8;
-
- scancode = address << 8 | command1;
-
- dev_dbg(dev, "IR-NEC ad %u cm %u s %u\n",
- (unsigned int)address,
- (unsigned int)command1,
- (unsigned int)scancode);
-
- wbcir_keydown(data, scancode, !data->last_toggle);
-}
-
-
-
-/*****************************************************************************
- *
- * INTERRUPT FUNCTIONS
- *
- *****************************************************************************/
-
-static irqreturn_t
-wbcir_irq_handler(int irqno, void *cookie)
-{
- struct pnp_dev *device = cookie;
- struct wbcir_data *data = pnp_get_drvdata(device);
- struct device *dev = &device->dev;
- u8 status;
- unsigned long flags;
- u8 irdata[8];
- int i;
- unsigned int hw;
-
- spin_lock_irqsave(&wbcir_lock, flags);
-
- wbcir_select_bank(data, WBCIR_BANK_0);
-
- status = inb(data->sbase + WBCIR_REG_SP3_EIR);
-
- if (!(status & (WBCIR_IRQ_RX | WBCIR_IRQ_ERR))) {
- spin_unlock_irqrestore(&wbcir_lock, flags);
- return IRQ_NONE;
- }
-
- if (status & WBCIR_IRQ_ERR)
- data->irdata_error = 1;
-
- if (!(status & WBCIR_IRQ_RX))
- goto out;
-
- /* Since RXHDLEV is set, at least 8 bytes are in the FIFO */
- insb(data->sbase + WBCIR_REG_SP3_RXDATA, &irdata[0], 8);
-
- for (i = 0; i < sizeof(irdata); i++) {
- hw = hweight8(irdata[i]);
- if (hw > 4)
- add_irdata_bit(data, 0);
- else
- add_irdata_bit(data, 1);
-
- if (hw == 8)
- data->idle_count++;
- else
- data->idle_count = 0;
- }
-
- if (data->idle_count > WBCIR_MAX_IDLE_BYTES) {
- /* Set RXINACTIVE... */
- outb(WBCIR_RX_DISABLE, data->sbase + WBCIR_REG_SP3_ASCR);
-
- /* ...and drain the FIFO */
- while (inb(data->sbase + WBCIR_REG_SP3_LSR) & WBCIR_RX_AVAIL)
- inb(data->sbase + WBCIR_REG_SP3_RXDATA);
-
- dev_dbg(dev, "IRDATA:\n");
- for (i = 0; i < data->irdata_count; i += BITS_PER_LONG)
- dev_dbg(dev, "0x%08lX\n", data->irdata[i/BITS_PER_LONG]);
-
- switch (protocol) {
- case IR_PROTOCOL_RC5:
- wbcir_parse_rc5(dev, data);
- break;
- case IR_PROTOCOL_RC6:
- wbcir_parse_rc6(dev, data);
- break;
- case IR_PROTOCOL_NEC:
- wbcir_parse_nec(dev, data);
- break;
- }
-
- wbcir_reset_irdata(data);
- }
-
-out:
- spin_unlock_irqrestore(&wbcir_lock, flags);
- return IRQ_HANDLED;
-}
-
-
-
-/*****************************************************************************
- *
- * SETUP/INIT/SUSPEND/RESUME FUNCTIONS
- *
- *****************************************************************************/
-
-static void
-wbcir_shutdown(struct pnp_dev *device)
-{
- struct device *dev = &device->dev;
- struct wbcir_data *data = pnp_get_drvdata(device);
- int do_wake = 1;
- u8 match[11];
- u8 mask[11];
- u8 rc6_csl = 0;
- int i;
-
- memset(match, 0, sizeof(match));
- memset(mask, 0, sizeof(mask));
-
- if (wake_sc == INVALID_SCANCODE || !device_may_wakeup(dev)) {
- do_wake = 0;
- goto finish;
- }
-
- switch (protocol) {
- case IR_PROTOCOL_RC5:
- if (wake_sc > 0xFFF) {
- do_wake = 0;
- dev_err(dev, "RC5 - Invalid wake scancode\n");
- break;
- }
-
- /* Mask = 13 bits, ex toggle */
- mask[0] = 0xFF;
- mask[1] = 0x17;
-
- match[0] = (wake_sc & 0x003F); /* 6 command bits */
- match[0] |= (wake_sc & 0x0180) >> 1; /* 2 address bits */
- match[1] = (wake_sc & 0x0E00) >> 9; /* 3 address bits */
- if (!(wake_sc & 0x0040)) /* 2nd start bit */
- match[1] |= 0x10;
-
- break;
-
- case IR_PROTOCOL_NEC:
- if (wake_sc > 0xFFFFFF) {
- do_wake = 0;
- dev_err(dev, "NEC - Invalid wake scancode\n");
- break;
- }
-
- mask[0] = mask[1] = mask[2] = mask[3] = 0xFF;
-
- match[1] = bitrev8((wake_sc & 0xFF));
- match[0] = ~match[1];
-
- match[3] = bitrev8((wake_sc & 0xFF00) >> 8);
- if (wake_sc > 0xFFFF)
- match[2] = bitrev8((wake_sc & 0xFF0000) >> 16);
- else
- match[2] = ~match[3];
-
- break;
-
- case IR_PROTOCOL_RC6:
-
- if (wake_rc6mode == 0) {
- if (wake_sc > 0xFFFF) {
- do_wake = 0;
- dev_err(dev, "RC6 - Invalid wake scancode\n");
- break;
- }
-
- /* Command */
- match[0] = wbcir_to_rc6cells(wake_sc >> 0);
- mask[0] = 0xFF;
- match[1] = wbcir_to_rc6cells(wake_sc >> 4);
- mask[1] = 0xFF;
-
- /* Address */
- match[2] = wbcir_to_rc6cells(wake_sc >> 8);
- mask[2] = 0xFF;
- match[3] = wbcir_to_rc6cells(wake_sc >> 12);
- mask[3] = 0xFF;
-
- /* Header */
- match[4] = 0x50; /* mode1 = mode0 = 0, ignore toggle */
- mask[4] = 0xF0;
- match[5] = 0x09; /* start bit = 1, mode2 = 0 */
- mask[5] = 0x0F;
-
- rc6_csl = 44;
-
- } else if (wake_rc6mode == 6) {
- i = 0;
-
- /* Command */
- match[i] = wbcir_to_rc6cells(wake_sc >> 0);
- mask[i++] = 0xFF;
- match[i] = wbcir_to_rc6cells(wake_sc >> 4);
- mask[i++] = 0xFF;
-
- /* Address + Toggle */
- match[i] = wbcir_to_rc6cells(wake_sc >> 8);
- mask[i++] = 0xFF;
- match[i] = wbcir_to_rc6cells(wake_sc >> 12);
- mask[i++] = 0x3F;
-
- /* Customer bits 7 - 0 */
- match[i] = wbcir_to_rc6cells(wake_sc >> 16);
- mask[i++] = 0xFF;
- match[i] = wbcir_to_rc6cells(wake_sc >> 20);
- mask[i++] = 0xFF;
-
- if (wake_sc & 0x80000000) {
- /* Customer range bit and bits 15 - 8 */
- match[i] = wbcir_to_rc6cells(wake_sc >> 24);
- mask[i++] = 0xFF;
- match[i] = wbcir_to_rc6cells(wake_sc >> 28);
- mask[i++] = 0xFF;
- rc6_csl = 76;
- } else if (wake_sc <= 0x007FFFFF) {
- rc6_csl = 60;
- } else {
- do_wake = 0;
- dev_err(dev, "RC6 - Invalid wake scancode\n");
- break;
- }
-
- /* Header */
- match[i] = 0x93; /* mode1 = mode0 = 1, submode = 0 */
- mask[i++] = 0xFF;
- match[i] = 0x0A; /* start bit = 1, mode2 = 1 */
- mask[i++] = 0x0F;
-
- } else {
- do_wake = 0;
- dev_err(dev, "RC6 - Invalid wake mode\n");
- }
-
- break;
-
- default:
- do_wake = 0;
- break;
- }
-
-finish:
- if (do_wake) {
- /* Set compare and compare mask */
- wbcir_set_bits(data->wbase + WBCIR_REG_WCEIR_INDEX,
- WBCIR_REGSEL_COMPARE | WBCIR_REG_ADDR0,
- 0x3F);
- outsb(data->wbase + WBCIR_REG_WCEIR_DATA, match, 11);
- wbcir_set_bits(data->wbase + WBCIR_REG_WCEIR_INDEX,
- WBCIR_REGSEL_MASK | WBCIR_REG_ADDR0,
- 0x3F);
- outsb(data->wbase + WBCIR_REG_WCEIR_DATA, mask, 11);
-
- /* RC6 Compare String Len */
- outb(rc6_csl, data->wbase + WBCIR_REG_WCEIR_CSL);
-
- /* Clear status bits NEC_REP, BUFF, MSG_END, MATCH */
- wbcir_set_bits(data->wbase + WBCIR_REG_WCEIR_STS, 0x17, 0x17);
-
- /* Clear BUFF_EN, Clear END_EN, Set MATCH_EN */
- wbcir_set_bits(data->wbase + WBCIR_REG_WCEIR_EV_EN, 0x01, 0x07);
-
- /* Set CEIR_EN */
- wbcir_set_bits(data->wbase + WBCIR_REG_WCEIR_CTL, 0x01, 0x01);
-
- } else {
- /* Clear BUFF_EN, Clear END_EN, Clear MATCH_EN */
- wbcir_set_bits(data->wbase + WBCIR_REG_WCEIR_EV_EN, 0x00, 0x07);
-
- /* Clear CEIR_EN */
- wbcir_set_bits(data->wbase + WBCIR_REG_WCEIR_CTL, 0x00, 0x01);
- }
-
- /* Disable interrupts */
- wbcir_select_bank(data, WBCIR_BANK_0);
- outb(WBCIR_IRQ_NONE, data->sbase + WBCIR_REG_SP3_IER);
-
- /*
- * ACPI will set the HW disable bit for SP3 which means that the
- * output signals are left in an undefined state which may cause
- * spurious interrupts which we need to ignore until the hardware
- * is reinitialized.
- */
- disable_irq(data->irq);
-}
-
-static int
-wbcir_suspend(struct pnp_dev *device, pm_message_t state)
-{
- wbcir_shutdown(device);
- return 0;
-}
-
-static void
-wbcir_init_hw(struct wbcir_data *data)
-{
- u8 tmp;
-
- /* Disable interrupts */
- wbcir_select_bank(data, WBCIR_BANK_0);
- outb(WBCIR_IRQ_NONE, data->sbase + WBCIR_REG_SP3_IER);
-
- /* Set PROT_SEL, RX_INV, Clear CEIR_EN (needed for the led) */
- tmp = protocol << 4;
- if (invert)
- tmp |= 0x08;
- outb(tmp, data->wbase + WBCIR_REG_WCEIR_CTL);
-
- /* Clear status bits NEC_REP, BUFF, MSG_END, MATCH */
- wbcir_set_bits(data->wbase + WBCIR_REG_WCEIR_STS, 0x17, 0x17);
-
- /* Clear BUFF_EN, Clear END_EN, Clear MATCH_EN */
- wbcir_set_bits(data->wbase + WBCIR_REG_WCEIR_EV_EN, 0x00, 0x07);
-
- /* Set RC5 cell time to correspond to 36 kHz */
- wbcir_set_bits(data->wbase + WBCIR_REG_WCEIR_CFG1, 0x4A, 0x7F);
-
- /* Set IRTX_INV */
- if (invert)
- outb(0x04, data->ebase + WBCIR_REG_ECEIR_CCTL);
- else
- outb(0x00, data->ebase + WBCIR_REG_ECEIR_CCTL);
-
- /*
- * Clear IR LED, set SP3 clock to 24Mhz
- * set SP3_IRRX_SW to binary 01, helpfully not documented
- */
- outb(0x10, data->ebase + WBCIR_REG_ECEIR_CTS);
-
- /* Enable extended mode */
- wbcir_select_bank(data, WBCIR_BANK_2);
- outb(WBCIR_EXT_ENABLE, data->sbase + WBCIR_REG_SP3_EXCR1);
-
- /*
- * Configure baud generator, IR data will be sampled at
- * a bitrate of: (24Mhz * prescaler) / (divisor * 16).
- *
- * The ECIR registers include a flag to change the
- * 24Mhz clock freq to 48Mhz.
- *
- * It's not documented in the specs, but fifo levels
- * other than 16 seems to be unsupported.
- */
-
- /* prescaler 1.0, tx/rx fifo lvl 16 */
- outb(0x30, data->sbase + WBCIR_REG_SP3_EXCR2);
-
- /* Set baud divisor to generate one byte per bit/cell */
- switch (protocol) {
- case IR_PROTOCOL_RC5:
- outb(0xA7, data->sbase + WBCIR_REG_SP3_BGDL);
- break;
- case IR_PROTOCOL_RC6:
- outb(0x53, data->sbase + WBCIR_REG_SP3_BGDL);
- break;
- case IR_PROTOCOL_NEC:
- outb(0x69, data->sbase + WBCIR_REG_SP3_BGDL);
- break;
- }
- outb(0x00, data->sbase + WBCIR_REG_SP3_BGDH);
-
- /* Set CEIR mode */
- wbcir_select_bank(data, WBCIR_BANK_0);
- outb(0xC0, data->sbase + WBCIR_REG_SP3_MCR);
- inb(data->sbase + WBCIR_REG_SP3_LSR); /* Clear LSR */
- inb(data->sbase + WBCIR_REG_SP3_MSR); /* Clear MSR */
-
- /* Disable RX demod, run-length encoding/decoding, set freq span */
- wbcir_select_bank(data, WBCIR_BANK_7);
- outb(0x10, data->sbase + WBCIR_REG_SP3_RCCFG);
-
- /* Disable timer */
- wbcir_select_bank(data, WBCIR_BANK_4);
- outb(0x00, data->sbase + WBCIR_REG_SP3_IRCR1);
-
- /* Enable MSR interrupt, Clear AUX_IRX */
- wbcir_select_bank(data, WBCIR_BANK_5);
- outb(0x00, data->sbase + WBCIR_REG_SP3_IRCR2);
-
- /* Disable CRC */
- wbcir_select_bank(data, WBCIR_BANK_6);
- outb(0x20, data->sbase + WBCIR_REG_SP3_IRCR3);
-
- /* Set RX/TX (de)modulation freq, not really used */
- wbcir_select_bank(data, WBCIR_BANK_7);
- outb(0xF2, data->sbase + WBCIR_REG_SP3_IRRXDC);
- outb(0x69, data->sbase + WBCIR_REG_SP3_IRTXMC);
-
- /* Set invert and pin direction */
- if (invert)
- outb(0x10, data->sbase + WBCIR_REG_SP3_IRCFG4);
- else
- outb(0x00, data->sbase + WBCIR_REG_SP3_IRCFG4);
-
- /* Set FIFO thresholds (RX = 8, TX = 3), reset RX/TX */
- wbcir_select_bank(data, WBCIR_BANK_0);
- outb(0x97, data->sbase + WBCIR_REG_SP3_FCR);
-
- /* Clear AUX status bits */
- outb(0xE0, data->sbase + WBCIR_REG_SP3_ASCR);
-
- /* Enable interrupts */
- wbcir_reset_irdata(data);
- outb(WBCIR_IRQ_RX | WBCIR_IRQ_ERR, data->sbase + WBCIR_REG_SP3_IER);
-}
-
-static int
-wbcir_resume(struct pnp_dev *device)
-{
- struct wbcir_data *data = pnp_get_drvdata(device);
-
- wbcir_init_hw(data);
- enable_irq(data->irq);
-
- return 0;
-}
-
-static int __devinit
-wbcir_probe(struct pnp_dev *device, const struct pnp_device_id *dev_id)
-{
- struct device *dev = &device->dev;
- struct wbcir_data *data;
- int err;
-
- if (!(pnp_port_len(device, 0) == EHFUNC_IOMEM_LEN &&
- pnp_port_len(device, 1) == WAKEUP_IOMEM_LEN &&
- pnp_port_len(device, 2) == SP_IOMEM_LEN)) {
- dev_err(dev, "Invalid resources\n");
- return -ENODEV;
- }
-
- data = kzalloc(sizeof(*data), GFP_KERNEL);
- if (!data) {
- err = -ENOMEM;
- goto exit;
- }
-
- pnp_set_drvdata(device, data);
-
- data->ebase = pnp_port_start(device, 0);
- data->wbase = pnp_port_start(device, 1);
- data->sbase = pnp_port_start(device, 2);
- data->irq = pnp_irq(device, 0);
-
- if (data->wbase == 0 || data->ebase == 0 ||
- data->sbase == 0 || data->irq == 0) {
- err = -ENODEV;
- dev_err(dev, "Invalid resources\n");
- goto exit_free_data;
- }
-
- dev_dbg(&device->dev, "Found device "
- "(w: 0x%lX, e: 0x%lX, s: 0x%lX, i: %u)\n",
- data->wbase, data->ebase, data->sbase, data->irq);
-
- if (!request_region(data->wbase, WAKEUP_IOMEM_LEN, DRVNAME)) {
- dev_err(dev, "Region 0x%lx-0x%lx already in use!\n",
- data->wbase, data->wbase + WAKEUP_IOMEM_LEN - 1);
- err = -EBUSY;
- goto exit_free_data;
- }
-
- if (!request_region(data->ebase, EHFUNC_IOMEM_LEN, DRVNAME)) {
- dev_err(dev, "Region 0x%lx-0x%lx already in use!\n",
- data->ebase, data->ebase + EHFUNC_IOMEM_LEN - 1);
- err = -EBUSY;
- goto exit_release_wbase;
- }
-
- if (!request_region(data->sbase, SP_IOMEM_LEN, DRVNAME)) {
- dev_err(dev, "Region 0x%lx-0x%lx already in use!\n",
- data->sbase, data->sbase + SP_IOMEM_LEN - 1);
- err = -EBUSY;
- goto exit_release_ebase;
- }
-
- err = request_irq(data->irq, wbcir_irq_handler,
- IRQF_DISABLED, DRVNAME, device);
- if (err) {
- dev_err(dev, "Failed to claim IRQ %u\n", data->irq);
- err = -EBUSY;
- goto exit_release_sbase;
- }
-
- led_trigger_register_simple("cir-tx", &data->txtrigger);
- if (!data->txtrigger) {
- err = -ENOMEM;
- goto exit_free_irq;
- }
-
- led_trigger_register_simple("cir-rx", &data->rxtrigger);
- if (!data->rxtrigger) {
- err = -ENOMEM;
- goto exit_unregister_txtrigger;
- }
-
- data->led.name = "cir::activity";
- data->led.default_trigger = "cir-rx";
- data->led.brightness_set = wbcir_led_brightness_set;
- data->led.brightness_get = wbcir_led_brightness_get;
- err = led_classdev_register(&device->dev, &data->led);
- if (err)
- goto exit_unregister_rxtrigger;
-
- data->input_dev = input_allocate_device();
- if (!data->input_dev) {
- err = -ENOMEM;
- goto exit_unregister_led;
- }
-
- data->input_dev->evbit[0] = BIT(EV_KEY);
- data->input_dev->name = WBCIR_NAME;
- data->input_dev->phys = "wbcir/cir0";
- data->input_dev->id.bustype = BUS_HOST;
- data->input_dev->id.vendor = PCI_VENDOR_ID_WINBOND;
- data->input_dev->id.product = WBCIR_ID_FAMILY;
- data->input_dev->id.version = WBCIR_ID_CHIP;
- data->input_dev->getkeycode = wbcir_getkeycode;
- data->input_dev->setkeycode = wbcir_setkeycode;
- input_set_capability(data->input_dev, EV_MSC, MSC_SCAN);
- input_set_drvdata(data->input_dev, data);
-
- err = input_register_device(data->input_dev);
- if (err)
- goto exit_free_input;
-
- data->last_scancode = INVALID_SCANCODE;
- INIT_LIST_HEAD(&data->keytable);
- setup_timer(&data->timer_keyup, wbcir_keyup, (unsigned long)data);
-
- /* Load default keymaps */
- if (protocol == IR_PROTOCOL_RC6) {
- int i;
- for (i = 0; i < ARRAY_SIZE(rc6_def_keymap); i++) {
- err = wbcir_setkeycode(data->input_dev,
- (int)rc6_def_keymap[i].scancode,
- (int)rc6_def_keymap[i].keycode);
- if (err)
- goto exit_unregister_keys;
- }
- }
-
- device_init_wakeup(&device->dev, 1);
-
- wbcir_init_hw(data);
-
- return 0;
-
-exit_unregister_keys:
- if (!list_empty(&data->keytable)) {
- struct wbcir_keyentry *key;
- struct wbcir_keyentry *keytmp;
-
- list_for_each_entry_safe(key, keytmp, &data->keytable, list) {
- list_del(&key->list);
- kfree(key);
- }
- }
- input_unregister_device(data->input_dev);
- /* Can't call input_free_device on an unregistered device */
- data->input_dev = NULL;
-exit_free_input:
- input_free_device(data->input_dev);
-exit_unregister_led:
- led_classdev_unregister(&data->led);
-exit_unregister_rxtrigger:
- led_trigger_unregister_simple(data->rxtrigger);
-exit_unregister_txtrigger:
- led_trigger_unregister_simple(data->txtrigger);
-exit_free_irq:
- free_irq(data->irq, device);
-exit_release_sbase:
- release_region(data->sbase, SP_IOMEM_LEN);
-exit_release_ebase:
- release_region(data->ebase, EHFUNC_IOMEM_LEN);
-exit_release_wbase:
- release_region(data->wbase, WAKEUP_IOMEM_LEN);
-exit_free_data:
- kfree(data);
- pnp_set_drvdata(device, NULL);
-exit:
- return err;
-}
-
-static void __devexit
-wbcir_remove(struct pnp_dev *device)
-{
- struct wbcir_data *data = pnp_get_drvdata(device);
- struct wbcir_keyentry *key;
- struct wbcir_keyentry *keytmp;
-
- /* Disable interrupts */
- wbcir_select_bank(data, WBCIR_BANK_0);
- outb(WBCIR_IRQ_NONE, data->sbase + WBCIR_REG_SP3_IER);
-
- del_timer_sync(&data->timer_keyup);
-
- free_irq(data->irq, device);
-
- /* Clear status bits NEC_REP, BUFF, MSG_END, MATCH */
- wbcir_set_bits(data->wbase + WBCIR_REG_WCEIR_STS, 0x17, 0x17);
-
- /* Clear CEIR_EN */
- wbcir_set_bits(data->wbase + WBCIR_REG_WCEIR_CTL, 0x00, 0x01);
-
- /* Clear BUFF_EN, END_EN, MATCH_EN */
- wbcir_set_bits(data->wbase + WBCIR_REG_WCEIR_EV_EN, 0x00, 0x07);
-
- /* This will generate a keyup event if necessary */
- input_unregister_device(data->input_dev);
-
- led_trigger_unregister_simple(data->rxtrigger);
- led_trigger_unregister_simple(data->txtrigger);
- led_classdev_unregister(&data->led);
-
- /* This is ok since &data->led isn't actually used */
- wbcir_led_brightness_set(&data->led, LED_OFF);
-
- release_region(data->wbase, WAKEUP_IOMEM_LEN);
- release_region(data->ebase, EHFUNC_IOMEM_LEN);
- release_region(data->sbase, SP_IOMEM_LEN);
-
- list_for_each_entry_safe(key, keytmp, &data->keytable, list) {
- list_del(&key->list);
- kfree(key);
- }
-
- kfree(data);
-
- pnp_set_drvdata(device, NULL);
-}
-
-static const struct pnp_device_id wbcir_ids[] = {
- { "WEC1022", 0 },
- { "", 0 }
-};
-MODULE_DEVICE_TABLE(pnp, wbcir_ids);
-
-static struct pnp_driver wbcir_driver = {
- .name = WBCIR_NAME,
- .id_table = wbcir_ids,
- .probe = wbcir_probe,
- .remove = __devexit_p(wbcir_remove),
- .suspend = wbcir_suspend,
- .resume = wbcir_resume,
- .shutdown = wbcir_shutdown
-};
-
-static int __init
-wbcir_init(void)
-{
- int ret;
-
- switch (protocol) {
- case IR_PROTOCOL_RC5:
- case IR_PROTOCOL_NEC:
- case IR_PROTOCOL_RC6:
- break;
- default:
- printk(KERN_ERR DRVNAME ": Invalid protocol argument\n");
- return -EINVAL;
- }
-
- ret = pnp_register_driver(&wbcir_driver);
- if (ret)
- printk(KERN_ERR DRVNAME ": Unable to register driver\n");
-
- return ret;
-}
-
-static void __exit
-wbcir_exit(void)
-{
- pnp_unregister_driver(&wbcir_driver);
-}
-
-MODULE_AUTHOR("David Härdeman <david@hardeman.nu>");
-MODULE_DESCRIPTION("Winbond SuperI/O Consumer IR Driver");
-MODULE_LICENSE("GPL");
-
-module_init(wbcir_init);
-module_exit(wbcir_exit);
-
-
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/Kconfig b/drivers/input/mouse/Kconfig
index bf5fd7f6a31..9c1e6ee8353 100644
--- a/drivers/input/mouse/Kconfig
+++ b/drivers/input/mouse/Kconfig
@@ -39,7 +39,7 @@ config MOUSE_PS2
module will be called psmouse.
config MOUSE_PS2_ALPS
- bool "ALPS PS/2 mouse protocol extension" if EMBEDDED
+ bool "ALPS PS/2 mouse protocol extension" if EXPERT
default y
depends on MOUSE_PS2
help
@@ -49,7 +49,7 @@ config MOUSE_PS2_ALPS
If unsure, say Y.
config MOUSE_PS2_LOGIPS2PP
- bool "Logitech PS/2++ mouse protocol extension" if EMBEDDED
+ bool "Logitech PS/2++ mouse protocol extension" if EXPERT
default y
depends on MOUSE_PS2
help
@@ -59,7 +59,7 @@ config MOUSE_PS2_LOGIPS2PP
If unsure, say Y.
config MOUSE_PS2_SYNAPTICS
- bool "Synaptics PS/2 mouse protocol extension" if EMBEDDED
+ bool "Synaptics PS/2 mouse protocol extension" if EXPERT
default y
depends on MOUSE_PS2
help
@@ -69,7 +69,7 @@ config MOUSE_PS2_SYNAPTICS
If unsure, say Y.
config MOUSE_PS2_LIFEBOOK
- bool "Fujitsu Lifebook PS/2 mouse protocol extension" if EMBEDDED
+ bool "Fujitsu Lifebook PS/2 mouse protocol extension" if EXPERT
default y
depends on MOUSE_PS2 && X86 && DMI
help
@@ -79,7 +79,7 @@ config MOUSE_PS2_LIFEBOOK
If unsure, say Y.
config MOUSE_PS2_TRACKPOINT
- bool "IBM Trackpoint PS/2 mouse protocol extension" if EMBEDDED
+ bool "IBM Trackpoint PS/2 mouse protocol extension" if EXPERT
default y
depends on MOUSE_PS2
help
diff --git a/drivers/input/mouse/bcm5974.c b/drivers/input/mouse/bcm5974.c
index d24837210f0..3aead91bacc 100644
--- a/drivers/input/mouse/bcm5974.c
+++ b/drivers/input/mouse/bcm5974.c
@@ -55,6 +55,18 @@
#define USB_DEVICE_ID_APPLE_WELLSPRING3_ANSI 0x0236
#define USB_DEVICE_ID_APPLE_WELLSPRING3_ISO 0x0237
#define USB_DEVICE_ID_APPLE_WELLSPRING3_JIS 0x0238
+/* MacbookAir3,2 (unibody), aka wellspring5 */
+#define USB_DEVICE_ID_APPLE_WELLSPRING4_ANSI 0x023f
+#define USB_DEVICE_ID_APPLE_WELLSPRING4_ISO 0x0240
+#define USB_DEVICE_ID_APPLE_WELLSPRING4_JIS 0x0241
+/* MacbookAir3,1 (unibody), aka wellspring4 */
+#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 | \
@@ -80,6 +92,18 @@ static const struct usb_device_id bcm5974_table[] = {
BCM5974_DEVICE(USB_DEVICE_ID_APPLE_WELLSPRING3_ANSI),
BCM5974_DEVICE(USB_DEVICE_ID_APPLE_WELLSPRING3_ISO),
BCM5974_DEVICE(USB_DEVICE_ID_APPLE_WELLSPRING3_JIS),
+ /* MacbookAir3,2 */
+ BCM5974_DEVICE(USB_DEVICE_ID_APPLE_WELLSPRING4_ANSI),
+ BCM5974_DEVICE(USB_DEVICE_ID_APPLE_WELLSPRING4_ISO),
+ BCM5974_DEVICE(USB_DEVICE_ID_APPLE_WELLSPRING4_JIS),
+ /* MacbookAir3,1 */
+ 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 */
{}
};
@@ -234,6 +258,42 @@ static const struct bcm5974_config bcm5974_config_table[] = {
{ DIM_X, DIM_X / SN_COORD, -4460, 5166 },
{ DIM_Y, DIM_Y / SN_COORD, -75, 6700 }
},
+ {
+ USB_DEVICE_ID_APPLE_WELLSPRING4_ANSI,
+ USB_DEVICE_ID_APPLE_WELLSPRING4_ISO,
+ USB_DEVICE_ID_APPLE_WELLSPRING4_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, -4620, 5140 },
+ { DIM_Y, DIM_Y / SN_COORD, -150, 6600 }
+ },
+ {
+ USB_DEVICE_ID_APPLE_WELLSPRING4A_ANSI,
+ USB_DEVICE_ID_APPLE_WELLSPRING4A_ISO,
+ USB_DEVICE_ID_APPLE_WELLSPRING4A_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, -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 }
+ },
{}
};
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/serio/Kconfig b/drivers/input/serio/Kconfig
index 6256233d2bf..55f2c2293ec 100644
--- a/drivers/input/serio/Kconfig
+++ b/drivers/input/serio/Kconfig
@@ -2,7 +2,7 @@
# Input core configuration
#
config SERIO
- tristate "Serial I/O support" if EMBEDDED || !X86
+ tristate "Serial I/O support" if EXPERT || !X86
default y
help
Say Yes here if you have any input device that uses serial I/O to
@@ -19,7 +19,7 @@ config SERIO
if SERIO
config SERIO_I8042
- tristate "i8042 PC Keyboard controller" if EMBEDDED || !X86
+ tristate "i8042 PC Keyboard controller" if EXPERT || !X86
default y
depends on !PARISC && (!ARM || ARCH_SHARK || FOOTBRIDGE_HOST) && \
(!SUPERH || SH_CAYMAN) && !M68K && !BLACKFIN
@@ -168,7 +168,7 @@ config SERIO_MACEPS2
module will be called maceps2.
config SERIO_LIBPS2
- tristate "PS/2 driver library" if EMBEDDED
+ tristate "PS/2 driver library" if EXPERT
depends on SERIO_I8042 || SERIO_I8042=n
help
Say Y here if you are using a driver for device connected
@@ -214,7 +214,6 @@ config SERIO_AMS_DELTA
tristate "Amstrad Delta (E3) mailboard support"
depends on MACH_AMS_DELTA
default y
- select AMS_DELTA_FIQ
---help---
Say Y here if you have an E3 and want to use its mailboard,
or any standard AT keyboard connected to the mailboard port.
@@ -230,7 +229,7 @@ config SERIO_PS2MULT
tristate "TQC PS/2 multiplexer"
help
Say Y here if you have the PS/2 line multiplexer like the one
- present on TQC boads.
+ present on TQC boards.
To compile this driver as a module, choose M here: the
module will be called ps2mult.
diff --git a/drivers/input/serio/gscps2.c b/drivers/input/serio/gscps2.c
index 3c287dd879d..4225f5d6b15 100644
--- a/drivers/input/serio/gscps2.c
+++ b/drivers/input/serio/gscps2.c
@@ -358,7 +358,7 @@ static int __devinit gscps2_probe(struct parisc_device *dev)
gscps2_reset(ps2port);
ps2port->id = readb(ps2port->addr + GSC_ID) & 0x0f;
- snprintf(serio->name, sizeof(serio->name), "GSC PS/2 %s",
+ snprintf(serio->name, sizeof(serio->name), "gsc-ps2-%s",
(ps2port->id == GSC_ID_KEYBOARD) ? "keyboard" : "mouse");
strlcpy(serio->phys, dev_name(&dev->dev), sizeof(serio->phys));
serio->id.type = SERIO_8042;
diff --git a/drivers/input/serio/serio_raw.c b/drivers/input/serio/serio_raw.c
index cd82bb12591..b7ba4597f7f 100644
--- a/drivers/input/serio/serio_raw.c
+++ b/drivers/input/serio/serio_raw.c
@@ -11,7 +11,6 @@
#include <linux/sched.h>
#include <linux/slab.h>
-#include <linux/smp_lock.h>
#include <linux/poll.h>
#include <linux/module.h>
#include <linux/serio.h>
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 07ac77d393a..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
@@ -540,62 +550,62 @@ config TOUCHSCREEN_MC13783
config TOUCHSCREEN_USB_EGALAX
default y
- bool "eGalax, eTurboTouch CT-410/510/700 device support" if EMBEDDED
+ bool "eGalax, eTurboTouch CT-410/510/700 device support" if EXPERT
depends on TOUCHSCREEN_USB_COMPOSITE
config TOUCHSCREEN_USB_PANJIT
default y
- bool "PanJit device support" if EMBEDDED
+ bool "PanJit device support" if EXPERT
depends on TOUCHSCREEN_USB_COMPOSITE
config TOUCHSCREEN_USB_3M
default y
- bool "3M/Microtouch EX II series device support" if EMBEDDED
+ bool "3M/Microtouch EX II series device support" if EXPERT
depends on TOUCHSCREEN_USB_COMPOSITE
config TOUCHSCREEN_USB_ITM
default y
- bool "ITM device support" if EMBEDDED
+ bool "ITM device support" if EXPERT
depends on TOUCHSCREEN_USB_COMPOSITE
config TOUCHSCREEN_USB_ETURBO
default y
- bool "eTurboTouch (non-eGalax compatible) device support" if EMBEDDED
+ bool "eTurboTouch (non-eGalax compatible) device support" if EXPERT
depends on TOUCHSCREEN_USB_COMPOSITE
config TOUCHSCREEN_USB_GUNZE
default y
- bool "Gunze AHL61 device support" if EMBEDDED
+ bool "Gunze AHL61 device support" if EXPERT
depends on TOUCHSCREEN_USB_COMPOSITE
config TOUCHSCREEN_USB_DMC_TSC10
default y
- bool "DMC TSC-10/25 device support" if EMBEDDED
+ bool "DMC TSC-10/25 device support" if EXPERT
depends on TOUCHSCREEN_USB_COMPOSITE
config TOUCHSCREEN_USB_IRTOUCH
default y
- bool "IRTOUCHSYSTEMS/UNITOP device support" if EMBEDDED
+ bool "IRTOUCHSYSTEMS/UNITOP device support" if EXPERT
depends on TOUCHSCREEN_USB_COMPOSITE
config TOUCHSCREEN_USB_IDEALTEK
default y
- bool "IdealTEK URTC1000 device support" if EMBEDDED
+ bool "IdealTEK URTC1000 device support" if EXPERT
depends on TOUCHSCREEN_USB_COMPOSITE
config TOUCHSCREEN_USB_GENERAL_TOUCH
default y
- bool "GeneralTouch Touchscreen device support" if EMBEDDED
+ bool "GeneralTouch Touchscreen device support" if EXPERT
depends on TOUCHSCREEN_USB_COMPOSITE
config TOUCHSCREEN_USB_GOTOP
default y
- bool "GoTop Super_Q2/GogoPen/PenPower tablet device support" if EMBEDDED
+ bool "GoTop Super_Q2/GogoPen/PenPower tablet device support" if EXPERT
depends on TOUCHSCREEN_USB_COMPOSITE
config TOUCHSCREEN_USB_JASTEC
default y
- bool "JASTEC/DigiTech DTR-02U USB touch controller device support" if EMBEDDED
+ bool "JASTEC/DigiTech DTR-02U USB touch controller device support" if EXPERT
depends on TOUCHSCREEN_USB_COMPOSITE
config TOUCHSCREEN_USB_E2I
@@ -605,17 +615,17 @@ config TOUCHSCREEN_USB_E2I
config TOUCHSCREEN_USB_ZYTRONIC
default y
- bool "Zytronic controller" if EMBEDDED
+ bool "Zytronic controller" if EXPERT
depends on TOUCHSCREEN_USB_COMPOSITE
config TOUCHSCREEN_USB_ETT_TC45USB
default y
- bool "ET&T USB series TC4UM/TC5UH touchscreen controler support" if EMBEDDED
+ bool "ET&T USB series TC4UM/TC5UH touchscreen controller support" if EXPERT
depends on TOUCHSCREEN_USB_COMPOSITE
config TOUCHSCREEN_USB_NEXIO
default y
- bool "NEXIO/iNexio device support" if EMBEDDED
+ bool "NEXIO/iNexio device support" if EXPERT
depends on TOUCHSCREEN_USB_COMPOSITE
config TOUCHSCREEN_TOUCHIT213
@@ -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");