summaryrefslogtreecommitdiffstats
path: root/drivers/input
diff options
context:
space:
mode:
authorLinus Torvalds <torvalds@linux-foundation.org>2009-09-23 15:39:36 -0700
committerLinus Torvalds <torvalds@linux-foundation.org>2009-09-23 15:39:36 -0700
commit0dd52d0df02733dfc2d5f3824e41b96492305384 (patch)
tree4cfd84b7a66d71d83c624275d889136fb23a33c9 /drivers/input
parentc37efa932598de5e30330a1414e34d9e082e0d9e (diff)
parentfde1132374c9ba7da98a73b9a3c150dca6cf8502 (diff)
Merge branch 'for-linus' of git://git.kernel.org/pub/scm/linux/kernel/git/dtor/input
* 'for-linus' of git://git.kernel.org/pub/scm/linux/kernel/git/dtor/input: Input: add driver for Atmel AT42QT2160 Sensor Chip Input: max7359 - use threaded IRQs Input: add driver for Maxim MAX7359 key switch controller Input: add driver for ADP5588 QWERTY I2C Keypad Input: add touchscreen driver for MELFAS MCS-5000 controller Input: add driver for OpenCores Keyboard Controller Input: dm355evm_keys - remove dm355evm_keys_hardirq Input: synaptics_i2c - switch to using __cancel_delayed_work() Input: ad7879 - add support for AD7889 Input: atkbd - rely on input core to restore state on resume Input: add generic suspend and resume for input devices Input: libps2 - additional locking for i8042 ports
Diffstat (limited to 'drivers/input')
-rw-r--r--drivers/input/input.c64
-rw-r--r--drivers/input/keyboard/Kconfig40
-rw-r--r--drivers/input/keyboard/Makefile4
-rw-r--r--drivers/input/keyboard/adp5588-keys.c361
-rw-r--r--drivers/input/keyboard/atkbd.c25
-rw-r--r--drivers/input/keyboard/max7359_keypad.c330
-rw-r--r--drivers/input/keyboard/opencores-kbd.c180
-rw-r--r--drivers/input/keyboard/qt2160.c397
-rw-r--r--drivers/input/misc/dm355evm_keys.c26
-rw-r--r--drivers/input/mouse/sentelic.c18
-rw-r--r--drivers/input/mouse/synaptics_i2c.c51
-rw-r--r--drivers/input/serio/i8042.c41
-rw-r--r--drivers/input/serio/libps2.c28
-rw-r--r--drivers/input/touchscreen/Kconfig17
-rw-r--r--drivers/input/touchscreen/Makefile1
-rw-r--r--drivers/input/touchscreen/ad7879.c6
-rw-r--r--drivers/input/touchscreen/mcs5000_ts.c318
17 files changed, 1828 insertions, 79 deletions
diff --git a/drivers/input/input.c b/drivers/input/input.c
index 556539d617a..e828aab7dac 100644
--- a/drivers/input/input.c
+++ b/drivers/input/input.c
@@ -11,6 +11,7 @@
*/
#include <linux/init.h>
+#include <linux/types.h>
#include <linux/input.h>
#include <linux/module.h>
#include <linux/random.h>
@@ -514,7 +515,7 @@ static void input_disconnect_device(struct input_dev *dev)
* that there are no threads in the middle of input_open_device()
*/
mutex_lock(&dev->mutex);
- dev->going_away = 1;
+ dev->going_away = true;
mutex_unlock(&dev->mutex);
spin_lock_irq(&dev->event_lock);
@@ -1259,10 +1260,71 @@ static int input_dev_uevent(struct device *device, struct kobj_uevent_env *env)
return 0;
}
+#define INPUT_DO_TOGGLE(dev, type, bits, on) \
+ do { \
+ int i; \
+ if (!test_bit(EV_##type, dev->evbit)) \
+ break; \
+ for (i = 0; i < type##_MAX; i++) { \
+ if (!test_bit(i, dev->bits##bit) || \
+ !test_bit(i, dev->bits)) \
+ continue; \
+ dev->event(dev, EV_##type, i, on); \
+ } \
+ } while (0)
+
+static void input_dev_reset(struct input_dev *dev, bool activate)
+{
+ if (!dev->event)
+ return;
+
+ INPUT_DO_TOGGLE(dev, LED, led, activate);
+ INPUT_DO_TOGGLE(dev, SND, snd, activate);
+
+ if (activate && test_bit(EV_REP, dev->evbit)) {
+ dev->event(dev, EV_REP, REP_PERIOD, dev->rep[REP_PERIOD]);
+ dev->event(dev, EV_REP, REP_DELAY, dev->rep[REP_DELAY]);
+ }
+}
+
+#ifdef CONFIG_PM
+static int input_dev_suspend(struct device *dev)
+{
+ struct input_dev *input_dev = to_input_dev(dev);
+
+ mutex_lock(&input_dev->mutex);
+ input_dev_reset(input_dev, false);
+ mutex_unlock(&input_dev->mutex);
+
+ return 0;
+}
+
+static int input_dev_resume(struct device *dev)
+{
+ struct input_dev *input_dev = to_input_dev(dev);
+
+ mutex_lock(&input_dev->mutex);
+ input_dev_reset(input_dev, true);
+ mutex_unlock(&input_dev->mutex);
+
+ return 0;
+}
+
+static const struct dev_pm_ops input_dev_pm_ops = {
+ .suspend = input_dev_suspend,
+ .resume = input_dev_resume,
+ .poweroff = input_dev_suspend,
+ .restore = input_dev_resume,
+};
+#endif /* CONFIG_PM */
+
static struct device_type input_dev_type = {
.groups = input_dev_attr_groups,
.release = input_dev_release,
.uevent = input_dev_uevent,
+#ifdef CONFIG_PM
+ .pm = &input_dev_pm_ops,
+#endif
};
static char *input_devnode(struct device *dev, mode_t *mode)
diff --git a/drivers/input/keyboard/Kconfig b/drivers/input/keyboard/Kconfig
index 3525c19be42..ee98b1bc5d8 100644
--- a/drivers/input/keyboard/Kconfig
+++ b/drivers/input/keyboard/Kconfig
@@ -24,6 +24,16 @@ config KEYBOARD_AAED2000
To compile this driver as a module, choose M here: the
module will be called aaed2000_kbd.
+config KEYBOARD_ADP5588
+ tristate "ADP5588 I2C QWERTY Keypad and IO Expander"
+ depends on I2C
+ help
+ Say Y here if you want to use a ADP5588 attached to your
+ system I2C bus.
+
+ To compile this driver as a module, choose M here: the
+ module will be called adp5588-keys.
+
config KEYBOARD_AMIGA
tristate "Amiga keyboard"
depends on AMIGA
@@ -104,6 +114,16 @@ config KEYBOARD_ATKBD_RDI_KEYCODES
right-hand column will be interpreted as the key shown in the
left-hand column.
+config QT2160
+ tristate "Atmel AT42QT2160 Touch Sensor Chip"
+ depends on I2C && EXPERIMENTAL
+ help
+ If you say yes here you get support for Atmel AT42QT2160 Touch
+ Sensor chip as a keyboard input.
+
+ This driver can also be built as a module. If so, the module
+ will be called qt2160.
+
config KEYBOARD_BFIN
tristate "Blackfin BF54x keypad support"
depends on (BF54x && !BF544)
@@ -251,6 +271,17 @@ config KEYBOARD_MAPLE
To compile this driver as a module, choose M here: the
module will be called maple_keyb.
+config KEYBOARD_MAX7359
+ tristate "Maxim MAX7359 Key Switch Controller"
+ depends on I2C
+ help
+ If you say yes here you get support for the Maxim MAX7359 Key
+ Switch Controller chip. This providers microprocessors with
+ management of up to 64 key switches
+
+ To compile this driver as a module, choose M here: the
+ module will be called max7359_keypad.
+
config KEYBOARD_NEWTON
tristate "Newton keyboard"
select SERIO
@@ -260,6 +291,15 @@ config KEYBOARD_NEWTON
To compile this driver as a module, choose M here: the
module will be called newtonkbd.
+config KEYBOARD_OPENCORES
+ tristate "OpenCores Keyboard Controller"
+ help
+ Say Y here if you want to use the OpenCores Keyboard Controller
+ http://www.opencores.org/project,keyboardcontroller
+
+ To compile this driver as a module, choose M here; the
+ module will be called opencores-kbd.
+
config KEYBOARD_PXA27x
tristate "PXA27x/PXA3xx keypad support"
depends on PXA27x || PXA3xx
diff --git a/drivers/input/keyboard/Makefile b/drivers/input/keyboard/Makefile
index 8a7a22b3026..babad5e58b7 100644
--- a/drivers/input/keyboard/Makefile
+++ b/drivers/input/keyboard/Makefile
@@ -5,6 +5,7 @@
# Each configuration option enables a list of files.
obj-$(CONFIG_KEYBOARD_AAED2000) += aaed2000_kbd.o
+obj-$(CONFIG_KEYBOARD_ADP5588) += adp5588-keys.o
obj-$(CONFIG_KEYBOARD_AMIGA) += amikbd.o
obj-$(CONFIG_KEYBOARD_ATARI) += atakbd.o
obj-$(CONFIG_KEYBOARD_ATKBD) += atkbd.o
@@ -21,10 +22,13 @@ obj-$(CONFIG_KEYBOARD_LM8323) += lm8323.o
obj-$(CONFIG_KEYBOARD_LOCOMO) += locomokbd.o
obj-$(CONFIG_KEYBOARD_MAPLE) += maple_keyb.o
obj-$(CONFIG_KEYBOARD_MATRIX) += matrix_keypad.o
+obj-$(CONFIG_KEYBOARD_MAX7359) += max7359_keypad.o
obj-$(CONFIG_KEYBOARD_NEWTON) += newtonkbd.o
obj-$(CONFIG_KEYBOARD_OMAP) += omap-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_QT2160) += qt2160.o
obj-$(CONFIG_KEYBOARD_SH_KEYSC) += sh_keysc.o
obj-$(CONFIG_KEYBOARD_SPITZ) += spitzkbd.o
obj-$(CONFIG_KEYBOARD_STOWAWAY) += stowaway.o
diff --git a/drivers/input/keyboard/adp5588-keys.c b/drivers/input/keyboard/adp5588-keys.c
new file mode 100644
index 00000000000..d48c808d592
--- /dev/null
+++ b/drivers/input/keyboard/adp5588-keys.c
@@ -0,0 +1,361 @@
+/*
+ * File: drivers/input/keyboard/adp5588_keys.c
+ * Description: keypad driver for ADP5588 I2C QWERTY Keypad and IO Expander
+ * Bugs: Enter bugs at http://blackfin.uclinux.org/
+ *
+ * Copyright (C) 2008-2009 Analog Devices Inc.
+ * Licensed under the GPL-2 or later.
+ */
+
+#include <linux/module.h>
+#include <linux/version.h>
+#include <linux/init.h>
+#include <linux/interrupt.h>
+#include <linux/irq.h>
+#include <linux/workqueue.h>
+#include <linux/errno.h>
+#include <linux/pm.h>
+#include <linux/platform_device.h>
+#include <linux/input.h>
+#include <linux/i2c.h>
+
+#include <linux/i2c/adp5588.h>
+
+ /* Configuration Register1 */
+#define AUTO_INC (1 << 7)
+#define GPIEM_CFG (1 << 6)
+#define OVR_FLOW_M (1 << 5)
+#define INT_CFG (1 << 4)
+#define OVR_FLOW_IEN (1 << 3)
+#define K_LCK_IM (1 << 2)
+#define GPI_IEN (1 << 1)
+#define KE_IEN (1 << 0)
+
+/* Interrupt Status Register */
+#define CMP2_INT (1 << 5)
+#define CMP1_INT (1 << 4)
+#define OVR_FLOW_INT (1 << 3)
+#define K_LCK_INT (1 << 2)
+#define GPI_INT (1 << 1)
+#define KE_INT (1 << 0)
+
+/* Key Lock and Event Counter Register */
+#define K_LCK_EN (1 << 6)
+#define LCK21 0x30
+#define KEC 0xF
+
+/* Key Event Register xy */
+#define KEY_EV_PRESSED (1 << 7)
+#define KEY_EV_MASK (0x7F)
+
+#define KP_SEL(x) (0xFFFF >> (16 - x)) /* 2^x-1 */
+
+#define KEYP_MAX_EVENT 10
+
+/*
+ * Early pre 4.0 Silicon required to delay readout by at least 25ms,
+ * since the Event Counter Register updated 25ms after the interrupt
+ * asserted.
+ */
+#define WA_DELAYED_READOUT_REVID(rev) ((rev) < 4)
+
+struct adp5588_kpad {
+ struct i2c_client *client;
+ struct input_dev *input;
+ struct delayed_work work;
+ unsigned long delay;
+ unsigned short keycode[ADP5588_KEYMAPSIZE];
+};
+
+static int adp5588_read(struct i2c_client *client, u8 reg)
+{
+ int ret = i2c_smbus_read_byte_data(client, reg);
+
+ if (ret < 0)
+ dev_err(&client->dev, "Read Error\n");
+
+ return ret;
+}
+
+static int adp5588_write(struct i2c_client *client, u8 reg, u8 val)
+{
+ return i2c_smbus_write_byte_data(client, reg, val);
+}
+
+static void adp5588_work(struct work_struct *work)
+{
+ struct adp5588_kpad *kpad = container_of(work,
+ struct adp5588_kpad, work.work);
+ struct i2c_client *client = kpad->client;
+ int i, key, status, ev_cnt;
+
+ status = adp5588_read(client, INT_STAT);
+
+ if (status & OVR_FLOW_INT) /* Unlikely and should never happen */
+ dev_err(&client->dev, "Event Overflow Error\n");
+
+ if (status & KE_INT) {
+ ev_cnt = adp5588_read(client, KEY_LCK_EC_STAT) & KEC;
+ if (ev_cnt) {
+ for (i = 0; i < ev_cnt; i++) {
+ key = adp5588_read(client, Key_EVENTA + i);
+ input_report_key(kpad->input,
+ kpad->keycode[(key & KEY_EV_MASK) - 1],
+ key & KEY_EV_PRESSED);
+ }
+ input_sync(kpad->input);
+ }
+ }
+ adp5588_write(client, INT_STAT, status); /* Status is W1C */
+}
+
+static irqreturn_t adp5588_irq(int irq, void *handle)
+{
+ struct adp5588_kpad *kpad = handle;
+
+ /*
+ * use keventd context to read the event fifo registers
+ * Schedule readout at least 25ms after notification for
+ * REVID < 4
+ */
+
+ schedule_delayed_work(&kpad->work, kpad->delay);
+
+ return IRQ_HANDLED;
+}
+
+static int __devinit adp5588_setup(struct i2c_client *client)
+{
+ struct adp5588_kpad_platform_data *pdata = client->dev.platform_data;
+ int i, ret;
+
+ ret = adp5588_write(client, KP_GPIO1, KP_SEL(pdata->rows));
+ ret |= adp5588_write(client, KP_GPIO2, KP_SEL(pdata->cols) & 0xFF);
+ ret |= adp5588_write(client, KP_GPIO3, KP_SEL(pdata->cols) >> 8);
+
+ if (pdata->en_keylock) {
+ ret |= adp5588_write(client, UNLOCK1, pdata->unlock_key1);
+ ret |= adp5588_write(client, UNLOCK2, pdata->unlock_key2);
+ ret |= adp5588_write(client, KEY_LCK_EC_STAT, K_LCK_EN);
+ }
+
+ for (i = 0; i < KEYP_MAX_EVENT; i++)
+ ret |= adp5588_read(client, Key_EVENTA);
+
+ ret |= adp5588_write(client, INT_STAT, CMP2_INT | CMP1_INT |
+ OVR_FLOW_INT | K_LCK_INT |
+ GPI_INT | KE_INT); /* Status is W1C */
+
+ ret |= adp5588_write(client, CFG, INT_CFG | OVR_FLOW_IEN | KE_IEN);
+
+ if (ret < 0) {
+ dev_err(&client->dev, "Write Error\n");
+ return ret;
+ }
+
+ return 0;
+}
+
+static int __devinit adp5588_probe(struct i2c_client *client,
+ const struct i2c_device_id *id)
+{
+ struct adp5588_kpad *kpad;
+ struct adp5588_kpad_platform_data *pdata = client->dev.platform_data;
+ struct input_dev *input;
+ unsigned int revid;
+ int ret, i;
+ int error;
+
+ if (!i2c_check_functionality(client->adapter,
+ I2C_FUNC_SMBUS_BYTE_DATA)) {
+ dev_err(&client->dev, "SMBUS Byte Data not Supported\n");
+ return -EIO;
+ }
+
+ if (!pdata) {
+ dev_err(&client->dev, "no platform data?\n");
+ return -EINVAL;
+ }
+
+ if (!pdata->rows || !pdata->cols || !pdata->keymap) {
+ dev_err(&client->dev, "no rows, cols or keymap from pdata\n");
+ return -EINVAL;
+ }
+
+ if (pdata->keymapsize != ADP5588_KEYMAPSIZE) {
+ dev_err(&client->dev, "invalid keymapsize\n");
+ return -EINVAL;
+ }
+
+ if (!client->irq) {
+ dev_err(&client->dev, "no IRQ?\n");
+ return -EINVAL;
+ }
+
+ kpad = kzalloc(sizeof(*kpad), GFP_KERNEL);
+ input = input_allocate_device();
+ if (!kpad || !input) {
+ error = -ENOMEM;
+ goto err_free_mem;
+ }
+
+ kpad->client = client;
+ kpad->input = input;
+ INIT_DELAYED_WORK(&kpad->work, adp5588_work);
+
+ ret = adp5588_read(client, DEV_ID);
+ if (ret < 0) {
+ error = ret;
+ goto err_free_mem;
+ }
+
+ revid = (u8) ret & ADP5588_DEVICE_ID_MASK;
+ if (WA_DELAYED_READOUT_REVID(revid))
+ kpad->delay = msecs_to_jiffies(30);
+
+ input->name = client->name;
+ input->phys = "adp5588-keys/input0";
+ input->dev.parent = &client->dev;
+
+ input_set_drvdata(input, kpad);
+
+ input->id.bustype = BUS_I2C;
+ input->id.vendor = 0x0001;
+ input->id.product = 0x0001;
+ input->id.version = revid;
+
+ input->keycodesize = sizeof(kpad->keycode[0]);
+ input->keycodemax = pdata->keymapsize;
+ input->keycode = kpad->keycode;
+
+ memcpy(kpad->keycode, pdata->keymap,
+ pdata->keymapsize * input->keycodesize);
+
+ /* setup input device */
+ __set_bit(EV_KEY, input->evbit);
+
+ if (pdata->repeat)
+ __set_bit(EV_REP, input->evbit);
+
+ for (i = 0; i < input->keycodemax; i++)
+ __set_bit(kpad->keycode[i] & KEY_MAX, input->keybit);
+ __clear_bit(KEY_RESERVED, input->keybit);
+
+ error = input_register_device(input);
+ if (error) {
+ dev_err(&client->dev, "unable to register input device\n");
+ goto err_free_mem;
+ }
+
+ error = request_irq(client->irq, adp5588_irq,
+ IRQF_TRIGGER_FALLING | IRQF_DISABLED,
+ client->dev.driver->name, kpad);
+ if (error) {
+ dev_err(&client->dev, "irq %d busy?\n", client->irq);
+ goto err_unreg_dev;
+ }
+
+ error = adp5588_setup(client);
+ if (error)
+ goto err_free_irq;
+
+ device_init_wakeup(&client->dev, 1);
+ i2c_set_clientdata(client, kpad);
+
+ dev_info(&client->dev, "Rev.%d keypad, irq %d\n", revid, client->irq);
+ return 0;
+
+ err_free_irq:
+ free_irq(client->irq, kpad);
+ err_unreg_dev:
+ input_unregister_device(input);
+ input = NULL;
+ err_free_mem:
+ input_free_device(input);
+ kfree(kpad);
+
+ return error;
+}
+
+static int __devexit adp5588_remove(struct i2c_client *client)
+{
+ struct adp5588_kpad *kpad = i2c_get_clientdata(client);
+
+ adp5588_write(client, CFG, 0);
+ free_irq(client->irq, kpad);
+ cancel_delayed_work_sync(&kpad->work);
+ input_unregister_device(kpad->input);
+ i2c_set_clientdata(client, NULL);
+ kfree(kpad);
+
+ return 0;
+}
+
+#ifdef CONFIG_PM
+static int adp5588_suspend(struct device *dev)
+{
+ struct adp5588_kpad *kpad = dev_get_drvdata(dev);
+ struct i2c_client *client = kpad->client;
+
+ disable_irq(client->irq);
+ cancel_delayed_work_sync(&kpad->work);
+
+ if (device_may_wakeup(&client->dev))
+ enable_irq_wake(client->irq);
+
+ return 0;
+}
+
+static int adp5588_resume(struct device *dev)
+{
+ struct adp5588_kpad *kpad = dev_get_drvdata(dev);
+ struct i2c_client *client = kpad->client;
+
+ if (device_may_wakeup(&client->dev))
+ disable_irq_wake(client->irq);
+
+ enable_irq(client->irq);
+
+ return 0;
+}
+
+static struct dev_pm_ops adp5588_dev_pm_ops = {
+ .suspend = adp5588_suspend,
+ .resume = adp5588_resume,
+};
+#endif
+
+static const struct i2c_device_id adp5588_id[] = {
+ { KBUILD_MODNAME, 0 },
+ { }
+};
+MODULE_DEVICE_TABLE(i2c, adp5588_id);
+
+static struct i2c_driver adp5588_driver = {
+ .driver = {
+ .name = KBUILD_MODNAME,
+#ifdef CONFIG_PM
+ .pm = &adp5588_dev_pm_ops,
+#endif
+ },
+ .probe = adp5588_probe,
+ .remove = __devexit_p(adp5588_remove),
+ .id_table = adp5588_id,
+};
+
+static int __init adp5588_init(void)
+{
+ return i2c_add_driver(&adp5588_driver);
+}
+module_init(adp5588_init);
+
+static void __exit adp5588_exit(void)
+{
+ i2c_del_driver(&adp5588_driver);
+}
+module_exit(adp5588_exit);
+
+MODULE_LICENSE("GPL");
+MODULE_AUTHOR("Michael Hennerich <hennerich@blackfin.uclinux.org>");
+MODULE_DESCRIPTION("ADP5588 Keypad driver");
+MODULE_ALIAS("platform:adp5588-keys");
diff --git a/drivers/input/keyboard/atkbd.c b/drivers/input/keyboard/atkbd.c
index adb09e2ba39..4709e15af60 100644
--- a/drivers/input/keyboard/atkbd.c
+++ b/drivers/input/keyboard/atkbd.c
@@ -773,23 +773,6 @@ static int atkbd_select_set(struct atkbd *atkbd, int target_set, int allow_extra
static int atkbd_activate(struct atkbd *atkbd)
{
struct ps2dev *ps2dev = &atkbd->ps2dev;
- unsigned char param[1];
-
-/*
- * Set the LEDs to a defined state.
- */
-
- param[0] = 0;
- if (ps2_command(ps2dev, param, ATKBD_CMD_SETLEDS))
- return -1;
-
-/*
- * Set autorepeat to fastest possible.
- */
-
- param[0] = 0;
- if (ps2_command(ps2dev, param, ATKBD_CMD_SETREP))
- return -1;
/*
* Enable the keyboard to receive keystrokes.
@@ -1158,14 +1141,6 @@ static int atkbd_reconnect(struct serio *serio)
return -1;
atkbd_activate(atkbd);
-
-/*
- * Restore repeat rate and LEDs (that were reset by atkbd_activate)
- * to pre-resume state
- */
- if (!atkbd->softrepeat)
- atkbd_set_repeat_rate(atkbd);
- atkbd_set_leds(atkbd);
}
atkbd_enable(atkbd);
diff --git a/drivers/input/keyboard/max7359_keypad.c b/drivers/input/keyboard/max7359_keypad.c
new file mode 100644
index 00000000000..3b5b948eba3
--- /dev/null
+++ b/drivers/input/keyboard/max7359_keypad.c
@@ -0,0 +1,330 @@
+/*
+ * max7359_keypad.c - MAX7359 Key Switch Controller Driver
+ *
+ * Copyright (C) 2009 Samsung Electronics
+ * Kim Kyuwon <q1.kim@samsung.com>
+ *
+ * Based on pxa27x_keypad.c
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * Datasheet: http://www.maxim-ic.com/quick_view2.cfm/qv_pk/5456
+ */
+
+#include <linux/module.h>
+#include <linux/i2c.h>
+#include <linux/interrupt.h>
+#include <linux/input.h>
+#include <linux/input/matrix_keypad.h>
+
+#define MAX7359_MAX_KEY_ROWS 8
+#define MAX7359_MAX_KEY_COLS 8
+#define MAX7359_MAX_KEY_NUM (MAX7359_MAX_KEY_ROWS * MAX7359_MAX_KEY_COLS)
+#define MAX7359_ROW_SHIFT 3
+
+/*
+ * MAX7359 registers
+ */
+#define MAX7359_REG_KEYFIFO 0x00
+#define MAX7359_REG_CONFIG 0x01
+#define MAX7359_REG_DEBOUNCE 0x02
+#define MAX7359_REG_INTERRUPT 0x03
+#define MAX7359_REG_PORTS 0x04
+#define MAX7359_REG_KEYREP 0x05
+#define MAX7359_REG_SLEEP 0x06
+
+/*
+ * Configuration register bits
+ */
+#define MAX7359_CFG_SLEEP (1 << 7)
+#define MAX7359_CFG_INTERRUPT (1 << 5)
+#define MAX7359_CFG_KEY_RELEASE (1 << 3)
+#define MAX7359_CFG_WAKEUP (1 << 1)
+#define MAX7359_CFG_TIMEOUT (1 << 0)
+
+/*
+ * Autosleep register values (ms)
+ */
+#define MAX7359_AUTOSLEEP_8192 0x01
+#define MAX7359_AUTOSLEEP_4096 0x02
+#define MAX7359_AUTOSLEEP_2048 0x03
+#define MAX7359_AUTOSLEEP_1024 0x04
+#define MAX7359_AUTOSLEEP_512 0x05
+#define MAX7359_AUTOSLEEP_256 0x06
+
+struct max7359_keypad {
+ /* matrix key code map */
+ unsigned short keycodes[MAX7359_MAX_KEY_NUM];
+
+ struct input_dev *input_dev;
+ struct i2c_client *client;
+};
+
+static int max7359_write_reg(struct i2c_client *client, u8 reg, u8 val)
+{
+ int ret = i2c_smbus_write_byte_data(client, reg, val);
+
+ if (ret < 0)
+ dev_err(&client->dev, "%s: reg 0x%x, val 0x%x, err %d\n",
+ __func__, reg, val, ret);
+ return ret;
+}
+
+static int max7359_read_reg(struct i2c_client *client, int reg)
+{
+ int ret = i2c_smbus_read_byte_data(client, reg);
+
+ if (ret < 0)
+ dev_err(&client->dev, "%s: reg 0x%x, err %d\n",
+ __func__, reg, ret);
+ return ret;
+}
+
+static void max7359_build_keycode(struct max7359_keypad *keypad,
+ const struct matrix_keymap_data *keymap_data)
+{
+ struct input_dev *input_dev = keypad->input_dev;
+ int i;
+
+ for (i = 0; i < keymap_data->keymap_size; i++) {
+ unsigned int key = keymap_data->keymap[i];
+ unsigned int row = KEY_ROW(key);
+ unsigned int col = KEY_COL(key);
+ unsigned int scancode = MATRIX_SCAN_CODE(row, col,
+ MAX7359_ROW_SHIFT);
+ unsigned short keycode = KEY_VAL(key);
+
+ keypad->keycodes[scancode] = keycode;
+ __set_bit(keycode, input_dev->keybit);
+ }
+ __clear_bit(KEY_RESERVED, input_dev->keybit);
+}
+
+/* runs in an IRQ thread -- can (and will!) sleep */
+static irqreturn_t max7359_interrupt(int irq, void *dev_id)
+{
+ struct max7359_keypad *keypad = dev_id;
+ struct input_dev *input_dev = keypad->input_dev;
+ int val, row, col, release, code;
+
+ val = max7359_read_reg(keypad->client, MAX7359_REG_KEYFIFO);
+ row = val & 0x7;
+ col = (val >> 3) & 0x7;
+ release = val & 0x40;
+
+ code = MATRIX_SCAN_CODE(row, col, MAX7359_ROW_SHIFT);
+
+ dev_dbg(&keypad->client->dev,
+ "key[%d:%d] %s\n", row, col, release ? "release" : "press");
+
+ input_event(input_dev, EV_MSC, MSC_SCAN, code);
+ input_report_key(input_dev, keypad->keycodes[code], !release);
+ input_sync(input_dev);
+
+ return IRQ_HANDLED;
+}
+
+/*
+ * Let MAX7359 fall into a deep sleep:
+ * If no keys are pressed, enter sleep mode for 8192 ms. And if any
+ * key is pressed, the MAX7359 returns to normal operating mode.
+ */
+static inline void max7359_fall_deepsleep(struct i2c_client *client)
+{
+ max7359_write_reg(client, MAX7359_REG_SLEEP, MAX7359_AUTOSLEEP_8192);
+}
+
+/*
+ * Let MAX7359 take a catnap:
+ * Autosleep just for 256 ms.
+ */
+static inline void max7359_take_catnap(struct i2c_client *client)
+{
+ max7359_write_reg(client, MAX7359_REG_SLEEP, MAX7359_AUTOSLEEP_256);
+}
+
+static int max7359_open(struct input_dev *dev)
+{
+ struct max7359_keypad *keypad = input_get_drvdata(dev);
+
+ max7359_take_catnap(keypad->client);
+
+ return 0;
+}
+
+static void max7359_close(struct input_dev *dev)
+{
+ struct max7359_keypad *keypad = input_get_drvdata(dev);
+
+ max7359_fall_deepsleep(keypad->client);
+}
+
+static void max7359_initialize(struct i2c_client *client)
+{
+ max7359_write_reg(client, MAX7359_REG_CONFIG,
+ MAX7359_CFG_INTERRUPT | /* Irq clears after host read */
+ MAX7359_CFG_KEY_RELEASE | /* Key release enable */
+ MAX7359_CFG_WAKEUP); /* Key press wakeup enable */
+
+ /* Full key-scan functionality */
+ max7359_write_reg(client, MAX7359_REG_DEBOUNCE, 0x1F);
+
+ /* nINT asserts every debounce cycles */
+ max7359_write_reg(client, MAX7359_REG_INTERRUPT, 0x01);
+
+ max7359_fall_deepsleep(client);
+}
+
+static int __devinit max7359_probe(struct i2c_client *client,
+ const struct i2c_device_id *id)
+{
+ const struct matrix_keymap_data *keymap_data = client->dev.platform_data;
+ struct max7359_keypad *keypad;
+ struct input_dev *input_dev;
+ int ret;
+ int error;
+
+ if (!client->irq) {
+ dev_err(&client->dev, "The irq number should not be zero\n");
+ return -EINVAL;
+ }
+
+ /* Detect MAX7359: The initial Keys FIFO value is '0x3F' */
+ ret = max7359_read_reg(client, MAX7359_REG_KEYFIFO);
+ if (ret < 0) {
+ dev_err(&client->dev, "failed to detect device\n");
+ return -ENODEV;
+ }
+
+ dev_dbg(&client->dev, "keys FIFO is 0x%02x\n", ret);
+
+ keypad = kzalloc(sizeof(struct max7359_keypad), GFP_KERNEL);
+ input_dev = input_allocate_device();
+ if (!keypad || !input_dev) {
+ dev_err(&client->dev, "failed to allocate memory\n");
+ error = -ENOMEM;
+ goto failed_free_mem;
+ }
+
+ keypad->client = client;
+ keypad->input_dev = input_dev;
+
+ input_dev->name = client->name;
+ input_dev->id.bustype = BUS_I2C;
+ input_dev->open = max7359_open;
+ input_dev->close = max7359_close;
+ input_dev->dev.parent = &client->dev;
+
+ input_dev->evbit[0] = BIT_MASK(EV_KEY) | BIT_MASK(EV_REP);
+ input_dev->keycodesize = sizeof(keypad->keycodes[0]);
+ input_dev->keycodemax = ARRAY_SIZE(keypad->keycodes);
+ input_dev->keycode = keypad->keycodes;
+
+ input_set_capability(input_dev, EV_MSC, MSC_SCAN);
+ input_set_drvdata(input_dev, keypad);
+
+ max7359_build_keycode(keypad, keymap_data);
+
+ error = request_threaded_irq(client->irq, NULL, max7359_interrupt,
+ IRQF_TRIGGER_LOW | IRQF_ONESHOT,
+ client->name, keypad);
+ if (error) {
+ dev_err(&client->dev, "failed to register interrupt\n");
+ goto failed_free_mem;
+ }
+
+ /* Register the input device */
+ error = input_register_device(input_dev);
+ if (error) {
+ dev_err(&client->dev, "failed to register input device\n");
+ goto failed_free_irq;
+ }
+
+ /* Initialize MAX7359 */
+ max7359_initialize(client);
+
+ i2c_set_clientdata(client, keypad);
+ device_init_wakeup(&client->dev, 1);
+
+ return 0;
+
+failed_free_irq:
+ free_irq(client->irq, keypad);
+failed_free_mem:
+ input_free_device(input_dev);
+ kfree(keypad);
+ return error;
+}
+
+static int __devexit max7359_remove(struct i2c_client *client)
+{
+ struct max7359_keypad *keypad = i2c_get_clientdata(client);
+
+ free_irq(client->irq, keypad);
+ input_unregister_device(keypad->input_dev);
+ i2c_set_clientdata(client, NULL);
+ kfree(keypad);
+
+ return 0;
+}
+
+#ifdef CONFIG_PM
+static int max7359_suspend(struct i2c_client *client, pm_message_t mesg)
+{
+ max7359_fall_deepsleep(client);
+
+ if (device_may_wakeup(&client->dev))
+ enable_irq_wake(client->irq);
+
+ return 0;
+}
+
+static int max7359_resume(struct i2c_client *client)
+{
+ if (device_may_wakeup(&client->dev))
+ disable_irq_wake(client->irq);
+
+ /* Restore the default setting */
+ max7359_take_catnap(client);
+
+ return 0;
+}
+#else
+#define max7359_suspend NULL
+#define max7359_resume NULL
+#endif
+
+static const struct i2c_device_id max7359_ids[] = {
+ { "max7359", 0 },
+ { }
+};
+MODULE_DEVICE_TABLE(i2c, max7359_ids);
+
+static struct i2c_driver max7359_i2c_driver = {
+ .driver = {
+ .name = "max7359",
+ },
+ .probe = max7359_probe,
+ .remove = __devexit_p(max7359_remove),
+ .suspend = max7359_suspend,
+ .resume = max7359_resume,
+ .id_table = max7359_ids,
+};
+
+static int __init max7359_init(void)
+{
+ return i2c_add_driver(&max7359_i2c_driver);
+}
+module_init(max7359_init);
+
+static void __exit max7359_exit(void)
+{
+ i2c_del_driver(&max7359_i2c_driver);
+}
+module_exit(max7359_exit);
+
+MODULE_AUTHOR("Kim Kyuwon <q1.kim@samsung.com>");
+MODULE_DESCRIPTION("MAX7359 Key Switch Controller Driver");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/input/keyboard/opencores-kbd.c b/drivers/input/keyboard/opencores-kbd.c
new file mode 100644
index 00000000000..78cccddbf55
--- /dev/null
+++ b/drivers/input/keyboard/opencores-kbd.c
@@ -0,0 +1,180 @@
+/*
+ * OpenCores Keyboard Controller Driver
+ * http://www.opencores.org/project,keyboardcontroller
+ *
+ * Copyright 2007-2009 HV Sistemas S.L.
+ *
+ * Licensed under the GPL-2 or later.
+ */
+
+#include <linux/input.h>
+#include <linux/interrupt.h>
+#include <linux/io.h>
+#include <linux/ioport.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/platform_device.h>
+
+struct opencores_kbd {
+ struct input_dev *input;
+ struct resource *addr_res;
+ void __iomem *addr;
+ int irq;
+ unsigned short keycodes[128];
+};
+
+static irqreturn_t opencores_kbd_isr(int irq, void *dev_id)
+{
+ struct opencores_kbd *opencores_kbd = dev_id;
+ struct input_dev *input = opencores_kbd->input;
+ unsigned char c;
+
+ c = readb(opencores_kbd->addr);
+ input_report_key(input, c & 0x7f, c & 0x80 ? 0 : 1);
+ input_sync(input);
+
+ return IRQ_HANDLED;
+}
+
+static int __devinit opencores_kbd_probe(struct platform_device *pdev)
+{
+ struct input_dev *input;
+ struct opencores_kbd *opencores_kbd;
+ struct resource *res;
+ int irq, i, error;
+
+ res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+ if (!res) {
+ dev_err(&pdev->dev, "missing board memory resource\n");
+ return -EINVAL;
+ }
+
+ irq = platform_get_irq(pdev, 0);
+ if (irq < 0) {
+ dev_err(&pdev->dev, "missing board IRQ resource\n");
+ return -EINVAL;
+ }
+
+ opencores_kbd = kzalloc(sizeof(*opencores_kbd), GFP_KERNEL);
+ input = input_allocate_device();
+ if (!opencores_kbd || !input) {
+ dev_err(&pdev->dev, "failed to allocate device structures\n");
+ error = -ENOMEM;
+ goto err_free_mem;
+ }
+
+ opencores_kbd->addr_res = res;
+ res = request_mem_region(res->start, resource_size(res), pdev->name);
+ if (!res) {
+ dev_err(&pdev->dev, "failed to request I/O memory\n");
+ error = -EBUSY;
+ goto err_free_mem;
+ }
+
+ opencores_kbd->addr = ioremap(res->start, resource_size(res));
+ if (!opencores_kbd->addr) {
+ dev_err(&pdev->dev, "failed to remap I/O memory\n");
+ error = -ENXIO;
+ goto err_rel_mem;
+ }
+
+ opencores_kbd->input = input;
+ opencores_kbd->irq = irq;
+
+ input->name = pdev->name;
+ input->phys = "opencores-kbd/input0";
+ input->dev.parent = &pdev->dev;
+
+ input_set_drvdata(input, opencores_kbd);
+
+ input->id.bustype = BUS_HOST;
+ input->id.vendor = 0x0001;
+ input->id.product = 0x0001;
+ input->id.version = 0x0100;
+
+ input->keycode = opencores_kbd->keycodes;
+ input->keycodesize = sizeof(opencores_kbd->keycodes[0]);
+ input->keycodemax = ARRAY_SIZE(opencores_kbd->keycodes);
+
+ __set_bit(EV_KEY, input->evbit);
+
+ for (i = 0; i < ARRAY_SIZE(opencores_kbd->keycodes); i++) {
+ /*
+ * OpenCores controller happens to have scancodes match
+ * our KEY_* definitions.
+ */
+ opencores_kbd->keycodes[i] = i;
+ __set_bit(opencores_kbd->keycodes[i], input->keybit);
+ }
+ __clear_bit(KEY_RESERVED, input->keybit);
+
+ error = request_irq(irq, &opencores_kbd_isr,
+ IRQF_TRIGGER_RISING, pdev->name, opencores_kbd);
+ if (error) {
+ dev_err(&pdev->dev, "unable to claim irq %d\n", irq);
+ goto err_unmap_mem;
+ }
+
+ error = input_register_device(input);
+ if (error) {
+ dev_err(&pdev->dev, "unable to register input device\n");
+ goto err_free_irq;
+ }
+
+ platform_set_drvdata(pdev, opencores_kbd);
+
+ return 0;
+
+ err_free_irq:
+ free_irq(irq, opencores_kbd);
+ err_unmap_mem:
+ iounmap(opencores_kbd->addr);
+ err_rel_mem:
+ release_mem_region(res->start, resource_size(res));
+ err_free_mem:
+ input_free_device(input);
+ kfree(opencores_kbd);
+
+ return error;
+}
+
+static int __devexit opencores_kbd_remove(struct platform_device *pdev)
+{
+ struct opencores_kbd *opencores_kbd = platform_get_drvdata(pdev);
+
+ free_irq(opencores_kbd->irq, opencores_kbd);
+
+ iounmap(opencores_kbd->addr);
+ release_mem_region(opencores_kbd->addr_res->start,
+ resource_size(opencores_kbd->addr_res));
+ input_unregister_device(opencores_kbd->input);
+ kfree(opencores_kbd);
+
+ platform_set_drvdata(pdev, NULL);
+
+ return 0;
+}
+
+static struct platform_driver opencores_kbd_device_driver = {
+ .probe = opencores_kbd_probe,
+ .remove = __devexit_p(opencores_kbd_remove),
+ .driver = {
+ .name = "opencores-kbd",
+ },
+};
+
+static int __init opencores_kbd_init(void)
+{
+ return platform_driver_register(&opencores_kbd_device_driver);
+}
+module_init(opencores_kbd_init);
+
+static void __exit opencores_kbd_exit(void)
+{
+ platform_driver_unregister(&opencores_kbd_device_driver);
+}
+module_exit(opencores_kbd_exit);
+
+MODULE_LICENSE("GPL");
+MODULE_AUTHOR("Javier Herrero <jherrero@hvsistemas.es>");
+MODULE_DESCRIPTION("Keyboard driver for OpenCores Keyboard Controller");
diff --git a/drivers/input/keyboard/qt2160.c b/drivers/input/keyboard/qt2160.c
new file mode 100644
index 00000000000..191cc51d6cf
--- /dev/null
+++ b/drivers/input/keyboard/qt2160.c
@@ -0,0 +1,397 @@
+/*
+ * qt2160.c - Atmel AT42QT2160 Touch Sense Controller
+ *
+ * Copyright (C) 2009 Raphael Derosso Pereira <raphaelpereira@gmail.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., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+#include <linux/kernel.h>
+#include <linux/init.h>
+#include <linux/module.h>
+#include <linux/slab.h>
+#include <linux/jiffies.h>
+#include <linux/i2c.h>
+#include <linux/irq.h>
+#include <linux/interrupt.h>
+#include <linux/input.h>
+
+#define QT2160_VALID_CHIPID 0x11
+
+#define QT2160_CMD_CHIPID 0
+#define QT2160_CMD_CODEVER 1
+#define QT2160_CMD_GSTAT 2
+#define QT2160_CMD_KEYS3 3
+#define QT2160_CMD_KEYS4 4
+#define QT2160_CMD_SLIDE 5
+#define QT2160_CMD_GPIOS 6
+#define QT2160_CMD_SUBVER 7
+#define QT2160_CMD_CALIBRATE 10
+
+#define QT2160_CYCLE_INTERVAL (2*HZ)
+
+static unsigned char qt2160_key2code[] = {
+ KEY_0, KEY_1, KEY_2, KEY_3,
+ KEY_4, KEY_5, KEY_6, KEY_7,
+ KEY_8, KEY_9, KEY_A, KEY_B,
+ KEY_C, KEY_D, KEY_E, KEY_F,
+};
+
+struct qt2160_data {
+ struct i2c_client *client;
+ struct input_dev *input;
+ struct delayed_work dwork;
+ spinlock_t lock; /* Protects canceling/rescheduling of dwork */
+ unsigned short keycodes[ARRAY_SIZE(qt2160_key2code)];
+ u16 key_matrix;
+};
+
+static int qt2160_read_block(struct i2c_client *client,
+ u8 inireg, u8 *buffer, unsigned int count)
+{
+ int error, idx = 0;
+
+ /*
+ * Can't use SMBus block data read. Check for I2C functionality to speed
+ * things up whenever possible. Otherwise we will be forced to read
+ * sequentially.
+ */
+ if (i2c_check_functionality(client->adapter, I2C_FUNC_I2C)) {
+
+ error = i2c_smbus_write_byte(client, inireg + idx);
+ if (error) {
+ dev_err(&client->dev,
+ "couldn't send request. Returned %d\n", error);
+ return error;
+ }
+
+ error = i2c_master_recv(client, buffer, count);
+ if (error != count) {
+ dev_err(&client->dev,
+ "couldn't read registers. Returned %d bytes\n", error);
+ return error;
+ }
+ } else {
+
+ while (count--) {
+ int data;
+
+ error = i2c_smbus_write_byte(client, inireg + idx);
+ if (error) {
+ dev_err(&client->dev,
+ "couldn't send request. Returned %d\n", error);
+ return error;
+ }
+
+ data = i2c_smbus_read_byte(client);
+ if (data < 0) {
+ dev_err(&client->dev,
+ "couldn't read register. Returned %d\n", data);
+ return data;
+ }
+
+ buffer[idx++] = data;
+ }
+ }
+
+ return 0;
+}
+
+static int qt2160_get_key_matrix(struct qt2160_data *qt2160)
+{
+ struct i2c_client *client = qt2160->client;
+ struct input_dev *input = qt2160->input;
+ u8 regs[6];
+ u16 old_matrix, new_matrix;
+ int ret, i, mask;
+
+ dev_dbg(&client->dev, "requesting keys...\n");
+
+ /*
+ * Read all registers from General Status Register
+ * to GPIOs register
+ */
+ ret = qt2160_read_block(client, QT2160_CMD_GSTAT, regs, 6);
+ if (ret) {
+ dev_err(&client->dev,
+ "could not perform chip read.\n");
+ return ret;
+ }
+
+ old_matrix = qt2160->key_matrix;
+ qt2160->key_matrix = new_matrix = (regs[2] << 8) | regs[1];
+
+ mask = 0x01;
+ for (i = 0; i < 16; ++i, mask <<= 1) {
+ int keyval = new_matrix & mask;
+
+ if ((old_matrix & mask) != keyval) {
+ input_report_key(input, qt2160->keycodes[i], keyval);
+ dev_dbg(&client->dev, "key %d %s\n",
+ i, keyval ? "pressed" : "released");
+ }
+ }
+
+ input_sync(input);
+
+ return 0;
+}
+
+static irqreturn_t qt2160_irq(int irq, void *_qt2160)
+{
+ struct qt2160_data *qt2160 = _qt2160;
+ unsigned long flags;
+
+ spin_lock_irqsave(&qt2160->lock, flags);
+
+ __cancel_delayed_work(&qt2160->dwork);
+ schedule_delayed_work(&qt2160->dwork, 0);
+
+ spin_unlock_irqrestore(&qt2160->lock, flags);
+
+ return IRQ_HANDLED;
+}
+
+static void qt2160_schedule_read(struct qt2160_data *qt2160)
+{
+ spin_lock_irq(&qt2160->lock);
+ schedule_delayed_work(&qt2160->dwork, QT2160_CYCLE_INTERVAL);
+ spin_unlock_irq(&qt2160->lock);
+}
+
+static void qt2160_worker(struct work_struct *work)
+{
+ struct qt2160_data *qt2160 =
+ container_of(work, struct qt2160_data, dwork.work);
+
+ dev_dbg(&qt2160->client->dev, "worker\n");
+
+ qt2160_get_key_matrix(qt2160);
+
+ /* Avoid device lock up by checking every so often */
+ qt2160_schedule_read(qt2160);
+}
+
+static int __devinit qt2160_read(struct i2c_client *client, u8 reg)
+{
+ int ret;
+
+ ret = i2c_smbus_write_byte(client, reg);
+ if (ret) {
+ dev_err(&client->dev,
+ "couldn't send request. Returned %d\n", ret);
+ return ret;
+ }
+
+ ret = i2c_smbus_read_byte(client);
+ if (ret < 0) {
+ dev_err(&client->dev,
+ "couldn't read register. Returned %d\n", ret);
+ return ret;
+ }
+
+ return ret;
+}
+
+static int __devinit qt2160_write(struct i2c_client *client, u8 reg, u8 data)
+{
+ int error;
+
+ error = i2c_smbus_write_byte(client, reg);
+ if (error) {
+ dev_err(&client->dev,
+ "couldn't send request. Returned %d\n", error);
+ return error;
+ }
+
+ error = i2c_smbus_write_byte(client, data);
+ if (error) {
+ dev_err(&client->dev,
+ "couldn't write data. Returned %d\n", error);
+ return error;
+ }
+
+ return error;
+}
+
+
+static bool __devinit qt2160_identify(struct i2c_client *client)
+{
+ int id, ver, rev;
+
+ /* Read Chid ID to check if chip is valid */
+ id = qt2160_read(client, QT2160_CMD_CHIPID);
+ if (id != QT2160_VALID_CHIPID) {
+ dev_err(&client->dev, "ID %d not supported\n", id);
+ return false;
+ }
+
+ /* Read chip firmware version */
+ ver = qt2160_read(client, QT2160_CMD_CODEVER);
+ if (ver < 0) {
+ dev_err(&client->dev, "could not get firmware version\n");
+ return false;
+ }
+
+ /* Read chip firmware revision */
+ rev = qt2160_read(client, QT2160_CMD_SUBVER);
+ if (rev < 0) {
+ dev_err(&client->dev, "could not get firmware revision\n");
+ return false;
+ }
+
+ dev_info(&client->dev, "AT42QT2160 firmware version %d.%d.%d\n",
+ ver >> 4, ver & 0xf, rev);
+
+ return true;
+}
+
+static int __devinit qt2160_probe(struct i2c_client *client,
+ const struct i2c_device_id *id)
+{
+ struct qt2160_data *qt2160;
+ struct input_dev *input;
+ int i;
+ int error;
+
+ /* Check functionality */
+ error = i2c_check_functionality(client->adapter,
+ I2C_FUNC_SMBUS_BYTE);
+ if (!error) {
+ dev_err(&client->dev, "%s adapter not supported\n",
+ dev_driver_string(&client->adapter->dev));
+ return -ENODEV;
+ }
+
+ if (!qt2160_identify(client))
+ return -ENODEV;
+
+ /* Chip is valid and active. Allocate structure */
+ qt2160 = kzalloc(sizeof(struct qt2160_data), GFP_KERNEL);
+ input = input_allocate_device();
+ if (!qt2160 || !input) {
+ dev_err(&client->dev, "insufficient memory\n");
+ error = -ENOMEM;
+ goto err_free_mem;
+ }
+
+ qt2160->client = client;
+ qt2160->input = input;
+ INIT_DELAYED_WORK(&qt2160->dwork, qt2160_worker);
+ spin_lock_init(&qt2160->lock);
+
+ input->name = "AT42QT2160 Touch Sense Keyboard";
+ input->id.bustype = BUS_I2C;
+
+ input->keycode = qt2160->keycodes;
+ input->keycodesize = sizeof(qt2160->keycodes[0]);
+ input->keycodemax = ARRAY_SIZE(qt2160_key2code);
+
+ __set_bit(EV_KEY, input->evbit);
+ __clear_bit(EV_REP, input->evbit);
+ for (i = 0; i < ARRAY_SIZE(qt2160_key2code); i++) {
+ qt2160->keycodes[i] = qt2160_key2code[i];
+ __set_bit(qt2160_key2code[i], input->keybit);
+ }
+ __clear_bit(KEY_RESERVED, input->keybit);
+
+ /* Calibrate device */
+ error = qt2160_write(client, QT2160_CMD_CALIBRATE, 1);
+ if (error) {
+ dev_err(&client->dev, "failed to calibrate device\n");
+ goto err_free_mem;
+ }
+
+ if (client->irq) {
+ error = request_irq(client->irq, qt2160_irq,
+ IRQF_TRIGGER_FALLING, "qt2160", qt2160);
+ if (error) {
+ dev_err(&client->dev,
+ "failed to allocate irq %d\n", client->irq);
+ goto err_free_mem;
+ }
+ }
+
+ error = input_register_device(qt2160->input);
+ if (error) {
+ dev_err(&client->dev,
+ "Failed to register input device\n");
+ goto err_free_irq;
+ }
+
+ i2c_set_clientdata(client, qt2160);
+ qt2160_schedule_read(qt2160);
+
+ return 0;
+
+err_free_irq:
+ if (client->irq)
+ free_irq(client->irq, qt2160);
+err_free_mem:
+ input_free_device(input);
+ kfree(qt2160);
+ return error;
+}
+
+static int __devexit qt2160_remove(struct i2c_client *client)
+{
+ struct qt2160_data *qt2160 = i2c_get_clientdata(client);
+
+ /* Release IRQ so no queue will be scheduled */
+ if (client->irq)
+ free_irq(client->irq, qt2160);
+
+ cancel_delayed_work_sync(&qt2160->dwork);
+
+ input_unregister_device(qt2160->input);
+ kfree(qt2160);
+
+ i2c_set_clientdata(client, NULL);
+ return 0;
+}
+
+static struct i2c_device_id qt2160_idtable[] = {
+ { "qt2160", 0, },
+ { }
+};
+
+MODULE_DEVICE_TABLE(i2c, qt2160_idtable);
+
+static struct i2c_driver qt2160_driver = {
+ .driver = {
+ .name = "qt2160",
+ .owner = THIS_MODULE,
+ },
+
+ .id_table = qt2160_idtable,
+ .probe = qt2160_probe,
+ .remove = __devexit_p(qt2160_remove),
+};
+
+static int __init qt2160_init(void)
+{
+ return i2c_add_driver(&qt2160_driver);
+}
+module_init(qt2160_init);
+
+static void __exit qt2160_cleanup(void)
+{
+ i2c_del_driver(&qt2160_driver);
+}
+module_exit(qt2160_cleanup);
+
+MODULE_AUTHOR("Raphael Derosso Pereira <raphaelpereira@gmail.com>");
+MODULE_DESCRIPTION("Driver for AT42QT2160 Touch Sensor");
+MODULE_LICENSE("GPL");
diff --git a/drivers/input/misc/dm355evm_keys.c b/drivers/input/misc/dm355evm_keys.c
index 0918acae584..f2b67dc81d8 100644
--- a/drivers/input/misc/dm355evm_keys.c
+++ b/drivers/input/misc/dm355evm_keys.c
@@ -96,7 +96,13 @@ static struct {
{ 0x3169, KEY_PAUSE, },
};
-/* runs in an IRQ thread -- can (and will!) sleep */
+/*
+ * Because we communicate with the MSP430 using I2C, and all I2C calls
+ * in Linux sleep, we use a threaded IRQ handler. The IRQ itself is
+ * active low, but we go through the GPIO controller so we can trigger
+ * on falling edges and not worry about enabling/disabling the IRQ in
+ * the keypress handling path.
+ */
static irqreturn_t dm355evm_keys_irq(int irq, void *_keys)
{
struct dm355evm_keys *keys = _keys;
@@ -171,18 +177,6 @@ static irqreturn_t dm355evm_keys_irq(int irq, void *_keys)
return IRQ_HANDLED;
}
-/*
- * Because we communicate with the MSP430 using I2C, and all I2C calls
- * in Linux sleep, we use a threaded IRQ handler. The IRQ itself is
- * active low, but we go through the GPIO controller so we can trigger
- * on falling edges and not worry about enabling/disabling the IRQ in
- * the keypress handling path.
- */
-static irqreturn_t dm355evm_keys_hardirq(int irq, void *_keys)
-{
- return IRQ_WAKE_THREAD;
-}
-
static int dm355evm_setkeycode(struct input_dev *dev, int index, int keycode)
{
u16 old_keycode;
@@ -257,10 +251,8 @@ static int __devinit dm355evm_keys_probe(struct platform_device *pdev)
/* REVISIT: flush the event queue? */
- status = request_threaded_irq(keys->irq,
- dm355evm_keys_hardirq, dm355evm_keys_irq,
- IRQF_TRIGGER_FALLING,
- dev_name(&pdev->dev), keys);
+ status = request_threaded_irq(keys->irq, NULL, dm355evm_keys_irq,
+ IRQF_TRIGGER_FALLING, dev_name(&pdev->dev), keys);
if (status < 0)
goto fail1;
diff --git a/drivers/input/mouse/sentelic.c b/drivers/input/mouse/sentelic.c
index 84e2fc04d11..f84cbd97c88 100644
--- a/drivers/input/mouse/sentelic.c
+++ b/drivers/input/mouse/sentelic.c
@@ -92,7 +92,8 @@ static int fsp_reg_read(struct psmouse *psmouse, int reg_addr, int *reg_val)
*/
ps2_command(ps2dev, NULL, PSMOUSE_CMD_DISABLE);
psmouse_set_state(psmouse, PSMOUSE_CMD_MODE);
- mutex_lock(&ps2dev->cmd_mutex);
+
+ ps2_begin_command(ps2dev);
if (ps2_sendbyte(ps2dev, 0xf3, FSP_CMD_TIMEOUT) < 0)
goto out;
@@ -126,7 +127,7 @@ static int fsp_reg_read(struct psmouse *psmouse, int reg_addr, int *reg_val)
rc = 0;
out:
- mutex_unlock(&ps2dev->cmd_mutex);
+ ps2_end_command(ps2dev);
ps2_command(ps2dev, NULL, PSMOUSE_CMD_ENABLE);
psmouse_set_state(psmouse, PSMOUSE_ACTIVATED);
dev_dbg(&ps2dev->serio->dev, "READ REG: 0x%02x is 0x%02x (rc = %d)\n",
@@ -140,7 +141,7 @@ static int fsp_reg_write(struct psmouse *psmouse, int reg_addr, int reg_val)
unsigned char v;
int rc = -1;
- mutex_lock(&ps2dev->cmd_mutex);
+ ps2_begin_command(ps2dev);
if (ps2_sendbyte(ps2dev, 0xf3, FSP_CMD_TIMEOUT) < 0)
goto out;
@@ -179,7 +180,7 @@ static int fsp_reg_write(struct psmouse *psmouse, int reg_addr, int reg_val)
rc = 0;
out:
- mutex_unlock(&ps2dev->cmd_mutex);
+ ps2_end_command(ps2dev);
dev_dbg(&ps2dev->serio->dev, "WRITE REG: 0x%02x to 0x%02x (rc = %d)\n",
reg_addr, reg_val, rc);
return rc;
@@ -214,7 +215,8 @@ static int fsp_page_reg_read(struct psmouse *psmouse, int *reg_val)
ps2_command(ps2dev, NULL, PSMOUSE_CMD_DISABLE);
psmouse_set_state(psmouse, PSMOUSE_CMD_MODE);
- mutex_lock(&ps2dev->cmd_mutex);
+
+ ps2_begin_command(ps2dev);
if (ps2_sendbyte(ps2dev, 0xf3, FSP_CMD_TIMEOUT) < 0)
goto out;
@@ -236,7 +238,7 @@ static int fsp_page_reg_read(struct psmouse *psmouse, int *reg_val)
rc = 0;
out:
- mutex_unlock(&ps2dev->cmd_mutex);
+ ps2_end_command(ps2dev);
ps2_command(ps2dev, NULL, PSMOUSE_CMD_ENABLE);
psmouse_set_state(psmouse, PSMOUSE_ACTIVATED);
dev_dbg(&ps2dev->serio->dev, "READ PAGE REG: 0x%02x (rc = %d)\n",
@@ -250,7 +252,7 @@ static int fsp_page_reg_write(struct psmouse *psmouse, int reg_val)
unsigned char v;
int rc = -1;
- mutex_lock(&ps2dev->cmd_mutex);
+ ps2_begin_command(ps2dev);
if (ps2_sendbyte(ps2dev, 0xf3, FSP_CMD_TIMEOUT) < 0)
goto out;
@@ -275,7 +277,7 @@ static int fsp_page_reg_write(struct psmouse *psmouse, int reg_val)
rc = 0;
out:
- mutex_unlock(&ps2dev->cmd_mutex);
+ ps2_end_command(ps2dev);
dev_dbg(&ps2dev->serio->dev, "WRITE PAGE REG: to 0x%02x (rc = %d)\n",
reg_val, rc);
return rc;
diff --git a/drivers/input/mouse/synaptics_i2c.c b/drivers/input/mouse/synaptics_i2c.c
index eac9fdde7ee..7283c78044a 100644
--- a/drivers/input/mouse/synaptics_i2c.c
+++ b/drivers/input/mouse/synaptics_i2c.c
@@ -203,7 +203,7 @@ MODULE_PARM_DESC(no_filter, "No Filter. Default = 0 (off)");
* and the irq configuration should be set to Falling Edge Trigger
*/
/* Control IRQ / Polling option */
-static int polling_req;
+static bool polling_req;
module_param(polling_req, bool, 0444);
MODULE_PARM_DESC(polling_req, "Request Polling. Default = 0 (use irq)");
@@ -217,6 +217,7 @@ struct synaptics_i2c {
struct i2c_client *client;
struct input_dev *input;
struct delayed_work dwork;
+ spinlock_t lock;
int no_data_count;
int no_decel_param;
int reduce_report_param;
@@ -366,17 +367,28 @@ static bool synaptics_i2c_get_input(struct synaptics_i2c *touch)
return xy_delta || gesture;
}
-static irqreturn_t synaptics_i2c_irq(int irq, void *dev_id)
+static void synaptics_i2c_reschedule_work(struct synaptics_i2c *touch,
+ unsigned long delay)
{
- struct synaptics_i2c *touch = dev_id;
+ unsigned long flags;
+
+ spin_lock_irqsave(&touch->lock, flags);
/*
- * We want to have the work run immediately but it might have
- * already been scheduled with a delay, that's why we have to
- * cancel it first.
+ * If work is already scheduled then subsequent schedules will not
+ * change the scheduled time that's why we have to cancel it first.
*/
- cancel_delayed_work(&touch->dwork);
- schedule_delayed_work(&touch->dwork, 0);
+ __cancel_delayed_work(&touch->dwork);
+ schedule_delayed_work(&touch->dwork, delay);
+
+ spin_unlock_irqrestore(&touch->lock, flags);
+}
+
+static irqreturn_t synaptics_i2c_irq(int irq, void *dev_id)
+{
+ struct synaptics_i2c *touch = dev_id;
+
+ synaptics_i2c_reschedule_work(touch, 0);
return IRQ_HANDLED;
}
@@ -452,7 +464,7 @@ static void synaptics_i2c_work_handler(struct work_struct *work)
* We poll the device once in THREAD_IRQ_SLEEP_SECS and
* if error is detected, we try to reset and reconfigure the touchpad.
*/
- schedule_delayed_work(&touch->dwork, delay);
+ synaptics_i2c_reschedule_work(touch, delay);
}
static int synaptics_i2c_open(struct input_dev *input)
@@ -465,8 +477,8 @@ static int synaptics_i2c_open(struct input_dev *input)
return ret;
if (polling_req)
- schedule_delayed_work(&touch->dwork,
- msecs_to_jiffies(NO_DATA_SLEEP_MSECS));
+ synaptics_i2c_reschedule_work(touch,
+ msecs_to_jiffies(NO_DATA_SLEEP_MSECS));
return 0;
}
@@ -521,6 +533,7 @@ struct synaptics_i2c *synaptics_i2c_touch_create(struct i2c_client *client)
touch->scan_rate_param = scan_rate;
set_scan_rate(touch, scan_rate);
INIT_DELAYED_WORK(&touch->dwork, synaptics_i2c_work_handler);
+ spin_lock_init(&touch->lock);
return touch;
}
@@ -535,14 +548,12 @@ static int __devinit synaptics_i2c_probe(struct i2c_client *client,
if (!touch)
return -ENOMEM;
- i2c_set_clientdata(client, touch);
-
ret = synaptics_i2c_reset_config(client);
if (ret)
goto err_mem_free;
if (client->irq < 1)
- polling_req = 1;
+ polling_req = true;
touch->input = input_allocate_device();
if (!touch->input) {
@@ -563,7 +574,7 @@ static int __devinit synaptics_i2c_probe(struct i2c_client *client,
dev_warn(&touch->client->dev,
"IRQ request failed: %d, "
"falling back to polling\n", ret);
- polling_req = 1;
+ polling_req = true;
synaptics_i2c_reg_set(touch->client,
INTERRUPT_EN_REG, 0);
}
@@ -580,12 +591,14 @@ static int __devinit synaptics_i2c_probe(struct i2c_client *client,
"Input device register failed: %d\n", ret);
goto err_input_free;
}
+
+ i2c_set_clientdata(client, touch);
+
return 0;
err_input_free:
input_free_device(touch->input);
err_mem_free:
- i2c_set_clientdata(client, NULL);
kfree(touch);
return ret;
@@ -596,7 +609,7 @@ static int __devexit synaptics_i2c_remove(struct i2c_client *client)
struct synaptics_i2c *touch = i2c_get_clientdata(client);
if (!polling_req)
- free_irq(touch->client->irq, touch);
+ free_irq(client->irq, touch);
input_unregister_device(touch->input);
i2c_set_clientdata(client, NULL);
@@ -627,8 +640,8 @@ static int synaptics_i2c_resume(struct i2c_client *client)
if (ret)
return ret;
- schedule_delayed_work(&touch->dwork,
- msecs_to_jiffies(NO_DATA_SLEEP_MSECS));
+ synaptics_i2c_reschedule_work(touch,
+ msecs_to_jiffies(NO_DATA_SLEEP_MSECS));
return 0;
}
diff --git a/drivers/input/serio/i8042.c b/drivers/input/serio/i8042.c
index eb3ff94af58..bc56e52b945 100644
--- a/drivers/input/serio/i8042.c
+++ b/drivers/input/serio/i8042.c
@@ -87,8 +87,22 @@ static bool i8042_bypass_aux_irq_test;
#include "i8042.h"
+/*
+ * i8042_lock protects serialization between i8042_command and
+ * the interrupt handler.
+ */
static DEFINE_SPINLOCK(i8042_lock);
+/*
+ * Writers to AUX and KBD ports as well as users issuing i8042_command
+ * directly should acquire i8042_mutex (by means of calling
+ * i8042_lock_chip() and i8042_unlock_ship() helpers) to ensure that
+ * they do not disturb each other (unfortunately in many i8042
+ * implementations write to one of the ports will immediately abort
+ * command that is being processed by another port).
+ */
+static DEFINE_MUTEX(i8042_mutex);
+
struct i8042_port {
struct serio *serio;
int irq;
@@ -113,6 +127,18 @@ static struct platform_device *i8042_platform_device;
static irqreturn_t i8042_interrupt(int irq, void *dev_id);
+void i8042_lock_chip(void)
+{
+ mutex_lock(&i8042_mutex);
+}
+EXPORT_SYMBOL(i8042_lock_chip);
+
+void i8042_unlock_chip(void)
+{
+ mutex_unlock(&i8042_mutex);
+}
+EXPORT_SYMBOL(i8042_unlock_chip);
+
/*
* The i8042_wait_read() and i8042_wait_write functions wait for the i8042 to
* be ready for reading values from it / writing values to it.
@@ -1161,6 +1187,21 @@ static void __devexit i8042_unregister_ports(void)
}
}
+/*
+ * Checks whether port belongs to i8042 controller.
+ */
+bool i8042_check_port_owner(const struct serio *port)
+{
+ int i;
+
+ for (i = 0; i < I8042_NUM_PORTS; i++)
+ if (i8042_ports[i].serio == port)
+ return true;
+
+ return false;
+}
+EXPORT_SYMBOL(i8042_check_port_owner);
+
static void i8042_free_irqs(void)
{
if (i8042_aux_irq_registered)
diff --git a/drivers/input/serio/libps2.c b/drivers/input/serio/libps2.c
index 3a95b508bf2..769ba65a585 100644
--- a/drivers/input/serio/libps2.c
+++ b/drivers/input/serio/libps2.c
@@ -17,6 +17,7 @@
#include <linux/interrupt.h>
#include <linux/input.h>
#include <linux/serio.h>
+#include <linux/i8042.h>
#include <linux/init.h>
#include <linux/libps2.h>
@@ -54,6 +55,24 @@ int ps2_sendbyte(struct ps2dev *ps2dev, unsigned char byte, int timeout)
}
EXPORT_SYMBOL(ps2_sendbyte);
+void ps2_begin_command(struct ps2dev *ps2dev)
+{
+ mutex_lock(&ps2dev->cmd_mutex);
+
+ if (i8042_check_port_owner(ps2dev->serio))
+ i8042_lock_chip();
+}
+EXPORT_SYMBOL(ps2_begin_command);
+
+void ps2_end_command(struct ps2dev *ps2dev)
+{
+ if (i8042_check_port_owner(ps2dev->serio))
+ i8042_unlock_chip();
+
+ mutex_unlock(&ps2dev->cmd_mutex);
+}
+EXPORT_SYMBOL(ps2_end_command);
+
/*
* ps2_drain() waits for device to transmit requested number of bytes
* and discards them.
@@ -66,7 +85,7 @@ void ps2_drain(struct ps2dev *ps2dev, int maxbytes, int timeout)
maxbytes = sizeof(ps2dev->cmdbuf);
}
- mutex_lock(&ps2dev->cmd_mutex);
+ ps2_begin_command(ps2dev);
serio_pause_rx(ps2dev->serio);
ps2dev->flags = PS2_FLAG_CMD;
@@ -76,7 +95,8 @@ void ps2_drain(struct ps2dev *ps2dev, int maxbytes, int timeout)
wait_event_timeout(ps2dev->wait,
!(ps2dev->flags & PS2_FLAG_CMD),
msecs_to_jiffies(timeout));
- mutex_unlock(&ps2dev->cmd_mutex);
+
+ ps2_end_command(ps2dev);
}
EXPORT_SYMBOL(ps2_drain);
@@ -237,9 +257,9 @@ int ps2_command(struct ps2dev *ps2dev, unsigned char *param, int command)
{
int rc;
- mutex_lock(&ps2dev->cmd_mutex);
+ ps2_begin_command(ps2dev);
rc = __ps2_command(ps2dev, param, command);
- mutex_unlock(&ps2dev->cmd_mutex);
+ ps2_end_command(ps2dev);
return rc;
}
diff --git a/drivers/input/touchscreen/Kconfig b/drivers/input/touchscreen/Kconfig
index ab02d72afbf..8cc453c85ea 100644
--- a/drivers/input/touchscreen/Kconfig
+++ b/drivers/input/touchscreen/Kconfig
@@ -48,8 +48,8 @@ config TOUCHSCREEN_AD7879_I2C
select TOUCHSCREEN_AD7879
help
Say Y here if you have a touchscreen interface using the
- AD7879-1 controller, and your board-specific initialization
- code includes that in its table of I2C devices.
+ AD7879-1/AD7889-1 controller, and your board-specific
+ initialization code includes that in its table of I2C devices.
If unsure, say N (but it's safe to say "Y").
@@ -62,7 +62,7 @@ config TOUCHSCREEN_AD7879_SPI
select TOUCHSCREEN_AD7879
help
Say Y here if you have a touchscreen interface using the
- AD7879 controller, and your board-specific initialization
+ AD7879/AD7889 controller, and your board-specific initialization
code includes that in its table of SPI devices.
If unsure, say N (but it's safe to say "Y").
@@ -169,6 +169,17 @@ config TOUCHSCREEN_WACOM_W8001
To compile this driver as a module, choose M here: the
module will be called wacom_w8001.
+config TOUCHSCREEN_MCS5000
+ tristate "MELFAS MCS-5000 touchscreen"
+ depends on I2C
+ help
+ Say Y here if you have the MELFAS MCS-5000 touchscreen controller
+ chip in your system.
+
+ If unsure, say N.
+
+ To compile this driver as a module, choose M here: the
+ module will be called mcs5000_ts.
config TOUCHSCREEN_MTOUCH
tristate "MicroTouch serial touchscreens"
diff --git a/drivers/input/touchscreen/Makefile b/drivers/input/touchscreen/Makefile
index 4599bf7ad81..15fa62cffc7 100644
--- a/drivers/input/touchscreen/Makefile
+++ b/drivers/input/touchscreen/Makefile
@@ -17,6 +17,7 @@ obj-$(CONFIG_TOUCHSCREEN_EETI) += eeti_ts.o
obj-$(CONFIG_TOUCHSCREEN_ELO) += elo.o
obj-$(CONFIG_TOUCHSCREEN_FUJITSU) += fujitsu_ts.o
obj-$(CONFIG_TOUCHSCREEN_INEXIO) += inexio.o
+obj-$(CONFIG_TOUCHSCREEN_MCS5000) += mcs5000_ts.o
obj-$(CONFIG_TOUCHSCREEN_MIGOR) += migor_ts.o
obj-$(CONFIG_TOUCHSCREEN_MTOUCH) += mtouch.o
obj-$(CONFIG_TOUCHSCREEN_MK712) += mk712.o
diff --git a/drivers/input/touchscreen/ad7879.c b/drivers/input/touchscreen/ad7879.c
index 19b4db7e974..f06332c9e21 100644
--- a/drivers/input/touchscreen/ad7879.c
+++ b/drivers/input/touchscreen/ad7879.c
@@ -1,7 +1,8 @@
/*
- * Copyright (C) 2008 Michael Hennerich, Analog Devices Inc.
+ * Copyright (C) 2008-2009 Michael Hennerich, Analog Devices Inc.
*
- * Description: AD7879 based touchscreen, and GPIO driver (I2C/SPI Interface)
+ * Description: AD7879/AD7889 based touchscreen, and GPIO driver
+ * (I2C/SPI Interface)
*
* Bugs: Enter bugs at http://blackfin.uclinux.org/
*
@@ -747,6 +748,7 @@ static int __devexit ad7879_remove(struct i2c_client *client)
static const struct i2c_device_id ad7879_id[] = {
{ "ad7879", 0 },
+ { "ad7889", 0 },
{ }
};
MODULE_DEVICE_TABLE(i2c, ad7879_id);
diff --git a/drivers/input/touchscreen/mcs5000_ts.c b/drivers/input/touchscreen/mcs5000_ts.c
new file mode 100644
index 00000000000..4c28b89757f
--- /dev/null
+++ b/drivers/input/touchscreen/mcs5000_ts.c
@@ -0,0 +1,318 @@
+/*
+ * mcs5000_ts.c - Touchscreen driver for MELFAS MCS-5000 controller
+ *
+ * Copyright (C) 2009 Samsung Electronics Co.Ltd
+ * Author: Joonyoung Shim <jy0922.shim@samsung.com>
+ *
+ * Based on wm97xx-core.c
+ *
+ * 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/i2c.h>
+#include <linux/i2c/mcs5000_ts.h>
+#include <linux/interrupt.h>
+#include <linux/input.h>
+#include <linux/irq.h>
+
+/* Registers */
+#define MCS5000_TS_STATUS 0x00
+#define STATUS_OFFSET 0
+#define STATUS_NO (0 << STATUS_OFFSET)
+#define STATUS_INIT (1 << STATUS_OFFSET)
+#define STATUS_SENSING (2 << STATUS_OFFSET)
+#define STATUS_COORD (3 << STATUS_OFFSET)
+#define STATUS_GESTURE (4 << STATUS_OFFSET)
+#define ERROR_OFFSET 4
+#define ERROR_NO (0 << ERROR_OFFSET)
+#define ERROR_POWER_ON_RESET (1 << ERROR_OFFSET)
+#define ERROR_INT_RESET (2 << ERROR_OFFSET)
+#define ERROR_EXT_RESET (3 << ERROR_OFFSET)
+#define ERROR_INVALID_REG_ADDRESS (8 << ERROR_OFFSET)
+#define ERROR_INVALID_REG_VALUE (9 << ERROR_OFFSET)
+
+#define MCS5000_TS_OP_MODE 0x01
+#define RESET_OFFSET 0
+#define RESET_NO (0 << RESET_OFFSET)
+#define RESET_EXT_SOFT (1 << RESET_OFFSET)
+#define OP_MODE_OFFSET 1
+#define OP_MODE_SLEEP (0 << OP_MODE_OFFSET)
+#define OP_MODE_ACTIVE (1 << OP_MODE_OFFSET)
+#define GESTURE_OFFSET 4
+#define GESTURE_DISABLE (0 << GESTURE_OFFSET)
+#define GESTURE_ENABLE (1 << GESTURE_OFFSET)
+#define PROXIMITY_OFFSET 5
+#define PROXIMITY_DISABLE (0 << PROXIMITY_OFFSET)
+#define PROXIMITY_ENABLE (1 << PROXIMITY_OFFSET)
+#define SCAN_MODE_OFFSET 6
+#define SCAN_MODE_INTERRUPT (0 << SCAN_MODE_OFFSET)
+#define SCAN_MODE_POLLING (1 << SCAN_MODE_OFFSET)
+#define REPORT_RATE_OFFSET 7
+#define REPORT_RATE_40 (0 << REPORT_RATE_OFFSET)
+#define REPORT_RATE_80 (1 << REPORT_RATE_OFFSET)
+
+#define MCS5000_TS_SENS_CTL 0x02
+#define MCS5000_TS_FILTER_CTL 0x03
+#define PRI_FILTER_OFFSET 0
+#define SEC_FILTER_OFFSET 4
+
+#define MCS5000_TS_X_SIZE_UPPER 0x08
+#define MCS5000_TS_X_SIZE_LOWER 0x09
+#define MCS5000_TS_Y_SIZE_UPPER 0x0A
+#define MCS5000_TS_Y_SIZE_LOWER 0x0B
+
+#define MCS5000_TS_INPUT_INFO 0x10
+#define INPUT_TYPE_OFFSET 0
+#define INPUT_TYPE_NONTOUCH (0 << INPUT_TYPE_OFFSET)
+#define INPUT_TYPE_SINGLE (1 << INPUT_TYPE_OFFSET)
+#define INPUT_TYPE_DUAL (2 << INPUT_TYPE_OFFSET)
+#define INPUT_TYPE_PALM (3 << INPUT_TYPE_OFFSET)
+#define INPUT_TYPE_PROXIMITY (7 << INPUT_TYPE_OFFSET)
+#define GESTURE_CODE_OFFSET 3
+#define GESTURE_CODE_NO (0 << GESTURE_CODE_OFFSET)
+
+#define MCS5000_TS_X_POS_UPPER 0x11
+#define MCS5000_TS_X_POS_LOWER 0x12
+#define MCS5000_TS_Y_POS_UPPER 0x13
+#define MCS5000_TS_Y_POS_LOWER 0x14
+#define MCS5000_TS_Z_POS 0x15
+#define MCS5000_TS_WIDTH 0x16
+#define MCS5000_TS_GESTURE_VAL 0x17
+#define MCS5000_TS_MODULE_REV 0x20
+#define MCS5000_TS_FIRMWARE_VER 0x21
+
+/* Touchscreen absolute values */
+#define MCS5000_MAX_XC 0x3ff
+#define MCS5000_MAX_YC 0x3ff
+
+enum mcs5000_ts_read_offset {
+ READ_INPUT_INFO,
+ READ_X_POS_UPPER,
+ READ_X_POS_LOWER,
+ READ_Y_POS_UPPER,
+ READ_Y_POS_LOWER,
+ READ_BLOCK_SIZE,
+};
+
+/* Each client has this additional data */
+struct mcs5000_ts_data {
+ struct i2c_client *client;
+ struct input_dev *input_dev;
+ const struct mcs5000_ts_platform_data *platform_data;
+};
+
+static irqreturn_t mcs5000_ts_interrupt(int irq, void *dev_id)
+{
+ struct mcs5000_ts_data *data = dev_id;
+ struct i2c_client *client = data->client;
+ u8 buffer[READ_BLOCK_SIZE];
+ int err;
+ int x;
+ int y;
+
+ err = i2c_smbus_read_i2c_block_data(client, MCS5000_TS_INPUT_INFO,
+ READ_BLOCK_SIZE, buffer);
+ if (err < 0) {
+ dev_err(&client->dev, "%s, err[%d]\n", __func__, err);
+ goto out;
+ }
+
+ switch (buffer[READ_INPUT_INFO]) {
+ case INPUT_TYPE_NONTOUCH:
+ input_report_key(data->input_dev, BTN_TOUCH, 0);
+ input_sync(data->input_dev);
+ break;
+
+ case INPUT_TYPE_SINGLE:
+ x = (buffer[READ_X_POS_UPPER] << 8) | buffer[READ_X_POS_LOWER];
+ y = (buffer[READ_Y_POS_UPPER] << 8) | buffer[READ_Y_POS_LOWER];
+
+ input_report_key(data->input_dev, BTN_TOUCH, 1);
+ input_report_abs(data->input_dev, ABS_X, x);
+ input_report_abs(data->input_dev, ABS_Y, y);
+ input_sync(data->input_dev);
+ break;
+
+ case INPUT_TYPE_DUAL:
+ /* TODO */
+ break;
+
+ case INPUT_TYPE_PALM:
+ /* TODO */
+ break;
+
+ case INPUT_TYPE_PROXIMITY:
+ /* TODO */
+ break;
+
+ default:
+ dev_err(&client->dev, "Unknown ts input type %d\n",
+ buffer[READ_INPUT_INFO]);
+ break;
+ }
+
+ out:
+ return IRQ_HANDLED;
+}
+
+static void mcs5000_ts_phys_init(struct mcs5000_ts_data *data)
+{
+ const struct mcs5000_ts_platform_data *platform_data =
+ data->platform_data;
+ struct i2c_client *client = data->client;
+
+ /* Touch reset & sleep mode */
+ i2c_smbus_write_byte_data(client, MCS5000_TS_OP_MODE,
+ RESET_EXT_SOFT | OP_MODE_SLEEP);
+
+ /* Touch size */
+ i2c_smbus_write_byte_data(client, MCS5000_TS_X_SIZE_UPPER,
+ platform_data->x_size >> 8);
+ i2c_smbus_write_byte_data(client, MCS5000_TS_X_SIZE_LOWER,
+ platform_data->x_size & 0xff);
+ i2c_smbus_write_byte_data(client, MCS5000_TS_Y_SIZE_UPPER,
+ platform_data->y_size >> 8);
+ i2c_smbus_write_byte_data(client, MCS5000_TS_Y_SIZE_LOWER,
+ platform_data->y_size & 0xff);
+
+ /* Touch active mode & 80 report rate */
+ i2c_smbus_write_byte_data(data->client, MCS5000_TS_OP_MODE,
+ OP_MODE_ACTIVE | REPORT_RATE_80);
+}
+
+static int __devinit mcs5000_ts_probe(struct i2c_client *client,
+ const struct i2c_device_id *id)
+{
+ struct mcs5000_ts_data *data;
+ struct input_dev *input_dev;
+ int ret;
+
+ if (!client->dev.platform_data)
+ return -EINVAL;
+
+ data = kzalloc(sizeof(struct mcs5000_ts_data), GFP_KERNEL);
+ input_dev = input_allocate_device();
+ if (!data || !input_dev) {
+ dev_err(&client->dev, "Failed to allocate memory\n");
+ ret = -ENOMEM;
+ goto err_free_mem;
+ }
+
+ data->client = client;
+ data->input_dev = input_dev;
+ data->platform_data = client->dev.platform_data;
+
+ input_dev->name = "MELPAS MCS-5000 Touchscreen";
+ input_dev->id.bustype = BUS_I2C;
+ input_dev->dev.parent = &client->dev;
+
+ __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, MCS5000_MAX_XC, 0, 0);
+ input_set_abs_params(input_dev, ABS_Y, 0, MCS5000_MAX_YC, 0, 0);
+
+ input_set_drvdata(input_dev, data);
+
+ if (data->platform_data->cfg_pin)
+ data->platform_data->cfg_pin();
+
+ ret = request_threaded_irq(client->irq, NULL, mcs5000_ts_interrupt,
+ IRQF_TRIGGER_LOW | IRQF_ONESHOT, "mcs5000_ts", data);
+
+ if (ret < 0) {
+ dev_err(&client->dev, "Failed to register interrupt\n");
+ goto err_free_mem;
+ }
+
+ ret = input_register_device(data->input_dev);
+ if (ret < 0)
+ goto err_free_irq;
+
+ mcs5000_ts_phys_init(data);
+ i2c_set_clientdata(client, data);
+
+ return 0;
+
+err_free_irq:
+ free_irq(client->irq, data);
+err_free_mem:
+ input_free_device(input_dev);
+ kfree(data);
+ return ret;
+}
+
+static int __devexit mcs5000_ts_remove(struct i2c_client *client)
+{
+ struct mcs5000_ts_data *data = i2c_get_clientdata(client);
+
+ free_irq(client->irq, data);
+ input_unregister_device(data->input_dev);
+ kfree(data);
+ i2c_set_clientdata(client, NULL);
+
+ return 0;
+}
+
+#ifdef CONFIG_PM
+static int mcs5000_ts_suspend(struct i2c_client *client, pm_message_t mesg)
+{
+ /* Touch sleep mode */
+ i2c_smbus_write_byte_data(client, MCS5000_TS_OP_MODE, OP_MODE_SLEEP);
+
+ return 0;
+}
+
+static int mcs5000_ts_resume(struct i2c_client *client)
+{
+ struct mcs5000_ts_data *data = i2c_get_clientdata(client);
+
+ mcs5000_ts_phys_init(data);
+
+ return 0;
+}
+#else
+#define mcs5000_ts_suspend NULL
+#define mcs5000_ts_resume NULL
+#endif
+
+static const struct i2c_device_id mcs5000_ts_id[] = {
+ { "mcs5000_ts", 0 },
+ { }
+};
+MODULE_DEVICE_TABLE(i2c, mcs5000_ts_id);
+
+static struct i2c_driver mcs5000_ts_driver = {
+ .probe = mcs5000_ts_probe,
+ .remove = __devexit_p(mcs5000_ts_remove),
+ .suspend = mcs5000_ts_suspend,
+ .resume = mcs5000_ts_resume,
+ .driver = {
+ .name = "mcs5000_ts",
+ },
+ .id_table = mcs5000_ts_id,
+};
+
+static int __init mcs5000_ts_init(void)
+{
+ return i2c_add_driver(&mcs5000_ts_driver);
+}
+
+static void __exit mcs5000_ts_exit(void)
+{
+ i2c_del_driver(&mcs5000_ts_driver);
+}
+
+module_init(mcs5000_ts_init);
+module_exit(mcs5000_ts_exit);
+
+/* Module information */
+MODULE_AUTHOR("Joonyoung Shim <jy0922.shim@samsung.com>");
+MODULE_DESCRIPTION("Touchscreen driver for MELFAS MCS-5000 controller");
+MODULE_LICENSE("GPL");