diff options
author | 2012-04-14 13:18:27 +0200 | |
---|---|---|
committer | 2012-04-14 13:19:04 +0200 | |
commit | 6ac1ef482d7ae0c690f1640bf6eb818ff9a2d91e (patch) | |
tree | 021cc9f6b477146fcebe6f3be4752abfa2ba18a9 /drivers/input | |
parent | uprobes/core: Optimize probe hits with the help of a counter (diff) | |
parent | Merge tag 'v3.4-rc2' into perf/core (diff) | |
download | linux-dev-6ac1ef482d7ae0c690f1640bf6eb818ff9a2d91e.tar.xz linux-dev-6ac1ef482d7ae0c690f1640bf6eb818ff9a2d91e.zip |
Merge branch 'perf/core' into perf/uprobes
Merge in latest upstream (and the latest perf development tree),
to prepare for tooling changes, and also to pick up v3.4 MM
changes that the uprobes code needs to take care of.
Signed-off-by: Ingo Molnar <mingo@kernel.org>
Diffstat (limited to 'drivers/input')
101 files changed, 4620 insertions, 926 deletions
diff --git a/drivers/input/Kconfig b/drivers/input/Kconfig index 001b147c7f95..332597980817 100644 --- a/drivers/input/Kconfig +++ b/drivers/input/Kconfig @@ -25,6 +25,10 @@ config INPUT if INPUT +config INPUT_OF_MATRIX_KEYMAP + depends on USE_OF + bool + config INPUT_FF_MEMLESS tristate "Support for memoryless force-feedback devices" help diff --git a/drivers/input/Makefile b/drivers/input/Makefile index 0c789490e0b3..b173a13a73ca 100644 --- a/drivers/input/Makefile +++ b/drivers/input/Makefile @@ -24,3 +24,4 @@ obj-$(CONFIG_INPUT_TOUCHSCREEN) += touchscreen/ obj-$(CONFIG_INPUT_MISC) += misc/ obj-$(CONFIG_INPUT_APMPOWER) += apm-power.o +obj-$(CONFIG_INPUT_OF_MATRIX_KEYMAP) += of_keymap.o diff --git a/drivers/input/evdev.c b/drivers/input/evdev.c index afc166fcc3d9..4b2e10d5d641 100644 --- a/drivers/input/evdev.c +++ b/drivers/input/evdev.c @@ -20,7 +20,7 @@ #include <linux/slab.h> #include <linux/module.h> #include <linux/init.h> -#include <linux/input.h> +#include <linux/input/mt.h> #include <linux/major.h> #include <linux/device.h> #include "input-compat.h" @@ -46,6 +46,7 @@ struct evdev_client { struct fasync_struct *fasync; struct evdev *evdev; struct list_head node; + int clkid; unsigned int bufsize; struct input_event buffer[]; }; @@ -54,8 +55,12 @@ static struct evdev *evdev_table[EVDEV_MINORS]; static DEFINE_MUTEX(evdev_table_mutex); static void evdev_pass_event(struct evdev_client *client, - struct input_event *event) + struct input_event *event, + ktime_t mono, ktime_t real) { + event->time = ktime_to_timeval(client->clkid == CLOCK_MONOTONIC ? + mono : real); + /* Interrupts are disabled, just acquire the lock. */ spin_lock(&client->buffer_lock); @@ -94,8 +99,11 @@ static void evdev_event(struct input_handle *handle, struct evdev *evdev = handle->private; struct evdev_client *client; struct input_event event; + ktime_t time_mono, time_real; + + time_mono = ktime_get(); + time_real = ktime_sub(time_mono, ktime_get_monotonic_offset()); - do_gettimeofday(&event.time); event.type = type; event.code = code; event.value = value; @@ -103,11 +111,12 @@ static void evdev_event(struct input_handle *handle, rcu_read_lock(); client = rcu_dereference(evdev->grab); + if (client) - evdev_pass_event(client, &event); + evdev_pass_event(client, &event, time_mono, time_real); else list_for_each_entry_rcu(client, &evdev->client_list, node) - evdev_pass_event(client, &event); + evdev_pass_event(client, &event, time_mono, time_real); rcu_read_unlock(); @@ -332,7 +341,7 @@ static ssize_t evdev_write(struct file *file, const char __user *buffer, struct evdev_client *client = file->private_data; struct evdev *evdev = client->evdev; struct input_event event; - int retval; + int retval = 0; if (count < input_event_size()) return -EINVAL; @@ -623,6 +632,28 @@ static int evdev_handle_set_keycode_v2(struct input_dev *dev, void __user *p) return input_set_keycode(dev, &ke); } +static int evdev_handle_mt_request(struct input_dev *dev, + unsigned int size, + int __user *ip) +{ + const struct input_mt_slot *mt = dev->mt; + unsigned int code; + int max_slots; + int i; + + if (get_user(code, &ip[0])) + return -EFAULT; + if (!input_is_mt_value(code)) + return -EINVAL; + + max_slots = (size - sizeof(__u32)) / sizeof(__s32); + for (i = 0; i < dev->mtsize && i < max_slots; i++) + if (put_user(input_mt_get_value(&mt[i], code), &ip[1 + i])) + return -EFAULT; + + return 0; +} + static long evdev_do_ioctl(struct file *file, unsigned int cmd, void __user *p, int compat_mode) { @@ -685,6 +716,14 @@ static long evdev_do_ioctl(struct file *file, unsigned int cmd, else return evdev_ungrab(evdev, client); + case EVIOCSCLOCKID: + if (copy_from_user(&i, p, sizeof(unsigned int))) + return -EFAULT; + if (i != CLOCK_MONOTONIC && i != CLOCK_REALTIME) + return -EINVAL; + client->clkid = i; + return 0; + case EVIOCGKEYCODE: return evdev_handle_get_keycode(dev, p); @@ -708,6 +747,9 @@ static long evdev_do_ioctl(struct file *file, unsigned int cmd, return bits_to_user(dev->propbit, INPUT_PROP_MAX, size, p, compat_mode); + case EVIOCGMTSLOTS(0): + return evdev_handle_mt_request(dev, size, ip); + case EVIOCGKEY(0): return bits_to_user(dev->key, KEY_MAX, size, p, compat_mode); diff --git a/drivers/input/gameport/gameport.c b/drivers/input/gameport/gameport.c index c351aa421f8f..da739d9d1905 100644 --- a/drivers/input/gameport/gameport.c +++ b/drivers/input/gameport/gameport.c @@ -449,7 +449,6 @@ static ssize_t gameport_rebind_driver(struct device *dev, struct device_attribut } else if ((drv = driver_find(buf, &gameport_bus)) != NULL) { gameport_disconnect_port(gameport); error = gameport_bind_driver(gameport, to_gameport_driver(drv)); - put_driver(drv); } else { error = -EINVAL; } diff --git a/drivers/input/input.c b/drivers/input/input.c index 1f78c957a75a..8921c6180c51 100644 --- a/drivers/input/input.c +++ b/drivers/input/input.c @@ -180,7 +180,7 @@ static int input_handle_abs_event(struct input_dev *dev, return INPUT_IGNORE_EVENT; } - is_mt_event = code >= ABS_MT_FIRST && code <= ABS_MT_LAST; + is_mt_event = input_is_mt_value(code); if (!is_mt_event) { pold = &dev->absinfo[code].value; diff --git a/drivers/input/joydev.c b/drivers/input/joydev.c index c24ec2d5f926..26043cc6a016 100644 --- a/drivers/input/joydev.c +++ b/drivers/input/joydev.c @@ -13,7 +13,6 @@ #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt #include <asm/io.h> -#include <asm/system.h> #include <linux/delay.h> #include <linux/errno.h> #include <linux/joystick.h> diff --git a/drivers/input/joystick/amijoy.c b/drivers/input/joystick/amijoy.c index 0bc86204213e..c65b5fa69f1e 100644 --- a/drivers/input/joystick/amijoy.c +++ b/drivers/input/joystick/amijoy.c @@ -35,7 +35,6 @@ #include <linux/interrupt.h> #include <linux/mutex.h> -#include <asm/system.h> #include <asm/amigahw.h> #include <asm/amigaints.h> @@ -108,6 +107,9 @@ static int __init amijoy_init(void) int i, j; int err; + if (!MACH_IS_AMIGA) + return -ENODEV; + for (i = 0; i < 2; i++) { if (!amijoy[i]) continue; diff --git a/drivers/input/joystick/as5011.c b/drivers/input/joystick/as5011.c index 6d6e7418dc21..3063464474bf 100644 --- a/drivers/input/joystick/as5011.c +++ b/drivers/input/joystick/as5011.c @@ -355,14 +355,4 @@ static struct i2c_driver as5011_driver = { .id_table = as5011_id, }; -static int __init as5011_init(void) -{ - return i2c_add_driver(&as5011_driver); -} -module_init(as5011_init); - -static void __exit as5011_exit(void) -{ - i2c_del_driver(&as5011_driver); -} -module_exit(as5011_exit); +module_i2c_driver(as5011_driver); diff --git a/drivers/input/keyboard/Kconfig b/drivers/input/keyboard/Kconfig index cdc385b2cf7d..f354813a13e8 100644 --- a/drivers/input/keyboard/Kconfig +++ b/drivers/input/keyboard/Kconfig @@ -394,6 +394,7 @@ config KEYBOARD_NOMADIK config KEYBOARD_TEGRA tristate "NVIDIA Tegra internal matrix keyboard controller support" depends on ARCH_TEGRA + select INPUT_OF_MATRIX_KEYMAP if USE_OF help Say Y here if you want to use a matrix keyboard connected directly to the internal keyboard controller on Tegra SoCs. @@ -512,7 +513,6 @@ config KEYBOARD_OMAP config KEYBOARD_OMAP4 tristate "TI OMAP4 keypad support" - depends on ARCH_OMAP4 help Say Y here if you want to use the OMAP4 keypad. diff --git a/drivers/input/keyboard/adp5588-keys.c b/drivers/input/keyboard/adp5588-keys.c index 4a7f534cf64b..39ebffac207e 100644 --- a/drivers/input/keyboard/adp5588-keys.c +++ b/drivers/input/keyboard/adp5588-keys.c @@ -653,17 +653,7 @@ static struct i2c_driver adp5588_driver = { .id_table = adp5588_id, }; -static int __init adp5588_init(void) -{ - return i2c_add_driver(&adp5588_driver); -} -module_init(adp5588_init); - -static void __exit adp5588_exit(void) -{ - i2c_del_driver(&adp5588_driver); -} -module_exit(adp5588_exit); +module_i2c_driver(adp5588_driver); MODULE_LICENSE("GPL"); MODULE_AUTHOR("Michael Hennerich <hennerich@blackfin.uclinux.org>"); diff --git a/drivers/input/keyboard/adp5589-keys.c b/drivers/input/keyboard/adp5589-keys.c index 02b5d53031bf..74e603213386 100644 --- a/drivers/input/keyboard/adp5589-keys.c +++ b/drivers/input/keyboard/adp5589-keys.c @@ -1108,17 +1108,7 @@ static struct i2c_driver adp5589_driver = { .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_i2c_driver(adp5589_driver); MODULE_LICENSE("GPL"); MODULE_AUTHOR("Michael Hennerich <hennerich@blackfin.uclinux.org>"); diff --git a/drivers/input/keyboard/gpio_keys.c b/drivers/input/keyboard/gpio_keys.c index ed1ed469d085..62bfce468f9f 100644 --- a/drivers/input/keyboard/gpio_keys.c +++ b/drivers/input/keyboard/gpio_keys.c @@ -28,14 +28,18 @@ #include <linux/gpio.h> #include <linux/of_platform.h> #include <linux/of_gpio.h> +#include <linux/spinlock.h> struct gpio_button_data { - struct gpio_keys_button *button; + const struct gpio_keys_button *button; struct input_dev *input; struct timer_list timer; struct work_struct work; - int timer_debounce; /* in msecs */ + unsigned int timer_debounce; /* in msecs */ + unsigned int irq; + spinlock_t lock; bool disabled; + bool key_pressed; }; struct gpio_keys_drvdata { @@ -114,7 +118,7 @@ static void gpio_keys_disable_button(struct gpio_button_data *bdata) /* * Disable IRQ and possible debouncing timer. */ - disable_irq(gpio_to_irq(bdata->button->gpio)); + disable_irq(bdata->irq); if (bdata->timer_debounce) del_timer_sync(&bdata->timer); @@ -135,7 +139,7 @@ static void gpio_keys_disable_button(struct gpio_button_data *bdata) static void gpio_keys_enable_button(struct gpio_button_data *bdata) { if (bdata->disabled) { - enable_irq(gpio_to_irq(bdata->button->gpio)); + enable_irq(bdata->irq); bdata->disabled = false; } } @@ -195,7 +199,7 @@ static ssize_t gpio_keys_attr_show_helper(struct gpio_keys_drvdata *ddata, * @type: button type (%EV_KEY, %EV_SW) * * This function parses stringified bitmap from @buf and disables/enables - * GPIO buttons accordinly. Returns 0 on success and negative error + * GPIO buttons accordingly. Returns 0 on success and negative error * on failure. */ static ssize_t gpio_keys_attr_store_helper(struct gpio_keys_drvdata *ddata, @@ -320,9 +324,9 @@ static struct attribute_group gpio_keys_attr_group = { .attrs = gpio_keys_attrs, }; -static void gpio_keys_report_event(struct gpio_button_data *bdata) +static void gpio_keys_gpio_report_event(struct gpio_button_data *bdata) { - struct gpio_keys_button *button = bdata->button; + const struct gpio_keys_button *button = bdata->button; struct input_dev *input = bdata->input; unsigned int type = button->type ?: EV_KEY; int state = (gpio_get_value_cansleep(button->gpio) ? 1 : 0) ^ button->active_low; @@ -336,27 +340,26 @@ static void gpio_keys_report_event(struct gpio_button_data *bdata) input_sync(input); } -static void gpio_keys_work_func(struct work_struct *work) +static void gpio_keys_gpio_work_func(struct work_struct *work) { struct gpio_button_data *bdata = container_of(work, struct gpio_button_data, work); - gpio_keys_report_event(bdata); + gpio_keys_gpio_report_event(bdata); } -static void gpio_keys_timer(unsigned long _data) +static void gpio_keys_gpio_timer(unsigned long _data) { - struct gpio_button_data *data = (struct gpio_button_data *)_data; + struct gpio_button_data *bdata = (struct gpio_button_data *)_data; - schedule_work(&data->work); + schedule_work(&bdata->work); } -static irqreturn_t gpio_keys_isr(int irq, void *dev_id) +static irqreturn_t gpio_keys_gpio_isr(int irq, void *dev_id) { struct gpio_button_data *bdata = dev_id; - struct gpio_keys_button *button = bdata->button; - BUG_ON(irq != gpio_to_irq(button->gpio)); + BUG_ON(irq != bdata->irq); if (bdata->timer_debounce) mod_timer(&bdata->timer, @@ -367,50 +370,133 @@ static irqreturn_t gpio_keys_isr(int irq, void *dev_id) return IRQ_HANDLED; } +static void gpio_keys_irq_timer(unsigned long _data) +{ + struct gpio_button_data *bdata = (struct gpio_button_data *)_data; + struct input_dev *input = bdata->input; + unsigned long flags; + + spin_lock_irqsave(&bdata->lock, flags); + if (bdata->key_pressed) { + input_event(input, EV_KEY, bdata->button->code, 0); + input_sync(input); + bdata->key_pressed = false; + } + spin_unlock_irqrestore(&bdata->lock, flags); +} + +static irqreturn_t gpio_keys_irq_isr(int irq, void *dev_id) +{ + struct gpio_button_data *bdata = dev_id; + const struct gpio_keys_button *button = bdata->button; + struct input_dev *input = bdata->input; + unsigned long flags; + + BUG_ON(irq != bdata->irq); + + spin_lock_irqsave(&bdata->lock, flags); + + if (!bdata->key_pressed) { + input_event(input, EV_KEY, button->code, 1); + input_sync(input); + + if (!bdata->timer_debounce) { + input_event(input, EV_KEY, button->code, 0); + input_sync(input); + goto out; + } + + bdata->key_pressed = true; + } + + if (bdata->timer_debounce) + mod_timer(&bdata->timer, + jiffies + msecs_to_jiffies(bdata->timer_debounce)); +out: + spin_unlock_irqrestore(&bdata->lock, flags); + return IRQ_HANDLED; +} + static int __devinit gpio_keys_setup_key(struct platform_device *pdev, + struct input_dev *input, struct gpio_button_data *bdata, - struct gpio_keys_button *button) + const struct gpio_keys_button *button) { const char *desc = button->desc ? button->desc : "gpio_keys"; struct device *dev = &pdev->dev; + irq_handler_t isr; unsigned long irqflags; int irq, error; - setup_timer(&bdata->timer, gpio_keys_timer, (unsigned long)bdata); - INIT_WORK(&bdata->work, gpio_keys_work_func); + bdata->input = input; + bdata->button = button; + spin_lock_init(&bdata->lock); - error = gpio_request(button->gpio, desc); - if (error < 0) { - dev_err(dev, "failed to request GPIO %d, error %d\n", - button->gpio, error); - goto fail2; - } + if (gpio_is_valid(button->gpio)) { - error = gpio_direction_input(button->gpio); - if (error < 0) { - dev_err(dev, "failed to configure" - " direction for GPIO %d, error %d\n", - button->gpio, error); - goto fail3; - } + error = gpio_request(button->gpio, desc); + if (error < 0) { + dev_err(dev, "Failed to request GPIO %d, error %d\n", + button->gpio, error); + return error; + } - if (button->debounce_interval) { - error = gpio_set_debounce(button->gpio, - button->debounce_interval * 1000); - /* use timer if gpiolib doesn't provide debounce */ - if (error < 0) - bdata->timer_debounce = button->debounce_interval; - } + error = gpio_direction_input(button->gpio); + if (error < 0) { + dev_err(dev, + "Failed to configure direction for GPIO %d, error %d\n", + button->gpio, error); + goto fail; + } - irq = gpio_to_irq(button->gpio); - if (irq < 0) { - error = irq; - dev_err(dev, "Unable to get irq number for GPIO %d, error %d\n", - button->gpio, error); - goto fail3; + if (button->debounce_interval) { + error = gpio_set_debounce(button->gpio, + button->debounce_interval * 1000); + /* use timer if gpiolib doesn't provide debounce */ + if (error < 0) + bdata->timer_debounce = + button->debounce_interval; + } + + irq = gpio_to_irq(button->gpio); + if (irq < 0) { + error = irq; + dev_err(dev, + "Unable to get irq number for GPIO %d, error %d\n", + button->gpio, error); + goto fail; + } + bdata->irq = irq; + + INIT_WORK(&bdata->work, gpio_keys_gpio_work_func); + setup_timer(&bdata->timer, + gpio_keys_gpio_timer, (unsigned long)bdata); + + isr = gpio_keys_gpio_isr; + irqflags = IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING; + + } else { + if (!button->irq) { + dev_err(dev, "No IRQ specified\n"); + return -EINVAL; + } + bdata->irq = button->irq; + + if (button->type && button->type != EV_KEY) { + dev_err(dev, "Only EV_KEY allowed for IRQ buttons.\n"); + return -EINVAL; + } + + bdata->timer_debounce = button->debounce_interval; + setup_timer(&bdata->timer, + gpio_keys_irq_timer, (unsigned long)bdata); + + isr = gpio_keys_irq_isr; + irqflags = 0; } - irqflags = IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING; + input_set_capability(input, button->type ?: EV_KEY, button->code); + /* * If platform has specified that the button can be disabled, * we don't want it to share the interrupt line. @@ -418,18 +504,19 @@ static int __devinit gpio_keys_setup_key(struct platform_device *pdev, if (!button->can_disable) irqflags |= IRQF_SHARED; - error = request_threaded_irq(irq, NULL, gpio_keys_isr, irqflags, desc, bdata); + error = request_any_context_irq(bdata->irq, isr, irqflags, desc, bdata); if (error < 0) { dev_err(dev, "Unable to claim irq %d; error %d\n", - irq, error); - goto fail3; + bdata->irq, error); + goto fail; } return 0; -fail3: - gpio_free(button->gpio); -fail2: +fail: + if (gpio_is_valid(button->gpio)) + gpio_free(button->gpio); + return error; } @@ -547,9 +634,19 @@ static int gpio_keys_get_devtree_pdata(struct device *dev, #endif +static void gpio_remove_key(struct gpio_button_data *bdata) +{ + free_irq(bdata->irq, bdata); + if (bdata->timer_debounce) + del_timer_sync(&bdata->timer); + cancel_work_sync(&bdata->work); + if (gpio_is_valid(bdata->button->gpio)) + gpio_free(bdata->button->gpio); +} + static int __devinit gpio_keys_probe(struct platform_device *pdev) { - struct gpio_keys_platform_data *pdata = pdev->dev.platform_data; + const struct gpio_keys_platform_data *pdata = pdev->dev.platform_data; struct gpio_keys_drvdata *ddata; struct device *dev = &pdev->dev; struct gpio_keys_platform_data alt_pdata; @@ -599,21 +696,15 @@ static int __devinit gpio_keys_probe(struct platform_device *pdev) __set_bit(EV_REP, input->evbit); for (i = 0; i < pdata->nbuttons; i++) { - struct gpio_keys_button *button = &pdata->buttons[i]; + const struct gpio_keys_button *button = &pdata->buttons[i]; struct gpio_button_data *bdata = &ddata->data[i]; - unsigned int type = button->type ?: EV_KEY; - - bdata->input = input; - bdata->button = button; - error = gpio_keys_setup_key(pdev, bdata, button); + error = gpio_keys_setup_key(pdev, input, bdata, button); if (error) goto fail2; if (button->wakeup) wakeup = 1; - - input_set_capability(input, type, button->code); } error = sysfs_create_group(&pdev->dev.kobj, &gpio_keys_attr_group); @@ -630,9 +721,12 @@ static int __devinit gpio_keys_probe(struct platform_device *pdev) goto fail3; } - /* get current state of buttons */ - for (i = 0; i < pdata->nbuttons; i++) - gpio_keys_report_event(&ddata->data[i]); + /* get current state of buttons that are connected to GPIOs */ + for (i = 0; i < pdata->nbuttons; i++) { + struct gpio_button_data *bdata = &ddata->data[i]; + if (gpio_is_valid(bdata->button->gpio)) + gpio_keys_gpio_report_event(bdata); + } input_sync(input); device_init_wakeup(&pdev->dev, wakeup); @@ -642,13 +736,8 @@ static int __devinit gpio_keys_probe(struct platform_device *pdev) fail3: sysfs_remove_group(&pdev->dev.kobj, &gpio_keys_attr_group); fail2: - while (--i >= 0) { - free_irq(gpio_to_irq(pdata->buttons[i].gpio), &ddata->data[i]); - if (ddata->data[i].timer_debounce) - del_timer_sync(&ddata->data[i].timer); - cancel_work_sync(&ddata->data[i].work); - gpio_free(pdata->buttons[i].gpio); - } + while (--i >= 0) + gpio_remove_key(&ddata->data[i]); platform_set_drvdata(pdev, NULL); fail1: @@ -671,14 +760,8 @@ static int __devexit gpio_keys_remove(struct platform_device *pdev) device_init_wakeup(&pdev->dev, 0); - for (i = 0; i < ddata->n_buttons; i++) { - int irq = gpio_to_irq(ddata->data[i].button->gpio); - free_irq(irq, &ddata->data[i]); - if (ddata->data[i].timer_debounce) - del_timer_sync(&ddata->data[i].timer); - cancel_work_sync(&ddata->data[i].work); - gpio_free(ddata->data[i].button->gpio); - } + for (i = 0; i < ddata->n_buttons; i++) + gpio_remove_key(&ddata->data[i]); input_unregister_device(input); @@ -703,11 +786,9 @@ static int gpio_keys_suspend(struct device *dev) if (device_may_wakeup(dev)) { for (i = 0; i < ddata->n_buttons; i++) { - struct gpio_keys_button *button = ddata->data[i].button; - if (button->wakeup) { - int irq = gpio_to_irq(button->gpio); - enable_irq_wake(irq); - } + struct gpio_button_data *bdata = &ddata->data[i]; + if (bdata->button->wakeup) + enable_irq_wake(bdata->irq); } } @@ -720,14 +801,12 @@ static int gpio_keys_resume(struct device *dev) int i; for (i = 0; i < ddata->n_buttons; i++) { + struct gpio_button_data *bdata = &ddata->data[i]; + if (bdata->button->wakeup && device_may_wakeup(dev)) + disable_irq_wake(bdata->irq); - struct gpio_keys_button *button = ddata->data[i].button; - if (button->wakeup && device_may_wakeup(dev)) { - int irq = gpio_to_irq(button->gpio); - disable_irq_wake(irq); - } - - gpio_keys_report_event(&ddata->data[i]); + if (gpio_is_valid(bdata->button->gpio)) + gpio_keys_gpio_report_event(bdata); } input_sync(ddata->input); diff --git a/drivers/input/keyboard/jornada720_kbd.c b/drivers/input/keyboard/jornada720_kbd.c index eeafc30b207b..9d639fa1afbd 100644 --- a/drivers/input/keyboard/jornada720_kbd.c +++ b/drivers/input/keyboard/jornada720_kbd.c @@ -27,6 +27,7 @@ #include <mach/jornada720.h> #include <mach/hardware.h> +#include <mach/irqs.h> MODULE_AUTHOR("Kristoffer Ericson <Kristoffer.Ericson@gmail.com>"); MODULE_DESCRIPTION("HP Jornada 710/720/728 keyboard driver"); diff --git a/drivers/input/keyboard/lm8323.c b/drivers/input/keyboard/lm8323.c index 21823bfc7911..39ac2787e275 100644 --- a/drivers/input/keyboard/lm8323.c +++ b/drivers/input/keyboard/lm8323.c @@ -851,17 +851,7 @@ static struct i2c_driver lm8323_i2c_driver = { }; MODULE_DEVICE_TABLE(i2c, lm8323_id); -static int __init lm8323_init(void) -{ - return i2c_add_driver(&lm8323_i2c_driver); -} -module_init(lm8323_init); - -static void __exit lm8323_exit(void) -{ - i2c_del_driver(&lm8323_i2c_driver); -} -module_exit(lm8323_exit); +module_i2c_driver(lm8323_i2c_driver); MODULE_AUTHOR("Timo O. Karjalainen <timo.o.karjalainen@nokia.com>"); MODULE_AUTHOR("Daniel Stone"); diff --git a/drivers/input/keyboard/max7359_keypad.c b/drivers/input/keyboard/max7359_keypad.c index 5afe35ad24d3..8edada8ae712 100644 --- a/drivers/input/keyboard/max7359_keypad.c +++ b/drivers/input/keyboard/max7359_keypad.c @@ -316,17 +316,7 @@ static struct i2c_driver max7359_i2c_driver = { .id_table = max7359_ids, }; -static int __init max7359_init(void) -{ - return i2c_add_driver(&max7359_i2c_driver); -} -module_init(max7359_init); - -static void __exit max7359_exit(void) -{ - i2c_del_driver(&max7359_i2c_driver); -} -module_exit(max7359_exit); +module_i2c_driver(max7359_i2c_driver); MODULE_AUTHOR("Kim Kyuwon <q1.kim@samsung.com>"); MODULE_DESCRIPTION("MAX7359 Key Switch Controller Driver"); diff --git a/drivers/input/keyboard/mcs_touchkey.c b/drivers/input/keyboard/mcs_touchkey.c index af1aab324a4c..64a0ca4c92f3 100644 --- a/drivers/input/keyboard/mcs_touchkey.c +++ b/drivers/input/keyboard/mcs_touchkey.c @@ -274,18 +274,7 @@ static struct i2c_driver mcs_touchkey_driver = { .id_table = mcs_touchkey_id, }; -static int __init mcs_touchkey_init(void) -{ - return i2c_add_driver(&mcs_touchkey_driver); -} - -static void __exit mcs_touchkey_exit(void) -{ - i2c_del_driver(&mcs_touchkey_driver); -} - -module_init(mcs_touchkey_init); -module_exit(mcs_touchkey_exit); +module_i2c_driver(mcs_touchkey_driver); /* Module information */ MODULE_AUTHOR("Joonyoung Shim <jy0922.shim@samsung.com>"); diff --git a/drivers/input/keyboard/mpr121_touchkey.c b/drivers/input/keyboard/mpr121_touchkey.c index 1c1615d9a7f9..caa218a51b5a 100644 --- a/drivers/input/keyboard/mpr121_touchkey.c +++ b/drivers/input/keyboard/mpr121_touchkey.c @@ -330,17 +330,7 @@ static struct i2c_driver mpr_touchkey_driver = { .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_i2c_driver(mpr_touchkey_driver); MODULE_LICENSE("GPL"); MODULE_AUTHOR("Zhang Jiejing <jiejing.zhang@freescale.com>"); diff --git a/drivers/input/keyboard/nomadik-ske-keypad.c b/drivers/input/keyboard/nomadik-ske-keypad.c index e35566aa102f..101e245944e7 100644 --- a/drivers/input/keyboard/nomadik-ske-keypad.c +++ b/drivers/input/keyboard/nomadik-ske-keypad.c @@ -88,7 +88,7 @@ static void ske_keypad_set_bits(struct ske_keypad *keypad, u16 addr, * * Enable Multi key press detection, auto scan mode */ -static int __devinit ske_keypad_chip_init(struct ske_keypad *keypad) +static int __init ske_keypad_chip_init(struct ske_keypad *keypad) { u32 value; int timeout = 50; @@ -198,7 +198,7 @@ static irqreturn_t ske_keypad_irq(int irq, void *dev_id) return IRQ_HANDLED; } -static int __devinit ske_keypad_probe(struct platform_device *pdev) +static int __init ske_keypad_probe(struct platform_device *pdev) { const struct ske_keypad_platform_data *plat = pdev->dev.platform_data; struct ske_keypad *keypad; @@ -344,7 +344,7 @@ static int __devexit ske_keypad_remove(struct platform_device *pdev) return 0; } -#ifdef CONFIG_PM +#ifdef CONFIG_PM_SLEEP static int ske_keypad_suspend(struct device *dev) { struct platform_device *pdev = to_platform_device(dev); @@ -372,22 +372,17 @@ static int ske_keypad_resume(struct device *dev) return 0; } - -static const struct dev_pm_ops ske_keypad_dev_pm_ops = { - .suspend = ske_keypad_suspend, - .resume = ske_keypad_resume, -}; #endif +static SIMPLE_DEV_PM_OPS(ske_keypad_dev_pm_ops, + ske_keypad_suspend, ske_keypad_resume); + static struct platform_driver ske_keypad_driver = { .driver = { .name = "nmk-ske-keypad", .owner = THIS_MODULE, -#ifdef CONFIG_PM .pm = &ske_keypad_dev_pm_ops, -#endif }, - .probe = ske_keypad_probe, .remove = __devexit_p(ske_keypad_remove), }; diff --git a/drivers/input/keyboard/omap4-keypad.c b/drivers/input/keyboard/omap4-keypad.c index d5c5d77f4b82..e809ac095a38 100644 --- a/drivers/input/keyboard/omap4-keypad.c +++ b/drivers/input/keyboard/omap4-keypad.c @@ -31,7 +31,7 @@ #include <linux/slab.h> #include <linux/pm_runtime.h> -#include <plat/omap4-keypad.h> +#include <linux/platform_data/omap4-keypad.h> /* OMAP4 registers */ #define OMAP4_KBD_REVISION 0x00 diff --git a/drivers/input/keyboard/qt1070.c b/drivers/input/keyboard/qt1070.c index b21bf5b876bb..0b7b2f891752 100644 --- a/drivers/input/keyboard/qt1070.c +++ b/drivers/input/keyboard/qt1070.c @@ -258,17 +258,7 @@ static struct i2c_driver qt1070_driver = { .remove = __devexit_p(qt1070_remove), }; -static int __init qt1070_init(void) -{ - return i2c_add_driver(&qt1070_driver); -} -module_init(qt1070_init); - -static void __exit qt1070_exit(void) -{ - i2c_del_driver(&qt1070_driver); -} -module_exit(qt1070_exit); +module_i2c_driver(qt1070_driver); MODULE_AUTHOR("Bo Shen <voice.shen@atmel.com>"); MODULE_DESCRIPTION("Driver for AT42QT1070 QTouch sensor"); diff --git a/drivers/input/keyboard/qt2160.c b/drivers/input/keyboard/qt2160.c index fac695157e8a..e7a5e36e1203 100644 --- a/drivers/input/keyboard/qt2160.c +++ b/drivers/input/keyboard/qt2160.c @@ -379,17 +379,7 @@ static struct i2c_driver qt2160_driver = { .remove = __devexit_p(qt2160_remove), }; -static int __init qt2160_init(void) -{ - return i2c_add_driver(&qt2160_driver); -} -module_init(qt2160_init); - -static void __exit qt2160_cleanup(void) -{ - i2c_del_driver(&qt2160_driver); -} -module_exit(qt2160_cleanup); +module_i2c_driver(qt2160_driver); MODULE_AUTHOR("Raphael Derosso Pereira <raphaelpereira@gmail.com>"); MODULE_DESCRIPTION("Driver for AT42QT2160 Touch Sensor"); diff --git a/drivers/input/keyboard/samsung-keypad.c b/drivers/input/keyboard/samsung-keypad.c index 17ba7f9f80f3..2391ae884fee 100644 --- a/drivers/input/keyboard/samsung-keypad.c +++ b/drivers/input/keyboard/samsung-keypad.c @@ -175,7 +175,7 @@ static irqreturn_t samsung_keypad_irq(int irq, void *dev_id) } while (key_down && !keypad->stopped); - pm_runtime_put_sync(&keypad->pdev->dev); + pm_runtime_put(&keypad->pdev->dev); return IRQ_HANDLED; } @@ -199,7 +199,7 @@ static void samsung_keypad_start(struct samsung_keypad *keypad) /* KEYIFCOL reg clear. */ writel(0, keypad->base + SAMSUNG_KEYIFCOL); - pm_runtime_put_sync(&keypad->pdev->dev); + pm_runtime_put(&keypad->pdev->dev); } static void samsung_keypad_stop(struct samsung_keypad *keypad) @@ -229,7 +229,7 @@ static void samsung_keypad_stop(struct samsung_keypad *keypad) */ enable_irq(keypad->irq); - pm_runtime_put_sync(&keypad->pdev->dev); + pm_runtime_put(&keypad->pdev->dev); } static int samsung_keypad_open(struct input_dev *input_dev) diff --git a/drivers/input/keyboard/spear-keyboard.c b/drivers/input/keyboard/spear-keyboard.c index c88bd63dc9cc..3b6b528f02fd 100644 --- a/drivers/input/keyboard/spear-keyboard.c +++ b/drivers/input/keyboard/spear-keyboard.c @@ -50,6 +50,7 @@ #define ROW_MASK 0xF0 #define COLUMN_MASK 0x0F #define ROW_SHIFT 4 +#define KEY_MATRIX_SHIFT 6 struct spear_kbd { struct input_dev *input; @@ -57,6 +58,7 @@ struct spear_kbd { void __iomem *io_base; struct clk *clk; unsigned int irq; + unsigned int mode; unsigned short last_key; unsigned short keycodes[256]; }; @@ -106,7 +108,8 @@ static int spear_kbd_open(struct input_dev *dev) return error; /* program keyboard */ - val = SCAN_RATE_80 | MODE_KEYBOARD | PCLK_FREQ_MSK; + val = SCAN_RATE_80 | MODE_KEYBOARD | PCLK_FREQ_MSK | + (kbd->mode << KEY_MATRIX_SHIFT); writew(val, kbd->io_base + MODE_REG); writeb(1, kbd->io_base + STATUS_REG); @@ -176,6 +179,8 @@ static int __devinit spear_kbd_probe(struct platform_device *pdev) kbd->input = input_dev; kbd->irq = irq; + kbd->mode = pdata->mode; + kbd->res = request_mem_region(res->start, resource_size(res), pdev->name); if (!kbd->res) { @@ -308,22 +313,17 @@ static int spear_kbd_resume(struct device *dev) return 0; } - -static const struct dev_pm_ops spear_kbd_pm_ops = { - .suspend = spear_kbd_suspend, - .resume = spear_kbd_resume, -}; #endif +static SIMPLE_DEV_PM_OPS(spear_kbd_pm_ops, spear_kbd_suspend, spear_kbd_resume); + static struct platform_driver spear_kbd_driver = { .probe = spear_kbd_probe, .remove = __devexit_p(spear_kbd_remove), .driver = { .name = "keyboard", .owner = THIS_MODULE, -#ifdef CONFIG_PM .pm = &spear_kbd_pm_ops, -#endif }, }; module_platform_driver(spear_kbd_driver); diff --git a/drivers/input/keyboard/tegra-kbc.c b/drivers/input/keyboard/tegra-kbc.c index a136e2e832be..fe4ac95ca6c8 100644 --- a/drivers/input/keyboard/tegra-kbc.c +++ b/drivers/input/keyboard/tegra-kbc.c @@ -48,6 +48,7 @@ #define KBC_FIFO_TH_CNT_SHIFT(cnt) (cnt << 14) #define KBC_DEBOUNCE_CNT_SHIFT(cnt) (cnt << 4) #define KBC_CONTROL_FIFO_CNT_INT_EN (1 << 3) +#define KBC_CONTROL_KEYPRESS_INT_EN (1 << 1) #define KBC_CONTROL_KBC_EN (1 << 0) /* KBC Interrupt Register */ @@ -356,6 +357,18 @@ static void tegra_kbc_set_fifo_interrupt(struct tegra_kbc *kbc, bool enable) writel(val, kbc->mmio + KBC_CONTROL_0); } +static void tegra_kbc_set_keypress_interrupt(struct tegra_kbc *kbc, bool enable) +{ + u32 val; + + val = readl(kbc->mmio + KBC_CONTROL_0); + if (enable) + val |= KBC_CONTROL_KEYPRESS_INT_EN; + else + val &= ~KBC_CONTROL_KEYPRESS_INT_EN; + writel(val, kbc->mmio + KBC_CONTROL_0); +} + static void tegra_kbc_keypress_timer(unsigned long data) { struct tegra_kbc *kbc = (struct tegra_kbc *)data; @@ -455,10 +468,18 @@ static void tegra_kbc_config_pins(struct tegra_kbc *kbc) row_cfg &= ~r_mask; col_cfg &= ~c_mask; - if (pdata->pin_cfg[i].is_row) + switch (pdata->pin_cfg[i].type) { + case PIN_CFG_ROW: row_cfg |= ((pdata->pin_cfg[i].num << 1) | 1) << r_shft; - else + break; + + case PIN_CFG_COL: col_cfg |= ((pdata->pin_cfg[i].num << 1) | 1) << c_shft; + break; + + case PIN_CFG_IGNORE: + break; + } writel(row_cfg, kbc->mmio + r_offs); writel(col_cfg, kbc->mmio + c_offs); @@ -563,7 +584,8 @@ tegra_kbc_check_pin_cfg(const struct tegra_kbc_platform_data *pdata, for (i = 0; i < KBC_MAX_GPIO; i++) { const struct tegra_kbc_pin_cfg *pin_cfg = &pdata->pin_cfg[i]; - if (pin_cfg->is_row) { + switch (pin_cfg->type) { + case PIN_CFG_ROW: if (pin_cfg->num >= KBC_MAX_ROW) { dev_err(dev, "pin_cfg[%d]: invalid row number %d\n", @@ -571,13 +593,25 @@ tegra_kbc_check_pin_cfg(const struct tegra_kbc_platform_data *pdata, return false; } (*num_rows)++; - } else { + break; + + case PIN_CFG_COL: if (pin_cfg->num >= KBC_MAX_COL) { dev_err(dev, "pin_cfg[%d]: invalid column number %d\n", i, pin_cfg->num); return false; } + break; + + case PIN_CFG_IGNORE: + break; + + default: + dev_err(dev, + "pin_cfg[%d]: invalid entry type %d\n", + pin_cfg->type, pin_cfg->num); + return false; } } @@ -590,6 +624,8 @@ tegra_kbc_dt_parse_pdata(struct platform_device *pdev) { struct tegra_kbc_platform_data *pdata; struct device_node *np = pdev->dev.of_node; + u32 prop; + int i; if (!np) return NULL; @@ -598,16 +634,16 @@ tegra_kbc_dt_parse_pdata(struct platform_device *pdev) if (!pdata) return NULL; - if (!of_property_read_u32(np, "debounce-delay", &prop)) + if (!of_property_read_u32(np, "nvidia,debounce-delay-ms", &prop)) pdata->debounce_cnt = prop; - if (!of_property_read_u32(np, "repeat-delay", &prop)) + if (!of_property_read_u32(np, "nvidia,repeat-delay-ms", &prop)) pdata->repeat_cnt = prop; - if (of_find_property(np, "needs-ghost-filter", NULL)) + if (of_find_property(np, "nvidia,needs-ghost-filter", NULL)) pdata->use_ghost_filter = true; - if (of_find_property(np, "wakeup-source", NULL)) + if (of_find_property(np, "nvidia,wakeup-source", NULL)) pdata->wakeup = true; /* @@ -616,14 +652,18 @@ tegra_kbc_dt_parse_pdata(struct platform_device *pdev) */ for (i = 0; i < KBC_MAX_ROW; i++) { pdata->pin_cfg[i].num = i; - pdata->pin_cfg[i].is_row = true; + pdata->pin_cfg[i].type = PIN_CFG_ROW; } for (i = 0; i < KBC_MAX_COL; i++) { pdata->pin_cfg[KBC_MAX_ROW + i].num = i; - pdata->pin_cfg[KBC_MAX_ROW + i].is_row = false; + pdata->pin_cfg[KBC_MAX_ROW + i].type = PIN_CFG_COL; } + pdata->keymap_data = matrix_keyboard_of_fill_keymap(np, "linux,keymap"); + + /* FIXME: Add handling of linux,fn-keymap here */ + return pdata; } #else @@ -759,6 +799,9 @@ static int __devinit tegra_kbc_probe(struct platform_device *pdev) platform_set_drvdata(pdev, kbc); device_init_wakeup(&pdev->dev, pdata->wakeup); + if (!pdev->dev.platform_data) + matrix_keyboard_of_free_keymap(pdata->keymap_data); + return 0; err_free_irq: @@ -773,8 +816,10 @@ err_free_mem: input_free_device(input_dev); kfree(kbc); err_free_pdata: - if (!pdev->dev.platform_data) + if (!pdev->dev.platform_data) { + matrix_keyboard_of_free_keymap(pdata->keymap_data); kfree(pdata); + } return err; } @@ -831,6 +876,8 @@ static int tegra_kbc_suspend(struct device *dev) msleep(30); kbc->keypress_caused_wake = false; + /* Enable keypress interrupt before going into suspend. */ + tegra_kbc_set_keypress_interrupt(kbc, true); enable_irq(kbc->irq); enable_irq_wake(kbc->irq); } else { @@ -852,6 +899,8 @@ static int tegra_kbc_resume(struct device *dev) if (device_may_wakeup(&pdev->dev)) { disable_irq_wake(kbc->irq); tegra_kbc_setup_wakekeys(kbc, false); + /* We will use fifo interrupts for key detection. */ + tegra_kbc_set_keypress_interrupt(kbc, false); /* Restore the resident time of continuous polling mode. */ writel(kbc->cp_to_wkup_dly, kbc->mmio + KBC_TO_CNT_0); diff --git a/drivers/input/misc/88pm860x_onkey.c b/drivers/input/misc/88pm860x_onkey.c index f2e0cbc5ab64..f9ce1835e4d7 100644 --- a/drivers/input/misc/88pm860x_onkey.c +++ b/drivers/input/misc/88pm860x_onkey.c @@ -105,6 +105,8 @@ static int __devinit pm860x_onkey_probe(struct platform_device *pdev) } platform_set_drvdata(pdev, info); + device_init_wakeup(&pdev->dev, 1); + return 0; out_irq: @@ -129,10 +131,34 @@ static int __devexit pm860x_onkey_remove(struct platform_device *pdev) return 0; } +#ifdef CONFIG_PM_SLEEP +static int pm860x_onkey_suspend(struct device *dev) +{ + struct platform_device *pdev = to_platform_device(dev); + struct pm860x_chip *chip = dev_get_drvdata(pdev->dev.parent); + + if (device_may_wakeup(dev)) + chip->wakeup_flag |= 1 << PM8607_IRQ_ONKEY; + return 0; +} +static int pm860x_onkey_resume(struct device *dev) +{ + struct platform_device *pdev = to_platform_device(dev); + struct pm860x_chip *chip = dev_get_drvdata(pdev->dev.parent); + + if (device_may_wakeup(dev)) + chip->wakeup_flag &= ~(1 << PM8607_IRQ_ONKEY); + return 0; +} +#endif + +static SIMPLE_DEV_PM_OPS(pm860x_onkey_pm_ops, pm860x_onkey_suspend, pm860x_onkey_resume); + static struct platform_driver pm860x_onkey_driver = { .driver = { .name = "88pm860x-onkey", .owner = THIS_MODULE, + .pm = &pm860x_onkey_pm_ops, }, .probe = pm860x_onkey_probe, .remove = __devexit_p(pm860x_onkey_remove), diff --git a/drivers/input/misc/Kconfig b/drivers/input/misc/Kconfig index 7b46781c30c9..2d787796bf50 100644 --- a/drivers/input/misc/Kconfig +++ b/drivers/input/misc/Kconfig @@ -134,6 +134,18 @@ config INPUT_MAX8925_ONKEY To compile this driver as a module, choose M here: the module will be called max8925_onkey. +config INPUT_MAX8997_HAPTIC + tristate "MAXIM MAX8997 haptic controller support" + depends on HAVE_PWM && MFD_MAX8997 + select INPUT_FF_MEMLESS + help + This option enables device driver support for the haptic controller + on MAXIM MAX8997 chip. This driver supports ff-memless interface + from input framework. + + To compile this driver as module, choose M here: the + module will be called max8997-haptic. + config INPUT_MC13783_PWRBUTTON tristate "MC13783 ON buttons" depends on MFD_MC13783 @@ -415,7 +427,7 @@ config INPUT_PCF8574 tristate "PCF8574 Keypad input device" depends on I2C && EXPERIMENTAL help - Say Y here if you want to support a keypad connetced via I2C + Say Y here if you want to support a keypad connected via I2C with a PCF8574. To compile this driver as a module, choose M here: the @@ -455,6 +467,16 @@ config INPUT_RB532_BUTTON To compile this driver as a module, choose M here: the module will be called rb532_button. +config INPUT_DA9052_ONKEY + tristate "Dialog DA9052/DA9053 Onkey" + depends on PMIC_DA9052 + help + Support the ONKEY of Dialog DA9052 PMICs as an input device + reporting power button status. + + To compile this driver as a module, choose M here: the + module will be called da9052_onkey. + config INPUT_DM355EVM tristate "TI DaVinci DM355 EVM Keypad and IR Remote" depends on MFD_DM355EVM_MSP @@ -558,7 +580,7 @@ config INPUT_CMA3000_I2C config INPUT_XEN_KBDDEV_FRONTEND tristate "Xen virtual keyboard and mouse support" - depends on XEN_FBDEV_FRONTEND + depends on XEN default y select XEN_XENBUS_FRONTEND help diff --git a/drivers/input/misc/Makefile b/drivers/input/misc/Makefile index 46671a875b91..f55cdf4916fa 100644 --- a/drivers/input/misc/Makefile +++ b/drivers/input/misc/Makefile @@ -21,6 +21,7 @@ obj-$(CONFIG_INPUT_CM109) += cm109.o obj-$(CONFIG_INPUT_CMA3000) += cma3000_d0x.o obj-$(CONFIG_INPUT_CMA3000_I2C) += cma3000_d0x_i2c.o obj-$(CONFIG_INPUT_COBALT_BTNS) += cobalt_btns.o +obj-$(CONFIG_INPUT_DA9052_ONKEY) += da9052_onkey.o obj-$(CONFIG_INPUT_DM355EVM) += dm355evm_keys.o obj-$(CONFIG_INPUT_GP2A) += gp2ap002a00f.o obj-$(CONFIG_INPUT_GPIO_TILT_POLLED) += gpio_tilt_polled.o @@ -30,6 +31,7 @@ obj-$(CONFIG_INPUT_KEYSPAN_REMOTE) += keyspan_remote.o obj-$(CONFIG_INPUT_KXTJ9) += kxtj9.o obj-$(CONFIG_INPUT_M68K_BEEP) += m68kspkr.o obj-$(CONFIG_INPUT_MAX8925_ONKEY) += max8925_onkey.o +obj-$(CONFIG_INPUT_MAX8997_HAPTIC) += max8997_haptic.o obj-$(CONFIG_INPUT_MC13783_PWRBUTTON) += mc13783-pwrbutton.o obj-$(CONFIG_INPUT_MMA8450) += mma8450.o obj-$(CONFIG_INPUT_MPU3050) += mpu3050.o diff --git a/drivers/input/misc/ad714x-i2c.c b/drivers/input/misc/ad714x-i2c.c index 56810fb4eadd..c8a79015472a 100644 --- a/drivers/input/misc/ad714x-i2c.c +++ b/drivers/input/misc/ad714x-i2c.c @@ -116,17 +116,7 @@ static struct i2c_driver ad714x_i2c_driver = { .id_table = ad714x_id, }; -static int __init ad714x_i2c_init(void) -{ - return i2c_add_driver(&ad714x_i2c_driver); -} -module_init(ad714x_i2c_init); - -static void __exit ad714x_i2c_exit(void) -{ - i2c_del_driver(&ad714x_i2c_driver); -} -module_exit(ad714x_i2c_exit); +module_i2c_driver(ad714x_i2c_driver); MODULE_DESCRIPTION("Analog Devices AD714X Capacitance Touch Sensor I2C Bus Driver"); MODULE_AUTHOR("Barry Song <21cnbao@gmail.com>"); diff --git a/drivers/input/misc/ad714x-spi.c b/drivers/input/misc/ad714x-spi.c index 875b50811361..75f6136d608e 100644 --- a/drivers/input/misc/ad714x-spi.c +++ b/drivers/input/misc/ad714x-spi.c @@ -123,17 +123,7 @@ static struct spi_driver ad714x_spi_driver = { .remove = __devexit_p(ad714x_spi_remove), }; -static __init int ad714x_spi_init(void) -{ - return spi_register_driver(&ad714x_spi_driver); -} -module_init(ad714x_spi_init); - -static __exit void ad714x_spi_exit(void) -{ - spi_unregister_driver(&ad714x_spi_driver); -} -module_exit(ad714x_spi_exit); +module_spi_driver(ad714x_spi_driver); MODULE_DESCRIPTION("Analog Devices AD714X Capacitance Touch Sensor SPI Bus Driver"); MODULE_AUTHOR("Barry Song <21cnbao@gmail.com>"); diff --git a/drivers/input/misc/adxl34x-i2c.c b/drivers/input/misc/adxl34x-i2c.c index ccacf2bb06a4..dd1d1c145a7f 100644 --- a/drivers/input/misc/adxl34x-i2c.c +++ b/drivers/input/misc/adxl34x-i2c.c @@ -148,17 +148,7 @@ static struct i2c_driver adxl34x_driver = { .id_table = adxl34x_id, }; -static int __init adxl34x_i2c_init(void) -{ - return i2c_add_driver(&adxl34x_driver); -} -module_init(adxl34x_i2c_init); - -static void __exit adxl34x_i2c_exit(void) -{ - i2c_del_driver(&adxl34x_driver); -} -module_exit(adxl34x_i2c_exit); +module_i2c_driver(adxl34x_driver); MODULE_AUTHOR("Michael Hennerich <hennerich@blackfin.uclinux.org>"); MODULE_DESCRIPTION("ADXL345/346 Three-Axis Digital Accelerometer I2C Bus Driver"); diff --git a/drivers/input/misc/adxl34x-spi.c b/drivers/input/misc/adxl34x-spi.c index 34d401efd4a1..820a802a1e6e 100644 --- a/drivers/input/misc/adxl34x-spi.c +++ b/drivers/input/misc/adxl34x-spi.c @@ -129,17 +129,7 @@ static struct spi_driver adxl34x_driver = { .remove = __devexit_p(adxl34x_spi_remove), }; -static int __init adxl34x_spi_init(void) -{ - return spi_register_driver(&adxl34x_driver); -} -module_init(adxl34x_spi_init); - -static void __exit adxl34x_spi_exit(void) -{ - spi_unregister_driver(&adxl34x_driver); -} -module_exit(adxl34x_spi_exit); +module_spi_driver(adxl34x_driver); MODULE_AUTHOR("Michael Hennerich <hennerich@blackfin.uclinux.org>"); MODULE_DESCRIPTION("ADXL345/346 Three-Axis Digital Accelerometer SPI Bus Driver"); diff --git a/drivers/input/misc/bma150.c b/drivers/input/misc/bma150.c index 8f55b54352b6..e2f1e9f952b1 100644 --- a/drivers/input/misc/bma150.c +++ b/drivers/input/misc/bma150.c @@ -673,19 +673,8 @@ static struct i2c_driver bma150_driver = { .remove = __devexit_p(bma150_remove), }; -static int __init BMA150_init(void) -{ - return i2c_add_driver(&bma150_driver); -} - -static void __exit BMA150_exit(void) -{ - i2c_del_driver(&bma150_driver); -} +module_i2c_driver(bma150_driver); MODULE_AUTHOR("Albert Zhang <xu.zhang@bosch-sensortec.com>"); MODULE_DESCRIPTION("BMA150 driver"); MODULE_LICENSE("GPL"); - -module_init(BMA150_init); -module_exit(BMA150_exit); diff --git a/drivers/input/misc/cma3000_d0x_i2c.c b/drivers/input/misc/cma3000_d0x_i2c.c index d100cc5c5783..fe9b85f07792 100644 --- a/drivers/input/misc/cma3000_d0x_i2c.c +++ b/drivers/input/misc/cma3000_d0x_i2c.c @@ -125,18 +125,7 @@ static struct i2c_driver cma3000_i2c_driver = { }, }; -static int __init cma3000_i2c_init(void) -{ - return i2c_add_driver(&cma3000_i2c_driver); -} - -static void __exit cma3000_i2c_exit(void) -{ - i2c_del_driver(&cma3000_i2c_driver); -} - -module_init(cma3000_i2c_init); -module_exit(cma3000_i2c_exit); +module_i2c_driver(cma3000_i2c_driver); MODULE_DESCRIPTION("CMA3000-D0x Accelerometer I2C Driver"); MODULE_LICENSE("GPL"); diff --git a/drivers/input/misc/da9052_onkey.c b/drivers/input/misc/da9052_onkey.c new file mode 100644 index 000000000000..34aebb8cd080 --- /dev/null +++ b/drivers/input/misc/da9052_onkey.c @@ -0,0 +1,169 @@ +/* + * ON pin driver for Dialog DA9052 PMICs + * + * Copyright(c) 2012 Dialog Semiconductor Ltd. + * + * Author: David Dajun Chen <dchen@diasemi.com> + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. + */ + +#include <linux/init.h> +#include <linux/input.h> +#include <linux/module.h> +#include <linux/platform_device.h> +#include <linux/workqueue.h> + +#include <linux/mfd/da9052/da9052.h> +#include <linux/mfd/da9052/reg.h> + +struct da9052_onkey { + struct da9052 *da9052; + struct input_dev *input; + struct delayed_work work; + unsigned int irq; +}; + +static void da9052_onkey_query(struct da9052_onkey *onkey) +{ + int key_stat; + + key_stat = da9052_reg_read(onkey->da9052, DA9052_EVENT_B_REG); + if (key_stat < 0) { + dev_err(onkey->da9052->dev, + "Failed to read onkey event %d\n", key_stat); + } else { + /* + * Since interrupt for deassertion of ONKEY pin is not + * generated, onkey event state determines the onkey + * button state. + */ + key_stat &= DA9052_EVENTB_ENONKEY; + input_report_key(onkey->input, KEY_POWER, key_stat); + input_sync(onkey->input); + } + + /* + * Interrupt is generated only when the ONKEY pin is asserted. + * Hence the deassertion of the pin is simulated through work queue. + */ + if (key_stat) + schedule_delayed_work(&onkey->work, msecs_to_jiffies(50)); +} + +static void da9052_onkey_work(struct work_struct *work) +{ + struct da9052_onkey *onkey = container_of(work, struct da9052_onkey, + work.work); + + da9052_onkey_query(onkey); +} + +static irqreturn_t da9052_onkey_irq(int irq, void *data) +{ + struct da9052_onkey *onkey = data; + + da9052_onkey_query(onkey); + + return IRQ_HANDLED; +} + +static int __devinit da9052_onkey_probe(struct platform_device *pdev) +{ + struct da9052 *da9052 = dev_get_drvdata(pdev->dev.parent); + struct da9052_onkey *onkey; + struct input_dev *input_dev; + int irq; + int error; + + if (!da9052) { + dev_err(&pdev->dev, "Failed to get the driver's data\n"); + return -EINVAL; + } + + irq = platform_get_irq_byname(pdev, "ONKEY"); + if (irq < 0) { + dev_err(&pdev->dev, + "Failed to get an IRQ for input device, %d\n", irq); + return -EINVAL; + } + + onkey = kzalloc(sizeof(*onkey), GFP_KERNEL); + input_dev = input_allocate_device(); + if (!onkey || !input_dev) { + dev_err(&pdev->dev, "Failed to allocate memory\n"); + return -ENOMEM; + } + + onkey->input = input_dev; + onkey->da9052 = da9052; + onkey->irq = irq; + INIT_DELAYED_WORK(&onkey->work, da9052_onkey_work); + + input_dev->name = "da9052-onkey"; + input_dev->phys = "da9052-onkey/input0"; + input_dev->dev.parent = &pdev->dev; + + input_dev->evbit[0] = BIT_MASK(EV_KEY); + __set_bit(KEY_POWER, input_dev->keybit); + + error = request_threaded_irq(onkey->irq, NULL, da9052_onkey_irq, + IRQF_TRIGGER_LOW | IRQF_ONESHOT, + "ONKEY", onkey); + if (error < 0) { + dev_err(onkey->da9052->dev, + "Failed to register ONKEY IRQ %d, error = %d\n", + onkey->irq, error); + goto err_free_mem; + } + + error = input_register_device(onkey->input); + if (error) { + dev_err(&pdev->dev, "Unable to register input device, %d\n", + error); + goto err_free_irq; + } + + platform_set_drvdata(pdev, onkey); + return 0; + +err_free_irq: + free_irq(onkey->irq, onkey); + cancel_delayed_work_sync(&onkey->work); +err_free_mem: + input_free_device(input_dev); + kfree(onkey); + + return error; +} + +static int __devexit da9052_onkey_remove(struct platform_device *pdev) +{ + struct da9052_onkey *onkey = platform_get_drvdata(pdev); + + free_irq(onkey->irq, onkey); + cancel_delayed_work_sync(&onkey->work); + + input_unregister_device(onkey->input); + kfree(onkey); + + return 0; +} + +static struct platform_driver da9052_onkey_driver = { + .probe = da9052_onkey_probe, + .remove = __devexit_p(da9052_onkey_remove), + .driver = { + .name = "da9052-onkey", + .owner = THIS_MODULE, + }, +}; +module_platform_driver(da9052_onkey_driver); + +MODULE_AUTHOR("David Dajun Chen <dchen@diasemi.com>"); +MODULE_DESCRIPTION("Onkey driver for DA9052"); +MODULE_LICENSE("GPL"); +MODULE_ALIAS("platform:da9052-onkey"); diff --git a/drivers/input/misc/gp2ap002a00f.c b/drivers/input/misc/gp2ap002a00f.c index 71fba8c2fc66..b6664cfa340a 100644 --- a/drivers/input/misc/gp2ap002a00f.c +++ b/drivers/input/misc/gp2ap002a00f.c @@ -281,18 +281,7 @@ static struct i2c_driver gp2a_i2c_driver = { .id_table = gp2a_i2c_id, }; -static int __init gp2a_init(void) -{ - return i2c_add_driver(&gp2a_i2c_driver); -} - -static void __exit gp2a_exit(void) -{ - i2c_del_driver(&gp2a_i2c_driver); -} - -module_init(gp2a_init); -module_exit(gp2a_exit); +module_i2c_driver(gp2a_i2c_driver); MODULE_AUTHOR("Courtney Cavin <courtney.cavin@sonyericsson.com>"); MODULE_DESCRIPTION("Sharp GP2AP002A00F I2C Proximity/Opto sensor driver"); diff --git a/drivers/input/misc/kxtj9.c b/drivers/input/misc/kxtj9.c index 783597a9a64a..f46139f19ff1 100644 --- a/drivers/input/misc/kxtj9.c +++ b/drivers/input/misc/kxtj9.c @@ -41,6 +41,14 @@ #define PC1_ON (1 << 7) /* Data ready funtion enable bit: set during probe if using irq mode */ #define DRDYE (1 << 5) +/* DATA CONTROL REGISTER BITS */ +#define ODR12_5F 0 +#define ODR25F 1 +#define ODR50F 2 +#define ODR100F 3 +#define ODR200F 4 +#define ODR400F 5 +#define ODR800F 6 /* INTERRUPT CONTROL REGISTER 1 BITS */ /* Set these during probe if using irq mode */ #define KXTJ9_IEL (1 << 3) @@ -116,9 +124,13 @@ static void kxtj9_report_acceleration_data(struct kxtj9_data *tj9) if (err < 0) dev_err(&tj9->client->dev, "accelerometer data read failed\n"); - x = le16_to_cpu(acc_data[tj9->pdata.axis_map_x]) >> tj9->shift; - y = le16_to_cpu(acc_data[tj9->pdata.axis_map_y]) >> tj9->shift; - z = le16_to_cpu(acc_data[tj9->pdata.axis_map_z]) >> tj9->shift; + x = le16_to_cpu(acc_data[tj9->pdata.axis_map_x]); + y = le16_to_cpu(acc_data[tj9->pdata.axis_map_y]); + z = le16_to_cpu(acc_data[tj9->pdata.axis_map_z]); + + x >>= tj9->shift; + y >>= tj9->shift; + z >>= tj9->shift; input_report_abs(tj9->input_dev, ABS_X, tj9->pdata.negate_x ? -x : x); input_report_abs(tj9->input_dev, ABS_Y, tj9->pdata.negate_y ? -y : y); @@ -487,7 +499,7 @@ static int __devinit kxtj9_verify(struct kxtj9_data *tj9) goto out; } - retval = retval != 0x06 ? -EIO : 0; + retval = (retval != 0x07 && retval != 0x08) ? -EIO : 0; out: kxtj9_device_power_off(tj9); @@ -537,7 +549,7 @@ static int __devinit kxtj9_probe(struct i2c_client *client, i2c_set_clientdata(client, tj9); tj9->ctrl_reg1 = tj9->pdata.res_12bit | tj9->pdata.g_range; - tj9->data_ctrl = tj9->pdata.data_odr_init; + tj9->last_poll_interval = tj9->pdata.init_interval; if (client->irq) { /* If in irq mode, populate INT_CTRL_REG1 and enable DRDY. */ @@ -655,17 +667,7 @@ static struct i2c_driver kxtj9_driver = { .id_table = kxtj9_id, }; -static int __init kxtj9_init(void) -{ - return i2c_add_driver(&kxtj9_driver); -} -module_init(kxtj9_init); - -static void __exit kxtj9_exit(void) -{ - i2c_del_driver(&kxtj9_driver); -} -module_exit(kxtj9_exit); +module_i2c_driver(kxtj9_driver); MODULE_DESCRIPTION("KXTJ9 accelerometer driver"); MODULE_AUTHOR("Chris Hudson <chudson@kionix.com>"); diff --git a/drivers/input/misc/max8925_onkey.c b/drivers/input/misc/max8925_onkey.c index 23cf08271049..0a12b74140d3 100644 --- a/drivers/input/misc/max8925_onkey.c +++ b/drivers/input/misc/max8925_onkey.c @@ -1,5 +1,5 @@ /** - * max8925_onkey.c - MAX8925 ONKEY driver + * MAX8925 ONKEY driver * * Copyright (C) 2009 Marvell International Ltd. * Haojian Zhuang <haojian.zhuang@marvell.com> @@ -35,7 +35,7 @@ struct max8925_onkey_info { struct input_dev *idev; struct i2c_client *i2c; struct device *dev; - int irq[2]; + unsigned int irq[2]; }; /* @@ -46,17 +46,14 @@ struct max8925_onkey_info { static irqreturn_t max8925_onkey_handler(int irq, void *data) { struct max8925_onkey_info *info = data; - int ret, event; - - ret = max8925_reg_read(info->i2c, MAX8925_ON_OFF_STATUS); - if (ret & SW_INPUT) - event = 1; - else - event = 0; - input_report_key(info->idev, KEY_POWER, event); + int state; + + state = max8925_reg_read(info->i2c, MAX8925_ON_OFF_STATUS); + + input_report_key(info->idev, KEY_POWER, state & SW_INPUT); input_sync(info->idev); - dev_dbg(info->dev, "onkey event:%d\n", event); + dev_dbg(info->dev, "onkey state:%d\n", state); /* Enable hardreset to halt if system isn't shutdown on time */ max8925_set_bits(info->i2c, MAX8925_SYSENSEL, @@ -69,6 +66,7 @@ static int __devinit max8925_onkey_probe(struct platform_device *pdev) { struct max8925_chip *chip = dev_get_drvdata(pdev->dev.parent); struct max8925_onkey_info *info; + struct input_dev *input; int irq[2], error; irq[0] = platform_get_irq(pdev, 0); @@ -76,6 +74,7 @@ static int __devinit max8925_onkey_probe(struct platform_device *pdev) dev_err(&pdev->dev, "No IRQ resource!\n"); return -EINVAL; } + irq[1] = platform_get_irq(pdev, 1); if (irq[1] < 0) { dev_err(&pdev->dev, "No IRQ resource!\n"); @@ -83,11 +82,24 @@ static int __devinit max8925_onkey_probe(struct platform_device *pdev) } info = kzalloc(sizeof(struct max8925_onkey_info), GFP_KERNEL); - if (!info) - return -ENOMEM; + input = input_allocate_device(); + if (!info || !input) { + error = -ENOMEM; + goto err_free_mem; + } + info->idev = input; info->i2c = chip->i2c; info->dev = &pdev->dev; + info->irq[0] = irq[0]; + info->irq[1] = irq[1]; + + input->name = "max8925_on"; + input->phys = "max8925_on/input0"; + input->id.bustype = BUS_I2C; + input->dev.parent = &pdev->dev; + input_set_capability(input, EV_KEY, KEY_POWER); + irq[0] += chip->irq_base; irq[1] += chip->irq_base; @@ -96,60 +108,46 @@ static int __devinit max8925_onkey_probe(struct platform_device *pdev) if (error < 0) { dev_err(chip->dev, "Failed to request IRQ: #%d: %d\n", irq[0], error); - goto out; + goto err_free_mem; } + error = request_threaded_irq(irq[1], NULL, max8925_onkey_handler, IRQF_ONESHOT, "onkey-up", info); if (error < 0) { dev_err(chip->dev, "Failed to request IRQ: #%d: %d\n", irq[1], error); - goto out_irq; + goto err_free_irq0; } - info->idev = input_allocate_device(); - if (!info->idev) { - dev_err(chip->dev, "Failed to allocate input dev\n"); - error = -ENOMEM; - goto out_input; - } - - info->idev->name = "max8925_on"; - info->idev->phys = "max8925_on/input0"; - info->idev->id.bustype = BUS_I2C; - info->idev->dev.parent = &pdev->dev; - info->irq[0] = irq[0]; - info->irq[1] = irq[1]; - info->idev->evbit[0] = BIT_MASK(EV_KEY); - info->idev->keybit[BIT_WORD(KEY_POWER)] = BIT_MASK(KEY_POWER); - - error = input_register_device(info->idev); if (error) { dev_err(chip->dev, "Can't register input device: %d\n", error); - goto out_reg; + goto err_free_irq1; } platform_set_drvdata(pdev, info); + device_init_wakeup(&pdev->dev, 1); return 0; -out_reg: - input_free_device(info->idev); -out_input: - free_irq(info->irq[1], info); -out_irq: - free_irq(info->irq[0], info); -out: +err_free_irq1: + free_irq(irq[1], info); +err_free_irq0: + free_irq(irq[0], info); +err_free_mem: + input_free_device(input); kfree(info); + return error; } static int __devexit max8925_onkey_remove(struct platform_device *pdev) { struct max8925_onkey_info *info = platform_get_drvdata(pdev); + struct max8925_chip *chip = dev_get_drvdata(pdev->dev.parent); - free_irq(info->irq[0], info); - free_irq(info->irq[1], info); + free_irq(info->irq[0] + chip->irq_base, info); + free_irq(info->irq[1] + chip->irq_base, info); input_unregister_device(info->idev); kfree(info); @@ -158,10 +156,43 @@ static int __devexit max8925_onkey_remove(struct platform_device *pdev) return 0; } +#ifdef CONFIG_PM_SLEEP +static int max8925_onkey_suspend(struct device *dev) +{ + struct platform_device *pdev = to_platform_device(dev); + struct max8925_onkey_info *info = platform_get_drvdata(pdev); + struct max8925_chip *chip = dev_get_drvdata(pdev->dev.parent); + + if (device_may_wakeup(dev)) { + chip->wakeup_flag |= 1 << info->irq[0]; + chip->wakeup_flag |= 1 << info->irq[1]; + } + + return 0; +} + +static int max8925_onkey_resume(struct device *dev) +{ + struct platform_device *pdev = to_platform_device(dev); + struct max8925_onkey_info *info = platform_get_drvdata(pdev); + struct max8925_chip *chip = dev_get_drvdata(pdev->dev.parent); + + if (device_may_wakeup(dev)) { + chip->wakeup_flag &= ~(1 << info->irq[0]); + chip->wakeup_flag &= ~(1 << info->irq[1]); + } + + return 0; +} +#endif + +static SIMPLE_DEV_PM_OPS(max8925_onkey_pm_ops, max8925_onkey_suspend, max8925_onkey_resume); + static struct platform_driver max8925_onkey_driver = { .driver = { .name = "max8925-onkey", .owner = THIS_MODULE, + .pm = &max8925_onkey_pm_ops, }, .probe = max8925_onkey_probe, .remove = __devexit_p(max8925_onkey_remove), diff --git a/drivers/input/misc/max8997_haptic.c b/drivers/input/misc/max8997_haptic.c new file mode 100644 index 000000000000..05b7b8bfaf0a --- /dev/null +++ b/drivers/input/misc/max8997_haptic.c @@ -0,0 +1,407 @@ +/* + * MAX8997-haptic controller driver + * + * Copyright (C) 2012 Samsung Electronics + * Donggeun Kim <dg77.kim@samsung.com> + * + * This program is not provided / owned by Maxim Integrated Products. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + */ + +#include <linux/module.h> +#include <linux/init.h> +#include <linux/slab.h> +#include <linux/platform_device.h> +#include <linux/err.h> +#include <linux/pwm.h> +#include <linux/input.h> +#include <linux/mfd/max8997-private.h> +#include <linux/mfd/max8997.h> +#include <linux/regulator/consumer.h> + +/* Haptic configuration 2 register */ +#define MAX8997_MOTOR_TYPE_SHIFT 7 +#define MAX8997_ENABLE_SHIFT 6 +#define MAX8997_MODE_SHIFT 5 + +/* Haptic driver configuration register */ +#define MAX8997_CYCLE_SHIFT 6 +#define MAX8997_SIG_PERIOD_SHIFT 4 +#define MAX8997_SIG_DUTY_SHIFT 2 +#define MAX8997_PWM_DUTY_SHIFT 0 + +struct max8997_haptic { + struct device *dev; + struct i2c_client *client; + struct input_dev *input_dev; + struct regulator *regulator; + + struct work_struct work; + struct mutex mutex; + + bool enabled; + unsigned int level; + + struct pwm_device *pwm; + unsigned int pwm_period; + enum max8997_haptic_pwm_divisor pwm_divisor; + + enum max8997_haptic_motor_type type; + enum max8997_haptic_pulse_mode mode; + + unsigned int internal_mode_pattern; + unsigned int pattern_cycle; + unsigned int pattern_signal_period; +}; + +static int max8997_haptic_set_duty_cycle(struct max8997_haptic *chip) +{ + int ret = 0; + + if (chip->mode == MAX8997_EXTERNAL_MODE) { + unsigned int duty = chip->pwm_period * chip->level / 100; + ret = pwm_config(chip->pwm, duty, chip->pwm_period); + } else { + int i; + u8 duty_index = 0; + + for (i = 0; i <= 64; i++) { + if (chip->level <= i * 100 / 64) { + duty_index = i; + break; + } + } + switch (chip->internal_mode_pattern) { + case 0: + max8997_write_reg(chip->client, + MAX8997_HAPTIC_REG_SIGPWMDC1, duty_index); + break; + case 1: + max8997_write_reg(chip->client, + MAX8997_HAPTIC_REG_SIGPWMDC2, duty_index); + break; + case 2: + max8997_write_reg(chip->client, + MAX8997_HAPTIC_REG_SIGPWMDC3, duty_index); + break; + case 3: + max8997_write_reg(chip->client, + MAX8997_HAPTIC_REG_SIGPWMDC4, duty_index); + break; + default: + break; + } + } + return ret; +} + +static void max8997_haptic_configure(struct max8997_haptic *chip) +{ + u8 value; + + value = chip->type << MAX8997_MOTOR_TYPE_SHIFT | + chip->enabled << MAX8997_ENABLE_SHIFT | + chip->mode << MAX8997_MODE_SHIFT | chip->pwm_divisor; + max8997_write_reg(chip->client, MAX8997_HAPTIC_REG_CONF2, value); + + if (chip->mode == MAX8997_INTERNAL_MODE && chip->enabled) { + value = chip->internal_mode_pattern << MAX8997_CYCLE_SHIFT | + chip->internal_mode_pattern << MAX8997_SIG_PERIOD_SHIFT | + chip->internal_mode_pattern << MAX8997_SIG_DUTY_SHIFT | + chip->internal_mode_pattern << MAX8997_PWM_DUTY_SHIFT; + max8997_write_reg(chip->client, + MAX8997_HAPTIC_REG_DRVCONF, value); + + switch (chip->internal_mode_pattern) { + case 0: + value = chip->pattern_cycle << 4; + max8997_write_reg(chip->client, + MAX8997_HAPTIC_REG_CYCLECONF1, value); + value = chip->pattern_signal_period; + max8997_write_reg(chip->client, + MAX8997_HAPTIC_REG_SIGCONF1, value); + break; + + case 1: + value = chip->pattern_cycle; + max8997_write_reg(chip->client, + MAX8997_HAPTIC_REG_CYCLECONF1, value); + value = chip->pattern_signal_period; + max8997_write_reg(chip->client, + MAX8997_HAPTIC_REG_SIGCONF2, value); + break; + + case 2: + value = chip->pattern_cycle << 4; + max8997_write_reg(chip->client, + MAX8997_HAPTIC_REG_CYCLECONF2, value); + value = chip->pattern_signal_period; + max8997_write_reg(chip->client, + MAX8997_HAPTIC_REG_SIGCONF3, value); + break; + + case 3: + value = chip->pattern_cycle; + max8997_write_reg(chip->client, + MAX8997_HAPTIC_REG_CYCLECONF2, value); + value = chip->pattern_signal_period; + max8997_write_reg(chip->client, + MAX8997_HAPTIC_REG_SIGCONF4, value); + break; + + default: + break; + } + } +} + +static void max8997_haptic_enable(struct max8997_haptic *chip) +{ + int error; + + mutex_lock(&chip->mutex); + + error = max8997_haptic_set_duty_cycle(chip); + if (error) { + dev_err(chip->dev, "set_pwm_cycle failed, error: %d\n", error); + goto out; + } + + if (!chip->enabled) { + chip->enabled = true; + regulator_enable(chip->regulator); + max8997_haptic_configure(chip); + if (chip->mode == MAX8997_EXTERNAL_MODE) + pwm_enable(chip->pwm); + } + +out: + mutex_unlock(&chip->mutex); +} + +static void max8997_haptic_disable(struct max8997_haptic *chip) +{ + mutex_lock(&chip->mutex); + + if (chip->enabled) { + chip->enabled = false; + max8997_haptic_configure(chip); + if (chip->mode == MAX8997_EXTERNAL_MODE) + pwm_disable(chip->pwm); + regulator_disable(chip->regulator); + } + + mutex_unlock(&chip->mutex); +} + +static void max8997_haptic_play_effect_work(struct work_struct *work) +{ + struct max8997_haptic *chip = + container_of(work, struct max8997_haptic, work); + + if (chip->level) + max8997_haptic_enable(chip); + else + max8997_haptic_disable(chip); +} + +static int max8997_haptic_play_effect(struct input_dev *dev, void *data, + struct ff_effect *effect) +{ + struct max8997_haptic *chip = input_get_drvdata(dev); + + chip->level = effect->u.rumble.strong_magnitude; + if (!chip->level) + chip->level = effect->u.rumble.weak_magnitude; + + schedule_work(&chip->work); + + return 0; +} + +static void max8997_haptic_close(struct input_dev *dev) +{ + struct max8997_haptic *chip = input_get_drvdata(dev); + + cancel_work_sync(&chip->work); + max8997_haptic_disable(chip); +} + +static int __devinit max8997_haptic_probe(struct platform_device *pdev) +{ + struct max8997_dev *iodev = dev_get_drvdata(pdev->dev.parent); + const struct max8997_platform_data *pdata = + dev_get_platdata(iodev->dev); + const struct max8997_haptic_platform_data *haptic_pdata = + pdata->haptic_pdata; + struct max8997_haptic *chip; + struct input_dev *input_dev; + int error; + + if (!haptic_pdata) { + dev_err(&pdev->dev, "no haptic platform data\n"); + return -EINVAL; + } + + chip = kzalloc(sizeof(struct max8997_haptic), GFP_KERNEL); + input_dev = input_allocate_device(); + if (!chip || !input_dev) { + dev_err(&pdev->dev, "unable to allocate memory\n"); + error = -ENOMEM; + goto err_free_mem; + } + + INIT_WORK(&chip->work, max8997_haptic_play_effect_work); + mutex_init(&chip->mutex); + + chip->client = iodev->haptic; + chip->dev = &pdev->dev; + chip->input_dev = input_dev; + chip->pwm_period = haptic_pdata->pwm_period; + chip->type = haptic_pdata->type; + chip->mode = haptic_pdata->mode; + chip->pwm_divisor = haptic_pdata->pwm_divisor; + + switch (chip->mode) { + case MAX8997_INTERNAL_MODE: + chip->internal_mode_pattern = + haptic_pdata->internal_mode_pattern; + chip->pattern_cycle = haptic_pdata->pattern_cycle; + chip->pattern_signal_period = + haptic_pdata->pattern_signal_period; + break; + + case MAX8997_EXTERNAL_MODE: + chip->pwm = pwm_request(haptic_pdata->pwm_channel_id, + "max8997-haptic"); + if (IS_ERR(chip->pwm)) { + error = PTR_ERR(chip->pwm); + dev_err(&pdev->dev, + "unable to request PWM for haptic, error: %d\n", + error); + goto err_free_mem; + } + break; + + default: + dev_err(&pdev->dev, + "Invalid chip mode specified (%d)\n", chip->mode); + error = -EINVAL; + goto err_free_mem; + } + + chip->regulator = regulator_get(&pdev->dev, "inmotor"); + if (IS_ERR(chip->regulator)) { + error = PTR_ERR(chip->regulator); + dev_err(&pdev->dev, + "unable to get regulator, error: %d\n", + error); + goto err_free_pwm; + } + + input_dev->name = "max8997-haptic"; + input_dev->id.version = 1; + input_dev->dev.parent = &pdev->dev; + input_dev->close = max8997_haptic_close; + input_set_drvdata(input_dev, chip); + input_set_capability(input_dev, EV_FF, FF_RUMBLE); + + error = input_ff_create_memless(input_dev, NULL, + max8997_haptic_play_effect); + if (error) { + dev_err(&pdev->dev, + "unable to create FF device, error: %d\n", + error); + goto err_put_regulator; + } + + error = input_register_device(input_dev); + if (error) { + dev_err(&pdev->dev, + "unable to register input device, error: %d\n", + error); + goto err_destroy_ff; + } + + platform_set_drvdata(pdev, chip); + return 0; + +err_destroy_ff: + input_ff_destroy(input_dev); +err_put_regulator: + regulator_put(chip->regulator); +err_free_pwm: + if (chip->mode == MAX8997_EXTERNAL_MODE) + pwm_free(chip->pwm); +err_free_mem: + input_free_device(input_dev); + kfree(chip); + + return error; +} + +static int __devexit max8997_haptic_remove(struct platform_device *pdev) +{ + struct max8997_haptic *chip = platform_get_drvdata(pdev); + + input_unregister_device(chip->input_dev); + regulator_put(chip->regulator); + + if (chip->mode == MAX8997_EXTERNAL_MODE) + pwm_free(chip->pwm); + + kfree(chip); + + return 0; +} + +#ifdef CONFIG_PM_SLEEP +static int max8997_haptic_suspend(struct device *dev) +{ + struct platform_device *pdev = to_platform_device(dev); + struct max8997_haptic *chip = platform_get_drvdata(pdev); + + max8997_haptic_disable(chip); + + return 0; +} +#endif + +static SIMPLE_DEV_PM_OPS(max8997_haptic_pm_ops, max8997_haptic_suspend, NULL); + +static const struct platform_device_id max8997_haptic_id[] = { + { "max8997-haptic", 0 }, + { }, +}; +MODULE_DEVICE_TABLE(i2c, max8997_haptic_id); + +static struct platform_driver max8997_haptic_driver = { + .driver = { + .name = "max8997-haptic", + .owner = THIS_MODULE, + .pm = &max8997_haptic_pm_ops, + }, + .probe = max8997_haptic_probe, + .remove = __devexit_p(max8997_haptic_remove), + .id_table = max8997_haptic_id, +}; +module_platform_driver(max8997_haptic_driver); + +MODULE_ALIAS("platform:max8997-haptic"); +MODULE_AUTHOR("Donggeun Kim <dg77.kim@samsung.com>"); +MODULE_DESCRIPTION("max8997_haptic driver"); +MODULE_LICENSE("GPL"); diff --git a/drivers/input/misc/mma8450.c b/drivers/input/misc/mma8450.c index 4d60080bb5d5..873ebced544e 100644 --- a/drivers/input/misc/mma8450.c +++ b/drivers/input/misc/mma8450.c @@ -247,17 +247,7 @@ static struct i2c_driver mma8450_driver = { .id_table = mma8450_id, }; -static int __init mma8450_init(void) -{ - return i2c_add_driver(&mma8450_driver); -} -module_init(mma8450_init); - -static void __exit mma8450_exit(void) -{ - i2c_del_driver(&mma8450_driver); -} -module_exit(mma8450_exit); +module_i2c_driver(mma8450_driver); MODULE_AUTHOR("Freescale Semiconductor, Inc."); MODULE_DESCRIPTION("MMA8450 3-Axis Accelerometer Driver"); diff --git a/drivers/input/misc/mpu3050.c b/drivers/input/misc/mpu3050.c index 208d1a1cc7f3..5403c571b6a5 100644 --- a/drivers/input/misc/mpu3050.c +++ b/drivers/input/misc/mpu3050.c @@ -475,17 +475,7 @@ static struct i2c_driver mpu3050_i2c_driver = { .id_table = mpu3050_ids, }; -static int __init mpu3050_init(void) -{ - return i2c_add_driver(&mpu3050_i2c_driver); -} -module_init(mpu3050_init); - -static void __exit mpu3050_exit(void) -{ - i2c_del_driver(&mpu3050_i2c_driver); -} -module_exit(mpu3050_exit); +module_i2c_driver(mpu3050_i2c_driver); MODULE_AUTHOR("Wistron Corp."); MODULE_DESCRIPTION("MPU3050 Tri-axis gyroscope driver"); diff --git a/drivers/input/misc/pcf8574_keypad.c b/drivers/input/misc/pcf8574_keypad.c index 08be1a355956..544c6635abe9 100644 --- a/drivers/input/misc/pcf8574_keypad.c +++ b/drivers/input/misc/pcf8574_keypad.c @@ -216,17 +216,7 @@ static struct i2c_driver pcf8574_kp_driver = { .id_table = pcf8574_kp_id, }; -static int __init pcf8574_kp_init(void) -{ - return i2c_add_driver(&pcf8574_kp_driver); -} -module_init(pcf8574_kp_init); - -static void __exit pcf8574_kp_exit(void) -{ - i2c_del_driver(&pcf8574_kp_driver); -} -module_exit(pcf8574_kp_exit); +module_i2c_driver(pcf8574_kp_driver); MODULE_AUTHOR("Michael Hennerich"); MODULE_DESCRIPTION("Keypad input driver for 16 keys connected to PCF8574"); diff --git a/drivers/input/misc/twl4030-vibra.c b/drivers/input/misc/twl4030-vibra.c index 37651373a95b..fc0ed9b43424 100644 --- a/drivers/input/misc/twl4030-vibra.c +++ b/drivers/input/misc/twl4030-vibra.c @@ -172,7 +172,7 @@ static void twl4030_vibra_close(struct input_dev *input) } /*** Module ***/ -#if CONFIG_PM +#ifdef CONFIG_PM_SLEEP static int twl4030_vibra_suspend(struct device *dev) { struct platform_device *pdev = to_platform_device(dev); @@ -189,10 +189,10 @@ static int twl4030_vibra_resume(struct device *dev) vibra_disable_leds(); return 0; } +#endif static SIMPLE_DEV_PM_OPS(twl4030_vibra_pm_ops, twl4030_vibra_suspend, twl4030_vibra_resume); -#endif static int __devinit twl4030_vibra_probe(struct platform_device *pdev) { @@ -273,9 +273,7 @@ static struct platform_driver twl4030_vibra_driver = { .driver = { .name = "twl4030-vibra", .owner = THIS_MODULE, -#ifdef CONFIG_PM .pm = &twl4030_vibra_pm_ops, -#endif }, }; module_platform_driver(twl4030_vibra_driver); diff --git a/drivers/input/mouse/Kconfig b/drivers/input/mouse/Kconfig index 9c1e6ee83531..9b8db821d5f0 100644 --- a/drivers/input/mouse/Kconfig +++ b/drivers/input/mouse/Kconfig @@ -322,4 +322,21 @@ config MOUSE_SYNAPTICS_I2C To compile this driver as a module, choose M here: the module will be called synaptics_i2c. +config MOUSE_SYNAPTICS_USB + tristate "Synaptics USB device support" + depends on USB_ARCH_HAS_HCD + select USB + help + Say Y here if you want to use a Synaptics USB touchpad or pointing + stick. + + While these devices emulate an USB mouse by default and can be used + with standard usbhid driver, this driver, together with its X.Org + counterpart, allows you to fully utilize capabilities of the device. + More information can be found at: + <http://jan-steinhoff.de/linux/synaptics-usb.html> + + To compile this driver as a module, choose M here: the + module will be called synaptics_usb. + endif diff --git a/drivers/input/mouse/Makefile b/drivers/input/mouse/Makefile index 570c84a4a654..4718effeb8d9 100644 --- a/drivers/input/mouse/Makefile +++ b/drivers/input/mouse/Makefile @@ -18,6 +18,7 @@ obj-$(CONFIG_MOUSE_PXA930_TRKBALL) += pxa930_trkball.o obj-$(CONFIG_MOUSE_RISCPC) += rpcmouse.o obj-$(CONFIG_MOUSE_SERIAL) += sermouse.o obj-$(CONFIG_MOUSE_SYNAPTICS_I2C) += synaptics_i2c.o +obj-$(CONFIG_MOUSE_SYNAPTICS_USB) += synaptics_usb.o obj-$(CONFIG_MOUSE_VSXXXAA) += vsxxxaa.o psmouse-objs := psmouse-base.o synaptics.o diff --git a/drivers/input/mouse/alps.c b/drivers/input/mouse/alps.c index bd87380bd879..4c6a72d3d48c 100644 --- a/drivers/input/mouse/alps.c +++ b/drivers/input/mouse/alps.c @@ -952,7 +952,9 @@ static const struct alps_model_info *alps_get_model(struct psmouse *psmouse, int /* * First try "E6 report". - * ALPS should return 0,0,10 or 0,0,100 + * ALPS should return 0,0,10 or 0,0,100 if no buttons are pressed. + * The bits 0-2 of the first byte will be 1s if some buttons are + * pressed. */ param[0] = 0; if (ps2_command(ps2dev, param, PSMOUSE_CMD_SETRES) || @@ -968,7 +970,8 @@ static const struct alps_model_info *alps_get_model(struct psmouse *psmouse, int psmouse_dbg(psmouse, "E6 report: %2.2x %2.2x %2.2x", param[0], param[1], param[2]); - if (param[0] != 0 || param[1] != 0 || (param[2] != 10 && param[2] != 100)) + if ((param[0] & 0xf8) != 0 || param[1] != 0 || + (param[2] != 10 && param[2] != 100)) return NULL; /* diff --git a/drivers/input/mouse/amimouse.c b/drivers/input/mouse/amimouse.c index ff5f61a0fd3a..5fa99341a39d 100644 --- a/drivers/input/mouse/amimouse.c +++ b/drivers/input/mouse/amimouse.c @@ -25,7 +25,6 @@ #include <asm/irq.h> #include <asm/setup.h> -#include <asm/system.h> #include <asm/uaccess.h> #include <asm/amigahw.h> #include <asm/amigaints.h> diff --git a/drivers/input/mouse/atarimouse.c b/drivers/input/mouse/atarimouse.c index 5c4a692bf73a..d1c43236b125 100644 --- a/drivers/input/mouse/atarimouse.c +++ b/drivers/input/mouse/atarimouse.c @@ -47,7 +47,6 @@ #include <asm/irq.h> #include <asm/setup.h> -#include <asm/system.h> #include <asm/uaccess.h> #include <asm/atarihw.h> #include <asm/atarikb.h> diff --git a/drivers/input/mouse/bcm5974.c b/drivers/input/mouse/bcm5974.c index 927e479c2649..f9e2758b9f46 100644 --- a/drivers/input/mouse/bcm5974.c +++ b/drivers/input/mouse/bcm5974.c @@ -433,6 +433,7 @@ static void setup_events_to_report(struct input_dev *input_dev, __set_bit(BTN_TOOL_QUADTAP, input_dev->keybit); __set_bit(BTN_LEFT, input_dev->keybit); + __set_bit(INPUT_PROP_POINTER, input_dev->propbit); if (cfg->caps & HAS_INTEGRATED_BUTTON) __set_bit(INPUT_PROP_BUTTONPAD, input_dev->propbit); diff --git a/drivers/input/mouse/hgpk.c b/drivers/input/mouse/hgpk.c index 1c5d521de600..575f880727fe 100644 --- a/drivers/input/mouse/hgpk.c +++ b/drivers/input/mouse/hgpk.c @@ -640,7 +640,6 @@ static int hgpk_reset_device(struct psmouse *psmouse, bool recalibrate) static int hgpk_force_recalibrate(struct psmouse *psmouse) { - struct ps2dev *ps2dev = &psmouse->ps2dev; struct hgpk_data *priv = psmouse->private; int err; @@ -669,12 +668,9 @@ static int hgpk_force_recalibrate(struct psmouse *psmouse) * we don't have a good way to deal with it. The 2s window stuff * (below) is our best option for now. */ - - if (ps2_command(ps2dev, NULL, PSMOUSE_CMD_ENABLE)) + if (psmouse_activate(psmouse)) return -1; - psmouse_set_state(psmouse, PSMOUSE_ACTIVATED); - if (tpdebug) psmouse_dbg(psmouse, "touchpad reactivated\n"); @@ -733,8 +729,7 @@ static int hgpk_toggle_powersave(struct psmouse *psmouse, int enable) } /* should be all set, enable the touchpad */ - ps2_command(&psmouse->ps2dev, NULL, PSMOUSE_CMD_ENABLE); - psmouse_set_state(psmouse, PSMOUSE_ACTIVATED); + psmouse_activate(psmouse); psmouse_dbg(psmouse, "Touchpad powered up.\n"); } else { psmouse_dbg(psmouse, "Powering off touchpad.\n"); diff --git a/drivers/input/mouse/psmouse-base.c b/drivers/input/mouse/psmouse-base.c index e6c9931f02c7..22fe2547e169 100644 --- a/drivers/input/mouse/psmouse-base.c +++ b/drivers/input/mouse/psmouse-base.c @@ -1092,28 +1092,33 @@ static void psmouse_initialize(struct psmouse *psmouse) * psmouse_activate() enables the mouse so that we get motion reports from it. */ -static void psmouse_activate(struct psmouse *psmouse) +int psmouse_activate(struct psmouse *psmouse) { - if (ps2_command(&psmouse->ps2dev, NULL, PSMOUSE_CMD_ENABLE)) + if (ps2_command(&psmouse->ps2dev, NULL, PSMOUSE_CMD_ENABLE)) { psmouse_warn(psmouse, "Failed to enable mouse on %s\n", psmouse->ps2dev.serio->phys); + return -1; + } psmouse_set_state(psmouse, PSMOUSE_ACTIVATED); + return 0; } - /* * psmouse_deactivate() puts the mouse into poll mode so that we don't get motion * reports from it unless we explicitly request it. */ -static void psmouse_deactivate(struct psmouse *psmouse) +int psmouse_deactivate(struct psmouse *psmouse) { - if (ps2_command(&psmouse->ps2dev, NULL, PSMOUSE_CMD_DISABLE)) + if (ps2_command(&psmouse->ps2dev, NULL, PSMOUSE_CMD_DISABLE)) { psmouse_warn(psmouse, "Failed to deactivate mouse on %s\n", psmouse->ps2dev.serio->phys); + return -1; + } psmouse_set_state(psmouse, PSMOUSE_CMD_MODE); + return 0; } diff --git a/drivers/input/mouse/psmouse.h b/drivers/input/mouse/psmouse.h index 6a417092d010..fe1df231ba4c 100644 --- a/drivers/input/mouse/psmouse.h +++ b/drivers/input/mouse/psmouse.h @@ -105,6 +105,8 @@ int psmouse_reset(struct psmouse *psmouse); void psmouse_set_state(struct psmouse *psmouse, enum psmouse_state new_state); void psmouse_set_resolution(struct psmouse *psmouse, unsigned int resolution); psmouse_ret_t psmouse_process_byte(struct psmouse *psmouse); +int psmouse_activate(struct psmouse *psmouse); +int psmouse_deactivate(struct psmouse *psmouse); struct psmouse_attribute { struct device_attribute dattr; diff --git a/drivers/input/mouse/sentelic.c b/drivers/input/mouse/sentelic.c index e36847de7617..a977bfaa6821 100644 --- a/drivers/input/mouse/sentelic.c +++ b/drivers/input/mouse/sentelic.c @@ -2,7 +2,7 @@ * Finger Sensing Pad PS/2 mouse driver. * * Copyright (C) 2005-2007 Asia Vital Components Co., Ltd. - * Copyright (C) 2005-2011 Tai-hwa Liang, Sentelic Corporation. + * Copyright (C) 2005-2012 Tai-hwa Liang, Sentelic Corporation. * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License @@ -21,6 +21,7 @@ #include <linux/module.h> #include <linux/input.h> +#include <linux/input/mt.h> #include <linux/ctype.h> #include <linux/libps2.h> #include <linux/serio.h> @@ -36,6 +37,9 @@ #define FSP_CMD_TIMEOUT 200 #define FSP_CMD_TIMEOUT2 30 +#define GET_ABS_X(packet) ((packet[1] << 2) | ((packet[3] >> 2) & 0x03)) +#define GET_ABS_Y(packet) ((packet[2] << 2) | (packet[3] & 0x03)) + /** Driver version. */ static const char fsp_drv_ver[] = "1.0.0-K"; @@ -90,8 +94,7 @@ static int fsp_reg_read(struct psmouse *psmouse, int reg_addr, int *reg_val) * to do that for writes because sysfs set helper does this for * us. */ - ps2_command(ps2dev, NULL, PSMOUSE_CMD_DISABLE); - psmouse_set_state(psmouse, PSMOUSE_CMD_MODE); + psmouse_deactivate(psmouse); ps2_begin_command(ps2dev); @@ -128,10 +131,10 @@ static int fsp_reg_read(struct psmouse *psmouse, int reg_addr, int *reg_val) out: ps2_end_command(ps2dev); - ps2_command(ps2dev, NULL, PSMOUSE_CMD_ENABLE); - psmouse_set_state(psmouse, PSMOUSE_ACTIVATED); - dev_dbg(&ps2dev->serio->dev, "READ REG: 0x%02x is 0x%02x (rc = %d)\n", - reg_addr, *reg_val, rc); + psmouse_activate(psmouse); + psmouse_dbg(psmouse, + "READ REG: 0x%02x is 0x%02x (rc = %d)\n", + reg_addr, *reg_val, rc); return rc; } @@ -181,8 +184,9 @@ static int fsp_reg_write(struct psmouse *psmouse, int reg_addr, int reg_val) out: ps2_end_command(ps2dev); - dev_dbg(&ps2dev->serio->dev, "WRITE REG: 0x%02x to 0x%02x (rc = %d)\n", - reg_addr, reg_val, rc); + psmouse_dbg(psmouse, + "WRITE REG: 0x%02x to 0x%02x (rc = %d)\n", + reg_addr, reg_val, rc); return rc; } @@ -213,8 +217,7 @@ static int fsp_page_reg_read(struct psmouse *psmouse, int *reg_val) unsigned char param[3]; int rc = -1; - ps2_command(ps2dev, NULL, PSMOUSE_CMD_DISABLE); - psmouse_set_state(psmouse, PSMOUSE_CMD_MODE); + psmouse_deactivate(psmouse); ps2_begin_command(ps2dev); @@ -239,10 +242,10 @@ static int fsp_page_reg_read(struct psmouse *psmouse, int *reg_val) out: ps2_end_command(ps2dev); - ps2_command(ps2dev, NULL, PSMOUSE_CMD_ENABLE); - psmouse_set_state(psmouse, PSMOUSE_ACTIVATED); - dev_dbg(&ps2dev->serio->dev, "READ PAGE REG: 0x%02x (rc = %d)\n", - *reg_val, rc); + psmouse_activate(psmouse); + psmouse_dbg(psmouse, + "READ PAGE REG: 0x%02x (rc = %d)\n", + *reg_val, rc); return rc; } @@ -278,8 +281,9 @@ static int fsp_page_reg_write(struct psmouse *psmouse, int reg_val) out: ps2_end_command(ps2dev); - dev_dbg(&ps2dev->serio->dev, "WRITE PAGE REG: to 0x%02x (rc = %d)\n", - reg_val, rc); + psmouse_dbg(psmouse, + "WRITE PAGE REG: to 0x%02x (rc = %d)\n", + reg_val, rc); return rc; } @@ -323,7 +327,7 @@ static int fsp_opc_tag_enable(struct psmouse *psmouse, bool enable) int res = 0; if (fsp_reg_read(psmouse, FSP_REG_OPC_QDOWN, &v) == -1) { - dev_err(&psmouse->ps2dev.serio->dev, "Unable get OPC state.\n"); + psmouse_err(psmouse, "Unable get OPC state.\n"); return -EIO; } @@ -340,8 +344,7 @@ static int fsp_opc_tag_enable(struct psmouse *psmouse, bool enable) } if (res != 0) { - dev_err(&psmouse->ps2dev.serio->dev, - "Unable to enable OPC tag.\n"); + psmouse_err(psmouse, "Unable to enable OPC tag.\n"); res = -EIO; } @@ -619,18 +622,40 @@ static struct attribute_group fsp_attribute_group = { .attrs = fsp_attributes, }; -#ifdef FSP_DEBUG -static void fsp_packet_debug(unsigned char packet[]) +#ifdef FSP_DEBUG +static void fsp_packet_debug(struct psmouse *psmouse, unsigned char packet[]) { static unsigned int ps2_packet_cnt; static unsigned int ps2_last_second; unsigned int jiffies_msec; + const char *packet_type = "UNKNOWN"; + unsigned short abs_x = 0, abs_y = 0; + + /* Interpret & dump the packet data. */ + switch (packet[0] >> FSP_PKT_TYPE_SHIFT) { + case FSP_PKT_TYPE_ABS: + packet_type = "Absolute"; + abs_x = GET_ABS_X(packet); + abs_y = GET_ABS_Y(packet); + break; + case FSP_PKT_TYPE_NORMAL: + packet_type = "Normal"; + break; + case FSP_PKT_TYPE_NOTIFY: + packet_type = "Notify"; + break; + case FSP_PKT_TYPE_NORMAL_OPC: + packet_type = "Normal-OPC"; + break; + } ps2_packet_cnt++; jiffies_msec = jiffies_to_msecs(jiffies); psmouse_dbg(psmouse, - "%08dms PS/2 packets: %02x, %02x, %02x, %02x\n", - jiffies_msec, packet[0], packet[1], packet[2], packet[3]); + "%08dms %s packets: %02x, %02x, %02x, %02x; " + "abs_x: %d, abs_y: %d\n", + jiffies_msec, packet_type, + packet[0], packet[1], packet[2], packet[3], abs_x, abs_y); if (jiffies_msec - ps2_last_second > 1000) { psmouse_dbg(psmouse, "PS/2 packets/sec = %d\n", ps2_packet_cnt); @@ -639,17 +664,29 @@ static void fsp_packet_debug(unsigned char packet[]) } } #else -static void fsp_packet_debug(unsigned char packet[]) +static void fsp_packet_debug(struct psmouse *psmouse, unsigned char packet[]) { } #endif +static void fsp_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); + } +} + static psmouse_ret_t fsp_process_byte(struct psmouse *psmouse) { struct input_dev *dev = psmouse->dev; struct fsp_data *ad = psmouse->private; unsigned char *packet = psmouse->packet; unsigned char button_status = 0, lscroll = 0, rscroll = 0; + unsigned short abs_x, abs_y, fgrs = 0; int rel_x, rel_y; if (psmouse->pktcnt < 4) @@ -659,16 +696,76 @@ static psmouse_ret_t fsp_process_byte(struct psmouse *psmouse) * Full packet accumulated, process it */ + fsp_packet_debug(psmouse, packet); + switch (psmouse->packet[0] >> FSP_PKT_TYPE_SHIFT) { case FSP_PKT_TYPE_ABS: - dev_warn(&psmouse->ps2dev.serio->dev, - "Unexpected absolute mode packet, ignored.\n"); + abs_x = GET_ABS_X(packet); + abs_y = GET_ABS_Y(packet); + + if (packet[0] & FSP_PB0_MFMC) { + /* + * MFMC packet: assume that there are two fingers on + * pad + */ + fgrs = 2; + + /* MFMC packet */ + if (packet[0] & FSP_PB0_MFMC_FGR2) { + /* 2nd finger */ + if (ad->last_mt_fgr == 2) { + /* + * workaround for buggy firmware + * which doesn't clear MFMC bit if + * the 1st finger is up + */ + fgrs = 1; + fsp_set_slot(dev, 0, false, 0, 0); + } + ad->last_mt_fgr = 2; + + fsp_set_slot(dev, 1, fgrs == 2, abs_x, abs_y); + } else { + /* 1st finger */ + if (ad->last_mt_fgr == 1) { + /* + * workaround for buggy firmware + * which doesn't clear MFMC bit if + * the 2nd finger is up + */ + fgrs = 1; + fsp_set_slot(dev, 1, false, 0, 0); + } + ad->last_mt_fgr = 1; + fsp_set_slot(dev, 0, fgrs != 0, abs_x, abs_y); + } + } else { + /* SFAC packet */ + + /* no multi-finger information */ + ad->last_mt_fgr = 0; + + if (abs_x != 0 && abs_y != 0) + fgrs = 1; + + fsp_set_slot(dev, 0, fgrs > 0, abs_x, abs_y); + fsp_set_slot(dev, 1, false, 0, 0); + } + if (fgrs > 0) { + input_report_abs(dev, ABS_X, abs_x); + input_report_abs(dev, ABS_Y, abs_y); + } + input_report_key(dev, BTN_LEFT, packet[0] & 0x01); + input_report_key(dev, BTN_RIGHT, packet[0] & 0x02); + input_report_key(dev, BTN_TOUCH, fgrs); + input_report_key(dev, BTN_TOOL_FINGER, fgrs == 1); + input_report_key(dev, BTN_TOOL_DOUBLETAP, fgrs == 2); break; case FSP_PKT_TYPE_NORMAL_OPC: /* on-pad click, filter it if necessary */ if ((ad->flags & FSPDRV_FLAG_EN_OPC) != FSPDRV_FLAG_EN_OPC) - packet[0] &= ~BIT(0); + packet[0] &= ~FSP_PB0_LBTN; /* fall through */ case FSP_PKT_TYPE_NORMAL: @@ -715,8 +812,6 @@ static psmouse_ret_t fsp_process_byte(struct psmouse *psmouse) input_sync(dev); - fsp_packet_debug(packet); - return PSMOUSE_FULL_PACKET; } @@ -740,42 +835,106 @@ static int fsp_activate_protocol(struct psmouse *psmouse) ps2_command(ps2dev, param, PSMOUSE_CMD_GETID); if (param[0] != 0x04) { - dev_err(&psmouse->ps2dev.serio->dev, - "Unable to enable 4 bytes packet format.\n"); + psmouse_err(psmouse, + "Unable to enable 4 bytes packet format.\n"); return -EIO; } - if (fsp_reg_read(psmouse, FSP_REG_SYSCTL5, &val)) { - dev_err(&psmouse->ps2dev.serio->dev, - "Unable to read SYSCTL5 register.\n"); - return -EIO; - } + if (pad->ver < FSP_VER_STL3888_C0) { + /* Preparing relative coordinates output for older hardware */ + if (fsp_reg_read(psmouse, FSP_REG_SYSCTL5, &val)) { + psmouse_err(psmouse, + "Unable to read SYSCTL5 register.\n"); + return -EIO; + } - val &= ~(FSP_BIT_EN_MSID7 | FSP_BIT_EN_MSID8 | FSP_BIT_EN_AUTO_MSID8); - /* Ensure we are not in absolute mode */ - val &= ~FSP_BIT_EN_PKT_G0; - if (pad->buttons == 0x06) { - /* Left/Middle/Right & Scroll Up/Down/Right/Left */ - val |= FSP_BIT_EN_MSID6; - } + if (fsp_get_buttons(psmouse, &pad->buttons)) { + psmouse_err(psmouse, + "Unable to retrieve number of buttons.\n"); + return -EIO; + } - if (fsp_reg_write(psmouse, FSP_REG_SYSCTL5, val)) { - dev_err(&psmouse->ps2dev.serio->dev, - "Unable to set up required mode bits.\n"); - return -EIO; + val &= ~(FSP_BIT_EN_MSID7 | FSP_BIT_EN_MSID8 | FSP_BIT_EN_AUTO_MSID8); + /* Ensure we are not in absolute mode */ + val &= ~FSP_BIT_EN_PKT_G0; + if (pad->buttons == 0x06) { + /* Left/Middle/Right & Scroll Up/Down/Right/Left */ + val |= FSP_BIT_EN_MSID6; + } + + if (fsp_reg_write(psmouse, FSP_REG_SYSCTL5, val)) { + psmouse_err(psmouse, + "Unable to set up required mode bits.\n"); + return -EIO; + } + + /* + * Enable OPC tags such that driver can tell the difference + * between on-pad and real button click + */ + if (fsp_opc_tag_enable(psmouse, true)) + psmouse_warn(psmouse, + "Failed to enable OPC tag mode.\n"); + /* enable on-pad click by default */ + pad->flags |= FSPDRV_FLAG_EN_OPC; + + /* Enable on-pad vertical and horizontal scrolling */ + fsp_onpad_vscr(psmouse, true); + fsp_onpad_hscr(psmouse, true); + } else { + /* Enable absolute coordinates output for Cx/Dx hardware */ + if (fsp_reg_write(psmouse, FSP_REG_SWC1, + FSP_BIT_SWC1_EN_ABS_1F | + FSP_BIT_SWC1_EN_ABS_2F | + FSP_BIT_SWC1_EN_FUP_OUT | + FSP_BIT_SWC1_EN_ABS_CON)) { + psmouse_err(psmouse, + "Unable to enable absolute coordinates output.\n"); + return -EIO; + } } - /* - * Enable OPC tags such that driver can tell the difference between - * on-pad and real button click - */ - if (fsp_opc_tag_enable(psmouse, true)) - dev_warn(&psmouse->ps2dev.serio->dev, - "Failed to enable OPC tag mode.\n"); + return 0; +} - /* Enable on-pad vertical and horizontal scrolling */ - fsp_onpad_vscr(psmouse, true); - fsp_onpad_hscr(psmouse, true); +static int fsp_set_input_params(struct psmouse *psmouse) +{ + struct input_dev *dev = psmouse->dev; + struct fsp_data *pad = psmouse->private; + + if (pad->ver < FSP_VER_STL3888_C0) { + __set_bit(BTN_MIDDLE, dev->keybit); + __set_bit(BTN_BACK, dev->keybit); + __set_bit(BTN_FORWARD, dev->keybit); + __set_bit(REL_WHEEL, dev->relbit); + __set_bit(REL_HWHEEL, dev->relbit); + } else { + /* + * Hardware prior to Cx performs much better in relative mode; + * hence, only enable absolute coordinates output as well as + * multi-touch output for the newer hardware. + * + * Maximum coordinates can be computed as: + * + * number of scanlines * 64 - 57 + * + * where number of X/Y scanline lines are 16/12. + */ + int abs_x = 967, abs_y = 711; + + __set_bit(EV_ABS, dev->evbit); + __clear_bit(EV_REL, dev->evbit); + __set_bit(BTN_TOUCH, dev->keybit); + __set_bit(BTN_TOOL_FINGER, dev->keybit); + __set_bit(BTN_TOOL_DOUBLETAP, dev->keybit); + __set_bit(INPUT_PROP_SEMI_MT, dev->propbit); + + input_set_abs_params(dev, ABS_X, 0, abs_x, 0, 0); + input_set_abs_params(dev, ABS_Y, 0, abs_y, 0, 0); + input_mt_init_slots(dev, 2); + input_set_abs_params(dev, ABS_MT_POSITION_X, 0, abs_x, 0, 0); + input_set_abs_params(dev, ABS_MT_POSITION_Y, 0, abs_y, 0, 0); + } return 0; } @@ -833,18 +992,16 @@ static int fsp_reconnect(struct psmouse *psmouse) int fsp_init(struct psmouse *psmouse) { struct fsp_data *priv; - int ver, rev, buttons; + int ver, rev; int error; if (fsp_get_version(psmouse, &ver) || - fsp_get_revision(psmouse, &rev) || - fsp_get_buttons(psmouse, &buttons)) { + fsp_get_revision(psmouse, &rev)) { return -ENODEV; } - psmouse_info(psmouse, - "Finger Sensing Pad, hw: %d.%d.%d, sw: %s, buttons: %d\n", - ver >> 4, ver & 0x0F, rev, fsp_drv_ver, buttons & 7); + psmouse_info(psmouse, "Finger Sensing Pad, hw: %d.%d.%d, sw: %s\n", + ver >> 4, ver & 0x0F, rev, fsp_drv_ver); psmouse->private = priv = kzalloc(sizeof(struct fsp_data), GFP_KERNEL); if (!priv) @@ -852,17 +1009,6 @@ int fsp_init(struct psmouse *psmouse) priv->ver = ver; priv->rev = rev; - priv->buttons = buttons; - - /* enable on-pad click by default */ - priv->flags |= FSPDRV_FLAG_EN_OPC; - - /* Set up various supported input event bits */ - __set_bit(BTN_MIDDLE, psmouse->dev->keybit); - __set_bit(BTN_BACK, psmouse->dev->keybit); - __set_bit(BTN_FORWARD, psmouse->dev->keybit); - __set_bit(REL_WHEEL, psmouse->dev->relbit); - __set_bit(REL_HWHEEL, psmouse->dev->relbit); psmouse->protocol_handler = fsp_process_byte; psmouse->disconnect = fsp_disconnect; @@ -870,16 +1016,20 @@ int fsp_init(struct psmouse *psmouse) psmouse->cleanup = fsp_reset; psmouse->pktsize = 4; - /* set default packet output based on number of buttons we found */ error = fsp_activate_protocol(psmouse); if (error) goto err_out; + /* Set up various supported input event bits */ + error = fsp_set_input_params(psmouse); + if (error) + goto err_out; + error = sysfs_create_group(&psmouse->ps2dev.serio->dev.kobj, &fsp_attribute_group); if (error) { - dev_err(&psmouse->ps2dev.serio->dev, - "Failed to create sysfs attributes (%d)", error); + psmouse_err(psmouse, + "Failed to create sysfs attributes (%d)", error); goto err_out; } diff --git a/drivers/input/mouse/sentelic.h b/drivers/input/mouse/sentelic.h index 2e4af24f8c15..334de19e5ddb 100644 --- a/drivers/input/mouse/sentelic.h +++ b/drivers/input/mouse/sentelic.h @@ -2,7 +2,7 @@ * Finger Sensing Pad PS/2 mouse driver. * * Copyright (C) 2005-2007 Asia Vital Components Co., Ltd. - * Copyright (C) 2005-2011 Tai-hwa Liang, Sentelic Corporation. + * Copyright (C) 2005-2012 Tai-hwa Liang, Sentelic Corporation. * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License @@ -55,6 +55,16 @@ #define FSP_BIT_FIX_HSCR BIT(5) #define FSP_BIT_DRAG_LOCK BIT(6) +#define FSP_REG_SWC1 (0x90) +#define FSP_BIT_SWC1_EN_ABS_1F BIT(0) +#define FSP_BIT_SWC1_EN_GID BIT(1) +#define FSP_BIT_SWC1_EN_ABS_2F BIT(2) +#define FSP_BIT_SWC1_EN_FUP_OUT BIT(3) +#define FSP_BIT_SWC1_EN_ABS_CON BIT(4) +#define FSP_BIT_SWC1_GST_GRP0 BIT(5) +#define FSP_BIT_SWC1_GST_GRP1 BIT(6) +#define FSP_BIT_SWC1_BX_COMPAT BIT(7) + /* Finger-sensing Pad packet formating related definitions */ /* absolute packet type */ @@ -64,12 +74,32 @@ #define FSP_PKT_TYPE_NORMAL_OPC (0x03) #define FSP_PKT_TYPE_SHIFT (6) +/* bit definitions for the first byte of report packet */ +#define FSP_PB0_LBTN BIT(0) +#define FSP_PB0_RBTN BIT(1) +#define FSP_PB0_MBTN BIT(2) +#define FSP_PB0_MFMC_FGR2 FSP_PB0_MBTN +#define FSP_PB0_MUST_SET BIT(3) +#define FSP_PB0_PHY_BTN BIT(4) +#define FSP_PB0_MFMC BIT(5) + +/* hardware revisions */ +#define FSP_VER_STL3888_A4 (0xC1) +#define FSP_VER_STL3888_B0 (0xD0) +#define FSP_VER_STL3888_B1 (0xD1) +#define FSP_VER_STL3888_B2 (0xD2) +#define FSP_VER_STL3888_C0 (0xE0) +#define FSP_VER_STL3888_C1 (0xE1) +#define FSP_VER_STL3888_D0 (0xE2) +#define FSP_VER_STL3888_D1 (0xE3) +#define FSP_VER_STL3888_E0 (0xE4) + #ifdef __KERNEL__ struct fsp_data { unsigned char ver; /* hardware version */ unsigned char rev; /* hardware revison */ - unsigned char buttons; /* Number of buttons */ + unsigned int buttons; /* Number of buttons */ unsigned int flags; #define FSPDRV_FLAG_EN_OPC (0x001) /* enable on-pad clicking */ @@ -78,6 +108,7 @@ struct fsp_data { unsigned char last_reg; /* Last register we requested read from */ unsigned char last_val; + unsigned int last_mt_fgr; /* Last seen finger(multitouch) */ }; #ifdef CONFIG_MOUSE_PS2_SENTELIC diff --git a/drivers/input/mouse/synaptics_i2c.c b/drivers/input/mouse/synaptics_i2c.c index 1c58aafa523f..f14675702c0f 100644 --- a/drivers/input/mouse/synaptics_i2c.c +++ b/drivers/input/mouse/synaptics_i2c.c @@ -672,18 +672,7 @@ static struct i2c_driver synaptics_i2c_driver = { .id_table = synaptics_i2c_id_table, }; -static int __init synaptics_i2c_init(void) -{ - return i2c_add_driver(&synaptics_i2c_driver); -} - -static void __exit synaptics_i2c_exit(void) -{ - i2c_del_driver(&synaptics_i2c_driver); -} - -module_init(synaptics_i2c_init); -module_exit(synaptics_i2c_exit); +module_i2c_driver(synaptics_i2c_driver); MODULE_DESCRIPTION("Synaptics I2C touchpad driver"); MODULE_AUTHOR("Mike Rapoport, Igor Grinberg, Compulab"); diff --git a/drivers/input/mouse/synaptics_usb.c b/drivers/input/mouse/synaptics_usb.c new file mode 100644 index 000000000000..3c5eaaa5d154 --- /dev/null +++ b/drivers/input/mouse/synaptics_usb.c @@ -0,0 +1,557 @@ +/* + * USB Synaptics device driver + * + * Copyright (c) 2002 Rob Miller (rob@inpharmatica . co . uk) + * Copyright (c) 2003 Ron Lee (ron@debian.org) + * cPad driver for kernel 2.4 + * + * Copyright (c) 2004 Jan Steinhoff (cpad@jan-steinhoff . de) + * Copyright (c) 2004 Ron Lee (ron@debian.org) + * rewritten for kernel 2.6 + * + * cPad display character device part is not included. It can be found at + * http://jan-steinhoff.de/linux/synaptics-usb.html + * + * Bases on: usb_skeleton.c v2.2 by Greg Kroah-Hartman + * drivers/hid/usbhid/usbmouse.c by Vojtech Pavlik + * drivers/input/mouse/synaptics.c by Peter Osterlund + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the Free + * Software Foundation; either version 2 of the License, or (at your option) + * any later version. + * + * Trademarks are the property of their respective owners. + */ + +/* + * There are three different types of Synaptics USB devices: Touchpads, + * touchsticks (or trackpoints), and touchscreens. Touchpads are well supported + * by this driver, touchstick support has not been tested much yet, and + * touchscreens have not been tested at all. + * + * Up to three alternate settings are possible: + * setting 0: one int endpoint for relative movement (used by usbhid.ko) + * setting 1: one int endpoint for absolute finger position + * setting 2 (cPad only): one int endpoint for absolute finger position and + * two bulk endpoints for the display (in/out) + * This driver uses setting 1. + */ + +#include <linux/kernel.h> +#include <linux/init.h> +#include <linux/slab.h> +#include <linux/module.h> +#include <linux/moduleparam.h> +#include <linux/usb.h> +#include <linux/input.h> +#include <linux/usb/input.h> + +#define USB_VENDOR_ID_SYNAPTICS 0x06cb +#define USB_DEVICE_ID_SYNAPTICS_TP 0x0001 /* Synaptics USB TouchPad */ +#define USB_DEVICE_ID_SYNAPTICS_INT_TP 0x0002 /* Integrated USB TouchPad */ +#define USB_DEVICE_ID_SYNAPTICS_CPAD 0x0003 /* Synaptics cPad */ +#define USB_DEVICE_ID_SYNAPTICS_TS 0x0006 /* Synaptics TouchScreen */ +#define USB_DEVICE_ID_SYNAPTICS_STICK 0x0007 /* Synaptics USB Styk */ +#define USB_DEVICE_ID_SYNAPTICS_WP 0x0008 /* Synaptics USB WheelPad */ +#define USB_DEVICE_ID_SYNAPTICS_COMP_TP 0x0009 /* Composite USB TouchPad */ +#define USB_DEVICE_ID_SYNAPTICS_WTP 0x0010 /* Wireless TouchPad */ +#define USB_DEVICE_ID_SYNAPTICS_DPAD 0x0013 /* DisplayPad */ + +#define SYNUSB_TOUCHPAD (1 << 0) +#define SYNUSB_STICK (1 << 1) +#define SYNUSB_TOUCHSCREEN (1 << 2) +#define SYNUSB_AUXDISPLAY (1 << 3) /* For cPad */ +#define SYNUSB_COMBO (1 << 4) /* Composite device (TP + stick) */ +#define SYNUSB_IO_ALWAYS (1 << 5) + +#define USB_DEVICE_SYNAPTICS(prod, kind) \ + USB_DEVICE(USB_VENDOR_ID_SYNAPTICS, \ + USB_DEVICE_ID_SYNAPTICS_##prod), \ + .driver_info = (kind), + +#define SYNUSB_RECV_SIZE 8 + +#define XMIN_NOMINAL 1472 +#define XMAX_NOMINAL 5472 +#define YMIN_NOMINAL 1408 +#define YMAX_NOMINAL 4448 + +struct synusb { + struct usb_device *udev; + struct usb_interface *intf; + struct urb *urb; + unsigned char *data; + + /* input device related data structures */ + struct input_dev *input; + char name[128]; + char phys[64]; + + /* characteristics of the device */ + unsigned long flags; +}; + +static void synusb_report_buttons(struct synusb *synusb) +{ + struct input_dev *input_dev = synusb->input; + + input_report_key(input_dev, BTN_LEFT, synusb->data[1] & 0x04); + input_report_key(input_dev, BTN_RIGHT, synusb->data[1] & 0x01); + input_report_key(input_dev, BTN_MIDDLE, synusb->data[1] & 0x02); +} + +static void synusb_report_stick(struct synusb *synusb) +{ + struct input_dev *input_dev = synusb->input; + int x, y; + unsigned int pressure; + + pressure = synusb->data[6]; + x = (s16)(be16_to_cpup((__be16 *)&synusb->data[2]) << 3) >> 7; + y = (s16)(be16_to_cpup((__be16 *)&synusb->data[4]) << 3) >> 7; + + if (pressure > 0) { + input_report_rel(input_dev, REL_X, x); + input_report_rel(input_dev, REL_Y, -y); + } + + input_report_abs(input_dev, ABS_PRESSURE, pressure); + + synusb_report_buttons(synusb); + + input_sync(input_dev); +} + +static void synusb_report_touchpad(struct synusb *synusb) +{ + struct input_dev *input_dev = synusb->input; + unsigned int num_fingers, tool_width; + unsigned int x, y; + unsigned int pressure, w; + + pressure = synusb->data[6]; + x = be16_to_cpup((__be16 *)&synusb->data[2]); + y = be16_to_cpup((__be16 *)&synusb->data[4]); + w = synusb->data[0] & 0x0f; + + if (pressure > 0) { + num_fingers = 1; + tool_width = 5; + switch (w) { + case 0 ... 1: + num_fingers = 2 + w; + break; + + case 2: /* pen, pretend its a finger */ + break; + + case 4 ... 15: + tool_width = w; + break; + } + } else { + num_fingers = 0; + tool_width = 0; + } + + /* + * Post events + * BTN_TOUCH has to be first as mousedev relies on it when doing + * absolute -> relative conversion + */ + + if (pressure > 30) + input_report_key(input_dev, BTN_TOUCH, 1); + if (pressure < 25) + input_report_key(input_dev, BTN_TOUCH, 0); + + if (num_fingers > 0) { + input_report_abs(input_dev, ABS_X, x); + input_report_abs(input_dev, ABS_Y, + YMAX_NOMINAL + YMIN_NOMINAL - y); + } + + input_report_abs(input_dev, ABS_PRESSURE, pressure); + input_report_abs(input_dev, ABS_TOOL_WIDTH, tool_width); + + input_report_key(input_dev, BTN_TOOL_FINGER, num_fingers == 1); + input_report_key(input_dev, BTN_TOOL_DOUBLETAP, num_fingers == 2); + input_report_key(input_dev, BTN_TOOL_TRIPLETAP, num_fingers == 3); + + synusb_report_buttons(synusb); + if (synusb->flags & SYNUSB_AUXDISPLAY) + input_report_key(input_dev, BTN_MIDDLE, synusb->data[1] & 0x08); + + input_sync(input_dev); +} + +static void synusb_irq(struct urb *urb) +{ + struct synusb *synusb = urb->context; + int error; + + /* Check our status in case we need to bail out early. */ + switch (urb->status) { + case 0: + usb_mark_last_busy(synusb->udev); + break; + + /* Device went away so don't keep trying to read from it. */ + case -ECONNRESET: + case -ENOENT: + case -ESHUTDOWN: + return; + + default: + goto resubmit; + break; + } + + if (synusb->flags & SYNUSB_STICK) + synusb_report_stick(synusb); + else + synusb_report_touchpad(synusb); + +resubmit: + error = usb_submit_urb(urb, GFP_ATOMIC); + if (error && error != -EPERM) + dev_err(&synusb->intf->dev, + "%s - usb_submit_urb failed with result: %d", + __func__, error); +} + +static struct usb_endpoint_descriptor * +synusb_get_in_endpoint(struct usb_host_interface *iface) +{ + + struct usb_endpoint_descriptor *endpoint; + int i; + + for (i = 0; i < iface->desc.bNumEndpoints; ++i) { + endpoint = &iface->endpoint[i].desc; + + if (usb_endpoint_is_int_in(endpoint)) { + /* we found our interrupt in endpoint */ + return endpoint; + } + } + + return NULL; +} + +static int synusb_open(struct input_dev *dev) +{ + struct synusb *synusb = input_get_drvdata(dev); + int retval; + + retval = usb_autopm_get_interface(synusb->intf); + if (retval) { + dev_err(&synusb->intf->dev, + "%s - usb_autopm_get_interface failed, error: %d\n", + __func__, retval); + return retval; + } + + retval = usb_submit_urb(synusb->urb, GFP_KERNEL); + if (retval) { + dev_err(&synusb->intf->dev, + "%s - usb_submit_urb failed, error: %d\n", + __func__, retval); + retval = -EIO; + goto out; + } + + synusb->intf->needs_remote_wakeup = 1; + +out: + usb_autopm_put_interface(synusb->intf); + return retval; +} + +static void synusb_close(struct input_dev *dev) +{ + struct synusb *synusb = input_get_drvdata(dev); + int autopm_error; + + autopm_error = usb_autopm_get_interface(synusb->intf); + + usb_kill_urb(synusb->urb); + synusb->intf->needs_remote_wakeup = 0; + + if (!autopm_error) + usb_autopm_put_interface(synusb->intf); +} + +static int synusb_probe(struct usb_interface *intf, + const struct usb_device_id *id) +{ + struct usb_device *udev = interface_to_usbdev(intf); + struct usb_endpoint_descriptor *ep; + struct synusb *synusb; + struct input_dev *input_dev; + unsigned int intf_num = intf->cur_altsetting->desc.bInterfaceNumber; + unsigned int altsetting = min(intf->num_altsetting, 1U); + int error; + + error = usb_set_interface(udev, intf_num, altsetting); + if (error) { + dev_err(&udev->dev, + "Can not set alternate setting to %i, error: %i", + altsetting, error); + return error; + } + + ep = synusb_get_in_endpoint(intf->cur_altsetting); + if (!ep) + return -ENODEV; + + synusb = kzalloc(sizeof(*synusb), GFP_KERNEL); + input_dev = input_allocate_device(); + if (!synusb || !input_dev) { + error = -ENOMEM; + goto err_free_mem; + } + + synusb->udev = udev; + synusb->intf = intf; + synusb->input = input_dev; + + synusb->flags = id->driver_info; + if (synusb->flags & SYNUSB_COMBO) { + /* + * This is a combo device, we need to set proper + * capability, depending on the interface. + */ + synusb->flags |= intf_num == 1 ? + SYNUSB_STICK : SYNUSB_TOUCHPAD; + } + + synusb->urb = usb_alloc_urb(0, GFP_KERNEL); + if (!synusb->urb) { + error = -ENOMEM; + goto err_free_mem; + } + + synusb->data = usb_alloc_coherent(udev, SYNUSB_RECV_SIZE, GFP_KERNEL, + &synusb->urb->transfer_dma); + if (!synusb->data) { + error = -ENOMEM; + goto err_free_urb; + } + + usb_fill_int_urb(synusb->urb, udev, + usb_rcvintpipe(udev, ep->bEndpointAddress), + synusb->data, SYNUSB_RECV_SIZE, + synusb_irq, synusb, + ep->bInterval); + synusb->urb->transfer_flags |= URB_NO_TRANSFER_DMA_MAP; + + if (udev->manufacturer) + strlcpy(synusb->name, udev->manufacturer, + sizeof(synusb->name)); + + if (udev->product) { + if (udev->manufacturer) + strlcat(synusb->name, " ", sizeof(synusb->name)); + strlcat(synusb->name, udev->product, sizeof(synusb->name)); + } + + if (!strlen(synusb->name)) + snprintf(synusb->name, sizeof(synusb->name), + "USB Synaptics Device %04x:%04x", + le16_to_cpu(udev->descriptor.idVendor), + le16_to_cpu(udev->descriptor.idProduct)); + + if (synusb->flags & SYNUSB_STICK) + strlcat(synusb->name, " (Stick) ", sizeof(synusb->name)); + + usb_make_path(udev, synusb->phys, sizeof(synusb->phys)); + strlcat(synusb->phys, "/input0", sizeof(synusb->phys)); + + input_dev->name = synusb->name; + input_dev->phys = synusb->phys; + usb_to_input_id(udev, &input_dev->id); + input_dev->dev.parent = &synusb->intf->dev; + + if (!(synusb->flags & SYNUSB_IO_ALWAYS)) { + input_dev->open = synusb_open; + input_dev->close = synusb_close; + } + + input_set_drvdata(input_dev, synusb); + + __set_bit(EV_ABS, input_dev->evbit); + __set_bit(EV_KEY, input_dev->evbit); + + if (synusb->flags & SYNUSB_STICK) { + __set_bit(EV_REL, input_dev->evbit); + __set_bit(REL_X, input_dev->relbit); + __set_bit(REL_Y, input_dev->relbit); + input_set_abs_params(input_dev, ABS_PRESSURE, 0, 127, 0, 0); + } else { + input_set_abs_params(input_dev, ABS_X, + XMIN_NOMINAL, XMAX_NOMINAL, 0, 0); + input_set_abs_params(input_dev, ABS_Y, + YMIN_NOMINAL, YMAX_NOMINAL, 0, 0); + input_set_abs_params(input_dev, ABS_PRESSURE, 0, 255, 0, 0); + input_set_abs_params(input_dev, ABS_TOOL_WIDTH, 0, 15, 0, 0); + __set_bit(BTN_TOUCH, input_dev->keybit); + __set_bit(BTN_TOOL_FINGER, input_dev->keybit); + __set_bit(BTN_TOOL_DOUBLETAP, input_dev->keybit); + __set_bit(BTN_TOOL_TRIPLETAP, input_dev->keybit); + } + + __set_bit(BTN_LEFT, input_dev->keybit); + __set_bit(BTN_RIGHT, input_dev->keybit); + __set_bit(BTN_MIDDLE, input_dev->keybit); + + usb_set_intfdata(intf, synusb); + + if (synusb->flags & SYNUSB_IO_ALWAYS) { + error = synusb_open(input_dev); + if (error) + goto err_free_dma; + } + + error = input_register_device(input_dev); + if (error) { + dev_err(&udev->dev, + "Failed to register input device, error %d\n", + error); + goto err_stop_io; + } + + return 0; + +err_stop_io: + if (synusb->flags & SYNUSB_IO_ALWAYS) + synusb_close(synusb->input); +err_free_dma: + usb_free_coherent(udev, SYNUSB_RECV_SIZE, synusb->data, + synusb->urb->transfer_dma); +err_free_urb: + usb_free_urb(synusb->urb); +err_free_mem: + input_free_device(input_dev); + kfree(synusb); + usb_set_intfdata(intf, NULL); + + return error; +} + +static void synusb_disconnect(struct usb_interface *intf) +{ + struct synusb *synusb = usb_get_intfdata(intf); + struct usb_device *udev = interface_to_usbdev(intf); + + if (synusb->flags & SYNUSB_IO_ALWAYS) + synusb_close(synusb->input); + + input_unregister_device(synusb->input); + + usb_free_coherent(udev, SYNUSB_RECV_SIZE, synusb->data, + synusb->urb->transfer_dma); + usb_free_urb(synusb->urb); + kfree(synusb); + + usb_set_intfdata(intf, NULL); +} + +static int synusb_suspend(struct usb_interface *intf, pm_message_t message) +{ + struct synusb *synusb = usb_get_intfdata(intf); + struct input_dev *input_dev = synusb->input; + + mutex_lock(&input_dev->mutex); + usb_kill_urb(synusb->urb); + mutex_unlock(&input_dev->mutex); + + return 0; +} + +static int synusb_resume(struct usb_interface *intf) +{ + struct synusb *synusb = usb_get_intfdata(intf); + struct input_dev *input_dev = synusb->input; + int retval = 0; + + mutex_lock(&input_dev->mutex); + + if ((input_dev->users || (synusb->flags & SYNUSB_IO_ALWAYS)) && + usb_submit_urb(synusb->urb, GFP_NOIO) < 0) { + retval = -EIO; + } + + mutex_unlock(&input_dev->mutex); + + return retval; +} + +static int synusb_pre_reset(struct usb_interface *intf) +{ + struct synusb *synusb = usb_get_intfdata(intf); + struct input_dev *input_dev = synusb->input; + + mutex_lock(&input_dev->mutex); + usb_kill_urb(synusb->urb); + + return 0; +} + +static int synusb_post_reset(struct usb_interface *intf) +{ + struct synusb *synusb = usb_get_intfdata(intf); + struct input_dev *input_dev = synusb->input; + int retval = 0; + + if ((input_dev->users || (synusb->flags & SYNUSB_IO_ALWAYS)) && + usb_submit_urb(synusb->urb, GFP_NOIO) < 0) { + retval = -EIO; + } + + mutex_unlock(&input_dev->mutex); + + return retval; +} + +static int synusb_reset_resume(struct usb_interface *intf) +{ + return synusb_resume(intf); +} + +static struct usb_device_id synusb_idtable[] = { + { USB_DEVICE_SYNAPTICS(TP, SYNUSB_TOUCHPAD) }, + { USB_DEVICE_SYNAPTICS(INT_TP, SYNUSB_TOUCHPAD) }, + { USB_DEVICE_SYNAPTICS(CPAD, + SYNUSB_TOUCHPAD | SYNUSB_AUXDISPLAY | SYNUSB_IO_ALWAYS) }, + { USB_DEVICE_SYNAPTICS(TS, SYNUSB_TOUCHSCREEN) }, + { USB_DEVICE_SYNAPTICS(STICK, SYNUSB_STICK) }, + { USB_DEVICE_SYNAPTICS(WP, SYNUSB_TOUCHPAD) }, + { USB_DEVICE_SYNAPTICS(COMP_TP, SYNUSB_COMBO) }, + { USB_DEVICE_SYNAPTICS(WTP, SYNUSB_TOUCHPAD) }, + { USB_DEVICE_SYNAPTICS(DPAD, SYNUSB_TOUCHPAD) }, + { } +}; +MODULE_DEVICE_TABLE(usb, synusb_idtable); + +static struct usb_driver synusb_driver = { + .name = "synaptics_usb", + .probe = synusb_probe, + .disconnect = synusb_disconnect, + .id_table = synusb_idtable, + .suspend = synusb_suspend, + .resume = synusb_resume, + .pre_reset = synusb_pre_reset, + .post_reset = synusb_post_reset, + .reset_resume = synusb_reset_resume, + .supports_autosuspend = 1, +}; + +module_usb_driver(synusb_driver); + +MODULE_AUTHOR("Rob Miller <rob@inpharmatica.co.uk>, " + "Ron Lee <ron@debian.org>, " + "Jan Steinhoff <cpad@jan-steinhoff.de>"); +MODULE_DESCRIPTION("Synaptics USB device driver"); +MODULE_LICENSE("GPL"); diff --git a/drivers/input/of_keymap.c b/drivers/input/of_keymap.c new file mode 100644 index 000000000000..061493d57682 --- /dev/null +++ b/drivers/input/of_keymap.c @@ -0,0 +1,87 @@ +/* + * Helpers for open firmware matrix keyboard bindings + * + * Copyright (C) 2012 Google, Inc + * + * Author: + * Olof Johansson <olof@lixom.net> + * + * This software is licensed under the terms of the GNU General Public + * License version 2, as published by the Free Software Foundation, and + * may be copied, distributed, and modified under those terms. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + */ + +#include <linux/kernel.h> +#include <linux/types.h> +#include <linux/input.h> +#include <linux/of.h> +#include <linux/input/matrix_keypad.h> +#include <linux/export.h> +#include <linux/gfp.h> +#include <linux/slab.h> + +struct matrix_keymap_data * +matrix_keyboard_of_fill_keymap(struct device_node *np, + const char *propname) +{ + struct matrix_keymap_data *kd; + u32 *keymap; + int proplen, i; + const __be32 *prop; + + if (!np) + return NULL; + + if (!propname) + propname = "linux,keymap"; + + prop = of_get_property(np, propname, &proplen); + if (!prop) + return NULL; + + if (proplen % sizeof(u32)) { + pr_warn("Malformed keymap property %s in %s\n", + propname, np->full_name); + return NULL; + } + + kd = kzalloc(sizeof(*kd), GFP_KERNEL); + if (!kd) + return NULL; + + kd->keymap = keymap = kzalloc(proplen, GFP_KERNEL); + if (!kd->keymap) { + kfree(kd); + return NULL; + } + + kd->keymap_size = proplen / sizeof(u32); + + for (i = 0; i < kd->keymap_size; i++) { + u32 tmp = be32_to_cpup(prop + i); + int key_code, row, col; + + row = (tmp >> 24) & 0xff; + col = (tmp >> 16) & 0xff; + key_code = tmp & 0xffff; + keymap[i] = KEY(row, col, key_code); + } + + return kd; +} +EXPORT_SYMBOL_GPL(matrix_keyboard_of_fill_keymap); + +void matrix_keyboard_of_free_keymap(const struct matrix_keymap_data *kd) +{ + if (kd) { + kfree(kd->keymap); + kfree(kd); + } +} +EXPORT_SYMBOL_GPL(matrix_keyboard_of_free_keymap); diff --git a/drivers/input/serio/altera_ps2.c b/drivers/input/serio/altera_ps2.c index 35864c6130bb..cc11f4efe119 100644 --- a/drivers/input/serio/altera_ps2.c +++ b/drivers/input/serio/altera_ps2.c @@ -180,8 +180,6 @@ static const struct of_device_id altera_ps2_match[] = { {}, }; MODULE_DEVICE_TABLE(of, altera_ps2_match); -#else /* CONFIG_OF */ -#define altera_ps2_match NULL #endif /* CONFIG_OF */ /* @@ -193,7 +191,7 @@ static struct platform_driver altera_ps2_driver = { .driver = { .name = DRV_NAME, .owner = THIS_MODULE, - .of_match_table = altera_ps2_match, + .of_match_table = of_match_ptr(altera_ps2_match), }, }; module_platform_driver(altera_ps2_driver); diff --git a/drivers/input/serio/ambakmi.c b/drivers/input/serio/ambakmi.c index 8407d5b0ced8..2ffd110bd5bc 100644 --- a/drivers/input/serio/ambakmi.c +++ b/drivers/input/serio/ambakmi.c @@ -208,18 +208,7 @@ static struct amba_driver ambakmi_driver = { .resume = amba_kmi_resume, }; -static int __init amba_kmi_init(void) -{ - return amba_driver_register(&ambakmi_driver); -} - -static void __exit amba_kmi_exit(void) -{ - amba_driver_unregister(&ambakmi_driver); -} - -module_init(amba_kmi_init); -module_exit(amba_kmi_exit); +module_amba_driver(ambakmi_driver); MODULE_AUTHOR("Russell King <rmk@arm.linux.org.uk>"); MODULE_DESCRIPTION("AMBA KMI controller driver"); diff --git a/drivers/input/serio/ams_delta_serio.c b/drivers/input/serio/ams_delta_serio.c index d4d08bd9205b..f5fbdf94de3b 100644 --- a/drivers/input/serio/ams_delta_serio.c +++ b/drivers/input/serio/ams_delta_serio.c @@ -92,8 +92,7 @@ static irqreturn_t ams_delta_serio_interrupt(int irq, void *dev_id) static int ams_delta_serio_open(struct serio *serio) { /* enable keyboard */ - ams_delta_latch2_write(AMD_DELTA_LATCH2_KEYBRD_PWR, - AMD_DELTA_LATCH2_KEYBRD_PWR); + gpio_set_value(AMS_DELTA_GPIO_PIN_KEYBRD_PWR, 1); return 0; } @@ -101,9 +100,32 @@ static int ams_delta_serio_open(struct serio *serio) static void ams_delta_serio_close(struct serio *serio) { /* disable keyboard */ - ams_delta_latch2_write(AMD_DELTA_LATCH2_KEYBRD_PWR, 0); + gpio_set_value(AMS_DELTA_GPIO_PIN_KEYBRD_PWR, 0); } +static const struct gpio ams_delta_gpios[] __initconst_or_module = { + { + .gpio = AMS_DELTA_GPIO_PIN_KEYBRD_DATA, + .flags = GPIOF_DIR_IN, + .label = "serio-data", + }, + { + .gpio = AMS_DELTA_GPIO_PIN_KEYBRD_CLK, + .flags = GPIOF_DIR_IN, + .label = "serio-clock", + }, + { + .gpio = AMS_DELTA_GPIO_PIN_KEYBRD_PWR, + .flags = GPIOF_OUT_INIT_LOW, + .label = "serio-power", + }, + { + .gpio = AMS_DELTA_GPIO_PIN_KEYBRD_DATAOUT, + .flags = GPIOF_OUT_INIT_LOW, + .label = "serio-dataout", + }, +}; + static int __init ams_delta_serio_init(void) { int err; @@ -123,19 +145,12 @@ static int __init ams_delta_serio_init(void) strlcpy(ams_delta_serio->phys, "GPIO/serio0", sizeof(ams_delta_serio->phys)); - err = gpio_request(AMS_DELTA_GPIO_PIN_KEYBRD_DATA, "serio-data"); + err = gpio_request_array(ams_delta_gpios, + ARRAY_SIZE(ams_delta_gpios)); if (err) { - pr_err("ams_delta_serio: Couldn't request gpio pin for data\n"); + pr_err("ams_delta_serio: Couldn't request gpio pins\n"); goto serio; } - gpio_direction_input(AMS_DELTA_GPIO_PIN_KEYBRD_DATA); - - err = gpio_request(AMS_DELTA_GPIO_PIN_KEYBRD_CLK, "serio-clock"); - if (err) { - pr_err("ams_delta_serio: couldn't request gpio pin for clock\n"); - goto gpio_data; - } - gpio_direction_input(AMS_DELTA_GPIO_PIN_KEYBRD_CLK); err = request_irq(gpio_to_irq(AMS_DELTA_GPIO_PIN_KEYBRD_CLK), ams_delta_serio_interrupt, IRQ_TYPE_EDGE_RISING, @@ -143,7 +158,7 @@ static int __init ams_delta_serio_init(void) if (err < 0) { pr_err("ams_delta_serio: couldn't request gpio interrupt %d\n", gpio_to_irq(AMS_DELTA_GPIO_PIN_KEYBRD_CLK)); - goto gpio_clk; + goto gpio; } /* * Since GPIO register handling for keyboard clock pin is performed @@ -157,10 +172,9 @@ static int __init ams_delta_serio_init(void) dev_info(&ams_delta_serio->dev, "%s\n", ams_delta_serio->name); return 0; -gpio_clk: - gpio_free(AMS_DELTA_GPIO_PIN_KEYBRD_CLK); -gpio_data: - gpio_free(AMS_DELTA_GPIO_PIN_KEYBRD_DATA); +gpio: + gpio_free_array(ams_delta_gpios, + ARRAY_SIZE(ams_delta_gpios)); serio: kfree(ams_delta_serio); return err; @@ -170,8 +184,8 @@ module_init(ams_delta_serio_init); static void __exit ams_delta_serio_exit(void) { serio_unregister_port(ams_delta_serio); - free_irq(OMAP_GPIO_IRQ(AMS_DELTA_GPIO_PIN_KEYBRD_CLK), 0); - gpio_free(AMS_DELTA_GPIO_PIN_KEYBRD_CLK); - gpio_free(AMS_DELTA_GPIO_PIN_KEYBRD_DATA); + free_irq(gpio_to_irq(AMS_DELTA_GPIO_PIN_KEYBRD_CLK), 0); + gpio_free_array(ams_delta_gpios, + ARRAY_SIZE(ams_delta_gpios)); } module_exit(ams_delta_serio_exit); diff --git a/drivers/input/serio/at32psif.c b/drivers/input/serio/at32psif.c index 95280f9207e1..36e799c31f5e 100644 --- a/drivers/input/serio/at32psif.c +++ b/drivers/input/serio/at32psif.c @@ -98,9 +98,9 @@ struct psif { struct serio *io; void __iomem *regs; unsigned int irq; - unsigned int open; /* Prevent concurrent writes to PSIF THR. */ spinlock_t lock; + bool open; }; static irqreturn_t psif_interrupt(int irq, void *_ptr) @@ -164,7 +164,7 @@ static int psif_open(struct serio *io) psif_writel(psif, CR, PSIF_BIT(CR_TXEN) | PSIF_BIT(CR_RXEN)); psif_writel(psif, IER, PSIF_BIT(RXRDY)); - psif->open = 1; + psif->open = true; out: return retval; } @@ -173,7 +173,7 @@ static void psif_close(struct serio *io) { struct psif *psif = io->port_data; - psif->open = 0; + psif->open = false; psif_writel(psif, IDR, ~0UL); psif_writel(psif, CR, PSIF_BIT(CR_TXDIS) | PSIF_BIT(CR_RXDIS)); @@ -319,9 +319,10 @@ static int __exit psif_remove(struct platform_device *pdev) return 0; } -#ifdef CONFIG_PM -static int psif_suspend(struct platform_device *pdev, pm_message_t state) +#ifdef CONFIG_PM_SLEEP +static int psif_suspend(struct device *dev) { + struct platform_device *pdev = to_platform_device(dev); struct psif *psif = platform_get_drvdata(pdev); if (psif->open) { @@ -332,8 +333,9 @@ static int psif_suspend(struct platform_device *pdev, pm_message_t state) return 0; } -static int psif_resume(struct platform_device *pdev) +static int psif_resume(struct device *dev) { + struct platform_device *pdev = to_platform_device(dev); struct psif *psif = platform_get_drvdata(pdev); if (psif->open) { @@ -344,19 +346,17 @@ static int psif_resume(struct platform_device *pdev) return 0; } -#else -#define psif_suspend NULL -#define psif_resume NULL #endif +static SIMPLE_DEV_PM_OPS(psif_pm_ops, psif_suspend, psif_resume); + static struct platform_driver psif_driver = { .remove = __exit_p(psif_remove), .driver = { .name = "atmel_psif", .owner = THIS_MODULE, + .pm = &psif_pm_ops, }, - .suspend = psif_suspend, - .resume = psif_resume, }; static int __init psif_init(void) diff --git a/drivers/input/serio/hp_sdc.c b/drivers/input/serio/hp_sdc.c index be3316073ae7..09a089996ded 100644 --- a/drivers/input/serio/hp_sdc.c +++ b/drivers/input/serio/hp_sdc.c @@ -71,7 +71,6 @@ #include <linux/slab.h> #include <linux/hil.h> #include <asm/io.h> -#include <asm/system.h> /* Machine-specific abstraction */ diff --git a/drivers/input/serio/maceps2.c b/drivers/input/serio/maceps2.c index 558200e96d0f..61da763b1209 100644 --- a/drivers/input/serio/maceps2.c +++ b/drivers/input/serio/maceps2.c @@ -21,7 +21,6 @@ #include <asm/io.h> #include <asm/irq.h> -#include <asm/system.h> #include <asm/ip32/mace.h> #include <asm/ip32/ip32_ints.h> diff --git a/drivers/input/serio/q40kbd.c b/drivers/input/serio/q40kbd.c index 5eb84b3b67fb..0c0df7f73802 100644 --- a/drivers/input/serio/q40kbd.c +++ b/drivers/input/serio/q40kbd.c @@ -44,26 +44,31 @@ #include <asm/irq.h> #include <asm/q40ints.h> +#define DRV_NAME "q40kbd" + MODULE_AUTHOR("Vojtech Pavlik <vojtech@ucw.cz>"); MODULE_DESCRIPTION("Q40 PS/2 keyboard controller driver"); MODULE_LICENSE("GPL"); +MODULE_ALIAS("platform:" DRV_NAME); -static DEFINE_SPINLOCK(q40kbd_lock); -static struct serio *q40kbd_port; -static struct platform_device *q40kbd_device; +struct q40kbd { + struct serio *port; + spinlock_t lock; +}; static irqreturn_t q40kbd_interrupt(int irq, void *dev_id) { + struct q40kbd *q40kbd = dev_id; unsigned long flags; - spin_lock_irqsave(&q40kbd_lock, flags); + spin_lock_irqsave(&q40kbd->lock, flags); if (Q40_IRQ_KEYB_MASK & master_inb(INTERRUPT_REG)) - serio_interrupt(q40kbd_port, master_inb(KEYCODE_REG), 0); + serio_interrupt(q40kbd->port, master_inb(KEYCODE_REG), 0); master_outb(-1, KEYBOARD_UNLOCK_REG); - spin_unlock_irqrestore(&q40kbd_lock, flags); + spin_unlock_irqrestore(&q40kbd->lock, flags); return IRQ_HANDLED; } @@ -72,17 +77,23 @@ static irqreturn_t q40kbd_interrupt(int irq, void *dev_id) * q40kbd_flush() flushes all data that may be in the keyboard buffers */ -static void q40kbd_flush(void) +static void q40kbd_flush(struct q40kbd *q40kbd) { int maxread = 100; unsigned long flags; - spin_lock_irqsave(&q40kbd_lock, flags); + spin_lock_irqsave(&q40kbd->lock, flags); while (maxread-- && (Q40_IRQ_KEYB_MASK & master_inb(INTERRUPT_REG))) master_inb(KEYCODE_REG); - spin_unlock_irqrestore(&q40kbd_lock, flags); + spin_unlock_irqrestore(&q40kbd->lock, flags); +} + +static void q40kbd_stop(void) +{ + master_outb(0, KEY_IRQ_ENABLE_REG); + master_outb(-1, KEYBOARD_UNLOCK_REG); } /* @@ -92,12 +103,9 @@ static void q40kbd_flush(void) static int q40kbd_open(struct serio *port) { - q40kbd_flush(); + struct q40kbd *q40kbd = port->port_data; - if (request_irq(Q40_IRQ_KEYBOARD, q40kbd_interrupt, 0, "q40kbd", NULL)) { - printk(KERN_ERR "q40kbd.c: Can't get irq %d.\n", Q40_IRQ_KEYBOARD); - return -EBUSY; - } + q40kbd_flush(q40kbd); /* off we go */ master_outb(-1, KEYBOARD_UNLOCK_REG); @@ -108,36 +116,72 @@ static int q40kbd_open(struct serio *port) static void q40kbd_close(struct serio *port) { - master_outb(0, KEY_IRQ_ENABLE_REG); - master_outb(-1, KEYBOARD_UNLOCK_REG); - free_irq(Q40_IRQ_KEYBOARD, NULL); + struct q40kbd *q40kbd = port->port_data; - q40kbd_flush(); + q40kbd_stop(); + q40kbd_flush(q40kbd); } -static int __devinit q40kbd_probe(struct platform_device *dev) +static int __devinit q40kbd_probe(struct platform_device *pdev) { - q40kbd_port = kzalloc(sizeof(struct serio), GFP_KERNEL); - if (!q40kbd_port) - return -ENOMEM; - - q40kbd_port->id.type = SERIO_8042; - q40kbd_port->open = q40kbd_open; - q40kbd_port->close = q40kbd_close; - q40kbd_port->dev.parent = &dev->dev; - strlcpy(q40kbd_port->name, "Q40 Kbd Port", sizeof(q40kbd_port->name)); - strlcpy(q40kbd_port->phys, "Q40", sizeof(q40kbd_port->phys)); - - serio_register_port(q40kbd_port); + struct q40kbd *q40kbd; + struct serio *port; + int error; + + q40kbd = kzalloc(sizeof(struct q40kbd), GFP_KERNEL); + port = kzalloc(sizeof(struct serio), GFP_KERNEL); + if (!q40kbd || !port) { + error = -ENOMEM; + goto err_free_mem; + } + + q40kbd->port = port; + spin_lock_init(&q40kbd->lock); + + port->id.type = SERIO_8042; + port->open = q40kbd_open; + port->close = q40kbd_close; + port->port_data = q40kbd; + port->dev.parent = &pdev->dev; + strlcpy(port->name, "Q40 Kbd Port", sizeof(port->name)); + strlcpy(port->phys, "Q40", sizeof(port->phys)); + + q40kbd_stop(); + + error = request_irq(Q40_IRQ_KEYBOARD, q40kbd_interrupt, 0, + DRV_NAME, q40kbd); + if (error) { + dev_err(&pdev->dev, "Can't get irq %d.\n", Q40_IRQ_KEYBOARD); + goto err_free_mem; + } + + serio_register_port(q40kbd->port); + + platform_set_drvdata(pdev, q40kbd); printk(KERN_INFO "serio: Q40 kbd registered\n"); return 0; + +err_free_mem: + kfree(port); + kfree(q40kbd); + return error; } -static int __devexit q40kbd_remove(struct platform_device *dev) +static int __devexit q40kbd_remove(struct platform_device *pdev) { - serio_unregister_port(q40kbd_port); - + struct q40kbd *q40kbd = platform_get_drvdata(pdev); + + /* + * q40kbd_close() will be called as part of unregistering + * and will ensure that IRQ is turned off, so it is safe + * to unregister port first and free IRQ later. + */ + serio_unregister_port(q40kbd->port); + free_irq(Q40_IRQ_KEYBOARD, q40kbd); + kfree(q40kbd); + + platform_set_drvdata(pdev, NULL); return 0; } @@ -146,41 +190,16 @@ static struct platform_driver q40kbd_driver = { .name = "q40kbd", .owner = THIS_MODULE, }, - .probe = q40kbd_probe, .remove = __devexit_p(q40kbd_remove), }; static int __init q40kbd_init(void) { - int error; - - if (!MACH_IS_Q40) - return -ENODEV; - - error = platform_driver_register(&q40kbd_driver); - if (error) - return error; - - q40kbd_device = platform_device_alloc("q40kbd", -1); - if (!q40kbd_device) - goto err_unregister_driver; - - error = platform_device_add(q40kbd_device); - if (error) - goto err_free_device; - - return 0; - - err_free_device: - platform_device_put(q40kbd_device); - err_unregister_driver: - platform_driver_unregister(&q40kbd_driver); - return error; + return platform_driver_probe(&q40kbd_driver, q40kbd_probe); } static void __exit q40kbd_exit(void) { - platform_device_unregister(q40kbd_device); platform_driver_unregister(&q40kbd_driver); } diff --git a/drivers/input/serio/rpckbd.c b/drivers/input/serio/rpckbd.c index 8b44ddc8041c..2af5df6a8fba 100644 --- a/drivers/input/serio/rpckbd.c +++ b/drivers/input/serio/rpckbd.c @@ -36,16 +36,19 @@ #include <linux/io.h> #include <linux/slab.h> -#include <asm/irq.h> #include <mach/hardware.h> #include <asm/hardware/iomd.h> -#include <asm/system.h> MODULE_AUTHOR("Vojtech Pavlik, Russell King"); MODULE_DESCRIPTION("Acorn RiscPC PS/2 keyboard controller driver"); MODULE_LICENSE("GPL"); MODULE_ALIAS("platform:kart"); +struct rpckbd_data { + int tx_irq; + int rx_irq; +}; + static int rpckbd_write(struct serio *port, unsigned char val) { while (!(iomd_readb(IOMD_KCTRL) & (1 << 7))) @@ -78,19 +81,21 @@ static irqreturn_t rpckbd_tx(int irq, void *dev_id) static int rpckbd_open(struct serio *port) { + struct rpckbd_data *rpckbd = port->port_data; + /* Reset the keyboard state machine. */ iomd_writeb(0, IOMD_KCTRL); iomd_writeb(8, IOMD_KCTRL); iomd_readb(IOMD_KARTRX); - if (request_irq(IRQ_KEYBOARDRX, rpckbd_rx, 0, "rpckbd", port) != 0) { + if (request_irq(rpckbd->rx_irq, rpckbd_rx, 0, "rpckbd", port) != 0) { printk(KERN_ERR "rpckbd.c: Could not allocate keyboard receive IRQ\n"); return -EBUSY; } - if (request_irq(IRQ_KEYBOARDTX, rpckbd_tx, 0, "rpckbd", port) != 0) { + if (request_irq(rpckbd->tx_irq, rpckbd_tx, 0, "rpckbd", port) != 0) { printk(KERN_ERR "rpckbd.c: Could not allocate keyboard transmit IRQ\n"); - free_irq(IRQ_KEYBOARDRX, port); + free_irq(rpckbd->rx_irq, port); return -EBUSY; } @@ -99,8 +104,10 @@ static int rpckbd_open(struct serio *port) static void rpckbd_close(struct serio *port) { - free_irq(IRQ_KEYBOARDRX, port); - free_irq(IRQ_KEYBOARDTX, port); + struct rpckbd_data *rpckbd = port->port_data; + + free_irq(rpckbd->rx_irq, port); + free_irq(rpckbd->tx_irq, port); } /* @@ -109,17 +116,35 @@ static void rpckbd_close(struct serio *port) */ static int __devinit rpckbd_probe(struct platform_device *dev) { + struct rpckbd_data *rpckbd; struct serio *serio; + int tx_irq, rx_irq; + + rx_irq = platform_get_irq(dev, 0); + if (rx_irq <= 0) + return rx_irq < 0 ? rx_irq : -ENXIO; + + tx_irq = platform_get_irq(dev, 1); + if (tx_irq <= 0) + return tx_irq < 0 ? tx_irq : -ENXIO; serio = kzalloc(sizeof(struct serio), GFP_KERNEL); - if (!serio) + rpckbd = kzalloc(sizeof(*rpckbd), GFP_KERNEL); + if (!serio || !rpckbd) { + kfree(rpckbd); + kfree(serio); return -ENOMEM; + } + + rpckbd->rx_irq = rx_irq; + rpckbd->tx_irq = tx_irq; serio->id.type = SERIO_8042; serio->write = rpckbd_write; serio->open = rpckbd_open; serio->close = rpckbd_close; serio->dev.parent = &dev->dev; + serio->port_data = rpckbd; strlcpy(serio->name, "RiscPC PS/2 kbd port", sizeof(serio->name)); strlcpy(serio->phys, "rpckbd/serio0", sizeof(serio->phys)); @@ -131,7 +156,11 @@ static int __devinit rpckbd_probe(struct platform_device *dev) static int __devexit rpckbd_remove(struct platform_device *dev) { struct serio *serio = platform_get_drvdata(dev); + struct rpckbd_data *rpckbd = serio->port_data; + serio_unregister_port(serio); + kfree(rpckbd); + return 0; } diff --git a/drivers/input/serio/sa1111ps2.c b/drivers/input/serio/sa1111ps2.c index 44fc8b4bcd81..389766707534 100644 --- a/drivers/input/serio/sa1111ps2.c +++ b/drivers/input/serio/sa1111ps2.c @@ -20,10 +20,29 @@ #include <linux/spinlock.h> #include <asm/io.h> -#include <asm/system.h> #include <asm/hardware/sa1111.h> +#define PS2CR 0x0000 +#define PS2STAT 0x0004 +#define PS2DATA 0x0008 +#define PS2CLKDIV 0x000c +#define PS2PRECNT 0x0010 + +#define PS2CR_ENA 0x08 +#define PS2CR_FKD 0x02 +#define PS2CR_FKC 0x01 + +#define PS2STAT_STP 0x0100 +#define PS2STAT_TXE 0x0080 +#define PS2STAT_TXB 0x0040 +#define PS2STAT_RXF 0x0020 +#define PS2STAT_RXB 0x0010 +#define PS2STAT_ENA 0x0008 +#define PS2STAT_RXP 0x0004 +#define PS2STAT_KBD 0x0002 +#define PS2STAT_KBC 0x0001 + struct ps2if { struct serio *io; struct sa1111_dev *dev; @@ -45,22 +64,22 @@ static irqreturn_t ps2_rxint(int irq, void *dev_id) struct ps2if *ps2if = dev_id; unsigned int scancode, flag, status; - status = sa1111_readl(ps2if->base + SA1111_PS2STAT); + status = sa1111_readl(ps2if->base + PS2STAT); while (status & PS2STAT_RXF) { if (status & PS2STAT_STP) - sa1111_writel(PS2STAT_STP, ps2if->base + SA1111_PS2STAT); + sa1111_writel(PS2STAT_STP, ps2if->base + PS2STAT); flag = (status & PS2STAT_STP ? SERIO_FRAME : 0) | (status & PS2STAT_RXP ? 0 : SERIO_PARITY); - scancode = sa1111_readl(ps2if->base + SA1111_PS2DATA) & 0xff; + scancode = sa1111_readl(ps2if->base + PS2DATA) & 0xff; if (hweight8(scancode) & 1) flag ^= SERIO_PARITY; serio_interrupt(ps2if->io, scancode, flag); - status = sa1111_readl(ps2if->base + SA1111_PS2STAT); + status = sa1111_readl(ps2if->base + PS2STAT); } return IRQ_HANDLED; @@ -75,12 +94,12 @@ static irqreturn_t ps2_txint(int irq, void *dev_id) unsigned int status; spin_lock(&ps2if->lock); - status = sa1111_readl(ps2if->base + SA1111_PS2STAT); + status = sa1111_readl(ps2if->base + PS2STAT); if (ps2if->head == ps2if->tail) { disable_irq_nosync(irq); /* done */ } else if (status & PS2STAT_TXE) { - sa1111_writel(ps2if->buf[ps2if->tail], ps2if->base + SA1111_PS2DATA); + sa1111_writel(ps2if->buf[ps2if->tail], ps2if->base + PS2DATA); ps2if->tail = (ps2if->tail + 1) & (sizeof(ps2if->buf) - 1); } spin_unlock(&ps2if->lock); @@ -103,8 +122,8 @@ static int ps2_write(struct serio *io, unsigned char val) /* * If the TX register is empty, we can go straight out. */ - if (sa1111_readl(ps2if->base + SA1111_PS2STAT) & PS2STAT_TXE) { - sa1111_writel(val, ps2if->base + SA1111_PS2DATA); + if (sa1111_readl(ps2if->base + PS2STAT) & PS2STAT_TXE) { + sa1111_writel(val, ps2if->base + PS2DATA); } else { if (ps2if->head == ps2if->tail) enable_irq(ps2if->dev->irq[1]); @@ -124,13 +143,16 @@ static int ps2_open(struct serio *io) struct ps2if *ps2if = io->port_data; int ret; - sa1111_enable_device(ps2if->dev); + ret = sa1111_enable_device(ps2if->dev); + if (ret) + return ret; ret = request_irq(ps2if->dev->irq[0], ps2_rxint, 0, SA1111_DRIVER_NAME(ps2if->dev), ps2if); if (ret) { printk(KERN_ERR "sa1111ps2: could not allocate IRQ%d: %d\n", ps2if->dev->irq[0], ret); + sa1111_disable_device(ps2if->dev); return ret; } @@ -140,6 +162,7 @@ static int ps2_open(struct serio *io) printk(KERN_ERR "sa1111ps2: could not allocate IRQ%d: %d\n", ps2if->dev->irq[1], ret); free_irq(ps2if->dev->irq[0], ps2if); + sa1111_disable_device(ps2if->dev); return ret; } @@ -147,7 +170,7 @@ static int ps2_open(struct serio *io) enable_irq_wake(ps2if->dev->irq[0]); - sa1111_writel(PS2CR_ENA, ps2if->base + SA1111_PS2CR); + sa1111_writel(PS2CR_ENA, ps2if->base + PS2CR); return 0; } @@ -155,7 +178,7 @@ static void ps2_close(struct serio *io) { struct ps2if *ps2if = io->port_data; - sa1111_writel(0, ps2if->base + SA1111_PS2CR); + sa1111_writel(0, ps2if->base + PS2CR); disable_irq_wake(ps2if->dev->irq[0]); @@ -175,7 +198,7 @@ static void __devinit ps2_clear_input(struct ps2if *ps2if) int maxread = 100; while (maxread--) { - if ((sa1111_readl(ps2if->base + SA1111_PS2DATA) & 0xff) == 0xff) + if ((sa1111_readl(ps2if->base + PS2DATA) & 0xff) == 0xff) break; } } @@ -185,11 +208,11 @@ static unsigned int __devinit ps2_test_one(struct ps2if *ps2if, { unsigned int val; - sa1111_writel(PS2CR_ENA | mask, ps2if->base + SA1111_PS2CR); + sa1111_writel(PS2CR_ENA | mask, ps2if->base + PS2CR); udelay(2); - val = sa1111_readl(ps2if->base + SA1111_PS2STAT); + val = sa1111_readl(ps2if->base + PS2STAT); return val & (PS2STAT_KBC | PS2STAT_KBD); } @@ -220,7 +243,7 @@ static int __devinit ps2_test(struct ps2if *ps2if) ret = -ENODEV; } - sa1111_writel(0, ps2if->base + SA1111_PS2CR); + sa1111_writel(0, ps2if->base + PS2CR); return ret; } @@ -274,8 +297,8 @@ static int __devinit ps2_probe(struct sa1111_dev *dev) sa1111_enable_device(ps2if->dev); /* Incoming clock is 8MHz */ - sa1111_writel(0, ps2if->base + SA1111_PS2CLKDIV); - sa1111_writel(127, ps2if->base + SA1111_PS2PRECNT); + sa1111_writel(0, ps2if->base + PS2CLKDIV); + sa1111_writel(127, ps2if->base + PS2PRECNT); /* * Flush any pending input. @@ -330,6 +353,7 @@ static int __devexit ps2_remove(struct sa1111_dev *dev) static struct sa1111_driver ps2_driver = { .drv = { .name = "sa1111-ps2", + .owner = THIS_MODULE, }, .devid = SA1111_DEVID_PS2, .probe = ps2_probe, diff --git a/drivers/input/serio/serio.c b/drivers/input/serio/serio.c index ba70058e2be3..d0f7533dbf88 100644 --- a/drivers/input/serio/serio.c +++ b/drivers/input/serio/serio.c @@ -441,7 +441,6 @@ static ssize_t serio_rebind_driver(struct device *dev, struct device_attribute * } else if ((drv = driver_find(buf, &serio_bus)) != NULL) { serio_disconnect_port(serio); error = serio_bind_driver(serio, to_serio_driver(drv)); - put_driver(drv); serio_remove_duplicate_events(serio, SERIO_RESCAN_PORT); } else { error = -EINVAL; diff --git a/drivers/input/tablet/Kconfig b/drivers/input/tablet/Kconfig index 58a87755b936..bed7cbf84cfd 100644 --- a/drivers/input/tablet/Kconfig +++ b/drivers/input/tablet/Kconfig @@ -76,7 +76,10 @@ config TABLET_USB_KBTAB config TABLET_USB_WACOM tristate "Wacom Intuos/Graphire tablet support (USB)" depends on USB_ARCH_HAS_HCD + select POWER_SUPPLY select USB + select NEW_LEDS + select LEDS_CLASS help Say Y here if you want to use the USB version of the Wacom Intuos or Graphire tablet. Make sure to say Y to "Mouse support" diff --git a/drivers/input/tablet/wacom.h b/drivers/input/tablet/wacom.h index 0783864a7dc2..b4842d0e61dd 100644 --- a/drivers/input/tablet/wacom.h +++ b/drivers/input/tablet/wacom.h @@ -88,6 +88,7 @@ #include <linux/mod_devicetable.h> #include <linux/init.h> #include <linux/usb/input.h> +#include <linux/power_supply.h> #include <asm/unaligned.h> /* @@ -112,6 +113,7 @@ struct wacom { struct urb *irq; struct wacom_wac wacom_wac; struct mutex lock; + struct work_struct work; bool open; char phys[32]; struct wacom_led { @@ -120,8 +122,15 @@ struct wacom { u8 hlv; /* status led brightness button pressed (1..127) */ u8 img_lum; /* OLED matrix display brightness */ } led; + struct power_supply battery; }; +static inline void wacom_schedule_work(struct wacom_wac *wacom_wac) +{ + struct wacom *wacom = container_of(wacom_wac, struct wacom, wacom_wac); + schedule_work(&wacom->work); +} + extern const struct usb_device_id wacom_ids[]; void wacom_wac_irq(struct wacom_wac *wacom_wac, size_t len); diff --git a/drivers/input/tablet/wacom_sys.c b/drivers/input/tablet/wacom_sys.c index 2a97b7e76db1..0d269212931e 100644 --- a/drivers/input/tablet/wacom_sys.c +++ b/drivers/input/tablet/wacom_sys.c @@ -167,6 +167,19 @@ static void wacom_close(struct input_dev *dev) usb_autopm_put_interface(wacom->intf); } +/* + * Static values for max X/Y and resolution of Pen interface is stored in + * features. This mean physical size of active area can be computed. + * This is useful to do when Pen and Touch have same active area of tablet. + * This means for Touch device, we only need to find max X/Y value and we + * have enough information to compute resolution of touch. + */ +static void wacom_set_phy_from_res(struct wacom_features *features) +{ + features->x_phy = (features->x_max * 100) / features->x_resolution; + features->y_phy = (features->y_max * 100) / features->y_resolution; +} + static int wacom_parse_logical_collection(unsigned char *report, struct wacom_features *features) { @@ -176,17 +189,9 @@ static int wacom_parse_logical_collection(unsigned char *report, /* Logical collection is only used by 3rd gen Bamboo Touch */ features->pktlen = WACOM_PKGLEN_BBTOUCH3; - features->device_type = BTN_TOOL_DOUBLETAP; - - /* - * Stylus and Touch have same active area - * so compute physical size based on stylus - * data before its overwritten. - */ - features->x_phy = - (features->x_max * features->x_resolution) / 100; - features->y_phy = - (features->y_max * features->y_resolution) / 100; + features->device_type = BTN_TOOL_FINGER; + + wacom_set_phy_from_res(features); features->x_max = features->y_max = get_unaligned_le16(&report[10]); @@ -286,12 +291,10 @@ static int wacom_parse_hid(struct usb_interface *intf, if (features->type == TABLETPC2FG) { /* need to reset back */ features->pktlen = WACOM_PKGLEN_TPC2FG; - features->device_type = BTN_TOOL_DOUBLETAP; } if (features->type == BAMBOO_PT) { /* need to reset back */ features->pktlen = WACOM_PKGLEN_BBTOUCH; - features->device_type = BTN_TOOL_DOUBLETAP; features->x_phy = get_unaligned_le16(&report[i + 5]); features->x_max = @@ -325,7 +328,6 @@ static int wacom_parse_hid(struct usb_interface *intf, if (features->type == TABLETPC2FG) { /* need to reset back */ features->pktlen = WACOM_PKGLEN_TPC2FG; - features->device_type = BTN_TOOL_DOUBLETAP; features->y_max = get_unaligned_le16(&report[i + 3]); features->y_phy = @@ -334,7 +336,6 @@ static int wacom_parse_hid(struct usb_interface *intf, } else if (features->type == BAMBOO_PT) { /* need to reset back */ features->pktlen = WACOM_PKGLEN_BBTOUCH; - features->device_type = BTN_TOOL_DOUBLETAP; features->y_phy = get_unaligned_le16(&report[i + 3]); features->y_max = @@ -426,6 +427,7 @@ static int wacom_query_tablet_data(struct usb_interface *intf, struct wacom_feat report_id, rep_data, 4, 1); } while ((error < 0 || rep_data[1] != 4) && limit++ < WAC_MSG_RETRIES); } else if (features->type != TABLETPC && + features->type != WIRELESS && features->device_type == BTN_TOOL_PEN) { do { rep_data[0] = 2; @@ -458,6 +460,21 @@ static int wacom_retrieve_hid_descriptor(struct usb_interface *intf, features->pressure_fuzz = 0; features->distance_fuzz = 0; + /* + * The wireless device HID is basic and layout conflicts with + * other tablets (monitor and touch interface can look like pen). + * Skip the query for this type and modify defaults based on + * interface number. + */ + if (features->type == WIRELESS) { + if (intf->cur_altsetting->desc.bInterfaceNumber == 0) { + features->device_type = 0; + } else if (intf->cur_altsetting->desc.bInterfaceNumber == 2) { + features->device_type = BTN_TOOL_DOUBLETAP; + features->pktlen = WACOM_PKGLEN_BBTOUCH3; + } + } + /* only Tablet PCs and Bamboo P&T need to retrieve the info */ if ((features->type != TABLETPC) && (features->type != TABLETPC2FG) && (features->type != BAMBOO_PT)) @@ -826,6 +843,152 @@ static void wacom_destroy_leds(struct wacom *wacom) } } +static enum power_supply_property wacom_battery_props[] = { + POWER_SUPPLY_PROP_CAPACITY +}; + +static int wacom_battery_get_property(struct power_supply *psy, + enum power_supply_property psp, + union power_supply_propval *val) +{ + struct wacom *wacom = container_of(psy, struct wacom, battery); + int ret = 0; + + switch (psp) { + case POWER_SUPPLY_PROP_CAPACITY: + val->intval = + wacom->wacom_wac.battery_capacity * 100 / 31; + break; + default: + ret = -EINVAL; + break; + } + + return ret; +} + +static int wacom_initialize_battery(struct wacom *wacom) +{ + int error = 0; + + if (wacom->wacom_wac.features.quirks & WACOM_QUIRK_MONITOR) { + wacom->battery.properties = wacom_battery_props; + wacom->battery.num_properties = ARRAY_SIZE(wacom_battery_props); + wacom->battery.get_property = wacom_battery_get_property; + wacom->battery.name = "wacom_battery"; + wacom->battery.type = POWER_SUPPLY_TYPE_BATTERY; + wacom->battery.use_for_apm = 0; + + error = power_supply_register(&wacom->usbdev->dev, + &wacom->battery); + } + + return error; +} + +static void wacom_destroy_battery(struct wacom *wacom) +{ + if (wacom->wacom_wac.features.quirks & WACOM_QUIRK_MONITOR) + power_supply_unregister(&wacom->battery); +} + +static int wacom_register_input(struct wacom *wacom) +{ + struct input_dev *input_dev; + struct usb_interface *intf = wacom->intf; + struct usb_device *dev = interface_to_usbdev(intf); + struct wacom_wac *wacom_wac = &(wacom->wacom_wac); + int error; + + input_dev = input_allocate_device(); + if (!input_dev) + return -ENOMEM; + + input_dev->name = wacom_wac->name; + input_dev->dev.parent = &intf->dev; + input_dev->open = wacom_open; + input_dev->close = wacom_close; + usb_to_input_id(dev, &input_dev->id); + input_set_drvdata(input_dev, wacom); + + wacom_wac->input = input_dev; + wacom_setup_input_capabilities(input_dev, wacom_wac); + + error = input_register_device(input_dev); + if (error) { + input_free_device(input_dev); + wacom_wac->input = NULL; + } + + return error; +} + +static void wacom_wireless_work(struct work_struct *work) +{ + struct wacom *wacom = container_of(work, struct wacom, work); + struct usb_device *usbdev = wacom->usbdev; + struct wacom_wac *wacom_wac = &wacom->wacom_wac; + + /* + * Regardless if this is a disconnect or a new tablet, + * remove any existing input devices. + */ + + /* Stylus interface */ + wacom = usb_get_intfdata(usbdev->config->interface[1]); + if (wacom->wacom_wac.input) + input_unregister_device(wacom->wacom_wac.input); + wacom->wacom_wac.input = 0; + + /* Touch interface */ + wacom = usb_get_intfdata(usbdev->config->interface[2]); + if (wacom->wacom_wac.input) + input_unregister_device(wacom->wacom_wac.input); + wacom->wacom_wac.input = 0; + + if (wacom_wac->pid == 0) { + printk(KERN_INFO "wacom: wireless tablet disconnected\n"); + } else { + const struct usb_device_id *id = wacom_ids; + + printk(KERN_INFO + "wacom: wireless tablet connected with PID %x\n", + wacom_wac->pid); + + while (id->match_flags) { + if (id->idVendor == USB_VENDOR_ID_WACOM && + id->idProduct == wacom_wac->pid) + break; + id++; + } + + if (!id->match_flags) { + printk(KERN_INFO + "wacom: ignorning unknown PID.\n"); + return; + } + + /* Stylus interface */ + wacom = usb_get_intfdata(usbdev->config->interface[1]); + wacom_wac = &wacom->wacom_wac; + wacom_wac->features = + *((struct wacom_features *)id->driver_info); + wacom_wac->features.device_type = BTN_TOOL_PEN; + wacom_register_input(wacom); + + /* Touch interface */ + wacom = usb_get_intfdata(usbdev->config->interface[2]); + wacom_wac = &wacom->wacom_wac; + wacom_wac->features = + *((struct wacom_features *)id->driver_info); + wacom_wac->features.pktlen = WACOM_PKGLEN_BBTOUCH3; + wacom_wac->features.device_type = BTN_TOOL_FINGER; + wacom_set_phy_from_res(&wacom_wac->features); + wacom_wac->features.x_max = wacom_wac->features.y_max = 4096; + wacom_register_input(wacom); + } +} + static int wacom_probe(struct usb_interface *intf, const struct usb_device_id *id) { struct usb_device *dev = interface_to_usbdev(intf); @@ -833,18 +996,14 @@ static int wacom_probe(struct usb_interface *intf, const struct usb_device_id *i struct wacom *wacom; struct wacom_wac *wacom_wac; struct wacom_features *features; - struct input_dev *input_dev; int error; if (!id->driver_info) return -EINVAL; wacom = kzalloc(sizeof(struct wacom), GFP_KERNEL); - input_dev = input_allocate_device(); - if (!wacom || !input_dev) { - error = -ENOMEM; - goto fail1; - } + if (!wacom) + return -ENOMEM; wacom_wac = &wacom->wacom_wac; wacom_wac->features = *((struct wacom_features *)id->driver_info); @@ -870,11 +1029,10 @@ static int wacom_probe(struct usb_interface *intf, const struct usb_device_id *i wacom->usbdev = dev; wacom->intf = intf; mutex_init(&wacom->lock); + INIT_WORK(&wacom->work, wacom_wireless_work); usb_make_path(dev, wacom->phys, sizeof(wacom->phys)); strlcat(wacom->phys, "/input0", sizeof(wacom->phys)); - wacom_wac->input = input_dev; - endpoint = &intf->cur_altsetting->endpoint[0].desc; /* Retrieve the physical and logical size for OEM devices */ @@ -898,15 +1056,6 @@ static int wacom_probe(struct usb_interface *intf, const struct usb_device_id *i goto fail3; } - input_dev->name = wacom_wac->name; - input_dev->dev.parent = &intf->dev; - input_dev->open = wacom_open; - input_dev->close = wacom_close; - usb_to_input_id(dev, &input_dev->id); - input_set_drvdata(input_dev, wacom); - - wacom_setup_input_capabilities(input_dev, wacom_wac); - usb_fill_int_urb(wacom->irq, dev, usb_rcvintpipe(dev, endpoint->bEndpointAddress), wacom_wac->data, features->pktlen, @@ -918,22 +1067,34 @@ static int wacom_probe(struct usb_interface *intf, const struct usb_device_id *i if (error) goto fail4; - error = input_register_device(input_dev); + error = wacom_initialize_battery(wacom); if (error) goto fail5; + if (!(features->quirks & WACOM_QUIRK_NO_INPUT)) { + error = wacom_register_input(wacom); + if (error) + goto fail6; + } + /* Note that if query fails it is not a hard failure */ wacom_query_tablet_data(intf, features); usb_set_intfdata(intf, wacom); + + if (features->quirks & WACOM_QUIRK_MONITOR) { + if (usb_submit_urb(wacom->irq, GFP_KERNEL)) + goto fail5; + } + return 0; + fail6: wacom_destroy_battery(wacom); fail5: wacom_destroy_leds(wacom); fail4: wacom_remove_shared_data(wacom_wac); fail3: usb_free_urb(wacom->irq); fail2: usb_free_coherent(dev, WACOM_PKGLEN_MAX, wacom_wac->data, wacom->data_dma); - fail1: input_free_device(input_dev); - kfree(wacom); + fail1: kfree(wacom); return error; } @@ -944,7 +1105,10 @@ static void wacom_disconnect(struct usb_interface *intf) usb_set_intfdata(intf, NULL); usb_kill_urb(wacom->irq); - input_unregister_device(wacom->wacom_wac.input); + cancel_work_sync(&wacom->work); + if (wacom->wacom_wac.input) + input_unregister_device(wacom->wacom_wac.input); + wacom_destroy_battery(wacom); wacom_destroy_leds(wacom); usb_free_urb(wacom->irq); usb_free_coherent(interface_to_usbdev(intf), WACOM_PKGLEN_MAX, @@ -976,7 +1140,8 @@ static int wacom_resume(struct usb_interface *intf) wacom_query_tablet_data(intf, features); wacom_led_control(wacom); - if (wacom->open && usb_submit_urb(wacom->irq, GFP_NOIO) < 0) + if ((wacom->open || features->quirks & WACOM_QUIRK_MONITOR) + && usb_submit_urb(wacom->irq, GFP_NOIO) < 0) rv = -EIO; mutex_unlock(&wacom->lock); diff --git a/drivers/input/tablet/wacom_wac.c b/drivers/input/tablet/wacom_wac.c index 88672ec296c1..cecd35c8f0b3 100644 --- a/drivers/input/tablet/wacom_wac.c +++ b/drivers/input/tablet/wacom_wac.c @@ -832,12 +832,24 @@ static int wacom_tpc_irq(struct wacom_wac *wacom, size_t len) dbg("wacom_tpc_irq: received report #%d", data[0]); - if (len == WACOM_PKGLEN_TPC1FG || data[0] == WACOM_REPORT_TPC1FG) - return wacom_tpc_single_touch(wacom, len); - else if (data[0] == WACOM_REPORT_TPC2FG) - return wacom_tpc_mt_touch(wacom); - else if (data[0] == WACOM_REPORT_PENABLED) - return wacom_tpc_pen(wacom); + switch (len) { + case WACOM_PKGLEN_TPC1FG: + return wacom_tpc_single_touch(wacom, len); + + case WACOM_PKGLEN_TPC2FG: + return wacom_tpc_mt_touch(wacom); + + default: + switch (data[0]) { + case WACOM_REPORT_TPC1FG: + case WACOM_REPORT_TPCHID: + case WACOM_REPORT_TPCST: + return wacom_tpc_single_touch(wacom, len); + + case WACOM_REPORT_PENABLED: + return wacom_tpc_pen(wacom); + } + } return 0; } @@ -926,7 +938,7 @@ static int wacom_bpt3_touch(struct wacom_wac *wacom) { struct input_dev *input = wacom->input; unsigned char *data = wacom->data; - int count = data[1] & 0x03; + int count = data[1] & 0x07; int i; if (data[0] != 0x02) @@ -1032,6 +1044,35 @@ static int wacom_bpt_irq(struct wacom_wac *wacom, size_t len) return 0; } +static int wacom_wireless_irq(struct wacom_wac *wacom, size_t len) +{ + unsigned char *data = wacom->data; + int connected; + + if (len != WACOM_PKGLEN_WIRELESS || data[0] != 0x80) + return 0; + + connected = data[1] & 0x01; + if (connected) { + int pid, battery; + + pid = get_unaligned_be16(&data[6]); + battery = data[5] & 0x3f; + if (wacom->pid != pid) { + wacom->pid = pid; + wacom_schedule_work(wacom); + } + wacom->battery_capacity = battery; + } else if (wacom->pid != 0) { + /* disconnected while previously connected */ + wacom->pid = 0; + wacom_schedule_work(wacom); + wacom->battery_capacity = 0; + } + + return 0; +} + void wacom_wac_irq(struct wacom_wac *wacom_wac, size_t len) { bool sync; @@ -1082,6 +1123,10 @@ void wacom_wac_irq(struct wacom_wac *wacom_wac, size_t len) sync = wacom_bpt_irq(wacom_wac, len); break; + case WIRELESS: + sync = wacom_wireless_irq(wacom_wac, len); + break; + default: sync = false; break; @@ -1143,7 +1188,7 @@ void wacom_setup_device_quirks(struct wacom_features *features) /* these device have multiple inputs */ if (features->type == TABLETPC || features->type == TABLETPC2FG || - features->type == BAMBOO_PT) + features->type == BAMBOO_PT || features->type == WIRELESS) features->quirks |= WACOM_QUIRK_MULTI_INPUT; /* quirk for bamboo touch with 2 low res touches */ @@ -1155,6 +1200,16 @@ void wacom_setup_device_quirks(struct wacom_features *features) features->y_fuzz <<= 5; features->quirks |= WACOM_QUIRK_BBTOUCH_LOWRES; } + + if (features->type == WIRELESS) { + + /* monitor never has input and pen/touch have delayed create */ + features->quirks |= WACOM_QUIRK_NO_INPUT; + + /* must be monitor interface if no device_type set */ + if (!features->device_type) + features->quirks |= WACOM_QUIRK_MONITOR; + } } static unsigned int wacom_calculate_touch_res(unsigned int logical_max, @@ -1317,7 +1372,7 @@ void wacom_setup_input_capabilities(struct input_dev *input_dev, break; case TABLETPC2FG: - if (features->device_type == BTN_TOOL_DOUBLETAP) { + if (features->device_type == BTN_TOOL_FINGER) { input_mt_init_slots(input_dev, 2); input_set_abs_params(input_dev, ABS_MT_TOOL_TYPE, @@ -1366,7 +1421,7 @@ void wacom_setup_input_capabilities(struct input_dev *input_dev, __set_bit(INPUT_PROP_POINTER, input_dev->propbit); - if (features->device_type == BTN_TOOL_DOUBLETAP) { + if (features->device_type == BTN_TOOL_FINGER) { __set_bit(BTN_LEFT, input_dev->keybit); __set_bit(BTN_FORWARD, input_dev->keybit); __set_bit(BTN_BACK, input_dev->keybit); @@ -1628,6 +1683,9 @@ static const struct wacom_features wacom_features_0xEC = static const struct wacom_features wacom_features_0x47 = { "Wacom Intuos2 6x8", WACOM_PKGLEN_INTUOS, 20320, 16240, 1023, 31, INTUOS, WACOM_INTUOS_RES, WACOM_INTUOS_RES }; +static const struct wacom_features wacom_features_0x84 = + { "Wacom Wireless Receiver", WACOM_PKGLEN_WIRELESS, 0, 0, 0, + 0, WIRELESS, 0, 0 }; static const struct wacom_features wacom_features_0xD0 = { "Wacom Bamboo 2FG", WACOM_PKGLEN_BBFUN, 14720, 9200, 1023, 31, BAMBOO_PT, WACOM_INTUOS_RES, WACOM_INTUOS_RES }; @@ -1754,6 +1812,7 @@ const struct usb_device_id wacom_ids[] = { { USB_DEVICE_DETAILED(0xCE, USB_CLASS_HID, USB_INTERFACE_SUBCLASS_BOOT, USB_INTERFACE_PROTOCOL_MOUSE) }, + { USB_DEVICE_WACOM(0x84) }, { USB_DEVICE_WACOM(0xD0) }, { USB_DEVICE_WACOM(0xD1) }, { USB_DEVICE_WACOM(0xD2) }, diff --git a/drivers/input/tablet/wacom_wac.h b/drivers/input/tablet/wacom_wac.h index 050acaefee7d..ba5a334e54d6 100644 --- a/drivers/input/tablet/wacom_wac.h +++ b/drivers/input/tablet/wacom_wac.h @@ -24,6 +24,7 @@ #define WACOM_PKGLEN_BBTOUCH 20 #define WACOM_PKGLEN_BBTOUCH3 64 #define WACOM_PKGLEN_BBPEN 10 +#define WACOM_PKGLEN_WIRELESS 32 /* device IDs */ #define STYLUS_DEVICE_ID 0x02 @@ -39,10 +40,14 @@ #define WACOM_REPORT_INTUOSPAD 12 #define WACOM_REPORT_TPC1FG 6 #define WACOM_REPORT_TPC2FG 13 +#define WACOM_REPORT_TPCHID 15 +#define WACOM_REPORT_TPCST 16 /* device quirks */ #define WACOM_QUIRK_MULTI_INPUT 0x0001 #define WACOM_QUIRK_BBTOUCH_LOWRES 0x0002 +#define WACOM_QUIRK_NO_INPUT 0x0004 +#define WACOM_QUIRK_MONITOR 0x0008 enum { PENPARTNER = 0, @@ -52,6 +57,7 @@ enum { PL, DTU, BAMBOO_PT, + WIRELESS, INTUOS, INTUOS3S, INTUOS3, @@ -105,6 +111,8 @@ struct wacom_wac { struct wacom_features features; struct wacom_shared *shared; struct input_dev *input; + int pid; + int battery_capacity; }; #endif diff --git a/drivers/input/touchscreen/Kconfig b/drivers/input/touchscreen/Kconfig index 4af2a18eb3ba..2a2141915aa0 100644 --- a/drivers/input/touchscreen/Kconfig +++ b/drivers/input/touchscreen/Kconfig @@ -139,7 +139,6 @@ config TOUCHSCREEN_CY8CTMG110 tristate "cy8ctmg110 touchscreen" depends on I2C depends on GPIOLIB - help Say Y here if you have a cy8ctmg110 capacitive touchscreen on an AAVA device. @@ -149,6 +148,37 @@ config TOUCHSCREEN_CY8CTMG110 To compile this driver as a module, choose M here: the module will be called cy8ctmg110_ts. +config TOUCHSCREEN_CYTTSP_CORE + tristate "Cypress TTSP touchscreen" + help + Say Y here if you have a touchscreen using controller from + the Cypress TrueTouch(tm) Standard Product family connected + to your system. You will also need to select appropriate + bus connection below. + + If unsure, say N. + + To compile this driver as a module, choose M here: the + module will be called cyttsp_core. + +config TOUCHSCREEN_CYTTSP_I2C + tristate "support I2C bus connection" + depends on TOUCHSCREEN_CYTTSP_CORE && I2C + help + Say Y here if the touchscreen is connected via I2C bus. + + To compile this driver as a module, choose M here: the + module will be called cyttsp_i2c. + +config TOUCHSCREEN_CYTTSP_SPI + tristate "support SPI bus connection" + depends on TOUCHSCREEN_CYTTSP_CORE && SPI_MASTER + help + Say Y here if the touchscreen is connected via SPI bus. + + To compile this driver as a module, choose M here: the + module will be called cyttsp_spi. + config TOUCHSCREEN_DA9034 tristate "Touchscreen support for Dialog Semiconductor DA9034" depends on PMIC_DA903X @@ -213,9 +243,24 @@ config TOUCHSCREEN_FUJITSU To compile this driver as a module, choose M here: the module will be called fujitsu-ts. +config TOUCHSCREEN_ILI210X + tristate "Ilitek ILI210X based touchscreen" + depends on I2C + help + Say Y here if you have a ILI210X based touchscreen + controller. This driver supports models ILI2102, + ILI2102s, ILI2103, ILI2103s and ILI2105. + Such kind of chipsets can be found in Amazon Kindle Fire + touchscreens. + + If unsure, say N. + + To compile this driver as a module, choose M here: the + module will be called ili210x. + config TOUCHSCREEN_S3C2410 tristate "Samsung S3C2410/generic touchscreen input driver" - depends on ARCH_S3C2410 || SAMSUNG_DEV_TS + depends on ARCH_S3C24XX || SAMSUNG_DEV_TS select S3C_ADC help Say Y here if you have the s3c2410 touchscreen. @@ -430,6 +475,18 @@ config TOUCHSCREEN_TOUCHWIN To compile this driver as a module, choose M here: the module will be called touchwin. +config TOUCHSCREEN_TI_TSCADC + tristate "TI Touchscreen Interface" + depends on ARCH_OMAP2PLUS + help + Say Y here if you have 4/5/8 wire touchscreen controller + to be connected to the ADC controller on your TI AM335x SoC. + + If unsure, say N. + + To compile this driver as a module, choose M here: the + module will be called ti_tscadc. + config TOUCHSCREEN_ATMEL_TSADCC tristate "Atmel Touchscreen Interface" depends on ARCH_AT91SAM9RL || ARCH_AT91SAM9G45 @@ -577,6 +634,7 @@ config TOUCHSCREEN_USB_COMPOSITE - JASTEC USB Touch Controller/DigiTech DTR-02U - Zytronic controllers - Elo TouchSystems 2700 IntelliTouch + - EasyTouch USB Touch Controller from Data Modul Have a look at <http://linux.chapter7.ch/touchkit/> for a usage description and the required user-space stuff. @@ -681,6 +739,14 @@ config TOUCHSCREEN_USB_NEXIO bool "NEXIO/iNexio device support" if EXPERT depends on TOUCHSCREEN_USB_COMPOSITE +config TOUCHSCREEN_USB_EASYTOUCH + default y + bool "EasyTouch USB Touch controller device support" if EMBEDDED + depends on TOUCHSCREEN_USB_COMPOSITE + help + Say Y here if you have a EasyTouch USB Touch controller device support. + If unsure, say N. + config TOUCHSCREEN_TOUCHIT213 tristate "Sahara TouchIT-213 touchscreen" select SERIO diff --git a/drivers/input/touchscreen/Makefile b/drivers/input/touchscreen/Makefile index 496091e88460..3d5cf8cbf89c 100644 --- a/drivers/input/touchscreen/Makefile +++ b/drivers/input/touchscreen/Makefile @@ -16,8 +16,11 @@ obj-$(CONFIG_TOUCHSCREEN_ATMEL_MXT) += atmel_mxt_ts.o obj-$(CONFIG_TOUCHSCREEN_ATMEL_TSADCC) += atmel_tsadcc.o obj-$(CONFIG_TOUCHSCREEN_AUO_PIXCIR) += auo-pixcir-ts.o obj-$(CONFIG_TOUCHSCREEN_BITSY) += h3600_ts_input.o -obj-$(CONFIG_TOUCHSCREEN_BU21013) += bu21013_ts.o +obj-$(CONFIG_TOUCHSCREEN_BU21013) += bu21013_ts.o obj-$(CONFIG_TOUCHSCREEN_CY8CTMG110) += cy8ctmg110_ts.o +obj-$(CONFIG_TOUCHSCREEN_CYTTSP_CORE) += cyttsp_core.o +obj-$(CONFIG_TOUCHSCREEN_CYTTSP_I2C) += cyttsp_i2c.o +obj-$(CONFIG_TOUCHSCREEN_CYTTSP_SPI) += cyttsp_spi.o obj-$(CONFIG_TOUCHSCREEN_DA9034) += da9034-ts.o obj-$(CONFIG_TOUCHSCREEN_DYNAPRO) += dynapro.o obj-$(CONFIG_TOUCHSCREEN_HAMPSHIRE) += hampshire.o @@ -26,6 +29,7 @@ obj-$(CONFIG_TOUCHSCREEN_EETI) += eeti_ts.o obj-$(CONFIG_TOUCHSCREEN_ELO) += elo.o obj-$(CONFIG_TOUCHSCREEN_EGALAX) += egalax_ts.o obj-$(CONFIG_TOUCHSCREEN_FUJITSU) += fujitsu_ts.o +obj-$(CONFIG_TOUCHSCREEN_ILI210X) += ili210x.o obj-$(CONFIG_TOUCHSCREEN_INEXIO) += inexio.o obj-$(CONFIG_TOUCHSCREEN_INTEL_MID) += intel-mid-touch.o obj-$(CONFIG_TOUCHSCREEN_LPC32XX) += lpc32xx_ts.o @@ -45,6 +49,7 @@ obj-$(CONFIG_TOUCHSCREEN_PIXCIR) += pixcir_i2c_ts.o obj-$(CONFIG_TOUCHSCREEN_S3C2410) += s3c2410_ts.o obj-$(CONFIG_TOUCHSCREEN_ST1232) += st1232.o obj-$(CONFIG_TOUCHSCREEN_STMPE) += stmpe-ts.o +obj-$(CONFIG_TOUCHSCREEN_TI_TSCADC) += ti_tscadc.o obj-$(CONFIG_TOUCHSCREEN_TNETV107X) += tnetv107x-ts.o obj-$(CONFIG_TOUCHSCREEN_TOUCHIT213) += touchit213.o obj-$(CONFIG_TOUCHSCREEN_TOUCHRIGHT) += touchright.o diff --git a/drivers/input/touchscreen/ad7877.c b/drivers/input/touchscreen/ad7877.c index 49a36df0b752..2c7692108e6c 100644 --- a/drivers/input/touchscreen/ad7877.c +++ b/drivers/input/touchscreen/ad7877.c @@ -860,17 +860,7 @@ static struct spi_driver ad7877_driver = { .remove = __devexit_p(ad7877_remove), }; -static int __init ad7877_init(void) -{ - return spi_register_driver(&ad7877_driver); -} -module_init(ad7877_init); - -static void __exit ad7877_exit(void) -{ - spi_unregister_driver(&ad7877_driver); -} -module_exit(ad7877_exit); +module_spi_driver(ad7877_driver); MODULE_AUTHOR("Michael Hennerich <hennerich@blackfin.uclinux.org>"); MODULE_DESCRIPTION("AD7877 touchscreen Driver"); diff --git a/drivers/input/touchscreen/ad7879-i2c.c b/drivers/input/touchscreen/ad7879-i2c.c index 0dac6712f42b..3054354d0dd3 100644 --- a/drivers/input/touchscreen/ad7879-i2c.c +++ b/drivers/input/touchscreen/ad7879-i2c.c @@ -102,17 +102,7 @@ static struct i2c_driver ad7879_i2c_driver = { .id_table = ad7879_id, }; -static int __init ad7879_i2c_init(void) -{ - return i2c_add_driver(&ad7879_i2c_driver); -} -module_init(ad7879_i2c_init); - -static void __exit ad7879_i2c_exit(void) -{ - i2c_del_driver(&ad7879_i2c_driver); -} -module_exit(ad7879_i2c_exit); +module_i2c_driver(ad7879_i2c_driver); MODULE_AUTHOR("Michael Hennerich <hennerich@blackfin.uclinux.org>"); MODULE_DESCRIPTION("AD7879(-1) touchscreen I2C bus driver"); diff --git a/drivers/input/touchscreen/ad7879-spi.c b/drivers/input/touchscreen/ad7879-spi.c index 9b2e1c2b1971..db49abf056ba 100644 --- a/drivers/input/touchscreen/ad7879-spi.c +++ b/drivers/input/touchscreen/ad7879-spi.c @@ -157,17 +157,7 @@ static struct spi_driver ad7879_spi_driver = { .remove = __devexit_p(ad7879_spi_remove), }; -static int __init ad7879_spi_init(void) -{ - return spi_register_driver(&ad7879_spi_driver); -} -module_init(ad7879_spi_init); - -static void __exit ad7879_spi_exit(void) -{ - spi_unregister_driver(&ad7879_spi_driver); -} -module_exit(ad7879_spi_exit); +module_spi_driver(ad7879_spi_driver); MODULE_AUTHOR("Michael Hennerich <hennerich@blackfin.uclinux.org>"); MODULE_DESCRIPTION("AD7879(-1) touchscreen SPI bus driver"); diff --git a/drivers/input/touchscreen/ads7846.c b/drivers/input/touchscreen/ads7846.c index 23fd90185659..f02028ec3db6 100644 --- a/drivers/input/touchscreen/ads7846.c +++ b/drivers/input/touchscreen/ads7846.c @@ -1433,17 +1433,7 @@ static struct spi_driver ads7846_driver = { .remove = __devexit_p(ads7846_remove), }; -static int __init ads7846_init(void) -{ - return spi_register_driver(&ads7846_driver); -} -module_init(ads7846_init); - -static void __exit ads7846_exit(void) -{ - spi_unregister_driver(&ads7846_driver); -} -module_exit(ads7846_exit); +module_spi_driver(ads7846_driver); MODULE_DESCRIPTION("ADS7846 TouchScreen Driver"); MODULE_LICENSE("GPL"); diff --git a/drivers/input/touchscreen/atmel-wm97xx.c b/drivers/input/touchscreen/atmel-wm97xx.c index 8034cbb20f74..c5c2dbb93869 100644 --- a/drivers/input/touchscreen/atmel-wm97xx.c +++ b/drivers/input/touchscreen/atmel-wm97xx.c @@ -392,9 +392,10 @@ static int __exit atmel_wm97xx_remove(struct platform_device *pdev) return 0; } -#ifdef CONFIG_PM -static int atmel_wm97xx_suspend(struct platform_device *pdev, pm_message_t msg) +#ifdef CONFIG_PM_SLEEP +static int atmel_wm97xx_suspend(struct *dev) { + struct platform_device *pdev = to_platform_device(dev); struct atmel_wm97xx *atmel_wm97xx = platform_get_drvdata(pdev); ac97c_writel(atmel_wm97xx, IDR, AC97C_INT_CBEVT); @@ -404,8 +405,9 @@ static int atmel_wm97xx_suspend(struct platform_device *pdev, pm_message_t msg) return 0; } -static int atmel_wm97xx_resume(struct platform_device *pdev) +static int atmel_wm97xx_resume(struct device *dev) { + struct platform_device *pdev = to_platform_device(dev); struct atmel_wm97xx *atmel_wm97xx = platform_get_drvdata(pdev); struct wm97xx *wm = atmel_wm97xx->wm; @@ -416,18 +418,18 @@ static int atmel_wm97xx_resume(struct platform_device *pdev) return 0; } -#else -#define atmel_wm97xx_suspend NULL -#define atmel_wm97xx_resume NULL #endif +static SIMPLE_DEV_PM_OPS(atmel_wm97xx_pm_ops, + atmel_wm97xx_suspend, atmel_wm97xx_resume); + static struct platform_driver atmel_wm97xx_driver = { .remove = __exit_p(atmel_wm97xx_remove), .driver = { - .name = "wm97xx-touch", + .name = "wm97xx-touch", + .owner = THIS_MODULE, + .pm = &atmel_wm97xx_pm_ops, }, - .suspend = atmel_wm97xx_suspend, - .resume = atmel_wm97xx_resume, }; static int __init atmel_wm97xx_init(void) diff --git a/drivers/input/touchscreen/atmel_mxt_ts.c b/drivers/input/touchscreen/atmel_mxt_ts.c index a596c2775d1a..19d4ea65ea01 100644 --- a/drivers/input/touchscreen/atmel_mxt_ts.c +++ b/drivers/input/touchscreen/atmel_mxt_ts.c @@ -1267,18 +1267,7 @@ static struct i2c_driver mxt_driver = { .id_table = mxt_id, }; -static int __init mxt_init(void) -{ - return i2c_add_driver(&mxt_driver); -} - -static void __exit mxt_exit(void) -{ - i2c_del_driver(&mxt_driver); -} - -module_init(mxt_init); -module_exit(mxt_exit); +module_i2c_driver(mxt_driver); /* Module information */ MODULE_AUTHOR("Joonyoung Shim <jy0922.shim@samsung.com>"); diff --git a/drivers/input/touchscreen/auo-pixcir-ts.c b/drivers/input/touchscreen/auo-pixcir-ts.c index 94fb9fbb08a9..c7047b6bb020 100644 --- a/drivers/input/touchscreen/auo-pixcir-ts.c +++ b/drivers/input/touchscreen/auo-pixcir-ts.c @@ -635,17 +635,7 @@ static struct i2c_driver auo_pixcir_driver = { .id_table = auo_pixcir_idtable, }; -static int __init auo_pixcir_init(void) -{ - return i2c_add_driver(&auo_pixcir_driver); -} -module_init(auo_pixcir_init); - -static void __exit auo_pixcir_exit(void) -{ - i2c_del_driver(&auo_pixcir_driver); -} -module_exit(auo_pixcir_exit); +module_i2c_driver(auo_pixcir_driver); MODULE_DESCRIPTION("AUO-PIXCIR touchscreen driver"); MODULE_LICENSE("GPL v2"); diff --git a/drivers/input/touchscreen/bu21013_ts.c b/drivers/input/touchscreen/bu21013_ts.c index 902c7214e887..f2d03c06c2da 100644 --- a/drivers/input/touchscreen/bu21013_ts.c +++ b/drivers/input/touchscreen/bu21013_ts.c @@ -652,30 +652,7 @@ static struct i2c_driver bu21013_driver = { .id_table = bu21013_id, }; -/** - * bu21013_init() - initializes the bu21013 touchscreen driver - * - * This function used to initializes the bu21013 - * touchscreen driver and returns integer. - */ -static int __init bu21013_init(void) -{ - return i2c_add_driver(&bu21013_driver); -} - -/** - * bu21013_exit() - de-initializes the bu21013 touchscreen driver - * - * This function uses to de-initializes the bu21013 - * touchscreen driver and returns none. - */ -static void __exit bu21013_exit(void) -{ - i2c_del_driver(&bu21013_driver); -} - -module_init(bu21013_init); -module_exit(bu21013_exit); +module_i2c_driver(bu21013_driver); MODULE_LICENSE("GPL v2"); MODULE_AUTHOR("Naveen Kumar G <naveen.gaddipati@stericsson.com>"); diff --git a/drivers/input/touchscreen/cy8ctmg110_ts.c b/drivers/input/touchscreen/cy8ctmg110_ts.c index d8815c5d54ad..237753ad1031 100644 --- a/drivers/input/touchscreen/cy8ctmg110_ts.c +++ b/drivers/input/touchscreen/cy8ctmg110_ts.c @@ -350,18 +350,7 @@ static struct i2c_driver cy8ctmg110_driver = { .remove = __devexit_p(cy8ctmg110_remove), }; -static int __init cy8ctmg110_init(void) -{ - return i2c_add_driver(&cy8ctmg110_driver); -} - -static void __exit cy8ctmg110_exit(void) -{ - i2c_del_driver(&cy8ctmg110_driver); -} - -module_init(cy8ctmg110_init); -module_exit(cy8ctmg110_exit); +module_i2c_driver(cy8ctmg110_driver); MODULE_AUTHOR("Samuli Konttila <samuli.konttila@aavamobile.com>"); MODULE_DESCRIPTION("cy8ctmg110 TouchScreen Driver"); diff --git a/drivers/input/touchscreen/cyttsp_core.c b/drivers/input/touchscreen/cyttsp_core.c new file mode 100644 index 000000000000..f030d9ec795d --- /dev/null +++ b/drivers/input/touchscreen/cyttsp_core.c @@ -0,0 +1,625 @@ +/* + * Core Source for: + * Cypress TrueTouch(TM) Standard Product (TTSP) touchscreen drivers. + * For use with Cypress Txx3xx parts. + * Supported parts include: + * CY8CTST341 + * CY8CTMA340 + * + * Copyright (C) 2009, 2010, 2011 Cypress Semiconductor, Inc. + * Copyright (C) 2012 Javier Martinez Canillas <javier@dowhile0.org> + * + * 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. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + * Contact Cypress Semiconductor at www.cypress.com <kev@cypress.com> + * + */ + +#include <linux/delay.h> +#include <linux/input.h> +#include <linux/input/mt.h> +#include <linux/gpio.h> +#include <linux/interrupt.h> +#include <linux/slab.h> + +#include "cyttsp_core.h" + +/* Bootloader number of command keys */ +#define CY_NUM_BL_KEYS 8 + +/* helpers */ +#define GET_NUM_TOUCHES(x) ((x) & 0x0F) +#define IS_LARGE_AREA(x) (((x) & 0x10) >> 4) +#define IS_BAD_PKT(x) ((x) & 0x20) +#define IS_VALID_APP(x) ((x) & 0x01) +#define IS_OPERATIONAL_ERR(x) ((x) & 0x3F) +#define GET_HSTMODE(reg) (((reg) & 0x70) >> 4) +#define GET_BOOTLOADERMODE(reg) (((reg) & 0x10) >> 4) + +#define CY_REG_BASE 0x00 +#define CY_REG_ACT_DIST 0x1E +#define CY_REG_ACT_INTRVL 0x1D +#define CY_REG_TCH_TMOUT (CY_REG_ACT_INTRVL + 1) +#define CY_REG_LP_INTRVL (CY_REG_TCH_TMOUT + 1) +#define CY_MAXZ 255 +#define CY_DELAY_DFLT 20 /* ms */ +#define CY_DELAY_MAX 500 +#define CY_ACT_DIST_DFLT 0xF8 +#define CY_HNDSHK_BIT 0x80 +/* device mode bits */ +#define CY_OPERATE_MODE 0x00 +#define CY_SYSINFO_MODE 0x10 +/* power mode select bits */ +#define CY_SOFT_RESET_MODE 0x01 /* return to Bootloader mode */ +#define CY_DEEP_SLEEP_MODE 0x02 +#define CY_LOW_POWER_MODE 0x04 + +/* Slots management */ +#define CY_MAX_FINGER 4 +#define CY_MAX_ID 16 + +static const u8 bl_command[] = { + 0x00, /* file offset */ + 0xFF, /* command */ + 0xA5, /* exit bootloader command */ + 0, 1, 2, 3, 4, 5, 6, 7 /* default keys */ +}; + +static int ttsp_read_block_data(struct cyttsp *ts, u8 command, + u8 length, void *buf) +{ + int error; + int tries; + + for (tries = 0; tries < CY_NUM_RETRY; tries++) { + error = ts->bus_ops->read(ts, command, length, buf); + if (!error) + return 0; + + msleep(CY_DELAY_DFLT); + } + + return -EIO; +} + +static int ttsp_write_block_data(struct cyttsp *ts, u8 command, + u8 length, void *buf) +{ + int error; + int tries; + + for (tries = 0; tries < CY_NUM_RETRY; tries++) { + error = ts->bus_ops->write(ts, command, length, buf); + if (!error) + return 0; + + msleep(CY_DELAY_DFLT); + } + + return -EIO; +} + +static int ttsp_send_command(struct cyttsp *ts, u8 cmd) +{ + return ttsp_write_block_data(ts, CY_REG_BASE, sizeof(cmd), &cmd); +} + +static int cyttsp_load_bl_regs(struct cyttsp *ts) +{ + memset(&ts->bl_data, 0, sizeof(ts->bl_data)); + ts->bl_data.bl_status = 0x10; + + return ttsp_read_block_data(ts, CY_REG_BASE, + sizeof(ts->bl_data), &ts->bl_data); +} + +static int cyttsp_exit_bl_mode(struct cyttsp *ts) +{ + int error; + u8 bl_cmd[sizeof(bl_command)]; + + memcpy(bl_cmd, bl_command, sizeof(bl_command)); + if (ts->pdata->bl_keys) + memcpy(&bl_cmd[sizeof(bl_command) - CY_NUM_BL_KEYS], + ts->pdata->bl_keys, sizeof(bl_command)); + + error = ttsp_write_block_data(ts, CY_REG_BASE, + sizeof(bl_cmd), bl_cmd); + if (error) + return error; + + /* wait for TTSP Device to complete the operation */ + msleep(CY_DELAY_DFLT); + + error = cyttsp_load_bl_regs(ts); + if (error) + return error; + + if (GET_BOOTLOADERMODE(ts->bl_data.bl_status)) + return -EIO; + + return 0; +} + +static int cyttsp_set_operational_mode(struct cyttsp *ts) +{ + int error; + + error = ttsp_send_command(ts, CY_OPERATE_MODE); + if (error) + return error; + + /* wait for TTSP Device to complete switch to Operational mode */ + error = ttsp_read_block_data(ts, CY_REG_BASE, + sizeof(ts->xy_data), &ts->xy_data); + if (error) + return error; + + return ts->xy_data.act_dist == CY_ACT_DIST_DFLT ? -EIO : 0; +} + +static int cyttsp_set_sysinfo_mode(struct cyttsp *ts) +{ + int error; + + memset(&ts->sysinfo_data, 0, sizeof(ts->sysinfo_data)); + + /* switch to sysinfo mode */ + error = ttsp_send_command(ts, CY_SYSINFO_MODE); + if (error) + return error; + + /* read sysinfo registers */ + msleep(CY_DELAY_DFLT); + error = ttsp_read_block_data(ts, CY_REG_BASE, sizeof(ts->sysinfo_data), + &ts->sysinfo_data); + if (error) + return error; + + if (!ts->sysinfo_data.tts_verh && !ts->sysinfo_data.tts_verl) + return -EIO; + + return 0; +} + +static int cyttsp_set_sysinfo_regs(struct cyttsp *ts) +{ + int retval = 0; + + if (ts->pdata->act_intrvl != CY_ACT_INTRVL_DFLT || + ts->pdata->tch_tmout != CY_TCH_TMOUT_DFLT || + ts->pdata->lp_intrvl != CY_LP_INTRVL_DFLT) { + + u8 intrvl_ray[] = { + ts->pdata->act_intrvl, + ts->pdata->tch_tmout, + ts->pdata->lp_intrvl + }; + + /* set intrvl registers */ + retval = ttsp_write_block_data(ts, CY_REG_ACT_INTRVL, + sizeof(intrvl_ray), intrvl_ray); + msleep(CY_DELAY_DFLT); + } + + return retval; +} + +static int cyttsp_soft_reset(struct cyttsp *ts) +{ + unsigned long timeout; + int retval; + + /* wait for interrupt to set ready completion */ + INIT_COMPLETION(ts->bl_ready); + ts->state = CY_BL_STATE; + + enable_irq(ts->irq); + + retval = ttsp_send_command(ts, CY_SOFT_RESET_MODE); + if (retval) + goto out; + + timeout = wait_for_completion_timeout(&ts->bl_ready, + msecs_to_jiffies(CY_DELAY_DFLT * CY_DELAY_MAX)); + retval = timeout ? 0 : -EIO; + +out: + ts->state = CY_IDLE_STATE; + disable_irq(ts->irq); + return retval; +} + +static int cyttsp_act_dist_setup(struct cyttsp *ts) +{ + u8 act_dist_setup = ts->pdata->act_dist; + + /* Init gesture; active distance setup */ + return ttsp_write_block_data(ts, CY_REG_ACT_DIST, + sizeof(act_dist_setup), &act_dist_setup); +} + +static void cyttsp_extract_track_ids(struct cyttsp_xydata *xy_data, int *ids) +{ + ids[0] = xy_data->touch12_id >> 4; + ids[1] = xy_data->touch12_id & 0xF; + ids[2] = xy_data->touch34_id >> 4; + ids[3] = xy_data->touch34_id & 0xF; +} + +static const struct cyttsp_tch *cyttsp_get_tch(struct cyttsp_xydata *xy_data, + int idx) +{ + switch (idx) { + case 0: + return &xy_data->tch1; + case 1: + return &xy_data->tch2; + case 2: + return &xy_data->tch3; + case 3: + return &xy_data->tch4; + default: + return NULL; + } +} + +static void cyttsp_report_tchdata(struct cyttsp *ts) +{ + struct cyttsp_xydata *xy_data = &ts->xy_data; + struct input_dev *input = ts->input; + int num_tch = GET_NUM_TOUCHES(xy_data->tt_stat); + const struct cyttsp_tch *tch; + int ids[CY_MAX_ID]; + int i; + DECLARE_BITMAP(used, CY_MAX_ID); + + if (IS_LARGE_AREA(xy_data->tt_stat) == 1) { + /* terminate all active tracks */ + num_tch = 0; + dev_dbg(ts->dev, "%s: Large area detected\n", __func__); + } else if (num_tch > CY_MAX_FINGER) { + /* terminate all active tracks */ + num_tch = 0; + dev_dbg(ts->dev, "%s: Num touch error detected\n", __func__); + } else if (IS_BAD_PKT(xy_data->tt_mode)) { + /* terminate all active tracks */ + num_tch = 0; + dev_dbg(ts->dev, "%s: Invalid buffer detected\n", __func__); + } + + cyttsp_extract_track_ids(xy_data, ids); + + bitmap_zero(used, CY_MAX_ID); + + for (i = 0; i < num_tch; i++) { + tch = cyttsp_get_tch(xy_data, i); + + input_mt_slot(input, ids[i]); + input_mt_report_slot_state(input, MT_TOOL_FINGER, true); + input_report_abs(input, ABS_MT_POSITION_X, be16_to_cpu(tch->x)); + input_report_abs(input, ABS_MT_POSITION_Y, be16_to_cpu(tch->y)); + input_report_abs(input, ABS_MT_TOUCH_MAJOR, tch->z); + + __set_bit(ids[i], used); + } + + for (i = 0; i < CY_MAX_ID; i++) { + if (test_bit(i, used)) + continue; + + input_mt_slot(input, i); + input_mt_report_slot_state(input, MT_TOOL_FINGER, false); + } + + input_sync(input); +} + +static irqreturn_t cyttsp_irq(int irq, void *handle) +{ + struct cyttsp *ts = handle; + int error; + + if (unlikely(ts->state == CY_BL_STATE)) { + complete(&ts->bl_ready); + goto out; + } + + /* Get touch data from CYTTSP device */ + error = ttsp_read_block_data(ts, CY_REG_BASE, + sizeof(struct cyttsp_xydata), &ts->xy_data); + if (error) + goto out; + + /* provide flow control handshake */ + if (ts->pdata->use_hndshk) { + error = ttsp_send_command(ts, + ts->xy_data.hst_mode ^ CY_HNDSHK_BIT); + if (error) + goto out; + } + + if (unlikely(ts->state == CY_IDLE_STATE)) + goto out; + + if (GET_BOOTLOADERMODE(ts->xy_data.tt_mode)) { + /* + * TTSP device has reset back to bootloader mode. + * Restore to operational mode. + */ + error = cyttsp_exit_bl_mode(ts); + if (error) { + dev_err(ts->dev, + "Could not return to operational mode, err: %d\n", + error); + ts->state = CY_IDLE_STATE; + } + } else { + cyttsp_report_tchdata(ts); + } + +out: + return IRQ_HANDLED; +} + +static int cyttsp_power_on(struct cyttsp *ts) +{ + int error; + + error = cyttsp_soft_reset(ts); + if (error) + return error; + + error = cyttsp_load_bl_regs(ts); + if (error) + return error; + + if (GET_BOOTLOADERMODE(ts->bl_data.bl_status) && + IS_VALID_APP(ts->bl_data.bl_status)) { + error = cyttsp_exit_bl_mode(ts); + if (error) + return error; + } + + if (GET_HSTMODE(ts->bl_data.bl_file) != CY_OPERATE_MODE || + IS_OPERATIONAL_ERR(ts->bl_data.bl_status)) { + return -ENODEV; + } + + error = cyttsp_set_sysinfo_mode(ts); + if (error) + return error; + + error = cyttsp_set_sysinfo_regs(ts); + if (error) + return error; + + error = cyttsp_set_operational_mode(ts); + if (error) + return error; + + /* init active distance */ + error = cyttsp_act_dist_setup(ts); + if (error) + return error; + + ts->state = CY_ACTIVE_STATE; + + return 0; +} + +static int cyttsp_enable(struct cyttsp *ts) +{ + int error; + + /* + * The device firmware can wake on an I2C or SPI memory slave + * address match. So just reading a register is sufficient to + * wake up the device. The first read attempt will fail but it + * will wake it up making the second read attempt successful. + */ + error = ttsp_read_block_data(ts, CY_REG_BASE, + sizeof(ts->xy_data), &ts->xy_data); + if (error) + return error; + + if (GET_HSTMODE(ts->xy_data.hst_mode)) + return -EIO; + + enable_irq(ts->irq); + + return 0; +} + +static int cyttsp_disable(struct cyttsp *ts) +{ + int error; + + error = ttsp_send_command(ts, CY_LOW_POWER_MODE); + if (error) + return error; + + disable_irq(ts->irq); + + return 0; +} + +#ifdef CONFIG_PM_SLEEP +static int cyttsp_suspend(struct device *dev) +{ + struct cyttsp *ts = dev_get_drvdata(dev); + int retval = 0; + + mutex_lock(&ts->input->mutex); + + if (ts->input->users) { + retval = cyttsp_disable(ts); + if (retval == 0) + ts->suspended = true; + } + + mutex_unlock(&ts->input->mutex); + + return retval; +} + +static int cyttsp_resume(struct device *dev) +{ + struct cyttsp *ts = dev_get_drvdata(dev); + + mutex_lock(&ts->input->mutex); + + if (ts->input->users) + cyttsp_enable(ts); + + ts->suspended = false; + + mutex_unlock(&ts->input->mutex); + + return 0; +} + +#endif + +SIMPLE_DEV_PM_OPS(cyttsp_pm_ops, cyttsp_suspend, cyttsp_resume); +EXPORT_SYMBOL_GPL(cyttsp_pm_ops); + +static int cyttsp_open(struct input_dev *dev) +{ + struct cyttsp *ts = input_get_drvdata(dev); + int retval = 0; + + if (!ts->suspended) + retval = cyttsp_enable(ts); + + return retval; +} + +static void cyttsp_close(struct input_dev *dev) +{ + struct cyttsp *ts = input_get_drvdata(dev); + + if (!ts->suspended) + cyttsp_disable(ts); +} + +struct cyttsp *cyttsp_probe(const struct cyttsp_bus_ops *bus_ops, + struct device *dev, int irq, size_t xfer_buf_size) +{ + const struct cyttsp_platform_data *pdata = dev->platform_data; + struct cyttsp *ts; + struct input_dev *input_dev; + int error; + + if (!pdata || !pdata->name || irq <= 0) { + error = -EINVAL; + goto err_out; + } + + ts = kzalloc(sizeof(*ts) + xfer_buf_size, GFP_KERNEL); + input_dev = input_allocate_device(); + if (!ts || !input_dev) { + error = -ENOMEM; + goto err_free_mem; + } + + ts->dev = dev; + ts->input = input_dev; + ts->pdata = dev->platform_data; + ts->bus_ops = bus_ops; + ts->irq = irq; + + init_completion(&ts->bl_ready); + snprintf(ts->phys, sizeof(ts->phys), "%s/input0", dev_name(dev)); + + if (pdata->init) { + error = pdata->init(); + if (error) { + dev_err(ts->dev, "platform init failed, err: %d\n", + error); + goto err_free_mem; + } + } + + input_dev->name = pdata->name; + input_dev->phys = ts->phys; + input_dev->id.bustype = bus_ops->bustype; + input_dev->dev.parent = ts->dev; + + input_dev->open = cyttsp_open; + input_dev->close = cyttsp_close; + + input_set_drvdata(input_dev, ts); + + __set_bit(EV_ABS, input_dev->evbit); + input_set_abs_params(input_dev, ABS_MT_POSITION_X, + 0, pdata->maxx, 0, 0); + input_set_abs_params(input_dev, ABS_MT_POSITION_Y, + 0, pdata->maxy, 0, 0); + input_set_abs_params(input_dev, ABS_MT_TOUCH_MAJOR, + 0, CY_MAXZ, 0, 0); + + input_mt_init_slots(input_dev, CY_MAX_ID); + + error = request_threaded_irq(ts->irq, NULL, cyttsp_irq, + IRQF_TRIGGER_FALLING | IRQF_ONESHOT, + pdata->name, ts); + if (error) { + dev_err(ts->dev, "failed to request IRQ %d, err: %d\n", + ts->irq, error); + goto err_platform_exit; + } + + disable_irq(ts->irq); + + error = cyttsp_power_on(ts); + if (error) + goto err_free_irq; + + error = input_register_device(input_dev); + if (error) { + dev_err(ts->dev, "failed to register input device: %d\n", + error); + goto err_free_irq; + } + + return ts; + +err_free_irq: + free_irq(ts->irq, ts); +err_platform_exit: + if (pdata->exit) + pdata->exit(); +err_free_mem: + input_free_device(input_dev); + kfree(ts); +err_out: + return ERR_PTR(error); +} +EXPORT_SYMBOL_GPL(cyttsp_probe); + +void cyttsp_remove(struct cyttsp *ts) +{ + free_irq(ts->irq, ts); + input_unregister_device(ts->input); + if (ts->pdata->exit) + ts->pdata->exit(); + kfree(ts); +} +EXPORT_SYMBOL_GPL(cyttsp_remove); + +MODULE_LICENSE("GPL"); +MODULE_DESCRIPTION("Cypress TrueTouch(R) Standard touchscreen driver core"); +MODULE_AUTHOR("Cypress"); diff --git a/drivers/input/touchscreen/cyttsp_core.h b/drivers/input/touchscreen/cyttsp_core.h new file mode 100644 index 000000000000..1aa3c6967e70 --- /dev/null +++ b/drivers/input/touchscreen/cyttsp_core.h @@ -0,0 +1,149 @@ +/* + * Header file for: + * Cypress TrueTouch(TM) Standard Product (TTSP) touchscreen drivers. + * For use with Cypress Txx3xx parts. + * Supported parts include: + * CY8CTST341 + * CY8CTMA340 + * + * Copyright (C) 2009, 2010, 2011 Cypress Semiconductor, Inc. + * Copyright (C) 2012 Javier Martinez Canillas <javier@dowhile0.org> + * + * 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. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + * Contact Cypress Semiconductor at www.cypress.com <kev@cypress.com> + * + */ + + +#ifndef __CYTTSP_CORE_H__ +#define __CYTTSP_CORE_H__ + +#include <linux/kernel.h> +#include <linux/err.h> +#include <linux/module.h> +#include <linux/types.h> +#include <linux/device.h> +#include <linux/input/cyttsp.h> + +#define CY_NUM_RETRY 16 /* max number of retries for read ops */ + +struct cyttsp_tch { + __be16 x, y; + u8 z; +} __packed; + +/* TrueTouch Standard Product Gen3 interface definition */ +struct cyttsp_xydata { + u8 hst_mode; + u8 tt_mode; + u8 tt_stat; + struct cyttsp_tch tch1; + u8 touch12_id; + struct cyttsp_tch tch2; + u8 gest_cnt; + u8 gest_id; + struct cyttsp_tch tch3; + u8 touch34_id; + struct cyttsp_tch tch4; + u8 tt_undef[3]; + u8 act_dist; + u8 tt_reserved; +} __packed; + + +/* TTSP System Information interface definition */ +struct cyttsp_sysinfo_data { + u8 hst_mode; + u8 mfg_cmd; + u8 mfg_stat; + u8 cid[3]; + u8 tt_undef1; + u8 uid[8]; + u8 bl_verh; + u8 bl_verl; + u8 tts_verh; + u8 tts_verl; + u8 app_idh; + u8 app_idl; + u8 app_verh; + u8 app_verl; + u8 tt_undef[5]; + u8 scn_typ; + u8 act_intrvl; + u8 tch_tmout; + u8 lp_intrvl; +}; + +/* TTSP Bootloader Register Map interface definition */ +#define CY_BL_CHKSUM_OK 0x01 +struct cyttsp_bootloader_data { + u8 bl_file; + u8 bl_status; + u8 bl_error; + u8 blver_hi; + u8 blver_lo; + u8 bld_blver_hi; + u8 bld_blver_lo; + u8 ttspver_hi; + u8 ttspver_lo; + u8 appid_hi; + u8 appid_lo; + u8 appver_hi; + u8 appver_lo; + u8 cid_0; + u8 cid_1; + u8 cid_2; +}; + +struct cyttsp; + +struct cyttsp_bus_ops { + u16 bustype; + int (*write)(struct cyttsp *ts, + u8 addr, u8 length, const void *values); + int (*read)(struct cyttsp *ts, u8 addr, u8 length, void *values); +}; + +enum cyttsp_state { + CY_IDLE_STATE, + CY_ACTIVE_STATE, + CY_BL_STATE, +}; + +struct cyttsp { + struct device *dev; + int irq; + struct input_dev *input; + char phys[32]; + const struct cyttsp_platform_data *pdata; + const struct cyttsp_bus_ops *bus_ops; + struct cyttsp_bootloader_data bl_data; + struct cyttsp_sysinfo_data sysinfo_data; + struct cyttsp_xydata xy_data; + struct completion bl_ready; + enum cyttsp_state state; + bool suspended; + + u8 xfer_buf[] ____cacheline_aligned; +}; + +struct cyttsp *cyttsp_probe(const struct cyttsp_bus_ops *bus_ops, + struct device *dev, int irq, size_t xfer_buf_size); +void cyttsp_remove(struct cyttsp *ts); + +extern const struct dev_pm_ops cyttsp_pm_ops; + +#endif /* __CYTTSP_CORE_H__ */ diff --git a/drivers/input/touchscreen/cyttsp_i2c.c b/drivers/input/touchscreen/cyttsp_i2c.c new file mode 100644 index 000000000000..2af1d0c52bcd --- /dev/null +++ b/drivers/input/touchscreen/cyttsp_i2c.c @@ -0,0 +1,136 @@ +/* + * Source for: + * Cypress TrueTouch(TM) Standard Product (TTSP) I2C touchscreen driver. + * For use with Cypress Txx3xx parts. + * Supported parts include: + * CY8CTST341 + * CY8CTMA340 + * + * Copyright (C) 2009, 2010, 2011 Cypress Semiconductor, Inc. + * Copyright (C) 2012 Javier Martinez Canillas <javier@dowhile0.org> + * + * 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. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + * Contact Cypress Semiconductor at www.cypress.com <kev@cypress.com> + * + */ + +#include "cyttsp_core.h" + +#include <linux/i2c.h> +#include <linux/input.h> + +#define CY_I2C_DATA_SIZE 128 + +static int cyttsp_i2c_read_block_data(struct cyttsp *ts, + u8 addr, u8 length, void *values) +{ + struct i2c_client *client = to_i2c_client(ts->dev); + struct i2c_msg msgs[] = { + { + .addr = client->addr, + .flags = 0, + .len = 1, + .buf = &addr, + }, + { + .addr = client->addr, + .flags = I2C_M_RD, + .len = length, + .buf = values, + }, + }; + int retval; + + retval = i2c_transfer(client->adapter, msgs, ARRAY_SIZE(msgs)); + if (retval < 0) + return retval; + + return retval != ARRAY_SIZE(msgs) ? -EIO : 0; +} + +static int cyttsp_i2c_write_block_data(struct cyttsp *ts, + u8 addr, u8 length, const void *values) +{ + struct i2c_client *client = to_i2c_client(ts->dev); + int retval; + + ts->xfer_buf[0] = addr; + memcpy(&ts->xfer_buf[1], values, length); + + retval = i2c_master_send(client, ts->xfer_buf, length + 1); + + return retval < 0 ? retval : 0; +} + +static const struct cyttsp_bus_ops cyttsp_i2c_bus_ops = { + .bustype = BUS_I2C, + .write = cyttsp_i2c_write_block_data, + .read = cyttsp_i2c_read_block_data, +}; + +static int __devinit cyttsp_i2c_probe(struct i2c_client *client, + const struct i2c_device_id *id) +{ + struct cyttsp *ts; + + if (!i2c_check_functionality(client->adapter, I2C_FUNC_I2C)) { + dev_err(&client->dev, "I2C functionality not Supported\n"); + return -EIO; + } + + ts = cyttsp_probe(&cyttsp_i2c_bus_ops, &client->dev, client->irq, + CY_I2C_DATA_SIZE); + + if (IS_ERR(ts)) + return PTR_ERR(ts); + + i2c_set_clientdata(client, ts); + + return 0; +} + +static int __devexit cyttsp_i2c_remove(struct i2c_client *client) +{ + struct cyttsp *ts = i2c_get_clientdata(client); + + cyttsp_remove(ts); + + return 0; +} + +static const struct i2c_device_id cyttsp_i2c_id[] = { + { CY_I2C_NAME, 0 }, + { } +}; +MODULE_DEVICE_TABLE(i2c, cyttsp_i2c_id); + +static struct i2c_driver cyttsp_i2c_driver = { + .driver = { + .name = CY_I2C_NAME, + .owner = THIS_MODULE, + .pm = &cyttsp_pm_ops, + }, + .probe = cyttsp_i2c_probe, + .remove = __devexit_p(cyttsp_i2c_remove), + .id_table = cyttsp_i2c_id, +}; + +module_i2c_driver(cyttsp_i2c_driver); + +MODULE_LICENSE("GPL"); +MODULE_DESCRIPTION("Cypress TrueTouch(R) Standard Product (TTSP) I2C driver"); +MODULE_AUTHOR("Cypress"); +MODULE_ALIAS("i2c:cyttsp"); diff --git a/drivers/input/touchscreen/cyttsp_spi.c b/drivers/input/touchscreen/cyttsp_spi.c new file mode 100644 index 000000000000..9f263410407b --- /dev/null +++ b/drivers/input/touchscreen/cyttsp_spi.c @@ -0,0 +1,200 @@ +/* + * Source for: + * Cypress TrueTouch(TM) Standard Product (TTSP) SPI touchscreen driver. + * For use with Cypress Txx3xx parts. + * Supported parts include: + * CY8CTST341 + * CY8CTMA340 + * + * Copyright (C) 2009, 2010, 2011 Cypress Semiconductor, Inc. + * Copyright (C) 2012 Javier Martinez Canillas <javier@dowhile0.org> + * + * 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. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + * Contact Cypress Semiconductor at www.cypress.com <kev@cypress.com> + * + */ + +#include "cyttsp_core.h" + +#include <linux/delay.h> +#include <linux/input.h> +#include <linux/spi/spi.h> + +#define CY_SPI_WR_OP 0x00 /* r/~w */ +#define CY_SPI_RD_OP 0x01 +#define CY_SPI_CMD_BYTES 4 +#define CY_SPI_SYNC_BYTE 2 +#define CY_SPI_SYNC_ACK1 0x62 /* from protocol v.2 */ +#define CY_SPI_SYNC_ACK2 0x9D /* from protocol v.2 */ +#define CY_SPI_DATA_SIZE 128 +#define CY_SPI_DATA_BUF_SIZE (CY_SPI_CMD_BYTES + CY_SPI_DATA_SIZE) +#define CY_SPI_BITS_PER_WORD 8 + +static int cyttsp_spi_xfer(struct cyttsp *ts, + u8 op, u8 reg, u8 *buf, int length) +{ + struct spi_device *spi = to_spi_device(ts->dev); + struct spi_message msg; + struct spi_transfer xfer[2]; + u8 *wr_buf = &ts->xfer_buf[0]; + u8 *rd_buf = &ts->xfer_buf[CY_SPI_DATA_BUF_SIZE]; + int retval; + int i; + + if (length > CY_SPI_DATA_SIZE) { + dev_err(ts->dev, "%s: length %d is too big.\n", + __func__, length); + return -EINVAL; + } + + memset(wr_buf, 0, CY_SPI_DATA_BUF_SIZE); + memset(rd_buf, 0, CY_SPI_DATA_BUF_SIZE); + + wr_buf[0] = 0x00; /* header byte 0 */ + wr_buf[1] = 0xFF; /* header byte 1 */ + wr_buf[2] = reg; /* reg index */ + wr_buf[3] = op; /* r/~w */ + if (op == CY_SPI_WR_OP) + memcpy(wr_buf + CY_SPI_CMD_BYTES, buf, length); + + memset(xfer, 0, sizeof(xfer)); + spi_message_init(&msg); + + /* + We set both TX and RX buffers because Cypress TTSP + requires full duplex operation. + */ + xfer[0].tx_buf = wr_buf; + xfer[0].rx_buf = rd_buf; + switch (op) { + case CY_SPI_WR_OP: + xfer[0].len = length + CY_SPI_CMD_BYTES; + spi_message_add_tail(&xfer[0], &msg); + break; + + case CY_SPI_RD_OP: + xfer[0].len = CY_SPI_CMD_BYTES; + spi_message_add_tail(&xfer[0], &msg); + + xfer[1].rx_buf = buf; + xfer[1].len = length; + spi_message_add_tail(&xfer[1], &msg); + break; + + default: + dev_err(ts->dev, "%s: bad operation code=%d\n", __func__, op); + return -EINVAL; + } + + retval = spi_sync(spi, &msg); + if (retval < 0) { + dev_dbg(ts->dev, "%s: spi_sync() error %d, len=%d, op=%d\n", + __func__, retval, xfer[1].len, op); + + /* + * do not return here since was a bad ACK sequence + * let the following ACK check handle any errors and + * allow silent retries + */ + } + + if (rd_buf[CY_SPI_SYNC_BYTE] != CY_SPI_SYNC_ACK1 || + rd_buf[CY_SPI_SYNC_BYTE + 1] != CY_SPI_SYNC_ACK2) { + + dev_dbg(ts->dev, "%s: operation %d failed\n", __func__, op); + + for (i = 0; i < CY_SPI_CMD_BYTES; i++) + dev_dbg(ts->dev, "%s: test rd_buf[%d]:0x%02x\n", + __func__, i, rd_buf[i]); + for (i = 0; i < length; i++) + dev_dbg(ts->dev, "%s: test buf[%d]:0x%02x\n", + __func__, i, buf[i]); + + return -EIO; + } + + return 0; +} + +static int cyttsp_spi_read_block_data(struct cyttsp *ts, + u8 addr, u8 length, void *data) +{ + return cyttsp_spi_xfer(ts, CY_SPI_RD_OP, addr, data, length); +} + +static int cyttsp_spi_write_block_data(struct cyttsp *ts, + u8 addr, u8 length, const void *data) +{ + return cyttsp_spi_xfer(ts, CY_SPI_WR_OP, addr, (void *)data, length); +} + +static const struct cyttsp_bus_ops cyttsp_spi_bus_ops = { + .bustype = BUS_SPI, + .write = cyttsp_spi_write_block_data, + .read = cyttsp_spi_read_block_data, +}; + +static int __devinit cyttsp_spi_probe(struct spi_device *spi) +{ + struct cyttsp *ts; + int error; + + /* Set up SPI*/ + spi->bits_per_word = CY_SPI_BITS_PER_WORD; + spi->mode = SPI_MODE_0; + error = spi_setup(spi); + if (error < 0) { + dev_err(&spi->dev, "%s: SPI setup error %d\n", + __func__, error); + return error; + } + + ts = cyttsp_probe(&cyttsp_spi_bus_ops, &spi->dev, spi->irq, + CY_SPI_DATA_BUF_SIZE * 2); + if (IS_ERR(ts)) + return PTR_ERR(ts); + + spi_set_drvdata(spi, ts); + + return 0; +} + +static int __devexit cyttsp_spi_remove(struct spi_device *spi) +{ + struct cyttsp *ts = spi_get_drvdata(spi); + + cyttsp_remove(ts); + + return 0; +} + +static struct spi_driver cyttsp_spi_driver = { + .driver = { + .name = CY_SPI_NAME, + .owner = THIS_MODULE, + .pm = &cyttsp_pm_ops, + }, + .probe = cyttsp_spi_probe, + .remove = __devexit_p(cyttsp_spi_remove), +}; + +module_spi_driver(cyttsp_spi_driver); + +MODULE_ALIAS("spi:cyttsp"); +MODULE_LICENSE("GPL"); +MODULE_DESCRIPTION("Cypress TrueTouch(R) Standard Product (TTSP) SPI driver"); +MODULE_AUTHOR("Cypress"); +MODULE_ALIAS("spi:cyttsp"); diff --git a/drivers/input/touchscreen/eeti_ts.c b/drivers/input/touchscreen/eeti_ts.c index 1df19bb8534a..503c7096ed36 100644 --- a/drivers/input/touchscreen/eeti_ts.c +++ b/drivers/input/touchscreen/eeti_ts.c @@ -320,20 +320,8 @@ static struct i2c_driver eeti_ts_driver = { .id_table = eeti_ts_id, }; -static int __init eeti_ts_init(void) -{ - return i2c_add_driver(&eeti_ts_driver); -} - -static void __exit eeti_ts_exit(void) -{ - i2c_del_driver(&eeti_ts_driver); -} +module_i2c_driver(eeti_ts_driver); MODULE_DESCRIPTION("EETI Touchscreen driver"); MODULE_AUTHOR("Daniel Mack <daniel@caiaq.de>"); MODULE_LICENSE("GPL"); - -module_init(eeti_ts_init); -module_exit(eeti_ts_exit); - diff --git a/drivers/input/touchscreen/egalax_ts.c b/drivers/input/touchscreen/egalax_ts.c index eadcc2e83c77..70524dd34f42 100644 --- a/drivers/input/touchscreen/egalax_ts.c +++ b/drivers/input/touchscreen/egalax_ts.c @@ -285,18 +285,7 @@ static struct i2c_driver egalax_ts_driver = { .remove = __devexit_p(egalax_ts_remove), }; -static int __init egalax_ts_init(void) -{ - return i2c_add_driver(&egalax_ts_driver); -} - -static void __exit egalax_ts_exit(void) -{ - i2c_del_driver(&egalax_ts_driver); -} - -module_init(egalax_ts_init); -module_exit(egalax_ts_exit); +module_i2c_driver(egalax_ts_driver); MODULE_AUTHOR("Freescale Semiconductor, Inc."); MODULE_DESCRIPTION("Touchscreen driver for EETI eGalax touch controller"); diff --git a/drivers/input/touchscreen/hp680_ts_input.c b/drivers/input/touchscreen/hp680_ts_input.c index 639a6044183d..85cf9bee8018 100644 --- a/drivers/input/touchscreen/hp680_ts_input.c +++ b/drivers/input/touchscreen/hp680_ts_input.c @@ -93,7 +93,7 @@ static int __init hp680_ts_init(void) hp680_ts_dev->phys = "hp680_ts/input0"; if (request_irq(HP680_TS_IRQ, hp680_ts_interrupt, - 0, MODNAME, 0) < 0) { + 0, MODNAME, NULL) < 0) { printk(KERN_ERR "hp680_touchscreen.c: Can't allocate irq %d\n", HP680_TS_IRQ); err = -EBUSY; diff --git a/drivers/input/touchscreen/ili210x.c b/drivers/input/touchscreen/ili210x.c new file mode 100644 index 000000000000..c0044175a921 --- /dev/null +++ b/drivers/input/touchscreen/ili210x.c @@ -0,0 +1,360 @@ +#include <linux/module.h> +#include <linux/i2c.h> +#include <linux/interrupt.h> +#include <linux/slab.h> +#include <linux/input.h> +#include <linux/input/mt.h> +#include <linux/delay.h> +#include <linux/workqueue.h> +#include <linux/input/ili210x.h> + +#define MAX_TOUCHES 2 +#define DEFAULT_POLL_PERIOD 20 + +/* Touchscreen commands */ +#define REG_TOUCHDATA 0x10 +#define REG_PANEL_INFO 0x20 +#define REG_FIRMWARE_VERSION 0x40 +#define REG_CALIBRATE 0xcc + +struct finger { + u8 x_low; + u8 x_high; + u8 y_low; + u8 y_high; +} __packed; + +struct touchdata { + u8 status; + struct finger finger[MAX_TOUCHES]; +} __packed; + +struct panel_info { + struct finger finger_max; + u8 xchannel_num; + u8 ychannel_num; +} __packed; + +struct firmware_version { + u8 id; + u8 major; + u8 minor; +} __packed; + +struct ili210x { + struct i2c_client *client; + struct input_dev *input; + bool (*get_pendown_state)(void); + unsigned int poll_period; + struct delayed_work dwork; +}; + +static int ili210x_read_reg(struct i2c_client *client, u8 reg, void *buf, + size_t len) +{ + struct i2c_msg msg[2] = { + { + .addr = client->addr, + .flags = 0, + .len = 1, + .buf = ®, + }, + { + .addr = client->addr, + .flags = I2C_M_RD, + .len = len, + .buf = buf, + } + }; + + if (i2c_transfer(client->adapter, msg, 2) != 2) { + dev_err(&client->dev, "i2c transfer failed\n"); + return -EIO; + } + + return 0; +} + +static void ili210x_report_events(struct input_dev *input, + const struct touchdata *touchdata) +{ + int i; + bool touch; + unsigned int x, y; + const struct finger *finger; + + for (i = 0; i < MAX_TOUCHES; i++) { + input_mt_slot(input, i); + + finger = &touchdata->finger[i]; + + touch = touchdata->status & (1 << i); + input_mt_report_slot_state(input, MT_TOOL_FINGER, touch); + if (touch) { + x = finger->x_low | (finger->x_high << 8); + y = finger->y_low | (finger->y_high << 8); + + input_report_abs(input, ABS_MT_POSITION_X, x); + input_report_abs(input, ABS_MT_POSITION_Y, y); + } + } + + input_mt_report_pointer_emulation(input, false); + input_sync(input); +} + +static bool get_pendown_state(const struct ili210x *priv) +{ + bool state = false; + + if (priv->get_pendown_state) + state = priv->get_pendown_state(); + + return state; +} + +static void ili210x_work(struct work_struct *work) +{ + struct ili210x *priv = container_of(work, struct ili210x, + dwork.work); + struct i2c_client *client = priv->client; + struct touchdata touchdata; + int error; + + error = ili210x_read_reg(client, REG_TOUCHDATA, + &touchdata, sizeof(touchdata)); + if (error) { + dev_err(&client->dev, + "Unable to get touchdata, err = %d\n", error); + return; + } + + ili210x_report_events(priv->input, &touchdata); + + if ((touchdata.status & 0xf3) || get_pendown_state(priv)) + schedule_delayed_work(&priv->dwork, + msecs_to_jiffies(priv->poll_period)); +} + +static irqreturn_t ili210x_irq(int irq, void *irq_data) +{ + struct ili210x *priv = irq_data; + + schedule_delayed_work(&priv->dwork, 0); + + return IRQ_HANDLED; +} + +static ssize_t ili210x_calibrate(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t count) +{ + struct i2c_client *client = to_i2c_client(dev); + struct ili210x *priv = i2c_get_clientdata(client); + unsigned long calibrate; + int rc; + u8 cmd = REG_CALIBRATE; + + if (kstrtoul(buf, 10, &calibrate)) + return -EINVAL; + + if (calibrate > 1) + return -EINVAL; + + if (calibrate) { + rc = i2c_master_send(priv->client, &cmd, sizeof(cmd)); + if (rc != sizeof(cmd)) + return -EIO; + } + + return count; +} +static DEVICE_ATTR(calibrate, 0644, NULL, ili210x_calibrate); + +static struct attribute *ili210x_attributes[] = { + &dev_attr_calibrate.attr, + NULL, +}; + +static const struct attribute_group ili210x_attr_group = { + .attrs = ili210x_attributes, +}; + +static int __devinit ili210x_i2c_probe(struct i2c_client *client, + const struct i2c_device_id *id) +{ + struct device *dev = &client->dev; + const struct ili210x_platform_data *pdata = dev->platform_data; + struct ili210x *priv; + struct input_dev *input; + struct panel_info panel; + struct firmware_version firmware; + int xmax, ymax; + int error; + + dev_dbg(dev, "Probing for ILI210X I2C Touschreen driver"); + + if (!pdata) { + dev_err(dev, "No platform data!\n"); + return -EINVAL; + } + + if (client->irq <= 0) { + dev_err(dev, "No IRQ!\n"); + return -EINVAL; + } + + /* Get firmware version */ + error = ili210x_read_reg(client, REG_FIRMWARE_VERSION, + &firmware, sizeof(firmware)); + if (error) { + dev_err(dev, "Failed to get firmware version, err: %d\n", + error); + return error; + } + + /* get panel info */ + error = ili210x_read_reg(client, REG_PANEL_INFO, &panel, sizeof(panel)); + if (error) { + dev_err(dev, "Failed to get panel informations, err: %d\n", + error); + return error; + } + + xmax = panel.finger_max.x_low | (panel.finger_max.x_high << 8); + ymax = panel.finger_max.y_low | (panel.finger_max.y_high << 8); + + priv = kzalloc(sizeof(*priv), GFP_KERNEL); + input = input_allocate_device(); + if (!priv || !input) { + error = -ENOMEM; + goto err_free_mem; + } + + priv->client = client; + priv->input = input; + priv->get_pendown_state = pdata->get_pendown_state; + priv->poll_period = pdata->poll_period ? : DEFAULT_POLL_PERIOD; + INIT_DELAYED_WORK(&priv->dwork, ili210x_work); + + /* Setup input device */ + input->name = "ILI210x Touchscreen"; + input->id.bustype = BUS_I2C; + input->dev.parent = dev; + + __set_bit(EV_SYN, input->evbit); + __set_bit(EV_KEY, input->evbit); + __set_bit(EV_ABS, input->evbit); + __set_bit(BTN_TOUCH, input->keybit); + + /* Single touch */ + input_set_abs_params(input, ABS_X, 0, xmax, 0, 0); + input_set_abs_params(input, ABS_Y, 0, ymax, 0, 0); + + /* Multi touch */ + input_mt_init_slots(input, MAX_TOUCHES); + input_set_abs_params(input, ABS_MT_POSITION_X, 0, xmax, 0, 0); + input_set_abs_params(input, ABS_MT_POSITION_Y, 0, ymax, 0, 0); + + input_set_drvdata(input, priv); + i2c_set_clientdata(client, priv); + + error = request_irq(client->irq, ili210x_irq, pdata->irq_flags, + client->name, priv); + if (error) { + dev_err(dev, "Unable to request touchscreen IRQ, err: %d\n", + error); + goto err_free_mem; + } + + error = sysfs_create_group(&dev->kobj, &ili210x_attr_group); + if (error) { + dev_err(dev, "Unable to create sysfs attributes, err: %d\n", + error); + goto err_free_irq; + } + + error = input_register_device(priv->input); + if (error) { + dev_err(dev, "Cannot regiser input device, err: %d\n", error); + goto err_remove_sysfs; + } + + device_init_wakeup(&client->dev, 1); + + dev_dbg(dev, + "ILI210x initialized (IRQ: %d), firmware version %d.%d.%d", + client->irq, firmware.id, firmware.major, firmware.minor); + + return 0; + +err_remove_sysfs: + sysfs_remove_group(&dev->kobj, &ili210x_attr_group); +err_free_irq: + free_irq(client->irq, priv); +err_free_mem: + input_free_device(input); + kfree(priv); + return error; +} + +static int __devexit ili210x_i2c_remove(struct i2c_client *client) +{ + struct ili210x *priv = i2c_get_clientdata(client); + + sysfs_remove_group(&client->dev.kobj, &ili210x_attr_group); + free_irq(priv->client->irq, priv); + cancel_delayed_work_sync(&priv->dwork); + input_unregister_device(priv->input); + kfree(priv); + + return 0; +} + +#ifdef CONFIG_PM_SLEEP +static int ili210x_i2c_suspend(struct device *dev) +{ + struct i2c_client *client = to_i2c_client(dev); + + if (device_may_wakeup(&client->dev)) + enable_irq_wake(client->irq); + + return 0; +} + +static int ili210x_i2c_resume(struct device *dev) +{ + struct i2c_client *client = to_i2c_client(dev); + + if (device_may_wakeup(&client->dev)) + disable_irq_wake(client->irq); + + return 0; +} +#endif + +static SIMPLE_DEV_PM_OPS(ili210x_i2c_pm, + ili210x_i2c_suspend, ili210x_i2c_resume); + +static const struct i2c_device_id ili210x_i2c_id[] = { + { "ili210x", 0 }, + { } +}; +MODULE_DEVICE_TABLE(i2c, ili210x_i2c_id); + +static struct i2c_driver ili210x_ts_driver = { + .driver = { + .name = "ili210x_i2c", + .owner = THIS_MODULE, + .pm = &ili210x_i2c_pm, + }, + .id_table = ili210x_i2c_id, + .probe = ili210x_i2c_probe, + .remove = __devexit_p(ili210x_i2c_remove), +}; + +module_i2c_driver(ili210x_ts_driver); + +MODULE_AUTHOR("Olivier Sobrie <olivier@sobrie.be>"); +MODULE_DESCRIPTION("ILI210X I2C Touchscreen Driver"); +MODULE_LICENSE("GPL"); diff --git a/drivers/input/touchscreen/jornada720_ts.c b/drivers/input/touchscreen/jornada720_ts.c index c3848ad2325b..d9be6eac99b1 100644 --- a/drivers/input/touchscreen/jornada720_ts.c +++ b/drivers/input/touchscreen/jornada720_ts.c @@ -22,6 +22,7 @@ #include <mach/hardware.h> #include <mach/jornada720.h> +#include <mach/irqs.h> MODULE_AUTHOR("Kristoffer Ericson <kristoffer.ericson@gmail.com>"); MODULE_DESCRIPTION("HP Jornada 710/720/728 touchscreen driver"); diff --git a/drivers/input/touchscreen/max11801_ts.c b/drivers/input/touchscreen/max11801_ts.c index 4627fe55b401..4eab50b856d7 100644 --- a/drivers/input/touchscreen/max11801_ts.c +++ b/drivers/input/touchscreen/max11801_ts.c @@ -255,18 +255,7 @@ static struct i2c_driver max11801_ts_driver = { .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_i2c_driver(max11801_ts_driver); MODULE_AUTHOR("Zhang Jiejing <jiejing.zhang@freescale.com>"); MODULE_DESCRIPTION("Touchscreen driver for MAXI MAX11801 controller"); diff --git a/drivers/input/touchscreen/mc13783_ts.c b/drivers/input/touchscreen/mc13783_ts.c index ede02743eac1..48dc5b0d26f1 100644 --- a/drivers/input/touchscreen/mc13783_ts.c +++ b/drivers/input/touchscreen/mc13783_ts.c @@ -39,6 +39,7 @@ struct mc13783_ts_priv { struct delayed_work work; struct workqueue_struct *workq; unsigned int sample[4]; + struct mc13xxx_ts_platform_data *touch; }; static irqreturn_t mc13783_ts_handler(int irq, void *data) @@ -125,7 +126,9 @@ static void mc13783_ts_work(struct work_struct *work) unsigned int channel = 12; if (mc13xxx_adc_do_conversion(priv->mc13xxx, - mode, channel, priv->sample) == 0) + mode, channel, + priv->touch->ato, priv->touch->atox, + priv->sample) == 0) mc13783_ts_report_sample(priv); } @@ -179,6 +182,12 @@ static int __init mc13783_ts_probe(struct platform_device *pdev) INIT_DELAYED_WORK(&priv->work, mc13783_ts_work); priv->mc13xxx = dev_get_drvdata(pdev->dev.parent); priv->idev = idev; + priv->touch = dev_get_platdata(&pdev->dev); + if (!priv->touch) { + dev_err(&pdev->dev, "missing platform data\n"); + ret = -ENODEV; + goto err_free_mem; + } /* * We need separate workqueue because mc13783_adc_do_conversion diff --git a/drivers/input/touchscreen/mcs5000_ts.c b/drivers/input/touchscreen/mcs5000_ts.c index 2d84c80ceb66..b528511861ce 100644 --- a/drivers/input/touchscreen/mcs5000_ts.c +++ b/drivers/input/touchscreen/mcs5000_ts.c @@ -302,18 +302,7 @@ static struct i2c_driver mcs5000_ts_driver = { .id_table = mcs5000_ts_id, }; -static int __init mcs5000_ts_init(void) -{ - return i2c_add_driver(&mcs5000_ts_driver); -} - -static void __exit mcs5000_ts_exit(void) -{ - i2c_del_driver(&mcs5000_ts_driver); -} - -module_init(mcs5000_ts_init); -module_exit(mcs5000_ts_exit); +module_i2c_driver(mcs5000_ts_driver); /* Module information */ MODULE_AUTHOR("Joonyoung Shim <jy0922.shim@samsung.com>"); diff --git a/drivers/input/touchscreen/migor_ts.c b/drivers/input/touchscreen/migor_ts.c index 5226194aa78e..c038db93e2c3 100644 --- a/drivers/input/touchscreen/migor_ts.c +++ b/drivers/input/touchscreen/migor_ts.c @@ -242,19 +242,8 @@ static struct i2c_driver migor_ts_driver = { .id_table = migor_ts_id, }; -static int __init migor_ts_init(void) -{ - return i2c_add_driver(&migor_ts_driver); -} - -static void __exit migor_ts_exit(void) -{ - i2c_del_driver(&migor_ts_driver); -} +module_i2c_driver(migor_ts_driver); MODULE_DESCRIPTION("MigoR Touchscreen driver"); MODULE_AUTHOR("Magnus Damm <damm@opensource.se>"); MODULE_LICENSE("GPL"); - -module_init(migor_ts_init); -module_exit(migor_ts_exit); diff --git a/drivers/input/touchscreen/pixcir_i2c_ts.c b/drivers/input/touchscreen/pixcir_i2c_ts.c index d5ac09a1ee56..72f6ba3a4709 100644 --- a/drivers/input/touchscreen/pixcir_i2c_ts.c +++ b/drivers/input/touchscreen/pixcir_i2c_ts.c @@ -222,17 +222,7 @@ static struct i2c_driver pixcir_i2c_ts_driver = { .id_table = pixcir_i2c_ts_id, }; -static int __init pixcir_i2c_ts_init(void) -{ - return i2c_add_driver(&pixcir_i2c_ts_driver); -} -module_init(pixcir_i2c_ts_init); - -static void __exit pixcir_i2c_ts_exit(void) -{ - i2c_del_driver(&pixcir_i2c_ts_driver); -} -module_exit(pixcir_i2c_ts_exit); +module_i2c_driver(pixcir_i2c_ts_driver); MODULE_AUTHOR("Jianchun Bian <jcbian@pixcir.com.cn>, Dequan Meng <dqmeng@pixcir.com.cn>"); MODULE_DESCRIPTION("Pixcir I2C Touchscreen Driver"); diff --git a/drivers/input/touchscreen/st1232.c b/drivers/input/touchscreen/st1232.c index 8825fe37d433..cbbf71b22696 100644 --- a/drivers/input/touchscreen/st1232.c +++ b/drivers/input/touchscreen/st1232.c @@ -268,17 +268,7 @@ static struct i2c_driver st1232_ts_driver = { }, }; -static int __init st1232_ts_init(void) -{ - return i2c_add_driver(&st1232_ts_driver); -} -module_init(st1232_ts_init); - -static void __exit st1232_ts_exit(void) -{ - i2c_del_driver(&st1232_ts_driver); -} -module_exit(st1232_ts_exit); +module_i2c_driver(st1232_ts_driver); MODULE_AUTHOR("Tony SIM <chinyeow.sim.xt@renesas.com>"); MODULE_DESCRIPTION("SITRONIX ST1232 Touchscreen Controller Driver"); diff --git a/drivers/input/touchscreen/ti_tscadc.c b/drivers/input/touchscreen/ti_tscadc.c new file mode 100644 index 000000000000..d229c741d544 --- /dev/null +++ b/drivers/input/touchscreen/ti_tscadc.c @@ -0,0 +1,486 @@ +/* + * TI Touch Screen driver + * + * Copyright (C) 2011 Texas Instruments Incorporated - http://www.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 the Free Software Foundation version 2. + * + * This program is distributed "as is" WITHOUT ANY WARRANTY of any + * kind, whether express or implied; without even the implied warranty + * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + + +#include <linux/init.h> +#include <linux/kernel.h> +#include <linux/err.h> +#include <linux/module.h> +#include <linux/input.h> +#include <linux/slab.h> +#include <linux/interrupt.h> +#include <linux/clk.h> +#include <linux/platform_device.h> +#include <linux/io.h> +#include <linux/input/ti_tscadc.h> +#include <linux/delay.h> + +#define REG_IRQEOI 0x020 +#define REG_RAWIRQSTATUS 0x024 +#define REG_IRQSTATUS 0x028 +#define REG_IRQENABLE 0x02C +#define REG_IRQWAKEUP 0x034 +#define REG_CTRL 0x040 +#define REG_ADCFSM 0x044 +#define REG_CLKDIV 0x04C +#define REG_SE 0x054 +#define REG_IDLECONFIG 0x058 +#define REG_CHARGECONFIG 0x05C +#define REG_CHARGEDELAY 0x060 +#define REG_STEPCONFIG(n) (0x64 + ((n - 1) * 8)) +#define REG_STEPDELAY(n) (0x68 + ((n - 1) * 8)) +#define REG_STEPCONFIG13 0x0C4 +#define REG_STEPDELAY13 0x0C8 +#define REG_STEPCONFIG14 0x0CC +#define REG_STEPDELAY14 0x0D0 +#define REG_FIFO0CNT 0xE4 +#define REG_FIFO1THR 0xF4 +#define REG_FIFO0 0x100 +#define REG_FIFO1 0x200 + +/* Register Bitfields */ +#define IRQWKUP_ENB BIT(0) +#define STPENB_STEPENB 0x7FFF +#define IRQENB_FIFO1THRES BIT(5) +#define IRQENB_PENUP BIT(9) +#define STEPCONFIG_MODE_HWSYNC 0x2 +#define STEPCONFIG_SAMPLES_AVG (1 << 4) +#define STEPCONFIG_XPP (1 << 5) +#define STEPCONFIG_XNN (1 << 6) +#define STEPCONFIG_YPP (1 << 7) +#define STEPCONFIG_YNN (1 << 8) +#define STEPCONFIG_XNP (1 << 9) +#define STEPCONFIG_YPN (1 << 10) +#define STEPCONFIG_INM (1 << 18) +#define STEPCONFIG_INP (1 << 20) +#define STEPCONFIG_INP_5 (1 << 21) +#define STEPCONFIG_FIFO1 (1 << 26) +#define STEPCONFIG_OPENDLY 0xff +#define STEPCONFIG_Z1 (3 << 19) +#define STEPIDLE_INP (1 << 22) +#define STEPCHARGE_RFP (1 << 12) +#define STEPCHARGE_INM (1 << 15) +#define STEPCHARGE_INP (1 << 19) +#define STEPCHARGE_RFM (1 << 23) +#define STEPCHARGE_DELAY 0x1 +#define CNTRLREG_TSCSSENB (1 << 0) +#define CNTRLREG_STEPID (1 << 1) +#define CNTRLREG_STEPCONFIGWRT (1 << 2) +#define CNTRLREG_4WIRE (1 << 5) +#define CNTRLREG_5WIRE (1 << 6) +#define CNTRLREG_8WIRE (3 << 5) +#define CNTRLREG_TSCENB (1 << 7) +#define ADCFSM_STEPID 0x10 + +#define SEQ_SETTLE 275 +#define ADC_CLK 3000000 +#define MAX_12BIT ((1 << 12) - 1) +#define TSCADC_DELTA_X 15 +#define TSCADC_DELTA_Y 15 + +struct tscadc { + struct input_dev *input; + struct clk *tsc_ick; + void __iomem *tsc_base; + unsigned int irq; + unsigned int wires; + unsigned int x_plate_resistance; + bool pen_down; +}; + +static unsigned int tscadc_readl(struct tscadc *ts, unsigned int reg) +{ + return readl(ts->tsc_base + reg); +} + +static void tscadc_writel(struct tscadc *tsc, unsigned int reg, + unsigned int val) +{ + writel(val, tsc->tsc_base + reg); +} + +static void tscadc_step_config(struct tscadc *ts_dev) +{ + unsigned int config; + int i; + + /* Configure the Step registers */ + + config = STEPCONFIG_MODE_HWSYNC | + STEPCONFIG_SAMPLES_AVG | STEPCONFIG_XPP; + switch (ts_dev->wires) { + case 4: + config |= STEPCONFIG_INP | STEPCONFIG_XNN; + break; + case 5: + config |= STEPCONFIG_YNN | + STEPCONFIG_INP_5 | STEPCONFIG_XNN | + STEPCONFIG_YPP; + break; + case 8: + config |= STEPCONFIG_INP | STEPCONFIG_XNN; + break; + } + + for (i = 1; i < 7; i++) { + tscadc_writel(ts_dev, REG_STEPCONFIG(i), config); + tscadc_writel(ts_dev, REG_STEPDELAY(i), STEPCONFIG_OPENDLY); + } + + config = 0; + config = STEPCONFIG_MODE_HWSYNC | + STEPCONFIG_SAMPLES_AVG | STEPCONFIG_YNN | + STEPCONFIG_INM | STEPCONFIG_FIFO1; + switch (ts_dev->wires) { + case 4: + config |= STEPCONFIG_YPP; + break; + case 5: + config |= STEPCONFIG_XPP | STEPCONFIG_INP_5 | + STEPCONFIG_XNP | STEPCONFIG_YPN; + break; + case 8: + config |= STEPCONFIG_YPP; + break; + } + + for (i = 7; i < 13; i++) { + tscadc_writel(ts_dev, REG_STEPCONFIG(i), config); + tscadc_writel(ts_dev, REG_STEPDELAY(i), STEPCONFIG_OPENDLY); + } + + config = 0; + /* Charge step configuration */ + config = STEPCONFIG_XPP | STEPCONFIG_YNN | + STEPCHARGE_RFP | STEPCHARGE_RFM | + STEPCHARGE_INM | STEPCHARGE_INP; + + tscadc_writel(ts_dev, REG_CHARGECONFIG, config); + tscadc_writel(ts_dev, REG_CHARGEDELAY, STEPCHARGE_DELAY); + + config = 0; + /* Configure to calculate pressure */ + config = STEPCONFIG_MODE_HWSYNC | + STEPCONFIG_SAMPLES_AVG | STEPCONFIG_YPP | + STEPCONFIG_XNN | STEPCONFIG_INM; + tscadc_writel(ts_dev, REG_STEPCONFIG13, config); + tscadc_writel(ts_dev, REG_STEPDELAY13, STEPCONFIG_OPENDLY); + + config |= STEPCONFIG_Z1 | STEPCONFIG_FIFO1; + tscadc_writel(ts_dev, REG_STEPCONFIG14, config); + tscadc_writel(ts_dev, REG_STEPDELAY14, STEPCONFIG_OPENDLY); + + tscadc_writel(ts_dev, REG_SE, STPENB_STEPENB); +} + +static void tscadc_idle_config(struct tscadc *ts_config) +{ + unsigned int idleconfig; + + idleconfig = STEPCONFIG_YNN | + STEPCONFIG_INM | + STEPCONFIG_YPN | STEPIDLE_INP; + tscadc_writel(ts_config, REG_IDLECONFIG, idleconfig); +} + +static void tscadc_read_coordinates(struct tscadc *ts_dev, + unsigned int *x, unsigned int *y) +{ + unsigned int fifocount = tscadc_readl(ts_dev, REG_FIFO0CNT); + unsigned int prev_val_x = ~0, prev_val_y = ~0; + unsigned int prev_diff_x = ~0, prev_diff_y = ~0; + unsigned int read, diff; + unsigned int i; + + /* + * Delta filter is used to remove large variations in sampled + * values from ADC. The filter tries to predict where the next + * coordinate could be. This is done by taking a previous + * coordinate and subtracting it form current one. Further the + * algorithm compares the difference with that of a present value, + * if true the value is reported to the sub system. + */ + for (i = 0; i < fifocount - 1; i++) { + read = tscadc_readl(ts_dev, REG_FIFO0) & 0xfff; + diff = abs(read - prev_val_x); + if (diff < prev_diff_x) { + prev_diff_x = diff; + *x = read; + } + prev_val_x = read; + + read = tscadc_readl(ts_dev, REG_FIFO1) & 0xfff; + diff = abs(read - prev_val_y); + if (diff < prev_diff_y) { + prev_diff_y = diff; + *y = read; + } + prev_val_y = read; + } +} + +static irqreturn_t tscadc_irq(int irq, void *dev) +{ + struct tscadc *ts_dev = dev; + struct input_dev *input_dev = ts_dev->input; + unsigned int status, irqclr = 0; + unsigned int x = 0, y = 0; + unsigned int z1, z2, z; + unsigned int fsm; + + status = tscadc_readl(ts_dev, REG_IRQSTATUS); + if (status & IRQENB_FIFO1THRES) { + tscadc_read_coordinates(ts_dev, &x, &y); + + z1 = tscadc_readl(ts_dev, REG_FIFO0) & 0xfff; + z2 = tscadc_readl(ts_dev, REG_FIFO1) & 0xfff; + + if (ts_dev->pen_down && z1 != 0 && z2 != 0) { + /* + * Calculate pressure using formula + * Resistance(touch) = x plate resistance * + * x postion/4096 * ((z2 / z1) - 1) + */ + z = z2 - z1; + z *= x; + z *= ts_dev->x_plate_resistance; + z /= z1; + z = (z + 2047) >> 12; + + if (z <= MAX_12BIT) { + input_report_abs(input_dev, ABS_X, x); + input_report_abs(input_dev, ABS_Y, y); + input_report_abs(input_dev, ABS_PRESSURE, z); + input_report_key(input_dev, BTN_TOUCH, 1); + input_sync(input_dev); + } + } + irqclr |= IRQENB_FIFO1THRES; + } + + /* + * Time for sequencer to settle, to read + * correct state of the sequencer. + */ + udelay(SEQ_SETTLE); + + status = tscadc_readl(ts_dev, REG_RAWIRQSTATUS); + if (status & IRQENB_PENUP) { + /* Pen up event */ + fsm = tscadc_readl(ts_dev, REG_ADCFSM); + if (fsm == ADCFSM_STEPID) { + ts_dev->pen_down = false; + input_report_key(input_dev, BTN_TOUCH, 0); + input_report_abs(input_dev, ABS_PRESSURE, 0); + input_sync(input_dev); + } else { + ts_dev->pen_down = true; + } + irqclr |= IRQENB_PENUP; + } + + tscadc_writel(ts_dev, REG_IRQSTATUS, irqclr); + /* check pending interrupts */ + tscadc_writel(ts_dev, REG_IRQEOI, 0x0); + + tscadc_writel(ts_dev, REG_SE, STPENB_STEPENB); + return IRQ_HANDLED; +} + +/* + * The functions for inserting/removing driver as a module. + */ + +static int __devinit tscadc_probe(struct platform_device *pdev) +{ + const struct tsc_data *pdata = pdev->dev.platform_data; + struct resource *res; + struct tscadc *ts_dev; + struct input_dev *input_dev; + struct clk *clk; + int err; + int clk_value, ctrl, irq; + + if (!pdata) { + dev_err(&pdev->dev, "missing platform data.\n"); + return -EINVAL; + } + + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + if (!res) { + dev_err(&pdev->dev, "no memory resource defined.\n"); + return -EINVAL; + } + + irq = platform_get_irq(pdev, 0); + if (irq < 0) { + dev_err(&pdev->dev, "no irq ID is specified.\n"); + return -EINVAL; + } + + /* Allocate memory for device */ + ts_dev = kzalloc(sizeof(struct tscadc), GFP_KERNEL); + input_dev = input_allocate_device(); + if (!ts_dev || !input_dev) { + dev_err(&pdev->dev, "failed to allocate memory.\n"); + err = -ENOMEM; + goto err_free_mem; + } + + ts_dev->input = input_dev; + ts_dev->irq = irq; + ts_dev->wires = pdata->wires; + ts_dev->x_plate_resistance = pdata->x_plate_resistance; + + res = request_mem_region(res->start, resource_size(res), pdev->name); + if (!res) { + dev_err(&pdev->dev, "failed to reserve registers.\n"); + err = -EBUSY; + goto err_free_mem; + } + + ts_dev->tsc_base = ioremap(res->start, resource_size(res)); + if (!ts_dev->tsc_base) { + dev_err(&pdev->dev, "failed to map registers.\n"); + err = -ENOMEM; + goto err_release_mem_region; + } + + err = request_irq(ts_dev->irq, tscadc_irq, + 0, pdev->dev.driver->name, ts_dev); + if (err) { + dev_err(&pdev->dev, "failed to allocate irq.\n"); + goto err_unmap_regs; + } + + ts_dev->tsc_ick = clk_get(&pdev->dev, "adc_tsc_ick"); + if (IS_ERR(ts_dev->tsc_ick)) { + dev_err(&pdev->dev, "failed to get TSC ick\n"); + goto err_free_irq; + } + clk_enable(ts_dev->tsc_ick); + + clk = clk_get(&pdev->dev, "adc_tsc_fck"); + if (IS_ERR(clk)) { + dev_err(&pdev->dev, "failed to get TSC fck\n"); + err = PTR_ERR(clk); + goto err_disable_clk; + } + + clk_value = clk_get_rate(clk) / ADC_CLK; + clk_put(clk); + + if (clk_value < 7) { + dev_err(&pdev->dev, "clock input less than min clock requirement\n"); + goto err_disable_clk; + } + /* CLKDIV needs to be configured to the value minus 1 */ + tscadc_writel(ts_dev, REG_CLKDIV, clk_value - 1); + + /* Enable wake-up of the SoC using touchscreen */ + tscadc_writel(ts_dev, REG_IRQWAKEUP, IRQWKUP_ENB); + + ctrl = CNTRLREG_STEPCONFIGWRT | + CNTRLREG_TSCENB | + CNTRLREG_STEPID; + switch (ts_dev->wires) { + case 4: + ctrl |= CNTRLREG_4WIRE; + break; + case 5: + ctrl |= CNTRLREG_5WIRE; + break; + case 8: + ctrl |= CNTRLREG_8WIRE; + break; + } + tscadc_writel(ts_dev, REG_CTRL, ctrl); + + tscadc_idle_config(ts_dev); + tscadc_writel(ts_dev, REG_IRQENABLE, IRQENB_FIFO1THRES); + tscadc_step_config(ts_dev); + tscadc_writel(ts_dev, REG_FIFO1THR, 6); + + ctrl |= CNTRLREG_TSCSSENB; + tscadc_writel(ts_dev, REG_CTRL, ctrl); + + input_dev->name = "ti-tsc-adc"; + input_dev->dev.parent = &pdev->dev; + + input_dev->evbit[0] = BIT_MASK(EV_KEY) | BIT_MASK(EV_ABS); + input_dev->keybit[BIT_WORD(BTN_TOUCH)] = BIT_MASK(BTN_TOUCH); + + input_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); + + /* register to the input system */ + err = input_register_device(input_dev); + if (err) + goto err_disable_clk; + + platform_set_drvdata(pdev, ts_dev); + return 0; + +err_disable_clk: + clk_disable(ts_dev->tsc_ick); + clk_put(ts_dev->tsc_ick); +err_free_irq: + free_irq(ts_dev->irq, ts_dev); +err_unmap_regs: + iounmap(ts_dev->tsc_base); +err_release_mem_region: + release_mem_region(res->start, resource_size(res)); +err_free_mem: + input_free_device(input_dev); + kfree(ts_dev); + return err; +} + +static int __devexit tscadc_remove(struct platform_device *pdev) +{ + struct tscadc *ts_dev = platform_get_drvdata(pdev); + struct resource *res; + + free_irq(ts_dev->irq, ts_dev); + + input_unregister_device(ts_dev->input); + + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + iounmap(ts_dev->tsc_base); + release_mem_region(res->start, resource_size(res)); + + clk_disable(ts_dev->tsc_ick); + clk_put(ts_dev->tsc_ick); + + kfree(ts_dev); + + platform_set_drvdata(pdev, NULL); + return 0; +} + +static struct platform_driver ti_tsc_driver = { + .probe = tscadc_probe, + .remove = __devexit_p(tscadc_remove), + .driver = { + .name = "tsc", + .owner = THIS_MODULE, + }, +}; +module_platform_driver(ti_tsc_driver); + +MODULE_DESCRIPTION("TI touchscreen controller driver"); +MODULE_AUTHOR("Rachna Patil <rachna@ti.com>"); +MODULE_LICENSE("GPL"); diff --git a/drivers/input/touchscreen/tsc2005.c b/drivers/input/touchscreen/tsc2005.c index 067d95662997..b6adeaee9cc5 100644 --- a/drivers/input/touchscreen/tsc2005.c +++ b/drivers/input/touchscreen/tsc2005.c @@ -747,17 +747,7 @@ static struct spi_driver tsc2005_driver = { .remove = __devexit_p(tsc2005_remove), }; -static int __init tsc2005_init(void) -{ - return spi_register_driver(&tsc2005_driver); -} -module_init(tsc2005_init); - -static void __exit tsc2005_exit(void) -{ - spi_unregister_driver(&tsc2005_driver); -} -module_exit(tsc2005_exit); +module_spi_driver(tsc2005_driver); MODULE_AUTHOR("Lauri Leukkunen <lauri.leukkunen@nokia.com>"); MODULE_DESCRIPTION("TSC2005 Touchscreen Driver"); diff --git a/drivers/input/touchscreen/tsc2007.c b/drivers/input/touchscreen/tsc2007.c index 1f674cb6c55b..1473d2382afd 100644 --- a/drivers/input/touchscreen/tsc2007.c +++ b/drivers/input/touchscreen/tsc2007.c @@ -399,18 +399,7 @@ static struct i2c_driver tsc2007_driver = { .remove = __devexit_p(tsc2007_remove), }; -static int __init tsc2007_init(void) -{ - return i2c_add_driver(&tsc2007_driver); -} - -static void __exit tsc2007_exit(void) -{ - i2c_del_driver(&tsc2007_driver); -} - -module_init(tsc2007_init); -module_exit(tsc2007_exit); +module_i2c_driver(tsc2007_driver); MODULE_AUTHOR("Kwangwoo Lee <kwlee@mtekvision.com>"); MODULE_DESCRIPTION("TSC2007 TouchScreen Driver"); diff --git a/drivers/input/touchscreen/usbtouchscreen.c b/drivers/input/touchscreen/usbtouchscreen.c index 3a5ebf452e81..22cd96f58c99 100644 --- a/drivers/input/touchscreen/usbtouchscreen.c +++ b/drivers/input/touchscreen/usbtouchscreen.c @@ -17,6 +17,7 @@ * - Zytronic capacitive touchscreen * - NEXIO/iNexio * - Elo TouchSystems 2700 IntelliTouch + * - EasyTouch USB Dual/Multi touch controller from Data Modul * * Copyright (C) 2004-2007 by Daniel Ritz <daniel.ritz@gmx.ch> * Copyright (C) by Todd E. Johnson (mtouchusb.c) @@ -140,6 +141,7 @@ enum { DEVTYPE_TC45USB, DEVTYPE_NEXIO, DEVTYPE_ELO, + DEVTYPE_ETOUCH, }; #define USB_DEVICE_HID_CLASS(vend, prod) \ @@ -245,6 +247,10 @@ static const struct usb_device_id usbtouch_devices[] = { {USB_DEVICE(0x04e7, 0x0020), .driver_info = DEVTYPE_ELO}, #endif +#ifdef CONFIG_TOUCHSCREEN_USB_EASYTOUCH + {USB_DEVICE(0x7374, 0x0001), .driver_info = DEVTYPE_ETOUCH}, +#endif + {} }; @@ -326,6 +332,51 @@ static int egalax_get_pkt_len(unsigned char *buf, int len) } #endif +/***************************************************************************** + * EasyTouch part + */ + +#ifdef CONFIG_TOUCHSCREEN_USB_EASYTOUCH + +#ifndef MULTI_PACKET +#define MULTI_PACKET +#endif + +#define ETOUCH_PKT_TYPE_MASK 0xFE +#define ETOUCH_PKT_TYPE_REPT 0x80 +#define ETOUCH_PKT_TYPE_REPT2 0xB0 +#define ETOUCH_PKT_TYPE_DIAG 0x0A + +static int etouch_read_data(struct usbtouch_usb *dev, unsigned char *pkt) +{ + if ((pkt[0] & ETOUCH_PKT_TYPE_MASK) != ETOUCH_PKT_TYPE_REPT && + (pkt[0] & ETOUCH_PKT_TYPE_MASK) != ETOUCH_PKT_TYPE_REPT2) + return 0; + + dev->x = ((pkt[1] & 0x1F) << 7) | (pkt[2] & 0x7F); + dev->y = ((pkt[3] & 0x1F) << 7) | (pkt[4] & 0x7F); + dev->touch = pkt[0] & 0x01; + + return 1; +} + +static int etouch_get_pkt_len(unsigned char *buf, int len) +{ + switch (buf[0] & ETOUCH_PKT_TYPE_MASK) { + case ETOUCH_PKT_TYPE_REPT: + case ETOUCH_PKT_TYPE_REPT2: + return 5; + + case ETOUCH_PKT_TYPE_DIAG: + if (len < 2) + return -1; + + return buf[1] + 2; + } + + return 0; +} +#endif /***************************************************************************** * PanJit Part @@ -1175,6 +1226,18 @@ static struct usbtouch_device_info usbtouch_dev_info[] = { .exit = nexio_exit, }, #endif +#ifdef CONFIG_TOUCHSCREEN_USB_EASYTOUCH + [DEVTYPE_ETOUCH] = { + .min_xc = 0x0, + .max_xc = 0x07ff, + .min_yc = 0x0, + .max_yc = 0x07ff, + .rept_size = 16, + .process_pkt = usbtouch_process_multi, + .get_pkt_len = etouch_get_pkt_len, + .read_data = etouch_read_data, + }, +#endif }; |