summaryrefslogtreecommitdiffstats
path: root/drivers/input
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/input')
-rw-r--r--drivers/input/Makefile2
-rw-r--r--drivers/input/apm-power.c14
-rw-r--r--drivers/input/evbug.c18
-rw-r--r--drivers/input/evdev.c132
-rw-r--r--drivers/input/ff-core.c11
-rw-r--r--drivers/input/ff-memless.c31
-rw-r--r--drivers/input/gameport/gameport.c154
-rw-r--r--drivers/input/input-mt.c170
-rw-r--r--drivers/input/input-polldev.c5
-rw-r--r--drivers/input/input.c91
-rw-r--r--drivers/input/joydev.c4
-rw-r--r--drivers/input/joystick/Kconfig10
-rw-r--r--drivers/input/joystick/Makefile1
-rw-r--r--drivers/input/joystick/as5011.c367
-rw-r--r--drivers/input/joystick/iforce/Makefile15
-rw-r--r--drivers/input/joystick/turbografx.c1
-rw-r--r--drivers/input/joystick/xpad.c123
-rw-r--r--drivers/input/keyboard/Kconfig57
-rw-r--r--drivers/input/keyboard/Makefile4
-rw-r--r--drivers/input/keyboard/aaed2000_kbd.c186
-rw-r--r--drivers/input/keyboard/gpio_keys_polled.c261
-rw-r--r--drivers/input/keyboard/omap-keypad.c41
-rw-r--r--drivers/input/keyboard/spear-keyboard.c344
-rw-r--r--drivers/input/keyboard/tc3589x-keypad.c472
-rw-r--r--drivers/input/keyboard/tca6416-keypad.c13
-rw-r--r--drivers/input/misc/Kconfig42
-rw-r--r--drivers/input/misc/Makefile3
-rw-r--r--drivers/input/misc/cma3000_d0x.c398
-rw-r--r--drivers/input/misc/cma3000_d0x.h42
-rw-r--r--drivers/input/misc/cma3000_d0x_i2c.c143
-rw-r--r--drivers/input/misc/pcf8574_keypad.c19
-rw-r--r--drivers/input/misc/uinput.c8
-rw-r--r--drivers/input/misc/winbond-cir.c1608
-rw-r--r--drivers/input/mouse/bcm5974.c40
-rw-r--r--drivers/input/mouse/hgpk.c695
-rw-r--r--drivers/input/mouse/hgpk.h31
-rw-r--r--drivers/input/mouse/psmouse-base.c1
-rw-r--r--drivers/input/mouse/synaptics.c129
-rw-r--r--drivers/input/mouse/synaptics.h6
-rw-r--r--drivers/input/mousedev.c10
-rw-r--r--drivers/input/serio/Kconfig3
-rw-r--r--drivers/input/serio/ams_delta_serio.c1
-rw-r--r--drivers/input/serio/ct82c710.c8
-rw-r--r--drivers/input/serio/hil_mlc.c5
-rw-r--r--drivers/input/serio/hp_sdc_mlc.c18
-rw-r--r--drivers/input/serio/i8042-x86ia64io.h58
-rw-r--r--drivers/input/serio/i8042.c98
-rw-r--r--drivers/input/serio/i8042.h14
-rw-r--r--drivers/input/serio/ps2mult.c2
-rw-r--r--drivers/input/serio/serio.c155
-rw-r--r--drivers/input/tablet/wacom.h1
-rw-r--r--drivers/input/tablet/wacom_wac.c45
-rw-r--r--drivers/input/tablet/wacom_wac.h4
-rw-r--r--drivers/input/touchscreen/Kconfig24
-rw-r--r--drivers/input/touchscreen/Makefile1
-rw-r--r--drivers/input/touchscreen/ad7879-i2c.c17
-rw-r--r--drivers/input/touchscreen/bu21013_ts.c2
-rw-r--r--drivers/input/touchscreen/cy8ctmg110_ts.c15
-rw-r--r--drivers/input/touchscreen/eeti_ts.c16
-rw-r--r--drivers/input/touchscreen/mcs5000_ts.c17
-rw-r--r--drivers/input/touchscreen/migor_ts.c12
-rw-r--r--drivers/input/touchscreen/qt602240_ts.c21
-rw-r--r--drivers/input/touchscreen/st1232.c274
-rw-r--r--drivers/input/touchscreen/usbtouchscreen.c1
-rw-r--r--drivers/input/touchscreen/wacom_w8001.c247
-rw-r--r--drivers/input/xen-kbdfront.c10
66 files changed, 4191 insertions, 2580 deletions
diff --git a/drivers/input/Makefile b/drivers/input/Makefile
index 7ad212d31f9..09614ce7496 100644
--- a/drivers/input/Makefile
+++ b/drivers/input/Makefile
@@ -5,7 +5,7 @@
# Each configuration option enables a list of files.
obj-$(CONFIG_INPUT) += input-core.o
-input-core-objs := input.o input-compat.o ff-core.o
+input-core-y := input.o input-compat.o input-mt.o ff-core.o
obj-$(CONFIG_INPUT_FF_MEMLESS) += ff-memless.o
obj-$(CONFIG_INPUT_POLLDEV) += input-polldev.o
diff --git a/drivers/input/apm-power.c b/drivers/input/apm-power.c
index 7d61a966080..e90ee3d3061 100644
--- a/drivers/input/apm-power.c
+++ b/drivers/input/apm-power.c
@@ -9,6 +9,8 @@
*
*/
+#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
+
#include <linux/module.h>
#include <linux/input.h>
#include <linux/slab.h>
@@ -23,8 +25,7 @@ static void system_power_event(unsigned int keycode)
switch (keycode) {
case KEY_SUSPEND:
apm_queue_event(APM_USER_SUSPEND);
-
- printk(KERN_INFO "apm-power: Requesting system suspend...\n");
+ pr_info("Requesting system suspend...\n");
break;
default:
break;
@@ -65,18 +66,15 @@ static int apmpower_connect(struct input_handler *handler,
error = input_register_handle(handle);
if (error) {
- printk(KERN_ERR
- "apm-power: Failed to register input power handler, "
- "error %d\n", error);
+ pr_err("Failed to register input power handler, error %d\n",
+ error);
kfree(handle);
return error;
}
error = input_open_device(handle);
if (error) {
- printk(KERN_ERR
- "apm-power: Failed to open input power device, "
- "error %d\n", error);
+ pr_err("Failed to open input power device, error %d\n", error);
input_unregister_handle(handle);
kfree(handle);
return error;
diff --git a/drivers/input/evbug.c b/drivers/input/evbug.c
index f7c5c14ec12..cd4e6679d61 100644
--- a/drivers/input/evbug.c
+++ b/drivers/input/evbug.c
@@ -26,6 +26,8 @@
* Vojtech Pavlik, Simunkova 1594, Prague 8, 182 00 Czech Republic
*/
+#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
+
#include <linux/slab.h>
#include <linux/module.h>
#include <linux/input.h>
@@ -38,8 +40,8 @@ MODULE_LICENSE("GPL");
static void evbug_event(struct input_handle *handle, unsigned int type, unsigned int code, int value)
{
- printk(KERN_DEBUG "evbug.c: Event. Dev: %s, Type: %d, Code: %d, Value: %d\n",
- dev_name(&handle->dev->dev), type, code, value);
+ printk(KERN_DEBUG pr_fmt("Event. Dev: %s, Type: %d, Code: %d, Value: %d\n"),
+ dev_name(&handle->dev->dev), type, code, value);
}
static int evbug_connect(struct input_handler *handler, struct input_dev *dev,
@@ -64,10 +66,10 @@ static int evbug_connect(struct input_handler *handler, struct input_dev *dev,
if (error)
goto err_unregister_handle;
- printk(KERN_DEBUG "evbug.c: Connected device: %s (%s at %s)\n",
- dev_name(&dev->dev),
- dev->name ?: "unknown",
- dev->phys ?: "unknown");
+ printk(KERN_DEBUG pr_fmt("Connected device: %s (%s at %s)\n"),
+ dev_name(&dev->dev),
+ dev->name ?: "unknown",
+ dev->phys ?: "unknown");
return 0;
@@ -80,8 +82,8 @@ static int evbug_connect(struct input_handler *handler, struct input_dev *dev,
static void evbug_disconnect(struct input_handle *handle)
{
- printk(KERN_DEBUG "evbug.c: Disconnected device: %s\n",
- dev_name(&handle->dev->dev));
+ printk(KERN_DEBUG pr_fmt("Disconnected device: %s\n"),
+ dev_name(&handle->dev->dev));
input_close_device(handle);
input_unregister_handle(handle);
diff --git a/drivers/input/evdev.c b/drivers/input/evdev.c
index e3f7fc6f956..c8471a2552e 100644
--- a/drivers/input/evdev.c
+++ b/drivers/input/evdev.c
@@ -8,6 +8,8 @@
* the Free Software Foundation.
*/
+#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
+
#define EVDEV_MINOR_BASE 64
#define EVDEV_MINORS 32
#define EVDEV_MIN_BUFFER_SIZE 64U
@@ -522,88 +524,84 @@ static int handle_eviocgbit(struct input_dev *dev,
if (type == EV_KEY && size == OLD_KEY_MAX) {
len = OLD_KEY_MAX;
if (printk_timed_ratelimit(&keymax_warn_time, 10 * 1000))
- printk(KERN_WARNING
- "evdev.c(EVIOCGBIT): Suspicious buffer size %u, "
- "limiting output to %zu bytes. See "
- "http://userweb.kernel.org/~dtor/eviocgbit-bug.html\n",
- OLD_KEY_MAX,
- BITS_TO_LONGS(OLD_KEY_MAX) * sizeof(long));
+ pr_warning("(EVIOCGBIT): Suspicious buffer size %u, "
+ "limiting output to %zu bytes. See "
+ "http://userweb.kernel.org/~dtor/eviocgbit-bug.html\n",
+ OLD_KEY_MAX,
+ BITS_TO_LONGS(OLD_KEY_MAX) * sizeof(long));
}
return bits_to_user(bits, len, size, p, compat_mode);
}
#undef OLD_KEY_MAX
-static int evdev_handle_get_keycode(struct input_dev *dev,
- void __user *p, size_t size)
+static int evdev_handle_get_keycode(struct input_dev *dev, void __user *p)
{
- struct input_keymap_entry ke;
+ struct input_keymap_entry ke = {
+ .len = sizeof(unsigned int),
+ .flags = 0,
+ };
+ int __user *ip = (int __user *)p;
int error;
- memset(&ke, 0, sizeof(ke));
+ /* legacy case */
+ if (copy_from_user(ke.scancode, p, sizeof(unsigned int)))
+ return -EFAULT;
- if (size == sizeof(unsigned int[2])) {
- /* legacy case */
- int __user *ip = (int __user *)p;
-
- if (copy_from_user(ke.scancode, p, sizeof(unsigned int)))
- return -EFAULT;
+ error = input_get_keycode(dev, &ke);
+ if (error)
+ return error;
- ke.len = sizeof(unsigned int);
- ke.flags = 0;
+ if (put_user(ke.keycode, ip + 1))
+ return -EFAULT;
- error = input_get_keycode(dev, &ke);
- if (error)
- return error;
+ return 0;
+}
- if (put_user(ke.keycode, ip + 1))
- return -EFAULT;
+static int evdev_handle_get_keycode_v2(struct input_dev *dev, void __user *p)
+{
+ struct input_keymap_entry ke;
+ int error;
- } else {
- size = min(size, sizeof(ke));
+ if (copy_from_user(&ke, p, sizeof(ke)))
+ return -EFAULT;
- if (copy_from_user(&ke, p, size))
- return -EFAULT;
+ error = input_get_keycode(dev, &ke);
+ if (error)
+ return error;
- error = input_get_keycode(dev, &ke);
- if (error)
- return error;
+ if (copy_to_user(p, &ke, sizeof(ke)))
+ return -EFAULT;
- if (copy_to_user(p, &ke, size))
- return -EFAULT;
- }
return 0;
}
-static int evdev_handle_set_keycode(struct input_dev *dev,
- void __user *p, size_t size)
+static int evdev_handle_set_keycode(struct input_dev *dev, void __user *p)
{
- struct input_keymap_entry ke;
-
- memset(&ke, 0, sizeof(ke));
+ struct input_keymap_entry ke = {
+ .len = sizeof(unsigned int),
+ .flags = 0,
+ };
+ int __user *ip = (int __user *)p;
- if (size == sizeof(unsigned int[2])) {
- /* legacy case */
- int __user *ip = (int __user *)p;
+ if (copy_from_user(ke.scancode, p, sizeof(unsigned int)))
+ return -EFAULT;
- if (copy_from_user(ke.scancode, p, sizeof(unsigned int)))
- return -EFAULT;
-
- if (get_user(ke.keycode, ip + 1))
- return -EFAULT;
+ if (get_user(ke.keycode, ip + 1))
+ return -EFAULT;
- ke.len = sizeof(unsigned int);
- ke.flags = 0;
+ return input_set_keycode(dev, &ke);
+}
- } else {
- size = min(size, sizeof(ke));
+static int evdev_handle_set_keycode_v2(struct input_dev *dev, void __user *p)
+{
+ struct input_keymap_entry ke;
- if (copy_from_user(&ke, p, size))
- return -EFAULT;
+ if (copy_from_user(&ke, p, sizeof(ke)))
+ return -EFAULT;
- if (ke.len > sizeof(ke.scancode))
- return -EINVAL;
- }
+ if (ke.len > sizeof(ke.scancode))
+ return -EINVAL;
return input_set_keycode(dev, &ke);
}
@@ -669,6 +667,18 @@ static long evdev_do_ioctl(struct file *file, unsigned int cmd,
return evdev_grab(evdev, client);
else
return evdev_ungrab(evdev, client);
+
+ case EVIOCGKEYCODE:
+ return evdev_handle_get_keycode(dev, p);
+
+ case EVIOCSKEYCODE:
+ return evdev_handle_set_keycode(dev, p);
+
+ case EVIOCGKEYCODE_V2:
+ return evdev_handle_get_keycode_v2(dev, p);
+
+ case EVIOCSKEYCODE_V2:
+ return evdev_handle_set_keycode_v2(dev, p);
}
size = _IOC_SIZE(cmd);
@@ -677,6 +687,10 @@ static long evdev_do_ioctl(struct file *file, unsigned int cmd,
#define EVIOC_MASK_SIZE(nr) ((nr) & ~(_IOC_SIZEMASK << _IOC_SIZESHIFT))
switch (EVIOC_MASK_SIZE(cmd)) {
+ case EVIOCGPROP(0):
+ return bits_to_user(dev->propbit, INPUT_PROP_MAX,
+ size, p, compat_mode);
+
case EVIOCGKEY(0):
return bits_to_user(dev->key, KEY_MAX, size, p, compat_mode);
@@ -708,12 +722,6 @@ static long evdev_do_ioctl(struct file *file, unsigned int cmd,
return -EFAULT;
return error;
-
- case EVIOC_MASK_SIZE(EVIOCGKEYCODE):
- return evdev_handle_get_keycode(dev, p, size);
-
- case EVIOC_MASK_SIZE(EVIOCSKEYCODE):
- return evdev_handle_set_keycode(dev, p, size);
}
/* Multi-number variable-length handlers */
@@ -894,7 +902,7 @@ static int evdev_connect(struct input_handler *handler, struct input_dev *dev,
break;
if (minor == EVDEV_MINORS) {
- printk(KERN_ERR "evdev: no more free evdev devices\n");
+ pr_err("no more free evdev devices\n");
return -ENFILE;
}
diff --git a/drivers/input/ff-core.c b/drivers/input/ff-core.c
index 03078c08309..3367f760d75 100644
--- a/drivers/input/ff-core.c
+++ b/drivers/input/ff-core.c
@@ -23,7 +23,7 @@
/* #define DEBUG */
-#define debug(format, arg...) pr_debug("ff-core: " format "\n", ## arg)
+#define pr_fmt(fmt) KBUILD_BASENAME ": " fmt
#include <linux/input.h>
#include <linux/module.h>
@@ -116,7 +116,7 @@ int input_ff_upload(struct input_dev *dev, struct ff_effect *effect,
if (effect->type < FF_EFFECT_MIN || effect->type > FF_EFFECT_MAX ||
!test_bit(effect->type, dev->ffbit)) {
- debug("invalid or not supported effect type in upload");
+ pr_debug("invalid or not supported effect type in upload\n");
return -EINVAL;
}
@@ -124,7 +124,7 @@ int input_ff_upload(struct input_dev *dev, struct ff_effect *effect,
(effect->u.periodic.waveform < FF_WAVEFORM_MIN ||
effect->u.periodic.waveform > FF_WAVEFORM_MAX ||
!test_bit(effect->u.periodic.waveform, dev->ffbit))) {
- debug("invalid or not supported wave form in upload");
+ pr_debug("invalid or not supported wave form in upload\n");
return -EINVAL;
}
@@ -246,7 +246,7 @@ static int flush_effects(struct input_dev *dev, struct file *file)
struct ff_device *ff = dev->ff;
int i;
- debug("flushing now");
+ pr_debug("flushing now\n");
mutex_lock(&ff->mutex);
@@ -315,8 +315,7 @@ int input_ff_create(struct input_dev *dev, int max_effects)
int i;
if (!max_effects) {
- printk(KERN_ERR
- "ff-core: cannot allocate device without any effects\n");
+ pr_err("cannot allocate device without any effects\n");
return -EINVAL;
}
diff --git a/drivers/input/ff-memless.c b/drivers/input/ff-memless.c
index 1d881c96ba8..117a59aaa70 100644
--- a/drivers/input/ff-memless.c
+++ b/drivers/input/ff-memless.c
@@ -23,7 +23,7 @@
/* #define DEBUG */
-#define debug(format, arg...) pr_debug("ff-memless: " format "\n", ## arg)
+#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
#include <linux/slab.h>
#include <linux/input.h>
@@ -129,7 +129,7 @@ static void ml_schedule_timer(struct ml_device *ml)
int events = 0;
int i;
- debug("calculating next timer");
+ pr_debug("calculating next timer\n");
for (i = 0; i < FF_MEMLESS_EFFECTS; i++) {
@@ -149,10 +149,10 @@ static void ml_schedule_timer(struct ml_device *ml)
}
if (!events) {
- debug("no actions");
+ pr_debug("no actions\n");
del_timer(&ml->timer);
} else {
- debug("timer set");
+ pr_debug("timer set\n");
mod_timer(&ml->timer, earliest);
}
}
@@ -173,8 +173,8 @@ static int apply_envelope(struct ml_effect_state *state, int value,
if (envelope->attack_length &&
time_before(now,
state->play_at + msecs_to_jiffies(envelope->attack_length))) {
- debug("value = 0x%x, attack_level = 0x%x", value,
- envelope->attack_level);
+ pr_debug("value = 0x%x, attack_level = 0x%x\n",
+ value, envelope->attack_level);
time_from_level = jiffies_to_msecs(now - state->play_at);
time_of_envelope = envelope->attack_length;
envelope_level = min_t(__s16, envelope->attack_level, 0x7fff);
@@ -191,13 +191,13 @@ static int apply_envelope(struct ml_effect_state *state, int value,
difference = abs(value) - envelope_level;
- debug("difference = %d", difference);
- debug("time_from_level = 0x%x", time_from_level);
- debug("time_of_envelope = 0x%x", time_of_envelope);
+ pr_debug("difference = %d\n", difference);
+ pr_debug("time_from_level = 0x%x\n", time_from_level);
+ pr_debug("time_of_envelope = 0x%x\n", time_of_envelope);
difference = difference * time_from_level / time_of_envelope;
- debug("difference = %d", difference);
+ pr_debug("difference = %d\n", difference);
return value < 0 ?
-(difference + envelope_level) : (difference + envelope_level);
@@ -215,8 +215,7 @@ static int get_compatible_type(struct ff_device *ff, int effect_type)
if (effect_type == FF_PERIODIC && test_bit(FF_RUMBLE, ff->ffbit))
return FF_RUMBLE;
- printk(KERN_ERR
- "ff-memless: invalid type in get_compatible_type()\n");
+ pr_err("invalid type in get_compatible_type()\n");
return 0;
}
@@ -312,7 +311,7 @@ static void ml_combine_effects(struct ff_effect *effect,
break;
default:
- printk(KERN_ERR "ff-memless: invalid type in ml_combine_effects()\n");
+ pr_err("invalid type in ml_combine_effects()\n");
break;
}
@@ -406,7 +405,7 @@ static void ml_effect_timer(unsigned long timer_data)
struct ml_device *ml = dev->ff->private;
unsigned long flags;
- debug("timer: updating effects");
+ pr_debug("timer: updating effects\n");
spin_lock_irqsave(&dev->event_lock, flags);
ml_play_effects(ml);
@@ -438,7 +437,7 @@ static int ml_ff_playback(struct input_dev *dev, int effect_id, int value)
struct ml_effect_state *state = &ml->states[effect_id];
if (value > 0) {
- debug("initiated play");
+ pr_debug("initiated play\n");
__set_bit(FF_EFFECT_STARTED, &state->flags);
state->count = value;
@@ -449,7 +448,7 @@ static int ml_ff_playback(struct input_dev *dev, int effect_id, int value)
state->adj_at = state->play_at;
} else {
- debug("initiated stop");
+ pr_debug("initiated stop\n");
if (test_bit(FF_EFFECT_PLAYING, &state->flags))
__set_bit(FF_EFFECT_ABORTING, &state->flags);
diff --git a/drivers/input/gameport/gameport.c b/drivers/input/gameport/gameport.c
index 46239e47a26..23cf8fc933e 100644
--- a/drivers/input/gameport/gameport.c
+++ b/drivers/input/gameport/gameport.c
@@ -18,13 +18,11 @@
#include <linux/ioport.h>
#include <linux/init.h>
#include <linux/gameport.h>
-#include <linux/wait.h>
#include <linux/slab.h>
#include <linux/delay.h>
-#include <linux/kthread.h>
+#include <linux/workqueue.h>
#include <linux/sched.h> /* HZ */
#include <linux/mutex.h>
-#include <linux/freezer.h>
/*#include <asm/io.h>*/
@@ -123,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
@@ -234,58 +232,22 @@ struct gameport_event {
static DEFINE_SPINLOCK(gameport_event_lock); /* protects gameport_event_list */
static LIST_HEAD(gameport_event_list);
-static DECLARE_WAIT_QUEUE_HEAD(gameport_wait);
-static struct task_struct *gameport_task;
-static int gameport_queue_event(void *object, struct module *owner,
- enum gameport_event_type event_type)
+static struct gameport_event *gameport_get_event(void)
{
+ struct gameport_event *event = NULL;
unsigned long flags;
- struct gameport_event *event;
- int retval = 0;
spin_lock_irqsave(&gameport_event_lock, flags);
- /*
- * Scan event list for the other events for the same gameport port,
- * starting with the most recent one. If event is the same we
- * do not need add new one. If event is of different type we
- * need to add this event and should not look further because
- * we need to preseve sequence of distinct events.
- */
- list_for_each_entry_reverse(event, &gameport_event_list, node) {
- if (event->object == object) {
- if (event->type == event_type)
- goto out;
- break;
- }
- }
-
- event = kmalloc(sizeof(struct gameport_event), GFP_ATOMIC);
- if (!event) {
- pr_err("Not enough memory to queue event %d\n", event_type);
- retval = -ENOMEM;
- goto out;
- }
-
- if (!try_module_get(owner)) {
- pr_warning("Can't get module reference, dropping event %d\n",
- event_type);
- kfree(event);
- retval = -EINVAL;
- goto out;
+ if (!list_empty(&gameport_event_list)) {
+ event = list_first_entry(&gameport_event_list,
+ struct gameport_event, node);
+ list_del_init(&event->node);
}
- event->type = event_type;
- event->object = object;
- event->owner = owner;
-
- list_add_tail(&event->node, &gameport_event_list);
- wake_up(&gameport_wait);
-
-out:
spin_unlock_irqrestore(&gameport_event_lock, flags);
- return retval;
+ return event;
}
static void gameport_free_event(struct gameport_event *event)
@@ -319,24 +281,8 @@ static void gameport_remove_duplicate_events(struct gameport_event *event)
spin_unlock_irqrestore(&gameport_event_lock, flags);
}
-static struct gameport_event *gameport_get_event(void)
-{
- struct gameport_event *event = NULL;
- unsigned long flags;
-
- spin_lock_irqsave(&gameport_event_lock, flags);
-
- if (!list_empty(&gameport_event_list)) {
- event = list_first_entry(&gameport_event_list,
- struct gameport_event, node);
- list_del_init(&event->node);
- }
-
- spin_unlock_irqrestore(&gameport_event_lock, flags);
- return event;
-}
-static void gameport_handle_event(void)
+static void gameport_handle_events(struct work_struct *work)
{
struct gameport_event *event;
@@ -368,6 +314,59 @@ static void gameport_handle_event(void)
mutex_unlock(&gameport_mutex);
}
+static DECLARE_WORK(gameport_event_work, gameport_handle_events);
+
+static int gameport_queue_event(void *object, struct module *owner,
+ enum gameport_event_type event_type)
+{
+ unsigned long flags;
+ struct gameport_event *event;
+ int retval = 0;
+
+ spin_lock_irqsave(&gameport_event_lock, flags);
+
+ /*
+ * Scan event list for the other events for the same gameport port,
+ * starting with the most recent one. If event is the same we
+ * do not need add new one. If event is of different type we
+ * need to add this event and should not look further because
+ * we need to preserve sequence of distinct events.
+ */
+ list_for_each_entry_reverse(event, &gameport_event_list, node) {
+ if (event->object == object) {
+ if (event->type == event_type)
+ goto out;
+ break;
+ }
+ }
+
+ event = kmalloc(sizeof(struct gameport_event), GFP_ATOMIC);
+ if (!event) {
+ pr_err("Not enough memory to queue event %d\n", event_type);
+ retval = -ENOMEM;
+ goto out;
+ }
+
+ if (!try_module_get(owner)) {
+ pr_warning("Can't get module reference, dropping event %d\n",
+ event_type);
+ kfree(event);
+ retval = -EINVAL;
+ goto out;
+ }
+
+ event->type = event_type;
+ event->object = object;
+ event->owner = owner;
+
+ list_add_tail(&event->node, &gameport_event_list);
+ schedule_work(&gameport_event_work);
+
+out:
+ spin_unlock_irqrestore(&gameport_event_lock, flags);
+ return retval;
+}
+
/*
* Remove all events that have been submitted for a given object,
* be it a gameport port or a driver.
@@ -419,19 +418,6 @@ static struct gameport *gameport_get_pending_child(struct gameport *parent)
return child;
}
-static int gameport_thread(void *nothing)
-{
- set_freezable();
- do {
- gameport_handle_event();
- wait_event_freezable(gameport_wait,
- kthread_should_stop() || !list_empty(&gameport_event_list));
- } while (!kthread_should_stop());
-
- return 0;
-}
-
-
/*
* Gameport port operations
*/
@@ -814,13 +800,6 @@ static int __init gameport_init(void)
return error;
}
- gameport_task = kthread_run(gameport_thread, NULL, "kgameportd");
- if (IS_ERR(gameport_task)) {
- bus_unregister(&gameport_bus);
- error = PTR_ERR(gameport_task);
- pr_err("Failed to start kgameportd, error: %d\n", error);
- return error;
- }
return 0;
}
@@ -828,7 +807,12 @@ static int __init gameport_init(void)
static void __exit gameport_exit(void)
{
bus_unregister(&gameport_bus);
- kthread_stop(gameport_task);
+
+ /*
+ * There should not be any outstanding events but work may
+ * still be scheduled so simply cancel it.
+ */
+ cancel_work_sync(&gameport_event_work);
}
subsys_initcall(gameport_init);
diff --git a/drivers/input/input-mt.c b/drivers/input/input-mt.c
new file mode 100644
index 00000000000..c48c81f0308
--- /dev/null
+++ b/drivers/input/input-mt.c
@@ -0,0 +1,170 @@
+/*
+ * Input Multitouch Library
+ *
+ * Copyright (c) 2008-2010 Henrik Rydberg
+ *
+ * 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.
+ */
+
+#include <linux/input/mt.h>
+#include <linux/slab.h>
+
+#define TRKID_SGN ((TRKID_MAX + 1) >> 1)
+
+/**
+ * input_mt_init_slots() - initialize MT input slots
+ * @dev: input device supporting MT events and finger tracking
+ * @num_slots: number of slots used by the device
+ *
+ * This function allocates all necessary memory for MT slot handling
+ * in the input device, prepares the ABS_MT_SLOT and
+ * ABS_MT_TRACKING_ID events for use and sets up appropriate buffers.
+ * May be called repeatedly. Returns -EINVAL if attempting to
+ * reinitialize with a different number of slots.
+ */
+int input_mt_init_slots(struct input_dev *dev, unsigned int num_slots)
+{
+ int i;
+
+ if (!num_slots)
+ return 0;
+ if (dev->mt)
+ return dev->mtsize != num_slots ? -EINVAL : 0;
+
+ dev->mt = kcalloc(num_slots, sizeof(struct input_mt_slot), GFP_KERNEL);
+ if (!dev->mt)
+ return -ENOMEM;
+
+ dev->mtsize = num_slots;
+ input_set_abs_params(dev, ABS_MT_SLOT, 0, num_slots - 1, 0, 0);
+ input_set_abs_params(dev, ABS_MT_TRACKING_ID, 0, TRKID_MAX, 0, 0);
+ input_set_events_per_packet(dev, 6 * num_slots);
+
+ /* Mark slots as 'unused' */
+ for (i = 0; i < num_slots; i++)
+ input_mt_set_value(&dev->mt[i], ABS_MT_TRACKING_ID, -1);
+
+ return 0;
+}
+EXPORT_SYMBOL(input_mt_init_slots);
+
+/**
+ * input_mt_destroy_slots() - frees the MT slots of the input device
+ * @dev: input device with allocated MT slots
+ *
+ * This function is only needed in error path as the input core will
+ * automatically free the MT slots when the device is destroyed.
+ */
+void input_mt_destroy_slots(struct input_dev *dev)
+{
+ kfree(dev->mt);
+ dev->mt = NULL;
+ dev->mtsize = 0;
+ dev->slot = 0;
+ dev->trkid = 0;
+}
+EXPORT_SYMBOL(input_mt_destroy_slots);
+
+/**
+ * input_mt_report_slot_state() - report contact state
+ * @dev: input device with allocated MT slots
+ * @tool_type: the tool type to use in this slot
+ * @active: true if contact is active, false otherwise
+ *
+ * Reports a contact via ABS_MT_TRACKING_ID, and optionally
+ * ABS_MT_TOOL_TYPE. If active is true and the slot is currently
+ * inactive, or if the tool type is changed, a new tracking id is
+ * assigned to the slot. The tool type is only reported if the
+ * corresponding absbit field is set.
+ */
+void input_mt_report_slot_state(struct input_dev *dev,
+ unsigned int tool_type, bool active)
+{
+ struct input_mt_slot *mt;
+ int id;
+
+ if (!dev->mt || !active) {
+ input_event(dev, EV_ABS, ABS_MT_TRACKING_ID, -1);
+ return;
+ }
+
+ mt = &dev->mt[dev->slot];
+ id = input_mt_get_value(mt, ABS_MT_TRACKING_ID);
+ if (id < 0 || input_mt_get_value(mt, ABS_MT_TOOL_TYPE) != tool_type)
+ id = input_mt_new_trkid(dev);
+
+ input_event(dev, EV_ABS, ABS_MT_TRACKING_ID, id);
+ input_event(dev, EV_ABS, ABS_MT_TOOL_TYPE, tool_type);
+}
+EXPORT_SYMBOL(input_mt_report_slot_state);
+
+/**
+ * input_mt_report_finger_count() - report contact count
+ * @dev: input device with allocated MT slots
+ * @count: the number of contacts
+ *
+ * Reports the contact count via BTN_TOOL_FINGER, BTN_TOOL_DOUBLETAP,
+ * BTN_TOOL_TRIPLETAP and BTN_TOOL_QUADTAP.
+ *
+ * The input core ensures only the KEY events already setup for
+ * this device will produce output.
+ */
+void input_mt_report_finger_count(struct input_dev *dev, int count)
+{
+ input_event(dev, EV_KEY, BTN_TOOL_FINGER, count == 1);
+ input_event(dev, EV_KEY, BTN_TOOL_DOUBLETAP, count == 2);
+ input_event(dev, EV_KEY, BTN_TOOL_TRIPLETAP, count == 3);
+ input_event(dev, EV_KEY, BTN_TOOL_QUADTAP, count == 4);
+}
+EXPORT_SYMBOL(input_mt_report_finger_count);
+
+/**
+ * input_mt_report_pointer_emulation() - common pointer emulation
+ * @dev: input device with allocated MT slots
+ * @use_count: report number of active contacts as finger count
+ *
+ * Performs legacy pointer emulation via BTN_TOUCH, ABS_X, ABS_Y and
+ * ABS_PRESSURE. Touchpad finger count is emulated if use_count is true.
+ *
+ * The input core ensures only the KEY and ABS axes already setup for
+ * this device will produce output.
+ */
+void input_mt_report_pointer_emulation(struct input_dev *dev, bool use_count)
+{
+ struct input_mt_slot *oldest = 0;
+ int oldid = dev->trkid;
+ int count = 0;
+ int i;
+
+ for (i = 0; i < dev->mtsize; ++i) {
+ struct input_mt_slot *ps = &dev->mt[i];
+ int id = input_mt_get_value(ps, ABS_MT_TRACKING_ID);
+
+ if (id < 0)
+ continue;
+ if ((id - oldid) & TRKID_SGN) {
+ oldest = ps;
+ oldid = id;
+ }
+ count++;
+ }
+
+ input_event(dev, EV_KEY, BTN_TOUCH, count > 0);
+ if (use_count)
+ input_mt_report_finger_count(dev, count);
+
+ if (oldest) {
+ int x = input_mt_get_value(oldest, ABS_MT_POSITION_X);
+ int y = input_mt_get_value(oldest, ABS_MT_POSITION_Y);
+ int p = input_mt_get_value(oldest, ABS_MT_PRESSURE);
+
+ input_event(dev, EV_ABS, ABS_X, x);
+ input_event(dev, EV_ABS, ABS_Y, y);
+ input_event(dev, EV_ABS, ABS_PRESSURE, p);
+ } else {
+ input_event(dev, EV_ABS, ABS_PRESSURE, 0);
+ }
+}
+EXPORT_SYMBOL(input_mt_report_pointer_emulation);
diff --git a/drivers/input/input-polldev.c b/drivers/input/input-polldev.c
index 10c9b0a845f..0559e309bac 100644
--- a/drivers/input/input-polldev.c
+++ b/drivers/input/input-polldev.c
@@ -8,6 +8,8 @@
* the Free Software Foundation.
*/
+#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
+
#include <linux/jiffies.h>
#include <linux/slab.h>
#include <linux/mutex.h>
@@ -33,8 +35,7 @@ static int input_polldev_start_workqueue(void)
if (!polldev_users) {
polldev_wq = create_singlethread_workqueue("ipolldevd");
if (!polldev_wq) {
- printk(KERN_ERR "input-polldev: failed to create "
- "ipolldevd workqueue\n");
+ pr_err("failed to create ipolldevd workqueue\n");
retval = -ENOMEM;
goto out;
}
diff --git a/drivers/input/input.c b/drivers/input/input.c
index db409d6bd5d..7985114beac 100644
--- a/drivers/input/input.c
+++ b/drivers/input/input.c
@@ -10,9 +10,11 @@
* the Free Software Foundation.
*/
+#define pr_fmt(fmt) KBUILD_BASENAME ": " fmt
+
#include <linux/init.h>
#include <linux/types.h>
-#include <linux/input.h>
+#include <linux/input/mt.h>
#include <linux/module.h>
#include <linux/slab.h>
#include <linux/random.h>
@@ -958,10 +960,8 @@ static int input_attach_handler(struct input_dev *dev, struct input_handler *han
error = handler->connect(handler, dev, id);
if (error && error != -ENODEV)
- printk(KERN_ERR
- "input: failed to attach handler %s to device %s, "
- "error: %d\n",
- handler->name, kobject_name(&dev->dev.kobj), error);
+ pr_err("failed to attach handler %s to device %s, error: %d\n",
+ handler->name, kobject_name(&dev->dev.kobj), error);
return error;
}
@@ -1109,6 +1109,8 @@ static int input_devices_seq_show(struct seq_file *seq, void *v)
seq_printf(seq, "%s ", handle->name);
seq_putc(seq, '\n');
+ input_seq_print_bitmap(seq, "PROP", dev->propbit, INPUT_PROP_MAX);
+
input_seq_print_bitmap(seq, "EV", dev->evbit, EV_MAX);
if (test_bit(EV_KEY, dev->evbit))
input_seq_print_bitmap(seq, "KEY", dev->keybit, KEY_MAX);
@@ -1332,11 +1334,26 @@ static ssize_t input_dev_show_modalias(struct device *dev,
}
static DEVICE_ATTR(modalias, S_IRUGO, input_dev_show_modalias, NULL);
+static int input_print_bitmap(char *buf, int buf_size, unsigned long *bitmap,
+ int max, int add_cr);
+
+static ssize_t input_dev_show_properties(struct device *dev,
+ struct device_attribute *attr,
+ char *buf)
+{
+ struct input_dev *input_dev = to_input_dev(dev);
+ int len = input_print_bitmap(buf, PAGE_SIZE, input_dev->propbit,
+ INPUT_PROP_MAX, true);
+ return min_t(int, len, PAGE_SIZE);
+}
+static DEVICE_ATTR(properties, S_IRUGO, input_dev_show_properties, NULL);
+
static struct attribute *input_dev_attrs[] = {
&dev_attr_name.attr,
&dev_attr_phys.attr,
&dev_attr_uniq.attr,
&dev_attr_modalias.attr,
+ &dev_attr_properties.attr,
NULL
};
@@ -1470,7 +1487,7 @@ static int input_add_uevent_bm_var(struct kobj_uevent_env *env,
{
int len;
- if (add_uevent_var(env, "%s=", name))
+ if (add_uevent_var(env, "%s", name))
return -ENOMEM;
len = input_print_bitmap(&env->buf[env->buflen - 1],
@@ -1536,6 +1553,8 @@ static int input_dev_uevent(struct device *device, struct kobj_uevent_env *env)
if (dev->uniq)
INPUT_ADD_HOTPLUG_VAR("UNIQ=\"%s\"", dev->uniq);
+ INPUT_ADD_HOTPLUG_BM_VAR("PROP=", dev->propbit, INPUT_PROP_MAX);
+
INPUT_ADD_HOTPLUG_BM_VAR("EV=", dev->evbit, EV_MAX);
if (test_bit(EV_KEY, dev->evbit))
INPUT_ADD_HOTPLUG_BM_VAR("KEY=", dev->keybit, KEY_MAX);
@@ -1725,52 +1744,6 @@ void input_free_device(struct input_dev *dev)
EXPORT_SYMBOL(input_free_device);
/**
- * input_mt_create_slots() - create MT input slots
- * @dev: input device supporting MT events and finger tracking
- * @num_slots: number of slots used by the device
- *
- * This function allocates all necessary memory for MT slot handling in the
- * input device, and adds ABS_MT_SLOT to the device capabilities. All slots
- * are initially marked as unused by setting ABS_MT_TRACKING_ID to -1.
- */
-int input_mt_create_slots(struct input_dev *dev, unsigned int num_slots)
-{
- int i;
-
- if (!num_slots)
- return 0;
-
- dev->mt = kcalloc(num_slots, sizeof(struct input_mt_slot), GFP_KERNEL);
- if (!dev->mt)
- return -ENOMEM;
-
- dev->mtsize = num_slots;
- input_set_abs_params(dev, ABS_MT_SLOT, 0, num_slots - 1, 0, 0);
-
- /* Mark slots as 'unused' */
- for (i = 0; i < num_slots; i++)
- dev->mt[i].abs[ABS_MT_TRACKING_ID - ABS_MT_FIRST] = -1;
-
- return 0;
-}
-EXPORT_SYMBOL(input_mt_create_slots);
-
-/**
- * input_mt_destroy_slots() - frees the MT slots of the input device
- * @dev: input device with allocated MT slots
- *
- * This function is only needed in error path as the input core will
- * automatically free the MT slots when the device is destroyed.
- */
-void input_mt_destroy_slots(struct input_dev *dev)
-{
- kfree(dev->mt);
- dev->mt = NULL;
- dev->mtsize = 0;
-}
-EXPORT_SYMBOL(input_mt_destroy_slots);
-
-/**
* input_set_capability - mark device as capable of a certain event
* @dev: device that is capable of emitting or accepting event
* @type: type of the event (EV_KEY, EV_REL, etc...)
@@ -1819,9 +1792,8 @@ void input_set_capability(struct input_dev *dev, unsigned int type, unsigned int
break;
default:
- printk(KERN_ERR
- "input_set_capability: unknown type %u (code %u)\n",
- type, code);
+ pr_err("input_set_capability: unknown type %u (code %u)\n",
+ type, code);
dump_stack();
return;
}
@@ -1903,8 +1875,9 @@ int input_register_device(struct input_dev *dev)
return error;
path = kobject_get_path(&dev->dev.kobj, GFP_KERNEL);
- printk(KERN_INFO "input: %s as %s\n",
- dev->name ? dev->name : "Unspecified device", path ? path : "N/A");
+ pr_info("%s as %s\n",
+ dev->name ? dev->name : "Unspecified device",
+ path ? path : "N/A");
kfree(path);
error = mutex_lock_interruptible(&input_mutex);
@@ -2186,7 +2159,7 @@ static int __init input_init(void)
err = class_register(&input_class);
if (err) {
- printk(KERN_ERR "input: unable to register input_dev class\n");
+ pr_err("unable to register input_dev class\n");
return err;
}
@@ -2196,7 +2169,7 @@ static int __init input_init(void)
err = register_chrdev(INPUT_MAJOR, "input", &input_fops);
if (err) {
- printk(KERN_ERR "input: unable to register char major %d", INPUT_MAJOR);
+ pr_err("unable to register char major %d", INPUT_MAJOR);
goto fail2;
}
diff --git a/drivers/input/joydev.c b/drivers/input/joydev.c
index 9d424cebfd2..3182c9cd1b0 100644
--- a/drivers/input/joydev.c
+++ b/drivers/input/joydev.c
@@ -10,6 +10,8 @@
* (at your option) any later version.
*/
+#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
+
#include <asm/io.h>
#include <asm/system.h>
#include <linux/delay.h>
@@ -806,7 +808,7 @@ static int joydev_connect(struct input_handler *handler, struct input_dev *dev,
break;
if (minor == JOYDEV_MINORS) {
- printk(KERN_ERR "joydev: no more free joydev devices\n");
+ pr_err("no more free joydev devices\n");
return -ENFILE;
}
diff --git a/drivers/input/joystick/Kconfig b/drivers/input/joystick/Kconfig
index 5b596165b57..56eb471b557 100644
--- a/drivers/input/joystick/Kconfig
+++ b/drivers/input/joystick/Kconfig
@@ -255,6 +255,16 @@ config JOYSTICK_AMIGA
To compile this driver as a module, choose M here: the
module will be called amijoy.
+config JOYSTICK_AS5011
+ tristate "Austria Microsystem AS5011 joystick"
+ depends on I2C
+ help
+ Say Y here if you have an AS5011 digital joystick connected to your
+ system.
+
+ To compile this driver as a module, choose M here: the
+ module will be called as5011.
+
config JOYSTICK_JOYDUMP
tristate "Gameport data dumper"
select GAMEPORT
diff --git a/drivers/input/joystick/Makefile b/drivers/input/joystick/Makefile
index f3a8cbe2abb..92dc0de9dfe 100644
--- a/drivers/input/joystick/Makefile
+++ b/drivers/input/joystick/Makefile
@@ -7,6 +7,7 @@
obj-$(CONFIG_JOYSTICK_A3D) += a3d.o
obj-$(CONFIG_JOYSTICK_ADI) += adi.o
obj-$(CONFIG_JOYSTICK_AMIGA) += amijoy.o
+obj-$(CONFIG_JOYSTICK_AS5011) += as5011.o
obj-$(CONFIG_JOYSTICK_ANALOG) += analog.o
obj-$(CONFIG_JOYSTICK_COBRA) += cobra.o
obj-$(CONFIG_JOYSTICK_DB9) += db9.o
diff --git a/drivers/input/joystick/as5011.c b/drivers/input/joystick/as5011.c
new file mode 100644
index 00000000000..f6732b57ca0
--- /dev/null
+++ b/drivers/input/joystick/as5011.c
@@ -0,0 +1,367 @@
+/*
+ * Copyright (c) 2010, 2011 Fabien Marteau <fabien.marteau@armadeus.com>
+ * Sponsored by ARMadeus Systems
+ *
+ * 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
+ *
+ * Driver for Austria Microsystems joysticks AS5011
+ *
+ * TODO:
+ * - Power on the chip when open() and power down when close()
+ * - Manage power mode
+ */
+
+#include <linux/i2c.h>
+#include <linux/interrupt.h>
+#include <linux/input.h>
+#include <linux/gpio.h>
+#include <linux/delay.h>
+#include <linux/input/as5011.h>
+#include <linux/slab.h>
+
+#define DRIVER_DESC "Driver for Austria Microsystems AS5011 joystick"
+#define MODULE_DEVICE_ALIAS "as5011"
+
+MODULE_AUTHOR("Fabien Marteau <fabien.marteau@armadeus.com>");
+MODULE_DESCRIPTION(DRIVER_DESC);
+MODULE_LICENSE("GPL");
+
+/* registers */
+#define AS5011_CTRL1 0x76
+#define AS5011_CTRL2 0x75
+#define AS5011_XP 0x43
+#define AS5011_XN 0x44
+#define AS5011_YP 0x53
+#define AS5011_YN 0x54
+#define AS5011_X_REG 0x41
+#define AS5011_Y_REG 0x42
+#define AS5011_X_RES_INT 0x51
+#define AS5011_Y_RES_INT 0x52
+
+/* CTRL1 bits */
+#define AS5011_CTRL1_LP_PULSED 0x80
+#define AS5011_CTRL1_LP_ACTIVE 0x40
+#define AS5011_CTRL1_LP_CONTINUE 0x20
+#define AS5011_CTRL1_INT_WUP_EN 0x10
+#define AS5011_CTRL1_INT_ACT_EN 0x08
+#define AS5011_CTRL1_EXT_CLK_EN 0x04
+#define AS5011_CTRL1_SOFT_RST 0x02
+#define AS5011_CTRL1_DATA_VALID 0x01
+
+/* CTRL2 bits */
+#define AS5011_CTRL2_EXT_SAMPLE_EN 0x08
+#define AS5011_CTRL2_RC_BIAS_ON 0x04
+#define AS5011_CTRL2_INV_SPINNING 0x02
+
+#define AS5011_MAX_AXIS 80
+#define AS5011_MIN_AXIS (-80)
+#define AS5011_FUZZ 8
+#define AS5011_FLAT 40
+
+struct as5011_device {
+ struct input_dev *input_dev;
+ struct i2c_client *i2c_client;
+ unsigned int button_gpio;
+ unsigned int button_irq;
+ unsigned int axis_irq;
+};
+
+static int as5011_i2c_write(struct i2c_client *client,
+ uint8_t aregaddr,
+ uint8_t avalue)
+{
+ uint8_t data[2] = { aregaddr, avalue };
+ struct i2c_msg msg = {
+ client->addr, I2C_M_IGNORE_NAK, 2, (uint8_t *)data
+ };
+ int error;
+
+ error = i2c_transfer(client->adapter, &msg, 1);
+ return error < 0 ? error : 0;
+}
+
+static int as5011_i2c_read(struct i2c_client *client,
+ uint8_t aregaddr, signed char *value)
+{
+ uint8_t data[2] = { aregaddr };
+ struct i2c_msg msg_set[2] = {
+ { client->addr, I2C_M_REV_DIR_ADDR, 1, (uint8_t *)data },
+ { client->addr, I2C_M_RD | I2C_M_NOSTART, 1, (uint8_t *)data }
+ };
+ int error;
+
+ error = i2c_transfer(client->adapter, msg_set, 2);
+ if (error < 0)
+ return error;
+
+ *value = data[0] & 0x80 ? -1 * (1 + ~data[0]) : data[0];
+ return 0;
+}
+
+static irqreturn_t as5011_button_interrupt(int irq, void *dev_id)
+{
+ struct as5011_device *as5011 = dev_id;
+ int val = gpio_get_value_cansleep(as5011->button_gpio);
+
+ input_report_key(as5011->input_dev, BTN_JOYSTICK, !val);
+ input_sync(as5011->input_dev);
+
+ return IRQ_HANDLED;
+}
+
+static irqreturn_t as5011_axis_interrupt(int irq, void *dev_id)
+{
+ struct as5011_device *as5011 = dev_id;
+ int error;
+ signed char x, y;
+
+ error = as5011_i2c_read(as5011->i2c_client, AS5011_X_RES_INT, &x);
+ if (error < 0)
+ goto out;
+
+ error = as5011_i2c_read(as5011->i2c_client, AS5011_Y_RES_INT, &y);
+ if (error < 0)
+ goto out;
+
+ input_report_abs(as5011->input_dev, ABS_X, x);
+ input_report_abs(as5011->input_dev, ABS_Y, y);
+ input_sync(as5011->input_dev);
+
+out:
+ return IRQ_HANDLED;
+}
+
+static int __devinit as5011_configure_chip(struct as5011_device *as5011,
+ const struct as5011_platform_data *plat_dat)
+{
+ struct i2c_client *client = as5011->i2c_client;
+ int error;
+ signed char value;
+
+ /* chip soft reset */
+ error = as5011_i2c_write(client, AS5011_CTRL1,
+ AS5011_CTRL1_SOFT_RST);
+ if (error < 0) {
+ dev_err(&client->dev, "Soft reset failed\n");
+ return error;
+ }
+
+ mdelay(10);
+
+ error = as5011_i2c_write(client, AS5011_CTRL1,
+ AS5011_CTRL1_LP_PULSED |
+ AS5011_CTRL1_LP_ACTIVE |
+ AS5011_CTRL1_INT_ACT_EN);
+ if (error < 0) {
+ dev_err(&client->dev, "Power config failed\n");
+ return error;
+ }
+
+ error = as5011_i2c_write(client, AS5011_CTRL2,
+ AS5011_CTRL2_INV_SPINNING);
+ if (error < 0) {
+ dev_err(&client->dev, "Can't invert spinning\n");
+ return error;
+ }
+
+ /* write threshold */
+ error = as5011_i2c_write(client, AS5011_XP, plat_dat->xp);
+ if (error < 0) {
+ dev_err(&client->dev, "Can't write threshold\n");
+ return error;
+ }
+
+ error = as5011_i2c_write(client, AS5011_XN, plat_dat->xn);
+ if (error < 0) {
+ dev_err(&client->dev, "Can't write threshold\n");
+ return error;
+ }
+
+ error = as5011_i2c_write(client, AS5011_YP, plat_dat->yp);
+ if (error < 0) {
+ dev_err(&client->dev, "Can't write threshold\n");
+ return error;
+ }
+
+ error = as5011_i2c_write(client, AS5011_YN, plat_dat->yn);
+ if (error < 0) {
+ dev_err(&client->dev, "Can't write threshold\n");
+ return error;
+ }
+
+ /* to free irq gpio in chip */
+ error = as5011_i2c_read(client, AS5011_X_RES_INT, &value);
+ if (error < 0) {
+ dev_err(&client->dev, "Can't read i2c X resolution value\n");
+ return error;
+ }
+
+ return 0;
+}
+
+static int __devinit as5011_probe(struct i2c_client *client,
+ const struct i2c_device_id *id)
+{
+ const struct as5011_platform_data *plat_data;
+ struct as5011_device *as5011;
+ struct input_dev *input_dev;
+ int irq;
+ int error;
+
+ plat_data = client->dev.platform_data;
+ if (!plat_data)
+ return -EINVAL;
+
+ if (!plat_data->axis_irq) {
+ dev_err(&client->dev, "No axis IRQ?\n");
+ return -EINVAL;
+ }
+
+ if (!i2c_check_functionality(client->adapter,
+ I2C_FUNC_PROTOCOL_MANGLING)) {
+ dev_err(&client->dev,
+ "need i2c bus that supports protocol mangling\n");
+ return -ENODEV;
+ }
+
+ as5011 = kmalloc(sizeof(struct as5011_device), GFP_KERNEL);
+ input_dev = input_allocate_device();
+ if (!as5011 || !input_dev) {
+ dev_err(&client->dev,
+ "Can't allocate memory for device structure\n");
+ error = -ENOMEM;
+ goto err_free_mem;
+ }
+
+ as5011->i2c_client = client;
+ as5011->input_dev = input_dev;
+ as5011->button_gpio = plat_data->button_gpio;
+ as5011->axis_irq = plat_data->axis_irq;
+
+ input_dev->name = "Austria Microsystem as5011 joystick";
+ input_dev->id.bustype = BUS_I2C;
+ input_dev->dev.parent = &client->dev;
+
+ __set_bit(EV_KEY, input_dev->evbit);
+ __set_bit(EV_ABS, input_dev->evbit);
+ __set_bit(BTN_JOYSTICK, input_dev->keybit);
+
+ input_set_abs_params(input_dev, ABS_X,
+ AS5011_MIN_AXIS, AS5011_MAX_AXIS, AS5011_FUZZ, AS5011_FLAT);
+ input_set_abs_params(as5011->input_dev, ABS_Y,
+ AS5011_MIN_AXIS, AS5011_MAX_AXIS, AS5011_FUZZ, AS5011_FLAT);
+
+ error = gpio_request(as5011->button_gpio, "AS5011 button");
+ if (error < 0) {
+ dev_err(&client->dev, "Failed to request button gpio\n");
+ goto err_free_mem;
+ }
+
+ irq = gpio_to_irq(as5011->button_gpio);
+ if (irq < 0) {
+ dev_err(&client->dev,
+ "Failed to get irq number for button gpio\n");
+ goto err_free_button_gpio;
+ }
+
+ as5011->button_irq = irq;
+
+ error = request_threaded_irq(as5011->button_irq,
+ NULL, as5011_button_interrupt,
+ IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING,
+ "as5011_button", as5011);
+ if (error < 0) {
+ dev_err(&client->dev,
+ "Can't allocate button irq %d\n", as5011->button_irq);
+ goto err_free_button_gpio;
+ }
+
+ error = as5011_configure_chip(as5011, plat_data);
+ if (error)
+ goto err_free_button_irq;
+
+ error = request_threaded_irq(as5011->axis_irq, NULL,
+ as5011_axis_interrupt,
+ plat_data->axis_irqflags,
+ "as5011_joystick", as5011);
+ if (error) {
+ dev_err(&client->dev,
+ "Can't allocate axis irq %d\n", plat_data->axis_irq);
+ goto err_free_button_irq;
+ }
+
+ error = input_register_device(as5011->input_dev);
+ if (error) {
+ dev_err(&client->dev, "Failed to register input device\n");
+ goto err_free_axis_irq;
+ }
+
+ i2c_set_clientdata(client, as5011);
+
+ return 0;
+
+err_free_axis_irq:
+ free_irq(as5011->axis_irq, as5011);
+err_free_button_irq:
+ free_irq(as5011->button_irq, as5011);
+err_free_button_gpio:
+ gpio_free(as5011->button_gpio);
+err_free_mem:
+ input_free_device(input_dev);
+ kfree(as5011);
+
+ return error;
+}
+
+static int __devexit as5011_remove(struct i2c_client *client)
+{
+ struct as5011_device *as5011 = i2c_get_clientdata(client);
+
+ free_irq(as5011->axis_irq, as5011);
+ free_irq(as5011->button_irq, as5011);
+ gpio_free(as5011->button_gpio);
+
+ input_unregister_device(as5011->input_dev);
+ kfree(as5011);
+
+ return 0;
+}
+
+static const struct i2c_device_id as5011_id[] = {
+ { MODULE_DEVICE_ALIAS, 0 },
+ { }
+};
+MODULE_DEVICE_TABLE(i2c, as5011_id);
+
+static struct i2c_driver as5011_driver = {
+ .driver = {
+ .name = "as5011",
+ },
+ .probe = as5011_probe,
+ .remove = __devexit_p(as5011_remove),
+ .id_table = as5011_id,
+};
+
+static int __init as5011_init(void)
+{
+ return i2c_add_driver(&as5011_driver);
+}
+module_init(as5011_init);
+
+static void __exit as5011_exit(void)
+{
+ i2c_del_driver(&as5011_driver);
+}
+module_exit(as5011_exit);
diff --git a/drivers/input/joystick/iforce/Makefile b/drivers/input/joystick/iforce/Makefile
index 74daff49ab6..bc5bda22f15 100644
--- a/drivers/input/joystick/iforce/Makefile
+++ b/drivers/input/joystick/iforce/Makefile
@@ -4,17 +4,8 @@
# By Johann Deneux <johann.deneux@gmail.com>
#
-# Goal definition
-iforce-objs := iforce-ff.o iforce-main.o iforce-packets.o
-
obj-$(CONFIG_JOYSTICK_IFORCE) += iforce.o
-ifeq ($(CONFIG_JOYSTICK_IFORCE_232),y)
- iforce-objs += iforce-serio.o
-endif
-
-ifeq ($(CONFIG_JOYSTICK_IFORCE_USB),y)
- iforce-objs += iforce-usb.o
-endif
-
-EXTRA_CFLAGS = -Werror-implicit-function-declaration
+iforce-y := iforce-ff.o iforce-main.o iforce-packets.o
+iforce-$(CONFIG_JOYSTICK_IFORCE_232) += iforce-serio.o
+iforce-$(CONFIG_JOYSTICK_IFORCE_USB) += iforce-usb.o
diff --git a/drivers/input/joystick/turbografx.c b/drivers/input/joystick/turbografx.c
index d53b9e90023..27b6a3ce18c 100644
--- a/drivers/input/joystick/turbografx.c
+++ b/drivers/input/joystick/turbografx.c
@@ -245,6 +245,7 @@ static struct tgfx __init *tgfx_probe(int parport, int *n_buttons, int n_devs)
goto err_free_tgfx;
}
+ parport_put_port(pp);
return tgfx;
err_free_dev:
diff --git a/drivers/input/joystick/xpad.c b/drivers/input/joystick/xpad.c
index f9fb7fa10af..56abf3d0e91 100644
--- a/drivers/input/joystick/xpad.c
+++ b/drivers/input/joystick/xpad.c
@@ -543,21 +543,25 @@ exit:
static int xpad_init_output(struct usb_interface *intf, struct usb_xpad *xpad)
{
struct usb_endpoint_descriptor *ep_irq_out;
- int error = -ENOMEM;
+ int error;
if (xpad->xtype != XTYPE_XBOX360 && xpad->xtype != XTYPE_XBOX)
return 0;
xpad->odata = usb_alloc_coherent(xpad->udev, XPAD_PKT_LEN,
GFP_KERNEL, &xpad->odata_dma);
- if (!xpad->odata)
+ if (!xpad->odata) {
+ error = -ENOMEM;
goto fail1;
+ }
mutex_init(&xpad->odata_mutex);
xpad->irq_out = usb_alloc_urb(0, GFP_KERNEL);
- if (!xpad->irq_out)
+ if (!xpad->irq_out) {
+ error = -ENOMEM;
goto fail2;
+ }
ep_irq_out = &intf->cur_altsetting->endpoint[1].desc;
usb_fill_int_urb(xpad->irq_out, xpad->udev,
@@ -728,7 +732,7 @@ static void xpad_led_disconnect(struct usb_xpad *xpad)
if (xpad_led) {
led_classdev_unregister(&xpad_led->led_cdev);
- kfree(xpad_led->name);
+ kfree(xpad_led);
}
}
#else
@@ -756,8 +760,9 @@ static void xpad_close(struct input_dev *dev)
{
struct usb_xpad *xpad = input_get_drvdata(dev);
- if(xpad->xtype != XTYPE_XBOX360W)
+ if (xpad->xtype != XTYPE_XBOX360W)
usb_kill_urb(xpad->irq_in);
+
xpad_stop_output(xpad);
}
@@ -789,8 +794,7 @@ static int xpad_probe(struct usb_interface *intf, const struct usb_device_id *id
struct usb_xpad *xpad;
struct input_dev *input_dev;
struct usb_endpoint_descriptor *ep_irq_in;
- int i;
- int error = -ENOMEM;
+ int i, error;
for (i = 0; xpad_device[i].idVendor; i++) {
if ((le16_to_cpu(udev->descriptor.idVendor) == xpad_device[i].idVendor) &&
@@ -800,17 +804,23 @@ static int xpad_probe(struct usb_interface *intf, const struct usb_device_id *id
xpad = kzalloc(sizeof(struct usb_xpad), GFP_KERNEL);
input_dev = input_allocate_device();
- if (!xpad || !input_dev)
+ if (!xpad || !input_dev) {
+ error = -ENOMEM;
goto fail1;
+ }
xpad->idata = usb_alloc_coherent(udev, XPAD_PKT_LEN,
GFP_KERNEL, &xpad->idata_dma);
- if (!xpad->idata)
+ if (!xpad->idata) {
+ error = -ENOMEM;
goto fail1;
+ }
xpad->irq_in = usb_alloc_urb(0, GFP_KERNEL);
- if (!xpad->irq_in)
+ if (!xpad->irq_in) {
+ error = -ENOMEM;
goto fail2;
+ }
xpad->udev = udev;
xpad->mapping = xpad_device[i].mapping;
@@ -887,15 +897,15 @@ static int xpad_probe(struct usb_interface *intf, const struct usb_device_id *id
error = xpad_init_output(intf, xpad);
if (error)
- goto fail2;
+ goto fail3;
error = xpad_init_ff(xpad);
if (error)
- goto fail3;
+ goto fail4;
error = xpad_led_probe(xpad);
if (error)
- goto fail3;
+ goto fail5;
ep_irq_in = &intf->cur_altsetting->endpoint[0].desc;
usb_fill_int_urb(xpad->irq_in, udev,
@@ -907,34 +917,26 @@ static int xpad_probe(struct usb_interface *intf, const struct usb_device_id *id
error = input_register_device(xpad->dev);
if (error)
- goto fail4;
+ goto fail6;
usb_set_intfdata(intf, xpad);
- /*
- * Submit the int URB immediatly rather than waiting for open
- * because we get status messages from the device whether
- * or not any controllers are attached. In fact, it's
- * exactly the message that a controller has arrived that
- * we're waiting for.
- */
if (xpad->xtype == XTYPE_XBOX360W) {
- xpad->irq_in->dev = xpad->udev;
- error = usb_submit_urb(xpad->irq_in, GFP_KERNEL);
- if (error)
- goto fail4;
-
/*
* Setup the message to set the LEDs on the
* controller when it shows up
*/
xpad->bulk_out = usb_alloc_urb(0, GFP_KERNEL);
- if(!xpad->bulk_out)
- goto fail5;
+ if (!xpad->bulk_out) {
+ error = -ENOMEM;
+ goto fail7;
+ }
xpad->bdata = kzalloc(XPAD_PKT_LEN, GFP_KERNEL);
- if(!xpad->bdata)
- goto fail6;
+ if (!xpad->bdata) {
+ error = -ENOMEM;
+ goto fail8;
+ }
xpad->bdata[2] = 0x08;
switch (intf->cur_altsetting->desc.bInterfaceNumber) {
@@ -955,14 +957,31 @@ static int xpad_probe(struct usb_interface *intf, const struct usb_device_id *id
usb_fill_bulk_urb(xpad->bulk_out, udev,
usb_sndbulkpipe(udev, ep_irq_in->bEndpointAddress),
xpad->bdata, XPAD_PKT_LEN, xpad_bulk_out, xpad);
+
+ /*
+ * Submit the int URB immediately rather than waiting for open
+ * because we get status messages from the device whether
+ * or not any controllers are attached. In fact, it's
+ * exactly the message that a controller has arrived that
+ * we're waiting for.
+ */
+ xpad->irq_in->dev = xpad->udev;
+ error = usb_submit_urb(xpad->irq_in, GFP_KERNEL);
+ if (error)
+ goto fail9;
}
return 0;
- fail6: usb_free_urb(xpad->bulk_out);
- fail5: usb_kill_urb(xpad->irq_in);
- fail4: usb_free_urb(xpad->irq_in);
- fail3: xpad_deinit_output(xpad);
+ fail9: kfree(xpad->bdata);
+ fail8: usb_free_urb(xpad->bulk_out);
+ fail7: input_unregister_device(input_dev);
+ input_dev = NULL;
+ fail6: xpad_led_disconnect(xpad);
+ fail5: if (input_dev)
+ input_ff_destroy(input_dev);
+ fail4: xpad_deinit_output(xpad);
+ fail3: usb_free_urb(xpad->irq_in);
fail2: usb_free_coherent(udev, XPAD_PKT_LEN, xpad->idata, xpad->idata_dma);
fail1: input_free_device(input_dev);
kfree(xpad);
@@ -974,21 +993,24 @@ static void xpad_disconnect(struct usb_interface *intf)
{
struct usb_xpad *xpad = usb_get_intfdata (intf);
- usb_set_intfdata(intf, NULL);
- if (xpad) {
- xpad_led_disconnect(xpad);
- input_unregister_device(xpad->dev);
- xpad_deinit_output(xpad);
- if (xpad->xtype == XTYPE_XBOX360W) {
- usb_kill_urb(xpad->bulk_out);
- usb_free_urb(xpad->bulk_out);
- usb_kill_urb(xpad->irq_in);
- }
- usb_free_urb(xpad->irq_in);
- usb_free_coherent(xpad->udev, XPAD_PKT_LEN,
- xpad->idata, xpad->idata_dma);
- kfree(xpad);
+ xpad_led_disconnect(xpad);
+ input_unregister_device(xpad->dev);
+ xpad_deinit_output(xpad);
+
+ if (xpad->xtype == XTYPE_XBOX360W) {
+ usb_kill_urb(xpad->bulk_out);
+ usb_free_urb(xpad->bulk_out);
+ usb_kill_urb(xpad->irq_in);
}
+
+ usb_free_urb(xpad->irq_in);
+ usb_free_coherent(xpad->udev, XPAD_PKT_LEN,
+ xpad->idata, xpad->idata_dma);
+
+ kfree(xpad->bdata);
+ kfree(xpad);
+
+ usb_set_intfdata(intf, NULL);
}
static struct usb_driver xpad_driver = {
@@ -1000,10 +1022,7 @@ static struct usb_driver xpad_driver = {
static int __init usb_xpad_init(void)
{
- int result = usb_register(&xpad_driver);
- if (result == 0)
- printk(KERN_INFO KBUILD_MODNAME ": " DRIVER_DESC "\n");
- return result;
+ return usb_register(&xpad_driver);
}
static void __exit usb_xpad_exit(void)
diff --git a/drivers/input/keyboard/Kconfig b/drivers/input/keyboard/Kconfig
index b8c51b9781d..7b3c0b8fa43 100644
--- a/drivers/input/keyboard/Kconfig
+++ b/drivers/input/keyboard/Kconfig
@@ -12,18 +12,6 @@ menuconfig INPUT_KEYBOARD
if INPUT_KEYBOARD
-config KEYBOARD_AAED2000
- tristate "AAED-2000 keyboard"
- depends on MACH_AAED2000
- select INPUT_POLLDEV
- default y
- help
- Say Y here to enable the keyboard on the Agilent AAED-2000
- development board.
-
- To compile this driver as a module, choose M here: the
- module will be called aaed2000_kbd.
-
config KEYBOARD_ADP5520
tristate "Keypad Support for ADP5520 PMIC"
depends on PMIC_ADP5520
@@ -179,21 +167,39 @@ config KEYBOARD_GPIO
To compile this driver as a module, choose M here: the
module will be called gpio_keys.
+config KEYBOARD_GPIO_POLLED
+ tristate "Polled GPIO buttons"
+ depends on GENERIC_GPIO
+ select INPUT_POLLDEV
+ help
+ This driver implements support for buttons connected
+ to GPIO pins that are not capable of generating interrupts.
+
+ Say Y here if your device has buttons connected
+ directly to such GPIO pins. Your board-specific
+ setup logic must also provide a platform device,
+ with configuration data saying which GPIOs are used.
+
+ To compile this driver as a module, choose M here: the
+ module will be called gpio_keys_polled.
+
config KEYBOARD_TCA6416
- tristate "TCA6416 Keypad Support"
+ tristate "TCA6416/TCA6408A Keypad Support"
depends on I2C
help
This driver implements basic keypad functionality
- for keys connected through TCA6416 IO expander
+ for keys connected through TCA6416/TCA6408A IO expanders.
Say Y here if your device has keys connected to
- TCA6416 IO expander. Your board-specific setup logic
+ TCA6416/TCA6408A IO expander. Your board-specific setup logic
must also provide pin-mask details(of which TCA6416 pins
are used for keypad).
- If enabled the complete TCA6416 device will be managed through
+ If enabled the entire TCA6416 device will be managed through
this driver.
+ To compile this driver as a module, choose M here: the
+ module will be called tca6416_keypad.
config KEYBOARD_MATRIX
tristate "GPIO driven matrix keypad support"
@@ -443,6 +449,25 @@ config KEYBOARD_OMAP4
To compile this driver as a module, choose M here: the
module will be called omap4-keypad.
+config KEYBOARD_SPEAR
+ tristate "ST SPEAR keyboard support"
+ depends on PLAT_SPEAR
+ help
+ Say Y here if you want to use the SPEAR keyboard.
+
+ 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 a34452e8ebe..4e5571b72cd 100644
--- a/drivers/input/keyboard/Makefile
+++ b/drivers/input/keyboard/Makefile
@@ -4,7 +4,6 @@
# Each configuration option enables a list of files.
-obj-$(CONFIG_KEYBOARD_AAED2000) += aaed2000_kbd.o
obj-$(CONFIG_KEYBOARD_ADP5520) += adp5520-keys.o
obj-$(CONFIG_KEYBOARD_ADP5588) += adp5588-keys.o
obj-$(CONFIG_KEYBOARD_AMIGA) += amikbd.o
@@ -14,6 +13,7 @@ obj-$(CONFIG_KEYBOARD_BFIN) += bf54x-keys.o
obj-$(CONFIG_KEYBOARD_DAVINCI) += davinci_keyscan.o
obj-$(CONFIG_KEYBOARD_EP93XX) += ep93xx_keypad.o
obj-$(CONFIG_KEYBOARD_GPIO) += gpio_keys.o
+obj-$(CONFIG_KEYBOARD_GPIO_POLLED) += gpio_keys_polled.o
obj-$(CONFIG_KEYBOARD_TCA6416) += tca6416-keypad.o
obj-$(CONFIG_KEYBOARD_HIL) += hil_kbd.o
obj-$(CONFIG_KEYBOARD_HIL_OLD) += hilkbd.o
@@ -37,9 +37,11 @@ obj-$(CONFIG_KEYBOARD_PXA930_ROTARY) += pxa930_rotary.o
obj-$(CONFIG_KEYBOARD_QT2160) += qt2160.o
obj-$(CONFIG_KEYBOARD_SAMSUNG) += samsung-keypad.o
obj-$(CONFIG_KEYBOARD_SH_KEYSC) += sh_keysc.o
+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_TNETV107X) += tnetv107x-keypad.o
obj-$(CONFIG_KEYBOARD_TWL4030) += twl4030_keypad.o
obj-$(CONFIG_KEYBOARD_XTKBD) += xtkbd.o
diff --git a/drivers/input/keyboard/aaed2000_kbd.c b/drivers/input/keyboard/aaed2000_kbd.c
deleted file mode 100644
index 18222a689a0..00000000000
--- a/drivers/input/keyboard/aaed2000_kbd.c
+++ /dev/null
@@ -1,186 +0,0 @@
-/*
- * Keyboard driver for the AAED-2000 dev board
- *
- * Copyright (c) 2006 Nicolas Bellido Y Ortega
- *
- * Based on corgikbd.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.
- *
- */
-
-#include <linux/delay.h>
-#include <linux/platform_device.h>
-#include <linux/init.h>
-#include <linux/input-polldev.h>
-#include <linux/interrupt.h>
-#include <linux/jiffies.h>
-#include <linux/module.h>
-#include <linux/slab.h>
-
-#include <mach/hardware.h>
-#include <mach/aaed2000.h>
-
-#define KB_ROWS 12
-#define KB_COLS 8
-#define KB_ROWMASK(r) (1 << (r))
-#define SCANCODE(r,c) (((c) * KB_ROWS) + (r))
-#define NR_SCANCODES (KB_COLS * KB_ROWS)
-
-#define SCAN_INTERVAL (50) /* ms */
-#define KB_ACTIVATE_DELAY (20) /* us */
-
-static unsigned char aaedkbd_keycode[NR_SCANCODES] = {
- KEY_9, KEY_0, KEY_MINUS, KEY_EQUAL, KEY_BACKSPACE, 0, KEY_SPACE, KEY_KP6, 0, KEY_KPDOT, 0, 0,
- KEY_K, KEY_M, KEY_O, KEY_DOT, KEY_SLASH, 0, KEY_F, 0, 0, 0, KEY_LEFTSHIFT, 0,
- KEY_I, KEY_P, KEY_LEFTBRACE, KEY_RIGHTBRACE, KEY_BACKSLASH, 0, 0, 0, 0, 0, KEY_RIGHTSHIFT, 0,
- KEY_8, KEY_L, KEY_SEMICOLON, KEY_APOSTROPHE, KEY_ENTER, 0, 0, 0, 0, 0, 0, 0,
- KEY_J, KEY_H, KEY_B, KEY_KP8, KEY_KP4, 0, KEY_C, KEY_D, KEY_S, KEY_A, 0, KEY_CAPSLOCK,
- KEY_Y, KEY_U, KEY_N, KEY_T, 0, 0, KEY_R, KEY_E, KEY_W, KEY_Q, 0, KEY_TAB,
- KEY_7, KEY_6, KEY_G, 0, KEY_5, 0, KEY_4, KEY_3, KEY_2, KEY_1, 0, KEY_GRAVE,
- 0, 0, KEY_COMMA, 0, KEY_KP2, 0, KEY_V, KEY_LEFTALT, KEY_X, KEY_Z, 0, KEY_LEFTCTRL
-};
-
-struct aaedkbd {
- unsigned char keycode[ARRAY_SIZE(aaedkbd_keycode)];
- struct input_polled_dev *poll_dev;
- int kbdscan_state[KB_COLS];
- int kbdscan_count[KB_COLS];
-};
-
-#define KBDSCAN_STABLE_COUNT 2
-
-static void aaedkbd_report_col(struct aaedkbd *aaedkbd,
- unsigned int col, unsigned int rowd)
-{
- unsigned int scancode, pressed;
- unsigned int row;
-
- for (row = 0; row < KB_ROWS; row++) {
- scancode = SCANCODE(row, col);
- pressed = rowd & KB_ROWMASK(row);
-
- input_report_key(aaedkbd->poll_dev->input,
- aaedkbd->keycode[scancode], pressed);
- }
-}
-
-/* Scan the hardware keyboard and push any changes up through the input layer */
-static void aaedkbd_poll(struct input_polled_dev *dev)
-{
- struct aaedkbd *aaedkbd = dev->private;
- unsigned int col, rowd;
-
- col = 0;
- do {
- AAEC_GPIO_KSCAN = col + 8;
- udelay(KB_ACTIVATE_DELAY);
- rowd = AAED_EXT_GPIO & AAED_EGPIO_KBD_SCAN;
-
- if (rowd != aaedkbd->kbdscan_state[col]) {
- aaedkbd->kbdscan_count[col] = 0;
- aaedkbd->kbdscan_state[col] = rowd;
- } else if (++aaedkbd->kbdscan_count[col] >= KBDSCAN_STABLE_COUNT) {
- aaedkbd_report_col(aaedkbd, col, rowd);
- col++;
- }
- } while (col < KB_COLS);
-
- AAEC_GPIO_KSCAN = 0x07;
- input_sync(dev->input);
-}
-
-static int __devinit aaedkbd_probe(struct platform_device *pdev)
-{
- struct aaedkbd *aaedkbd;
- struct input_polled_dev *poll_dev;
- struct input_dev *input_dev;
- int i;
- int error;
-
- aaedkbd = kzalloc(sizeof(struct aaedkbd), GFP_KERNEL);
- poll_dev = input_allocate_polled_device();
- if (!aaedkbd || !poll_dev) {
- error = -ENOMEM;
- goto fail;
- }
-
- platform_set_drvdata(pdev, aaedkbd);
-
- aaedkbd->poll_dev = poll_dev;
- memcpy(aaedkbd->keycode, aaedkbd_keycode, sizeof(aaedkbd->keycode));
-
- poll_dev->private = aaedkbd;
- poll_dev->poll = aaedkbd_poll;
- poll_dev->poll_interval = SCAN_INTERVAL;
-
- input_dev = poll_dev->input;
- input_dev->name = "AAED-2000 Keyboard";
- input_dev->phys = "aaedkbd/input0";
- input_dev->id.bustype = BUS_HOST;
- input_dev->id.vendor = 0x0001;
- input_dev->id.product = 0x0001;
- input_dev->id.version = 0x0100;
- input_dev->dev.parent = &pdev->dev;
-
- input_dev->evbit[0] = BIT_MASK(EV_KEY) | BIT_MASK(EV_REP);
- input_dev->keycode = aaedkbd->keycode;
- input_dev->keycodesize = sizeof(unsigned char);
- input_dev->keycodemax = ARRAY_SIZE(aaedkbd_keycode);
-
- for (i = 0; i < ARRAY_SIZE(aaedkbd_keycode); i++)
- set_bit(aaedkbd->keycode[i], input_dev->keybit);
- clear_bit(0, input_dev->keybit);
-
- error = input_register_polled_device(aaedkbd->poll_dev);
- if (error)
- goto fail;
-
- return 0;
-
- fail: kfree(aaedkbd);
- input_free_polled_device(poll_dev);
- return error;
-}
-
-static int __devexit aaedkbd_remove(struct platform_device *pdev)
-{
- struct aaedkbd *aaedkbd = platform_get_drvdata(pdev);
-
- input_unregister_polled_device(aaedkbd->poll_dev);
- input_free_polled_device(aaedkbd->poll_dev);
- kfree(aaedkbd);
-
- return 0;
-}
-
-/* work with hotplug and coldplug */
-MODULE_ALIAS("platform:aaed2000-keyboard");
-
-static struct platform_driver aaedkbd_driver = {
- .probe = aaedkbd_probe,
- .remove = __devexit_p(aaedkbd_remove),
- .driver = {
- .name = "aaed2000-keyboard",
- .owner = THIS_MODULE,
- },
-};
-
-static int __init aaedkbd_init(void)
-{
- return platform_driver_register(&aaedkbd_driver);
-}
-
-static void __exit aaedkbd_exit(void)
-{
- platform_driver_unregister(&aaedkbd_driver);
-}
-
-module_init(aaedkbd_init);
-module_exit(aaedkbd_exit);
-
-MODULE_AUTHOR("Nicolas Bellido Y Ortega");
-MODULE_DESCRIPTION("AAED-2000 Keyboard Driver");
-MODULE_LICENSE("GPL v2");
diff --git a/drivers/input/keyboard/gpio_keys_polled.c b/drivers/input/keyboard/gpio_keys_polled.c
new file mode 100644
index 00000000000..4c17aff2065
--- /dev/null
+++ b/drivers/input/keyboard/gpio_keys_polled.c
@@ -0,0 +1,261 @@
+/*
+ * Driver for buttons on GPIO lines not capable of generating interrupts
+ *
+ * Copyright (C) 2007-2010 Gabor Juhos <juhosg@openwrt.org>
+ * Copyright (C) 2010 Nuno Goncalves <nunojpg@gmail.com>
+ *
+ * This file was based on: /drivers/input/misc/cobalt_btns.c
+ * Copyright (C) 2007 Yoichi Yuasa <yoichi_yuasa@tripeaks.co.jp>
+ *
+ * also was based on: /drivers/input/keyboard/gpio_keys.c
+ * Copyright 2005 Phil Blundell
+ *
+ * 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.
+ */
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/slab.h>
+#include <linux/input.h>
+#include <linux/input-polldev.h>
+#include <linux/ioport.h>
+#include <linux/platform_device.h>
+#include <linux/gpio.h>
+#include <linux/gpio_keys.h>
+
+#define DRV_NAME "gpio-keys-polled"
+
+struct gpio_keys_button_data {
+ int last_state;
+ int count;
+ int threshold;
+ int can_sleep;
+};
+
+struct gpio_keys_polled_dev {
+ struct input_polled_dev *poll_dev;
+ struct device *dev;
+ struct gpio_keys_platform_data *pdata;
+ struct gpio_keys_button_data data[0];
+};
+
+static void gpio_keys_polled_check_state(struct input_dev *input,
+ struct gpio_keys_button *button,
+ struct gpio_keys_button_data *bdata)
+{
+ int state;
+
+ if (bdata->can_sleep)
+ state = !!gpio_get_value_cansleep(button->gpio);
+ else
+ state = !!gpio_get_value(button->gpio);
+
+ if (state != bdata->last_state) {
+ unsigned int type = button->type ?: EV_KEY;
+
+ input_event(input, type, button->code,
+ !!(state ^ button->active_low));
+ input_sync(input);
+ bdata->count = 0;
+ bdata->last_state = state;
+ }
+}
+
+static void gpio_keys_polled_poll(struct input_polled_dev *dev)
+{
+ struct gpio_keys_polled_dev *bdev = dev->private;
+ struct gpio_keys_platform_data *pdata = bdev->pdata;
+ struct input_dev *input = dev->input;
+ int i;
+
+ for (i = 0; i < bdev->pdata->nbuttons; i++) {
+ struct gpio_keys_button_data *bdata = &bdev->data[i];
+
+ if (bdata->count < bdata->threshold)
+ bdata->count++;
+ else
+ gpio_keys_polled_check_state(input, &pdata->buttons[i],
+ bdata);
+ }
+}
+
+static void gpio_keys_polled_open(struct input_polled_dev *dev)
+{
+ struct gpio_keys_polled_dev *bdev = dev->private;
+ struct gpio_keys_platform_data *pdata = bdev->pdata;
+
+ if (pdata->enable)
+ pdata->enable(bdev->dev);
+}
+
+static void gpio_keys_polled_close(struct input_polled_dev *dev)
+{
+ struct gpio_keys_polled_dev *bdev = dev->private;
+ struct gpio_keys_platform_data *pdata = bdev->pdata;
+
+ if (pdata->disable)
+ pdata->disable(bdev->dev);
+}
+
+static int __devinit gpio_keys_polled_probe(struct platform_device *pdev)
+{
+ struct gpio_keys_platform_data *pdata = pdev->dev.platform_data;
+ struct device *dev = &pdev->dev;
+ struct gpio_keys_polled_dev *bdev;
+ struct input_polled_dev *poll_dev;
+ struct input_dev *input;
+ int error;
+ int i;
+
+ if (!pdata || !pdata->poll_interval)
+ return -EINVAL;
+
+ bdev = kzalloc(sizeof(struct gpio_keys_polled_dev) +
+ pdata->nbuttons * sizeof(struct gpio_keys_button_data),
+ GFP_KERNEL);
+ if (!bdev) {
+ dev_err(dev, "no memory for private data\n");
+ return -ENOMEM;
+ }
+
+ poll_dev = input_allocate_polled_device();
+ if (!poll_dev) {
+ dev_err(dev, "no memory for polled device\n");
+ error = -ENOMEM;
+ goto err_free_bdev;
+ }
+
+ poll_dev->private = bdev;
+ poll_dev->poll = gpio_keys_polled_poll;
+ poll_dev->poll_interval = pdata->poll_interval;
+ poll_dev->open = gpio_keys_polled_open;
+ poll_dev->close = gpio_keys_polled_close;
+
+ input = poll_dev->input;
+
+ input->evbit[0] = BIT(EV_KEY);
+ input->name = pdev->name;
+ input->phys = DRV_NAME"/input0";
+ input->dev.parent = &pdev->dev;
+
+ input->id.bustype = BUS_HOST;
+ input->id.vendor = 0x0001;
+ input->id.product = 0x0001;
+ input->id.version = 0x0100;
+
+ for (i = 0; i < pdata->nbuttons; i++) {
+ struct gpio_keys_button *button = &pdata->buttons[i];
+ struct gpio_keys_button_data *bdata = &bdev->data[i];
+ unsigned int gpio = button->gpio;
+ unsigned int type = button->type ?: EV_KEY;
+
+ if (button->wakeup) {
+ dev_err(dev, DRV_NAME " does not support wakeup\n");
+ error = -EINVAL;
+ goto err_free_gpio;
+ }
+
+ error = gpio_request(gpio,
+ button->desc ? button->desc : DRV_NAME);
+ if (error) {
+ dev_err(dev, "unable to claim gpio %u, err=%d\n",
+ gpio, error);
+ goto err_free_gpio;
+ }
+
+ error = gpio_direction_input(gpio);
+ if (error) {
+ dev_err(dev,
+ "unable to set direction on gpio %u, err=%d\n",
+ gpio, error);
+ goto err_free_gpio;
+ }
+
+ bdata->can_sleep = gpio_cansleep(gpio);
+ bdata->last_state = -1;
+ bdata->threshold = DIV_ROUND_UP(button->debounce_interval,
+ pdata->poll_interval);
+
+ input_set_capability(input, type, button->code);
+ }
+
+ bdev->poll_dev = poll_dev;
+ bdev->dev = dev;
+ bdev->pdata = pdata;
+ platform_set_drvdata(pdev, bdev);
+
+ error = input_register_polled_device(poll_dev);
+ if (error) {
+ dev_err(dev, "unable to register polled device, err=%d\n",
+ error);
+ goto err_free_gpio;
+ }
+
+ /* report initial state of the buttons */
+ for (i = 0; i < pdata->nbuttons; i++)
+ gpio_keys_polled_check_state(input, &pdata->buttons[i],
+ &bdev->data[i]);
+
+ return 0;
+
+err_free_gpio:
+ while (--i >= 0)
+ gpio_free(pdata->buttons[i].gpio);
+
+ input_free_polled_device(poll_dev);
+
+err_free_bdev:
+ kfree(bdev);
+
+ platform_set_drvdata(pdev, NULL);
+ return error;
+}
+
+static int __devexit gpio_keys_polled_remove(struct platform_device *pdev)
+{
+ struct gpio_keys_polled_dev *bdev = platform_get_drvdata(pdev);
+ struct gpio_keys_platform_data *pdata = bdev->pdata;
+ int i;
+
+ input_unregister_polled_device(bdev->poll_dev);
+
+ for (i = 0; i < pdata->nbuttons; i++)
+ gpio_free(pdata->buttons[i].gpio);
+
+ input_free_polled_device(bdev->poll_dev);
+
+ kfree(bdev);
+ platform_set_drvdata(pdev, NULL);
+
+ return 0;
+}
+
+static struct platform_driver gpio_keys_polled_driver = {
+ .probe = gpio_keys_polled_probe,
+ .remove = __devexit_p(gpio_keys_polled_remove),
+ .driver = {
+ .name = DRV_NAME,
+ .owner = THIS_MODULE,
+ },
+};
+
+static int __init gpio_keys_polled_init(void)
+{
+ return platform_driver_register(&gpio_keys_polled_driver);
+}
+
+static void __exit gpio_keys_polled_exit(void)
+{
+ platform_driver_unregister(&gpio_keys_polled_driver);
+}
+
+module_init(gpio_keys_polled_init);
+module_exit(gpio_keys_polled_exit);
+
+MODULE_LICENSE("GPL v2");
+MODULE_AUTHOR("Gabor Juhos <juhosg@openwrt.org>");
+MODULE_DESCRIPTION("Polled GPIO Buttons driver");
+MODULE_ALIAS("platform:" DRV_NAME);
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/spear-keyboard.c b/drivers/input/keyboard/spear-keyboard.c
new file mode 100644
index 00000000000..bee03d64c45
--- /dev/null
+++ b/drivers/input/keyboard/spear-keyboard.c
@@ -0,0 +1,344 @@
+/*
+ * SPEAr Keyboard Driver
+ * Based on omap-keypad driver
+ *
+ * Copyright (C) 2010 ST Microelectronics
+ * Rajeev Kumar<rajeev-dlh.kumar@st.com>
+ *
+ * This file is licensed under the terms of the GNU General Public
+ * License version 2. This program is licensed "as is" without any
+ * warranty of any kind, whether express or implied.
+ */
+
+#include <linux/clk.h>
+#include <linux/errno.h>
+#include <linux/init.h>
+#include <linux/interrupt.h>
+#include <linux/input.h>
+#include <linux/io.h>
+#include <linux/irq.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/platform_device.h>
+#include <linux/pm_wakeup.h>
+#include <linux/slab.h>
+#include <linux/types.h>
+#include <plat/keyboard.h>
+
+/* Keyboard Registers */
+#define MODE_REG 0x00 /* 16 bit reg */
+#define STATUS_REG 0x0C /* 2 bit reg */
+#define DATA_REG 0x10 /* 8 bit reg */
+#define INTR_MASK 0x54
+
+/* Register Values */
+/*
+ * pclk freq mask = (APB FEQ -1)= 82 MHZ.Programme bit 15-9 in mode
+ * control register as 1010010(82MHZ)
+ */
+#define PCLK_FREQ_MSK 0xA400 /* 82 MHz */
+#define START_SCAN 0x0100
+#define SCAN_RATE_10 0x0000
+#define SCAN_RATE_20 0x0004
+#define SCAN_RATE_40 0x0008
+#define SCAN_RATE_80 0x000C
+#define MODE_KEYBOARD 0x0002
+#define DATA_AVAIL 0x2
+
+#define KEY_MASK 0xFF000000
+#define KEY_VALUE 0x00FFFFFF
+#define ROW_MASK 0xF0
+#define COLUMN_MASK 0x0F
+#define ROW_SHIFT 4
+
+struct spear_kbd {
+ struct input_dev *input;
+ struct resource *res;
+ void __iomem *io_base;
+ struct clk *clk;
+ unsigned int irq;
+ unsigned short last_key;
+ unsigned short keycodes[256];
+};
+
+static irqreturn_t spear_kbd_interrupt(int irq, void *dev_id)
+{
+ struct spear_kbd *kbd = dev_id;
+ struct input_dev *input = kbd->input;
+ unsigned int key;
+ u8 sts, val;
+
+ sts = readb(kbd->io_base + STATUS_REG);
+ if (sts & DATA_AVAIL)
+ return IRQ_NONE;
+
+ if (kbd->last_key != KEY_RESERVED) {
+ input_report_key(input, kbd->last_key, 0);
+ kbd->last_key = KEY_RESERVED;
+ }
+
+ /* following reads active (row, col) pair */
+ val = readb(kbd->io_base + DATA_REG);
+ key = kbd->keycodes[val];
+
+ input_event(input, EV_MSC, MSC_SCAN, val);
+ input_report_key(input, key, 1);
+ input_sync(input);
+
+ kbd->last_key = key;
+
+ /* clear interrupt */
+ writeb(0, kbd->io_base + STATUS_REG);
+
+ return IRQ_HANDLED;
+}
+
+static int spear_kbd_open(struct input_dev *dev)
+{
+ struct spear_kbd *kbd = input_get_drvdata(dev);
+ int error;
+ u16 val;
+
+ kbd->last_key = KEY_RESERVED;
+
+ error = clk_enable(kbd->clk);
+ if (error)
+ return error;
+
+ /* program keyboard */
+ val = SCAN_RATE_80 | MODE_KEYBOARD | PCLK_FREQ_MSK;
+ writew(val, kbd->io_base + MODE_REG);
+ writeb(1, kbd->io_base + STATUS_REG);
+
+ /* start key scan */
+ val = readw(kbd->io_base + MODE_REG);
+ val |= START_SCAN;
+ writew(val, kbd->io_base + MODE_REG);
+
+ return 0;
+}
+
+static void spear_kbd_close(struct input_dev *dev)
+{
+ struct spear_kbd *kbd = input_get_drvdata(dev);
+ u16 val;
+
+ /* stop key scan */
+ val = readw(kbd->io_base + MODE_REG);
+ val &= ~START_SCAN;
+ writew(val, kbd->io_base + MODE_REG);
+
+ clk_disable(kbd->clk);
+
+ kbd->last_key = KEY_RESERVED;
+}
+
+static int __devinit spear_kbd_probe(struct platform_device *pdev)
+{
+ const struct kbd_platform_data *pdata = pdev->dev.platform_data;
+ const struct matrix_keymap_data *keymap;
+ struct spear_kbd *kbd;
+ struct input_dev *input_dev;
+ struct resource *res;
+ int irq;
+ int error;
+
+ if (!pdata) {
+ dev_err(&pdev->dev, "Invalid platform data\n");
+ return -EINVAL;
+ }
+
+ keymap = pdata->keymap;
+ if (!keymap) {
+ dev_err(&pdev->dev, "no keymap defined\n");
+ return -EINVAL;
+ }
+
+ res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+ if (!res) {
+ dev_err(&pdev->dev, "no keyboard resource defined\n");
+ return -EBUSY;
+ }
+
+ irq = platform_get_irq(pdev, 0);
+ if (irq < 0) {
+ dev_err(&pdev->dev, "not able to get irq for the device\n");
+ return irq;
+ }
+
+ kbd = kzalloc(sizeof(*kbd), GFP_KERNEL);
+ input_dev = input_allocate_device();
+ if (!kbd || !input_dev) {
+ dev_err(&pdev->dev, "out of memory\n");
+ error = -ENOMEM;
+ goto err_free_mem;
+ }
+
+ kbd->input = input_dev;
+ kbd->irq = irq;
+ kbd->res = request_mem_region(res->start, resource_size(res),
+ pdev->name);
+ if (!kbd->res) {
+ dev_err(&pdev->dev, "keyboard region already claimed\n");
+ error = -EBUSY;
+ goto err_free_mem;
+ }
+
+ kbd->io_base = ioremap(res->start, resource_size(res));
+ if (!kbd->io_base) {
+ dev_err(&pdev->dev, "ioremap failed for kbd_region\n");
+ error = -ENOMEM;
+ goto err_release_mem_region;
+ }
+
+ kbd->clk = clk_get(&pdev->dev, NULL);
+ if (IS_ERR(kbd->clk)) {
+ error = PTR_ERR(kbd->clk);
+ goto err_iounmap;
+ }
+
+ input_dev->name = "Spear Keyboard";
+ input_dev->phys = "keyboard/input0";
+ input_dev->dev.parent = &pdev->dev;
+ input_dev->id.bustype = BUS_HOST;
+ input_dev->id.vendor = 0x0001;
+ input_dev->id.product = 0x0001;
+ input_dev->id.version = 0x0100;
+ input_dev->open = spear_kbd_open;
+ input_dev->close = spear_kbd_close;
+
+ __set_bit(EV_KEY, input_dev->evbit);
+ if (pdata->rep)
+ __set_bit(EV_REP, input_dev->evbit);
+ input_set_capability(input_dev, EV_MSC, MSC_SCAN);
+
+ input_dev->keycode = kbd->keycodes;
+ input_dev->keycodesize = sizeof(kbd->keycodes[0]);
+ input_dev->keycodemax = ARRAY_SIZE(kbd->keycodes);
+
+ matrix_keypad_build_keymap(keymap, ROW_SHIFT,
+ input_dev->keycode, input_dev->keybit);
+
+ input_set_drvdata(input_dev, kbd);
+
+ error = request_irq(irq, spear_kbd_interrupt, 0, "keyboard", kbd);
+ if (error) {
+ dev_err(&pdev->dev, "request_irq fail\n");
+ goto err_put_clk;
+ }
+
+ error = input_register_device(input_dev);
+ if (error) {
+ dev_err(&pdev->dev, "Unable to register keyboard device\n");
+ goto err_free_irq;
+ }
+
+ device_init_wakeup(&pdev->dev, 1);
+ platform_set_drvdata(pdev, kbd);
+
+ return 0;
+
+err_free_irq:
+ free_irq(kbd->irq, kbd);
+err_put_clk:
+ clk_put(kbd->clk);
+err_iounmap:
+ iounmap(kbd->io_base);
+err_release_mem_region:
+ release_mem_region(res->start, resource_size(res));
+err_free_mem:
+ input_free_device(input_dev);
+ kfree(kbd);
+
+ return error;
+}
+
+static int __devexit spear_kbd_remove(struct platform_device *pdev)
+{
+ struct spear_kbd *kbd = platform_get_drvdata(pdev);
+
+ free_irq(kbd->irq, kbd);
+ input_unregister_device(kbd->input);
+ clk_put(kbd->clk);
+ iounmap(kbd->io_base);
+ release_mem_region(kbd->res->start, resource_size(kbd->res));
+ kfree(kbd);
+
+ device_init_wakeup(&pdev->dev, 1);
+ platform_set_drvdata(pdev, NULL);
+
+ return 0;
+}
+
+#ifdef CONFIG_PM
+static int spear_kbd_suspend(struct device *dev)
+{
+ struct platform_device *pdev = to_platform_device(dev);
+ struct spear_kbd *kbd = platform_get_drvdata(pdev);
+ struct input_dev *input_dev = kbd->input;
+
+ mutex_lock(&input_dev->mutex);
+
+ if (input_dev->users)
+ clk_enable(kbd->clk);
+
+ if (device_may_wakeup(&pdev->dev))
+ enable_irq_wake(kbd->irq);
+
+ mutex_unlock(&input_dev->mutex);
+
+ return 0;
+}
+
+static int spear_kbd_resume(struct device *dev)
+{
+ struct platform_device *pdev = to_platform_device(dev);
+ struct spear_kbd *kbd = platform_get_drvdata(pdev);
+ struct input_dev *input_dev = kbd->input;
+
+ mutex_lock(&input_dev->mutex);
+
+ if (device_may_wakeup(&pdev->dev))
+ disable_irq_wake(kbd->irq);
+
+ if (input_dev->users)
+ clk_enable(kbd->clk);
+
+ mutex_unlock(&input_dev->mutex);
+
+ return 0;
+}
+
+static const struct dev_pm_ops spear_kbd_pm_ops = {
+ .suspend = spear_kbd_suspend,
+ .resume = spear_kbd_resume,
+};
+#endif
+
+static struct platform_driver spear_kbd_driver = {
+ .probe = spear_kbd_probe,
+ .remove = __devexit_p(spear_kbd_remove),
+ .driver = {
+ .name = "keyboard",
+ .owner = THIS_MODULE,
+#ifdef CONFIG_PM
+ .pm = &spear_kbd_pm_ops,
+#endif
+ },
+};
+
+static int __init spear_kbd_init(void)
+{
+ return platform_driver_register(&spear_kbd_driver);
+}
+module_init(spear_kbd_init);
+
+static void __exit spear_kbd_exit(void)
+{
+ platform_driver_unregister(&spear_kbd_driver);
+}
+module_exit(spear_kbd_exit);
+
+MODULE_AUTHOR("Rajeev Kumar");
+MODULE_DESCRIPTION("SPEAr Keyboard Driver");
+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..dbbe761778d
--- /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
+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;
+}
+
+static const SIMPLE_DEV_PM_OPS(tc3589x_keypad_dev_pm_ops,
+ tc3589x_keypad_suspend, tc3589x_keypad_resume);
+#endif
+
+static struct platform_driver tc3589x_keypad_driver = {
+ .driver.name = "tc3589x-keypad",
+ .driver.owner = THIS_MODULE,
+#ifdef CONFIG_PM
+ .driver.pm = &tc3589x_keypad_dev_pm_ops,
+#endif
+ .probe = tc3589x_keypad_probe,
+ .remove = __devexit_p(tc3589x_keypad_remove),
+};
+
+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 00137bebcf9..800fbccf1f0 100644
--- a/drivers/input/keyboard/tca6416-keypad.c
+++ b/drivers/input/keyboard/tca6416-keypad.c
@@ -29,6 +29,7 @@
static const struct i2c_device_id tca6416_id[] = {
{ "tca6416-keys", 16, },
+ { "tca6408-keys", 8, },
{ }
};
MODULE_DEVICE_TABLE(i2c, tca6416_id);
@@ -46,8 +47,9 @@ struct tca6416_keypad_chip {
struct i2c_client *client;
struct input_dev *input;
struct delayed_work dwork;
- u16 pinmask;
+ int io_size;
int irqnum;
+ u16 pinmask;
bool use_polling;
struct tca6416_button buttons[0];
};
@@ -56,7 +58,9 @@ static int tca6416_write_reg(struct tca6416_keypad_chip *chip, int reg, u16 val)
{
int error;
- error = i2c_smbus_write_word_data(chip->client, reg << 1, val);
+ error = chip->io_size > 8 ?
+ i2c_smbus_write_word_data(chip->client, reg << 1, val) :
+ i2c_smbus_write_byte_data(chip->client, reg, val);
if (error < 0) {
dev_err(&chip->client->dev,
"%s failed, reg: %d, val: %d, error: %d\n",
@@ -71,7 +75,9 @@ static int tca6416_read_reg(struct tca6416_keypad_chip *chip, int reg, u16 *val)
{
int retval;
- retval = i2c_smbus_read_word_data(chip->client, reg << 1);
+ retval = chip->io_size > 8 ?
+ i2c_smbus_read_word_data(chip->client, reg << 1) :
+ i2c_smbus_read_byte_data(chip->client, reg);
if (retval < 0) {
dev_err(&chip->client->dev, "%s failed, reg: %d, error: %d\n",
__func__, reg, retval);
@@ -224,6 +230,7 @@ static int __devinit tca6416_keypad_probe(struct i2c_client *client,
chip->client = client;
chip->input = input;
+ chip->io_size = id->driver_data;
chip->pinmask = pdata->pinmask;
chip->use_polling = pdata->use_polling;
diff --git a/drivers/input/misc/Kconfig b/drivers/input/misc/Kconfig
index b99b8cbde02..b0c6772851a 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
@@ -448,4 +430,28 @@ config INPUT_ADXL34X_SPI
To compile this driver as a module, choose M here: the
module will be called adxl34x-spi.
+config INPUT_CMA3000
+ tristate "VTI CMA3000 Tri-axis accelerometer"
+ help
+ Say Y here if you want to use VTI CMA3000_D0x Accelerometer
+ driver
+
+ This driver currently only supports I2C interface to the
+ controller. Also select the I2C method.
+
+ If unsure, say N
+
+ To compile this driver as a module, choose M here: the
+ module will be called cma3000_d0x.
+
+config INPUT_CMA3000_I2C
+ tristate "Support I2C bus connection"
+ depends on INPUT_CMA3000 && I2C
+ help
+ Say Y here if you want to use VTI CMA3000_D0x Accelerometer
+ through I2C interface.
+
+ To compile this driver as a module, choose M here: the
+ module will be called cma3000_d0x_i2c.
+
endif
diff --git a/drivers/input/misc/Makefile b/drivers/input/misc/Makefile
index 1fe1f6c8b73..9b4797112c9 100644
--- a/drivers/input/misc/Makefile
+++ b/drivers/input/misc/Makefile
@@ -18,6 +18,8 @@ obj-$(CONFIG_INPUT_ATI_REMOTE2) += ati_remote2.o
obj-$(CONFIG_INPUT_ATLAS_BTNS) += atlas_btns.o
obj-$(CONFIG_INPUT_BFIN_ROTARY) += bfin_rotary.o
obj-$(CONFIG_INPUT_CM109) += cm109.o
+obj-$(CONFIG_INPUT_CMA3000) += cma3000_d0x.o
+obj-$(CONFIG_INPUT_CMA3000_I2C) += cma3000_d0x_i2c.o
obj-$(CONFIG_INPUT_COBALT_BTNS) += cobalt_btns.o
obj-$(CONFIG_INPUT_DM355EVM) += dm355evm_keys.o
obj-$(CONFIG_HP_SDC_RTC) += hp_sdc_rtc.o
@@ -38,7 +40,6 @@ 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_YEALINK) += yealink.o
diff --git a/drivers/input/misc/cma3000_d0x.c b/drivers/input/misc/cma3000_d0x.c
new file mode 100644
index 00000000000..1633b634226
--- /dev/null
+++ b/drivers/input/misc/cma3000_d0x.c
@@ -0,0 +1,398 @@
+/*
+ * VTI CMA3000_D0x Accelerometer driver
+ *
+ * Copyright (C) 2010 Texas Instruments
+ * Author: Hemanth V <hemanthv@ti.com>
+ *
+ * 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.
+ *
+ * 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, see <http://www.gnu.org/licenses/>.
+ */
+
+#include <linux/types.h>
+#include <linux/interrupt.h>
+#include <linux/delay.h>
+#include <linux/slab.h>
+#include <linux/input.h>
+#include <linux/input/cma3000.h>
+
+#include "cma3000_d0x.h"
+
+#define CMA3000_WHOAMI 0x00
+#define CMA3000_REVID 0x01
+#define CMA3000_CTRL 0x02
+#define CMA3000_STATUS 0x03
+#define CMA3000_RSTR 0x04
+#define CMA3000_INTSTATUS 0x05
+#define CMA3000_DOUTX 0x06
+#define CMA3000_DOUTY 0x07
+#define CMA3000_DOUTZ 0x08
+#define CMA3000_MDTHR 0x09
+#define CMA3000_MDFFTMR 0x0A
+#define CMA3000_FFTHR 0x0B
+
+#define CMA3000_RANGE2G (1 << 7)
+#define CMA3000_RANGE8G (0 << 7)
+#define CMA3000_BUSI2C (0 << 4)
+#define CMA3000_MODEMASK (7 << 1)
+#define CMA3000_GRANGEMASK (1 << 7)
+
+#define CMA3000_STATUS_PERR 1
+#define CMA3000_INTSTATUS_FFDET (1 << 2)
+
+/* Settling time delay in ms */
+#define CMA3000_SETDELAY 30
+
+/* Delay for clearing interrupt in us */
+#define CMA3000_INTDELAY 44
+
+
+/*
+ * Bit weights in mg for bit 0, other bits need
+ * multipy factor 2^n. Eight bit is the sign bit.
+ */
+#define BIT_TO_2G 18
+#define BIT_TO_8G 71
+
+struct cma3000_accl_data {
+ const struct cma3000_bus_ops *bus_ops;
+ const struct cma3000_platform_data *pdata;
+
+ struct device *dev;
+ struct input_dev *input_dev;
+
+ int bit_to_mg;
+ int irq;
+
+ int g_range;
+ u8 mode;
+
+ struct mutex mutex;
+ bool opened;
+ bool suspended;
+};
+
+#define CMA3000_READ(data, reg, msg) \
+ (data->bus_ops->read(data->dev, reg, msg))
+#define CMA3000_SET(data, reg, val, msg) \
+ ((data)->bus_ops->write(data->dev, reg, val, msg))
+
+/*
+ * Conversion for each of the eight modes to g, depending
+ * on G range i.e 2G or 8G. Some modes always operate in
+ * 8G.
+ */
+
+static int mode_to_mg[8][2] = {
+ { 0, 0 },
+ { BIT_TO_8G, BIT_TO_2G },
+ { BIT_TO_8G, BIT_TO_2G },
+ { BIT_TO_8G, BIT_TO_8G },
+ { BIT_TO_8G, BIT_TO_8G },
+ { BIT_TO_8G, BIT_TO_2G },
+ { BIT_TO_8G, BIT_TO_2G },
+ { 0, 0},
+};
+
+static void decode_mg(struct cma3000_accl_data *data, int *datax,
+ int *datay, int *dataz)
+{
+ /* Data in 2's complement, convert to mg */
+ *datax = ((s8)*datax) * data->bit_to_mg;
+ *datay = ((s8)*datay) * data->bit_to_mg;
+ *dataz = ((s8)*dataz) * data->bit_to_mg;
+}
+
+static irqreturn_t cma3000_thread_irq(int irq, void *dev_id)
+{
+ struct cma3000_accl_data *data = dev_id;
+ int datax, datay, dataz;
+ u8 ctrl, mode, range, intr_status;
+
+ intr_status = CMA3000_READ(data, CMA3000_INTSTATUS, "interrupt status");
+ if (intr_status < 0)
+ return IRQ_NONE;
+
+ /* Check if free fall is detected, report immediately */
+ if (intr_status & CMA3000_INTSTATUS_FFDET) {
+ input_report_abs(data->input_dev, ABS_MISC, 1);
+ input_sync(data->input_dev);
+ } else {
+ input_report_abs(data->input_dev, ABS_MISC, 0);
+ }
+
+ datax = CMA3000_READ(data, CMA3000_DOUTX, "X");
+ datay = CMA3000_READ(data, CMA3000_DOUTY, "Y");
+ dataz = CMA3000_READ(data, CMA3000_DOUTZ, "Z");
+
+ ctrl = CMA3000_READ(data, CMA3000_CTRL, "ctrl");
+ mode = (ctrl & CMA3000_MODEMASK) >> 1;
+ range = (ctrl & CMA3000_GRANGEMASK) >> 7;
+
+ data->bit_to_mg = mode_to_mg[mode][range];
+
+ /* Interrupt not for this device */
+ if (data->bit_to_mg == 0)
+ return IRQ_NONE;
+
+ /* Decode register values to milli g */
+ decode_mg(data, &datax, &datay, &dataz);
+
+ input_report_abs(data->input_dev, ABS_X, datax);
+ input_report_abs(data->input_dev, ABS_Y, datay);
+ input_report_abs(data->input_dev, ABS_Z, dataz);
+ input_sync(data->input_dev);
+
+ return IRQ_HANDLED;
+}
+
+static int cma3000_reset(struct cma3000_accl_data *data)
+{
+ int val;
+
+ /* Reset sequence */
+ CMA3000_SET(data, CMA3000_RSTR, 0x02, "Reset");
+ CMA3000_SET(data, CMA3000_RSTR, 0x0A, "Reset");
+ CMA3000_SET(data, CMA3000_RSTR, 0x04, "Reset");
+
+ /* Settling time delay */
+ mdelay(10);
+
+ val = CMA3000_READ(data, CMA3000_STATUS, "Status");
+ if (val < 0) {
+ dev_err(data->dev, "Reset failed\n");
+ return val;
+ }
+
+ if (val & CMA3000_STATUS_PERR) {
+ dev_err(data->dev, "Parity Error\n");
+ return -EIO;
+ }
+
+ return 0;
+}
+
+static int cma3000_poweron(struct cma3000_accl_data *data)
+{
+ const struct cma3000_platform_data *pdata = data->pdata;
+ u8 ctrl = 0;
+ int ret;
+
+ if (data->g_range == CMARANGE_2G) {
+ ctrl = (data->mode << 1) | CMA3000_RANGE2G;
+ } else if (data->g_range == CMARANGE_8G) {
+ ctrl = (data->mode << 1) | CMA3000_RANGE8G;
+ } else {
+ dev_info(data->dev,
+ "Invalid G range specified, assuming 8G\n");
+ ctrl = (data->mode << 1) | CMA3000_RANGE8G;
+ }
+
+ ctrl |= data->bus_ops->ctrl_mod;
+
+ CMA3000_SET(data, CMA3000_MDTHR, pdata->mdthr,
+ "Motion Detect Threshold");
+ CMA3000_SET(data, CMA3000_MDFFTMR, pdata->mdfftmr,
+ "Time register");
+ CMA3000_SET(data, CMA3000_FFTHR, pdata->ffthr,
+ "Free fall threshold");
+ ret = CMA3000_SET(data, CMA3000_CTRL, ctrl, "Mode setting");
+ if (ret < 0)
+ return -EIO;
+
+ msleep(CMA3000_SETDELAY);
+
+ return 0;
+}
+
+static int cma3000_poweroff(struct cma3000_accl_data *data)
+{
+ int ret;
+
+ ret = CMA3000_SET(data, CMA3000_CTRL, CMAMODE_POFF, "Mode setting");
+ msleep(CMA3000_SETDELAY);
+
+ return ret;
+}
+
+static int cma3000_open(struct input_dev *input_dev)
+{
+ struct cma3000_accl_data *data = input_get_drvdata(input_dev);
+
+ mutex_lock(&data->mutex);
+
+ if (!data->suspended)
+ cma3000_poweron(data);
+
+ data->opened = true;
+
+ mutex_unlock(&data->mutex);
+
+ return 0;
+}
+
+static void cma3000_close(struct input_dev *input_dev)
+{
+ struct cma3000_accl_data *data = input_get_drvdata(input_dev);
+
+ mutex_lock(&data->mutex);
+
+ if (!data->suspended)
+ cma3000_poweroff(data);
+
+ data->opened = false;
+
+ mutex_unlock(&data->mutex);
+}
+
+void cma3000_suspend(struct cma3000_accl_data *data)
+{
+ mutex_lock(&data->mutex);
+
+ if (!data->suspended && data->opened)
+ cma3000_poweroff(data);
+
+ data->suspended = true;
+
+ mutex_unlock(&data->mutex);
+}
+EXPORT_SYMBOL(cma3000_suspend);
+
+
+void cma3000_resume(struct cma3000_accl_data *data)
+{
+ mutex_lock(&data->mutex);
+
+ if (data->suspended && data->opened)
+ cma3000_poweron(data);
+
+ data->suspended = false;
+
+ mutex_unlock(&data->mutex);
+}
+EXPORT_SYMBOL(cma3000_resume);
+
+struct cma3000_accl_data *cma3000_init(struct device *dev, int irq,
+ const struct cma3000_bus_ops *bops)
+{
+ const struct cma3000_platform_data *pdata = dev->platform_data;
+ struct cma3000_accl_data *data;
+ struct input_dev *input_dev;
+ int rev;
+ int error;
+
+ if (!pdata) {
+ dev_err(dev, "platform data not found\n");
+ error = -EINVAL;
+ goto err_out;
+ }
+
+
+ /* if no IRQ return error */
+ if (irq == 0) {
+ error = -EINVAL;
+ goto err_out;
+ }
+
+ data = kzalloc(sizeof(struct cma3000_accl_data), GFP_KERNEL);
+ input_dev = input_allocate_device();
+ if (!data || !input_dev) {
+ error = -ENOMEM;
+ goto err_free_mem;
+ }
+
+ data->dev = dev;
+ data->input_dev = input_dev;
+ data->bus_ops = bops;
+ data->pdata = pdata;
+ data->irq = irq;
+ mutex_init(&data->mutex);
+
+ data->mode = pdata->mode;
+ if (data->mode < CMAMODE_DEFAULT || data->mode > CMAMODE_POFF) {
+ data->mode = CMAMODE_MOTDET;
+ dev_warn(dev,
+ "Invalid mode specified, assuming Motion Detect\n");
+ }
+
+ data->g_range = pdata->g_range;
+ if (data->g_range != CMARANGE_2G && data->g_range != CMARANGE_8G) {
+ dev_info(dev,
+ "Invalid G range specified, assuming 8G\n");
+ data->g_range = CMARANGE_8G;
+ }
+
+ input_dev->name = "cma3000-accelerometer";
+ input_dev->id.bustype = bops->bustype;
+ input_dev->open = cma3000_open;
+ input_dev->close = cma3000_close;
+
+ __set_bit(EV_ABS, input_dev->evbit);
+
+ input_set_abs_params(input_dev, ABS_X,
+ -data->g_range, data->g_range, pdata->fuzz_x, 0);
+ input_set_abs_params(input_dev, ABS_Y,
+ -data->g_range, data->g_range, pdata->fuzz_y, 0);
+ input_set_abs_params(input_dev, ABS_Z,
+ -data->g_range, data->g_range, pdata->fuzz_z, 0);
+ input_set_abs_params(input_dev, ABS_MISC, 0, 1, 0, 0);
+
+ input_set_drvdata(input_dev, data);
+
+ error = cma3000_reset(data);
+ if (error)
+ goto err_free_mem;
+
+ rev = CMA3000_READ(data, CMA3000_REVID, "Revid");
+ if (rev < 0) {
+ error = rev;
+ goto err_free_mem;
+ }
+
+ pr_info("CMA3000 Accelerometer: Revision %x\n", rev);
+
+ error = request_threaded_irq(irq, NULL, cma3000_thread_irq,
+ pdata->irqflags | IRQF_ONESHOT,
+ "cma3000_d0x", data);
+ if (error) {
+ dev_err(dev, "request_threaded_irq failed\n");
+ goto err_free_mem;
+ }
+
+ error = input_register_device(data->input_dev);
+ if (error) {
+ dev_err(dev, "Unable to register input device\n");
+ goto err_free_irq;
+ }
+
+ return data;
+
+err_free_irq:
+ free_irq(irq, data);
+err_free_mem:
+ input_free_device(input_dev);
+ kfree(data);
+err_out:
+ return ERR_PTR(error);
+}
+EXPORT_SYMBOL(cma3000_init);
+
+void cma3000_exit(struct cma3000_accl_data *data)
+{
+ free_irq(data->irq, data);
+ input_unregister_device(data->input_dev);
+ kfree(data);
+}
+EXPORT_SYMBOL(cma3000_exit);
+
+MODULE_DESCRIPTION("CMA3000-D0x Accelerometer Driver");
+MODULE_LICENSE("GPL");
+MODULE_AUTHOR("Hemanth V <hemanthv@ti.com>");
diff --git a/drivers/input/misc/cma3000_d0x.h b/drivers/input/misc/cma3000_d0x.h
new file mode 100644
index 00000000000..2304ce306e1
--- /dev/null
+++ b/drivers/input/misc/cma3000_d0x.h
@@ -0,0 +1,42 @@
+/*
+ * VTI CMA3000_D0x Accelerometer driver
+ *
+ * Copyright (C) 2010 Texas Instruments
+ * Author: Hemanth V <hemanthv@ti.com>
+ *
+ * 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.
+ *
+ * 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, see <http://www.gnu.org/licenses/>.
+ */
+
+#ifndef _INPUT_CMA3000_H
+#define _INPUT_CMA3000_H
+
+#include <linux/types.h>
+#include <linux/input.h>
+
+struct device;
+struct cma3000_accl_data;
+
+struct cma3000_bus_ops {
+ u16 bustype;
+ u8 ctrl_mod;
+ int (*read)(struct device *, u8, char *);
+ int (*write)(struct device *, u8, u8, char *);
+};
+
+struct cma3000_accl_data *cma3000_init(struct device *dev, int irq,
+ const struct cma3000_bus_ops *bops);
+void cma3000_exit(struct cma3000_accl_data *);
+void cma3000_suspend(struct cma3000_accl_data *);
+void cma3000_resume(struct cma3000_accl_data *);
+
+#endif
diff --git a/drivers/input/misc/cma3000_d0x_i2c.c b/drivers/input/misc/cma3000_d0x_i2c.c
new file mode 100644
index 00000000000..d100cc5c578
--- /dev/null
+++ b/drivers/input/misc/cma3000_d0x_i2c.c
@@ -0,0 +1,143 @@
+/*
+ * Implements I2C interface for VTI CMA300_D0x Accelerometer driver
+ *
+ * Copyright (C) 2010 Texas Instruments
+ * Author: Hemanth V <hemanthv@ti.com>
+ *
+ * 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.
+ *
+ * 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, see <http://www.gnu.org/licenses/>.
+ */
+
+#include <linux/module.h>
+#include <linux/i2c.h>
+#include <linux/input/cma3000.h>
+#include "cma3000_d0x.h"
+
+static int cma3000_i2c_set(struct device *dev,
+ u8 reg, u8 val, char *msg)
+{
+ struct i2c_client *client = to_i2c_client(dev);
+ int ret;
+
+ ret = i2c_smbus_write_byte_data(client, reg, val);
+ if (ret < 0)
+ dev_err(&client->dev,
+ "%s failed (%s, %d)\n", __func__, msg, ret);
+ return ret;
+}
+
+static int cma3000_i2c_read(struct device *dev, u8 reg, char *msg)
+{
+ struct i2c_client *client = to_i2c_client(dev);
+ int ret;
+
+ ret = i2c_smbus_read_byte_data(client, reg);
+ if (ret < 0)
+ dev_err(&client->dev,
+ "%s failed (%s, %d)\n", __func__, msg, ret);
+ return ret;
+}
+
+static const struct cma3000_bus_ops cma3000_i2c_bops = {
+ .bustype = BUS_I2C,
+#define CMA3000_BUSI2C (0 << 4)
+ .ctrl_mod = CMA3000_BUSI2C,
+ .read = cma3000_i2c_read,
+ .write = cma3000_i2c_set,
+};
+
+static int __devinit cma3000_i2c_probe(struct i2c_client *client,
+ const struct i2c_device_id *id)
+{
+ struct cma3000_accl_data *data;
+
+ data = cma3000_init(&client->dev, client->irq, &cma3000_i2c_bops);
+ if (IS_ERR(data))
+ return PTR_ERR(data);
+
+ i2c_set_clientdata(client, data);
+
+ return 0;
+}
+
+static int __devexit cma3000_i2c_remove(struct i2c_client *client)
+{
+ struct cma3000_accl_data *data = i2c_get_clientdata(client);
+
+ cma3000_exit(data);
+
+ return 0;
+}
+
+#ifdef CONFIG_PM
+static int cma3000_i2c_suspend(struct device *dev)
+{
+ struct i2c_client *client = to_i2c_client(dev);
+ struct cma3000_accl_data *data = i2c_get_clientdata(client);
+
+ cma3000_suspend(data);
+
+ return 0;
+}
+
+static int cma3000_i2c_resume(struct device *dev)
+{
+ struct i2c_client *client = to_i2c_client(dev);
+ struct cma3000_accl_data *data = i2c_get_clientdata(client);
+
+ cma3000_resume(data);
+
+ return 0;
+}
+
+static const struct dev_pm_ops cma3000_i2c_pm_ops = {
+ .suspend = cma3000_i2c_suspend,
+ .resume = cma3000_i2c_resume,
+};
+#endif
+
+static const struct i2c_device_id cma3000_i2c_id[] = {
+ { "cma3000_d01", 0 },
+ { },
+};
+
+MODULE_DEVICE_TABLE(i2c, cma3000_i2c_id);
+
+static struct i2c_driver cma3000_i2c_driver = {
+ .probe = cma3000_i2c_probe,
+ .remove = __devexit_p(cma3000_i2c_remove),
+ .id_table = cma3000_i2c_id,
+ .driver = {
+ .name = "cma3000_i2c_accl",
+ .owner = THIS_MODULE,
+#ifdef CONFIG_PM
+ .pm = &cma3000_i2c_pm_ops,
+#endif
+ },
+};
+
+static int __init cma3000_i2c_init(void)
+{
+ return i2c_add_driver(&cma3000_i2c_driver);
+}
+
+static void __exit cma3000_i2c_exit(void)
+{
+ i2c_del_driver(&cma3000_i2c_driver);
+}
+
+module_init(cma3000_i2c_init);
+module_exit(cma3000_i2c_exit);
+
+MODULE_DESCRIPTION("CMA3000-D0x Accelerometer I2C Driver");
+MODULE_LICENSE("GPL");
+MODULE_AUTHOR("Hemanth V <hemanthv@ti.com>");
diff --git a/drivers/input/misc/pcf8574_keypad.c b/drivers/input/misc/pcf8574_keypad.c
index d1583aea172..08be1a35595 100644
--- a/drivers/input/misc/pcf8574_keypad.c
+++ b/drivers/input/misc/pcf8574_keypad.c
@@ -169,19 +169,29 @@ static int __devexit pcf8574_kp_remove(struct i2c_client *client)
}
#ifdef CONFIG_PM
-static int pcf8574_kp_resume(struct i2c_client *client)
+static int pcf8574_kp_resume(struct device *dev)
{
+ struct i2c_client *client = to_i2c_client(dev);
+
enable_irq(client->irq);
return 0;
}
-static int pcf8574_kp_suspend(struct i2c_client *client, pm_message_t mesg)
+static int pcf8574_kp_suspend(struct device *dev)
{
+ struct i2c_client *client = to_i2c_client(dev);
+
disable_irq(client->irq);
return 0;
}
+
+static const struct dev_pm_ops pcf8574_kp_pm_ops = {
+ .suspend = pcf8574_kp_suspend,
+ .resume = pcf8574_kp_resume,
+};
+
#else
# define pcf8574_kp_resume NULL
# define pcf8574_kp_suspend NULL
@@ -197,11 +207,12 @@ static struct i2c_driver pcf8574_kp_driver = {
.driver = {
.name = DRV_NAME,
.owner = THIS_MODULE,
+#ifdef CONFIG_PM
+ .pm = &pcf8574_kp_pm_ops,
+#endif
},
.probe = pcf8574_kp_probe,
.remove = __devexit_p(pcf8574_kp_remove),
- .suspend = pcf8574_kp_suspend,
- .resume = pcf8574_kp_resume,
.id_table = pcf8574_kp_id,
};
diff --git a/drivers/input/misc/uinput.c b/drivers/input/misc/uinput.c
index b9410784e6a..82542a1c109 100644
--- a/drivers/input/misc/uinput.c
+++ b/drivers/input/misc/uinput.c
@@ -37,6 +37,7 @@
#include <linux/fs.h>
#include <linux/miscdevice.h>
#include <linux/uinput.h>
+#include <linux/input/mt.h>
#include "../input-compat.h"
static int uinput_dev_event(struct input_dev *dev, unsigned int type, unsigned int code, int value)
@@ -406,8 +407,7 @@ static int uinput_setup_device(struct uinput_device *udev, const char __user *bu
goto exit;
if (test_bit(ABS_MT_SLOT, dev->absbit)) {
int nslot = input_abs_get_max(dev, ABS_MT_SLOT) + 1;
- input_mt_create_slots(dev, nslot);
- input_set_events_per_packet(dev, 6 * nslot);
+ input_mt_init_slots(dev, nslot);
} else if (test_bit(ABS_MT_POSITION_X, dev->absbit)) {
input_set_events_per_packet(dev, 60);
}
@@ -680,6 +680,10 @@ static long uinput_ioctl_handler(struct file *file, unsigned int cmd,
retval = uinput_set_bit(arg, swbit, SW_MAX);
break;
+ case UI_SET_PROPBIT:
+ retval = uinput_set_bit(arg, propbit, INPUT_PROP_MAX);
+ break;
+
case UI_SET_PHYS:
if (udev->state == UIST_CREATED) {
retval = -EINVAL;
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/mouse/bcm5974.c b/drivers/input/mouse/bcm5974.c
index b9523176391..ee82851afe3 100644
--- a/drivers/input/mouse/bcm5974.c
+++ b/drivers/input/mouse/bcm5974.c
@@ -55,6 +55,14 @@
#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
#define BCM5974_DEVICE(prod) { \
.match_flags = (USB_DEVICE_ID_MATCH_DEVICE | \
@@ -80,6 +88,14 @@ 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),
/* Terminating entry */
{}
};
@@ -234,6 +250,30 @@ 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 }
+ },
{}
};
diff --git a/drivers/input/mouse/hgpk.c b/drivers/input/mouse/hgpk.c
index 1d2205b2480..95577c15ae5 100644
--- a/drivers/input/mouse/hgpk.c
+++ b/drivers/input/mouse/hgpk.c
@@ -40,6 +40,8 @@
#include "psmouse.h"
#include "hgpk.h"
+#define ILLEGAL_XY 999999
+
static bool tpdebug;
module_param(tpdebug, bool, 0644);
MODULE_PARM_DESC(tpdebug, "enable debugging, dumping packets to KERN_DEBUG.");
@@ -47,48 +49,150 @@ MODULE_PARM_DESC(tpdebug, "enable debugging, dumping packets to KERN_DEBUG.");
static int recalib_delta = 100;
module_param(recalib_delta, int, 0644);
MODULE_PARM_DESC(recalib_delta,
- "packets containing a delta this large will cause a recalibration.");
+ "packets containing a delta this large will be discarded, and a "
+ "recalibration may be scheduled.");
-static int jumpy_delay = 1000;
+static int jumpy_delay = 20;
module_param(jumpy_delay, int, 0644);
MODULE_PARM_DESC(jumpy_delay,
"delay (ms) before recal after jumpiness detected");
-static int spew_delay = 1000;
+static int spew_delay = 1;
module_param(spew_delay, int, 0644);
MODULE_PARM_DESC(spew_delay,
"delay (ms) before recal after packet spew detected");
-static int recal_guard_time = 2000;
+static int recal_guard_time;
module_param(recal_guard_time, int, 0644);
MODULE_PARM_DESC(recal_guard_time,
"interval (ms) during which recal will be restarted if packet received");
-static int post_interrupt_delay = 1000;
+static int post_interrupt_delay = 40;
module_param(post_interrupt_delay, int, 0644);
MODULE_PARM_DESC(post_interrupt_delay,
"delay (ms) before recal after recal interrupt detected");
+static bool autorecal = true;
+module_param(autorecal, bool, 0644);
+MODULE_PARM_DESC(autorecal, "enable recalibration in the driver");
+
+static char hgpk_mode_name[16];
+module_param_string(hgpk_mode, hgpk_mode_name, sizeof(hgpk_mode_name), 0644);
+MODULE_PARM_DESC(hgpk_mode,
+ "default hgpk mode: mouse, glidesensor or pentablet");
+
+static int hgpk_default_mode = HGPK_MODE_MOUSE;
+
+static const char * const hgpk_mode_names[] = {
+ [HGPK_MODE_MOUSE] = "Mouse",
+ [HGPK_MODE_GLIDESENSOR] = "GlideSensor",
+ [HGPK_MODE_PENTABLET] = "PenTablet",
+};
+
+static int hgpk_mode_from_name(const char *buf, int len)
+{
+ int i;
+
+ for (i = 0; i < ARRAY_SIZE(hgpk_mode_names); i++) {
+ const char *name = hgpk_mode_names[i];
+ if (strlen(name) == len && !strncasecmp(name, buf, len))
+ return i;
+ }
+
+ return HGPK_MODE_INVALID;
+}
+
+/*
+ * see if new value is within 20% of half of old value
+ */
+static int approx_half(int curr, int prev)
+{
+ int belowhalf, abovehalf;
+
+ if (curr < 5 || prev < 5)
+ return 0;
+
+ belowhalf = (prev * 8) / 20;
+ abovehalf = (prev * 12) / 20;
+
+ return belowhalf < curr && curr <= abovehalf;
+}
+
/*
- * When the touchpad gets ultra-sensitive, one can keep their finger 1/2"
- * above the pad and still have it send packets. This causes a jump cursor
- * when one places their finger on the pad. We can probably detect the
- * jump as we see a large deltas (>= 100px). In mouse mode, I've been
- * unable to even come close to 100px deltas during normal usage, so I think
- * this threshold is safe. If a large delta occurs, trigger a recalibration.
+ * Throw out oddly large delta packets, and any that immediately follow whose
+ * values are each approximately half of the previous. It seems that the ALPS
+ * firmware emits errant packets, and they get averaged out slowly.
*/
-static void hgpk_jumpy_hack(struct psmouse *psmouse, int x, int y)
+static int hgpk_discard_decay_hack(struct psmouse *psmouse, int x, int y)
{
struct hgpk_data *priv = psmouse->private;
+ int avx, avy;
+ bool do_recal = false;
+
+ avx = abs(x);
+ avy = abs(y);
+
+ /* discard if too big, or half that but > 4 times the prev delta */
+ if (avx > recalib_delta ||
+ (avx > recalib_delta / 2 && ((avx / 4) > priv->xlast))) {
+ hgpk_err(psmouse, "detected %dpx jump in x\n", x);
+ priv->xbigj = avx;
+ } else if (approx_half(avx, priv->xbigj)) {
+ hgpk_err(psmouse, "detected secondary %dpx jump in x\n", x);
+ priv->xbigj = avx;
+ priv->xsaw_secondary++;
+ } else {
+ if (priv->xbigj && priv->xsaw_secondary > 1)
+ do_recal = true;
+ priv->xbigj = 0;
+ priv->xsaw_secondary = 0;
+ }
+
+ if (avy > recalib_delta ||
+ (avy > recalib_delta / 2 && ((avy / 4) > priv->ylast))) {
+ hgpk_err(psmouse, "detected %dpx jump in y\n", y);
+ priv->ybigj = avy;
+ } else if (approx_half(avy, priv->ybigj)) {
+ hgpk_err(psmouse, "detected secondary %dpx jump in y\n", y);
+ priv->ybigj = avy;
+ priv->ysaw_secondary++;
+ } else {
+ if (priv->ybigj && priv->ysaw_secondary > 1)
+ do_recal = true;
+ priv->ybigj = 0;
+ priv->ysaw_secondary = 0;
+ }
- if (abs(x) > recalib_delta || abs(y) > recalib_delta) {
- hgpk_err(psmouse, ">%dpx jump detected (%d,%d)\n",
- recalib_delta, x, y);
- /* My car gets forty rods to the hogshead and that's the
- * way I likes it! */
+ priv->xlast = avx;
+ priv->ylast = avy;
+
+ if (do_recal && jumpy_delay) {
+ hgpk_err(psmouse, "scheduling recalibration\n");
psmouse_queue_work(psmouse, &priv->recalib_wq,
msecs_to_jiffies(jumpy_delay));
}
+
+ return priv->xbigj || priv->ybigj;
+}
+
+static void hgpk_reset_spew_detection(struct hgpk_data *priv)
+{
+ priv->spew_count = 0;
+ priv->dupe_count = 0;
+ priv->x_tally = 0;
+ priv->y_tally = 0;
+ priv->spew_flag = NO_SPEW;
+}
+
+static void hgpk_reset_hack_state(struct psmouse *psmouse)
+{
+ struct hgpk_data *priv = psmouse->private;
+
+ priv->abs_x = priv->abs_y = -1;
+ priv->xlast = priv->ylast = ILLEGAL_XY;
+ priv->xbigj = priv->ybigj = 0;
+ priv->xsaw_secondary = priv->ysaw_secondary = 0;
+ hgpk_reset_spew_detection(priv);
}
/*
@@ -116,20 +220,57 @@ static void hgpk_spewing_hack(struct psmouse *psmouse,
if (l || r)
return;
+ /* don't track spew if the workaround feature has been turned off */
+ if (!spew_delay)
+ return;
+
+ if (abs(x) > 3 || abs(y) > 3) {
+ /* no spew, or spew ended */
+ hgpk_reset_spew_detection(priv);
+ return;
+ }
+
+ /* Keep a tally of the overall delta to the cursor position caused by
+ * the spew */
priv->x_tally += x;
priv->y_tally += y;
- if (++priv->count > 100) {
+ switch (priv->spew_flag) {
+ case NO_SPEW:
+ /* we're not spewing, but this packet might be the start */
+ priv->spew_flag = MAYBE_SPEWING;
+
+ /* fall-through */
+
+ case MAYBE_SPEWING:
+ priv->spew_count++;
+
+ if (priv->spew_count < SPEW_WATCH_COUNT)
+ break;
+
+ /* excessive spew detected, request recalibration */
+ priv->spew_flag = SPEW_DETECTED;
+
+ /* fall-through */
+
+ case SPEW_DETECTED:
+ /* only recalibrate when the overall delta to the cursor
+ * is really small. if the spew is causing significant cursor
+ * movement, it is probably a case of the user moving the
+ * cursor very slowly across the screen. */
if (abs(priv->x_tally) < 3 && abs(priv->y_tally) < 3) {
- hgpk_dbg(psmouse, "packet spew detected (%d,%d)\n",
+ hgpk_err(psmouse, "packet spew detected (%d,%d)\n",
priv->x_tally, priv->y_tally);
+ priv->spew_flag = RECALIBRATING;
psmouse_queue_work(psmouse, &priv->recalib_wq,
msecs_to_jiffies(spew_delay));
}
- /* reset every 100 packets */
- priv->count = 0;
- priv->x_tally = 0;
- priv->y_tally = 0;
+
+ break;
+ case RECALIBRATING:
+ /* we already detected a spew and requested a recalibration,
+ * just wait for the queue to kick into action. */
+ break;
}
}
@@ -143,25 +284,168 @@ static void hgpk_spewing_hack(struct psmouse *psmouse,
* swr/swl are the left/right buttons.
* x-neg/y-neg are the x and y delta negative bits
* x-over/y-over are the x and y overflow bits
+ *
+ * ---
+ *
+ * HGPK Advanced Mode - single-mode format
+ *
+ * byte 0(PT): 1 1 0 0 1 1 1 1
+ * byte 0(GS): 1 1 1 1 1 1 1 1
+ * byte 1: 0 x6 x5 x4 x3 x2 x1 x0
+ * byte 2(PT): 0 0 x9 x8 x7 ? pt-dsw 0
+ * byte 2(GS): 0 x10 x9 x8 x7 ? gs-dsw pt-dsw
+ * byte 3: 0 y9 y8 y7 1 0 swr swl
+ * byte 4: 0 y6 y5 y4 y3 y2 y1 y0
+ * byte 5: 0 z6 z5 z4 z3 z2 z1 z0
+ *
+ * ?'s are not defined in the protocol spec, may vary between models.
+ *
+ * swr/swl are the left/right buttons.
+ *
+ * pt-dsw/gs-dsw indicate that the pt/gs sensor is detecting a
+ * pen/finger
*/
-static int hgpk_validate_byte(unsigned char *packet)
+static bool hgpk_is_byte_valid(struct psmouse *psmouse, unsigned char *packet)
{
- return (packet[0] & 0x0C) != 0x08;
+ struct hgpk_data *priv = psmouse->private;
+ int pktcnt = psmouse->pktcnt;
+ bool valid;
+
+ switch (priv->mode) {
+ case HGPK_MODE_MOUSE:
+ valid = (packet[0] & 0x0C) == 0x08;
+ break;
+
+ case HGPK_MODE_GLIDESENSOR:
+ valid = pktcnt == 1 ?
+ packet[0] == HGPK_GS : !(packet[pktcnt - 1] & 0x80);
+ break;
+
+ case HGPK_MODE_PENTABLET:
+ valid = pktcnt == 1 ?
+ packet[0] == HGPK_PT : !(packet[pktcnt - 1] & 0x80);
+ break;
+
+ default:
+ valid = false;
+ break;
+ }
+
+ if (!valid)
+ hgpk_dbg(psmouse,
+ "bad data, mode %d (%d) %02x %02x %02x %02x %02x %02x\n",
+ priv->mode, pktcnt,
+ psmouse->packet[0], psmouse->packet[1],
+ psmouse->packet[2], psmouse->packet[3],
+ psmouse->packet[4], psmouse->packet[5]);
+
+ return valid;
}
-static void hgpk_process_packet(struct psmouse *psmouse)
+static void hgpk_process_advanced_packet(struct psmouse *psmouse)
{
- struct input_dev *dev = psmouse->dev;
+ struct hgpk_data *priv = psmouse->private;
+ struct input_dev *idev = psmouse->dev;
unsigned char *packet = psmouse->packet;
- int x, y, left, right;
+ int down = !!(packet[2] & 2);
+ int left = !!(packet[3] & 1);
+ int right = !!(packet[3] & 2);
+ int x = packet[1] | ((packet[2] & 0x78) << 4);
+ int y = packet[4] | ((packet[3] & 0x70) << 3);
+
+ if (priv->mode == HGPK_MODE_GLIDESENSOR) {
+ int pt_down = !!(packet[2] & 1);
+ int finger_down = !!(packet[2] & 2);
+ int z = packet[5];
+
+ input_report_abs(idev, ABS_PRESSURE, z);
+ if (tpdebug)
+ hgpk_dbg(psmouse, "pd=%d fd=%d z=%d",
+ pt_down, finger_down, z);
+ } else {
+ /*
+ * PenTablet mode does not report pressure, so we don't
+ * report it here
+ */
+ if (tpdebug)
+ hgpk_dbg(psmouse, "pd=%d ", down);
+ }
+
+ if (tpdebug)
+ hgpk_dbg(psmouse, "l=%d r=%d x=%d y=%d\n", left, right, x, y);
+
+ input_report_key(idev, BTN_TOUCH, down);
+ input_report_key(idev, BTN_LEFT, left);
+ input_report_key(idev, BTN_RIGHT, right);
+
+ /*
+ * If this packet says that the finger was removed, reset our position
+ * tracking so that we don't erroneously detect a jump on next press.
+ */
+ if (!down) {
+ hgpk_reset_hack_state(psmouse);
+ goto done;
+ }
+
+ /*
+ * Weed out duplicate packets (we get quite a few, and they mess up
+ * our jump detection)
+ */
+ if (x == priv->abs_x && y == priv->abs_y) {
+ if (++priv->dupe_count > SPEW_WATCH_COUNT) {
+ if (tpdebug)
+ hgpk_dbg(psmouse, "hard spew detected\n");
+ priv->spew_flag = RECALIBRATING;
+ psmouse_queue_work(psmouse, &priv->recalib_wq,
+ msecs_to_jiffies(spew_delay));
+ }
+ goto done;
+ }
- left = packet[0] & 1;
- right = (packet[0] >> 1) & 1;
+ /* not a duplicate, continue with position reporting */
+ priv->dupe_count = 0;
+
+ /* Don't apply hacks in PT mode, it seems reliable */
+ if (priv->mode != HGPK_MODE_PENTABLET && priv->abs_x != -1) {
+ int x_diff = priv->abs_x - x;
+ int y_diff = priv->abs_y - y;
+ if (hgpk_discard_decay_hack(psmouse, x_diff, y_diff)) {
+ if (tpdebug)
+ hgpk_dbg(psmouse, "discarding\n");
+ goto done;
+ }
+ hgpk_spewing_hack(psmouse, left, right, x_diff, y_diff);
+ }
- x = packet[1] - ((packet[0] << 4) & 0x100);
- y = ((packet[0] << 3) & 0x100) - packet[2];
+ input_report_abs(idev, ABS_X, x);
+ input_report_abs(idev, ABS_Y, y);
+ priv->abs_x = x;
+ priv->abs_y = y;
+
+done:
+ input_sync(idev);
+}
+
+static void hgpk_process_simple_packet(struct psmouse *psmouse)
+{
+ struct input_dev *dev = psmouse->dev;
+ unsigned char *packet = psmouse->packet;
+ int left = packet[0] & 1;
+ int right = (packet[0] >> 1) & 1;
+ int x = packet[1] - ((packet[0] << 4) & 0x100);
+ int y = ((packet[0] << 3) & 0x100) - packet[2];
+
+ if (packet[0] & 0xc0)
+ hgpk_dbg(psmouse,
+ "overflow -- 0x%02x 0x%02x 0x%02x\n",
+ packet[0], packet[1], packet[2]);
+
+ if (hgpk_discard_decay_hack(psmouse, x, y)) {
+ if (tpdebug)
+ hgpk_dbg(psmouse, "discarding\n");
+ return;
+ }
- hgpk_jumpy_hack(psmouse, x, y);
hgpk_spewing_hack(psmouse, left, right, x, y);
if (tpdebug)
@@ -180,15 +464,14 @@ static psmouse_ret_t hgpk_process_byte(struct psmouse *psmouse)
{
struct hgpk_data *priv = psmouse->private;
- if (hgpk_validate_byte(psmouse->packet)) {
- hgpk_dbg(psmouse, "%s: (%d) %02x %02x %02x\n",
- __func__, psmouse->pktcnt, psmouse->packet[0],
- psmouse->packet[1], psmouse->packet[2]);
+ if (!hgpk_is_byte_valid(psmouse, psmouse->packet))
return PSMOUSE_BAD_DATA;
- }
if (psmouse->pktcnt >= psmouse->pktsize) {
- hgpk_process_packet(psmouse);
+ if (priv->mode == HGPK_MODE_MOUSE)
+ hgpk_process_simple_packet(psmouse);
+ else
+ hgpk_process_advanced_packet(psmouse);
return PSMOUSE_FULL_PACKET;
}
@@ -210,33 +493,176 @@ static psmouse_ret_t hgpk_process_byte(struct psmouse *psmouse)
return PSMOUSE_GOOD_DATA;
}
+static int hgpk_select_mode(struct psmouse *psmouse)
+{
+ struct ps2dev *ps2dev = &psmouse->ps2dev;
+ struct hgpk_data *priv = psmouse->private;
+ int i;
+ int cmd;
+
+ /*
+ * 4 disables to enable advanced mode
+ * then 3 0xf2 bytes as the preamble for GS/PT selection
+ */
+ const int advanced_init[] = {
+ PSMOUSE_CMD_DISABLE, PSMOUSE_CMD_DISABLE,
+ PSMOUSE_CMD_DISABLE, PSMOUSE_CMD_DISABLE,
+ 0xf2, 0xf2, 0xf2,
+ };
+
+ switch (priv->mode) {
+ case HGPK_MODE_MOUSE:
+ psmouse->pktsize = 3;
+ break;
+
+ case HGPK_MODE_GLIDESENSOR:
+ case HGPK_MODE_PENTABLET:
+ psmouse->pktsize = 6;
+
+ /* Switch to 'Advanced mode.', four disables in a row. */
+ for (i = 0; i < ARRAY_SIZE(advanced_init); i++)
+ if (ps2_command(ps2dev, NULL, advanced_init[i]))
+ return -EIO;
+
+ /* select between GlideSensor (mouse) or PenTablet */
+ cmd = priv->mode == HGPK_MODE_GLIDESENSOR ?
+ PSMOUSE_CMD_SETSCALE11 : PSMOUSE_CMD_SETSCALE21;
+
+ if (ps2_command(ps2dev, NULL, cmd))
+ return -EIO;
+ break;
+
+ default:
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+static void hgpk_setup_input_device(struct input_dev *input,
+ struct input_dev *old_input,
+ enum hgpk_mode mode)
+{
+ if (old_input) {
+ input->name = old_input->name;
+ input->phys = old_input->phys;
+ input->id = old_input->id;
+ input->dev.parent = old_input->dev.parent;
+ }
+
+ memset(input->evbit, 0, sizeof(input->evbit));
+ memset(input->relbit, 0, sizeof(input->relbit));
+ memset(input->keybit, 0, sizeof(input->keybit));
+
+ /* All modes report left and right buttons */
+ __set_bit(EV_KEY, input->evbit);
+ __set_bit(BTN_LEFT, input->keybit);
+ __set_bit(BTN_RIGHT, input->keybit);
+
+ switch (mode) {
+ case HGPK_MODE_MOUSE:
+ __set_bit(EV_REL, input->evbit);
+ __set_bit(REL_X, input->relbit);
+ __set_bit(REL_Y, input->relbit);
+ break;
+
+ case HGPK_MODE_GLIDESENSOR:
+ __set_bit(BTN_TOUCH, input->keybit);
+ __set_bit(BTN_TOOL_FINGER, input->keybit);
+
+ __set_bit(EV_ABS, input->evbit);
+
+ /* GlideSensor has pressure sensor, PenTablet does not */
+ input_set_abs_params(input, ABS_PRESSURE, 0, 15, 0, 0);
+
+ /* From device specs */
+ input_set_abs_params(input, ABS_X, 0, 399, 0, 0);
+ input_set_abs_params(input, ABS_Y, 0, 290, 0, 0);
+
+ /* Calculated by hand based on usable size (52mm x 38mm) */
+ input_abs_set_res(input, ABS_X, 8);
+ input_abs_set_res(input, ABS_Y, 8);
+ break;
+
+ case HGPK_MODE_PENTABLET:
+ __set_bit(BTN_TOUCH, input->keybit);
+ __set_bit(BTN_TOOL_FINGER, input->keybit);
+
+ __set_bit(EV_ABS, input->evbit);
+
+ /* From device specs */
+ input_set_abs_params(input, ABS_X, 0, 999, 0, 0);
+ input_set_abs_params(input, ABS_Y, 5, 239, 0, 0);
+
+ /* Calculated by hand based on usable size (156mm x 38mm) */
+ input_abs_set_res(input, ABS_X, 6);
+ input_abs_set_res(input, ABS_Y, 8);
+ break;
+
+ default:
+ BUG();
+ }
+}
+
+static int hgpk_reset_device(struct psmouse *psmouse, bool recalibrate)
+{
+ int err;
+
+ psmouse_reset(psmouse);
+
+ if (recalibrate) {
+ struct ps2dev *ps2dev = &psmouse->ps2dev;
+
+ /* send the recalibrate request */
+ if (ps2_command(ps2dev, NULL, 0xf5) ||
+ ps2_command(ps2dev, NULL, 0xf5) ||
+ ps2_command(ps2dev, NULL, 0xe6) ||
+ ps2_command(ps2dev, NULL, 0xf5)) {
+ return -1;
+ }
+
+ /* according to ALPS, 150mS is required for recalibration */
+ msleep(150);
+ }
+
+ err = hgpk_select_mode(psmouse);
+ if (err) {
+ hgpk_err(psmouse, "failed to select mode\n");
+ return err;
+ }
+
+ hgpk_reset_hack_state(psmouse);
+
+ return 0;
+}
+
static int hgpk_force_recalibrate(struct psmouse *psmouse)
{
struct ps2dev *ps2dev = &psmouse->ps2dev;
struct hgpk_data *priv = psmouse->private;
+ int err;
/* C-series touchpads added the recalibrate command */
if (psmouse->model < HGPK_MODEL_C)
return 0;
+ if (!autorecal) {
+ hgpk_dbg(psmouse, "recalibrations disabled, ignoring\n");
+ return 0;
+ }
+
+ hgpk_dbg(psmouse, "recalibrating touchpad..\n");
+
/* we don't want to race with the irq handler, nor with resyncs */
psmouse_set_state(psmouse, PSMOUSE_INITIALIZING);
/* start by resetting the device */
- psmouse_reset(psmouse);
-
- /* send the recalibrate request */
- if (ps2_command(ps2dev, NULL, 0xf5) ||
- ps2_command(ps2dev, NULL, 0xf5) ||
- ps2_command(ps2dev, NULL, 0xe6) ||
- ps2_command(ps2dev, NULL, 0xf5)) {
- return -1;
- }
-
- /* according to ALPS, 150mS is required for recalibration */
- msleep(150);
+ err = hgpk_reset_device(psmouse, true);
+ if (err)
+ return err;
- /* XXX: If a finger is down during this delay, recalibration will
+ /*
+ * XXX: If a finger is down during this delay, recalibration will
* detect capacitance incorrectly. This is a hardware bug, and
* we don't have a good way to deal with it. The 2s window stuff
* (below) is our best option for now.
@@ -247,25 +673,35 @@ static int hgpk_force_recalibrate(struct psmouse *psmouse)
psmouse_set_state(psmouse, PSMOUSE_ACTIVATED);
- /* After we recalibrate, we shouldn't get any packets for 2s. If
- * we do, it's likely that someone's finger was on the touchpad.
- * If someone's finger *was* on the touchpad, it's probably
- * miscalibrated. So, we should schedule another recalibration
+ if (tpdebug)
+ hgpk_dbg(psmouse, "touchpad reactivated\n");
+
+ /*
+ * If we get packets right away after recalibrating, it's likely
+ * that a finger was on the touchpad. If so, it's probably
+ * miscalibrated, so we optionally schedule another.
*/
- priv->recalib_window = jiffies + msecs_to_jiffies(recal_guard_time);
+ if (recal_guard_time)
+ priv->recalib_window = jiffies +
+ msecs_to_jiffies(recal_guard_time);
return 0;
}
/*
- * This kills power to the touchpad; according to ALPS, current consumption
- * goes down to 50uA after running this. To turn power back on, we drive
- * MS-DAT low.
+ * This puts the touchpad in a power saving mode; according to ALPS, current
+ * consumption goes down to 50uA after running this. To turn power back on,
+ * we drive MS-DAT low. Measuring with a 1mA resolution ammeter says that
+ * the current on the SUS_3.3V rail drops from 3mA or 4mA to 0 when we do this.
+ *
+ * We have no formal spec that details this operation -- the low-power
+ * sequence came from a long-lost email trail.
*/
-static int hgpk_toggle_power(struct psmouse *psmouse, int enable)
+static int hgpk_toggle_powersave(struct psmouse *psmouse, int enable)
{
struct ps2dev *ps2dev = &psmouse->ps2dev;
int timeo;
+ int err;
/* Added on D-series touchpads */
if (psmouse->model < HGPK_MODEL_D)
@@ -279,24 +715,27 @@ static int hgpk_toggle_power(struct psmouse *psmouse, int enable)
* the controller. Once we get an ACK back from it, it
* means we can continue with the touchpad re-init. ALPS
* tells us that 1s should be long enough, so set that as
- * the upper bound.
+ * the upper bound. (in practice, it takes about 3 loops.)
*/
for (timeo = 20; timeo > 0; timeo--) {
if (!ps2_sendbyte(&psmouse->ps2dev,
PSMOUSE_CMD_DISABLE, 20))
break;
- msleep(50);
+ msleep(25);
}
- psmouse_reset(psmouse);
+ err = hgpk_reset_device(psmouse, false);
+ if (err) {
+ hgpk_err(psmouse, "Failed to reset device!\n");
+ return err;
+ }
/* should be all set, enable the touchpad */
ps2_command(&psmouse->ps2dev, NULL, PSMOUSE_CMD_ENABLE);
psmouse_set_state(psmouse, PSMOUSE_ACTIVATED);
-
+ hgpk_dbg(psmouse, "Touchpad powered up.\n");
} else {
hgpk_dbg(psmouse, "Powering off touchpad.\n");
- psmouse_set_state(psmouse, PSMOUSE_IGNORE);
if (ps2_command(ps2dev, NULL, 0xec) ||
ps2_command(ps2dev, NULL, 0xec) ||
@@ -304,6 +743,8 @@ static int hgpk_toggle_power(struct psmouse *psmouse, int enable)
return -1;
}
+ psmouse_set_state(psmouse, PSMOUSE_IGNORE);
+
/* probably won't see an ACK, the touchpad will be off */
ps2_sendbyte(&psmouse->ps2dev, 0xec, 20);
}
@@ -319,17 +760,20 @@ static int hgpk_poll(struct psmouse *psmouse)
static int hgpk_reconnect(struct psmouse *psmouse)
{
- /* During suspend/resume the ps2 rails remain powered. We don't want
+ struct hgpk_data *priv = psmouse->private;
+
+ /*
+ * During suspend/resume the ps2 rails remain powered. We don't want
* to do a reset because it's flush data out of buffers; however,
- * earlier prototypes (B1) had some brokenness that required a reset. */
+ * earlier prototypes (B1) had some brokenness that required a reset.
+ */
if (olpc_board_at_least(olpc_board(0xb2)))
if (psmouse->ps2dev.serio->dev.power.power_state.event !=
PM_EVENT_ON)
return 0;
- psmouse_reset(psmouse);
-
- return 0;
+ priv->powered = 1;
+ return hgpk_reset_device(psmouse, false);
}
static ssize_t hgpk_show_powered(struct psmouse *psmouse, void *data, char *buf)
@@ -355,7 +799,7 @@ static ssize_t hgpk_set_powered(struct psmouse *psmouse, void *data,
* hgpk_toggle_power will deal w/ state so
* we're not racing w/ irq
*/
- err = hgpk_toggle_power(psmouse, value);
+ err = hgpk_toggle_powersave(psmouse, value);
if (!err)
priv->powered = value;
}
@@ -366,6 +810,65 @@ static ssize_t hgpk_set_powered(struct psmouse *psmouse, void *data,
__PSMOUSE_DEFINE_ATTR(powered, S_IWUSR | S_IRUGO, NULL,
hgpk_show_powered, hgpk_set_powered, false);
+static ssize_t attr_show_mode(struct psmouse *psmouse, void *data, char *buf)
+{
+ struct hgpk_data *priv = psmouse->private;
+
+ return sprintf(buf, "%s\n", hgpk_mode_names[priv->mode]);
+}
+
+static ssize_t attr_set_mode(struct psmouse *psmouse, void *data,
+ const char *buf, size_t len)
+{
+ struct hgpk_data *priv = psmouse->private;
+ enum hgpk_mode old_mode = priv->mode;
+ enum hgpk_mode new_mode = hgpk_mode_from_name(buf, len);
+ struct input_dev *old_dev = psmouse->dev;
+ struct input_dev *new_dev;
+ int err;
+
+ if (new_mode == HGPK_MODE_INVALID)
+ return -EINVAL;
+
+ if (old_mode == new_mode)
+ return len;
+
+ new_dev = input_allocate_device();
+ if (!new_dev)
+ return -ENOMEM;
+
+ psmouse_set_state(psmouse, PSMOUSE_INITIALIZING);
+
+ /* Switch device into the new mode */
+ priv->mode = new_mode;
+ err = hgpk_reset_device(psmouse, false);
+ if (err)
+ goto err_try_restore;
+
+ hgpk_setup_input_device(new_dev, old_dev, new_mode);
+
+ psmouse_set_state(psmouse, PSMOUSE_CMD_MODE);
+
+ err = input_register_device(new_dev);
+ if (err)
+ goto err_try_restore;
+
+ psmouse->dev = new_dev;
+ input_unregister_device(old_dev);
+
+ return len;
+
+err_try_restore:
+ input_free_device(new_dev);
+ priv->mode = old_mode;
+ hgpk_reset_device(psmouse, false);
+
+ return err;
+}
+
+PSMOUSE_DEFINE_ATTR(hgpk_mode, S_IWUSR | S_IRUGO, NULL,
+ attr_show_mode, attr_set_mode);
+
static ssize_t hgpk_trigger_recal_show(struct psmouse *psmouse,
void *data, char *buf)
{
@@ -401,6 +904,8 @@ static void hgpk_disconnect(struct psmouse *psmouse)
device_remove_file(&psmouse->ps2dev.serio->dev,
&psmouse_attr_powered.dattr);
+ device_remove_file(&psmouse->ps2dev.serio->dev,
+ &psmouse_attr_hgpk_mode.dattr);
if (psmouse->model >= HGPK_MODEL_C)
device_remove_file(&psmouse->ps2dev.serio->dev,
@@ -416,14 +921,13 @@ static void hgpk_recalib_work(struct work_struct *work)
struct hgpk_data *priv = container_of(w, struct hgpk_data, recalib_wq);
struct psmouse *psmouse = priv->psmouse;
- hgpk_dbg(psmouse, "recalibrating touchpad..\n");
-
if (hgpk_force_recalibrate(psmouse))
hgpk_err(psmouse, "recalibration failed!\n");
}
static int hgpk_register(struct psmouse *psmouse)
{
+ struct hgpk_data *priv = psmouse->private;
int err;
/* register handlers */
@@ -431,13 +935,14 @@ static int hgpk_register(struct psmouse *psmouse)
psmouse->poll = hgpk_poll;
psmouse->disconnect = hgpk_disconnect;
psmouse->reconnect = hgpk_reconnect;
- psmouse->pktsize = 3;
/* Disable the idle resync. */
psmouse->resync_time = 0;
/* Reset after a lot of bad bytes. */
psmouse->resetafter = 1024;
+ hgpk_setup_input_device(psmouse->dev, NULL, priv->mode);
+
err = device_create_file(&psmouse->ps2dev.serio->dev,
&psmouse_attr_powered.dattr);
if (err) {
@@ -445,6 +950,13 @@ static int hgpk_register(struct psmouse *psmouse)
return err;
}
+ err = device_create_file(&psmouse->ps2dev.serio->dev,
+ &psmouse_attr_hgpk_mode.dattr);
+ if (err) {
+ hgpk_err(psmouse, "Failed creating 'hgpk_mode' sysfs node\n");
+ goto err_remove_powered;
+ }
+
/* C-series touchpads added the recalibrate command */
if (psmouse->model >= HGPK_MODEL_C) {
err = device_create_file(&psmouse->ps2dev.serio->dev,
@@ -452,30 +964,40 @@ static int hgpk_register(struct psmouse *psmouse)
if (err) {
hgpk_err(psmouse,
"Failed creating 'recalibrate' sysfs node\n");
- device_remove_file(&psmouse->ps2dev.serio->dev,
- &psmouse_attr_powered.dattr);
- return err;
+ goto err_remove_mode;
}
}
return 0;
+
+err_remove_mode:
+ device_remove_file(&psmouse->ps2dev.serio->dev,
+ &psmouse_attr_hgpk_mode.dattr);
+err_remove_powered:
+ device_remove_file(&psmouse->ps2dev.serio->dev,
+ &psmouse_attr_powered.dattr);
+ return err;
}
int hgpk_init(struct psmouse *psmouse)
{
struct hgpk_data *priv;
- int err = -ENOMEM;
+ int err;
priv = kzalloc(sizeof(struct hgpk_data), GFP_KERNEL);
- if (!priv)
+ if (!priv) {
+ err = -ENOMEM;
goto alloc_fail;
+ }
psmouse->private = priv;
+
priv->psmouse = psmouse;
priv->powered = true;
+ priv->mode = hgpk_default_mode;
INIT_DELAYED_WORK(&priv->recalib_wq, hgpk_recalib_work);
- err = psmouse_reset(psmouse);
+ err = hgpk_reset_device(psmouse, false);
if (err)
goto init_fail;
@@ -531,3 +1053,14 @@ int hgpk_detect(struct psmouse *psmouse, bool set_properties)
return 0;
}
+
+void hgpk_module_init(void)
+{
+ hgpk_default_mode = hgpk_mode_from_name(hgpk_mode_name,
+ strlen(hgpk_mode_name));
+ if (hgpk_default_mode == HGPK_MODE_INVALID) {
+ hgpk_default_mode = HGPK_MODE_MOUSE;
+ strlcpy(hgpk_mode_name, hgpk_mode_names[HGPK_MODE_MOUSE],
+ sizeof(hgpk_mode_name));
+ }
+}
diff --git a/drivers/input/mouse/hgpk.h b/drivers/input/mouse/hgpk.h
index d61cfd3ee9c..311c0e87fcb 100644
--- a/drivers/input/mouse/hgpk.h
+++ b/drivers/input/mouse/hgpk.h
@@ -5,6 +5,9 @@
#ifndef _HGPK_H
#define _HGPK_H
+#define HGPK_GS 0xff /* The GlideSensor */
+#define HGPK_PT 0xcf /* The PenTablet */
+
enum hgpk_model_t {
HGPK_MODEL_PREA = 0x0a, /* pre-B1s */
HGPK_MODEL_A = 0x14, /* found on B1s, PT disabled in hardware */
@@ -13,12 +16,34 @@ enum hgpk_model_t {
HGPK_MODEL_D = 0x50, /* C1, mass production */
};
+enum hgpk_spew_flag {
+ NO_SPEW,
+ MAYBE_SPEWING,
+ SPEW_DETECTED,
+ RECALIBRATING,
+};
+
+#define SPEW_WATCH_COUNT 42 /* at 12ms/packet, this is 1/2 second */
+
+enum hgpk_mode {
+ HGPK_MODE_MOUSE,
+ HGPK_MODE_GLIDESENSOR,
+ HGPK_MODE_PENTABLET,
+ HGPK_MODE_INVALID
+};
+
struct hgpk_data {
struct psmouse *psmouse;
+ enum hgpk_mode mode;
bool powered;
- int count, x_tally, y_tally; /* hardware workaround stuff */
+ enum hgpk_spew_flag spew_flag;
+ int spew_count, x_tally, y_tally; /* spew detection */
unsigned long recalib_window;
struct delayed_work recalib_wq;
+ int abs_x, abs_y;
+ int dupe_count;
+ int xbigj, ybigj, xlast, ylast; /* jumpiness detection */
+ int xsaw_secondary, ysaw_secondary; /* jumpiness detection */
};
#define hgpk_dbg(psmouse, format, arg...) \
@@ -33,9 +58,13 @@ struct hgpk_data {
dev_notice(&(psmouse)->ps2dev.serio->dev, format, ## arg)
#ifdef CONFIG_MOUSE_PS2_OLPC
+void hgpk_module_init(void);
int hgpk_detect(struct psmouse *psmouse, bool set_properties);
int hgpk_init(struct psmouse *psmouse);
#else
+static inline void hgpk_module_init(void)
+{
+}
static inline int hgpk_detect(struct psmouse *psmouse, bool set_properties)
{
return -ENODEV;
diff --git a/drivers/input/mouse/psmouse-base.c b/drivers/input/mouse/psmouse-base.c
index cd9d0c97e42..3f74baee102 100644
--- a/drivers/input/mouse/psmouse-base.c
+++ b/drivers/input/mouse/psmouse-base.c
@@ -1711,6 +1711,7 @@ static int __init psmouse_init(void)
lifebook_module_init();
synaptics_module_init();
+ hgpk_module_init();
kpsmoused_wq = create_singlethread_workqueue("kpsmoused");
if (!kpsmoused_wq) {
diff --git a/drivers/input/mouse/synaptics.c b/drivers/input/mouse/synaptics.c
index 2e300a46055..da392c22fc6 100644
--- a/drivers/input/mouse/synaptics.c
+++ b/drivers/input/mouse/synaptics.c
@@ -25,7 +25,7 @@
#include <linux/module.h>
#include <linux/dmi.h>
-#include <linux/input.h>
+#include <linux/input/mt.h>
#include <linux/serio.h>
#include <linux/libps2.h>
#include <linux/slab.h>
@@ -279,6 +279,25 @@ static void synaptics_set_rate(struct psmouse *psmouse, unsigned int rate)
synaptics_mode_cmd(psmouse, priv->mode);
}
+static int synaptics_set_advanced_gesture_mode(struct psmouse *psmouse)
+{
+ static unsigned char param = 0xc8;
+ struct synaptics_data *priv = psmouse->private;
+
+ if (!SYN_CAP_ADV_GESTURE(priv->ext_cap_0c))
+ return 0;
+
+ if (psmouse_sliced_command(psmouse, SYN_QUE_MODEL))
+ return -1;
+ if (ps2_command(&psmouse->ps2dev, &param, PSMOUSE_CMD_SETRATE))
+ return -1;
+
+ /* Advanced gesture mode also sends multi finger data */
+ priv->capabilities |= BIT(1);
+
+ return 0;
+}
+
/*****************************************************************************
* Synaptics pass-through PS/2 port support
****************************************************************************/
@@ -380,7 +399,9 @@ static void synaptics_pt_create(struct psmouse *psmouse)
* Functions to interpret the absolute mode packets
****************************************************************************/
-static void synaptics_parse_hw_state(unsigned char buf[], struct synaptics_data *priv, struct synaptics_hw_state *hw)
+static int synaptics_parse_hw_state(const unsigned char buf[],
+ struct synaptics_data *priv,
+ struct synaptics_hw_state *hw)
{
memset(hw, 0, sizeof(struct synaptics_hw_state));
@@ -397,6 +418,14 @@ static void synaptics_parse_hw_state(unsigned char buf[], struct synaptics_data
((buf[0] & 0x04) >> 1) |
((buf[3] & 0x04) >> 2));
+ if (SYN_CAP_ADV_GESTURE(priv->ext_cap_0c) && hw->w == 2) {
+ /* Gesture packet: (x, y, z) at half resolution */
+ priv->mt.x = (((buf[4] & 0x0f) << 8) | buf[1]) << 1;
+ priv->mt.y = (((buf[4] & 0xf0) << 4) | buf[2]) << 1;
+ priv->mt.z = ((buf[3] & 0x30) | (buf[5] & 0x0f)) << 1;
+ return 1;
+ }
+
hw->left = (buf[0] & 0x01) ? 1 : 0;
hw->right = (buf[0] & 0x02) ? 1 : 0;
@@ -452,6 +481,36 @@ static void synaptics_parse_hw_state(unsigned char buf[], struct synaptics_data
hw->left = (buf[0] & 0x01) ? 1 : 0;
hw->right = (buf[0] & 0x02) ? 1 : 0;
}
+
+ return 0;
+}
+
+static void set_slot(struct input_dev *dev, int slot, bool active, int x, int y)
+{
+ input_mt_slot(dev, slot);
+ input_mt_report_slot_state(dev, MT_TOOL_FINGER, active);
+ if (active) {
+ input_report_abs(dev, ABS_MT_POSITION_X, x);
+ input_report_abs(dev, ABS_MT_POSITION_Y,
+ YMAX_NOMINAL + YMIN_NOMINAL - y);
+ }
+}
+
+static void synaptics_report_semi_mt_data(struct input_dev *dev,
+ const struct synaptics_hw_state *a,
+ const struct synaptics_hw_state *b,
+ int num_fingers)
+{
+ if (num_fingers >= 2) {
+ set_slot(dev, 0, true, min(a->x, b->x), min(a->y, b->y));
+ set_slot(dev, 1, true, max(a->x, b->x), max(a->y, b->y));
+ } else if (num_fingers == 1) {
+ set_slot(dev, 0, true, a->x, a->y);
+ set_slot(dev, 1, false, 0, 0);
+ } else {
+ set_slot(dev, 0, false, 0, 0);
+ set_slot(dev, 1, false, 0, 0);
+ }
}
/*
@@ -466,7 +525,8 @@ static void synaptics_process_packet(struct psmouse *psmouse)
int finger_width;
int i;
- synaptics_parse_hw_state(psmouse->packet, priv, &hw);
+ if (synaptics_parse_hw_state(psmouse->packet, priv, &hw))
+ return;
if (hw.scroll) {
priv->scroll += hw.scroll;
@@ -488,7 +548,7 @@ static void synaptics_process_packet(struct psmouse *psmouse)
return;
}
- if (hw.z > 0) {
+ if (hw.z > 0 && hw.x > 1) {
num_fingers = 1;
finger_width = 5;
if (SYN_CAP_EXTENDED(priv->capabilities)) {
@@ -512,6 +572,9 @@ static void synaptics_process_packet(struct psmouse *psmouse)
finger_width = 0;
}
+ if (SYN_CAP_ADV_GESTURE(priv->ext_cap_0c))
+ synaptics_report_semi_mt_data(dev, &hw, &priv->mt, num_fingers);
+
/* Post events
* BTN_TOUCH has to be first as mousedev relies on it when doing
* absolute -> relative conversion
@@ -519,7 +582,7 @@ static void synaptics_process_packet(struct psmouse *psmouse)
if (hw.z > 30) input_report_key(dev, BTN_TOUCH, 1);
if (hw.z < 25) input_report_key(dev, BTN_TOUCH, 0);
- if (hw.z > 0) {
+ if (num_fingers > 0) {
input_report_abs(dev, ABS_X, hw.x);
input_report_abs(dev, ABS_Y, YMAX_NOMINAL + YMIN_NOMINAL - hw.y);
}
@@ -622,6 +685,8 @@ static void set_input_params(struct input_dev *dev, struct synaptics_data *priv)
{
int i;
+ __set_bit(INPUT_PROP_POINTER, dev->propbit);
+
__set_bit(EV_ABS, dev->evbit);
input_set_abs_params(dev, ABS_X,
XMIN_NOMINAL, priv->x_max ?: XMAX_NOMINAL, 0, 0);
@@ -629,6 +694,15 @@ static void set_input_params(struct input_dev *dev, struct synaptics_data *priv)
YMIN_NOMINAL, priv->y_max ?: YMAX_NOMINAL, 0, 0);
input_set_abs_params(dev, ABS_PRESSURE, 0, 255, 0, 0);
+ if (SYN_CAP_ADV_GESTURE(priv->ext_cap_0c)) {
+ __set_bit(INPUT_PROP_SEMI_MT, dev->propbit);
+ input_mt_init_slots(dev, 2);
+ input_set_abs_params(dev, ABS_MT_POSITION_X, XMIN_NOMINAL,
+ priv->x_max ?: XMAX_NOMINAL, 0, 0);
+ input_set_abs_params(dev, ABS_MT_POSITION_Y, YMIN_NOMINAL,
+ priv->y_max ?: YMAX_NOMINAL, 0, 0);
+ }
+
if (SYN_CAP_PALMDETECT(priv->capabilities))
input_set_abs_params(dev, ABS_TOOL_WIDTH, 0, 15, 0, 0);
@@ -663,6 +737,7 @@ static void set_input_params(struct input_dev *dev, struct synaptics_data *priv)
input_abs_set_res(dev, ABS_Y, priv->y_res);
if (SYN_CAP_CLICKPAD(priv->ext_cap_0c)) {
+ __set_bit(INPUT_PROP_BUTTONPAD, dev->propbit);
/* Clickpads report only left button */
__clear_bit(BTN_RIGHT, dev->keybit);
__clear_bit(BTN_MIDDLE, dev->keybit);
@@ -702,6 +777,11 @@ static int synaptics_reconnect(struct psmouse *psmouse)
return -1;
}
+ if (synaptics_set_advanced_gesture_mode(psmouse)) {
+ printk(KERN_ERR "Advanced gesture mode reconnect failed.\n");
+ return -1;
+ }
+
return 0;
}
@@ -744,15 +824,45 @@ static const struct dmi_system_id __initconst toshiba_dmi_table[] = {
#endif
};
+static bool broken_olpc_ec;
+
+static const struct dmi_system_id __initconst olpc_dmi_table[] = {
+#if defined(CONFIG_DMI) && defined(CONFIG_OLPC)
+ {
+ /* OLPC XO-1 or XO-1.5 */
+ .matches = {
+ DMI_MATCH(DMI_SYS_VENDOR, "OLPC"),
+ DMI_MATCH(DMI_PRODUCT_NAME, "XO"),
+ },
+ },
+ { }
+#endif
+};
+
void __init synaptics_module_init(void)
{
impaired_toshiba_kbc = dmi_check_system(toshiba_dmi_table);
+ broken_olpc_ec = dmi_check_system(olpc_dmi_table);
}
int synaptics_init(struct psmouse *psmouse)
{
struct synaptics_data *priv;
+ /*
+ * The OLPC XO has issues with Synaptics' absolute mode; similarly to
+ * the HGPK, it quickly degrades and the hardware becomes jumpy and
+ * overly sensitive. Not only that, but the constant packet spew
+ * (even at a lowered 40pps rate) overloads the EC such that key
+ * presses on the keyboard are missed. Given all of that, don't
+ * even attempt to use Synaptics mode. Relative mode seems to work
+ * just fine.
+ */
+ if (broken_olpc_ec) {
+ printk(KERN_INFO "synaptics: OLPC XO detected, not enabling Synaptics protocol.\n");
+ return -ENODEV;
+ }
+
psmouse->private = priv = kzalloc(sizeof(struct synaptics_data), GFP_KERNEL);
if (!priv)
return -ENOMEM;
@@ -769,6 +879,11 @@ int synaptics_init(struct psmouse *psmouse)
goto init_fail;
}
+ if (synaptics_set_advanced_gesture_mode(psmouse)) {
+ printk(KERN_ERR "Advanced gesture mode init failed.\n");
+ goto init_fail;
+ }
+
priv->pkt_type = SYN_MODEL_NEWABS(priv->model_id) ? SYN_NEWABS : SYN_OLDABS;
printk(KERN_INFO "Synaptics Touchpad, model: %ld, fw: %ld.%ld, id: %#lx, caps: %#lx/%#lx/%#lx\n",
@@ -802,8 +917,8 @@ int synaptics_init(struct psmouse *psmouse)
/*
* Toshiba's KBC seems to have trouble handling data from
- * Synaptics as full rate, switch to lower rate which is roughly
- * thye same as rate of standard PS/2 mouse.
+ * Synaptics at full rate. Switch to a lower rate (roughly
+ * the same rate as a standard PS/2 mouse).
*/
if (psmouse->rate >= 80 && impaired_toshiba_kbc) {
printk(KERN_INFO "synaptics: Toshiba %s detected, limiting rate to 40pps.\n",
diff --git a/drivers/input/mouse/synaptics.h b/drivers/input/mouse/synaptics.h
index 613a3652f98..25e5d042a72 100644
--- a/drivers/input/mouse/synaptics.h
+++ b/drivers/input/mouse/synaptics.h
@@ -51,8 +51,10 @@
#define SYN_EXT_CAP_REQUESTS(c) (((c) & 0x700000) >> 20)
#define SYN_CAP_MULTI_BUTTON_NO(ec) (((ec) & 0x00f000) >> 12)
#define SYN_CAP_PRODUCT_ID(ec) (((ec) & 0xff0000) >> 16)
-#define SYN_CAP_CLICKPAD(ex0c) ((ex0c) & 0x100100)
+#define SYN_CAP_CLICKPAD(ex0c) ((ex0c) & 0x100000) /* 1-button ClickPad */
+#define SYN_CAP_CLICKPAD2BTN(ex0c) ((ex0c) & 0x000100) /* 2-button ClickPad */
#define SYN_CAP_MAX_DIMENSIONS(ex0c) ((ex0c) & 0x020000)
+#define SYN_CAP_ADV_GESTURE(ex0c) ((ex0c) & 0x080000)
/* synaptics modes query bits */
#define SYN_MODE_ABSOLUTE(m) ((m) & (1 << 7))
@@ -112,6 +114,8 @@ struct synaptics_data {
int scroll;
struct serio *pt_port; /* Pass-through serio port */
+
+ struct synaptics_hw_state mt; /* current gesture packet */
};
void synaptics_module_init(void);
diff --git a/drivers/input/mousedev.c b/drivers/input/mousedev.c
index 2a00ddf4f23..7630273e947 100644
--- a/drivers/input/mousedev.c
+++ b/drivers/input/mousedev.c
@@ -9,6 +9,8 @@
* the Free Software Foundation.
*/
+#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
+
#define MOUSEDEV_MINOR_BASE 32
#define MOUSEDEV_MINORS 32
#define MOUSEDEV_MIX 31
@@ -977,7 +979,7 @@ static int mousedev_connect(struct input_handler *handler,
break;
if (minor == MOUSEDEV_MINORS) {
- printk(KERN_ERR "mousedev: no more free mousedev devices\n");
+ pr_err("no more free mousedev devices\n");
return -ENFILE;
}
@@ -1087,13 +1089,13 @@ static int __init mousedev_init(void)
#ifdef CONFIG_INPUT_MOUSEDEV_PSAUX
error = misc_register(&psaux_mouse);
if (error)
- printk(KERN_WARNING "mice: could not register psaux device, "
- "error: %d\n", error);
+ pr_warning("could not register psaux device, error: %d\n",
+ error);
else
psaux_registered = 1;
#endif
- printk(KERN_INFO "mice: PS/2 mouse device common for all mice\n");
+ pr_info("PS/2 mouse device common for all mice\n");
return 0;
}
diff --git a/drivers/input/serio/Kconfig b/drivers/input/serio/Kconfig
index 6256233d2bf..307eef77a17 100644
--- a/drivers/input/serio/Kconfig
+++ b/drivers/input/serio/Kconfig
@@ -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/ams_delta_serio.c b/drivers/input/serio/ams_delta_serio.c
index 8f1770e1e08..ebe95532567 100644
--- a/drivers/input/serio/ams_delta_serio.c
+++ b/drivers/input/serio/ams_delta_serio.c
@@ -172,6 +172,5 @@ static void __exit ams_delta_serio_exit(void)
free_irq(OMAP_GPIO_IRQ(AMS_DELTA_GPIO_PIN_KEYBRD_CLK), 0);
gpio_free(AMS_DELTA_GPIO_PIN_KEYBRD_CLK);
gpio_free(AMS_DELTA_GPIO_PIN_KEYBRD_DATA);
- kfree(ams_delta_serio);
}
module_exit(ams_delta_serio_exit);
diff --git a/drivers/input/serio/ct82c710.c b/drivers/input/serio/ct82c710.c
index 4a3084695c0..448c7724beb 100644
--- a/drivers/input/serio/ct82c710.c
+++ b/drivers/input/serio/ct82c710.c
@@ -191,6 +191,9 @@ static int __devinit ct82c710_probe(struct platform_device *dev)
serio_register_port(ct82c710_port);
+ printk(KERN_INFO "serio: C&T 82c710 mouse port at %#llx irq %d\n",
+ (unsigned long long)CT82C710_DATA, CT82C710_IRQ);
+
return 0;
}
@@ -237,11 +240,6 @@ static int __init ct82c710_init(void)
if (error)
goto err_free_device;
- serio_register_port(ct82c710_port);
-
- printk(KERN_INFO "serio: C&T 82c710 mouse port at %#llx irq %d\n",
- (unsigned long long)CT82C710_DATA, CT82C710_IRQ);
-
return 0;
err_free_device:
diff --git a/drivers/input/serio/hil_mlc.c b/drivers/input/serio/hil_mlc.c
index e5624d8f170..bfd3865d886 100644
--- a/drivers/input/serio/hil_mlc.c
+++ b/drivers/input/serio/hil_mlc.c
@@ -932,6 +932,11 @@ int hil_mlc_register(hil_mlc *mlc)
hil_mlc_copy_di_scratch(mlc, i);
mlc_serio = kzalloc(sizeof(*mlc_serio), GFP_KERNEL);
mlc->serio[i] = mlc_serio;
+ if (!mlc->serio[i]) {
+ for (; i >= 0; i--)
+ kfree(mlc->serio[i]);
+ return -ENOMEM;
+ }
snprintf(mlc_serio->name, sizeof(mlc_serio->name)-1, "HIL_SERIO%d", i);
snprintf(mlc_serio->phys, sizeof(mlc_serio->phys)-1, "HIL%d", i);
mlc_serio->id = hil_mlc_serio_id;
diff --git a/drivers/input/serio/hp_sdc_mlc.c b/drivers/input/serio/hp_sdc_mlc.c
index 7d2b820ef58..d50f0678bf4 100644
--- a/drivers/input/serio/hp_sdc_mlc.c
+++ b/drivers/input/serio/hp_sdc_mlc.c
@@ -305,6 +305,7 @@ static void hp_sdc_mlc_out(hil_mlc *mlc)
static int __init hp_sdc_mlc_init(void)
{
hil_mlc *mlc = &hp_sdc_mlc;
+ int err;
#ifdef __mc68000__
if (!MACH_IS_HP300)
@@ -323,22 +324,21 @@ static int __init hp_sdc_mlc_init(void)
mlc->out = &hp_sdc_mlc_out;
mlc->priv = &hp_sdc_mlc_priv;
- if (hil_mlc_register(mlc)) {
+ err = hil_mlc_register(mlc);
+ if (err) {
printk(KERN_WARNING PREFIX "Failed to register MLC structure with hil_mlc\n");
- goto err0;
+ return err;
}
if (hp_sdc_request_hil_irq(&hp_sdc_mlc_isr)) {
printk(KERN_WARNING PREFIX "Request for raw HIL ISR hook denied\n");
- goto err1;
+ if (hil_mlc_unregister(mlc))
+ printk(KERN_ERR PREFIX "Failed to unregister MLC structure with hil_mlc.\n"
+ "This is bad. Could cause an oops.\n");
+ return -EBUSY;
}
+
return 0;
- err1:
- if (hil_mlc_unregister(mlc))
- printk(KERN_ERR PREFIX "Failed to unregister MLC structure with hil_mlc.\n"
- "This is bad. Could cause an oops.\n");
- err0:
- return -EBUSY;
}
static void __exit hp_sdc_mlc_exit(void)
diff --git a/drivers/input/serio/i8042-x86ia64io.h b/drivers/input/serio/i8042-x86ia64io.h
index a5475b57708..bb9f5d31f0d 100644
--- a/drivers/input/serio/i8042-x86ia64io.h
+++ b/drivers/input/serio/i8042-x86ia64io.h
@@ -424,6 +424,13 @@ static const struct dmi_system_id __initconst i8042_dmi_nomux_table[] = {
DMI_MATCH(DMI_PRODUCT_VERSION, "0100"),
},
},
+ {
+ /* Dell Vostro V13 */
+ .matches = {
+ DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc."),
+ DMI_MATCH(DMI_PRODUCT_NAME, "Vostro V13"),
+ },
+ },
{ }
};
@@ -545,6 +552,17 @@ static const struct dmi_system_id __initconst i8042_dmi_laptop_table[] = {
};
#endif
+static const struct dmi_system_id __initconst i8042_dmi_notimeout_table[] = {
+ {
+ /* Dell Vostro V13 */
+ .matches = {
+ DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc."),
+ DMI_MATCH(DMI_PRODUCT_NAME, "Vostro V13"),
+ },
+ },
+ { }
+};
+
/*
* Some Wistron based laptops need us to explicitly enable the 'Dritek
* keyboard extension' to make their extra keys start generating scancodes.
@@ -553,6 +571,13 @@ static const struct dmi_system_id __initconst i8042_dmi_laptop_table[] = {
*/
static const struct dmi_system_id __initconst i8042_dmi_dritek_table[] = {
{
+ /* Acer Aspire 5100 */
+ .matches = {
+ DMI_MATCH(DMI_SYS_VENDOR, "Acer"),
+ DMI_MATCH(DMI_PRODUCT_NAME, "Aspire 5100"),
+ },
+ },
+ {
/* Acer Aspire 5610 */
.matches = {
DMI_MATCH(DMI_SYS_VENDOR, "Acer"),
@@ -752,7 +777,7 @@ static int __init i8042_pnp_init(void)
#endif
if (i8042_nopnp) {
- printk(KERN_INFO "i8042: PNP detection disabled\n");
+ pr_info("PNP detection disabled\n");
return 0;
}
@@ -769,7 +794,7 @@ static int __init i8042_pnp_init(void)
#if defined(__ia64__)
return -ENODEV;
#else
- printk(KERN_INFO "PNP: No PS/2 controller found. Probing ports directly.\n");
+ pr_info("PNP: No PS/2 controller found. Probing ports directly.\n");
return 0;
#endif
}
@@ -781,7 +806,7 @@ static int __init i8042_pnp_init(void)
snprintf(aux_irq_str, sizeof(aux_irq_str),
"%d", i8042_pnp_aux_irq);
- printk(KERN_INFO "PNP: PS/2 Controller [%s%s%s] at %#x,%#x irq %s%s%s\n",
+ pr_info("PNP: PS/2 Controller [%s%s%s] at %#x,%#x irq %s%s%s\n",
i8042_pnp_kbd_name, (i8042_pnp_kbd_devices && i8042_pnp_aux_devices) ? "," : "",
i8042_pnp_aux_name,
i8042_pnp_data_reg, i8042_pnp_command_reg,
@@ -798,9 +823,7 @@ static int __init i8042_pnp_init(void)
if (((i8042_pnp_data_reg & ~0xf) == (i8042_data_reg & ~0xf) &&
i8042_pnp_data_reg != i8042_data_reg) ||
!i8042_pnp_data_reg) {
- printk(KERN_WARNING
- "PNP: PS/2 controller has invalid data port %#x; "
- "using default %#x\n",
+ pr_warn("PNP: PS/2 controller has invalid data port %#x; using default %#x\n",
i8042_pnp_data_reg, i8042_data_reg);
i8042_pnp_data_reg = i8042_data_reg;
pnp_data_busted = true;
@@ -809,33 +832,27 @@ static int __init i8042_pnp_init(void)
if (((i8042_pnp_command_reg & ~0xf) == (i8042_command_reg & ~0xf) &&
i8042_pnp_command_reg != i8042_command_reg) ||
!i8042_pnp_command_reg) {
- printk(KERN_WARNING
- "PNP: PS/2 controller has invalid command port %#x; "
- "using default %#x\n",
+ pr_warn("PNP: PS/2 controller has invalid command port %#x; using default %#x\n",
i8042_pnp_command_reg, i8042_command_reg);
i8042_pnp_command_reg = i8042_command_reg;
pnp_data_busted = true;
}
if (!i8042_nokbd && !i8042_pnp_kbd_irq) {
- printk(KERN_WARNING
- "PNP: PS/2 controller doesn't have KBD irq; "
- "using default %d\n", i8042_kbd_irq);
+ pr_warn("PNP: PS/2 controller doesn't have KBD irq; using default %d\n",
+ i8042_kbd_irq);
i8042_pnp_kbd_irq = i8042_kbd_irq;
pnp_data_busted = true;
}
if (!i8042_noaux && !i8042_pnp_aux_irq) {
if (!pnp_data_busted && i8042_pnp_kbd_irq) {
- printk(KERN_WARNING
- "PNP: PS/2 appears to have AUX port disabled, "
- "if this is incorrect please boot with "
- "i8042.nopnp\n");
+ pr_warn("PNP: PS/2 appears to have AUX port disabled, "
+ "if this is incorrect please boot with i8042.nopnp\n");
i8042_noaux = true;
} else {
- printk(KERN_WARNING
- "PNP: PS/2 controller doesn't have AUX irq; "
- "using default %d\n", i8042_aux_irq);
+ pr_warn("PNP: PS/2 controller doesn't have AUX irq; using default %d\n",
+ i8042_aux_irq);
i8042_pnp_aux_irq = i8042_aux_irq;
}
}
@@ -897,6 +914,9 @@ static int __init i8042_platform_init(void)
if (dmi_check_system(i8042_dmi_nomux_table))
i8042_nomux = true;
+ if (dmi_check_system(i8042_dmi_notimeout_table))
+ i8042_notimeout = true;
+
if (dmi_check_system(i8042_dmi_dritek_table))
i8042_dritek = true;
#endif /* CONFIG_X86 */
diff --git a/drivers/input/serio/i8042.c b/drivers/input/serio/i8042.c
index 18db5a8c747..ac4c93689ab 100644
--- a/drivers/input/serio/i8042.c
+++ b/drivers/input/serio/i8042.c
@@ -10,6 +10,8 @@
* the Free Software Foundation.
*/
+#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
+
#include <linux/types.h>
#include <linux/delay.h>
#include <linux/module.h>
@@ -61,6 +63,10 @@ static bool i8042_noloop;
module_param_named(noloop, i8042_noloop, bool, 0);
MODULE_PARM_DESC(noloop, "Disable the AUX Loopback command while probing for the AUX port");
+static bool i8042_notimeout;
+module_param_named(notimeout, i8042_notimeout, bool, 0);
+MODULE_PARM_DESC(notimeout, "Ignore timeouts signalled by i8042");
+
#ifdef CONFIG_X86
static bool i8042_dritek;
module_param_named(dritek, i8042_dritek, bool, 0);
@@ -225,8 +231,8 @@ static int i8042_flush(void)
udelay(50);
data = i8042_read_data();
i++;
- dbg("%02x <- i8042 (flush, %s)", data,
- str & I8042_STR_AUXDATA ? "aux" : "kbd");
+ dbg("%02x <- i8042 (flush, %s)\n",
+ data, str & I8042_STR_AUXDATA ? "aux" : "kbd");
}
spin_unlock_irqrestore(&i8042_lock, flags);
@@ -253,32 +259,32 @@ static int __i8042_command(unsigned char *param, int command)
if (error)
return error;
- dbg("%02x -> i8042 (command)", command & 0xff);
+ dbg("%02x -> i8042 (command)\n", command & 0xff);
i8042_write_command(command & 0xff);
for (i = 0; i < ((command >> 12) & 0xf); i++) {
error = i8042_wait_write();
if (error)
return error;
- dbg("%02x -> i8042 (parameter)", param[i]);
+ dbg("%02x -> i8042 (parameter)\n", param[i]);
i8042_write_data(param[i]);
}
for (i = 0; i < ((command >> 8) & 0xf); i++) {
error = i8042_wait_read();
if (error) {
- dbg(" -- i8042 (timeout)");
+ dbg(" -- i8042 (timeout)\n");
return error;
}
if (command == I8042_CMD_AUX_LOOP &&
!(i8042_read_status() & I8042_STR_AUXDATA)) {
- dbg(" -- i8042 (auxerr)");
+ dbg(" -- i8042 (auxerr)\n");
return -1;
}
param[i] = i8042_read_data();
- dbg("%02x <- i8042 (return)", param[i]);
+ dbg("%02x <- i8042 (return)\n", param[i]);
}
return 0;
@@ -309,7 +315,7 @@ static int i8042_kbd_write(struct serio *port, unsigned char c)
spin_lock_irqsave(&i8042_lock, flags);
if (!(retval = i8042_wait_write())) {
- dbg("%02x -> i8042 (kbd-data)", c);
+ dbg("%02x -> i8042 (kbd-data)\n", c);
i8042_write_data(c);
}
@@ -355,17 +361,14 @@ static void i8042_port_close(struct serio *serio)
i8042_ctr &= ~irq_bit;
if (i8042_command(&i8042_ctr, I8042_CMD_CTL_WCTR))
- printk(KERN_WARNING
- "i8042.c: Can't write CTR while closing %s port.\n",
- port_name);
+ pr_warn("Can't write CTR while closing %s port\n", port_name);
udelay(50);
i8042_ctr &= ~disable_bit;
i8042_ctr |= irq_bit;
if (i8042_command(&i8042_ctr, I8042_CMD_CTL_WCTR))
- printk(KERN_ERR "i8042.c: Can't reactivate %s port.\n",
- port_name);
+ pr_err("Can't reactivate %s port\n", port_name);
/*
* See if there is any data appeared while we were messing with
@@ -456,7 +459,8 @@ static irqreturn_t i8042_interrupt(int irq, void *dev_id)
str = i8042_read_status();
if (unlikely(~str & I8042_STR_OBF)) {
spin_unlock_irqrestore(&i8042_lock, flags);
- if (irq) dbg("Interrupt %d, without any data", irq);
+ if (irq)
+ dbg("Interrupt %d, without any data\n", irq);
ret = 0;
goto out;
}
@@ -469,7 +473,8 @@ static irqreturn_t i8042_interrupt(int irq, void *dev_id)
dfl = 0;
if (str & I8042_STR_MUXERR) {
- dbg("MUX error, status is %02x, data is %02x", str, data);
+ dbg("MUX error, status is %02x, data is %02x\n",
+ str, data);
/*
* When MUXERR condition is signalled the data register can only contain
* 0xfd, 0xfe or 0xff if implementation follows the spec. Unfortunately
@@ -503,7 +508,7 @@ static irqreturn_t i8042_interrupt(int irq, void *dev_id)
} else {
dfl = ((str & I8042_STR_PARITY) ? SERIO_PARITY : 0) |
- ((str & I8042_STR_TIMEOUT) ? SERIO_TIMEOUT : 0);
+ ((str & I8042_STR_TIMEOUT && !i8042_notimeout) ? SERIO_TIMEOUT : 0);
port_no = (str & I8042_STR_AUXDATA) ?
I8042_AUX_PORT_NO : I8042_KBD_PORT_NO;
@@ -512,7 +517,7 @@ static irqreturn_t i8042_interrupt(int irq, void *dev_id)
port = &i8042_ports[port_no];
serio = port->exists ? port->serio : NULL;
- dbg("%02x <- i8042 (interrupt, %d, %d%s%s)",
+ dbg("%02x <- i8042 (interrupt, %d, %d%s%s)\n",
data, port_no, irq,
dfl & SERIO_PARITY ? ", bad parity" : "",
dfl & SERIO_TIMEOUT ? ", timeout" : "");
@@ -540,7 +545,7 @@ static int i8042_enable_kbd_port(void)
if (i8042_command(&i8042_ctr, I8042_CMD_CTL_WCTR)) {
i8042_ctr &= ~I8042_CTR_KBDINT;
i8042_ctr |= I8042_CTR_KBDDIS;
- printk(KERN_ERR "i8042.c: Failed to enable KBD port.\n");
+ pr_err("Failed to enable KBD port\n");
return -EIO;
}
@@ -559,7 +564,7 @@ static int i8042_enable_aux_port(void)
if (i8042_command(&i8042_ctr, I8042_CMD_CTL_WCTR)) {
i8042_ctr &= ~I8042_CTR_AUXINT;
i8042_ctr |= I8042_CTR_AUXDIS;
- printk(KERN_ERR "i8042.c: Failed to enable AUX port.\n");
+ pr_err("Failed to enable AUX port\n");
return -EIO;
}
@@ -641,7 +646,7 @@ static int __init i8042_check_mux(void)
if (i8042_set_mux_mode(true, &mux_version))
return -1;
- printk(KERN_INFO "i8042.c: Detected active multiplexing controller, rev %d.%d.\n",
+ pr_info("Detected active multiplexing controller, rev %d.%d\n",
(mux_version >> 4) & 0xf, mux_version & 0xf);
/*
@@ -651,7 +656,7 @@ static int __init i8042_check_mux(void)
i8042_ctr &= ~I8042_CTR_AUXINT;
if (i8042_command(&i8042_ctr, I8042_CMD_CTL_WCTR)) {
- printk(KERN_ERR "i8042.c: Failed to disable AUX port, can't use MUX.\n");
+ pr_err("Failed to disable AUX port, can't use MUX\n");
return -EIO;
}
@@ -676,8 +681,8 @@ static irqreturn_t __init i8042_aux_test_irq(int irq, void *dev_id)
str = i8042_read_status();
if (str & I8042_STR_OBF) {
data = i8042_read_data();
- dbg("%02x <- i8042 (aux_test_irq, %s)",
- data, str & I8042_STR_AUXDATA ? "aux" : "kbd");
+ dbg("%02x <- i8042 (aux_test_irq, %s)\n",
+ data, str & I8042_STR_AUXDATA ? "aux" : "kbd");
if (i8042_irq_being_tested &&
data == 0xa5 && (str & I8042_STR_AUXDATA))
complete(&i8042_aux_irq_delivered);
@@ -770,8 +775,8 @@ static int __init i8042_check_aux(void)
*/
if (i8042_toggle_aux(false)) {
- printk(KERN_WARNING "Failed to disable AUX port, but continuing anyway... Is this a SiS?\n");
- printk(KERN_WARNING "If AUX port is really absent please use the 'i8042.noaux' option.\n");
+ pr_warn("Failed to disable AUX port, but continuing anyway... Is this a SiS?\n");
+ pr_warn("If AUX port is really absent please use the 'i8042.noaux' option\n");
}
if (i8042_toggle_aux(true))
@@ -819,7 +824,7 @@ static int __init i8042_check_aux(void)
* AUX IRQ was never delivered so we need to flush the controller to
* get rid of the byte we put there; otherwise keyboard may not work.
*/
- dbg(" -- i8042 (aux irq test timeout)");
+ dbg(" -- i8042 (aux irq test timeout)\n");
i8042_flush();
retval = -1;
}
@@ -845,7 +850,7 @@ static int __init i8042_check_aux(void)
static int i8042_controller_check(void)
{
if (i8042_flush() == I8042_BUFFER_SIZE) {
- printk(KERN_ERR "i8042.c: No controller found.\n");
+ pr_err("No controller found\n");
return -ENODEV;
}
@@ -864,15 +869,15 @@ static int i8042_controller_selftest(void)
do {
if (i8042_command(&param, I8042_CMD_CTL_TEST)) {
- printk(KERN_ERR "i8042.c: i8042 controller self test timeout.\n");
+ pr_err("i8042 controller self test timeout\n");
return -ENODEV;
}
if (param == I8042_RET_CTL_TEST)
return 0;
- printk(KERN_ERR "i8042.c: i8042 controller selftest failed. (%#x != %#x)\n",
- param, I8042_RET_CTL_TEST);
+ pr_err("i8042 controller selftest failed. (%#x != %#x)\n",
+ param, I8042_RET_CTL_TEST);
msleep(50);
} while (i++ < 5);
@@ -883,8 +888,7 @@ static int i8042_controller_selftest(void)
* and user will still get a working keyboard. This is especially
* important on netbooks. On other arches we trust hardware more.
*/
- printk(KERN_INFO
- "i8042: giving up on controller selftest, continuing anyway...\n");
+ pr_info("giving up on controller selftest, continuing anyway...\n");
return 0;
#else
return -EIO;
@@ -909,8 +913,7 @@ static int i8042_controller_init(void)
do {
if (n >= 10) {
- printk(KERN_ERR
- "i8042.c: Unable to get stable CTR read.\n");
+ pr_err("Unable to get stable CTR read\n");
return -EIO;
}
@@ -918,8 +921,7 @@ static int i8042_controller_init(void)
udelay(50);
if (i8042_command(&ctr[n++ % 2], I8042_CMD_CTL_RCTR)) {
- printk(KERN_ERR
- "i8042.c: Can't read CTR while initializing i8042.\n");
+ pr_err("Can't read CTR while initializing i8042\n");
return -EIO;
}
@@ -943,7 +945,7 @@ static int i8042_controller_init(void)
if (i8042_unlock)
i8042_ctr |= I8042_CTR_IGNKEYLOCK;
else
- printk(KERN_WARNING "i8042.c: Warning: Keylock active.\n");
+ pr_warn("Warning: Keylock active\n");
}
spin_unlock_irqrestore(&i8042_lock, flags);
@@ -970,7 +972,7 @@ static int i8042_controller_init(void)
*/
if (i8042_command(&i8042_ctr, I8042_CMD_CTL_WCTR)) {
- printk(KERN_ERR "i8042.c: Can't write CTR while initializing i8042.\n");
+ pr_err("Can't write CTR while initializing i8042\n");
return -EIO;
}
@@ -1000,7 +1002,7 @@ static void i8042_controller_reset(void)
i8042_ctr &= ~(I8042_CTR_KBDINT | I8042_CTR_AUXINT);
if (i8042_command(&i8042_ctr, I8042_CMD_CTL_WCTR))
- printk(KERN_WARNING "i8042.c: Can't write CTR while resetting.\n");
+ pr_warn("Can't write CTR while resetting\n");
/*
* Disable MUX mode if present.
@@ -1021,7 +1023,7 @@ static void i8042_controller_reset(void)
*/
if (i8042_command(&i8042_initial_ctr, I8042_CMD_CTL_WCTR))
- printk(KERN_WARNING "i8042.c: Can't restore CTR.\n");
+ pr_warn("Can't restore CTR\n");
}
@@ -1045,14 +1047,14 @@ static long i8042_panic_blink(int state)
led = (state) ? 0x01 | 0x04 : 0;
while (i8042_read_status() & I8042_STR_IBF)
DELAY;
- dbg("%02x -> i8042 (panic blink)", 0xed);
+ dbg("%02x -> i8042 (panic blink)\n", 0xed);
i8042_suppress_kbd_ack = 2;
i8042_write_data(0xed); /* set leds */
DELAY;
while (i8042_read_status() & I8042_STR_IBF)
DELAY;
DELAY;
- dbg("%02x -> i8042 (panic blink)", led);
+ dbg("%02x -> i8042 (panic blink)\n", led);
i8042_write_data(led);
DELAY;
return delay;
@@ -1068,9 +1070,7 @@ static void i8042_dritek_enable(void)
error = i8042_command(&param, 0x1059);
if (error)
- printk(KERN_WARNING
- "Failed to enable DRITEK extension: %d\n",
- error);
+ pr_warn("Failed to enable DRITEK extension: %d\n", error);
}
#endif
@@ -1105,10 +1105,10 @@ static int i8042_controller_resume(bool force_reset)
i8042_ctr |= I8042_CTR_AUXDIS | I8042_CTR_KBDDIS;
i8042_ctr &= ~(I8042_CTR_AUXINT | I8042_CTR_KBDINT);
if (i8042_command(&i8042_ctr, I8042_CMD_CTL_WCTR)) {
- printk(KERN_WARNING "i8042: Can't write CTR to resume, retrying...\n");
+ pr_warn("Can't write CTR to resume, retrying...\n");
msleep(50);
if (i8042_command(&i8042_ctr, I8042_CMD_CTL_WCTR)) {
- printk(KERN_ERR "i8042: CTR write retry failed\n");
+ pr_err("CTR write retry failed\n");
return -EIO;
}
}
@@ -1121,9 +1121,7 @@ static int i8042_controller_resume(bool force_reset)
if (i8042_mux_present) {
if (i8042_set_mux_mode(true, NULL) || i8042_enable_mux_ports())
- printk(KERN_WARNING
- "i8042: failed to resume active multiplexor, "
- "mouse won't work.\n");
+ pr_warn("failed to resume active multiplexor, mouse won't work\n");
} else if (i8042_ports[I8042_AUX_PORT_NO].serio)
i8042_enable_aux_port();
diff --git a/drivers/input/serio/i8042.h b/drivers/input/serio/i8042.h
index cbc1beb6657..ac1d759d0f5 100644
--- a/drivers/input/serio/i8042.h
+++ b/drivers/input/serio/i8042.h
@@ -89,15 +89,19 @@
#ifdef DEBUG
static unsigned long i8042_start_time;
#define dbg_init() do { i8042_start_time = jiffies; } while (0)
-#define dbg(format, arg...) \
- do { \
+#define dbg(format, arg...) \
+ do { \
if (i8042_debug) \
- printk(KERN_DEBUG __FILE__ ": " format " [%d]\n" , \
- ## arg, (int) (jiffies - i8042_start_time)); \
+ printk(KERN_DEBUG KBUILD_MODNAME ": [%d] " format, \
+ (int) (jiffies - i8042_start_time), ##arg); \
} while (0)
#else
#define dbg_init() do { } while (0)
-#define dbg(format, arg...) do {} while (0)
+#define dbg(format, arg...) \
+ do { \
+ if (0) \
+ printk(KERN_DEBUG pr_fmt(format), ##arg); \
+ } while (0)
#endif
#endif /* _I8042_H */
diff --git a/drivers/input/serio/ps2mult.c b/drivers/input/serio/ps2mult.c
index 6bce22e4e49..15aa81c9f1f 100644
--- a/drivers/input/serio/ps2mult.c
+++ b/drivers/input/serio/ps2mult.c
@@ -207,7 +207,7 @@ static int ps2mult_connect(struct serio *serio, struct serio_driver *drv)
err_out:
while (--i >= 0)
kfree(psm->ports[i].serio);
- kfree(serio);
+ kfree(psm);
return error;
}
diff --git a/drivers/input/serio/serio.c b/drivers/input/serio/serio.c
index 405bf214527..db5b0bca1a1 100644
--- a/drivers/input/serio/serio.c
+++ b/drivers/input/serio/serio.c
@@ -32,10 +32,9 @@
#include <linux/module.h>
#include <linux/serio.h>
#include <linux/errno.h>
-#include <linux/wait.h>
#include <linux/sched.h>
#include <linux/slab.h>
-#include <linux/kthread.h>
+#include <linux/workqueue.h>
#include <linux/mutex.h>
MODULE_AUTHOR("Vojtech Pavlik <vojtech@ucw.cz>");
@@ -44,7 +43,7 @@ MODULE_LICENSE("GPL");
/*
* serio_mutex protects entire serio subsystem and is taken every time
- * serio port or driver registrered or unregistered.
+ * serio port or driver registered or unregistered.
*/
static DEFINE_MUTEX(serio_mutex);
@@ -165,58 +164,22 @@ struct serio_event {
static DEFINE_SPINLOCK(serio_event_lock); /* protects serio_event_list */
static LIST_HEAD(serio_event_list);
-static DECLARE_WAIT_QUEUE_HEAD(serio_wait);
-static struct task_struct *serio_task;
-static int serio_queue_event(void *object, struct module *owner,
- enum serio_event_type event_type)
+static struct serio_event *serio_get_event(void)
{
+ struct serio_event *event = NULL;
unsigned long flags;
- struct serio_event *event;
- int retval = 0;
spin_lock_irqsave(&serio_event_lock, flags);
- /*
- * Scan event list for the other events for the same serio port,
- * starting with the most recent one. If event is the same we
- * do not need add new one. If event is of different type we
- * need to add this event and should not look further because
- * we need to preseve sequence of distinct events.
- */
- list_for_each_entry_reverse(event, &serio_event_list, node) {
- if (event->object == object) {
- if (event->type == event_type)
- goto out;
- break;
- }
- }
-
- event = kmalloc(sizeof(struct serio_event), GFP_ATOMIC);
- if (!event) {
- pr_err("Not enough memory to queue event %d\n", event_type);
- retval = -ENOMEM;
- goto out;
- }
-
- if (!try_module_get(owner)) {
- pr_warning("Can't get module reference, dropping event %d\n",
- event_type);
- kfree(event);
- retval = -EINVAL;
- goto out;
+ if (!list_empty(&serio_event_list)) {
+ event = list_first_entry(&serio_event_list,
+ struct serio_event, node);
+ list_del_init(&event->node);
}
- event->type = event_type;
- event->object = object;
- event->owner = owner;
-
- list_add_tail(&event->node, &serio_event_list);
- wake_up(&serio_wait);
-
-out:
spin_unlock_irqrestore(&serio_event_lock, flags);
- return retval;
+ return event;
}
static void serio_free_event(struct serio_event *event)
@@ -250,25 +213,7 @@ static void serio_remove_duplicate_events(struct serio_event *event)
spin_unlock_irqrestore(&serio_event_lock, flags);
}
-
-static struct serio_event *serio_get_event(void)
-{
- struct serio_event *event = NULL;
- unsigned long flags;
-
- spin_lock_irqsave(&serio_event_lock, flags);
-
- if (!list_empty(&serio_event_list)) {
- event = list_first_entry(&serio_event_list,
- struct serio_event, node);
- list_del_init(&event->node);
- }
-
- spin_unlock_irqrestore(&serio_event_lock, flags);
- return event;
-}
-
-static void serio_handle_event(void)
+static void serio_handle_event(struct work_struct *work)
{
struct serio_event *event;
@@ -307,6 +252,59 @@ static void serio_handle_event(void)
mutex_unlock(&serio_mutex);
}
+static DECLARE_WORK(serio_event_work, serio_handle_event);
+
+static int serio_queue_event(void *object, struct module *owner,
+ enum serio_event_type event_type)
+{
+ unsigned long flags;
+ struct serio_event *event;
+ int retval = 0;
+
+ spin_lock_irqsave(&serio_event_lock, flags);
+
+ /*
+ * Scan event list for the other events for the same serio port,
+ * starting with the most recent one. If event is the same we
+ * do not need add new one. If event is of different type we
+ * need to add this event and should not look further because
+ * we need to preseve sequence of distinct events.
+ */
+ list_for_each_entry_reverse(event, &serio_event_list, node) {
+ if (event->object == object) {
+ if (event->type == event_type)
+ goto out;
+ break;
+ }
+ }
+
+ event = kmalloc(sizeof(struct serio_event), GFP_ATOMIC);
+ if (!event) {
+ pr_err("Not enough memory to queue event %d\n", event_type);
+ retval = -ENOMEM;
+ goto out;
+ }
+
+ if (!try_module_get(owner)) {
+ pr_warning("Can't get module reference, dropping event %d\n",
+ event_type);
+ kfree(event);
+ retval = -EINVAL;
+ goto out;
+ }
+
+ event->type = event_type;
+ event->object = object;
+ event->owner = owner;
+
+ list_add_tail(&event->node, &serio_event_list);
+ schedule_work(&serio_event_work);
+
+out:
+ spin_unlock_irqrestore(&serio_event_lock, flags);
+ return retval;
+}
+
/*
* Remove all events that have been submitted for a given
* object, be it serio port or driver.
@@ -356,18 +354,6 @@ static struct serio *serio_get_pending_child(struct serio *parent)
return child;
}
-static int serio_thread(void *nothing)
-{
- do {
- serio_handle_event();
- wait_event_interruptible(serio_wait,
- kthread_should_stop() || !list_empty(&serio_event_list));
- } while (!kthread_should_stop());
-
- return 0;
-}
-
-
/*
* Serio port operations
*/
@@ -1040,21 +1026,18 @@ static int __init serio_init(void)
return error;
}
- serio_task = kthread_run(serio_thread, NULL, "kseriod");
- if (IS_ERR(serio_task)) {
- bus_unregister(&serio_bus);
- error = PTR_ERR(serio_task);
- pr_err("Failed to start kseriod, error: %d\n", error);
- return error;
- }
-
return 0;
}
static void __exit serio_exit(void)
{
bus_unregister(&serio_bus);
- kthread_stop(serio_task);
+
+ /*
+ * There should not be any outstanding events but work may
+ * still be scheduled so simply cancel it.
+ */
+ cancel_work_sync(&serio_event_work);
}
subsys_initcall(serio_init);
diff --git a/drivers/input/tablet/wacom.h b/drivers/input/tablet/wacom.h
index de5adb10903..23317bd09c8 100644
--- a/drivers/input/tablet/wacom.h
+++ b/drivers/input/tablet/wacom.h
@@ -103,6 +103,7 @@ MODULE_DESCRIPTION(DRIVER_DESC);
MODULE_LICENSE(DRIVER_LICENSE);
#define USB_VENDOR_ID_WACOM 0x056a
+#define USB_VENDOR_ID_LENOVO 0x17ef
struct wacom {
dma_addr_t data_dma;
diff --git a/drivers/input/tablet/wacom_wac.c b/drivers/input/tablet/wacom_wac.c
index b3252ef1e27..518782999fe 100644
--- a/drivers/input/tablet/wacom_wac.c
+++ b/drivers/input/tablet/wacom_wac.c
@@ -14,6 +14,7 @@
#include "wacom_wac.h"
#include "wacom.h"
+#include <linux/input/mt.h>
static int wacom_penpartner_irq(struct wacom_wac *wacom)
{
@@ -862,19 +863,21 @@ static int wacom_bpt_touch(struct wacom_wac *wacom)
struct wacom_features *features = &wacom->features;
struct input_dev *input = wacom->input;
unsigned char *data = wacom->data;
- int sp = 0, sx = 0, sy = 0, count = 0;
int i;
for (i = 0; i < 2; i++) {
int p = data[9 * i + 2];
+ bool touch = p && !wacom->shared->stylus_in_proximity;
+
input_mt_slot(input, i);
+ input_mt_report_slot_state(input, MT_TOOL_FINGER, touch);
/*
* Touch events need to be disabled while stylus is
* in proximity because user's hand is resting on touchpad
* and sending unwanted events. User expects tablet buttons
* to continue working though.
*/
- if (p && !wacom->shared->stylus_in_proximity) {
+ if (touch) {
int x = get_unaligned_be16(&data[9 * i + 3]) & 0x7ff;
int y = get_unaligned_be16(&data[9 * i + 5]) & 0x7ff;
if (features->quirks & WACOM_QUIRK_BBTOUCH_LOWRES) {
@@ -884,23 +887,10 @@ static int wacom_bpt_touch(struct wacom_wac *wacom)
input_report_abs(input, ABS_MT_PRESSURE, p);
input_report_abs(input, ABS_MT_POSITION_X, x);
input_report_abs(input, ABS_MT_POSITION_Y, y);
- if (wacom->id[i] < 0)
- wacom->id[i] = wacom->trk_id++ & MAX_TRACKING_ID;
- if (!count++)
- sp = p, sx = x, sy = y;
- } else {
- wacom->id[i] = -1;
}
- input_report_abs(input, ABS_MT_TRACKING_ID, wacom->id[i]);
}
- input_report_key(input, BTN_TOUCH, count > 0);
- input_report_key(input, BTN_TOOL_FINGER, count == 1);
- input_report_key(input, BTN_TOOL_DOUBLETAP, count == 2);
-
- input_report_abs(input, ABS_PRESSURE, sp);
- input_report_abs(input, ABS_X, sx);
- input_report_abs(input, ABS_Y, sy);
+ input_mt_report_pointer_emulation(input, true);
input_report_key(input, BTN_LEFT, (data[1] & 0x08) != 0);
input_report_key(input, BTN_FORWARD, (data[1] & 0x04) != 0);
@@ -1272,7 +1262,7 @@ void wacom_setup_input_capabilities(struct input_dev *input_dev,
__set_bit(BTN_TOOL_FINGER, input_dev->keybit);
__set_bit(BTN_TOOL_DOUBLETAP, input_dev->keybit);
- input_mt_create_slots(input_dev, 2);
+ input_mt_init_slots(input_dev, 2);
input_set_abs_params(input_dev, ABS_MT_POSITION_X,
0, features->x_max,
features->x_fuzz, 0);
@@ -1282,8 +1272,6 @@ void wacom_setup_input_capabilities(struct input_dev *input_dev,
input_set_abs_params(input_dev, ABS_MT_PRESSURE,
0, features->pressure_max,
features->pressure_fuzz, 0);
- input_set_abs_params(input_dev, ABS_MT_TRACKING_ID, 0,
- MAX_TRACKING_ID, 0, 0);
} else if (features->device_type == BTN_TOOL_PEN) {
__set_bit(BTN_TOOL_RUBBER, input_dev->keybit);
__set_bit(BTN_TOOL_PEN, input_dev->keybit);
@@ -1436,11 +1424,25 @@ static struct wacom_features wacom_features_0xD2 =
{ "Wacom Bamboo Craft", WACOM_PKGLEN_BBFUN, 14720, 9200, 1023, 63, BAMBOO_PT };
static struct wacom_features wacom_features_0xD3 =
{ "Wacom Bamboo 2FG 6x8", WACOM_PKGLEN_BBFUN, 21648, 13530, 1023, 63, BAMBOO_PT };
+static const struct wacom_features wacom_features_0xD4 =
+ { "Wacom Bamboo Pen", WACOM_PKGLEN_BBFUN, 14720, 9200, 255, 63, BAMBOO_PT };
+static struct wacom_features wacom_features_0xD8 =
+ { "Wacom Bamboo Comic 2FG", WACOM_PKGLEN_BBFUN, 21648, 13530, 1023, 63, BAMBOO_PT };
+static struct wacom_features wacom_features_0xDA =
+ { "Wacom Bamboo 2FG 4x5 SE", WACOM_PKGLEN_BBFUN, 14720, 9200, 1023, 63, BAMBOO_PT };
+static struct wacom_features wacom_features_0xDB =
+ { "Wacom Bamboo 2FG 6x8 SE", WACOM_PKGLEN_BBFUN, 21648, 13530, 1023, 63, BAMBOO_PT };
+static const struct wacom_features wacom_features_0x6004 =
+ { "ISD-V4", WACOM_PKGLEN_GRAPHIRE, 12800, 8000, 255, 0, TABLETPC };
#define USB_DEVICE_WACOM(prod) \
USB_DEVICE(USB_VENDOR_ID_WACOM, prod), \
.driver_info = (kernel_ulong_t)&wacom_features_##prod
+#define USB_DEVICE_LENOVO(prod) \
+ USB_DEVICE(USB_VENDOR_ID_LENOVO, prod), \
+ .driver_info = (kernel_ulong_t)&wacom_features_##prod
+
const struct usb_device_id wacom_ids[] = {
{ USB_DEVICE_WACOM(0x00) },
{ USB_DEVICE_WACOM(0x10) },
@@ -1504,6 +1506,10 @@ const struct usb_device_id wacom_ids[] = {
{ USB_DEVICE_WACOM(0xD1) },
{ USB_DEVICE_WACOM(0xD2) },
{ USB_DEVICE_WACOM(0xD3) },
+ { USB_DEVICE_WACOM(0xD4) },
+ { USB_DEVICE_WACOM(0xD8) },
+ { USB_DEVICE_WACOM(0xDA) },
+ { USB_DEVICE_WACOM(0xDB) },
{ USB_DEVICE_WACOM(0xF0) },
{ USB_DEVICE_WACOM(0xCC) },
{ USB_DEVICE_WACOM(0x90) },
@@ -1513,6 +1519,7 @@ const struct usb_device_id wacom_ids[] = {
{ USB_DEVICE_WACOM(0xE2) },
{ USB_DEVICE_WACOM(0xE3) },
{ USB_DEVICE_WACOM(0x47) },
+ { USB_DEVICE_LENOVO(0x6004) },
{ }
};
MODULE_DEVICE_TABLE(usb, wacom_ids);
diff --git a/drivers/input/tablet/wacom_wac.h b/drivers/input/tablet/wacom_wac.h
index 00ca01541d8..b1310ec9720 100644
--- a/drivers/input/tablet/wacom_wac.h
+++ b/drivers/input/tablet/wacom_wac.h
@@ -42,9 +42,6 @@
#define WACOM_QUIRK_MULTI_INPUT 0x0001
#define WACOM_QUIRK_BBTOUCH_LOWRES 0x0002
-/* largest reported tracking id */
-#define MAX_TRACKING_ID 0xfff
-
enum {
PENPARTNER = 0,
GRAPHIRE,
@@ -100,7 +97,6 @@ struct wacom_wac {
int id[3];
__u32 serial[2];
int last_finger;
- int trk_id;
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 06ea8da95c6..0c9f4b158ff 100644
--- a/drivers/input/touchscreen/Kconfig
+++ b/drivers/input/touchscreen/Kconfig
@@ -610,7 +610,7 @@ config TOUCHSCREEN_USB_ZYTRONIC
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 EMBEDDED
depends on TOUCHSCREEN_USB_COMPOSITE
config TOUCHSCREEN_USB_NEXIO
@@ -659,17 +659,17 @@ config TOUCHSCREEN_PCAP
To compile this driver as a module, choose M here: the
module will be called pcap_ts.
-config TOUCHSCREEN_TPS6507X
- tristate "TPS6507x based touchscreens"
+config TOUCHSCREEN_ST1232
+ tristate "Sitronix ST1232 touchscreen controllers"
depends on I2C
help
- Say Y here if you have a TPS6507x based touchscreen
- controller.
+ Say Y here if you want to support Sitronix ST1232
+ touchscreen controller.
If unsure, say N.
To compile this driver as a module, choose M here: the
- module will be called tps6507x_ts.
+ module will be called st1232_ts.
config TOUCHSCREEN_STMPE
tristate "STMicroelectronics STMPE touchscreens"
@@ -681,4 +681,16 @@ config TOUCHSCREEN_STMPE
To compile this driver as a module, choose M here: the
module will be called stmpe-ts.
+config TOUCHSCREEN_TPS6507X
+ tristate "TPS6507x based touchscreens"
+ depends on I2C
+ help
+ Say Y here if you have a TPS6507x based touchscreen
+ controller.
+
+ If unsure, say N.
+
+ To compile this driver as a module, choose M here: the
+ module will be called tps6507x_ts.
+
endif
diff --git a/drivers/input/touchscreen/Makefile b/drivers/input/touchscreen/Makefile
index 7cc1b4f4b67..718bcc81495 100644
--- a/drivers/input/touchscreen/Makefile
+++ b/drivers/input/touchscreen/Makefile
@@ -39,6 +39,7 @@ 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
obj-$(CONFIG_TOUCHSCREEN_TNETV107X) += tnetv107x-ts.o
obj-$(CONFIG_TOUCHSCREEN_TOUCHIT213) += touchit213.o
diff --git a/drivers/input/touchscreen/ad7879-i2c.c b/drivers/input/touchscreen/ad7879-i2c.c
index d82a38ee9a3..4e4e58cec6c 100644
--- a/drivers/input/touchscreen/ad7879-i2c.c
+++ b/drivers/input/touchscreen/ad7879-i2c.c
@@ -10,14 +10,16 @@
#include <linux/i2c.h>
#include <linux/module.h>
#include <linux/types.h>
+#include <linux/pm.h>
#include "ad7879.h"
#define AD7879_DEVID 0x79 /* AD7879-1/AD7889-1 */
#ifdef CONFIG_PM
-static int ad7879_i2c_suspend(struct i2c_client *client, pm_message_t message)
+static int ad7879_i2c_suspend(struct device *dev)
{
+ struct i2c_client *client = to_i2c_client(dev);
struct ad7879 *ts = i2c_get_clientdata(client);
ad7879_suspend(ts);
@@ -25,17 +27,17 @@ static int ad7879_i2c_suspend(struct i2c_client *client, pm_message_t message)
return 0;
}
-static int ad7879_i2c_resume(struct i2c_client *client)
+static int ad7879_i2c_resume(struct device *dev)
{
+ struct i2c_client *client = to_i2c_client(dev);
struct ad7879 *ts = i2c_get_clientdata(client);
ad7879_resume(ts);
return 0;
}
-#else
-# define ad7879_i2c_suspend NULL
-# define ad7879_i2c_resume NULL
+
+static SIMPLE_DEV_PM_OPS(ad7879_i2c_pm, ad7879_i2c_suspend, ad7879_i2c_resume);
#endif
/* All registers are word-sized.
@@ -117,11 +119,12 @@ static struct i2c_driver ad7879_i2c_driver = {
.driver = {
.name = "ad7879",
.owner = THIS_MODULE,
+#ifdef CONFIG_PM
+ .pm = &ad7879_i2c_pm,
+#endif
},
.probe = ad7879_i2c_probe,
.remove = __devexit_p(ad7879_i2c_remove),
- .suspend = ad7879_i2c_suspend,
- .resume = ad7879_i2c_resume,
.id_table = ad7879_id,
};
diff --git a/drivers/input/touchscreen/bu21013_ts.c b/drivers/input/touchscreen/bu21013_ts.c
index 2ca9e5d6646..f7fa9ef4cd6 100644
--- a/drivers/input/touchscreen/bu21013_ts.c
+++ b/drivers/input/touchscreen/bu21013_ts.c
@@ -365,7 +365,7 @@ static int bu21013_init_chip(struct bu21013_ts_data *data)
}
retval = i2c_smbus_write_byte_data(i2c, BU21013_TH_OFF_REG,
- BU21013_TH_OFF_4 || BU21013_TH_OFF_3);
+ BU21013_TH_OFF_4 | BU21013_TH_OFF_3);
if (retval < 0) {
dev_err(&i2c->dev, "BU21013_TH_OFF reg write failed\n");
return retval;
diff --git a/drivers/input/touchscreen/cy8ctmg110_ts.c b/drivers/input/touchscreen/cy8ctmg110_ts.c
index d0c3a7229ad..a93c5c26ab3 100644
--- a/drivers/input/touchscreen/cy8ctmg110_ts.c
+++ b/drivers/input/touchscreen/cy8ctmg110_ts.c
@@ -280,8 +280,9 @@ err_free_mem:
}
#ifdef CONFIG_PM
-static int cy8ctmg110_suspend(struct i2c_client *client, pm_message_t mesg)
+static int cy8ctmg110_suspend(struct device *dev)
{
+ struct i2c_client *client = to_i2c_client(dev);
struct cy8ctmg110 *ts = i2c_get_clientdata(client);
if (device_may_wakeup(&client->dev))
@@ -293,8 +294,9 @@ static int cy8ctmg110_suspend(struct i2c_client *client, pm_message_t mesg)
return 0;
}
-static int cy8ctmg110_resume(struct i2c_client *client)
+static int cy8ctmg110_resume(struct device *dev)
{
+ struct i2c_client *client = to_i2c_client(dev);
struct cy8ctmg110 *ts = i2c_get_clientdata(client);
if (device_may_wakeup(&client->dev))
@@ -305,6 +307,8 @@ static int cy8ctmg110_resume(struct i2c_client *client)
}
return 0;
}
+
+static SIMPLE_DEV_PM_OPS(cy8ctmg110_pm, cy8ctmg110_suspend, cy8ctmg110_resume);
#endif
static int __devexit cy8ctmg110_remove(struct i2c_client *client)
@@ -335,14 +339,13 @@ static struct i2c_driver cy8ctmg110_driver = {
.driver = {
.owner = THIS_MODULE,
.name = CY8CTMG110_DRIVER_NAME,
+#ifdef CONFIG_PM
+ .pm = &cy8ctmg110_pm,
+#endif
},
.id_table = cy8ctmg110_idtable,
.probe = cy8ctmg110_probe,
.remove = __devexit_p(cy8ctmg110_remove),
-#ifdef CONFIG_PM
- .suspend = cy8ctmg110_suspend,
- .resume = cy8ctmg110_resume,
-#endif
};
static int __init cy8ctmg110_init(void)
diff --git a/drivers/input/touchscreen/eeti_ts.c b/drivers/input/touchscreen/eeti_ts.c
index 7a3a916f84a..7f8f538a980 100644
--- a/drivers/input/touchscreen/eeti_ts.c
+++ b/drivers/input/touchscreen/eeti_ts.c
@@ -261,8 +261,9 @@ static int __devexit eeti_ts_remove(struct i2c_client *client)
}
#ifdef CONFIG_PM
-static int eeti_ts_suspend(struct i2c_client *client, pm_message_t mesg)
+static int eeti_ts_suspend(struct device *dev)
{
+ struct i2c_client *client = to_i2c_client(dev);
struct eeti_ts_priv *priv = i2c_get_clientdata(client);
struct input_dev *input_dev = priv->input;
@@ -279,8 +280,9 @@ static int eeti_ts_suspend(struct i2c_client *client, pm_message_t mesg)
return 0;
}
-static int eeti_ts_resume(struct i2c_client *client)
+static int eeti_ts_resume(struct device *dev)
{
+ struct i2c_client *client = to_i2c_client(dev);
struct eeti_ts_priv *priv = i2c_get_clientdata(client);
struct input_dev *input_dev = priv->input;
@@ -296,9 +298,8 @@ static int eeti_ts_resume(struct i2c_client *client)
return 0;
}
-#else
-#define eeti_ts_suspend NULL
-#define eeti_ts_resume NULL
+
+static SIMPLE_DEV_PM_OPS(eeti_ts_pm, eeti_ts_suspend, eeti_ts_resume);
#endif
static const struct i2c_device_id eeti_ts_id[] = {
@@ -310,11 +311,12 @@ MODULE_DEVICE_TABLE(i2c, eeti_ts_id);
static struct i2c_driver eeti_ts_driver = {
.driver = {
.name = "eeti_ts",
+#ifdef CONFIG_PM
+ .pm = &eeti_ts_pm,
+#endif
},
.probe = eeti_ts_probe,
.remove = __devexit_p(eeti_ts_remove),
- .suspend = eeti_ts_suspend,
- .resume = eeti_ts_resume,
.id_table = eeti_ts_id,
};
diff --git a/drivers/input/touchscreen/mcs5000_ts.c b/drivers/input/touchscreen/mcs5000_ts.c
index 6ee9940aaf5..2d84c80ceb6 100644
--- a/drivers/input/touchscreen/mcs5000_ts.c
+++ b/drivers/input/touchscreen/mcs5000_ts.c
@@ -261,25 +261,27 @@ static int __devexit mcs5000_ts_remove(struct i2c_client *client)
}
#ifdef CONFIG_PM
-static int mcs5000_ts_suspend(struct i2c_client *client, pm_message_t mesg)
+static int mcs5000_ts_suspend(struct device *dev)
{
+ struct i2c_client *client = to_i2c_client(dev);
+
/* 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)
+static int mcs5000_ts_resume(struct device *dev)
{
+ struct i2c_client *client = to_i2c_client(dev);
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
+
+static SIMPLE_DEV_PM_OPS(mcs5000_ts_pm, mcs5000_ts_suspend, mcs5000_ts_resume);
#endif
static const struct i2c_device_id mcs5000_ts_id[] = {
@@ -291,10 +293,11 @@ 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",
+#ifdef CONFIG_PM
+ .pm = &mcs5000_ts_pm,
+#endif
},
.id_table = mcs5000_ts_id,
};
diff --git a/drivers/input/touchscreen/migor_ts.c b/drivers/input/touchscreen/migor_ts.c
index defe5dd3627..5803bd0c1cc 100644
--- a/drivers/input/touchscreen/migor_ts.c
+++ b/drivers/input/touchscreen/migor_ts.c
@@ -23,6 +23,7 @@
#include <linux/kernel.h>
#include <linux/input.h>
#include <linux/interrupt.h>
+#include <linux/pm.h>
#include <linux/slab.h>
#include <asm/io.h>
#include <linux/i2c.h>
@@ -226,8 +227,9 @@ static int migor_ts_remove(struct i2c_client *client)
return 0;
}
-static int migor_ts_suspend(struct i2c_client *client, pm_message_t mesg)
+static int migor_ts_suspend(struct device *dev)
{
+ struct i2c_client *client = to_i2c_client(dev);
struct migor_ts_priv *priv = dev_get_drvdata(&client->dev);
if (device_may_wakeup(&client->dev))
@@ -236,8 +238,9 @@ static int migor_ts_suspend(struct i2c_client *client, pm_message_t mesg)
return 0;
}
-static int migor_ts_resume(struct i2c_client *client)
+static int migor_ts_resume(struct device *dev)
{
+ struct i2c_client *client = to_i2c_client(dev);
struct migor_ts_priv *priv = dev_get_drvdata(&client->dev);
if (device_may_wakeup(&client->dev))
@@ -246,6 +249,8 @@ static int migor_ts_resume(struct i2c_client *client)
return 0;
}
+static SIMPLE_DEV_PM_OPS(migor_ts_pm, migor_ts_suspend, migor_ts_resume);
+
static const struct i2c_device_id migor_ts_id[] = {
{ "migor_ts", 0 },
{ }
@@ -255,11 +260,10 @@ MODULE_DEVICE_TABLE(i2c, migor_ts);
static struct i2c_driver migor_ts_driver = {
.driver = {
.name = "migor_ts",
+ .pm = &migor_ts_pm,
},
.probe = migor_ts_probe,
.remove = migor_ts_remove,
- .suspend = migor_ts_suspend,
- .resume = migor_ts_resume,
.id_table = migor_ts_id,
};
diff --git a/drivers/input/touchscreen/qt602240_ts.c b/drivers/input/touchscreen/qt602240_ts.c
index 66b26ad3032..4dcb0e872f6 100644
--- a/drivers/input/touchscreen/qt602240_ts.c
+++ b/drivers/input/touchscreen/qt602240_ts.c
@@ -969,7 +969,7 @@ static int qt602240_initialize(struct qt602240_data *data)
return error;
data->object_table = kcalloc(info->object_num,
- sizeof(struct qt602240_data),
+ sizeof(struct qt602240_object),
GFP_KERNEL);
if (!data->object_table) {
dev_err(&client->dev, "Failed to allocate memory\n");
@@ -1324,8 +1324,9 @@ static int __devexit qt602240_remove(struct i2c_client *client)
}
#ifdef CONFIG_PM
-static int qt602240_suspend(struct i2c_client *client, pm_message_t mesg)
+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;
@@ -1339,8 +1340,9 @@ static int qt602240_suspend(struct i2c_client *client, pm_message_t mesg)
return 0;
}
-static int qt602240_resume(struct i2c_client *client)
+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;
@@ -1359,9 +1361,11 @@ static int qt602240_resume(struct i2c_client *client)
return 0;
}
-#else
-#define qt602240_suspend NULL
-#define qt602240_resume NULL
+
+static const struct dev_pm_ops qt602240_pm_ops = {
+ .suspend = qt602240_suspend,
+ .resume = qt602240_resume,
+};
#endif
static const struct i2c_device_id qt602240_id[] = {
@@ -1374,11 +1378,12 @@ 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),
- .suspend = qt602240_suspend,
- .resume = qt602240_resume,
.id_table = qt602240_id,
};
diff --git a/drivers/input/touchscreen/st1232.c b/drivers/input/touchscreen/st1232.c
new file mode 100644
index 00000000000..4ab371358b3
--- /dev/null
+++ b/drivers/input/touchscreen/st1232.c
@@ -0,0 +1,274 @@
+/*
+ * ST1232 Touchscreen Controller Driver
+ *
+ * Copyright (C) 2010 Renesas Solutions Corp.
+ * Tony SIM <chinyeow.sim.xt@renesas.com>
+ *
+ * Using code from:
+ * - android.git.kernel.org: projects/kernel/common.git: synaptics_i2c_rmi.c
+ * Copyright (C) 2007 Google, Inc.
+ *
+ * This software is licensed under the terms of the GNU General Public
+ * License version 2, as published by the Free Software Foundation, and
+ * may be copied, distributed, and modified under those terms.
+ *
+ * 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.
+ */
+
+#include <linux/delay.h>
+#include <linux/i2c.h>
+#include <linux/input.h>
+#include <linux/interrupt.h>
+#include <linux/module.h>
+#include <linux/slab.h>
+#include <linux/types.h>
+
+#define ST1232_TS_NAME "st1232-ts"
+
+#define MIN_X 0x00
+#define MIN_Y 0x00
+#define MAX_X 0x31f /* (800 - 1) */
+#define MAX_Y 0x1df /* (480 - 1) */
+#define MAX_AREA 0xff
+#define MAX_FINGERS 2
+
+struct st1232_ts_finger {
+ u16 x;
+ u16 y;
+ u8 t;
+ bool is_valid;
+};
+
+struct st1232_ts_data {
+ struct i2c_client *client;
+ struct input_dev *input_dev;
+ struct st1232_ts_finger finger[MAX_FINGERS];
+};
+
+static int st1232_ts_read_data(struct st1232_ts_data *ts)
+{
+ struct st1232_ts_finger *finger = ts->finger;
+ struct i2c_client *client = ts->client;
+ struct i2c_msg msg[2];
+ int error;
+ u8 start_reg;
+ u8 buf[10];
+
+ /* read touchscreen data from ST1232 */
+ msg[0].addr = client->addr;
+ msg[0].flags = 0;
+ msg[0].len = 1;
+ msg[0].buf = &start_reg;
+ start_reg = 0x10;
+
+ msg[1].addr = ts->client->addr;
+ msg[1].flags = I2C_M_RD;
+ msg[1].len = sizeof(buf);
+ msg[1].buf = buf;
+
+ error = i2c_transfer(client->adapter, msg, 2);
+ if (error < 0)
+ return error;
+
+ /* get "valid" bits */
+ finger[0].is_valid = buf[2] >> 7;
+ finger[1].is_valid = buf[5] >> 7;
+
+ /* get xy coordinate */
+ if (finger[0].is_valid) {
+ finger[0].x = ((buf[2] & 0x0070) << 4) | buf[3];
+ finger[0].y = ((buf[2] & 0x0007) << 8) | buf[4];
+ finger[0].t = buf[8];
+ }
+
+ if (finger[1].is_valid) {
+ finger[1].x = ((buf[5] & 0x0070) << 4) | buf[6];
+ finger[1].y = ((buf[5] & 0x0007) << 8) | buf[7];
+ finger[1].t = buf[9];
+ }
+
+ return 0;
+}
+
+static irqreturn_t st1232_ts_irq_handler(int irq, void *dev_id)
+{
+ struct st1232_ts_data *ts = dev_id;
+ struct st1232_ts_finger *finger = ts->finger;
+ struct input_dev *input_dev = ts->input_dev;
+ int count = 0;
+ int i, ret;
+
+ ret = st1232_ts_read_data(ts);
+ if (ret < 0)
+ goto end;
+
+ /* multi touch protocol */
+ for (i = 0; i < MAX_FINGERS; i++) {
+ if (!finger[i].is_valid)
+ continue;
+
+ input_report_abs(input_dev, ABS_MT_TOUCH_MAJOR, finger[i].t);
+ input_report_abs(input_dev, ABS_MT_POSITION_X, finger[i].x);
+ input_report_abs(input_dev, ABS_MT_POSITION_Y, finger[i].y);
+ input_mt_sync(input_dev);
+ count++;
+ }
+
+ /* SYN_MT_REPORT only if no contact */
+ if (!count)
+ input_mt_sync(input_dev);
+
+ /* SYN_REPORT */
+ input_sync(input_dev);
+
+end:
+ return IRQ_HANDLED;
+}
+
+static int __devinit st1232_ts_probe(struct i2c_client *client,
+ const struct i2c_device_id *id)
+{
+ struct st1232_ts_data *ts;
+ struct input_dev *input_dev;
+ int error;
+
+ if (!i2c_check_functionality(client->adapter, I2C_FUNC_I2C)) {
+ dev_err(&client->dev, "need I2C_FUNC_I2C\n");
+ return -EIO;
+ }
+
+ if (!client->irq) {
+ dev_err(&client->dev, "no IRQ?\n");
+ return -EINVAL;
+ }
+
+
+ ts = kzalloc(sizeof(struct st1232_ts_data), GFP_KERNEL);
+ input_dev = input_allocate_device();
+ if (!ts || !input_dev) {
+ error = -ENOMEM;
+ goto err_free_mem;
+ }
+
+ ts->client = client;
+ ts->input_dev = input_dev;
+
+ input_dev->name = "st1232-touchscreen";
+ input_dev->id.bustype = BUS_I2C;
+ input_dev->dev.parent = &client->dev;
+
+ __set_bit(EV_SYN, input_dev->evbit);
+ __set_bit(EV_KEY, input_dev->evbit);
+ __set_bit(EV_ABS, input_dev->evbit);
+
+ input_set_abs_params(input_dev, ABS_MT_TOUCH_MAJOR, 0, MAX_AREA, 0, 0);
+ input_set_abs_params(input_dev, ABS_MT_POSITION_X, MIN_X, MAX_X, 0, 0);
+ input_set_abs_params(input_dev, ABS_MT_POSITION_Y, MIN_Y, MAX_Y, 0, 0);
+
+ error = request_threaded_irq(client->irq, NULL, st1232_ts_irq_handler,
+ IRQF_ONESHOT, client->name, ts);
+ if (error) {
+ dev_err(&client->dev, "Failed to register interrupt\n");
+ goto err_free_mem;
+ }
+
+ error = input_register_device(ts->input_dev);
+ if (error) {
+ dev_err(&client->dev, "Unable to register %s input device\n",
+ input_dev->name);
+ goto err_free_irq;
+ }
+
+ i2c_set_clientdata(client, ts);
+ device_init_wakeup(&client->dev, 1);
+
+ return 0;
+
+err_free_irq:
+ free_irq(client->irq, ts);
+err_free_mem:
+ input_free_device(input_dev);
+ kfree(ts);
+ return error;
+}
+
+static int __devexit st1232_ts_remove(struct i2c_client *client)
+{
+ struct st1232_ts_data *ts = i2c_get_clientdata(client);
+
+ device_init_wakeup(&client->dev, 0);
+ free_irq(client->irq, ts);
+ input_unregister_device(ts->input_dev);
+ kfree(ts);
+
+ return 0;
+}
+
+#ifdef CONFIG_PM
+static int st1232_ts_suspend(struct device *dev)
+{
+ struct i2c_client *client = to_i2c_client(dev);
+
+ if (device_may_wakeup(&client->dev))
+ enable_irq_wake(client->irq);
+ else
+ disable_irq(client->irq);
+
+ return 0;
+}
+
+static int st1232_ts_resume(struct device *dev)
+{
+ struct i2c_client *client = to_i2c_client(dev);
+
+ if (device_may_wakeup(&client->dev))
+ disable_irq_wake(client->irq);
+ else
+ enable_irq(client->irq);
+
+ return 0;
+}
+
+static const struct dev_pm_ops st1232_ts_pm_ops = {
+ .suspend = st1232_ts_suspend,
+ .resume = st1232_ts_resume,
+};
+#endif
+
+static const struct i2c_device_id st1232_ts_id[] = {
+ { ST1232_TS_NAME, 0 },
+ { }
+};
+MODULE_DEVICE_TABLE(i2c, st1232_ts_id);
+
+static struct i2c_driver st1232_ts_driver = {
+ .probe = st1232_ts_probe,
+ .remove = __devexit_p(st1232_ts_remove),
+ .id_table = st1232_ts_id,
+ .driver = {
+ .name = ST1232_TS_NAME,
+ .owner = THIS_MODULE,
+#ifdef CONFIG_PM
+ .pm = &st1232_ts_pm_ops,
+#endif
+ },
+};
+
+static int __init st1232_ts_init(void)
+{
+ return i2c_add_driver(&st1232_ts_driver);
+}
+module_init(st1232_ts_init);
+
+static void __exit st1232_ts_exit(void)
+{
+ i2c_del_driver(&st1232_ts_driver);
+}
+module_exit(st1232_ts_exit);
+
+MODULE_AUTHOR("Tony SIM <chinyeow.sim.xt@renesas.com>");
+MODULE_DESCRIPTION("SITRONIX ST1232 Touchscreen Controller Driver");
+MODULE_LICENSE("GPL");
diff --git a/drivers/input/touchscreen/usbtouchscreen.c b/drivers/input/touchscreen/usbtouchscreen.c
index f45f80f6d33..73fd6642b68 100644
--- a/drivers/input/touchscreen/usbtouchscreen.c
+++ b/drivers/input/touchscreen/usbtouchscreen.c
@@ -178,6 +178,7 @@ static const struct usb_device_id usbtouch_devices[] = {
#ifdef CONFIG_TOUCHSCREEN_USB_ITM
{USB_DEVICE(0x0403, 0xf9e9), .driver_info = DEVTYPE_ITM},
+ {USB_DEVICE(0x16e3, 0xf9e9), .driver_info = DEVTYPE_ITM},
#endif
#ifdef CONFIG_TOUCHSCREEN_USB_ETURBO
diff --git a/drivers/input/touchscreen/wacom_w8001.c b/drivers/input/touchscreen/wacom_w8001.c
index 9ae4c7b16ba..5cb8449c909 100644
--- a/drivers/input/touchscreen/wacom_w8001.c
+++ b/drivers/input/touchscreen/wacom_w8001.c
@@ -3,6 +3,7 @@
*
* Copyright (c) 2008 Jaya Kumar
* Copyright (c) 2010 Red Hat, Inc.
+ * Copyright (c) 2010 - 2011 Ping Cheng, Wacom. <pingc@wacom.com>
*
* This file is subject to the terms and conditions of the GNU General Public
* License. See the file COPYING in the main directory of this archive for
@@ -15,10 +16,11 @@
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/slab.h>
-#include <linux/input.h>
+#include <linux/input/mt.h>
#include <linux/serio.h>
#include <linux/init.h>
#include <linux/ctype.h>
+#include <linux/delay.h>
#define DRIVER_DESC "Wacom W8001 serial touchscreen driver"
@@ -37,6 +39,7 @@ MODULE_LICENSE("GPL");
#define W8001_QUERY_PACKET 0x20
+#define W8001_CMD_STOP '0'
#define W8001_CMD_START '1'
#define W8001_CMD_QUERY '*'
#define W8001_CMD_TOUCHQUERY '%'
@@ -48,8 +51,6 @@ MODULE_LICENSE("GPL");
#define W8001_PKTLEN_TPCCTL 11 /* control packet */
#define W8001_PKTLEN_TOUCH2FG 13
-#define MAX_TRACKING_ID 0xFF /* arbitrarily chosen */
-
struct w8001_coord {
u8 rdy;
u8 tsw;
@@ -64,11 +65,11 @@ struct w8001_coord {
/* touch query reply packet */
struct w8001_touch_query {
+ u16 x;
+ u16 y;
u8 panel_res;
u8 capacity_res;
u8 sensor_id;
- u16 x;
- u16 y;
};
/*
@@ -87,10 +88,14 @@ struct w8001 {
char phys[32];
int type;
unsigned int pktlen;
- int trkid[2];
+ u16 max_touch_x;
+ u16 max_touch_y;
+ u16 max_pen_x;
+ u16 max_pen_y;
+ char name[64];
};
-static void parse_data(u8 *data, struct w8001_coord *coord)
+static void parse_pen_data(u8 *data, struct w8001_coord *coord)
{
memset(coord, 0, sizeof(*coord));
@@ -114,30 +119,58 @@ static void parse_data(u8 *data, struct w8001_coord *coord)
coord->tilt_y = data[8] & 0x7F;
}
-static void parse_touch(struct w8001 *w8001)
+static void parse_single_touch(u8 *data, struct w8001_coord *coord)
+{
+ coord->x = (data[1] << 7) | data[2];
+ coord->y = (data[3] << 7) | data[4];
+ coord->tsw = data[0] & 0x01;
+}
+
+static void scale_touch_coordinates(struct w8001 *w8001,
+ unsigned int *x, unsigned int *y)
+{
+ if (w8001->max_pen_x && w8001->max_touch_x)
+ *x = *x * w8001->max_pen_x / w8001->max_touch_x;
+
+ if (w8001->max_pen_y && w8001->max_touch_y)
+ *y = *y * w8001->max_pen_y / w8001->max_touch_y;
+}
+
+static void parse_multi_touch(struct w8001 *w8001)
{
- static int trkid;
struct input_dev *dev = w8001->dev;
unsigned char *data = w8001->data;
+ unsigned int x, y;
int i;
+ int count = 0;
for (i = 0; i < 2; i++) {
- input_mt_slot(dev, i);
+ bool touch = data[0] & (1 << i);
- if (data[0] & (1 << i)) {
- int x = (data[6 * i + 1] << 7) | (data[6 * i + 2]);
- int y = (data[6 * i + 3] << 7) | (data[6 * i + 4]);
+ input_mt_slot(dev, i);
+ input_mt_report_slot_state(dev, MT_TOOL_FINGER, touch);
+ if (touch) {
+ x = (data[6 * i + 1] << 7) | data[6 * i + 2];
+ y = (data[6 * i + 3] << 7) | data[6 * i + 4];
/* data[5,6] and [11,12] is finger capacity */
+ /* scale to pen maximum */
+ scale_touch_coordinates(w8001, &x, &y);
+
input_report_abs(dev, ABS_MT_POSITION_X, x);
input_report_abs(dev, ABS_MT_POSITION_Y, y);
- input_report_abs(dev, ABS_MT_TOOL_TYPE, MT_TOOL_FINGER);
- if (w8001->trkid[i] < 0)
- w8001->trkid[i] = trkid++ & MAX_TRACKING_ID;
- } else {
- w8001->trkid[i] = -1;
+ count++;
}
- input_report_abs(dev, ABS_MT_TRACKING_ID, w8001->trkid[i]);
+ }
+
+ /* emulate single touch events when stylus is out of proximity.
+ * This is to make single touch backward support consistent
+ * across all Wacom single touch devices.
+ */
+ if (w8001->type != BTN_TOOL_PEN &&
+ w8001->type != BTN_TOOL_RUBBER) {
+ w8001->type = count == 1 ? BTN_TOOL_FINGER : KEY_RESERVED;
+ input_mt_report_pointer_emulation(dev, true);
}
input_sync(dev);
@@ -158,6 +191,15 @@ static void parse_touchquery(u8 *data, struct w8001_touch_query *query)
query->y = data[5] << 9;
query->y |= data[6] << 2;
query->y |= (data[2] >> 3) & 0x3;
+
+ /* Early days' single-finger touch models need the following defaults */
+ if (!query->x && !query->y) {
+ query->x = 1024;
+ query->y = 1024;
+ if (query->panel_res)
+ query->x = query->y = (1 << query->panel_res);
+ query->panel_res = 10;
+ }
}
static void report_pen_events(struct w8001 *w8001, struct w8001_coord *coord)
@@ -167,16 +209,15 @@ static void report_pen_events(struct w8001 *w8001, struct w8001_coord *coord)
/*
* We have 1 bit for proximity (rdy) and 3 bits for tip, side,
* side2/eraser. If rdy && f2 are set, this can be either pen + side2,
- * or eraser. assume
+ * or eraser. Assume:
* - if dev is already in proximity and f2 is toggled → pen + side2
* - if dev comes into proximity with f2 set → eraser
* If f2 disappears after assuming eraser, fake proximity out for
* eraser and in for pen.
*/
- if (!w8001->type) {
- w8001->type = coord->f2 ? BTN_TOOL_RUBBER : BTN_TOOL_PEN;
- } else if (w8001->type == BTN_TOOL_RUBBER) {
+ switch (w8001->type) {
+ case BTN_TOOL_RUBBER:
if (!coord->f2) {
input_report_abs(dev, ABS_PRESSURE, 0);
input_report_key(dev, BTN_TOUCH, 0);
@@ -186,8 +227,21 @@ static void report_pen_events(struct w8001 *w8001, struct w8001_coord *coord)
input_sync(dev);
w8001->type = BTN_TOOL_PEN;
}
- } else {
+ break;
+
+ case BTN_TOOL_FINGER:
+ input_report_key(dev, BTN_TOUCH, 0);
+ input_report_key(dev, BTN_TOOL_FINGER, 0);
+ input_sync(dev);
+ /* fall through */
+
+ case KEY_RESERVED:
+ w8001->type = coord->f2 ? BTN_TOOL_RUBBER : BTN_TOOL_PEN;
+ break;
+
+ default:
input_report_key(dev, BTN_STYLUS2, coord->f2);
+ break;
}
input_report_abs(dev, ABS_X, coord->x);
@@ -199,7 +253,26 @@ static void report_pen_events(struct w8001 *w8001, struct w8001_coord *coord)
input_sync(dev);
if (!coord->rdy)
- w8001->type = 0;
+ w8001->type = KEY_RESERVED;
+}
+
+static void report_single_touch(struct w8001 *w8001, struct w8001_coord *coord)
+{
+ struct input_dev *dev = w8001->dev;
+ unsigned int x = coord->x;
+ unsigned int y = coord->y;
+
+ /* scale to pen maximum */
+ scale_touch_coordinates(w8001, &x, &y);
+
+ input_report_abs(dev, ABS_X, x);
+ input_report_abs(dev, ABS_Y, y);
+ input_report_key(dev, BTN_TOUCH, coord->tsw);
+ input_report_key(dev, BTN_TOOL_FINGER, coord->tsw);
+
+ input_sync(dev);
+
+ w8001->type = coord->tsw ? BTN_TOOL_FINGER : KEY_RESERVED;
}
static irqreturn_t w8001_interrupt(struct serio *serio,
@@ -220,9 +293,18 @@ static irqreturn_t w8001_interrupt(struct serio *serio,
case W8001_PKTLEN_TOUCH93 - 1:
case W8001_PKTLEN_TOUCH9A - 1:
- /* ignore one-finger touch packet. */
- if (w8001->pktlen == w8001->idx)
+ tmp = w8001->data[0] & W8001_TOUCH_BYTE;
+ if (tmp != W8001_TOUCH_BYTE)
+ break;
+
+ if (w8001->pktlen == w8001->idx) {
w8001->idx = 0;
+ if (w8001->type != BTN_TOOL_PEN &&
+ w8001->type != BTN_TOOL_RUBBER) {
+ parse_single_touch(w8001->data, &coord);
+ report_single_touch(w8001, &coord);
+ }
+ }
break;
/* Pen coordinates packet */
@@ -231,18 +313,18 @@ static irqreturn_t w8001_interrupt(struct serio *serio,
if (unlikely(tmp == W8001_TAB_BYTE))
break;
- tmp = (w8001->data[0] & W8001_TOUCH_BYTE);
+ tmp = w8001->data[0] & W8001_TOUCH_BYTE;
if (tmp == W8001_TOUCH_BYTE)
break;
w8001->idx = 0;
- parse_data(w8001->data, &coord);
+ parse_pen_data(w8001->data, &coord);
report_pen_events(w8001, &coord);
break;
/* control packet */
case W8001_PKTLEN_TPCCTL - 1:
- tmp = (w8001->data[0] & W8001_TOUCH_MASK);
+ tmp = w8001->data[0] & W8001_TOUCH_MASK;
if (tmp == W8001_TOUCH_BYTE)
break;
@@ -255,7 +337,7 @@ static irqreturn_t w8001_interrupt(struct serio *serio,
/* 2 finger touch packet */
case W8001_PKTLEN_TOUCH2FG - 1:
w8001->idx = 0;
- parse_touch(w8001);
+ parse_multi_touch(w8001);
break;
}
@@ -285,52 +367,104 @@ static int w8001_setup(struct w8001 *w8001)
{
struct input_dev *dev = w8001->dev;
struct w8001_coord coord;
+ struct w8001_touch_query touch;
int error;
- error = w8001_command(w8001, W8001_CMD_QUERY, true);
+ error = w8001_command(w8001, W8001_CMD_STOP, false);
if (error)
return error;
- parse_data(w8001->response, &coord);
+ msleep(250); /* wait 250ms before querying the device */
- input_set_abs_params(dev, ABS_X, 0, coord.x, 0, 0);
- input_set_abs_params(dev, ABS_Y, 0, coord.y, 0, 0);
- input_set_abs_params(dev, ABS_PRESSURE, 0, coord.pen_pressure, 0, 0);
- input_set_abs_params(dev, ABS_TILT_X, 0, coord.tilt_x, 0, 0);
- input_set_abs_params(dev, ABS_TILT_Y, 0, coord.tilt_y, 0, 0);
+ dev->evbit[0] = BIT_MASK(EV_KEY) | BIT_MASK(EV_ABS);
+ strlcat(w8001->name, "Wacom Serial", sizeof(w8001->name));
- error = w8001_command(w8001, W8001_CMD_TOUCHQUERY, true);
+ /* penabled? */
+ error = w8001_command(w8001, W8001_CMD_QUERY, true);
if (!error) {
- struct w8001_touch_query touch;
+ __set_bit(BTN_TOUCH, dev->keybit);
+ __set_bit(BTN_TOOL_PEN, dev->keybit);
+ __set_bit(BTN_TOOL_RUBBER, dev->keybit);
+ __set_bit(BTN_STYLUS, dev->keybit);
+ __set_bit(BTN_STYLUS2, dev->keybit);
+
+ parse_pen_data(w8001->response, &coord);
+ w8001->max_pen_x = coord.x;
+ w8001->max_pen_y = coord.y;
+
+ input_set_abs_params(dev, ABS_X, 0, coord.x, 0, 0);
+ input_set_abs_params(dev, ABS_Y, 0, coord.y, 0, 0);
+ input_set_abs_params(dev, ABS_PRESSURE, 0, coord.pen_pressure, 0, 0);
+ if (coord.tilt_x && coord.tilt_y) {
+ input_set_abs_params(dev, ABS_TILT_X, 0, coord.tilt_x, 0, 0);
+ input_set_abs_params(dev, ABS_TILT_Y, 0, coord.tilt_y, 0, 0);
+ }
+ w8001->id = 0x90;
+ strlcat(w8001->name, " Penabled", sizeof(w8001->name));
+ }
+
+ /* Touch enabled? */
+ error = w8001_command(w8001, W8001_CMD_TOUCHQUERY, true);
+
+ /*
+ * Some non-touch devices may reply to the touch query. But their
+ * second byte is empty, which indicates touch is not supported.
+ */
+ if (!error && w8001->response[1]) {
+ __set_bit(BTN_TOUCH, dev->keybit);
+ __set_bit(BTN_TOOL_FINGER, dev->keybit);
parse_touchquery(w8001->response, &touch);
+ w8001->max_touch_x = touch.x;
+ w8001->max_touch_y = touch.y;
+
+ /* scale to pen maximum */
+ if (w8001->max_pen_x && w8001->max_pen_y) {
+ touch.x = w8001->max_pen_x;
+ touch.y = w8001->max_pen_y;
+ }
+
+ input_set_abs_params(dev, ABS_X, 0, touch.x, 0, 0);
+ input_set_abs_params(dev, ABS_Y, 0, touch.y, 0, 0);
switch (touch.sensor_id) {
case 0:
case 2:
w8001->pktlen = W8001_PKTLEN_TOUCH93;
+ w8001->id = 0x93;
+ strlcat(w8001->name, " 1FG", sizeof(w8001->name));
break;
+
case 1:
case 3:
case 4:
w8001->pktlen = W8001_PKTLEN_TOUCH9A;
+ strlcat(w8001->name, " 1FG", sizeof(w8001->name));
+ w8001->id = 0x9a;
break;
+
case 5:
w8001->pktlen = W8001_PKTLEN_TOUCH2FG;
- input_mt_create_slots(dev, 2);
- input_set_abs_params(dev, ABS_MT_TRACKING_ID,
- 0, MAX_TRACKING_ID, 0, 0);
+ input_mt_init_slots(dev, 2);
input_set_abs_params(dev, ABS_MT_POSITION_X,
0, touch.x, 0, 0);
input_set_abs_params(dev, ABS_MT_POSITION_Y,
0, touch.y, 0, 0);
input_set_abs_params(dev, ABS_MT_TOOL_TYPE,
- 0, 0, 0, 0);
+ 0, MT_TOOL_MAX, 0, 0);
+
+ strlcat(w8001->name, " 2FG", sizeof(w8001->name));
+ if (w8001->max_pen_x && w8001->max_pen_y)
+ w8001->id = 0xE3;
+ else
+ w8001->id = 0xE2;
break;
}
}
+ strlcat(w8001->name, " Touchscreen", sizeof(w8001->name));
+
return w8001_command(w8001, W8001_CMD_START, false);
}
@@ -370,27 +504,10 @@ static int w8001_connect(struct serio *serio, struct serio_driver *drv)
}
w8001->serio = serio;
- w8001->id = serio->id.id;
w8001->dev = input_dev;
- w8001->trkid[0] = w8001->trkid[1] = -1;
init_completion(&w8001->cmd_done);
snprintf(w8001->phys, sizeof(w8001->phys), "%s/input0", serio->phys);
- input_dev->name = "Wacom W8001 Penabled Serial TouchScreen";
- input_dev->phys = w8001->phys;
- input_dev->id.bustype = BUS_RS232;
- input_dev->id.vendor = SERIO_W8001;
- input_dev->id.product = w8001->id;
- input_dev->id.version = 0x0100;
- input_dev->dev.parent = &serio->dev;
-
- input_dev->evbit[0] = BIT_MASK(EV_KEY) | BIT_MASK(EV_ABS);
- input_dev->keybit[BIT_WORD(BTN_TOUCH)] = BIT_MASK(BTN_TOUCH);
- input_dev->keybit[BIT_WORD(BTN_TOOL_PEN)] |= BIT_MASK(BTN_TOOL_PEN);
- input_dev->keybit[BIT_WORD(BTN_TOOL_RUBBER)] |= BIT_MASK(BTN_TOOL_RUBBER);
- input_dev->keybit[BIT_WORD(BTN_STYLUS)] |= BIT_MASK(BTN_STYLUS);
- input_dev->keybit[BIT_WORD(BTN_STYLUS2)] |= BIT_MASK(BTN_STYLUS2);
-
serio_set_drvdata(serio, w8001);
err = serio_open(serio, drv);
if (err)
@@ -400,6 +517,14 @@ static int w8001_connect(struct serio *serio, struct serio_driver *drv)
if (err)
goto fail3;
+ input_dev->name = w8001->name;
+ input_dev->phys = w8001->phys;
+ input_dev->id.product = w8001->id;
+ input_dev->id.bustype = BUS_RS232;
+ input_dev->id.vendor = 0x056a;
+ input_dev->id.version = 0x0100;
+ input_dev->dev.parent = &serio->dev;
+
err = input_register_device(w8001->dev);
if (err)
goto fail3;
diff --git a/drivers/input/xen-kbdfront.c b/drivers/input/xen-kbdfront.c
index e0c024db2ca..7f85a862ad1 100644
--- a/drivers/input/xen-kbdfront.c
+++ b/drivers/input/xen-kbdfront.c
@@ -17,6 +17,8 @@
* Switch to grant tables together with xen-fbfront.c.
*/
+#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
+
#include <linux/kernel.h>
#include <linux/errno.h>
#include <linux/module.h>
@@ -84,9 +86,8 @@ static irqreturn_t input_handler(int rq, void *dev_id)
input_report_key(dev, event->key.keycode,
event->key.pressed);
else
- printk(KERN_WARNING
- "xenkbd: unhandled keycode 0x%x\n",
- event->key.keycode);
+ pr_warning("unhandled keycode 0x%x\n",
+ event->key.keycode);
break;
case XENKBD_TYPE_POS:
input_report_abs(dev, ABS_X, event->pos.abs_x);
@@ -292,8 +293,7 @@ InitWait:
ret = xenbus_printf(XBT_NIL, info->xbdev->nodename,
"request-abs-pointer", "1");
if (ret)
- printk(KERN_WARNING
- "xenkbd: can't request abs-pointer");
+ pr_warning("can't request abs-pointer\n");
}
xenbus_switch_state(dev, XenbusStateConnected);
break;