diff options
| author | 2011-06-07 15:37:51 +0200 | |
|---|---|---|
| committer | 2011-06-07 15:37:51 +0200 | |
| commit | 2e483528cebad089d0bb3f9aebb0ada22d968ffa (patch) | |
| tree | d701405826b271e819a9a8500838cebd37b1364a /drivers/input | |
| parent | kbuild: Fix reference to vermagic.h (diff) | |
| parent | Linux 3.0-rc1 (diff) | |
Merge commit 'v3.0-rc1' into kbuild/kbuild
Diffstat (limited to 'drivers/input')
64 files changed, 3264 insertions, 503 deletions
diff --git a/drivers/input/evdev.c b/drivers/input/evdev.c index 7f42d3a454d2..be0921ef6b52 100644 --- a/drivers/input/evdev.c +++ b/drivers/input/evdev.c @@ -39,13 +39,14 @@ struct evdev { }; struct evdev_client { - int head; - int tail; + unsigned int head; + unsigned int tail; + unsigned int packet_head; /* [future] position of the first element of next packet */ spinlock_t buffer_lock; /* protects access to buffer, head and tail */ struct fasync_struct *fasync; struct evdev *evdev; struct list_head node; - int bufsize; + unsigned int bufsize; struct input_event buffer[]; }; @@ -55,20 +56,33 @@ static DEFINE_MUTEX(evdev_table_mutex); static void evdev_pass_event(struct evdev_client *client, struct input_event *event) { - /* - * Interrupts are disabled, just acquire the lock. - * Make sure we don't leave with the client buffer - * "empty" by having client->head == client->tail. - */ + /* Interrupts are disabled, just acquire the lock. */ spin_lock(&client->buffer_lock); - do { - client->buffer[client->head++] = *event; - client->head &= client->bufsize - 1; - } while (client->head == client->tail); - spin_unlock(&client->buffer_lock); - if (event->type == EV_SYN) + client->buffer[client->head++] = *event; + client->head &= client->bufsize - 1; + + if (unlikely(client->head == client->tail)) { + /* + * This effectively "drops" all unconsumed events, leaving + * EV_SYN/SYN_DROPPED plus the newest event in the queue. + */ + client->tail = (client->head - 2) & (client->bufsize - 1); + + client->buffer[client->tail].time = event->time; + client->buffer[client->tail].type = EV_SYN; + client->buffer[client->tail].code = SYN_DROPPED; + client->buffer[client->tail].value = 0; + + client->packet_head = client->tail; + } + + if (event->type == EV_SYN && event->code == SYN_REPORT) { + client->packet_head = client->head; kill_fasync(&client->fasync, SIGIO, POLL_IN); + } + + spin_unlock(&client->buffer_lock); } /* @@ -150,7 +164,6 @@ static int evdev_grab(struct evdev *evdev, struct evdev_client *client) return error; rcu_assign_pointer(evdev->grab, client); - synchronize_rcu(); return 0; } @@ -173,7 +186,6 @@ static void evdev_attach_client(struct evdev *evdev, spin_lock(&evdev->client_lock); list_add_tail_rcu(&client->node, &evdev->client_list); spin_unlock(&evdev->client_lock); - synchronize_rcu(); } static void evdev_detach_client(struct evdev *evdev, @@ -378,12 +390,12 @@ static ssize_t evdev_read(struct file *file, char __user *buffer, if (count < input_event_size()) return -EINVAL; - if (client->head == client->tail && evdev->exist && + if (client->packet_head == client->tail && evdev->exist && (file->f_flags & O_NONBLOCK)) return -EAGAIN; retval = wait_event_interruptible(evdev->wait, - client->head != client->tail || !evdev->exist); + client->packet_head != client->tail || !evdev->exist); if (retval) return retval; @@ -412,7 +424,7 @@ static unsigned int evdev_poll(struct file *file, poll_table *wait) poll_wait(file, &evdev->wait, wait); mask = evdev->exist ? POLLOUT | POLLWRNORM : POLLHUP | POLLERR; - if (client->head != client->tail) + if (client->packet_head != client->tail) mask |= POLLIN | POLLRDNORM; return mask; diff --git a/drivers/input/input-compat.h b/drivers/input/input-compat.h index 4d8ea32e8a00..22be27b424de 100644 --- a/drivers/input/input-compat.h +++ b/drivers/input/input-compat.h @@ -19,7 +19,7 @@ /* Note to the author of this code: did it ever occur to you why the ifdefs are needed? Think about it again. -AK */ -#ifdef CONFIG_X86_64 +#if defined(CONFIG_X86_64) || defined(CONFIG_TILE) # define INPUT_COMPAT_TEST is_compat_task() #elif defined(CONFIG_S390) # define INPUT_COMPAT_TEST test_thread_flag(TIF_31BIT) diff --git a/drivers/input/input-polldev.c b/drivers/input/input-polldev.c index 3037842a60d8..b1aabde87523 100644 --- a/drivers/input/input-polldev.c +++ b/drivers/input/input-polldev.c @@ -13,6 +13,7 @@ #include <linux/jiffies.h> #include <linux/slab.h> #include <linux/mutex.h> +#include <linux/workqueue.h> #include <linux/input-polldev.h> MODULE_AUTHOR("Dmitry Torokhov <dtor@mail.ru>"); @@ -20,44 +21,6 @@ MODULE_DESCRIPTION("Generic implementation of a polled input device"); MODULE_LICENSE("GPL v2"); MODULE_VERSION("0.1"); -static DEFINE_MUTEX(polldev_mutex); -static int polldev_users; -static struct workqueue_struct *polldev_wq; - -static int input_polldev_start_workqueue(void) -{ - int retval; - - retval = mutex_lock_interruptible(&polldev_mutex); - if (retval) - return retval; - - if (!polldev_users) { - polldev_wq = create_singlethread_workqueue("ipolldevd"); - if (!polldev_wq) { - pr_err("failed to create ipolldevd workqueue\n"); - retval = -ENOMEM; - goto out; - } - } - - polldev_users++; - - out: - mutex_unlock(&polldev_mutex); - return retval; -} - -static void input_polldev_stop_workqueue(void) -{ - mutex_lock(&polldev_mutex); - - if (!--polldev_users) - destroy_workqueue(polldev_wq); - - mutex_unlock(&polldev_mutex); -} - static void input_polldev_queue_work(struct input_polled_dev *dev) { unsigned long delay; @@ -66,7 +29,7 @@ static void input_polldev_queue_work(struct input_polled_dev *dev) if (delay >= HZ) delay = round_jiffies_relative(delay); - queue_delayed_work(polldev_wq, &dev->work, delay); + queue_delayed_work(system_freezable_wq, &dev->work, delay); } static void input_polled_device_work(struct work_struct *work) @@ -81,18 +44,13 @@ static void input_polled_device_work(struct work_struct *work) static int input_open_polled_device(struct input_dev *input) { struct input_polled_dev *dev = input_get_drvdata(input); - int error; - - error = input_polldev_start_workqueue(); - if (error) - return error; if (dev->open) dev->open(dev); /* Only start polling if polling is enabled */ if (dev->poll_interval > 0) - queue_delayed_work(polldev_wq, &dev->work, 0); + queue_delayed_work(system_freezable_wq, &dev->work, 0); return 0; } @@ -102,13 +60,6 @@ static void input_close_polled_device(struct input_dev *input) struct input_polled_dev *dev = input_get_drvdata(input); cancel_delayed_work_sync(&dev->work); - /* - * Clean up work struct to remove references to the workqueue. - * It may be destroyed by the next call. This causes problems - * at next device open-close in case of poll_interval == 0. - */ - INIT_DELAYED_WORK(&dev->work, dev->work.work.func); - input_polldev_stop_workqueue(); if (dev->close) dev->close(dev); @@ -295,4 +246,3 @@ void input_unregister_polled_device(struct input_polled_dev *dev) input_unregister_device(dev->input); } EXPORT_SYMBOL(input_unregister_polled_device); - diff --git a/drivers/input/input.c b/drivers/input/input.c index d6e8bd8a851c..75e11c7b70fd 100644 --- a/drivers/input/input.c +++ b/drivers/input/input.c @@ -451,7 +451,6 @@ int input_grab_device(struct input_handle *handle) } rcu_assign_pointer(dev->grab, handle); - synchronize_rcu(); out: mutex_unlock(&dev->mutex); @@ -1746,6 +1745,42 @@ void input_set_capability(struct input_dev *dev, unsigned int type, unsigned int } EXPORT_SYMBOL(input_set_capability); +static unsigned int input_estimate_events_per_packet(struct input_dev *dev) +{ + int mt_slots; + int i; + unsigned int events; + + if (dev->mtsize) { + mt_slots = dev->mtsize; + } else if (test_bit(ABS_MT_TRACKING_ID, dev->absbit)) { + mt_slots = dev->absinfo[ABS_MT_TRACKING_ID].maximum - + dev->absinfo[ABS_MT_TRACKING_ID].minimum + 1, + clamp(mt_slots, 2, 32); + } else if (test_bit(ABS_MT_POSITION_X, dev->absbit)) { + mt_slots = 2; + } else { + mt_slots = 0; + } + + events = mt_slots + 1; /* count SYN_MT_REPORT and SYN_REPORT */ + + for (i = 0; i < ABS_CNT; i++) { + if (test_bit(i, dev->absbit)) { + if (input_is_mt_axis(i)) + events += mt_slots; + else + events++; + } + } + + for (i = 0; i < REL_CNT; i++) + if (test_bit(i, dev->relbit)) + events++; + + return events; +} + #define INPUT_CLEANSE_BITMASK(dev, type, bits) \ do { \ if (!test_bit(EV_##type, dev->evbit)) \ @@ -1793,6 +1828,10 @@ int input_register_device(struct input_dev *dev) /* Make sure that bitmasks not mentioned in dev->evbit are clean. */ input_cleanse_bitmasks(dev); + if (!dev->hint_events_per_packet) + dev->hint_events_per_packet = + input_estimate_events_per_packet(dev); + /* * If delay and period are pre-set by the driver, then autorepeating * is handled by the driver itself and we don't do it in input.c. diff --git a/drivers/input/joydev.c b/drivers/input/joydev.c index 3182c9cd1b0e..c24ec2d5f926 100644 --- a/drivers/input/joydev.c +++ b/drivers/input/joydev.c @@ -180,7 +180,6 @@ static void joydev_attach_client(struct joydev *joydev, spin_lock(&joydev->client_lock); list_add_tail_rcu(&client->node, &joydev->client_list); spin_unlock(&joydev->client_lock); - synchronize_rcu(); } static void joydev_detach_client(struct joydev *joydev, @@ -758,7 +757,7 @@ static void joydev_remove_chrdev(struct joydev *joydev) } /* - * Mark device non-existant. This disables writes, ioctls and + * Mark device non-existent. This disables writes, ioctls and * prevents new users from opening the device. Already posted * blocking reads will stay, however new ones will fail. */ @@ -777,7 +776,7 @@ static void joydev_cleanup(struct joydev *joydev) joydev_hangup(joydev); joydev_remove_chrdev(joydev); - /* joydev is marked dead so noone else accesses joydev->open */ + /* joydev is marked dead so no one else accesses joydev->open */ if (joydev->open) input_close_device(handle); } diff --git a/drivers/input/joystick/a3d.c b/drivers/input/joystick/a3d.c index d259b41354b8..1639ab2b94b7 100644 --- a/drivers/input/joystick/a3d.c +++ b/drivers/input/joystick/a3d.c @@ -3,7 +3,7 @@ */ /* - * FP-Gaming Assasin 3D joystick driver for Linux + * FP-Gaming Assassin 3D joystick driver for Linux */ /* @@ -34,7 +34,7 @@ #include <linux/input.h> #include <linux/jiffies.h> -#define DRIVER_DESC "FP-Gaming Assasin 3D joystick driver" +#define DRIVER_DESC "FP-Gaming Assassin 3D joystick driver" MODULE_AUTHOR("Vojtech Pavlik <vojtech@ucw.cz>"); MODULE_DESCRIPTION(DRIVER_DESC); diff --git a/drivers/input/keyboard/Kconfig b/drivers/input/keyboard/Kconfig index b16bed038f72..b4dee9d5a055 100644 --- a/drivers/input/keyboard/Kconfig +++ b/drivers/input/keyboard/Kconfig @@ -32,6 +32,16 @@ config KEYBOARD_ADP5588 To compile this driver as a module, choose M here: the module will be called adp5588-keys. +config KEYBOARD_ADP5589 + tristate "ADP5589 I2C QWERTY Keypad and IO Expander" + depends on I2C + help + Say Y here if you want to use a ADP5589 attached to your + system I2C bus. + + To compile this driver as a module, choose M here: the + module will be called adp5589-keys. + config KEYBOARD_AMIGA tristate "Amiga keyboard" depends on AMIGA @@ -325,6 +335,18 @@ config KEYBOARD_MCS To compile this driver as a module, choose M here: the module will be called mcs_touchkey. +config KEYBOARD_MPR121 + tristate "Freescale MPR121 Touchkey" + depends on I2C + help + Say Y here if you have Freescale MPR121 touchkey controller + chip in your system. + + If unsure, say N. + + To compile this driver as a module, choose M here: the + module will be called mpr121_touchkey. + config KEYBOARD_IMX tristate "IMX keypad support" depends on ARCH_MXC @@ -390,6 +412,17 @@ config KEYBOARD_PXA930_ROTARY To compile this driver as a module, choose M here: the module will be called pxa930_rotary. +config KEYBOARD_PMIC8XXX + tristate "Qualcomm PMIC8XXX keypad support" + depends on MFD_PM8XXX + help + Say Y here if you want to enable the driver for the PMIC8XXX + keypad provided as a reference design from Qualcomm. This is intended + to support upto 18x8 matrix based keypad design. + + To compile this driver as a module, choose M here: the module will + be called pmic8xxx-keypad. + config KEYBOARD_SAMSUNG tristate "Samsung keypad support" depends on SAMSUNG_DEV_KEYPAD diff --git a/drivers/input/keyboard/Makefile b/drivers/input/keyboard/Makefile index 878e6c20deb0..ddde0fd476f7 100644 --- a/drivers/input/keyboard/Makefile +++ b/drivers/input/keyboard/Makefile @@ -6,6 +6,7 @@ obj-$(CONFIG_KEYBOARD_ADP5520) += adp5520-keys.o obj-$(CONFIG_KEYBOARD_ADP5588) += adp5588-keys.o +obj-$(CONFIG_KEYBOARD_ADP5589) += adp5589-keys.o obj-$(CONFIG_KEYBOARD_AMIGA) += amikbd.o obj-$(CONFIG_KEYBOARD_ATARI) += atakbd.o obj-$(CONFIG_KEYBOARD_ATKBD) += atkbd.o @@ -27,11 +28,13 @@ obj-$(CONFIG_KEYBOARD_MAPLE) += maple_keyb.o obj-$(CONFIG_KEYBOARD_MATRIX) += matrix_keypad.o obj-$(CONFIG_KEYBOARD_MAX7359) += max7359_keypad.o obj-$(CONFIG_KEYBOARD_MCS) += mcs_touchkey.o +obj-$(CONFIG_KEYBOARD_MPR121) += mpr121_touchkey.o obj-$(CONFIG_KEYBOARD_NEWTON) += newtonkbd.o obj-$(CONFIG_KEYBOARD_NOMADIK) += nomadik-ske-keypad.o obj-$(CONFIG_KEYBOARD_OMAP) += omap-keypad.o obj-$(CONFIG_KEYBOARD_OMAP4) += omap4-keypad.o obj-$(CONFIG_KEYBOARD_OPENCORES) += opencores-kbd.o +obj-$(CONFIG_KEYBOARD_PMIC8XXX) += pmic8xxx-keypad.o obj-$(CONFIG_KEYBOARD_PXA27x) += pxa27x_keypad.o obj-$(CONFIG_KEYBOARD_PXA930_ROTARY) += pxa930_rotary.o obj-$(CONFIG_KEYBOARD_QT1070) += qt1070.o diff --git a/drivers/input/keyboard/adp5589-keys.c b/drivers/input/keyboard/adp5589-keys.c new file mode 100644 index 000000000000..631598663aab --- /dev/null +++ b/drivers/input/keyboard/adp5589-keys.c @@ -0,0 +1,771 @@ +/* + * Description: keypad driver for ADP5589 + * I2C QWERTY Keypad and IO Expander + * Bugs: Enter bugs at http://blackfin.uclinux.org/ + * + * Copyright (C) 2010-2011 Analog Devices Inc. + * Licensed under the GPL-2. + */ + +#include <linux/module.h> +#include <linux/version.h> +#include <linux/init.h> +#include <linux/interrupt.h> +#include <linux/irq.h> +#include <linux/workqueue.h> +#include <linux/errno.h> +#include <linux/pm.h> +#include <linux/platform_device.h> +#include <linux/input.h> +#include <linux/i2c.h> +#include <linux/gpio.h> +#include <linux/slab.h> + +#include <linux/input/adp5589.h> + +/* GENERAL_CFG Register */ +#define OSC_EN (1 << 7) +#define CORE_CLK(x) (((x) & 0x3) << 5) +#define LCK_TRK_LOGIC (1 << 4) +#define LCK_TRK_GPI (1 << 3) +#define INT_CFG (1 << 1) +#define RST_CFG (1 << 0) + +/* INT_EN Register */ +#define LOGIC2_IEN (1 << 5) +#define LOGIC1_IEN (1 << 4) +#define LOCK_IEN (1 << 3) +#define OVRFLOW_IEN (1 << 2) +#define GPI_IEN (1 << 1) +#define EVENT_IEN (1 << 0) + +/* Interrupt Status Register */ +#define LOGIC2_INT (1 << 5) +#define LOGIC1_INT (1 << 4) +#define LOCK_INT (1 << 3) +#define OVRFLOW_INT (1 << 2) +#define GPI_INT (1 << 1) +#define EVENT_INT (1 << 0) + +/* STATUS Register */ + +#define LOGIC2_STAT (1 << 7) +#define LOGIC1_STAT (1 << 6) +#define LOCK_STAT (1 << 5) +#define KEC 0xF + +/* PIN_CONFIG_D Register */ +#define C4_EXTEND_CFG (1 << 6) /* RESET2 */ +#define R4_EXTEND_CFG (1 << 5) /* RESET1 */ + +/* LOCK_CFG */ +#define LOCK_EN (1 << 0) + +#define PTIME_MASK 0x3 +#define LTIME_MASK 0x3 + +/* Key Event Register xy */ +#define KEY_EV_PRESSED (1 << 7) +#define KEY_EV_MASK (0x7F) + +#define KEYP_MAX_EVENT 16 + +#define MAXGPIO 19 +#define ADP_BANK(offs) ((offs) >> 3) +#define ADP_BIT(offs) (1u << ((offs) & 0x7)) + +struct adp5589_kpad { + struct i2c_client *client; + struct input_dev *input; + unsigned short keycode[ADP5589_KEYMAPSIZE]; + const struct adp5589_gpi_map *gpimap; + unsigned short gpimapsize; + unsigned extend_cfg; +#ifdef CONFIG_GPIOLIB + unsigned char gpiomap[MAXGPIO]; + bool export_gpio; + struct gpio_chip gc; + struct mutex gpio_lock; /* Protect cached dir, dat_out */ + u8 dat_out[3]; + u8 dir[3]; +#endif +}; + +static int adp5589_read(struct i2c_client *client, u8 reg) +{ + int ret = i2c_smbus_read_byte_data(client, reg); + + if (ret < 0) + dev_err(&client->dev, "Read Error\n"); + + return ret; +} + +static int adp5589_write(struct i2c_client *client, u8 reg, u8 val) +{ + return i2c_smbus_write_byte_data(client, reg, val); +} + +#ifdef CONFIG_GPIOLIB +static int adp5589_gpio_get_value(struct gpio_chip *chip, unsigned off) +{ + struct adp5589_kpad *kpad = container_of(chip, struct adp5589_kpad, gc); + unsigned int bank = ADP_BANK(kpad->gpiomap[off]); + unsigned int bit = ADP_BIT(kpad->gpiomap[off]); + + return !!(adp5589_read(kpad->client, ADP5589_GPI_STATUS_A + bank) & + bit); +} + +static void adp5589_gpio_set_value(struct gpio_chip *chip, + unsigned off, int val) +{ + struct adp5589_kpad *kpad = container_of(chip, struct adp5589_kpad, gc); + unsigned int bank = ADP_BANK(kpad->gpiomap[off]); + unsigned int bit = ADP_BIT(kpad->gpiomap[off]); + + mutex_lock(&kpad->gpio_lock); + + if (val) + kpad->dat_out[bank] |= bit; + else + kpad->dat_out[bank] &= ~bit; + + adp5589_write(kpad->client, ADP5589_GPO_DATA_OUT_A + bank, + kpad->dat_out[bank]); + + mutex_unlock(&kpad->gpio_lock); +} + +static int adp5589_gpio_direction_input(struct gpio_chip *chip, unsigned off) +{ + struct adp5589_kpad *kpad = container_of(chip, struct adp5589_kpad, gc); + unsigned int bank = ADP_BANK(kpad->gpiomap[off]); + unsigned int bit = ADP_BIT(kpad->gpiomap[off]); + int ret; + + mutex_lock(&kpad->gpio_lock); + + kpad->dir[bank] &= ~bit; + ret = adp5589_write(kpad->client, ADP5589_GPIO_DIRECTION_A + bank, + kpad->dir[bank]); + + mutex_unlock(&kpad->gpio_lock); + + return ret; +} + +static int adp5589_gpio_direction_output(struct gpio_chip *chip, + unsigned off, int val) +{ + struct adp5589_kpad *kpad = container_of(chip, struct adp5589_kpad, gc); + unsigned int bank = ADP_BANK(kpad->gpiomap[off]); + unsigned int bit = ADP_BIT(kpad->gpiomap[off]); + int ret; + + mutex_lock(&kpad->gpio_lock); + + kpad->dir[bank] |= bit; + + if (val) + kpad->dat_out[bank] |= bit; + else + kpad->dat_out[bank] &= ~bit; + + ret = adp5589_write(kpad->client, ADP5589_GPO_DATA_OUT_A + bank, + kpad->dat_out[bank]); + ret |= adp5589_write(kpad->client, ADP5589_GPIO_DIRECTION_A + bank, + kpad->dir[bank]); + + mutex_unlock(&kpad->gpio_lock); + + return ret; +} + +static int __devinit adp5589_build_gpiomap(struct adp5589_kpad *kpad, + const struct adp5589_kpad_platform_data *pdata) +{ + bool pin_used[MAXGPIO]; + int n_unused = 0; + int i; + + memset(pin_used, false, sizeof(pin_used)); + + for (i = 0; i < MAXGPIO; i++) + if (pdata->keypad_en_mask & (1 << i)) + pin_used[i] = true; + + for (i = 0; i < kpad->gpimapsize; i++) + pin_used[kpad->gpimap[i].pin - ADP5589_GPI_PIN_BASE] = true; + + if (kpad->extend_cfg & R4_EXTEND_CFG) + pin_used[4] = true; + + if (kpad->extend_cfg & C4_EXTEND_CFG) + pin_used[12] = true; + + for (i = 0; i < MAXGPIO; i++) + if (!pin_used[i]) + kpad->gpiomap[n_unused++] = i; + + return n_unused; +} + +static int __devinit adp5589_gpio_add(struct adp5589_kpad *kpad) +{ + struct device *dev = &kpad->client->dev; + const struct adp5589_kpad_platform_data *pdata = dev->platform_data; + const struct adp5589_gpio_platform_data *gpio_data = pdata->gpio_data; + int i, error; + + if (!gpio_data) + return 0; + + kpad->gc.ngpio = adp5589_build_gpiomap(kpad, pdata); + if (kpad->gc.ngpio == 0) { + dev_info(dev, "No unused gpios left to export\n"); + return 0; + } + + kpad->export_gpio = true; + + kpad->gc.direction_input = adp5589_gpio_direction_input; + kpad->gc.direction_output = adp5589_gpio_direction_output; + kpad->gc.get = adp5589_gpio_get_value; + kpad->gc.set = adp5589_gpio_set_value; + kpad->gc.can_sleep = 1; + + kpad->gc.base = gpio_data->gpio_start; + kpad->gc.label = kpad->client->name; + kpad->gc.owner = THIS_MODULE; + + mutex_init(&kpad->gpio_lock); + + error = gpiochip_add(&kpad->gc); + if (error) { + dev_err(dev, "gpiochip_add failed, err: %d\n", error); + return error; + } + + for (i = 0; i <= ADP_BANK(MAXGPIO); i++) { + kpad->dat_out[i] = adp5589_read(kpad->client, + ADP5589_GPO_DATA_OUT_A + i); + kpad->dir[i] = adp5589_read(kpad->client, + ADP5589_GPIO_DIRECTION_A + i); + } + + if (gpio_data->setup) { + error = gpio_data->setup(kpad->client, + kpad->gc.base, kpad->gc.ngpio, + gpio_data->context); + if (error) + dev_warn(dev, "setup failed, %d\n", error); + } + + return 0; +} + +static void __devexit adp5589_gpio_remove(struct adp5589_kpad *kpad) +{ + struct device *dev = &kpad->client->dev; + const struct adp5589_kpad_platform_data *pdata = dev->platform_data; + const struct adp5589_gpio_platform_data *gpio_data = pdata->gpio_data; + int error; + + if (!kpad->export_gpio) + return; + + if (gpio_data->teardown) { + error = gpio_data->teardown(kpad->client, + kpad->gc.base, kpad->gc.ngpio, + gpio_data->context); + if (error) + dev_warn(dev, "teardown failed %d\n", error); + } + + error = gpiochip_remove(&kpad->gc); + if (error) + dev_warn(dev, "gpiochip_remove failed %d\n", error); +} +#else +static inline int adp5589_gpio_add(struct adp5589_kpad *kpad) +{ + return 0; +} + +static inline void adp5589_gpio_remove(struct adp5589_kpad *kpad) +{ +} +#endif + +static void adp5589_report_switches(struct adp5589_kpad *kpad, + int key, int key_val) +{ + int i; + + for (i = 0; i < kpad->gpimapsize; i++) { + if (key_val == kpad->gpimap[i].pin) { + input_report_switch(kpad->input, + kpad->gpimap[i].sw_evt, + key & KEY_EV_PRESSED); + break; + } + } +} + +static void adp5589_report_events(struct adp5589_kpad *kpad, int ev_cnt) +{ + int i; + + for (i = 0; i < ev_cnt; i++) { + int key = adp5589_read(kpad->client, ADP5589_FIFO_1 + i); + int key_val = key & KEY_EV_MASK; + + if (key_val >= ADP5589_GPI_PIN_BASE && + key_val <= ADP5589_GPI_PIN_END) { + adp5589_report_switches(kpad, key, key_val); + } else { + input_report_key(kpad->input, + kpad->keycode[key_val - 1], + key & KEY_EV_PRESSED); + } + } +} + +static irqreturn_t adp5589_irq(int irq, void *handle) +{ + struct adp5589_kpad *kpad = handle; + struct i2c_client *client = kpad->client; + int status, ev_cnt; + + status = adp5589_read(client, ADP5589_INT_STATUS); + + if (status & OVRFLOW_INT) /* Unlikely and should never happen */ + dev_err(&client->dev, "Event Overflow Error\n"); + + if (status & EVENT_INT) { + ev_cnt = adp5589_read(client, ADP5589_STATUS) & KEC; + if (ev_cnt) { + adp5589_report_events(kpad, ev_cnt); + input_sync(kpad->input); + } + } + + adp5589_write(client, ADP5589_INT_STATUS, status); /* Status is W1C */ + + return IRQ_HANDLED; +} + +static int __devinit adp5589_get_evcode(struct adp5589_kpad *kpad, unsigned short key) +{ + int i; + + for (i = 0; i < ADP5589_KEYMAPSIZE; i++) + if (key == kpad->keycode[i]) + return (i + 1) | KEY_EV_PRESSED; + + dev_err(&kpad->client->dev, "RESET/UNLOCK key not in keycode map\n"); + + return -EINVAL; +} + +static int __devinit adp5589_setup(struct adp5589_kpad *kpad) +{ + struct i2c_client *client = kpad->client; + const struct adp5589_kpad_platform_data *pdata = + client->dev.platform_data; + int i, ret; + unsigned char evt_mode1 = 0, evt_mode2 = 0, evt_mode3 = 0; + unsigned char pull_mask = 0; + + ret = adp5589_write(client, ADP5589_PIN_CONFIG_A, + pdata->keypad_en_mask & 0xFF); + ret |= adp5589_write(client, ADP5589_PIN_CONFIG_B, + (pdata->keypad_en_mask >> 8) & 0xFF); + ret |= adp5589_write(client, ADP5589_PIN_CONFIG_C, + (pdata->keypad_en_mask >> 16) & 0xFF); + + if (pdata->en_keylock) { + ret |= adp5589_write(client, ADP5589_UNLOCK1, + pdata->unlock_key1); + ret |= adp5589_write(client, ADP5589_UNLOCK2, + pdata->unlock_key2); + ret |= adp5589_write(client, ADP5589_UNLOCK_TIMERS, + pdata->unlock_timer & LTIME_MASK); + ret |= adp5589_write(client, ADP5589_LOCK_CFG, LOCK_EN); + } + + for (i = 0; i < KEYP_MAX_EVENT; i++) + ret |= adp5589_read(client, ADP5589_FIFO_1 + i); + + for (i = 0; i < pdata->gpimapsize; i++) { + unsigned short pin = pdata->gpimap[i].pin; + + if (pin <= ADP5589_GPI_PIN_ROW_END) { + evt_mode1 |= (1 << (pin - ADP5589_GPI_PIN_ROW_BASE)); + } else { + evt_mode2 |= + ((1 << (pin - ADP5589_GPI_PIN_COL_BASE)) & 0xFF); + evt_mode3 |= + ((1 << (pin - ADP5589_GPI_PIN_COL_BASE)) >> 8); + } + } + + if (pdata->gpimapsize) { + ret |= adp5589_write(client, ADP5589_GPI_EVENT_EN_A, evt_mode1); + ret |= adp5589_write(client, ADP5589_GPI_EVENT_EN_B, evt_mode2); + ret |= adp5589_write(client, ADP5589_GPI_EVENT_EN_C, evt_mode3); + } + + if (pdata->pull_dis_mask & pdata->pullup_en_100k & + pdata->pullup_en_300k & pdata->pulldown_en_300k) + dev_warn(&client->dev, "Conflicting pull resistor config\n"); + + for (i = 0; i < MAXGPIO; i++) { + unsigned val = 0; + + if (pdata->pullup_en_300k & (1 << i)) + val = 0; + else if (pdata->pulldown_en_300k & (1 << i)) + val = 1; + else if (pdata->pullup_en_100k & (1 << i)) + val = 2; + else if (pdata->pull_dis_mask & (1 << i)) + val = 3; + + pull_mask |= val << (2 * (i & 0x3)); + + if ((i & 0x3) == 0x3 || i == MAXGPIO - 1) { + ret |= adp5589_write(client, + ADP5589_RPULL_CONFIG_A + (i >> 2), + pull_mask); + pull_mask = 0; + } + } + + if (pdata->reset1_key_1 && pdata->reset1_key_2 && pdata->reset1_key_3) { + ret |= adp5589_write(client, ADP5589_RESET1_EVENT_A, + adp5589_get_evcode(kpad, + pdata->reset1_key_1)); + ret |= adp5589_write(client, ADP5589_RESET1_EVENT_B, + adp5589_get_evcode(kpad, + pdata->reset1_key_2)); + ret |= adp5589_write(client, ADP5589_RESET1_EVENT_C, + adp5589_get_evcode(kpad, + pdata->reset1_key_3)); + kpad->extend_cfg |= R4_EXTEND_CFG; + } + + if (pdata->reset2_key_1 && pdata->reset2_key_2) { + ret |= adp5589_write(client, ADP5589_RESET2_EVENT_A, + adp5589_get_evcode(kpad, + pdata->reset2_key_1)); + ret |= adp5589_write(client, ADP5589_RESET2_EVENT_B, + adp5589_get_evcode(kpad, + pdata->reset2_key_2)); + kpad->extend_cfg |= C4_EXTEND_CFG; + } + + if (kpad->extend_cfg) { + ret |= adp5589_write(client, ADP5589_RESET_CFG, + pdata->reset_cfg); + ret |= adp5589_write(client, ADP5589_PIN_CONFIG_D, + kpad->extend_cfg); + } + + for (i = 0; i <= ADP_BANK(MAXGPIO); i++) + ret |= adp5589_write(client, ADP5589_DEBOUNCE_DIS_A + i, + pdata->debounce_dis_mask >> (i * 8)); + + ret |= adp5589_write(client, ADP5589_POLL_PTIME_CFG, + pdata->scan_cycle_time & PTIME_MASK); + ret |= adp5589_write(client, ADP5589_INT_STATUS, LOGIC2_INT | + LOGIC1_INT | OVRFLOW_INT | LOCK_INT | + GPI_INT | EVENT_INT); /* Status is W1C */ + + ret |= adp5589_write(client, ADP5589_GENERAL_CFG, + INT_CFG | OSC_EN | CORE_CLK(3)); + ret |= adp5589_write(client, ADP5589_INT_EN, + OVRFLOW_IEN | GPI_IEN | EVENT_IEN); + + if (ret < 0) { + dev_err(&client->dev, "Write Error\n"); + return ret; + } + + return 0; +} + +static void __devinit adp5589_report_switch_state(struct adp5589_kpad *kpad) +{ + int gpi_stat1 = adp5589_read(kpad->client, ADP5589_GPI_STATUS_A); + int gpi_stat2 = adp5589_read(kpad->client, ADP5589_GPI_STATUS_B); + int gpi_stat3 = adp5589_read(kpad->client, ADP5589_GPI_STATUS_C); + int gpi_stat_tmp, pin_loc; + int i; + + for (i = 0; i < kpad->gpimapsize; i++) { + unsigned short pin = kpad->gpimap[i].pin; + + if (pin <= ADP5589_GPI_PIN_ROW_END) { + gpi_stat_tmp = gpi_stat1; + pin_loc = pin - ADP5589_GPI_PIN_ROW_BASE; + } else if ((pin - ADP5589_GPI_PIN_COL_BASE) < 8) { + gpi_stat_tmp = gpi_stat2; + pin_loc = pin - ADP5589_GPI_PIN_COL_BASE; + } else { + gpi_stat_tmp = gpi_stat3; + pin_loc = pin - ADP5589_GPI_PIN_COL_BASE - 8; + } + + if (gpi_stat_tmp < 0) { + dev_err(&kpad->client->dev, + "Can't read GPIO_DAT_STAT switch" + " %d default to OFF\n", pin); + gpi_stat_tmp = 0; + } + + input_report_switch(kpad->input, + kpad->gpimap[i].sw_evt, + !(gpi_stat_tmp & (1 << pin_loc))); + } + + input_sync(kpad->input); +} + +static int __devinit adp5589_probe(struct i2c_client *client, + const struct i2c_device_id *id) +{ + struct adp5589_kpad *kpad; + const struct adp5589_kpad_platform_data *pdata; + struct input_dev *input; + unsigned int revid; + int ret, i; + int error; + + if (!i2c_check_functionality(client->adapter, + I2C_FUNC_SMBUS_BYTE_DATA)) { + dev_err(&client->dev, "SMBUS Byte Data not Supported\n"); + return -EIO; + } + + pdata = client->dev.platform_data; + if (!pdata) { + dev_err(&client->dev, "no platform data?\n"); + return -EINVAL; + } + + if (!((pdata->keypad_en_mask & 0xFF) && + (pdata->keypad_en_mask >> 8)) || !pdata->keymap) { + dev_err(&client->dev, "no rows, cols or keymap from pdata\n"); + return -EINVAL; + } + + if (pdata->keymapsize != ADP5589_KEYMAPSIZE) { + dev_err(&client->dev, "invalid keymapsize\n"); + return -EINVAL; + } + + if (!pdata->gpimap && pdata->gpimapsize) { + dev_err(&client->dev, "invalid gpimap from pdata\n"); + return -EINVAL; + } + + if (pdata->gpimapsize > ADP5589_GPIMAPSIZE_MAX) { + dev_err(&client->dev, "invalid gpimapsize\n"); + return -EINVAL; + } + + for (i = 0; i < pdata->gpimapsize; i++) { + unsigned short pin = pdata->gpimap[i].pin; + + if (pin < ADP5589_GPI_PIN_BASE || pin > ADP5589_GPI_PIN_END) { + dev_err(&client->dev, "invalid gpi pin data\n"); + return -EINVAL; + } + + if ((1 << (pin - ADP5589_GPI_PIN_ROW_BASE)) & + pdata->keypad_en_mask) { + dev_err(&client->dev, "invalid gpi row/col data\n"); + return -EINVAL; + } + } + + if (!client->irq) { + dev_err(&client->dev, "no IRQ?\n"); + return -EINVAL; + } + + kpad = kzalloc(sizeof(*kpad), GFP_KERNEL); + input = input_allocate_device(); + if (!kpad || !input) { + error = -ENOMEM; + goto err_free_mem; + } + + kpad->client = client; + kpad->input = input; + + ret = adp5589_read(client, ADP5589_ID); + if (ret < 0) { + error = ret; + goto err_free_mem; + } + + revid = (u8) ret & ADP5589_DEVICE_ID_MASK; + + input->name = client->name; + input->phys = "adp5589-keys/input0"; + input->dev.parent = &client->dev; + + input_set_drvdata(input, kpad); + + input->id.bustype = BUS_I2C; + input->id.vendor = 0x0001; + input->id.product = 0x0001; + input->id.version = revid; + + input->keycodesize = sizeof(kpad->keycode[0]); + input->keycodemax = pdata->keymapsize; + input->keycode = kpad->keycode; + + memcpy(kpad->keycode, pdata->keymap, + pdata->keymapsize * input->keycodesize); + + kpad->gpimap = pdata->gpimap; + kpad->gpimapsize = pdata->gpimapsize; + + /* setup input device */ + __set_bit(EV_KEY, input->evbit); + + if (pdata->repeat) + __set_bit(EV_REP, input->evbit); + + for (i = 0; i < input->keycodemax; i++) + __set_bit(kpad->keycode[i] & KEY_MAX, input->keybit); + __clear_bit(KEY_RESERVED, input->keybit); + + if (kpad->gpimapsize) + __set_bit(EV_SW, input->evbit); + for (i = 0; i < kpad->gpimapsize; i++) + __set_bit(kpad->gpimap[i].sw_evt, input->swbit); + + error = input_register_device(input); + if (error) { + dev_err(&client->dev, "unable to register input device\n"); + goto err_free_mem; + } + + error = request_threaded_irq(client->irq, NULL, adp5589_irq, + IRQF_TRIGGER_FALLING | IRQF_ONESHOT, + client->dev.driver->name, kpad); + if (error) { + dev_err(&client->dev, "irq %d busy?\n", client->irq); + goto err_unreg_dev; + } + + error = adp5589_setup(kpad); + if (error) + goto err_free_irq; + + if (kpad->gpimapsize) + adp5589_report_switch_state(kpad); + + error = adp5589_gpio_add(kpad); + if (error) + goto err_free_irq; + + device_init_wakeup(&client->dev, 1); + i2c_set_clientdata(client, kpad); + + dev_info(&client->dev, "Rev.%d keypad, irq %d\n", revid, client->irq); + return 0; + +err_free_irq: + free_irq(client->irq, kpad); +err_unreg_dev: + input_unregister_device(input); + input = NULL; +err_free_mem: + input_free_device(input); + kfree(kpad); + + return error; +} + +static int __devexit adp5589_remove(struct i2c_client *client) +{ + struct adp5589_kpad *kpad = i2c_get_clientdata(client); + + adp5589_write(client, ADP5589_GENERAL_CFG, 0); + free_irq(client->irq, kpad); + input_unregister_device(kpad->input); + adp5589_gpio_remove(kpad); + kfree(kpad); + + return 0; +} + +#ifdef CONFIG_PM_SLEEP +static int adp5589_suspend(struct device *dev) +{ + struct adp5589_kpad *kpad = dev_get_drvdata(dev); + struct i2c_client *client = kpad->client; + + disable_irq(client->irq); + + if (device_may_wakeup(&client->dev)) + enable_irq_wake(client->irq); + + return 0; +} + +static int adp5589_resume(struct device *dev) +{ + struct adp5589_kpad *kpad = dev_get_drvdata(dev); + struct i2c_client *client = kpad->client; + + if (device_may_wakeup(&client->dev)) + disable_irq_wake(client->irq); + + enable_irq(client->irq); + + return 0; +} +#endif + +static SIMPLE_DEV_PM_OPS(adp5589_dev_pm_ops, adp5589_suspend, adp5589_resume); + +static const struct i2c_device_id adp5589_id[] = { + {"adp5589-keys", 0}, + {} +}; + +MODULE_DEVICE_TABLE(i2c, adp5589_id); + +static struct i2c_driver adp5589_driver = { + .driver = { + .name = KBUILD_MODNAME, + .owner = THIS_MODULE, + .pm = &adp5589_dev_pm_ops, + }, + .probe = adp5589_probe, + .remove = __devexit_p(adp5589_remove), + .id_table = adp5589_id, +}; + +static int __init adp5589_init(void) +{ + return i2c_add_driver(&adp5589_driver); +} +module_init(adp5589_init); + +static void __exit adp5589_exit(void) +{ + i2c_del_driver(&adp5589_driver); +} +module_exit(adp5589_exit); + +MODULE_LICENSE("GPL"); +MODULE_AUTHOR("Michael Hennerich <hennerich@blackfin.uclinux.org>"); +MODULE_DESCRIPTION("ADP5589 Keypad driver"); diff --git a/drivers/input/keyboard/atakbd.c b/drivers/input/keyboard/atakbd.c index 1839194ea987..10bcd4ae5402 100644 --- a/drivers/input/keyboard/atakbd.c +++ b/drivers/input/keyboard/atakbd.c @@ -223,8 +223,9 @@ static int __init atakbd_init(void) return -ENODEV; // need to init core driver if not already done so - if (atari_keyb_init()) - return -ENODEV; + error = atari_keyb_init(); + if (error) + return error; atakbd_dev = input_allocate_device(); if (!atakbd_dev) diff --git a/drivers/input/keyboard/davinci_keyscan.c b/drivers/input/keyboard/davinci_keyscan.c index a91ee941b5c1..cd89d17162a3 100644 --- a/drivers/input/keyboard/davinci_keyscan.c +++ b/drivers/input/keyboard/davinci_keyscan.c @@ -5,7 +5,7 @@ * * Author: Miguel Aguilar <miguel.aguilar@ridgerun.com> * - * Intial Code: Sandeep Paulraj <s-paulraj@ti.com> + * Initial Code: Sandeep Paulraj <s-paulraj@ti.com> * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by diff --git a/drivers/input/keyboard/gpio_keys.c b/drivers/input/keyboard/gpio_keys.c index eb3006361ee4..6e6145b9a4c1 100644 --- a/drivers/input/keyboard/gpio_keys.c +++ b/drivers/input/keyboard/gpio_keys.c @@ -324,7 +324,12 @@ static void gpio_keys_report_event(struct gpio_button_data *bdata) unsigned int type = button->type ?: EV_KEY; int state = (gpio_get_value_cansleep(button->gpio) ? 1 : 0) ^ button->active_low; - input_event(input, type, button->code, !!state); + if (type == EV_ABS) { + if (state) + input_event(input, type, button->code, button->value); + } else { + input_event(input, type, button->code, !!state); + } input_sync(input); } @@ -363,7 +368,7 @@ static int __devinit gpio_keys_setup_key(struct platform_device *pdev, struct gpio_button_data *bdata, struct gpio_keys_button *button) { - char *desc = button->desc ? button->desc : "gpio_keys"; + const char *desc = button->desc ? button->desc : "gpio_keys"; struct device *dev = &pdev->dev; unsigned long irqflags; int irq, error; @@ -468,7 +473,7 @@ static int __devinit gpio_keys_probe(struct platform_device *pdev) platform_set_drvdata(pdev, ddata); input_set_drvdata(input, ddata); - input->name = pdev->name; + input->name = pdata->name ? : pdev->name; input->phys = "gpio-keys/input0"; input->dev.parent = &pdev->dev; input->open = gpio_keys_open; diff --git a/drivers/input/keyboard/mpr121_touchkey.c b/drivers/input/keyboard/mpr121_touchkey.c new file mode 100644 index 000000000000..0a9e81194888 --- /dev/null +++ b/drivers/input/keyboard/mpr121_touchkey.c @@ -0,0 +1,339 @@ +/* + * Touchkey driver for Freescale MPR121 Controllor + * + * Copyright (C) 2011 Freescale Semiconductor, Inc. + * Author: Zhang Jiejing <jiejing.zhang@freescale.com> + * + * Based on mcs_touchkey.c + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + */ + +#include <linux/module.h> +#include <linux/init.h> +#include <linux/input.h> +#include <linux/i2c.h> +#include <linux/slab.h> +#include <linux/delay.h> +#include <linux/bitops.h> +#include <linux/interrupt.h> +#include <linux/i2c/mpr121_touchkey.h> + +/* Register definitions */ +#define ELE_TOUCH_STATUS_0_ADDR 0x0 +#define ELE_TOUCH_STATUS_1_ADDR 0X1 +#define MHD_RISING_ADDR 0x2b +#define NHD_RISING_ADDR 0x2c +#define NCL_RISING_ADDR 0x2d +#define FDL_RISING_ADDR 0x2e +#define MHD_FALLING_ADDR 0x2f +#define NHD_FALLING_ADDR 0x30 +#define NCL_FALLING_ADDR 0x31 +#define FDL_FALLING_ADDR 0x32 +#define ELE0_TOUCH_THRESHOLD_ADDR 0x41 +#define ELE0_RELEASE_THRESHOLD_ADDR 0x42 +#define AFE_CONF_ADDR 0x5c +#define FILTER_CONF_ADDR 0x5d + +/* + * ELECTRODE_CONF_ADDR: This register configures the number of + * enabled capacitance sensing inputs and its run/suspend mode. + */ +#define ELECTRODE_CONF_ADDR 0x5e +#define AUTO_CONFIG_CTRL_ADDR 0x7b +#define AUTO_CONFIG_USL_ADDR 0x7d +#define AUTO_CONFIG_LSL_ADDR 0x7e +#define AUTO_CONFIG_TL_ADDR 0x7f + +/* Threshold of touch/release trigger */ +#define TOUCH_THRESHOLD 0x0f +#define RELEASE_THRESHOLD 0x0a +/* Masks for touch and release triggers */ +#define TOUCH_STATUS_MASK 0xfff +/* MPR121 has 12 keys */ +#define MPR121_MAX_KEY_COUNT 12 + +struct mpr121_touchkey { + struct i2c_client *client; + struct input_dev *input_dev; + unsigned int key_val; + unsigned int statusbits; + unsigned int keycount; + u16 keycodes[MPR121_MAX_KEY_COUNT]; +}; + +struct mpr121_init_register { + int addr; + u8 val; +}; + +static const struct mpr121_init_register init_reg_table[] __devinitconst = { + { MHD_RISING_ADDR, 0x1 }, + { NHD_RISING_ADDR, 0x1 }, + { MHD_FALLING_ADDR, 0x1 }, + { NHD_FALLING_ADDR, 0x1 }, + { NCL_FALLING_ADDR, 0xff }, + { FDL_FALLING_ADDR, 0x02 }, + { FILTER_CONF_ADDR, 0x04 }, + { AFE_CONF_ADDR, 0x0b }, + { AUTO_CONFIG_CTRL_ADDR, 0x0b }, +}; + +static irqreturn_t mpr_touchkey_interrupt(int irq, void *dev_id) +{ + struct mpr121_touchkey *mpr121 = dev_id; + struct i2c_client *client = mpr121->client; + struct input_dev *input = mpr121->input_dev; + unsigned int key_num, key_val, pressed; + int reg; + + reg = i2c_smbus_read_byte_data(client, ELE_TOUCH_STATUS_1_ADDR); + if (reg < 0) { + dev_err(&client->dev, "i2c read error [%d]\n", reg); + goto out; + } + + reg <<= 8; + reg |= i2c_smbus_read_byte_data(client, ELE_TOUCH_STATUS_0_ADDR); + if (reg < 0) { + dev_err(&client->dev, "i2c read error [%d]\n", reg); + goto out; + } + + reg &= TOUCH_STATUS_MASK; + /* use old press bit to figure out which bit changed */ + key_num = ffs(reg ^ mpr121->statusbits) - 1; + pressed = reg & (1 << key_num); + mpr121->statusbits = reg; + + key_val = mpr121->keycodes[key_num]; + + input_event(input, EV_MSC, MSC_SCAN, key_num); + input_report_key(input, key_val, pressed); + input_sync(input); + + dev_dbg(&client->dev, "key %d %d %s\n", key_num, key_val, + pressed ? "pressed" : "released"); + +out: + return IRQ_HANDLED; +} + +static int __devinit mpr121_phys_init(const struct mpr121_platform_data *pdata, + struct mpr121_touchkey *mpr121, + struct i2c_client *client) +{ + const struct mpr121_init_register *reg; + unsigned char usl, lsl, tl; + int i, t, vdd, ret; + + /* Set up touch/release threshold for ele0-ele11 */ + for (i = 0; i <= MPR121_MAX_KEY_COUNT; i++) { + t = ELE0_TOUCH_THRESHOLD_ADDR + (i * 2); + ret = i2c_smbus_write_byte_data(client, t, TOUCH_THRESHOLD); + if (ret < 0) + goto err_i2c_write; + ret = i2c_smbus_write_byte_data(client, t + 1, + RELEASE_THRESHOLD); + if (ret < 0) + goto err_i2c_write; + } + + /* Set up init register */ + for (i = 0; i < ARRAY_SIZE(init_reg_table); i++) { + reg = &init_reg_table[i]; + ret = i2c_smbus_write_byte_data(client, reg->addr, reg->val); + if (ret < 0) + goto err_i2c_write; + } + + + /* + * Capacitance on sensing input varies and needs to be compensated. + * The internal MPR121-auto-configuration can do this if it's + * registers are set properly (based on pdata->vdd_uv). + */ + vdd = pdata->vdd_uv / 1000; + usl = ((vdd - 700) * 256) / vdd; + lsl = (usl * 65) / 100; + tl = (usl * 90) / 100; + ret = i2c_smbus_write_byte_data(client, AUTO_CONFIG_USL_ADDR, usl); + ret |= i2c_smbus_write_byte_data(client, AUTO_CONFIG_LSL_ADDR, lsl); + ret |= i2c_smbus_write_byte_data(client, AUTO_CONFIG_TL_ADDR, tl); + ret |= i2c_smbus_write_byte_data(client, ELECTRODE_CONF_ADDR, + mpr121->keycount); + if (ret != 0) + goto err_i2c_write; + + dev_dbg(&client->dev, "set up with %x keys.\n", mpr121->keycount); + + return 0; + +err_i2c_write: + dev_err(&client->dev, "i2c write error: %d\n", ret); + return ret; +} + +static int __devinit mpr_touchkey_probe(struct i2c_client *client, + const struct i2c_device_id *id) +{ + const struct mpr121_platform_data *pdata = client->dev.platform_data; + struct mpr121_touchkey *mpr121; + struct input_dev *input_dev; + int error; + int i; + + if (!pdata) { + dev_err(&client->dev, "no platform data defined\n"); + return -EINVAL; + } + + if (!pdata->keymap || !pdata->keymap_size) { + dev_err(&client->dev, "missing keymap data\n"); + return -EINVAL; + } + + if (pdata->keymap_size > MPR121_MAX_KEY_COUNT) { + dev_err(&client->dev, "too many keys defined\n"); + return -EINVAL; + } + + if (!client->irq) { + dev_err(&client->dev, "irq number should not be zero\n"); + return -EINVAL; + } + + mpr121 = kzalloc(sizeof(struct mpr121_touchkey), GFP_KERNEL); + input_dev = input_allocate_device(); + if (!mpr121 || !input_dev) { + dev_err(&client->dev, "Failed to allocate memory\n"); + error = -ENOMEM; + goto err_free_mem; + } + + mpr121->client = client; + mpr121->input_dev = input_dev; + mpr121->keycount = pdata->keymap_size; + + input_dev->name = "Freescale MPR121 Touchkey"; + input_dev->id.bustype = BUS_I2C; + input_dev->dev.parent = &client->dev; + input_dev->evbit[0] = BIT_MASK(EV_KEY) | BIT_MASK(EV_REP); + + input_dev->keycode = mpr121->keycodes; + input_dev->keycodesize = sizeof(mpr121->keycodes[0]); + input_dev->keycodemax = mpr121->keycount; + + for (i = 0; i < pdata->keymap_size; i++) { + input_set_capability(input_dev, EV_KEY, pdata->keymap[i]); + mpr121->keycodes[i] = pdata->keymap[i]; + } + + error = mpr121_phys_init(pdata, mpr121, client); + if (error) { + dev_err(&client->dev, "Failed to init register\n"); + goto err_free_mem; + } + + error = request_threaded_irq(client->irq, NULL, + mpr_touchkey_interrupt, + IRQF_TRIGGER_FALLING, + client->dev.driver->name, mpr121); + if (error) { + dev_err(&client->dev, "Failed to register interrupt\n"); + goto err_free_mem; + } + + error = input_register_device(input_dev); + if (error) + goto err_free_irq; + + i2c_set_clientdata(client, mpr121); + device_init_wakeup(&client->dev, pdata->wakeup); + + return 0; + +err_free_irq: + free_irq(client->irq, mpr121); +err_free_mem: + input_free_device(input_dev); + kfree(mpr121); + return error; +} + +static int __devexit mpr_touchkey_remove(struct i2c_client *client) +{ + struct mpr121_touchkey *mpr121 = i2c_get_clientdata(client); + + free_irq(client->irq, mpr121); + input_unregister_device(mpr121->input_dev); + kfree(mpr121); + + return 0; +} + +#ifdef CONFIG_PM_SLEEP +static int mpr_suspend(struct device *dev) +{ + struct i2c_client *client = to_i2c_client(dev); + + if (device_may_wakeup(&client->dev)) + enable_irq_wake(client->irq); + + i2c_smbus_write_byte_data(client, ELECTRODE_CONF_ADDR, 0x00); + + return 0; +} + +static int mpr_resume(struct device *dev) +{ + struct i2c_client *client = to_i2c_client(dev); + struct mpr121_touchkey *mpr121 = i2c_get_clientdata(client); + + if (device_may_wakeup(&client->dev)) + disable_irq_wake(client->irq); + + i2c_smbus_write_byte_data(client, ELECTRODE_CONF_ADDR, + mpr121->keycount); + + return 0; +} +#endif + +static SIMPLE_DEV_PM_OPS(mpr121_touchkey_pm_ops, mpr_suspend, mpr_resume); + +static const struct i2c_device_id mpr121_id[] = { + { "mpr121_touchkey", 0 }, + { } +}; +MODULE_DEVICE_TABLE(i2c, mpr121_id); + +static struct i2c_driver mpr_touchkey_driver = { + .driver = { + .name = "mpr121", + .owner = THIS_MODULE, + .pm = &mpr121_touchkey_pm_ops, + }, + .id_table = mpr121_id, + .probe = mpr_touchkey_probe, + .remove = __devexit_p(mpr_touchkey_remove), +}; + +static int __init mpr_touchkey_init(void) +{ + return i2c_add_driver(&mpr_touchkey_driver); +} +module_init(mpr_touchkey_init); + +static void __exit mpr_touchkey_exit(void) +{ + i2c_del_driver(&mpr_touchkey_driver); +} +module_exit(mpr_touchkey_exit); + +MODULE_LICENSE("GPL"); +MODULE_AUTHOR("Zhang Jiejing <jiejing.zhang@freescale.com>"); +MODULE_DESCRIPTION("Touch Key driver for Freescale MPR121 Chip"); diff --git a/drivers/input/keyboard/omap-keypad.c b/drivers/input/keyboard/omap-keypad.c index 0e2a19cb43d8..f23a743817db 100644 --- a/drivers/input/keyboard/omap-keypad.c +++ b/drivers/input/keyboard/omap-keypad.c @@ -413,7 +413,7 @@ static int __devinit omap_kp_probe(struct platform_device *pdev) return 0; err5: for (i = irq_idx - 1; i >=0; i--) - free_irq(row_gpios[i], NULL); + free_irq(row_gpios[i], omap_kp); err4: input_unregister_device(omap_kp->input); input_dev = NULL; @@ -444,11 +444,11 @@ static int __devexit omap_kp_remove(struct platform_device *pdev) gpio_free(col_gpios[i]); for (i = 0; i < omap_kp->rows; i++) { gpio_free(row_gpios[i]); - free_irq(gpio_to_irq(row_gpios[i]), NULL); + free_irq(gpio_to_irq(row_gpios[i]), omap_kp); } } else { omap_writew(1, OMAP1_MPUIO_BASE + OMAP_MPUIO_KBD_MASKIT); - free_irq(omap_kp->irq, NULL); + free_irq(omap_kp->irq, omap_kp); } del_timer_sync(&omap_kp->timer); diff --git a/drivers/input/keyboard/pmic8xxx-keypad.c b/drivers/input/keyboard/pmic8xxx-keypad.c new file mode 100644 index 000000000000..40b02ae96f86 --- /dev/null +++ b/drivers/input/keyboard/pmic8xxx-keypad.c @@ -0,0 +1,799 @@ +/* Copyright (c) 2009-2011, Code Aurora Forum. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 and + * only 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. + */ + +#include <linux/module.h> +#include <linux/platform_device.h> +#include <linux/kernel.h> +#include <linux/interrupt.h> +#include <linux/slab.h> +#include <linux/input.h> +#include <linux/bitops.h> +#include <linux/delay.h> +#include <linux/mutex.h> + +#include <linux/mfd/pm8xxx/core.h> +#include <linux/mfd/pm8xxx/gpio.h> +#include <linux/input/pmic8xxx-keypad.h> + +#define PM8XXX_MAX_ROWS 18 +#define PM8XXX_MAX_COLS 8 +#define PM8XXX_ROW_SHIFT 3 +#define PM8XXX_MATRIX_MAX_SIZE (PM8XXX_MAX_ROWS * PM8XXX_MAX_COLS) + +#define PM8XXX_MIN_ROWS 5 +#define PM8XXX_MIN_COLS 5 + +#define MAX_SCAN_DELAY 128 +#define MIN_SCAN_DELAY 1 + +/* in nanoseconds */ +#define MAX_ROW_HOLD_DELAY 122000 +#define MIN_ROW_HOLD_DELAY 30500 + +#define MAX_DEBOUNCE_TIME 20 +#define MIN_DEBOUNCE_TIME 5 + +#define KEYP_CTRL 0x148 + +#define KEYP_CTRL_EVNTS BIT(0) +#define KEYP_CTRL_EVNTS_MASK 0x3 + +#define KEYP_CTRL_SCAN_COLS_SHIFT 5 +#define KEYP_CTRL_SCAN_COLS_MIN 5 +#define KEYP_CTRL_SCAN_COLS_BITS 0x3 + +#define KEYP_CTRL_SCAN_ROWS_SHIFT 2 +#define KEYP_CTRL_SCAN_ROWS_MIN 5 +#define KEYP_CTRL_SCAN_ROWS_BITS 0x7 + +#define KEYP_CTRL_KEYP_EN BIT(7) + +#define KEYP_SCAN 0x149 + +#define KEYP_SCAN_READ_STATE BIT(0) +#define KEYP_SCAN_DBOUNCE_SHIFT 1 +#define KEYP_SCAN_PAUSE_SHIFT 3 +#define KEYP_SCAN_ROW_HOLD_SHIFT 6 + +#define KEYP_TEST 0x14A + +#define KEYP_TEST_CLEAR_RECENT_SCAN BIT(6) +#define KEYP_TEST_CLEAR_OLD_SCAN BIT(5) +#define KEYP_TEST_READ_RESET BIT(4) +#define KEYP_TEST_DTEST_EN BIT(3) +#define KEYP_TEST_ABORT_READ BIT(0) + +#define KEYP_TEST_DBG_SELECT_SHIFT 1 + +/* bits of these registers represent + * '0' for key press + * '1' for key release + */ +#define KEYP_RECENT_DATA 0x14B +#define KEYP_OLD_DATA 0x14C + +#define KEYP_CLOCK_FREQ 32768 + +/** + * struct pmic8xxx_kp - internal keypad data structure + * @pdata - keypad platform data pointer + * @input - input device pointer for keypad + * @key_sense_irq - key press/release irq number + * @key_stuck_irq - key stuck notification irq number + * @keycodes - array to hold the key codes + * @dev - parent device pointer + * @keystate - present key press/release state + * @stuckstate - present state when key stuck irq + * @ctrl_reg - control register value + */ +struct pmic8xxx_kp { + const struct pm8xxx_keypad_platform_data *pdata; + struct input_dev *input; + int key_sense_irq; + int key_stuck_irq; + + unsigned short keycodes[PM8XXX_MATRIX_MAX_SIZE]; + + struct device *dev; + u16 keystate[PM8XXX_MAX_ROWS]; + u16 stuckstate[PM8XXX_MAX_ROWS]; + + u8 ctrl_reg; +}; + +static int pmic8xxx_kp_write_u8(struct pmic8xxx_kp *kp, + u8 data, u16 reg) +{ + int rc; + + rc = pm8xxx_writeb(kp->dev->parent, reg, data); + return rc; +} + +static int pmic8xxx_kp_read(struct pmic8xxx_kp *kp, + u8 *data, u16 reg, unsigned num_bytes) +{ + int rc; + + rc = pm8xxx_read_buf(kp->dev->parent, reg, data, num_bytes); + return rc; +} + +static int pmic8xxx_kp_read_u8(struct pmic8xxx_kp *kp, + u8 *data, u16 reg) +{ + int rc; + + rc = pmic8xxx_kp_read(kp, data, reg, 1); + return rc; +} + +static u8 pmic8xxx_col_state(struct pmic8xxx_kp *kp, u8 col) +{ + /* all keys pressed on that particular row? */ + if (col == 0x00) + return 1 << kp->pdata->num_cols; + else + return col & ((1 << kp->pdata->num_cols) - 1); +} + +/* + * Synchronous read protocol for RevB0 onwards: + * + * 1. Write '1' to ReadState bit in KEYP_SCAN register + * 2. Wait 2*32KHz clocks, so that HW can successfully enter read mode + * synchronously + * 3. Read rows in old array first if events are more than one + * 4. Read rows in recent array + * 5. Wait 4*32KHz clocks + * 6. Write '0' to ReadState bit of KEYP_SCAN register so that hw can + * synchronously exit read mode. + */ +static int pmic8xxx_chk_sync_read(struct pmic8xxx_kp *kp) +{ + int rc; + u8 scan_val; + + rc = pmic8xxx_kp_read_u8(kp, &scan_val, KEYP_SCAN); + if (rc < 0) { + dev_err(kp->dev, "Error reading KEYP_SCAN reg, rc=%d\n", rc); + return rc; + } + + scan_val |= 0x1; + + rc = pmic8xxx_kp_write_u8(kp, scan_val, KEYP_SCAN); + if (rc < 0) { + dev_err(kp->dev, "Error writing KEYP_SCAN reg, rc=%d\n", rc); + return rc; + } + + /* 2 * 32KHz clocks */ + udelay((2 * DIV_ROUND_UP(USEC_PER_SEC, KEYP_CLOCK_FREQ)) + 1); + + return rc; +} + +static int pmic8xxx_kp_read_data(struct pmic8xxx_kp *kp, u16 *state, + u16 data_reg, int read_rows) +{ + int rc, row; + u8 new_data[PM8XXX_MAX_ROWS]; + + rc = pmic8xxx_kp_read(kp, new_data, data_reg, read_rows); + if (rc) + return rc; + + for (row = 0; row < kp->pdata->num_rows; row++) { + dev_dbg(kp->dev, "new_data[%d] = %d\n", row, + new_data[row]); + state[row] = pmic8xxx_col_state(kp, new_data[row]); + } + + return rc; +} + +static int pmic8xxx_kp_read_matrix(struct pmic8xxx_kp *kp, u16 *new_state, + u16 *old_state) +{ + int rc, read_rows; + u8 scan_val; + + if (kp->pdata->num_rows < PM8XXX_MIN_ROWS) + read_rows = PM8XXX_MIN_ROWS; + else + read_rows = kp->pdata->num_rows; + + pmic8xxx_chk_sync_read(kp); + + if (old_state) { + rc = pmic8xxx_kp_read_data(kp, old_state, KEYP_OLD_DATA, + read_rows); + if (rc < 0) { + dev_err(kp->dev, + "Error reading KEYP_OLD_DATA, rc=%d\n", rc); + return rc; + } + } + + rc = pmic8xxx_kp_read_data(kp, new_state, KEYP_RECENT_DATA, + read_rows); + if (rc < 0) { + dev_err(kp->dev, + "Error reading KEYP_RECENT_DATA, rc=%d\n", rc); + return rc; + } + + /* 4 * 32KHz clocks */ + udelay((4 * DIV_ROUND_UP(USEC_PER_SEC, KEYP_CLOCK_FREQ)) + 1); + + rc = pmic8xxx_kp_read_u8(kp, &scan_val, KEYP_SCAN); + if (rc < 0) { + dev_err(kp->dev, "Error reading KEYP_SCAN reg, rc=%d\n", rc); + return rc; + } + + scan_val &= 0xFE; + rc = pmic8xxx_kp_write_u8(kp, scan_val, KEYP_SCAN); + if (rc < 0) + dev_err(kp->dev, "Error writing KEYP_SCAN reg, rc=%d\n", rc); + + return rc; +} + +static void __pmic8xxx_kp_scan_matrix(struct pmic8xxx_kp *kp, u16 *new_state, + u16 *old_state) +{ + int row, col, code; + + for (row = 0; row < kp->pdata->num_rows; row++) { + int bits_changed = new_state[row] ^ old_state[row]; + + if (!bits_changed) + continue; + + for (col = 0; col < kp->pdata->num_cols; col++) { + if (!(bits_changed & (1 << col))) + continue; + + dev_dbg(kp->dev, "key [%d:%d] %s\n", row, col, + !(new_state[row] & (1 << col)) ? + "pressed" : "released"); + + code = MATRIX_SCAN_CODE(row, col, PM8XXX_ROW_SHIFT); + + input_event(kp->input, EV_MSC, MSC_SCAN, code); + input_report_key(kp->input, + kp->keycodes[code], + !(new_state[row] & (1 << col))); + + input_sync(kp->input); + } + } +} + +static bool pmic8xxx_detect_ghost_keys(struct pmic8xxx_kp *kp, u16 *new_state) +{ + int row, found_first = -1; + u16 check, row_state; + + check = 0; + for (row = 0; row < kp->pdata->num_rows; row++) { + row_state = (~new_state[row]) & + ((1 << kp->pdata->num_cols) - 1); + + if (hweight16(row_state) > 1) { + if (found_first == -1) + found_first = row; + if (check & row_state) { + dev_dbg(kp->dev, "detected ghost key on row[%d]" + " and row[%d]\n", found_first, row); + return true; + } + } + check |= row_state; + } + return false; +} + +static int pmic8xxx_kp_scan_matrix(struct pmic8xxx_kp *kp, unsigned int events) +{ + u16 new_state[PM8XXX_MAX_ROWS]; + u16 old_state[PM8XXX_MAX_ROWS]; + int rc; + + switch (events) { + case 0x1: + rc = pmic8xxx_kp_read_matrix(kp, new_state, NULL); + if (rc < 0) + return rc; + + /* detecting ghost key is not an error */ + if (pmic8xxx_detect_ghost_keys(kp, new_state)) + return 0; + __pmic8xxx_kp_scan_matrix(kp, new_state, kp->keystate); + memcpy(kp->keystate, new_state, sizeof(new_state)); + break; + case 0x3: /* two events - eventcounter is gray-coded */ + rc = pmic8xxx_kp_read_matrix(kp, new_state, old_state); + if (rc < 0) + return rc; + + __pmic8xxx_kp_scan_matrix(kp, old_state, kp->keystate); + __pmic8xxx_kp_scan_matrix(kp, new_state, old_state); + memcpy(kp->keystate, new_state, sizeof(new_state)); + break; + case 0x2: + dev_dbg(kp->dev, "Some key events were lost\n"); + rc = pmic8xxx_kp_read_matrix(kp, new_state, old_state); + if (rc < 0) + return rc; + __pmic8xxx_kp_scan_matrix(kp, old_state, kp->keystate); + __pmic8xxx_kp_scan_matrix(kp, new_state, old_state); + memcpy(kp->keystate, new_state, sizeof(new_state)); + break; + default: + rc = -EINVAL; + } + return rc; +} + +/* + * NOTE: We are reading recent and old data registers blindly + * whenever key-stuck interrupt happens, because events counter doesn't + * get updated when this interrupt happens due to key stuck doesn't get + * considered as key state change. + * + * We are not using old data register contents after they are being read + * because it might report the key which was pressed before the key being stuck + * as stuck key because it's pressed status is stored in the old data + * register. + */ +static irqreturn_t pmic8xxx_kp_stuck_irq(int irq, void *data) +{ + u16 new_state[PM8XXX_MAX_ROWS]; + u16 old_state[PM8XXX_MAX_ROWS]; + int rc; + struct pmic8xxx_kp *kp = data; + + rc = pmic8xxx_kp_read_matrix(kp, new_state, old_state); + if (rc < 0) { + dev_err(kp->dev, "failed to read keypad matrix\n"); + return IRQ_HANDLED; + } + + __pmic8xxx_kp_scan_matrix(kp, new_state, kp->stuckstate); + + return IRQ_HANDLED; +} + +static irqreturn_t pmic8xxx_kp_irq(int irq, void *data) +{ + struct pmic8xxx_kp *kp = data; + u8 ctrl_val, events; + int rc; + + rc = pmic8xxx_kp_read(kp, &ctrl_val, KEYP_CTRL, 1); + if (rc < 0) { + dev_err(kp->dev, "failed to read keyp_ctrl register\n"); + return IRQ_HANDLED; + } + + events = ctrl_val & KEYP_CTRL_EVNTS_MASK; + + rc = pmic8xxx_kp_scan_matrix(kp, events); + if (rc < 0) + dev_err(kp->dev, "failed to scan matrix\n"); + + return IRQ_HANDLED; +} + +static int __devinit pmic8xxx_kpd_init(struct pmic8xxx_kp *kp) +{ + int bits, rc, cycles; + u8 scan_val = 0, ctrl_val = 0; + static const u8 row_bits[] = { + 0, 1, 2, 3, 4, 4, 5, 5, 6, 6, 6, 7, 7, 7, + }; + + /* Find column bits */ + if (kp->pdata->num_cols < KEYP_CTRL_SCAN_COLS_MIN) + bits = 0; + else + bits = kp->pdata->num_cols - KEYP_CTRL_SCAN_COLS_MIN; + ctrl_val = (bits & KEYP_CTRL_SCAN_COLS_BITS) << + KEYP_CTRL_SCAN_COLS_SHIFT; + + /* Find row bits */ + if (kp->pdata->num_rows < KEYP_CTRL_SCAN_ROWS_MIN) + bits = 0; + else + bits = row_bits[kp->pdata->num_rows - KEYP_CTRL_SCAN_ROWS_MIN]; + + ctrl_val |= (bits << KEYP_CTRL_SCAN_ROWS_SHIFT); + + rc = pmic8xxx_kp_write_u8(kp, ctrl_val, KEYP_CTRL); + if (rc < 0) { + dev_err(kp->dev, "Error writing KEYP_CTRL reg, rc=%d\n", rc); + return rc; + } + + bits = (kp->pdata->debounce_ms / 5) - 1; + + scan_val |= (bits << KEYP_SCAN_DBOUNCE_SHIFT); + + bits = fls(kp->pdata->scan_delay_ms) - 1; + scan_val |= (bits << KEYP_SCAN_PAUSE_SHIFT); + + /* Row hold time is a multiple of 32KHz cycles. */ + cycles = (kp->pdata->row_hold_ns * KEYP_CLOCK_FREQ) / NSEC_PER_SEC; + + scan_val |= (cycles << KEYP_SCAN_ROW_HOLD_SHIFT); + + rc = pmic8xxx_kp_write_u8(kp, scan_val, KEYP_SCAN); + if (rc) + dev_err(kp->dev, "Error writing KEYP_SCAN reg, rc=%d\n", rc); + + return rc; + +} + +static int __devinit pmic8xxx_kp_config_gpio(int gpio_start, int num_gpios, + struct pmic8xxx_kp *kp, struct pm_gpio *gpio_config) +{ + int rc, i; + + if (gpio_start < 0 || num_gpios < 0) + return -EINVAL; + + for (i = 0; i < num_gpios; i++) { + rc = pm8xxx_gpio_config(gpio_start + i, gpio_config); + if (rc) { + dev_err(kp->dev, "%s: FAIL pm8xxx_gpio_config():" + "for PM GPIO [%d] rc=%d.\n", + __func__, gpio_start + i, rc); + return rc; + } + } + + return 0; +} + +static int pmic8xxx_kp_enable(struct pmic8xxx_kp *kp) +{ + int rc; + + kp->ctrl_reg |= KEYP_CTRL_KEYP_EN; + + rc = pmic8xxx_kp_write_u8(kp, kp->ctrl_reg, KEYP_CTRL); + if (rc < 0) + dev_err(kp->dev, "Error writing KEYP_CTRL reg, rc=%d\n", rc); + + return rc; +} + +static int pmic8xxx_kp_disable(struct pmic8xxx_kp *kp) +{ + int rc; + + kp->ctrl_reg &= ~KEYP_CTRL_KEYP_EN; + + rc = pmic8xxx_kp_write_u8(kp, kp->ctrl_reg, KEYP_CTRL); + if (rc < 0) + return rc; + + return rc; +} + +static int pmic8xxx_kp_open(struct input_dev *dev) +{ + struct pmic8xxx_kp *kp = input_get_drvdata(dev); + + return pmic8xxx_kp_enable(kp); +} + +static void pmic8xxx_kp_close(struct input_dev *dev) +{ + struct pmic8xxx_kp *kp = input_get_drvdata(dev); + + pmic8xxx_kp_disable(kp); +} + +/* + * keypad controller should be initialized in the following sequence + * only, otherwise it might get into FSM stuck state. + * + * - Initialize keypad control parameters, like no. of rows, columns, + * timing values etc., + * - configure rows and column gpios pull up/down. + * - set irq edge type. + * - enable the keypad controller. + */ +static int __devinit pmic8xxx_kp_probe(struct platform_device *pdev) +{ + const struct pm8xxx_keypad_platform_data *pdata = mfd_get_data(pdev); + const struct matrix_keymap_data *keymap_data; + struct pmic8xxx_kp *kp; + int rc; + u8 ctrl_val; + + struct pm_gpio kypd_drv = { + .direction = PM_GPIO_DIR_OUT, + .output_buffer = PM_GPIO_OUT_BUF_OPEN_DRAIN, + .output_value = 0, + .pull = PM_GPIO_PULL_NO, + .vin_sel = PM_GPIO_VIN_S3, + .out_strength = PM_GPIO_STRENGTH_LOW, + .function = PM_GPIO_FUNC_1, + .inv_int_pol = 1, + }; + + struct pm_gpio kypd_sns = { + .direction = PM_GPIO_DIR_IN, + .pull = PM_GPIO_PULL_UP_31P5, + .vin_sel = PM_GPIO_VIN_S3, + .out_strength = PM_GPIO_STRENGTH_NO, + .function = PM_GPIO_FUNC_NORMAL, + .inv_int_pol = 1, + }; + + + if (!pdata || !pdata->num_cols || !pdata->num_rows || + pdata->num_cols > PM8XXX_MAX_COLS || + pdata->num_rows > PM8XXX_MAX_ROWS || + pdata->num_cols < PM8XXX_MIN_COLS) { + dev_err(&pdev->dev, "invalid platform data\n"); + return -EINVAL; + } + + if (!pdata->scan_delay_ms || + pdata->scan_delay_ms > MAX_SCAN_DELAY || + pdata->scan_delay_ms < MIN_SCAN_DELAY || + !is_power_of_2(pdata->scan_delay_ms)) { + dev_err(&pdev->dev, "invalid keypad scan time supplied\n"); + return -EINVAL; + } + + if (!pdata->row_hold_ns || + pdata->row_hold_ns > MAX_ROW_HOLD_DELAY || + pdata->row_hold_ns < MIN_ROW_HOLD_DELAY || + ((pdata->row_hold_ns % MIN_ROW_HOLD_DELAY) != 0)) { + dev_err(&pdev->dev, "invalid keypad row hold time supplied\n"); + return -EINVAL; + } + + if (!pdata->debounce_ms || + ((pdata->debounce_ms % 5) != 0) || + pdata->debounce_ms > MAX_DEBOUNCE_TIME || + pdata->debounce_ms < MIN_DEBOUNCE_TIME) { + dev_err(&pdev->dev, "invalid debounce time supplied\n"); + return -EINVAL; + } + + keymap_data = pdata->keymap_data; + if (!keymap_data) { + dev_err(&pdev->dev, "no keymap data supplied\n"); + return -EINVAL; + } + + kp = kzalloc(sizeof(*kp), GFP_KERNEL); + if (!kp) + return -ENOMEM; + + platform_set_drvdata(pdev, kp); + + kp->pdata = pdata; + kp->dev = &pdev->dev; + + kp->input = input_allocate_device(); + if (!kp->input) { + dev_err(&pdev->dev, "unable to allocate input device\n"); + rc = -ENOMEM; + goto err_alloc_device; + } + + kp->key_sense_irq = platform_get_irq(pdev, 0); + if (kp->key_sense_irq < 0) { + dev_err(&pdev->dev, "unable to get keypad sense irq\n"); + rc = -ENXIO; + goto err_get_irq; + } + + kp->key_stuck_irq = platform_get_irq(pdev, 1); + if (kp->key_stuck_irq < 0) { + dev_err(&pdev->dev, "unable to get keypad stuck irq\n"); + rc = -ENXIO; + goto err_get_irq; + } + + kp->input->name = pdata->input_name ? : "PMIC8XXX keypad"; + kp->input->phys = pdata->input_phys_device ? : "pmic8xxx_keypad/input0"; + + kp->input->dev.parent = &pdev->dev; + + kp->input->id.bustype = BUS_I2C; + kp->input->id.version = 0x0001; + kp->input->id.product = 0x0001; + kp->input->id.vendor = 0x0001; + + kp->input->evbit[0] = BIT_MASK(EV_KEY); + + if (pdata->rep) + __set_bit(EV_REP, kp->input->evbit); + + kp->input->keycode = kp->keycodes; + kp->input->keycodemax = PM8XXX_MATRIX_MAX_SIZE; + kp->input->keycodesize = sizeof(kp->keycodes); + kp->input->open = pmic8xxx_kp_open; + kp->input->close = pmic8xxx_kp_close; + + matrix_keypad_build_keymap(keymap_data, PM8XXX_ROW_SHIFT, + kp->input->keycode, kp->input->keybit); + + input_set_capability(kp->input, EV_MSC, MSC_SCAN); + input_set_drvdata(kp->input, kp); + + /* initialize keypad state */ + memset(kp->keystate, 0xff, sizeof(kp->keystate)); + memset(kp->stuckstate, 0xff, sizeof(kp->stuckstate)); + + rc = pmic8xxx_kpd_init(kp); + if (rc < 0) { + dev_err(&pdev->dev, "unable to initialize keypad controller\n"); + goto err_get_irq; + } + + rc = pmic8xxx_kp_config_gpio(pdata->cols_gpio_start, + pdata->num_cols, kp, &kypd_sns); + if (rc < 0) { + dev_err(&pdev->dev, "unable to configure keypad sense lines\n"); + goto err_gpio_config; + } + + rc = pmic8xxx_kp_config_gpio(pdata->rows_gpio_start, + pdata->num_rows, kp, &kypd_drv); + if (rc < 0) { + dev_err(&pdev->dev, "unable to configure keypad drive lines\n"); + goto err_gpio_config; + } + + rc = request_any_context_irq(kp->key_sense_irq, pmic8xxx_kp_irq, + IRQF_TRIGGER_RISING, "pmic-keypad", kp); + if (rc < 0) { + dev_err(&pdev->dev, "failed to request keypad sense irq\n"); + goto err_get_irq; + } + + rc = request_any_context_irq(kp->key_stuck_irq, pmic8xxx_kp_stuck_irq, + IRQF_TRIGGER_RISING, "pmic-keypad-stuck", kp); + if (rc < 0) { + dev_err(&pdev->dev, "failed to request keypad stuck irq\n"); + goto err_req_stuck_irq; + } + + rc = pmic8xxx_kp_read_u8(kp, &ctrl_val, KEYP_CTRL); + if (rc < 0) { + dev_err(&pdev->dev, "failed to read KEYP_CTRL register\n"); + goto err_pmic_reg_read; + } + + kp->ctrl_reg = ctrl_val; + + rc = input_register_device(kp->input); + if (rc < 0) { + dev_err(&pdev->dev, "unable to register keypad input device\n"); + goto err_pmic_reg_read; + } + + device_init_wakeup(&pdev->dev, pdata->wakeup); + + return 0; + +err_pmic_reg_read: + free_irq(kp->key_stuck_irq, NULL); +err_req_stuck_irq: + free_irq(kp->key_sense_irq, NULL); +err_gpio_config: +err_get_irq: + input_free_device(kp->input); +err_alloc_device: + platform_set_drvdata(pdev, NULL); + kfree(kp); + return rc; +} + +static int __devexit pmic8xxx_kp_remove(struct platform_device *pdev) +{ + struct pmic8xxx_kp *kp = platform_get_drvdata(pdev); + + device_init_wakeup(&pdev->dev, 0); + free_irq(kp->key_stuck_irq, NULL); + free_irq(kp->key_sense_irq, NULL); + input_unregister_device(kp->input); + kfree(kp); + + platform_set_drvdata(pdev, NULL); + return 0; +} + +#ifdef CONFIG_PM_SLEEP +static int pmic8xxx_kp_suspend(struct device *dev) +{ + struct platform_device *pdev = to_platform_device(dev); + struct pmic8xxx_kp *kp = platform_get_drvdata(pdev); + struct input_dev *input_dev = kp->input; + + if (device_may_wakeup(dev)) { + enable_irq_wake(kp->key_sense_irq); + } else { + mutex_lock(&input_dev->mutex); + + if (input_dev->users) + pmic8xxx_kp_disable(kp); + + mutex_unlock(&input_dev->mutex); + } + + return 0; +} + +static int pmic8xxx_kp_resume(struct device *dev) +{ + struct platform_device *pdev = to_platform_device(dev); + struct pmic8xxx_kp *kp = platform_get_drvdata(pdev); + struct input_dev *input_dev = kp->input; + + if (device_may_wakeup(dev)) { + disable_irq_wake(kp->key_sense_irq); + } else { + mutex_lock(&input_dev->mutex); + + if (input_dev->users) + pmic8xxx_kp_enable(kp); + + mutex_unlock(&input_dev->mutex); + } + + return 0; +} +#endif + +static SIMPLE_DEV_PM_OPS(pm8xxx_kp_pm_ops, + pmic8xxx_kp_suspend, pmic8xxx_kp_resume); + +static struct platform_driver pmic8xxx_kp_driver = { + .probe = pmic8xxx_kp_probe, + .remove = __devexit_p(pmic8xxx_kp_remove), + .driver = { + .name = PM8XXX_KEYPAD_DEV_NAME, + .owner = THIS_MODULE, + .pm = &pm8xxx_kp_pm_ops, + }, +}; + +static int __init pmic8xxx_kp_init(void) +{ + return platform_driver_register(&pmic8xxx_kp_driver); +} +module_init(pmic8xxx_kp_init); + +static void __exit pmic8xxx_kp_exit(void) +{ + platform_driver_unregister(&pmic8xxx_kp_driver); +} +module_exit(pmic8xxx_kp_exit); + +MODULE_LICENSE("GPL v2"); +MODULE_DESCRIPTION("PMIC8XXX keypad driver"); +MODULE_VERSION("1.0"); +MODULE_ALIAS("platform:pmic8xxx_keypad"); +MODULE_AUTHOR("Trilok Soni <tsoni@codeaurora.org>"); diff --git a/drivers/input/keyboard/qt1070.c b/drivers/input/keyboard/qt1070.c index fba8404c7297..ca7b89196ab7 100644 --- a/drivers/input/keyboard/qt1070.c +++ b/drivers/input/keyboard/qt1070.c @@ -248,6 +248,7 @@ static const struct i2c_device_id qt1070_id[] = { { "qt1070", 0 }, { }, }; +MODULE_DEVICE_TABLE(i2c, qt1070_id); static struct i2c_driver qt1070_driver = { .driver = { diff --git a/drivers/input/keyboard/sh_keysc.c b/drivers/input/keyboard/sh_keysc.c index d7dafd9425b6..834cf98e7efb 100644 --- a/drivers/input/keyboard/sh_keysc.c +++ b/drivers/input/keyboard/sh_keysc.c @@ -20,7 +20,7 @@ #include <linux/input.h> #include <linux/input/sh_keysc.h> #include <linux/bitmap.h> -#include <linux/clk.h> +#include <linux/pm_runtime.h> #include <linux/io.h> #include <linux/slab.h> @@ -37,7 +37,6 @@ static const struct { struct sh_keysc_priv { void __iomem *iomem_base; - struct clk *clk; DECLARE_BITMAP(last_keys, SH_KEYSC_MAXKEYS); struct input_dev *input; struct sh_keysc_info pdata; @@ -169,7 +168,6 @@ static int __devinit sh_keysc_probe(struct platform_device *pdev) struct sh_keysc_info *pdata; struct resource *res; struct input_dev *input; - char clk_name[8]; int i; int irq, error; @@ -210,19 +208,11 @@ static int __devinit sh_keysc_probe(struct platform_device *pdev) goto err1; } - snprintf(clk_name, sizeof(clk_name), "keysc%d", pdev->id); - priv->clk = clk_get(&pdev->dev, clk_name); - if (IS_ERR(priv->clk)) { - dev_err(&pdev->dev, "cannot get clock \"%s\"\n", clk_name); - error = PTR_ERR(priv->clk); - goto err2; - } - priv->input = input_allocate_device(); if (!priv->input) { dev_err(&pdev->dev, "failed to allocate input device\n"); error = -ENOMEM; - goto err3; + goto err2; } input = priv->input; @@ -241,10 +231,11 @@ static int __devinit sh_keysc_probe(struct platform_device *pdev) input->keycodesize = sizeof(pdata->keycodes[0]); input->keycodemax = ARRAY_SIZE(pdata->keycodes); - error = request_irq(irq, sh_keysc_isr, 0, pdev->name, pdev); + error = request_threaded_irq(irq, NULL, sh_keysc_isr, IRQF_ONESHOT, + dev_name(&pdev->dev), pdev); if (error) { dev_err(&pdev->dev, "failed to request IRQ\n"); - goto err4; + goto err3; } for (i = 0; i < SH_KEYSC_MAXKEYS; i++) @@ -254,10 +245,11 @@ static int __devinit sh_keysc_probe(struct platform_device *pdev) error = input_register_device(input); if (error) { dev_err(&pdev->dev, "failed to register input device\n"); - goto err5; + goto err4; } - clk_enable(priv->clk); + pm_runtime_enable(&pdev->dev); + pm_runtime_get_sync(&pdev->dev); sh_keysc_write(priv, KYCR1, (sh_keysc_mode[pdata->mode].kymd << 8) | pdata->scan_timing); @@ -267,12 +259,10 @@ static int __devinit sh_keysc_probe(struct platform_device *pdev) return 0; - err5: - free_irq(irq, pdev); err4: - input_free_device(input); + free_irq(irq, pdev); err3: - clk_put(priv->clk); + input_free_device(input); err2: iounmap(priv->iomem_base); err1: @@ -292,8 +282,8 @@ static int __devexit sh_keysc_remove(struct platform_device *pdev) free_irq(platform_get_irq(pdev, 0), pdev); iounmap(priv->iomem_base); - clk_disable(priv->clk); - clk_put(priv->clk); + pm_runtime_put_sync(&pdev->dev); + pm_runtime_disable(&pdev->dev); platform_set_drvdata(pdev, NULL); kfree(priv); @@ -301,6 +291,7 @@ static int __devexit sh_keysc_remove(struct platform_device *pdev) return 0; } +#if CONFIG_PM_SLEEP static int sh_keysc_suspend(struct device *dev) { struct platform_device *pdev = to_platform_device(dev); @@ -311,14 +302,13 @@ static int sh_keysc_suspend(struct device *dev) value = sh_keysc_read(priv, KYCR1); if (device_may_wakeup(dev)) { - value |= 0x80; + sh_keysc_write(priv, KYCR1, value | 0x80); enable_irq_wake(irq); } else { - value &= ~0x80; + sh_keysc_write(priv, KYCR1, value & ~0x80); + pm_runtime_put_sync(dev); } - sh_keysc_write(priv, KYCR1, value); - return 0; } @@ -329,16 +319,17 @@ static int sh_keysc_resume(struct device *dev) if (device_may_wakeup(dev)) disable_irq_wake(irq); + else + pm_runtime_get_sync(dev); return 0; } +#endif -static const struct dev_pm_ops sh_keysc_dev_pm_ops = { - .suspend = sh_keysc_suspend, - .resume = sh_keysc_resume, -}; +static SIMPLE_DEV_PM_OPS(sh_keysc_dev_pm_ops, + sh_keysc_suspend, sh_keysc_resume); -struct platform_driver sh_keysc_device_driver = { +static struct platform_driver sh_keysc_device_driver = { .probe = sh_keysc_probe, .remove = __devexit_p(sh_keysc_remove), .driver = { diff --git a/drivers/input/keyboard/spear-keyboard.c b/drivers/input/keyboard/spear-keyboard.c index bee03d64c453..d712dffd2157 100644 --- a/drivers/input/keyboard/spear-keyboard.c +++ b/drivers/input/keyboard/spear-keyboard.c @@ -69,7 +69,7 @@ static irqreturn_t spear_kbd_interrupt(int irq, void *dev_id) u8 sts, val; sts = readb(kbd->io_base + STATUS_REG); - if (sts & DATA_AVAIL) + if (!(sts & DATA_AVAIL)) return IRQ_NONE; if (kbd->last_key != KEY_RESERVED) { diff --git a/drivers/input/keyboard/tegra-kbc.c b/drivers/input/keyboard/tegra-kbc.c index 99ce9032d08c..2b3b73ec6689 100644 --- a/drivers/input/keyboard/tegra-kbc.c +++ b/drivers/input/keyboard/tegra-kbc.c @@ -66,12 +66,11 @@ struct tegra_kbc { void __iomem *mmio; struct input_dev *idev; unsigned int irq; - unsigned int wake_enable_rows; - unsigned int wake_enable_cols; spinlock_t lock; unsigned int repoll_dly; unsigned long cp_dly_jiffies; bool use_fn_map; + bool use_ghost_filter; const struct tegra_kbc_platform_data *pdata; unsigned short keycode[KBC_MAX_KEY * 2]; unsigned short current_keys[KBC_MAX_KPENT]; @@ -260,6 +259,8 @@ static void tegra_kbc_report_keys(struct tegra_kbc *kbc) unsigned int num_down = 0; unsigned long flags; bool fn_keypress = false; + bool key_in_same_row = false; + bool key_in_same_col = false; spin_lock_irqsave(&kbc->lock, flags); for (i = 0; i < KBC_MAX_KPENT; i++) { @@ -285,6 +286,34 @@ static void tegra_kbc_report_keys(struct tegra_kbc *kbc) } /* + * Matrix keyboard designs are prone to keyboard ghosting. + * Ghosting occurs if there are 3 keys such that - + * any 2 of the 3 keys share a row, and any 2 of them share a column. + * If so ignore the key presses for this iteration. + */ + if ((kbc->use_ghost_filter) && (num_down >= 3)) { + for (i = 0; i < num_down; i++) { + unsigned int j; + u8 curr_col = scancodes[i] & 0x07; + u8 curr_row = scancodes[i] >> KBC_ROW_SHIFT; + + /* + * Find 2 keys such that one key is in the same row + * and the other is in the same column as the i-th key. + */ + for (j = i + 1; j < num_down; j++) { + u8 col = scancodes[j] & 0x07; + u8 row = scancodes[j] >> KBC_ROW_SHIFT; + + if (col == curr_col) + key_in_same_col = true; + if (row == curr_row) + key_in_same_row = true; + } + } + } + + /* * If the platform uses Fn keymaps, translate keys on a Fn keypress. * Function keycodes are KBC_MAX_KEY apart from the plain keycodes. */ @@ -297,6 +326,10 @@ static void tegra_kbc_report_keys(struct tegra_kbc *kbc) spin_unlock_irqrestore(&kbc->lock, flags); + /* Ignore the key presses for this iteration? */ + if (key_in_same_col && key_in_same_row) + return; + tegra_kbc_report_released_keys(kbc->idev, kbc->current_keys, kbc->num_pressed_keys, keycodes, num_down); @@ -383,21 +416,11 @@ static void tegra_kbc_setup_wakekeys(struct tegra_kbc *kbc, bool filter) int i; unsigned int rst_val; - BUG_ON(pdata->wake_cnt > KBC_MAX_KEY); - rst_val = (filter && pdata->wake_cnt) ? ~0 : 0; + /* Either mask all keys or none. */ + rst_val = (filter && !pdata->wakeup) ? ~0 : 0; for (i = 0; i < KBC_MAX_ROW; i++) writel(rst_val, kbc->mmio + KBC_ROW0_MASK_0 + i * 4); - - if (filter) { - for (i = 0; i < pdata->wake_cnt; i++) { - u32 val, addr; - addr = pdata->wake_cfg[i].row * 4 + KBC_ROW0_MASK_0; - val = readl(kbc->mmio + addr); - val &= ~(1 << pdata->wake_cfg[i].col); - writel(val, kbc->mmio + addr); - } - } } static void tegra_kbc_config_pins(struct tegra_kbc *kbc) @@ -559,7 +582,6 @@ static int __devinit tegra_kbc_probe(struct platform_device *pdev) struct resource *res; int irq; int err; - int i; int num_rows = 0; unsigned int debounce_cnt; unsigned int scan_time_rows; @@ -616,13 +638,6 @@ static int __devinit tegra_kbc_probe(struct platform_device *pdev) goto err_iounmap; } - kbc->wake_enable_rows = 0; - kbc->wake_enable_cols = 0; - for (i = 0; i < pdata->wake_cnt; i++) { - kbc->wake_enable_rows |= (1 << pdata->wake_cfg[i].row); - kbc->wake_enable_cols |= (1 << pdata->wake_cfg[i].col); - } - /* * The time delay between two consecutive reads of the FIFO is * the sum of the repeat time and the time taken for scanning @@ -652,6 +667,7 @@ static int __devinit tegra_kbc_probe(struct platform_device *pdev) input_dev->keycodemax *= 2; kbc->use_fn_map = pdata->use_fn_map; + kbc->use_ghost_filter = pdata->use_ghost_filter; keymap_data = pdata->keymap_data ?: &tegra_kbc_default_keymap_data; matrix_keypad_build_keymap(keymap_data, KBC_ROW_SHIFT, input_dev->keycode, input_dev->keybit); diff --git a/drivers/input/keyboard/twl4030_keypad.c b/drivers/input/keyboard/twl4030_keypad.c index 09bef79d9da1..a26922cf0e84 100644 --- a/drivers/input/keyboard/twl4030_keypad.c +++ b/drivers/input/keyboard/twl4030_keypad.c @@ -332,18 +332,20 @@ static int __devinit twl4030_kp_program(struct twl4030_keypad *kp) static int __devinit twl4030_kp_probe(struct platform_device *pdev) { struct twl4030_keypad_data *pdata = pdev->dev.platform_data; - const struct matrix_keymap_data *keymap_data = pdata->keymap_data; + const struct matrix_keymap_data *keymap_data; struct twl4030_keypad *kp; struct input_dev *input; u8 reg; int error; - if (!pdata || !pdata->rows || !pdata->cols || + if (!pdata || !pdata->rows || !pdata->cols || !pdata->keymap_data || pdata->rows > TWL4030_MAX_ROWS || pdata->cols > TWL4030_MAX_COLS) { dev_err(&pdev->dev, "Invalid platform_data\n"); return -EINVAL; } + keymap_data = pdata->keymap_data; + kp = kzalloc(sizeof(*kp), GFP_KERNEL); input = input_allocate_device(); if (!kp || !input) { diff --git a/drivers/input/misc/Kconfig b/drivers/input/misc/Kconfig index f9cf0881b0e3..45dc6aa62ba4 100644 --- a/drivers/input/misc/Kconfig +++ b/drivers/input/misc/Kconfig @@ -330,6 +330,17 @@ config INPUT_PWM_BEEPER To compile this driver as a module, choose M here: the module will be called pwm-beeper. +config INPUT_PMIC8XXX_PWRKEY + tristate "PMIC8XXX power key support" + depends on MFD_PM8XXX + help + Say Y here if you want support for the PMIC8XXX power key. + + If unsure, say N. + + To compile this driver as a module, choose M here: the + module will be called pmic8xxx-pwrkey. + config INPUT_GPIO_ROTARY_ENCODER tristate "Rotary encoders connected to GPIO pins" depends on GPIOLIB && GENERIC_GPIO diff --git a/drivers/input/misc/Makefile b/drivers/input/misc/Makefile index e3f7984e6274..38efb2cb182b 100644 --- a/drivers/input/misc/Makefile +++ b/drivers/input/misc/Makefile @@ -33,6 +33,7 @@ obj-$(CONFIG_INPUT_PCF8574) += pcf8574_keypad.o obj-$(CONFIG_INPUT_PCSPKR) += pcspkr.o obj-$(CONFIG_INPUT_POWERMATE) += powermate.o obj-$(CONFIG_INPUT_PWM_BEEPER) += pwm-beeper.o +obj-$(CONFIG_INPUT_PMIC8XXX_PWRKEY) += pmic8xxx-pwrkey.o obj-$(CONFIG_INPUT_RB532_BUTTON) += rb532_button.o obj-$(CONFIG_INPUT_GPIO_ROTARY_ENCODER) += rotary_encoder.o obj-$(CONFIG_INPUT_SGI_BTNS) += sgi_btns.o diff --git a/drivers/input/misc/ad714x.c b/drivers/input/misc/ad714x.c index c431d09e401a..c3a62c42cd28 100644 --- a/drivers/input/misc/ad714x.c +++ b/drivers/input/misc/ad714x.c @@ -79,13 +79,7 @@ struct ad714x_slider_drv { struct ad714x_wheel_drv { int abs_pos; int flt_pos; - int pre_mean_value; int pre_highest_stage; - int pre_mean_value_no_offset; - int mean_value; - int mean_value_no_offset; - int pos_offset; - int pos_ratio; int highest_stage; enum ad714x_device_state state; struct input_dev *input; @@ -158,10 +152,10 @@ static void ad714x_use_com_int(struct ad714x_chip *ad714x, unsigned short data; unsigned short mask; - mask = ((1 << (end_stage + 1)) - 1) - (1 << start_stage); + mask = ((1 << (end_stage + 1)) - 1) - ((1 << start_stage) - 1); ad714x->read(ad714x->dev, STG_COM_INT_EN_REG, &data); - data |= 1 << start_stage; + data |= 1 << end_stage; ad714x->write(ad714x->dev, STG_COM_INT_EN_REG, data); ad714x->read(ad714x->dev, STG_HIGH_INT_EN_REG, &data); @@ -175,10 +169,10 @@ static void ad714x_use_thr_int(struct ad714x_chip *ad714x, unsigned short data; unsigned short mask; - mask = ((1 << (end_stage + 1)) - 1) - (1 << start_stage); + mask = ((1 << (end_stage + 1)) - 1) - ((1 << start_stage) - 1); ad714x->read(ad714x->dev, STG_COM_INT_EN_REG, &data); - data &= ~(1 << start_stage); + data &= ~(1 << end_stage); ad714x->write(ad714x->dev, STG_COM_INT_EN_REG, data); ad714x->read(ad714x->dev, STG_HIGH_INT_EN_REG, &data); @@ -404,7 +398,6 @@ static void ad714x_slider_state_machine(struct ad714x_chip *ad714x, int idx) ad714x_slider_cal_highest_stage(ad714x, idx); ad714x_slider_cal_abs_pos(ad714x, idx); ad714x_slider_cal_flt_pos(ad714x, idx); - input_report_abs(sw->input, ABS_X, sw->flt_pos); input_report_key(sw->input, BTN_TOUCH, 1); } else { @@ -468,104 +461,41 @@ static void ad714x_wheel_cal_sensor_val(struct ad714x_chip *ad714x, int idx) /* * When the scroll wheel is activated, we compute the absolute position based * on the sensor values. To calculate the position, we first determine the - * sensor that has the greatest response among the 8 sensors that constitutes - * the scrollwheel. Then we determined the 2 sensors on either sides of the + * sensor that has the greatest response among the sensors that constitutes + * the scrollwheel. Then we determined the sensors on either sides of the * sensor with the highest response and we apply weights to these sensors. The - * result of this computation gives us the mean value which defined by the - * following formula: - * For i= second_before_highest_stage to i= second_after_highest_stage - * v += Sensor response(i)*WEIGHT*(i+3) - * w += Sensor response(i) - * Mean_Value=v/w - * pos_on_scrollwheel = (Mean_Value - position_offset) / position_ratio + * result of this computation gives us the mean value. */ -#define WEIGHT_FACTOR 30 -/* This constant prevents the "PositionOffset" from reaching a big value */ -#define OFFSET_POSITION_CLAMP 120 static void ad714x_wheel_cal_abs_pos(struct ad714x_chip *ad714x, int idx) { struct ad714x_wheel_plat *hw = &ad714x->hw->wheel[idx]; struct ad714x_wheel_drv *sw = &ad714x->sw->wheel[idx]; int stage_num = hw->end_stage - hw->start_stage + 1; - int second_before, first_before, highest, first_after, second_after; + int first_before, highest, first_after; int a_param, b_param; - /* Calculate Mean value */ - - second_before = (sw->highest_stage + stage_num - 2) % stage_num; first_before = (sw->highest_stage + stage_num - 1) % stage_num; highest = sw->highest_stage; first_after = (sw->highest_stage + stage_num + 1) % stage_num; - second_after = (sw->highest_stage + stage_num + 2) % stage_num; - - if (((sw->highest_stage - hw->start_stage) > 1) && - ((hw->end_stage - sw->highest_stage) > 1)) { - a_param = ad714x->sensor_val[second_before] * - (second_before - hw->start_stage + 3) + - ad714x->sensor_val[first_before] * - (second_before - hw->start_stage + 3) + - ad714x->sensor_val[highest] * - (second_before - hw->start_stage + 3) + - ad714x->sensor_val[first_after] * - (first_after - hw->start_stage + 3) + - ad714x->sensor_val[second_after] * - (second_after - hw->start_stage + 3); - } else { - a_param = ad714x->sensor_val[second_before] * - (second_before - hw->start_stage + 1) + - ad714x->sensor_val[first_before] * - (second_before - hw->start_stage + 2) + - ad714x->sensor_val[highest] * - (second_before - hw->start_stage + 3) + - ad714x->sensor_val[first_after] * - (first_after - hw->start_stage + 4) + - ad714x->sensor_val[second_after] * - (second_after - hw->start_stage + 5); - } - a_param *= WEIGHT_FACTOR; - b_param = ad714x->sensor_val[second_before] + + a_param = ad714x->sensor_val[highest] * + (highest - hw->start_stage) + + ad714x->sensor_val[first_before] * + (highest - hw->start_stage - 1) + + ad714x->sensor_val[first_after] * + (highest - hw->start_stage + 1); + b_param = ad714x->sensor_val[highest] + ad714x->sensor_val[first_before] + - ad714x->sensor_val[highest] + - ad714x->sensor_val[first_after] + - ad714x->sensor_val[second_after]; + ad714x->sensor_val[first_after]; - sw->pre_mean_value = sw->mean_value; - sw->mean_value = a_param / b_param; + sw->abs_pos = ((hw->max_coord / (hw->end_stage - hw->start_stage)) * + a_param) / b_param; - /* Calculate the offset */ - - if ((sw->pre_highest_stage == hw->end_stage) && - (sw->highest_stage == hw->start_stage)) - sw->pos_offset = sw->mean_value; - else if ((sw->pre_highest_stage == hw->start_stage) && - (sw->highest_stage == hw->end_stage)) - sw->pos_offset = sw->pre_mean_value; - - if (sw->pos_offset > OFFSET_POSITION_CLAMP) - sw->pos_offset = OFFSET_POSITION_CLAMP; - - /* Calculate the mean value without the offset */ - - sw->pre_mean_value_no_offset = sw->mean_value_no_offset; - sw->mean_value_no_offset = sw->mean_value - sw->pos_offset; - if (sw->mean_value_no_offset < 0) - sw->mean_value_no_offset = 0; - - /* Calculate ratio to scale down to NUMBER_OF_WANTED_POSITIONS */ - - if ((sw->pre_highest_stage == hw->end_stage) && - (sw->highest_stage == hw->start_stage)) - sw->pos_ratio = (sw->pre_mean_value_no_offset * 100) / - hw->max_coord; - else if ((sw->pre_highest_stage == hw->start_stage) && - (sw->highest_stage == hw->end_stage)) - sw->pos_ratio = (sw->mean_value_no_offset * 100) / - hw->max_coord; - sw->abs_pos = (sw->mean_value_no_offset * 100) / sw->pos_ratio; if (sw->abs_pos > hw->max_coord) sw->abs_pos = hw->max_coord; + else if (sw->abs_pos < 0) + sw->abs_pos = 0; } static void ad714x_wheel_cal_flt_pos(struct ad714x_chip *ad714x, int idx) @@ -639,9 +569,8 @@ static void ad714x_wheel_state_machine(struct ad714x_chip *ad714x, int idx) ad714x_wheel_cal_highest_stage(ad714x, idx); ad714x_wheel_cal_abs_pos(ad714x, idx); ad714x_wheel_cal_flt_pos(ad714x, idx); - input_report_abs(sw->input, ABS_WHEEL, - sw->abs_pos); + sw->flt_pos); input_report_key(sw->input, BTN_TOUCH, 1); } else { /* When the user lifts off the sensor, configure @@ -1149,6 +1078,8 @@ struct ad714x_chip *ad714x_probe(struct device *dev, u16 bus_type, int irq, input[alloc_idx]->id.bustype = bus_type; input[alloc_idx]->id.product = ad714x->product; input[alloc_idx]->id.version = ad714x->version; + input[alloc_idx]->name = "ad714x_captouch_slider"; + input[alloc_idx]->dev.parent = dev; error = input_register_device(input[alloc_idx]); if (error) @@ -1179,6 +1110,8 @@ struct ad714x_chip *ad714x_probe(struct device *dev, u16 bus_type, int irq, input[alloc_idx]->id.bustype = bus_type; input[alloc_idx]->id.product = ad714x->product; input[alloc_idx]->id.version = ad714x->version; + input[alloc_idx]->name = "ad714x_captouch_wheel"; + input[alloc_idx]->dev.parent = dev; error = input_register_device(input[alloc_idx]); if (error) @@ -1212,6 +1145,8 @@ struct ad714x_chip *ad714x_probe(struct device *dev, u16 bus_type, int irq, input[alloc_idx]->id.bustype = bus_type; input[alloc_idx]->id.product = ad714x->product; input[alloc_idx]->id.version = ad714x->version; + input[alloc_idx]->name = "ad714x_captouch_pad"; + input[alloc_idx]->dev.parent = dev; error = input_register_device(input[alloc_idx]); if (error) @@ -1240,6 +1175,8 @@ struct ad714x_chip *ad714x_probe(struct device *dev, u16 bus_type, int irq, input[alloc_idx]->id.bustype = bus_type; input[alloc_idx]->id.product = ad714x->product; input[alloc_idx]->id.version = ad714x->version; + input[alloc_idx]->name = "ad714x_captouch_button"; + input[alloc_idx]->dev.parent = dev; error = input_register_device(input[alloc_idx]); if (error) @@ -1249,7 +1186,9 @@ struct ad714x_chip *ad714x_probe(struct device *dev, u16 bus_type, int irq, } error = request_threaded_irq(ad714x->irq, NULL, ad714x_interrupt_thread, - IRQF_TRIGGER_FALLING, "ad714x_captouch", ad714x); + plat_data->irqflags ? + plat_data->irqflags : IRQF_TRIGGER_FALLING, + "ad714x_captouch", ad714x); if (error) { dev_err(dev, "can't allocate irq %d\n", ad714x->irq); goto err_unreg_dev; diff --git a/drivers/input/misc/adxl34x.c b/drivers/input/misc/adxl34x.c index de5900d50788..144ddbdeb9b3 100644 --- a/drivers/input/misc/adxl34x.c +++ b/drivers/input/misc/adxl34x.c @@ -716,7 +716,7 @@ struct adxl34x *adxl34x_probe(struct device *dev, int irq, pdata = dev->platform_data; if (!pdata) { dev_dbg(dev, - "No platfrom data: Using default initialization\n"); + "No platform data: Using default initialization\n"); pdata = &adxl34x_default_init; } diff --git a/drivers/input/misc/ati_remote2.c b/drivers/input/misc/ati_remote2.c index 9ccdb82d869a..1de58e8a1b71 100644 --- a/drivers/input/misc/ati_remote2.c +++ b/drivers/input/misc/ati_remote2.c @@ -737,14 +737,17 @@ static ssize_t ati_remote2_store_channel_mask(struct device *dev, mutex_lock(&ati_remote2_mutex); - if (mask != ar2->channel_mask && !ati_remote2_setup(ar2, mask)) - ar2->channel_mask = mask; + if (mask != ar2->channel_mask) { + r = ati_remote2_setup(ar2, mask); + if (!r) + ar2->channel_mask = mask; + } mutex_unlock(&ati_remote2_mutex); usb_autopm_put_interface(ar2->intf[0]); - return count; + return r ? r : count; } static ssize_t ati_remote2_show_mode_mask(struct device *dev, diff --git a/drivers/input/misc/keyspan_remote.c b/drivers/input/misc/keyspan_remote.c index a93c525475c6..fc62256c963f 100644 --- a/drivers/input/misc/keyspan_remote.c +++ b/drivers/input/misc/keyspan_remote.c @@ -312,7 +312,7 @@ static void keyspan_check_data(struct usb_keyspan *remote) remote->data.tester = remote->data.tester >> 5; remote->data.bits_left -= 5; } else { - err("Bad message recieved, no stop bit found.\n"); + err("Bad message received, no stop bit found.\n"); } dev_dbg(&remote->udev->dev, diff --git a/drivers/input/misc/pmic8xxx-pwrkey.c b/drivers/input/misc/pmic8xxx-pwrkey.c new file mode 100644 index 000000000000..97e07e786e41 --- /dev/null +++ b/drivers/input/misc/pmic8xxx-pwrkey.c @@ -0,0 +1,231 @@ +/* Copyright (c) 2010-2011, Code Aurora Forum. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 and + * only 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. + */ + +#include <linux/module.h> +#include <linux/init.h> +#include <linux/kernel.h> +#include <linux/errno.h> +#include <linux/slab.h> +#include <linux/input.h> +#include <linux/interrupt.h> +#include <linux/platform_device.h> +#include <linux/log2.h> + +#include <linux/mfd/pm8xxx/core.h> +#include <linux/input/pmic8xxx-pwrkey.h> + +#define PON_CNTL_1 0x1C +#define PON_CNTL_PULL_UP BIT(7) +#define PON_CNTL_TRIG_DELAY_MASK (0x7) + +/** + * struct pmic8xxx_pwrkey - pmic8xxx pwrkey information + * @key_press_irq: key press irq number + */ +struct pmic8xxx_pwrkey { + struct input_dev *pwr; + int key_press_irq; +}; + +static irqreturn_t pwrkey_press_irq(int irq, void *_pwrkey) +{ + struct pmic8xxx_pwrkey *pwrkey = _pwrkey; + + input_report_key(pwrkey->pwr, KEY_POWER, 1); + input_sync(pwrkey->pwr); + + return IRQ_HANDLED; +} + +static irqreturn_t pwrkey_release_irq(int irq, void *_pwrkey) +{ + struct pmic8xxx_pwrkey *pwrkey = _pwrkey; + + input_report_key(pwrkey->pwr, KEY_POWER, 0); + input_sync(pwrkey->pwr); + + return IRQ_HANDLED; +} + +#ifdef CONFIG_PM_SLEEP +static int pmic8xxx_pwrkey_suspend(struct device *dev) +{ + struct pmic8xxx_pwrkey *pwrkey = dev_get_drvdata(dev); + + if (device_may_wakeup(dev)) + enable_irq_wake(pwrkey->key_press_irq); + + return 0; +} + +static int pmic8xxx_pwrkey_resume(struct device *dev) +{ + struct pmic8xxx_pwrkey *pwrkey = dev_get_drvdata(dev); + + if (device_may_wakeup(dev)) + disable_irq_wake(pwrkey->key_press_irq); + + return 0; +} +#endif + +static SIMPLE_DEV_PM_OPS(pm8xxx_pwr_key_pm_ops, + pmic8xxx_pwrkey_suspend, pmic8xxx_pwrkey_resume); + +static int __devinit pmic8xxx_pwrkey_probe(struct platform_device *pdev) +{ + struct input_dev *pwr; + int key_release_irq = platform_get_irq(pdev, 0); + int key_press_irq = platform_get_irq(pdev, 1); + int err; + unsigned int delay; + u8 pon_cntl; + struct pmic8xxx_pwrkey *pwrkey; + const struct pm8xxx_pwrkey_platform_data *pdata = mfd_get_data(pdev); + + if (!pdata) { + dev_err(&pdev->dev, "power key platform data not supplied\n"); + return -EINVAL; + } + + if (pdata->kpd_trigger_delay_us > 62500) { + dev_err(&pdev->dev, "invalid power key trigger delay\n"); + return -EINVAL; + } + + pwrkey = kzalloc(sizeof(*pwrkey), GFP_KERNEL); + if (!pwrkey) + return -ENOMEM; + + pwr = input_allocate_device(); + if (!pwr) { + dev_dbg(&pdev->dev, "Can't allocate power button\n"); + err = -ENOMEM; + goto free_pwrkey; + } + + input_set_capability(pwr, EV_KEY, KEY_POWER); + + pwr->name = "pmic8xxx_pwrkey"; + pwr->phys = "pmic8xxx_pwrkey/input0"; + pwr->dev.parent = &pdev->dev; + + delay = (pdata->kpd_trigger_delay_us << 10) / USEC_PER_SEC; + delay = 1 + ilog2(delay); + + err = pm8xxx_readb(pdev->dev.parent, PON_CNTL_1, &pon_cntl); + if (err < 0) { + dev_err(&pdev->dev, "failed reading PON_CNTL_1 err=%d\n", err); + goto free_input_dev; + } + + pon_cntl &= ~PON_CNTL_TRIG_DELAY_MASK; + pon_cntl |= (delay & PON_CNTL_TRIG_DELAY_MASK); + if (pdata->pull_up) + pon_cntl |= PON_CNTL_PULL_UP; + else + pon_cntl &= ~PON_CNTL_PULL_UP; + + err = pm8xxx_writeb(pdev->dev.parent, PON_CNTL_1, pon_cntl); + if (err < 0) { + dev_err(&pdev->dev, "failed writing PON_CNTL_1 err=%d\n", err); + goto free_input_dev; + } + + err = input_register_device(pwr); + if (err) { + dev_dbg(&pdev->dev, "Can't register power key: %d\n", err); + goto free_input_dev; + } + + pwrkey->key_press_irq = key_press_irq; + pwrkey->pwr = pwr; + + platform_set_drvdata(pdev, pwrkey); + + err = request_irq(key_press_irq, pwrkey_press_irq, + IRQF_TRIGGER_RISING, "pmic8xxx_pwrkey_press", pwrkey); + if (err < 0) { + dev_dbg(&pdev->dev, "Can't get %d IRQ for pwrkey: %d\n", + key_press_irq, err); + goto unreg_input_dev; + } + + err = request_irq(key_release_irq, pwrkey_release_irq, + IRQF_TRIGGER_RISING, "pmic8xxx_pwrkey_release", pwrkey); + if (err < 0) { + dev_dbg(&pdev->dev, "Can't get %d IRQ for pwrkey: %d\n", + key_release_irq, err); + + goto free_press_irq; + } + + device_init_wakeup(&pdev->dev, pdata->wakeup); + + return 0; + +free_press_irq: + free_irq(key_press_irq, NULL); +unreg_input_dev: + platform_set_drvdata(pdev, NULL); + input_unregister_device(pwr); + pwr = NULL; +free_input_dev: + input_free_device(pwr); +free_pwrkey: + kfree(pwrkey); + return err; +} + +static int __devexit pmic8xxx_pwrkey_remove(struct platform_device *pdev) +{ + struct pmic8xxx_pwrkey *pwrkey = platform_get_drvdata(pdev); + int key_release_irq = platform_get_irq(pdev, 0); + int key_press_irq = platform_get_irq(pdev, 1); + + device_init_wakeup(&pdev->dev, 0); + + free_irq(key_press_irq, pwrkey); + free_irq(key_release_irq, pwrkey); + input_unregister_device(pwrkey->pwr); + platform_set_drvdata(pdev, NULL); + kfree(pwrkey); + + return 0; +} + +static struct platform_driver pmic8xxx_pwrkey_driver = { + .probe = pmic8xxx_pwrkey_probe, + .remove = __devexit_p(pmic8xxx_pwrkey_remove), + .driver = { + .name = PM8XXX_PWRKEY_DEV_NAME, + .owner = THIS_MODULE, + .pm = &pm8xxx_pwr_key_pm_ops, + }, +}; + +static int __init pmic8xxx_pwrkey_init(void) +{ + return platform_driver_register(&pmic8xxx_pwrkey_driver); +} +module_init(pmic8xxx_pwrkey_init); + +static void __exit pmic8xxx_pwrkey_exit(void) +{ + platform_driver_unregister(&pmic8xxx_pwrkey_driver); +} +module_exit(pmic8xxx_pwrkey_exit); + +MODULE_ALIAS("platform:pmic8xxx_pwrkey"); +MODULE_DESCRIPTION("PMIC8XXX Power Key driver"); +MODULE_LICENSE("GPL v2"); +MODULE_AUTHOR("Trilok Soni <tsoni@codeaurora.org>"); diff --git a/drivers/input/misc/rotary_encoder.c b/drivers/input/misc/rotary_encoder.c index 7e64d01da2be..2c8b84dd9dac 100644 --- a/drivers/input/misc/rotary_encoder.c +++ b/drivers/input/misc/rotary_encoder.c @@ -2,6 +2,7 @@ * rotary_encoder.c * * (c) 2009 Daniel Mack <daniel@caiaq.de> + * Copyright (C) 2011 Johan Hovold <jhovold@gmail.com> * * state machine code inspired by code from Tim Ruetz * @@ -38,52 +39,66 @@ struct rotary_encoder { bool armed; unsigned char dir; /* 0 - clockwise, 1 - CCW */ + + char last_stable; }; -static irqreturn_t rotary_encoder_irq(int irq, void *dev_id) +static int rotary_encoder_get_state(struct rotary_encoder_platform_data *pdata) { - struct rotary_encoder *encoder = dev_id; - struct rotary_encoder_platform_data *pdata = encoder->pdata; int a = !!gpio_get_value(pdata->gpio_a); int b = !!gpio_get_value(pdata->gpio_b); - int state; a ^= pdata->inverted_a; b ^= pdata->inverted_b; - state = (a << 1) | b; - switch (state) { + return ((a << 1) | b); +} - case 0x0: - if (!encoder->armed) - break; +static void rotary_encoder_report_event(struct rotary_encoder *encoder) +{ + struct rotary_encoder_platform_data *pdata = encoder->pdata; - if (pdata->relative_axis) { - input_report_rel(encoder->input, pdata->axis, - encoder->dir ? -1 : 1); - } else { - unsigned int pos = encoder->pos; + if (pdata->relative_axis) { + input_report_rel(encoder->input, + pdata->axis, encoder->dir ? -1 : 1); + } else { + unsigned int pos = encoder->pos; - if (encoder->dir) { - /* turning counter-clockwise */ - if (pdata->rollover) - pos += pdata->steps; - if (pos) - pos--; - } else { - /* turning clockwise */ - if (pdata->rollover || pos < pdata->steps) - pos++; - } + if (encoder->dir) { + /* turning counter-clockwise */ if (pdata->rollover) - pos %= pdata->steps; - encoder->pos = pos; - input_report_abs(encoder->input, pdata->axis, - encoder->pos); + pos += pdata->steps; + if (pos) + pos--; + } else { + /* turning clockwise */ + if (pdata->rollover || pos < pdata->steps) + pos++; } - input_sync(encoder->input); - encoder->armed = false; + if (pdata->rollover) + pos %= pdata->steps; + + encoder->pos = pos; + input_report_abs(encoder->input, pdata->axis, encoder->pos); + } + + input_sync(encoder->input); +} + +static irqreturn_t rotary_encoder_irq(int irq, void *dev_id) +{ + struct rotary_encoder *encoder = dev_id; + int state; + + state = rotary_encoder_get_state(encoder->pdata); + + switch (state) { + case 0x0: + if (encoder->armed) { + rotary_encoder_report_event(encoder); + encoder->armed = false; + } break; case 0x1: @@ -100,11 +115,37 @@ static irqreturn_t rotary_encoder_irq(int irq, void *dev_id) return IRQ_HANDLED; } +static irqreturn_t rotary_encoder_half_period_irq(int irq, void *dev_id) +{ + struct rotary_encoder *encoder = dev_id; + int state; + + state = rotary_encoder_get_state(encoder->pdata); + + switch (state) { + case 0x00: + case 0x03: + if (state != encoder->last_stable) { + rotary_encoder_report_event(encoder); + encoder->last_stable = state; + } + break; + + case 0x01: + case 0x02: + encoder->dir = (encoder->last_stable + state) & 0x01; + break; + } + + return IRQ_HANDLED; +} + static int __devinit rotary_encoder_probe(struct platform_device *pdev) { struct rotary_encoder_platform_data *pdata = pdev->dev.platform_data; struct rotary_encoder *encoder; struct input_dev *input; + irq_handler_t handler; int err; if (!pdata) { @@ -175,7 +216,14 @@ static int __devinit rotary_encoder_probe(struct platform_device *pdev) } /* request the IRQs */ - err = request_irq(encoder->irq_a, &rotary_encoder_irq, + if (pdata->half_period) { + handler = &rotary_encoder_half_period_irq; + encoder->last_stable = rotary_encoder_get_state(pdata); + } else { + handler = &rotary_encoder_irq; + } + + err = request_irq(encoder->irq_a, handler, IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING, DRV_NAME, encoder); if (err) { @@ -184,7 +232,7 @@ static int __devinit rotary_encoder_probe(struct platform_device *pdev) goto exit_free_gpio_b; } - err = request_irq(encoder->irq_b, &rotary_encoder_irq, + err = request_irq(encoder->irq_b, handler, IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING, DRV_NAME, encoder); if (err) { @@ -252,6 +300,5 @@ module_exit(rotary_encoder_exit); MODULE_ALIAS("platform:" DRV_NAME); MODULE_DESCRIPTION("GPIO rotary encoder driver"); -MODULE_AUTHOR("Daniel Mack <daniel@caiaq.de>"); +MODULE_AUTHOR("Daniel Mack <daniel@caiaq.de>, Johan Hovold"); MODULE_LICENSE("GPL v2"); - diff --git a/drivers/input/misc/twl4030-pwrbutton.c b/drivers/input/misc/twl4030-pwrbutton.c index f16972bddca4..38e4b507b94c 100644 --- a/drivers/input/misc/twl4030-pwrbutton.c +++ b/drivers/input/misc/twl4030-pwrbutton.c @@ -89,7 +89,7 @@ static int __init twl4030_pwrbutton_probe(struct platform_device *pdev) return 0; free_irq: - free_irq(irq, NULL); + free_irq(irq, pwr); free_input_dev: input_free_device(pwr); return err; diff --git a/drivers/input/misc/twl4030-vibra.c b/drivers/input/misc/twl4030-vibra.c index 6a11694e3fc7..014dd4ad0d4f 100644 --- a/drivers/input/misc/twl4030-vibra.c +++ b/drivers/input/misc/twl4030-vibra.c @@ -29,7 +29,6 @@ #include <linux/workqueue.h> #include <linux/i2c/twl.h> #include <linux/mfd/twl4030-codec.h> -#include <linux/mfd/core.h> #include <linux/input.h> #include <linux/slab.h> @@ -197,7 +196,7 @@ static SIMPLE_DEV_PM_OPS(twl4030_vibra_pm_ops, static int __devinit twl4030_vibra_probe(struct platform_device *pdev) { - struct twl4030_codec_vibra_data *pdata = mfd_get_data(pdev); + struct twl4030_codec_vibra_data *pdata = pdev->dev.platform_data; struct vibra_info *info; int ret; diff --git a/drivers/input/misc/uinput.c b/drivers/input/misc/uinput.c index 364bdf43a381..736056897e50 100644 --- a/drivers/input/misc/uinput.c +++ b/drivers/input/misc/uinput.c @@ -302,10 +302,14 @@ static int uinput_validate_absbits(struct input_dev *dev) int retval = 0; for (cnt = 0; cnt < ABS_CNT; cnt++) { + int min, max; if (!test_bit(cnt, dev->absbit)) continue; - if (input_abs_get_max(dev, cnt) <= input_abs_get_min(dev, cnt)) { + min = input_abs_get_min(dev, cnt); + max = input_abs_get_max(dev, cnt); + + if ((min != 0 || max != 0) && max <= min) { printk(KERN_DEBUG "%s: invalid abs[%02x] min:%d max:%d\n", UINPUT_NAME, cnt, diff --git a/drivers/input/misc/wistron_btns.c b/drivers/input/misc/wistron_btns.c index 12501de0c5cd..52b419348983 100644 --- a/drivers/input/misc/wistron_btns.c +++ b/drivers/input/misc/wistron_btns.c @@ -274,7 +274,7 @@ static struct key_entry keymap_fs_amilo_pro_v3505[] __initdata = { { KE_BLUETOOTH, 0x30 }, /* Fn+F10 */ { KE_KEY, 0x31, {KEY_MAIL} }, /* mail button */ { KE_KEY, 0x36, {KEY_WWW} }, /* www button */ - { KE_WIFI, 0x78 }, /* satelite dish button */ + { KE_WIFI, 0x78 }, /* satellite dish button */ { KE_END, 0 } }; diff --git a/drivers/input/misc/xen-kbdfront.c b/drivers/input/misc/xen-kbdfront.c index 7077f9bf5ead..62bae99424e6 100644 --- a/drivers/input/misc/xen-kbdfront.c +++ b/drivers/input/misc/xen-kbdfront.c @@ -303,7 +303,7 @@ static void xenkbd_backend_changed(struct xenbus_device *dev, enum xenbus_state backend_state) { struct xenkbd_info *info = dev_get_drvdata(&dev->dev); - int val; + int ret, val; switch (backend_state) { case XenbusStateInitialising: @@ -316,6 +316,17 @@ static void xenkbd_backend_changed(struct xenbus_device *dev, case XenbusStateInitWait: InitWait: + ret = xenbus_scanf(XBT_NIL, info->xbdev->otherend, + "feature-abs-pointer", "%d", &val); + if (ret < 0) + val = 0; + if (val) { + ret = xenbus_printf(XBT_NIL, info->xbdev->nodename, + "request-abs-pointer", "1"); + if (ret) + pr_warning("xenkbd: can't request abs-pointer"); + } + xenbus_switch_state(dev, XenbusStateConnected); break; diff --git a/drivers/input/mouse/atarimouse.c b/drivers/input/mouse/atarimouse.c index adf45b3040e9..5c4a692bf73a 100644 --- a/drivers/input/mouse/atarimouse.c +++ b/drivers/input/mouse/atarimouse.c @@ -77,15 +77,15 @@ static void atamouse_interrupt(char *buf) #endif /* only relative events get here */ - dx = buf[1]; - dy = -buf[2]; + dx = buf[1]; + dy = buf[2]; input_report_rel(atamouse_dev, REL_X, dx); input_report_rel(atamouse_dev, REL_Y, dy); - input_report_key(atamouse_dev, BTN_LEFT, buttons & 0x1); + input_report_key(atamouse_dev, BTN_LEFT, buttons & 0x4); input_report_key(atamouse_dev, BTN_MIDDLE, buttons & 0x2); - input_report_key(atamouse_dev, BTN_RIGHT, buttons & 0x4); + input_report_key(atamouse_dev, BTN_RIGHT, buttons & 0x1); input_sync(atamouse_dev); @@ -108,7 +108,7 @@ static int atamouse_open(struct input_dev *dev) static void atamouse_close(struct input_dev *dev) { ikbd_mouse_disable(); - atari_mouse_interrupt_hook = NULL; + atari_input_mouse_interrupt_hook = NULL; } static int __init atamouse_init(void) @@ -118,8 +118,9 @@ static int __init atamouse_init(void) if (!MACH_IS_ATARI || !ATARIHW_PRESENT(ST_MFP)) return -ENODEV; - if (!atari_keyb_init()) - return -ENODEV; + error = atari_keyb_init(); + if (error) + return error; atamouse_dev = input_allocate_device(); if (!atamouse_dev) diff --git a/drivers/input/mouse/bcm5974.c b/drivers/input/mouse/bcm5974.c index 3aead91bacc8..3126983c004a 100644 --- a/drivers/input/mouse/bcm5974.c +++ b/drivers/input/mouse/bcm5974.c @@ -639,7 +639,7 @@ exit: * device, resulting in trackpad malfunction under certain * circumstances. To get around this problem, there is at least one * example that utilizes the USB_QUIRK_RESET_RESUME quirk in order to - * recieve a reset_resume request rather than the normal resume. + * receive a reset_resume request rather than the normal resume. * Since the implementation of reset_resume is equal to mode switch * plus start_traffic, it seems easier to always do the switch when * starting traffic on the device. diff --git a/drivers/input/mouse/elantech.c b/drivers/input/mouse/elantech.c index 04d9bf320a4f..32503565faf9 100644 --- a/drivers/input/mouse/elantech.c +++ b/drivers/input/mouse/elantech.c @@ -16,6 +16,7 @@ #include <linux/slab.h> #include <linux/module.h> #include <linux/input.h> +#include <linux/input/mt.h> #include <linux/serio.h> #include <linux/libps2.h> #include "psmouse.h" @@ -242,15 +243,37 @@ static void elantech_report_absolute_v1(struct psmouse *psmouse) input_sync(dev); } +static void elantech_set_slot(struct input_dev *dev, int slot, bool active, + unsigned int x, unsigned int y) +{ + input_mt_slot(dev, slot); + input_mt_report_slot_state(dev, MT_TOOL_FINGER, active); + if (active) { + input_report_abs(dev, ABS_MT_POSITION_X, x); + input_report_abs(dev, ABS_MT_POSITION_Y, y); + } +} + +/* x1 < x2 and y1 < y2 when two fingers, x = y = 0 when not pressed */ +static void elantech_report_semi_mt_data(struct input_dev *dev, + unsigned int num_fingers, + unsigned int x1, unsigned int y1, + unsigned int x2, unsigned int y2) +{ + elantech_set_slot(dev, 0, num_fingers != 0, x1, y1); + elantech_set_slot(dev, 1, num_fingers == 2, x2, y2); +} + /* * Interpret complete data packets and report absolute mode input events for * hardware version 2. (6 byte packets) */ static void elantech_report_absolute_v2(struct psmouse *psmouse) { + struct elantech_data *etd = psmouse->private; struct input_dev *dev = psmouse->dev; unsigned char *packet = psmouse->packet; - int fingers, x1, y1, x2, y2; + unsigned int fingers, x1 = 0, y1 = 0, x2 = 0, y2 = 0, width = 0, pres = 0; /* byte 0: n1 n0 . . . . R L */ fingers = (packet[0] & 0xc0) >> 6; @@ -270,14 +293,18 @@ static void elantech_report_absolute_v2(struct psmouse *psmouse) * byte 1: . . . . . x10 x9 x8 * byte 2: x7 x6 x5 x4 x4 x2 x1 x0 */ - input_report_abs(dev, ABS_X, - ((packet[1] & 0x07) << 8) | packet[2]); + x1 = ((packet[1] & 0x07) << 8) | packet[2]; /* * byte 4: . . . . . . y9 y8 * byte 5: y7 y6 y5 y4 y3 y2 y1 y0 */ - input_report_abs(dev, ABS_Y, - ETP_YMAX_V2 - (((packet[4] & 0x03) << 8) | packet[5])); + y1 = ETP_YMAX_V2 - (((packet[4] & 0x03) << 8) | packet[5]); + + input_report_abs(dev, ABS_X, x1); + input_report_abs(dev, ABS_Y, y1); + + pres = (packet[1] & 0xf0) | ((packet[4] & 0xf0) >> 4); + width = ((packet[0] & 0x30) >> 2) | ((packet[3] & 0x30) >> 4); break; case 2: @@ -303,23 +330,24 @@ static void elantech_report_absolute_v2(struct psmouse *psmouse) */ input_report_abs(dev, ABS_X, x1 << 2); input_report_abs(dev, ABS_Y, y1 << 2); - /* - * For compatibility with the proprietary X Elantech driver - * report both coordinates as hat coordinates - */ - input_report_abs(dev, ABS_HAT0X, x1); - input_report_abs(dev, ABS_HAT0Y, y1); - input_report_abs(dev, ABS_HAT1X, x2); - input_report_abs(dev, ABS_HAT1Y, y2); + + /* Unknown so just report sensible values */ + pres = 127; + width = 7; break; } + elantech_report_semi_mt_data(dev, fingers, x1, y1, x2, y2); input_report_key(dev, BTN_TOOL_FINGER, fingers == 1); input_report_key(dev, BTN_TOOL_DOUBLETAP, fingers == 2); input_report_key(dev, BTN_TOOL_TRIPLETAP, fingers == 3); input_report_key(dev, BTN_TOOL_QUADTAP, fingers == 4); input_report_key(dev, BTN_LEFT, packet[0] & 0x01); input_report_key(dev, BTN_RIGHT, packet[0] & 0x02); + if (etd->reports_pressure) { + input_report_abs(dev, ABS_PRESSURE, pres); + input_report_abs(dev, ABS_TOOL_WIDTH, width); + } input_sync(dev); } @@ -478,10 +506,16 @@ static void elantech_set_input_params(struct psmouse *psmouse) __set_bit(BTN_TOOL_QUADTAP, dev->keybit); input_set_abs_params(dev, ABS_X, ETP_XMIN_V2, ETP_XMAX_V2, 0, 0); input_set_abs_params(dev, ABS_Y, ETP_YMIN_V2, ETP_YMAX_V2, 0, 0); - input_set_abs_params(dev, ABS_HAT0X, ETP_2FT_XMIN, ETP_2FT_XMAX, 0, 0); - input_set_abs_params(dev, ABS_HAT0Y, ETP_2FT_YMIN, ETP_2FT_YMAX, 0, 0); - input_set_abs_params(dev, ABS_HAT1X, ETP_2FT_XMIN, ETP_2FT_XMAX, 0, 0); - input_set_abs_params(dev, ABS_HAT1Y, ETP_2FT_YMIN, ETP_2FT_YMAX, 0, 0); + if (etd->reports_pressure) { + input_set_abs_params(dev, ABS_PRESSURE, ETP_PMIN_V2, + ETP_PMAX_V2, 0, 0); + input_set_abs_params(dev, ABS_TOOL_WIDTH, ETP_WMIN_V2, + ETP_WMAX_V2, 0, 0); + } + __set_bit(INPUT_PROP_SEMI_MT, dev->propbit); + input_mt_init_slots(dev, 2); + input_set_abs_params(dev, ABS_MT_POSITION_X, ETP_XMIN_V2, ETP_XMAX_V2, 0, 0); + input_set_abs_params(dev, ABS_MT_POSITION_Y, ETP_YMIN_V2, ETP_YMAX_V2, 0, 0); break; } } @@ -725,6 +759,10 @@ int elantech_init(struct psmouse *psmouse) etd->debug = 1; /* Don't know how to do parity checking for version 2 */ etd->paritycheck = 0; + + if (etd->fw_version >= 0x020800) + etd->reports_pressure = true; + } else { etd->hw_version = 1; etd->paritycheck = 1; diff --git a/drivers/input/mouse/elantech.h b/drivers/input/mouse/elantech.h index aa4aac5d2198..fabb2b99615c 100644 --- a/drivers/input/mouse/elantech.h +++ b/drivers/input/mouse/elantech.h @@ -77,6 +77,11 @@ #define ETP_YMIN_V2 ( 0 + ETP_EDGE_FUZZ_V2) #define ETP_YMAX_V2 ( 768 - ETP_EDGE_FUZZ_V2) +#define ETP_PMIN_V2 0 +#define ETP_PMAX_V2 255 +#define ETP_WMIN_V2 0 +#define ETP_WMAX_V2 15 + /* * For two finger touches the coordinate of each finger gets reported * separately but with reduced resolution. @@ -102,6 +107,7 @@ struct elantech_data { unsigned char capabilities; bool paritycheck; bool jumpy_cursor; + bool reports_pressure; unsigned char hw_version; unsigned int fw_version; unsigned int single_finger_reports; diff --git a/drivers/input/mouse/synaptics.c b/drivers/input/mouse/synaptics.c index aa186cf6c514..e06e045bf907 100644 --- a/drivers/input/mouse/synaptics.c +++ b/drivers/input/mouse/synaptics.c @@ -836,8 +836,8 @@ static const struct dmi_system_id __initconst toshiba_dmi_table[] = { }, }, - { } #endif + { } }; static bool broken_olpc_ec; @@ -851,8 +851,8 @@ static const struct dmi_system_id __initconst olpc_dmi_table[] = { DMI_MATCH(DMI_PRODUCT_NAME, "XO"), }, }, - { } #endif + { } }; void __init synaptics_module_init(void) diff --git a/drivers/input/mouse/synaptics_i2c.c b/drivers/input/mouse/synaptics_i2c.c index f6aa26d305ed..cba3c84d2f21 100644 --- a/drivers/input/mouse/synaptics_i2c.c +++ b/drivers/input/mouse/synaptics_i2c.c @@ -462,7 +462,7 @@ static void synaptics_i2c_work_handler(struct work_struct *work) * While interrupt driven, there is no real need to poll the device. * But touchpads are very sensitive, so there could be errors * related to physical environment and the attention line isn't - * neccesarily asserted. In such case we can lose the touchpad. + * necessarily asserted. In such case we can lose the touchpad. * We poll the device once in THREAD_IRQ_SLEEP_SECS and * if error is detected, we try to reset and reconfigure the touchpad. */ diff --git a/drivers/input/mouse/vsxxxaa.c b/drivers/input/mouse/vsxxxaa.c index bf2c0c80d6cc..eb9a3cfbeefa 100644 --- a/drivers/input/mouse/vsxxxaa.c +++ b/drivers/input/mouse/vsxxxaa.c @@ -334,7 +334,7 @@ static void vsxxxaa_handle_POR_packet(struct vsxxxaa *mouse) * M: manufacturer location code * R: revision code * E: Error code. If it's in the range of 0x00..0x1f, only some - * minor problem occured. Errors >= 0x20 are considered bad + * minor problem occurred. Errors >= 0x20 are considered bad * and the device may not work properly... * D: <0010> == mouse, <0100> == tablet */ diff --git a/drivers/input/mousedev.c b/drivers/input/mousedev.c index 7630273e9474..257e033986e4 100644 --- a/drivers/input/mousedev.c +++ b/drivers/input/mousedev.c @@ -508,7 +508,6 @@ static void mousedev_attach_client(struct mousedev *mousedev, spin_lock(&mousedev->client_lock); list_add_tail_rcu(&client->node, &mousedev->client_list); spin_unlock(&mousedev->client_lock); - synchronize_rcu(); } static void mousedev_detach_client(struct mousedev *mousedev, diff --git a/drivers/input/serio/hp_sdc.c b/drivers/input/serio/hp_sdc.c index 8c0b51c31424..42206205e4f5 100644 --- a/drivers/input/serio/hp_sdc.c +++ b/drivers/input/serio/hp_sdc.c @@ -955,7 +955,7 @@ static int __init hp_sdc_init_hppa(struct parisc_device *d) INIT_DELAYED_WORK(&moduleloader_work, request_module_delayed); ret = hp_sdc_init(); - /* after successfull initialization give SDC some time to settle + /* after successful initialization give SDC some time to settle * and then load the hp_sdc_mlc upper layer driver */ if (!ret) schedule_delayed_work(&moduleloader_work, diff --git a/drivers/input/serio/i8042.c b/drivers/input/serio/i8042.c index ac4c93689ab9..d37a48e099d0 100644 --- a/drivers/input/serio/i8042.c +++ b/drivers/input/serio/i8042.c @@ -869,15 +869,15 @@ static int i8042_controller_selftest(void) do { if (i8042_command(¶m, I8042_CMD_CTL_TEST)) { - pr_err("i8042 controller self test timeout\n"); + pr_err("i8042 controller selftest timeout\n"); return -ENODEV; } if (param == I8042_RET_CTL_TEST) return 0; - pr_err("i8042 controller selftest failed. (%#x != %#x)\n", - param, I8042_RET_CTL_TEST); + dbg("i8042 controller selftest: %#x != %#x\n", + param, I8042_RET_CTL_TEST); msleep(50); } while (i++ < 5); @@ -891,6 +891,7 @@ static int i8042_controller_selftest(void) pr_info("giving up on controller selftest, continuing anyway...\n"); return 0; #else + pr_err("i8042 controller selftest failed\n"); return -EIO; #endif } diff --git a/drivers/input/serio/rpckbd.c b/drivers/input/serio/rpckbd.c index 9da6fbcaaa7e..7ec3c97dc1b9 100644 --- a/drivers/input/serio/rpckbd.c +++ b/drivers/input/serio/rpckbd.c @@ -90,7 +90,7 @@ static int rpckbd_open(struct serio *port) if (request_irq(IRQ_KEYBOARDTX, rpckbd_tx, 0, "rpckbd", port) != 0) { printk(KERN_ERR "rpckbd.c: Could not allocate keyboard transmit IRQ\n"); - free_irq(IRQ_KEYBOARDRX, NULL); + free_irq(IRQ_KEYBOARDRX, port); return -EBUSY; } diff --git a/drivers/input/serio/serport.c b/drivers/input/serio/serport.c index 8755f5f3ad37..f3698967edf6 100644 --- a/drivers/input/serio/serport.c +++ b/drivers/input/serio/serport.c @@ -120,17 +120,21 @@ static void serport_ldisc_close(struct tty_struct *tty) * 'interrupt' routine. */ -static void serport_ldisc_receive(struct tty_struct *tty, const unsigned char *cp, char *fp, int count) +static unsigned int serport_ldisc_receive(struct tty_struct *tty, + const unsigned char *cp, char *fp, int count) { struct serport *serport = (struct serport*) tty->disc_data; unsigned long flags; unsigned int ch_flags; + int ret = 0; int i; spin_lock_irqsave(&serport->lock, flags); - if (!test_bit(SERPORT_ACTIVE, &serport->flags)) + if (!test_bit(SERPORT_ACTIVE, &serport->flags)) { + ret = -EINVAL; goto out; + } for (i = 0; i < count; i++) { switch (fp[i]) { @@ -152,6 +156,8 @@ static void serport_ldisc_receive(struct tty_struct *tty, const unsigned char *c out: spin_unlock_irqrestore(&serport->lock, flags); + + return ret == 0 ? count : ret; } /* diff --git a/drivers/input/serio/xilinx_ps2.c b/drivers/input/serio/xilinx_ps2.c index 7540bafc95cf..80baa53da5b1 100644 --- a/drivers/input/serio/xilinx_ps2.c +++ b/drivers/input/serio/xilinx_ps2.c @@ -225,7 +225,7 @@ static void sxps2_close(struct serio *pserio) /** * xps2_of_probe - probe method for the PS/2 device. * @of_dev: pointer to OF device structure - * @match: pointer to the stucture used for matching a device + * @match: pointer to the structure used for matching a device * * This function probes the PS/2 device in the device tree. * It initializes the driver data structure and the hardware. diff --git a/drivers/input/sparse-keymap.c b/drivers/input/sparse-keymap.c index 337bf51bc984..fdb6a3976f94 100644 --- a/drivers/input/sparse-keymap.c +++ b/drivers/input/sparse-keymap.c @@ -208,6 +208,12 @@ int sparse_keymap_setup(struct input_dev *dev, } } + if (test_bit(EV_KEY, dev->evbit)) { + __set_bit(KEY_UNKNOWN, dev->keybit); + __set_bit(EV_MSC, dev->evbit); + __set_bit(MSC_SCAN, dev->mscbit); + } + dev->keycode = map; dev->keycodemax = map_size; dev->getkeycode = sparse_keymap_getkeycode; @@ -268,6 +274,7 @@ void sparse_keymap_report_entry(struct input_dev *dev, const struct key_entry *k { switch (ke->type) { case KE_KEY: + input_event(dev, EV_MSC, MSC_SCAN, ke->code); input_report_key(dev, ke->keycode, value); input_sync(dev); if (value && autorelease) { @@ -305,12 +312,19 @@ bool sparse_keymap_report_event(struct input_dev *dev, unsigned int code, { const struct key_entry *ke = sparse_keymap_entry_from_scancode(dev, code); + struct key_entry unknown_ke; if (ke) { sparse_keymap_report_entry(dev, ke, value, autorelease); return true; } + /* Report an unknown key event as a debugging aid */ + unknown_ke.type = KE_KEY; + unknown_ke.code = code; + unknown_ke.keycode = KEY_UNKNOWN; + sparse_keymap_report_entry(dev, &unknown_ke, value, true); + return false; } EXPORT_SYMBOL(sparse_keymap_report_event); diff --git a/drivers/input/tablet/wacom_wac.c b/drivers/input/tablet/wacom_wac.c index 5597637cfd41..08ba5ad9c9be 100644 --- a/drivers/input/tablet/wacom_wac.c +++ b/drivers/input/tablet/wacom_wac.c @@ -16,6 +16,14 @@ #include "wacom.h" #include <linux/input/mt.h> +/* resolution for penabled devices */ +#define WACOM_PL_RES 20 +#define WACOM_PENPRTN_RES 40 +#define WACOM_VOLITO_RES 50 +#define WACOM_GRAPHIRE_RES 80 +#define WACOM_INTUOS_RES 100 +#define WACOM_INTUOS3_RES 200 + static int wacom_penpartner_irq(struct wacom_wac *wacom) { unsigned char *data = wacom->data; @@ -1055,6 +1063,19 @@ void wacom_setup_input_capabilities(struct input_dev *input_dev, input_set_abs_params(input_dev, ABS_PRESSURE, 0, features->pressure_max, features->pressure_fuzz, 0); + if (features->device_type == BTN_TOOL_PEN) { + /* penabled devices have fixed resolution for each model */ + input_abs_set_res(input_dev, ABS_X, features->x_resolution); + input_abs_set_res(input_dev, ABS_Y, features->y_resolution); + } else { + input_abs_set_res(input_dev, ABS_X, + wacom_calculate_touch_res(features->x_max, + features->x_phy)); + input_abs_set_res(input_dev, ABS_Y, + wacom_calculate_touch_res(features->y_max, + features->y_phy)); + } + __set_bit(ABS_MISC, input_dev->absbit); switch (wacom_wac->features.type) { @@ -1171,15 +1192,9 @@ void wacom_setup_input_capabilities(struct input_dev *input_dev, case TABLETPC: __clear_bit(ABS_MISC, input_dev->absbit); - if (features->device_type != BTN_TOOL_PEN) { - input_abs_set_res(input_dev, ABS_X, - wacom_calculate_touch_res(features->x_max, - features->x_phy)); - input_abs_set_res(input_dev, ABS_Y, - wacom_calculate_touch_res(features->y_max, - features->y_phy)); + if (features->device_type != BTN_TOOL_PEN) break; /* no need to process stylus stuff */ - } + /* fall through */ case PL: @@ -1216,12 +1231,6 @@ void wacom_setup_input_capabilities(struct input_dev *input_dev, input_set_abs_params(input_dev, ABS_MT_PRESSURE, 0, features->pressure_max, features->pressure_fuzz, 0); - input_abs_set_res(input_dev, ABS_X, - wacom_calculate_touch_res(features->x_max, - features->x_phy)); - input_abs_set_res(input_dev, ABS_Y, - wacom_calculate_touch_res(features->y_max, - features->y_phy)); } else if (features->device_type == BTN_TOOL_PEN) { __set_bit(BTN_TOOL_RUBBER, input_dev->keybit); __set_bit(BTN_TOOL_PEN, input_dev->keybit); @@ -1233,161 +1242,242 @@ void wacom_setup_input_capabilities(struct input_dev *input_dev, } static const struct wacom_features wacom_features_0x00 = - { "Wacom Penpartner", WACOM_PKGLEN_PENPRTN, 5040, 3780, 255, 0, PENPARTNER }; + { "Wacom Penpartner", WACOM_PKGLEN_PENPRTN, 5040, 3780, 255, + 0, PENPARTNER, WACOM_PENPRTN_RES, WACOM_PENPRTN_RES }; static const struct wacom_features wacom_features_0x10 = - { "Wacom Graphire", WACOM_PKGLEN_GRAPHIRE, 10206, 7422, 511, 63, GRAPHIRE }; + { "Wacom Graphire", WACOM_PKGLEN_GRAPHIRE, 10206, 7422, 511, + 63, GRAPHIRE, WACOM_GRAPHIRE_RES, WACOM_GRAPHIRE_RES }; static const struct wacom_features wacom_features_0x11 = - { "Wacom Graphire2 4x5", WACOM_PKGLEN_GRAPHIRE, 10206, 7422, 511, 63, GRAPHIRE }; + { "Wacom Graphire2 4x5", WACOM_PKGLEN_GRAPHIRE, 10206, 7422, 511, + 63, GRAPHIRE, WACOM_GRAPHIRE_RES, WACOM_GRAPHIRE_RES }; static const struct wacom_features wacom_features_0x12 = - { "Wacom Graphire2 5x7", WACOM_PKGLEN_GRAPHIRE, 13918, 10206, 511, 63, GRAPHIRE }; + { "Wacom Graphire2 5x7", WACOM_PKGLEN_GRAPHIRE, 13918, 10206, 511, + 63, GRAPHIRE, WACOM_GRAPHIRE_RES, WACOM_GRAPHIRE_RES }; static const struct wacom_features wacom_features_0x13 = - { "Wacom Graphire3", WACOM_PKGLEN_GRAPHIRE, 10208, 7424, 511, 63, GRAPHIRE }; + { "Wacom Graphire3", WACOM_PKGLEN_GRAPHIRE, 10208, 7424, 511, + 63, GRAPHIRE, WACOM_GRAPHIRE_RES, WACOM_GRAPHIRE_RES }; static const struct wacom_features wacom_features_0x14 = - { "Wacom Graphire3 6x8", WACOM_PKGLEN_GRAPHIRE, 16704, 12064, 511, 63, GRAPHIRE }; + { "Wacom Graphire3 6x8", WACOM_PKGLEN_GRAPHIRE, 16704, 12064, 511, + 63, GRAPHIRE, WACOM_GRAPHIRE_RES, WACOM_GRAPHIRE_RES }; static const struct wacom_features wacom_features_0x15 = - { "Wacom Graphire4 4x5", WACOM_PKGLEN_GRAPHIRE, 10208, 7424, 511, 63, WACOM_G4 }; + { "Wacom Graphire4 4x5", WACOM_PKGLEN_GRAPHIRE, 10208, 7424, 511, + 63, WACOM_G4, WACOM_GRAPHIRE_RES, WACOM_GRAPHIRE_RES }; static const struct wacom_features wacom_features_0x16 = - { "Wacom Graphire4 6x8", WACOM_PKGLEN_GRAPHIRE, 16704, 12064, 511, 63, WACOM_G4 }; + { "Wacom Graphire4 6x8", WACOM_PKGLEN_GRAPHIRE, 16704, 12064, 511, + 63, WACOM_G4, WACOM_GRAPHIRE_RES, WACOM_GRAPHIRE_RES }; static const struct wacom_features wacom_features_0x17 = - { "Wacom BambooFun 4x5", WACOM_PKGLEN_BBFUN, 14760, 9225, 511, 63, WACOM_MO }; + { "Wacom BambooFun 4x5", WACOM_PKGLEN_BBFUN, 14760, 9225, 511, + 63, WACOM_MO, WACOM_INTUOS_RES, WACOM_INTUOS_RES }; static const struct wacom_features wacom_features_0x18 = - { "Wacom BambooFun 6x8", WACOM_PKGLEN_BBFUN, 21648, 13530, 511, 63, WACOM_MO }; + { "Wacom BambooFun 6x8", WACOM_PKGLEN_BBFUN, 21648, 13530, 511, + 63, WACOM_MO, WACOM_INTUOS_RES, WACOM_INTUOS_RES }; static const struct wacom_features wacom_features_0x19 = - { "Wacom Bamboo1 Medium", WACOM_PKGLEN_GRAPHIRE, 16704, 12064, 511, 63, GRAPHIRE }; + { "Wacom Bamboo1 Medium", WACOM_PKGLEN_GRAPHIRE, 16704, 12064, 511, + 63, GRAPHIRE, WACOM_GRAPHIRE_RES, WACOM_GRAPHIRE_RES }; static const struct wacom_features wacom_features_0x60 = - { "Wacom Volito", WACOM_PKGLEN_GRAPHIRE, 5104, 3712, 511, 63, GRAPHIRE }; + { "Wacom Volito", WACOM_PKGLEN_GRAPHIRE, 5104, 3712, 511, + 63, GRAPHIRE, WACOM_VOLITO_RES, WACOM_VOLITO_RES }; static const struct wacom_features wacom_features_0x61 = - { "Wacom PenStation2", WACOM_PKGLEN_GRAPHIRE, 3250, 2320, 255, 63, GRAPHIRE }; + { "Wacom PenStation2", WACOM_PKGLEN_GRAPHIRE, 3250, 2320, 255, + 63, GRAPHIRE, WACOM_VOLITO_RES, WACOM_VOLITO_RES }; static const struct wacom_features wacom_features_0x62 = - { "Wacom Volito2 4x5", WACOM_PKGLEN_GRAPHIRE, 5104, 3712, 511, 63, GRAPHIRE }; + { "Wacom Volito2 4x5", WACOM_PKGLEN_GRAPHIRE, 5104, 3712, 511, + 63, GRAPHIRE, WACOM_VOLITO_RES, WACOM_VOLITO_RES }; static const struct wacom_features wacom_features_0x63 = - { "Wacom Volito2 2x3", WACOM_PKGLEN_GRAPHIRE, 3248, 2320, 511, 63, GRAPHIRE }; + { "Wacom Volito2 2x3", WACOM_PKGLEN_GRAPHIRE, 3248, 2320, 511, + 63, GRAPHIRE, WACOM_VOLITO_RES, WACOM_VOLITO_RES }; static const struct wacom_features wacom_features_0x64 = - { "Wacom PenPartner2", WACOM_PKGLEN_GRAPHIRE, 3250, 2320, 511, 63, GRAPHIRE }; + { "Wacom PenPartner2", WACOM_PKGLEN_GRAPHIRE, 3250, 2320, 511, + 63, GRAPHIRE, WACOM_VOLITO_RES, WACOM_VOLITO_RES }; static const struct wacom_features wacom_features_0x65 = - { "Wacom Bamboo", WACOM_PKGLEN_BBFUN, 14760, 9225, 511, 63, WACOM_MO }; + { "Wacom Bamboo", WACOM_PKGLEN_BBFUN, 14760, 9225, 511, + 63, WACOM_MO, WACOM_INTUOS_RES, WACOM_INTUOS_RES }; static const struct wacom_features wacom_features_0x69 = - { "Wacom Bamboo1", WACOM_PKGLEN_GRAPHIRE, 5104, 3712, 511, 63, GRAPHIRE }; + { "Wacom Bamboo1", WACOM_PKGLEN_GRAPHIRE, 5104, 3712, 511, + 63, GRAPHIRE, WACOM_PENPRTN_RES, WACOM_PENPRTN_RES }; static const struct wacom_features wacom_features_0x20 = - { "Wacom Intuos 4x5", WACOM_PKGLEN_INTUOS, 12700, 10600, 1023, 31, INTUOS }; + { "Wacom Intuos 4x5", WACOM_PKGLEN_INTUOS, 12700, 10600, 1023, + 31, INTUOS, WACOM_INTUOS_RES, WACOM_INTUOS_RES }; static const struct wacom_features wacom_features_0x21 = - { "Wacom Intuos 6x8", WACOM_PKGLEN_INTUOS, 20320, 16240, 1023, 31, INTUOS }; + { "Wacom Intuos 6x8", WACOM_PKGLEN_INTUOS, 20320, 16240, 1023, + 31, INTUOS, WACOM_INTUOS_RES, WACOM_INTUOS_RES }; static const struct wacom_features wacom_features_0x22 = - { "Wacom Intuos 9x12", WACOM_PKGLEN_INTUOS, 30480, 24060, 1023, 31, INTUOS }; + { "Wacom Intuos 9x12", WACOM_PKGLEN_INTUOS, 30480, 24060, 1023, + 31, INTUOS, WACOM_INTUOS_RES, WACOM_INTUOS_RES }; static const struct wacom_features wacom_features_0x23 = - { "Wacom Intuos 12x12", WACOM_PKGLEN_INTUOS, 30480, 31680, 1023, 31, INTUOS }; + { "Wacom Intuos 12x12", WACOM_PKGLEN_INTUOS, 30480, 31680, 1023, + 31, INTUOS, WACOM_INTUOS_RES, WACOM_INTUOS_RES }; static const struct wacom_features wacom_features_0x24 = - { "Wacom Intuos 12x18", WACOM_PKGLEN_INTUOS, 45720, 31680, 1023, 31, INTUOS }; + { "Wacom Intuos 12x18", WACOM_PKGLEN_INTUOS, 45720, 31680, 1023, + 31, INTUOS, WACOM_INTUOS_RES, WACOM_INTUOS_RES }; static const struct wacom_features wacom_features_0x30 = - { "Wacom PL400", WACOM_PKGLEN_GRAPHIRE, 5408, 4056, 255, 0, PL }; + { "Wacom PL400", WACOM_PKGLEN_GRAPHIRE, 5408, 4056, 255, + 0, PL, WACOM_PL_RES, WACOM_PL_RES }; static const struct wacom_features wacom_features_0x31 = - { "Wacom PL500", WACOM_PKGLEN_GRAPHIRE, 6144, 4608, 255, 0, PL }; + { "Wacom PL500", WACOM_PKGLEN_GRAPHIRE, 6144, 4608, 255, + 0, PL, WACOM_PL_RES, WACOM_PL_RES }; static const struct wacom_features wacom_features_0x32 = - { "Wacom PL600", WACOM_PKGLEN_GRAPHIRE, 6126, 4604, 255, 0, PL }; + { "Wacom PL600", WACOM_PKGLEN_GRAPHIRE, 6126, 4604, 255, + 0, PL, WACOM_PL_RES, WACOM_PL_RES }; static const struct wacom_features wacom_features_0x33 = - { "Wacom PL600SX", WACOM_PKGLEN_GRAPHIRE, 6260, 5016, 255, 0, PL }; + { "Wacom PL600SX", WACOM_PKGLEN_GRAPHIRE, 6260, 5016, 255, + 0, PL, WACOM_PL_RES, WACOM_PL_RES }; static const struct wacom_features wacom_features_0x34 = - { "Wacom PL550", WACOM_PKGLEN_GRAPHIRE, 6144, 4608, 511, 0, PL }; + { "Wacom PL550", WACOM_PKGLEN_GRAPHIRE, 6144, 4608, 511, + 0, PL, WACOM_PL_RES, WACOM_PL_RES }; static const struct wacom_features wacom_features_0x35 = - { "Wacom PL800", WACOM_PKGLEN_GRAPHIRE, 7220, 5780, 511, 0, PL }; + { "Wacom PL800", WACOM_PKGLEN_GRAPHIRE, 7220, 5780, 511, + 0, PL, WACOM_PL_RES, WACOM_PL_RES }; static const struct wacom_features wacom_features_0x37 = - { "Wacom PL700", WACOM_PKGLEN_GRAPHIRE, 6758, 5406, 511, 0, PL }; + { "Wacom PL700", WACOM_PKGLEN_GRAPHIRE, 6758, 5406, 511, + 0, PL, WACOM_PL_RES, WACOM_PL_RES }; static const struct wacom_features wacom_features_0x38 = - { "Wacom PL510", WACOM_PKGLEN_GRAPHIRE, 6282, 4762, 511, 0, PL }; + { "Wacom PL510", WACOM_PKGLEN_GRAPHIRE, 6282, 4762, 511, + 0, PL, WACOM_PL_RES, WACOM_PL_RES }; static const struct wacom_features wacom_features_0x39 = - { "Wacom DTU710", WACOM_PKGLEN_GRAPHIRE, 34080, 27660, 511, 0, PL }; + { "Wacom DTU710", WACOM_PKGLEN_GRAPHIRE, 34080, 27660, 511, + 0, PL, WACOM_PL_RES, WACOM_PL_RES }; static const struct wacom_features wacom_features_0xC4 = - { "Wacom DTF521", WACOM_PKGLEN_GRAPHIRE, 6282, 4762, 511, 0, PL }; + { "Wacom DTF521", WACOM_PKGLEN_GRAPHIRE, 6282, 4762, 511, + 0, PL, WACOM_PL_RES, WACOM_PL_RES }; static const struct wacom_features wacom_features_0xC0 = - { "Wacom DTF720", WACOM_PKGLEN_GRAPHIRE, 6858, 5506, 511, 0, PL }; + { "Wacom DTF720", WACOM_PKGLEN_GRAPHIRE, 6858, 5506, 511, + 0, PL, WACOM_PL_RES, WACOM_PL_RES }; static const struct wacom_features wacom_features_0xC2 = - { "Wacom DTF720a", WACOM_PKGLEN_GRAPHIRE, 6858, 5506, 511, 0, PL }; + { "Wacom DTF720a", WACOM_PKGLEN_GRAPHIRE, 6858, 5506, 511, + 0, PL, WACOM_PL_RES, WACOM_PL_RES }; static const struct wacom_features wacom_features_0x03 = - { "Wacom Cintiq Partner", WACOM_PKGLEN_GRAPHIRE, 20480, 15360, 511, 0, PTU }; + { "Wacom Cintiq Partner", WACOM_PKGLEN_GRAPHIRE, 20480, 15360, 511, + 0, PTU, WACOM_PL_RES, WACOM_PL_RES }; static const struct wacom_features wacom_features_0x41 = - { "Wacom Intuos2 4x5", WACOM_PKGLEN_INTUOS, 12700, 10600, 1023, 31, INTUOS }; + { "Wacom Intuos2 4x5", WACOM_PKGLEN_INTUOS, 12700, 10600, 1023, + 31, INTUOS, WACOM_INTUOS_RES, WACOM_INTUOS_RES }; static const struct wacom_features wacom_features_0x42 = - { "Wacom Intuos2 6x8", WACOM_PKGLEN_INTUOS, 20320, 16240, 1023, 31, INTUOS }; + { "Wacom Intuos2 6x8", WACOM_PKGLEN_INTUOS, 20320, 16240, 1023, + 31, INTUOS, WACOM_INTUOS_RES, WACOM_INTUOS_RES }; static const struct wacom_features wacom_features_0x43 = - { "Wacom Intuos2 9x12", WACOM_PKGLEN_INTUOS, 30480, 24060, 1023, 31, INTUOS }; + { "Wacom Intuos2 9x12", WACOM_PKGLEN_INTUOS, 30480, 24060, 1023, + 31, INTUOS, WACOM_INTUOS_RES, WACOM_INTUOS_RES }; static const struct wacom_features wacom_features_0x44 = - { "Wacom Intuos2 12x12", WACOM_PKGLEN_INTUOS, 30480, 31680, 1023, 31, INTUOS }; + { "Wacom Intuos2 12x12", WACOM_PKGLEN_INTUOS, 30480, 31680, 1023, + 31, INTUOS, WACOM_INTUOS_RES, WACOM_INTUOS_RES }; static const struct wacom_features wacom_features_0x45 = - { "Wacom Intuos2 12x18", WACOM_PKGLEN_INTUOS, 45720, 31680, 1023, 31, INTUOS }; + { "Wacom Intuos2 12x18", WACOM_PKGLEN_INTUOS, 45720, 31680, 1023, + 31, INTUOS, WACOM_INTUOS_RES, WACOM_INTUOS_RES }; static const struct wacom_features wacom_features_0xB0 = - { "Wacom Intuos3 4x5", WACOM_PKGLEN_INTUOS, 25400, 20320, 1023, 63, INTUOS3S }; + { "Wacom Intuos3 4x5", WACOM_PKGLEN_INTUOS, 25400, 20320, 1023, + 63, INTUOS3S, WACOM_INTUOS3_RES, WACOM_INTUOS3_RES }; static const struct wacom_features wacom_features_0xB1 = - { "Wacom Intuos3 6x8", WACOM_PKGLEN_INTUOS, 40640, 30480, 1023, 63, INTUOS3 }; + { "Wacom Intuos3 6x8", WACOM_PKGLEN_INTUOS, 40640, 30480, 1023, + 63, INTUOS3, WACOM_INTUOS3_RES, WACOM_INTUOS3_RES }; static const struct wacom_features wacom_features_0xB2 = - { "Wacom Intuos3 9x12", WACOM_PKGLEN_INTUOS, 60960, 45720, 1023, 63, INTUOS3 }; + { "Wacom Intuos3 9x12", WACOM_PKGLEN_INTUOS, 60960, 45720, 1023, + 63, INTUOS3, WACOM_INTUOS3_RES, WACOM_INTUOS3_RES }; static const struct wacom_features wacom_features_0xB3 = - { "Wacom Intuos3 12x12", WACOM_PKGLEN_INTUOS, 60960, 60960, 1023, 63, INTUOS3L }; + { "Wacom Intuos3 12x12", WACOM_PKGLEN_INTUOS, 60960, 60960, 1023, + 63, INTUOS3L, WACOM_INTUOS3_RES, WACOM_INTUOS3_RES }; static const struct wacom_features wacom_features_0xB4 = - { "Wacom Intuos3 12x19", WACOM_PKGLEN_INTUOS, 97536, 60960, 1023, 63, INTUOS3L }; + { "Wacom Intuos3 12x19", WACOM_PKGLEN_INTUOS, 97536, 60960, 1023, + 63, INTUOS3L, WACOM_INTUOS3_RES, WACOM_INTUOS3_RES }; static const struct wacom_features wacom_features_0xB5 = - { "Wacom Intuos3 6x11", WACOM_PKGLEN_INTUOS, 54204, 31750, 1023, 63, INTUOS3 }; + { "Wacom Intuos3 6x11", WACOM_PKGLEN_INTUOS, 54204, 31750, 1023, + 63, INTUOS3, WACOM_INTUOS3_RES, WACOM_INTUOS3_RES }; static const struct wacom_features wacom_features_0xB7 = - { "Wacom Intuos3 4x6", WACOM_PKGLEN_INTUOS, 31496, 19685, 1023, 63, INTUOS3S }; + { "Wacom Intuos3 4x6", WACOM_PKGLEN_INTUOS, 31496, 19685, 1023, + 63, INTUOS3S, WACOM_INTUOS3_RES, WACOM_INTUOS3_RES }; static const struct wacom_features wacom_features_0xB8 = - { "Wacom Intuos4 4x6", WACOM_PKGLEN_INTUOS, 31496, 19685, 2047, 63, INTUOS4S }; + { "Wacom Intuos4 4x6", WACOM_PKGLEN_INTUOS, 31496, 19685, 2047, + 63, INTUOS4S, WACOM_INTUOS3_RES, WACOM_INTUOS3_RES }; static const struct wacom_features wacom_features_0xB9 = - { "Wacom Intuos4 6x9", WACOM_PKGLEN_INTUOS, 44704, 27940, 2047, 63, INTUOS4 }; + { "Wacom Intuos4 6x9", WACOM_PKGLEN_INTUOS, 44704, 27940, 2047, + 63, INTUOS4, WACOM_INTUOS3_RES, WACOM_INTUOS3_RES }; static const struct wacom_features wacom_features_0xBA = - { "Wacom Intuos4 8x13", WACOM_PKGLEN_INTUOS, 65024, 40640, 2047, 63, INTUOS4L }; + { "Wacom Intuos4 8x13", WACOM_PKGLEN_INTUOS, 65024, 40640, 2047, + 63, INTUOS4L, WACOM_INTUOS3_RES, WACOM_INTUOS3_RES }; static const struct wacom_features wacom_features_0xBB = - { "Wacom Intuos4 12x19", WACOM_PKGLEN_INTUOS, 97536, 60960, 2047, 63, INTUOS4L }; + { "Wacom Intuos4 12x19", WACOM_PKGLEN_INTUOS, 97536, 60960, 2047, + 63, INTUOS4L, WACOM_INTUOS3_RES, WACOM_INTUOS3_RES }; static const struct wacom_features wacom_features_0xBC = - { "Wacom Intuos4 WL", WACOM_PKGLEN_INTUOS, 40840, 25400, 2047, 63, INTUOS4 }; + { "Wacom Intuos4 WL", WACOM_PKGLEN_INTUOS, 40840, 25400, 2047, + 63, INTUOS4, WACOM_INTUOS3_RES, WACOM_INTUOS3_RES }; static const struct wacom_features wacom_features_0x3F = - { "Wacom Cintiq 21UX", WACOM_PKGLEN_INTUOS, 87200, 65600, 1023, 63, CINTIQ }; + { "Wacom Cintiq 21UX", WACOM_PKGLEN_INTUOS, 87200, 65600, 1023, + 63, CINTIQ, WACOM_INTUOS3_RES, WACOM_INTUOS3_RES }; static const struct wacom_features wacom_features_0xC5 = - { "Wacom Cintiq 20WSX", WACOM_PKGLEN_INTUOS, 86680, 54180, 1023, 63, WACOM_BEE }; + { "Wacom Cintiq 20WSX", WACOM_PKGLEN_INTUOS, 86680, 54180, 1023, + 63, WACOM_BEE, WACOM_INTUOS3_RES, WACOM_INTUOS3_RES }; static const struct wacom_features wacom_features_0xC6 = - { "Wacom Cintiq 12WX", WACOM_PKGLEN_INTUOS, 53020, 33440, 1023, 63, WACOM_BEE }; + { "Wacom Cintiq 12WX", WACOM_PKGLEN_INTUOS, 53020, 33440, 1023, + 63, WACOM_BEE, WACOM_INTUOS3_RES, WACOM_INTUOS3_RES }; static const struct wacom_features wacom_features_0xC7 = - { "Wacom DTU1931", WACOM_PKGLEN_GRAPHIRE, 37832, 30305, 511, 0, PL }; + { "Wacom DTU1931", WACOM_PKGLEN_GRAPHIRE, 37832, 30305, 511, + 0, PL, WACOM_INTUOS_RES, WACOM_INTUOS_RES }; static const struct wacom_features wacom_features_0xCE = - { "Wacom DTU2231", WACOM_PKGLEN_GRAPHIRE, 47864, 27011, 511, 0, DTU }; + { "Wacom DTU2231", WACOM_PKGLEN_GRAPHIRE, 47864, 27011, 511, + 0, DTU, WACOM_INTUOS_RES, WACOM_INTUOS_RES }; static const struct wacom_features wacom_features_0xF0 = - { "Wacom DTU1631", WACOM_PKGLEN_GRAPHIRE, 34623, 19553, 511, 0, DTU }; + { "Wacom DTU1631", WACOM_PKGLEN_GRAPHIRE, 34623, 19553, 511, + 0, DTU, WACOM_INTUOS_RES, WACOM_INTUOS_RES }; static const struct wacom_features wacom_features_0xCC = - { "Wacom Cintiq 21UX2", WACOM_PKGLEN_INTUOS, 87200, 65600, 2047, 63, WACOM_21UX2 }; + { "Wacom Cintiq 21UX2", WACOM_PKGLEN_INTUOS, 87200, 65600, 2047, + 63, WACOM_21UX2, WACOM_INTUOS3_RES, WACOM_INTUOS3_RES }; static const struct wacom_features wacom_features_0x90 = - { "Wacom ISDv4 90", WACOM_PKGLEN_GRAPHIRE, 26202, 16325, 255, 0, TABLETPC }; + { "Wacom ISDv4 90", WACOM_PKGLEN_GRAPHIRE, 26202, 16325, 255, + 0, TABLETPC, WACOM_INTUOS_RES, WACOM_INTUOS_RES }; static const struct wacom_features wacom_features_0x93 = - { "Wacom ISDv4 93", WACOM_PKGLEN_GRAPHIRE, 26202, 16325, 255, 0, TABLETPC }; + { "Wacom ISDv4 93", WACOM_PKGLEN_GRAPHIRE, 26202, 16325, 255, + 0, TABLETPC, WACOM_INTUOS_RES, WACOM_INTUOS_RES }; static const struct wacom_features wacom_features_0x9A = - { "Wacom ISDv4 9A", WACOM_PKGLEN_GRAPHIRE, 26202, 16325, 255, 0, TABLETPC }; + { "Wacom ISDv4 9A", WACOM_PKGLEN_GRAPHIRE, 26202, 16325, 255, + 0, TABLETPC, WACOM_INTUOS_RES, WACOM_INTUOS_RES }; static const struct wacom_features wacom_features_0x9F = - { "Wacom ISDv4 9F", WACOM_PKGLEN_GRAPHIRE, 26202, 16325, 255, 0, TABLETPC }; + { "Wacom ISDv4 9F", WACOM_PKGLEN_GRAPHIRE, 26202, 16325, 255, + 0, TABLETPC, WACOM_INTUOS_RES, WACOM_INTUOS_RES }; static const struct wacom_features wacom_features_0xE2 = - { "Wacom ISDv4 E2", WACOM_PKGLEN_TPC2FG, 26202, 16325, 255, 0, TABLETPC2FG }; + { "Wacom ISDv4 E2", WACOM_PKGLEN_TPC2FG, 26202, 16325, 255, + 0, TABLETPC2FG, WACOM_INTUOS_RES, WACOM_INTUOS_RES }; static const struct wacom_features wacom_features_0xE3 = - { "Wacom ISDv4 E3", WACOM_PKGLEN_TPC2FG, 26202, 16325, 255, 0, TABLETPC2FG }; + { "Wacom ISDv4 E3", WACOM_PKGLEN_TPC2FG, 26202, 16325, 255, + 0, TABLETPC2FG, WACOM_INTUOS_RES, WACOM_INTUOS_RES }; +static const struct wacom_features wacom_features_0xE6 = + { "Wacom ISDv4 E6", WACOM_PKGLEN_TPC2FG, 27760, 15694, 255, + 0, TABLETPC2FG, WACOM_INTUOS_RES, WACOM_INTUOS_RES }; static const struct wacom_features wacom_features_0x47 = - { "Wacom Intuos2 6x8", WACOM_PKGLEN_INTUOS, 20320, 16240, 1023, 31, INTUOS }; -static struct wacom_features wacom_features_0xD0 = - { "Wacom Bamboo 2FG", WACOM_PKGLEN_BBFUN, 14720, 9200, 1023, 63, BAMBOO_PT }; -static struct wacom_features wacom_features_0xD1 = - { "Wacom Bamboo 2FG 4x5", WACOM_PKGLEN_BBFUN, 14720, 9200, 1023, 63, BAMBOO_PT }; -static struct wacom_features wacom_features_0xD2 = - { "Wacom Bamboo Craft", WACOM_PKGLEN_BBFUN, 14720, 9200, 1023, 63, BAMBOO_PT }; -static struct wacom_features wacom_features_0xD3 = - { "Wacom Bamboo 2FG 6x8", WACOM_PKGLEN_BBFUN, 21648, 13530, 1023, 63, BAMBOO_PT }; + { "Wacom Intuos2 6x8", WACOM_PKGLEN_INTUOS, 20320, 16240, 1023, + 31, INTUOS, WACOM_INTUOS_RES, WACOM_INTUOS_RES }; +static const struct wacom_features wacom_features_0xD0 = + { "Wacom Bamboo 2FG", WACOM_PKGLEN_BBFUN, 14720, 9200, 1023, + 63, BAMBOO_PT, WACOM_INTUOS_RES, WACOM_INTUOS_RES }; +static const struct wacom_features wacom_features_0xD1 = + { "Wacom Bamboo 2FG 4x5", WACOM_PKGLEN_BBFUN, 14720, 9200, 1023, + 63, BAMBOO_PT, WACOM_INTUOS_RES, WACOM_INTUOS_RES }; +static const struct wacom_features wacom_features_0xD2 = + { "Wacom Bamboo Craft", WACOM_PKGLEN_BBFUN, 14720, 9200, 1023, + 63, BAMBOO_PT, WACOM_INTUOS_RES, WACOM_INTUOS_RES }; +static const struct wacom_features wacom_features_0xD3 = + { "Wacom Bamboo 2FG 6x8", WACOM_PKGLEN_BBFUN, 21648, 13530, 1023, + 63, BAMBOO_PT, WACOM_INTUOS_RES, WACOM_INTUOS_RES }; static const struct wacom_features wacom_features_0xD4 = - { "Wacom Bamboo Pen", WACOM_PKGLEN_BBFUN, 14720, 9200, 255, 63, BAMBOO_PT }; -static struct wacom_features wacom_features_0xD6 = - { "Wacom BambooPT 2FG 4x5", WACOM_PKGLEN_BBFUN, 14720, 9200, 1023, 63, BAMBOO_PT }; -static struct wacom_features wacom_features_0xD7 = - { "Wacom BambooPT 2FG Small", WACOM_PKGLEN_BBFUN, 14720, 9200, 1023, 63, BAMBOO_PT }; -static struct wacom_features wacom_features_0xD8 = - { "Wacom Bamboo Comic 2FG", WACOM_PKGLEN_BBFUN, 21648, 13530, 1023, 63, BAMBOO_PT }; -static struct wacom_features wacom_features_0xDA = - { "Wacom Bamboo 2FG 4x5 SE", WACOM_PKGLEN_BBFUN, 14720, 9200, 1023, 63, BAMBOO_PT }; + { "Wacom Bamboo Pen", WACOM_PKGLEN_BBFUN, 14720, 9200, 255, + 63, BAMBOO_PT, WACOM_INTUOS_RES, WACOM_INTUOS_RES }; +static const struct wacom_features wacom_features_0xD6 = + { "Wacom BambooPT 2FG 4x5", WACOM_PKGLEN_BBFUN, 14720, 9200, 1023, + 63, BAMBOO_PT, WACOM_INTUOS_RES, WACOM_INTUOS_RES }; +static const struct wacom_features wacom_features_0xD7 = + { "Wacom BambooPT 2FG Small", WACOM_PKGLEN_BBFUN, 14720, 9200, 1023, + 63, BAMBOO_PT, WACOM_INTUOS_RES, WACOM_INTUOS_RES }; +static const struct wacom_features wacom_features_0xD8 = + { "Wacom Bamboo Comic 2FG", WACOM_PKGLEN_BBFUN, 21648, 13530, 1023, + 63, BAMBOO_PT, WACOM_INTUOS_RES, WACOM_INTUOS_RES }; +static const struct wacom_features wacom_features_0xDA = + { "Wacom Bamboo 2FG 4x5 SE", WACOM_PKGLEN_BBFUN, 14720, 9200, 1023, + 63, BAMBOO_PT, WACOM_INTUOS_RES, WACOM_INTUOS_RES }; static struct wacom_features wacom_features_0xDB = - { "Wacom Bamboo 2FG 6x8 SE", WACOM_PKGLEN_BBFUN, 21648, 13530, 1023, 63, BAMBOO_PT }; + { "Wacom Bamboo 2FG 6x8 SE", WACOM_PKGLEN_BBFUN, 21648, 13530, 1023, + 63, BAMBOO_PT, WACOM_INTUOS_RES, WACOM_INTUOS_RES }; static const struct wacom_features wacom_features_0x6004 = - { "ISD-V4", WACOM_PKGLEN_GRAPHIRE, 12800, 8000, 255, 0, TABLETPC }; + { "ISD-V4", WACOM_PKGLEN_GRAPHIRE, 12800, 8000, 255, + 0, TABLETPC, WACOM_INTUOS_RES, WACOM_INTUOS_RES }; #define USB_DEVICE_WACOM(prod) \ USB_DEVICE(USB_VENDOR_ID_WACOM, prod), \ @@ -1474,6 +1564,7 @@ const struct usb_device_id wacom_ids[] = { { USB_DEVICE_WACOM(0x9F) }, { USB_DEVICE_WACOM(0xE2) }, { USB_DEVICE_WACOM(0xE3) }, + { USB_DEVICE_WACOM(0xE6) }, { USB_DEVICE_WACOM(0x47) }, { USB_DEVICE_LENOVO(0x6004) }, { } diff --git a/drivers/input/tablet/wacom_wac.h b/drivers/input/tablet/wacom_wac.h index 835f756b150c..53eb71b68330 100644 --- a/drivers/input/tablet/wacom_wac.h +++ b/drivers/input/tablet/wacom_wac.h @@ -74,6 +74,8 @@ struct wacom_features { int pressure_max; int distance_max; int type; + int x_resolution; + int y_resolution; int device_type; int x_phy; int y_phy; diff --git a/drivers/input/touchscreen/Kconfig b/drivers/input/touchscreen/Kconfig index 434fd800cd24..cabd9e54863f 100644 --- a/drivers/input/touchscreen/Kconfig +++ b/drivers/input/touchscreen/Kconfig @@ -248,6 +248,18 @@ config TOUCHSCREEN_LPC32XX To compile this driver as a module, choose M here: the module will be called lpc32xx_ts. +config TOUCHSCREEN_MAX11801 + tristate "MAX11801 based touchscreens" + depends on I2C + help + Say Y here if you have a MAX11801 based touchscreen + controller. + + If unsure, say N. + + To compile this driver as a module, choose M here: the + module will be called max11801_ts. + config TOUCHSCREEN_MCS5000 tristate "MELFAS MCS-5000 touchscreen" depends on I2C diff --git a/drivers/input/touchscreen/Makefile b/drivers/input/touchscreen/Makefile index ca94098d4c92..282d6f76ae26 100644 --- a/drivers/input/touchscreen/Makefile +++ b/drivers/input/touchscreen/Makefile @@ -27,6 +27,7 @@ obj-$(CONFIG_TOUCHSCREEN_FUJITSU) += fujitsu_ts.o obj-$(CONFIG_TOUCHSCREEN_INEXIO) += inexio.o obj-$(CONFIG_TOUCHSCREEN_INTEL_MID) += intel-mid-touch.o obj-$(CONFIG_TOUCHSCREEN_LPC32XX) += lpc32xx_ts.o +obj-$(CONFIG_TOUCHSCREEN_MAX11801) += max11801_ts.o obj-$(CONFIG_TOUCHSCREEN_MC13783) += mc13783_ts.o obj-$(CONFIG_TOUCHSCREEN_MCS5000) += mcs5000_ts.o obj-$(CONFIG_TOUCHSCREEN_MIGOR) += migor_ts.o diff --git a/drivers/input/touchscreen/ads7846.c b/drivers/input/touchscreen/ads7846.c index c24946f51256..5196861b86ef 100644 --- a/drivers/input/touchscreen/ads7846.c +++ b/drivers/input/touchscreen/ads7846.c @@ -109,6 +109,7 @@ struct ads7846 { u16 pressure_max; bool swap_xy; + bool use_internal; struct ads7846_packet *packet; @@ -281,17 +282,24 @@ struct ser_req { u8 command; u8 ref_off; u16 scratch; - __be16 sample; struct spi_message msg; struct spi_transfer xfer[6]; + /* + * DMA (thus cache coherency maintenance) requires the + * transfer buffers to live in their own cache lines. + */ + __be16 sample ____cacheline_aligned; }; struct ads7845_ser_req { u8 command[3]; - u8 pwrdown[3]; - u8 sample[3]; struct spi_message msg; struct spi_transfer xfer[2]; + /* + * DMA (thus cache coherency maintenance) requires the + * transfer buffers to live in their own cache lines. + */ + u8 sample[3] ____cacheline_aligned; }; static int ads7846_read12_ser(struct device *dev, unsigned command) @@ -300,7 +308,6 @@ static int ads7846_read12_ser(struct device *dev, unsigned command) struct ads7846 *ts = dev_get_drvdata(dev); struct ser_req *req; int status; - int use_internal; req = kzalloc(sizeof *req, GFP_KERNEL); if (!req) @@ -308,11 +315,8 @@ static int ads7846_read12_ser(struct device *dev, unsigned command) spi_message_init(&req->msg); - /* FIXME boards with ads7846 might use external vref instead ... */ - use_internal = (ts->model == 7846); - /* maybe turn on internal vREF, and let it settle */ - if (use_internal) { + if (ts->use_internal) { req->ref_on = REF_ON; req->xfer[0].tx_buf = &req->ref_on; req->xfer[0].len = 1; @@ -324,8 +328,14 @@ static int ads7846_read12_ser(struct device *dev, unsigned command) /* for 1uF, settle for 800 usec; no cap, 100 usec. */ req->xfer[1].delay_usecs = ts->vref_delay_usecs; spi_message_add_tail(&req->xfer[1], &req->msg); + + /* Enable reference voltage */ + command |= ADS_PD10_REF_ON; } + /* Enable ADC in every case */ + command |= ADS_PD10_ADC_ON; + /* take sample */ req->command = (u8) command; req->xfer[2].tx_buf = &req->command; @@ -409,7 +419,7 @@ name ## _show(struct device *dev, struct device_attribute *attr, char *buf) \ { \ struct ads7846 *ts = dev_get_drvdata(dev); \ ssize_t v = ads7846_read12_ser(dev, \ - READ_12BIT_SER(var) | ADS_PD10_ALL_ON); \ + READ_12BIT_SER(var)); \ if (v < 0) \ return v; \ return sprintf(buf, "%u\n", adjust(ts, v)); \ @@ -502,6 +512,7 @@ static int ads784x_hwmon_register(struct spi_device *spi, struct ads7846 *ts) if (!ts->vref_mv) { dev_dbg(&spi->dev, "assuming 2.5V internal vREF\n"); ts->vref_mv = 2500; + ts->use_internal = true; } break; case 7845: @@ -962,6 +973,13 @@ static int __devinit ads7846_setup_pendown(struct spi_device *spi, struct ads784 pdata->gpio_pendown); return err; } + err = gpio_direction_input(pdata->gpio_pendown); + if (err) { + dev_err(&spi->dev, "failed to setup pendown GPIO%d\n", + pdata->gpio_pendown); + gpio_free(pdata->gpio_pendown); + return err; + } ts->gpio_pendown = pdata->gpio_pendown; @@ -1333,8 +1351,7 @@ static int __devinit ads7846_probe(struct spi_device *spi) if (ts->model == 7845) ads7845_read12_ser(&spi->dev, PWRDOWN); else - (void) ads7846_read12_ser(&spi->dev, - READ_12BIT_SER(vaux) | ADS_PD10_ALL_ON); + (void) ads7846_read12_ser(&spi->dev, READ_12BIT_SER(vaux)); err = sysfs_create_group(&spi->dev.kobj, &ads784x_attr_group); if (err) diff --git a/drivers/input/touchscreen/atmel_mxt_ts.c b/drivers/input/touchscreen/atmel_mxt_ts.c index 4012436633b1..1e61387c73ca 100644 --- a/drivers/input/touchscreen/atmel_mxt_ts.c +++ b/drivers/input/touchscreen/atmel_mxt_ts.c @@ -17,7 +17,7 @@ #include <linux/firmware.h> #include <linux/i2c.h> #include <linux/i2c/atmel_mxt_ts.h> -#include <linux/input.h> +#include <linux/input/mt.h> #include <linux/interrupt.h> #include <linux/slab.h> @@ -196,9 +196,12 @@ #define MXT_PRESS (1 << 6) #define MXT_DETECT (1 << 7) +/* Touch orient bits */ +#define MXT_XY_SWITCH (1 << 0) +#define MXT_X_INVERT (1 << 1) +#define MXT_Y_INVERT (1 << 2) + /* Touchscreen absolute values */ -#define MXT_MAX_XC 0x3ff -#define MXT_MAX_YC 0x3ff #define MXT_MAX_AREA 0xff #define MXT_MAX_FINGER 10 @@ -246,6 +249,8 @@ struct mxt_data { struct mxt_info info; struct mxt_finger finger[MXT_MAX_FINGER]; unsigned int irq; + unsigned int max_x; + unsigned int max_y; }; static bool mxt_object_readable(unsigned int type) @@ -499,19 +504,21 @@ static void mxt_input_report(struct mxt_data *data, int single_id) if (!finger[id].status) continue; - input_report_abs(input_dev, ABS_MT_TOUCH_MAJOR, - finger[id].status != MXT_RELEASE ? - finger[id].area : 0); - input_report_abs(input_dev, ABS_MT_POSITION_X, - finger[id].x); - input_report_abs(input_dev, ABS_MT_POSITION_Y, - finger[id].y); - input_mt_sync(input_dev); + input_mt_slot(input_dev, id); + input_mt_report_slot_state(input_dev, MT_TOOL_FINGER, + finger[id].status != MXT_RELEASE); - if (finger[id].status == MXT_RELEASE) - finger[id].status = 0; - else + if (finger[id].status != MXT_RELEASE) { finger_num++; + input_report_abs(input_dev, ABS_MT_TOUCH_MAJOR, + finger[id].area); + input_report_abs(input_dev, ABS_MT_POSITION_X, + finger[id].x); + input_report_abs(input_dev, ABS_MT_POSITION_Y, + finger[id].y); + } else { + finger[id].status = 0; + } } input_report_key(input_dev, BTN_TOUCH, finger_num > 0); @@ -549,8 +556,13 @@ static void mxt_input_touchevent(struct mxt_data *data, if (!(status & (MXT_PRESS | MXT_MOVE))) return; - x = (message->message[1] << 2) | ((message->message[3] & ~0x3f) >> 6); - y = (message->message[2] << 2) | ((message->message[3] & ~0xf3) >> 2); + x = (message->message[1] << 4) | ((message->message[3] >> 4) & 0xf); + y = (message->message[2] << 4) | ((message->message[3] & 0xf)); + if (data->max_x < 1024) + x = x >> 2; + if (data->max_y < 1024) + y = y >> 2; + area = message->message[4]; dev_dbg(dev, "[%d] %s x: %d, y: %d, area: %d\n", id, @@ -804,10 +816,6 @@ static int mxt_initialize(struct mxt_data *data) if (error) return error; - error = mxt_make_highchg(data); - if (error) - return error; - mxt_handle_pdata(data); /* Backup to memory */ @@ -845,6 +853,20 @@ static int mxt_initialize(struct mxt_data *data) return 0; } +static void mxt_calc_resolution(struct mxt_data *data) +{ + unsigned int max_x = data->pdata->x_size - 1; + unsigned int max_y = data->pdata->y_size - 1; + + if (data->pdata->orient & MXT_XY_SWITCH) { + data->max_x = max_y; + data->max_y = max_x; + } else { + data->max_x = max_x; + data->max_y = max_y; + } +} + static ssize_t mxt_object_show(struct device *dev, struct device_attribute *attr, char *buf) { @@ -981,6 +1003,10 @@ static ssize_t mxt_update_fw_store(struct device *dev, enable_irq(data->irq); + error = mxt_make_highchg(data); + if (error) + return error; + return count; } @@ -1052,31 +1078,33 @@ static int __devinit mxt_probe(struct i2c_client *client, input_dev->open = mxt_input_open; input_dev->close = mxt_input_close; + data->client = client; + data->input_dev = input_dev; + data->pdata = pdata; + data->irq = client->irq; + + mxt_calc_resolution(data); + __set_bit(EV_ABS, input_dev->evbit); __set_bit(EV_KEY, input_dev->evbit); __set_bit(BTN_TOUCH, input_dev->keybit); /* For single touch */ input_set_abs_params(input_dev, ABS_X, - 0, MXT_MAX_XC, 0, 0); + 0, data->max_x, 0, 0); input_set_abs_params(input_dev, ABS_Y, - 0, MXT_MAX_YC, 0, 0); + 0, data->max_y, 0, 0); /* For multi touch */ + input_mt_init_slots(input_dev, MXT_MAX_FINGER); input_set_abs_params(input_dev, ABS_MT_TOUCH_MAJOR, 0, MXT_MAX_AREA, 0, 0); input_set_abs_params(input_dev, ABS_MT_POSITION_X, - 0, MXT_MAX_XC, 0, 0); + 0, data->max_x, 0, 0); input_set_abs_params(input_dev, ABS_MT_POSITION_Y, - 0, MXT_MAX_YC, 0, 0); + 0, data->max_y, 0, 0); input_set_drvdata(input_dev, data); - - data->client = client; - data->input_dev = input_dev; - data->pdata = pdata; - data->irq = client->irq; - i2c_set_clientdata(client, data); error = mxt_initialize(data); @@ -1090,6 +1118,10 @@ static int __devinit mxt_probe(struct i2c_client *client, goto err_free_object; } + error = mxt_make_highchg(data); + if (error) + goto err_free_irq; + error = input_register_device(input_dev); if (error) goto err_free_irq; diff --git a/drivers/input/touchscreen/atmel_tsadcc.c b/drivers/input/touchscreen/atmel_tsadcc.c index 3d9b5166ebe9..432c69be6ac6 100644 --- a/drivers/input/touchscreen/atmel_tsadcc.c +++ b/drivers/input/touchscreen/atmel_tsadcc.c @@ -317,7 +317,7 @@ err_unmap_regs: err_release_mem: release_mem_region(res->start, resource_size(res)); err_free_dev: - input_free_device(ts_dev->input); + input_free_device(input_dev); err_free_mem: kfree(ts_dev); return err; diff --git a/drivers/input/touchscreen/h3600_ts_input.c b/drivers/input/touchscreen/h3600_ts_input.c index b4d7f63deff1..211811ae5525 100644 --- a/drivers/input/touchscreen/h3600_ts_input.c +++ b/drivers/input/touchscreen/h3600_ts_input.c @@ -62,7 +62,7 @@ MODULE_LICENSE("GPL"); Programmer has no control over these numbers. TODO there are holes - specifically 1,7,0x0a */ -#define VERSION_ID 0 /* Get Version (request/respose) */ +#define VERSION_ID 0 /* Get Version (request/response) */ #define KEYBD_ID 2 /* Keyboard (event) */ #define TOUCHS_ID 3 /* Touch Screen (event)*/ #define EEPROM_READ_ID 4 /* (request/response) */ @@ -396,34 +396,37 @@ static int h3600ts_connect(struct serio *serio, struct serio_driver *drv) set_GPIO_IRQ_edge(GPIO_BITSY_NPOWER_BUTTON, GPIO_RISING_EDGE); if (request_irq(IRQ_GPIO_BITSY_ACTION_BUTTON, action_button_handler, - IRQF_SHARED | IRQF_DISABLED, "h3600_action", &ts->dev)) { + IRQF_SHARED | IRQF_DISABLED, "h3600_action", ts->dev)) { printk(KERN_ERR "h3600ts.c: Could not allocate Action Button IRQ!\n"); err = -EBUSY; - goto fail2; + goto fail1; } if (request_irq(IRQ_GPIO_BITSY_NPOWER_BUTTON, npower_button_handler, - IRQF_SHARED | IRQF_DISABLED, "h3600_suspend", &ts->dev)) { + IRQF_SHARED | IRQF_DISABLED, "h3600_suspend", ts->dev)) { printk(KERN_ERR "h3600ts.c: Could not allocate Power Button IRQ!\n"); err = -EBUSY; - goto fail3; + goto fail2; } serio_set_drvdata(serio, ts); err = serio_open(serio, drv); if (err) - return err; + goto fail3; //h3600_flite_control(1, 25); /* default brightness */ - input_register_device(ts->dev); + err = input_register_device(ts->dev); + if (err) + goto fail4; return 0; -fail3: free_irq(IRQ_GPIO_BITSY_NPOWER_BUTTON, ts->dev); +fail4: serio_close(serio); +fail3: serio_set_drvdata(serio, NULL); + free_irq(IRQ_GPIO_BITSY_NPOWER_BUTTON, ts->dev); fail2: free_irq(IRQ_GPIO_BITSY_ACTION_BUTTON, ts->dev); -fail1: serio_set_drvdata(serio, NULL); - input_free_device(input_dev); +fail1: input_free_device(input_dev); kfree(ts); return err; } @@ -436,8 +439,8 @@ static void h3600ts_disconnect(struct serio *serio) { struct h3600_dev *ts = serio_get_drvdata(serio); - free_irq(IRQ_GPIO_BITSY_ACTION_BUTTON, &ts->dev); - free_irq(IRQ_GPIO_BITSY_NPOWER_BUTTON, &ts->dev); + free_irq(IRQ_GPIO_BITSY_ACTION_BUTTON, ts->dev); + free_irq(IRQ_GPIO_BITSY_NPOWER_BUTTON, ts->dev); input_get_device(ts->dev); input_unregister_device(ts->dev); serio_close(serio); diff --git a/drivers/input/touchscreen/intel-mid-touch.c b/drivers/input/touchscreen/intel-mid-touch.c index c0307b22d86f..66c96bfc5522 100644 --- a/drivers/input/touchscreen/intel-mid-touch.c +++ b/drivers/input/touchscreen/intel-mid-touch.c @@ -542,7 +542,7 @@ static int __devinit mrstouch_adc_init(struct mrstouch_dev *tsdev) * ADC power on, start, enable PENDET and set loop delay * ADC loop delay is set to 4.5 ms approximately * Loop delay more than this results in jitter in adc readings - * Setting loop delay to 0 (continous loop) in MAXIM stops PENDET + * Setting loop delay to 0 (continuous loop) in MAXIM stops PENDET * interrupt generation sometimes. */ diff --git a/drivers/input/touchscreen/max11801_ts.c b/drivers/input/touchscreen/max11801_ts.c new file mode 100644 index 000000000000..4f2713d92791 --- /dev/null +++ b/drivers/input/touchscreen/max11801_ts.c @@ -0,0 +1,272 @@ +/* + * Driver for MAXI MAX11801 - A Resistive touch screen controller with + * i2c interface + * + * Copyright (C) 2011 Freescale Semiconductor, Inc. + * Author: Zhang Jiejing <jiejing.zhang@freescale.com> + * + * Based on mcs5000_ts.c + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License. + */ + +/* + * This driver aims to support the series of MAXI touch chips max11801 + * through max11803. The main difference between these 4 chips can be + * found in the table below: + * ----------------------------------------------------- + * | CHIP | AUTO MODE SUPPORT(FIFO) | INTERFACE | + * |----------------------------------------------------| + * | max11800 | YES | SPI | + * | max11801 | YES | I2C | + * | max11802 | NO | SPI | + * | max11803 | NO | I2C | + * ------------------------------------------------------ + * + * Currently, this driver only supports max11801. + * + * Data Sheet: + * http://www.maxim-ic.com/datasheet/index.mvp/id/5943 + */ + +#include <linux/module.h> +#include <linux/init.h> +#include <linux/i2c.h> +#include <linux/interrupt.h> +#include <linux/input.h> +#include <linux/slab.h> +#include <linux/bitops.h> + +/* Register Address define */ +#define GENERNAL_STATUS_REG 0x00 +#define GENERNAL_CONF_REG 0x01 +#define MESURE_RES_CONF_REG 0x02 +#define MESURE_AVER_CONF_REG 0x03 +#define ADC_SAMPLE_TIME_CONF_REG 0x04 +#define PANEL_SETUPTIME_CONF_REG 0x05 +#define DELAY_CONVERSION_CONF_REG 0x06 +#define TOUCH_DETECT_PULLUP_CONF_REG 0x07 +#define AUTO_MODE_TIME_CONF_REG 0x08 /* only for max11800/max11801 */ +#define APERTURE_CONF_REG 0x09 /* only for max11800/max11801 */ +#define AUX_MESURE_CONF_REG 0x0a +#define OP_MODE_CONF_REG 0x0b + +/* FIFO is found only in max11800 and max11801 */ +#define FIFO_RD_CMD (0x50 << 1) +#define MAX11801_FIFO_INT (1 << 2) +#define MAX11801_FIFO_OVERFLOW (1 << 3) + +#define XY_BUFSIZE 4 +#define XY_BUF_OFFSET 4 + +#define MAX11801_MAX_X 0xfff +#define MAX11801_MAX_Y 0xfff + +#define MEASURE_TAG_OFFSET 2 +#define MEASURE_TAG_MASK (3 << MEASURE_TAG_OFFSET) +#define EVENT_TAG_OFFSET 0 +#define EVENT_TAG_MASK (3 << EVENT_TAG_OFFSET) +#define MEASURE_X_TAG (0 << MEASURE_TAG_OFFSET) +#define MEASURE_Y_TAG (1 << MEASURE_TAG_OFFSET) + +/* These are the state of touch event state machine */ +enum { + EVENT_INIT, + EVENT_MIDDLE, + EVENT_RELEASE, + EVENT_FIFO_END +}; + +struct max11801_data { + struct i2c_client *client; + struct input_dev *input_dev; +}; + +static u8 read_register(struct i2c_client *client, int addr) +{ + /* XXX: The chip ignores LSB of register address */ + return i2c_smbus_read_byte_data(client, addr << 1); +} + +static int max11801_write_reg(struct i2c_client *client, int addr, int data) +{ + /* XXX: The chip ignores LSB of register address */ + return i2c_smbus_write_byte_data(client, addr << 1, data); +} + +static irqreturn_t max11801_ts_interrupt(int irq, void *dev_id) +{ + struct max11801_data *data = dev_id; + struct i2c_client *client = data->client; + int status, i, ret; + u8 buf[XY_BUFSIZE]; + int x = -1; + int y = -1; + + status = read_register(data->client, GENERNAL_STATUS_REG); + + if (status & (MAX11801_FIFO_INT | MAX11801_FIFO_OVERFLOW)) { + status = read_register(data->client, GENERNAL_STATUS_REG); + + ret = i2c_smbus_read_i2c_block_data(client, FIFO_RD_CMD, + XY_BUFSIZE, buf); + + /* + * We should get 4 bytes buffer that contains X,Y + * and event tag + */ + if (ret < XY_BUFSIZE) + goto out; + + for (i = 0; i < XY_BUFSIZE; i += XY_BUFSIZE / 2) { + if ((buf[i + 1] & MEASURE_TAG_MASK) == MEASURE_X_TAG) + x = (buf[i] << XY_BUF_OFFSET) + + (buf[i + 1] >> XY_BUF_OFFSET); + else if ((buf[i + 1] & MEASURE_TAG_MASK) == MEASURE_Y_TAG) + y = (buf[i] << XY_BUF_OFFSET) + + (buf[i + 1] >> XY_BUF_OFFSET); + } + + if ((buf[1] & EVENT_TAG_MASK) != (buf[3] & EVENT_TAG_MASK)) + goto out; + + switch (buf[1] & EVENT_TAG_MASK) { + case EVENT_INIT: + /* fall through */ + case EVENT_MIDDLE: + input_report_abs(data->input_dev, ABS_X, x); + input_report_abs(data->input_dev, ABS_Y, y); + input_event(data->input_dev, EV_KEY, BTN_TOUCH, 1); + input_sync(data->input_dev); + break; + + case EVENT_RELEASE: + input_event(data->input_dev, EV_KEY, BTN_TOUCH, 0); + input_sync(data->input_dev); + break; + + case EVENT_FIFO_END: + break; + } + } +out: + return IRQ_HANDLED; +} + +static void __devinit max11801_ts_phy_init(struct max11801_data *data) +{ + struct i2c_client *client = data->client; + + /* Average X,Y, take 16 samples, average eight media sample */ + max11801_write_reg(client, MESURE_AVER_CONF_REG, 0xff); + /* X,Y panel setup time set to 20us */ + max11801_write_reg(client, PANEL_SETUPTIME_CONF_REG, 0x11); + /* Rough pullup time (2uS), Fine pullup time (10us) */ + max11801_write_reg(client, TOUCH_DETECT_PULLUP_CONF_REG, 0x10); + /* Auto mode init period = 5ms , scan period = 5ms*/ + max11801_write_reg(client, AUTO_MODE_TIME_CONF_REG, 0xaa); + /* Aperture X,Y set to +- 4LSB */ + max11801_write_reg(client, APERTURE_CONF_REG, 0x33); + /* Enable Power, enable Automode, enable Aperture, enable Average X,Y */ + max11801_write_reg(client, OP_MODE_CONF_REG, 0x36); +} + +static int __devinit max11801_ts_probe(struct i2c_client *client, + const struct i2c_device_id *id) +{ + struct max11801_data *data; + struct input_dev *input_dev; + int error; + + data = kzalloc(sizeof(struct max11801_data), GFP_KERNEL); + input_dev = input_allocate_device(); + if (!data || !input_dev) { + dev_err(&client->dev, "Failed to allocate memory\n"); + error = -ENOMEM; + goto err_free_mem; + } + + data->client = client; + data->input_dev = input_dev; + + input_dev->name = "max11801_ts"; + input_dev->id.bustype = BUS_I2C; + input_dev->dev.parent = &client->dev; + + __set_bit(EV_ABS, input_dev->evbit); + __set_bit(EV_KEY, input_dev->evbit); + __set_bit(BTN_TOUCH, input_dev->keybit); + input_set_abs_params(input_dev, ABS_X, 0, MAX11801_MAX_X, 0, 0); + input_set_abs_params(input_dev, ABS_Y, 0, MAX11801_MAX_Y, 0, 0); + input_set_drvdata(input_dev, data); + + max11801_ts_phy_init(data); + + error = request_threaded_irq(client->irq, NULL, max11801_ts_interrupt, + IRQF_TRIGGER_LOW | IRQF_ONESHOT, + "max11801_ts", data); + if (error) { + dev_err(&client->dev, "Failed to register interrupt\n"); + goto err_free_mem; + } + + error = input_register_device(data->input_dev); + if (error) + goto err_free_irq; + + i2c_set_clientdata(client, data); + return 0; + +err_free_irq: + free_irq(client->irq, data); +err_free_mem: + input_free_device(input_dev); + kfree(data); + return error; +} + +static __devexit int max11801_ts_remove(struct i2c_client *client) +{ + struct max11801_data *data = i2c_get_clientdata(client); + + free_irq(client->irq, data); + input_unregister_device(data->input_dev); + kfree(data); + + return 0; +} + +static const struct i2c_device_id max11801_ts_id[] = { + {"max11801", 0}, + { } +}; +MODULE_DEVICE_TABLE(i2c, max11801_ts_id); + +static struct i2c_driver max11801_ts_driver = { + .driver = { + .name = "max11801_ts", + .owner = THIS_MODULE, + }, + .id_table = max11801_ts_id, + .probe = max11801_ts_probe, + .remove = __devexit_p(max11801_ts_remove), +}; + +static int __init max11801_ts_init(void) +{ + return i2c_add_driver(&max11801_ts_driver); +} + +static void __exit max11801_ts_exit(void) +{ + i2c_del_driver(&max11801_ts_driver); +} + +module_init(max11801_ts_init); +module_exit(max11801_ts_exit); + +MODULE_AUTHOR("Zhang Jiejing <jiejing.zhang@freescale.com>"); +MODULE_DESCRIPTION("Touchscreen driver for MAXI MAX11801 controller"); +MODULE_LICENSE("GPL"); diff --git a/drivers/input/touchscreen/tsc2007.c b/drivers/input/touchscreen/tsc2007.c index 80467f262331..fadc11545b1e 100644 --- a/drivers/input/touchscreen/tsc2007.c +++ b/drivers/input/touchscreen/tsc2007.c @@ -27,9 +27,6 @@ #include <linux/i2c.h> #include <linux/i2c/tsc2007.h> -#define TS_POLL_DELAY 1 /* ms delay between samples */ -#define TS_POLL_PERIOD 1 /* ms delay between samples */ - #define TSC2007_MEASURE_TEMP0 (0x0 << 4) #define TSC2007_MEASURE_AUX (0x2 << 4) #define TSC2007_MEASURE_TEMP1 (0x4 << 4) @@ -75,6 +72,9 @@ struct tsc2007 { u16 model; u16 x_plate_ohms; + u16 max_rt; + unsigned long poll_delay; + unsigned long poll_period; bool pendown; int irq; @@ -156,6 +156,7 @@ static void tsc2007_work(struct work_struct *work) { struct tsc2007 *ts = container_of(to_delayed_work(work), struct tsc2007, work); + bool debounced = false; struct ts_event tc; u32 rt; @@ -184,13 +185,14 @@ static void tsc2007_work(struct work_struct *work) tsc2007_read_values(ts, &tc); rt = tsc2007_calculate_pressure(ts, &tc); - if (rt > MAX_12BIT) { + if (rt > ts->max_rt) { /* * Sample found inconsistent by debouncing or pressure is * beyond the maximum. Don't report it to user space, * repeat at least once more the measurement. */ dev_dbg(&ts->client->dev, "ignored pressure %d\n", rt); + debounced = true; goto out; } @@ -225,9 +227,9 @@ static void tsc2007_work(struct work_struct *work) } out: - if (ts->pendown) + if (ts->pendown || debounced) schedule_delayed_work(&ts->work, - msecs_to_jiffies(TS_POLL_PERIOD)); + msecs_to_jiffies(ts->poll_period)); else enable_irq(ts->irq); } @@ -239,7 +241,7 @@ static irqreturn_t tsc2007_irq(int irq, void *handle) if (!ts->get_pendown_state || likely(ts->get_pendown_state())) { disable_irq_nosync(ts->irq); schedule_delayed_work(&ts->work, - msecs_to_jiffies(TS_POLL_DELAY)); + msecs_to_jiffies(ts->poll_delay)); } if (ts->clear_penirq) @@ -292,6 +294,9 @@ static int __devinit tsc2007_probe(struct i2c_client *client, ts->model = pdata->model; ts->x_plate_ohms = pdata->x_plate_ohms; + ts->max_rt = pdata->max_rt ? : MAX_12BIT; + ts->poll_delay = pdata->poll_delay ? : 1; + ts->poll_period = pdata->poll_period ? : 1; ts->get_pendown_state = pdata->get_pendown_state; ts->clear_penirq = pdata->clear_penirq; @@ -305,9 +310,10 @@ static int __devinit tsc2007_probe(struct i2c_client *client, input_dev->evbit[0] = BIT_MASK(EV_KEY) | BIT_MASK(EV_ABS); input_dev->keybit[BIT_WORD(BTN_TOUCH)] = BIT_MASK(BTN_TOUCH); - input_set_abs_params(input_dev, ABS_X, 0, MAX_12BIT, 0, 0); - input_set_abs_params(input_dev, ABS_Y, 0, MAX_12BIT, 0, 0); - input_set_abs_params(input_dev, ABS_PRESSURE, 0, MAX_12BIT, 0, 0); + input_set_abs_params(input_dev, ABS_X, 0, MAX_12BIT, pdata->fuzzx, 0); + input_set_abs_params(input_dev, ABS_Y, 0, MAX_12BIT, pdata->fuzzy, 0); + input_set_abs_params(input_dev, ABS_PRESSURE, 0, MAX_12BIT, + pdata->fuzzz, 0); if (pdata->init_platform_hw) pdata->init_platform_hw(); diff --git a/drivers/input/touchscreen/ucb1400_ts.c b/drivers/input/touchscreen/ucb1400_ts.c index 028a5363eea1..3b5b5df04dd6 100644 --- a/drivers/input/touchscreen/ucb1400_ts.c +++ b/drivers/input/touchscreen/ucb1400_ts.c @@ -6,7 +6,7 @@ * Copyright: MontaVista Software, Inc. * * Spliting done by: Marek Vasut <marek.vasut@gmail.com> - * If something doesnt work and it worked before spliting, e-mail me, + * If something doesn't work and it worked before spliting, e-mail me, * dont bother Nicolas please ;-) * * This program is free software; you can redistribute it and/or modify diff --git a/drivers/input/touchscreen/wm831x-ts.c b/drivers/input/touchscreen/wm831x-ts.c index 6ae054f8e0aa..9175d49d2546 100644 --- a/drivers/input/touchscreen/wm831x-ts.c +++ b/drivers/input/touchscreen/wm831x-ts.c @@ -68,8 +68,23 @@ struct wm831x_ts { unsigned int pd_irq; bool pressure; bool pen_down; + struct work_struct pd_data_work; }; +static void wm831x_pd_data_work(struct work_struct *work) +{ + struct wm831x_ts *wm831x_ts = + container_of(work, struct wm831x_ts, pd_data_work); + + if (wm831x_ts->pen_down) { + enable_irq(wm831x_ts->data_irq); + dev_dbg(wm831x_ts->wm831x->dev, "IRQ PD->DATA done\n"); + } else { + enable_irq(wm831x_ts->pd_irq); + dev_dbg(wm831x_ts->wm831x->dev, "IRQ DATA->PD done\n"); + } +} + static irqreturn_t wm831x_ts_data_irq(int irq, void *irq_data) { struct wm831x_ts *wm831x_ts = irq_data; @@ -110,6 +125,9 @@ static irqreturn_t wm831x_ts_data_irq(int irq, void *irq_data) } if (!wm831x_ts->pen_down) { + /* Switch from data to pen down */ + dev_dbg(wm831x->dev, "IRQ DATA->PD\n"); + disable_irq_nosync(wm831x_ts->data_irq); /* Don't need data any more */ @@ -128,6 +146,10 @@ static irqreturn_t wm831x_ts_data_irq(int irq, void *irq_data) ABS_PRESSURE, 0); input_report_key(wm831x_ts->input_dev, BTN_TOUCH, 0); + + schedule_work(&wm831x_ts->pd_data_work); + } else { + input_report_key(wm831x_ts->input_dev, BTN_TOUCH, 1); } input_sync(wm831x_ts->input_dev); @@ -141,6 +163,11 @@ static irqreturn_t wm831x_ts_pen_down_irq(int irq, void *irq_data) struct wm831x *wm831x = wm831x_ts->wm831x; int ena = 0; + if (wm831x_ts->pen_down) + return IRQ_HANDLED; + + disable_irq_nosync(wm831x_ts->pd_irq); + /* Start collecting data */ if (wm831x_ts->pressure) ena |= WM831X_TCH_Z_ENA; @@ -149,14 +176,14 @@ static irqreturn_t wm831x_ts_pen_down_irq(int irq, void *irq_data) WM831X_TCH_X_ENA | WM831X_TCH_Y_ENA | WM831X_TCH_Z_ENA, WM831X_TCH_X_ENA | WM831X_TCH_Y_ENA | ena); - input_report_key(wm831x_ts->input_dev, BTN_TOUCH, 1); - input_sync(wm831x_ts->input_dev); - wm831x_set_bits(wm831x, WM831X_INTERRUPT_STATUS_1, WM831X_TCHPD_EINT, WM831X_TCHPD_EINT); wm831x_ts->pen_down = true; - enable_irq(wm831x_ts->data_irq); + + /* Switch from pen down to data */ + dev_dbg(wm831x->dev, "IRQ PD->DATA\n"); + schedule_work(&wm831x_ts->pd_data_work); return IRQ_HANDLED; } @@ -182,13 +209,28 @@ static void wm831x_ts_input_close(struct input_dev *idev) struct wm831x_ts *wm831x_ts = input_get_drvdata(idev); struct wm831x *wm831x = wm831x_ts->wm831x; + /* Shut the controller down, disabling all other functionality too */ wm831x_set_bits(wm831x, WM831X_TOUCH_CONTROL_1, - WM831X_TCH_ENA | WM831X_TCH_CVT_ENA | - WM831X_TCH_X_ENA | WM831X_TCH_Y_ENA | - WM831X_TCH_Z_ENA, 0); + WM831X_TCH_ENA | WM831X_TCH_X_ENA | + WM831X_TCH_Y_ENA | WM831X_TCH_Z_ENA, 0); - if (wm831x_ts->pen_down) + /* Make sure any pending IRQs are done, the above will prevent + * new ones firing. + */ + synchronize_irq(wm831x_ts->data_irq); + synchronize_irq(wm831x_ts->pd_irq); + + /* Make sure the IRQ completion work is quiesced */ + flush_work_sync(&wm831x_ts->pd_data_work); + + /* If we ended up with the pen down then make sure we revert back + * to pen detection state for the next time we start up. + */ + if (wm831x_ts->pen_down) { disable_irq(wm831x_ts->data_irq); + enable_irq(wm831x_ts->pd_irq); + wm831x_ts->pen_down = false; + } } static __devinit int wm831x_ts_probe(struct platform_device *pdev) @@ -198,7 +240,7 @@ static __devinit int wm831x_ts_probe(struct platform_device *pdev) struct wm831x_pdata *core_pdata = dev_get_platdata(pdev->dev.parent); struct wm831x_touch_pdata *pdata = NULL; struct input_dev *input_dev; - int error; + int error, irqf; if (core_pdata) pdata = core_pdata->touch; @@ -212,6 +254,7 @@ static __devinit int wm831x_ts_probe(struct platform_device *pdev) wm831x_ts->wm831x = wm831x; wm831x_ts->input_dev = input_dev; + INIT_WORK(&wm831x_ts->pd_data_work, wm831x_pd_data_work); /* * If we have a direct IRQ use it, otherwise use the interrupt @@ -270,9 +313,14 @@ static __devinit int wm831x_ts_probe(struct platform_device *pdev) wm831x_set_bits(wm831x, WM831X_TOUCH_CONTROL_1, WM831X_TCH_RATE_MASK, 6); + if (pdata && pdata->data_irqf) + irqf = pdata->data_irqf; + else + irqf = IRQF_TRIGGER_HIGH; + error = request_threaded_irq(wm831x_ts->data_irq, NULL, wm831x_ts_data_irq, - IRQF_ONESHOT, + irqf | IRQF_ONESHOT, "Touchscreen data", wm831x_ts); if (error) { dev_err(&pdev->dev, "Failed to request data IRQ %d: %d\n", @@ -281,9 +329,14 @@ static __devinit int wm831x_ts_probe(struct platform_device *pdev) } disable_irq(wm831x_ts->data_irq); + if (pdata && pdata->pd_irqf) + irqf = pdata->pd_irqf; + else + irqf = IRQF_TRIGGER_HIGH; + error = request_threaded_irq(wm831x_ts->pd_irq, NULL, wm831x_ts_pen_down_irq, - IRQF_ONESHOT, + irqf | IRQF_ONESHOT, "Touchscreen pen down", wm831x_ts); if (error) { dev_err(&pdev->dev, "Failed to request pen down IRQ %d: %d\n", diff --git a/drivers/input/touchscreen/wm9705.c b/drivers/input/touchscreen/wm9705.c index 6b5be742c27d..98e61175d3f5 100644 --- a/drivers/input/touchscreen/wm9705.c +++ b/drivers/input/touchscreen/wm9705.c @@ -306,7 +306,7 @@ static int wm9705_acc_enable(struct wm97xx *wm, int enable) dig2 = wm->dig[2]; if (enable) { - /* continous mode */ + /* continuous mode */ if (wm->mach_ops->acc_startup && (ret = wm->mach_ops->acc_startup(wm)) < 0) return ret; diff --git a/drivers/input/touchscreen/wm9712.c b/drivers/input/touchscreen/wm9712.c index 7490b05c3566..2bc2fb801009 100644 --- a/drivers/input/touchscreen/wm9712.c +++ b/drivers/input/touchscreen/wm9712.c @@ -419,7 +419,7 @@ static int wm9712_acc_enable(struct wm97xx *wm, int enable) dig2 = wm->dig[2]; if (enable) { - /* continous mode */ + /* continuous mode */ if (wm->mach_ops->acc_startup) { ret = wm->mach_ops->acc_startup(wm); if (ret < 0) diff --git a/drivers/input/touchscreen/wm9713.c b/drivers/input/touchscreen/wm9713.c index 238b5132712e..73ec99568f12 100644 --- a/drivers/input/touchscreen/wm9713.c +++ b/drivers/input/touchscreen/wm9713.c @@ -431,7 +431,7 @@ static int wm9713_acc_enable(struct wm97xx *wm, int enable) dig3 = wm->dig[2]; if (enable) { - /* continous mode */ + /* continuous mode */ if (wm->mach_ops->acc_startup && (ret = wm->mach_ops->acc_startup(wm)) < 0) return ret; diff --git a/drivers/input/touchscreen/wm97xx-core.c b/drivers/input/touchscreen/wm97xx-core.c index 6b75c9f660ae..5dbe73af2f8f 100644 --- a/drivers/input/touchscreen/wm97xx-core.c +++ b/drivers/input/touchscreen/wm97xx-core.c @@ -335,7 +335,7 @@ static void wm97xx_pen_irq_worker(struct work_struct *work) */ if (!wm->mach_ops->acc_enabled || wm->mach_ops->acc_pen_down) { if (wm->pen_is_down && !pen_was_down) { - /* Data is not availiable immediately on pen down */ + /* Data is not available immediately on pen down */ queue_delayed_work(wm->ts_workq, &wm->ts_reader, 1); } @@ -354,7 +354,7 @@ static void wm97xx_pen_irq_worker(struct work_struct *work) * Codec PENDOWN irq handler * * We have to disable the codec interrupt in the handler because it - * can take upto 1ms to clear the interrupt source. We schedule a task + * can take up to 1ms to clear the interrupt source. We schedule a task * in a work queue to do the actual interaction with the chip. The * interrupt is then enabled again in the slow handler when the source * has been cleared. |
