diff options
52 files changed, 2868 insertions, 593 deletions
diff --git a/Documentation/input/cma3000_d0x.txt b/Documentation/input/cma3000_d0x.txt new file mode 100644 index 000000000000..29d088db4afd --- /dev/null +++ b/Documentation/input/cma3000_d0x.txt @@ -0,0 +1,115 @@ +Kernel driver for CMA3000-D0x +============================ + +Supported chips: +* VTI CMA3000-D0x +Datasheet: + CMA3000-D0X Product Family Specification 8281000A.02.pdf + <http://www.vti.fi/en/> + +Author: Hemanth V <hemanthv@ti.com> + + +Description +----------- +CMA3000 Tri-axis accelerometer supports Motion detect, Measurement and +Free fall modes. + +Motion Detect Mode: Its the low power mode where interrupts are generated only +when motion exceeds the defined thresholds. + +Measurement Mode: This mode is used to read the acceleration data on X,Y,Z +axis and supports 400, 100, 40 Hz sample frequency. + +Free fall Mode: This mode is intended to save system resources. + +Threshold values: Chip supports defining threshold values for above modes +which includes time and g value. Refer product specifications for more details. + +CMA3000 chip supports mutually exclusive I2C and SPI interfaces for +communication, currently the driver supports I2C based communication only. +Initial configuration for bus mode is set in non volatile memory and can later +be modified through bus interface command. + +Driver reports acceleration data through input subsystem. It generates ABS_MISC +event with value 1 when free fall is detected. + +Platform data need to be configured for initial default values. + +Platform Data +------------- +fuzz_x: Noise on X Axis + +fuzz_y: Noise on Y Axis + +fuzz_z: Noise on Z Axis + +g_range: G range in milli g i.e 2000 or 8000 + +mode: Default Operating mode + +mdthr: Motion detect g range threshold value + +mdfftmr: Motion detect and free fall time threshold value + +ffthr: Free fall g range threshold value + +Input Interface +-------------- +Input driver version is 1.0.0 +Input device ID: bus 0x18 vendor 0x0 product 0x0 version 0x0 +Input device name: "cma3000-accelerometer" +Supported events: + Event type 0 (Sync) + Event type 3 (Absolute) + Event code 0 (X) + Value 47 + Min -8000 + Max 8000 + Fuzz 200 + Event code 1 (Y) + Value -28 + Min -8000 + Max 8000 + Fuzz 200 + Event code 2 (Z) + Value 905 + Min -8000 + Max 8000 + Fuzz 200 + Event code 40 (Misc) + Value 0 + Min 0 + Max 1 + Event type 4 (Misc) + + +Register/Platform parameters Description +---------------------------------------- + +mode: + 0: power down mode + 1: 100 Hz Measurement mode + 2: 400 Hz Measurement mode + 3: 40 Hz Measurement mode + 4: Motion Detect mode (default) + 5: 100 Hz Free fall mode + 6: 40 Hz Free fall mode + 7: Power off mode + +grange: + 2000: 2000 mg or 2G Range + 8000: 8000 mg or 8G Range + +mdthr: + X: X * 71mg (8G Range) + X: X * 18mg (2G Range) + +mdfftmr: + X: (X & 0x70) * 100 ms (MDTMR) + (X & 0x0F) * 2.5 ms (FFTMR 400 Hz) + (X & 0x0F) * 10 ms (FFTMR 100 Hz) + +ffthr: + X: (X >> 2) * 18mg (2G Range) + X: (X & 0x0F) * 71 mg (8G Range) diff --git a/arch/arm/plat-spear/include/plat/keyboard.h b/arch/arm/plat-spear/include/plat/keyboard.h new file mode 100644 index 000000000000..68b5394fc583 --- /dev/null +++ b/arch/arm/plat-spear/include/plat/keyboard.h @@ -0,0 +1,141 @@ +/* + * 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. + */ + +#ifndef __PLAT_KEYBOARD_H +#define __PLAT_KEYBOARD_H + +#include <linux/bitops.h> +#include <linux/input.h> +#include <linux/input/matrix_keypad.h> +#include <linux/types.h> + +#define DECLARE_KEYMAP(_name) \ +int _name[] = { \ + KEY(0, 0, KEY_ESC), \ + KEY(0, 1, KEY_1), \ + KEY(0, 2, KEY_2), \ + KEY(0, 3, KEY_3), \ + KEY(0, 4, KEY_4), \ + KEY(0, 5, KEY_5), \ + KEY(0, 6, KEY_6), \ + KEY(0, 7, KEY_7), \ + KEY(0, 8, KEY_8), \ + KEY(1, 0, KEY_9), \ + KEY(1, 1, KEY_MINUS), \ + KEY(1, 2, KEY_EQUAL), \ + KEY(1, 3, KEY_BACKSPACE), \ + KEY(1, 4, KEY_TAB), \ + KEY(1, 5, KEY_Q), \ + KEY(1, 6, KEY_W), \ + KEY(1, 7, KEY_E), \ + KEY(1, 8, KEY_R), \ + KEY(2, 0, KEY_T), \ + KEY(2, 1, KEY_Y), \ + KEY(2, 2, KEY_U), \ + KEY(2, 3, KEY_I), \ + KEY(2, 4, KEY_O), \ + KEY(2, 5, KEY_P), \ + KEY(2, 6, KEY_LEFTBRACE), \ + KEY(2, 7, KEY_RIGHTBRACE), \ + KEY(2, 8, KEY_ENTER), \ + KEY(3, 0, KEY_LEFTCTRL), \ + KEY(3, 1, KEY_A), \ + KEY(3, 2, KEY_S), \ + KEY(3, 3, KEY_D), \ + KEY(3, 4, KEY_F), \ + KEY(3, 5, KEY_G), \ + KEY(3, 6, KEY_H), \ + KEY(3, 7, KEY_J), \ + KEY(3, 8, KEY_K), \ + KEY(4, 0, KEY_L), \ + KEY(4, 1, KEY_SEMICOLON), \ + KEY(4, 2, KEY_APOSTROPHE), \ + KEY(4, 3, KEY_GRAVE), \ + KEY(4, 4, KEY_LEFTSHIFT), \ + KEY(4, 5, KEY_BACKSLASH), \ + KEY(4, 6, KEY_Z), \ + KEY(4, 7, KEY_X), \ + KEY(4, 8, KEY_C), \ + KEY(4, 0, KEY_L), \ + KEY(4, 1, KEY_SEMICOLON), \ + KEY(4, 2, KEY_APOSTROPHE), \ + KEY(4, 3, KEY_GRAVE), \ + KEY(4, 4, KEY_LEFTSHIFT), \ + KEY(4, 5, KEY_BACKSLASH), \ + KEY(4, 6, KEY_Z), \ + KEY(4, 7, KEY_X), \ + KEY(4, 8, KEY_C), \ + KEY(4, 0, KEY_L), \ + KEY(4, 1, KEY_SEMICOLON), \ + KEY(4, 2, KEY_APOSTROPHE), \ + KEY(4, 3, KEY_GRAVE), \ + KEY(4, 4, KEY_LEFTSHIFT), \ + KEY(4, 5, KEY_BACKSLASH), \ + KEY(4, 6, KEY_Z), \ + KEY(4, 7, KEY_X), \ + KEY(4, 8, KEY_C), \ + KEY(5, 0, KEY_V), \ + KEY(5, 1, KEY_B), \ + KEY(5, 2, KEY_N), \ + KEY(5, 3, KEY_M), \ + KEY(5, 4, KEY_COMMA), \ + KEY(5, 5, KEY_DOT), \ + KEY(5, 6, KEY_SLASH), \ + KEY(5, 7, KEY_RIGHTSHIFT), \ + KEY(5, 8, KEY_KPASTERISK), \ + KEY(6, 0, KEY_LEFTALT), \ + KEY(6, 1, KEY_SPACE), \ + KEY(6, 2, KEY_CAPSLOCK), \ + KEY(6, 3, KEY_F1), \ + KEY(6, 4, KEY_F2), \ + KEY(6, 5, KEY_F3), \ + KEY(6, 6, KEY_F4), \ + KEY(6, 7, KEY_F5), \ + KEY(6, 8, KEY_F6), \ + KEY(7, 0, KEY_F7), \ + KEY(7, 1, KEY_F8), \ + KEY(7, 2, KEY_F9), \ + KEY(7, 3, KEY_F10), \ + KEY(7, 4, KEY_NUMLOCK), \ + KEY(7, 5, KEY_SCROLLLOCK), \ + KEY(7, 6, KEY_KP7), \ + KEY(7, 7, KEY_KP8), \ + KEY(7, 8, KEY_KP9), \ + KEY(8, 0, KEY_KPMINUS), \ + KEY(8, 1, KEY_KP4), \ + KEY(8, 2, KEY_KP5), \ + KEY(8, 3, KEY_KP6), \ + KEY(8, 4, KEY_KPPLUS), \ + KEY(8, 5, KEY_KP1), \ + KEY(8, 6, KEY_KP2), \ + KEY(8, 7, KEY_KP3), \ + KEY(8, 8, KEY_KP0), \ +} + +/** + * struct kbd_platform_data - spear keyboard platform data + * keymap: pointer to keymap data (table and size) + * rep: enables key autorepeat + * + * This structure is supposed to be used by platform code to supply + * keymaps to drivers that implement keyboards. + */ +struct kbd_platform_data { + const struct matrix_keymap_data *keymap; + bool rep; +}; + +/* This function is used to set platform data field of pdev->dev */ +static inline void +kbd_set_plat_data(struct platform_device *pdev, struct kbd_platform_data *data) +{ + pdev->dev.platform_data = data; +} + +#endif /* __PLAT_KEYBOARD_H */ diff --git a/drivers/input/Makefile b/drivers/input/Makefile index 569938b3cc04..09614ce74961 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-mt.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 7d61a9660806..e90ee3d30613 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 f7c5c14ec12a..cd4e6679d61a 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 0cd97e8f0c9a..f8635b7b4295 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,12 +524,11 @@ 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); @@ -898,7 +899,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 03078c08309a..3367f760d75a 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 1d881c96ba8f..117a59aaa70e 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 46239e47a260..dbf741c95835 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>*/ @@ -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-polldev.c b/drivers/input/input-polldev.c index 10c9b0a845f0..0559e309bac9 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 3bb6907f26d6..9408dba2cd31 100644 --- a/drivers/input/input.c +++ b/drivers/input/input.c @@ -10,6 +10,8 @@ * the Free Software Foundation. */ +#define pr_fmt(fmt) KBUILD_BASENAME ": " fmt + #include <linux/init.h> #include <linux/types.h> #include <linux/input/mt.h> @@ -74,6 +76,7 @@ static int input_defuzz_abs_event(int value, int old_val, int fuzz) * dev->event_lock held and interrupts disabled. */ static void input_pass_event(struct input_dev *dev, + struct input_handler *src_handler, unsigned int type, unsigned int code, int value) { struct input_handler *handler; @@ -92,6 +95,15 @@ static void input_pass_event(struct input_dev *dev, continue; handler = handle->handler; + + /* + * If this is the handler that injected this + * particular event we want to skip it to avoid + * filters firing again and again. + */ + if (handler == src_handler) + continue; + if (!handler->filter) { if (filtered) break; @@ -121,7 +133,7 @@ static void input_repeat_key(unsigned long data) if (test_bit(dev->repeat_key, dev->key) && is_event_supported(dev->repeat_key, dev->keybit, KEY_MAX)) { - input_pass_event(dev, EV_KEY, dev->repeat_key, 2); + input_pass_event(dev, NULL, EV_KEY, dev->repeat_key, 2); if (dev->sync) { /* @@ -130,7 +142,7 @@ static void input_repeat_key(unsigned long data) * Otherwise assume that the driver will send * SYN_REPORT once it's done. */ - input_pass_event(dev, EV_SYN, SYN_REPORT, 1); + input_pass_event(dev, NULL, EV_SYN, SYN_REPORT, 1); } if (dev->rep[REP_PERIOD]) @@ -163,6 +175,7 @@ static void input_stop_autorepeat(struct input_dev *dev) #define INPUT_PASS_TO_ALL (INPUT_PASS_TO_HANDLERS | INPUT_PASS_TO_DEVICE) static int input_handle_abs_event(struct input_dev *dev, + struct input_handler *src_handler, unsigned int code, int *pval) { bool is_mt_event; @@ -206,13 +219,15 @@ static int input_handle_abs_event(struct input_dev *dev, /* Flush pending "slot" event */ if (is_mt_event && dev->slot != input_abs_get_val(dev, ABS_MT_SLOT)) { input_abs_set_val(dev, ABS_MT_SLOT, dev->slot); - input_pass_event(dev, EV_ABS, ABS_MT_SLOT, dev->slot); + input_pass_event(dev, src_handler, + EV_ABS, ABS_MT_SLOT, dev->slot); } return INPUT_PASS_TO_HANDLERS; } static void input_handle_event(struct input_dev *dev, + struct input_handler *src_handler, unsigned int type, unsigned int code, int value) { int disposition = INPUT_IGNORE_EVENT; @@ -265,7 +280,8 @@ static void input_handle_event(struct input_dev *dev, case EV_ABS: if (is_event_supported(code, dev->absbit, ABS_MAX)) - disposition = input_handle_abs_event(dev, code, &value); + disposition = input_handle_abs_event(dev, src_handler, + code, &value); break; @@ -323,7 +339,7 @@ static void input_handle_event(struct input_dev *dev, dev->event(dev, type, code, value); if (disposition & INPUT_PASS_TO_HANDLERS) - input_pass_event(dev, type, code, value); + input_pass_event(dev, src_handler, type, code, value); } /** @@ -352,7 +368,7 @@ void input_event(struct input_dev *dev, spin_lock_irqsave(&dev->event_lock, flags); add_input_randomness(type, code, value); - input_handle_event(dev, type, code, value); + input_handle_event(dev, NULL, type, code, value); spin_unlock_irqrestore(&dev->event_lock, flags); } } @@ -382,7 +398,8 @@ void input_inject_event(struct input_handle *handle, rcu_read_lock(); grab = rcu_dereference(dev->grab); if (!grab || grab == handle) - input_handle_event(dev, type, code, value); + input_handle_event(dev, handle->handler, + type, code, value); rcu_read_unlock(); spin_unlock_irqrestore(&dev->event_lock, flags); @@ -595,10 +612,10 @@ static void input_dev_release_keys(struct input_dev *dev) for (code = 0; code <= KEY_MAX; code++) { if (is_event_supported(code, dev->keybit, KEY_MAX) && __test_and_clear_bit(code, dev->key)) { - input_pass_event(dev, EV_KEY, code, 0); + input_pass_event(dev, NULL, EV_KEY, code, 0); } } - input_pass_event(dev, EV_SYN, SYN_REPORT, 1); + input_pass_event(dev, NULL, EV_SYN, SYN_REPORT, 1); } } @@ -873,9 +890,9 @@ int input_set_keycode(struct input_dev *dev, !is_event_supported(old_keycode, dev->keybit, KEY_MAX) && __test_and_clear_bit(old_keycode, dev->key)) { - input_pass_event(dev, EV_KEY, old_keycode, 0); + input_pass_event(dev, NULL, EV_KEY, old_keycode, 0); if (dev->sync) - input_pass_event(dev, EV_SYN, SYN_REPORT, 1); + input_pass_event(dev, NULL, EV_SYN, SYN_REPORT, 1); } out: @@ -944,10 +961,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; } @@ -1584,8 +1599,7 @@ static int input_dev_uevent(struct device *device, struct kobj_uevent_env *env) } \ } while (0) -#ifdef CONFIG_PM -static void input_dev_reset(struct input_dev *dev, bool activate) +static void input_dev_toggle(struct input_dev *dev, bool activate) { if (!dev->event) return; @@ -1599,12 +1613,44 @@ static void input_dev_reset(struct input_dev *dev, bool activate) } } +/** + * input_reset_device() - reset/restore the state of input device + * @dev: input device whose state needs to be reset + * + * This function tries to reset the state of an opened input device and + * bring internal state and state if the hardware in sync with each other. + * We mark all keys as released, restore LED state, repeat rate, etc. + */ +void input_reset_device(struct input_dev *dev) +{ + mutex_lock(&dev->mutex); + + if (dev->users) { + input_dev_toggle(dev, true); + + /* + * Keys that have been pressed at suspend time are unlikely + * to be still pressed when we resume. + */ + spin_lock_irq(&dev->event_lock); + input_dev_release_keys(dev); + spin_unlock_irq(&dev->event_lock); + } + + mutex_unlock(&dev->mutex); +} +EXPORT_SYMBOL(input_reset_device); + +#ifdef CONFIG_PM static int input_dev_suspend(struct device *dev) { struct input_dev *input_dev = to_input_dev(dev); mutex_lock(&input_dev->mutex); - input_dev_reset(input_dev, false); + + if (input_dev->users) + input_dev_toggle(input_dev, false); + mutex_unlock(&input_dev->mutex); return 0; @@ -1614,18 +1660,7 @@ static int input_dev_resume(struct device *dev) { struct input_dev *input_dev = to_input_dev(dev); - mutex_lock(&input_dev->mutex); - input_dev_reset(input_dev, true); - - /* - * Keys that have been pressed at suspend time are unlikely - * to be still pressed when we resume. - */ - spin_lock_irq(&input_dev->event_lock); - input_dev_release_keys(input_dev); - spin_unlock_irq(&input_dev->event_lock); - - mutex_unlock(&input_dev->mutex); + input_reset_device(input_dev); return 0; } @@ -1758,9 +1793,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; } @@ -1842,8 +1876,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); @@ -2125,7 +2160,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; } @@ -2135,7 +2170,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 9d424cebfd2c..3182c9cd1b0e 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/iforce/Makefile b/drivers/input/joystick/iforce/Makefile index 74daff49ab6e..bc5bda22f15e 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/xpad.c b/drivers/input/joystick/xpad.c index f9fb7fa10af3..56abf3d0e911 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 b8c51b9781db..a378e959368e 100644 --- a/drivers/input/keyboard/Kconfig +++ b/drivers/input/keyboard/Kconfig @@ -180,20 +180,22 @@ config KEYBOARD_GPIO module will be called gpio_keys. 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 +445,15 @@ 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_TNETV107X tristate "TI TNETV107X keypad support" depends on ARCH_DAVINCI_TNETV107X diff --git a/drivers/input/keyboard/Makefile b/drivers/input/keyboard/Makefile index a34452e8ebe2..8449c730892c 100644 --- a/drivers/input/keyboard/Makefile +++ b/drivers/input/keyboard/Makefile @@ -37,6 +37,7 @@ 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 diff --git a/drivers/input/keyboard/adp5588-keys.c b/drivers/input/keyboard/adp5588-keys.c index b92d1cd5cba1..af45d275f686 100644 --- a/drivers/input/keyboard/adp5588-keys.c +++ b/drivers/input/keyboard/adp5588-keys.c @@ -4,7 +4,7 @@ * I2C QWERTY Keypad and IO Expander * Bugs: Enter bugs at http://blackfin.uclinux.org/ * - * Copyright (C) 2008-2009 Analog Devices Inc. + * Copyright (C) 2008-2010 Analog Devices Inc. * Licensed under the GPL-2 or later. */ @@ -24,29 +24,6 @@ #include <linux/i2c/adp5588.h> - /* Configuration Register1 */ -#define AUTO_INC (1 << 7) -#define GPIEM_CFG (1 << 6) -#define OVR_FLOW_M (1 << 5) -#define INT_CFG (1 << 4) -#define OVR_FLOW_IEN (1 << 3) -#define K_LCK_IM (1 << 2) -#define GPI_IEN (1 << 1) -#define KE_IEN (1 << 0) - -/* Interrupt Status Register */ -#define CMP2_INT (1 << 5) -#define CMP1_INT (1 << 4) -#define OVR_FLOW_INT (1 << 3) -#define K_LCK_INT (1 << 2) -#define GPI_INT (1 << 1) -#define KE_INT (1 << 0) - -/* Key Lock and Event Counter Register */ -#define K_LCK_EN (1 << 6) -#define LCK21 0x30 -#define KEC 0xF - /* Key Event Register xy */ #define KEY_EV_PRESSED (1 << 7) #define KEY_EV_MASK (0x7F) @@ -55,10 +32,6 @@ #define KEYP_MAX_EVENT 10 -#define MAXGPIO 18 -#define ADP_BANK(offs) ((offs) >> 3) -#define ADP_BIT(offs) (1u << ((offs) & 0x7)) - /* * Early pre 4.0 Silicon required to delay readout by at least 25ms, * since the Event Counter Register updated 25ms after the interrupt @@ -75,7 +48,7 @@ struct adp5588_kpad { const struct adp5588_gpi_map *gpimap; unsigned short gpimapsize; #ifdef CONFIG_GPIOLIB - unsigned char gpiomap[MAXGPIO]; + unsigned char gpiomap[ADP5588_MAXGPIO]; bool export_gpio; struct gpio_chip gc; struct mutex gpio_lock; /* Protect cached dir, dat_out */ @@ -103,8 +76,8 @@ static int adp5588_write(struct i2c_client *client, u8 reg, u8 val) static int adp5588_gpio_get_value(struct gpio_chip *chip, unsigned off) { struct adp5588_kpad *kpad = container_of(chip, struct adp5588_kpad, gc); - unsigned int bank = ADP_BANK(kpad->gpiomap[off]); - unsigned int bit = ADP_BIT(kpad->gpiomap[off]); + unsigned int bank = ADP5588_BANK(kpad->gpiomap[off]); + unsigned int bit = ADP5588_BIT(kpad->gpiomap[off]); return !!(adp5588_read(kpad->client, GPIO_DAT_STAT1 + bank) & bit); } @@ -113,8 +86,8 @@ static void adp5588_gpio_set_value(struct gpio_chip *chip, unsigned off, int val) { struct adp5588_kpad *kpad = container_of(chip, struct adp5588_kpad, gc); - unsigned int bank = ADP_BANK(kpad->gpiomap[off]); - unsigned int bit = ADP_BIT(kpad->gpiomap[off]); + unsigned int bank = ADP5588_BANK(kpad->gpiomap[off]); + unsigned int bit = ADP5588_BIT(kpad->gpiomap[off]); mutex_lock(&kpad->gpio_lock); @@ -132,8 +105,8 @@ static void adp5588_gpio_set_value(struct gpio_chip *chip, static int adp5588_gpio_direction_input(struct gpio_chip *chip, unsigned off) { struct adp5588_kpad *kpad = container_of(chip, struct adp5588_kpad, gc); - unsigned int bank = ADP_BANK(kpad->gpiomap[off]); - unsigned int bit = ADP_BIT(kpad->gpiomap[off]); + unsigned int bank = ADP5588_BANK(kpad->gpiomap[off]); + unsigned int bit = ADP5588_BIT(kpad->gpiomap[off]); int ret; mutex_lock(&kpad->gpio_lock); @@ -150,8 +123,8 @@ static int adp5588_gpio_direction_output(struct gpio_chip *chip, unsigned off, int val) { struct adp5588_kpad *kpad = container_of(chip, struct adp5588_kpad, gc); - unsigned int bank = ADP_BANK(kpad->gpiomap[off]); - unsigned int bit = ADP_BIT(kpad->gpiomap[off]); + unsigned int bank = ADP5588_BANK(kpad->gpiomap[off]); + unsigned int bit = ADP5588_BIT(kpad->gpiomap[off]); int ret; mutex_lock(&kpad->gpio_lock); @@ -176,7 +149,7 @@ static int adp5588_gpio_direction_output(struct gpio_chip *chip, static int __devinit adp5588_build_gpiomap(struct adp5588_kpad *kpad, const struct adp5588_kpad_platform_data *pdata) { - bool pin_used[MAXGPIO]; + bool pin_used[ADP5588_MAXGPIO]; int n_unused = 0; int i; @@ -191,7 +164,7 @@ static int __devinit adp5588_build_gpiomap(struct adp5588_kpad *kpad, for (i = 0; i < kpad->gpimapsize; i++) pin_used[kpad->gpimap[i].pin - GPI_PIN_BASE] = true; - for (i = 0; i < MAXGPIO; i++) + for (i = 0; i < ADP5588_MAXGPIO; i++) if (!pin_used[i]) kpad->gpiomap[n_unused++] = i; @@ -234,7 +207,7 @@ static int __devinit adp5588_gpio_add(struct adp5588_kpad *kpad) return error; } - for (i = 0; i <= ADP_BANK(MAXGPIO); i++) { + for (i = 0; i <= ADP5588_BANK(ADP5588_MAXGPIO); i++) { kpad->dat_out[i] = adp5588_read(kpad->client, GPIO_DAT_OUT1 + i); kpad->dir[i] = adp5588_read(kpad->client, GPIO_DIR1 + i); @@ -318,11 +291,11 @@ static void adp5588_work(struct work_struct *work) status = adp5588_read(client, INT_STAT); - if (status & OVR_FLOW_INT) /* Unlikely and should never happen */ + if (status & ADP5588_OVR_FLOW_INT) /* Unlikely and should never happen */ dev_err(&client->dev, "Event Overflow Error\n"); - if (status & KE_INT) { - ev_cnt = adp5588_read(client, KEY_LCK_EC_STAT) & KEC; + if (status & ADP5588_KE_INT) { + ev_cnt = adp5588_read(client, KEY_LCK_EC_STAT) & ADP5588_KEC; if (ev_cnt) { adp5588_report_events(kpad, ev_cnt); input_sync(kpad->input); @@ -360,7 +333,7 @@ static int __devinit adp5588_setup(struct i2c_client *client) if (pdata->en_keylock) { ret |= adp5588_write(client, UNLOCK1, pdata->unlock_key1); ret |= adp5588_write(client, UNLOCK2, pdata->unlock_key2); - ret |= adp5588_write(client, KEY_LCK_EC_STAT, K_LCK_EN); + ret |= adp5588_write(client, KEY_LCK_EC_STAT, ADP5588_K_LCK_EN); } for (i = 0; i < KEYP_MAX_EVENT; i++) @@ -384,7 +357,7 @@ static int __devinit adp5588_setup(struct i2c_client *client) } if (gpio_data) { - for (i = 0; i <= ADP_BANK(MAXGPIO); i++) { + for (i = 0; i <= ADP5588_BANK(ADP5588_MAXGPIO); i++) { int pull_mask = gpio_data->pullup_dis_mask; ret |= adp5588_write(client, GPIO_PULL1 + i, @@ -392,11 +365,14 @@ static int __devinit adp5588_setup(struct i2c_client *client) } } - ret |= adp5588_write(client, INT_STAT, CMP2_INT | CMP1_INT | - OVR_FLOW_INT | K_LCK_INT | - GPI_INT | KE_INT); /* Status is W1C */ + ret |= adp5588_write(client, INT_STAT, + ADP5588_CMP2_INT | ADP5588_CMP1_INT | + ADP5588_OVR_FLOW_INT | ADP5588_K_LCK_INT | + ADP5588_GPI_INT | ADP5588_KE_INT); /* Status is W1C */ - ret |= adp5588_write(client, CFG, INT_CFG | OVR_FLOW_IEN | KE_IEN); + ret |= adp5588_write(client, CFG, ADP5588_INT_CFG | + ADP5588_OVR_FLOW_IEN | + ADP5588_KE_IEN); if (ret < 0) { dev_err(&client->dev, "Write Error\n"); diff --git a/drivers/input/keyboard/atkbd.c b/drivers/input/keyboard/atkbd.c index d358ef8623f4..11478eb2c27d 100644 --- a/drivers/input/keyboard/atkbd.c +++ b/drivers/input/keyboard/atkbd.c @@ -63,6 +63,10 @@ static bool atkbd_extra; module_param_named(extra, atkbd_extra, bool, 0); MODULE_PARM_DESC(extra, "Enable extra LEDs and keys on IBM RapidAcces, EzKey and similar keyboards"); +static bool atkbd_terminal; +module_param_named(terminal, atkbd_terminal, bool, 0); +MODULE_PARM_DESC(terminal, "Enable break codes on an IBM Terminal keyboard connected via AT/PS2"); + /* * Scancode to keycode tables. These are just the default setting, and * are loadable via a userland utility. @@ -136,7 +140,8 @@ static const unsigned short atkbd_unxlate_table[128] = { #define ATKBD_CMD_ENABLE 0x00f4 #define ATKBD_CMD_RESET_DIS 0x00f5 /* Reset to defaults and disable */ #define ATKBD_CMD_RESET_DEF 0x00f6 /* Reset to defaults */ -#define ATKBD_CMD_SETALL_MBR 0x00fa +#define ATKBD_CMD_SETALL_MB 0x00f8 /* Set all keys to give break codes */ +#define ATKBD_CMD_SETALL_MBR 0x00fa /* ... and repeat */ #define ATKBD_CMD_RESET_BAT 0x02ff #define ATKBD_CMD_RESEND 0x00fe #define ATKBD_CMD_EX_ENABLE 0x10ea @@ -764,6 +769,11 @@ static int atkbd_select_set(struct atkbd *atkbd, int target_set, int allow_extra } } + if (atkbd_terminal) { + ps2_command(ps2dev, param, ATKBD_CMD_SETALL_MB); + return 3; + } + if (target_set != 3) return 2; diff --git a/drivers/input/keyboard/spear-keyboard.c b/drivers/input/keyboard/spear-keyboard.c new file mode 100644 index 000000000000..bee03d64c453 --- /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/tca6416-keypad.c b/drivers/input/keyboard/tca6416-keypad.c index 00137bebcf97..800fbccf1f0f 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 b99b8cbde02f..f0d90172a65f 100644 --- a/drivers/input/misc/Kconfig +++ b/drivers/input/misc/Kconfig @@ -448,4 +448,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 1fe1f6c8b737..35bcfe46555e 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 diff --git a/drivers/input/misc/cma3000_d0x.c b/drivers/input/misc/cma3000_d0x.c new file mode 100644 index 000000000000..1633b6342267 --- /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 000000000000..2304ce306e1c --- /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 000000000000..d100cc5c5783 --- /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 4b42ffc0532a..08be1a355956 100644 --- a/drivers/input/misc/pcf8574_keypad.c +++ b/drivers/input/misc/pcf8574_keypad.c @@ -127,14 +127,6 @@ static int __devinit pcf8574_kp_probe(struct i2c_client *client, const struct i2 idev->id.product = 0x0001; idev->id.version = 0x0100; - input_set_drvdata(idev, lp); - - ret = input_register_device(idev); - if (ret) { - dev_err(&client->dev, "input_register_device() failed\n"); - goto fail_register; - } - lp->laststate = read_state(lp); ret = request_threaded_irq(client->irq, NULL, pcf8574_kp_irq_handler, @@ -142,16 +134,21 @@ static int __devinit pcf8574_kp_probe(struct i2c_client *client, const struct i2 DRV_NAME, lp); if (ret) { dev_err(&client->dev, "IRQ %d is not free\n", client->irq); - goto fail_irq; + goto fail_free_device; + } + + ret = input_register_device(idev); + if (ret) { + dev_err(&client->dev, "input_register_device() failed\n"); + goto fail_free_irq; } i2c_set_clientdata(client, lp); return 0; - fail_irq: - input_unregister_device(idev); - fail_register: - input_set_drvdata(idev, NULL); + fail_free_irq: + free_irq(client->irq, lp); + fail_free_device: input_free_device(idev); fail_allocate: kfree(lp); @@ -172,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 @@ -200,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/mouse/hgpk.c b/drivers/input/mouse/hgpk.c index 1d2205b24800..95577c15ae56 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 d61cfd3ee9cb..311c0e87fcbf 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 cd9d0c97e429..3f74baee102b 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 720729afe6dc..da392c22fc6c 100644 --- a/drivers/input/mouse/synaptics.c +++ b/drivers/input/mouse/synaptics.c @@ -824,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; @@ -887,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/mousedev.c b/drivers/input/mousedev.c index 2a00ddf4f23a..7630273e9474 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/ams_delta_serio.c b/drivers/input/serio/ams_delta_serio.c index 8f1770e1e08b..ebe955325677 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 4a3084695c00..448c7724beb9 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 e5624d8f1709..bfd3865d886b 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 7d2b820ef58d..d50f0678bf47 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 ed7ad7416b24..5ae0fc4578fe 100644 --- a/drivers/input/serio/i8042-x86ia64io.h +++ b/drivers/input/serio/i8042-x86ia64io.h @@ -351,6 +351,17 @@ static const struct dmi_system_id __initconst i8042_dmi_nomux_table[] = { }, }, { + /* + * Most (all?) VAIOs do not have external PS/2 ports nor + * they implement active multiplexing properly, and + * MUX discovery usually messes up keyboard/touchpad. + */ + .matches = { + DMI_MATCH(DMI_SYS_VENDOR, "Sony Corporation"), + DMI_MATCH(DMI_BOARD_NAME, "VAIO"), + }, + }, + { /* Amoi M636/A737 */ .matches = { DMI_MATCH(DMI_SYS_VENDOR, "Amoi Electronics CO.,LTD."), @@ -542,6 +553,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"), @@ -741,7 +759,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; } @@ -758,7 +776,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 } @@ -770,7 +788,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, @@ -787,9 +805,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; @@ -798,33 +814,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; } } diff --git a/drivers/input/serio/i8042.c b/drivers/input/serio/i8042.c index 18db5a8c7478..c04ff00a3663 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> @@ -225,8 +227,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 +255,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 +311,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 +357,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 +455,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 +469,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 @@ -512,7 +513,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 +541,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 +560,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 +642,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 +652,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 +677,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 +771,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 +820,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 +846,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 +865,15 @@ static int i8042_controller_selftest(void) do { if (i8042_command(¶m, 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 +884,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 +909,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 +917,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 +941,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 +968,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 +998,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 +1019,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 +1043,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 +1066,7 @@ static void i8042_dritek_enable(void) error = i8042_command(¶m, 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 +1101,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 +1117,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 cbc1beb66574..ac1d759d0f55 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 6bce22e4e495..15aa81c9f1fb 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 405bf214527c..db5b0bca1a1a 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/acecad.c b/drivers/input/tablet/acecad.c index aea9a9399a36..d94f7e9aa997 100644 --- a/drivers/input/tablet/acecad.c +++ b/drivers/input/tablet/acecad.c @@ -229,12 +229,13 @@ static int usb_acecad_probe(struct usb_interface *intf, const struct usb_device_ err = input_register_device(acecad->input); if (err) - goto fail2; + goto fail3; usb_set_intfdata(intf, acecad); return 0; + fail3: usb_free_urb(acecad->irq); fail2: usb_free_coherent(dev, 8, acecad->data, acecad->data_dma); fail1: input_free_device(input_dev); kfree(acecad); diff --git a/drivers/input/touchscreen/Kconfig b/drivers/input/touchscreen/Kconfig index 06ea8da95c62..07ac77d393a4 100644 --- a/drivers/input/touchscreen/Kconfig +++ b/drivers/input/touchscreen/Kconfig @@ -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 7cc1b4f4b677..718bcc814952 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/bu21013_ts.c b/drivers/input/touchscreen/bu21013_ts.c index 2ca9e5d66460..f7fa9ef4cd65 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/qt602240_ts.c b/drivers/input/touchscreen/qt602240_ts.c index 66b26ad3032a..4dcb0e872f6a 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 000000000000..4ab371358b33 --- /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/xen-kbdfront.c b/drivers/input/xen-kbdfront.c index e0c024db2ca5..7f85a862ad11 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; diff --git a/drivers/macintosh/mac_hid.c b/drivers/macintosh/mac_hid.c index 067f9962f499..6a82388505f0 100644 --- a/drivers/macintosh/mac_hid.c +++ b/drivers/macintosh/mac_hid.c @@ -23,6 +23,8 @@ static int mouse_button3_keycode = KEY_RIGHTALT; /* right option key */ static struct input_dev *mac_hid_emumouse_dev; +static DEFINE_MUTEX(mac_hid_emumouse_mutex); + static int mac_hid_create_emumouse(void) { static struct lock_class_key mac_hid_emumouse_dev_event_class; @@ -187,6 +189,10 @@ static int mac_hid_toggle_emumouse(ctl_table *table, int write, int old_val = *valp; int rc; + rc = mutex_lock_killable(&mac_hid_emumouse_mutex); + if (rc) + return rc; + rc = proc_dointvec(table, write, buffer, lenp, ppos); if (rc == 0 && write && *valp != old_val) { @@ -202,6 +208,8 @@ static int mac_hid_toggle_emumouse(ctl_table *table, int write, if (rc) *valp = old_val; + mutex_unlock(&mac_hid_emumouse_mutex); + return rc; } diff --git a/drivers/serial/kgdboc.c b/drivers/serial/kgdboc.c index d4b711c9a416..3374618300af 100644 --- a/drivers/serial/kgdboc.c +++ b/drivers/serial/kgdboc.c @@ -18,6 +18,7 @@ #include <linux/tty.h> #include <linux/console.h> #include <linux/vt_kern.h> +#include <linux/input.h> #define MAX_CONFIG_LEN 40 @@ -37,6 +38,61 @@ static struct tty_driver *kgdb_tty_driver; static int kgdb_tty_line; #ifdef CONFIG_KDB_KEYBOARD +static int kgdboc_reset_connect(struct input_handler *handler, + struct input_dev *dev, + const struct input_device_id *id) +{ + input_reset_device(dev); + + /* Retrun an error - we do not want to bind, just to reset */ + return -ENODEV; +} + +static void kgdboc_reset_disconnect(struct input_handle *handle) +{ + /* We do not expect anyone to actually bind to us */ + BUG(); +} + +static const struct input_device_id kgdboc_reset_ids[] = { + { + .flags = INPUT_DEVICE_ID_MATCH_EVBIT, + .evbit = { BIT_MASK(EV_KEY) }, + }, + { } +}; + +static struct input_handler kgdboc_reset_handler = { + .connect = kgdboc_reset_connect, + .disconnect = kgdboc_reset_disconnect, + .name = "kgdboc_reset", + .id_table = kgdboc_reset_ids, +}; + +static DEFINE_MUTEX(kgdboc_reset_mutex); + +static void kgdboc_restore_input_helper(struct work_struct *dummy) +{ + /* + * We need to take a mutex to prevent several instances of + * this work running on different CPUs so they don't try + * to register again already registered handler. + */ + mutex_lock(&kgdboc_reset_mutex); + + if (input_register_handler(&kgdboc_reset_handler) == 0) + input_unregister_handler(&kgdboc_reset_handler); + + mutex_unlock(&kgdboc_reset_mutex); +} + +static DECLARE_WORK(kgdboc_restore_input_work, kgdboc_restore_input_helper); + +static void kgdboc_restore_input(void) +{ + schedule_work(&kgdboc_restore_input_work); +} + static int kgdboc_register_kbd(char **cptr) { if (strncmp(*cptr, "kbd", 3) == 0) { @@ -64,10 +120,12 @@ static void kgdboc_unregister_kbd(void) i--; } } + flush_work_sync(&kgdboc_restore_input_work); } #else /* ! CONFIG_KDB_KEYBOARD */ #define kgdboc_register_kbd(x) 0 #define kgdboc_unregister_kbd() +#define kgdboc_restore_input() #endif /* ! CONFIG_KDB_KEYBOARD */ static int kgdboc_option_setup(char *opt) @@ -231,6 +289,7 @@ static void kgdboc_post_exp_handler(void) dbg_restore_graphics = 0; con_debug_leave(); } + kgdboc_restore_input(); } static struct kgdb_io kgdboc_io_ops = { diff --git a/include/linux/i2c/adp5588.h b/include/linux/i2c/adp5588.h index 3c5d6b6e765c..cec17cf6cac2 100644 --- a/include/linux/i2c/adp5588.h +++ b/include/linux/i2c/adp5588.h @@ -1,7 +1,7 @@ /* * Analog Devices ADP5588 I/O Expander and QWERTY Keypad Controller * - * Copyright 2009 Analog Devices Inc. + * Copyright 2009-2010 Analog Devices Inc. * * Licensed under the GPL-2 or later. */ @@ -77,13 +77,26 @@ /* Configuration Register1 */ #define ADP5588_AUTO_INC (1 << 7) #define ADP5588_GPIEM_CFG (1 << 6) +#define ADP5588_OVR_FLOW_M (1 << 5) #define ADP5588_INT_CFG (1 << 4) +#define ADP5588_OVR_FLOW_IEN (1 << 3) +#define ADP5588_K_LCK_IM (1 << 2) #define ADP5588_GPI_IEN (1 << 1) +#define ADP5588_KE_IEN (1 << 0) /* Interrupt Status Register */ +#define ADP5588_CMP2_INT (1 << 5) +#define ADP5588_CMP1_INT (1 << 4) +#define ADP5588_OVR_FLOW_INT (1 << 3) +#define ADP5588_K_LCK_INT (1 << 2) #define ADP5588_GPI_INT (1 << 1) #define ADP5588_KE_INT (1 << 0) +/* Key Lock and Event Counter Register */ +#define ADP5588_K_LCK_EN (1 << 6) +#define ADP5588_LCK21 0x30 +#define ADP5588_KEC 0xF + #define ADP5588_MAXGPIO 18 #define ADP5588_BANK(offs) ((offs) >> 3) #define ADP5588_BIT(offs) (1u << ((offs) & 0x7)) diff --git a/include/linux/input.h b/include/linux/input.h index 8d9c76cd3c43..6c407e553a40 100644 --- a/include/linux/input.h +++ b/include/linux/input.h @@ -1418,6 +1418,8 @@ static inline void input_set_drvdata(struct input_dev *dev, void *data) int __must_check input_register_device(struct input_dev *); void input_unregister_device(struct input_dev *); +void input_reset_device(struct input_dev *); + int __must_check input_register_handler(struct input_handler *); void input_unregister_handler(struct input_handler *); @@ -1433,7 +1435,7 @@ void input_release_device(struct input_handle *); int input_open_device(struct input_handle *); void input_close_device(struct input_handle *); -int input_flush_device(struct input_handle* handle, struct file* file); +int input_flush_device(struct input_handle *handle, struct file *file); void input_event(struct input_dev *dev, unsigned int type, unsigned int code, int value); void input_inject_event(struct input_handle *handle, unsigned int type, unsigned int code, int value); diff --git a/include/linux/input/cma3000.h b/include/linux/input/cma3000.h new file mode 100644 index 000000000000..cbbaac27d311 --- /dev/null +++ b/include/linux/input/cma3000.h @@ -0,0 +1,59 @@ +/* + * VTI CMA3000_Dxx 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 _LINUX_CMA3000_H +#define _LINUX_CMA3000_H + +#define CMAMODE_DEFAULT 0 +#define CMAMODE_MEAS100 1 +#define CMAMODE_MEAS400 2 +#define CMAMODE_MEAS40 3 +#define CMAMODE_MOTDET 4 +#define CMAMODE_FF100 5 +#define CMAMODE_FF400 6 +#define CMAMODE_POFF 7 + +#define CMARANGE_2G 2000 +#define CMARANGE_8G 8000 + +/** + * struct cma3000_i2c_platform_data - CMA3000 Platform data + * @fuzz_x: Noise on X Axis + * @fuzz_y: Noise on Y Axis + * @fuzz_z: Noise on Z Axis + * @g_range: G range in milli g i.e 2000 or 8000 + * @mode: Operating mode + * @mdthr: Motion detect threshold value + * @mdfftmr: Motion detect and free fall time value + * @ffthr: Free fall threshold value + */ + +struct cma3000_platform_data { + int fuzz_x; + int fuzz_y; + int fuzz_z; + int g_range; + uint8_t mode; + uint8_t mdthr; + uint8_t mdfftmr; + uint8_t ffthr; + unsigned long irqflags; +}; + +#endif |