diff options
Diffstat (limited to 'drivers/input')
106 files changed, 10774 insertions, 2707 deletions
diff --git a/drivers/input/Kconfig b/drivers/input/Kconfig index b8e0424d0f8..07c2cd43109 100644 --- a/drivers/input/Kconfig +++ b/drivers/input/Kconfig @@ -8,7 +8,7 @@ menu "Input device support" config INPUT tristate "Generic input layer (needed for keyboard, mouse, ...)" if EMBEDDED default y - ---help--- + help Say Y here if you have any input device (mouse, keyboard, tablet, joystick, steering wheel ...) connected to your system and want it to be available to applications. This includes standard PS/2 @@ -27,8 +27,7 @@ if INPUT config INPUT_FF_MEMLESS tristate "Support for memoryless force-feedback devices" - default n - ---help--- + help Say Y here if you have memoryless force-feedback input device such as Logitech WingMan Force 3D, ThrustMaster FireStorm Dual Power 2, or similar. You will also need to enable hardware-specific @@ -52,12 +51,25 @@ config INPUT_POLLDEV To compile this driver as a module, choose M here: the module will be called input-polldev. +config INPUT_SPARSEKMAP + tristate "Sparse keymap support library" + help + Say Y here if you are using a driver for an input + device that uses sparse keymap. This option is only + useful for out-of-tree drivers since in-tree drivers + select it automatically. + + If unsure, say N. + + To compile this driver as a module, choose M here: the + module will be called sparse-keymap. + comment "Userland interfaces" config INPUT_MOUSEDEV tristate "Mouse interface" if EMBEDDED default y - ---help--- + help Say Y here if you want your mouse to be accessible as char devices 13:32+ - /dev/input/mouseX and 13:63 - /dev/input/mice as an emulated IntelliMouse Explorer PS/2 mouse. That way, all user space @@ -73,7 +85,7 @@ config INPUT_MOUSEDEV_PSAUX bool "Provide legacy /dev/psaux device" default y depends on INPUT_MOUSEDEV - ---help--- + help Say Y here if you want your mouse also be accessible as char device 10:1 - /dev/psaux. The data available through /dev/psaux is exactly the same as the data from /dev/input/mice. @@ -103,7 +115,7 @@ config INPUT_MOUSEDEV_SCREEN_Y config INPUT_JOYDEV tristate "Joystick interface" - ---help--- + help Say Y here if you want your joystick or gamepad to be accessible as char device 13:0+ - /dev/input/jsX device. @@ -125,7 +137,7 @@ config INPUT_EVDEV config INPUT_EVBUG tristate "Event debugging" - ---help--- + help Say Y here if you have a problem with the input subsystem and want all events (keypresses, mouse movements), to be output to the system log. While this is useful for debugging, it's also @@ -140,7 +152,7 @@ config INPUT_EVBUG config INPUT_APMPOWER tristate "Input Power Event -> APM Bridge" if EMBEDDED depends on INPUT && APM_EMULATION - ---help--- + help Say Y here if you want suspend key events to trigger a user requested suspend through APM. This is useful on embedded systems where such behaviour is desired without userspace diff --git a/drivers/input/Makefile b/drivers/input/Makefile index 4c9c745a702..7ad212d31f9 100644 --- a/drivers/input/Makefile +++ b/drivers/input/Makefile @@ -9,6 +9,7 @@ input-core-objs := input.o input-compat.o ff-core.o obj-$(CONFIG_INPUT_FF_MEMLESS) += ff-memless.o obj-$(CONFIG_INPUT_POLLDEV) += input-polldev.o +obj-$(CONFIG_INPUT_SPARSEKMAP) += sparse-keymap.o obj-$(CONFIG_INPUT_MOUSEDEV) += mousedev.o obj-$(CONFIG_INPUT_JOYDEV) += joydev.o diff --git a/drivers/input/evdev.c b/drivers/input/evdev.c index 1148140d08a..dee6706038a 100644 --- a/drivers/input/evdev.c +++ b/drivers/input/evdev.c @@ -13,6 +13,7 @@ #define EVDEV_BUFFER_SIZE 64 #include <linux/poll.h> +#include <linux/sched.h> #include <linux/slab.h> #include <linux/module.h> #include <linux/init.h> diff --git a/drivers/input/ff-core.c b/drivers/input/ff-core.c index 72c63e5dd63..b2f07aa1604 100644 --- a/drivers/input/ff-core.c +++ b/drivers/input/ff-core.c @@ -337,23 +337,23 @@ int input_ff_create(struct input_dev *dev, int max_effects) dev->ff = ff; dev->flush = flush_effects; dev->event = input_ff_event; - set_bit(EV_FF, dev->evbit); + __set_bit(EV_FF, dev->evbit); /* Copy "true" bits into ff device bitmap */ for (i = 0; i <= FF_MAX; i++) if (test_bit(i, dev->ffbit)) - set_bit(i, ff->ffbit); + __set_bit(i, ff->ffbit); /* we can emulate RUMBLE with periodic effects */ if (test_bit(FF_PERIODIC, ff->ffbit)) - set_bit(FF_RUMBLE, dev->ffbit); + __set_bit(FF_RUMBLE, dev->ffbit); return 0; } EXPORT_SYMBOL_GPL(input_ff_create); /** - * input_ff_free() - frees force feedback portion of input device + * input_ff_destroy() - frees force feedback portion of input device * @dev: input device supporting force feedback * * This function is only needed in error path as input core will @@ -362,12 +362,15 @@ EXPORT_SYMBOL_GPL(input_ff_create); */ void input_ff_destroy(struct input_dev *dev) { - clear_bit(EV_FF, dev->evbit); - if (dev->ff) { - if (dev->ff->destroy) - dev->ff->destroy(dev->ff); - kfree(dev->ff->private); - kfree(dev->ff); + struct ff_device *ff = dev->ff; + + __clear_bit(EV_FF, dev->evbit); + if (ff) { + if (ff->destroy) + ff->destroy(ff); + kfree(ff->private); + kfree(ff->effects); + kfree(ff); dev->ff = NULL; } } diff --git a/drivers/input/ff-memless.c b/drivers/input/ff-memless.c index 2d1415e1683..b483b2995fa 100644 --- a/drivers/input/ff-memless.c +++ b/drivers/input/ff-memless.c @@ -61,7 +61,6 @@ struct ml_device { struct ml_effect_state states[FF_MEMLESS_EFFECTS]; int gain; struct timer_list timer; - spinlock_t timer_lock; struct input_dev *dev; int (*play_effect)(struct input_dev *dev, void *data, @@ -368,38 +367,38 @@ static void ml_effect_timer(unsigned long timer_data) { struct input_dev *dev = (struct input_dev *)timer_data; struct ml_device *ml = dev->ff->private; + unsigned long flags; debug("timer: updating effects"); - spin_lock(&ml->timer_lock); + spin_lock_irqsave(&dev->event_lock, flags); ml_play_effects(ml); - spin_unlock(&ml->timer_lock); + spin_unlock_irqrestore(&dev->event_lock, flags); } +/* + * Sets requested gain for FF effects. Called with dev->event_lock held. + */ static void ml_ff_set_gain(struct input_dev *dev, u16 gain) { struct ml_device *ml = dev->ff->private; int i; - spin_lock_bh(&ml->timer_lock); - ml->gain = gain; for (i = 0; i < FF_MEMLESS_EFFECTS; i++) __clear_bit(FF_EFFECT_PLAYING, &ml->states[i].flags); ml_play_effects(ml); - - spin_unlock_bh(&ml->timer_lock); } +/* + * Start/stop specified FF effect. Called with dev->event_lock held. + */ static int ml_ff_playback(struct input_dev *dev, int effect_id, int value) { struct ml_device *ml = dev->ff->private; struct ml_effect_state *state = &ml->states[effect_id]; - unsigned long flags; - - spin_lock_irqsave(&ml->timer_lock, flags); if (value > 0) { debug("initiated play"); @@ -425,8 +424,6 @@ static int ml_ff_playback(struct input_dev *dev, int effect_id, int value) ml_play_effects(ml); } - spin_unlock_irqrestore(&ml->timer_lock, flags); - return 0; } @@ -436,7 +433,7 @@ static int ml_ff_upload(struct input_dev *dev, struct ml_device *ml = dev->ff->private; struct ml_effect_state *state = &ml->states[effect->id]; - spin_lock_bh(&ml->timer_lock); + spin_lock_irq(&dev->event_lock); if (test_bit(FF_EFFECT_STARTED, &state->flags)) { __clear_bit(FF_EFFECT_PLAYING, &state->flags); @@ -448,7 +445,7 @@ static int ml_ff_upload(struct input_dev *dev, ml_schedule_timer(ml); } - spin_unlock_bh(&ml->timer_lock); + spin_unlock_irq(&dev->event_lock); return 0; } @@ -482,7 +479,6 @@ int input_ff_create_memless(struct input_dev *dev, void *data, ml->private = data; ml->play_effect = play_effect; ml->gain = 0xffff; - spin_lock_init(&ml->timer_lock); setup_timer(&ml->timer, ml_effect_timer, (unsigned long)dev); set_bit(FF_GAIN, dev->ffbit); diff --git a/drivers/input/input-polldev.c b/drivers/input/input-polldev.c index 0d3ce7a50fb..aa6713b4a98 100644 --- a/drivers/input/input-polldev.c +++ b/drivers/input/input-polldev.c @@ -56,14 +56,10 @@ static void input_polldev_stop_workqueue(void) mutex_unlock(&polldev_mutex); } -static void input_polled_device_work(struct work_struct *work) +static void input_polldev_queue_work(struct input_polled_dev *dev) { - struct input_polled_dev *dev = - container_of(work, struct input_polled_dev, work.work); unsigned long delay; - dev->poll(dev); - delay = msecs_to_jiffies(dev->poll_interval); if (delay >= HZ) delay = round_jiffies_relative(delay); @@ -71,6 +67,15 @@ static void input_polled_device_work(struct work_struct *work) queue_delayed_work(polldev_wq, &dev->work, delay); } +static void input_polled_device_work(struct work_struct *work) +{ + struct input_polled_dev *dev = + container_of(work, struct input_polled_dev, work.work); + + dev->poll(dev); + input_polldev_queue_work(dev); +} + static int input_open_polled_device(struct input_dev *input) { struct input_polled_dev *dev = input_get_drvdata(input); @@ -80,11 +85,12 @@ static int input_open_polled_device(struct input_dev *input) if (error) return error; - if (dev->flush) - dev->flush(dev); + if (dev->open) + dev->open(dev); - queue_delayed_work(polldev_wq, &dev->work, - msecs_to_jiffies(dev->poll_interval)); + /* Only start polling if polling is enabled */ + if (dev->poll_interval > 0) + queue_delayed_work(polldev_wq, &dev->work, 0); return 0; } @@ -95,8 +101,88 @@ static void input_close_polled_device(struct input_dev *input) cancel_delayed_work_sync(&dev->work); input_polldev_stop_workqueue(); + + if (dev->close) + dev->close(dev); } +/* SYSFS interface */ + +static ssize_t input_polldev_get_poll(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct input_polled_dev *polldev = dev_get_drvdata(dev); + + return sprintf(buf, "%d\n", polldev->poll_interval); +} + +static ssize_t input_polldev_set_poll(struct device *dev, + struct device_attribute *attr, const char *buf, + size_t count) +{ + struct input_polled_dev *polldev = dev_get_drvdata(dev); + struct input_dev *input = polldev->input; + unsigned long interval; + + if (strict_strtoul(buf, 0, &interval)) + return -EINVAL; + + if (interval < polldev->poll_interval_min) + return -EINVAL; + + if (interval > polldev->poll_interval_max) + return -EINVAL; + + mutex_lock(&input->mutex); + + polldev->poll_interval = interval; + + if (input->users) { + cancel_delayed_work_sync(&polldev->work); + if (polldev->poll_interval > 0) + input_polldev_queue_work(polldev); + } + + mutex_unlock(&input->mutex); + + return count; +} + +static DEVICE_ATTR(poll, S_IRUGO | S_IWUSR, input_polldev_get_poll, + input_polldev_set_poll); + + +static ssize_t input_polldev_get_max(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct input_polled_dev *polldev = dev_get_drvdata(dev); + + return sprintf(buf, "%d\n", polldev->poll_interval_max); +} + +static DEVICE_ATTR(max, S_IRUGO, input_polldev_get_max, NULL); + +static ssize_t input_polldev_get_min(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct input_polled_dev *polldev = dev_get_drvdata(dev); + + return sprintf(buf, "%d\n", polldev->poll_interval_min); +} + +static DEVICE_ATTR(min, S_IRUGO, input_polldev_get_min, NULL); + +static struct attribute *sysfs_attrs[] = { + &dev_attr_poll.attr, + &dev_attr_max.attr, + &dev_attr_min.attr, + NULL +}; + +static struct attribute_group input_polldev_attribute_group = { + .attrs = sysfs_attrs +}; + /** * input_allocate_polled_device - allocated memory polled device * @@ -126,7 +212,7 @@ EXPORT_SYMBOL(input_allocate_polled_device); * @dev: device to free * * The function frees memory allocated for polling device and drops - * reference to the associated input device (if present). + * reference to the associated input device. */ void input_free_polled_device(struct input_polled_dev *dev) { @@ -150,15 +236,38 @@ EXPORT_SYMBOL(input_free_polled_device); int input_register_polled_device(struct input_polled_dev *dev) { struct input_dev *input = dev->input; + int error; input_set_drvdata(input, dev); INIT_DELAYED_WORK(&dev->work, input_polled_device_work); if (!dev->poll_interval) dev->poll_interval = 500; + if (!dev->poll_interval_max) + dev->poll_interval_max = dev->poll_interval; input->open = input_open_polled_device; input->close = input_close_polled_device; - return input_register_device(input); + error = input_register_device(input); + if (error) + return error; + + error = sysfs_create_group(&input->dev.kobj, + &input_polldev_attribute_group); + if (error) { + input_unregister_device(input); + return error; + } + + /* + * Take extra reference to the underlying input device so + * that it survives call to input_unregister_polled_device() + * and is deleted only after input_free_polled_device() + * has been invoked. This is needed to ease task of freeing + * sparse keymaps. + */ + input_get_device(input); + + return 0; } EXPORT_SYMBOL(input_register_polled_device); @@ -169,13 +278,13 @@ EXPORT_SYMBOL(input_register_polled_device); * The function unregisters previously registered polled input * device from input layer. Polling is stopped and device is * ready to be freed with call to input_free_polled_device(). - * Callers should not attempt to access dev->input pointer - * after calling this function. */ void input_unregister_polled_device(struct input_polled_dev *dev) { + sysfs_remove_group(&dev->input->dev.kobj, + &input_polldev_attribute_group); + input_unregister_device(dev->input); - dev->input = NULL; } EXPORT_SYMBOL(input_unregister_polled_device); diff --git a/drivers/input/input.c b/drivers/input/input.c index 7c237e6ac71..5c16001959c 100644 --- a/drivers/input/input.c +++ b/drivers/input/input.c @@ -11,11 +11,13 @@ */ #include <linux/init.h> +#include <linux/types.h> #include <linux/input.h> #include <linux/module.h> #include <linux/random.h> #include <linux/major.h> #include <linux/proc_fs.h> +#include <linux/sched.h> #include <linux/seq_file.h> #include <linux/poll.h> #include <linux/device.h> @@ -514,7 +516,7 @@ static void input_disconnect_device(struct input_dev *dev) * that there are no threads in the middle of input_open_device() */ mutex_lock(&dev->mutex); - dev->going_away = 1; + dev->going_away = true; mutex_unlock(&dev->mutex); spin_lock_irq(&dev->event_lock); @@ -780,10 +782,29 @@ static unsigned int input_proc_devices_poll(struct file *file, poll_table *wait) return 0; } +union input_seq_state { + struct { + unsigned short pos; + bool mutex_acquired; + }; + void *p; +}; + static void *input_devices_seq_start(struct seq_file *seq, loff_t *pos) { - if (mutex_lock_interruptible(&input_mutex)) - return NULL; + union input_seq_state *state = (union input_seq_state *)&seq->private; + int error; + + /* We need to fit into seq->private pointer */ + BUILD_BUG_ON(sizeof(union input_seq_state) != sizeof(seq->private)); + + error = mutex_lock_interruptible(&input_mutex); + if (error) { + state->mutex_acquired = false; + return ERR_PTR(error); + } + + state->mutex_acquired = true; return seq_list_start(&input_dev_list, *pos); } @@ -793,9 +814,12 @@ static void *input_devices_seq_next(struct seq_file *seq, void *v, loff_t *pos) return seq_list_next(v, &input_dev_list, pos); } -static void input_devices_seq_stop(struct seq_file *seq, void *v) +static void input_seq_stop(struct seq_file *seq, void *v) { - mutex_unlock(&input_mutex); + union input_seq_state *state = (union input_seq_state *)&seq->private; + + if (state->mutex_acquired) + mutex_unlock(&input_mutex); } static void input_seq_print_bitmap(struct seq_file *seq, const char *name, @@ -859,7 +883,7 @@ static int input_devices_seq_show(struct seq_file *seq, void *v) static const struct seq_operations input_devices_seq_ops = { .start = input_devices_seq_start, .next = input_devices_seq_next, - .stop = input_devices_seq_stop, + .stop = input_seq_stop, .show = input_devices_seq_show, }; @@ -879,40 +903,49 @@ static const struct file_operations input_devices_fileops = { static void *input_handlers_seq_start(struct seq_file *seq, loff_t *pos) { - if (mutex_lock_interruptible(&input_mutex)) - return NULL; + union input_seq_state *state = (union input_seq_state *)&seq->private; + int error; + + /* We need to fit into seq->private pointer */ + BUILD_BUG_ON(sizeof(union input_seq_state) != sizeof(seq->private)); + + error = mutex_lock_interruptible(&input_mutex); + if (error) { + state->mutex_acquired = false; + return ERR_PTR(error); + } + + state->mutex_acquired = true; + state->pos = *pos; - seq->private = (void *)(unsigned long)*pos; return seq_list_start(&input_handler_list, *pos); } static void *input_handlers_seq_next(struct seq_file *seq, void *v, loff_t *pos) { - seq->private = (void *)(unsigned long)(*pos + 1); - return seq_list_next(v, &input_handler_list, pos); -} + union input_seq_state *state = (union input_seq_state *)&seq->private; -static void input_handlers_seq_stop(struct seq_file *seq, void *v) -{ - mutex_unlock(&input_mutex); + state->pos = *pos + 1; + return seq_list_next(v, &input_handler_list, pos); } static int input_handlers_seq_show(struct seq_file *seq, void *v) { struct input_handler *handler = container_of(v, struct input_handler, node); + union input_seq_state *state = (union input_seq_state *)&seq->private; - seq_printf(seq, "N: Number=%ld Name=%s", - (unsigned long)seq->private, handler->name); + seq_printf(seq, "N: Number=%u Name=%s", state->pos, handler->name); if (handler->fops) seq_printf(seq, " Minor=%d", handler->minor); seq_putc(seq, '\n'); return 0; } + static const struct seq_operations input_handlers_seq_ops = { .start = input_handlers_seq_start, .next = input_handlers_seq_next, - .stop = input_handlers_seq_stop, + .stop = input_seq_stop, .show = input_handlers_seq_show, }; @@ -1144,7 +1177,7 @@ static struct attribute_group input_dev_caps_attr_group = { .attrs = input_dev_caps_attrs, }; -static struct attribute_group *input_dev_attr_groups[] = { +static const struct attribute_group *input_dev_attr_groups[] = { &input_dev_attr_group, &input_dev_id_attr_group, &input_dev_caps_attr_group, @@ -1259,20 +1292,88 @@ static int input_dev_uevent(struct device *device, struct kobj_uevent_env *env) return 0; } +#define INPUT_DO_TOGGLE(dev, type, bits, on) \ + do { \ + int i; \ + bool active; \ + \ + if (!test_bit(EV_##type, dev->evbit)) \ + break; \ + \ + for (i = 0; i < type##_MAX; i++) { \ + if (!test_bit(i, dev->bits##bit)) \ + continue; \ + \ + active = test_bit(i, dev->bits); \ + if (!active && !on) \ + continue; \ + \ + dev->event(dev, EV_##type, i, on ? active : 0); \ + } \ + } while (0) + +#ifdef CONFIG_PM +static void input_dev_reset(struct input_dev *dev, bool activate) +{ + if (!dev->event) + return; + + INPUT_DO_TOGGLE(dev, LED, led, activate); + INPUT_DO_TOGGLE(dev, SND, snd, activate); + + if (activate && test_bit(EV_REP, dev->evbit)) { + dev->event(dev, EV_REP, REP_PERIOD, dev->rep[REP_PERIOD]); + dev->event(dev, EV_REP, REP_DELAY, dev->rep[REP_DELAY]); + } +} + +static int input_dev_suspend(struct device *dev) +{ + struct input_dev *input_dev = to_input_dev(dev); + + mutex_lock(&input_dev->mutex); + input_dev_reset(input_dev, false); + mutex_unlock(&input_dev->mutex); + + return 0; +} + +static int input_dev_resume(struct device *dev) +{ + struct input_dev *input_dev = to_input_dev(dev); + + mutex_lock(&input_dev->mutex); + input_dev_reset(input_dev, true); + mutex_unlock(&input_dev->mutex); + + return 0; +} + +static const struct dev_pm_ops input_dev_pm_ops = { + .suspend = input_dev_suspend, + .resume = input_dev_resume, + .poweroff = input_dev_suspend, + .restore = input_dev_resume, +}; +#endif /* CONFIG_PM */ + static struct device_type input_dev_type = { .groups = input_dev_attr_groups, .release = input_dev_release, .uevent = input_dev_uevent, +#ifdef CONFIG_PM + .pm = &input_dev_pm_ops, +#endif }; -static char *input_nodename(struct device *dev) +static char *input_devnode(struct device *dev, mode_t *mode) { return kasprintf(GFP_KERNEL, "input/%s", dev_name(dev)); } struct class input_class = { .name = "input", - .nodename = input_nodename, + .devnode = input_devnode, }; EXPORT_SYMBOL_GPL(input_class); @@ -1557,6 +1658,38 @@ void input_unregister_handler(struct input_handler *handler) EXPORT_SYMBOL(input_unregister_handler); /** + * input_handler_for_each_handle - handle iterator + * @handler: input handler to iterate + * @data: data for the callback + * @fn: function to be called for each handle + * + * Iterate over @bus's list of devices, and call @fn for each, passing + * it @data and stop when @fn returns a non-zero value. The function is + * using RCU to traverse the list and therefore may be usind in atonic + * contexts. The @fn callback is invoked from RCU critical section and + * thus must not sleep. + */ +int input_handler_for_each_handle(struct input_handler *handler, void *data, + int (*fn)(struct input_handle *, void *)) +{ + struct input_handle *handle; + int retval = 0; + + rcu_read_lock(); + + list_for_each_entry_rcu(handle, &handler->h_list, h_node) { + retval = fn(handle, data); + if (retval) + break; + } + + rcu_read_unlock(); + + return retval; +} +EXPORT_SYMBOL(input_handler_for_each_handle); + +/** * input_register_handle - register a new input handle * @handle: handle to register * @@ -1589,7 +1722,7 @@ int input_register_handle(struct input_handle *handle) * we can't be racing with input_unregister_handle() * and so separate lock is not needed here. */ - list_add_tail(&handle->h_node, &handler->h_list); + list_add_tail_rcu(&handle->h_node, &handler->h_list); if (handler->start) handler->start(handle); @@ -1612,7 +1745,7 @@ void input_unregister_handle(struct input_handle *handle) { struct input_dev *dev = handle->dev; - list_del_init(&handle->h_node); + list_del_rcu(&handle->h_node); /* * Take dev->mutex to prevent race with input_release_device(). @@ -1620,6 +1753,7 @@ void input_unregister_handle(struct input_handle *handle) mutex_lock(&dev->mutex); list_del_rcu(&handle->d_node); mutex_unlock(&dev->mutex); + synchronize_rcu(); } EXPORT_SYMBOL(input_unregister_handle); diff --git a/drivers/input/joydev.c b/drivers/input/joydev.c index 9a1d55b74d7..b1bd6dd3228 100644 --- a/drivers/input/joydev.c +++ b/drivers/input/joydev.c @@ -18,6 +18,7 @@ #include <linux/input.h> #include <linux/kernel.h> #include <linux/major.h> +#include <linux/sched.h> #include <linux/slab.h> #include <linux/mm.h> #include <linux/miscdevice.h> @@ -452,6 +453,76 @@ static unsigned int joydev_poll(struct file *file, poll_table *wait) (joydev->exist ? 0 : (POLLHUP | POLLERR)); } +static int joydev_handle_JSIOCSAXMAP(struct joydev *joydev, + void __user *argp, size_t len) +{ + __u8 *abspam; + int i; + int retval = 0; + + len = min(len, sizeof(joydev->abspam)); + + /* Validate the map. */ + abspam = kmalloc(len, GFP_KERNEL); + if (!abspam) + return -ENOMEM; + + if (copy_from_user(abspam, argp, len)) { + retval = -EFAULT; + goto out; + } + + for (i = 0; i < joydev->nabs; i++) { + if (abspam[i] > ABS_MAX) { + retval = -EINVAL; + goto out; + } + } + + memcpy(joydev->abspam, abspam, len); + + out: + kfree(abspam); + return retval; +} + +static int joydev_handle_JSIOCSBTNMAP(struct joydev *joydev, + void __user *argp, size_t len) +{ + __u16 *keypam; + int i; + int retval = 0; + + len = min(len, sizeof(joydev->keypam)); + + /* Validate the map. */ + keypam = kmalloc(len, GFP_KERNEL); + if (!keypam) + return -ENOMEM; + + if (copy_from_user(keypam, argp, len)) { + retval = -EFAULT; + goto out; + } + + for (i = 0; i < joydev->nkey; i++) { + if (keypam[i] > KEY_MAX || keypam[i] < BTN_MISC) { + retval = -EINVAL; + goto out; + } + } + + memcpy(joydev->keypam, keypam, len); + + for (i = 0; i < joydev->nkey; i++) + joydev->keymap[keypam[i] - BTN_MISC] = i; + + out: + kfree(keypam); + return retval; +} + + static int joydev_ioctl_common(struct joydev *joydev, unsigned int cmd, void __user *argp) { @@ -512,46 +583,18 @@ static int joydev_ioctl_common(struct joydev *joydev, switch (cmd & ~IOCSIZE_MASK) { case (JSIOCSAXMAP & ~IOCSIZE_MASK): - len = min_t(size_t, _IOC_SIZE(cmd), sizeof(joydev->abspam)); - /* - * FIXME: we should not copy into our axis map before - * validating the data. - */ - if (copy_from_user(joydev->abspam, argp, len)) - return -EFAULT; - - for (i = 0; i < joydev->nabs; i++) { - if (joydev->abspam[i] > ABS_MAX) - return -EINVAL; - joydev->absmap[joydev->abspam[i]] = i; - } - return 0; + return joydev_handle_JSIOCSAXMAP(joydev, argp, _IOC_SIZE(cmd)); case (JSIOCGAXMAP & ~IOCSIZE_MASK): len = min_t(size_t, _IOC_SIZE(cmd), sizeof(joydev->abspam)); - return copy_to_user(argp, joydev->abspam, len) ? -EFAULT : 0; + return copy_to_user(argp, joydev->abspam, len) ? -EFAULT : len; case (JSIOCSBTNMAP & ~IOCSIZE_MASK): - len = min_t(size_t, _IOC_SIZE(cmd), sizeof(joydev->keypam)); - /* - * FIXME: we should not copy into our keymap before - * validating the data. - */ - if (copy_from_user(joydev->keypam, argp, len)) - return -EFAULT; - - for (i = 0; i < joydev->nkey; i++) { - if (joydev->keypam[i] > KEY_MAX || - joydev->keypam[i] < BTN_MISC) - return -EINVAL; - joydev->keymap[joydev->keypam[i] - BTN_MISC] = i; - } - - return 0; + return joydev_handle_JSIOCSBTNMAP(joydev, argp, _IOC_SIZE(cmd)); case (JSIOCGBTNMAP & ~IOCSIZE_MASK): len = min_t(size_t, _IOC_SIZE(cmd), sizeof(joydev->keypam)); - return copy_to_user(argp, joydev->keypam, len) ? -EFAULT : 0; + return copy_to_user(argp, joydev->keypam, len) ? -EFAULT : len; case JSIOCGNAME(0): name = dev->name; diff --git a/drivers/input/joystick/xpad.c b/drivers/input/joystick/xpad.c index f155ad8cdae..482cb1204e4 100644 --- a/drivers/input/joystick/xpad.c +++ b/drivers/input/joystick/xpad.c @@ -125,6 +125,7 @@ static const struct xpad_device { { 0x0738, 0x4540, "Mad Catz Beat Pad", MAP_DPAD_TO_BUTTONS, XTYPE_XBOX }, { 0x0738, 0x4556, "Mad Catz Lynx Wireless Controller", MAP_DPAD_TO_AXES, XTYPE_XBOX }, { 0x0738, 0x4716, "Mad Catz Wired Xbox 360 Controller", MAP_DPAD_TO_AXES, XTYPE_XBOX360 }, + { 0x0738, 0x4738, "Mad Catz Wired Xbox 360 Controller (SFIV)", MAP_DPAD_TO_AXES, XTYPE_XBOX360 }, { 0x0738, 0x6040, "Mad Catz Beat Pad Pro", MAP_DPAD_TO_BUTTONS, XTYPE_XBOX }, { 0x0c12, 0x8802, "Zeroplus Xbox Controller", MAP_DPAD_TO_AXES, XTYPE_XBOX }, { 0x0c12, 0x880a, "Pelican Eclipse PL-2023", MAP_DPAD_TO_AXES, XTYPE_XBOX }, @@ -143,7 +144,10 @@ static const struct xpad_device { { 0x12ab, 0x8809, "Xbox DDR dancepad", MAP_DPAD_TO_BUTTONS, XTYPE_XBOX }, { 0x1430, 0x4748, "RedOctane Guitar Hero X-plorer", MAP_DPAD_TO_AXES, XTYPE_XBOX360 }, { 0x1430, 0x8888, "TX6500+ Dance Pad (first generation)", MAP_DPAD_TO_BUTTONS, XTYPE_XBOX }, + { 0x146b, 0x0601, "BigBen Interactive XBOX 360 Controller", MAP_DPAD_TO_AXES, XTYPE_XBOX360 }, { 0x045e, 0x028e, "Microsoft X-Box 360 pad", MAP_DPAD_TO_AXES, XTYPE_XBOX360 }, + { 0x1bad, 0x0003, "Harmonix Rock Band Drumkit", MAP_DPAD_TO_BUTTONS, XTYPE_XBOX360 }, + { 0x0f0d, 0x0016, "Hori Real Arcade Pro.EX", MAP_DPAD_TO_AXES, XTYPE_XBOX360 }, { 0xffff, 0xffff, "Chinese-made Xbox Controller", MAP_DPAD_TO_AXES, XTYPE_XBOX }, { 0x0000, 0x0000, "Generic X-Box pad", MAP_DPAD_UNKNOWN, XTYPE_UNKNOWN } }; @@ -208,6 +212,9 @@ static struct usb_device_id xpad_table [] = { XPAD_XBOX360_VENDOR(0x0738), /* Mad Catz X-Box 360 controllers */ XPAD_XBOX360_VENDOR(0x0e6f), /* 0x0e6f X-Box 360 controllers */ XPAD_XBOX360_VENDOR(0x1430), /* RedOctane X-Box 360 controllers */ + XPAD_XBOX360_VENDOR(0x146b), /* BigBen Interactive Controllers */ + XPAD_XBOX360_VENDOR(0x1bad), /* Rock Band Drums */ + XPAD_XBOX360_VENDOR(0x0f0d), /* Hori Controllers */ { } }; diff --git a/drivers/input/keyboard/Kconfig b/drivers/input/keyboard/Kconfig index a6b989a9dc0..203b88a82b5 100644 --- a/drivers/input/keyboard/Kconfig +++ b/drivers/input/keyboard/Kconfig @@ -24,6 +24,16 @@ config KEYBOARD_AAED2000 To compile this driver as a module, choose M here: the module will be called aaed2000_kbd. +config KEYBOARD_ADP5588 + tristate "ADP5588 I2C QWERTY Keypad and IO Expander" + depends on I2C + help + Say Y here if you want to use a ADP5588 attached to your + system I2C bus. + + To compile this driver as a module, choose M here: the + module will be called adp5588-keys. + config KEYBOARD_AMIGA tristate "Amiga keyboard" depends on AMIGA @@ -104,6 +114,16 @@ config KEYBOARD_ATKBD_RDI_KEYCODES right-hand column will be interpreted as the key shown in the left-hand column. +config QT2160 + tristate "Atmel AT42QT2160 Touch Sensor Chip" + depends on I2C && EXPERIMENTAL + help + If you say yes here you get support for Atmel AT42QT2160 Touch + Sensor chip as a keyboard input. + + This driver can also be built as a module. If so, the module + will be called qt2160. + config KEYBOARD_BFIN tristate "Blackfin BF54x keypad support" depends on (BF54x && !BF544) @@ -187,7 +207,7 @@ config KEYBOARD_HIL_OLD submenu. config KEYBOARD_HIL - tristate "HP HIL keyboard support" + tristate "HP HIL keyboard/pointer support" depends on GSC || HP300 default y select HP_SDC @@ -196,7 +216,8 @@ config KEYBOARD_HIL help The "Human Interface Loop" is a older, 8-channel USB-like controller used in several Hewlett Packard models. - This driver implements support for HIL-keyboards attached + This driver implements support for HIL-keyboards and pointing + devices (mice, tablets, touchscreens) attached to your machine, so normally you should say Y here. config KEYBOARD_HP6XX @@ -250,6 +271,17 @@ config KEYBOARD_MAPLE To compile this driver as a module, choose M here: the module will be called maple_keyb. +config KEYBOARD_MAX7359 + tristate "Maxim MAX7359 Key Switch Controller" + depends on I2C + help + If you say yes here you get support for the Maxim MAX7359 Key + Switch Controller chip. This providers microprocessors with + management of up to 64 key switches + + To compile this driver as a module, choose M here: the + module will be called max7359_keypad. + config KEYBOARD_NEWTON tristate "Newton keyboard" select SERIO @@ -259,6 +291,15 @@ config KEYBOARD_NEWTON To compile this driver as a module, choose M here: the module will be called newtonkbd. +config KEYBOARD_OPENCORES + tristate "OpenCores Keyboard Controller" + help + Say Y here if you want to use the OpenCores Keyboard Controller + http://www.opencores.org/project,keyboardcontroller + + To compile this driver as a module, choose M here; the + module will be called opencores-kbd. + config KEYBOARD_PXA27x tristate "PXA27x/PXA3xx keypad support" depends on PXA27x || PXA3xx @@ -320,6 +361,16 @@ config KEYBOARD_SH_KEYSC To compile this driver as a module, choose M here: the module will be called sh_keysc. +config KEYBOARD_DAVINCI + tristate "TI DaVinci Key Scan" + depends on ARCH_DAVINCI_DM365 + help + Say Y to enable keypad module support for the TI DaVinci + platforms (DM365). + + To compile this driver as a module, choose M here: the + module will be called davinci_keyscan. + config KEYBOARD_OMAP tristate "TI OMAP keypad support" depends on (ARCH_OMAP1 || ARCH_OMAP2) @@ -329,6 +380,17 @@ config KEYBOARD_OMAP To compile this driver as a module, choose M here: the module will be called omap-keypad. +config KEYBOARD_TWL4030 + tristate "TI TWL4030/TWL5030/TPS659x0 keypad support" + depends on TWL4030_CORE + help + Say Y here if your board use the keypad controller on + TWL4030 family chips. It's safe to say enable this + even on boards that don't use the keypad controller. + + To compile this driver as a module, choose M here: the + module will be called twl4030_keypad. + config KEYBOARD_TOSA tristate "Tosa keyboard" depends on MACH_TOSA @@ -361,4 +423,14 @@ config KEYBOARD_XTKBD To compile this driver as a module, choose M here: the module will be called xtkbd. +config KEYBOARD_W90P910 + tristate "W90P910 Matrix Keypad support" + depends on ARCH_W90X900 + help + Say Y here to enable the matrix keypad on evaluation board + based on W90P910. + + To compile this driver as a module, choose M here: the + module will be called w90p910_keypad. + endif diff --git a/drivers/input/keyboard/Makefile b/drivers/input/keyboard/Makefile index b5b5eae9724..68c017235ce 100644 --- a/drivers/input/keyboard/Makefile +++ b/drivers/input/keyboard/Makefile @@ -5,11 +5,13 @@ # Each configuration option enables a list of files. obj-$(CONFIG_KEYBOARD_AAED2000) += aaed2000_kbd.o +obj-$(CONFIG_KEYBOARD_ADP5588) += adp5588-keys.o obj-$(CONFIG_KEYBOARD_AMIGA) += amikbd.o obj-$(CONFIG_KEYBOARD_ATARI) += atakbd.o obj-$(CONFIG_KEYBOARD_ATKBD) += atkbd.o obj-$(CONFIG_KEYBOARD_BFIN) += bf54x-keys.o obj-$(CONFIG_KEYBOARD_CORGI) += corgikbd.o +obj-$(CONFIG_KEYBOARD_DAVINCI) += davinci_keyscan.o obj-$(CONFIG_KEYBOARD_EP93XX) += ep93xx_keypad.o obj-$(CONFIG_KEYBOARD_GPIO) += gpio_keys.o obj-$(CONFIG_KEYBOARD_HIL) += hil_kbd.o @@ -21,13 +23,18 @@ obj-$(CONFIG_KEYBOARD_LM8323) += lm8323.o obj-$(CONFIG_KEYBOARD_LOCOMO) += locomokbd.o obj-$(CONFIG_KEYBOARD_MAPLE) += maple_keyb.o obj-$(CONFIG_KEYBOARD_MATRIX) += matrix_keypad.o +obj-$(CONFIG_KEYBOARD_MAX7359) += max7359_keypad.o obj-$(CONFIG_KEYBOARD_NEWTON) += newtonkbd.o obj-$(CONFIG_KEYBOARD_OMAP) += omap-keypad.o +obj-$(CONFIG_KEYBOARD_OPENCORES) += opencores-kbd.o obj-$(CONFIG_KEYBOARD_PXA27x) += pxa27x_keypad.o obj-$(CONFIG_KEYBOARD_PXA930_ROTARY) += pxa930_rotary.o +obj-$(CONFIG_KEYBOARD_QT2160) += qt2160.o obj-$(CONFIG_KEYBOARD_SH_KEYSC) += sh_keysc.o obj-$(CONFIG_KEYBOARD_SPITZ) += spitzkbd.o obj-$(CONFIG_KEYBOARD_STOWAWAY) += stowaway.o obj-$(CONFIG_KEYBOARD_SUNKBD) += sunkbd.o obj-$(CONFIG_KEYBOARD_TOSA) += tosakbd.o +obj-$(CONFIG_KEYBOARD_TWL4030) += twl4030_keypad.o obj-$(CONFIG_KEYBOARD_XTKBD) += xtkbd.o +obj-$(CONFIG_KEYBOARD_W90P910) += w90p910_keypad.o diff --git a/drivers/input/keyboard/adp5588-keys.c b/drivers/input/keyboard/adp5588-keys.c new file mode 100644 index 00000000000..d48c808d592 --- /dev/null +++ b/drivers/input/keyboard/adp5588-keys.c @@ -0,0 +1,361 @@ +/* + * File: drivers/input/keyboard/adp5588_keys.c + * Description: keypad driver for ADP5588 I2C QWERTY Keypad and IO Expander + * Bugs: Enter bugs at http://blackfin.uclinux.org/ + * + * Copyright (C) 2008-2009 Analog Devices Inc. + * Licensed under the GPL-2 or later. + */ + +#include <linux/module.h> +#include <linux/version.h> +#include <linux/init.h> +#include <linux/interrupt.h> +#include <linux/irq.h> +#include <linux/workqueue.h> +#include <linux/errno.h> +#include <linux/pm.h> +#include <linux/platform_device.h> +#include <linux/input.h> +#include <linux/i2c.h> + +#include <linux/i2c/adp5588.h> + + /* Configuration Register1 */ +#define AUTO_INC (1 << 7) +#define GPIEM_CFG (1 << 6) +#define OVR_FLOW_M (1 << 5) +#define INT_CFG (1 << 4) +#define OVR_FLOW_IEN (1 << 3) +#define K_LCK_IM (1 << 2) +#define GPI_IEN (1 << 1) +#define KE_IEN (1 << 0) + +/* Interrupt Status Register */ +#define CMP2_INT (1 << 5) +#define CMP1_INT (1 << 4) +#define OVR_FLOW_INT (1 << 3) +#define K_LCK_INT (1 << 2) +#define GPI_INT (1 << 1) +#define KE_INT (1 << 0) + +/* Key Lock and Event Counter Register */ +#define K_LCK_EN (1 << 6) +#define LCK21 0x30 +#define KEC 0xF + +/* Key Event Register xy */ +#define KEY_EV_PRESSED (1 << 7) +#define KEY_EV_MASK (0x7F) + +#define KP_SEL(x) (0xFFFF >> (16 - x)) /* 2^x-1 */ + +#define KEYP_MAX_EVENT 10 + +/* + * Early pre 4.0 Silicon required to delay readout by at least 25ms, + * since the Event Counter Register updated 25ms after the interrupt + * asserted. + */ +#define WA_DELAYED_READOUT_REVID(rev) ((rev) < 4) + +struct adp5588_kpad { + struct i2c_client *client; + struct input_dev *input; + struct delayed_work work; + unsigned long delay; + unsigned short keycode[ADP5588_KEYMAPSIZE]; +}; + +static int adp5588_read(struct i2c_client *client, u8 reg) +{ + int ret = i2c_smbus_read_byte_data(client, reg); + + if (ret < 0) + dev_err(&client->dev, "Read Error\n"); + + return ret; +} + +static int adp5588_write(struct i2c_client *client, u8 reg, u8 val) +{ + return i2c_smbus_write_byte_data(client, reg, val); +} + +static void adp5588_work(struct work_struct *work) +{ + struct adp5588_kpad *kpad = container_of(work, + struct adp5588_kpad, work.work); + struct i2c_client *client = kpad->client; + int i, key, status, ev_cnt; + + status = adp5588_read(client, INT_STAT); + + if (status & OVR_FLOW_INT) /* Unlikely and should never happen */ + dev_err(&client->dev, "Event Overflow Error\n"); + + if (status & KE_INT) { + ev_cnt = adp5588_read(client, KEY_LCK_EC_STAT) & KEC; + if (ev_cnt) { + for (i = 0; i < ev_cnt; i++) { + key = adp5588_read(client, Key_EVENTA + i); + input_report_key(kpad->input, + kpad->keycode[(key & KEY_EV_MASK) - 1], + key & KEY_EV_PRESSED); + } + input_sync(kpad->input); + } + } + adp5588_write(client, INT_STAT, status); /* Status is W1C */ +} + +static irqreturn_t adp5588_irq(int irq, void *handle) +{ + struct adp5588_kpad *kpad = handle; + + /* + * use keventd context to read the event fifo registers + * Schedule readout at least 25ms after notification for + * REVID < 4 + */ + + schedule_delayed_work(&kpad->work, kpad->delay); + + return IRQ_HANDLED; +} + +static int __devinit adp5588_setup(struct i2c_client *client) +{ + struct adp5588_kpad_platform_data *pdata = client->dev.platform_data; + int i, ret; + + ret = adp5588_write(client, KP_GPIO1, KP_SEL(pdata->rows)); + ret |= adp5588_write(client, KP_GPIO2, KP_SEL(pdata->cols) & 0xFF); + ret |= adp5588_write(client, KP_GPIO3, KP_SEL(pdata->cols) >> 8); + + if (pdata->en_keylock) { + ret |= adp5588_write(client, UNLOCK1, pdata->unlock_key1); + ret |= adp5588_write(client, UNLOCK2, pdata->unlock_key2); + ret |= adp5588_write(client, KEY_LCK_EC_STAT, K_LCK_EN); + } + + for (i = 0; i < KEYP_MAX_EVENT; i++) + ret |= adp5588_read(client, Key_EVENTA); + + ret |= adp5588_write(client, INT_STAT, CMP2_INT | CMP1_INT | + OVR_FLOW_INT | K_LCK_INT | + GPI_INT | KE_INT); /* Status is W1C */ + + ret |= adp5588_write(client, CFG, INT_CFG | OVR_FLOW_IEN | KE_IEN); + + if (ret < 0) { + dev_err(&client->dev, "Write Error\n"); + return ret; + } + + return 0; +} + +static int __devinit adp5588_probe(struct i2c_client *client, + const struct i2c_device_id *id) +{ + struct adp5588_kpad *kpad; + struct adp5588_kpad_platform_data *pdata = client->dev.platform_data; + struct input_dev *input; + unsigned int revid; + int ret, i; + int error; + + if (!i2c_check_functionality(client->adapter, + I2C_FUNC_SMBUS_BYTE_DATA)) { + dev_err(&client->dev, "SMBUS Byte Data not Supported\n"); + return -EIO; + } + + if (!pdata) { + dev_err(&client->dev, "no platform data?\n"); + return -EINVAL; + } + + if (!pdata->rows || !pdata->cols || !pdata->keymap) { + dev_err(&client->dev, "no rows, cols or keymap from pdata\n"); + return -EINVAL; + } + + if (pdata->keymapsize != ADP5588_KEYMAPSIZE) { + dev_err(&client->dev, "invalid keymapsize\n"); + return -EINVAL; + } + + if (!client->irq) { + dev_err(&client->dev, "no IRQ?\n"); + return -EINVAL; + } + + kpad = kzalloc(sizeof(*kpad), GFP_KERNEL); + input = input_allocate_device(); + if (!kpad || !input) { + error = -ENOMEM; + goto err_free_mem; + } + + kpad->client = client; + kpad->input = input; + INIT_DELAYED_WORK(&kpad->work, adp5588_work); + + ret = adp5588_read(client, DEV_ID); + if (ret < 0) { + error = ret; + goto err_free_mem; + } + + revid = (u8) ret & ADP5588_DEVICE_ID_MASK; + if (WA_DELAYED_READOUT_REVID(revid)) + kpad->delay = msecs_to_jiffies(30); + + input->name = client->name; + input->phys = "adp5588-keys/input0"; + input->dev.parent = &client->dev; + + input_set_drvdata(input, kpad); + + input->id.bustype = BUS_I2C; + input->id.vendor = 0x0001; + input->id.product = 0x0001; + input->id.version = revid; + + input->keycodesize = sizeof(kpad->keycode[0]); + input->keycodemax = pdata->keymapsize; + input->keycode = kpad->keycode; + + memcpy(kpad->keycode, pdata->keymap, + pdata->keymapsize * input->keycodesize); + + /* setup input device */ + __set_bit(EV_KEY, input->evbit); + + if (pdata->repeat) + __set_bit(EV_REP, input->evbit); + + for (i = 0; i < input->keycodemax; i++) + __set_bit(kpad->keycode[i] & KEY_MAX, input->keybit); + __clear_bit(KEY_RESERVED, input->keybit); + + error = input_register_device(input); + if (error) { + dev_err(&client->dev, "unable to register input device\n"); + goto err_free_mem; + } + + error = request_irq(client->irq, adp5588_irq, + IRQF_TRIGGER_FALLING | IRQF_DISABLED, + client->dev.driver->name, kpad); + if (error) { + dev_err(&client->dev, "irq %d busy?\n", client->irq); + goto err_unreg_dev; + } + + error = adp5588_setup(client); + if (error) + goto err_free_irq; + + device_init_wakeup(&client->dev, 1); + i2c_set_clientdata(client, kpad); + + dev_info(&client->dev, "Rev.%d keypad, irq %d\n", revid, client->irq); + return 0; + + err_free_irq: + free_irq(client->irq, kpad); + err_unreg_dev: + input_unregister_device(input); + input = NULL; + err_free_mem: + input_free_device(input); + kfree(kpad); + + return error; +} + +static int __devexit adp5588_remove(struct i2c_client *client) +{ + struct adp5588_kpad *kpad = i2c_get_clientdata(client); + + adp5588_write(client, CFG, 0); + free_irq(client->irq, kpad); + cancel_delayed_work_sync(&kpad->work); + input_unregister_device(kpad->input); + i2c_set_clientdata(client, NULL); + kfree(kpad); + + return 0; +} + +#ifdef CONFIG_PM +static int adp5588_suspend(struct device *dev) +{ + struct adp5588_kpad *kpad = dev_get_drvdata(dev); + struct i2c_client *client = kpad->client; + + disable_irq(client->irq); + cancel_delayed_work_sync(&kpad->work); + + if (device_may_wakeup(&client->dev)) + enable_irq_wake(client->irq); + + return 0; +} + +static int adp5588_resume(struct device *dev) +{ + struct adp5588_kpad *kpad = dev_get_drvdata(dev); + struct i2c_client *client = kpad->client; + + if (device_may_wakeup(&client->dev)) + disable_irq_wake(client->irq); + + enable_irq(client->irq); + + return 0; +} + +static struct dev_pm_ops adp5588_dev_pm_ops = { + .suspend = adp5588_suspend, + .resume = adp5588_resume, +}; +#endif + +static const struct i2c_device_id adp5588_id[] = { + { KBUILD_MODNAME, 0 }, + { } +}; +MODULE_DEVICE_TABLE(i2c, adp5588_id); + +static struct i2c_driver adp5588_driver = { + .driver = { + .name = KBUILD_MODNAME, +#ifdef CONFIG_PM + .pm = &adp5588_dev_pm_ops, +#endif + }, + .probe = adp5588_probe, + .remove = __devexit_p(adp5588_remove), + .id_table = adp5588_id, +}; + +static int __init adp5588_init(void) +{ + return i2c_add_driver(&adp5588_driver); +} +module_init(adp5588_init); + +static void __exit adp5588_exit(void) +{ + i2c_del_driver(&adp5588_driver); +} +module_exit(adp5588_exit); + +MODULE_LICENSE("GPL"); +MODULE_AUTHOR("Michael Hennerich <hennerich@blackfin.uclinux.org>"); +MODULE_DESCRIPTION("ADP5588 Keypad driver"); +MODULE_ALIAS("platform:adp5588-keys"); diff --git a/drivers/input/keyboard/atkbd.c b/drivers/input/keyboard/atkbd.c index 6c6a09b1c0f..a3573570c52 100644 --- a/drivers/input/keyboard/atkbd.c +++ b/drivers/input/keyboard/atkbd.c @@ -68,7 +68,9 @@ MODULE_PARM_DESC(extra, "Enable extra LEDs and keys on IBM RapidAcces, EzKey and * are loadable via a userland utility. */ -static const unsigned short atkbd_set2_keycode[512] = { +#define ATKBD_KEYMAP_SIZE 512 + +static const unsigned short atkbd_set2_keycode[ATKBD_KEYMAP_SIZE] = { #ifdef CONFIG_KEYBOARD_ATKBD_HP_KEYCODES @@ -99,7 +101,7 @@ static const unsigned short atkbd_set2_keycode[512] = { #endif }; -static const unsigned short atkbd_set3_keycode[512] = { +static const unsigned short atkbd_set3_keycode[ATKBD_KEYMAP_SIZE] = { 0, 0, 0, 0, 0, 0, 0, 59, 1,138,128,129,130, 15, 41, 60, 131, 29, 42, 86, 58, 16, 2, 61,133, 56, 44, 31, 30, 17, 3, 62, @@ -200,8 +202,8 @@ struct atkbd { char phys[32]; unsigned short id; - unsigned short keycode[512]; - DECLARE_BITMAP(force_release_mask, 512); + unsigned short keycode[ATKBD_KEYMAP_SIZE]; + DECLARE_BITMAP(force_release_mask, ATKBD_KEYMAP_SIZE); unsigned char set; unsigned char translated; unsigned char extra; @@ -227,10 +229,11 @@ struct atkbd { }; /* - * System-specific ketymap fixup routine + * System-specific keymap fixup routine */ static void (*atkbd_platform_fixup)(struct atkbd *, const void *data); static void *atkbd_platform_fixup_data; +static unsigned int (*atkbd_platform_scancode_fixup)(struct atkbd *, unsigned int); static ssize_t atkbd_attr_show_helper(struct device *dev, char *buf, ssize_t (*handler)(struct atkbd *, char *)); @@ -253,6 +256,7 @@ static struct device_attribute atkbd_attr_##_name = \ __ATTR(_name, S_IWUSR | S_IRUGO, atkbd_do_show_##_name, atkbd_do_set_##_name); ATKBD_DEFINE_ATTR(extra); +ATKBD_DEFINE_ATTR(force_release); ATKBD_DEFINE_ATTR(scroll); ATKBD_DEFINE_ATTR(set); ATKBD_DEFINE_ATTR(softrepeat); @@ -272,6 +276,7 @@ ATKBD_DEFINE_RO_ATTR(err_count); static struct attribute *atkbd_attributes[] = { &atkbd_attr_extra.attr, + &atkbd_attr_force_release.attr, &atkbd_attr_scroll.attr, &atkbd_attr_set.attr, &atkbd_attr_softrepeat.attr, @@ -389,6 +394,9 @@ static irqreturn_t atkbd_interrupt(struct serio *serio, unsigned char data, input_event(dev, EV_MSC, MSC_RAW, code); + if (atkbd_platform_scancode_fixup) + code = atkbd_platform_scancode_fixup(atkbd, code); + if (atkbd->translated) { if (atkbd->emul || atkbd_need_xlate(atkbd->xl_bit, code)) { @@ -570,11 +578,22 @@ static void atkbd_event_work(struct work_struct *work) mutex_lock(&atkbd->event_mutex); - if (test_and_clear_bit(ATKBD_LED_EVENT_BIT, &atkbd->event_mask)) - atkbd_set_leds(atkbd); + if (!atkbd->enabled) { + /* + * Serio ports are resumed asynchronously so while driver core + * thinks that device is already fully operational in reality + * it may not be ready yet. In this case we need to keep + * rescheduling till reconnect completes. + */ + schedule_delayed_work(&atkbd->event_work, + msecs_to_jiffies(100)); + } else { + if (test_and_clear_bit(ATKBD_LED_EVENT_BIT, &atkbd->event_mask)) + atkbd_set_leds(atkbd); - if (test_and_clear_bit(ATKBD_REP_EVENT_BIT, &atkbd->event_mask)) - atkbd_set_repeat_rate(atkbd); + if (test_and_clear_bit(ATKBD_REP_EVENT_BIT, &atkbd->event_mask)) + atkbd_set_repeat_rate(atkbd); + } mutex_unlock(&atkbd->event_mutex); } @@ -766,13 +785,13 @@ static int atkbd_select_set(struct atkbd *atkbd, int target_set, int allow_extra return 3; } -static int atkbd_activate(struct atkbd *atkbd) +static int atkbd_reset_state(struct atkbd *atkbd) { - struct ps2dev *ps2dev = &atkbd->ps2dev; + struct ps2dev *ps2dev = &atkbd->ps2dev; unsigned char param[1]; /* - * Set the LEDs to a defined state. + * Set the LEDs to a predefined state (all off). */ param[0] = 0; @@ -787,6 +806,13 @@ static int atkbd_activate(struct atkbd *atkbd) if (ps2_command(ps2dev, param, ATKBD_CMD_SETREP)) return -1; + return 0; +} + +static int atkbd_activate(struct atkbd *atkbd) +{ + struct ps2dev *ps2dev = &atkbd->ps2dev; + /* * Enable the keyboard to receive keystrokes. */ @@ -865,29 +891,6 @@ static unsigned int atkbd_hp_forced_release_keys[] = { }; /* - * Inventec system with broken key release on volume keys - */ -static unsigned int atkbd_inventec_forced_release_keys[] = { - 0xae, 0xb0, -1U -}; - -/* - * Perform fixup for HP Pavilion ZV6100 laptop that doesn't generate release - * for its volume buttons - */ -static unsigned int atkbd_hp_zv6100_forced_release_keys[] = { - 0xae, 0xb0, -1U -}; - -/* - * Perform fixup for HP (Compaq) Presario R4000 R4100 R4200 that don't generate - * release for their volume buttons - */ -static unsigned int atkbd_hp_r4000_forced_release_keys[] = { - 0xae, 0xb0, -1U -}; - -/* * Samsung NC10,NC20 with Fn+F? key release not working */ static unsigned int atkbd_samsung_forced_release_keys[] = { @@ -895,14 +898,6 @@ static unsigned int atkbd_samsung_forced_release_keys[] = { }; /* - * The volume up and volume down special keys on a Fujitsu Amilo PA 1510 laptop - * do not generate release events so we have to do it ourselves. - */ -static unsigned int atkbd_amilo_pa1510_forced_release_keys[] = { - 0xb0, 0xae, -1U -}; - -/* * Amilo Pi 3525 key release for Fn+Volume keys not working */ static unsigned int atkbd_amilo_pi3525_forced_release_keys[] = { @@ -924,6 +919,30 @@ static unsigned int atkdb_soltech_ta12_forced_release_keys[] = { }; /* + * Many notebooks don't send key release event for volume up/down + * keys, with key list below common among them + */ +static unsigned int atkbd_volume_forced_release_keys[] = { + 0xae, 0xb0, -1U +}; + +/* + * OQO 01+ multimedia keys (64--66) generate e0 6x upon release whereas + * they should be generating e4-e6 (0x80 | code). + */ +static unsigned int atkbd_oqo_01plus_scancode_fixup(struct atkbd *atkbd, + unsigned int code) +{ + if (atkbd->translated && atkbd->emul == 1 && + (code == 0x64 || code == 0x65 || code == 0x66)) { + atkbd->emul = 0; + code |= 0x80; + } + + return code; +} + +/* * atkbd_set_keycode_table() initializes keyboard's keycode table * according to the selected scancode set */ @@ -934,7 +953,7 @@ static void atkbd_set_keycode_table(struct atkbd *atkbd) int i, j; memset(atkbd->keycode, 0, sizeof(atkbd->keycode)); - bitmap_zero(atkbd->force_release_mask, 512); + bitmap_zero(atkbd->force_release_mask, ATKBD_KEYMAP_SIZE); if (atkbd->translated) { for (i = 0; i < 128; i++) { @@ -1041,7 +1060,7 @@ static void atkbd_set_device_attrs(struct atkbd *atkbd) input_dev->keycodesize = sizeof(unsigned short); input_dev->keycodemax = ARRAY_SIZE(atkbd_set2_keycode); - for (i = 0; i < 512; i++) + for (i = 0; i < ATKBD_KEYMAP_SIZE; i++) if (atkbd->keycode[i] && atkbd->keycode[i] < ATKBD_SPECIAL) __set_bit(atkbd->keycode[i], input_dev->keybit); } @@ -1100,6 +1119,7 @@ static int atkbd_connect(struct serio *serio, struct serio_driver *drv) } atkbd->set = atkbd_select_set(atkbd, atkbd_set, atkbd_extra); + atkbd_reset_state(atkbd); atkbd_activate(atkbd); } else { @@ -1155,13 +1175,17 @@ static int atkbd_reconnect(struct serio *serio) atkbd_activate(atkbd); -/* - * Restore repeat rate and LEDs (that were reset by atkbd_activate) - * to pre-resume state - */ + /* + * Restore LED state and repeat rate. While input core + * will do this for us at resume time reconnect may happen + * because user requested it via sysfs or simply because + * keyboard was unplugged and plugged in again so we need + * to do it ourselves here. + */ + atkbd_set_leds(atkbd); if (!atkbd->softrepeat) atkbd_set_repeat_rate(atkbd); - atkbd_set_leds(atkbd); + } atkbd_enable(atkbd); @@ -1288,6 +1312,7 @@ static ssize_t atkbd_set_extra(struct atkbd *atkbd, const char *buf, size_t coun atkbd->dev = new_dev; atkbd->set = atkbd_select_set(atkbd, atkbd->set, value); + atkbd_reset_state(atkbd); atkbd_activate(atkbd); atkbd_set_keycode_table(atkbd); atkbd_set_device_attrs(atkbd); @@ -1309,6 +1334,33 @@ static ssize_t atkbd_set_extra(struct atkbd *atkbd, const char *buf, size_t coun return count; } +static ssize_t atkbd_show_force_release(struct atkbd *atkbd, char *buf) +{ + size_t len = bitmap_scnlistprintf(buf, PAGE_SIZE - 2, + atkbd->force_release_mask, ATKBD_KEYMAP_SIZE); + + buf[len++] = '\n'; + buf[len] = '\0'; + + return len; +} + +static ssize_t atkbd_set_force_release(struct atkbd *atkbd, + const char *buf, size_t count) +{ + /* 64 bytes on stack should be acceptable */ + DECLARE_BITMAP(new_mask, ATKBD_KEYMAP_SIZE); + int err; + + err = bitmap_parselist(buf, new_mask, ATKBD_KEYMAP_SIZE); + if (err) + return err; + + memcpy(atkbd->force_release_mask, new_mask, sizeof(atkbd->force_release_mask)); + return count; +} + + static ssize_t atkbd_show_scroll(struct atkbd *atkbd, char *buf) { return sprintf(buf, "%d\n", atkbd->scroll ? 1 : 0); @@ -1382,6 +1434,7 @@ static ssize_t atkbd_set_set(struct atkbd *atkbd, const char *buf, size_t count) atkbd->dev = new_dev; atkbd->set = atkbd_select_set(atkbd, value, atkbd->extra); + atkbd_reset_state(atkbd); atkbd_activate(atkbd); atkbd_set_keycode_table(atkbd); atkbd_set_device_attrs(atkbd); @@ -1507,9 +1560,15 @@ static int __init atkbd_setup_forced_release(const struct dmi_system_id *id) return 0; } -static struct dmi_system_id atkbd_dmi_quirk_table[] __initdata = { +static int __init atkbd_setup_scancode_fixup(const struct dmi_system_id *id) +{ + atkbd_platform_scancode_fixup = id->driver_data; + + return 0; +} + +static const struct dmi_system_id atkbd_dmi_quirk_table[] __initconst = { { - .ident = "Dell Laptop", .matches = { DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc."), DMI_MATCH(DMI_CHASSIS_TYPE, "8"), /* Portable */ @@ -1518,7 +1577,6 @@ static struct dmi_system_id atkbd_dmi_quirk_table[] __initdata = { .driver_data = atkbd_dell_laptop_forced_release_keys, }, { - .ident = "Dell Laptop", .matches = { DMI_MATCH(DMI_SYS_VENDOR, "Dell Computer Corporation"), DMI_MATCH(DMI_CHASSIS_TYPE, "8"), /* Portable */ @@ -1527,7 +1585,6 @@ static struct dmi_system_id atkbd_dmi_quirk_table[] __initdata = { .driver_data = atkbd_dell_laptop_forced_release_keys, }, { - .ident = "HP 2133", .matches = { DMI_MATCH(DMI_SYS_VENDOR, "Hewlett-Packard"), DMI_MATCH(DMI_PRODUCT_NAME, "HP 2133"), @@ -1536,52 +1593,48 @@ static struct dmi_system_id atkbd_dmi_quirk_table[] __initdata = { .driver_data = atkbd_hp_forced_release_keys, }, { - .ident = "HP Pavilion ZV6100", .matches = { DMI_MATCH(DMI_SYS_VENDOR, "Hewlett-Packard"), DMI_MATCH(DMI_PRODUCT_NAME, "Pavilion ZV6100"), }, .callback = atkbd_setup_forced_release, - .driver_data = atkbd_hp_zv6100_forced_release_keys, + .driver_data = atkbd_volume_forced_release_keys, }, { - .ident = "HP Presario R4000", .matches = { DMI_MATCH(DMI_SYS_VENDOR, "Hewlett-Packard"), DMI_MATCH(DMI_PRODUCT_NAME, "Presario R4000"), }, .callback = atkbd_setup_forced_release, - .driver_data = atkbd_hp_r4000_forced_release_keys, + .driver_data = atkbd_volume_forced_release_keys, }, { - .ident = "HP Presario R4100", .matches = { DMI_MATCH(DMI_SYS_VENDOR, "Hewlett-Packard"), DMI_MATCH(DMI_PRODUCT_NAME, "Presario R4100"), }, .callback = atkbd_setup_forced_release, - .driver_data = atkbd_hp_r4000_forced_release_keys, + .driver_data = atkbd_volume_forced_release_keys, }, { - .ident = "HP Presario R4200", .matches = { DMI_MATCH(DMI_SYS_VENDOR, "Hewlett-Packard"), DMI_MATCH(DMI_PRODUCT_NAME, "Presario R4200"), }, .callback = atkbd_setup_forced_release, - .driver_data = atkbd_hp_r4000_forced_release_keys, + .driver_data = atkbd_volume_forced_release_keys, }, { - .ident = "Inventec Symphony", + /* Inventec Symphony */ .matches = { DMI_MATCH(DMI_SYS_VENDOR, "INVENTEC"), DMI_MATCH(DMI_PRODUCT_NAME, "SYMPHONY 6.0/7.0"), }, .callback = atkbd_setup_forced_release, - .driver_data = atkbd_inventec_forced_release_keys, + .driver_data = atkbd_volume_forced_release_keys, }, { - .ident = "Samsung NC10", + /* Samsung NC10 */ .matches = { DMI_MATCH(DMI_SYS_VENDOR, "SAMSUNG ELECTRONICS CO., LTD."), DMI_MATCH(DMI_PRODUCT_NAME, "NC10"), @@ -1590,7 +1643,7 @@ static struct dmi_system_id atkbd_dmi_quirk_table[] __initdata = { .driver_data = atkbd_samsung_forced_release_keys, }, { - .ident = "Samsung NC20", + /* Samsung NC20 */ .matches = { DMI_MATCH(DMI_SYS_VENDOR, "SAMSUNG ELECTRONICS CO., LTD."), DMI_MATCH(DMI_PRODUCT_NAME, "NC20"), @@ -1599,7 +1652,7 @@ static struct dmi_system_id atkbd_dmi_quirk_table[] __initdata = { .driver_data = atkbd_samsung_forced_release_keys, }, { - .ident = "Samsung SQ45S70S", + /* Samsung SQ45S70S */ .matches = { DMI_MATCH(DMI_SYS_VENDOR, "SAMSUNG ELECTRONICS CO., LTD."), DMI_MATCH(DMI_PRODUCT_NAME, "SQ45S70S"), @@ -1608,16 +1661,16 @@ static struct dmi_system_id atkbd_dmi_quirk_table[] __initdata = { .driver_data = atkbd_samsung_forced_release_keys, }, { - .ident = "Fujitsu Amilo PA 1510", + /* Fujitsu Amilo PA 1510 */ .matches = { DMI_MATCH(DMI_SYS_VENDOR, "FUJITSU SIEMENS"), DMI_MATCH(DMI_PRODUCT_NAME, "AMILO Pa 1510"), }, .callback = atkbd_setup_forced_release, - .driver_data = atkbd_amilo_pa1510_forced_release_keys, + .driver_data = atkbd_volume_forced_release_keys, }, { - .ident = "Fujitsu Amilo Pi 3525", + /* Fujitsu Amilo Pi 3525 */ .matches = { DMI_MATCH(DMI_SYS_VENDOR, "FUJITSU SIEMENS"), DMI_MATCH(DMI_PRODUCT_NAME, "AMILO Pi 3525"), @@ -1626,7 +1679,7 @@ static struct dmi_system_id atkbd_dmi_quirk_table[] __initdata = { .driver_data = atkbd_amilo_pi3525_forced_release_keys, }, { - .ident = "Fujitsu Amilo Xi 3650", + /* Fujitsu Amilo Xi 3650 */ .matches = { DMI_MATCH(DMI_SYS_VENDOR, "FUJITSU SIEMENS"), DMI_MATCH(DMI_PRODUCT_NAME, "AMILO Xi 3650"), @@ -1635,7 +1688,6 @@ static struct dmi_system_id atkbd_dmi_quirk_table[] __initdata = { .driver_data = atkbd_amilo_xi3650_forced_release_keys, }, { - .ident = "Soltech Corporation TA12", .matches = { DMI_MATCH(DMI_SYS_VENDOR, "Soltech Corporation"), DMI_MATCH(DMI_PRODUCT_NAME, "TA12"), @@ -1643,6 +1695,15 @@ static struct dmi_system_id atkbd_dmi_quirk_table[] __initdata = { .callback = atkbd_setup_forced_release, .driver_data = atkdb_soltech_ta12_forced_release_keys, }, + { + /* OQO Model 01+ */ + .matches = { + DMI_MATCH(DMI_SYS_VENDOR, "OQO"), + DMI_MATCH(DMI_PRODUCT_NAME, "ZEPTO"), + }, + .callback = atkbd_setup_scancode_fixup, + .driver_data = atkbd_oqo_01plus_scancode_fixup, + }, { } }; diff --git a/drivers/input/keyboard/bf54x-keys.c b/drivers/input/keyboard/bf54x-keys.c index d427f322e20..fe376a27fe5 100644 --- a/drivers/input/keyboard/bf54x-keys.c +++ b/drivers/input/keyboard/bf54x-keys.c @@ -184,14 +184,13 @@ static int __devinit bfin_kpad_probe(struct platform_device *pdev) int i, error; if (!pdata->rows || !pdata->cols || !pdata->keymap) { - printk(KERN_ERR DRV_NAME - ": No rows, cols or keymap from pdata\n"); + dev_err(&pdev->dev, "no rows, cols or keymap from pdata\n"); return -EINVAL; } if (!pdata->keymapsize || pdata->keymapsize > (pdata->rows * pdata->cols)) { - printk(KERN_ERR DRV_NAME ": Invalid keymapsize\n"); + dev_err(&pdev->dev, "invalid keymapsize\n"); return -EINVAL; } @@ -211,8 +210,8 @@ static int __devinit bfin_kpad_probe(struct platform_device *pdev) if (!pdata->debounce_time || pdata->debounce_time > MAX_MULT || !pdata->coldrive_time || pdata->coldrive_time > MAX_MULT) { - printk(KERN_WARNING DRV_NAME - ": Invalid Debounce/Columndrive Time in platform data\n"); + dev_warn(&pdev->dev, + "invalid platform debounce/columndrive time\n"); bfin_write_KPAD_MSEL(0xFF0); /* Default MSEL */ } else { bfin_write_KPAD_MSEL( @@ -231,16 +230,14 @@ static int __devinit bfin_kpad_probe(struct platform_device *pdev) if (peripheral_request_list((u16 *)&per_rows[MAX_RC - pdata->rows], DRV_NAME)) { - printk(KERN_ERR DRV_NAME - ": Requesting Peripherals failed\n"); + dev_err(&pdev->dev, "requesting peripherals failed\n"); error = -EFAULT; goto out0; } if (peripheral_request_list((u16 *)&per_cols[MAX_RC - pdata->cols], DRV_NAME)) { - printk(KERN_ERR DRV_NAME - ": Requesting Peripherals failed\n"); + dev_err(&pdev->dev, "requesting peripherals failed\n"); error = -EFAULT; goto out1; } @@ -254,9 +251,8 @@ static int __devinit bfin_kpad_probe(struct platform_device *pdev) error = request_irq(bf54x_kpad->irq, bfin_kpad_isr, 0, DRV_NAME, pdev); if (error) { - printk(KERN_ERR DRV_NAME - ": unable to claim irq %d; error %d\n", - bf54x_kpad->irq, error); + dev_err(&pdev->dev, "unable to claim irq %d\n", + bf54x_kpad->irq); goto out2; } @@ -297,8 +293,7 @@ static int __devinit bfin_kpad_probe(struct platform_device *pdev) error = input_register_device(input); if (error) { - printk(KERN_ERR DRV_NAME - ": Unable to register input device (%d)\n", error); + dev_err(&pdev->dev, "unable to register input device\n"); goto out4; } @@ -316,9 +311,6 @@ static int __devinit bfin_kpad_probe(struct platform_device *pdev) device_init_wakeup(&pdev->dev, 1); - printk(KERN_ERR DRV_NAME - ": Blackfin BF54x Keypad registered IRQ %d\n", bf54x_kpad->irq); - return 0; out4: diff --git a/drivers/input/keyboard/davinci_keyscan.c b/drivers/input/keyboard/davinci_keyscan.c new file mode 100644 index 00000000000..6e52d855f63 --- /dev/null +++ b/drivers/input/keyboard/davinci_keyscan.c @@ -0,0 +1,337 @@ +/* + * DaVinci Key Scan Driver for TI platforms + * + * Copyright (C) 2009 Texas Instruments, Inc + * + * Author: Miguel Aguilar <miguel.aguilar@ridgerun.com> + * + * Intial Code: Sandeep Paulraj <s-paulraj@ti.com> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * 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/interrupt.h> +#include <linux/types.h> +#include <linux/input.h> +#include <linux/kernel.h> +#include <linux/delay.h> +#include <linux/platform_device.h> +#include <linux/errno.h> + +#include <asm/irq.h> + +#include <mach/hardware.h> +#include <mach/irqs.h> +#include <mach/keyscan.h> + +/* Key scan registers */ +#define DAVINCI_KEYSCAN_KEYCTRL 0x0000 +#define DAVINCI_KEYSCAN_INTENA 0x0004 +#define DAVINCI_KEYSCAN_INTFLAG 0x0008 +#define DAVINCI_KEYSCAN_INTCLR 0x000c +#define DAVINCI_KEYSCAN_STRBWIDTH 0x0010 +#define DAVINCI_KEYSCAN_INTERVAL 0x0014 +#define DAVINCI_KEYSCAN_CONTTIME 0x0018 +#define DAVINCI_KEYSCAN_CURRENTST 0x001c +#define DAVINCI_KEYSCAN_PREVSTATE 0x0020 +#define DAVINCI_KEYSCAN_EMUCTRL 0x0024 +#define DAVINCI_KEYSCAN_IODFTCTRL 0x002c + +/* Key Control Register (KEYCTRL) */ +#define DAVINCI_KEYSCAN_KEYEN 0x00000001 +#define DAVINCI_KEYSCAN_PREVMODE 0x00000002 +#define DAVINCI_KEYSCAN_CHATOFF 0x00000004 +#define DAVINCI_KEYSCAN_AUTODET 0x00000008 +#define DAVINCI_KEYSCAN_SCANMODE 0x00000010 +#define DAVINCI_KEYSCAN_OUTTYPE 0x00000020 + +/* Masks for the interrupts */ +#define DAVINCI_KEYSCAN_INT_CONT 0x00000008 +#define DAVINCI_KEYSCAN_INT_OFF 0x00000004 +#define DAVINCI_KEYSCAN_INT_ON 0x00000002 +#define DAVINCI_KEYSCAN_INT_CHANGE 0x00000001 +#define DAVINCI_KEYSCAN_INT_ALL 0x0000000f + +struct davinci_ks { + struct input_dev *input; + struct davinci_ks_platform_data *pdata; + int irq; + void __iomem *base; + resource_size_t pbase; + size_t base_size; + unsigned short keymap[]; +}; + +/* Initializing the kp Module */ +static int __init davinci_ks_initialize(struct davinci_ks *davinci_ks) +{ + struct device *dev = &davinci_ks->input->dev; + struct davinci_ks_platform_data *pdata = davinci_ks->pdata; + u32 matrix_ctrl; + + /* Enable all interrupts */ + __raw_writel(DAVINCI_KEYSCAN_INT_ALL, + davinci_ks->base + DAVINCI_KEYSCAN_INTENA); + + /* Clear interrupts if any */ + __raw_writel(DAVINCI_KEYSCAN_INT_ALL, + davinci_ks->base + DAVINCI_KEYSCAN_INTCLR); + + /* Setup the scan period = strobe + interval */ + __raw_writel(pdata->strobe, + davinci_ks->base + DAVINCI_KEYSCAN_STRBWIDTH); + __raw_writel(pdata->interval, + davinci_ks->base + DAVINCI_KEYSCAN_INTERVAL); + __raw_writel(0x01, + davinci_ks->base + DAVINCI_KEYSCAN_CONTTIME); + + /* Define matrix type */ + switch (pdata->matrix_type) { + case DAVINCI_KEYSCAN_MATRIX_4X4: + matrix_ctrl = 0; + break; + case DAVINCI_KEYSCAN_MATRIX_5X3: + matrix_ctrl = (1 << 6); + break; + default: + dev_err(dev->parent, "wrong matrix type\n"); + return -EINVAL; + } + + /* Enable key scan module and set matrix type */ + __raw_writel(DAVINCI_KEYSCAN_AUTODET | DAVINCI_KEYSCAN_KEYEN | + matrix_ctrl, davinci_ks->base + DAVINCI_KEYSCAN_KEYCTRL); + + return 0; +} + +static irqreturn_t davinci_ks_interrupt(int irq, void *dev_id) +{ + struct davinci_ks *davinci_ks = dev_id; + struct device *dev = &davinci_ks->input->dev; + unsigned short *keymap = davinci_ks->keymap; + int keymapsize = davinci_ks->pdata->keymapsize; + u32 prev_status, new_status, changed; + bool release; + int keycode = KEY_UNKNOWN; + int i; + + /* Disable interrupt */ + __raw_writel(0x0, davinci_ks->base + DAVINCI_KEYSCAN_INTENA); + + /* Reading previous and new status of the key scan */ + prev_status = __raw_readl(davinci_ks->base + DAVINCI_KEYSCAN_PREVSTATE); + new_status = __raw_readl(davinci_ks->base + DAVINCI_KEYSCAN_CURRENTST); + + changed = prev_status ^ new_status; + + if (changed) { + /* + * It goes through all bits in 'changed' to ensure + * that no key changes are being missed + */ + for (i = 0 ; i < keymapsize; i++) { + if ((changed>>i) & 0x1) { + keycode = keymap[i]; + release = (new_status >> i) & 0x1; + dev_dbg(dev->parent, "key %d %s\n", keycode, + release ? "released" : "pressed"); + input_report_key(davinci_ks->input, keycode, + !release); + input_sync(davinci_ks->input); + } + } + /* Clearing interrupt */ + __raw_writel(DAVINCI_KEYSCAN_INT_ALL, + davinci_ks->base + DAVINCI_KEYSCAN_INTCLR); + } + + /* Enable interrupts */ + __raw_writel(0x1, davinci_ks->base + DAVINCI_KEYSCAN_INTENA); + + return IRQ_HANDLED; +} + +static int __init davinci_ks_probe(struct platform_device *pdev) +{ + struct davinci_ks *davinci_ks; + struct input_dev *key_dev; + struct resource *res, *mem; + struct device *dev = &pdev->dev; + struct davinci_ks_platform_data *pdata = pdev->dev.platform_data; + int error, i; + + if (!pdata->keymap) { + dev_dbg(dev, "no keymap from pdata\n"); + return -EINVAL; + } + + davinci_ks = kzalloc(sizeof(struct davinci_ks) + + sizeof(unsigned short) * pdata->keymapsize, GFP_KERNEL); + if (!davinci_ks) { + dev_dbg(dev, "could not allocate memory for private data\n"); + return -ENOMEM; + } + + memcpy(davinci_ks->keymap, pdata->keymap, + sizeof(unsigned short) * pdata->keymapsize); + + key_dev = input_allocate_device(); + if (!key_dev) { + dev_dbg(dev, "could not allocate input device\n"); + error = -ENOMEM; + goto fail1; + } + + davinci_ks->input = key_dev; + + davinci_ks->irq = platform_get_irq(pdev, 0); + if (davinci_ks->irq < 0) { + dev_err(dev, "no key scan irq\n"); + error = davinci_ks->irq; + goto fail2; + } + + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + if (!res) { + dev_err(dev, "no mem resource\n"); + error = -EINVAL; + goto fail2; + } + + davinci_ks->pbase = res->start; + davinci_ks->base_size = resource_size(res); + + mem = request_mem_region(davinci_ks->pbase, davinci_ks->base_size, + pdev->name); + if (!mem) { + dev_err(dev, "key scan registers at %08x are not free\n", + davinci_ks->pbase); + error = -EBUSY; + goto fail2; + } + + davinci_ks->base = ioremap(davinci_ks->pbase, davinci_ks->base_size); + if (!davinci_ks->base) { + dev_err(dev, "can't ioremap MEM resource.\n"); + error = -ENOMEM; + goto fail3; + } + + /* Enable auto repeat feature of Linux input subsystem */ + if (pdata->rep) + __set_bit(EV_REP, key_dev->evbit); + + /* Setup input device */ + __set_bit(EV_KEY, key_dev->evbit); + + /* Setup the platform data */ + davinci_ks->pdata = pdata; + + for (i = 0; i < davinci_ks->pdata->keymapsize; i++) + __set_bit(davinci_ks->pdata->keymap[i], key_dev->keybit); + + key_dev->name = "davinci_keyscan"; + key_dev->phys = "davinci_keyscan/input0"; + key_dev->dev.parent = &pdev->dev; + key_dev->id.bustype = BUS_HOST; + key_dev->id.vendor = 0x0001; + key_dev->id.product = 0x0001; + key_dev->id.version = 0x0001; + key_dev->keycode = davinci_ks->keymap; + key_dev->keycodesize = sizeof(davinci_ks->keymap[0]); + key_dev->keycodemax = davinci_ks->pdata->keymapsize; + + error = input_register_device(davinci_ks->input); + if (error < 0) { + dev_err(dev, "unable to register davinci key scan device\n"); + goto fail4; + } + + error = request_irq(davinci_ks->irq, davinci_ks_interrupt, + IRQF_DISABLED, pdev->name, davinci_ks); + if (error < 0) { + dev_err(dev, "unable to register davinci key scan interrupt\n"); + goto fail5; + } + + error = davinci_ks_initialize(davinci_ks); + if (error < 0) { + dev_err(dev, "unable to initialize davinci key scan device\n"); + goto fail6; + } + + platform_set_drvdata(pdev, davinci_ks); + return 0; + +fail6: + free_irq(davinci_ks->irq, davinci_ks); +fail5: + input_unregister_device(davinci_ks->input); + key_dev = NULL; +fail4: + iounmap(davinci_ks->base); +fail3: + release_mem_region(davinci_ks->pbase, davinci_ks->base_size); +fail2: + input_free_device(key_dev); +fail1: + kfree(davinci_ks); + + return error; +} + +static int __devexit davinci_ks_remove(struct platform_device *pdev) +{ + struct davinci_ks *davinci_ks = platform_get_drvdata(pdev); + + free_irq(davinci_ks->irq, davinci_ks); + + input_unregister_device(davinci_ks->input); + + iounmap(davinci_ks->base); + release_mem_region(davinci_ks->pbase, davinci_ks->base_size); + + platform_set_drvdata(pdev, NULL); + + kfree(davinci_ks); + + return 0; +} + +static struct platform_driver davinci_ks_driver = { + .driver = { + .name = "davinci_keyscan", + .owner = THIS_MODULE, + }, + .remove = __devexit_p(davinci_ks_remove), +}; + +static int __init davinci_ks_init(void) +{ + return platform_driver_probe(&davinci_ks_driver, davinci_ks_probe); +} +module_init(davinci_ks_init); + +static void __exit davinci_ks_exit(void) +{ + platform_driver_unregister(&davinci_ks_driver); +} +module_exit(davinci_ks_exit); + +MODULE_AUTHOR("Miguel Aguilar"); +MODULE_DESCRIPTION("Texas Instruments DaVinci Key Scan Driver"); +MODULE_LICENSE("GPL"); diff --git a/drivers/input/keyboard/gpio_keys.c b/drivers/input/keyboard/gpio_keys.c index efed0c9e242..1aff3b76eff 100644 --- a/drivers/input/keyboard/gpio_keys.c +++ b/drivers/input/keyboard/gpio_keys.c @@ -23,8 +23,7 @@ #include <linux/input.h> #include <linux/gpio_keys.h> #include <linux/workqueue.h> - -#include <asm/gpio.h> +#include <linux/gpio.h> struct gpio_button_data { struct gpio_keys_button *button; @@ -38,10 +37,8 @@ struct gpio_keys_drvdata { struct gpio_button_data data[0]; }; -static void gpio_keys_report_event(struct work_struct *work) +static void gpio_keys_report_event(struct gpio_button_data *bdata) { - struct gpio_button_data *bdata = - container_of(work, struct gpio_button_data, work); struct gpio_keys_button *button = bdata->button; struct input_dev *input = bdata->input; unsigned int type = button->type ?: EV_KEY; @@ -51,6 +48,14 @@ static void gpio_keys_report_event(struct work_struct *work) input_sync(input); } +static void gpio_keys_work_func(struct work_struct *work) +{ + struct gpio_button_data *bdata = + container_of(work, struct gpio_button_data, work); + + gpio_keys_report_event(bdata); +} + static void gpio_keys_timer(unsigned long _data) { struct gpio_button_data *data = (struct gpio_button_data *)_data; @@ -74,10 +79,62 @@ static irqreturn_t gpio_keys_isr(int irq, void *dev_id) return IRQ_HANDLED; } +static int __devinit gpio_keys_setup_key(struct device *dev, + struct gpio_button_data *bdata, + struct gpio_keys_button *button) +{ + char *desc = button->desc ? button->desc : "gpio_keys"; + int irq, error; + + setup_timer(&bdata->timer, gpio_keys_timer, (unsigned long)bdata); + INIT_WORK(&bdata->work, gpio_keys_work_func); + + 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; + } + + 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; + } + + 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; + } + + error = request_irq(irq, gpio_keys_isr, + IRQF_SHARED | + IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING, + desc, bdata); + if (error) { + dev_err(dev, "Unable to claim irq %d; error %d\n", + irq, error); + goto fail3; + } + + return 0; + +fail3: + gpio_free(button->gpio); +fail2: + return error; +} + static int __devinit gpio_keys_probe(struct platform_device *pdev) { struct gpio_keys_platform_data *pdata = pdev->dev.platform_data; struct gpio_keys_drvdata *ddata; + struct device *dev = &pdev->dev; struct input_dev *input; int i, error; int wakeup = 0; @@ -87,6 +144,7 @@ static int __devinit gpio_keys_probe(struct platform_device *pdev) GFP_KERNEL); input = input_allocate_device(); if (!ddata || !input) { + dev_err(dev, "failed to allocate state\n"); error = -ENOMEM; goto fail1; } @@ -111,51 +169,14 @@ static int __devinit gpio_keys_probe(struct platform_device *pdev) for (i = 0; i < pdata->nbuttons; i++) { struct gpio_keys_button *button = &pdata->buttons[i]; struct gpio_button_data *bdata = &ddata->data[i]; - int irq; unsigned int type = button->type ?: EV_KEY; bdata->input = input; bdata->button = button; - setup_timer(&bdata->timer, - gpio_keys_timer, (unsigned long)bdata); - INIT_WORK(&bdata->work, gpio_keys_report_event); - - error = gpio_request(button->gpio, button->desc ?: "gpio_keys"); - if (error < 0) { - pr_err("gpio-keys: failed to request GPIO %d," - " error %d\n", button->gpio, error); - goto fail2; - } - error = gpio_direction_input(button->gpio); - if (error < 0) { - pr_err("gpio-keys: failed to configure input" - " direction for GPIO %d, error %d\n", - button->gpio, error); - gpio_free(button->gpio); + error = gpio_keys_setup_key(dev, bdata, button); + if (error) goto fail2; - } - - irq = gpio_to_irq(button->gpio); - if (irq < 0) { - error = irq; - pr_err("gpio-keys: Unable to get irq number" - " for GPIO %d, error %d\n", - button->gpio, error); - gpio_free(button->gpio); - goto fail2; - } - - error = request_irq(irq, gpio_keys_isr, - IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING, - button->desc ? button->desc : "gpio_keys", - bdata); - if (error) { - pr_err("gpio-keys: Unable to claim irq %d; error %d\n", - irq, error); - gpio_free(button->gpio); - goto fail2; - } if (button->wakeup) wakeup = 1; @@ -165,11 +186,16 @@ static int __devinit gpio_keys_probe(struct platform_device *pdev) error = input_register_device(input); if (error) { - pr_err("gpio-keys: Unable to register input device, " + dev_err(dev, "Unable to register input device, " "error: %d\n", error); goto fail2; } + /* get current state of buttons */ + for (i = 0; i < pdata->nbuttons; i++) + gpio_keys_report_event(&ddata->data[i]); + input_sync(input); + device_init_wakeup(&pdev->dev, wakeup); return 0; @@ -216,8 +242,9 @@ static int __devexit gpio_keys_remove(struct platform_device *pdev) #ifdef CONFIG_PM -static int gpio_keys_suspend(struct platform_device *pdev, pm_message_t state) +static int gpio_keys_suspend(struct device *dev) { + struct platform_device *pdev = to_platform_device(dev); struct gpio_keys_platform_data *pdata = pdev->dev.platform_data; int i; @@ -234,36 +261,43 @@ static int gpio_keys_suspend(struct platform_device *pdev, pm_message_t state) return 0; } -static int gpio_keys_resume(struct platform_device *pdev) +static int gpio_keys_resume(struct device *dev) { + struct platform_device *pdev = to_platform_device(dev); + struct gpio_keys_drvdata *ddata = platform_get_drvdata(pdev); struct gpio_keys_platform_data *pdata = pdev->dev.platform_data; int i; - if (device_may_wakeup(&pdev->dev)) { - for (i = 0; i < pdata->nbuttons; i++) { - struct gpio_keys_button *button = &pdata->buttons[i]; - if (button->wakeup) { - int irq = gpio_to_irq(button->gpio); - disable_irq_wake(irq); - } + for (i = 0; i < pdata->nbuttons; i++) { + + struct gpio_keys_button *button = &pdata->buttons[i]; + if (button->wakeup && device_may_wakeup(&pdev->dev)) { + int irq = gpio_to_irq(button->gpio); + disable_irq_wake(irq); } + + gpio_keys_report_event(&ddata->data[i]); } + input_sync(ddata->input); return 0; } -#else -#define gpio_keys_suspend NULL -#define gpio_keys_resume NULL + +static const struct dev_pm_ops gpio_keys_pm_ops = { + .suspend = gpio_keys_suspend, + .resume = gpio_keys_resume, +}; #endif static struct platform_driver gpio_keys_device_driver = { .probe = gpio_keys_probe, .remove = __devexit_p(gpio_keys_remove), - .suspend = gpio_keys_suspend, - .resume = gpio_keys_resume, .driver = { .name = "gpio-keys", .owner = THIS_MODULE, +#ifdef CONFIG_PM + .pm = &gpio_keys_pm_ops, +#endif } }; diff --git a/drivers/input/keyboard/hil_kbd.c b/drivers/input/keyboard/hil_kbd.c index 6f356705ee3..c83f4b2ec7d 100644 --- a/drivers/input/keyboard/hil_kbd.c +++ b/drivers/input/keyboard/hil_kbd.c @@ -37,19 +37,19 @@ #include <linux/kernel.h> #include <linux/module.h> #include <linux/init.h> -#include <linux/semaphore.h> +#include <linux/completion.h> #include <linux/slab.h> #include <linux/pci_ids.h> -#define PREFIX "HIL KEYB: " -#define HIL_GENERIC_NAME "HIL keyboard" +#define PREFIX "HIL: " MODULE_AUTHOR("Brian S. Julin <bri@calyx.com>"); -MODULE_DESCRIPTION(HIL_GENERIC_NAME " driver"); +MODULE_DESCRIPTION("HIL keyboard/mouse driver"); MODULE_LICENSE("Dual BSD/GPL"); -MODULE_ALIAS("serio:ty03pr25id00ex*"); +MODULE_ALIAS("serio:ty03pr25id00ex*"); /* HIL keyboard */ +MODULE_ALIAS("serio:ty03pr25id0Fex*"); /* HIL mouse */ -#define HIL_KBD_MAX_LENGTH 16 +#define HIL_PACKET_MAX_LENGTH 16 #define HIL_KBD_SET1_UPBIT 0x01 #define HIL_KBD_SET1_SHIFT 1 @@ -67,308 +67,497 @@ static unsigned int hil_kbd_set3[HIL_KEYCODES_SET3_TBLSIZE] __read_mostly = static const char hil_language[][16] = { HIL_LOCALE_MAP }; -struct hil_kbd { +struct hil_dev { struct input_dev *dev; struct serio *serio; /* Input buffer and index for packets from HIL bus. */ - hil_packet data[HIL_KBD_MAX_LENGTH]; + hil_packet data[HIL_PACKET_MAX_LENGTH]; int idx4; /* four counts per packet */ /* Raw device info records from HIL bus, see hil.h for fields. */ - char idd[HIL_KBD_MAX_LENGTH]; /* DID byte and IDD record */ - char rsc[HIL_KBD_MAX_LENGTH]; /* RSC record */ - char exd[HIL_KBD_MAX_LENGTH]; /* EXD record */ - char rnm[HIL_KBD_MAX_LENGTH + 1]; /* RNM record + NULL term. */ + char idd[HIL_PACKET_MAX_LENGTH]; /* DID byte and IDD record */ + char rsc[HIL_PACKET_MAX_LENGTH]; /* RSC record */ + char exd[HIL_PACKET_MAX_LENGTH]; /* EXD record */ + char rnm[HIL_PACKET_MAX_LENGTH + 1]; /* RNM record + NULL term. */ - /* Something to sleep around with. */ - struct semaphore sem; + struct completion cmd_done; + + bool is_pointer; + /* Extra device details needed for pointing devices. */ + unsigned int nbtn, naxes; + unsigned int btnmap[7]; }; -/* Process a complete packet after transfer from the HIL */ -static void hil_kbd_process_record(struct hil_kbd *kbd) +static bool hil_dev_is_command_response(hil_packet p) { - struct input_dev *dev = kbd->dev; - hil_packet *data = kbd->data; - hil_packet p; - int idx, i, cnt; + if ((p & ~HIL_CMDCT_POL) == (HIL_ERR_INT | HIL_PKT_CMD | HIL_CMD_POL)) + return false; - idx = kbd->idx4/4; - p = data[idx - 1]; + if ((p & ~HIL_CMDCT_RPL) == (HIL_ERR_INT | HIL_PKT_CMD | HIL_CMD_RPL)) + return false; - if ((p & ~HIL_CMDCT_POL) == - (HIL_ERR_INT | HIL_PKT_CMD | HIL_CMD_POL)) - goto report; - if ((p & ~HIL_CMDCT_RPL) == - (HIL_ERR_INT | HIL_PKT_CMD | HIL_CMD_RPL)) - goto report; + return true; +} + +static void hil_dev_handle_command_response(struct hil_dev *dev) +{ + hil_packet p; + char *buf; + int i, idx; + + idx = dev->idx4 / 4; + p = dev->data[idx - 1]; - /* Not a poll response. See if we are loading config records. */ switch (p & HIL_PKT_DATA_MASK) { case HIL_CMD_IDD: - for (i = 0; i < idx; i++) - kbd->idd[i] = kbd->data[i] & HIL_PKT_DATA_MASK; - for (; i < HIL_KBD_MAX_LENGTH; i++) - kbd->idd[i] = 0; + buf = dev->idd; break; case HIL_CMD_RSC: - for (i = 0; i < idx; i++) - kbd->rsc[i] = kbd->data[i] & HIL_PKT_DATA_MASK; - for (; i < HIL_KBD_MAX_LENGTH; i++) - kbd->rsc[i] = 0; + buf = dev->rsc; break; case HIL_CMD_EXD: - for (i = 0; i < idx; i++) - kbd->exd[i] = kbd->data[i] & HIL_PKT_DATA_MASK; - for (; i < HIL_KBD_MAX_LENGTH; i++) - kbd->exd[i] = 0; + buf = dev->exd; break; case HIL_CMD_RNM: - for (i = 0; i < idx; i++) - kbd->rnm[i] = kbd->data[i] & HIL_PKT_DATA_MASK; - for (; i < HIL_KBD_MAX_LENGTH + 1; i++) - kbd->rnm[i] = '\0'; + dev->rnm[HIL_PACKET_MAX_LENGTH] = 0; + buf = dev->rnm; break; default: /* These occur when device isn't present */ - if (p == (HIL_ERR_INT | HIL_PKT_CMD)) - break; - /* Anything else we'd like to know about. */ - printk(KERN_WARNING PREFIX "Device sent unknown record %x\n", p); - break; + if (p != (HIL_ERR_INT | HIL_PKT_CMD)) { + /* Anything else we'd like to know about. */ + printk(KERN_WARNING PREFIX "Device sent unknown record %x\n", p); + } + goto out; } - goto out; - report: - cnt = 1; + for (i = 0; i < idx; i++) + buf[i] = dev->data[i] & HIL_PKT_DATA_MASK; + for (; i < HIL_PACKET_MAX_LENGTH; i++) + buf[i] = 0; + out: + complete(&dev->cmd_done); +} + +static void hil_dev_handle_kbd_events(struct hil_dev *kbd) +{ + struct input_dev *dev = kbd->dev; + int idx = kbd->idx4 / 4; + int i; + switch (kbd->data[0] & HIL_POL_CHARTYPE_MASK) { case HIL_POL_CHARTYPE_NONE: - break; + return; case HIL_POL_CHARTYPE_ASCII: - while (cnt < idx - 1) - input_report_key(dev, kbd->data[cnt++] & 0x7f, 1); + for (i = 1; i < idx - 1; i++) + input_report_key(dev, kbd->data[i] & 0x7f, 1); break; case HIL_POL_CHARTYPE_RSVD1: case HIL_POL_CHARTYPE_RSVD2: case HIL_POL_CHARTYPE_BINARY: - while (cnt < idx - 1) - input_report_key(dev, kbd->data[cnt++], 1); + for (i = 1; i < idx - 1; i++) + input_report_key(dev, kbd->data[i], 1); break; case HIL_POL_CHARTYPE_SET1: - while (cnt < idx - 1) { - unsigned int key; - int up; - key = kbd->data[cnt++]; - up = key & HIL_KBD_SET1_UPBIT; + for (i = 1; i < idx - 1; i++) { + unsigned int key = kbd->data[i]; + int up = key & HIL_KBD_SET1_UPBIT; + key &= (~HIL_KBD_SET1_UPBIT & 0xff); key = hil_kbd_set1[key >> HIL_KBD_SET1_SHIFT]; - if (key != KEY_RESERVED) - input_report_key(dev, key, !up); + input_report_key(dev, key, !up); } break; case HIL_POL_CHARTYPE_SET2: - while (cnt < idx - 1) { - unsigned int key; - int up; - key = kbd->data[cnt++]; - up = key & HIL_KBD_SET2_UPBIT; + for (i = 1; i < idx - 1; i++) { + unsigned int key = kbd->data[i]; + int up = key & HIL_KBD_SET2_UPBIT; + key &= (~HIL_KBD_SET1_UPBIT & 0xff); key = key >> HIL_KBD_SET2_SHIFT; - if (key != KEY_RESERVED) - input_report_key(dev, key, !up); + input_report_key(dev, key, !up); } break; case HIL_POL_CHARTYPE_SET3: - while (cnt < idx - 1) { - unsigned int key; - int up; - key = kbd->data[cnt++]; - up = key & HIL_KBD_SET3_UPBIT; + for (i = 1; i < idx - 1; i++) { + unsigned int key = kbd->data[i]; + int up = key & HIL_KBD_SET3_UPBIT; + key &= (~HIL_KBD_SET1_UPBIT & 0xff); key = hil_kbd_set3[key >> HIL_KBD_SET3_SHIFT]; - if (key != KEY_RESERVED) - input_report_key(dev, key, !up); + input_report_key(dev, key, !up); } break; } - out: - kbd->idx4 = 0; - up(&kbd->sem); + + input_sync(dev); } -static void hil_kbd_process_err(struct hil_kbd *kbd) +static void hil_dev_handle_ptr_events(struct hil_dev *ptr) +{ + struct input_dev *dev = ptr->dev; + int idx = ptr->idx4 / 4; + hil_packet p = ptr->data[idx - 1]; + int i, cnt, laxis; + bool absdev, ax16; + + if ((p & HIL_CMDCT_POL) != idx - 1) { + printk(KERN_WARNING PREFIX + "Malformed poll packet %x (idx = %i)\n", p, idx); + return; + } + + i = (p & HIL_POL_AXIS_ALT) ? 3 : 0; + laxis = (p & HIL_POL_NUM_AXES_MASK) + i; + + ax16 = ptr->idd[1] & HIL_IDD_HEADER_16BIT; /* 8 or 16bit resolution */ + absdev = ptr->idd[1] & HIL_IDD_HEADER_ABS; + + for (cnt = 1; i < laxis; i++) { + unsigned int lo, hi, val; + + lo = ptr->data[cnt++] & HIL_PKT_DATA_MASK; + hi = ax16 ? (ptr->data[cnt++] & HIL_PKT_DATA_MASK) : 0; + + if (absdev) { + val = lo + (hi << 8); +#ifdef TABLET_AUTOADJUST + if (val < dev->absmin[ABS_X + i]) + dev->absmin[ABS_X + i] = val; + if (val > dev->absmax[ABS_X + i]) + dev->absmax[ABS_X + i] = val; +#endif + if (i%3) val = dev->absmax[ABS_X + i] - val; + input_report_abs(dev, ABS_X + i, val); + } else { + val = (int) (((int8_t)lo) | ((int8_t)hi << 8)); + if (i % 3) + val *= -1; + input_report_rel(dev, REL_X + i, val); + } + } + + while (cnt < idx - 1) { + unsigned int btn = ptr->data[cnt++]; + int up = btn & 1; + + btn &= 0xfe; + if (btn == 0x8e) + continue; /* TODO: proximity == touch? */ + if (btn > 0x8c || btn < 0x80) + continue; + btn = (btn - 0x80) >> 1; + btn = ptr->btnmap[btn]; + input_report_key(dev, btn, !up); + } + + input_sync(dev); +} + +static void hil_dev_process_err(struct hil_dev *dev) { printk(KERN_WARNING PREFIX "errored HIL packet\n"); - kbd->idx4 = 0; - up(&kbd->sem); + dev->idx4 = 0; + complete(&dev->cmd_done); /* just in case somebody is waiting */ } -static irqreturn_t hil_kbd_interrupt(struct serio *serio, +static irqreturn_t hil_dev_interrupt(struct serio *serio, unsigned char data, unsigned int flags) { - struct hil_kbd *kbd; + struct hil_dev *dev; hil_packet packet; int idx; - kbd = serio_get_drvdata(serio); - BUG_ON(kbd == NULL); + dev = serio_get_drvdata(serio); + BUG_ON(dev == NULL); - if (kbd->idx4 >= (HIL_KBD_MAX_LENGTH * sizeof(hil_packet))) { - hil_kbd_process_err(kbd); - return IRQ_HANDLED; + if (dev->idx4 >= HIL_PACKET_MAX_LENGTH * sizeof(hil_packet)) { + hil_dev_process_err(dev); + goto out; } - idx = kbd->idx4/4; - if (!(kbd->idx4 % 4)) - kbd->data[idx] = 0; - packet = kbd->data[idx]; - packet |= ((hil_packet)data) << ((3 - (kbd->idx4 % 4)) * 8); - kbd->data[idx] = packet; + + idx = dev->idx4 / 4; + if (!(dev->idx4 % 4)) + dev->data[idx] = 0; + packet = dev->data[idx]; + packet |= ((hil_packet)data) << ((3 - (dev->idx4 % 4)) * 8); + dev->data[idx] = packet; /* Records of N 4-byte hil_packets must terminate with a command. */ - if ((++(kbd->idx4)) % 4) - return IRQ_HANDLED; - if ((packet & 0xffff0000) != HIL_ERR_INT) { - hil_kbd_process_err(kbd); - return IRQ_HANDLED; + if ((++dev->idx4 % 4) == 0) { + if ((packet & 0xffff0000) != HIL_ERR_INT) { + hil_dev_process_err(dev); + } else if (packet & HIL_PKT_CMD) { + if (hil_dev_is_command_response(packet)) + hil_dev_handle_command_response(dev); + else if (dev->is_pointer) + hil_dev_handle_ptr_events(dev); + else + hil_dev_handle_kbd_events(dev); + dev->idx4 = 0; + } } - if (packet & HIL_PKT_CMD) - hil_kbd_process_record(kbd); + out: return IRQ_HANDLED; } -static void hil_kbd_disconnect(struct serio *serio) +static void hil_dev_disconnect(struct serio *serio) { - struct hil_kbd *kbd; + struct hil_dev *dev = serio_get_drvdata(serio); - kbd = serio_get_drvdata(serio); - BUG_ON(kbd == NULL); + BUG_ON(dev == NULL); serio_close(serio); - input_unregister_device(kbd->dev); - kfree(kbd); + input_unregister_device(dev->dev); + serio_set_drvdata(serio, NULL); + kfree(dev); +} + +static void hil_dev_keyboard_setup(struct hil_dev *kbd) +{ + struct input_dev *input_dev = kbd->dev; + uint8_t did = kbd->idd[0]; + int i; + + input_dev->evbit[0] = BIT_MASK(EV_KEY) | BIT_MASK(EV_REP); + input_dev->ledbit[0] = BIT_MASK(LED_NUML) | BIT_MASK(LED_CAPSL) | + BIT_MASK(LED_SCROLLL); + + for (i = 0; i < 128; i++) { + __set_bit(hil_kbd_set1[i], input_dev->keybit); + __set_bit(hil_kbd_set3[i], input_dev->keybit); + } + __clear_bit(KEY_RESERVED, input_dev->keybit); + + input_dev->keycodemax = HIL_KEYCODES_SET1_TBLSIZE; + input_dev->keycodesize = sizeof(hil_kbd_set1[0]); + input_dev->keycode = hil_kbd_set1; + + input_dev->name = strlen(kbd->rnm) ? kbd->rnm : "HIL keyboard"; + input_dev->phys = "hpkbd/input0"; + + printk(KERN_INFO PREFIX "HIL keyboard found (did = 0x%02x, lang = %s)\n", + did, hil_language[did & HIL_IDD_DID_TYPE_KB_LANG_MASK]); } -static int hil_kbd_connect(struct serio *serio, struct serio_driver *drv) +static void hil_dev_pointer_setup(struct hil_dev *ptr) { - struct hil_kbd *kbd; - uint8_t did, *idd; - int i; + struct input_dev *input_dev = ptr->dev; + uint8_t did = ptr->idd[0]; + uint8_t *idd = ptr->idd + 1; + unsigned int naxsets = HIL_IDD_NUM_AXSETS(*idd); + unsigned int i, btntype; + const char *txt; + + ptr->naxes = HIL_IDD_NUM_AXES_PER_SET(*idd); + + switch (did & HIL_IDD_DID_TYPE_MASK) { + case HIL_IDD_DID_TYPE_REL: + input_dev->evbit[0] = BIT_MASK(EV_REL); - kbd = kzalloc(sizeof(*kbd), GFP_KERNEL); - if (!kbd) - return -ENOMEM; + for (i = 0; i < ptr->naxes; i++) + __set_bit(REL_X + i, input_dev->relbit); - kbd->dev = input_allocate_device(); - if (!kbd->dev) + for (i = 3; naxsets > 1 && i < ptr->naxes + 3; i++) + __set_bit(REL_X + i, input_dev->relbit); + + txt = "relative"; + break; + + case HIL_IDD_DID_TYPE_ABS: + input_dev->evbit[0] = BIT_MASK(EV_ABS); + + for (i = 0; i < ptr->naxes; i++) + input_set_abs_params(input_dev, ABS_X + i, + 0, HIL_IDD_AXIS_MAX(idd, i), 0, 0); + + for (i = 3; naxsets > 1 && i < ptr->naxes + 3; i++) + input_set_abs_params(input_dev, ABS_X + i, + 0, HIL_IDD_AXIS_MAX(idd, i - 3), 0, 0); + +#ifdef TABLET_AUTOADJUST + for (i = 0; i < ABS_MAX; i++) { + int diff = input_dev->absmax[ABS_X + i] / 10; + input_dev->absmin[ABS_X + i] += diff; + input_dev->absmax[ABS_X + i] -= diff; + } +#endif + + txt = "absolute"; + break; + + default: + BUG(); + } + + ptr->nbtn = HIL_IDD_NUM_BUTTONS(idd); + if (ptr->nbtn) + input_dev->evbit[0] |= BIT_MASK(EV_KEY); + + btntype = BTN_MISC; + if ((did & HIL_IDD_DID_ABS_TABLET_MASK) == HIL_IDD_DID_ABS_TABLET) +#ifdef TABLET_SIMULATES_MOUSE + btntype = BTN_TOUCH; +#else + btntype = BTN_DIGI; +#endif + if ((did & HIL_IDD_DID_ABS_TSCREEN_MASK) == HIL_IDD_DID_ABS_TSCREEN) + btntype = BTN_TOUCH; + + if ((did & HIL_IDD_DID_REL_MOUSE_MASK) == HIL_IDD_DID_REL_MOUSE) + btntype = BTN_MOUSE; + + for (i = 0; i < ptr->nbtn; i++) { + __set_bit(btntype | i, input_dev->keybit); + ptr->btnmap[i] = btntype | i; + } + + if (btntype == BTN_MOUSE) { + /* Swap buttons 2 and 3 */ + ptr->btnmap[1] = BTN_MIDDLE; + ptr->btnmap[2] = BTN_RIGHT; + } + + input_dev->name = strlen(ptr->rnm) ? ptr->rnm : "HIL pointer device"; + + printk(KERN_INFO PREFIX + "HIL pointer device found (did: 0x%02x, axis: %s)\n", + did, txt); + printk(KERN_INFO PREFIX + "HIL pointer has %i buttons and %i sets of %i axes\n", + ptr->nbtn, naxsets, ptr->naxes); +} + +static int hil_dev_connect(struct serio *serio, struct serio_driver *drv) +{ + struct hil_dev *dev; + struct input_dev *input_dev; + uint8_t did, *idd; + int error; + + dev = kzalloc(sizeof(*dev), GFP_KERNEL); + input_dev = input_allocate_device(); + if (!dev || !input_dev) { + error = -ENOMEM; goto bail0; + } - if (serio_open(serio, drv)) - goto bail1; + dev->serio = serio; + dev->dev = input_dev; - serio_set_drvdata(serio, kbd); - kbd->serio = serio; + error = serio_open(serio, drv); + if (error) + goto bail0; - init_MUTEX_LOCKED(&kbd->sem); + serio_set_drvdata(serio, dev); /* Get device info. MLC driver supplies devid/status/etc. */ - serio->write(serio, 0); - serio->write(serio, 0); - serio->write(serio, HIL_PKT_CMD >> 8); - serio->write(serio, HIL_CMD_IDD); - down(&kbd->sem); - - serio->write(serio, 0); - serio->write(serio, 0); - serio->write(serio, HIL_PKT_CMD >> 8); - serio->write(serio, HIL_CMD_RSC); - down(&kbd->sem); - - serio->write(serio, 0); - serio->write(serio, 0); - serio->write(serio, HIL_PKT_CMD >> 8); - serio->write(serio, HIL_CMD_RNM); - down(&kbd->sem); - - serio->write(serio, 0); - serio->write(serio, 0); - serio->write(serio, HIL_PKT_CMD >> 8); - serio->write(serio, HIL_CMD_EXD); - down(&kbd->sem); - - up(&kbd->sem); - - did = kbd->idd[0]; - idd = kbd->idd + 1; + init_completion(&dev->cmd_done); + serio_write(serio, 0); + serio_write(serio, 0); + serio_write(serio, HIL_PKT_CMD >> 8); + serio_write(serio, HIL_CMD_IDD); + error = wait_for_completion_killable(&dev->cmd_done); + if (error) + goto bail1; + + init_completion(&dev->cmd_done); + serio_write(serio, 0); + serio_write(serio, 0); + serio_write(serio, HIL_PKT_CMD >> 8); + serio_write(serio, HIL_CMD_RSC); + error = wait_for_completion_killable(&dev->cmd_done); + if (error) + goto bail1; + + init_completion(&dev->cmd_done); + serio_write(serio, 0); + serio_write(serio, 0); + serio_write(serio, HIL_PKT_CMD >> 8); + serio_write(serio, HIL_CMD_RNM); + error = wait_for_completion_killable(&dev->cmd_done); + if (error) + goto bail1; + + init_completion(&dev->cmd_done); + serio_write(serio, 0); + serio_write(serio, 0); + serio_write(serio, HIL_PKT_CMD >> 8); + serio_write(serio, HIL_CMD_EXD); + error = wait_for_completion_killable(&dev->cmd_done); + if (error) + goto bail1; + + did = dev->idd[0]; + idd = dev->idd + 1; + switch (did & HIL_IDD_DID_TYPE_MASK) { case HIL_IDD_DID_TYPE_KB_INTEGRAL: case HIL_IDD_DID_TYPE_KB_ITF: case HIL_IDD_DID_TYPE_KB_RSVD: case HIL_IDD_DID_TYPE_CHAR: - printk(KERN_INFO PREFIX "HIL keyboard found (did = 0x%02x, lang = %s)\n", - did, hil_language[did & HIL_IDD_DID_TYPE_KB_LANG_MASK]); - break; - default: - goto bail2; - } + if (HIL_IDD_NUM_BUTTONS(idd) || + HIL_IDD_NUM_AXES_PER_SET(*idd)) { + printk(KERN_INFO PREFIX + "combo devices are not supported.\n"); + goto bail1; + } - if (HIL_IDD_NUM_BUTTONS(idd) || HIL_IDD_NUM_AXES_PER_SET(*idd)) { - printk(KERN_INFO PREFIX "keyboards only, no combo devices supported.\n"); - goto bail2; - } + dev->is_pointer = false; + hil_dev_keyboard_setup(dev); + break; - kbd->dev->evbit[0] = BIT_MASK(EV_KEY) | BIT_MASK(EV_REP); - kbd->dev->ledbit[0] = BIT_MASK(LED_NUML) | BIT_MASK(LED_CAPSL) | - BIT_MASK(LED_SCROLLL); - kbd->dev->keycodemax = HIL_KEYCODES_SET1_TBLSIZE; - kbd->dev->keycodesize = sizeof(hil_kbd_set1[0]); - kbd->dev->keycode = hil_kbd_set1; - kbd->dev->name = strlen(kbd->rnm) ? kbd->rnm : HIL_GENERIC_NAME; - kbd->dev->phys = "hpkbd/input0"; /* XXX */ - - kbd->dev->id.bustype = BUS_HIL; - kbd->dev->id.vendor = PCI_VENDOR_ID_HP; - kbd->dev->id.product = 0x0001; /* TODO: get from kbd->rsc */ - kbd->dev->id.version = 0x0100; /* TODO: get from kbd->rsc */ - kbd->dev->dev.parent = &serio->dev; + case HIL_IDD_DID_TYPE_REL: + case HIL_IDD_DID_TYPE_ABS: + dev->is_pointer = true; + hil_dev_pointer_setup(dev); + break; - for (i = 0; i < 128; i++) { - set_bit(hil_kbd_set1[i], kbd->dev->keybit); - set_bit(hil_kbd_set3[i], kbd->dev->keybit); + default: + goto bail1; } - clear_bit(0, kbd->dev->keybit); - input_register_device(kbd->dev); - printk(KERN_INFO "input: %s, ID: %d\n", - kbd->dev->name, did); + input_dev->id.bustype = BUS_HIL; + input_dev->id.vendor = PCI_VENDOR_ID_HP; + input_dev->id.product = 0x0001; /* TODO: get from kbd->rsc */ + input_dev->id.version = 0x0100; /* TODO: get from kbd->rsc */ + input_dev->dev.parent = &serio->dev; + + if (!dev->is_pointer) { + serio_write(serio, 0); + serio_write(serio, 0); + serio_write(serio, HIL_PKT_CMD >> 8); + /* Enable Keyswitch Autorepeat 1 */ + serio_write(serio, HIL_CMD_EK1); + /* No need to wait for completion */ + } - serio->write(serio, 0); - serio->write(serio, 0); - serio->write(serio, HIL_PKT_CMD >> 8); - serio->write(serio, HIL_CMD_EK1); /* Enable Keyswitch Autorepeat 1 */ - down(&kbd->sem); - up(&kbd->sem); + error = input_register_device(input_dev); + if (error) + goto bail1; return 0; - bail2: + + bail1: serio_close(serio); serio_set_drvdata(serio, NULL); - bail1: - input_free_device(kbd->dev); bail0: - kfree(kbd); - return -EIO; + input_free_device(input_dev); + kfree(dev); + return error; } -static struct serio_device_id hil_kbd_ids[] = { +static struct serio_device_id hil_dev_ids[] = { { .type = SERIO_HIL_MLC, .proto = SERIO_HIL, @@ -378,26 +567,26 @@ static struct serio_device_id hil_kbd_ids[] = { { 0 } }; -static struct serio_driver hil_kbd_serio_drv = { +static struct serio_driver hil_serio_drv = { .driver = { - .name = "hil_kbd", + .name = "hil_dev", }, - .description = "HP HIL keyboard driver", - .id_table = hil_kbd_ids, - .connect = hil_kbd_connect, - .disconnect = hil_kbd_disconnect, - .interrupt = hil_kbd_interrupt + .description = "HP HIL keyboard/mouse/tablet driver", + .id_table = hil_dev_ids, + .connect = hil_dev_connect, + .disconnect = hil_dev_disconnect, + .interrupt = hil_dev_interrupt }; -static int __init hil_kbd_init(void) +static int __init hil_dev_init(void) { - return serio_register_driver(&hil_kbd_serio_drv); + return serio_register_driver(&hil_serio_drv); } -static void __exit hil_kbd_exit(void) +static void __exit hil_dev_exit(void) { - serio_unregister_driver(&hil_kbd_serio_drv); + serio_unregister_driver(&hil_serio_drv); } -module_init(hil_kbd_init); -module_exit(hil_kbd_exit); +module_init(hil_dev_init); +module_exit(hil_dev_exit); diff --git a/drivers/input/keyboard/hilkbd.c b/drivers/input/keyboard/hilkbd.c index e9d639ec283..5f72440b50c 100644 --- a/drivers/input/keyboard/hilkbd.c +++ b/drivers/input/keyboard/hilkbd.c @@ -24,6 +24,7 @@ #include <linux/interrupt.h> #include <linux/hil.h> #include <linux/io.h> +#include <linux/sched.h> #include <linux/spinlock.h> #include <asm/irq.h> #ifdef CONFIG_HP300 diff --git a/drivers/input/keyboard/lkkbd.c b/drivers/input/keyboard/lkkbd.c index 4730ef35c73..fa9bb6d235e 100644 --- a/drivers/input/keyboard/lkkbd.c +++ b/drivers/input/keyboard/lkkbd.c @@ -72,9 +72,9 @@ #define DRIVER_DESC "LK keyboard driver" -MODULE_AUTHOR ("Jan-Benedict Glaw <jbglaw@lug-owl.de>"); -MODULE_DESCRIPTION (DRIVER_DESC); -MODULE_LICENSE ("GPL"); +MODULE_AUTHOR("Jan-Benedict Glaw <jbglaw@lug-owl.de>"); +MODULE_DESCRIPTION(DRIVER_DESC); +MODULE_LICENSE("GPL"); /* * Known parameters: @@ -85,27 +85,27 @@ MODULE_LICENSE ("GPL"); * Please notice that there's not yet an API to set these at runtime. */ static int bell_volume = 100; /* % */ -module_param (bell_volume, int, 0); -MODULE_PARM_DESC (bell_volume, "Bell volume (in %). default is 100%"); +module_param(bell_volume, int, 0); +MODULE_PARM_DESC(bell_volume, "Bell volume (in %). default is 100%"); static int keyclick_volume = 100; /* % */ -module_param (keyclick_volume, int, 0); -MODULE_PARM_DESC (keyclick_volume, "Keyclick volume (in %), default is 100%"); +module_param(keyclick_volume, int, 0); +MODULE_PARM_DESC(keyclick_volume, "Keyclick volume (in %), default is 100%"); static int ctrlclick_volume = 100; /* % */ -module_param (ctrlclick_volume, int, 0); -MODULE_PARM_DESC (ctrlclick_volume, "Ctrlclick volume (in %), default is 100%"); +module_param(ctrlclick_volume, int, 0); +MODULE_PARM_DESC(ctrlclick_volume, "Ctrlclick volume (in %), default is 100%"); static int lk201_compose_is_alt; -module_param (lk201_compose_is_alt, int, 0); -MODULE_PARM_DESC (lk201_compose_is_alt, "If set non-zero, LK201' Compose key " - "will act as an Alt key"); +module_param(lk201_compose_is_alt, int, 0); +MODULE_PARM_DESC(lk201_compose_is_alt, + "If set non-zero, LK201' Compose key will act as an Alt key"); #undef LKKBD_DEBUG #ifdef LKKBD_DEBUG -#define DBG(x...) printk (x) +#define DBG(x...) printk(x) #else #define DBG(x...) do {} while (0) #endif @@ -122,7 +122,7 @@ MODULE_PARM_DESC (lk201_compose_is_alt, "If set non-zero, LK201' Compose key " #define LK_MODE_DOWN 0x80 #define LK_MODE_AUTODOWN 0x82 #define LK_MODE_UPDOWN 0x86 -#define LK_CMD_SET_MODE(mode,div) ((mode) | ((div) << 3)) +#define LK_CMD_SET_MODE(mode, div) ((mode) | ((div) << 3)) /* Misc commands */ #define LK_CMD_ENABLE_KEYCLICK 0x1b @@ -152,11 +152,8 @@ MODULE_PARM_DESC (lk201_compose_is_alt, "If set non-zero, LK201' Compose key " #define LK_NUM_KEYCODES 256 #define LK_NUM_IGNORE_BYTES 6 -typedef u_int16_t lk_keycode_t; - - -static lk_keycode_t lkkbd_keycode[LK_NUM_KEYCODES] = { +static unsigned short lkkbd_keycode[LK_NUM_KEYCODES] = { [0x56] = KEY_F1, [0x57] = KEY_F2, [0x58] = KEY_F3, @@ -268,7 +265,7 @@ static lk_keycode_t lkkbd_keycode[LK_NUM_KEYCODES] = { }; #define CHECK_LED(LK, VAR_ON, VAR_OFF, LED, BITS) do { \ - if (test_bit (LED, (LK)->dev->led)) \ + if (test_bit(LED, (LK)->dev->led)) \ VAR_ON |= BITS; \ else \ VAR_OFF |= BITS; \ @@ -278,7 +275,7 @@ static lk_keycode_t lkkbd_keycode[LK_NUM_KEYCODES] = { * Per-keyboard data */ struct lkkbd { - lk_keycode_t keycode[LK_NUM_KEYCODES]; + unsigned short keycode[LK_NUM_KEYCODES]; int ignore_bytes; unsigned char id[LK_NUM_IGNORE_BYTES]; struct input_dev *dev; @@ -301,26 +298,25 @@ static struct { unsigned char *name; } lk_response[] = { #define RESPONSE(x) { .value = (x), .name = #x, } - RESPONSE (LK_STUCK_KEY), - RESPONSE (LK_SELFTEST_FAILED), - RESPONSE (LK_ALL_KEYS_UP), - RESPONSE (LK_METRONOME), - RESPONSE (LK_OUTPUT_ERROR), - RESPONSE (LK_INPUT_ERROR), - RESPONSE (LK_KBD_LOCKED), - RESPONSE (LK_KBD_TEST_MODE_ACK), - RESPONSE (LK_PREFIX_KEY_DOWN), - RESPONSE (LK_MODE_CHANGE_ACK), - RESPONSE (LK_RESPONSE_RESERVED), + RESPONSE(LK_STUCK_KEY), + RESPONSE(LK_SELFTEST_FAILED), + RESPONSE(LK_ALL_KEYS_UP), + RESPONSE(LK_METRONOME), + RESPONSE(LK_OUTPUT_ERROR), + RESPONSE(LK_INPUT_ERROR), + RESPONSE(LK_KBD_LOCKED), + RESPONSE(LK_KBD_TEST_MODE_ACK), + RESPONSE(LK_PREFIX_KEY_DOWN), + RESPONSE(LK_MODE_CHANGE_ACK), + RESPONSE(LK_RESPONSE_RESERVED), #undef RESPONSE }; -static unsigned char * -response_name (unsigned char value) +static unsigned char *response_name(unsigned char value) { int i; - for (i = 0; i < ARRAY_SIZE (lk_response); i++) + for (i = 0; i < ARRAY_SIZE(lk_response); i++) if (lk_response[i].value == value) return lk_response[i].name; @@ -331,8 +327,7 @@ response_name (unsigned char value) /* * Calculate volume parameter byte for a given volume. */ -static unsigned char -volume_to_hw (int volume_percent) +static unsigned char volume_to_hw(int volume_percent) { unsigned char ret = 0; @@ -363,8 +358,7 @@ volume_to_hw (int volume_percent) return ret; } -static void -lkkbd_detection_done (struct lkkbd *lk) +static void lkkbd_detection_done(struct lkkbd *lk) { int i; @@ -377,190 +371,202 @@ lkkbd_detection_done (struct lkkbd *lk) * Print keyboard name and modify Compose=Alt on user's request. */ switch (lk->id[4]) { - case 1: - strlcpy (lk->name, "DEC LK201 keyboard", - sizeof (lk->name)); - - if (lk201_compose_is_alt) - lk->keycode[0xb1] = KEY_LEFTALT; - break; - - case 2: - strlcpy (lk->name, "DEC LK401 keyboard", - sizeof (lk->name)); - break; - - default: - strlcpy (lk->name, "Unknown DEC keyboard", - sizeof (lk->name)); - printk (KERN_ERR "lkkbd: keyboard on %s is unknown, " - "please report to Jan-Benedict Glaw " - "<jbglaw@lug-owl.de>\n", lk->phys); - printk (KERN_ERR "lkkbd: keyboard ID'ed as:"); - for (i = 0; i < LK_NUM_IGNORE_BYTES; i++) - printk (" 0x%02x", lk->id[i]); - printk ("\n"); - break; + case 1: + strlcpy(lk->name, "DEC LK201 keyboard", sizeof(lk->name)); + + if (lk201_compose_is_alt) + lk->keycode[0xb1] = KEY_LEFTALT; + break; + + case 2: + strlcpy(lk->name, "DEC LK401 keyboard", sizeof(lk->name)); + break; + + default: + strlcpy(lk->name, "Unknown DEC keyboard", sizeof(lk->name)); + printk(KERN_ERR + "lkkbd: keyboard on %s is unknown, please report to " + "Jan-Benedict Glaw <jbglaw@lug-owl.de>\n", lk->phys); + printk(KERN_ERR "lkkbd: keyboard ID'ed as:"); + for (i = 0; i < LK_NUM_IGNORE_BYTES; i++) + printk(" 0x%02x", lk->id[i]); + printk("\n"); + break; } - printk (KERN_INFO "lkkbd: keyboard on %s identified as: %s\n", - lk->phys, lk->name); + + printk(KERN_INFO "lkkbd: keyboard on %s identified as: %s\n", + lk->phys, lk->name); /* * Report errors during keyboard boot-up. */ switch (lk->id[2]) { - case 0x00: - /* All okay */ - break; - - case LK_STUCK_KEY: - printk (KERN_ERR "lkkbd: Stuck key on keyboard at " - "%s\n", lk->phys); - break; - - case LK_SELFTEST_FAILED: - printk (KERN_ERR "lkkbd: Selftest failed on keyboard " - "at %s, keyboard may not work " - "properly\n", lk->phys); - break; - - default: - printk (KERN_ERR "lkkbd: Unknown error %02x on " - "keyboard at %s\n", lk->id[2], - lk->phys); - break; + case 0x00: + /* All okay */ + break; + + case LK_STUCK_KEY: + printk(KERN_ERR "lkkbd: Stuck key on keyboard at %s\n", + lk->phys); + break; + + case LK_SELFTEST_FAILED: + printk(KERN_ERR + "lkkbd: Selftest failed on keyboard at %s, " + "keyboard may not work properly\n", lk->phys); + break; + + default: + printk(KERN_ERR + "lkkbd: Unknown error %02x on keyboard at %s\n", + lk->id[2], lk->phys); + break; } /* * Try to hint user if there's a stuck key. */ if (lk->id[2] == LK_STUCK_KEY && lk->id[3] != 0) - printk (KERN_ERR "Scancode of stuck key is 0x%02x, keycode " - "is 0x%04x\n", lk->id[3], - lk->keycode[lk->id[3]]); - - return; + printk(KERN_ERR + "Scancode of stuck key is 0x%02x, keycode is 0x%04x\n", + lk->id[3], lk->keycode[lk->id[3]]); } /* * lkkbd_interrupt() is called by the low level driver when a character * is received. */ -static irqreturn_t -lkkbd_interrupt (struct serio *serio, unsigned char data, unsigned int flags) +static irqreturn_t lkkbd_interrupt(struct serio *serio, + unsigned char data, unsigned int flags) { - struct lkkbd *lk = serio_get_drvdata (serio); + struct lkkbd *lk = serio_get_drvdata(serio); + struct input_dev *input_dev = lk->dev; + unsigned int keycode; int i; - DBG (KERN_INFO "Got byte 0x%02x\n", data); + DBG(KERN_INFO "Got byte 0x%02x\n", data); if (lk->ignore_bytes > 0) { - DBG (KERN_INFO "Ignoring a byte on %s\n", lk->name); + DBG(KERN_INFO "Ignoring a byte on %s\n", lk->name); lk->id[LK_NUM_IGNORE_BYTES - lk->ignore_bytes--] = data; if (lk->ignore_bytes == 0) - lkkbd_detection_done (lk); + lkkbd_detection_done(lk); return IRQ_HANDLED; } switch (data) { - case LK_ALL_KEYS_UP: - for (i = 0; i < ARRAY_SIZE (lkkbd_keycode); i++) - if (lk->keycode[i] != KEY_RESERVED) - input_report_key (lk->dev, lk->keycode[i], 0); - input_sync (lk->dev); - break; - - case 0x01: - DBG (KERN_INFO "Got 0x01, scheduling re-initialization\n"); - lk->ignore_bytes = LK_NUM_IGNORE_BYTES; - lk->id[LK_NUM_IGNORE_BYTES - lk->ignore_bytes--] = data; - schedule_work (&lk->tq); - break; - - case LK_METRONOME: - case LK_OUTPUT_ERROR: - case LK_INPUT_ERROR: - case LK_KBD_LOCKED: - case LK_KBD_TEST_MODE_ACK: - case LK_PREFIX_KEY_DOWN: - case LK_MODE_CHANGE_ACK: - case LK_RESPONSE_RESERVED: - DBG (KERN_INFO "Got %s and don't know how to handle...\n", - response_name (data)); - break; - - default: - if (lk->keycode[data] != KEY_RESERVED) { - if (!test_bit (lk->keycode[data], lk->dev->key)) - input_report_key (lk->dev, lk->keycode[data], 1); - else - input_report_key (lk->dev, lk->keycode[data], 0); - input_sync (lk->dev); - } else - printk (KERN_WARNING "%s: Unknown key with " - "scancode 0x%02x on %s.\n", - __FILE__, data, lk->name); + case LK_ALL_KEYS_UP: + for (i = 0; i < ARRAY_SIZE(lkkbd_keycode); i++) + input_report_key(input_dev, lk->keycode[i], 0); + input_sync(input_dev); + break; + + case 0x01: + DBG(KERN_INFO "Got 0x01, scheduling re-initialization\n"); + lk->ignore_bytes = LK_NUM_IGNORE_BYTES; + lk->id[LK_NUM_IGNORE_BYTES - lk->ignore_bytes--] = data; + schedule_work(&lk->tq); + break; + + case LK_METRONOME: + case LK_OUTPUT_ERROR: + case LK_INPUT_ERROR: + case LK_KBD_LOCKED: + case LK_KBD_TEST_MODE_ACK: + case LK_PREFIX_KEY_DOWN: + case LK_MODE_CHANGE_ACK: + case LK_RESPONSE_RESERVED: + DBG(KERN_INFO "Got %s and don't know how to handle...\n", + response_name(data)); + break; + + default: + keycode = lk->keycode[data]; + if (keycode != KEY_RESERVED) { + input_report_key(input_dev, keycode, + !test_bit(keycode, input_dev->key)); + input_sync(input_dev); + } else { + printk(KERN_WARNING + "%s: Unknown key with scancode 0x%02x on %s.\n", + __FILE__, data, lk->name); + } } return IRQ_HANDLED; } +static void lkkbd_toggle_leds(struct lkkbd *lk) +{ + struct serio *serio = lk->serio; + unsigned char leds_on = 0; + unsigned char leds_off = 0; + + CHECK_LED(lk, leds_on, leds_off, LED_CAPSL, LK_LED_SHIFTLOCK); + CHECK_LED(lk, leds_on, leds_off, LED_COMPOSE, LK_LED_COMPOSE); + CHECK_LED(lk, leds_on, leds_off, LED_SCROLLL, LK_LED_SCROLLLOCK); + CHECK_LED(lk, leds_on, leds_off, LED_SLEEP, LK_LED_WAIT); + if (leds_on != 0) { + serio_write(serio, LK_CMD_LED_ON); + serio_write(serio, leds_on); + } + if (leds_off != 0) { + serio_write(serio, LK_CMD_LED_OFF); + serio_write(serio, leds_off); + } +} + +static void lkkbd_toggle_keyclick(struct lkkbd *lk, bool on) +{ + struct serio *serio = lk->serio; + + if (on) { + DBG("%s: Activating key clicks\n", __func__); + serio_write(serio, LK_CMD_ENABLE_KEYCLICK); + serio_write(serio, volume_to_hw(lk->keyclick_volume)); + serio_write(serio, LK_CMD_ENABLE_CTRCLICK); + serio_write(serio, volume_to_hw(lk->ctrlclick_volume)); + } else { + DBG("%s: Deactivating key clicks\n", __func__); + serio_write(serio, LK_CMD_DISABLE_KEYCLICK); + serio_write(serio, LK_CMD_DISABLE_CTRCLICK); + } + +} + /* * lkkbd_event() handles events from the input module. */ -static int -lkkbd_event (struct input_dev *dev, unsigned int type, unsigned int code, - int value) +static int lkkbd_event(struct input_dev *dev, + unsigned int type, unsigned int code, int value) { - struct lkkbd *lk = input_get_drvdata (dev); - unsigned char leds_on = 0; - unsigned char leds_off = 0; + struct lkkbd *lk = input_get_drvdata(dev); switch (type) { - case EV_LED: - CHECK_LED (lk, leds_on, leds_off, LED_CAPSL, LK_LED_SHIFTLOCK); - CHECK_LED (lk, leds_on, leds_off, LED_COMPOSE, LK_LED_COMPOSE); - CHECK_LED (lk, leds_on, leds_off, LED_SCROLLL, LK_LED_SCROLLLOCK); - CHECK_LED (lk, leds_on, leds_off, LED_SLEEP, LK_LED_WAIT); - if (leds_on != 0) { - lk->serio->write (lk->serio, LK_CMD_LED_ON); - lk->serio->write (lk->serio, leds_on); - } - if (leds_off != 0) { - lk->serio->write (lk->serio, LK_CMD_LED_OFF); - lk->serio->write (lk->serio, leds_off); - } + case EV_LED: + lkkbd_toggle_leds(lk); + return 0; + + case EV_SND: + switch (code) { + case SND_CLICK: + lkkbd_toggle_keyclick(lk, value); return 0; - case EV_SND: - switch (code) { - case SND_CLICK: - if (value == 0) { - DBG ("%s: Deactivating key clicks\n", __func__); - lk->serio->write (lk->serio, LK_CMD_DISABLE_KEYCLICK); - lk->serio->write (lk->serio, LK_CMD_DISABLE_CTRCLICK); - } else { - DBG ("%s: Activating key clicks\n", __func__); - lk->serio->write (lk->serio, LK_CMD_ENABLE_KEYCLICK); - lk->serio->write (lk->serio, volume_to_hw (lk->keyclick_volume)); - lk->serio->write (lk->serio, LK_CMD_ENABLE_CTRCLICK); - lk->serio->write (lk->serio, volume_to_hw (lk->ctrlclick_volume)); - } - return 0; - - case SND_BELL: - if (value != 0) - lk->serio->write (lk->serio, LK_CMD_SOUND_BELL); - - return 0; - } - break; - - default: - printk (KERN_ERR "%s (): Got unknown type %d, code %d, value %d\n", - __func__, type, code, value); + case SND_BELL: + if (value != 0) + serio_write(lk->serio, LK_CMD_SOUND_BELL); + + return 0; + } + + break; + + default: + printk(KERN_ERR "%s(): Got unknown type %d, code %d, value %d\n", + __func__, type, code, value); } return -1; @@ -570,79 +576,56 @@ lkkbd_event (struct input_dev *dev, unsigned int type, unsigned int code, * lkkbd_reinit() sets leds and beeps to a state the computer remembers they * were in. */ -static void -lkkbd_reinit (struct work_struct *work) +static void lkkbd_reinit(struct work_struct *work) { struct lkkbd *lk = container_of(work, struct lkkbd, tq); int division; - unsigned char leds_on = 0; - unsigned char leds_off = 0; /* Ask for ID */ - lk->serio->write (lk->serio, LK_CMD_REQUEST_ID); + serio_write(lk->serio, LK_CMD_REQUEST_ID); /* Reset parameters */ - lk->serio->write (lk->serio, LK_CMD_SET_DEFAULTS); + serio_write(lk->serio, LK_CMD_SET_DEFAULTS); /* Set LEDs */ - CHECK_LED (lk, leds_on, leds_off, LED_CAPSL, LK_LED_SHIFTLOCK); - CHECK_LED (lk, leds_on, leds_off, LED_COMPOSE, LK_LED_COMPOSE); - CHECK_LED (lk, leds_on, leds_off, LED_SCROLLL, LK_LED_SCROLLLOCK); - CHECK_LED (lk, leds_on, leds_off, LED_SLEEP, LK_LED_WAIT); - if (leds_on != 0) { - lk->serio->write (lk->serio, LK_CMD_LED_ON); - lk->serio->write (lk->serio, leds_on); - } - if (leds_off != 0) { - lk->serio->write (lk->serio, LK_CMD_LED_OFF); - lk->serio->write (lk->serio, leds_off); - } + lkkbd_toggle_leds(lk); /* * Try to activate extended LK401 mode. This command will * only work with a LK401 keyboard and grants access to * LAlt, RAlt, RCompose and RShift. */ - lk->serio->write (lk->serio, LK_CMD_ENABLE_LK401); + serio_write(lk->serio, LK_CMD_ENABLE_LK401); /* Set all keys to UPDOWN mode */ for (division = 1; division <= 14; division++) - lk->serio->write (lk->serio, LK_CMD_SET_MODE (LK_MODE_UPDOWN, - division)); + serio_write(lk->serio, + LK_CMD_SET_MODE(LK_MODE_UPDOWN, division)); /* Enable bell and set volume */ - lk->serio->write (lk->serio, LK_CMD_ENABLE_BELL); - lk->serio->write (lk->serio, volume_to_hw (lk->bell_volume)); + serio_write(lk->serio, LK_CMD_ENABLE_BELL); + serio_write(lk->serio, volume_to_hw(lk->bell_volume)); /* Enable/disable keyclick (and possibly set volume) */ - if (test_bit (SND_CLICK, lk->dev->snd)) { - lk->serio->write (lk->serio, LK_CMD_ENABLE_KEYCLICK); - lk->serio->write (lk->serio, volume_to_hw (lk->keyclick_volume)); - lk->serio->write (lk->serio, LK_CMD_ENABLE_CTRCLICK); - lk->serio->write (lk->serio, volume_to_hw (lk->ctrlclick_volume)); - } else { - lk->serio->write (lk->serio, LK_CMD_DISABLE_KEYCLICK); - lk->serio->write (lk->serio, LK_CMD_DISABLE_CTRCLICK); - } + lkkbd_toggle_keyclick(lk, test_bit(SND_CLICK, lk->dev->snd)); /* Sound the bell if needed */ - if (test_bit (SND_BELL, lk->dev->snd)) - lk->serio->write (lk->serio, LK_CMD_SOUND_BELL); + if (test_bit(SND_BELL, lk->dev->snd)) + serio_write(lk->serio, LK_CMD_SOUND_BELL); } /* * lkkbd_connect() probes for a LK keyboard and fills the necessary structures. */ -static int -lkkbd_connect (struct serio *serio, struct serio_driver *drv) +static int lkkbd_connect(struct serio *serio, struct serio_driver *drv) { struct lkkbd *lk; struct input_dev *input_dev; int i; int err; - lk = kzalloc (sizeof (struct lkkbd), GFP_KERNEL); - input_dev = input_allocate_device (); + lk = kzalloc(sizeof(struct lkkbd), GFP_KERNEL); + input_dev = input_allocate_device(); if (!lk || !input_dev) { err = -ENOMEM; goto fail1; @@ -650,14 +633,14 @@ lkkbd_connect (struct serio *serio, struct serio_driver *drv) lk->serio = serio; lk->dev = input_dev; - INIT_WORK (&lk->tq, lkkbd_reinit); + INIT_WORK(&lk->tq, lkkbd_reinit); lk->bell_volume = bell_volume; lk->keyclick_volume = keyclick_volume; lk->ctrlclick_volume = ctrlclick_volume; - memcpy (lk->keycode, lkkbd_keycode, sizeof (lk_keycode_t) * LK_NUM_KEYCODES); + memcpy(lk->keycode, lkkbd_keycode, sizeof(lk->keycode)); - strlcpy (lk->name, "DEC LK keyboard", sizeof(lk->name)); - snprintf (lk->phys, sizeof(lk->phys), "%s/input0", serio->phys); + strlcpy(lk->name, "DEC LK keyboard", sizeof(lk->name)); + snprintf(lk->phys, sizeof(lk->phys), "%s/input0", serio->phys); input_dev->name = lk->name; input_dev->phys = lk->phys; @@ -668,60 +651,61 @@ lkkbd_connect (struct serio *serio, struct serio_driver *drv) input_dev->dev.parent = &serio->dev; input_dev->event = lkkbd_event; - input_set_drvdata (input_dev, lk); + input_set_drvdata(input_dev, lk); - set_bit (EV_KEY, input_dev->evbit); - set_bit (EV_LED, input_dev->evbit); - set_bit (EV_SND, input_dev->evbit); - set_bit (EV_REP, input_dev->evbit); - set_bit (LED_CAPSL, input_dev->ledbit); - set_bit (LED_SLEEP, input_dev->ledbit); - set_bit (LED_COMPOSE, input_dev->ledbit); - set_bit (LED_SCROLLL, input_dev->ledbit); - set_bit (SND_BELL, input_dev->sndbit); - set_bit (SND_CLICK, input_dev->sndbit); + __set_bit(EV_KEY, input_dev->evbit); + __set_bit(EV_LED, input_dev->evbit); + __set_bit(EV_SND, input_dev->evbit); + __set_bit(EV_REP, input_dev->evbit); + __set_bit(LED_CAPSL, input_dev->ledbit); + __set_bit(LED_SLEEP, input_dev->ledbit); + __set_bit(LED_COMPOSE, input_dev->ledbit); + __set_bit(LED_SCROLLL, input_dev->ledbit); + __set_bit(SND_BELL, input_dev->sndbit); + __set_bit(SND_CLICK, input_dev->sndbit); input_dev->keycode = lk->keycode; - input_dev->keycodesize = sizeof (lk_keycode_t); - input_dev->keycodemax = LK_NUM_KEYCODES; + input_dev->keycodesize = sizeof(lk->keycode[0]); + input_dev->keycodemax = ARRAY_SIZE(lk->keycode); + for (i = 0; i < LK_NUM_KEYCODES; i++) - set_bit (lk->keycode[i], input_dev->keybit); + __set_bit(lk->keycode[i], input_dev->keybit); + __clear_bit(KEY_RESERVED, input_dev->keybit); - serio_set_drvdata (serio, lk); + serio_set_drvdata(serio, lk); - err = serio_open (serio, drv); + err = serio_open(serio, drv); if (err) goto fail2; - err = input_register_device (lk->dev); + err = input_register_device(lk->dev); if (err) goto fail3; - lk->serio->write (lk->serio, LK_CMD_POWERCYCLE_RESET); + serio_write(lk->serio, LK_CMD_POWERCYCLE_RESET); return 0; - fail3: serio_close (serio); - fail2: serio_set_drvdata (serio, NULL); - fail1: input_free_device (input_dev); - kfree (lk); + fail3: serio_close(serio); + fail2: serio_set_drvdata(serio, NULL); + fail1: input_free_device(input_dev); + kfree(lk); return err; } /* * lkkbd_disconnect() unregisters and closes behind us. */ -static void -lkkbd_disconnect (struct serio *serio) +static void lkkbd_disconnect(struct serio *serio) { - struct lkkbd *lk = serio_get_drvdata (serio); - - input_get_device (lk->dev); - input_unregister_device (lk->dev); - serio_close (serio); - serio_set_drvdata (serio, NULL); - input_put_device (lk->dev); - kfree (lk); + struct lkkbd *lk = serio_get_drvdata(serio); + + input_get_device(lk->dev); + input_unregister_device(lk->dev); + serio_close(serio); + serio_set_drvdata(serio, NULL); + input_put_device(lk->dev); + kfree(lk); } static struct serio_device_id lkkbd_serio_ids[] = { @@ -750,18 +734,16 @@ static struct serio_driver lkkbd_drv = { /* * The functions for insering/removing us as a module. */ -static int __init -lkkbd_init (void) +static int __init lkkbd_init(void) { return serio_register_driver(&lkkbd_drv); } -static void __exit -lkkbd_exit (void) +static void __exit lkkbd_exit(void) { serio_unregister_driver(&lkkbd_drv); } -module_init (lkkbd_init); -module_exit (lkkbd_exit); +module_init(lkkbd_init); +module_exit(lkkbd_exit); diff --git a/drivers/input/keyboard/matrix_keypad.c b/drivers/input/keyboard/matrix_keypad.c index 541b981ff07..34f4a29d497 100644 --- a/drivers/input/keyboard/matrix_keypad.c +++ b/drivers/input/keyboard/matrix_keypad.c @@ -213,8 +213,9 @@ static void matrix_keypad_stop(struct input_dev *dev) } #ifdef CONFIG_PM -static int matrix_keypad_suspend(struct platform_device *pdev, pm_message_t state) +static int matrix_keypad_suspend(struct device *dev) { + struct platform_device *pdev = to_platform_device(dev); struct matrix_keypad *keypad = platform_get_drvdata(pdev); const struct matrix_keypad_platform_data *pdata = keypad->pdata; int i; @@ -228,8 +229,9 @@ static int matrix_keypad_suspend(struct platform_device *pdev, pm_message_t stat return 0; } -static int matrix_keypad_resume(struct platform_device *pdev) +static int matrix_keypad_resume(struct device *dev) { + struct platform_device *pdev = to_platform_device(dev); struct matrix_keypad *keypad = platform_get_drvdata(pdev); const struct matrix_keypad_platform_data *pdata = keypad->pdata; int i; @@ -242,9 +244,9 @@ static int matrix_keypad_resume(struct platform_device *pdev) return 0; } -#else -#define matrix_keypad_suspend NULL -#define matrix_keypad_resume NULL + +static const SIMPLE_DEV_PM_OPS(matrix_keypad_pm_ops, + matrix_keypad_suspend, matrix_keypad_resume); #endif static int __devinit init_matrix_gpio(struct platform_device *pdev, @@ -319,7 +321,6 @@ static int __devinit matrix_keypad_probe(struct platform_device *pdev) struct input_dev *input_dev; unsigned short *keycodes; unsigned int row_shift; - int i; int err; pdata = pdev->dev.platform_data; @@ -363,18 +364,10 @@ static int __devinit matrix_keypad_probe(struct platform_device *pdev) input_dev->keycode = keycodes; input_dev->keycodesize = sizeof(*keycodes); - input_dev->keycodemax = pdata->num_row_gpios << keypad->row_shift; - - for (i = 0; i < keymap_data->keymap_size; i++) { - unsigned int key = keymap_data->keymap[i]; - unsigned int row = KEY_ROW(key); - unsigned int col = KEY_COL(key); - unsigned short code = KEY_VAL(key); + input_dev->keycodemax = pdata->num_row_gpios << row_shift; - keycodes[MATRIX_SCAN_CODE(row, col, row_shift)] = code; - __set_bit(code, input_dev->keybit); - } - __clear_bit(KEY_RESERVED, input_dev->keybit); + matrix_keypad_build_keymap(keymap_data, row_shift, + input_dev->keycode, input_dev->keybit); input_set_capability(input_dev, EV_MSC, MSC_SCAN); input_set_drvdata(input_dev, keypad); @@ -426,11 +419,12 @@ static int __devexit matrix_keypad_remove(struct platform_device *pdev) static struct platform_driver matrix_keypad_driver = { .probe = matrix_keypad_probe, .remove = __devexit_p(matrix_keypad_remove), - .suspend = matrix_keypad_suspend, - .resume = matrix_keypad_resume, .driver = { .name = "matrix-keypad", .owner = THIS_MODULE, +#ifdef CONFIG_PM + .pm = &matrix_keypad_pm_ops, +#endif }, }; diff --git a/drivers/input/keyboard/max7359_keypad.c b/drivers/input/keyboard/max7359_keypad.c new file mode 100644 index 00000000000..3b5b948eba3 --- /dev/null +++ b/drivers/input/keyboard/max7359_keypad.c @@ -0,0 +1,330 @@ +/* + * max7359_keypad.c - MAX7359 Key Switch Controller Driver + * + * Copyright (C) 2009 Samsung Electronics + * Kim Kyuwon <q1.kim@samsung.com> + * + * Based on pxa27x_keypad.c + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * Datasheet: http://www.maxim-ic.com/quick_view2.cfm/qv_pk/5456 + */ + +#include <linux/module.h> +#include <linux/i2c.h> +#include <linux/interrupt.h> +#include <linux/input.h> +#include <linux/input/matrix_keypad.h> + +#define MAX7359_MAX_KEY_ROWS 8 +#define MAX7359_MAX_KEY_COLS 8 +#define MAX7359_MAX_KEY_NUM (MAX7359_MAX_KEY_ROWS * MAX7359_MAX_KEY_COLS) +#define MAX7359_ROW_SHIFT 3 + +/* + * MAX7359 registers + */ +#define MAX7359_REG_KEYFIFO 0x00 +#define MAX7359_REG_CONFIG 0x01 +#define MAX7359_REG_DEBOUNCE 0x02 +#define MAX7359_REG_INTERRUPT 0x03 +#define MAX7359_REG_PORTS 0x04 +#define MAX7359_REG_KEYREP 0x05 +#define MAX7359_REG_SLEEP 0x06 + +/* + * Configuration register bits + */ +#define MAX7359_CFG_SLEEP (1 << 7) +#define MAX7359_CFG_INTERRUPT (1 << 5) +#define MAX7359_CFG_KEY_RELEASE (1 << 3) +#define MAX7359_CFG_WAKEUP (1 << 1) +#define MAX7359_CFG_TIMEOUT (1 << 0) + +/* + * Autosleep register values (ms) + */ +#define MAX7359_AUTOSLEEP_8192 0x01 +#define MAX7359_AUTOSLEEP_4096 0x02 +#define MAX7359_AUTOSLEEP_2048 0x03 +#define MAX7359_AUTOSLEEP_1024 0x04 +#define MAX7359_AUTOSLEEP_512 0x05 +#define MAX7359_AUTOSLEEP_256 0x06 + +struct max7359_keypad { + /* matrix key code map */ + unsigned short keycodes[MAX7359_MAX_KEY_NUM]; + + struct input_dev *input_dev; + struct i2c_client *client; +}; + +static int max7359_write_reg(struct i2c_client *client, u8 reg, u8 val) +{ + int ret = i2c_smbus_write_byte_data(client, reg, val); + + if (ret < 0) + dev_err(&client->dev, "%s: reg 0x%x, val 0x%x, err %d\n", + __func__, reg, val, ret); + return ret; +} + +static int max7359_read_reg(struct i2c_client *client, int reg) +{ + int ret = i2c_smbus_read_byte_data(client, reg); + + if (ret < 0) + dev_err(&client->dev, "%s: reg 0x%x, err %d\n", + __func__, reg, ret); + return ret; +} + +static void max7359_build_keycode(struct max7359_keypad *keypad, + const struct matrix_keymap_data *keymap_data) +{ + struct input_dev *input_dev = keypad->input_dev; + int i; + + for (i = 0; i < keymap_data->keymap_size; i++) { + unsigned int key = keymap_data->keymap[i]; + unsigned int row = KEY_ROW(key); + unsigned int col = KEY_COL(key); + unsigned int scancode = MATRIX_SCAN_CODE(row, col, + MAX7359_ROW_SHIFT); + unsigned short keycode = KEY_VAL(key); + + keypad->keycodes[scancode] = keycode; + __set_bit(keycode, input_dev->keybit); + } + __clear_bit(KEY_RESERVED, input_dev->keybit); +} + +/* runs in an IRQ thread -- can (and will!) sleep */ +static irqreturn_t max7359_interrupt(int irq, void *dev_id) +{ + struct max7359_keypad *keypad = dev_id; + struct input_dev *input_dev = keypad->input_dev; + int val, row, col, release, code; + + val = max7359_read_reg(keypad->client, MAX7359_REG_KEYFIFO); + row = val & 0x7; + col = (val >> 3) & 0x7; + release = val & 0x40; + + code = MATRIX_SCAN_CODE(row, col, MAX7359_ROW_SHIFT); + + dev_dbg(&keypad->client->dev, + "key[%d:%d] %s\n", row, col, release ? "release" : "press"); + + input_event(input_dev, EV_MSC, MSC_SCAN, code); + input_report_key(input_dev, keypad->keycodes[code], !release); + input_sync(input_dev); + + return IRQ_HANDLED; +} + +/* + * Let MAX7359 fall into a deep sleep: + * If no keys are pressed, enter sleep mode for 8192 ms. And if any + * key is pressed, the MAX7359 returns to normal operating mode. + */ +static inline void max7359_fall_deepsleep(struct i2c_client *client) +{ + max7359_write_reg(client, MAX7359_REG_SLEEP, MAX7359_AUTOSLEEP_8192); +} + +/* + * Let MAX7359 take a catnap: + * Autosleep just for 256 ms. + */ +static inline void max7359_take_catnap(struct i2c_client *client) +{ + max7359_write_reg(client, MAX7359_REG_SLEEP, MAX7359_AUTOSLEEP_256); +} + +static int max7359_open(struct input_dev *dev) +{ + struct max7359_keypad *keypad = input_get_drvdata(dev); + + max7359_take_catnap(keypad->client); + + return 0; +} + +static void max7359_close(struct input_dev *dev) +{ + struct max7359_keypad *keypad = input_get_drvdata(dev); + + max7359_fall_deepsleep(keypad->client); +} + +static void max7359_initialize(struct i2c_client *client) +{ + max7359_write_reg(client, MAX7359_REG_CONFIG, + MAX7359_CFG_INTERRUPT | /* Irq clears after host read */ + MAX7359_CFG_KEY_RELEASE | /* Key release enable */ + MAX7359_CFG_WAKEUP); /* Key press wakeup enable */ + + /* Full key-scan functionality */ + max7359_write_reg(client, MAX7359_REG_DEBOUNCE, 0x1F); + + /* nINT asserts every debounce cycles */ + max7359_write_reg(client, MAX7359_REG_INTERRUPT, 0x01); + + max7359_fall_deepsleep(client); +} + +static int __devinit max7359_probe(struct i2c_client *client, + const struct i2c_device_id *id) +{ + const struct matrix_keymap_data *keymap_data = client->dev.platform_data; + struct max7359_keypad *keypad; + struct input_dev *input_dev; + int ret; + int error; + + if (!client->irq) { + dev_err(&client->dev, "The irq number should not be zero\n"); + return -EINVAL; + } + + /* Detect MAX7359: The initial Keys FIFO value is '0x3F' */ + ret = max7359_read_reg(client, MAX7359_REG_KEYFIFO); + if (ret < 0) { + dev_err(&client->dev, "failed to detect device\n"); + return -ENODEV; + } + + dev_dbg(&client->dev, "keys FIFO is 0x%02x\n", ret); + + keypad = kzalloc(sizeof(struct max7359_keypad), GFP_KERNEL); + input_dev = input_allocate_device(); + if (!keypad || !input_dev) { + dev_err(&client->dev, "failed to allocate memory\n"); + error = -ENOMEM; + goto failed_free_mem; + } + + keypad->client = client; + keypad->input_dev = input_dev; + + input_dev->name = client->name; + input_dev->id.bustype = BUS_I2C; + input_dev->open = max7359_open; + input_dev->close = max7359_close; + input_dev->dev.parent = &client->dev; + + input_dev->evbit[0] = BIT_MASK(EV_KEY) | BIT_MASK(EV_REP); + input_dev->keycodesize = sizeof(keypad->keycodes[0]); + input_dev->keycodemax = ARRAY_SIZE(keypad->keycodes); + input_dev->keycode = keypad->keycodes; + + input_set_capability(input_dev, EV_MSC, MSC_SCAN); + input_set_drvdata(input_dev, keypad); + + max7359_build_keycode(keypad, keymap_data); + + error = request_threaded_irq(client->irq, NULL, max7359_interrupt, + IRQF_TRIGGER_LOW | IRQF_ONESHOT, + client->name, keypad); + if (error) { + dev_err(&client->dev, "failed to register interrupt\n"); + goto failed_free_mem; + } + + /* Register the input device */ + error = input_register_device(input_dev); + if (error) { + dev_err(&client->dev, "failed to register input device\n"); + goto failed_free_irq; + } + + /* Initialize MAX7359 */ + max7359_initialize(client); + + i2c_set_clientdata(client, keypad); + device_init_wakeup(&client->dev, 1); + + return 0; + +failed_free_irq: + free_irq(client->irq, keypad); +failed_free_mem: + input_free_device(input_dev); + kfree(keypad); + return error; +} + +static int __devexit max7359_remove(struct i2c_client *client) +{ + struct max7359_keypad *keypad = i2c_get_clientdata(client); + + free_irq(client->irq, keypad); + input_unregister_device(keypad->input_dev); + i2c_set_clientdata(client, NULL); + kfree(keypad); + + return 0; +} + +#ifdef CONFIG_PM +static int max7359_suspend(struct i2c_client *client, pm_message_t mesg) +{ + max7359_fall_deepsleep(client); + + if (device_may_wakeup(&client->dev)) + enable_irq_wake(client->irq); + + return 0; +} + +static int max7359_resume(struct i2c_client *client) +{ + if (device_may_wakeup(&client->dev)) + disable_irq_wake(client->irq); + + /* Restore the default setting */ + max7359_take_catnap(client); + + return 0; +} +#else +#define max7359_suspend NULL +#define max7359_resume NULL +#endif + +static const struct i2c_device_id max7359_ids[] = { + { "max7359", 0 }, + { } +}; +MODULE_DEVICE_TABLE(i2c, max7359_ids); + +static struct i2c_driver max7359_i2c_driver = { + .driver = { + .name = "max7359", + }, + .probe = max7359_probe, + .remove = __devexit_p(max7359_remove), + .suspend = max7359_suspend, + .resume = max7359_resume, + .id_table = max7359_ids, +}; + +static int __init max7359_init(void) +{ + return i2c_add_driver(&max7359_i2c_driver); +} +module_init(max7359_init); + +static void __exit max7359_exit(void) +{ + i2c_del_driver(&max7359_i2c_driver); +} +module_exit(max7359_exit); + +MODULE_AUTHOR("Kim Kyuwon <q1.kim@samsung.com>"); +MODULE_DESCRIPTION("MAX7359 Key Switch Controller Driver"); +MODULE_LICENSE("GPL v2"); diff --git a/drivers/input/keyboard/omap-keypad.c b/drivers/input/keyboard/omap-keypad.c index 87ec7b18ac6..1a494d50543 100644 --- a/drivers/input/keyboard/omap-keypad.c +++ b/drivers/input/keyboard/omap-keypad.c @@ -35,12 +35,12 @@ #include <linux/mutex.h> #include <linux/errno.h> #include <mach/gpio.h> -#include <mach/keypad.h> -#include <mach/menelaus.h> +#include <plat/keypad.h> +#include <plat/menelaus.h> #include <asm/irq.h> #include <mach/hardware.h> #include <asm/io.h> -#include <mach/mux.h> +#include <plat/mux.h> #undef NEW_BOARD_LEARNING_MODE @@ -116,7 +116,7 @@ static irqreturn_t omap_kp_interrupt(int irq, void *dev_id) } } else /* disable keyboard interrupt and schedule for handling */ - omap_writew(1, OMAP_MPUIO_BASE + OMAP_MPUIO_KBD_MASKIT); + omap_writew(1, OMAP1_MPUIO_BASE + OMAP_MPUIO_KBD_MASKIT); tasklet_schedule(&kp_tasklet); @@ -143,20 +143,20 @@ static void omap_kp_scan_keypad(struct omap_kp *omap_kp, unsigned char *state) } else { /* disable keyboard interrupt and schedule for handling */ - omap_writew(1, OMAP_MPUIO_BASE + OMAP_MPUIO_KBD_MASKIT); + omap_writew(1, OMAP1_MPUIO_BASE + OMAP_MPUIO_KBD_MASKIT); /* read the keypad status */ - omap_writew(0xff, OMAP_MPUIO_BASE + OMAP_MPUIO_KBC); + omap_writew(0xff, OMAP1_MPUIO_BASE + OMAP_MPUIO_KBC); for (col = 0; col < omap_kp->cols; col++) { omap_writew(~(1 << col) & 0xff, - OMAP_MPUIO_BASE + OMAP_MPUIO_KBC); + OMAP1_MPUIO_BASE + OMAP_MPUIO_KBC); udelay(omap_kp->delay); - state[col] = ~omap_readw(OMAP_MPUIO_BASE + + state[col] = ~omap_readw(OMAP1_MPUIO_BASE + OMAP_MPUIO_KBR_LATCH) & 0xff; } - omap_writew(0x00, OMAP_MPUIO_BASE + OMAP_MPUIO_KBC); + omap_writew(0x00, OMAP1_MPUIO_BASE + OMAP_MPUIO_KBC); udelay(2); } } @@ -234,7 +234,7 @@ static void omap_kp_tasklet(unsigned long data) for (i = 0; i < omap_kp_data->rows; i++) enable_irq(gpio_to_irq(row_gpios[i])); } else { - omap_writew(0, OMAP_MPUIO_BASE + OMAP_MPUIO_KBD_MASKIT); + omap_writew(0, OMAP1_MPUIO_BASE + OMAP_MPUIO_KBD_MASKIT); kp_cur_group = -1; } } @@ -317,7 +317,7 @@ static int __devinit omap_kp_probe(struct platform_device *pdev) /* Disable the interrupt for the MPUIO keyboard */ if (!cpu_is_omap24xx()) - omap_writew(1, OMAP_MPUIO_BASE + OMAP_MPUIO_KBD_MASKIT); + omap_writew(1, OMAP1_MPUIO_BASE + OMAP_MPUIO_KBD_MASKIT); keymap = pdata->keymap; @@ -391,7 +391,7 @@ static int __devinit omap_kp_probe(struct platform_device *pdev) } if (pdata->dbounce) - omap_writew(0xff, OMAP_MPUIO_BASE + OMAP_MPUIO_GPIO_DEBOUNCING); + omap_writew(0xff, OMAP1_MPUIO_BASE + OMAP_MPUIO_GPIO_DEBOUNCING); /* scan current status and enable interrupt */ omap_kp_scan_keypad(omap_kp, keypad_state); @@ -402,7 +402,7 @@ static int __devinit omap_kp_probe(struct platform_device *pdev) "omap-keypad", omap_kp) < 0) goto err4; } - omap_writew(0, OMAP_MPUIO_BASE + OMAP_MPUIO_KBD_MASKIT); + omap_writew(0, OMAP1_MPUIO_BASE + OMAP_MPUIO_KBD_MASKIT); } else { for (irq_idx = 0; irq_idx < omap_kp->rows; irq_idx++) { if (request_irq(gpio_to_irq(row_gpios[irq_idx]), @@ -449,7 +449,7 @@ static int __devexit omap_kp_remove(struct platform_device *pdev) free_irq(gpio_to_irq(row_gpios[i]), 0); } } else { - omap_writew(1, OMAP_MPUIO_BASE + OMAP_MPUIO_KBD_MASKIT); + omap_writew(1, OMAP1_MPUIO_BASE + OMAP_MPUIO_KBD_MASKIT); free_irq(omap_kp->irq, 0); } diff --git a/drivers/input/keyboard/opencores-kbd.c b/drivers/input/keyboard/opencores-kbd.c new file mode 100644 index 00000000000..78cccddbf55 --- /dev/null +++ b/drivers/input/keyboard/opencores-kbd.c @@ -0,0 +1,180 @@ +/* + * OpenCores Keyboard Controller Driver + * http://www.opencores.org/project,keyboardcontroller + * + * Copyright 2007-2009 HV Sistemas S.L. + * + * Licensed under the GPL-2 or later. + */ + +#include <linux/input.h> +#include <linux/interrupt.h> +#include <linux/io.h> +#include <linux/ioport.h> +#include <linux/kernel.h> +#include <linux/module.h> +#include <linux/platform_device.h> + +struct opencores_kbd { + struct input_dev *input; + struct resource *addr_res; + void __iomem *addr; + int irq; + unsigned short keycodes[128]; +}; + +static irqreturn_t opencores_kbd_isr(int irq, void *dev_id) +{ + struct opencores_kbd *opencores_kbd = dev_id; + struct input_dev *input = opencores_kbd->input; + unsigned char c; + + c = readb(opencores_kbd->addr); + input_report_key(input, c & 0x7f, c & 0x80 ? 0 : 1); + input_sync(input); + + return IRQ_HANDLED; +} + +static int __devinit opencores_kbd_probe(struct platform_device *pdev) +{ + struct input_dev *input; + struct opencores_kbd *opencores_kbd; + struct resource *res; + int irq, i, error; + + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + if (!res) { + dev_err(&pdev->dev, "missing board memory resource\n"); + return -EINVAL; + } + + irq = platform_get_irq(pdev, 0); + if (irq < 0) { + dev_err(&pdev->dev, "missing board IRQ resource\n"); + return -EINVAL; + } + + opencores_kbd = kzalloc(sizeof(*opencores_kbd), GFP_KERNEL); + input = input_allocate_device(); + if (!opencores_kbd || !input) { + dev_err(&pdev->dev, "failed to allocate device structures\n"); + error = -ENOMEM; + goto err_free_mem; + } + + opencores_kbd->addr_res = res; + res = request_mem_region(res->start, resource_size(res), pdev->name); + if (!res) { + dev_err(&pdev->dev, "failed to request I/O memory\n"); + error = -EBUSY; + goto err_free_mem; + } + + opencores_kbd->addr = ioremap(res->start, resource_size(res)); + if (!opencores_kbd->addr) { + dev_err(&pdev->dev, "failed to remap I/O memory\n"); + error = -ENXIO; + goto err_rel_mem; + } + + opencores_kbd->input = input; + opencores_kbd->irq = irq; + + input->name = pdev->name; + input->phys = "opencores-kbd/input0"; + input->dev.parent = &pdev->dev; + + input_set_drvdata(input, opencores_kbd); + + input->id.bustype = BUS_HOST; + input->id.vendor = 0x0001; + input->id.product = 0x0001; + input->id.version = 0x0100; + + input->keycode = opencores_kbd->keycodes; + input->keycodesize = sizeof(opencores_kbd->keycodes[0]); + input->keycodemax = ARRAY_SIZE(opencores_kbd->keycodes); + + __set_bit(EV_KEY, input->evbit); + + for (i = 0; i < ARRAY_SIZE(opencores_kbd->keycodes); i++) { + /* + * OpenCores controller happens to have scancodes match + * our KEY_* definitions. + */ + opencores_kbd->keycodes[i] = i; + __set_bit(opencores_kbd->keycodes[i], input->keybit); + } + __clear_bit(KEY_RESERVED, input->keybit); + + error = request_irq(irq, &opencores_kbd_isr, + IRQF_TRIGGER_RISING, pdev->name, opencores_kbd); + if (error) { + dev_err(&pdev->dev, "unable to claim irq %d\n", irq); + goto err_unmap_mem; + } + + error = input_register_device(input); + if (error) { + dev_err(&pdev->dev, "unable to register input device\n"); + goto err_free_irq; + } + + platform_set_drvdata(pdev, opencores_kbd); + + return 0; + + err_free_irq: + free_irq(irq, opencores_kbd); + err_unmap_mem: + iounmap(opencores_kbd->addr); + err_rel_mem: + release_mem_region(res->start, resource_size(res)); + err_free_mem: + input_free_device(input); + kfree(opencores_kbd); + + return error; +} + +static int __devexit opencores_kbd_remove(struct platform_device *pdev) +{ + struct opencores_kbd *opencores_kbd = platform_get_drvdata(pdev); + + free_irq(opencores_kbd->irq, opencores_kbd); + + iounmap(opencores_kbd->addr); + release_mem_region(opencores_kbd->addr_res->start, + resource_size(opencores_kbd->addr_res)); + input_unregister_device(opencores_kbd->input); + kfree(opencores_kbd); + + platform_set_drvdata(pdev, NULL); + + return 0; +} + +static struct platform_driver opencores_kbd_device_driver = { + .probe = opencores_kbd_probe, + .remove = __devexit_p(opencores_kbd_remove), + .driver = { + .name = "opencores-kbd", + }, +}; + +static int __init opencores_kbd_init(void) +{ + return platform_driver_register(&opencores_kbd_device_driver); +} +module_init(opencores_kbd_init); + +static void __exit opencores_kbd_exit(void) +{ + platform_driver_unregister(&opencores_kbd_device_driver); +} +module_exit(opencores_kbd_exit); + +MODULE_LICENSE("GPL"); +MODULE_AUTHOR("Javier Herrero <jherrero@hvsistemas.es>"); +MODULE_DESCRIPTION("Keyboard driver for OpenCores Keyboard Controller"); diff --git a/drivers/input/keyboard/pxa27x_keypad.c b/drivers/input/keyboard/pxa27x_keypad.c index 0d2fc64a5e1..79cd3e9fdf2 100644 --- a/drivers/input/keyboard/pxa27x_keypad.c +++ b/drivers/input/keyboard/pxa27x_keypad.c @@ -8,7 +8,7 @@ * * Based on a previous implementations by Kevin O'Connor * <kevin_at_koconnor.net> and Alex Osborne <bobofdoom@gmail.com> and - * on some suggestions by Nicolas Pitre <nico@cam.org>. + * on some suggestions by Nicolas Pitre <nico@fluxnic.net>. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 as @@ -25,13 +25,13 @@ #include <linux/platform_device.h> #include <linux/clk.h> #include <linux/err.h> +#include <linux/input/matrix_keypad.h> #include <asm/mach/arch.h> #include <asm/mach/map.h> #include <mach/hardware.h> #include <mach/pxa27x_keypad.h> - /* * Keypad Controller registers */ @@ -95,7 +95,8 @@ #define keypad_readl(off) __raw_readl(keypad->mmio_base + (off)) #define keypad_writel(off, v) __raw_writel((v), keypad->mmio_base + (off)) -#define MAX_MATRIX_KEY_NUM (8 * 8) +#define MAX_MATRIX_KEY_NUM (MAX_MATRIX_KEY_ROWS * MAX_MATRIX_KEY_COLS) +#define MAX_KEYPAD_KEYS (MAX_MATRIX_KEY_NUM + MAX_DIRECT_KEY_NUM) struct pxa27x_keypad { struct pxa27x_keypad_platform_data *pdata; @@ -106,73 +107,82 @@ struct pxa27x_keypad { int irq; - /* matrix key code map */ - unsigned int matrix_keycodes[MAX_MATRIX_KEY_NUM]; + unsigned short keycodes[MAX_KEYPAD_KEYS]; + int rotary_rel_code[2]; /* state row bits of each column scan */ uint32_t matrix_key_state[MAX_MATRIX_KEY_COLS]; uint32_t direct_key_state; unsigned int direct_key_mask; - - int rotary_rel_code[2]; - int rotary_up_key[2]; - int rotary_down_key[2]; }; static void pxa27x_keypad_build_keycode(struct pxa27x_keypad *keypad) { struct pxa27x_keypad_platform_data *pdata = keypad->pdata; struct input_dev *input_dev = keypad->input_dev; - unsigned int *key; + unsigned short keycode; int i; - key = &pdata->matrix_key_map[0]; - for (i = 0; i < pdata->matrix_key_map_size; i++, key++) { - int row = ((*key) >> 28) & 0xf; - int col = ((*key) >> 24) & 0xf; - int code = (*key) & 0xffffff; + for (i = 0; i < pdata->matrix_key_map_size; i++) { + unsigned int key = pdata->matrix_key_map[i]; + unsigned int row = KEY_ROW(key); + unsigned int col = KEY_COL(key); + unsigned int scancode = MATRIX_SCAN_CODE(row, col, + MATRIX_ROW_SHIFT); - keypad->matrix_keycodes[(row << 3) + col] = code; - set_bit(code, input_dev->keybit); + keycode = KEY_VAL(key); + keypad->keycodes[scancode] = keycode; + __set_bit(keycode, input_dev->keybit); } - for (i = 0; i < pdata->direct_key_num; i++) - set_bit(pdata->direct_key_map[i], input_dev->keybit); - - keypad->rotary_up_key[0] = pdata->rotary0_up_key; - keypad->rotary_up_key[1] = pdata->rotary1_up_key; - keypad->rotary_down_key[0] = pdata->rotary0_down_key; - keypad->rotary_down_key[1] = pdata->rotary1_down_key; - keypad->rotary_rel_code[0] = pdata->rotary0_rel_code; - keypad->rotary_rel_code[1] = pdata->rotary1_rel_code; + for (i = 0; i < pdata->direct_key_num; i++) { + keycode = pdata->direct_key_map[i]; + keypad->keycodes[MAX_MATRIX_KEY_NUM + i] = keycode; + __set_bit(keycode, input_dev->keybit); + } if (pdata->enable_rotary0) { if (pdata->rotary0_up_key && pdata->rotary0_down_key) { - set_bit(pdata->rotary0_up_key, input_dev->keybit); - set_bit(pdata->rotary0_down_key, input_dev->keybit); - } else - set_bit(pdata->rotary0_rel_code, input_dev->relbit); + keycode = pdata->rotary0_up_key; + keypad->keycodes[MAX_MATRIX_KEY_NUM + 0] = keycode; + __set_bit(keycode, input_dev->keybit); + + keycode = pdata->rotary0_down_key; + keypad->keycodes[MAX_MATRIX_KEY_NUM + 1] = keycode; + __set_bit(keycode, input_dev->keybit); + + keypad->rotary_rel_code[0] = -1; + } else { + keypad->rotary_rel_code[0] = pdata->rotary0_rel_code; + __set_bit(pdata->rotary0_rel_code, input_dev->relbit); + } } if (pdata->enable_rotary1) { if (pdata->rotary1_up_key && pdata->rotary1_down_key) { - set_bit(pdata->rotary1_up_key, input_dev->keybit); - set_bit(pdata->rotary1_down_key, input_dev->keybit); - } else - set_bit(pdata->rotary1_rel_code, input_dev->relbit); + keycode = pdata->rotary1_up_key; + keypad->keycodes[MAX_MATRIX_KEY_NUM + 2] = keycode; + __set_bit(keycode, input_dev->keybit); + + keycode = pdata->rotary1_down_key; + keypad->keycodes[MAX_MATRIX_KEY_NUM + 3] = keycode; + __set_bit(keycode, input_dev->keybit); + + keypad->rotary_rel_code[1] = -1; + } else { + keypad->rotary_rel_code[1] = pdata->rotary1_rel_code; + __set_bit(pdata->rotary1_rel_code, input_dev->relbit); + } } -} -static inline unsigned int lookup_matrix_keycode( - struct pxa27x_keypad *keypad, int row, int col) -{ - return keypad->matrix_keycodes[(row << 3) + col]; + __clear_bit(KEY_RESERVED, input_dev->keybit); } static void pxa27x_keypad_scan_matrix(struct pxa27x_keypad *keypad) { struct pxa27x_keypad_platform_data *pdata = keypad->pdata; + struct input_dev *input_dev = keypad->input_dev; int row, col, num_keys_pressed = 0; uint32_t new_state[MAX_MATRIX_KEY_COLS]; uint32_t kpas = keypad_readl(KPAS); @@ -215,6 +225,7 @@ static void pxa27x_keypad_scan_matrix(struct pxa27x_keypad *keypad) scan: for (col = 0; col < pdata->matrix_key_cols; col++) { uint32_t bits_changed; + int code; bits_changed = keypad->matrix_key_state[col] ^ new_state[col]; if (bits_changed == 0) @@ -224,12 +235,13 @@ scan: if ((bits_changed & (1 << row)) == 0) continue; - input_report_key(keypad->input_dev, - lookup_matrix_keycode(keypad, row, col), - new_state[col] & (1 << row)); + code = MATRIX_SCAN_CODE(row, col, MATRIX_ROW_SHIFT); + input_event(input_dev, EV_MSC, MSC_SCAN, code); + input_report_key(input_dev, keypad->keycodes[code], + new_state[col] & (1 << row)); } } - input_sync(keypad->input_dev); + input_sync(input_dev); memcpy(keypad->matrix_key_state, new_state, sizeof(new_state)); } @@ -252,13 +264,15 @@ static void report_rotary_event(struct pxa27x_keypad *keypad, int r, int delta) if (delta == 0) return; - if (keypad->rotary_up_key[r] && keypad->rotary_down_key[r]) { - int keycode = (delta > 0) ? keypad->rotary_up_key[r] : - keypad->rotary_down_key[r]; + if (keypad->rotary_rel_code[r] == -1) { + int code = MAX_MATRIX_KEY_NUM + 2 * r + (delta > 0 ? 0 : 1); + unsigned char keycode = keypad->keycodes[code]; /* simulate a press-n-release */ + input_event(dev, EV_MSC, MSC_SCAN, code); input_report_key(dev, keycode, 1); input_sync(dev); + input_event(dev, EV_MSC, MSC_SCAN, code); input_report_key(dev, keycode, 0); input_sync(dev); } else { @@ -286,6 +300,7 @@ static void pxa27x_keypad_scan_rotary(struct pxa27x_keypad *keypad) static void pxa27x_keypad_scan_direct(struct pxa27x_keypad *keypad) { struct pxa27x_keypad_platform_data *pdata = keypad->pdata; + struct input_dev *input_dev = keypad->input_dev; unsigned int new_state; uint32_t kpdk, bits_changed; int i; @@ -295,9 +310,6 @@ static void pxa27x_keypad_scan_direct(struct pxa27x_keypad *keypad) if (pdata->enable_rotary0 || pdata->enable_rotary1) pxa27x_keypad_scan_rotary(keypad); - if (pdata->direct_key_map == NULL) - return; - new_state = KPDK_DK(kpdk) & keypad->direct_key_mask; bits_changed = keypad->direct_key_state ^ new_state; @@ -305,12 +317,15 @@ static void pxa27x_keypad_scan_direct(struct pxa27x_keypad *keypad) return; for (i = 0; i < pdata->direct_key_num; i++) { - if (bits_changed & (1 << i)) - input_report_key(keypad->input_dev, - pdata->direct_key_map[i], - (new_state & (1 << i))); + if (bits_changed & (1 << i)) { + int code = MAX_MATRIX_KEY_NUM + i; + + input_event(input_dev, EV_MSC, MSC_SCAN, code); + input_report_key(input_dev, keypad->keycodes[code], + new_state & (1 << i)); + } } - input_sync(keypad->input_dev); + input_sync(input_dev); keypad->direct_key_state = new_state; } @@ -388,8 +403,9 @@ static void pxa27x_keypad_close(struct input_dev *dev) } #ifdef CONFIG_PM -static int pxa27x_keypad_suspend(struct platform_device *pdev, pm_message_t state) +static int pxa27x_keypad_suspend(struct device *dev) { + struct platform_device *pdev = to_platform_device(dev); struct pxa27x_keypad *keypad = platform_get_drvdata(pdev); clk_disable(keypad->clk); @@ -400,8 +416,9 @@ static int pxa27x_keypad_suspend(struct platform_device *pdev, pm_message_t stat return 0; } -static int pxa27x_keypad_resume(struct platform_device *pdev) +static int pxa27x_keypad_resume(struct device *dev) { + struct platform_device *pdev = to_platform_device(dev); struct pxa27x_keypad *keypad = platform_get_drvdata(pdev); struct input_dev *input_dev = keypad->input_dev; @@ -420,55 +437,58 @@ static int pxa27x_keypad_resume(struct platform_device *pdev) return 0; } -#else -#define pxa27x_keypad_suspend NULL -#define pxa27x_keypad_resume NULL -#endif -#define res_size(res) ((res)->end - (res)->start + 1) +static const struct dev_pm_ops pxa27x_keypad_pm_ops = { + .suspend = pxa27x_keypad_suspend, + .resume = pxa27x_keypad_resume, +}; +#endif static int __devinit pxa27x_keypad_probe(struct platform_device *pdev) { + struct pxa27x_keypad_platform_data *pdata = pdev->dev.platform_data; struct pxa27x_keypad *keypad; struct input_dev *input_dev; struct resource *res; int irq, error; - keypad = kzalloc(sizeof(struct pxa27x_keypad), GFP_KERNEL); - if (keypad == NULL) { - dev_err(&pdev->dev, "failed to allocate driver data\n"); - return -ENOMEM; - } - - keypad->pdata = pdev->dev.platform_data; - if (keypad->pdata == NULL) { + if (pdata == NULL) { dev_err(&pdev->dev, "no platform data defined\n"); - error = -EINVAL; - goto failed_free; + return -EINVAL; } irq = platform_get_irq(pdev, 0); if (irq < 0) { dev_err(&pdev->dev, "failed to get keypad irq\n"); - error = -ENXIO; - goto failed_free; + return -ENXIO; } res = platform_get_resource(pdev, IORESOURCE_MEM, 0); if (res == NULL) { dev_err(&pdev->dev, "failed to get I/O memory\n"); - error = -ENXIO; + return -ENXIO; + } + + keypad = kzalloc(sizeof(struct pxa27x_keypad), GFP_KERNEL); + input_dev = input_allocate_device(); + if (!keypad || !input_dev) { + dev_err(&pdev->dev, "failed to allocate memory\n"); + error = -ENOMEM; goto failed_free; } - res = request_mem_region(res->start, res_size(res), pdev->name); + keypad->pdata = pdata; + keypad->input_dev = input_dev; + keypad->irq = irq; + + res = request_mem_region(res->start, resource_size(res), pdev->name); if (res == NULL) { dev_err(&pdev->dev, "failed to request I/O memory\n"); error = -EBUSY; goto failed_free; } - keypad->mmio_base = ioremap(res->start, res_size(res)); + keypad->mmio_base = ioremap(res->start, resource_size(res)); if (keypad->mmio_base == NULL) { dev_err(&pdev->dev, "failed to remap I/O memory\n"); error = -ENXIO; @@ -482,43 +502,35 @@ static int __devinit pxa27x_keypad_probe(struct platform_device *pdev) goto failed_free_io; } - /* Create and register the input driver. */ - input_dev = input_allocate_device(); - if (!input_dev) { - dev_err(&pdev->dev, "failed to allocate input device\n"); - error = -ENOMEM; - goto failed_put_clk; - } - input_dev->name = pdev->name; input_dev->id.bustype = BUS_HOST; input_dev->open = pxa27x_keypad_open; input_dev->close = pxa27x_keypad_close; input_dev->dev.parent = &pdev->dev; - keypad->input_dev = input_dev; + input_dev->keycode = keypad->keycodes; + input_dev->keycodesize = sizeof(keypad->keycodes[0]); + input_dev->keycodemax = ARRAY_SIZE(keypad->keycodes); + input_set_drvdata(input_dev, keypad); input_dev->evbit[0] = BIT_MASK(EV_KEY) | BIT_MASK(EV_REP); - if ((keypad->pdata->enable_rotary0 && - keypad->pdata->rotary0_rel_code) || - (keypad->pdata->enable_rotary1 && - keypad->pdata->rotary1_rel_code)) { - input_dev->evbit[0] |= BIT_MASK(EV_REL); - } + input_set_capability(input_dev, EV_MSC, MSC_SCAN); pxa27x_keypad_build_keycode(keypad); - platform_set_drvdata(pdev, keypad); + + if ((pdata->enable_rotary0 && keypad->rotary_rel_code[0] != -1) || + (pdata->enable_rotary1 && keypad->rotary_rel_code[1] != -1)) { + input_dev->evbit[0] |= BIT_MASK(EV_REL); + } error = request_irq(irq, pxa27x_keypad_irq_handler, IRQF_DISABLED, pdev->name, keypad); if (error) { dev_err(&pdev->dev, "failed to request IRQ\n"); - goto failed_free_dev; + goto failed_put_clk; } - keypad->irq = irq; - /* Register the input device */ error = input_register_device(input_dev); if (error) { @@ -526,22 +538,21 @@ static int __devinit pxa27x_keypad_probe(struct platform_device *pdev) goto failed_free_irq; } + platform_set_drvdata(pdev, keypad); device_init_wakeup(&pdev->dev, 1); return 0; failed_free_irq: free_irq(irq, pdev); - platform_set_drvdata(pdev, NULL); -failed_free_dev: - input_free_device(input_dev); failed_put_clk: clk_put(keypad->clk); failed_free_io: iounmap(keypad->mmio_base); failed_free_mem: - release_mem_region(res->start, res_size(res)); + release_mem_region(res->start, resource_size(res)); failed_free: + input_free_device(input_dev); kfree(keypad); return error; } @@ -552,8 +563,6 @@ static int __devexit pxa27x_keypad_remove(struct platform_device *pdev) struct resource *res; free_irq(keypad->irq, pdev); - - clk_disable(keypad->clk); clk_put(keypad->clk); input_unregister_device(keypad->input_dev); @@ -562,10 +571,11 @@ static int __devexit pxa27x_keypad_remove(struct platform_device *pdev) iounmap(keypad->mmio_base); res = platform_get_resource(pdev, IORESOURCE_MEM, 0); - release_mem_region(res->start, res_size(res)); + release_mem_region(res->start, resource_size(res)); platform_set_drvdata(pdev, NULL); kfree(keypad); + return 0; } @@ -575,11 +585,12 @@ MODULE_ALIAS("platform:pxa27x-keypad"); static struct platform_driver pxa27x_keypad_driver = { .probe = pxa27x_keypad_probe, .remove = __devexit_p(pxa27x_keypad_remove), - .suspend = pxa27x_keypad_suspend, - .resume = pxa27x_keypad_resume, .driver = { .name = "pxa27x-keypad", .owner = THIS_MODULE, +#ifdef CONFIG_PM + .pm = &pxa27x_keypad_pm_ops, +#endif }, }; diff --git a/drivers/input/keyboard/qt2160.c b/drivers/input/keyboard/qt2160.c new file mode 100644 index 00000000000..191cc51d6cf --- /dev/null +++ b/drivers/input/keyboard/qt2160.c @@ -0,0 +1,397 @@ +/* + * qt2160.c - Atmel AT42QT2160 Touch Sense Controller + * + * Copyright (C) 2009 Raphael Derosso Pereira <raphaelpereira@gmail.com> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +#include <linux/kernel.h> +#include <linux/init.h> +#include <linux/module.h> +#include <linux/slab.h> +#include <linux/jiffies.h> +#include <linux/i2c.h> +#include <linux/irq.h> +#include <linux/interrupt.h> +#include <linux/input.h> + +#define QT2160_VALID_CHIPID 0x11 + +#define QT2160_CMD_CHIPID 0 +#define QT2160_CMD_CODEVER 1 +#define QT2160_CMD_GSTAT 2 +#define QT2160_CMD_KEYS3 3 +#define QT2160_CMD_KEYS4 4 +#define QT2160_CMD_SLIDE 5 +#define QT2160_CMD_GPIOS 6 +#define QT2160_CMD_SUBVER 7 +#define QT2160_CMD_CALIBRATE 10 + +#define QT2160_CYCLE_INTERVAL (2*HZ) + +static unsigned char qt2160_key2code[] = { + KEY_0, KEY_1, KEY_2, KEY_3, + KEY_4, KEY_5, KEY_6, KEY_7, + KEY_8, KEY_9, KEY_A, KEY_B, + KEY_C, KEY_D, KEY_E, KEY_F, +}; + +struct qt2160_data { + struct i2c_client *client; + struct input_dev *input; + struct delayed_work dwork; + spinlock_t lock; /* Protects canceling/rescheduling of dwork */ + unsigned short keycodes[ARRAY_SIZE(qt2160_key2code)]; + u16 key_matrix; +}; + +static int qt2160_read_block(struct i2c_client *client, + u8 inireg, u8 *buffer, unsigned int count) +{ + int error, idx = 0; + + /* + * Can't use SMBus block data read. Check for I2C functionality to speed + * things up whenever possible. Otherwise we will be forced to read + * sequentially. + */ + if (i2c_check_functionality(client->adapter, I2C_FUNC_I2C)) { + + error = i2c_smbus_write_byte(client, inireg + idx); + if (error) { + dev_err(&client->dev, + "couldn't send request. Returned %d\n", error); + return error; + } + + error = i2c_master_recv(client, buffer, count); + if (error != count) { + dev_err(&client->dev, + "couldn't read registers. Returned %d bytes\n", error); + return error; + } + } else { + + while (count--) { + int data; + + error = i2c_smbus_write_byte(client, inireg + idx); + if (error) { + dev_err(&client->dev, + "couldn't send request. Returned %d\n", error); + return error; + } + + data = i2c_smbus_read_byte(client); + if (data < 0) { + dev_err(&client->dev, + "couldn't read register. Returned %d\n", data); + return data; + } + + buffer[idx++] = data; + } + } + + return 0; +} + +static int qt2160_get_key_matrix(struct qt2160_data *qt2160) +{ + struct i2c_client *client = qt2160->client; + struct input_dev *input = qt2160->input; + u8 regs[6]; + u16 old_matrix, new_matrix; + int ret, i, mask; + + dev_dbg(&client->dev, "requesting keys...\n"); + + /* + * Read all registers from General Status Register + * to GPIOs register + */ + ret = qt2160_read_block(client, QT2160_CMD_GSTAT, regs, 6); + if (ret) { + dev_err(&client->dev, + "could not perform chip read.\n"); + return ret; + } + + old_matrix = qt2160->key_matrix; + qt2160->key_matrix = new_matrix = (regs[2] << 8) | regs[1]; + + mask = 0x01; + for (i = 0; i < 16; ++i, mask <<= 1) { + int keyval = new_matrix & mask; + + if ((old_matrix & mask) != keyval) { + input_report_key(input, qt2160->keycodes[i], keyval); + dev_dbg(&client->dev, "key %d %s\n", + i, keyval ? "pressed" : "released"); + } + } + + input_sync(input); + + return 0; +} + +static irqreturn_t qt2160_irq(int irq, void *_qt2160) +{ + struct qt2160_data *qt2160 = _qt2160; + unsigned long flags; + + spin_lock_irqsave(&qt2160->lock, flags); + + __cancel_delayed_work(&qt2160->dwork); + schedule_delayed_work(&qt2160->dwork, 0); + + spin_unlock_irqrestore(&qt2160->lock, flags); + + return IRQ_HANDLED; +} + +static void qt2160_schedule_read(struct qt2160_data *qt2160) +{ + spin_lock_irq(&qt2160->lock); + schedule_delayed_work(&qt2160->dwork, QT2160_CYCLE_INTERVAL); + spin_unlock_irq(&qt2160->lock); +} + +static void qt2160_worker(struct work_struct *work) +{ + struct qt2160_data *qt2160 = + container_of(work, struct qt2160_data, dwork.work); + + dev_dbg(&qt2160->client->dev, "worker\n"); + + qt2160_get_key_matrix(qt2160); + + /* Avoid device lock up by checking every so often */ + qt2160_schedule_read(qt2160); +} + +static int __devinit qt2160_read(struct i2c_client *client, u8 reg) +{ + int ret; + + ret = i2c_smbus_write_byte(client, reg); + if (ret) { + dev_err(&client->dev, + "couldn't send request. Returned %d\n", ret); + return ret; + } + + ret = i2c_smbus_read_byte(client); + if (ret < 0) { + dev_err(&client->dev, + "couldn't read register. Returned %d\n", ret); + return ret; + } + + return ret; +} + +static int __devinit qt2160_write(struct i2c_client *client, u8 reg, u8 data) +{ + int error; + + error = i2c_smbus_write_byte(client, reg); + if (error) { + dev_err(&client->dev, + "couldn't send request. Returned %d\n", error); + return error; + } + + error = i2c_smbus_write_byte(client, data); + if (error) { + dev_err(&client->dev, + "couldn't write data. Returned %d\n", error); + return error; + } + + return error; +} + + +static bool __devinit qt2160_identify(struct i2c_client *client) +{ + int id, ver, rev; + + /* Read Chid ID to check if chip is valid */ + id = qt2160_read(client, QT2160_CMD_CHIPID); + if (id != QT2160_VALID_CHIPID) { + dev_err(&client->dev, "ID %d not supported\n", id); + return false; + } + + /* Read chip firmware version */ + ver = qt2160_read(client, QT2160_CMD_CODEVER); + if (ver < 0) { + dev_err(&client->dev, "could not get firmware version\n"); + return false; + } + + /* Read chip firmware revision */ + rev = qt2160_read(client, QT2160_CMD_SUBVER); + if (rev < 0) { + dev_err(&client->dev, "could not get firmware revision\n"); + return false; + } + + dev_info(&client->dev, "AT42QT2160 firmware version %d.%d.%d\n", + ver >> 4, ver & 0xf, rev); + + return true; +} + +static int __devinit qt2160_probe(struct i2c_client *client, + const struct i2c_device_id *id) +{ + struct qt2160_data *qt2160; + struct input_dev *input; + int i; + int error; + + /* Check functionality */ + error = i2c_check_functionality(client->adapter, + I2C_FUNC_SMBUS_BYTE); + if (!error) { + dev_err(&client->dev, "%s adapter not supported\n", + dev_driver_string(&client->adapter->dev)); + return -ENODEV; + } + + if (!qt2160_identify(client)) + return -ENODEV; + + /* Chip is valid and active. Allocate structure */ + qt2160 = kzalloc(sizeof(struct qt2160_data), GFP_KERNEL); + input = input_allocate_device(); + if (!qt2160 || !input) { + dev_err(&client->dev, "insufficient memory\n"); + error = -ENOMEM; + goto err_free_mem; + } + + qt2160->client = client; + qt2160->input = input; + INIT_DELAYED_WORK(&qt2160->dwork, qt2160_worker); + spin_lock_init(&qt2160->lock); + + input->name = "AT42QT2160 Touch Sense Keyboard"; + input->id.bustype = BUS_I2C; + + input->keycode = qt2160->keycodes; + input->keycodesize = sizeof(qt2160->keycodes[0]); + input->keycodemax = ARRAY_SIZE(qt2160_key2code); + + __set_bit(EV_KEY, input->evbit); + __clear_bit(EV_REP, input->evbit); + for (i = 0; i < ARRAY_SIZE(qt2160_key2code); i++) { + qt2160->keycodes[i] = qt2160_key2code[i]; + __set_bit(qt2160_key2code[i], input->keybit); + } + __clear_bit(KEY_RESERVED, input->keybit); + + /* Calibrate device */ + error = qt2160_write(client, QT2160_CMD_CALIBRATE, 1); + if (error) { + dev_err(&client->dev, "failed to calibrate device\n"); + goto err_free_mem; + } + + if (client->irq) { + error = request_irq(client->irq, qt2160_irq, + IRQF_TRIGGER_FALLING, "qt2160", qt2160); + if (error) { + dev_err(&client->dev, + "failed to allocate irq %d\n", client->irq); + goto err_free_mem; + } + } + + error = input_register_device(qt2160->input); + if (error) { + dev_err(&client->dev, + "Failed to register input device\n"); + goto err_free_irq; + } + + i2c_set_clientdata(client, qt2160); + qt2160_schedule_read(qt2160); + + return 0; + +err_free_irq: + if (client->irq) + free_irq(client->irq, qt2160); +err_free_mem: + input_free_device(input); + kfree(qt2160); + return error; +} + +static int __devexit qt2160_remove(struct i2c_client *client) +{ + struct qt2160_data *qt2160 = i2c_get_clientdata(client); + + /* Release IRQ so no queue will be scheduled */ + if (client->irq) + free_irq(client->irq, qt2160); + + cancel_delayed_work_sync(&qt2160->dwork); + + input_unregister_device(qt2160->input); + kfree(qt2160); + + i2c_set_clientdata(client, NULL); + return 0; +} + +static struct i2c_device_id qt2160_idtable[] = { + { "qt2160", 0, }, + { } +}; + +MODULE_DEVICE_TABLE(i2c, qt2160_idtable); + +static struct i2c_driver qt2160_driver = { + .driver = { + .name = "qt2160", + .owner = THIS_MODULE, + }, + + .id_table = qt2160_idtable, + .probe = qt2160_probe, + .remove = __devexit_p(qt2160_remove), +}; + +static int __init qt2160_init(void) +{ + return i2c_add_driver(&qt2160_driver); +} +module_init(qt2160_init); + +static void __exit qt2160_cleanup(void) +{ + i2c_del_driver(&qt2160_driver); +} +module_exit(qt2160_cleanup); + +MODULE_AUTHOR("Raphael Derosso Pereira <raphaelpereira@gmail.com>"); +MODULE_DESCRIPTION("Driver for AT42QT2160 Touch Sensor"); +MODULE_LICENSE("GPL"); diff --git a/drivers/input/keyboard/sh_keysc.c b/drivers/input/keyboard/sh_keysc.c index cea70e6a103..076111fc72d 100644 --- a/drivers/input/keyboard/sh_keysc.c +++ b/drivers/input/keyboard/sh_keysc.c @@ -18,9 +18,9 @@ #include <linux/delay.h> #include <linux/platform_device.h> #include <linux/input.h> +#include <linux/input/sh_keysc.h> #include <linux/clk.h> #include <linux/io.h> -#include <asm/sh_keysc.h> #define KYCR1_OFFS 0x00 #define KYCR2_OFFS 0x04 @@ -80,6 +80,9 @@ static irqreturn_t sh_keysc_isr(int irq, void *dev_id) iowrite16(KYCR2_IRQ_LEVEL | (keyin_set << 8), priv->iomem_base + KYCR2_OFFS); + if (pdata->kycr2_delay) + udelay(pdata->kycr2_delay); + keys ^= ~0; keys &= (1 << (sh_keysc_mode[pdata->mode].keyin * sh_keysc_mode[pdata->mode].keyout)) - 1; @@ -128,7 +131,7 @@ static int __devinit sh_keysc_probe(struct platform_device *pdev) struct resource *res; struct input_dev *input; char clk_name[8]; - int i, k; + int i; int irq, error; if (!pdev->dev.platform_data) { @@ -195,17 +198,19 @@ static int __devinit sh_keysc_probe(struct platform_device *pdev) input->id.product = 0x0001; input->id.version = 0x0100; + input->keycode = pdata->keycodes; + input->keycodesize = sizeof(pdata->keycodes[0]); + input->keycodemax = ARRAY_SIZE(pdata->keycodes); + error = request_irq(irq, sh_keysc_isr, 0, pdev->name, pdev); if (error) { dev_err(&pdev->dev, "failed to request IRQ\n"); goto err4; } - for (i = 0; i < SH_KEYSC_MAXKEYS; i++) { - k = pdata->keycodes[i]; - if (k) - input_set_capability(input, EV_KEY, k); - } + for (i = 0; i < SH_KEYSC_MAXKEYS; i++) + __set_bit(pdata->keycodes[i], input->keybit); + __clear_bit(KEY_RESERVED, input->keybit); error = input_register_device(input); if (error) { @@ -221,7 +226,9 @@ static int __devinit sh_keysc_probe(struct platform_device *pdev) iowrite16(KYCR2_IRQ_LEVEL, priv->iomem_base + KYCR2_OFFS); device_init_wakeup(&pdev->dev, 1); + return 0; + err5: free_irq(irq, pdev); err4: @@ -252,6 +259,7 @@ static int __devexit sh_keysc_remove(struct platform_device *pdev) platform_set_drvdata(pdev, NULL); kfree(priv); + return 0; } @@ -267,11 +275,12 @@ static int sh_keysc_suspend(struct device *dev) if (device_may_wakeup(dev)) { value |= 0x80; enable_irq_wake(irq); - } - else + } else { value &= ~0x80; + } iowrite16(value, priv->iomem_base + KYCR1_OFFS); + return 0; } diff --git a/drivers/input/keyboard/sunkbd.c b/drivers/input/keyboard/sunkbd.c index 9fce6d1e29b..a99a04b03ee 100644 --- a/drivers/input/keyboard/sunkbd.c +++ b/drivers/input/keyboard/sunkbd.c @@ -27,6 +27,7 @@ */ #include <linux/delay.h> +#include <linux/sched.h> #include <linux/slab.h> #include <linux/module.h> #include <linux/interrupt.h> @@ -73,7 +74,7 @@ static unsigned char sunkbd_keycode[128] = { */ struct sunkbd { - unsigned char keycode[128]; + unsigned char keycode[ARRAY_SIZE(sunkbd_keycode)]; struct input_dev *dev; struct serio *serio; struct work_struct tq; @@ -81,7 +82,7 @@ struct sunkbd { char name[64]; char phys[32]; char type; - unsigned char enabled; + bool enabled; volatile s8 reset; volatile s8 layout; }; @@ -94,10 +95,14 @@ struct sunkbd { static irqreturn_t sunkbd_interrupt(struct serio *serio, unsigned char data, unsigned int flags) { - struct sunkbd* sunkbd = serio_get_drvdata(serio); + struct sunkbd *sunkbd = serio_get_drvdata(serio); - if (sunkbd->reset <= -1) { /* If cp[i] is 0xff, sunkbd->reset will stay -1. */ - sunkbd->reset = data; /* The keyboard sends 0xff 0xff 0xID on powerup */ + if (sunkbd->reset <= -1) { + /* + * If cp[i] is 0xff, sunkbd->reset will stay -1. + * The keyboard sends 0xff 0xff 0xID on powerup. + */ + sunkbd->reset = data; wake_up_interruptible(&sunkbd->wait); goto out; } @@ -110,29 +115,33 @@ static irqreturn_t sunkbd_interrupt(struct serio *serio, switch (data) { - case SUNKBD_RET_RESET: - schedule_work(&sunkbd->tq); - sunkbd->reset = -1; - break; + case SUNKBD_RET_RESET: + schedule_work(&sunkbd->tq); + sunkbd->reset = -1; + break; - case SUNKBD_RET_LAYOUT: - sunkbd->layout = -1; - break; + case SUNKBD_RET_LAYOUT: + sunkbd->layout = -1; + break; + + case SUNKBD_RET_ALLUP: /* All keys released */ + break; - case SUNKBD_RET_ALLUP: /* All keys released */ + default: + if (!sunkbd->enabled) break; - default: - if (!sunkbd->enabled) - break; - - if (sunkbd->keycode[data & SUNKBD_KEY]) { - input_report_key(sunkbd->dev, sunkbd->keycode[data & SUNKBD_KEY], !(data & SUNKBD_RELEASE)); - input_sync(sunkbd->dev); - } else { - printk(KERN_WARNING "sunkbd.c: Unknown key (scancode %#x) %s.\n", - data & SUNKBD_KEY, data & SUNKBD_RELEASE ? "released" : "pressed"); - } + if (sunkbd->keycode[data & SUNKBD_KEY]) { + input_report_key(sunkbd->dev, + sunkbd->keycode[data & SUNKBD_KEY], + !(data & SUNKBD_RELEASE)); + input_sync(sunkbd->dev); + } else { + printk(KERN_WARNING + "sunkbd.c: Unknown key (scancode %#x) %s.\n", + data & SUNKBD_KEY, + data & SUNKBD_RELEASE ? "released" : "pressed"); + } } out: return IRQ_HANDLED; @@ -142,34 +151,37 @@ out: * sunkbd_event() handles events from the input module. */ -static int sunkbd_event(struct input_dev *dev, unsigned int type, unsigned int code, int value) +static int sunkbd_event(struct input_dev *dev, + unsigned int type, unsigned int code, int value) { struct sunkbd *sunkbd = input_get_drvdata(dev); switch (type) { - case EV_LED: + case EV_LED: - sunkbd->serio->write(sunkbd->serio, SUNKBD_CMD_SETLED); - sunkbd->serio->write(sunkbd->serio, - (!!test_bit(LED_CAPSL, dev->led) << 3) | (!!test_bit(LED_SCROLLL, dev->led) << 2) | - (!!test_bit(LED_COMPOSE, dev->led) << 1) | !!test_bit(LED_NUML, dev->led)); - return 0; + serio_write(sunkbd->serio, SUNKBD_CMD_SETLED); + serio_write(sunkbd->serio, + (!!test_bit(LED_CAPSL, dev->led) << 3) | + (!!test_bit(LED_SCROLLL, dev->led) << 2) | + (!!test_bit(LED_COMPOSE, dev->led) << 1) | + !!test_bit(LED_NUML, dev->led)); + return 0; - case EV_SND: + case EV_SND: - switch (code) { + switch (code) { - case SND_CLICK: - sunkbd->serio->write(sunkbd->serio, SUNKBD_CMD_NOCLICK - value); - return 0; + case SND_CLICK: + serio_write(sunkbd->serio, SUNKBD_CMD_NOCLICK - value); + return 0; - case SND_BELL: - sunkbd->serio->write(sunkbd->serio, SUNKBD_CMD_BELLOFF - value); - return 0; - } + case SND_BELL: + serio_write(sunkbd->serio, SUNKBD_CMD_BELLOFF - value); + return 0; + } - break; + break; } return -1; @@ -183,7 +195,7 @@ static int sunkbd_event(struct input_dev *dev, unsigned int type, unsigned int c static int sunkbd_initialize(struct sunkbd *sunkbd) { sunkbd->reset = -2; - sunkbd->serio->write(sunkbd->serio, SUNKBD_CMD_RESET); + serio_write(sunkbd->serio, SUNKBD_CMD_RESET); wait_event_interruptible_timeout(sunkbd->wait, sunkbd->reset >= 0, HZ); if (sunkbd->reset < 0) return -1; @@ -192,10 +204,13 @@ static int sunkbd_initialize(struct sunkbd *sunkbd) if (sunkbd->type == 4) { /* Type 4 keyboard */ sunkbd->layout = -2; - sunkbd->serio->write(sunkbd->serio, SUNKBD_CMD_LAYOUT); - wait_event_interruptible_timeout(sunkbd->wait, sunkbd->layout >= 0, HZ/4); - if (sunkbd->layout < 0) return -1; - if (sunkbd->layout & SUNKBD_LAYOUT_5_MASK) sunkbd->type = 5; + serio_write(sunkbd->serio, SUNKBD_CMD_LAYOUT); + wait_event_interruptible_timeout(sunkbd->wait, + sunkbd->layout >= 0, HZ / 4); + if (sunkbd->layout < 0) + return -1; + if (sunkbd->layout & SUNKBD_LAYOUT_5_MASK) + sunkbd->type = 5; } return 0; @@ -212,15 +227,19 @@ static void sunkbd_reinit(struct work_struct *work) wait_event_interruptible_timeout(sunkbd->wait, sunkbd->reset >= 0, HZ); - sunkbd->serio->write(sunkbd->serio, SUNKBD_CMD_SETLED); - sunkbd->serio->write(sunkbd->serio, - (!!test_bit(LED_CAPSL, sunkbd->dev->led) << 3) | (!!test_bit(LED_SCROLLL, sunkbd->dev->led) << 2) | - (!!test_bit(LED_COMPOSE, sunkbd->dev->led) << 1) | !!test_bit(LED_NUML, sunkbd->dev->led)); - sunkbd->serio->write(sunkbd->serio, SUNKBD_CMD_NOCLICK - !!test_bit(SND_CLICK, sunkbd->dev->snd)); - sunkbd->serio->write(sunkbd->serio, SUNKBD_CMD_BELLOFF - !!test_bit(SND_BELL, sunkbd->dev->snd)); + serio_write(sunkbd->serio, SUNKBD_CMD_SETLED); + serio_write(sunkbd->serio, + (!!test_bit(LED_CAPSL, sunkbd->dev->led) << 3) | + (!!test_bit(LED_SCROLLL, sunkbd->dev->led) << 2) | + (!!test_bit(LED_COMPOSE, sunkbd->dev->led) << 1) | + !!test_bit(LED_NUML, sunkbd->dev->led)); + serio_write(sunkbd->serio, + SUNKBD_CMD_NOCLICK - !!test_bit(SND_CLICK, sunkbd->dev->snd)); + serio_write(sunkbd->serio, + SUNKBD_CMD_BELLOFF - !!test_bit(SND_BELL, sunkbd->dev->snd)); } -static void sunkbd_enable(struct sunkbd *sunkbd, int enable) +static void sunkbd_enable(struct sunkbd *sunkbd, bool enable) { serio_pause_rx(sunkbd->serio); sunkbd->enabled = enable; @@ -228,7 +247,8 @@ static void sunkbd_enable(struct sunkbd *sunkbd, int enable) } /* - * sunkbd_connect() probes for a Sun keyboard and fills the necessary structures. + * sunkbd_connect() probes for a Sun keyboard and fills the necessary + * structures. */ static int sunkbd_connect(struct serio *serio, struct serio_driver *drv) @@ -260,7 +280,8 @@ static int sunkbd_connect(struct serio *serio, struct serio_driver *drv) goto fail3; } - snprintf(sunkbd->name, sizeof(sunkbd->name), "Sun Type %d keyboard", sunkbd->type); + snprintf(sunkbd->name, sizeof(sunkbd->name), + "Sun Type %d keyboard", sunkbd->type); memcpy(sunkbd->keycode, sunkbd_keycode, sizeof(sunkbd->keycode)); input_dev->name = sunkbd->name; @@ -284,11 +305,11 @@ static int sunkbd_connect(struct serio *serio, struct serio_driver *drv) input_dev->keycode = sunkbd->keycode; input_dev->keycodesize = sizeof(unsigned char); input_dev->keycodemax = ARRAY_SIZE(sunkbd_keycode); - for (i = 0; i < 128; i++) - set_bit(sunkbd->keycode[i], input_dev->keybit); - clear_bit(0, input_dev->keybit); + for (i = 0; i < ARRAY_SIZE(sunkbd_keycode); i++) + __set_bit(sunkbd->keycode[i], input_dev->keybit); + __clear_bit(KEY_RESERVED, input_dev->keybit); - sunkbd_enable(sunkbd, 1); + sunkbd_enable(sunkbd, true); err = input_register_device(sunkbd->dev); if (err) @@ -296,7 +317,7 @@ static int sunkbd_connect(struct serio *serio, struct serio_driver *drv) return 0; - fail4: sunkbd_enable(sunkbd, 0); + fail4: sunkbd_enable(sunkbd, false); fail3: serio_close(serio); fail2: serio_set_drvdata(serio, NULL); fail1: input_free_device(input_dev); @@ -312,7 +333,7 @@ static void sunkbd_disconnect(struct serio *serio) { struct sunkbd *sunkbd = serio_get_drvdata(serio); - sunkbd_enable(sunkbd, 0); + sunkbd_enable(sunkbd, false); input_unregister_device(sunkbd->dev); serio_close(serio); serio_set_drvdata(serio, NULL); diff --git a/drivers/input/keyboard/tosakbd.c b/drivers/input/keyboard/tosakbd.c index 677276b1202..42cb3faf733 100644 --- a/drivers/input/keyboard/tosakbd.c +++ b/drivers/input/keyboard/tosakbd.c @@ -31,7 +31,7 @@ #define KB_DISCHARGE_DELAY 10 #define KB_ACTIVATE_DELAY 10 -static unsigned int tosakbd_keycode[NR_SCANCODES] = { +static unsigned short tosakbd_keycode[NR_SCANCODES] = { 0, 0, KEY_W, 0, 0, 0, KEY_K, KEY_BACKSPACE, KEY_P, 0, 0, 0, 0, 0, 0, 0, 0, @@ -50,9 +50,9 @@ KEY_X, KEY_F, KEY_SPACE, KEY_APOSTROPHE, TOSA_KEY_MAIL, KEY_LEFT, KEY_DOWN, KEY_ }; struct tosakbd { - unsigned int keycode[ARRAY_SIZE(tosakbd_keycode)]; + unsigned short keycode[ARRAY_SIZE(tosakbd_keycode)]; struct input_dev *input; - int suspended; + bool suspended; spinlock_t lock; /* protect kbd scanning */ struct timer_list timer; }; @@ -215,7 +215,7 @@ static int tosakbd_suspend(struct platform_device *dev, pm_message_t state) unsigned long flags; spin_lock_irqsave(&tosakbd->lock, flags); - tosakbd->suspended = 1; + tosakbd->suspended = true; spin_unlock_irqrestore(&tosakbd->lock, flags); del_timer_sync(&tosakbd->timer); @@ -227,7 +227,7 @@ static int tosakbd_resume(struct platform_device *dev) { struct tosakbd *tosakbd = platform_get_drvdata(dev); - tosakbd->suspended = 0; + tosakbd->suspended = false; tosakbd_scankeyboard(dev); return 0; @@ -277,14 +277,14 @@ static int __devinit tosakbd_probe(struct platform_device *pdev) { input_dev->evbit[0] = BIT(EV_KEY) | BIT(EV_REP); input_dev->keycode = tosakbd->keycode; - input_dev->keycodesize = sizeof(unsigned int); + input_dev->keycodesize = sizeof(tosakbd->keycode[0]); input_dev->keycodemax = ARRAY_SIZE(tosakbd_keycode); memcpy(tosakbd->keycode, tosakbd_keycode, sizeof(tosakbd_keycode)); for (i = 0; i < ARRAY_SIZE(tosakbd_keycode); i++) __set_bit(tosakbd->keycode[i], input_dev->keybit); - clear_bit(0, input_dev->keybit); + __clear_bit(KEY_RESERVED, input_dev->keybit); /* Setup sense interrupts - RisingEdge Detect, sense lines as inputs */ for (i = 0; i < TOSA_KEY_SENSE_NUM; i++) { @@ -344,7 +344,7 @@ static int __devinit tosakbd_probe(struct platform_device *pdev) { " direction for GPIO %d, error %d\n", gpio, error); gpio_free(gpio); - goto fail; + goto fail2; } } @@ -353,7 +353,7 @@ static int __devinit tosakbd_probe(struct platform_device *pdev) { if (error) { printk(KERN_ERR "tosakbd: Unable to register input device, " "error: %d\n", error); - goto fail; + goto fail2; } printk(KERN_INFO "input: Tosa Keyboard Registered\n"); diff --git a/drivers/input/keyboard/twl4030_keypad.c b/drivers/input/keyboard/twl4030_keypad.c new file mode 100644 index 00000000000..9a2977c2169 --- /dev/null +++ b/drivers/input/keyboard/twl4030_keypad.c @@ -0,0 +1,480 @@ +/* + * twl4030_keypad.c - driver for 8x8 keypad controller in twl4030 chips + * + * Copyright (C) 2007 Texas Instruments, Inc. + * Copyright (C) 2008 Nokia Corporation + * + * Code re-written for 2430SDP by: + * Syed Mohammed Khasim <x0khasim@ti.com> + * + * Initial Code: + * Manjunatha G K <manjugk@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; 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/kernel.h> +#include <linux/module.h> +#include <linux/init.h> +#include <linux/interrupt.h> +#include <linux/input.h> +#include <linux/platform_device.h> +#include <linux/i2c/twl4030.h> + + +/* + * The TWL4030 family chips include a keypad controller that supports + * up to an 8x8 switch matrix. The controller can issue system wakeup + * events, since it uses only the always-on 32KiHz oscillator, and has + * an internal state machine that decodes pressed keys, including + * multi-key combinations. + * + * This driver lets boards define what keycodes they wish to report for + * which scancodes, as part of the "struct twl4030_keypad_data" used in + * the probe() routine. + * + * See the TPS65950 documentation; that's the general availability + * version of the TWL5030 second generation part. + */ +#define TWL4030_MAX_ROWS 8 /* TWL4030 hard limit */ +#define TWL4030_MAX_COLS 8 +#define TWL4030_ROW_SHIFT 3 +#define TWL4030_KEYMAP_SIZE (TWL4030_MAX_ROWS * TWL4030_MAX_COLS) + +struct twl4030_keypad { + unsigned short keymap[TWL4030_KEYMAP_SIZE]; + u16 kp_state[TWL4030_MAX_ROWS]; + unsigned n_rows; + unsigned n_cols; + unsigned irq; + + struct device *dbg_dev; + struct input_dev *input; +}; + +/*----------------------------------------------------------------------*/ + +/* arbitrary prescaler value 0..7 */ +#define PTV_PRESCALER 4 + +/* Register Offsets */ +#define KEYP_CTRL 0x00 +#define KEYP_DEB 0x01 +#define KEYP_LONG_KEY 0x02 +#define KEYP_LK_PTV 0x03 +#define KEYP_TIMEOUT_L 0x04 +#define KEYP_TIMEOUT_H 0x05 +#define KEYP_KBC 0x06 +#define KEYP_KBR 0x07 +#define KEYP_SMS 0x08 +#define KEYP_FULL_CODE_7_0 0x09 /* row 0 column status */ +#define KEYP_FULL_CODE_15_8 0x0a /* ... row 1 ... */ +#define KEYP_FULL_CODE_23_16 0x0b +#define KEYP_FULL_CODE_31_24 0x0c +#define KEYP_FULL_CODE_39_32 0x0d +#define KEYP_FULL_CODE_47_40 0x0e +#define KEYP_FULL_CODE_55_48 0x0f +#define KEYP_FULL_CODE_63_56 0x10 +#define KEYP_ISR1 0x11 +#define KEYP_IMR1 0x12 +#define KEYP_ISR2 0x13 +#define KEYP_IMR2 0x14 +#define KEYP_SIR 0x15 +#define KEYP_EDR 0x16 /* edge triggers */ +#define KEYP_SIH_CTRL 0x17 + +/* KEYP_CTRL_REG Fields */ +#define KEYP_CTRL_SOFT_NRST BIT(0) +#define KEYP_CTRL_SOFTMODEN BIT(1) +#define KEYP_CTRL_LK_EN BIT(2) +#define KEYP_CTRL_TOE_EN BIT(3) +#define KEYP_CTRL_TOLE_EN BIT(4) +#define KEYP_CTRL_RP_EN BIT(5) +#define KEYP_CTRL_KBD_ON BIT(6) + +/* KEYP_DEB, KEYP_LONG_KEY, KEYP_TIMEOUT_x*/ +#define KEYP_PERIOD_US(t, prescale) ((t) / (31 << (prescale + 1)) - 1) + +/* KEYP_LK_PTV_REG Fields */ +#define KEYP_LK_PTV_PTV_SHIFT 5 + +/* KEYP_{IMR,ISR,SIR} Fields */ +#define KEYP_IMR1_MIS BIT(3) +#define KEYP_IMR1_TO BIT(2) +#define KEYP_IMR1_LK BIT(1) +#define KEYP_IMR1_KP BIT(0) + +/* KEYP_EDR Fields */ +#define KEYP_EDR_KP_FALLING 0x01 +#define KEYP_EDR_KP_RISING 0x02 +#define KEYP_EDR_KP_BOTH 0x03 +#define KEYP_EDR_LK_FALLING 0x04 +#define KEYP_EDR_LK_RISING 0x08 +#define KEYP_EDR_TO_FALLING 0x10 +#define KEYP_EDR_TO_RISING 0x20 +#define KEYP_EDR_MIS_FALLING 0x40 +#define KEYP_EDR_MIS_RISING 0x80 + + +/*----------------------------------------------------------------------*/ + +static int twl4030_kpread(struct twl4030_keypad *kp, + u8 *data, u32 reg, u8 num_bytes) +{ + int ret = twl4030_i2c_read(TWL4030_MODULE_KEYPAD, data, reg, num_bytes); + + if (ret < 0) + dev_warn(kp->dbg_dev, + "Couldn't read TWL4030: %X - ret %d[%x]\n", + reg, ret, ret); + + return ret; +} + +static int twl4030_kpwrite_u8(struct twl4030_keypad *kp, u8 data, u32 reg) +{ + int ret = twl4030_i2c_write_u8(TWL4030_MODULE_KEYPAD, data, reg); + + if (ret < 0) + dev_warn(kp->dbg_dev, + "Could not write TWL4030: %X - ret %d[%x]\n", + reg, ret, ret); + + return ret; +} + +static inline u16 twl4030_col_xlate(struct twl4030_keypad *kp, u8 col) +{ + /* If all bits in a row are active for all coloumns then + * we have that row line connected to gnd. Mark this + * key on as if it was on matrix position n_cols (ie + * one higher than the size of the matrix). + */ + if (col == 0xFF) + return 1 << kp->n_cols; + else + return col & ((1 << kp->n_cols) - 1); +} + +static int twl4030_read_kp_matrix_state(struct twl4030_keypad *kp, u16 *state) +{ + u8 new_state[TWL4030_MAX_ROWS]; + int row; + int ret = twl4030_kpread(kp, new_state, + KEYP_FULL_CODE_7_0, kp->n_rows); + if (ret >= 0) + for (row = 0; row < kp->n_rows; row++) + state[row] = twl4030_col_xlate(kp, new_state[row]); + + return ret; +} + +static int twl4030_is_in_ghost_state(struct twl4030_keypad *kp, u16 *key_state) +{ + int i; + u16 check = 0; + + for (i = 0; i < kp->n_rows; i++) { + u16 col = key_state[i]; + + if ((col & check) && hweight16(col) > 1) + return 1; + + check |= col; + } + + return 0; +} + +static void twl4030_kp_scan(struct twl4030_keypad *kp, bool release_all) +{ + struct input_dev *input = kp->input; + u16 new_state[TWL4030_MAX_ROWS]; + int col, row; + + if (release_all) + memset(new_state, 0, sizeof(new_state)); + else { + /* check for any changes */ + int ret = twl4030_read_kp_matrix_state(kp, new_state); + + if (ret < 0) /* panic ... */ + return; + + if (twl4030_is_in_ghost_state(kp, new_state)) + return; + } + + /* check for changes and print those */ + for (row = 0; row < kp->n_rows; row++) { + int changed = new_state[row] ^ kp->kp_state[row]; + + if (!changed) + continue; + + for (col = 0; col < kp->n_cols; col++) { + int code; + + if (!(changed & (1 << col))) + continue; + + dev_dbg(kp->dbg_dev, "key [%d:%d] %s\n", row, col, + (new_state[row] & (1 << col)) ? + "press" : "release"); + + code = MATRIX_SCAN_CODE(row, col, TWL4030_ROW_SHIFT); + input_event(input, EV_MSC, MSC_SCAN, code); + input_report_key(input, kp->keymap[code], + new_state[row] & (1 << col)); + } + kp->kp_state[row] = new_state[row]; + } + input_sync(input); +} + +/* + * Keypad interrupt handler + */ +static irqreturn_t do_kp_irq(int irq, void *_kp) +{ + struct twl4030_keypad *kp = _kp; + u8 reg; + int ret; + +#ifdef CONFIG_LOCKDEP + /* WORKAROUND for lockdep forcing IRQF_DISABLED on us, which + * we don't want and can't tolerate. Although it might be + * friendlier not to borrow this thread context... + */ + local_irq_enable(); +#endif + + /* Read & Clear TWL4030 pending interrupt */ + ret = twl4030_kpread(kp, ®, KEYP_ISR1, 1); + + /* Release all keys if I2C has gone bad or + * the KEYP has gone to idle state */ + if (ret >= 0 && (reg & KEYP_IMR1_KP)) + twl4030_kp_scan(kp, false); + else + twl4030_kp_scan(kp, true); + + return IRQ_HANDLED; +} + +static int __devinit twl4030_kp_program(struct twl4030_keypad *kp) +{ + u8 reg; + int i; + + /* Enable controller, with hardware decoding but not autorepeat */ + reg = KEYP_CTRL_SOFT_NRST | KEYP_CTRL_SOFTMODEN + | KEYP_CTRL_TOE_EN | KEYP_CTRL_KBD_ON; + if (twl4030_kpwrite_u8(kp, reg, KEYP_CTRL) < 0) + return -EIO; + + /* NOTE: we could use sih_setup() here to package keypad + * event sources as four different IRQs ... but we don't. + */ + + /* Enable TO rising and KP rising and falling edge detection */ + reg = KEYP_EDR_KP_BOTH | KEYP_EDR_TO_RISING; + if (twl4030_kpwrite_u8(kp, reg, KEYP_EDR) < 0) + return -EIO; + + /* Set PTV prescaler Field */ + reg = (PTV_PRESCALER << KEYP_LK_PTV_PTV_SHIFT); + if (twl4030_kpwrite_u8(kp, reg, KEYP_LK_PTV) < 0) + return -EIO; + + /* Set key debounce time to 20 ms */ + i = KEYP_PERIOD_US(20000, PTV_PRESCALER); + if (twl4030_kpwrite_u8(kp, i, KEYP_DEB) < 0) + return -EIO; + + /* Set timeout period to 100 ms */ + i = KEYP_PERIOD_US(200000, PTV_PRESCALER); + if (twl4030_kpwrite_u8(kp, (i & 0xFF), KEYP_TIMEOUT_L) < 0) + return -EIO; + + if (twl4030_kpwrite_u8(kp, (i >> 8), KEYP_TIMEOUT_H) < 0) + return -EIO; + + /* + * Enable Clear-on-Read; disable remembering events that fire + * after the IRQ but before our handler acks (reads) them, + */ + reg = TWL4030_SIH_CTRL_COR_MASK | TWL4030_SIH_CTRL_PENDDIS_MASK; + if (twl4030_kpwrite_u8(kp, reg, KEYP_SIH_CTRL) < 0) + return -EIO; + + /* initialize key state; irqs update it from here on */ + if (twl4030_read_kp_matrix_state(kp, kp->kp_state) < 0) + return -EIO; + + return 0; +} + +/* + * Registers keypad device with input subsystem + * and configures TWL4030 keypad registers + */ +static int __devinit twl4030_kp_probe(struct platform_device *pdev) +{ + struct twl4030_keypad_data *pdata = pdev->dev.platform_data; + const struct matrix_keymap_data *keymap_data = pdata->keymap_data; + struct twl4030_keypad *kp; + struct input_dev *input; + u8 reg; + int error; + + if (!pdata || !pdata->rows || !pdata->cols || + pdata->rows > TWL4030_MAX_ROWS || pdata->cols > TWL4030_MAX_COLS) { + dev_err(&pdev->dev, "Invalid platform_data\n"); + return -EINVAL; + } + + kp = kzalloc(sizeof(*kp), GFP_KERNEL); + input = input_allocate_device(); + if (!kp || !input) { + error = -ENOMEM; + goto err1; + } + + /* Get the debug Device */ + kp->dbg_dev = &pdev->dev; + kp->input = input; + + kp->n_rows = pdata->rows; + kp->n_cols = pdata->cols; + kp->irq = platform_get_irq(pdev, 0); + + /* setup input device */ + __set_bit(EV_KEY, input->evbit); + + /* Enable auto repeat feature of Linux input subsystem */ + if (pdata->rep) + __set_bit(EV_REP, input->evbit); + + input_set_capability(input, EV_MSC, MSC_SCAN); + + input->name = "TWL4030 Keypad"; + input->phys = "twl4030_keypad/input0"; + input->dev.parent = &pdev->dev; + + input->id.bustype = BUS_HOST; + input->id.vendor = 0x0001; + input->id.product = 0x0001; + input->id.version = 0x0003; + + input->keycode = kp->keymap; + input->keycodesize = sizeof(kp->keymap[0]); + input->keycodemax = ARRAY_SIZE(kp->keymap); + + matrix_keypad_build_keymap(keymap_data, TWL4030_ROW_SHIFT, + input->keycode, input->keybit); + + error = input_register_device(input); + if (error) { + dev_err(kp->dbg_dev, + "Unable to register twl4030 keypad device\n"); + goto err1; + } + + error = twl4030_kp_program(kp); + if (error) + goto err2; + + /* + * This ISR will always execute in kernel thread context because of + * the need to access the TWL4030 over the I2C bus. + * + * NOTE: we assume this host is wired to TWL4040 INT1, not INT2 ... + */ + error = request_irq(kp->irq, do_kp_irq, 0, pdev->name, kp); + if (error) { + dev_info(kp->dbg_dev, "request_irq failed for irq no=%d\n", + kp->irq); + goto err3; + } + + /* Enable KP and TO interrupts now. */ + reg = (u8) ~(KEYP_IMR1_KP | KEYP_IMR1_TO); + if (twl4030_kpwrite_u8(kp, reg, KEYP_IMR1)) { + error = -EIO; + goto err4; + } + + platform_set_drvdata(pdev, kp); + return 0; + +err4: + /* mask all events - we don't care about the result */ + (void) twl4030_kpwrite_u8(kp, 0xff, KEYP_IMR1); +err3: + free_irq(kp->irq, NULL); +err2: + input_unregister_device(input); + input = NULL; +err1: + input_free_device(input); + kfree(kp); + return error; +} + +static int __devexit twl4030_kp_remove(struct platform_device *pdev) +{ + struct twl4030_keypad *kp = platform_get_drvdata(pdev); + + free_irq(kp->irq, kp); + input_unregister_device(kp->input); + platform_set_drvdata(pdev, NULL); + kfree(kp); + + return 0; +} + +/* + * NOTE: twl4030 are multi-function devices connected via I2C. + * So this device is a child of an I2C parent, thus it needs to + * support unplug/replug (which most platform devices don't). + */ + +static struct platform_driver twl4030_kp_driver = { + .probe = twl4030_kp_probe, + .remove = __devexit_p(twl4030_kp_remove), + .driver = { + .name = "twl4030_keypad", + .owner = THIS_MODULE, + }, +}; + +static int __init twl4030_kp_init(void) +{ + return platform_driver_register(&twl4030_kp_driver); +} +module_init(twl4030_kp_init); + +static void __exit twl4030_kp_exit(void) +{ + platform_driver_unregister(&twl4030_kp_driver); +} +module_exit(twl4030_kp_exit); + +MODULE_AUTHOR("Texas Instruments"); +MODULE_DESCRIPTION("TWL4030 Keypad Driver"); +MODULE_LICENSE("GPL"); +MODULE_ALIAS("platform:twl4030_keypad"); + diff --git a/drivers/input/keyboard/w90p910_keypad.c b/drivers/input/keyboard/w90p910_keypad.c new file mode 100644 index 00000000000..6032def0370 --- /dev/null +++ b/drivers/input/keyboard/w90p910_keypad.c @@ -0,0 +1,281 @@ +/* + * Copyright (c) 2008-2009 Nuvoton technology corporation. + * + * Wan ZongShun <mcuos.com@gmail.com> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation;version 2 of the License. + * + */ + +#include <linux/kernel.h> +#include <linux/module.h> +#include <linux/init.h> +#include <linux/interrupt.h> +#include <linux/input.h> +#include <linux/device.h> +#include <linux/platform_device.h> +#include <linux/clk.h> +#include <linux/err.h> +#include <linux/io.h> + +#include <mach/w90p910_keypad.h> + +/* Keypad Interface Control Registers */ +#define KPI_CONF 0x00 +#define KPI_3KCONF 0x04 +#define KPI_LPCONF 0x08 +#define KPI_STATUS 0x0C + +#define IS1KEY (0x01 << 16) +#define INTTR (0x01 << 21) +#define KEY0R (0x0f << 3) +#define KEY0C 0x07 +#define DEBOUNCE_BIT 0x08 +#define KSIZE0 (0x01 << 16) +#define KSIZE1 (0x01 << 17) +#define KPSEL (0x01 << 19) +#define ENKP (0x01 << 18) + +#define KGET_RAW(n) (((n) & KEY0R) >> 3) +#define KGET_COLUMN(n) ((n) & KEY0C) + +#define W90P910_MAX_KEY_NUM (8 * 8) +#define W90P910_ROW_SHIFT 3 + +struct w90p910_keypad { + const struct w90p910_keypad_platform_data *pdata; + struct clk *clk; + struct input_dev *input_dev; + void __iomem *mmio_base; + int irq; + unsigned short keymap[W90P910_MAX_KEY_NUM]; +}; + +static void w90p910_keypad_scan_matrix(struct w90p910_keypad *keypad, + unsigned int status) +{ + struct input_dev *input_dev = keypad->input_dev; + unsigned int row = KGET_RAW(status); + unsigned int col = KGET_COLUMN(status); + unsigned int code = MATRIX_SCAN_CODE(row, col, W90P910_ROW_SHIFT); + unsigned int key = keypad->keymap[code]; + + input_event(input_dev, EV_MSC, MSC_SCAN, code); + input_report_key(input_dev, key, 1); + input_sync(input_dev); + + input_event(input_dev, EV_MSC, MSC_SCAN, code); + input_report_key(input_dev, key, 0); + input_sync(input_dev); +} + +static irqreturn_t w90p910_keypad_irq_handler(int irq, void *dev_id) +{ + struct w90p910_keypad *keypad = dev_id; + unsigned int kstatus, val; + + kstatus = __raw_readl(keypad->mmio_base + KPI_STATUS); + + val = INTTR | IS1KEY; + + if (kstatus & val) + w90p910_keypad_scan_matrix(keypad, kstatus); + + return IRQ_HANDLED; +} + +static int w90p910_keypad_open(struct input_dev *dev) +{ + struct w90p910_keypad *keypad = input_get_drvdata(dev); + const struct w90p910_keypad_platform_data *pdata = keypad->pdata; + unsigned int val, config; + + /* Enable unit clock */ + clk_enable(keypad->clk); + + val = __raw_readl(keypad->mmio_base + KPI_CONF); + val |= (KPSEL | ENKP); + val &= ~(KSIZE0 | KSIZE1); + + config = pdata->prescale | (pdata->debounce << DEBOUNCE_BIT); + + val |= config; + + __raw_writel(val, keypad->mmio_base + KPI_CONF); + + return 0; +} + +static void w90p910_keypad_close(struct input_dev *dev) +{ + struct w90p910_keypad *keypad = input_get_drvdata(dev); + + /* Disable clock unit */ + clk_disable(keypad->clk); +} + +static int __devinit w90p910_keypad_probe(struct platform_device *pdev) +{ + const struct w90p910_keypad_platform_data *pdata = + pdev->dev.platform_data; + const struct matrix_keymap_data *keymap_data; + struct w90p910_keypad *keypad; + struct input_dev *input_dev; + struct resource *res; + int irq; + int error; + + if (!pdata) { + dev_err(&pdev->dev, "no platform data defined\n"); + return -EINVAL; + } + + keymap_data = pdata->keymap_data; + + irq = platform_get_irq(pdev, 0); + if (irq < 0) { + dev_err(&pdev->dev, "failed to get keypad irq\n"); + return -ENXIO; + } + + keypad = kzalloc(sizeof(struct w90p910_keypad), GFP_KERNEL); + input_dev = input_allocate_device(); + if (!keypad || !input_dev) { + dev_err(&pdev->dev, "failed to allocate driver data\n"); + error = -ENOMEM; + goto failed_free; + } + + keypad->pdata = pdata; + keypad->input_dev = input_dev; + keypad->irq = irq; + + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + if (res == NULL) { + dev_err(&pdev->dev, "failed to get I/O memory\n"); + error = -ENXIO; + goto failed_free; + } + + res = request_mem_region(res->start, resource_size(res), pdev->name); + if (res == NULL) { + dev_err(&pdev->dev, "failed to request I/O memory\n"); + error = -EBUSY; + goto failed_free; + } + + keypad->mmio_base = ioremap(res->start, resource_size(res)); + if (keypad->mmio_base == NULL) { + dev_err(&pdev->dev, "failed to remap I/O memory\n"); + error = -ENXIO; + goto failed_free_res; + } + + keypad->clk = clk_get(&pdev->dev, NULL); + if (IS_ERR(keypad->clk)) { + dev_err(&pdev->dev, "failed to get keypad clock\n"); + error = PTR_ERR(keypad->clk); + goto failed_free_io; + } + + /* set multi-function pin for w90p910 kpi. */ + mfp_set_groupi(&pdev->dev); + + input_dev->name = pdev->name; + input_dev->id.bustype = BUS_HOST; + input_dev->open = w90p910_keypad_open; + input_dev->close = w90p910_keypad_close; + input_dev->dev.parent = &pdev->dev; + + input_dev->keycode = keypad->keymap; + input_dev->keycodesize = sizeof(keypad->keymap[0]); + input_dev->keycodemax = ARRAY_SIZE(keypad->keymap); + + input_set_drvdata(input_dev, keypad); + + input_dev->evbit[0] = BIT_MASK(EV_KEY) | BIT_MASK(EV_REP); + input_set_capability(input_dev, EV_MSC, MSC_SCAN); + + matrix_keypad_build_keymap(keymap_data, W90P910_ROW_SHIFT, + input_dev->keycode, input_dev->keybit); + + error = request_irq(keypad->irq, w90p910_keypad_irq_handler, + IRQF_DISABLED, pdev->name, keypad); + if (error) { + dev_err(&pdev->dev, "failed to request IRQ\n"); + goto failed_put_clk; + } + + /* Register the input device */ + error = input_register_device(input_dev); + if (error) { + dev_err(&pdev->dev, "failed to register input device\n"); + goto failed_free_irq; + } + + platform_set_drvdata(pdev, keypad); + return 0; + +failed_free_irq: + free_irq(irq, pdev); +failed_put_clk: + clk_put(keypad->clk); +failed_free_io: + iounmap(keypad->mmio_base); +failed_free_res: + release_mem_region(res->start, resource_size(res)); +failed_free: + input_free_device(input_dev); + kfree(keypad); + return error; +} + +static int __devexit w90p910_keypad_remove(struct platform_device *pdev) +{ + struct w90p910_keypad *keypad = platform_get_drvdata(pdev); + struct resource *res; + + free_irq(keypad->irq, pdev); + + clk_put(keypad->clk); + + input_unregister_device(keypad->input_dev); + + iounmap(keypad->mmio_base); + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + release_mem_region(res->start, resource_size(res)); + + platform_set_drvdata(pdev, NULL); + kfree(keypad); + + return 0; +} + +static struct platform_driver w90p910_keypad_driver = { + .probe = w90p910_keypad_probe, + .remove = __devexit_p(w90p910_keypad_remove), + .driver = { + .name = "nuc900-keypad", + .owner = THIS_MODULE, + }, +}; + +static int __init w90p910_keypad_init(void) +{ + return platform_driver_register(&w90p910_keypad_driver); +} + +static void __exit w90p910_keypad_exit(void) +{ + platform_driver_unregister(&w90p910_keypad_driver); +} + +module_init(w90p910_keypad_init); +module_exit(w90p910_keypad_exit); + +MODULE_AUTHOR("Wan ZongShun <mcuos.com@gmail.com>"); +MODULE_DESCRIPTION("w90p910 keypad driver"); +MODULE_LICENSE("GPL"); +MODULE_ALIAS("platform:nuc900-keypad"); diff --git a/drivers/input/misc/Kconfig b/drivers/input/misc/Kconfig index 1acfa3a05aa..16ec5233441 100644 --- a/drivers/input/misc/Kconfig +++ b/drivers/input/misc/Kconfig @@ -80,6 +80,7 @@ config INPUT_WISTRON_BTNS tristate "x86 Wistron laptop button interface" depends on X86 && !X86_64 select INPUT_POLLDEV + select INPUT_SPARSEKMAP select NEW_LEDS select LEDS_CLASS select CHECK_SIGNATURE @@ -222,6 +223,24 @@ config INPUT_SGI_BTNS To compile this driver as a module, choose M here: the module will be called sgi_btns. +config INPUT_WINBOND_CIR + tristate "Winbond IR remote control" + depends on X86 && PNP + select NEW_LEDS + select LEDS_CLASS + select LEDS_TRIGGERS + select BITREVERSE + help + Say Y here if you want to use the IR remote functionality found + in some Winbond SuperI/O chips. Currently only the WPCD376I + chip is supported (included in some Intel Media series motherboards). + + IR Receive and wake-on-IR from suspend and power-off is currently + supported. + + To compile this driver as a module, choose M here: the module will be + called winbond_cir. + config HP_SDC_RTC tristate "HP SDC Real Time Clock" depends on (GSC || HP300) && SERIO @@ -263,10 +282,41 @@ config INPUT_RB532_BUTTON config INPUT_DM355EVM tristate "TI DaVinci DM355 EVM Keypad and IR Remote" depends on MFD_DM355EVM_MSP + select INPUT_SPARSEKMAP help Supports the pushbuttons and IR remote used with the DM355 EVM board. To compile this driver as a module, choose M here: the module will be called dm355evm_keys. + +config INPUT_BFIN_ROTARY + tristate "Blackfin Rotary support" + depends on BF54x || BF52x + help + Say Y here if you want to use the Blackfin Rotary. + + To compile this driver as a module, choose M here: the + module will be called bfin-rotary. + +config INPUT_WM831X_ON + tristate "WM831X ON pin" + depends on MFD_WM831X + help + Support the ON pin of WM831X PMICs as an input device + reporting power button status. + + To compile this driver as a module, choose M here: the module + will be called wm831x_on. + +config INPUT_PCAP + tristate "Motorola EZX PCAP misc input events" + depends on EZX_PCAP + help + Say Y here if you want to use Power key and Headphone button + on Motorola EZX phones. + + To compile this driver as a module, choose M here: the + module will be called pcap_keys. + endif diff --git a/drivers/input/misc/Makefile b/drivers/input/misc/Makefile index 0d979fd4cd5..a8b84854fb7 100644 --- a/drivers/input/misc/Makefile +++ b/drivers/input/misc/Makefile @@ -8,6 +8,7 @@ obj-$(CONFIG_INPUT_APANEL) += apanel.o obj-$(CONFIG_INPUT_ATI_REMOTE) += ati_remote.o obj-$(CONFIG_INPUT_ATI_REMOTE2) += ati_remote2.o obj-$(CONFIG_INPUT_ATLAS_BTNS) += atlas_btns.o +obj-$(CONFIG_INPUT_BFIN_ROTARY) += bfin_rotary.o obj-$(CONFIG_INPUT_CM109) += cm109.o obj-$(CONFIG_INPUT_COBALT_BTNS) += cobalt_btns.o obj-$(CONFIG_INPUT_DM355EVM) += dm355evm_keys.o @@ -15,6 +16,7 @@ obj-$(CONFIG_HP_SDC_RTC) += hp_sdc_rtc.o obj-$(CONFIG_INPUT_IXP4XX_BEEPER) += ixp4xx-beeper.o obj-$(CONFIG_INPUT_KEYSPAN_REMOTE) += keyspan_remote.o obj-$(CONFIG_INPUT_M68K_BEEP) += m68kspkr.o +obj-$(CONFIG_INPUT_PCAP) += pcap_keys.o obj-$(CONFIG_INPUT_PCF50633_PMU) += pcf50633-input.o obj-$(CONFIG_INPUT_PCSPKR) += pcspkr.o obj-$(CONFIG_INPUT_POWERMATE) += powermate.o @@ -24,5 +26,8 @@ obj-$(CONFIG_INPUT_SGI_BTNS) += sgi_btns.o obj-$(CONFIG_INPUT_SPARCSPKR) += sparcspkr.o obj-$(CONFIG_INPUT_TWL4030_PWRBUTTON) += twl4030-pwrbutton.o obj-$(CONFIG_INPUT_UINPUT) += uinput.o +obj-$(CONFIG_INPUT_WINBOND_CIR) += winbond-cir.o obj-$(CONFIG_INPUT_WISTRON_BTNS) += wistron_btns.o +obj-$(CONFIG_INPUT_WM831X_ON) += wm831x-on.o obj-$(CONFIG_INPUT_YEALINK) += yealink.o + diff --git a/drivers/input/misc/ati_remote.c b/drivers/input/misc/ati_remote.c index e290fde35e7..614b65d78fe 100644 --- a/drivers/input/misc/ati_remote.c +++ b/drivers/input/misc/ati_remote.c @@ -766,7 +766,7 @@ static int ati_remote_probe(struct usb_interface *interface, const struct usb_de ati_remote->interface = interface; usb_make_path(udev, ati_remote->phys, sizeof(ati_remote->phys)); - strlcpy(ati_remote->phys, "/input0", sizeof(ati_remote->phys)); + strlcat(ati_remote->phys, "/input0", sizeof(ati_remote->phys)); if (udev->manufacturer) strlcpy(ati_remote->name, udev->manufacturer, sizeof(ati_remote->name)); diff --git a/drivers/input/misc/bfin_rotary.c b/drivers/input/misc/bfin_rotary.c new file mode 100644 index 00000000000..690f3fafa03 --- /dev/null +++ b/drivers/input/misc/bfin_rotary.c @@ -0,0 +1,283 @@ +/* + * Rotary counter driver for Analog Devices Blackfin Processors + * + * Copyright 2008-2009 Analog Devices Inc. + * Licensed under the GPL-2 or later. + */ + +#include <linux/module.h> +#include <linux/version.h> +#include <linux/init.h> +#include <linux/interrupt.h> +#include <linux/irq.h> +#include <linux/pm.h> +#include <linux/platform_device.h> +#include <linux/input.h> + +#include <asm/portmux.h> +#include <asm/bfin_rotary.h> + +static const u16 per_cnt[] = { + P_CNT_CUD, + P_CNT_CDG, + P_CNT_CZM, + 0 +}; + +struct bfin_rot { + struct input_dev *input; + int irq; + unsigned int up_key; + unsigned int down_key; + unsigned int button_key; + unsigned int rel_code; + unsigned short cnt_config; + unsigned short cnt_imask; + unsigned short cnt_debounce; +}; + +static void report_key_event(struct input_dev *input, int keycode) +{ + /* simulate a press-n-release */ + input_report_key(input, keycode, 1); + input_sync(input); + input_report_key(input, keycode, 0); + input_sync(input); +} + +static void report_rotary_event(struct bfin_rot *rotary, int delta) +{ + struct input_dev *input = rotary->input; + + if (rotary->up_key) { + report_key_event(input, + delta > 0 ? rotary->up_key : rotary->down_key); + } else { + input_report_rel(input, rotary->rel_code, delta); + input_sync(input); + } +} + +static irqreturn_t bfin_rotary_isr(int irq, void *dev_id) +{ + struct platform_device *pdev = dev_id; + struct bfin_rot *rotary = platform_get_drvdata(pdev); + int delta; + + switch (bfin_read_CNT_STATUS()) { + + case ICII: + break; + + case UCII: + case DCII: + delta = bfin_read_CNT_COUNTER(); + if (delta) + report_rotary_event(rotary, delta); + break; + + case CZMII: + report_key_event(rotary->input, rotary->button_key); + break; + + default: + break; + } + + bfin_write_CNT_COMMAND(W1LCNT_ZERO); /* Clear COUNTER */ + bfin_write_CNT_STATUS(-1); /* Clear STATUS */ + + return IRQ_HANDLED; +} + +static int __devinit bfin_rotary_probe(struct platform_device *pdev) +{ + struct bfin_rotary_platform_data *pdata = pdev->dev.platform_data; + struct bfin_rot *rotary; + struct input_dev *input; + int error; + + /* Basic validation */ + if ((pdata->rotary_up_key && !pdata->rotary_down_key) || + (!pdata->rotary_up_key && pdata->rotary_down_key)) { + return -EINVAL; + } + + error = peripheral_request_list(per_cnt, dev_name(&pdev->dev)); + if (error) { + dev_err(&pdev->dev, "requesting peripherals failed\n"); + return error; + } + + rotary = kzalloc(sizeof(struct bfin_rot), GFP_KERNEL); + input = input_allocate_device(); + if (!rotary || !input) { + error = -ENOMEM; + goto out1; + } + + rotary->input = input; + + rotary->up_key = pdata->rotary_up_key; + rotary->down_key = pdata->rotary_down_key; + rotary->button_key = pdata->rotary_button_key; + rotary->rel_code = pdata->rotary_rel_code; + + error = rotary->irq = platform_get_irq(pdev, 0); + if (error < 0) + goto out1; + + input->name = pdev->name; + input->phys = "bfin-rotary/input0"; + input->dev.parent = &pdev->dev; + + input_set_drvdata(input, rotary); + + input->id.bustype = BUS_HOST; + input->id.vendor = 0x0001; + input->id.product = 0x0001; + input->id.version = 0x0100; + + if (rotary->up_key) { + __set_bit(EV_KEY, input->evbit); + __set_bit(rotary->up_key, input->keybit); + __set_bit(rotary->down_key, input->keybit); + } else { + __set_bit(EV_REL, input->evbit); + __set_bit(rotary->rel_code, input->relbit); + } + + if (rotary->button_key) { + __set_bit(EV_KEY, input->evbit); + __set_bit(rotary->button_key, input->keybit); + } + + error = request_irq(rotary->irq, bfin_rotary_isr, + 0, dev_name(&pdev->dev), pdev); + if (error) { + dev_err(&pdev->dev, + "unable to claim irq %d; error %d\n", + rotary->irq, error); + goto out1; + } + + error = input_register_device(input); + if (error) { + dev_err(&pdev->dev, + "unable to register input device (%d)\n", error); + goto out2; + } + + if (pdata->rotary_button_key) + bfin_write_CNT_IMASK(CZMIE); + + if (pdata->mode & ROT_DEBE) + bfin_write_CNT_DEBOUNCE(pdata->debounce & DPRESCALE); + + if (pdata->mode) + bfin_write_CNT_CONFIG(bfin_read_CNT_CONFIG() | + (pdata->mode & ~CNTE)); + + bfin_write_CNT_IMASK(bfin_read_CNT_IMASK() | UCIE | DCIE); + bfin_write_CNT_CONFIG(bfin_read_CNT_CONFIG() | CNTE); + + platform_set_drvdata(pdev, rotary); + device_init_wakeup(&pdev->dev, 1); + + return 0; + +out2: + free_irq(rotary->irq, pdev); +out1: + input_free_device(input); + kfree(rotary); + peripheral_free_list(per_cnt); + + return error; +} + +static int __devexit bfin_rotary_remove(struct platform_device *pdev) +{ + struct bfin_rot *rotary = platform_get_drvdata(pdev); + + bfin_write_CNT_CONFIG(0); + bfin_write_CNT_IMASK(0); + + free_irq(rotary->irq, pdev); + input_unregister_device(rotary->input); + peripheral_free_list(per_cnt); + + kfree(rotary); + platform_set_drvdata(pdev, NULL); + + return 0; +} + +#ifdef CONFIG_PM +static int bfin_rotary_suspend(struct device *dev) +{ + struct platform_device *pdev = to_platform_device(dev); + struct bfin_rot *rotary = platform_get_drvdata(pdev); + + rotary->cnt_config = bfin_read_CNT_CONFIG(); + rotary->cnt_imask = bfin_read_CNT_IMASK(); + rotary->cnt_debounce = bfin_read_CNT_DEBOUNCE(); + + if (device_may_wakeup(&pdev->dev)) + enable_irq_wake(rotary->irq); + + return 0; +} + +static int bfin_rotary_resume(struct device *dev) +{ + struct platform_device *pdev = to_platform_device(dev); + struct bfin_rot *rotary = platform_get_drvdata(pdev); + + bfin_write_CNT_DEBOUNCE(rotary->cnt_debounce); + bfin_write_CNT_IMASK(rotary->cnt_imask); + bfin_write_CNT_CONFIG(rotary->cnt_config & ~CNTE); + + if (device_may_wakeup(&pdev->dev)) + disable_irq_wake(rotary->irq); + + if (rotary->cnt_config & CNTE) + bfin_write_CNT_CONFIG(rotary->cnt_config); + + return 0; +} + +static struct dev_pm_ops bfin_rotary_pm_ops = { + .suspend = bfin_rotary_suspend, + .resume = bfin_rotary_resume, +}; +#endif + +static struct platform_driver bfin_rotary_device_driver = { + .probe = bfin_rotary_probe, + .remove = __devexit_p(bfin_rotary_remove), + .driver = { + .name = "bfin-rotary", + .owner = THIS_MODULE, +#ifdef CONFIG_PM + .pm = &bfin_rotary_pm_ops, +#endif + }, +}; + +static int __init bfin_rotary_init(void) +{ + return platform_driver_register(&bfin_rotary_device_driver); +} +module_init(bfin_rotary_init); + +static void __exit bfin_rotary_exit(void) +{ + platform_driver_unregister(&bfin_rotary_device_driver); +} +module_exit(bfin_rotary_exit); + +MODULE_LICENSE("GPL"); +MODULE_AUTHOR("Michael Hennerich <hennerich@blackfin.uclinux.org>"); +MODULE_DESCRIPTION("Rotary Counter driver for Blackfin Processors"); +MODULE_ALIAS("platform:bfin-rotary"); diff --git a/drivers/input/misc/cobalt_btns.c b/drivers/input/misc/cobalt_btns.c index d114d3a9e1e..ee73d7219c9 100644 --- a/drivers/input/misc/cobalt_btns.c +++ b/drivers/input/misc/cobalt_btns.c @@ -116,7 +116,7 @@ static int __devinit cobalt_buttons_probe(struct platform_device *pdev) } bdev->poll_dev = poll_dev; - bdev->reg = ioremap(res->start, res->end - res->start + 1); + bdev->reg = ioremap(res->start, resource_size(res)); dev_set_drvdata(&pdev->dev, bdev); error = input_register_polled_device(poll_dev); diff --git a/drivers/input/misc/dm355evm_keys.c b/drivers/input/misc/dm355evm_keys.c index a63315ce4a6..766c06911f4 100644 --- a/drivers/input/misc/dm355evm_keys.c +++ b/drivers/input/misc/dm355evm_keys.c @@ -11,6 +11,7 @@ #include <linux/kernel.h> #include <linux/init.h> #include <linux/input.h> +#include <linux/input/sparse-keymap.h> #include <linux/platform_device.h> #include <linux/interrupt.h> @@ -23,36 +24,18 @@ * pressed, or its autorepeat kicks in, an event is sent. This driver * read those events from the small (32 event) queue and reports them. * - * Because we communicate with the MSP430 using I2C, and all I2C calls - * in Linux sleep, we need to cons up a kind of threaded IRQ handler - * using a work_struct. The IRQ is active low, but we use it through - * the GPIO controller so we can trigger on falling edges. - * * Note that physically there can only be one of these devices. * * This driver was tested with firmware revision A4. */ struct dm355evm_keys { - struct work_struct work; struct input_dev *input; struct device *dev; int irq; }; -static irqreturn_t dm355evm_keys_irq(int irq, void *_keys) -{ - struct dm355evm_keys *keys = _keys; - - schedule_work(&keys->work); - return IRQ_HANDLED; -} - -/* These initial keycodes can be remapped by dm355evm_setkeycode(). */ -static struct { - u16 event; - u16 keycode; -} dm355evm_keys[] = { - +/* These initial keycodes can be remapped */ +static const struct key_entry dm355evm_keys[] = { /* * Pushbuttons on the EVM board ... note that the labels for these * are SW10/SW11/etc on the PC board. The left/right orientation @@ -61,11 +44,11 @@ static struct { * is to the right. (That is, rotate the board counter-clockwise * by 90 degrees from the SW10/etc and "DM355 EVM" labels.) */ - { 0x00d8, KEY_OK, }, /* SW12 */ - { 0x00b8, KEY_UP, }, /* SW13 */ - { 0x00e8, KEY_DOWN, }, /* SW11 */ - { 0x0078, KEY_LEFT, }, /* SW14 */ - { 0x00f0, KEY_RIGHT, }, /* SW10 */ + { KE_KEY, 0x00d8, { KEY_OK } }, /* SW12 */ + { KE_KEY, 0x00b8, { KEY_UP } }, /* SW13 */ + { KE_KEY, 0x00e8, { KEY_DOWN } }, /* SW11 */ + { KE_KEY, 0x0078, { KEY_LEFT } }, /* SW14 */ + { KE_KEY, 0x00f0, { KEY_RIGHT } }, /* SW10 */ /* * IR buttons ... codes assigned to match the universal remote @@ -79,54 +62,58 @@ static struct { * RC5 codes are 14 bits, with two start bits (0x3 prefix) * and a toggle bit (masked out below). */ - { 0x300c, KEY_POWER, }, /* NOTE: docs omit this */ - { 0x3000, KEY_NUMERIC_0, }, - { 0x3001, KEY_NUMERIC_1, }, - { 0x3002, KEY_NUMERIC_2, }, - { 0x3003, KEY_NUMERIC_3, }, - { 0x3004, KEY_NUMERIC_4, }, - { 0x3005, KEY_NUMERIC_5, }, - { 0x3006, KEY_NUMERIC_6, }, - { 0x3007, KEY_NUMERIC_7, }, - { 0x3008, KEY_NUMERIC_8, }, - { 0x3009, KEY_NUMERIC_9, }, - { 0x3022, KEY_ENTER, }, - { 0x30ec, KEY_MODE, }, /* "tv/vcr/..." */ - { 0x300f, KEY_SELECT, }, /* "info" */ - { 0x3020, KEY_CHANNELUP, }, /* "up" */ - { 0x302e, KEY_MENU, }, /* "in/out" */ - { 0x3011, KEY_VOLUMEDOWN, }, /* "left" */ - { 0x300d, KEY_MUTE, }, /* "ok" */ - { 0x3010, KEY_VOLUMEUP, }, /* "right" */ - { 0x301e, KEY_SUBTITLE, }, /* "cc" */ - { 0x3021, KEY_CHANNELDOWN, }, /* "down" */ - { 0x3022, KEY_PREVIOUS, }, - { 0x3026, KEY_SLEEP, }, - { 0x3172, KEY_REWIND, }, /* NOTE: docs wrongly say 0x30ca */ - { 0x3175, KEY_PLAY, }, - { 0x3174, KEY_FASTFORWARD, }, - { 0x3177, KEY_RECORD, }, - { 0x3176, KEY_STOP, }, - { 0x3169, KEY_PAUSE, }, + { KE_KEY, 0x300c, { KEY_POWER } }, /* NOTE: docs omit this */ + { KE_KEY, 0x3000, { KEY_NUMERIC_0 } }, + { KE_KEY, 0x3001, { KEY_NUMERIC_1 } }, + { KE_KEY, 0x3002, { KEY_NUMERIC_2 } }, + { KE_KEY, 0x3003, { KEY_NUMERIC_3 } }, + { KE_KEY, 0x3004, { KEY_NUMERIC_4 } }, + { KE_KEY, 0x3005, { KEY_NUMERIC_5 } }, + { KE_KEY, 0x3006, { KEY_NUMERIC_6 } }, + { KE_KEY, 0x3007, { KEY_NUMERIC_7 } }, + { KE_KEY, 0x3008, { KEY_NUMERIC_8 } }, + { KE_KEY, 0x3009, { KEY_NUMERIC_9 } }, + { KE_KEY, 0x3022, { KEY_ENTER } }, + { KE_KEY, 0x30ec, { KEY_MODE } }, /* "tv/vcr/..." */ + { KE_KEY, 0x300f, { KEY_SELECT } }, /* "info" */ + { KE_KEY, 0x3020, { KEY_CHANNELUP } }, /* "up" */ + { KE_KEY, 0x302e, { KEY_MENU } }, /* "in/out" */ + { KE_KEY, 0x3011, { KEY_VOLUMEDOWN } }, /* "left" */ + { KE_KEY, 0x300d, { KEY_MUTE } }, /* "ok" */ + { KE_KEY, 0x3010, { KEY_VOLUMEUP } }, /* "right" */ + { KE_KEY, 0x301e, { KEY_SUBTITLE } }, /* "cc" */ + { KE_KEY, 0x3021, { KEY_CHANNELDOWN } },/* "down" */ + { KE_KEY, 0x3022, { KEY_PREVIOUS } }, + { KE_KEY, 0x3026, { KEY_SLEEP } }, + { KE_KEY, 0x3172, { KEY_REWIND } }, /* NOTE: docs wrongly say 0x30ca */ + { KE_KEY, 0x3175, { KEY_PLAY } }, + { KE_KEY, 0x3174, { KEY_FASTFORWARD } }, + { KE_KEY, 0x3177, { KEY_RECORD } }, + { KE_KEY, 0x3176, { KEY_STOP } }, + { KE_KEY, 0x3169, { KEY_PAUSE } }, }; -static void dm355evm_keys_work(struct work_struct *work) +/* + * Because we communicate with the MSP430 using I2C, and all I2C calls + * in Linux sleep, we use a threaded IRQ handler. The IRQ itself is + * active low, but we go through the GPIO controller so we can trigger + * on falling edges and not worry about enabling/disabling the IRQ in + * the keypress handling path. + */ +static irqreturn_t dm355evm_keys_irq(int irq, void *_keys) { - struct dm355evm_keys *keys; - int status; - - keys = container_of(work, struct dm355evm_keys, work); + static u16 last_event; + struct dm355evm_keys *keys = _keys; + const struct key_entry *ke; + unsigned int keycode; + int status; + u16 event; /* For simplicity we ignore INPUT_COUNT and just read * events until we get the "queue empty" indicator. * Reading INPUT_LOW decrements the count. */ for (;;) { - static u16 last_event; - u16 event; - int keycode; - int i; - status = dm355evm_msp_read(DM355EVM_MSP_INPUT_HIGH); if (status < 0) { dev_dbg(keys->dev, "input high err %d\n", @@ -165,14 +152,9 @@ static void dm355evm_keys_work(struct work_struct *work) /* ignore the RC5 toggle bit */ event &= ~0x0800; - /* find the key, or leave it as unknown */ - keycode = KEY_UNKNOWN; - for (i = 0; i < ARRAY_SIZE(dm355evm_keys); i++) { - if (dm355evm_keys[i].event != event) - continue; - keycode = dm355evm_keys[i].keycode; - break; - } + /* find the key, or report it as unknown */ + ke = sparse_keymap_entry_from_scancode(keys->input, event); + keycode = ke ? ke->keycode : KEY_UNKNOWN; dev_dbg(keys->dev, "input event 0x%04x--> keycode %d\n", event, keycode); @@ -183,35 +165,8 @@ static void dm355evm_keys_work(struct work_struct *work) input_report_key(keys->input, keycode, 0); input_sync(keys->input); } -} - -static int dm355evm_setkeycode(struct input_dev *dev, int index, int keycode) -{ - u16 old_keycode; - unsigned i; - - if (((unsigned)index) >= ARRAY_SIZE(dm355evm_keys)) - return -EINVAL; - - old_keycode = dm355evm_keys[index].keycode; - dm355evm_keys[index].keycode = keycode; - set_bit(keycode, dev->keybit); - for (i = 0; i < ARRAY_SIZE(dm355evm_keys); i++) { - if (dm355evm_keys[index].keycode == old_keycode) - goto done; - } - clear_bit(old_keycode, dev->keybit); -done: - return 0; -} - -static int dm355evm_getkeycode(struct input_dev *dev, int index, int *keycode) -{ - if (((unsigned)index) >= ARRAY_SIZE(dm355evm_keys)) - return -EINVAL; - - return dm355evm_keys[index].keycode; + return IRQ_HANDLED; } /*----------------------------------------------------------------------*/ @@ -221,7 +176,6 @@ static int __devinit dm355evm_keys_probe(struct platform_device *pdev) struct dm355evm_keys *keys; struct input_dev *input; int status; - int i; /* allocate instance struct and input dev */ keys = kzalloc(sizeof *keys, GFP_KERNEL); @@ -233,7 +187,6 @@ static int __devinit dm355evm_keys_probe(struct platform_device *pdev) keys->dev = &pdev->dev; keys->input = input; - INIT_WORK(&keys->work, dm355evm_keys_work); /* set up "threaded IRQ handler" */ status = platform_get_irq(pdev, 0); @@ -251,32 +204,30 @@ static int __devinit dm355evm_keys_probe(struct platform_device *pdev) input->id.product = 0x0355; input->id.version = dm355evm_msp_read(DM355EVM_MSP_FIRMREV); - input->evbit[0] = BIT(EV_KEY); - for (i = 0; i < ARRAY_SIZE(dm355evm_keys); i++) - __set_bit(dm355evm_keys[i].keycode, input->keybit); - - input->setkeycode = dm355evm_setkeycode; - input->getkeycode = dm355evm_getkeycode; + status = sparse_keymap_setup(input, dm355evm_keys, NULL); + if (status) + goto fail1; /* REVISIT: flush the event queue? */ - status = request_irq(keys->irq, dm355evm_keys_irq, - IRQF_TRIGGER_FALLING, - dev_name(&pdev->dev), keys); + status = request_threaded_irq(keys->irq, NULL, dm355evm_keys_irq, + IRQF_TRIGGER_FALLING, dev_name(&pdev->dev), keys); if (status < 0) - goto fail1; + goto fail2; /* register */ status = input_register_device(input); if (status < 0) - goto fail2; + goto fail3; platform_set_drvdata(pdev, keys); return 0; -fail2: +fail3: free_irq(keys->irq, keys); +fail2: + sparse_keymap_free(input); fail1: input_free_device(input); kfree(keys); @@ -290,6 +241,7 @@ static int __devexit dm355evm_keys_remove(struct platform_device *pdev) struct dm355evm_keys *keys = platform_get_drvdata(pdev); free_irq(keys->irq, keys); + sparse_keymap_free(keys->input); input_unregister_device(keys->input); kfree(keys); diff --git a/drivers/input/misc/hp_sdc_rtc.c b/drivers/input/misc/hp_sdc_rtc.c index 216a559f55e..ad730e15afc 100644 --- a/drivers/input/misc/hp_sdc_rtc.c +++ b/drivers/input/misc/hp_sdc_rtc.c @@ -35,7 +35,6 @@ #include <linux/hp_sdc.h> #include <linux/errno.h> -#include <linux/smp_lock.h> #include <linux/types.h> #include <linux/init.h> #include <linux/module.h> @@ -209,7 +208,7 @@ static inline int hp_sdc_rtc_read_rt(struct timeval *res) { /* Read the i8042 fast handshake timer */ static inline int hp_sdc_rtc_read_fhs(struct timeval *res) { - uint64_t raw; + int64_t raw; unsigned int tenms; raw = hp_sdc_rtc_read_i8042timer(HP_SDC_CMD_LOAD_FHS, 2); @@ -409,7 +408,6 @@ static unsigned int hp_sdc_rtc_poll(struct file *file, poll_table *wait) static int hp_sdc_rtc_open(struct inode *inode, struct file *file) { - cycle_kernel_lock(); return 0; } diff --git a/drivers/input/misc/pcap_keys.c b/drivers/input/misc/pcap_keys.c new file mode 100644 index 00000000000..7ea969347ca --- /dev/null +++ b/drivers/input/misc/pcap_keys.c @@ -0,0 +1,144 @@ +/* + * Input driver for PCAP events: + * * Power key + * * Headphone button + * + * Copyright (c) 2008,2009 Ilya Petrov <ilya.muromec@gmail.com> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + */ + +#include <linux/module.h> +#include <linux/init.h> +#include <linux/interrupt.h> +#include <linux/platform_device.h> +#include <linux/input.h> +#include <linux/mfd/ezx-pcap.h> + +struct pcap_keys { + struct pcap_chip *pcap; + struct input_dev *input; +}; + +/* PCAP2 interrupts us on keypress */ +static irqreturn_t pcap_keys_handler(int irq, void *_pcap_keys) +{ + struct pcap_keys *pcap_keys = _pcap_keys; + int pirq = irq_to_pcap(pcap_keys->pcap, irq); + u32 pstat; + + ezx_pcap_read(pcap_keys->pcap, PCAP_REG_PSTAT, &pstat); + pstat &= 1 << pirq; + + switch (pirq) { + case PCAP_IRQ_ONOFF: + input_report_key(pcap_keys->input, KEY_POWER, !pstat); + break; + case PCAP_IRQ_MIC: + input_report_key(pcap_keys->input, KEY_HP, !pstat); + break; + } + + input_sync(pcap_keys->input); + + return IRQ_HANDLED; +} + +static int __devinit pcap_keys_probe(struct platform_device *pdev) +{ + int err = -ENOMEM; + struct pcap_keys *pcap_keys; + struct input_dev *input_dev; + + pcap_keys = kmalloc(sizeof(struct pcap_keys), GFP_KERNEL); + if (!pcap_keys) + return err; + + pcap_keys->pcap = dev_get_drvdata(pdev->dev.parent); + + input_dev = input_allocate_device(); + if (!input_dev) + goto fail; + + pcap_keys->input = input_dev; + + platform_set_drvdata(pdev, pcap_keys); + input_dev->name = pdev->name; + input_dev->phys = "pcap-keys/input0"; + input_dev->id.bustype = BUS_HOST; + input_dev->dev.parent = &pdev->dev; + + __set_bit(EV_KEY, input_dev->evbit); + __set_bit(KEY_POWER, input_dev->keybit); + __set_bit(KEY_HP, input_dev->keybit); + + err = input_register_device(input_dev); + if (err) + goto fail_allocate; + + err = request_irq(pcap_to_irq(pcap_keys->pcap, PCAP_IRQ_ONOFF), + pcap_keys_handler, 0, "Power key", pcap_keys); + if (err) + goto fail_register; + + err = request_irq(pcap_to_irq(pcap_keys->pcap, PCAP_IRQ_MIC), + pcap_keys_handler, 0, "Headphone button", pcap_keys); + if (err) + goto fail_pwrkey; + + return 0; + +fail_pwrkey: + free_irq(pcap_to_irq(pcap_keys->pcap, PCAP_IRQ_ONOFF), pcap_keys); +fail_register: + input_unregister_device(input_dev); + goto fail; +fail_allocate: + input_free_device(input_dev); +fail: + kfree(pcap_keys); + return err; +} + +static int __devexit pcap_keys_remove(struct platform_device *pdev) +{ + struct pcap_keys *pcap_keys = platform_get_drvdata(pdev); + + free_irq(pcap_to_irq(pcap_keys->pcap, PCAP_IRQ_ONOFF), pcap_keys); + free_irq(pcap_to_irq(pcap_keys->pcap, PCAP_IRQ_MIC), pcap_keys); + + input_unregister_device(pcap_keys->input); + kfree(pcap_keys); + + return 0; +} + +static struct platform_driver pcap_keys_device_driver = { + .probe = pcap_keys_probe, + .remove = __devexit_p(pcap_keys_remove), + .driver = { + .name = "pcap-keys", + .owner = THIS_MODULE, + } +}; + +static int __init pcap_keys_init(void) +{ + return platform_driver_register(&pcap_keys_device_driver); +}; + +static void __exit pcap_keys_exit(void) +{ + platform_driver_unregister(&pcap_keys_device_driver); +}; + +module_init(pcap_keys_init); +module_exit(pcap_keys_exit); + +MODULE_DESCRIPTION("Motorola PCAP2 input events driver"); +MODULE_AUTHOR("Ilya Petrov <ilya.muromec@gmail.com>"); +MODULE_LICENSE("GPL"); +MODULE_ALIAS("platform:pcap_keys"); diff --git a/drivers/input/misc/powermate.c b/drivers/input/misc/powermate.c index a53c4885fba..668913d1204 100644 --- a/drivers/input/misc/powermate.c +++ b/drivers/input/misc/powermate.c @@ -338,7 +338,7 @@ static int powermate_probe(struct usb_interface *intf, const struct usb_device_i pm->input = input_dev; usb_make_path(udev, pm->phys, sizeof(pm->phys)); - strlcpy(pm->phys, "/input0", sizeof(pm->phys)); + strlcat(pm->phys, "/input0", sizeof(pm->phys)); spin_lock_init(&pm->lock); diff --git a/drivers/input/misc/rotary_encoder.c b/drivers/input/misc/rotary_encoder.c index c806fbf1e17..3b9f588fc74 100644 --- a/drivers/input/misc/rotary_encoder.c +++ b/drivers/input/misc/rotary_encoder.c @@ -106,8 +106,8 @@ static int __devinit rotary_encoder_probe(struct platform_device *pdev) struct input_dev *input; int err; - if (!pdata || !pdata->steps) { - dev_err(&pdev->dev, "invalid platform data\n"); + if (!pdata) { + dev_err(&pdev->dev, "missing platform data\n"); return -ENOENT; } diff --git a/drivers/input/misc/sparcspkr.c b/drivers/input/misc/sparcspkr.c index c4f42311fde..b064419b90a 100644 --- a/drivers/input/misc/sparcspkr.c +++ b/drivers/input/misc/sparcspkr.c @@ -230,7 +230,7 @@ out_err: return err; } -static int bbc_remove(struct of_device *op) +static int __devexit bbc_remove(struct of_device *op) { struct sparcspkr_state *state = dev_get_drvdata(&op->dev); struct input_dev *input_dev = state->input_dev; @@ -308,7 +308,7 @@ out_err: return err; } -static int grover_remove(struct of_device *op) +static int __devexit grover_remove(struct of_device *op) { struct sparcspkr_state *state = dev_get_drvdata(&op->dev); struct grover_beep_info *info = &state->u.grover; diff --git a/drivers/input/misc/uinput.c b/drivers/input/misc/uinput.c index c5a49aba418..d3f57245420 100644 --- a/drivers/input/misc/uinput.c +++ b/drivers/input/misc/uinput.c @@ -30,6 +30,7 @@ * - first public version */ #include <linux/poll.h> +#include <linux/sched.h> #include <linux/slab.h> #include <linux/module.h> #include <linux/init.h> diff --git a/drivers/input/misc/winbond-cir.c b/drivers/input/misc/winbond-cir.c new file mode 100644 index 00000000000..33309fe44e2 --- /dev/null +++ b/drivers/input/misc/winbond-cir.c @@ -0,0 +1,1614 @@ +/* + * winbond-cir.c - Driver for the Consumer IR functionality of Winbond + * SuperI/O chips. + * + * Currently supports the Winbond WPCD376i chip (PNP id WEC1022), but + * could probably support others (Winbond WEC102X, NatSemi, etc) + * with minor modifications. + * + * Original Author: David Härdeman <david@hardeman.nu> + * Copyright (C) 2009 David Härdeman <david@hardeman.nu> + * + * Dedicated to Matilda, my newborn daughter, without whose loving attention + * this driver would have been finished in half the time and with a fraction + * of the bugs. + * + * Written using: + * o Winbond WPCD376I datasheet helpfully provided by Jesse Barnes at Intel + * o NatSemi PC87338/PC97338 datasheet (for the serial port stuff) + * o DSDT dumps + * + * Supported features: + * o RC6 + * o Wake-On-CIR functionality + * + * To do: + * o Test NEC and RC5 + * + * Left as an exercise for the reader: + * o Learning (I have neither the hardware, nor the need) + * o IR Transmit (ibid) + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +#include <linux/module.h> +#include <linux/pnp.h> +#include <linux/interrupt.h> +#include <linux/timer.h> +#include <linux/input.h> +#include <linux/leds.h> +#include <linux/list.h> +#include <linux/spinlock.h> +#include <linux/pci_ids.h> +#include <linux/io.h> +#include <linux/bitrev.h> +#include <linux/bitops.h> + +#define DRVNAME "winbond-cir" + +/* CEIR Wake-Up Registers, relative to data->wbase */ +#define WBCIR_REG_WCEIR_CTL 0x03 /* CEIR Receiver Control */ +#define WBCIR_REG_WCEIR_STS 0x04 /* CEIR Receiver Status */ +#define WBCIR_REG_WCEIR_EV_EN 0x05 /* CEIR Receiver Event Enable */ +#define WBCIR_REG_WCEIR_CNTL 0x06 /* CEIR Receiver Counter Low */ +#define WBCIR_REG_WCEIR_CNTH 0x07 /* CEIR Receiver Counter High */ +#define WBCIR_REG_WCEIR_INDEX 0x08 /* CEIR Receiver Index */ +#define WBCIR_REG_WCEIR_DATA 0x09 /* CEIR Receiver Data */ +#define WBCIR_REG_WCEIR_CSL 0x0A /* CEIR Re. Compare Strlen */ +#define WBCIR_REG_WCEIR_CFG1 0x0B /* CEIR Re. Configuration 1 */ +#define WBCIR_REG_WCEIR_CFG2 0x0C /* CEIR Re. Configuration 2 */ + +/* CEIR Enhanced Functionality Registers, relative to data->ebase */ +#define WBCIR_REG_ECEIR_CTS 0x00 /* Enhanced IR Control Status */ +#define WBCIR_REG_ECEIR_CCTL 0x01 /* Infrared Counter Control */ +#define WBCIR_REG_ECEIR_CNT_LO 0x02 /* Infrared Counter LSB */ +#define WBCIR_REG_ECEIR_CNT_HI 0x03 /* Infrared Counter MSB */ +#define WBCIR_REG_ECEIR_IREM 0x04 /* Infrared Emitter Status */ + +/* SP3 Banked Registers, relative to data->sbase */ +#define WBCIR_REG_SP3_BSR 0x03 /* Bank Select, all banks */ + /* Bank 0 */ +#define WBCIR_REG_SP3_RXDATA 0x00 /* FIFO RX data (r) */ +#define WBCIR_REG_SP3_TXDATA 0x00 /* FIFO TX data (w) */ +#define WBCIR_REG_SP3_IER 0x01 /* Interrupt Enable */ +#define WBCIR_REG_SP3_EIR 0x02 /* Event Identification (r) */ +#define WBCIR_REG_SP3_FCR 0x02 /* FIFO Control (w) */ +#define WBCIR_REG_SP3_MCR 0x04 /* Mode Control */ +#define WBCIR_REG_SP3_LSR 0x05 /* Link Status */ +#define WBCIR_REG_SP3_MSR 0x06 /* Modem Status */ +#define WBCIR_REG_SP3_ASCR 0x07 /* Aux Status and Control */ + /* Bank 2 */ +#define WBCIR_REG_SP3_BGDL 0x00 /* Baud Divisor LSB */ +#define WBCIR_REG_SP3_BGDH 0x01 /* Baud Divisor MSB */ +#define WBCIR_REG_SP3_EXCR1 0x02 /* Extended Control 1 */ +#define WBCIR_REG_SP3_EXCR2 0x04 /* Extended Control 2 */ +#define WBCIR_REG_SP3_TXFLV 0x06 /* TX FIFO Level */ +#define WBCIR_REG_SP3_RXFLV 0x07 /* RX FIFO Level */ + /* Bank 3 */ +#define WBCIR_REG_SP3_MRID 0x00 /* Module Identification */ +#define WBCIR_REG_SP3_SH_LCR 0x01 /* LCR Shadow */ +#define WBCIR_REG_SP3_SH_FCR 0x02 /* FCR Shadow */ + /* Bank 4 */ +#define WBCIR_REG_SP3_IRCR1 0x02 /* Infrared Control 1 */ + /* Bank 5 */ +#define WBCIR_REG_SP3_IRCR2 0x04 /* Infrared Control 2 */ + /* Bank 6 */ +#define WBCIR_REG_SP3_IRCR3 0x00 /* Infrared Control 3 */ +#define WBCIR_REG_SP3_SIR_PW 0x02 /* SIR Pulse Width */ + /* Bank 7 */ +#define WBCIR_REG_SP3_IRRXDC 0x00 /* IR RX Demod Control */ +#define WBCIR_REG_SP3_IRTXMC 0x01 /* IR TX Mod Control */ +#define WBCIR_REG_SP3_RCCFG 0x02 /* CEIR Config */ +#define WBCIR_REG_SP3_IRCFG1 0x04 /* Infrared Config 1 */ +#define WBCIR_REG_SP3_IRCFG4 0x07 /* Infrared Config 4 */ + +/* + * Magic values follow + */ + +/* No interrupts for WBCIR_REG_SP3_IER and WBCIR_REG_SP3_EIR */ +#define WBCIR_IRQ_NONE 0x00 +/* RX data bit for WBCIR_REG_SP3_IER and WBCIR_REG_SP3_EIR */ +#define WBCIR_IRQ_RX 0x01 +/* Over/Under-flow bit for WBCIR_REG_SP3_IER and WBCIR_REG_SP3_EIR */ +#define WBCIR_IRQ_ERR 0x04 +/* Led enable/disable bit for WBCIR_REG_ECEIR_CTS */ +#define WBCIR_LED_ENABLE 0x80 +/* RX data available bit for WBCIR_REG_SP3_LSR */ +#define WBCIR_RX_AVAIL 0x01 +/* RX disable bit for WBCIR_REG_SP3_ASCR */ +#define WBCIR_RX_DISABLE 0x20 +/* Extended mode enable bit for WBCIR_REG_SP3_EXCR1 */ +#define WBCIR_EXT_ENABLE 0x01 +/* Select compare register in WBCIR_REG_WCEIR_INDEX (bits 5 & 6) */ +#define WBCIR_REGSEL_COMPARE 0x10 +/* Select mask register in WBCIR_REG_WCEIR_INDEX (bits 5 & 6) */ +#define WBCIR_REGSEL_MASK 0x20 +/* Starting address of selected register in WBCIR_REG_WCEIR_INDEX */ +#define WBCIR_REG_ADDR0 0x00 + +/* Valid banks for the SP3 UART */ +enum wbcir_bank { + WBCIR_BANK_0 = 0x00, + WBCIR_BANK_1 = 0x80, + WBCIR_BANK_2 = 0xE0, + WBCIR_BANK_3 = 0xE4, + WBCIR_BANK_4 = 0xE8, + WBCIR_BANK_5 = 0xEC, + WBCIR_BANK_6 = 0xF0, + WBCIR_BANK_7 = 0xF4, +}; + +/* Supported IR Protocols */ +enum wbcir_protocol { + IR_PROTOCOL_RC5 = 0x0, + IR_PROTOCOL_NEC = 0x1, + IR_PROTOCOL_RC6 = 0x2, +}; + +/* Misc */ +#define WBCIR_NAME "Winbond CIR" +#define WBCIR_ID_FAMILY 0xF1 /* Family ID for the WPCD376I */ +#define WBCIR_ID_CHIP 0x04 /* Chip ID for the WPCD376I */ +#define IR_KEYPRESS_TIMEOUT 250 /* FIXME: should be per-protocol? */ +#define INVALID_SCANCODE 0x7FFFFFFF /* Invalid with all protos */ +#define WAKEUP_IOMEM_LEN 0x10 /* Wake-Up I/O Reg Len */ +#define EHFUNC_IOMEM_LEN 0x10 /* Enhanced Func I/O Reg Len */ +#define SP_IOMEM_LEN 0x08 /* Serial Port 3 (IR) Reg Len */ +#define WBCIR_MAX_IDLE_BYTES 10 + +static DEFINE_SPINLOCK(wbcir_lock); +static DEFINE_RWLOCK(keytable_lock); + +struct wbcir_key { + u32 scancode; + unsigned int keycode; +}; + +struct wbcir_keyentry { + struct wbcir_key key; + struct list_head list; +}; + +static struct wbcir_key rc6_def_keymap[] = { + { 0x800F0400, KEY_NUMERIC_0 }, + { 0x800F0401, KEY_NUMERIC_1 }, + { 0x800F0402, KEY_NUMERIC_2 }, + { 0x800F0403, KEY_NUMERIC_3 }, + { 0x800F0404, KEY_NUMERIC_4 }, + { 0x800F0405, KEY_NUMERIC_5 }, + { 0x800F0406, KEY_NUMERIC_6 }, + { 0x800F0407, KEY_NUMERIC_7 }, + { 0x800F0408, KEY_NUMERIC_8 }, + { 0x800F0409, KEY_NUMERIC_9 }, + { 0x800F041D, KEY_NUMERIC_STAR }, + { 0x800F041C, KEY_NUMERIC_POUND }, + { 0x800F0410, KEY_VOLUMEUP }, + { 0x800F0411, KEY_VOLUMEDOWN }, + { 0x800F0412, KEY_CHANNELUP }, + { 0x800F0413, KEY_CHANNELDOWN }, + { 0x800F040E, KEY_MUTE }, + { 0x800F040D, KEY_VENDOR }, /* Vista Logo Key */ + { 0x800F041E, KEY_UP }, + { 0x800F041F, KEY_DOWN }, + { 0x800F0420, KEY_LEFT }, + { 0x800F0421, KEY_RIGHT }, + { 0x800F0422, KEY_OK }, + { 0x800F0423, KEY_ESC }, + { 0x800F040F, KEY_INFO }, + { 0x800F040A, KEY_CLEAR }, + { 0x800F040B, KEY_ENTER }, + { 0x800F045B, KEY_RED }, + { 0x800F045C, KEY_GREEN }, + { 0x800F045D, KEY_YELLOW }, + { 0x800F045E, KEY_BLUE }, + { 0x800F045A, KEY_TEXT }, + { 0x800F0427, KEY_SWITCHVIDEOMODE }, + { 0x800F040C, KEY_POWER }, + { 0x800F0450, KEY_RADIO }, + { 0x800F0448, KEY_PVR }, + { 0x800F0447, KEY_AUDIO }, + { 0x800F0426, KEY_EPG }, + { 0x800F0449, KEY_CAMERA }, + { 0x800F0425, KEY_TV }, + { 0x800F044A, KEY_VIDEO }, + { 0x800F0424, KEY_DVD }, + { 0x800F0416, KEY_PLAY }, + { 0x800F0418, KEY_PAUSE }, + { 0x800F0419, KEY_STOP }, + { 0x800F0414, KEY_FASTFORWARD }, + { 0x800F041A, KEY_NEXT }, + { 0x800F041B, KEY_PREVIOUS }, + { 0x800F0415, KEY_REWIND }, + { 0x800F0417, KEY_RECORD }, +}; + +/* Registers and other state is protected by wbcir_lock */ +struct wbcir_data { + unsigned long wbase; /* Wake-Up Baseaddr */ + unsigned long ebase; /* Enhanced Func. Baseaddr */ + unsigned long sbase; /* Serial Port Baseaddr */ + unsigned int irq; /* Serial Port IRQ */ + + struct input_dev *input_dev; + struct timer_list timer_keyup; + struct led_trigger *rxtrigger; + struct led_trigger *txtrigger; + struct led_classdev led; + + u32 last_scancode; + unsigned int last_keycode; + u8 last_toggle; + u8 keypressed; + unsigned long keyup_jiffies; + unsigned int idle_count; + + /* RX irdata and parsing state */ + unsigned long irdata[30]; + unsigned int irdata_count; + unsigned int irdata_idle; + unsigned int irdata_off; + unsigned int irdata_error; + + /* Protected by keytable_lock */ + struct list_head keytable; +}; + +static enum wbcir_protocol protocol = IR_PROTOCOL_RC6; +module_param(protocol, uint, 0444); +MODULE_PARM_DESC(protocol, "IR protocol to use " + "(0 = RC5, 1 = NEC, 2 = RC6A, default)"); + +static int invert; /* default = 0 */ +module_param(invert, bool, 0444); +MODULE_PARM_DESC(invert, "Invert the signal from the IR receiver"); + +static unsigned int wake_sc = 0x800F040C; +module_param(wake_sc, uint, 0644); +MODULE_PARM_DESC(wake_sc, "Scancode of the power-on IR command"); + +static unsigned int wake_rc6mode = 6; +module_param(wake_rc6mode, uint, 0644); +MODULE_PARM_DESC(wake_rc6mode, "RC6 mode for the power-on command " + "(0 = 0, 6 = 6A, default)"); + + + +/***************************************************************************** + * + * UTILITY FUNCTIONS + * + *****************************************************************************/ + +/* Caller needs to hold wbcir_lock */ +static void +wbcir_set_bits(unsigned long addr, u8 bits, u8 mask) +{ + u8 val; + + val = inb(addr); + val = ((val & ~mask) | (bits & mask)); + outb(val, addr); +} + +/* Selects the register bank for the serial port */ +static inline void +wbcir_select_bank(struct wbcir_data *data, enum wbcir_bank bank) +{ + outb(bank, data->sbase + WBCIR_REG_SP3_BSR); +} + +static enum led_brightness +wbcir_led_brightness_get(struct led_classdev *led_cdev) +{ + struct wbcir_data *data = container_of(led_cdev, + struct wbcir_data, + led); + + if (inb(data->ebase + WBCIR_REG_ECEIR_CTS) & WBCIR_LED_ENABLE) + return LED_FULL; + else + return LED_OFF; +} + +static void +wbcir_led_brightness_set(struct led_classdev *led_cdev, + enum led_brightness brightness) +{ + struct wbcir_data *data = container_of(led_cdev, + struct wbcir_data, + led); + + wbcir_set_bits(data->ebase + WBCIR_REG_ECEIR_CTS, + brightness == LED_OFF ? 0x00 : WBCIR_LED_ENABLE, + WBCIR_LED_ENABLE); +} + +/* Manchester encodes bits to RC6 message cells (see wbcir_parse_rc6) */ +static u8 +wbcir_to_rc6cells(u8 val) +{ + u8 coded = 0x00; + int i; + + val &= 0x0F; + for (i = 0; i < 4; i++) { + if (val & 0x01) + coded |= 0x02 << (i * 2); + else + coded |= 0x01 << (i * 2); + val >>= 1; + } + + return coded; +} + + + +/***************************************************************************** + * + * INPUT FUNCTIONS + * + *****************************************************************************/ + +static unsigned int +wbcir_do_getkeycode(struct wbcir_data *data, u32 scancode) +{ + struct wbcir_keyentry *keyentry; + unsigned int keycode = KEY_RESERVED; + unsigned long flags; + + read_lock_irqsave(&keytable_lock, flags); + + list_for_each_entry(keyentry, &data->keytable, list) { + if (keyentry->key.scancode == scancode) { + keycode = keyentry->key.keycode; + break; + } + } + + read_unlock_irqrestore(&keytable_lock, flags); + return keycode; +} + +static int +wbcir_getkeycode(struct input_dev *dev, int scancode, int *keycode) +{ + struct wbcir_data *data = input_get_drvdata(dev); + + *keycode = (int)wbcir_do_getkeycode(data, (u32)scancode); + return 0; +} + +static int +wbcir_setkeycode(struct input_dev *dev, int sscancode, int keycode) +{ + struct wbcir_data *data = input_get_drvdata(dev); + struct wbcir_keyentry *keyentry; + struct wbcir_keyentry *new_keyentry; + unsigned long flags; + unsigned int old_keycode = KEY_RESERVED; + u32 scancode = (u32)sscancode; + + if (keycode < 0 || keycode > KEY_MAX) + return -EINVAL; + + new_keyentry = kmalloc(sizeof(*new_keyentry), GFP_KERNEL); + if (!new_keyentry) + return -ENOMEM; + + write_lock_irqsave(&keytable_lock, flags); + + list_for_each_entry(keyentry, &data->keytable, list) { + if (keyentry->key.scancode != scancode) + continue; + + old_keycode = keyentry->key.keycode; + keyentry->key.keycode = keycode; + + if (keyentry->key.keycode == KEY_RESERVED) { + list_del(&keyentry->list); + kfree(keyentry); + } + + break; + } + + set_bit(keycode, dev->keybit); + + if (old_keycode == KEY_RESERVED) { + new_keyentry->key.scancode = scancode; + new_keyentry->key.keycode = keycode; + list_add(&new_keyentry->list, &data->keytable); + } else { + kfree(new_keyentry); + clear_bit(old_keycode, dev->keybit); + list_for_each_entry(keyentry, &data->keytable, list) { + if (keyentry->key.keycode == old_keycode) { + set_bit(old_keycode, dev->keybit); + break; + } + } + } + + write_unlock_irqrestore(&keytable_lock, flags); + return 0; +} + +/* + * Timer function to report keyup event some time after keydown is + * reported by the ISR. + */ +static void +wbcir_keyup(unsigned long cookie) +{ + struct wbcir_data *data = (struct wbcir_data *)cookie; + unsigned long flags; + + /* + * data->keyup_jiffies is used to prevent a race condition if a + * hardware interrupt occurs at this point and the keyup timer + * event is moved further into the future as a result. + * + * The timer will then be reactivated and this function called + * again in the future. We need to exit gracefully in that case + * to allow the input subsystem to do its auto-repeat magic or + * a keyup event might follow immediately after the keydown. + */ + + spin_lock_irqsave(&wbcir_lock, flags); + + if (time_is_after_eq_jiffies(data->keyup_jiffies) && data->keypressed) { + data->keypressed = 0; + led_trigger_event(data->rxtrigger, LED_OFF); + input_report_key(data->input_dev, data->last_keycode, 0); + input_sync(data->input_dev); + } + + spin_unlock_irqrestore(&wbcir_lock, flags); +} + +static void +wbcir_keydown(struct wbcir_data *data, u32 scancode, u8 toggle) +{ + unsigned int keycode; + + /* Repeat? */ + if (data->last_scancode == scancode && + data->last_toggle == toggle && + data->keypressed) + goto set_timer; + data->last_scancode = scancode; + + /* Do we need to release an old keypress? */ + if (data->keypressed) { + input_report_key(data->input_dev, data->last_keycode, 0); + input_sync(data->input_dev); + data->keypressed = 0; + } + + /* Report scancode */ + input_event(data->input_dev, EV_MSC, MSC_SCAN, (int)scancode); + + /* Do we know this scancode? */ + keycode = wbcir_do_getkeycode(data, scancode); + if (keycode == KEY_RESERVED) + goto set_timer; + + /* Register a keypress */ + input_report_key(data->input_dev, keycode, 1); + data->keypressed = 1; + data->last_keycode = keycode; + data->last_toggle = toggle; + +set_timer: + input_sync(data->input_dev); + led_trigger_event(data->rxtrigger, + data->keypressed ? LED_FULL : LED_OFF); + data->keyup_jiffies = jiffies + msecs_to_jiffies(IR_KEYPRESS_TIMEOUT); + mod_timer(&data->timer_keyup, data->keyup_jiffies); +} + + + +/***************************************************************************** + * + * IR PARSING FUNCTIONS + * + *****************************************************************************/ + +/* Resets all irdata */ +static void +wbcir_reset_irdata(struct wbcir_data *data) +{ + memset(data->irdata, 0, sizeof(data->irdata)); + data->irdata_count = 0; + data->irdata_off = 0; + data->irdata_error = 0; +} + +/* Adds one bit of irdata */ +static void +add_irdata_bit(struct wbcir_data *data, int set) +{ + if (data->irdata_count >= sizeof(data->irdata) * 8) { + data->irdata_error = 1; + return; + } + + if (set) + __set_bit(data->irdata_count, data->irdata); + data->irdata_count++; +} + +/* Gets count bits of irdata */ +static u16 +get_bits(struct wbcir_data *data, int count) +{ + u16 val = 0x0; + + if (data->irdata_count - data->irdata_off < count) { + data->irdata_error = 1; + return 0x0; + } + + while (count > 0) { + val <<= 1; + if (test_bit(data->irdata_off, data->irdata)) + val |= 0x1; + count--; + data->irdata_off++; + } + + return val; +} + +/* Reads 16 cells and converts them to a byte */ +static u8 +wbcir_rc6cells_to_byte(struct wbcir_data *data) +{ + u16 raw = get_bits(data, 16); + u8 val = 0x00; + int bit; + + for (bit = 0; bit < 8; bit++) { + switch (raw & 0x03) { + case 0x01: + break; + case 0x02: + val |= (0x01 << bit); + break; + default: + data->irdata_error = 1; + break; + } + raw >>= 2; + } + + return val; +} + +/* Decodes a number of bits from raw RC5 data */ +static u8 +wbcir_get_rc5bits(struct wbcir_data *data, unsigned int count) +{ + u16 raw = get_bits(data, count * 2); + u8 val = 0x00; + int bit; + + for (bit = 0; bit < count; bit++) { + switch (raw & 0x03) { + case 0x01: + val |= (0x01 << bit); + break; + case 0x02: + break; + default: + data->irdata_error = 1; + break; + } + raw >>= 2; + } + + return val; +} + +static void +wbcir_parse_rc6(struct device *dev, struct wbcir_data *data) +{ + /* + * Normal bits are manchester coded as follows: + * cell0 + cell1 = logic "0" + * cell1 + cell0 = logic "1" + * + * The IR pulse has the following components: + * + * Leader - 6 * cell1 - discarded + * Gap - 2 * cell0 - discarded + * Start bit - Normal Coding - always "1" + * Mode Bit 2 - 0 - Normal Coding + * Toggle bit - Normal Coding with double bit time, + * e.g. cell0 + cell0 + cell1 + cell1 + * means logic "0". + * + * The rest depends on the mode, the following modes are known: + * + * MODE 0: + * Address Bit 7 - 0 - Normal Coding + * Command Bit 7 - 0 - Normal Coding + * + * MODE 6: + * The above Toggle Bit is used as a submode bit, 0 = A, 1 = B. + * Submode B is for pointing devices, only remotes using submode A + * are supported. + * + * Customer range bit - 0 => Customer = 7 bits, 0...127 + * 1 => Customer = 15 bits, 32768...65535 + * Customer Bits - Normal Coding + * + * Customer codes are allocated by Philips. The rest of the bits + * are customer dependent. The following is commonly used (and the + * only supported config): + * + * Toggle Bit - Normal Coding + * Address Bit 6 - 0 - Normal Coding + * Command Bit 7 - 0 - Normal Coding + * + * All modes are followed by at least 6 * cell0. + * + * MODE 0 msglen: + * 1 * 2 (start bit) + 3 * 2 (mode) + 2 * 2 (toggle) + + * 8 * 2 (address) + 8 * 2 (command) = + * 44 cells + * + * MODE 6A msglen: + * 1 * 2 (start bit) + 3 * 2 (mode) + 2 * 2 (submode) + + * 1 * 2 (customer range bit) + 7/15 * 2 (customer bits) + + * 1 * 2 (toggle bit) + 7 * 2 (address) + 8 * 2 (command) = + * 60 - 76 cells + */ + u8 mode; + u8 toggle; + u16 customer = 0x0; + u8 address; + u8 command; + u32 scancode; + + /* Leader mark */ + while (get_bits(data, 1) && !data->irdata_error) + /* Do nothing */; + + /* Leader space */ + if (get_bits(data, 1)) { + dev_dbg(dev, "RC6 - Invalid leader space\n"); + return; + } + + /* Start bit */ + if (get_bits(data, 2) != 0x02) { + dev_dbg(dev, "RC6 - Invalid start bit\n"); + return; + } + + /* Mode */ + mode = get_bits(data, 6); + switch (mode) { + case 0x15: /* 010101 = b000 */ + mode = 0; + break; + case 0x29: /* 101001 = b110 */ + mode = 6; + break; + default: + dev_dbg(dev, "RC6 - Invalid mode\n"); + return; + } + + /* Toggle bit / Submode bit */ + toggle = get_bits(data, 4); + switch (toggle) { + case 0x03: + toggle = 0; + break; + case 0x0C: + toggle = 1; + break; + default: + dev_dbg(dev, "RC6 - Toggle bit error\n"); + break; + } + + /* Customer */ + if (mode == 6) { + if (toggle != 0) { + dev_dbg(dev, "RC6B - Not Supported\n"); + return; + } + + customer = wbcir_rc6cells_to_byte(data); + + if (customer & 0x80) { + /* 15 bit customer value */ + customer <<= 8; + customer |= wbcir_rc6cells_to_byte(data); + } + } + + /* Address */ + address = wbcir_rc6cells_to_byte(data); + if (mode == 6) { + toggle = address >> 7; + address &= 0x7F; + } + + /* Command */ + command = wbcir_rc6cells_to_byte(data); + + /* Create scancode */ + scancode = command; + scancode |= address << 8; + scancode |= customer << 16; + + /* Last sanity check */ + if (data->irdata_error) { + dev_dbg(dev, "RC6 - Cell error(s)\n"); + return; + } + + dev_info(dev, "IR-RC6 ad 0x%02X cm 0x%02X cu 0x%04X " + "toggle %u mode %u scan 0x%08X\n", + address, + command, + customer, + (unsigned int)toggle, + (unsigned int)mode, + scancode); + + wbcir_keydown(data, scancode, toggle); +} + +static void +wbcir_parse_rc5(struct device *dev, struct wbcir_data *data) +{ + /* + * Bits are manchester coded as follows: + * cell1 + cell0 = logic "0" + * cell0 + cell1 = logic "1" + * (i.e. the reverse of RC6) + * + * Start bit 1 - "1" - discarded + * Start bit 2 - Must be inverted to get command bit 6 + * Toggle bit + * Address Bit 4 - 0 + * Command Bit 5 - 0 + */ + u8 toggle; + u8 address; + u8 command; + u32 scancode; + + /* Start bit 1 */ + if (!get_bits(data, 1)) { + dev_dbg(dev, "RC5 - Invalid start bit\n"); + return; + } + + /* Start bit 2 */ + if (!wbcir_get_rc5bits(data, 1)) + command = 0x40; + else + command = 0x00; + + toggle = wbcir_get_rc5bits(data, 1); + address = wbcir_get_rc5bits(data, 5); + command |= wbcir_get_rc5bits(data, 6); + scancode = address << 7 | command; + + /* Last sanity check */ + if (data->irdata_error) { + dev_dbg(dev, "RC5 - Invalid message\n"); + return; + } + + dev_dbg(dev, "IR-RC5 ad %u cm %u t %u s %u\n", + (unsigned int)address, + (unsigned int)command, + (unsigned int)toggle, + (unsigned int)scancode); + + wbcir_keydown(data, scancode, toggle); +} + +static void +wbcir_parse_nec(struct device *dev, struct wbcir_data *data) +{ + /* + * Each bit represents 560 us. + * + * Leader - 9 ms burst + * Gap - 4.5 ms silence + * Address1 bit 0 - 7 - Address 1 + * Address2 bit 0 - 7 - Address 2 + * Command1 bit 0 - 7 - Command 1 + * Command2 bit 0 - 7 - Command 2 + * + * Note the bit order! + * + * With the old NEC protocol, Address2 was the inverse of Address1 + * and Command2 was the inverse of Command1 and were used as + * an error check. + * + * With NEC extended, Address1 is the LSB of the Address and + * Address2 is the MSB, Command parsing remains unchanged. + * + * A repeat message is coded as: + * Leader - 9 ms burst + * Gap - 2.25 ms silence + * Repeat - 560 us active + */ + u8 address1; + u8 address2; + u8 command1; + u8 command2; + u16 address; + u32 scancode; + + /* Leader mark */ + while (get_bits(data, 1) && !data->irdata_error) + /* Do nothing */; + + /* Leader space */ + if (get_bits(data, 4)) { + dev_dbg(dev, "NEC - Invalid leader space\n"); + return; + } + + /* Repeat? */ + if (get_bits(data, 1)) { + if (!data->keypressed) { + dev_dbg(dev, "NEC - Stray repeat message\n"); + return; + } + + dev_dbg(dev, "IR-NEC repeat s %u\n", + (unsigned int)data->last_scancode); + + wbcir_keydown(data, data->last_scancode, data->last_toggle); + return; + } + + /* Remaining leader space */ + if (get_bits(data, 3)) { + dev_dbg(dev, "NEC - Invalid leader space\n"); + return; + } + + address1 = bitrev8(get_bits(data, 8)); + address2 = bitrev8(get_bits(data, 8)); + command1 = bitrev8(get_bits(data, 8)); + command2 = bitrev8(get_bits(data, 8)); + + /* Sanity check */ + if (data->irdata_error) { + dev_dbg(dev, "NEC - Invalid message\n"); + return; + } + + /* Check command validity */ + if (command1 != ~command2) { + dev_dbg(dev, "NEC - Command bytes mismatch\n"); + return; + } + + /* Check for extended NEC protocol */ + address = address1; + if (address1 != ~address2) + address |= address2 << 8; + + scancode = address << 8 | command1; + + dev_dbg(dev, "IR-NEC ad %u cm %u s %u\n", + (unsigned int)address, + (unsigned int)command1, + (unsigned int)scancode); + + wbcir_keydown(data, scancode, !data->last_toggle); +} + + + +/***************************************************************************** + * + * INTERRUPT FUNCTIONS + * + *****************************************************************************/ + +static irqreturn_t +wbcir_irq_handler(int irqno, void *cookie) +{ + struct pnp_dev *device = cookie; + struct wbcir_data *data = pnp_get_drvdata(device); + struct device *dev = &device->dev; + u8 status; + unsigned long flags; + u8 irdata[8]; + int i; + unsigned int hw; + + spin_lock_irqsave(&wbcir_lock, flags); + + wbcir_select_bank(data, WBCIR_BANK_0); + + status = inb(data->sbase + WBCIR_REG_SP3_EIR); + + if (!(status & (WBCIR_IRQ_RX | WBCIR_IRQ_ERR))) { + spin_unlock_irqrestore(&wbcir_lock, flags); + return IRQ_NONE; + } + + if (status & WBCIR_IRQ_ERR) + data->irdata_error = 1; + + if (!(status & WBCIR_IRQ_RX)) + goto out; + + /* Since RXHDLEV is set, at least 8 bytes are in the FIFO */ + insb(data->sbase + WBCIR_REG_SP3_RXDATA, &irdata[0], 8); + + for (i = 0; i < sizeof(irdata); i++) { + hw = hweight8(irdata[i]); + if (hw > 4) + add_irdata_bit(data, 0); + else + add_irdata_bit(data, 1); + + if (hw == 8) + data->idle_count++; + else + data->idle_count = 0; + } + + if (data->idle_count > WBCIR_MAX_IDLE_BYTES) { + /* Set RXINACTIVE... */ + outb(WBCIR_RX_DISABLE, data->sbase + WBCIR_REG_SP3_ASCR); + + /* ...and drain the FIFO */ + while (inb(data->sbase + WBCIR_REG_SP3_LSR) & WBCIR_RX_AVAIL) + inb(data->sbase + WBCIR_REG_SP3_RXDATA); + + dev_dbg(dev, "IRDATA:\n"); + for (i = 0; i < data->irdata_count; i += BITS_PER_LONG) + dev_dbg(dev, "0x%08lX\n", data->irdata[i/BITS_PER_LONG]); + + switch (protocol) { + case IR_PROTOCOL_RC5: + wbcir_parse_rc5(dev, data); + break; + case IR_PROTOCOL_RC6: + wbcir_parse_rc6(dev, data); + break; + case IR_PROTOCOL_NEC: + wbcir_parse_nec(dev, data); + break; + } + + wbcir_reset_irdata(data); + data->idle_count = 0; + } + +out: + spin_unlock_irqrestore(&wbcir_lock, flags); + return IRQ_HANDLED; +} + + + +/***************************************************************************** + * + * SUSPEND/RESUME FUNCTIONS + * + *****************************************************************************/ + +static void +wbcir_shutdown(struct pnp_dev *device) +{ + struct device *dev = &device->dev; + struct wbcir_data *data = pnp_get_drvdata(device); + int do_wake = 1; + u8 match[11]; + u8 mask[11]; + u8 rc6_csl = 0; + int i; + + memset(match, 0, sizeof(match)); + memset(mask, 0, sizeof(mask)); + + if (wake_sc == INVALID_SCANCODE || !device_may_wakeup(dev)) { + do_wake = 0; + goto finish; + } + + switch (protocol) { + case IR_PROTOCOL_RC5: + if (wake_sc > 0xFFF) { + do_wake = 0; + dev_err(dev, "RC5 - Invalid wake scancode\n"); + break; + } + + /* Mask = 13 bits, ex toggle */ + mask[0] = 0xFF; + mask[1] = 0x17; + + match[0] = (wake_sc & 0x003F); /* 6 command bits */ + match[0] |= (wake_sc & 0x0180) >> 1; /* 2 address bits */ + match[1] = (wake_sc & 0x0E00) >> 9; /* 3 address bits */ + if (!(wake_sc & 0x0040)) /* 2nd start bit */ + match[1] |= 0x10; + + break; + + case IR_PROTOCOL_NEC: + if (wake_sc > 0xFFFFFF) { + do_wake = 0; + dev_err(dev, "NEC - Invalid wake scancode\n"); + break; + } + + mask[0] = mask[1] = mask[2] = mask[3] = 0xFF; + + match[1] = bitrev8((wake_sc & 0xFF)); + match[0] = ~match[1]; + + match[3] = bitrev8((wake_sc & 0xFF00) >> 8); + if (wake_sc > 0xFFFF) + match[2] = bitrev8((wake_sc & 0xFF0000) >> 16); + else + match[2] = ~match[3]; + + break; + + case IR_PROTOCOL_RC6: + + if (wake_rc6mode == 0) { + if (wake_sc > 0xFFFF) { + do_wake = 0; + dev_err(dev, "RC6 - Invalid wake scancode\n"); + break; + } + + /* Command */ + match[0] = wbcir_to_rc6cells(wake_sc >> 0); + mask[0] = 0xFF; + match[1] = wbcir_to_rc6cells(wake_sc >> 4); + mask[1] = 0xFF; + + /* Address */ + match[2] = wbcir_to_rc6cells(wake_sc >> 8); + mask[2] = 0xFF; + match[3] = wbcir_to_rc6cells(wake_sc >> 12); + mask[3] = 0xFF; + + /* Header */ + match[4] = 0x50; /* mode1 = mode0 = 0, ignore toggle */ + mask[4] = 0xF0; + match[5] = 0x09; /* start bit = 1, mode2 = 0 */ + mask[5] = 0x0F; + + rc6_csl = 44; + + } else if (wake_rc6mode == 6) { + i = 0; + + /* Command */ + match[i] = wbcir_to_rc6cells(wake_sc >> 0); + mask[i++] = 0xFF; + match[i] = wbcir_to_rc6cells(wake_sc >> 4); + mask[i++] = 0xFF; + + /* Address + Toggle */ + match[i] = wbcir_to_rc6cells(wake_sc >> 8); + mask[i++] = 0xFF; + match[i] = wbcir_to_rc6cells(wake_sc >> 12); + mask[i++] = 0x3F; + + /* Customer bits 7 - 0 */ + match[i] = wbcir_to_rc6cells(wake_sc >> 16); + mask[i++] = 0xFF; + match[i] = wbcir_to_rc6cells(wake_sc >> 20); + mask[i++] = 0xFF; + + if (wake_sc & 0x80000000) { + /* Customer range bit and bits 15 - 8 */ + match[i] = wbcir_to_rc6cells(wake_sc >> 24); + mask[i++] = 0xFF; + match[i] = wbcir_to_rc6cells(wake_sc >> 28); + mask[i++] = 0xFF; + rc6_csl = 76; + } else if (wake_sc <= 0x007FFFFF) { + rc6_csl = 60; + } else { + do_wake = 0; + dev_err(dev, "RC6 - Invalid wake scancode\n"); + break; + } + + /* Header */ + match[i] = 0x93; /* mode1 = mode0 = 1, submode = 0 */ + mask[i++] = 0xFF; + match[i] = 0x0A; /* start bit = 1, mode2 = 1 */ + mask[i++] = 0x0F; + + } else { + do_wake = 0; + dev_err(dev, "RC6 - Invalid wake mode\n"); + } + + break; + + default: + do_wake = 0; + break; + } + +finish: + if (do_wake) { + /* Set compare and compare mask */ + wbcir_set_bits(data->wbase + WBCIR_REG_WCEIR_INDEX, + WBCIR_REGSEL_COMPARE | WBCIR_REG_ADDR0, + 0x3F); + outsb(data->wbase + WBCIR_REG_WCEIR_DATA, match, 11); + wbcir_set_bits(data->wbase + WBCIR_REG_WCEIR_INDEX, + WBCIR_REGSEL_MASK | WBCIR_REG_ADDR0, + 0x3F); + outsb(data->wbase + WBCIR_REG_WCEIR_DATA, mask, 11); + + /* RC6 Compare String Len */ + outb(rc6_csl, data->wbase + WBCIR_REG_WCEIR_CSL); + + /* Clear status bits NEC_REP, BUFF, MSG_END, MATCH */ + wbcir_set_bits(data->wbase + WBCIR_REG_WCEIR_STS, 0x17, 0x17); + + /* Clear BUFF_EN, Clear END_EN, Set MATCH_EN */ + wbcir_set_bits(data->wbase + WBCIR_REG_WCEIR_EV_EN, 0x01, 0x07); + + /* Set CEIR_EN */ + wbcir_set_bits(data->wbase + WBCIR_REG_WCEIR_CTL, 0x01, 0x01); + + } else { + /* Clear BUFF_EN, Clear END_EN, Clear MATCH_EN */ + wbcir_set_bits(data->wbase + WBCIR_REG_WCEIR_EV_EN, 0x00, 0x07); + + /* Clear CEIR_EN */ + wbcir_set_bits(data->wbase + WBCIR_REG_WCEIR_CTL, 0x00, 0x01); + } + + /* Disable interrupts */ + outb(WBCIR_IRQ_NONE, data->sbase + WBCIR_REG_SP3_IER); +} + +static int +wbcir_suspend(struct pnp_dev *device, pm_message_t state) +{ + wbcir_shutdown(device); + return 0; +} + +static int +wbcir_resume(struct pnp_dev *device) +{ + struct wbcir_data *data = pnp_get_drvdata(device); + + /* Clear BUFF_EN, Clear END_EN, Clear MATCH_EN */ + wbcir_set_bits(data->wbase + WBCIR_REG_WCEIR_EV_EN, 0x00, 0x07); + + /* Clear CEIR_EN */ + wbcir_set_bits(data->wbase + WBCIR_REG_WCEIR_CTL, 0x00, 0x01); + + /* Enable interrupts */ + wbcir_reset_irdata(data); + outb(WBCIR_IRQ_RX | WBCIR_IRQ_ERR, data->sbase + WBCIR_REG_SP3_IER); + + return 0; +} + + + +/***************************************************************************** + * + * SETUP/INIT FUNCTIONS + * + *****************************************************************************/ + +static void +wbcir_cfg_ceir(struct wbcir_data *data) +{ + u8 tmp; + + /* Set PROT_SEL, RX_INV, Clear CEIR_EN (needed for the led) */ + tmp = protocol << 4; + if (invert) + tmp |= 0x08; + outb(tmp, data->wbase + WBCIR_REG_WCEIR_CTL); + + /* Clear status bits NEC_REP, BUFF, MSG_END, MATCH */ + wbcir_set_bits(data->wbase + WBCIR_REG_WCEIR_STS, 0x17, 0x17); + + /* Clear BUFF_EN, Clear END_EN, Clear MATCH_EN */ + wbcir_set_bits(data->wbase + WBCIR_REG_WCEIR_EV_EN, 0x00, 0x07); + + /* Set RC5 cell time to correspond to 36 kHz */ + wbcir_set_bits(data->wbase + WBCIR_REG_WCEIR_CFG1, 0x4A, 0x7F); + + /* Set IRTX_INV */ + if (invert) + outb(0x04, data->ebase + WBCIR_REG_ECEIR_CCTL); + else + outb(0x00, data->ebase + WBCIR_REG_ECEIR_CCTL); + + /* + * Clear IR LED, set SP3 clock to 24Mhz + * set SP3_IRRX_SW to binary 01, helpfully not documented + */ + outb(0x10, data->ebase + WBCIR_REG_ECEIR_CTS); +} + +static int __devinit +wbcir_probe(struct pnp_dev *device, const struct pnp_device_id *dev_id) +{ + struct device *dev = &device->dev; + struct wbcir_data *data; + int err; + + if (!(pnp_port_len(device, 0) == EHFUNC_IOMEM_LEN && + pnp_port_len(device, 1) == WAKEUP_IOMEM_LEN && + pnp_port_len(device, 2) == SP_IOMEM_LEN)) { + dev_err(dev, "Invalid resources\n"); + return -ENODEV; + } + + data = kzalloc(sizeof(*data), GFP_KERNEL); + if (!data) { + err = -ENOMEM; + goto exit; + } + + pnp_set_drvdata(device, data); + + data->ebase = pnp_port_start(device, 0); + data->wbase = pnp_port_start(device, 1); + data->sbase = pnp_port_start(device, 2); + data->irq = pnp_irq(device, 0); + + if (data->wbase == 0 || data->ebase == 0 || + data->sbase == 0 || data->irq == 0) { + err = -ENODEV; + dev_err(dev, "Invalid resources\n"); + goto exit_free_data; + } + + dev_dbg(&device->dev, "Found device " + "(w: 0x%lX, e: 0x%lX, s: 0x%lX, i: %u)\n", + data->wbase, data->ebase, data->sbase, data->irq); + + if (!request_region(data->wbase, WAKEUP_IOMEM_LEN, DRVNAME)) { + dev_err(dev, "Region 0x%lx-0x%lx already in use!\n", + data->wbase, data->wbase + WAKEUP_IOMEM_LEN - 1); + err = -EBUSY; + goto exit_free_data; + } + + if (!request_region(data->ebase, EHFUNC_IOMEM_LEN, DRVNAME)) { + dev_err(dev, "Region 0x%lx-0x%lx already in use!\n", + data->ebase, data->ebase + EHFUNC_IOMEM_LEN - 1); + err = -EBUSY; + goto exit_release_wbase; + } + + if (!request_region(data->sbase, SP_IOMEM_LEN, DRVNAME)) { + dev_err(dev, "Region 0x%lx-0x%lx already in use!\n", + data->sbase, data->sbase + SP_IOMEM_LEN - 1); + err = -EBUSY; + goto exit_release_ebase; + } + + err = request_irq(data->irq, wbcir_irq_handler, + IRQF_DISABLED, DRVNAME, device); + if (err) { + dev_err(dev, "Failed to claim IRQ %u\n", data->irq); + err = -EBUSY; + goto exit_release_sbase; + } + + led_trigger_register_simple("cir-tx", &data->txtrigger); + if (!data->txtrigger) { + err = -ENOMEM; + goto exit_free_irq; + } + + led_trigger_register_simple("cir-rx", &data->rxtrigger); + if (!data->rxtrigger) { + err = -ENOMEM; + goto exit_unregister_txtrigger; + } + + data->led.name = "cir::activity"; + data->led.default_trigger = "cir-rx"; + data->led.brightness_set = wbcir_led_brightness_set; + data->led.brightness_get = wbcir_led_brightness_get; + err = led_classdev_register(&device->dev, &data->led); + if (err) + goto exit_unregister_rxtrigger; + + data->input_dev = input_allocate_device(); + if (!data->input_dev) { + err = -ENOMEM; + goto exit_unregister_led; + } + + data->input_dev->evbit[0] = BIT(EV_KEY); + data->input_dev->name = WBCIR_NAME; + data->input_dev->phys = "wbcir/cir0"; + data->input_dev->id.bustype = BUS_HOST; + data->input_dev->id.vendor = PCI_VENDOR_ID_WINBOND; + data->input_dev->id.product = WBCIR_ID_FAMILY; + data->input_dev->id.version = WBCIR_ID_CHIP; + data->input_dev->getkeycode = wbcir_getkeycode; + data->input_dev->setkeycode = wbcir_setkeycode; + input_set_capability(data->input_dev, EV_MSC, MSC_SCAN); + input_set_drvdata(data->input_dev, data); + + err = input_register_device(data->input_dev); + if (err) + goto exit_free_input; + + data->last_scancode = INVALID_SCANCODE; + INIT_LIST_HEAD(&data->keytable); + setup_timer(&data->timer_keyup, wbcir_keyup, (unsigned long)data); + + /* Load default keymaps */ + if (protocol == IR_PROTOCOL_RC6) { + int i; + for (i = 0; i < ARRAY_SIZE(rc6_def_keymap); i++) { + err = wbcir_setkeycode(data->input_dev, + (int)rc6_def_keymap[i].scancode, + (int)rc6_def_keymap[i].keycode); + if (err) + goto exit_unregister_keys; + } + } + + device_init_wakeup(&device->dev, 1); + + wbcir_cfg_ceir(data); + + /* Disable interrupts */ + wbcir_select_bank(data, WBCIR_BANK_0); + outb(WBCIR_IRQ_NONE, data->sbase + WBCIR_REG_SP3_IER); + + /* Enable extended mode */ + wbcir_select_bank(data, WBCIR_BANK_2); + outb(WBCIR_EXT_ENABLE, data->sbase + WBCIR_REG_SP3_EXCR1); + + /* + * Configure baud generator, IR data will be sampled at + * a bitrate of: (24Mhz * prescaler) / (divisor * 16). + * + * The ECIR registers include a flag to change the + * 24Mhz clock freq to 48Mhz. + * + * It's not documented in the specs, but fifo levels + * other than 16 seems to be unsupported. + */ + + /* prescaler 1.0, tx/rx fifo lvl 16 */ + outb(0x30, data->sbase + WBCIR_REG_SP3_EXCR2); + + /* Set baud divisor to generate one byte per bit/cell */ + switch (protocol) { + case IR_PROTOCOL_RC5: + outb(0xA7, data->sbase + WBCIR_REG_SP3_BGDL); + break; + case IR_PROTOCOL_RC6: + outb(0x53, data->sbase + WBCIR_REG_SP3_BGDL); + break; + case IR_PROTOCOL_NEC: + outb(0x69, data->sbase + WBCIR_REG_SP3_BGDL); + break; + } + outb(0x00, data->sbase + WBCIR_REG_SP3_BGDH); + + /* Set CEIR mode */ + wbcir_select_bank(data, WBCIR_BANK_0); + outb(0xC0, data->sbase + WBCIR_REG_SP3_MCR); + inb(data->sbase + WBCIR_REG_SP3_LSR); /* Clear LSR */ + inb(data->sbase + WBCIR_REG_SP3_MSR); /* Clear MSR */ + + /* Disable RX demod, run-length encoding/decoding, set freq span */ + wbcir_select_bank(data, WBCIR_BANK_7); + outb(0x10, data->sbase + WBCIR_REG_SP3_RCCFG); + + /* Disable timer */ + wbcir_select_bank(data, WBCIR_BANK_4); + outb(0x00, data->sbase + WBCIR_REG_SP3_IRCR1); + + /* Enable MSR interrupt, Clear AUX_IRX */ + wbcir_select_bank(data, WBCIR_BANK_5); + outb(0x00, data->sbase + WBCIR_REG_SP3_IRCR2); + + /* Disable CRC */ + wbcir_select_bank(data, WBCIR_BANK_6); + outb(0x20, data->sbase + WBCIR_REG_SP3_IRCR3); + + /* Set RX/TX (de)modulation freq, not really used */ + wbcir_select_bank(data, WBCIR_BANK_7); + outb(0xF2, data->sbase + WBCIR_REG_SP3_IRRXDC); + outb(0x69, data->sbase + WBCIR_REG_SP3_IRTXMC); + + /* Set invert and pin direction */ + if (invert) + outb(0x10, data->sbase + WBCIR_REG_SP3_IRCFG4); + else + outb(0x00, data->sbase + WBCIR_REG_SP3_IRCFG4); + + /* Set FIFO thresholds (RX = 8, TX = 3), reset RX/TX */ + wbcir_select_bank(data, WBCIR_BANK_0); + outb(0x97, data->sbase + WBCIR_REG_SP3_FCR); + + /* Clear AUX status bits */ + outb(0xE0, data->sbase + WBCIR_REG_SP3_ASCR); + + /* Enable interrupts */ + outb(WBCIR_IRQ_RX | WBCIR_IRQ_ERR, data->sbase + WBCIR_REG_SP3_IER); + + return 0; + +exit_unregister_keys: + if (!list_empty(&data->keytable)) { + struct wbcir_keyentry *key; + struct wbcir_keyentry *keytmp; + + list_for_each_entry_safe(key, keytmp, &data->keytable, list) { + list_del(&key->list); + kfree(key); + } + } + input_unregister_device(data->input_dev); + /* Can't call input_free_device on an unregistered device */ + data->input_dev = NULL; +exit_free_input: + input_free_device(data->input_dev); +exit_unregister_led: + led_classdev_unregister(&data->led); +exit_unregister_rxtrigger: + led_trigger_unregister_simple(data->rxtrigger); +exit_unregister_txtrigger: + led_trigger_unregister_simple(data->txtrigger); +exit_free_irq: + free_irq(data->irq, device); +exit_release_sbase: + release_region(data->sbase, SP_IOMEM_LEN); +exit_release_ebase: + release_region(data->ebase, EHFUNC_IOMEM_LEN); +exit_release_wbase: + release_region(data->wbase, WAKEUP_IOMEM_LEN); +exit_free_data: + kfree(data); + pnp_set_drvdata(device, NULL); +exit: + return err; +} + +static void __devexit +wbcir_remove(struct pnp_dev *device) +{ + struct wbcir_data *data = pnp_get_drvdata(device); + struct wbcir_keyentry *key; + struct wbcir_keyentry *keytmp; + + /* Disable interrupts */ + wbcir_select_bank(data, WBCIR_BANK_0); + outb(WBCIR_IRQ_NONE, data->sbase + WBCIR_REG_SP3_IER); + + del_timer_sync(&data->timer_keyup); + + free_irq(data->irq, device); + + /* Clear status bits NEC_REP, BUFF, MSG_END, MATCH */ + wbcir_set_bits(data->wbase + WBCIR_REG_WCEIR_STS, 0x17, 0x17); + + /* Clear CEIR_EN */ + wbcir_set_bits(data->wbase + WBCIR_REG_WCEIR_CTL, 0x00, 0x01); + + /* Clear BUFF_EN, END_EN, MATCH_EN */ + wbcir_set_bits(data->wbase + WBCIR_REG_WCEIR_EV_EN, 0x00, 0x07); + + /* This will generate a keyup event if necessary */ + input_unregister_device(data->input_dev); + + led_trigger_unregister_simple(data->rxtrigger); + led_trigger_unregister_simple(data->txtrigger); + led_classdev_unregister(&data->led); + + /* This is ok since &data->led isn't actually used */ + wbcir_led_brightness_set(&data->led, LED_OFF); + + release_region(data->wbase, WAKEUP_IOMEM_LEN); + release_region(data->ebase, EHFUNC_IOMEM_LEN); + release_region(data->sbase, SP_IOMEM_LEN); + + list_for_each_entry_safe(key, keytmp, &data->keytable, list) { + list_del(&key->list); + kfree(key); + } + + kfree(data); + + pnp_set_drvdata(device, NULL); +} + +static const struct pnp_device_id wbcir_ids[] = { + { "WEC1022", 0 }, + { "", 0 } +}; +MODULE_DEVICE_TABLE(pnp, wbcir_ids); + +static struct pnp_driver wbcir_driver = { + .name = WBCIR_NAME, + .id_table = wbcir_ids, + .probe = wbcir_probe, + .remove = __devexit_p(wbcir_remove), + .suspend = wbcir_suspend, + .resume = wbcir_resume, + .shutdown = wbcir_shutdown +}; + +static int __init +wbcir_init(void) +{ + int ret; + + switch (protocol) { + case IR_PROTOCOL_RC5: + case IR_PROTOCOL_NEC: + case IR_PROTOCOL_RC6: + break; + default: + printk(KERN_ERR DRVNAME ": Invalid protocol argument\n"); + return -EINVAL; + } + + ret = pnp_register_driver(&wbcir_driver); + if (ret) + printk(KERN_ERR DRVNAME ": Unable to register driver\n"); + + return ret; +} + +static void __exit +wbcir_exit(void) +{ + pnp_unregister_driver(&wbcir_driver); +} + +MODULE_AUTHOR("David Härdeman <david@hardeman.nu>"); +MODULE_DESCRIPTION("Winbond SuperI/O Consumer IR Driver"); +MODULE_LICENSE("GPL"); + +module_init(wbcir_init); +module_exit(wbcir_exit); + + diff --git a/drivers/input/misc/wistron_btns.c b/drivers/input/misc/wistron_btns.c index 27ee976eb54..38da6ab0438 100644 --- a/drivers/input/misc/wistron_btns.c +++ b/drivers/input/misc/wistron_btns.c @@ -21,6 +21,7 @@ #include <linux/dmi.h> #include <linux/init.h> #include <linux/input-polldev.h> +#include <linux/input/sparse-keymap.h> #include <linux/interrupt.h> #include <linux/jiffies.h> #include <linux/kernel.h> @@ -224,28 +225,17 @@ static void bios_set_state(u8 subsys, int enable) /* Hardware database */ -struct key_entry { - char type; /* See KE_* below */ - u8 code; - union { - u16 keycode; /* For KE_KEY */ - struct { /* For KE_SW */ - u8 code; - u8 value; - } sw; - }; -}; - -enum { KE_END, KE_KEY, KE_SW, KE_WIFI, KE_BLUETOOTH }; +#define KE_WIFI (KE_LAST + 1) +#define KE_BLUETOOTH (KE_LAST + 2) #define FE_MAIL_LED 0x01 #define FE_WIFI_LED 0x02 #define FE_UNTESTED 0x80 static struct key_entry *keymap; /* = NULL; Current key map */ -static int have_wifi; -static int have_bluetooth; -static int have_leds; +static bool have_wifi; +static bool have_bluetooth; +static int leds_present; /* bitmask of leds present */ static int __init dmi_matched(const struct dmi_system_id *dmi) { @@ -254,11 +244,11 @@ static int __init dmi_matched(const struct dmi_system_id *dmi) keymap = dmi->driver_data; for (key = keymap; key->type != KE_END; key++) { if (key->type == KE_WIFI) - have_wifi = 1; + have_wifi = true; else if (key->type == KE_BLUETOOTH) - have_bluetooth = 1; + have_bluetooth = true; } - have_leds = key->code & (FE_MAIL_LED | FE_WIFI_LED); + leds_present = key->code & (FE_MAIL_LED | FE_WIFI_LED); return 1; } @@ -611,10 +601,24 @@ static struct key_entry keymap_wistron_generic[] __initdata = { { KE_END, 0 } }; +static struct key_entry keymap_aopen_1557[] __initdata = { + { KE_KEY, 0x01, {KEY_HELP} }, + { KE_KEY, 0x11, {KEY_PROG1} }, + { KE_KEY, 0x12, {KEY_PROG2} }, + { KE_WIFI, 0x30 }, + { KE_KEY, 0x22, {KEY_REWIND} }, + { KE_KEY, 0x23, {KEY_FORWARD} }, + { KE_KEY, 0x24, {KEY_PLAYPAUSE} }, + { KE_KEY, 0x25, {KEY_STOPCD} }, + { KE_KEY, 0x31, {KEY_MAIL} }, + { KE_KEY, 0x36, {KEY_WWW} }, + { KE_END, 0 } +}; + static struct key_entry keymap_prestigio[] __initdata = { { KE_KEY, 0x11, {KEY_PROG1} }, { KE_KEY, 0x12, {KEY_PROG2} }, - { KE_WIFI, 0x30 }, + { KE_WIFI, 0x30 }, { KE_KEY, 0x22, {KEY_REWIND} }, { KE_KEY, 0x23, {KEY_FORWARD} }, { KE_KEY, 0x24, {KEY_PLAYPAUSE} }, @@ -630,10 +634,10 @@ static struct key_entry keymap_prestigio[] __initdata = { * a list of buttons and their key codes (reported when loading this module * with force=1) and the output of dmidecode to $MODULE_AUTHOR. */ -static struct dmi_system_id dmi_ids[] __initdata = { +static const struct dmi_system_id __initconst dmi_ids[] = { { + /* Fujitsu-Siemens Amilo Pro V2000 */ .callback = dmi_matched, - .ident = "Fujitsu-Siemens Amilo Pro V2000", .matches = { DMI_MATCH(DMI_SYS_VENDOR, "FUJITSU SIEMENS"), DMI_MATCH(DMI_PRODUCT_NAME, "AMILO Pro V2000"), @@ -641,8 +645,8 @@ static struct dmi_system_id dmi_ids[] __initdata = { .driver_data = keymap_fs_amilo_pro_v2000 }, { + /* Fujitsu-Siemens Amilo Pro Edition V3505 */ .callback = dmi_matched, - .ident = "Fujitsu-Siemens Amilo Pro Edition V3505", .matches = { DMI_MATCH(DMI_SYS_VENDOR, "FUJITSU SIEMENS"), DMI_MATCH(DMI_PRODUCT_NAME, "AMILO Pro Edition V3505"), @@ -650,8 +654,8 @@ static struct dmi_system_id dmi_ids[] __initdata = { .driver_data = keymap_fs_amilo_pro_v3505 }, { + /* Fujitsu-Siemens Amilo M7400 */ .callback = dmi_matched, - .ident = "Fujitsu-Siemens Amilo M7400", .matches = { DMI_MATCH(DMI_SYS_VENDOR, "FUJITSU SIEMENS"), DMI_MATCH(DMI_PRODUCT_NAME, "AMILO M "), @@ -659,8 +663,8 @@ static struct dmi_system_id dmi_ids[] __initdata = { .driver_data = keymap_fs_amilo_pro_v2000 }, { + /* Maxdata Pro 7000 DX */ .callback = dmi_matched, - .ident = "Maxdata Pro 7000 DX", .matches = { DMI_MATCH(DMI_SYS_VENDOR, "MAXDATA"), DMI_MATCH(DMI_PRODUCT_NAME, "Pro 7000"), @@ -668,8 +672,8 @@ static struct dmi_system_id dmi_ids[] __initdata = { .driver_data = keymap_fs_amilo_pro_v2000 }, { + /* Fujitsu N3510 */ .callback = dmi_matched, - .ident = "Fujitsu N3510", .matches = { DMI_MATCH(DMI_SYS_VENDOR, "FUJITSU"), DMI_MATCH(DMI_PRODUCT_NAME, "N3510"), @@ -677,8 +681,8 @@ static struct dmi_system_id dmi_ids[] __initdata = { .driver_data = keymap_fujitsu_n3510 }, { + /* Acer Aspire 1500 */ .callback = dmi_matched, - .ident = "Acer Aspire 1500", .matches = { DMI_MATCH(DMI_SYS_VENDOR, "Acer"), DMI_MATCH(DMI_PRODUCT_NAME, "Aspire 1500"), @@ -686,8 +690,8 @@ static struct dmi_system_id dmi_ids[] __initdata = { .driver_data = keymap_acer_aspire_1500 }, { + /* Acer Aspire 1600 */ .callback = dmi_matched, - .ident = "Acer Aspire 1600", .matches = { DMI_MATCH(DMI_SYS_VENDOR, "Acer"), DMI_MATCH(DMI_PRODUCT_NAME, "Aspire 1600"), @@ -695,8 +699,8 @@ static struct dmi_system_id dmi_ids[] __initdata = { .driver_data = keymap_acer_aspire_1600 }, { + /* Acer Aspire 3020 */ .callback = dmi_matched, - .ident = "Acer Aspire 3020", .matches = { DMI_MATCH(DMI_SYS_VENDOR, "Acer"), DMI_MATCH(DMI_PRODUCT_NAME, "Aspire 3020"), @@ -704,8 +708,8 @@ static struct dmi_system_id dmi_ids[] __initdata = { .driver_data = keymap_acer_aspire_5020 }, { + /* Acer Aspire 5020 */ .callback = dmi_matched, - .ident = "Acer Aspire 5020", .matches = { DMI_MATCH(DMI_SYS_VENDOR, "Acer"), DMI_MATCH(DMI_PRODUCT_NAME, "Aspire 5020"), @@ -713,8 +717,8 @@ static struct dmi_system_id dmi_ids[] __initdata = { .driver_data = keymap_acer_aspire_5020 }, { + /* Acer TravelMate 2100 */ .callback = dmi_matched, - .ident = "Acer TravelMate 2100", .matches = { DMI_MATCH(DMI_SYS_VENDOR, "Acer"), DMI_MATCH(DMI_PRODUCT_NAME, "TravelMate 2100"), @@ -722,8 +726,8 @@ static struct dmi_system_id dmi_ids[] __initdata = { .driver_data = keymap_acer_aspire_5020 }, { + /* Acer TravelMate 2410 */ .callback = dmi_matched, - .ident = "Acer TravelMate 2410", .matches = { DMI_MATCH(DMI_SYS_VENDOR, "Acer"), DMI_MATCH(DMI_PRODUCT_NAME, "TravelMate 2410"), @@ -731,8 +735,8 @@ static struct dmi_system_id dmi_ids[] __initdata = { .driver_data = keymap_acer_travelmate_2410 }, { + /* Acer TravelMate C300 */ .callback = dmi_matched, - .ident = "Acer TravelMate C300", .matches = { DMI_MATCH(DMI_SYS_VENDOR, "Acer"), DMI_MATCH(DMI_PRODUCT_NAME, "TravelMate C300"), @@ -740,8 +744,8 @@ static struct dmi_system_id dmi_ids[] __initdata = { .driver_data = keymap_acer_travelmate_300 }, { + /* Acer TravelMate C100 */ .callback = dmi_matched, - .ident = "Acer TravelMate C100", .matches = { DMI_MATCH(DMI_SYS_VENDOR, "Acer"), DMI_MATCH(DMI_PRODUCT_NAME, "TravelMate C100"), @@ -749,8 +753,8 @@ static struct dmi_system_id dmi_ids[] __initdata = { .driver_data = keymap_acer_travelmate_300 }, { + /* Acer TravelMate C110 */ .callback = dmi_matched, - .ident = "Acer TravelMate C110", .matches = { DMI_MATCH(DMI_SYS_VENDOR, "Acer"), DMI_MATCH(DMI_PRODUCT_NAME, "TravelMate C110"), @@ -758,8 +762,8 @@ static struct dmi_system_id dmi_ids[] __initdata = { .driver_data = keymap_acer_travelmate_110 }, { + /* Acer TravelMate 380 */ .callback = dmi_matched, - .ident = "Acer TravelMate 380", .matches = { DMI_MATCH(DMI_SYS_VENDOR, "Acer"), DMI_MATCH(DMI_PRODUCT_NAME, "TravelMate 380"), @@ -767,8 +771,8 @@ static struct dmi_system_id dmi_ids[] __initdata = { .driver_data = keymap_acer_travelmate_380 }, { + /* Acer TravelMate 370 */ .callback = dmi_matched, - .ident = "Acer TravelMate 370", .matches = { DMI_MATCH(DMI_SYS_VENDOR, "Acer"), DMI_MATCH(DMI_PRODUCT_NAME, "TravelMate 370"), @@ -776,8 +780,8 @@ static struct dmi_system_id dmi_ids[] __initdata = { .driver_data = keymap_acer_travelmate_380 /* keyboard minus 1 key */ }, { + /* Acer TravelMate 220 */ .callback = dmi_matched, - .ident = "Acer TravelMate 220", .matches = { DMI_MATCH(DMI_SYS_VENDOR, "Acer"), DMI_MATCH(DMI_PRODUCT_NAME, "TravelMate 220"), @@ -785,8 +789,8 @@ static struct dmi_system_id dmi_ids[] __initdata = { .driver_data = keymap_acer_travelmate_220 }, { + /* Acer TravelMate 260 */ .callback = dmi_matched, - .ident = "Acer TravelMate 260", .matches = { DMI_MATCH(DMI_SYS_VENDOR, "Acer"), DMI_MATCH(DMI_PRODUCT_NAME, "TravelMate 260"), @@ -794,8 +798,8 @@ static struct dmi_system_id dmi_ids[] __initdata = { .driver_data = keymap_acer_travelmate_220 }, { + /* Acer TravelMate 230 */ .callback = dmi_matched, - .ident = "Acer TravelMate 230", .matches = { DMI_MATCH(DMI_SYS_VENDOR, "Acer"), DMI_MATCH(DMI_PRODUCT_NAME, "TravelMate 230"), @@ -804,8 +808,8 @@ static struct dmi_system_id dmi_ids[] __initdata = { .driver_data = keymap_acer_travelmate_230 }, { + /* Acer TravelMate 280 */ .callback = dmi_matched, - .ident = "Acer TravelMate 280", .matches = { DMI_MATCH(DMI_SYS_VENDOR, "Acer"), DMI_MATCH(DMI_PRODUCT_NAME, "TravelMate 280"), @@ -813,8 +817,8 @@ static struct dmi_system_id dmi_ids[] __initdata = { .driver_data = keymap_acer_travelmate_230 }, { + /* Acer TravelMate 240 */ .callback = dmi_matched, - .ident = "Acer TravelMate 240", .matches = { DMI_MATCH(DMI_SYS_VENDOR, "Acer"), DMI_MATCH(DMI_PRODUCT_NAME, "TravelMate 240"), @@ -822,8 +826,8 @@ static struct dmi_system_id dmi_ids[] __initdata = { .driver_data = keymap_acer_travelmate_240 }, { + /* Acer TravelMate 250 */ .callback = dmi_matched, - .ident = "Acer TravelMate 250", .matches = { DMI_MATCH(DMI_SYS_VENDOR, "Acer"), DMI_MATCH(DMI_PRODUCT_NAME, "TravelMate 250"), @@ -831,8 +835,8 @@ static struct dmi_system_id dmi_ids[] __initdata = { .driver_data = keymap_acer_travelmate_240 }, { + /* Acer TravelMate 2424NWXCi */ .callback = dmi_matched, - .ident = "Acer TravelMate 2424NWXCi", .matches = { DMI_MATCH(DMI_SYS_VENDOR, "Acer"), DMI_MATCH(DMI_PRODUCT_NAME, "TravelMate 2420"), @@ -840,8 +844,8 @@ static struct dmi_system_id dmi_ids[] __initdata = { .driver_data = keymap_acer_travelmate_240 }, { + /* Acer TravelMate 350 */ .callback = dmi_matched, - .ident = "Acer TravelMate 350", .matches = { DMI_MATCH(DMI_SYS_VENDOR, "Acer"), DMI_MATCH(DMI_PRODUCT_NAME, "TravelMate 350"), @@ -849,8 +853,8 @@ static struct dmi_system_id dmi_ids[] __initdata = { .driver_data = keymap_acer_travelmate_350 }, { + /* Acer TravelMate 360 */ .callback = dmi_matched, - .ident = "Acer TravelMate 360", .matches = { DMI_MATCH(DMI_SYS_VENDOR, "Acer"), DMI_MATCH(DMI_PRODUCT_NAME, "TravelMate 360"), @@ -858,8 +862,8 @@ static struct dmi_system_id dmi_ids[] __initdata = { .driver_data = keymap_acer_travelmate_360 }, { + /* Acer TravelMate 610 */ .callback = dmi_matched, - .ident = "Acer TravelMate 610", .matches = { DMI_MATCH(DMI_SYS_VENDOR, "ACER"), DMI_MATCH(DMI_PRODUCT_NAME, "TravelMate 610"), @@ -867,8 +871,8 @@ static struct dmi_system_id dmi_ids[] __initdata = { .driver_data = keymap_acer_travelmate_610 }, { + /* Acer TravelMate 620 */ .callback = dmi_matched, - .ident = "Acer TravelMate 620", .matches = { DMI_MATCH(DMI_SYS_VENDOR, "Acer"), DMI_MATCH(DMI_PRODUCT_NAME, "TravelMate 620"), @@ -876,8 +880,8 @@ static struct dmi_system_id dmi_ids[] __initdata = { .driver_data = keymap_acer_travelmate_630 }, { + /* Acer TravelMate 630 */ .callback = dmi_matched, - .ident = "Acer TravelMate 630", .matches = { DMI_MATCH(DMI_SYS_VENDOR, "Acer"), DMI_MATCH(DMI_PRODUCT_NAME, "TravelMate 630"), @@ -885,8 +889,8 @@ static struct dmi_system_id dmi_ids[] __initdata = { .driver_data = keymap_acer_travelmate_630 }, { + /* AOpen 1559AS */ .callback = dmi_matched, - .ident = "AOpen 1559AS", .matches = { DMI_MATCH(DMI_PRODUCT_NAME, "E2U"), DMI_MATCH(DMI_BOARD_NAME, "E2U"), @@ -894,8 +898,8 @@ static struct dmi_system_id dmi_ids[] __initdata = { .driver_data = keymap_aopen_1559as }, { + /* Medion MD 9783 */ .callback = dmi_matched, - .ident = "Medion MD 9783", .matches = { DMI_MATCH(DMI_SYS_VENDOR, "MEDIONNB"), DMI_MATCH(DMI_PRODUCT_NAME, "MD 9783"), @@ -903,8 +907,8 @@ static struct dmi_system_id dmi_ids[] __initdata = { .driver_data = keymap_wistron_ms2111 }, { + /* Medion MD 40100 */ .callback = dmi_matched, - .ident = "Medion MD 40100", .matches = { DMI_MATCH(DMI_SYS_VENDOR, "MEDIONNB"), DMI_MATCH(DMI_PRODUCT_NAME, "WID2000"), @@ -912,8 +916,8 @@ static struct dmi_system_id dmi_ids[] __initdata = { .driver_data = keymap_wistron_md40100 }, { + /* Medion MD 2900 */ .callback = dmi_matched, - .ident = "Medion MD 2900", .matches = { DMI_MATCH(DMI_SYS_VENDOR, "MEDIONNB"), DMI_MATCH(DMI_PRODUCT_NAME, "WIM 2000"), @@ -921,8 +925,17 @@ static struct dmi_system_id dmi_ids[] __initdata = { .driver_data = keymap_wistron_md2900 }, { + /* Medion MD 42200 */ + .callback = dmi_matched, + .matches = { + DMI_MATCH(DMI_SYS_VENDOR, "Medion"), + DMI_MATCH(DMI_PRODUCT_NAME, "WIM 2030"), + }, + .driver_data = keymap_fs_amilo_pro_v2000 + }, + { + /* Medion MD 96500 */ .callback = dmi_matched, - .ident = "Medion MD 96500", .matches = { DMI_MATCH(DMI_SYS_VENDOR, "MEDIONPC"), DMI_MATCH(DMI_PRODUCT_NAME, "WIM 2040"), @@ -930,8 +943,8 @@ static struct dmi_system_id dmi_ids[] __initdata = { .driver_data = keymap_wistron_md96500 }, { + /* Medion MD 95400 */ .callback = dmi_matched, - .ident = "Medion MD 95400", .matches = { DMI_MATCH(DMI_SYS_VENDOR, "MEDIONPC"), DMI_MATCH(DMI_PRODUCT_NAME, "WIM 2050"), @@ -939,8 +952,8 @@ static struct dmi_system_id dmi_ids[] __initdata = { .driver_data = keymap_wistron_md96500 }, { + /* Fujitsu Siemens Amilo D7820 */ .callback = dmi_matched, - .ident = "Fujitsu Siemens Amilo D7820", .matches = { DMI_MATCH(DMI_SYS_VENDOR, "FUJITSU SIEMENS"), /* not sure */ DMI_MATCH(DMI_PRODUCT_NAME, "Amilo D"), @@ -948,8 +961,8 @@ static struct dmi_system_id dmi_ids[] __initdata = { .driver_data = keymap_fs_amilo_d88x0 }, { + /* Fujitsu Siemens Amilo D88x0 */ .callback = dmi_matched, - .ident = "Fujitsu Siemens Amilo D88x0", .matches = { DMI_MATCH(DMI_SYS_VENDOR, "FUJITSU SIEMENS"), DMI_MATCH(DMI_PRODUCT_NAME, "AMILO D"), @@ -985,6 +998,8 @@ static int __init select_keymap(void) if (keymap_name != NULL) { if (strcmp (keymap_name, "1557/MS2141") == 0) keymap = keymap_wistron_ms2141; + else if (strcmp (keymap_name, "aopen1557") == 0) + keymap = keymap_aopen_1557; else if (strcmp (keymap_name, "prestigio") == 0) keymap = keymap_prestigio; else if (strcmp (keymap_name, "generic") == 0) @@ -1009,23 +1024,8 @@ static int __init select_keymap(void) static struct input_polled_dev *wistron_idev; static unsigned long jiffies_last_press; -static int wifi_enabled; -static int bluetooth_enabled; - -static void report_key(struct input_dev *dev, unsigned int keycode) -{ - input_report_key(dev, keycode, 1); - input_sync(dev); - input_report_key(dev, keycode, 0); - input_sync(dev); -} - -static void report_switch(struct input_dev *dev, unsigned int code, int value) -{ - input_report_switch(dev, code, value); - input_sync(dev); -} - +static bool wifi_enabled; +static bool bluetooth_enabled; /* led management */ static void wistron_mail_led_set(struct led_classdev *led_cdev, @@ -1053,24 +1053,24 @@ static struct led_classdev wistron_wifi_led = { static void __devinit wistron_led_init(struct device *parent) { - if (have_leds & FE_WIFI_LED) { + if (leds_present & FE_WIFI_LED) { u16 wifi = bios_get_default_setting(WIFI); if (wifi & 1) { wistron_wifi_led.brightness = (wifi & 2) ? LED_FULL : LED_OFF; if (led_classdev_register(parent, &wistron_wifi_led)) - have_leds &= ~FE_WIFI_LED; + leds_present &= ~FE_WIFI_LED; else bios_set_state(WIFI, wistron_wifi_led.brightness); } else - have_leds &= ~FE_WIFI_LED; + leds_present &= ~FE_WIFI_LED; } - if (have_leds & FE_MAIL_LED) { + if (leds_present & FE_MAIL_LED) { /* bios_get_default_setting(MAIL) always retuns 0, so just turn the led off */ wistron_mail_led.brightness = LED_OFF; if (led_classdev_register(parent, &wistron_mail_led)) - have_leds &= ~FE_MAIL_LED; + leds_present &= ~FE_MAIL_LED; else bios_set_state(MAIL_LED, wistron_mail_led.brightness); } @@ -1078,68 +1078,38 @@ static void __devinit wistron_led_init(struct device *parent) static void __devexit wistron_led_remove(void) { - if (have_leds & FE_MAIL_LED) + if (leds_present & FE_MAIL_LED) led_classdev_unregister(&wistron_mail_led); - if (have_leds & FE_WIFI_LED) + if (leds_present & FE_WIFI_LED) led_classdev_unregister(&wistron_wifi_led); } static inline void wistron_led_suspend(void) { - if (have_leds & FE_MAIL_LED) + if (leds_present & FE_MAIL_LED) led_classdev_suspend(&wistron_mail_led); - if (have_leds & FE_WIFI_LED) + if (leds_present & FE_WIFI_LED) led_classdev_suspend(&wistron_wifi_led); } static inline void wistron_led_resume(void) { - if (have_leds & FE_MAIL_LED) + if (leds_present & FE_MAIL_LED) led_classdev_resume(&wistron_mail_led); - if (have_leds & FE_WIFI_LED) + if (leds_present & FE_WIFI_LED) led_classdev_resume(&wistron_wifi_led); } -static struct key_entry *wistron_get_entry_by_scancode(int code) -{ - struct key_entry *key; - - for (key = keymap; key->type != KE_END; key++) - if (code == key->code) - return key; - - return NULL; -} - -static struct key_entry *wistron_get_entry_by_keycode(int keycode) -{ - struct key_entry *key; - - for (key = keymap; key->type != KE_END; key++) - if (key->type == KE_KEY && keycode == key->keycode) - return key; - - return NULL; -} - static void handle_key(u8 code) { - const struct key_entry *key = wistron_get_entry_by_scancode(code); + const struct key_entry *key = + sparse_keymap_entry_from_scancode(wistron_idev->input, code); if (key) { switch (key->type) { - case KE_KEY: - report_key(wistron_idev->input, key->keycode); - break; - - case KE_SW: - report_switch(wistron_idev->input, - key->sw.code, key->sw.value); - break; - case KE_WIFI: if (have_wifi) { wifi_enabled = !wifi_enabled; @@ -1155,7 +1125,9 @@ static void handle_key(u8 code) break; default: - BUG(); + sparse_keymap_report_entry(wistron_idev->input, + key, 1, true); + break; } jiffies_last_press = jiffies; } else @@ -1195,42 +1167,39 @@ static void wistron_poll(struct input_polled_dev *dev) dev->poll_interval = POLL_INTERVAL_DEFAULT; } -static int wistron_getkeycode(struct input_dev *dev, int scancode, int *keycode) +static int __devinit wistron_setup_keymap(struct input_dev *dev, + struct key_entry *entry) { - const struct key_entry *key = wistron_get_entry_by_scancode(scancode); - - if (key && key->type == KE_KEY) { - *keycode = key->keycode; - return 0; - } + switch (entry->type) { - return -EINVAL; -} + /* if wifi or bluetooth are not available, create normal keys */ + case KE_WIFI: + if (!have_wifi) { + entry->type = KE_KEY; + entry->keycode = KEY_WLAN; + } + break; -static int wistron_setkeycode(struct input_dev *dev, int scancode, int keycode) -{ - struct key_entry *key; - int old_keycode; - - if (keycode < 0 || keycode > KEY_MAX) - return -EINVAL; - - key = wistron_get_entry_by_scancode(scancode); - if (key && key->type == KE_KEY) { - old_keycode = key->keycode; - key->keycode = keycode; - set_bit(keycode, dev->keybit); - if (!wistron_get_entry_by_keycode(old_keycode)) - clear_bit(old_keycode, dev->keybit); - return 0; + case KE_BLUETOOTH: + if (!have_bluetooth) { + entry->type = KE_KEY; + entry->keycode = KEY_BLUETOOTH; + } + break; + + case KE_END: + if (entry->code & FE_UNTESTED) + printk(KERN_WARNING "Untested laptop multimedia keys, " + "please report success or failure to " + "eric.piel@tremplin-utc.net\n"); + break; } - return -EINVAL; + return 0; } static int __devinit setup_input_dev(void) { - struct key_entry *key; struct input_dev *input_dev; int error; @@ -1238,7 +1207,7 @@ static int __devinit setup_input_dev(void) if (!wistron_idev) return -ENOMEM; - wistron_idev->flush = wistron_flush; + wistron_idev->open = wistron_flush; wistron_idev->poll = wistron_poll; wistron_idev->poll_interval = POLL_INTERVAL_DEFAULT; @@ -1248,56 +1217,21 @@ static int __devinit setup_input_dev(void) input_dev->id.bustype = BUS_HOST; input_dev->dev.parent = &wistron_device->dev; - input_dev->getkeycode = wistron_getkeycode; - input_dev->setkeycode = wistron_setkeycode; - - for (key = keymap; key->type != KE_END; key++) { - switch (key->type) { - case KE_KEY: - set_bit(EV_KEY, input_dev->evbit); - set_bit(key->keycode, input_dev->keybit); - break; - - case KE_SW: - set_bit(EV_SW, input_dev->evbit); - set_bit(key->sw.code, input_dev->swbit); - break; - - /* if wifi or bluetooth are not available, create normal keys */ - case KE_WIFI: - if (!have_wifi) { - key->type = KE_KEY; - key->keycode = KEY_WLAN; - key--; - } - break; - - case KE_BLUETOOTH: - if (!have_bluetooth) { - key->type = KE_KEY; - key->keycode = KEY_BLUETOOTH; - key--; - } - break; - - default: - break; - } - } - - /* reads information flags on KE_END */ - if (key->code & FE_UNTESTED) - printk(KERN_WARNING "Untested laptop multimedia keys, " - "please report success or failure to eric.piel" - "@tremplin-utc.net\n"); + error = sparse_keymap_setup(input_dev, keymap, wistron_setup_keymap); + if (error) + goto err_free_dev; error = input_register_polled_device(wistron_idev); - if (error) { - input_free_polled_device(wistron_idev); - return error; - } + if (error) + goto err_free_keymap; return 0; + + err_free_keymap: + sparse_keymap_free(input_dev); + err_free_dev: + input_free_polled_device(wistron_idev); + return error; } /* Driver core */ @@ -1312,7 +1246,7 @@ static int __devinit wistron_probe(struct platform_device *dev) if (have_wifi) { u16 wifi = bios_get_default_setting(WIFI); if (wifi & 1) - wifi_enabled = (wifi & 2) ? 1 : 0; + wifi_enabled = wifi & 2; else have_wifi = 0; @@ -1323,15 +1257,16 @@ static int __devinit wistron_probe(struct platform_device *dev) if (have_bluetooth) { u16 bt = bios_get_default_setting(BLUETOOTH); if (bt & 1) - bluetooth_enabled = (bt & 2) ? 1 : 0; + bluetooth_enabled = bt & 2; else - have_bluetooth = 0; + have_bluetooth = false; if (have_bluetooth) bios_set_state(BLUETOOTH, bluetooth_enabled); } wistron_led_init(&dev->dev); + err = setup_input_dev(); if (err) { bios_detach(); @@ -1345,6 +1280,7 @@ static int __devexit wistron_remove(struct platform_device *dev) { wistron_led_remove(); input_unregister_polled_device(wistron_idev); + sparse_keymap_free(wistron_idev->input); input_free_polled_device(wistron_idev); bios_detach(); @@ -1352,7 +1288,7 @@ static int __devexit wistron_remove(struct platform_device *dev) } #ifdef CONFIG_PM -static int wistron_suspend(struct platform_device *dev, pm_message_t state) +static int wistron_suspend(struct device *dev) { if (have_wifi) bios_set_state(WIFI, 0); @@ -1361,10 +1297,11 @@ static int wistron_suspend(struct platform_device *dev, pm_message_t state) bios_set_state(BLUETOOTH, 0); wistron_led_suspend(); + return 0; } -static int wistron_resume(struct platform_device *dev) +static int wistron_resume(struct device *dev) { if (have_wifi) bios_set_state(WIFI, wifi_enabled); @@ -1373,24 +1310,30 @@ static int wistron_resume(struct platform_device *dev) bios_set_state(BLUETOOTH, bluetooth_enabled); wistron_led_resume(); + poll_bios(true); return 0; } -#else -#define wistron_suspend NULL -#define wistron_resume NULL + +static const struct dev_pm_ops wistron_pm_ops = { + .suspend = wistron_suspend, + .resume = wistron_resume, + .poweroff = wistron_suspend, + .restore = wistron_resume, +}; #endif static struct platform_driver wistron_driver = { .driver = { .name = "wistron-bios", .owner = THIS_MODULE, +#if CONFIG_PM + .pm = &wistron_pm_ops, +#endif }, .probe = wistron_probe, .remove = __devexit_p(wistron_remove), - .suspend = wistron_suspend, - .resume = wistron_resume, }; static int __init wb_module_init(void) diff --git a/drivers/input/misc/wm831x-on.c b/drivers/input/misc/wm831x-on.c new file mode 100644 index 00000000000..ba4f5dd7c60 --- /dev/null +++ b/drivers/input/misc/wm831x-on.c @@ -0,0 +1,163 @@ +/** + * wm831x-on.c - WM831X ON pin driver + * + * Copyright (C) 2009 Wolfson Microelectronics plc + * + * This file is subject to the terms and conditions of the GNU General + * Public License. See the file "COPYING" in the main directory of this + * archive for more details. + * + * 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/kernel.h> +#include <linux/errno.h> +#include <linux/input.h> +#include <linux/interrupt.h> +#include <linux/platform_device.h> +#include <linux/workqueue.h> +#include <linux/mfd/wm831x/core.h> + +struct wm831x_on { + struct input_dev *dev; + struct delayed_work work; + struct wm831x *wm831x; +}; + +/* + * The chip gives us an interrupt when the ON pin is asserted but we + * then need to poll to see when the pin is deasserted. + */ +static void wm831x_poll_on(struct work_struct *work) +{ + struct wm831x_on *wm831x_on = container_of(work, struct wm831x_on, + work.work); + struct wm831x *wm831x = wm831x_on->wm831x; + int poll, ret; + + ret = wm831x_reg_read(wm831x, WM831X_ON_PIN_CONTROL); + if (ret >= 0) { + poll = !(ret & WM831X_ON_PIN_STS); + + input_report_key(wm831x_on->dev, KEY_POWER, poll); + input_sync(wm831x_on->dev); + } else { + dev_err(wm831x->dev, "Failed to read ON status: %d\n", ret); + poll = 1; + } + + if (poll) + schedule_delayed_work(&wm831x_on->work, 100); +} + +static irqreturn_t wm831x_on_irq(int irq, void *data) +{ + struct wm831x_on *wm831x_on = data; + + schedule_delayed_work(&wm831x_on->work, 0); + + return IRQ_HANDLED; +} + +static int __devinit wm831x_on_probe(struct platform_device *pdev) +{ + struct wm831x *wm831x = dev_get_drvdata(pdev->dev.parent); + struct wm831x_on *wm831x_on; + int irq = platform_get_irq(pdev, 0); + int ret; + + wm831x_on = kzalloc(sizeof(struct wm831x_on), GFP_KERNEL); + if (!wm831x_on) { + dev_err(&pdev->dev, "Can't allocate data\n"); + return -ENOMEM; + } + + wm831x_on->wm831x = wm831x; + INIT_DELAYED_WORK(&wm831x_on->work, wm831x_poll_on); + + wm831x_on->dev = input_allocate_device(); + if (!wm831x_on->dev) { + dev_err(&pdev->dev, "Can't allocate input dev\n"); + ret = -ENOMEM; + goto err; + } + + wm831x_on->dev->evbit[0] = BIT_MASK(EV_KEY); + wm831x_on->dev->keybit[BIT_WORD(KEY_POWER)] = BIT_MASK(KEY_POWER); + wm831x_on->dev->name = "wm831x_on"; + wm831x_on->dev->phys = "wm831x_on/input0"; + wm831x_on->dev->dev.parent = &pdev->dev; + + ret = wm831x_request_irq(wm831x, irq, wm831x_on_irq, + IRQF_TRIGGER_RISING, "wm831x_on", wm831x_on); + if (ret < 0) { + dev_err(&pdev->dev, "Unable to request IRQ: %d\n", ret); + goto err_input_dev; + } + ret = input_register_device(wm831x_on->dev); + if (ret) { + dev_dbg(&pdev->dev, "Can't register input device: %d\n", ret); + goto err_irq; + } + + platform_set_drvdata(pdev, wm831x_on); + + return 0; + +err_irq: + wm831x_free_irq(wm831x, irq, NULL); +err_input_dev: + input_free_device(wm831x_on->dev); +err: + kfree(wm831x_on); + return ret; +} + +static int __devexit wm831x_on_remove(struct platform_device *pdev) +{ + struct wm831x_on *wm831x_on = platform_get_drvdata(pdev); + int irq = platform_get_irq(pdev, 0); + + wm831x_free_irq(wm831x_on->wm831x, irq, wm831x_on); + cancel_delayed_work_sync(&wm831x_on->work); + input_unregister_device(wm831x_on->dev); + kfree(wm831x_on); + + return 0; +} + +static struct platform_driver wm831x_on_driver = { + .probe = wm831x_on_probe, + .remove = __devexit_p(wm831x_on_remove), + .driver = { + .name = "wm831x-on", + .owner = THIS_MODULE, + }, +}; + +static int __init wm831x_on_init(void) +{ + return platform_driver_register(&wm831x_on_driver); +} +module_init(wm831x_on_init); + +static void __exit wm831x_on_exit(void) +{ + platform_driver_unregister(&wm831x_on_driver); +} +module_exit(wm831x_on_exit); + +MODULE_ALIAS("platform:wm831x-on"); +MODULE_DESCRIPTION("WM831x ON pin"); +MODULE_LICENSE("GPL"); +MODULE_AUTHOR("Mark Brown <broonie@opensource.wolfsonmicro.com>"); + diff --git a/drivers/input/mouse/Kconfig b/drivers/input/mouse/Kconfig index 8a2c5b14c8d..3feeb3af8ab 100644 --- a/drivers/input/mouse/Kconfig +++ b/drivers/input/mouse/Kconfig @@ -107,6 +107,14 @@ config MOUSE_PS2_ELANTECH entries. For further information, see <file:Documentation/input/elantech.txt>. +config MOUSE_PS2_SENTELIC + bool "Sentelic Finger Sensing Pad PS/2 protocol extension" + depends on MOUSE_PS2 + help + Say Y here if you have a laptop (such as MSI WIND Netbook) + with Sentelic Finger Sensing Pad touchpad. + + If unsure, say N. config MOUSE_PS2_TOUCHKIT bool "eGalax TouchKit PS/2 protocol extension" @@ -262,14 +270,6 @@ config MOUSE_VSXXXAA described in the source file). This driver also works with the digitizer (VSXXX-AB) DEC produced. -config MOUSE_HIL - tristate "HIL pointers (mice etc)." - depends on GSC || HP300 - select HP_SDC - select HIL_MLC - help - Say Y here to support HIL pointers. - config MOUSE_GPIO tristate "GPIO mouse" depends on GENERIC_GPIO diff --git a/drivers/input/mouse/Makefile b/drivers/input/mouse/Makefile index 010f265ec15..570c84a4a65 100644 --- a/drivers/input/mouse/Makefile +++ b/drivers/input/mouse/Makefile @@ -9,7 +9,6 @@ obj-$(CONFIG_MOUSE_APPLETOUCH) += appletouch.o obj-$(CONFIG_MOUSE_ATARI) += atarimouse.o obj-$(CONFIG_MOUSE_BCM5974) += bcm5974.o obj-$(CONFIG_MOUSE_GPIO) += gpio_mouse.o -obj-$(CONFIG_MOUSE_HIL) += hil_ptr.o obj-$(CONFIG_MOUSE_INPORT) += inport.o obj-$(CONFIG_MOUSE_LOGIBM) += logibm.o obj-$(CONFIG_MOUSE_MAPLE) += maplemouse.o @@ -28,5 +27,6 @@ psmouse-$(CONFIG_MOUSE_PS2_ELANTECH) += elantech.o psmouse-$(CONFIG_MOUSE_PS2_OLPC) += hgpk.o psmouse-$(CONFIG_MOUSE_PS2_LOGIPS2PP) += logips2pp.o psmouse-$(CONFIG_MOUSE_PS2_LIFEBOOK) += lifebook.o +psmouse-$(CONFIG_MOUSE_PS2_SENTELIC) += sentelic.o psmouse-$(CONFIG_MOUSE_PS2_TRACKPOINT) += trackpoint.o psmouse-$(CONFIG_MOUSE_PS2_TOUCHKIT) += touchkit_ps2.o diff --git a/drivers/input/mouse/alps.c b/drivers/input/mouse/alps.c index 5547e2429fb..a3f492a5085 100644 --- a/drivers/input/mouse/alps.c +++ b/drivers/input/mouse/alps.c @@ -28,13 +28,16 @@ #define dbg(format, arg...) do {} while (0) #endif -#define ALPS_DUALPOINT 0x01 -#define ALPS_WHEEL 0x02 -#define ALPS_FW_BK_1 0x04 -#define ALPS_4BTN 0x08 -#define ALPS_OLDPROTO 0x10 -#define ALPS_PASS 0x20 -#define ALPS_FW_BK_2 0x40 + +#define ALPS_OLDPROTO 0x01 /* old style input */ +#define ALPS_DUALPOINT 0x02 /* touchpad has trackstick */ +#define ALPS_PASS 0x04 /* device has a pass-through port */ + +#define ALPS_WHEEL 0x08 /* hardware wheel present */ +#define ALPS_FW_BK_1 0x10 /* front & back buttons present */ +#define ALPS_FW_BK_2 0x20 /* front & back buttons present */ +#define ALPS_FOUR_BUTTONS 0x40 /* 4 direction button present */ + static const struct alps_model_info alps_model_data[] = { { { 0x32, 0x02, 0x14 }, 0xf8, 0xf8, ALPS_PASS | ALPS_DUALPOINT }, /* Toshiba Salellite Pro M10 */ @@ -56,7 +59,7 @@ static const struct alps_model_info alps_model_data[] = { { { 0x22, 0x02, 0x0a }, 0xf8, 0xf8, ALPS_PASS | ALPS_DUALPOINT }, { { 0x22, 0x02, 0x14 }, 0xff, 0xff, ALPS_PASS | ALPS_DUALPOINT }, /* Dell Latitude D600 */ { { 0x62, 0x02, 0x14 }, 0xcf, 0xcf, ALPS_PASS | ALPS_DUALPOINT }, /* Dell Latitude E6500 */ - { { 0x73, 0x02, 0x50 }, 0xcf, 0xcf, ALPS_FW_BK_1 }, /* Dell Vostro 1400 */ + { { 0x73, 0x02, 0x50 }, 0xcf, 0xcf, ALPS_FOUR_BUTTONS }, /* Dell Vostro 1400 */ }; /* @@ -83,6 +86,7 @@ static const struct alps_model_info alps_model_data[] = { static void alps_process_packet(struct psmouse *psmouse) { struct alps_data *priv = psmouse->private; + const struct alps_model_info *model = priv->i; unsigned char *packet = psmouse->packet; struct input_dev *dev = psmouse->dev; struct input_dev *dev2 = priv->dev2; @@ -101,7 +105,7 @@ static void alps_process_packet(struct psmouse *psmouse) return; } - if (priv->i->flags & ALPS_OLDPROTO) { + if (model->flags & ALPS_OLDPROTO) { left = packet[2] & 0x10; right = packet[2] & 0x08; middle = 0; @@ -117,12 +121,12 @@ static void alps_process_packet(struct psmouse *psmouse) z = packet[5]; } - if (priv->i->flags & ALPS_FW_BK_1) { + if (model->flags & ALPS_FW_BK_1) { back = packet[0] & 0x10; forward = packet[2] & 4; } - if (priv->i->flags & ALPS_FW_BK_2) { + if (model->flags & ALPS_FW_BK_2) { back = packet[3] & 4; forward = packet[2] & 4; if ((middle = forward && back)) @@ -132,7 +136,7 @@ static void alps_process_packet(struct psmouse *psmouse) ges = packet[2] & 1; fin = packet[2] & 2; - if ((priv->i->flags & ALPS_DUALPOINT) && z == 127) { + if ((model->flags & ALPS_DUALPOINT) && z == 127) { input_report_rel(dev2, REL_X, (x > 383 ? (x - 768) : x)); input_report_rel(dev2, REL_Y, -(y > 255 ? (y - 512) : y)); @@ -150,7 +154,8 @@ static void alps_process_packet(struct psmouse *psmouse) input_report_key(dev, BTN_MIDDLE, middle); /* Convert hardware tap to a reasonable Z value */ - if (ges && !fin) z = 40; + if (ges && !fin) + z = 40; /* * A "tap and drag" operation is reported by the hardware as a transition @@ -166,8 +171,10 @@ static void alps_process_packet(struct psmouse *psmouse) } priv->prev_fin = fin; - if (z > 30) input_report_key(dev, BTN_TOUCH, 1); - if (z < 25) input_report_key(dev, BTN_TOUCH, 0); + if (z > 30) + input_report_key(dev, BTN_TOUCH, 1); + if (z < 25) + input_report_key(dev, BTN_TOUCH, 0); if (z > 0) { input_report_abs(dev, ABS_X, x); @@ -177,14 +184,21 @@ static void alps_process_packet(struct psmouse *psmouse) input_report_abs(dev, ABS_PRESSURE, z); input_report_key(dev, BTN_TOOL_FINGER, z > 0); - if (priv->i->flags & ALPS_WHEEL) + if (model->flags & ALPS_WHEEL) input_report_rel(dev, REL_WHEEL, ((packet[2] << 1) & 0x08) - ((packet[0] >> 4) & 0x07)); - if (priv->i->flags & (ALPS_FW_BK_1 | ALPS_FW_BK_2)) { + if (model->flags & (ALPS_FW_BK_1 | ALPS_FW_BK_2)) { input_report_key(dev, BTN_FORWARD, forward); input_report_key(dev, BTN_BACK, back); } + if (model->flags & ALPS_FOUR_BUTTONS) { + input_report_key(dev, BTN_0, packet[2] & 4); + input_report_key(dev, BTN_1, packet[0] & 0x10); + input_report_key(dev, BTN_2, packet[3] & 4); + input_report_key(dev, BTN_3, packet[0] & 0x20); + } + input_sync(dev); } @@ -279,7 +293,7 @@ static const struct alps_model_info *alps_get_model(struct psmouse *psmouse, int * subsequent commands. It looks like glidepad is behind stickpointer, * I'd thought it would be other way around... */ -static int alps_passthrough_mode(struct psmouse *psmouse, int enable) +static int alps_passthrough_mode(struct psmouse *psmouse, bool enable) { struct ps2dev *ps2dev = &psmouse->ps2dev; int cmd = enable ? PSMOUSE_CMD_SETSCALE21 : PSMOUSE_CMD_SETSCALE11; @@ -367,16 +381,16 @@ static int alps_poll(struct psmouse *psmouse) { struct alps_data *priv = psmouse->private; unsigned char buf[6]; - int poll_failed; + bool poll_failed; if (priv->i->flags & ALPS_PASS) - alps_passthrough_mode(psmouse, 1); + alps_passthrough_mode(psmouse, true); poll_failed = ps2_command(&psmouse->ps2dev, buf, PSMOUSE_CMD_POLL | (psmouse->pktsize << 8)) < 0; if (priv->i->flags & ALPS_PASS) - alps_passthrough_mode(psmouse, 0); + alps_passthrough_mode(psmouse, false); if (poll_failed || (buf[0] & priv->i->mask0) != priv->i->byte0) return -1; @@ -393,18 +407,17 @@ static int alps_poll(struct psmouse *psmouse) return 0; } -static int alps_hw_init(struct psmouse *psmouse, int *version) +static int alps_hw_init(struct psmouse *psmouse) { struct alps_data *priv = psmouse->private; + const struct alps_model_info *model = priv->i; - priv->i = alps_get_model(psmouse, version); - if (!priv->i) - return -1; - - if ((priv->i->flags & ALPS_PASS) && alps_passthrough_mode(psmouse, 1)) + if ((model->flags & ALPS_PASS) && + alps_passthrough_mode(psmouse, true)) { return -1; + } - if (alps_tap_mode(psmouse, 1)) { + if (alps_tap_mode(psmouse, true)) { printk(KERN_WARNING "alps.c: Failed to enable hardware tapping\n"); return -1; } @@ -414,8 +427,10 @@ static int alps_hw_init(struct psmouse *psmouse, int *version) return -1; } - if ((priv->i->flags & ALPS_PASS) && alps_passthrough_mode(psmouse, 0)) + if ((model->flags & ALPS_PASS) && + alps_passthrough_mode(psmouse, false)) { return -1; + } /* ALPS needs stream mode, otherwise it won't report any data */ if (ps2_command(&psmouse->ps2dev, NULL, PSMOUSE_CMD_SETSTREAM)) { @@ -428,12 +443,15 @@ static int alps_hw_init(struct psmouse *psmouse, int *version) static int alps_reconnect(struct psmouse *psmouse) { + const struct alps_model_info *model; + psmouse_reset(psmouse); - if (alps_hw_init(psmouse, NULL)) + model = alps_get_model(psmouse, NULL); + if (!model) return -1; - return 0; + return alps_hw_init(psmouse); } static void alps_disconnect(struct psmouse *psmouse) @@ -448,6 +466,7 @@ static void alps_disconnect(struct psmouse *psmouse) int alps_init(struct psmouse *psmouse) { struct alps_data *priv; + const struct alps_model_info *model; struct input_dev *dev1 = psmouse->dev, *dev2; int version; @@ -459,33 +478,48 @@ int alps_init(struct psmouse *psmouse) priv->dev2 = dev2; psmouse->private = priv; - if (alps_hw_init(psmouse, &version)) + model = alps_get_model(psmouse, &version); + if (!model) + goto init_fail; + + priv->i = model; + + if (alps_hw_init(psmouse)) goto init_fail; dev1->evbit[BIT_WORD(EV_KEY)] |= BIT_MASK(EV_KEY); dev1->keybit[BIT_WORD(BTN_TOUCH)] |= BIT_MASK(BTN_TOUCH); dev1->keybit[BIT_WORD(BTN_TOOL_FINGER)] |= BIT_MASK(BTN_TOOL_FINGER); - dev1->keybit[BIT_WORD(BTN_LEFT)] |= BIT_MASK(BTN_LEFT) | - BIT_MASK(BTN_MIDDLE) | BIT_MASK(BTN_RIGHT); + dev1->keybit[BIT_WORD(BTN_LEFT)] |= + BIT_MASK(BTN_LEFT) | BIT_MASK(BTN_RIGHT); dev1->evbit[BIT_WORD(EV_ABS)] |= BIT_MASK(EV_ABS); input_set_abs_params(dev1, ABS_X, 0, 1023, 0, 0); input_set_abs_params(dev1, ABS_Y, 0, 767, 0, 0); input_set_abs_params(dev1, ABS_PRESSURE, 0, 127, 0, 0); - if (priv->i->flags & ALPS_WHEEL) { + if (model->flags & ALPS_WHEEL) { dev1->evbit[BIT_WORD(EV_REL)] |= BIT_MASK(EV_REL); dev1->relbit[BIT_WORD(REL_WHEEL)] |= BIT_MASK(REL_WHEEL); } - if (priv->i->flags & (ALPS_FW_BK_1 | ALPS_FW_BK_2)) { + if (model->flags & (ALPS_FW_BK_1 | ALPS_FW_BK_2)) { dev1->keybit[BIT_WORD(BTN_FORWARD)] |= BIT_MASK(BTN_FORWARD); dev1->keybit[BIT_WORD(BTN_BACK)] |= BIT_MASK(BTN_BACK); } + if (model->flags & ALPS_FOUR_BUTTONS) { + dev1->keybit[BIT_WORD(BTN_0)] |= BIT_MASK(BTN_0); + dev1->keybit[BIT_WORD(BTN_1)] |= BIT_MASK(BTN_1); + dev1->keybit[BIT_WORD(BTN_2)] |= BIT_MASK(BTN_2); + dev1->keybit[BIT_WORD(BTN_3)] |= BIT_MASK(BTN_3); + } else { + dev1->keybit[BIT_WORD(BTN_MIDDLE)] |= BIT_MASK(BTN_MIDDLE); + } + snprintf(priv->phys, sizeof(priv->phys), "%s/input1", psmouse->ps2dev.serio->phys); dev2->phys = priv->phys; - dev2->name = (priv->i->flags & ALPS_DUALPOINT) ? "DualPoint Stick" : "PS/2 Mouse"; + dev2->name = (model->flags & ALPS_DUALPOINT) ? "DualPoint Stick" : "PS/2 Mouse"; dev2->id.bustype = BUS_I8042; dev2->id.vendor = 0x0002; dev2->id.product = PSMOUSE_ALPS; @@ -493,9 +527,9 @@ int alps_init(struct psmouse *psmouse) dev2->dev.parent = &psmouse->ps2dev.serio->dev; dev2->evbit[0] = BIT_MASK(EV_KEY) | BIT_MASK(EV_REL); - dev2->relbit[BIT_WORD(REL_X)] |= BIT_MASK(REL_X) | BIT_MASK(REL_Y); - dev2->keybit[BIT_WORD(BTN_LEFT)] |= BIT_MASK(BTN_LEFT) | - BIT_MASK(BTN_MIDDLE) | BIT_MASK(BTN_RIGHT); + dev2->relbit[BIT_WORD(REL_X)] = BIT_MASK(REL_X) | BIT_MASK(REL_Y); + dev2->keybit[BIT_WORD(BTN_LEFT)] = + BIT_MASK(BTN_LEFT) | BIT_MASK(BTN_MIDDLE) | BIT_MASK(BTN_RIGHT); if (input_register_device(priv->dev2)) goto init_fail; @@ -519,7 +553,7 @@ init_fail: return -1; } -int alps_detect(struct psmouse *psmouse, int set_properties) +int alps_detect(struct psmouse *psmouse, bool set_properties) { int version; const struct alps_model_info *model; diff --git a/drivers/input/mouse/alps.h b/drivers/input/mouse/alps.h index 4bbddc99962..bc87936fee1 100644 --- a/drivers/input/mouse/alps.h +++ b/drivers/input/mouse/alps.h @@ -26,10 +26,10 @@ struct alps_data { }; #ifdef CONFIG_MOUSE_PS2_ALPS -int alps_detect(struct psmouse *psmouse, int set_properties); +int alps_detect(struct psmouse *psmouse, bool set_properties); int alps_init(struct psmouse *psmouse); #else -inline int alps_detect(struct psmouse *psmouse, int set_properties) +inline int alps_detect(struct psmouse *psmouse, bool set_properties) { return -ENOSYS; } diff --git a/drivers/input/mouse/bcm5974.c b/drivers/input/mouse/bcm5974.c index 2d8fc0bf692..0d1d33468b4 100644 --- a/drivers/input/mouse/bcm5974.c +++ b/drivers/input/mouse/bcm5974.c @@ -317,7 +317,7 @@ static int report_tp_state(struct bcm5974 *dev, int size) const struct tp_finger *f; struct input_dev *input = dev->input; int raw_p, raw_w, raw_x, raw_y, raw_n; - int ptest = 0, origin = 0, ibt = 0, nmin = 0, nmax = 0; + int ptest, origin, ibt = 0, nmin = 0, nmax = 0; int abs_p = 0, abs_w = 0, abs_x = 0, abs_y = 0; if (size < c->tp_offset || (size - c->tp_offset) % SIZEOF_FINGER != 0) @@ -345,21 +345,22 @@ static int report_tp_state(struct bcm5974 *dev, int size) /* set the integrated button if applicable */ if (c->tp_type == TYPE2) ibt = raw2int(dev->tp_data[BUTTON_TYPE2]); - } - /* while tracking finger still valid, count all fingers */ - if (ptest > PRESSURE_LOW && origin) { - abs_p = ptest; - abs_w = int2bound(&c->w, raw_w); - abs_x = int2bound(&c->x, raw_x - c->x.devmin); - abs_y = int2bound(&c->y, c->y.devmax - raw_y); - while (raw_n--) { - ptest = int2bound(&c->p, raw2int(f->force_major)); - if (ptest > PRESSURE_LOW) - nmax++; - if (ptest > PRESSURE_HIGH) - nmin++; - f++; + /* while tracking finger still valid, count all fingers */ + if (ptest > PRESSURE_LOW && origin) { + abs_p = ptest; + abs_w = int2bound(&c->w, raw_w); + abs_x = int2bound(&c->x, raw_x - c->x.devmin); + abs_y = int2bound(&c->y, c->y.devmax - raw_y); + while (raw_n--) { + ptest = int2bound(&c->p, + raw2int(f->force_major)); + if (ptest > PRESSURE_LOW) + nmax++; + if (ptest > PRESSURE_HIGH) + nmin++; + f++; + } } } diff --git a/drivers/input/mouse/elantech.c b/drivers/input/mouse/elantech.c index 4bc78892ba9..b27684f267b 100644 --- a/drivers/input/mouse/elantech.c +++ b/drivers/input/mouse/elantech.c @@ -420,6 +420,7 @@ static void elantech_set_input_params(struct psmouse *psmouse) __set_bit(EV_KEY, dev->evbit); __set_bit(EV_ABS, dev->evbit); + __clear_bit(EV_REL, dev->evbit); __set_bit(BTN_LEFT, dev->keybit); __set_bit(BTN_RIGHT, dev->keybit); @@ -553,7 +554,7 @@ static struct attribute_group elantech_attr_group = { /* * Use magic knock to detect Elantech touchpad */ -int elantech_detect(struct psmouse *psmouse, int set_properties) +int elantech_detect(struct psmouse *psmouse, bool set_properties) { struct ps2dev *ps2dev = &psmouse->ps2dev; unsigned char param[3]; diff --git a/drivers/input/mouse/elantech.h b/drivers/input/mouse/elantech.h index ed848cc8081..feac5f7af96 100644 --- a/drivers/input/mouse/elantech.h +++ b/drivers/input/mouse/elantech.h @@ -109,10 +109,10 @@ struct elantech_data { }; #ifdef CONFIG_MOUSE_PS2_ELANTECH -int elantech_detect(struct psmouse *psmouse, int set_properties); +int elantech_detect(struct psmouse *psmouse, bool set_properties); int elantech_init(struct psmouse *psmouse); #else -static inline int elantech_detect(struct psmouse *psmouse, int set_properties) +static inline int elantech_detect(struct psmouse *psmouse, bool set_properties) { return -ENOSYS; } diff --git a/drivers/input/mouse/hgpk.c b/drivers/input/mouse/hgpk.c index a1ad2f1a7bb..b146237266d 100644 --- a/drivers/input/mouse/hgpk.c +++ b/drivers/input/mouse/hgpk.c @@ -367,7 +367,36 @@ static ssize_t hgpk_set_powered(struct psmouse *psmouse, void *data, } __PSMOUSE_DEFINE_ATTR(powered, S_IWUSR | S_IRUGO, NULL, - hgpk_show_powered, hgpk_set_powered, 0); + hgpk_show_powered, hgpk_set_powered, false); + +static ssize_t hgpk_trigger_recal_show(struct psmouse *psmouse, + void *data, char *buf) +{ + return -EINVAL; +} + +static ssize_t hgpk_trigger_recal(struct psmouse *psmouse, void *data, + const char *buf, size_t count) +{ + struct hgpk_data *priv = psmouse->private; + unsigned long value; + int err; + + err = strict_strtoul(buf, 10, &value); + if (err || value != 1) + return -EINVAL; + + /* + * We queue work instead of doing recalibration right here + * to avoid adding locking to to hgpk_force_recalibrate() + * since workqueue provides serialization. + */ + psmouse_queue_work(psmouse, &priv->recalib_wq, 0); + return count; +} + +__PSMOUSE_DEFINE_ATTR(recalibrate, S_IWUSR | S_IRUGO, NULL, + hgpk_trigger_recal_show, hgpk_trigger_recal, false); static void hgpk_disconnect(struct psmouse *psmouse) { @@ -375,6 +404,11 @@ static void hgpk_disconnect(struct psmouse *psmouse) device_remove_file(&psmouse->ps2dev.serio->dev, &psmouse_attr_powered.dattr); + + if (psmouse->model >= HGPK_MODEL_C) + device_remove_file(&psmouse->ps2dev.serio->dev, + &psmouse_attr_recalibrate.dattr); + psmouse_reset(psmouse); kfree(priv); } @@ -396,19 +430,6 @@ static int hgpk_register(struct psmouse *psmouse) struct input_dev *dev = psmouse->dev; int err; - /* unset the things that psmouse-base sets which we don't have */ - __clear_bit(BTN_MIDDLE, dev->keybit); - - /* set the things we do have */ - __set_bit(EV_KEY, dev->evbit); - __set_bit(EV_REL, dev->evbit); - - __set_bit(REL_X, dev->relbit); - __set_bit(REL_Y, dev->relbit); - - __set_bit(BTN_LEFT, dev->keybit); - __set_bit(BTN_RIGHT, dev->keybit); - /* register handlers */ psmouse->protocol_handler = hgpk_process_byte; psmouse->poll = hgpk_poll; @@ -423,10 +444,25 @@ static int hgpk_register(struct psmouse *psmouse) err = device_create_file(&psmouse->ps2dev.serio->dev, &psmouse_attr_powered.dattr); - if (err) - hgpk_err(psmouse, "Failed to create sysfs attribute\n"); + if (err) { + hgpk_err(psmouse, "Failed creating 'powered' sysfs node\n"); + return err; + } - return err; + /* C-series touchpads added the recalibrate command */ + if (psmouse->model >= HGPK_MODEL_C) { + err = device_create_file(&psmouse->ps2dev.serio->dev, + &psmouse_attr_recalibrate.dattr); + if (err) { + hgpk_err(psmouse, + "Failed creating 'recalibrate' sysfs node\n"); + device_remove_file(&psmouse->ps2dev.serio->dev, + &psmouse_attr_powered.dattr); + return err; + } + } + + return 0; } int hgpk_init(struct psmouse *psmouse) @@ -440,7 +476,7 @@ int hgpk_init(struct psmouse *psmouse) psmouse->private = priv; priv->psmouse = psmouse; - priv->powered = 1; + priv->powered = true; INIT_DELAYED_WORK(&priv->recalib_wq, hgpk_recalib_work); err = psmouse_reset(psmouse); @@ -483,7 +519,7 @@ static enum hgpk_model_t hgpk_get_model(struct psmouse *psmouse) return param[2]; } -int hgpk_detect(struct psmouse *psmouse, int set_properties) +int hgpk_detect(struct psmouse *psmouse, bool set_properties) { int version; diff --git a/drivers/input/mouse/hgpk.h b/drivers/input/mouse/hgpk.h index a4b2a96f5f5..d61cfd3ee9c 100644 --- a/drivers/input/mouse/hgpk.h +++ b/drivers/input/mouse/hgpk.h @@ -15,7 +15,7 @@ enum hgpk_model_t { struct hgpk_data { struct psmouse *psmouse; - int powered; + bool powered; int count, x_tally, y_tally; /* hardware workaround stuff */ unsigned long recalib_window; struct delayed_work recalib_wq; @@ -33,10 +33,10 @@ struct hgpk_data { dev_notice(&(psmouse)->ps2dev.serio->dev, format, ## arg) #ifdef CONFIG_MOUSE_PS2_OLPC -int hgpk_detect(struct psmouse *psmouse, int set_properties); +int hgpk_detect(struct psmouse *psmouse, bool set_properties); int hgpk_init(struct psmouse *psmouse); #else -static inline int hgpk_detect(struct psmouse *psmouse, int set_properties) +static inline int hgpk_detect(struct psmouse *psmouse, bool set_properties) { return -ENODEV; } diff --git a/drivers/input/mouse/hil_ptr.c b/drivers/input/mouse/hil_ptr.c deleted file mode 100644 index 3263ce083bf..00000000000 --- a/drivers/input/mouse/hil_ptr.c +++ /dev/null @@ -1,447 +0,0 @@ -/* - * Generic linux-input device driver for axis-bearing devices - * - * Copyright (c) 2001 Brian S. Julin - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions, and the following disclaimer, - * without modification. - * 2. The name of the author may not be used to endorse or promote products - * derived from this software without specific prior written permission. - * - * Alternatively, this software may be distributed under the terms of the - * GNU General Public License ("GPL"). - * - * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND - * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE - * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE FOR - * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL - * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS - * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) - * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT - * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY - * - * References: - * HP-HIL Technical Reference Manual. Hewlett Packard Product No. 45918A - * - */ - -#include <linux/hil.h> -#include <linux/input.h> -#include <linux/serio.h> -#include <linux/kernel.h> -#include <linux/module.h> -#include <linux/init.h> -#include <linux/slab.h> -#include <linux/pci_ids.h> - -#define PREFIX "HIL PTR: " -#define HIL_GENERIC_NAME "HIL pointer device" - -MODULE_AUTHOR("Brian S. Julin <bri@calyx.com>"); -MODULE_DESCRIPTION(HIL_GENERIC_NAME " driver"); -MODULE_LICENSE("Dual BSD/GPL"); -MODULE_ALIAS("serio:ty03pr25id0Fex*"); - -#define TABLET_SIMULATES_MOUSE /* allow tablet to be used as mouse */ -#undef TABLET_AUTOADJUST /* auto-adjust valid tablet ranges */ - - -#define HIL_PTR_MAX_LENGTH 16 - -struct hil_ptr { - struct input_dev *dev; - struct serio *serio; - - /* Input buffer and index for packets from HIL bus. */ - hil_packet data[HIL_PTR_MAX_LENGTH]; - int idx4; /* four counts per packet */ - - /* Raw device info records from HIL bus, see hil.h for fields. */ - char idd[HIL_PTR_MAX_LENGTH]; /* DID byte and IDD record */ - char rsc[HIL_PTR_MAX_LENGTH]; /* RSC record */ - char exd[HIL_PTR_MAX_LENGTH]; /* EXD record */ - char rnm[HIL_PTR_MAX_LENGTH + 1]; /* RNM record + NULL term. */ - - /* Extra device details not contained in struct input_dev. */ - unsigned int nbtn, naxes; - unsigned int btnmap[7]; - - /* Something to sleep around with. */ - struct semaphore sem; -}; - -/* Process a complete packet after transfer from the HIL */ -static void hil_ptr_process_record(struct hil_ptr *ptr) -{ - struct input_dev *dev = ptr->dev; - hil_packet *data = ptr->data; - hil_packet p; - int idx, i, cnt, laxis; - int ax16, absdev; - - idx = ptr->idx4/4; - p = data[idx - 1]; - - if ((p & ~HIL_CMDCT_POL) == - (HIL_ERR_INT | HIL_PKT_CMD | HIL_CMD_POL)) - goto report; - if ((p & ~HIL_CMDCT_RPL) == - (HIL_ERR_INT | HIL_PKT_CMD | HIL_CMD_RPL)) - goto report; - - /* Not a poll response. See if we are loading config records. */ - switch (p & HIL_PKT_DATA_MASK) { - case HIL_CMD_IDD: - for (i = 0; i < idx; i++) - ptr->idd[i] = ptr->data[i] & HIL_PKT_DATA_MASK; - for (; i < HIL_PTR_MAX_LENGTH; i++) - ptr->idd[i] = 0; - break; - - case HIL_CMD_RSC: - for (i = 0; i < idx; i++) - ptr->rsc[i] = ptr->data[i] & HIL_PKT_DATA_MASK; - for (; i < HIL_PTR_MAX_LENGTH; i++) - ptr->rsc[i] = 0; - break; - - case HIL_CMD_EXD: - for (i = 0; i < idx; i++) - ptr->exd[i] = ptr->data[i] & HIL_PKT_DATA_MASK; - for (; i < HIL_PTR_MAX_LENGTH; i++) - ptr->exd[i] = 0; - break; - - case HIL_CMD_RNM: - for (i = 0; i < idx; i++) - ptr->rnm[i] = ptr->data[i] & HIL_PKT_DATA_MASK; - for (; i < HIL_PTR_MAX_LENGTH + 1; i++) - ptr->rnm[i] = 0; - break; - - default: - /* These occur when device isn't present */ - if (p == (HIL_ERR_INT | HIL_PKT_CMD)) - break; - /* Anything else we'd like to know about. */ - printk(KERN_WARNING PREFIX "Device sent unknown record %x\n", p); - break; - } - goto out; - - report: - if ((p & HIL_CMDCT_POL) != idx - 1) { - printk(KERN_WARNING PREFIX - "Malformed poll packet %x (idx = %i)\n", p, idx); - goto out; - } - - i = (ptr->data[0] & HIL_POL_AXIS_ALT) ? 3 : 0; - laxis = ptr->data[0] & HIL_POL_NUM_AXES_MASK; - laxis += i; - - ax16 = ptr->idd[1] & HIL_IDD_HEADER_16BIT; /* 8 or 16bit resolution */ - absdev = ptr->idd[1] & HIL_IDD_HEADER_ABS; - - for (cnt = 1; i < laxis; i++) { - unsigned int lo,hi,val; - lo = ptr->data[cnt++] & HIL_PKT_DATA_MASK; - hi = ax16 ? (ptr->data[cnt++] & HIL_PKT_DATA_MASK) : 0; - if (absdev) { - val = lo + (hi<<8); -#ifdef TABLET_AUTOADJUST - if (val < dev->absmin[ABS_X + i]) - dev->absmin[ABS_X + i] = val; - if (val > dev->absmax[ABS_X + i]) - dev->absmax[ABS_X + i] = val; -#endif - if (i%3) val = dev->absmax[ABS_X + i] - val; - input_report_abs(dev, ABS_X + i, val); - } else { - val = (int) (((int8_t)lo) | ((int8_t)hi<<8)); - if (i%3) - val *= -1; - input_report_rel(dev, REL_X + i, val); - } - } - - while (cnt < idx - 1) { - unsigned int btn; - int up; - btn = ptr->data[cnt++]; - up = btn & 1; - btn &= 0xfe; - if (btn == 0x8e) - continue; /* TODO: proximity == touch? */ - else - if ((btn > 0x8c) || (btn < 0x80)) - continue; - btn = (btn - 0x80) >> 1; - btn = ptr->btnmap[btn]; - input_report_key(dev, btn, !up); - } - input_sync(dev); - out: - ptr->idx4 = 0; - up(&ptr->sem); -} - -static void hil_ptr_process_err(struct hil_ptr *ptr) -{ - printk(KERN_WARNING PREFIX "errored HIL packet\n"); - ptr->idx4 = 0; - up(&ptr->sem); -} - -static irqreturn_t hil_ptr_interrupt(struct serio *serio, - unsigned char data, unsigned int flags) -{ - struct hil_ptr *ptr; - hil_packet packet; - int idx; - - ptr = serio_get_drvdata(serio); - BUG_ON(ptr == NULL); - - if (ptr->idx4 >= (HIL_PTR_MAX_LENGTH * sizeof(hil_packet))) { - hil_ptr_process_err(ptr); - return IRQ_HANDLED; - } - idx = ptr->idx4/4; - if (!(ptr->idx4 % 4)) - ptr->data[idx] = 0; - packet = ptr->data[idx]; - packet |= ((hil_packet)data) << ((3 - (ptr->idx4 % 4)) * 8); - ptr->data[idx] = packet; - - /* Records of N 4-byte hil_packets must terminate with a command. */ - if ((++(ptr->idx4)) % 4) - return IRQ_HANDLED; - if ((packet & 0xffff0000) != HIL_ERR_INT) { - hil_ptr_process_err(ptr); - return IRQ_HANDLED; - } - if (packet & HIL_PKT_CMD) - hil_ptr_process_record(ptr); - - return IRQ_HANDLED; -} - -static void hil_ptr_disconnect(struct serio *serio) -{ - struct hil_ptr *ptr; - - ptr = serio_get_drvdata(serio); - BUG_ON(ptr == NULL); - - serio_close(serio); - input_unregister_device(ptr->dev); - kfree(ptr); -} - -static int hil_ptr_connect(struct serio *serio, struct serio_driver *driver) -{ - struct hil_ptr *ptr; - const char *txt; - unsigned int i, naxsets, btntype; - uint8_t did, *idd; - int error; - - ptr = kzalloc(sizeof(struct hil_ptr), GFP_KERNEL); - if (!ptr) - return -ENOMEM; - - ptr->dev = input_allocate_device(); - if (!ptr->dev) { - error = -ENOMEM; - goto bail0; - } - - error = serio_open(serio, driver); - if (error) - goto bail1; - - serio_set_drvdata(serio, ptr); - ptr->serio = serio; - - init_MUTEX_LOCKED(&ptr->sem); - - /* Get device info. MLC driver supplies devid/status/etc. */ - serio->write(serio, 0); - serio->write(serio, 0); - serio->write(serio, HIL_PKT_CMD >> 8); - serio->write(serio, HIL_CMD_IDD); - down(&ptr->sem); - - serio->write(serio, 0); - serio->write(serio, 0); - serio->write(serio, HIL_PKT_CMD >> 8); - serio->write(serio, HIL_CMD_RSC); - down(&ptr->sem); - - serio->write(serio, 0); - serio->write(serio, 0); - serio->write(serio, HIL_PKT_CMD >> 8); - serio->write(serio, HIL_CMD_RNM); - down(&ptr->sem); - - serio->write(serio, 0); - serio->write(serio, 0); - serio->write(serio, HIL_PKT_CMD >> 8); - serio->write(serio, HIL_CMD_EXD); - down(&ptr->sem); - - up(&ptr->sem); - - did = ptr->idd[0]; - idd = ptr->idd + 1; - txt = "unknown"; - - if ((did & HIL_IDD_DID_TYPE_MASK) == HIL_IDD_DID_TYPE_REL) { - ptr->dev->evbit[0] = BIT_MASK(EV_REL); - txt = "relative"; - } - - if ((did & HIL_IDD_DID_TYPE_MASK) == HIL_IDD_DID_TYPE_ABS) { - ptr->dev->evbit[0] = BIT_MASK(EV_ABS); - txt = "absolute"; - } - - if (!ptr->dev->evbit[0]) { - error = -ENODEV; - goto bail2; - } - - ptr->nbtn = HIL_IDD_NUM_BUTTONS(idd); - if (ptr->nbtn) - ptr->dev->evbit[0] |= BIT_MASK(EV_KEY); - - naxsets = HIL_IDD_NUM_AXSETS(*idd); - ptr->naxes = HIL_IDD_NUM_AXES_PER_SET(*idd); - - printk(KERN_INFO PREFIX "HIL pointer device found (did: 0x%02x, axis: %s)\n", - did, txt); - printk(KERN_INFO PREFIX "HIL pointer has %i buttons and %i sets of %i axes\n", - ptr->nbtn, naxsets, ptr->naxes); - - btntype = BTN_MISC; - if ((did & HIL_IDD_DID_ABS_TABLET_MASK) == HIL_IDD_DID_ABS_TABLET) -#ifdef TABLET_SIMULATES_MOUSE - btntype = BTN_TOUCH; -#else - btntype = BTN_DIGI; -#endif - if ((did & HIL_IDD_DID_ABS_TSCREEN_MASK) == HIL_IDD_DID_ABS_TSCREEN) - btntype = BTN_TOUCH; - - if ((did & HIL_IDD_DID_REL_MOUSE_MASK) == HIL_IDD_DID_REL_MOUSE) - btntype = BTN_MOUSE; - - for (i = 0; i < ptr->nbtn; i++) { - set_bit(btntype | i, ptr->dev->keybit); - ptr->btnmap[i] = btntype | i; - } - - if (btntype == BTN_MOUSE) { - /* Swap buttons 2 and 3 */ - ptr->btnmap[1] = BTN_MIDDLE; - ptr->btnmap[2] = BTN_RIGHT; - } - - if ((did & HIL_IDD_DID_TYPE_MASK) == HIL_IDD_DID_TYPE_REL) { - for (i = 0; i < ptr->naxes; i++) - set_bit(REL_X + i, ptr->dev->relbit); - for (i = 3; (i < ptr->naxes + 3) && (naxsets > 1); i++) - set_bit(REL_X + i, ptr->dev->relbit); - } else { - for (i = 0; i < ptr->naxes; i++) { - set_bit(ABS_X + i, ptr->dev->absbit); - ptr->dev->absmin[ABS_X + i] = 0; - ptr->dev->absmax[ABS_X + i] = - HIL_IDD_AXIS_MAX((ptr->idd + 1), i); - } - for (i = 3; (i < ptr->naxes + 3) && (naxsets > 1); i++) { - set_bit(ABS_X + i, ptr->dev->absbit); - ptr->dev->absmin[ABS_X + i] = 0; - ptr->dev->absmax[ABS_X + i] = - HIL_IDD_AXIS_MAX((ptr->idd + 1), (i - 3)); - } -#ifdef TABLET_AUTOADJUST - for (i = 0; i < ABS_MAX; i++) { - int diff = ptr->dev->absmax[ABS_X + i] / 10; - ptr->dev->absmin[ABS_X + i] += diff; - ptr->dev->absmax[ABS_X + i] -= diff; - } -#endif - } - - ptr->dev->name = strlen(ptr->rnm) ? ptr->rnm : HIL_GENERIC_NAME; - - ptr->dev->id.bustype = BUS_HIL; - ptr->dev->id.vendor = PCI_VENDOR_ID_HP; - ptr->dev->id.product = 0x0001; /* TODO: get from ptr->rsc */ - ptr->dev->id.version = 0x0100; /* TODO: get from ptr->rsc */ - ptr->dev->dev.parent = &serio->dev; - - error = input_register_device(ptr->dev); - if (error) { - printk(KERN_INFO PREFIX "Unable to register input device\n"); - goto bail2; - } - - printk(KERN_INFO "input: %s (%s), ID: %d\n", - ptr->dev->name, - (btntype == BTN_MOUSE) ? "HIL mouse":"HIL tablet or touchpad", - did); - - return 0; - - bail2: - serio_close(serio); - bail1: - input_free_device(ptr->dev); - bail0: - kfree(ptr); - serio_set_drvdata(serio, NULL); - return error; -} - -static struct serio_device_id hil_ptr_ids[] = { - { - .type = SERIO_HIL_MLC, - .proto = SERIO_HIL, - .id = SERIO_ANY, - .extra = SERIO_ANY, - }, - { 0 } -}; - -static struct serio_driver hil_ptr_serio_driver = { - .driver = { - .name = "hil_ptr", - }, - .description = "HP HIL mouse/tablet driver", - .id_table = hil_ptr_ids, - .connect = hil_ptr_connect, - .disconnect = hil_ptr_disconnect, - .interrupt = hil_ptr_interrupt -}; - -static int __init hil_ptr_init(void) -{ - return serio_register_driver(&hil_ptr_serio_driver); -} - -static void __exit hil_ptr_exit(void) -{ - serio_unregister_driver(&hil_ptr_serio_driver); -} - -module_init(hil_ptr_init); -module_exit(hil_ptr_exit); diff --git a/drivers/input/mouse/lifebook.c b/drivers/input/mouse/lifebook.c index dcd4236af1e..2e6bdfea016 100644 --- a/drivers/input/mouse/lifebook.c +++ b/drivers/input/mouse/lifebook.c @@ -25,69 +25,71 @@ struct lifebook_data { char phys[32]; }; +static bool lifebook_present; + static const char *desired_serio_phys; -static int lifebook_set_serio_phys(const struct dmi_system_id *d) +static int lifebook_limit_serio3(const struct dmi_system_id *d) { - desired_serio_phys = d->driver_data; + desired_serio_phys = "isa0060/serio3"; return 0; } -static unsigned char lifebook_use_6byte_proto; +static bool lifebook_use_6byte_proto; static int lifebook_set_6byte_proto(const struct dmi_system_id *d) { - lifebook_use_6byte_proto = 1; + lifebook_use_6byte_proto = true; return 0; } -static const struct dmi_system_id lifebook_dmi_table[] = { +static const struct dmi_system_id __initconst lifebook_dmi_table[] = { +#if defined(CONFIG_DMI) && defined(CONFIG_X86) { - .ident = "FLORA-ie 55mi", + /* FLORA-ie 55mi */ .matches = { DMI_MATCH(DMI_PRODUCT_NAME, "FLORA-ie 55mi"), }, }, { - .ident = "LifeBook B", + /* LifeBook B */ .matches = { DMI_MATCH(DMI_PRODUCT_NAME, "LifeBook B Series"), }, }, { - .ident = "Lifebook B", + /* Lifebook B */ .matches = { DMI_MATCH(DMI_PRODUCT_NAME, "LIFEBOOK B Series"), }, }, { - .ident = "Lifebook B-2130", + /* Lifebook B-2130 */ .matches = { DMI_MATCH(DMI_BOARD_NAME, "ZEPHYR"), }, }, { - .ident = "Lifebook B213x/B2150", + /* Lifebook B213x/B2150 */ .matches = { DMI_MATCH(DMI_PRODUCT_NAME, "LifeBook B2131/B2133/B2150"), }, }, { - .ident = "Zephyr", + /* Zephyr */ .matches = { DMI_MATCH(DMI_PRODUCT_NAME, "ZEPHYR"), }, }, { - .ident = "CF-18", + /* Panasonic CF-18 */ .matches = { DMI_MATCH(DMI_PRODUCT_NAME, "CF-18"), }, - .callback = lifebook_set_serio_phys, - .driver_data = "isa0060/serio3", + .callback = lifebook_limit_serio3, }, { - .ident = "Panasonic CF-28", + /* Panasonic CF-28 */ .matches = { DMI_MATCH(DMI_SYS_VENDOR, "Matsushita"), DMI_MATCH(DMI_PRODUCT_NAME, "CF-28"), @@ -95,7 +97,7 @@ static const struct dmi_system_id lifebook_dmi_table[] = { .callback = lifebook_set_6byte_proto, }, { - .ident = "Panasonic CF-29", + /* Panasonic CF-29 */ .matches = { DMI_MATCH(DMI_SYS_VENDOR, "Matsushita"), DMI_MATCH(DMI_PRODUCT_NAME, "CF-29"), @@ -103,29 +105,34 @@ static const struct dmi_system_id lifebook_dmi_table[] = { .callback = lifebook_set_6byte_proto, }, { - .ident = "CF-72", + /* Panasonic CF-72 */ .matches = { DMI_MATCH(DMI_PRODUCT_NAME, "CF-72"), }, - .callback = lifebook_set_serio_phys, - .driver_data = "isa0060/serio3", + .callback = lifebook_set_6byte_proto, }, { - .ident = "Lifebook B142", + /* Lifebook B142 */ .matches = { DMI_MATCH(DMI_PRODUCT_NAME, "LifeBook B142"), }, }, { } +#endif }; +void __init lifebook_module_init(void) +{ + lifebook_present = dmi_check_system(lifebook_dmi_table); +} + static psmouse_ret_t lifebook_process_byte(struct psmouse *psmouse) { struct lifebook_data *priv = psmouse->private; struct input_dev *dev1 = psmouse->dev; struct input_dev *dev2 = priv ? priv->dev2 : NULL; unsigned char *packet = psmouse->packet; - int relative_packet = packet[0] & 0x08; + bool relative_packet = packet[0] & 0x08; if (relative_packet || !lifebook_use_6byte_proto) { if (psmouse->pktcnt != 3) @@ -199,10 +206,10 @@ static int lifebook_absolute_mode(struct psmouse *psmouse) return -1; /* - Enable absolute output -- ps2_command fails always but if - you leave this call out the touchsreen will never send - absolute coordinates - */ + * Enable absolute output -- ps2_command fails always but if + * you leave this call out the touchsreen will never send + * absolute coordinates + */ param = lifebook_use_6byte_proto ? 0x08 : 0x07; ps2_command(ps2dev, ¶m, PSMOUSE_CMD_SETRES); @@ -242,9 +249,9 @@ static void lifebook_disconnect(struct psmouse *psmouse) psmouse->private = NULL; } -int lifebook_detect(struct psmouse *psmouse, int set_properties) +int lifebook_detect(struct psmouse *psmouse, bool set_properties) { - if (!dmi_check_system(lifebook_dmi_table)) + if (!lifebook_present) return -1; if (desired_serio_phys && @@ -284,8 +291,8 @@ static int lifebook_create_relative_device(struct psmouse *psmouse) dev2->evbit[0] = BIT_MASK(EV_KEY) | BIT_MASK(EV_REL); dev2->relbit[BIT_WORD(REL_X)] = BIT_MASK(REL_X) | BIT_MASK(REL_Y); - dev2->keybit[BIT_WORD(BTN_LEFT)] = BIT_MASK(BTN_LEFT) | - BIT_MASK(BTN_RIGHT); + dev2->keybit[BIT_WORD(BTN_LEFT)] = + BIT_MASK(BTN_LEFT) | BIT_MASK(BTN_RIGHT); error = input_register_device(priv->dev2); if (error) @@ -310,6 +317,7 @@ int lifebook_init(struct psmouse *psmouse) dev1->evbit[0] = BIT_MASK(EV_ABS) | BIT_MASK(EV_KEY); dev1->relbit[0] = 0; + dev1->keybit[BIT_WORD(BTN_MOUSE)] = 0; dev1->keybit[BIT_WORD(BTN_TOUCH)] = BIT_MASK(BTN_TOUCH); input_set_abs_params(dev1, ABS_X, 0, max_coord, 0, 0); input_set_abs_params(dev1, ABS_Y, 0, max_coord, 0, 0); diff --git a/drivers/input/mouse/lifebook.h b/drivers/input/mouse/lifebook.h index c1647cf036c..4c4326c6f50 100644 --- a/drivers/input/mouse/lifebook.h +++ b/drivers/input/mouse/lifebook.h @@ -12,10 +12,14 @@ #define _LIFEBOOK_H #ifdef CONFIG_MOUSE_PS2_LIFEBOOK -int lifebook_detect(struct psmouse *psmouse, int set_properties); +void lifebook_module_init(void); +int lifebook_detect(struct psmouse *psmouse, bool set_properties); int lifebook_init(struct psmouse *psmouse); #else -inline int lifebook_detect(struct psmouse *psmouse, int set_properties) +inline void lifebook_module_init(void) +{ +} +inline int lifebook_detect(struct psmouse *psmouse, bool set_properties) { return -ENOSYS; } diff --git a/drivers/input/mouse/logips2pp.c b/drivers/input/mouse/logips2pp.c index 390f1dbb98a..543c240a85f 100644 --- a/drivers/input/mouse/logips2pp.c +++ b/drivers/input/mouse/logips2pp.c @@ -130,14 +130,11 @@ static int ps2pp_cmd(struct psmouse *psmouse, unsigned char *param, unsigned cha * 0 - disabled */ -static void ps2pp_set_smartscroll(struct psmouse *psmouse, unsigned int smartscroll) +static void ps2pp_set_smartscroll(struct psmouse *psmouse, bool smartscroll) { struct ps2dev *ps2dev = &psmouse->ps2dev; unsigned char param[4]; - if (smartscroll > 1) - smartscroll = 1; - ps2pp_cmd(psmouse, param, 0x32); param[0] = 0; @@ -149,12 +146,14 @@ static void ps2pp_set_smartscroll(struct psmouse *psmouse, unsigned int smartscr ps2_command(ps2dev, param, PSMOUSE_CMD_SETRES); } -static ssize_t ps2pp_attr_show_smartscroll(struct psmouse *psmouse, void *data, char *buf) +static ssize_t ps2pp_attr_show_smartscroll(struct psmouse *psmouse, + void *data, char *buf) { - return sprintf(buf, "%d\n", psmouse->smartscroll ? 1 : 0); + return sprintf(buf, "%d\n", psmouse->smartscroll); } -static ssize_t ps2pp_attr_set_smartscroll(struct psmouse *psmouse, void *data, const char *buf, size_t count) +static ssize_t ps2pp_attr_set_smartscroll(struct psmouse *psmouse, void *data, + const char *buf, size_t count) { unsigned long value; @@ -220,7 +219,7 @@ static const struct ps2pp_info *get_model_info(unsigned char model) PS2PP_WHEEL | PS2PP_SIDE_BTN | PS2PP_TASK_BTN | PS2PP_EXTRA_BTN | PS2PP_NAV_BTN | PS2PP_HWHEEL }, { 72, PS2PP_KIND_TRACKMAN, 0 }, /* T-CH11: TrackMan Marble */ - { 73, 0, PS2PP_SIDE_BTN }, + { 73, PS2PP_KIND_TRACKMAN, PS2PP_SIDE_BTN }, /* TrackMan FX */ { 75, PS2PP_KIND_WHEEL, PS2PP_WHEEL }, { 76, PS2PP_KIND_WHEEL, PS2PP_WHEEL }, { 79, PS2PP_KIND_TRACKMAN, PS2PP_WHEEL }, /* TrackMan with wheel */ @@ -261,29 +260,29 @@ static const struct ps2pp_info *get_model_info(unsigned char model) static void ps2pp_set_model_properties(struct psmouse *psmouse, const struct ps2pp_info *model_info, - int using_ps2pp) + bool using_ps2pp) { struct input_dev *input_dev = psmouse->dev; if (model_info->features & PS2PP_SIDE_BTN) - set_bit(BTN_SIDE, input_dev->keybit); + __set_bit(BTN_SIDE, input_dev->keybit); if (model_info->features & PS2PP_EXTRA_BTN) - set_bit(BTN_EXTRA, input_dev->keybit); + __set_bit(BTN_EXTRA, input_dev->keybit); if (model_info->features & PS2PP_TASK_BTN) - set_bit(BTN_TASK, input_dev->keybit); + __set_bit(BTN_TASK, input_dev->keybit); if (model_info->features & PS2PP_NAV_BTN) { - set_bit(BTN_FORWARD, input_dev->keybit); - set_bit(BTN_BACK, input_dev->keybit); + __set_bit(BTN_FORWARD, input_dev->keybit); + __set_bit(BTN_BACK, input_dev->keybit); } if (model_info->features & PS2PP_WHEEL) - set_bit(REL_WHEEL, input_dev->relbit); + __set_bit(REL_WHEEL, input_dev->relbit); if (model_info->features & PS2PP_HWHEEL) - set_bit(REL_HWHEEL, input_dev->relbit); + __set_bit(REL_HWHEEL, input_dev->relbit); switch (model_info->kind) { case PS2PP_KIND_WHEEL: @@ -321,13 +320,13 @@ static void ps2pp_set_model_properties(struct psmouse *psmouse, * that support it. */ -int ps2pp_init(struct psmouse *psmouse, int set_properties) +int ps2pp_init(struct psmouse *psmouse, bool set_properties) { struct ps2dev *ps2dev = &psmouse->ps2dev; unsigned char param[4]; unsigned char model, buttons; const struct ps2pp_info *model_info; - int use_ps2pp = 0; + bool use_ps2pp = false; int error; param[0] = 0; @@ -364,7 +363,7 @@ int ps2pp_init(struct psmouse *psmouse, int set_properties) param[0] = 0; if (!ps2_command(ps2dev, param, 0x13d1) && param[0] == 0x06 && param[1] == 0x00 && param[2] == 0x14) { - use_ps2pp = 1; + use_ps2pp = true; } } else { @@ -376,8 +375,8 @@ int ps2pp_init(struct psmouse *psmouse, int set_properties) if ((param[0] & 0x78) == 0x48 && (param[1] & 0xf3) == 0xc2 && (param[2] & 0x03) == ((param[1] >> 2) & 3)) { - ps2pp_set_smartscroll(psmouse, psmouse->smartscroll); - use_ps2pp = 1; + ps2pp_set_smartscroll(psmouse, false); + use_ps2pp = true; } } } @@ -405,8 +404,8 @@ int ps2pp_init(struct psmouse *psmouse, int set_properties) } } - if (buttons < 3) - clear_bit(BTN_MIDDLE, psmouse->dev->keybit); + if (buttons >= 3) + __set_bit(BTN_MIDDLE, psmouse->dev->keybit); if (model_info) ps2pp_set_model_properties(psmouse, model_info, use_ps2pp); diff --git a/drivers/input/mouse/logips2pp.h b/drivers/input/mouse/logips2pp.h index 6e5712525fd..0c186f0282d 100644 --- a/drivers/input/mouse/logips2pp.h +++ b/drivers/input/mouse/logips2pp.h @@ -12,9 +12,9 @@ #define _LOGIPS2PP_H #ifdef CONFIG_MOUSE_PS2_LOGIPS2PP -int ps2pp_init(struct psmouse *psmouse, int set_properties); +int ps2pp_init(struct psmouse *psmouse, bool set_properties); #else -inline int ps2pp_init(struct psmouse *psmouse, int set_properties) +inline int ps2pp_init(struct psmouse *psmouse, bool set_properties) { return -ENOSYS; } diff --git a/drivers/input/mouse/psmouse-base.c b/drivers/input/mouse/psmouse-base.c index b407b355dce..fd0bc094616 100644 --- a/drivers/input/mouse/psmouse-base.c +++ b/drivers/input/mouse/psmouse-base.c @@ -30,6 +30,7 @@ #include "trackpoint.h" #include "touchkit_ps2.h" #include "elantech.h" +#include "sentelic.h" #define DRIVER_DESC "PS/2 mouse driver" @@ -108,10 +109,10 @@ static struct workqueue_struct *kpsmoused_wq; struct psmouse_protocol { enum psmouse_type type; + bool maxproto; const char *name; const char *alias; - int maxproto; - int (*detect)(struct psmouse *, int); + int (*detect)(struct psmouse *, bool); int (*init)(struct psmouse *); }; @@ -216,7 +217,7 @@ void psmouse_queue_work(struct psmouse *psmouse, struct delayed_work *work, static inline void __psmouse_set_state(struct psmouse *psmouse, enum psmouse_state new_state) { psmouse->state = new_state; - psmouse->pktcnt = psmouse->out_of_sync = 0; + psmouse->pktcnt = psmouse->out_of_sync_cnt = 0; psmouse->ps2dev.flags = 0; psmouse->last = jiffies; } @@ -249,7 +250,7 @@ static int psmouse_handle_byte(struct psmouse *psmouse) if (psmouse->state == PSMOUSE_ACTIVATED) { printk(KERN_WARNING "psmouse.c: %s at %s lost sync at byte %d\n", psmouse->name, psmouse->phys, psmouse->pktcnt); - if (++psmouse->out_of_sync == psmouse->resetafter) { + if (++psmouse->out_of_sync_cnt == psmouse->resetafter) { __psmouse_set_state(psmouse, PSMOUSE_IGNORE); printk(KERN_NOTICE "psmouse.c: issuing reconnect request\n"); serio_reconnect(psmouse->ps2dev.serio); @@ -261,8 +262,8 @@ static int psmouse_handle_byte(struct psmouse *psmouse) case PSMOUSE_FULL_PACKET: psmouse->pktcnt = 0; - if (psmouse->out_of_sync) { - psmouse->out_of_sync = 0; + if (psmouse->out_of_sync_cnt) { + psmouse->out_of_sync_cnt = 0; printk(KERN_NOTICE "psmouse.c: %s at %s - driver resynched.\n", psmouse->name, psmouse->phys); } @@ -408,7 +409,7 @@ int psmouse_reset(struct psmouse *psmouse) /* * Genius NetMouse magic init. */ -static int genius_detect(struct psmouse *psmouse, int set_properties) +static int genius_detect(struct psmouse *psmouse, bool set_properties) { struct ps2dev *ps2dev = &psmouse->ps2dev; unsigned char param[4]; @@ -424,9 +425,10 @@ static int genius_detect(struct psmouse *psmouse, int set_properties) return -1; if (set_properties) { - set_bit(BTN_EXTRA, psmouse->dev->keybit); - set_bit(BTN_SIDE, psmouse->dev->keybit); - set_bit(REL_WHEEL, psmouse->dev->relbit); + __set_bit(BTN_MIDDLE, psmouse->dev->keybit); + __set_bit(BTN_EXTRA, psmouse->dev->keybit); + __set_bit(BTN_SIDE, psmouse->dev->keybit); + __set_bit(REL_WHEEL, psmouse->dev->relbit); psmouse->vendor = "Genius"; psmouse->name = "Mouse"; @@ -439,7 +441,7 @@ static int genius_detect(struct psmouse *psmouse, int set_properties) /* * IntelliMouse magic init. */ -static int intellimouse_detect(struct psmouse *psmouse, int set_properties) +static int intellimouse_detect(struct psmouse *psmouse, bool set_properties) { struct ps2dev *ps2dev = &psmouse->ps2dev; unsigned char param[2]; @@ -456,11 +458,13 @@ static int intellimouse_detect(struct psmouse *psmouse, int set_properties) return -1; if (set_properties) { - set_bit(BTN_MIDDLE, psmouse->dev->keybit); - set_bit(REL_WHEEL, psmouse->dev->relbit); + __set_bit(BTN_MIDDLE, psmouse->dev->keybit); + __set_bit(REL_WHEEL, psmouse->dev->relbit); - if (!psmouse->vendor) psmouse->vendor = "Generic"; - if (!psmouse->name) psmouse->name = "Wheel Mouse"; + if (!psmouse->vendor) + psmouse->vendor = "Generic"; + if (!psmouse->name) + psmouse->name = "Wheel Mouse"; psmouse->pktsize = 4; } @@ -470,7 +474,7 @@ static int intellimouse_detect(struct psmouse *psmouse, int set_properties) /* * Try IntelliMouse/Explorer magic init. */ -static int im_explorer_detect(struct psmouse *psmouse, int set_properties) +static int im_explorer_detect(struct psmouse *psmouse, bool set_properties) { struct ps2dev *ps2dev = &psmouse->ps2dev; unsigned char param[2]; @@ -497,14 +501,16 @@ static int im_explorer_detect(struct psmouse *psmouse, int set_properties) ps2_command(ps2dev, param, PSMOUSE_CMD_SETRATE); if (set_properties) { - set_bit(BTN_MIDDLE, psmouse->dev->keybit); - set_bit(REL_WHEEL, psmouse->dev->relbit); - set_bit(REL_HWHEEL, psmouse->dev->relbit); - set_bit(BTN_SIDE, psmouse->dev->keybit); - set_bit(BTN_EXTRA, psmouse->dev->keybit); - - if (!psmouse->vendor) psmouse->vendor = "Generic"; - if (!psmouse->name) psmouse->name = "Explorer Mouse"; + __set_bit(BTN_MIDDLE, psmouse->dev->keybit); + __set_bit(REL_WHEEL, psmouse->dev->relbit); + __set_bit(REL_HWHEEL, psmouse->dev->relbit); + __set_bit(BTN_SIDE, psmouse->dev->keybit); + __set_bit(BTN_EXTRA, psmouse->dev->keybit); + + if (!psmouse->vendor) + psmouse->vendor = "Generic"; + if (!psmouse->name) + psmouse->name = "Explorer Mouse"; psmouse->pktsize = 4; } @@ -514,7 +520,7 @@ static int im_explorer_detect(struct psmouse *psmouse, int set_properties) /* * Kensington ThinkingMouse / ExpertMouse magic init. */ -static int thinking_detect(struct psmouse *psmouse, int set_properties) +static int thinking_detect(struct psmouse *psmouse, bool set_properties) { struct ps2dev *ps2dev = &psmouse->ps2dev; unsigned char param[2]; @@ -535,7 +541,8 @@ static int thinking_detect(struct psmouse *psmouse, int set_properties) return -1; if (set_properties) { - set_bit(BTN_EXTRA, psmouse->dev->keybit); + __set_bit(BTN_MIDDLE, psmouse->dev->keybit); + __set_bit(BTN_EXTRA, psmouse->dev->keybit); psmouse->vendor = "Kensington"; psmouse->name = "ThinkingMouse"; @@ -547,11 +554,19 @@ static int thinking_detect(struct psmouse *psmouse, int set_properties) /* * Bare PS/2 protocol "detection". Always succeeds. */ -static int ps2bare_detect(struct psmouse *psmouse, int set_properties) +static int ps2bare_detect(struct psmouse *psmouse, bool set_properties) { if (set_properties) { - if (!psmouse->vendor) psmouse->vendor = "Generic"; - if (!psmouse->name) psmouse->name = "Mouse"; + if (!psmouse->vendor) + psmouse->vendor = "Generic"; + if (!psmouse->name) + psmouse->name = "Mouse"; + +/* + * We have no way of figuring true number of buttons so let's + * assume that the device has 3. + */ + __set_bit(BTN_MIDDLE, psmouse->dev->keybit); } return 0; @@ -561,12 +576,14 @@ static int ps2bare_detect(struct psmouse *psmouse, int set_properties) * Cortron PS/2 protocol detection. There's no special way to detect it, so it * must be forced by sysfs protocol writing. */ -static int cortron_detect(struct psmouse *psmouse, int set_properties) +static int cortron_detect(struct psmouse *psmouse, bool set_properties) { if (set_properties) { psmouse->vendor = "Cortron"; psmouse->name = "PS/2 Trackball"; - set_bit(BTN_SIDE, psmouse->dev->keybit); + + __set_bit(BTN_MIDDLE, psmouse->dev->keybit); + __set_bit(BTN_SIDE, psmouse->dev->keybit); } return 0; @@ -578,9 +595,9 @@ static int cortron_detect(struct psmouse *psmouse, int set_properties) */ static int psmouse_extensions(struct psmouse *psmouse, - unsigned int max_proto, int set_properties) + unsigned int max_proto, bool set_properties) { - int synaptics_hardware = 0; + bool synaptics_hardware = false; /* * We always check for lifebook because it does not disturb mouse @@ -607,7 +624,7 @@ static int psmouse_extensions(struct psmouse *psmouse, * can reset it properly after probing for intellimouse. */ if (max_proto > PSMOUSE_PS2 && synaptics_detect(psmouse, set_properties) == 0) { - synaptics_hardware = 1; + synaptics_hardware = true; if (max_proto > PSMOUSE_IMEX) { if (!set_properties || synaptics_init(psmouse) == 0) @@ -666,6 +683,20 @@ static int psmouse_extensions(struct psmouse *psmouse, max_proto = PSMOUSE_IMEX; } +/* + * Try Finger Sensing Pad + */ + if (max_proto > PSMOUSE_IMEX) { + if (fsp_detect(psmouse, set_properties) == 0) { + if (!set_properties || fsp_init(psmouse) == 0) + return PSMOUSE_FSP; +/* + * Init failed, try basic relative protocols + */ + max_proto = PSMOUSE_IMEX; + } + } + if (max_proto > PSMOUSE_IMEX) { if (genius_detect(psmouse, set_properties) == 0) return PSMOUSE_GENPS; @@ -718,7 +749,7 @@ static const struct psmouse_protocol psmouse_protocols[] = { .type = PSMOUSE_PS2, .name = "PS/2", .alias = "bare", - .maxproto = 1, + .maxproto = true, .detect = ps2bare_detect, }, #ifdef CONFIG_MOUSE_PS2_LOGIPS2PP @@ -745,14 +776,14 @@ static const struct psmouse_protocol psmouse_protocols[] = { .type = PSMOUSE_IMPS, .name = "ImPS/2", .alias = "imps", - .maxproto = 1, + .maxproto = true, .detect = intellimouse_detect, }, { .type = PSMOUSE_IMEX, .name = "ImExPS/2", .alias = "exps", - .maxproto = 1, + .maxproto = true, .detect = im_explorer_detect, }, #ifdef CONFIG_MOUSE_PS2_SYNAPTICS @@ -813,7 +844,16 @@ static const struct psmouse_protocol psmouse_protocols[] = { .detect = elantech_detect, .init = elantech_init, }, - #endif +#endif +#ifdef CONFIG_MOUSE_PS2_SENTELIC + { + .type = PSMOUSE_FSP, + .name = "FSPPS/2", + .alias = "fsp", + .detect = fsp_detect, + .init = fsp_init, + }, +#endif { .type = PSMOUSE_CORTRON, .name = "CortronPS/2", @@ -824,7 +864,7 @@ static const struct psmouse_protocol psmouse_protocols[] = { .type = PSMOUSE_AUTO, .name = "auto", .alias = "any", - .maxproto = 1, + .maxproto = true, }, }; @@ -990,7 +1030,7 @@ static void psmouse_resync(struct work_struct *work) container_of(work, struct psmouse, resync_work.work); struct serio *serio = psmouse->ps2dev.serio; psmouse_ret_t rc = PSMOUSE_GOOD_DATA; - int failed = 0, enabled = 0; + bool failed = false, enabled = false; int i; mutex_lock(&psmouse_mutex); @@ -1017,9 +1057,9 @@ static void psmouse_resync(struct work_struct *work) if (ps2_sendbyte(&psmouse->ps2dev, PSMOUSE_CMD_DISABLE, 20)) { if (psmouse->num_resyncs < 3 || psmouse->acks_disable_command) - failed = 1; + failed = true; } else - psmouse->acks_disable_command = 1; + psmouse->acks_disable_command = true; /* * Poll the mouse. If it was reset the packet will be shorter than @@ -1030,7 +1070,7 @@ static void psmouse_resync(struct work_struct *work) */ if (!failed) { if (psmouse->poll(psmouse)) - failed = 1; + failed = true; else { psmouse_set_state(psmouse, PSMOUSE_CMD_MODE); for (i = 0; i < psmouse->pktsize; i++) { @@ -1040,7 +1080,7 @@ static void psmouse_resync(struct work_struct *work) break; } if (rc != PSMOUSE_FULL_PACKET) - failed = 1; + failed = true; psmouse_set_state(psmouse, PSMOUSE_RESYNCING); } } @@ -1051,7 +1091,7 @@ static void psmouse_resync(struct work_struct *work) */ for (i = 0; i < 5; i++) { if (!ps2_command(&psmouse->ps2dev, NULL, PSMOUSE_CMD_ENABLE)) { - enabled = 1; + enabled = true; break; } msleep(200); @@ -1060,7 +1100,7 @@ static void psmouse_resync(struct work_struct *work) if (!enabled) { printk(KERN_WARNING "psmouse.c: failed to re-enable mouse on %s\n", psmouse->ps2dev.serio->phys); - failed = 1; + failed = true; } if (failed) { @@ -1160,15 +1200,16 @@ static void psmouse_disconnect(struct serio *serio) mutex_unlock(&psmouse_mutex); } -static int psmouse_switch_protocol(struct psmouse *psmouse, const struct psmouse_protocol *proto) +static int psmouse_switch_protocol(struct psmouse *psmouse, + const struct psmouse_protocol *proto) { struct input_dev *input_dev = psmouse->dev; input_dev->dev.parent = &psmouse->ps2dev.serio->dev; input_dev->evbit[0] = BIT_MASK(EV_KEY) | BIT_MASK(EV_REL); - input_dev->keybit[BIT_WORD(BTN_MOUSE)] = BIT_MASK(BTN_LEFT) | - BIT_MASK(BTN_MIDDLE) | BIT_MASK(BTN_RIGHT); + input_dev->keybit[BIT_WORD(BTN_MOUSE)] = + BIT_MASK(BTN_LEFT) | BIT_MASK(BTN_RIGHT); input_dev->relbit[0] = BIT_MASK(REL_X) | BIT_MASK(REL_Y); psmouse->set_rate = psmouse_set_rate; @@ -1185,9 +1226,9 @@ static int psmouse_switch_protocol(struct psmouse *psmouse, const struct psmouse return -1; psmouse->type = proto->type; - } - else - psmouse->type = psmouse_extensions(psmouse, psmouse_max_proto, 1); + } else + psmouse->type = psmouse_extensions(psmouse, + psmouse_max_proto, true); /* * If mouse's packet size is 3 there is no point in polling the @@ -1342,8 +1383,10 @@ static int psmouse_reconnect(struct serio *serio) if (psmouse->reconnect(psmouse)) goto out; } else if (psmouse_probe(psmouse) < 0 || - psmouse->type != psmouse_extensions(psmouse, psmouse_max_proto, 0)) + psmouse->type != psmouse_extensions(psmouse, + psmouse_max_proto, false)) { goto out; + } /* ok, the device type (and capabilities) match the old one, * we can continue using it, complete intialization @@ -1528,7 +1571,9 @@ static ssize_t psmouse_attr_set_protocol(struct psmouse *psmouse, void *data, co while (serio->child) { if (++retry > 3) { - printk(KERN_WARNING "psmouse: failed to destroy child port, protocol change aborted.\n"); + printk(KERN_WARNING + "psmouse: failed to destroy child port, " + "protocol change aborted.\n"); input_free_device(new_dev); return -EIO; } @@ -1644,13 +1689,16 @@ static int psmouse_get_maxproto(char *buffer, struct kernel_param *kp) { int type = *((unsigned int *)kp->arg); - return sprintf(buffer, "%s\n", psmouse_protocol_by_type(type)->name); + return sprintf(buffer, "%s", psmouse_protocol_by_type(type)->name); } static int __init psmouse_init(void) { int err; + lifebook_module_init(); + synaptics_module_init(); + kpsmoused_wq = create_singlethread_workqueue("kpsmoused"); if (!kpsmoused_wq) { printk(KERN_ERR "psmouse: failed to create kpsmoused workqueue\n"); diff --git a/drivers/input/mouse/psmouse.h b/drivers/input/mouse/psmouse.h index 54ed267894b..e053bdd137f 100644 --- a/drivers/input/mouse/psmouse.h +++ b/drivers/input/mouse/psmouse.h @@ -47,10 +47,10 @@ struct psmouse { unsigned char pktcnt; unsigned char pktsize; unsigned char type; - unsigned char acks_disable_command; + bool acks_disable_command; unsigned int model; unsigned long last; - unsigned long out_of_sync; + unsigned long out_of_sync_cnt; unsigned long num_resyncs; enum psmouse_state state; char devname[64]; @@ -60,7 +60,7 @@ struct psmouse { unsigned int resolution; unsigned int resetafter; unsigned int resync_time; - unsigned int smartscroll; /* Logitech only */ + bool smartscroll; /* Logitech only */ psmouse_ret_t (*protocol_handler)(struct psmouse *psmouse); void (*set_rate)(struct psmouse *psmouse, unsigned int rate); @@ -91,6 +91,7 @@ enum psmouse_type { PSMOUSE_CORTRON, PSMOUSE_HGPK, PSMOUSE_ELANTECH, + PSMOUSE_FSP, PSMOUSE_AUTO /* This one should always be last */ }; @@ -107,7 +108,7 @@ struct psmouse_attribute { ssize_t (*show)(struct psmouse *psmouse, void *data, char *buf); ssize_t (*set)(struct psmouse *psmouse, void *data, const char *buf, size_t count); - int protect; + bool protect; }; #define to_psmouse_attr(a) container_of((a), struct psmouse_attribute, dattr) @@ -116,9 +117,7 @@ ssize_t psmouse_attr_show_helper(struct device *dev, struct device_attribute *at ssize_t psmouse_attr_set_helper(struct device *dev, struct device_attribute *attr, const char *buf, size_t count); -#define __PSMOUSE_DEFINE_ATTR(_name, _mode, _data, _show, _set, _protect) \ -static ssize_t _show(struct psmouse *, void *data, char *); \ -static ssize_t _set(struct psmouse *, void *data, const char *, size_t); \ +#define __PSMOUSE_DEFINE_ATTR_VAR(_name, _mode, _data, _show, _set, _protect) \ static struct psmouse_attribute psmouse_attr_##_name = { \ .dattr = { \ .attr = { \ @@ -134,7 +133,20 @@ static struct psmouse_attribute psmouse_attr_##_name = { \ .protect = _protect, \ } -#define PSMOUSE_DEFINE_ATTR(_name, _mode, _data, _show, _set) \ - __PSMOUSE_DEFINE_ATTR(_name, _mode, _data, _show, _set, 1) +#define __PSMOUSE_DEFINE_ATTR(_name, _mode, _data, _show, _set, _protect) \ + static ssize_t _show(struct psmouse *, void *, char *); \ + static ssize_t _set(struct psmouse *, void *, const char *, size_t); \ + __PSMOUSE_DEFINE_ATTR_VAR(_name, _mode, _data, _show, _set, _protect) + +#define PSMOUSE_DEFINE_ATTR(_name, _mode, _data, _show, _set) \ + __PSMOUSE_DEFINE_ATTR(_name, _mode, _data, _show, _set, true) + +#define PSMOUSE_DEFINE_RO_ATTR(_name, _mode, _data, _show) \ + static ssize_t _show(struct psmouse *, void *, char *); \ + __PSMOUSE_DEFINE_ATTR_VAR(_name, _mode, _data, _show, NULL, true) + +#define PSMOUSE_DEFINE_WO_ATTR(_name, _mode, _data, _set) \ + static ssize_t _set(struct psmouse *, void *, const char *, size_t); \ + __PSMOUSE_DEFINE_ATTR_VAR(_name, _mode, _data, NULL, _set, true) #endif /* _PSMOUSE_H */ diff --git a/drivers/input/mouse/sentelic.c b/drivers/input/mouse/sentelic.c new file mode 100644 index 00000000000..77b9fd0b3fb --- /dev/null +++ b/drivers/input/mouse/sentelic.c @@ -0,0 +1,870 @@ +/*- + * Finger Sensing Pad PS/2 mouse driver. + * + * Copyright (C) 2005-2007 Asia Vital Components Co., Ltd. + * Copyright (C) 2005-2009 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 + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +#include <linux/module.h> +#include <linux/version.h> +#include <linux/input.h> +#include <linux/ctype.h> +#include <linux/libps2.h> +#include <linux/serio.h> +#include <linux/jiffies.h> + +#include "psmouse.h" +#include "sentelic.h" + +/* + * Timeout for FSP PS/2 command only (in milliseconds). + */ +#define FSP_CMD_TIMEOUT 200 +#define FSP_CMD_TIMEOUT2 30 + +/** Driver version. */ +static const char fsp_drv_ver[] = "1.0.0-K"; + +/* + * Make sure that the value being sent to FSP will not conflict with + * possible sample rate values. + */ +static unsigned char fsp_test_swap_cmd(unsigned char reg_val) +{ + switch (reg_val) { + case 10: case 20: case 40: case 60: case 80: case 100: case 200: + /* + * The requested value being sent to FSP matched to possible + * sample rates, swap the given value such that the hardware + * wouldn't get confused. + */ + return (reg_val >> 4) | (reg_val << 4); + default: + return reg_val; /* swap isn't necessary */ + } +} + +/* + * Make sure that the value being sent to FSP will not conflict with certain + * commands. + */ +static unsigned char fsp_test_invert_cmd(unsigned char reg_val) +{ + switch (reg_val) { + case 0xe9: case 0xee: case 0xf2: case 0xff: + /* + * The requested value being sent to FSP matched to certain + * commands, inverse the given value such that the hardware + * wouldn't get confused. + */ + return ~reg_val; + default: + return reg_val; /* inversion isn't necessary */ + } +} + +static int fsp_reg_read(struct psmouse *psmouse, int reg_addr, int *reg_val) +{ + struct ps2dev *ps2dev = &psmouse->ps2dev; + unsigned char param[3]; + unsigned char addr; + int rc = -1; + + /* + * We need to shut off the device and switch it into command + * mode so we don't confuse our protocol handler. We don't need + * 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); + + ps2_begin_command(ps2dev); + + if (ps2_sendbyte(ps2dev, 0xf3, FSP_CMD_TIMEOUT) < 0) + goto out; + + /* should return 0xfe(request for resending) */ + ps2_sendbyte(ps2dev, 0x66, FSP_CMD_TIMEOUT2); + /* should return 0xfc(failed) */ + ps2_sendbyte(ps2dev, 0x88, FSP_CMD_TIMEOUT2); + + if (ps2_sendbyte(ps2dev, 0xf3, FSP_CMD_TIMEOUT) < 0) + goto out; + + if ((addr = fsp_test_invert_cmd(reg_addr)) != reg_addr) { + ps2_sendbyte(ps2dev, 0x68, FSP_CMD_TIMEOUT2); + } else if ((addr = fsp_test_swap_cmd(reg_addr)) != reg_addr) { + /* swapping is required */ + ps2_sendbyte(ps2dev, 0xcc, FSP_CMD_TIMEOUT2); + /* expect 0xfe */ + } else { + /* swapping isn't necessary */ + ps2_sendbyte(ps2dev, 0x66, FSP_CMD_TIMEOUT2); + /* expect 0xfe */ + } + /* should return 0xfc(failed) */ + ps2_sendbyte(ps2dev, addr, FSP_CMD_TIMEOUT); + + if (__ps2_command(ps2dev, param, PSMOUSE_CMD_GETINFO) < 0) + goto out; + + *reg_val = param[2]; + rc = 0; + + 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); + return rc; +} + +static int fsp_reg_write(struct psmouse *psmouse, int reg_addr, int reg_val) +{ + struct ps2dev *ps2dev = &psmouse->ps2dev; + unsigned char v; + int rc = -1; + + ps2_begin_command(ps2dev); + + if (ps2_sendbyte(ps2dev, 0xf3, FSP_CMD_TIMEOUT) < 0) + goto out; + + if ((v = fsp_test_invert_cmd(reg_addr)) != reg_addr) { + /* inversion is required */ + ps2_sendbyte(ps2dev, 0x74, FSP_CMD_TIMEOUT2); + } else { + if ((v = fsp_test_swap_cmd(reg_addr)) != reg_addr) { + /* swapping is required */ + ps2_sendbyte(ps2dev, 0x77, FSP_CMD_TIMEOUT2); + } else { + /* swapping isn't necessary */ + ps2_sendbyte(ps2dev, 0x55, FSP_CMD_TIMEOUT2); + } + } + /* write the register address in correct order */ + ps2_sendbyte(ps2dev, v, FSP_CMD_TIMEOUT2); + + if (ps2_sendbyte(ps2dev, 0xf3, FSP_CMD_TIMEOUT) < 0) + return -1; + + if ((v = fsp_test_invert_cmd(reg_val)) != reg_val) { + /* inversion is required */ + ps2_sendbyte(ps2dev, 0x47, FSP_CMD_TIMEOUT2); + } else if ((v = fsp_test_swap_cmd(reg_val)) != reg_val) { + /* swapping is required */ + ps2_sendbyte(ps2dev, 0x44, FSP_CMD_TIMEOUT2); + } else { + /* swapping isn't necessary */ + ps2_sendbyte(ps2dev, 0x33, FSP_CMD_TIMEOUT2); + } + + /* write the register value in correct order */ + ps2_sendbyte(ps2dev, v, FSP_CMD_TIMEOUT2); + rc = 0; + + 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); + return rc; +} + +/* Enable register clock gating for writing certain registers */ +static int fsp_reg_write_enable(struct psmouse *psmouse, bool enable) +{ + int v, nv; + + if (fsp_reg_read(psmouse, FSP_REG_SYSCTL1, &v) == -1) + return -1; + + if (enable) + nv = v | FSP_BIT_EN_REG_CLK; + else + nv = v & ~FSP_BIT_EN_REG_CLK; + + /* only write if necessary */ + if (nv != v) + if (fsp_reg_write(psmouse, FSP_REG_SYSCTL1, nv) == -1) + return -1; + + return 0; +} + +static int fsp_page_reg_read(struct psmouse *psmouse, int *reg_val) +{ + struct ps2dev *ps2dev = &psmouse->ps2dev; + unsigned char param[3]; + int rc = -1; + + ps2_command(ps2dev, NULL, PSMOUSE_CMD_DISABLE); + psmouse_set_state(psmouse, PSMOUSE_CMD_MODE); + + ps2_begin_command(ps2dev); + + if (ps2_sendbyte(ps2dev, 0xf3, FSP_CMD_TIMEOUT) < 0) + goto out; + + ps2_sendbyte(ps2dev, 0x66, FSP_CMD_TIMEOUT2); + ps2_sendbyte(ps2dev, 0x88, FSP_CMD_TIMEOUT2); + + if (ps2_sendbyte(ps2dev, 0xf3, FSP_CMD_TIMEOUT) < 0) + goto out; + + ps2_sendbyte(ps2dev, 0x83, FSP_CMD_TIMEOUT2); + ps2_sendbyte(ps2dev, 0x88, FSP_CMD_TIMEOUT2); + + /* get the returned result */ + if (__ps2_command(ps2dev, param, PSMOUSE_CMD_GETINFO)) + goto out; + + *reg_val = param[2]; + rc = 0; + + 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); + return rc; +} + +static int fsp_page_reg_write(struct psmouse *psmouse, int reg_val) +{ + struct ps2dev *ps2dev = &psmouse->ps2dev; + unsigned char v; + int rc = -1; + + ps2_begin_command(ps2dev); + + if (ps2_sendbyte(ps2dev, 0xf3, FSP_CMD_TIMEOUT) < 0) + goto out; + + ps2_sendbyte(ps2dev, 0x38, FSP_CMD_TIMEOUT2); + ps2_sendbyte(ps2dev, 0x88, FSP_CMD_TIMEOUT2); + + if (ps2_sendbyte(ps2dev, 0xf3, FSP_CMD_TIMEOUT) < 0) + return -1; + + if ((v = fsp_test_invert_cmd(reg_val)) != reg_val) { + ps2_sendbyte(ps2dev, 0x47, FSP_CMD_TIMEOUT2); + } else if ((v = fsp_test_swap_cmd(reg_val)) != reg_val) { + /* swapping is required */ + ps2_sendbyte(ps2dev, 0x44, FSP_CMD_TIMEOUT2); + } else { + /* swapping isn't necessary */ + ps2_sendbyte(ps2dev, 0x33, FSP_CMD_TIMEOUT2); + } + + ps2_sendbyte(ps2dev, v, FSP_CMD_TIMEOUT2); + rc = 0; + + out: + ps2_end_command(ps2dev); + dev_dbg(&ps2dev->serio->dev, "WRITE PAGE REG: to 0x%02x (rc = %d)\n", + reg_val, rc); + return rc; +} + +static int fsp_get_version(struct psmouse *psmouse, int *version) +{ + if (fsp_reg_read(psmouse, FSP_REG_VERSION, version)) + return -EIO; + + return 0; +} + +static int fsp_get_revision(struct psmouse *psmouse, int *rev) +{ + if (fsp_reg_read(psmouse, FSP_REG_REVISION, rev)) + return -EIO; + + return 0; +} + +static int fsp_get_buttons(struct psmouse *psmouse, int *btn) +{ + static const int buttons[] = { + 0x16, /* Left/Middle/Right/Forward/Backward & Scroll Up/Down */ + 0x06, /* Left/Middle/Right & Scroll Up/Down/Right/Left */ + 0x04, /* Left/Middle/Right & Scroll Up/Down */ + 0x02, /* Left/Middle/Right */ + }; + int val; + + if (fsp_reg_read(psmouse, FSP_REG_TMOD_STATUS1, &val) == -1) + return -EIO; + + *btn = buttons[(val & 0x30) >> 4]; + return 0; +} + +/* Enable on-pad command tag output */ +static int fsp_opc_tag_enable(struct psmouse *psmouse, bool enable) +{ + int v, nv; + 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"); + return -EIO; + } + + if (enable) + nv = v | FSP_BIT_EN_OPC_TAG; + else + nv = v & ~FSP_BIT_EN_OPC_TAG; + + /* only write if necessary */ + if (nv != v) { + fsp_reg_write_enable(psmouse, true); + res = fsp_reg_write(psmouse, FSP_REG_OPC_QDOWN, nv); + fsp_reg_write_enable(psmouse, false); + } + + if (res != 0) { + dev_err(&psmouse->ps2dev.serio->dev, + "Unable to enable OPC tag.\n"); + res = -EIO; + } + + return res; +} + +static int fsp_onpad_vscr(struct psmouse *psmouse, bool enable) +{ + struct fsp_data *pad = psmouse->private; + int val; + + if (fsp_reg_read(psmouse, FSP_REG_ONPAD_CTL, &val)) + return -EIO; + + pad->vscroll = enable; + + if (enable) + val |= (FSP_BIT_FIX_VSCR | FSP_BIT_ONPAD_ENABLE); + else + val &= ~FSP_BIT_FIX_VSCR; + + if (fsp_reg_write(psmouse, FSP_REG_ONPAD_CTL, val)) + return -EIO; + + return 0; +} + +static int fsp_onpad_hscr(struct psmouse *psmouse, bool enable) +{ + struct fsp_data *pad = psmouse->private; + int val, v2; + + if (fsp_reg_read(psmouse, FSP_REG_ONPAD_CTL, &val)) + return -EIO; + + if (fsp_reg_read(psmouse, FSP_REG_SYSCTL5, &v2)) + return -EIO; + + pad->hscroll = enable; + + if (enable) { + val |= (FSP_BIT_FIX_HSCR | FSP_BIT_ONPAD_ENABLE); + v2 |= FSP_BIT_EN_MSID6; + } else { + val &= ~FSP_BIT_FIX_HSCR; + v2 &= ~(FSP_BIT_EN_MSID6 | FSP_BIT_EN_MSID7 | FSP_BIT_EN_MSID8); + } + + if (fsp_reg_write(psmouse, FSP_REG_ONPAD_CTL, val)) + return -EIO; + + /* reconfigure horizontal scrolling packet output */ + if (fsp_reg_write(psmouse, FSP_REG_SYSCTL5, v2)) + return -EIO; + + return 0; +} + +/* + * Write device specific initial parameters. + * + * ex: 0xab 0xcd - write oxcd into register 0xab + */ +static ssize_t fsp_attr_set_setreg(struct psmouse *psmouse, void *data, + const char *buf, size_t count) +{ + unsigned long reg, val; + char *rest; + ssize_t retval; + + reg = simple_strtoul(buf, &rest, 16); + if (rest == buf || *rest != ' ' || reg > 0xff) + return -EINVAL; + + if (strict_strtoul(rest + 1, 16, &val) || val > 0xff) + return -EINVAL; + + if (fsp_reg_write_enable(psmouse, true)) + return -EIO; + + retval = fsp_reg_write(psmouse, reg, val) < 0 ? -EIO : count; + + fsp_reg_write_enable(psmouse, false); + + return count; +} + +PSMOUSE_DEFINE_WO_ATTR(setreg, S_IWUSR, NULL, fsp_attr_set_setreg); + +static ssize_t fsp_attr_show_getreg(struct psmouse *psmouse, + void *data, char *buf) +{ + struct fsp_data *pad = psmouse->private; + + return sprintf(buf, "%02x%02x\n", pad->last_reg, pad->last_val); +} + +/* + * Read a register from device. + * + * ex: 0xab -- read content from register 0xab + */ +static ssize_t fsp_attr_set_getreg(struct psmouse *psmouse, void *data, + const char *buf, size_t count) +{ + struct fsp_data *pad = psmouse->private; + unsigned long reg; + int val; + + if (strict_strtoul(buf, 16, ®) || reg > 0xff) + return -EINVAL; + + if (fsp_reg_read(psmouse, reg, &val)) + return -EIO; + + pad->last_reg = reg; + pad->last_val = val; + + return count; +} + +PSMOUSE_DEFINE_ATTR(getreg, S_IWUSR | S_IRUGO, NULL, + fsp_attr_show_getreg, fsp_attr_set_getreg); + +static ssize_t fsp_attr_show_pagereg(struct psmouse *psmouse, + void *data, char *buf) +{ + int val = 0; + + if (fsp_page_reg_read(psmouse, &val)) + return -EIO; + + return sprintf(buf, "%02x\n", val); +} + +static ssize_t fsp_attr_set_pagereg(struct psmouse *psmouse, void *data, + const char *buf, size_t count) +{ + unsigned long val; + + if (strict_strtoul(buf, 16, &val) || val > 0xff) + return -EINVAL; + + if (fsp_page_reg_write(psmouse, val)) + return -EIO; + + return count; +} + +PSMOUSE_DEFINE_ATTR(page, S_IWUSR | S_IRUGO, NULL, + fsp_attr_show_pagereg, fsp_attr_set_pagereg); + +static ssize_t fsp_attr_show_vscroll(struct psmouse *psmouse, + void *data, char *buf) +{ + struct fsp_data *pad = psmouse->private; + + return sprintf(buf, "%d\n", pad->vscroll); +} + +static ssize_t fsp_attr_set_vscroll(struct psmouse *psmouse, void *data, + const char *buf, size_t count) +{ + unsigned long val; + + if (strict_strtoul(buf, 10, &val) || val > 1) + return -EINVAL; + + fsp_onpad_vscr(psmouse, val); + + return count; +} + +PSMOUSE_DEFINE_ATTR(vscroll, S_IWUSR | S_IRUGO, NULL, + fsp_attr_show_vscroll, fsp_attr_set_vscroll); + +static ssize_t fsp_attr_show_hscroll(struct psmouse *psmouse, + void *data, char *buf) +{ + struct fsp_data *pad = psmouse->private; + + return sprintf(buf, "%d\n", pad->hscroll); +} + +static ssize_t fsp_attr_set_hscroll(struct psmouse *psmouse, void *data, + const char *buf, size_t count) +{ + unsigned long val; + + if (strict_strtoul(buf, 10, &val) || val > 1) + return -EINVAL; + + fsp_onpad_hscr(psmouse, val); + + return count; +} + +PSMOUSE_DEFINE_ATTR(hscroll, S_IWUSR | S_IRUGO, NULL, + fsp_attr_show_hscroll, fsp_attr_set_hscroll); + +static ssize_t fsp_attr_show_flags(struct psmouse *psmouse, + void *data, char *buf) +{ + struct fsp_data *pad = psmouse->private; + + return sprintf(buf, "%c\n", + pad->flags & FSPDRV_FLAG_EN_OPC ? 'C' : 'c'); +} + +static ssize_t fsp_attr_set_flags(struct psmouse *psmouse, void *data, + const char *buf, size_t count) +{ + struct fsp_data *pad = psmouse->private; + size_t i; + + for (i = 0; i < count; i++) { + switch (buf[i]) { + case 'C': + pad->flags |= FSPDRV_FLAG_EN_OPC; + break; + case 'c': + pad->flags &= ~FSPDRV_FLAG_EN_OPC; + break; + default: + return -EINVAL; + } + } + return count; +} + +PSMOUSE_DEFINE_ATTR(flags, S_IWUSR | S_IRUGO, NULL, + fsp_attr_show_flags, fsp_attr_set_flags); + +static ssize_t fsp_attr_show_ver(struct psmouse *psmouse, + void *data, char *buf) +{ + return sprintf(buf, "Sentelic FSP kernel module %s\n", fsp_drv_ver); +} + +PSMOUSE_DEFINE_RO_ATTR(ver, S_IRUGO, NULL, fsp_attr_show_ver); + +static struct attribute *fsp_attributes[] = { + &psmouse_attr_setreg.dattr.attr, + &psmouse_attr_getreg.dattr.attr, + &psmouse_attr_page.dattr.attr, + &psmouse_attr_vscroll.dattr.attr, + &psmouse_attr_hscroll.dattr.attr, + &psmouse_attr_flags.dattr.attr, + &psmouse_attr_ver.dattr.attr, + NULL +}; + +static struct attribute_group fsp_attribute_group = { + .attrs = fsp_attributes, +}; + +#ifdef FSP_DEBUG +static void fsp_packet_debug(unsigned char packet[]) +{ + static unsigned int ps2_packet_cnt; + static unsigned int ps2_last_second; + unsigned int jiffies_msec; + + ps2_packet_cnt++; + jiffies_msec = jiffies_to_msecs(jiffies); + printk(KERN_DEBUG "%08dms PS/2 packets: %02x, %02x, %02x, %02x\n", + jiffies_msec, packet[0], packet[1], packet[2], packet[3]); + + if (jiffies_msec - ps2_last_second > 1000) { + printk(KERN_DEBUG "PS/2 packets/sec = %d\n", ps2_packet_cnt); + ps2_packet_cnt = 0; + ps2_last_second = jiffies_msec; + } +} +#else +static void fsp_packet_debug(unsigned char packet[]) +{ +} +#endif + +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; + int rel_x, rel_y; + + if (psmouse->pktcnt < 4) + return PSMOUSE_GOOD_DATA; + + /* + * Full packet accumulated, process it + */ + + 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"); + 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); + /* fall through */ + + case FSP_PKT_TYPE_NORMAL: + /* normal packet */ + /* special packet data translation from on-pad packets */ + if (packet[3] != 0) { + if (packet[3] & BIT(0)) + button_status |= 0x01; /* wheel down */ + if (packet[3] & BIT(1)) + button_status |= 0x0f; /* wheel up */ + if (packet[3] & BIT(2)) + button_status |= BIT(5);/* horizontal left */ + if (packet[3] & BIT(3)) + button_status |= BIT(4);/* horizontal right */ + /* push back to packet queue */ + if (button_status != 0) + packet[3] = button_status; + rscroll = (packet[3] >> 4) & 1; + lscroll = (packet[3] >> 5) & 1; + } + /* + * Processing wheel up/down and extra button events + */ + input_report_rel(dev, REL_WHEEL, + (int)(packet[3] & 8) - (int)(packet[3] & 7)); + input_report_rel(dev, REL_HWHEEL, lscroll - rscroll); + input_report_key(dev, BTN_BACK, lscroll); + input_report_key(dev, BTN_FORWARD, rscroll); + + /* + * Standard PS/2 Mouse + */ + input_report_key(dev, BTN_LEFT, packet[0] & 1); + input_report_key(dev, BTN_MIDDLE, (packet[0] >> 2) & 1); + input_report_key(dev, BTN_RIGHT, (packet[0] >> 1) & 1); + + rel_x = packet[1] ? (int)packet[1] - (int)((packet[0] << 4) & 0x100) : 0; + rel_y = packet[2] ? (int)((packet[0] << 3) & 0x100) - (int)packet[2] : 0; + + input_report_rel(dev, REL_X, rel_x); + input_report_rel(dev, REL_Y, rel_y); + break; + } + + input_sync(dev); + + fsp_packet_debug(packet); + + return PSMOUSE_FULL_PACKET; +} + +static int fsp_activate_protocol(struct psmouse *psmouse) +{ + struct fsp_data *pad = psmouse->private; + struct ps2dev *ps2dev = &psmouse->ps2dev; + unsigned char param[2]; + int val; + + /* + * Standard procedure to enter FSP Intellimouse mode + * (scrolling wheel, 4th and 5th buttons) + */ + param[0] = 200; + ps2_command(ps2dev, param, PSMOUSE_CMD_SETRATE); + param[0] = 200; + ps2_command(ps2dev, param, PSMOUSE_CMD_SETRATE); + param[0] = 80; + ps2_command(ps2dev, param, PSMOUSE_CMD_SETRATE); + + 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"); + 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; + } + + 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)) { + dev_err(&psmouse->ps2dev.serio->dev, + "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)) + dev_warn(&psmouse->ps2dev.serio->dev, + "Failed to enable OPC tag mode.\n"); + + /* Enable on-pad vertical and horizontal scrolling */ + fsp_onpad_vscr(psmouse, true); + fsp_onpad_hscr(psmouse, true); + + return 0; +} + +int fsp_detect(struct psmouse *psmouse, bool set_properties) +{ + int id; + + if (fsp_reg_read(psmouse, FSP_REG_DEVICE_ID, &id)) + return -EIO; + + if (id != 0x01) + return -ENODEV; + + if (set_properties) { + psmouse->vendor = "Sentelic"; + psmouse->name = "FingerSensingPad"; + } + + return 0; +} + +static void fsp_reset(struct psmouse *psmouse) +{ + fsp_opc_tag_enable(psmouse, false); + fsp_onpad_vscr(psmouse, false); + fsp_onpad_hscr(psmouse, false); +} + +static void fsp_disconnect(struct psmouse *psmouse) +{ + sysfs_remove_group(&psmouse->ps2dev.serio->dev.kobj, + &fsp_attribute_group); + + fsp_reset(psmouse); + kfree(psmouse->private); +} + +static int fsp_reconnect(struct psmouse *psmouse) +{ + int version; + + if (fsp_detect(psmouse, 0)) + return -ENODEV; + + if (fsp_get_version(psmouse, &version)) + return -ENODEV; + + if (fsp_activate_protocol(psmouse)) + return -EIO; + + return 0; +} + +int fsp_init(struct psmouse *psmouse) +{ + struct fsp_data *priv; + int ver, rev, buttons; + int error; + + if (fsp_get_version(psmouse, &ver) || + fsp_get_revision(psmouse, &rev) || + fsp_get_buttons(psmouse, &buttons)) { + return -ENODEV; + } + + printk(KERN_INFO + "Finger Sensing Pad, hw: %d.%d.%d, sw: %s, buttons: %d\n", + ver >> 4, ver & 0x0F, rev, fsp_drv_ver, buttons & 7); + + psmouse->private = priv = kzalloc(sizeof(struct fsp_data), GFP_KERNEL); + if (!priv) + return -ENOMEM; + + 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; + psmouse->reconnect = fsp_reconnect; + 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; + + 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); + goto err_out; + } + + return 0; + + err_out: + kfree(psmouse->private); + psmouse->private = NULL; + return error; +} diff --git a/drivers/input/mouse/sentelic.h b/drivers/input/mouse/sentelic.h new file mode 100644 index 00000000000..ed1395ac7b8 --- /dev/null +++ b/drivers/input/mouse/sentelic.h @@ -0,0 +1,98 @@ +/*- + * Finger Sensing Pad PS/2 mouse driver. + * + * Copyright (C) 2005-2007 Asia Vital Components Co., Ltd. + * Copyright (C) 2005-2009 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 + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +#ifndef __SENTELIC_H +#define __SENTELIC_H + +/* Finger-sensing Pad information registers */ +#define FSP_REG_DEVICE_ID 0x00 +#define FSP_REG_VERSION 0x01 +#define FSP_REG_REVISION 0x04 +#define FSP_REG_TMOD_STATUS1 0x0B +#define FSP_BIT_NO_ROTATION BIT(3) +#define FSP_REG_PAGE_CTRL 0x0F + +/* Finger-sensing Pad control registers */ +#define FSP_REG_SYSCTL1 0x10 +#define FSP_BIT_EN_REG_CLK BIT(5) +#define FSP_REG_OPC_QDOWN 0x31 +#define FSP_BIT_EN_OPC_TAG BIT(7) +#define FSP_REG_OPTZ_XLO 0x34 +#define FSP_REG_OPTZ_XHI 0x35 +#define FSP_REG_OPTZ_YLO 0x36 +#define FSP_REG_OPTZ_YHI 0x37 +#define FSP_REG_SYSCTL5 0x40 +#define FSP_BIT_90_DEGREE BIT(0) +#define FSP_BIT_EN_MSID6 BIT(1) +#define FSP_BIT_EN_MSID7 BIT(2) +#define FSP_BIT_EN_MSID8 BIT(3) +#define FSP_BIT_EN_AUTO_MSID8 BIT(5) +#define FSP_BIT_EN_PKT_G0 BIT(6) + +#define FSP_REG_ONPAD_CTL 0x43 +#define FSP_BIT_ONPAD_ENABLE BIT(0) +#define FSP_BIT_ONPAD_FBBB BIT(1) +#define FSP_BIT_FIX_VSCR BIT(3) +#define FSP_BIT_FIX_HSCR BIT(5) +#define FSP_BIT_DRAG_LOCK BIT(6) + +/* Finger-sensing Pad packet formating related definitions */ + +/* absolute packet type */ +#define FSP_PKT_TYPE_NORMAL (0x00) +#define FSP_PKT_TYPE_ABS (0x01) +#define FSP_PKT_TYPE_NOTIFY (0x02) +#define FSP_PKT_TYPE_NORMAL_OPC (0x03) +#define FSP_PKT_TYPE_SHIFT (6) + +#ifdef __KERNEL__ + +struct fsp_data { + unsigned char ver; /* hardware version */ + unsigned char rev; /* hardware revison */ + unsigned char buttons; /* Number of buttons */ + unsigned int flags; +#define FSPDRV_FLAG_EN_OPC (0x001) /* enable on-pad clicking */ + + bool vscroll; /* Vertical scroll zone enabled */ + bool hscroll; /* Horizontal scroll zone enabled */ + + unsigned char last_reg; /* Last register we requested read from */ + unsigned char last_val; +}; + +#ifdef CONFIG_MOUSE_PS2_SENTELIC +extern int fsp_detect(struct psmouse *psmouse, bool set_properties); +extern int fsp_init(struct psmouse *psmouse); +#else +inline int fsp_detect(struct psmouse *psmouse, bool set_properties) +{ + return -ENOSYS; +} +inline int fsp_init(struct psmouse *psmouse) +{ + return -ENOSYS; +} +#endif + +#endif /* __KERNEL__ */ + +#endif /* !__SENTELIC_H */ diff --git a/drivers/input/mouse/synaptics.c b/drivers/input/mouse/synaptics.c index 19984bf06ca..05689e73219 100644 --- a/drivers/input/mouse/synaptics.c +++ b/drivers/input/mouse/synaptics.c @@ -24,6 +24,7 @@ */ #include <linux/module.h> +#include <linux/dmi.h> #include <linux/input.h> #include <linux/serio.h> #include <linux/libps2.h> @@ -60,7 +61,7 @@ static int synaptics_mode_cmd(struct psmouse *psmouse, unsigned char mode) return 0; } -int synaptics_detect(struct psmouse *psmouse, int set_properties) +int synaptics_detect(struct psmouse *psmouse, bool set_properties) { struct ps2dev *ps2dev = &psmouse->ps2dev; unsigned char param[4]; @@ -556,38 +557,38 @@ static void set_input_params(struct input_dev *dev, struct synaptics_data *priv) { int i; - set_bit(EV_ABS, dev->evbit); + __set_bit(EV_ABS, dev->evbit); input_set_abs_params(dev, ABS_X, XMIN_NOMINAL, XMAX_NOMINAL, 0, 0); input_set_abs_params(dev, ABS_Y, YMIN_NOMINAL, YMAX_NOMINAL, 0, 0); input_set_abs_params(dev, ABS_PRESSURE, 0, 255, 0, 0); - set_bit(ABS_TOOL_WIDTH, dev->absbit); + __set_bit(ABS_TOOL_WIDTH, dev->absbit); - set_bit(EV_KEY, dev->evbit); - set_bit(BTN_TOUCH, dev->keybit); - set_bit(BTN_TOOL_FINGER, dev->keybit); - set_bit(BTN_LEFT, dev->keybit); - set_bit(BTN_RIGHT, dev->keybit); + __set_bit(EV_KEY, dev->evbit); + __set_bit(BTN_TOUCH, dev->keybit); + __set_bit(BTN_TOOL_FINGER, dev->keybit); + __set_bit(BTN_LEFT, dev->keybit); + __set_bit(BTN_RIGHT, dev->keybit); if (SYN_CAP_MULTIFINGER(priv->capabilities)) { - set_bit(BTN_TOOL_DOUBLETAP, dev->keybit); - set_bit(BTN_TOOL_TRIPLETAP, dev->keybit); + __set_bit(BTN_TOOL_DOUBLETAP, dev->keybit); + __set_bit(BTN_TOOL_TRIPLETAP, dev->keybit); } if (SYN_CAP_MIDDLE_BUTTON(priv->capabilities)) - set_bit(BTN_MIDDLE, dev->keybit); + __set_bit(BTN_MIDDLE, dev->keybit); if (SYN_CAP_FOUR_BUTTON(priv->capabilities) || SYN_CAP_MIDDLE_BUTTON(priv->capabilities)) { - set_bit(BTN_FORWARD, dev->keybit); - set_bit(BTN_BACK, dev->keybit); + __set_bit(BTN_FORWARD, dev->keybit); + __set_bit(BTN_BACK, dev->keybit); } for (i = 0; i < SYN_CAP_MULTI_BUTTON_NO(priv->ext_cap); i++) - set_bit(BTN_0 + i, dev->keybit); + __set_bit(BTN_0 + i, dev->keybit); - clear_bit(EV_REL, dev->evbit); - clear_bit(REL_X, dev->relbit); - clear_bit(REL_Y, dev->relbit); + __clear_bit(EV_REL, dev->evbit); + __clear_bit(REL_X, dev->relbit); + __clear_bit(REL_Y, dev->relbit); dev->absres[ABS_X] = priv->x_res; dev->absres[ABS_Y] = priv->y_res; @@ -629,33 +630,49 @@ static int synaptics_reconnect(struct psmouse *psmouse) return 0; } -#if defined(__i386__) -#include <linux/dmi.h> -static const struct dmi_system_id toshiba_dmi_table[] = { +static bool impaired_toshiba_kbc; + +static const struct dmi_system_id __initconst toshiba_dmi_table[] = { +#if defined(CONFIG_DMI) && defined(CONFIG_X86) { - .ident = "Toshiba Satellite", + /* Toshiba Satellite */ .matches = { DMI_MATCH(DMI_SYS_VENDOR, "TOSHIBA"), DMI_MATCH(DMI_PRODUCT_NAME, "Satellite"), }, }, { - .ident = "Toshiba Dynabook", + /* Toshiba Dynabook */ .matches = { DMI_MATCH(DMI_SYS_VENDOR, "TOSHIBA"), DMI_MATCH(DMI_PRODUCT_NAME, "dynabook"), }, }, { - .ident = "Toshiba Portege M300", + /* Toshiba Portege M300 */ .matches = { DMI_MATCH(DMI_SYS_VENDOR, "TOSHIBA"), DMI_MATCH(DMI_PRODUCT_NAME, "PORTEGE M300"), }, + + }, + { + /* Toshiba Portege M300 */ + .matches = { + DMI_MATCH(DMI_SYS_VENDOR, "TOSHIBA"), + DMI_MATCH(DMI_PRODUCT_NAME, "Portable PC"), + DMI_MATCH(DMI_PRODUCT_VERSION, "Version 1.0"), + }, + }, { } -}; #endif +}; + +void __init synaptics_module_init(void) +{ + impaired_toshiba_kbc = dmi_check_system(toshiba_dmi_table); +} int synaptics_init(struct psmouse *psmouse) { @@ -708,18 +725,16 @@ int synaptics_init(struct psmouse *psmouse) if (SYN_CAP_PASS_THROUGH(priv->capabilities)) synaptics_pt_create(psmouse); -#if defined(__i386__) /* * Toshiba's KBC seems to have trouble handling data from * Synaptics as full rate, switch to lower rate which is roughly * thye same as rate of standard PS/2 mouse. */ - if (psmouse->rate >= 80 && dmi_check_system(toshiba_dmi_table)) { + if (psmouse->rate >= 80 && impaired_toshiba_kbc) { printk(KERN_INFO "synaptics: Toshiba %s detected, limiting rate to 40pps.\n", dmi_get_system_info(DMI_PRODUCT_NAME)); psmouse->rate = 40; } -#endif return 0; @@ -730,6 +745,10 @@ int synaptics_init(struct psmouse *psmouse) #else /* CONFIG_MOUSE_PS2_SYNAPTICS */ +void __init synaptics_module_init(void) +{ +} + int synaptics_init(struct psmouse *psmouse) { return -ENOSYS; diff --git a/drivers/input/mouse/synaptics.h b/drivers/input/mouse/synaptics.h index 30238215175..838e7f2c9b3 100644 --- a/drivers/input/mouse/synaptics.h +++ b/drivers/input/mouse/synaptics.h @@ -105,7 +105,8 @@ struct synaptics_data { int scroll; }; -int synaptics_detect(struct psmouse *psmouse, int set_properties); +void synaptics_module_init(void); +int synaptics_detect(struct psmouse *psmouse, bool set_properties); int synaptics_init(struct psmouse *psmouse); void synaptics_reset(struct psmouse *psmouse); diff --git a/drivers/input/mouse/synaptics_i2c.c b/drivers/input/mouse/synaptics_i2c.c index eac9fdde7ee..9867dfe2a63 100644 --- a/drivers/input/mouse/synaptics_i2c.c +++ b/drivers/input/mouse/synaptics_i2c.c @@ -203,7 +203,7 @@ MODULE_PARM_DESC(no_filter, "No Filter. Default = 0 (off)"); * and the irq configuration should be set to Falling Edge Trigger */ /* Control IRQ / Polling option */ -static int polling_req; +static bool polling_req; module_param(polling_req, bool, 0444); MODULE_PARM_DESC(polling_req, "Request Polling. Default = 0 (use irq)"); @@ -217,6 +217,7 @@ struct synaptics_i2c { struct i2c_client *client; struct input_dev *input; struct delayed_work dwork; + spinlock_t lock; int no_data_count; int no_decel_param; int reduce_report_param; @@ -366,17 +367,28 @@ static bool synaptics_i2c_get_input(struct synaptics_i2c *touch) return xy_delta || gesture; } -static irqreturn_t synaptics_i2c_irq(int irq, void *dev_id) +static void synaptics_i2c_reschedule_work(struct synaptics_i2c *touch, + unsigned long delay) { - struct synaptics_i2c *touch = dev_id; + unsigned long flags; + + spin_lock_irqsave(&touch->lock, flags); /* - * We want to have the work run immediately but it might have - * already been scheduled with a delay, that's why we have to - * cancel it first. + * If work is already scheduled then subsequent schedules will not + * change the scheduled time that's why we have to cancel it first. */ - cancel_delayed_work(&touch->dwork); - schedule_delayed_work(&touch->dwork, 0); + __cancel_delayed_work(&touch->dwork); + schedule_delayed_work(&touch->dwork, delay); + + spin_unlock_irqrestore(&touch->lock, flags); +} + +static irqreturn_t synaptics_i2c_irq(int irq, void *dev_id) +{ + struct synaptics_i2c *touch = dev_id; + + synaptics_i2c_reschedule_work(touch, 0); return IRQ_HANDLED; } @@ -408,8 +420,8 @@ static void synaptics_i2c_check_params(struct synaptics_i2c *touch) } /* Control the Device polling rate / Work Handler sleep time */ -unsigned long synaptics_i2c_adjust_delay(struct synaptics_i2c *touch, - bool have_data) +static unsigned long synaptics_i2c_adjust_delay(struct synaptics_i2c *touch, + bool have_data) { unsigned long delay, nodata_count_thres; @@ -452,7 +464,7 @@ static void synaptics_i2c_work_handler(struct work_struct *work) * We poll the device once in THREAD_IRQ_SLEEP_SECS and * if error is detected, we try to reset and reconfigure the touchpad. */ - schedule_delayed_work(&touch->dwork, delay); + synaptics_i2c_reschedule_work(touch, delay); } static int synaptics_i2c_open(struct input_dev *input) @@ -465,8 +477,8 @@ static int synaptics_i2c_open(struct input_dev *input) return ret; if (polling_req) - schedule_delayed_work(&touch->dwork, - msecs_to_jiffies(NO_DATA_SLEEP_MSECS)); + synaptics_i2c_reschedule_work(touch, + msecs_to_jiffies(NO_DATA_SLEEP_MSECS)); return 0; } @@ -508,7 +520,7 @@ static void synaptics_i2c_set_input_params(struct synaptics_i2c *touch) __set_bit(BTN_LEFT, input->keybit); } -struct synaptics_i2c *synaptics_i2c_touch_create(struct i2c_client *client) +static struct synaptics_i2c *synaptics_i2c_touch_create(struct i2c_client *client) { struct synaptics_i2c *touch; @@ -521,6 +533,7 @@ struct synaptics_i2c *synaptics_i2c_touch_create(struct i2c_client *client) touch->scan_rate_param = scan_rate; set_scan_rate(touch, scan_rate); INIT_DELAYED_WORK(&touch->dwork, synaptics_i2c_work_handler); + spin_lock_init(&touch->lock); return touch; } @@ -535,14 +548,12 @@ static int __devinit synaptics_i2c_probe(struct i2c_client *client, if (!touch) return -ENOMEM; - i2c_set_clientdata(client, touch); - ret = synaptics_i2c_reset_config(client); if (ret) goto err_mem_free; if (client->irq < 1) - polling_req = 1; + polling_req = true; touch->input = input_allocate_device(); if (!touch->input) { @@ -563,7 +574,7 @@ static int __devinit synaptics_i2c_probe(struct i2c_client *client, dev_warn(&touch->client->dev, "IRQ request failed: %d, " "falling back to polling\n", ret); - polling_req = 1; + polling_req = true; synaptics_i2c_reg_set(touch->client, INTERRUPT_EN_REG, 0); } @@ -580,12 +591,14 @@ static int __devinit synaptics_i2c_probe(struct i2c_client *client, "Input device register failed: %d\n", ret); goto err_input_free; } + + i2c_set_clientdata(client, touch); + return 0; err_input_free: input_free_device(touch->input); err_mem_free: - i2c_set_clientdata(client, NULL); kfree(touch); return ret; @@ -596,7 +609,7 @@ static int __devexit synaptics_i2c_remove(struct i2c_client *client) struct synaptics_i2c *touch = i2c_get_clientdata(client); if (!polling_req) - free_irq(touch->client->irq, touch); + free_irq(client->irq, touch); input_unregister_device(touch->input); i2c_set_clientdata(client, NULL); @@ -627,8 +640,8 @@ static int synaptics_i2c_resume(struct i2c_client *client) if (ret) return ret; - schedule_delayed_work(&touch->dwork, - msecs_to_jiffies(NO_DATA_SLEEP_MSECS)); + synaptics_i2c_reschedule_work(touch, + msecs_to_jiffies(NO_DATA_SLEEP_MSECS)); return 0; } diff --git a/drivers/input/mouse/touchkit_ps2.c b/drivers/input/mouse/touchkit_ps2.c index 3fadb2accac..909431c31ab 100644 --- a/drivers/input/mouse/touchkit_ps2.c +++ b/drivers/input/mouse/touchkit_ps2.c @@ -67,7 +67,7 @@ static psmouse_ret_t touchkit_ps2_process_byte(struct psmouse *psmouse) return PSMOUSE_FULL_PACKET; } -int touchkit_ps2_detect(struct psmouse *psmouse, int set_properties) +int touchkit_ps2_detect(struct psmouse *psmouse, bool set_properties) { struct input_dev *dev = psmouse->dev; unsigned char param[3]; @@ -86,7 +86,8 @@ int touchkit_ps2_detect(struct psmouse *psmouse, int set_properties) if (set_properties) { dev->evbit[0] = BIT_MASK(EV_KEY) | BIT_MASK(EV_ABS); - set_bit(BTN_TOUCH, dev->keybit); + dev->keybit[BIT_WORD(BTN_MOUSE)] = 0; + dev->keybit[BIT_WORD(BTN_TOUCH)] = BIT_MASK(BTN_TOUCH); input_set_abs_params(dev, ABS_X, 0, TOUCHKIT_MAX_XC, 0, 0); input_set_abs_params(dev, ABS_Y, 0, TOUCHKIT_MAX_YC, 0, 0); diff --git a/drivers/input/mouse/touchkit_ps2.h b/drivers/input/mouse/touchkit_ps2.h index 8a0dd3574ae..2efe9ea29d0 100644 --- a/drivers/input/mouse/touchkit_ps2.h +++ b/drivers/input/mouse/touchkit_ps2.h @@ -13,10 +13,10 @@ #define _TOUCHKIT_PS2_H #ifdef CONFIG_MOUSE_PS2_TOUCHKIT -int touchkit_ps2_detect(struct psmouse *psmouse, int set_properties); +int touchkit_ps2_detect(struct psmouse *psmouse, bool set_properties); #else static inline int touchkit_ps2_detect(struct psmouse *psmouse, - int set_properties) + bool set_properties) { return -ENOSYS; } diff --git a/drivers/input/mouse/trackpoint.c b/drivers/input/mouse/trackpoint.c index e68c814c436..63d4a67830f 100644 --- a/drivers/input/mouse/trackpoint.c +++ b/drivers/input/mouse/trackpoint.c @@ -282,9 +282,8 @@ static int trackpoint_reconnect(struct psmouse *psmouse) return 0; } -int trackpoint_detect(struct psmouse *psmouse, int set_properties) +int trackpoint_detect(struct psmouse *psmouse, bool set_properties) { - struct trackpoint_data *priv; struct ps2dev *ps2dev = &psmouse->ps2dev; unsigned char firmware_id; unsigned char button_info; @@ -301,8 +300,8 @@ int trackpoint_detect(struct psmouse *psmouse, int set_properties) button_info = 0; } - psmouse->private = priv = kzalloc(sizeof(struct trackpoint_data), GFP_KERNEL); - if (!priv) + psmouse->private = kzalloc(sizeof(struct trackpoint_data), GFP_KERNEL); + if (!psmouse->private) return -1; psmouse->vendor = "IBM"; @@ -311,7 +310,10 @@ int trackpoint_detect(struct psmouse *psmouse, int set_properties) psmouse->reconnect = trackpoint_reconnect; psmouse->disconnect = trackpoint_disconnect; - trackpoint_defaults(priv); + if ((button_info & 0x0f) >= 3) + __set_bit(BTN_MIDDLE, psmouse->dev->keybit); + + trackpoint_defaults(psmouse->private); trackpoint_sync(psmouse); error = sysfs_create_group(&ps2dev->serio->dev.kobj, &trackpoint_attr_group); @@ -319,7 +321,8 @@ int trackpoint_detect(struct psmouse *psmouse, int set_properties) printk(KERN_ERR "trackpoint.c: failed to create sysfs attributes, error: %d\n", error); - kfree(priv); + kfree(psmouse->private); + psmouse->private = NULL; return -1; } diff --git a/drivers/input/mouse/trackpoint.h b/drivers/input/mouse/trackpoint.h index c10a6e7d010..e558a709661 100644 --- a/drivers/input/mouse/trackpoint.h +++ b/drivers/input/mouse/trackpoint.h @@ -143,9 +143,9 @@ struct trackpoint_data }; #ifdef CONFIG_MOUSE_PS2_TRACKPOINT -int trackpoint_detect(struct psmouse *psmouse, int set_properties); +int trackpoint_detect(struct psmouse *psmouse, bool set_properties); #else -inline int trackpoint_detect(struct psmouse *psmouse, int set_properties) +inline int trackpoint_detect(struct psmouse *psmouse, bool set_properties) { return -ENOSYS; } diff --git a/drivers/input/mouse/vsxxxaa.c b/drivers/input/mouse/vsxxxaa.c index 404eedd5ffa..bf2c0c80d6c 100644 --- a/drivers/input/mouse/vsxxxaa.c +++ b/drivers/input/mouse/vsxxxaa.c @@ -86,27 +86,28 @@ #define DRIVER_DESC "Driver for DEC VSXXX-AA and -GA mice and VSXXX-AB tablet" -MODULE_AUTHOR ("Jan-Benedict Glaw <jbglaw@lug-owl.de>"); -MODULE_DESCRIPTION (DRIVER_DESC); -MODULE_LICENSE ("GPL"); +MODULE_AUTHOR("Jan-Benedict Glaw <jbglaw@lug-owl.de>"); +MODULE_DESCRIPTION(DRIVER_DESC); +MODULE_LICENSE("GPL"); #undef VSXXXAA_DEBUG #ifdef VSXXXAA_DEBUG -#define DBG(x...) printk (x) +#define DBG(x...) printk(x) #else #define DBG(x...) do {} while (0) #endif #define VSXXXAA_INTRO_MASK 0x80 #define VSXXXAA_INTRO_HEAD 0x80 -#define IS_HDR_BYTE(x) (((x) & VSXXXAA_INTRO_MASK) \ - == VSXXXAA_INTRO_HEAD) +#define IS_HDR_BYTE(x) \ + (((x) & VSXXXAA_INTRO_MASK) == VSXXXAA_INTRO_HEAD) #define VSXXXAA_PACKET_MASK 0xe0 #define VSXXXAA_PACKET_REL 0x80 #define VSXXXAA_PACKET_ABS 0xc0 #define VSXXXAA_PACKET_POR 0xa0 -#define MATCH_PACKET_TYPE(data, type) (((data) & VSXXXAA_PACKET_MASK) == (type)) +#define MATCH_PACKET_TYPE(data, type) \ + (((data) & VSXXXAA_PACKET_MASK) == (type)) @@ -123,52 +124,50 @@ struct vsxxxaa { char phys[32]; }; -static void -vsxxxaa_drop_bytes (struct vsxxxaa *mouse, int num) +static void vsxxxaa_drop_bytes(struct vsxxxaa *mouse, int num) { - if (num >= mouse->count) + if (num >= mouse->count) { mouse->count = 0; - else { - memmove (mouse->buf, mouse->buf + num - 1, BUFLEN - num); + } else { + memmove(mouse->buf, mouse->buf + num - 1, BUFLEN - num); mouse->count -= num; } } -static void -vsxxxaa_queue_byte (struct vsxxxaa *mouse, unsigned char byte) +static void vsxxxaa_queue_byte(struct vsxxxaa *mouse, unsigned char byte) { if (mouse->count == BUFLEN) { - printk (KERN_ERR "%s on %s: Dropping a byte of full buffer.\n", - mouse->name, mouse->phys); - vsxxxaa_drop_bytes (mouse, 1); + printk(KERN_ERR "%s on %s: Dropping a byte of full buffer.\n", + mouse->name, mouse->phys); + vsxxxaa_drop_bytes(mouse, 1); } - DBG (KERN_INFO "Queueing byte 0x%02x\n", byte); + + DBG(KERN_INFO "Queueing byte 0x%02x\n", byte); mouse->buf[mouse->count++] = byte; } -static void -vsxxxaa_detection_done (struct vsxxxaa *mouse) +static void vsxxxaa_detection_done(struct vsxxxaa *mouse) { switch (mouse->type) { - case 0x02: - strlcpy (mouse->name, "DEC VSXXX-AA/-GA mouse", - sizeof (mouse->name)); - break; - - case 0x04: - strlcpy (mouse->name, "DEC VSXXX-AB digitizer", - sizeof (mouse->name)); - break; - - default: - snprintf (mouse->name, sizeof (mouse->name), - "unknown DEC pointer device (type = 0x%02x)", - mouse->type); - break; + case 0x02: + strlcpy(mouse->name, "DEC VSXXX-AA/-GA mouse", + sizeof(mouse->name)); + break; + + case 0x04: + strlcpy(mouse->name, "DEC VSXXX-AB digitizer", + sizeof(mouse->name)); + break; + + default: + snprintf(mouse->name, sizeof(mouse->name), + "unknown DEC pointer device (type = 0x%02x)", + mouse->type); + break; } - printk (KERN_INFO + printk(KERN_INFO "Found %s version 0x%02x from country 0x%02x on port %s\n", mouse->name, mouse->version, mouse->country, mouse->phys); } @@ -176,42 +175,38 @@ vsxxxaa_detection_done (struct vsxxxaa *mouse) /* * Returns number of bytes to be dropped, 0 if packet is okay. */ -static int -vsxxxaa_check_packet (struct vsxxxaa *mouse, int packet_len) +static int vsxxxaa_check_packet(struct vsxxxaa *mouse, int packet_len) { int i; /* First byte must be a header byte */ - if (!IS_HDR_BYTE (mouse->buf[0])) { - DBG ("vsck: len=%d, 1st=0x%02x\n", packet_len, mouse->buf[0]); + if (!IS_HDR_BYTE(mouse->buf[0])) { + DBG("vsck: len=%d, 1st=0x%02x\n", packet_len, mouse->buf[0]); return 1; } /* Check all following bytes */ - if (packet_len > 1) { - for (i = 1; i < packet_len; i++) { - if (IS_HDR_BYTE (mouse->buf[i])) { - printk (KERN_ERR "Need to drop %d bytes " - "of a broken packet.\n", - i - 1); - DBG (KERN_INFO "check: len=%d, b[%d]=0x%02x\n", - packet_len, i, mouse->buf[i]); - return i - 1; - } + for (i = 1; i < packet_len; i++) { + if (IS_HDR_BYTE(mouse->buf[i])) { + printk(KERN_ERR + "Need to drop %d bytes of a broken packet.\n", + i - 1); + DBG(KERN_INFO "check: len=%d, b[%d]=0x%02x\n", + packet_len, i, mouse->buf[i]); + return i - 1; } } return 0; } -static __inline__ int -vsxxxaa_smells_like_packet (struct vsxxxaa *mouse, unsigned char type, size_t len) +static inline int vsxxxaa_smells_like_packet(struct vsxxxaa *mouse, + unsigned char type, size_t len) { - return (mouse->count >= len) && MATCH_PACKET_TYPE (mouse->buf[0], type); + return mouse->count >= len && MATCH_PACKET_TYPE(mouse->buf[0], type); } -static void -vsxxxaa_handle_REL_packet (struct vsxxxaa *mouse) +static void vsxxxaa_handle_REL_packet(struct vsxxxaa *mouse) { struct input_dev *dev = mouse->dev; unsigned char *buf = mouse->buf; @@ -232,43 +227,42 @@ vsxxxaa_handle_REL_packet (struct vsxxxaa *mouse) * 0, bit 4 of byte 0 is direction. */ dx = buf[1] & 0x7f; - dx *= ((buf[0] >> 4) & 0x01)? 1: -1; + dx *= ((buf[0] >> 4) & 0x01) ? 1 : -1; /* * Low 7 bit of byte 2 are abs(dy), bit 7 is * 0, bit 3 of byte 0 is direction. */ dy = buf[2] & 0x7f; - dy *= ((buf[0] >> 3) & 0x01)? -1: 1; + dy *= ((buf[0] >> 3) & 0x01) ? -1 : 1; /* * Get button state. It's the low three bits * (for three buttons) of byte 0. */ - left = (buf[0] & 0x04)? 1: 0; - middle = (buf[0] & 0x02)? 1: 0; - right = (buf[0] & 0x01)? 1: 0; + left = buf[0] & 0x04; + middle = buf[0] & 0x02; + right = buf[0] & 0x01; - vsxxxaa_drop_bytes (mouse, 3); + vsxxxaa_drop_bytes(mouse, 3); - DBG (KERN_INFO "%s on %s: dx=%d, dy=%d, buttons=%s%s%s\n", - mouse->name, mouse->phys, dx, dy, - left? "L": "l", middle? "M": "m", right? "R": "r"); + DBG(KERN_INFO "%s on %s: dx=%d, dy=%d, buttons=%s%s%s\n", + mouse->name, mouse->phys, dx, dy, + left ? "L" : "l", middle ? "M" : "m", right ? "R" : "r"); /* * Report what we've found so far... */ - input_report_key (dev, BTN_LEFT, left); - input_report_key (dev, BTN_MIDDLE, middle); - input_report_key (dev, BTN_RIGHT, right); - input_report_key (dev, BTN_TOUCH, 0); - input_report_rel (dev, REL_X, dx); - input_report_rel (dev, REL_Y, dy); - input_sync (dev); + input_report_key(dev, BTN_LEFT, left); + input_report_key(dev, BTN_MIDDLE, middle); + input_report_key(dev, BTN_RIGHT, right); + input_report_key(dev, BTN_TOUCH, 0); + input_report_rel(dev, REL_X, dx); + input_report_rel(dev, REL_Y, dy); + input_sync(dev); } -static void -vsxxxaa_handle_ABS_packet (struct vsxxxaa *mouse) +static void vsxxxaa_handle_ABS_packet(struct vsxxxaa *mouse) { struct input_dev *dev = mouse->dev; unsigned char *buf = mouse->buf; @@ -296,32 +290,31 @@ vsxxxaa_handle_ABS_packet (struct vsxxxaa *mouse) /* * Get button state. It's bits <4..1> of byte 0. */ - left = (buf[0] & 0x02)? 1: 0; - middle = (buf[0] & 0x04)? 1: 0; - right = (buf[0] & 0x08)? 1: 0; - touch = (buf[0] & 0x10)? 1: 0; + left = buf[0] & 0x02; + middle = buf[0] & 0x04; + right = buf[0] & 0x08; + touch = buf[0] & 0x10; - vsxxxaa_drop_bytes (mouse, 5); + vsxxxaa_drop_bytes(mouse, 5); - DBG (KERN_INFO "%s on %s: x=%d, y=%d, buttons=%s%s%s%s\n", - mouse->name, mouse->phys, x, y, - left? "L": "l", middle? "M": "m", - right? "R": "r", touch? "T": "t"); + DBG(KERN_INFO "%s on %s: x=%d, y=%d, buttons=%s%s%s%s\n", + mouse->name, mouse->phys, x, y, + left ? "L" : "l", middle ? "M" : "m", + right ? "R" : "r", touch ? "T" : "t"); /* * Report what we've found so far... */ - input_report_key (dev, BTN_LEFT, left); - input_report_key (dev, BTN_MIDDLE, middle); - input_report_key (dev, BTN_RIGHT, right); - input_report_key (dev, BTN_TOUCH, touch); - input_report_abs (dev, ABS_X, x); - input_report_abs (dev, ABS_Y, y); - input_sync (dev); + input_report_key(dev, BTN_LEFT, left); + input_report_key(dev, BTN_MIDDLE, middle); + input_report_key(dev, BTN_RIGHT, right); + input_report_key(dev, BTN_TOUCH, touch); + input_report_abs(dev, ABS_X, x); + input_report_abs(dev, ABS_Y, y); + input_sync(dev); } -static void -vsxxxaa_handle_POR_packet (struct vsxxxaa *mouse) +static void vsxxxaa_handle_POR_packet(struct vsxxxaa *mouse) { struct input_dev *dev = mouse->dev; unsigned char *buf = mouse->buf; @@ -356,24 +349,24 @@ vsxxxaa_handle_POR_packet (struct vsxxxaa *mouse) * (for three buttons) of byte 0. Maybe even the bit <3> * has some meaning if a tablet is attached. */ - left = (buf[0] & 0x04)? 1: 0; - middle = (buf[0] & 0x02)? 1: 0; - right = (buf[0] & 0x01)? 1: 0; + left = buf[0] & 0x04; + middle = buf[0] & 0x02; + right = buf[0] & 0x01; - vsxxxaa_drop_bytes (mouse, 4); - vsxxxaa_detection_done (mouse); + vsxxxaa_drop_bytes(mouse, 4); + vsxxxaa_detection_done(mouse); if (error <= 0x1f) { /* No (serious) error. Report buttons */ - input_report_key (dev, BTN_LEFT, left); - input_report_key (dev, BTN_MIDDLE, middle); - input_report_key (dev, BTN_RIGHT, right); - input_report_key (dev, BTN_TOUCH, 0); - input_sync (dev); + input_report_key(dev, BTN_LEFT, left); + input_report_key(dev, BTN_MIDDLE, middle); + input_report_key(dev, BTN_RIGHT, right); + input_report_key(dev, BTN_TOUCH, 0); + input_sync(dev); if (error != 0) - printk (KERN_INFO "Your %s on %s reports error=0x%02x\n", - mouse->name, mouse->phys, error); + printk(KERN_INFO "Your %s on %s reports error=0x%02x\n", + mouse->name, mouse->phys, error); } @@ -381,18 +374,18 @@ vsxxxaa_handle_POR_packet (struct vsxxxaa *mouse) * If the mouse was hot-plugged, we need to force differential mode * now... However, give it a second to recover from it's reset. */ - printk (KERN_NOTICE "%s on %s: Forceing standard packet format, " - "incremental streaming mode and 72 samples/sec\n", - mouse->name, mouse->phys); - mouse->serio->write (mouse->serio, 'S'); /* Standard format */ - mdelay (50); - mouse->serio->write (mouse->serio, 'R'); /* Incremental */ - mdelay (50); - mouse->serio->write (mouse->serio, 'L'); /* 72 samples/sec */ + printk(KERN_NOTICE + "%s on %s: Forcing standard packet format, " + "incremental streaming mode and 72 samples/sec\n", + mouse->name, mouse->phys); + serio_write(mouse->serio, 'S'); /* Standard format */ + mdelay(50); + serio_write(mouse->serio, 'R'); /* Incremental */ + mdelay(50); + serio_write(mouse->serio, 'L'); /* 72 samples/sec */ } -static void -vsxxxaa_parse_buffer (struct vsxxxaa *mouse) +static void vsxxxaa_parse_buffer(struct vsxxxaa *mouse) { unsigned char *buf = mouse->buf; int stray_bytes; @@ -409,122 +402,107 @@ vsxxxaa_parse_buffer (struct vsxxxaa *mouse) * activity on the mouse. */ while (mouse->count > 0 && !IS_HDR_BYTE(buf[0])) { - printk (KERN_ERR "%s on %s: Dropping a byte to regain " - "sync with mouse data stream...\n", - mouse->name, mouse->phys); - vsxxxaa_drop_bytes (mouse, 1); + printk(KERN_ERR "%s on %s: Dropping a byte to regain " + "sync with mouse data stream...\n", + mouse->name, mouse->phys); + vsxxxaa_drop_bytes(mouse, 1); } /* * Check for packets we know about. */ - if (vsxxxaa_smells_like_packet (mouse, VSXXXAA_PACKET_REL, 3)) { + if (vsxxxaa_smells_like_packet(mouse, VSXXXAA_PACKET_REL, 3)) { /* Check for broken packet */ - stray_bytes = vsxxxaa_check_packet (mouse, 3); - if (stray_bytes > 0) { - printk (KERN_ERR "Dropping %d bytes now...\n", - stray_bytes); - vsxxxaa_drop_bytes (mouse, stray_bytes); - continue; - } - - vsxxxaa_handle_REL_packet (mouse); - continue; /* More to parse? */ - } + stray_bytes = vsxxxaa_check_packet(mouse, 3); + if (!stray_bytes) + vsxxxaa_handle_REL_packet(mouse); - if (vsxxxaa_smells_like_packet (mouse, VSXXXAA_PACKET_ABS, 5)) { + } else if (vsxxxaa_smells_like_packet(mouse, + VSXXXAA_PACKET_ABS, 5)) { /* Check for broken packet */ - stray_bytes = vsxxxaa_check_packet (mouse, 5); - if (stray_bytes > 0) { - printk (KERN_ERR "Dropping %d bytes now...\n", - stray_bytes); - vsxxxaa_drop_bytes (mouse, stray_bytes); - continue; - } - - vsxxxaa_handle_ABS_packet (mouse); - continue; /* More to parse? */ - } + stray_bytes = vsxxxaa_check_packet(mouse, 5); + if (!stray_bytes) + vsxxxaa_handle_ABS_packet(mouse); - if (vsxxxaa_smells_like_packet (mouse, VSXXXAA_PACKET_POR, 4)) { + } else if (vsxxxaa_smells_like_packet(mouse, + VSXXXAA_PACKET_POR, 4)) { /* Check for broken packet */ - stray_bytes = vsxxxaa_check_packet (mouse, 4); - if (stray_bytes > 0) { - printk (KERN_ERR "Dropping %d bytes now...\n", - stray_bytes); - vsxxxaa_drop_bytes (mouse, stray_bytes); - continue; - } - - vsxxxaa_handle_POR_packet (mouse); - continue; /* More to parse? */ + stray_bytes = vsxxxaa_check_packet(mouse, 4); + if (!stray_bytes) + vsxxxaa_handle_POR_packet(mouse); + + } else { + break; /* No REL, ABS or POR packet found */ + } + + if (stray_bytes > 0) { + printk(KERN_ERR "Dropping %d bytes now...\n", + stray_bytes); + vsxxxaa_drop_bytes(mouse, stray_bytes); } - break; /* No REL, ABS or POR packet found */ } while (1); } -static irqreturn_t -vsxxxaa_interrupt (struct serio *serio, unsigned char data, unsigned int flags) +static irqreturn_t vsxxxaa_interrupt(struct serio *serio, + unsigned char data, unsigned int flags) { - struct vsxxxaa *mouse = serio_get_drvdata (serio); + struct vsxxxaa *mouse = serio_get_drvdata(serio); - vsxxxaa_queue_byte (mouse, data); - vsxxxaa_parse_buffer (mouse); + vsxxxaa_queue_byte(mouse, data); + vsxxxaa_parse_buffer(mouse); return IRQ_HANDLED; } -static void -vsxxxaa_disconnect (struct serio *serio) +static void vsxxxaa_disconnect(struct serio *serio) { - struct vsxxxaa *mouse = serio_get_drvdata (serio); + struct vsxxxaa *mouse = serio_get_drvdata(serio); - serio_close (serio); - serio_set_drvdata (serio, NULL); - input_unregister_device (mouse->dev); - kfree (mouse); + serio_close(serio); + serio_set_drvdata(serio, NULL); + input_unregister_device(mouse->dev); + kfree(mouse); } -static int -vsxxxaa_connect (struct serio *serio, struct serio_driver *drv) +static int vsxxxaa_connect(struct serio *serio, struct serio_driver *drv) { struct vsxxxaa *mouse; struct input_dev *input_dev; int err = -ENOMEM; - mouse = kzalloc (sizeof (struct vsxxxaa), GFP_KERNEL); - input_dev = input_allocate_device (); + mouse = kzalloc(sizeof(struct vsxxxaa), GFP_KERNEL); + input_dev = input_allocate_device(); if (!mouse || !input_dev) goto fail1; mouse->dev = input_dev; mouse->serio = serio; - strlcat (mouse->name, "DEC VSXXX-AA/-GA mouse or VSXXX-AB digitizer", - sizeof (mouse->name)); - snprintf (mouse->phys, sizeof (mouse->phys), "%s/input0", serio->phys); + strlcat(mouse->name, "DEC VSXXX-AA/-GA mouse or VSXXX-AB digitizer", + sizeof(mouse->name)); + snprintf(mouse->phys, sizeof(mouse->phys), "%s/input0", serio->phys); input_dev->name = mouse->name; input_dev->phys = mouse->phys; input_dev->id.bustype = BUS_RS232; input_dev->dev.parent = &serio->dev; - set_bit (EV_KEY, input_dev->evbit); /* We have buttons */ - set_bit (EV_REL, input_dev->evbit); - set_bit (EV_ABS, input_dev->evbit); - set_bit (BTN_LEFT, input_dev->keybit); /* We have 3 buttons */ - set_bit (BTN_MIDDLE, input_dev->keybit); - set_bit (BTN_RIGHT, input_dev->keybit); - set_bit (BTN_TOUCH, input_dev->keybit); /* ...and Tablet */ - set_bit (REL_X, input_dev->relbit); - set_bit (REL_Y, input_dev->relbit); - input_set_abs_params (input_dev, ABS_X, 0, 1023, 0, 0); - input_set_abs_params (input_dev, ABS_Y, 0, 1023, 0, 0); - - serio_set_drvdata (serio, mouse); - - err = serio_open (serio, drv); + __set_bit(EV_KEY, input_dev->evbit); /* We have buttons */ + __set_bit(EV_REL, input_dev->evbit); + __set_bit(EV_ABS, input_dev->evbit); + __set_bit(BTN_LEFT, input_dev->keybit); /* We have 3 buttons */ + __set_bit(BTN_MIDDLE, input_dev->keybit); + __set_bit(BTN_RIGHT, input_dev->keybit); + __set_bit(BTN_TOUCH, input_dev->keybit); /* ...and Tablet */ + __set_bit(REL_X, input_dev->relbit); + __set_bit(REL_Y, input_dev->relbit); + input_set_abs_params(input_dev, ABS_X, 0, 1023, 0, 0); + input_set_abs_params(input_dev, ABS_Y, 0, 1023, 0, 0); + + serio_set_drvdata(serio, mouse); + + err = serio_open(serio, drv); if (err) goto fail2; @@ -532,18 +510,18 @@ vsxxxaa_connect (struct serio *serio, struct serio_driver *drv) * Request selftest. Standard packet format and differential * mode will be requested after the device ID'ed successfully. */ - serio->write (serio, 'T'); /* Test */ + serio_write(serio, 'T'); /* Test */ - err = input_register_device (input_dev); + err = input_register_device(input_dev); if (err) goto fail3; return 0; - fail3: serio_close (serio); - fail2: serio_set_drvdata (serio, NULL); - fail1: input_free_device (input_dev); - kfree (mouse); + fail3: serio_close(serio); + fail2: serio_set_drvdata(serio, NULL); + fail1: input_free_device(input_dev); + kfree(mouse); return err; } @@ -570,18 +548,16 @@ static struct serio_driver vsxxxaa_drv = { .disconnect = vsxxxaa_disconnect, }; -static int __init -vsxxxaa_init (void) +static int __init vsxxxaa_init(void) { return serio_register_driver(&vsxxxaa_drv); } -static void __exit -vsxxxaa_exit (void) +static void __exit vsxxxaa_exit(void) { serio_unregister_driver(&vsxxxaa_drv); } -module_init (vsxxxaa_init); -module_exit (vsxxxaa_exit); +module_init(vsxxxaa_init); +module_exit(vsxxxaa_exit); diff --git a/drivers/input/mousedev.c b/drivers/input/mousedev.c index 966b8868f79..a13d80f7da1 100644 --- a/drivers/input/mousedev.c +++ b/drivers/input/mousedev.c @@ -13,6 +13,7 @@ #define MOUSEDEV_MINORS 32 #define MOUSEDEV_MIX 31 +#include <linux/sched.h> #include <linux/slab.h> #include <linux/smp_lock.h> #include <linux/poll.h> diff --git a/drivers/input/serio/Kconfig b/drivers/input/serio/Kconfig index c4b3fbd1a80..7e319d65ec5 100644 --- a/drivers/input/serio/Kconfig +++ b/drivers/input/serio/Kconfig @@ -4,7 +4,7 @@ config SERIO tristate "Serial I/O support" if EMBEDDED || !X86 default y - ---help--- + help Say Yes here if you have any input device that uses serial I/O to communicate with the system. This includes the * standard AT keyboard and PS/2 mouse * @@ -22,7 +22,7 @@ config SERIO_I8042 tristate "i8042 PC Keyboard controller" if EMBEDDED || !X86 default y depends on !PARISC && (!ARM || ARCH_SHARK || FOOTBRIDGE_HOST) && !M68K && !BLACKFIN - ---help--- + help i8042 is the chip over which the standard AT keyboard and PS/2 mouse are connected to the computer. If you use these devices, you'll need to say Y here. @@ -35,7 +35,7 @@ config SERIO_I8042 config SERIO_SERPORT tristate "Serial port line discipline" default y - ---help--- + help Say Y here if you plan to use an input device (mouse, joystick, tablet, 6dof) that communicates over the RS232 serial (COM) port. @@ -49,7 +49,7 @@ config SERIO_SERPORT config SERIO_CT82C710 tristate "ct82c710 Aux port controller" depends on X86 - ---help--- + help Say Y here if you have a Texas Instruments TravelMate notebook equipped with the ct82c710 chip and want to use a mouse connected to the "QuickPort". @@ -66,7 +66,7 @@ config SERIO_Q40KBD config SERIO_PARKBD tristate "Parallel port keyboard adapter" depends on PARPORT - ---help--- + help Say Y here if you built a simple parallel port adapter to attach an additional AT keyboard, XT keyboard or PS/2 mouse. @@ -124,7 +124,7 @@ config HP_SDC tristate "HP System Device Controller i8042 Support" depends on (GSC || HP300) && SERIO default y - ---help--- + help This option enables support for the "System Device Controller", an i8042 carrying microcode to manage a few miscellaneous devices on some Hewlett Packard systems. @@ -168,6 +168,7 @@ config SERIO_MACEPS2 config SERIO_LIBPS2 tristate "PS/2 driver library" if EMBEDDED + depends on SERIO_I8042 || SERIO_I8042=n help Say Y here if you are using a driver for device connected to a PS/2 port, such as PS/2 mouse or standard AT keyboard. @@ -200,4 +201,12 @@ config SERIO_XILINX_XPS_PS2 To compile this driver as a module, choose M here: the module will be called xilinx_ps2. +config SERIO_ALTERA_PS2 + tristate "Altera UP PS/2 controller" + help + Say Y here if you have Altera University Program PS/2 ports. + + To compile this driver as a module, choose M here: the + module will be called altera_ps2. + endif diff --git a/drivers/input/serio/Makefile b/drivers/input/serio/Makefile index 9b6c8135955..bf945f789d0 100644 --- a/drivers/input/serio/Makefile +++ b/drivers/input/serio/Makefile @@ -22,3 +22,4 @@ obj-$(CONFIG_SERIO_MACEPS2) += maceps2.o obj-$(CONFIG_SERIO_LIBPS2) += libps2.o obj-$(CONFIG_SERIO_RAW) += serio_raw.o obj-$(CONFIG_SERIO_XILINX_XPS_PS2) += xilinx_ps2.o +obj-$(CONFIG_SERIO_ALTERA_PS2) += altera_ps2.o diff --git a/drivers/input/serio/altera_ps2.c b/drivers/input/serio/altera_ps2.c new file mode 100644 index 00000000000..f479ea50919 --- /dev/null +++ b/drivers/input/serio/altera_ps2.c @@ -0,0 +1,200 @@ +/* + * Altera University Program PS2 controller driver + * + * Copyright (C) 2008 Thomas Chou <thomas@wytron.com.tw> + * + * Based on sa1111ps2.c, which is: + * Copyright (C) 2002 Russell King + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ + +#include <linux/module.h> +#include <linux/init.h> +#include <linux/input.h> +#include <linux/serio.h> +#include <linux/interrupt.h> +#include <linux/platform_device.h> +#include <linux/io.h> + +#define DRV_NAME "altera_ps2" + +struct ps2if { + struct serio *io; + struct resource *iomem_res; + void __iomem *base; + unsigned irq; +}; + +/* + * Read all bytes waiting in the PS2 port. There should be + * at the most one, but we loop for safety. + */ +static irqreturn_t altera_ps2_rxint(int irq, void *dev_id) +{ + struct ps2if *ps2if = dev_id; + unsigned int status; + int handled = IRQ_NONE; + + while ((status = readl(ps2if->base)) & 0xffff0000) { + serio_interrupt(ps2if->io, status & 0xff, 0); + handled = IRQ_HANDLED; + } + + return handled; +} + +/* + * Write a byte to the PS2 port. + */ +static int altera_ps2_write(struct serio *io, unsigned char val) +{ + struct ps2if *ps2if = io->port_data; + + writel(val, ps2if->base); + return 0; +} + +static int altera_ps2_open(struct serio *io) +{ + struct ps2if *ps2if = io->port_data; + + /* clear fifo */ + while (readl(ps2if->base) & 0xffff0000) + /* empty */; + + writel(1, ps2if->base + 4); /* enable rx irq */ + return 0; +} + +static void altera_ps2_close(struct serio *io) +{ + struct ps2if *ps2if = io->port_data; + + writel(0, ps2if->base); /* disable rx irq */ +} + +/* + * Add one device to this driver. + */ +static int altera_ps2_probe(struct platform_device *pdev) +{ + struct ps2if *ps2if; + struct serio *serio; + int error; + + ps2if = kzalloc(sizeof(struct ps2if), GFP_KERNEL); + serio = kzalloc(sizeof(struct serio), GFP_KERNEL); + if (!ps2if || !serio) { + error = -ENOMEM; + goto err_free_mem; + } + + serio->id.type = SERIO_8042; + serio->write = altera_ps2_write; + serio->open = altera_ps2_open; + serio->close = altera_ps2_close; + strlcpy(serio->name, dev_name(&pdev->dev), sizeof(serio->name)); + strlcpy(serio->phys, dev_name(&pdev->dev), sizeof(serio->phys)); + serio->port_data = ps2if; + serio->dev.parent = &pdev->dev; + ps2if->io = serio; + + ps2if->iomem_res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + if (ps2if->iomem_res == NULL) { + error = -ENOENT; + goto err_free_mem; + } + + ps2if->irq = platform_get_irq(pdev, 0); + if (ps2if->irq < 0) { + error = -ENXIO; + goto err_free_mem; + } + + if (!request_mem_region(ps2if->iomem_res->start, + resource_size(ps2if->iomem_res), pdev->name)) { + error = -EBUSY; + goto err_free_mem; + } + + ps2if->base = ioremap(ps2if->iomem_res->start, + resource_size(ps2if->iomem_res)); + if (!ps2if->base) { + error = -ENOMEM; + goto err_free_res; + } + + error = request_irq(ps2if->irq, altera_ps2_rxint, 0, pdev->name, ps2if); + if (error) { + dev_err(&pdev->dev, "could not allocate IRQ %d: %d\n", + ps2if->irq, error); + goto err_unmap; + } + + dev_info(&pdev->dev, "base %p, irq %d\n", ps2if->base, ps2if->irq); + + serio_register_port(ps2if->io); + platform_set_drvdata(pdev, ps2if); + + return 0; + + err_unmap: + iounmap(ps2if->base); + err_free_res: + release_mem_region(ps2if->iomem_res->start, + resource_size(ps2if->iomem_res)); + err_free_mem: + kfree(ps2if); + kfree(serio); + return error; +} + +/* + * Remove one device from this driver. + */ +static int altera_ps2_remove(struct platform_device *pdev) +{ + struct ps2if *ps2if = platform_get_drvdata(pdev); + + platform_set_drvdata(pdev, NULL); + serio_unregister_port(ps2if->io); + free_irq(ps2if->irq, ps2if); + iounmap(ps2if->base); + release_mem_region(ps2if->iomem_res->start, + resource_size(ps2if->iomem_res)); + kfree(ps2if); + + return 0; +} + +/* + * Our device driver structure + */ +static struct platform_driver altera_ps2_driver = { + .probe = altera_ps2_probe, + .remove = altera_ps2_remove, + .driver = { + .name = DRV_NAME, + }, +}; + +static int __init altera_ps2_init(void) +{ + return platform_driver_register(&altera_ps2_driver); +} + +static void __exit altera_ps2_exit(void) +{ + platform_driver_unregister(&altera_ps2_driver); +} + +module_init(altera_ps2_init); +module_exit(altera_ps2_exit); + +MODULE_DESCRIPTION("Altera University Program PS2 controller driver"); +MODULE_AUTHOR("Thomas Chou <thomas@wytron.com.tw>"); +MODULE_LICENSE("GPL"); +MODULE_ALIAS("platform:" DRV_NAME); diff --git a/drivers/input/serio/at32psif.c b/drivers/input/serio/at32psif.c index 41fda8c67b1..a6fb7a3dcc4 100644 --- a/drivers/input/serio/at32psif.c +++ b/drivers/input/serio/at32psif.c @@ -231,7 +231,7 @@ static int __init psif_probe(struct platform_device *pdev) goto out_free_io; } - psif->regs = ioremap(regs->start, regs->end - regs->start + 1); + psif->regs = ioremap(regs->start, resource_size(regs)); if (!psif->regs) { ret = -ENOMEM; dev_dbg(&pdev->dev, "could not map I/O memory\n"); diff --git a/drivers/input/serio/hp_sdc.c b/drivers/input/serio/hp_sdc.c index 1c9410d1822..bcc2d30ec24 100644 --- a/drivers/input/serio/hp_sdc.c +++ b/drivers/input/serio/hp_sdc.c @@ -955,7 +955,7 @@ static int __init hp_sdc_init_hppa(struct parisc_device *d) INIT_DELAYED_WORK(&moduleloader_work, request_module_delayed); ret = hp_sdc_init(); - /* after sucessfull initialization give SDC some time to settle + /* after successfull initialization give SDC some time to settle * and then load the hp_sdc_mlc upper layer driver */ if (!ret) schedule_delayed_work(&moduleloader_work, diff --git a/drivers/input/serio/hp_sdc_mlc.c b/drivers/input/serio/hp_sdc_mlc.c index 820e51673b2..7d2b820ef58 100644 --- a/drivers/input/serio/hp_sdc_mlc.c +++ b/drivers/input/serio/hp_sdc_mlc.c @@ -125,7 +125,7 @@ static void hp_sdc_mlc_isr (int irq, void *dev_id, break; default: - printk(KERN_WARNING PREFIX "Unkown HIL Error status (%x)!\n", data); + printk(KERN_WARNING PREFIX "Unknown HIL Error status (%x)!\n", data); break; } diff --git a/drivers/input/serio/i8042-x86ia64io.h b/drivers/input/serio/i8042-x86ia64io.h index ccbf23ece8e..7fbffe431bc 100644 --- a/drivers/input/serio/i8042-x86ia64io.h +++ b/drivers/input/serio/i8042-x86ia64io.h @@ -67,10 +67,12 @@ static inline void i8042_write_command(int val) #include <linux/dmi.h> -static struct dmi_system_id __initdata i8042_dmi_noloop_table[] = { +static const struct dmi_system_id __initconst i8042_dmi_noloop_table[] = { { - /* AUX LOOP command does not raise AUX IRQ */ - .ident = "Arima-Rioworks HDAMB", + /* + * Arima-Rioworks HDAMB - + * AUX LOOP command does not raise AUX IRQ + */ .matches = { DMI_MATCH(DMI_BOARD_VENDOR, "RIOWORKS"), DMI_MATCH(DMI_BOARD_NAME, "HDAMB"), @@ -78,7 +80,7 @@ static struct dmi_system_id __initdata i8042_dmi_noloop_table[] = { }, }, { - .ident = "ASUS G1S", + /* ASUS G1S */ .matches = { DMI_MATCH(DMI_BOARD_VENDOR, "ASUSTeK Computer Inc."), DMI_MATCH(DMI_BOARD_NAME, "G1S"), @@ -86,8 +88,7 @@ static struct dmi_system_id __initdata i8042_dmi_noloop_table[] = { }, }, { - /* AUX LOOP command does not raise AUX IRQ */ - .ident = "ASUS P65UP5", + /* ASUS P65UP5 - AUX LOOP command does not raise AUX IRQ */ .matches = { DMI_MATCH(DMI_BOARD_VENDOR, "ASUSTeK Computer INC."), DMI_MATCH(DMI_BOARD_NAME, "P/I-P65UP5"), @@ -95,7 +96,6 @@ static struct dmi_system_id __initdata i8042_dmi_noloop_table[] = { }, }, { - .ident = "Compaq Proliant 8500", .matches = { DMI_MATCH(DMI_SYS_VENDOR, "Compaq"), DMI_MATCH(DMI_PRODUCT_NAME , "ProLiant"), @@ -103,7 +103,6 @@ static struct dmi_system_id __initdata i8042_dmi_noloop_table[] = { }, }, { - .ident = "Compaq Proliant DL760", .matches = { DMI_MATCH(DMI_SYS_VENDOR, "Compaq"), DMI_MATCH(DMI_PRODUCT_NAME , "ProLiant"), @@ -111,7 +110,7 @@ static struct dmi_system_id __initdata i8042_dmi_noloop_table[] = { }, }, { - .ident = "OQO Model 01", + /* OQO Model 01 */ .matches = { DMI_MATCH(DMI_SYS_VENDOR, "OQO"), DMI_MATCH(DMI_PRODUCT_NAME, "ZEPTO"), @@ -119,8 +118,7 @@ static struct dmi_system_id __initdata i8042_dmi_noloop_table[] = { }, }, { - /* AUX LOOP does not work properly */ - .ident = "ULI EV4873", + /* ULI EV4873 - AUX LOOP does not work properly */ .matches = { DMI_MATCH(DMI_SYS_VENDOR, "ULI"), DMI_MATCH(DMI_PRODUCT_NAME, "EV4873"), @@ -128,7 +126,7 @@ static struct dmi_system_id __initdata i8042_dmi_noloop_table[] = { }, }, { - .ident = "Microsoft Virtual Machine", + /* Microsoft Virtual Machine */ .matches = { DMI_MATCH(DMI_SYS_VENDOR, "Microsoft Corporation"), DMI_MATCH(DMI_PRODUCT_NAME, "Virtual Machine"), @@ -136,7 +134,7 @@ static struct dmi_system_id __initdata i8042_dmi_noloop_table[] = { }, }, { - .ident = "Medion MAM 2070", + /* Medion MAM 2070 */ .matches = { DMI_MATCH(DMI_SYS_VENDOR, "Notebook"), DMI_MATCH(DMI_PRODUCT_NAME, "MAM 2070"), @@ -144,7 +142,7 @@ static struct dmi_system_id __initdata i8042_dmi_noloop_table[] = { }, }, { - .ident = "Blue FB5601", + /* Blue FB5601 */ .matches = { DMI_MATCH(DMI_SYS_VENDOR, "blue"), DMI_MATCH(DMI_PRODUCT_NAME, "FB5601"), @@ -152,7 +150,7 @@ static struct dmi_system_id __initdata i8042_dmi_noloop_table[] = { }, }, { - .ident = "Gigabyte M912", + /* Gigabyte M912 */ .matches = { DMI_MATCH(DMI_SYS_VENDOR, "GIGABYTE"), DMI_MATCH(DMI_PRODUCT_NAME, "M912"), @@ -160,7 +158,6 @@ static struct dmi_system_id __initdata i8042_dmi_noloop_table[] = { }, }, { - .ident = "HP DV9700", .matches = { DMI_MATCH(DMI_SYS_VENDOR, "Hewlett-Packard"), DMI_MATCH(DMI_PRODUCT_NAME, "HP Pavilion dv9700"), @@ -177,72 +174,72 @@ static struct dmi_system_id __initdata i8042_dmi_noloop_table[] = { * ... apparently some Toshibas don't like MUX mode either and * die horrible death on reboot. */ -static struct dmi_system_id __initdata i8042_dmi_nomux_table[] = { +static const struct dmi_system_id __initconst i8042_dmi_nomux_table[] = { { - .ident = "Fujitsu Lifebook P7010/P7010D", + /* Fujitsu Lifebook P7010/P7010D */ .matches = { DMI_MATCH(DMI_SYS_VENDOR, "FUJITSU"), DMI_MATCH(DMI_PRODUCT_NAME, "P7010"), }, }, { - .ident = "Fujitsu Lifebook P7010", + /* Fujitsu Lifebook P7010 */ .matches = { DMI_MATCH(DMI_SYS_VENDOR, "FUJITSU SIEMENS"), DMI_MATCH(DMI_PRODUCT_NAME, "0000000000"), }, }, { - .ident = "Fujitsu Lifebook P5020D", + /* Fujitsu Lifebook P5020D */ .matches = { DMI_MATCH(DMI_SYS_VENDOR, "FUJITSU"), DMI_MATCH(DMI_PRODUCT_NAME, "LifeBook P Series"), }, }, { - .ident = "Fujitsu Lifebook S2000", + /* Fujitsu Lifebook S2000 */ .matches = { DMI_MATCH(DMI_SYS_VENDOR, "FUJITSU"), DMI_MATCH(DMI_PRODUCT_NAME, "LifeBook S Series"), }, }, { - .ident = "Fujitsu Lifebook S6230", + /* Fujitsu Lifebook S6230 */ .matches = { DMI_MATCH(DMI_SYS_VENDOR, "FUJITSU"), DMI_MATCH(DMI_PRODUCT_NAME, "LifeBook S6230"), }, }, { - .ident = "Fujitsu T70H", + /* Fujitsu T70H */ .matches = { DMI_MATCH(DMI_SYS_VENDOR, "FUJITSU"), DMI_MATCH(DMI_PRODUCT_NAME, "FMVLT70H"), }, }, { - .ident = "Fujitsu-Siemens Lifebook T3010", + /* Fujitsu-Siemens Lifebook T3010 */ .matches = { DMI_MATCH(DMI_SYS_VENDOR, "FUJITSU SIEMENS"), DMI_MATCH(DMI_PRODUCT_NAME, "LIFEBOOK T3010"), }, }, { - .ident = "Fujitsu-Siemens Lifebook E4010", + /* Fujitsu-Siemens Lifebook E4010 */ .matches = { DMI_MATCH(DMI_SYS_VENDOR, "FUJITSU SIEMENS"), DMI_MATCH(DMI_PRODUCT_NAME, "LIFEBOOK E4010"), }, }, { - .ident = "Fujitsu-Siemens Amilo Pro 2010", + /* Fujitsu-Siemens Amilo Pro 2010 */ .matches = { DMI_MATCH(DMI_SYS_VENDOR, "FUJITSU SIEMENS"), DMI_MATCH(DMI_PRODUCT_NAME, "AMILO Pro V2010"), }, }, { - .ident = "Fujitsu-Siemens Amilo Pro 2030", + /* Fujitsu-Siemens Amilo Pro 2030 */ .matches = { DMI_MATCH(DMI_SYS_VENDOR, "FUJITSU SIEMENS"), DMI_MATCH(DMI_PRODUCT_NAME, "AMILO PRO V2030"), @@ -253,7 +250,7 @@ static struct dmi_system_id __initdata i8042_dmi_nomux_table[] = { * No data is coming from the touchscreen unless KBC * is in legacy mode. */ - .ident = "Panasonic CF-29", + /* Panasonic CF-29 */ .matches = { DMI_MATCH(DMI_SYS_VENDOR, "Matsushita"), DMI_MATCH(DMI_PRODUCT_NAME, "CF-29"), @@ -261,10 +258,10 @@ static struct dmi_system_id __initdata i8042_dmi_nomux_table[] = { }, { /* - * Errors on MUX ports are reported without raising AUXDATA + * HP Pavilion DV4017EA - + * errors on MUX ports are reported without raising AUXDATA * causing "spurious NAK" messages. */ - .ident = "HP Pavilion DV4017EA", .matches = { DMI_MATCH(DMI_SYS_VENDOR, "Hewlett-Packard"), DMI_MATCH(DMI_PRODUCT_NAME, "Pavilion dv4000 (EA032EA#ABF)"), @@ -272,9 +269,9 @@ static struct dmi_system_id __initdata i8042_dmi_nomux_table[] = { }, { /* - * Like DV4017EA does not raise AUXERR for errors on MUX ports. + * HP Pavilion ZT1000 - + * like DV4017EA does not raise AUXERR for errors on MUX ports. */ - .ident = "HP Pavilion ZT1000", .matches = { DMI_MATCH(DMI_SYS_VENDOR, "Hewlett-Packard"), DMI_MATCH(DMI_PRODUCT_NAME, "HP Pavilion Notebook PC"), @@ -283,107 +280,114 @@ static struct dmi_system_id __initdata i8042_dmi_nomux_table[] = { }, { /* - * Like DV4017EA does not raise AUXERR for errors on MUX ports. + * HP Pavilion DV4270ca - + * like DV4017EA does not raise AUXERR for errors on MUX ports. */ - .ident = "HP Pavilion DV4270ca", .matches = { DMI_MATCH(DMI_SYS_VENDOR, "Hewlett-Packard"), DMI_MATCH(DMI_PRODUCT_NAME, "Pavilion dv4000 (EH476UA#ABL)"), }, }, { - .ident = "Toshiba P10", .matches = { DMI_MATCH(DMI_SYS_VENDOR, "TOSHIBA"), DMI_MATCH(DMI_PRODUCT_NAME, "Satellite P10"), }, }, { - .ident = "Toshiba Equium A110", .matches = { DMI_MATCH(DMI_SYS_VENDOR, "TOSHIBA"), DMI_MATCH(DMI_PRODUCT_NAME, "EQUIUM A110"), }, }, { - .ident = "Alienware Sentia", .matches = { DMI_MATCH(DMI_SYS_VENDOR, "ALIENWARE"), DMI_MATCH(DMI_PRODUCT_NAME, "Sentia"), }, }, { - .ident = "Sharp Actius MM20", + /* Sharp Actius MM20 */ .matches = { DMI_MATCH(DMI_SYS_VENDOR, "SHARP"), DMI_MATCH(DMI_PRODUCT_NAME, "PC-MM20 Series"), }, }, { - .ident = "Sony Vaio FS-115b", + /* Sony Vaio FS-115b */ .matches = { DMI_MATCH(DMI_SYS_VENDOR, "Sony Corporation"), DMI_MATCH(DMI_PRODUCT_NAME, "VGN-FS115B"), }, }, { - .ident = "Amoi M636/A737", + /* + * Sony Vaio FZ-240E - + * reset and GET ID commands issued via KBD port are + * sometimes being delivered to AUX3. + */ + .matches = { + DMI_MATCH(DMI_SYS_VENDOR, "Sony Corporation"), + DMI_MATCH(DMI_PRODUCT_NAME, "VGN-FZ240E"), + }, + }, + { + /* Amoi M636/A737 */ .matches = { DMI_MATCH(DMI_SYS_VENDOR, "Amoi Electronics CO.,LTD."), DMI_MATCH(DMI_PRODUCT_NAME, "M636/A737 platform"), }, }, { - .ident = "Lenovo 3000 n100", + /* Lenovo 3000 n100 */ .matches = { DMI_MATCH(DMI_SYS_VENDOR, "LENOVO"), DMI_MATCH(DMI_PRODUCT_NAME, "076804U"), }, }, { - .ident = "Acer Aspire 1360", .matches = { DMI_MATCH(DMI_SYS_VENDOR, "Acer"), DMI_MATCH(DMI_PRODUCT_NAME, "Aspire 1360"), }, }, { - .ident = "Gericom Bellagio", + /* Gericom Bellagio */ .matches = { DMI_MATCH(DMI_SYS_VENDOR, "Gericom"), DMI_MATCH(DMI_PRODUCT_NAME, "N34AS6"), }, }, { - .ident = "IBM 2656", + /* IBM 2656 */ .matches = { DMI_MATCH(DMI_SYS_VENDOR, "IBM"), DMI_MATCH(DMI_PRODUCT_NAME, "2656"), }, }, { - .ident = "Dell XPS M1530", + /* Dell XPS M1530 */ .matches = { DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc."), DMI_MATCH(DMI_PRODUCT_NAME, "XPS M1530"), }, }, { - .ident = "Compal HEL80I", + /* Compal HEL80I */ .matches = { DMI_MATCH(DMI_SYS_VENDOR, "COMPAL"), DMI_MATCH(DMI_PRODUCT_NAME, "HEL80I"), }, }, { - .ident = "Dell Vostro 1510", + /* Dell Vostro 1510 */ .matches = { DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc."), DMI_MATCH(DMI_PRODUCT_NAME, "Vostro1510"), }, }, { - .ident = "Acer Aspire 5536", + /* Acer Aspire 5536 */ .matches = { DMI_MATCH(DMI_SYS_VENDOR, "Acer"), DMI_MATCH(DMI_PRODUCT_NAME, "Aspire 5536"), @@ -393,63 +397,84 @@ static struct dmi_system_id __initdata i8042_dmi_nomux_table[] = { { } }; -static struct dmi_system_id __initdata i8042_dmi_reset_table[] = { +static const struct dmi_system_id __initconst i8042_dmi_reset_table[] = { { - .ident = "MSI Wind U-100", + /* MSI Wind U-100 */ .matches = { DMI_MATCH(DMI_BOARD_NAME, "U-100"), DMI_MATCH(DMI_BOARD_VENDOR, "MICRO-STAR INTERNATIONAL CO., LTD"), }, }, { - .ident = "LG Electronics X110", + /* LG Electronics X110 */ .matches = { DMI_MATCH(DMI_BOARD_NAME, "X110"), DMI_MATCH(DMI_BOARD_VENDOR, "LG Electronics Inc."), }, }, { - .ident = "Acer Aspire One 150", + /* Acer Aspire One 150 */ .matches = { DMI_MATCH(DMI_SYS_VENDOR, "Acer"), DMI_MATCH(DMI_PRODUCT_NAME, "AOA150"), }, }, { - .ident = "Advent 4211", + /* Advent 4211 */ .matches = { DMI_MATCH(DMI_SYS_VENDOR, "DIXONSXP"), DMI_MATCH(DMI_PRODUCT_NAME, "Advent 4211"), }, }, { - .ident = "Medion Akoya Mini E1210", + /* Medion Akoya Mini E1210 */ .matches = { DMI_MATCH(DMI_SYS_VENDOR, "MEDION"), DMI_MATCH(DMI_PRODUCT_NAME, "E1210"), }, }, { - .ident = "Mivvy M310", + /* Mivvy M310 */ .matches = { DMI_MATCH(DMI_SYS_VENDOR, "VIOOO"), DMI_MATCH(DMI_PRODUCT_NAME, "N10"), }, }, + { + /* Dell Vostro 1320 */ + .matches = { + DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc."), + DMI_MATCH(DMI_PRODUCT_NAME, "Vostro 1320"), + }, + }, + { + /* Dell Vostro 1520 */ + .matches = { + DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc."), + DMI_MATCH(DMI_PRODUCT_NAME, "Vostro 1520"), + }, + }, + { + /* Dell Vostro 1720 */ + .matches = { + DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc."), + DMI_MATCH(DMI_PRODUCT_NAME, "Vostro 1720"), + }, + }, { } }; #ifdef CONFIG_PNP -static struct dmi_system_id __initdata i8042_dmi_nopnp_table[] = { +static const struct dmi_system_id __initconst i8042_dmi_nopnp_table[] = { { - .ident = "Intel MBO Desktop D845PESV", + /* Intel MBO Desktop D845PESV */ .matches = { DMI_MATCH(DMI_BOARD_NAME, "D845PESV"), DMI_MATCH(DMI_BOARD_VENDOR, "Intel Corporation"), }, }, { - .ident = "MSI Wind U-100", + /* MSI Wind U-100 */ .matches = { DMI_MATCH(DMI_BOARD_NAME, "U-100"), DMI_MATCH(DMI_BOARD_VENDOR, "MICRO-STAR INTERNATIONAL CO., LTD"), @@ -457,6 +482,30 @@ static struct dmi_system_id __initdata i8042_dmi_nopnp_table[] = { }, { } }; + +static const struct dmi_system_id __initconst i8042_dmi_laptop_table[] = { + { + .matches = { + DMI_MATCH(DMI_CHASSIS_TYPE, "8"), /* Portable */ + }, + }, + { + .matches = { + DMI_MATCH(DMI_CHASSIS_TYPE, "9"), /* Laptop */ + }, + }, + { + .matches = { + DMI_MATCH(DMI_CHASSIS_TYPE, "10"), /* Notebook */ + }, + }, + { + .matches = { + DMI_MATCH(DMI_CHASSIS_TYPE, "14"), /* Sub-Notebook */ + }, + }, + { } +}; #endif /* @@ -465,58 +514,58 @@ static struct dmi_system_id __initdata i8042_dmi_nopnp_table[] = { * Originally, this was just confined to older laptops, but a few Acer laptops * have turned up in 2007 that also need this again. */ -static struct dmi_system_id __initdata i8042_dmi_dritek_table[] = { +static const struct dmi_system_id __initconst i8042_dmi_dritek_table[] = { { - .ident = "Acer Aspire 5630", + /* Acer Aspire 5630 */ .matches = { DMI_MATCH(DMI_SYS_VENDOR, "Acer"), DMI_MATCH(DMI_PRODUCT_NAME, "Aspire 5630"), }, }, { - .ident = "Acer Aspire 5650", + /* Acer Aspire 5650 */ .matches = { DMI_MATCH(DMI_SYS_VENDOR, "Acer"), DMI_MATCH(DMI_PRODUCT_NAME, "Aspire 5650"), }, }, { - .ident = "Acer Aspire 5680", + /* Acer Aspire 5680 */ .matches = { DMI_MATCH(DMI_SYS_VENDOR, "Acer"), DMI_MATCH(DMI_PRODUCT_NAME, "Aspire 5680"), }, }, { - .ident = "Acer Aspire 5720", + /* Acer Aspire 5720 */ .matches = { DMI_MATCH(DMI_SYS_VENDOR, "Acer"), DMI_MATCH(DMI_PRODUCT_NAME, "Aspire 5720"), }, }, { - .ident = "Acer Aspire 9110", + /* Acer Aspire 9110 */ .matches = { DMI_MATCH(DMI_SYS_VENDOR, "Acer"), DMI_MATCH(DMI_PRODUCT_NAME, "Aspire 9110"), }, }, { - .ident = "Acer TravelMate 660", + /* Acer TravelMate 660 */ .matches = { DMI_MATCH(DMI_SYS_VENDOR, "Acer"), DMI_MATCH(DMI_PRODUCT_NAME, "TravelMate 660"), }, }, { - .ident = "Acer TravelMate 2490", + /* Acer TravelMate 2490 */ .matches = { DMI_MATCH(DMI_SYS_VENDOR, "Acer"), DMI_MATCH(DMI_PRODUCT_NAME, "TravelMate 2490"), }, }, { - .ident = "Acer TravelMate 4280", + /* Acer TravelMate 4280 */ .matches = { DMI_MATCH(DMI_SYS_VENDOR, "Acer"), DMI_MATCH(DMI_PRODUCT_NAME, "TravelMate 4280"), @@ -530,9 +579,9 @@ static struct dmi_system_id __initdata i8042_dmi_dritek_table[] = { #ifdef CONFIG_PNP #include <linux/pnp.h> -static int i8042_pnp_kbd_registered; +static bool i8042_pnp_kbd_registered; static unsigned int i8042_pnp_kbd_devices; -static int i8042_pnp_aux_registered; +static bool i8042_pnp_aux_registered; static unsigned int i8042_pnp_aux_devices; static int i8042_pnp_command_reg; @@ -620,12 +669,12 @@ static struct pnp_driver i8042_pnp_aux_driver = { static void i8042_pnp_exit(void) { if (i8042_pnp_kbd_registered) { - i8042_pnp_kbd_registered = 0; + i8042_pnp_kbd_registered = false; pnp_unregister_driver(&i8042_pnp_kbd_driver); } if (i8042_pnp_aux_registered) { - i8042_pnp_aux_registered = 0; + i8042_pnp_aux_registered = false; pnp_unregister_driver(&i8042_pnp_aux_driver); } } @@ -633,12 +682,12 @@ static void i8042_pnp_exit(void) static int __init i8042_pnp_init(void) { char kbd_irq_str[4] = { 0 }, aux_irq_str[4] = { 0 }; - int pnp_data_busted = 0; + bool pnp_data_busted = false; int err; #ifdef CONFIG_X86 if (dmi_check_system(i8042_dmi_nopnp_table)) - i8042_nopnp = 1; + i8042_nopnp = true; #endif if (i8042_nopnp) { @@ -648,11 +697,11 @@ static int __init i8042_pnp_init(void) err = pnp_register_driver(&i8042_pnp_kbd_driver); if (!err) - i8042_pnp_kbd_registered = 1; + i8042_pnp_kbd_registered = true; err = pnp_register_driver(&i8042_pnp_aux_driver); if (!err) - i8042_pnp_aux_registered = 1; + i8042_pnp_aux_registered = true; if (!i8042_pnp_kbd_devices && !i8042_pnp_aux_devices) { i8042_pnp_exit(); @@ -680,9 +729,9 @@ static int __init i8042_pnp_init(void) #if defined(__ia64__) if (!i8042_pnp_kbd_devices) - i8042_nokbd = 1; + i8042_nokbd = true; if (!i8042_pnp_aux_devices) - i8042_noaux = 1; + i8042_noaux = true; #endif if (((i8042_pnp_data_reg & ~0xf) == (i8042_data_reg & ~0xf) && @@ -693,7 +742,7 @@ static int __init i8042_pnp_init(void) "using default %#x\n", i8042_pnp_data_reg, i8042_data_reg); i8042_pnp_data_reg = i8042_data_reg; - pnp_data_busted = 1; + pnp_data_busted = true; } if (((i8042_pnp_command_reg & ~0xf) == (i8042_command_reg & ~0xf) && @@ -704,7 +753,7 @@ static int __init i8042_pnp_init(void) "using default %#x\n", i8042_pnp_command_reg, i8042_command_reg); i8042_pnp_command_reg = i8042_command_reg; - pnp_data_busted = 1; + pnp_data_busted = true; } if (!i8042_nokbd && !i8042_pnp_kbd_irq) { @@ -712,7 +761,7 @@ static int __init i8042_pnp_init(void) "PNP: PS/2 controller doesn't have KBD irq; " "using default %d\n", i8042_kbd_irq); i8042_pnp_kbd_irq = i8042_kbd_irq; - pnp_data_busted = 1; + pnp_data_busted = true; } if (!i8042_noaux && !i8042_pnp_aux_irq) { @@ -721,7 +770,7 @@ static int __init i8042_pnp_init(void) "PNP: PS/2 appears to have AUX port disabled, " "if this is incorrect please boot with " "i8042.nopnp\n"); - i8042_noaux = 1; + i8042_noaux = true; } else { printk(KERN_WARNING "PNP: PS/2 controller doesn't have AUX irq; " @@ -735,6 +784,11 @@ static int __init i8042_pnp_init(void) i8042_kbd_irq = i8042_pnp_kbd_irq; i8042_aux_irq = i8042_pnp_aux_irq; +#ifdef CONFIG_X86 + i8042_bypass_aux_irq_test = !pnp_data_busted && + dmi_check_system(i8042_dmi_laptop_table); +#endif + return 0; } @@ -763,21 +817,21 @@ static int __init i8042_platform_init(void) return retval; #if defined(__ia64__) - i8042_reset = 1; + i8042_reset = true; #endif #ifdef CONFIG_X86 if (dmi_check_system(i8042_dmi_reset_table)) - i8042_reset = 1; + i8042_reset = true; if (dmi_check_system(i8042_dmi_noloop_table)) - i8042_noloop = 1; + i8042_noloop = true; if (dmi_check_system(i8042_dmi_nomux_table)) - i8042_nomux = 1; + i8042_nomux = true; if (dmi_check_system(i8042_dmi_dritek_table)) - i8042_dritek = 1; + i8042_dritek = true; #endif /* CONFIG_X86 */ return retval; diff --git a/drivers/input/serio/i8042.c b/drivers/input/serio/i8042.c index 582245c497e..1df02d25aca 100644 --- a/drivers/input/serio/i8042.c +++ b/drivers/input/serio/i8042.c @@ -28,35 +28,35 @@ MODULE_AUTHOR("Vojtech Pavlik <vojtech@suse.cz>"); MODULE_DESCRIPTION("i8042 keyboard and mouse controller driver"); MODULE_LICENSE("GPL"); -static unsigned int i8042_nokbd; +static bool i8042_nokbd; module_param_named(nokbd, i8042_nokbd, bool, 0); MODULE_PARM_DESC(nokbd, "Do not probe or use KBD port."); -static unsigned int i8042_noaux; +static bool i8042_noaux; module_param_named(noaux, i8042_noaux, bool, 0); MODULE_PARM_DESC(noaux, "Do not probe or use AUX (mouse) port."); -static unsigned int i8042_nomux; +static bool i8042_nomux; module_param_named(nomux, i8042_nomux, bool, 0); MODULE_PARM_DESC(nomux, "Do not check whether an active multiplexing conrtoller is present."); -static unsigned int i8042_unlock; +static bool i8042_unlock; module_param_named(unlock, i8042_unlock, bool, 0); MODULE_PARM_DESC(unlock, "Ignore keyboard lock."); -static unsigned int i8042_reset; +static bool i8042_reset; module_param_named(reset, i8042_reset, bool, 0); MODULE_PARM_DESC(reset, "Reset controller during init and cleanup."); -static unsigned int i8042_direct; +static bool i8042_direct; module_param_named(direct, i8042_direct, bool, 0); MODULE_PARM_DESC(direct, "Put keyboard port into non-translated mode."); -static unsigned int i8042_dumbkbd; +static bool i8042_dumbkbd; module_param_named(dumbkbd, i8042_dumbkbd, bool, 0); MODULE_PARM_DESC(dumbkbd, "Pretend that controller can only read data from keyboard"); -static unsigned int i8042_noloop; +static bool i8042_noloop; module_param_named(noloop, i8042_noloop, bool, 0); MODULE_PARM_DESC(noloop, "Disable the AUX Loopback command while probing for the AUX port"); @@ -65,32 +65,48 @@ module_param_named(panicblink, i8042_blink_frequency, uint, 0600); MODULE_PARM_DESC(panicblink, "Frequency with which keyboard LEDs should blink when kernel panics"); #ifdef CONFIG_X86 -static unsigned int i8042_dritek; +static bool i8042_dritek; module_param_named(dritek, i8042_dritek, bool, 0); MODULE_PARM_DESC(dritek, "Force enable the Dritek keyboard extension"); #endif #ifdef CONFIG_PNP -static int i8042_nopnp; +static bool i8042_nopnp; module_param_named(nopnp, i8042_nopnp, bool, 0); MODULE_PARM_DESC(nopnp, "Do not use PNP to detect controller settings"); #endif #define DEBUG #ifdef DEBUG -static int i8042_debug; +static bool i8042_debug; module_param_named(debug, i8042_debug, bool, 0600); MODULE_PARM_DESC(debug, "Turn i8042 debugging mode on and off"); #endif +static bool i8042_bypass_aux_irq_test; + #include "i8042.h" +/* + * i8042_lock protects serialization between i8042_command and + * the interrupt handler. + */ static DEFINE_SPINLOCK(i8042_lock); +/* + * Writers to AUX and KBD ports as well as users issuing i8042_command + * directly should acquire i8042_mutex (by means of calling + * i8042_lock_chip() and i8042_unlock_ship() helpers) to ensure that + * they do not disturb each other (unfortunately in many i8042 + * implementations write to one of the ports will immediately abort + * command that is being processed by another port). + */ +static DEFINE_MUTEX(i8042_mutex); + struct i8042_port { struct serio *serio; int irq; - unsigned char exists; + bool exists; signed char mux; }; @@ -103,14 +119,26 @@ static struct i8042_port i8042_ports[I8042_NUM_PORTS]; static unsigned char i8042_initial_ctr; static unsigned char i8042_ctr; -static unsigned char i8042_mux_present; -static unsigned char i8042_kbd_irq_registered; -static unsigned char i8042_aux_irq_registered; +static bool i8042_mux_present; +static bool i8042_kbd_irq_registered; +static bool i8042_aux_irq_registered; static unsigned char i8042_suppress_kbd_ack; static struct platform_device *i8042_platform_device; static irqreturn_t i8042_interrupt(int irq, void *dev_id); +void i8042_lock_chip(void) +{ + mutex_lock(&i8042_mutex); +} +EXPORT_SYMBOL(i8042_lock_chip); + +void i8042_unlock_chip(void) +{ + mutex_unlock(&i8042_mutex); +} +EXPORT_SYMBOL(i8042_unlock_chip); + /* * The i8042_wait_read() and i8042_wait_write functions wait for the i8042 to * be ready for reading values from it / writing values to it. @@ -262,6 +290,49 @@ static int i8042_aux_write(struct serio *serio, unsigned char c) I8042_CMD_MUX_SEND + port->mux); } + +/* + * i8042_aux_close attempts to clear AUX or KBD port state by disabling + * and then re-enabling it. + */ + +static void i8042_port_close(struct serio *serio) +{ + int irq_bit; + int disable_bit; + const char *port_name; + + if (serio == i8042_ports[I8042_AUX_PORT_NO].serio) { + irq_bit = I8042_CTR_AUXINT; + disable_bit = I8042_CTR_AUXDIS; + port_name = "AUX"; + } else { + irq_bit = I8042_CTR_KBDINT; + disable_bit = I8042_CTR_KBDDIS; + port_name = "KBD"; + } + + i8042_ctr &= ~irq_bit; + if (i8042_command(&i8042_ctr, I8042_CMD_CTL_WCTR)) + printk(KERN_WARNING + "i8042.c: Can't write CTR while closing %s port.\n", + port_name); + + udelay(50); + + i8042_ctr &= ~disable_bit; + i8042_ctr |= irq_bit; + if (i8042_command(&i8042_ctr, I8042_CMD_CTL_WCTR)) + printk(KERN_ERR "i8042.c: Can't reactivate %s port.\n", + port_name); + + /* + * See if there is any data appeared while we were messing with + * port state. + */ + i8042_interrupt(0, NULL); +} + /* * i8042_start() is called by serio core when port is about to finish * registering. It will mark port as existing so i8042_interrupt can @@ -271,7 +342,7 @@ static int i8042_start(struct serio *serio) { struct i8042_port *port = serio->port_data; - port->exists = 1; + port->exists = true; mb(); return 0; } @@ -285,7 +356,7 @@ static void i8042_stop(struct serio *serio) { struct i8042_port *port = serio->port_data; - port->exists = 0; + port->exists = false; /* * We synchronize with both AUX and KBD IRQs because there is @@ -391,7 +462,7 @@ static irqreturn_t i8042_interrupt(int irq, void *dev_id) } /* - * i8042_enable_kbd_port enables keybaord port on chip + * i8042_enable_kbd_port enables keyboard port on chip */ static int i8042_enable_kbd_port(void) @@ -447,14 +518,15 @@ static int i8042_enable_mux_ports(void) } /* - * i8042_set_mux_mode checks whether the controller has an active - * multiplexor and puts the chip into Multiplexed (1) or Legacy (0) mode. + * i8042_set_mux_mode checks whether the controller has an + * active multiplexor and puts the chip into Multiplexed (true) + * or Legacy (false) mode. */ -static int i8042_set_mux_mode(unsigned int mode, unsigned char *mux_version) +static int i8042_set_mux_mode(bool multiplex, unsigned char *mux_version) { - unsigned char param; + unsigned char param, val; /* * Get rid of bytes in the queue. */ @@ -466,14 +538,21 @@ static int i8042_set_mux_mode(unsigned int mode, unsigned char *mux_version) * mouse interface, the last should be version. */ - param = 0xf0; - if (i8042_command(¶m, I8042_CMD_AUX_LOOP) || param != 0xf0) + param = val = 0xf0; + if (i8042_command(¶m, I8042_CMD_AUX_LOOP) || param != val) return -1; - param = mode ? 0x56 : 0xf6; - if (i8042_command(¶m, I8042_CMD_AUX_LOOP) || param != (mode ? 0x56 : 0xf6)) + param = val = multiplex ? 0x56 : 0xf6; + if (i8042_command(¶m, I8042_CMD_AUX_LOOP) || param != val) return -1; - param = mode ? 0xa4 : 0xa5; - if (i8042_command(¶m, I8042_CMD_AUX_LOOP) || param == (mode ? 0xa4 : 0xa5)) + param = val = multiplex ? 0xa4 : 0xa5; + if (i8042_command(¶m, I8042_CMD_AUX_LOOP) || param == val) + return -1; + +/* + * Workaround for interference with USB Legacy emulation + * that causes a v10.12 MUX to be found. + */ + if (param == 0xac) return -1; if (mux_version) @@ -488,18 +567,11 @@ static int i8042_set_mux_mode(unsigned int mode, unsigned char *mux_version) * LCS/Telegraphics. */ -static int __devinit i8042_check_mux(void) +static int __init i8042_check_mux(void) { unsigned char mux_version; - if (i8042_set_mux_mode(1, &mux_version)) - return -1; - -/* - * Workaround for interference with USB Legacy emulation - * that causes a v10.12 MUX to be found. - */ - if (mux_version == 0xAC) + if (i8042_set_mux_mode(true, &mux_version)) return -1; printk(KERN_INFO "i8042.c: Detected active multiplexing controller, rev %d.%d.\n", @@ -516,7 +588,7 @@ static int __devinit i8042_check_mux(void) return -EIO; } - i8042_mux_present = 1; + i8042_mux_present = true; return 0; } @@ -524,10 +596,10 @@ static int __devinit i8042_check_mux(void) /* * The following is used to test AUX IRQ delivery. */ -static struct completion i8042_aux_irq_delivered __devinitdata; -static int i8042_irq_being_tested __devinitdata; +static struct completion i8042_aux_irq_delivered __initdata; +static bool i8042_irq_being_tested __initdata; -static irqreturn_t __devinit i8042_aux_test_irq(int irq, void *dev_id) +static irqreturn_t __init i8042_aux_test_irq(int irq, void *dev_id) { unsigned long flags; unsigned char str, data; @@ -537,6 +609,8 @@ static irqreturn_t __devinit i8042_aux_test_irq(int irq, void *dev_id) str = i8042_read_status(); if (str & I8042_STR_OBF) { data = i8042_read_data(); + dbg("%02x <- i8042 (aux_test_irq, %s)", + data, str & I8042_STR_AUXDATA ? "aux" : "kbd"); if (i8042_irq_being_tested && data == 0xa5 && (str & I8042_STR_AUXDATA)) complete(&i8042_aux_irq_delivered); @@ -552,7 +626,7 @@ static irqreturn_t __devinit i8042_aux_test_irq(int irq, void *dev_id) * verifies success by readinng CTR. Used when testing for presence of AUX * port. */ -static int __devinit i8042_toggle_aux(int on) +static int __init i8042_toggle_aux(bool on) { unsigned char param; int i; @@ -580,11 +654,11 @@ static int __devinit i8042_toggle_aux(int on) * the presence of an AUX interface. */ -static int __devinit i8042_check_aux(void) +static int __init i8042_check_aux(void) { int retval = -1; - int irq_registered = 0; - int aux_loop_broken = 0; + bool irq_registered = false; + bool aux_loop_broken = false; unsigned long flags; unsigned char param; @@ -621,19 +695,19 @@ static int __devinit i8042_check_aux(void) * mark it as broken */ if (!retval) - aux_loop_broken = 1; + aux_loop_broken = true; } /* * Bit assignment test - filters out PS/2 i8042's in AT mode */ - if (i8042_toggle_aux(0)) { + if (i8042_toggle_aux(false)) { printk(KERN_WARNING "Failed to disable AUX port, but continuing anyway... Is this a SiS?\n"); printk(KERN_WARNING "If AUX port is really absent please use the 'i8042.noaux' option.\n"); } - if (i8042_toggle_aux(1)) + if (i8042_toggle_aux(true)) return -1; /* @@ -641,7 +715,7 @@ static int __devinit i8042_check_aux(void) * used it for a PCI card or somethig else. */ - if (i8042_noloop || aux_loop_broken) { + if (i8042_noloop || i8042_bypass_aux_irq_test || aux_loop_broken) { /* * Without LOOP command we can't test AUX IRQ delivery. Assume the port * is working and hope we are right. @@ -654,7 +728,7 @@ static int __devinit i8042_check_aux(void) "i8042", i8042_platform_device)) goto out; - irq_registered = 1; + irq_registered = true; if (i8042_enable_aux_port()) goto out; @@ -662,7 +736,7 @@ static int __devinit i8042_check_aux(void) spin_lock_irqsave(&i8042_lock, flags); init_completion(&i8042_aux_irq_delivered); - i8042_irq_being_tested = 1; + i8042_irq_being_tested = true; param = 0xa5; retval = __i8042_command(¶m, I8042_CMD_AUX_LOOP & 0xf0ff); @@ -678,6 +752,7 @@ static int __devinit i8042_check_aux(void) * AUX IRQ was never delivered so we need to flush the controller to * get rid of the byte we put there; otherwise keyboard may not work. */ + dbg(" -- i8042 (aux irq test timeout)"); i8042_flush(); retval = -1; } @@ -761,17 +836,32 @@ static int i8042_controller_selftest(void) static int i8042_controller_init(void) { unsigned long flags; + int n = 0; + unsigned char ctr[2]; /* - * Save the CTR for restoral on unload / reboot. + * Save the CTR for restore on unload / reboot. */ - if (i8042_command(&i8042_ctr, I8042_CMD_CTL_RCTR)) { - printk(KERN_ERR "i8042.c: Can't read CTR while initializing i8042.\n"); - return -EIO; - } + do { + if (n >= 10) { + printk(KERN_ERR + "i8042.c: Unable to get stable CTR read.\n"); + return -EIO; + } + + if (n != 0) + udelay(50); + + if (i8042_command(&ctr[n++ % 2], I8042_CMD_CTL_RCTR)) { + printk(KERN_ERR + "i8042.c: Can't read CTR while initializing i8042.\n"); + return -EIO; + } - i8042_initial_ctr = i8042_ctr; + } while (n < 2 || ctr[0] != ctr[1]); + + i8042_initial_ctr = i8042_ctr = ctr[0]; /* * Disable the keyboard interface and interrupt. @@ -799,7 +889,7 @@ static int i8042_controller_init(void) */ if (~i8042_ctr & I8042_CTR_XLATE) - i8042_direct = 1; + i8042_direct = true; /* * Set nontranslated mode for the kbd interface if requested by an option. @@ -820,6 +910,12 @@ static int i8042_controller_init(void) return -EIO; } +/* + * Flush whatever accumulated while we were disabling keyboard port. + */ + + i8042_flush(); + return 0; } @@ -839,12 +935,15 @@ static void i8042_controller_reset(void) i8042_ctr |= I8042_CTR_KBDDIS | I8042_CTR_AUXDIS; i8042_ctr &= ~(I8042_CTR_KBDINT | I8042_CTR_AUXINT); + if (i8042_command(&i8042_ctr, I8042_CMD_CTL_WCTR)) + printk(KERN_WARNING "i8042.c: Can't write CTR while resetting.\n"); + /* * Disable MUX mode if present. */ if (i8042_mux_present) - i8042_set_mux_mode(0, NULL); + i8042_set_mux_mode(false, NULL); /* * Reset the controller if requested. @@ -923,41 +1022,27 @@ static void i8042_dritek_enable(void) #ifdef CONFIG_PM -static bool i8042_suspended; - /* - * Here we try to restore the original BIOS settings. We only want to - * do that once, when we really suspend, not when we taking memory - * snapshot for swsusp (in this case we'll perform required cleanup - * as part of shutdown process). + * Here we try to restore the original BIOS settings to avoid + * upsetting it. */ -static int i8042_suspend(struct platform_device *dev, pm_message_t state) +static int i8042_pm_reset(struct device *dev) { - if (!i8042_suspended && state.event == PM_EVENT_SUSPEND) - i8042_controller_reset(); - - i8042_suspended = state.event == PM_EVENT_SUSPEND || - state.event == PM_EVENT_FREEZE; + i8042_controller_reset(); return 0; } - /* - * Here we try to reset everything back to a state in which suspended + * Here we try to reset everything back to a state we had + * before suspending. */ -static int i8042_resume(struct platform_device *dev) +static int i8042_pm_restore(struct device *dev) { int error; -/* - * Do not bother with restoring state if we haven't suspened yet - */ - if (!i8042_suspended) - return 0; - error = i8042_controller_check(); if (error) return error; @@ -991,7 +1076,7 @@ static int i8042_resume(struct platform_device *dev) #endif if (i8042_mux_present) { - if (i8042_set_mux_mode(1, NULL) || i8042_enable_mux_ports()) + if (i8042_set_mux_mode(true, NULL) || i8042_enable_mux_ports()) printk(KERN_WARNING "i8042: failed to resume active multiplexor, " "mouse won't work.\n"); @@ -1001,11 +1086,18 @@ static int i8042_resume(struct platform_device *dev) if (i8042_ports[I8042_KBD_PORT_NO].serio) i8042_enable_kbd_port(); - i8042_suspended = false; i8042_interrupt(0, NULL); return 0; } + +static const struct dev_pm_ops i8042_pm_ops = { + .suspend = i8042_pm_reset, + .resume = i8042_pm_restore, + .poweroff = i8042_pm_reset, + .restore = i8042_pm_restore, +}; + #endif /* CONFIG_PM */ /* @@ -1018,7 +1110,7 @@ static void i8042_shutdown(struct platform_device *dev) i8042_controller_reset(); } -static int __devinit i8042_create_kbd_port(void) +static int __init i8042_create_kbd_port(void) { struct serio *serio; struct i8042_port *port = &i8042_ports[I8042_KBD_PORT_NO]; @@ -1031,6 +1123,7 @@ static int __devinit i8042_create_kbd_port(void) serio->write = i8042_dumbkbd ? NULL : i8042_kbd_write; serio->start = i8042_start; serio->stop = i8042_stop; + serio->close = i8042_port_close; serio->port_data = port; serio->dev.parent = &i8042_platform_device->dev; strlcpy(serio->name, "i8042 KBD port", sizeof(serio->name)); @@ -1042,7 +1135,7 @@ static int __devinit i8042_create_kbd_port(void) return 0; } -static int __devinit i8042_create_aux_port(int idx) +static int __init i8042_create_aux_port(int idx) { struct serio *serio; int port_no = idx < 0 ? I8042_AUX_PORT_NO : I8042_MUX_PORT_NO + idx; @@ -1061,6 +1154,7 @@ static int __devinit i8042_create_aux_port(int idx) if (idx < 0) { strlcpy(serio->name, "i8042 AUX port", sizeof(serio->name)); strlcpy(serio->phys, I8042_AUX_PHYS_DESC, sizeof(serio->phys)); + serio->close = i8042_port_close; } else { snprintf(serio->name, sizeof(serio->name), "i8042 AUX%d port", idx); snprintf(serio->phys, sizeof(serio->phys), I8042_MUX_PHYS_DESC, idx + 1); @@ -1073,13 +1167,13 @@ static int __devinit i8042_create_aux_port(int idx) return 0; } -static void __devinit i8042_free_kbd_port(void) +static void __init i8042_free_kbd_port(void) { kfree(i8042_ports[I8042_KBD_PORT_NO].serio); i8042_ports[I8042_KBD_PORT_NO].serio = NULL; } -static void __devinit i8042_free_aux_ports(void) +static void __init i8042_free_aux_ports(void) { int i; @@ -1089,7 +1183,7 @@ static void __devinit i8042_free_aux_ports(void) } } -static void __devinit i8042_register_ports(void) +static void __init i8042_register_ports(void) { int i; @@ -1117,6 +1211,21 @@ static void __devexit i8042_unregister_ports(void) } } +/* + * Checks whether port belongs to i8042 controller. + */ +bool i8042_check_port_owner(const struct serio *port) +{ + int i; + + for (i = 0; i < I8042_NUM_PORTS; i++) + if (i8042_ports[i].serio == port) + return true; + + return false; +} +EXPORT_SYMBOL(i8042_check_port_owner); + static void i8042_free_irqs(void) { if (i8042_aux_irq_registered) @@ -1124,10 +1233,10 @@ static void i8042_free_irqs(void) if (i8042_kbd_irq_registered) free_irq(I8042_KBD_IRQ, i8042_platform_device); - i8042_aux_irq_registered = i8042_kbd_irq_registered = 0; + i8042_aux_irq_registered = i8042_kbd_irq_registered = false; } -static int __devinit i8042_setup_aux(void) +static int __init i8042_setup_aux(void) { int (*aux_enable)(void); int error; @@ -1158,7 +1267,7 @@ static int __devinit i8042_setup_aux(void) if (aux_enable()) goto err_free_irq; - i8042_aux_irq_registered = 1; + i8042_aux_irq_registered = true; return 0; err_free_irq: @@ -1168,7 +1277,7 @@ static int __devinit i8042_setup_aux(void) return error; } -static int __devinit i8042_setup_kbd(void) +static int __init i8042_setup_kbd(void) { int error; @@ -1185,7 +1294,7 @@ static int __devinit i8042_setup_kbd(void) if (error) goto err_free_irq; - i8042_kbd_irq_registered = 1; + i8042_kbd_irq_registered = true; return 0; err_free_irq: @@ -1195,7 +1304,7 @@ static int __devinit i8042_setup_kbd(void) return error; } -static int __devinit i8042_probe(struct platform_device *dev) +static int __init i8042_probe(struct platform_device *dev) { int error; @@ -1251,14 +1360,12 @@ static struct platform_driver i8042_driver = { .driver = { .name = "i8042", .owner = THIS_MODULE, +#ifdef CONFIG_PM + .pm = &i8042_pm_ops, +#endif }, - .probe = i8042_probe, .remove = __devexit_p(i8042_remove), .shutdown = i8042_shutdown, -#ifdef CONFIG_PM - .suspend = i8042_suspend, - .resume = i8042_resume, -#endif }; static int __init i8042_init(void) @@ -1275,28 +1382,28 @@ static int __init i8042_init(void) if (err) goto err_platform_exit; - err = platform_driver_register(&i8042_driver); - if (err) - goto err_platform_exit; - i8042_platform_device = platform_device_alloc("i8042", -1); if (!i8042_platform_device) { err = -ENOMEM; - goto err_unregister_driver; + goto err_platform_exit; } err = platform_device_add(i8042_platform_device); if (err) goto err_free_device; + err = platform_driver_probe(&i8042_driver, i8042_probe); + if (err) + goto err_del_device; + panic_blink = i8042_panic_blink; return 0; + err_del_device: + platform_device_del(i8042_platform_device); err_free_device: platform_device_put(i8042_platform_device); - err_unregister_driver: - platform_driver_unregister(&i8042_driver); err_platform_exit: i8042_platform_exit(); @@ -1305,8 +1412,8 @@ static int __init i8042_init(void) static void __exit i8042_exit(void) { - platform_device_unregister(i8042_platform_device); platform_driver_unregister(&i8042_driver); + platform_device_unregister(i8042_platform_device); i8042_platform_exit(); panic_blink = NULL; diff --git a/drivers/input/serio/libps2.c b/drivers/input/serio/libps2.c index be5bbbb8ae4..f3876acc3e8 100644 --- a/drivers/input/serio/libps2.c +++ b/drivers/input/serio/libps2.c @@ -13,10 +13,12 @@ #include <linux/delay.h> #include <linux/module.h> +#include <linux/sched.h> #include <linux/slab.h> #include <linux/interrupt.h> #include <linux/input.h> #include <linux/serio.h> +#include <linux/i8042.h> #include <linux/init.h> #include <linux/libps2.h> @@ -54,6 +56,24 @@ int ps2_sendbyte(struct ps2dev *ps2dev, unsigned char byte, int timeout) } EXPORT_SYMBOL(ps2_sendbyte); +void ps2_begin_command(struct ps2dev *ps2dev) +{ + mutex_lock(&ps2dev->cmd_mutex); + + if (i8042_check_port_owner(ps2dev->serio)) + i8042_lock_chip(); +} +EXPORT_SYMBOL(ps2_begin_command); + +void ps2_end_command(struct ps2dev *ps2dev) +{ + if (i8042_check_port_owner(ps2dev->serio)) + i8042_unlock_chip(); + + mutex_unlock(&ps2dev->cmd_mutex); +} +EXPORT_SYMBOL(ps2_end_command); + /* * ps2_drain() waits for device to transmit requested number of bytes * and discards them. @@ -66,7 +86,7 @@ void ps2_drain(struct ps2dev *ps2dev, int maxbytes, int timeout) maxbytes = sizeof(ps2dev->cmdbuf); } - mutex_lock(&ps2dev->cmd_mutex); + ps2_begin_command(ps2dev); serio_pause_rx(ps2dev->serio); ps2dev->flags = PS2_FLAG_CMD; @@ -76,7 +96,8 @@ void ps2_drain(struct ps2dev *ps2dev, int maxbytes, int timeout) wait_event_timeout(ps2dev->wait, !(ps2dev->flags & PS2_FLAG_CMD), msecs_to_jiffies(timeout)); - mutex_unlock(&ps2dev->cmd_mutex); + + ps2_end_command(ps2dev); } EXPORT_SYMBOL(ps2_drain); @@ -161,7 +182,7 @@ static int ps2_adjust_timeout(struct ps2dev *ps2dev, int command, int timeout) * ps2_command() can only be called from a process context */ -int ps2_command(struct ps2dev *ps2dev, unsigned char *param, int command) +int __ps2_command(struct ps2dev *ps2dev, unsigned char *param, int command) { int timeout; int send = (command >> 12) & 0xf; @@ -179,8 +200,6 @@ int ps2_command(struct ps2dev *ps2dev, unsigned char *param, int command) return -1; } - mutex_lock(&ps2dev->cmd_mutex); - serio_pause_rx(ps2dev->serio); ps2dev->flags = command == PS2_CMD_GETID ? PS2_FLAG_WAITID : 0; ps2dev->cmdcnt = receive; @@ -231,7 +250,18 @@ int ps2_command(struct ps2dev *ps2dev, unsigned char *param, int command) ps2dev->flags = 0; serio_continue_rx(ps2dev->serio); - mutex_unlock(&ps2dev->cmd_mutex); + return rc; +} +EXPORT_SYMBOL(__ps2_command); + +int ps2_command(struct ps2dev *ps2dev, unsigned char *param, int command) +{ + int rc; + + ps2_begin_command(ps2dev); + rc = __ps2_command(ps2dev, param, command); + ps2_end_command(ps2dev); + return rc; } EXPORT_SYMBOL(ps2_command); diff --git a/drivers/input/serio/serio.c b/drivers/input/serio/serio.c index d66f4944f2a..0236f0d5fd9 100644 --- a/drivers/input/serio/serio.c +++ b/drivers/input/serio/serio.c @@ -931,15 +931,11 @@ static int serio_uevent(struct device *dev, struct kobj_uevent_env *env) #endif /* CONFIG_HOTPLUG */ #ifdef CONFIG_PM -static int serio_suspend(struct device *dev, pm_message_t state) +static int serio_suspend(struct device *dev) { struct serio *serio = to_serio_port(dev); - if (!serio->suspended && state.event == PM_EVENT_SUSPEND) - serio_cleanup(serio); - - serio->suspended = state.event == PM_EVENT_SUSPEND || - state.event == PM_EVENT_FREEZE; + serio_cleanup(serio); return 0; } @@ -952,13 +948,17 @@ static int serio_resume(struct device *dev) * Driver reconnect can take a while, so better let kseriod * deal with it. */ - if (serio->suspended) { - serio->suspended = false; - serio_queue_event(serio, NULL, SERIO_RECONNECT_PORT); - } + serio_queue_event(serio, NULL, SERIO_RECONNECT_PORT); return 0; } + +static const struct dev_pm_ops serio_pm_ops = { + .suspend = serio_suspend, + .resume = serio_resume, + .poweroff = serio_suspend, + .restore = serio_resume, +}; #endif /* CONFIG_PM */ /* called from serio_driver->connect/disconnect methods under serio_mutex */ @@ -1015,8 +1015,7 @@ static struct bus_type serio_bus = { .remove = serio_driver_remove, .shutdown = serio_shutdown, #ifdef CONFIG_PM - .suspend = serio_suspend, - .resume = serio_resume, + .pm = &serio_pm_ops, #endif }; diff --git a/drivers/input/serio/serio_raw.c b/drivers/input/serio/serio_raw.c index b03009bb746..27fdaaffbb4 100644 --- a/drivers/input/serio/serio_raw.c +++ b/drivers/input/serio/serio_raw.c @@ -9,6 +9,7 @@ * the Free Software Foundation. */ +#include <linux/sched.h> #include <linux/slab.h> #include <linux/smp_lock.h> #include <linux/poll.h> diff --git a/drivers/input/serio/serport.c b/drivers/input/serio/serport.c index b9694b6445d..6d345112bcb 100644 --- a/drivers/input/serio/serport.c +++ b/drivers/input/serio/serport.c @@ -15,6 +15,7 @@ #include <asm/uaccess.h> #include <linux/kernel.h> +#include <linux/sched.h> #include <linux/slab.h> #include <linux/module.h> #include <linux/init.h> diff --git a/drivers/input/sparse-keymap.c b/drivers/input/sparse-keymap.c new file mode 100644 index 00000000000..fbd3987af57 --- /dev/null +++ b/drivers/input/sparse-keymap.c @@ -0,0 +1,250 @@ +/* + * Generic support for sparse keymaps + * + * Copyright (c) 2009 Dmitry Torokhov + * + * Derived from wistron button driver: + * Copyright (C) 2005 Miloslav Trmac <mitr@volny.cz> + * Copyright (C) 2005 Bernhard Rosenkraenzer <bero@arklinux.org> + * Copyright (C) 2005 Dmitry Torokhov <dtor@mail.ru> + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 as published by + * the Free Software Foundation. + */ + +#include <linux/input.h> +#include <linux/input/sparse-keymap.h> + +MODULE_AUTHOR("Dmitry Torokhov <dtor@mail.ru>"); +MODULE_DESCRIPTION("Generic support for sparse keymaps"); +MODULE_LICENSE("GPL v2"); +MODULE_VERSION("0.1"); + +/** + * sparse_keymap_entry_from_scancode - perform sparse keymap lookup + * @dev: Input device using sparse keymap + * @code: Scan code + * + * This function is used to perform &struct key_entry lookup in an + * input device using sparse keymap. + */ +struct key_entry *sparse_keymap_entry_from_scancode(struct input_dev *dev, + unsigned int code) +{ + struct key_entry *key; + + for (key = dev->keycode; key->type != KE_END; key++) + if (code == key->code) + return key; + + return NULL; +} +EXPORT_SYMBOL(sparse_keymap_entry_from_scancode); + +/** + * sparse_keymap_entry_from_keycode - perform sparse keymap lookup + * @dev: Input device using sparse keymap + * @keycode: Key code + * + * This function is used to perform &struct key_entry lookup in an + * input device using sparse keymap. + */ +struct key_entry *sparse_keymap_entry_from_keycode(struct input_dev *dev, + unsigned int keycode) +{ + struct key_entry *key; + + for (key = dev->keycode; key->type != KE_END; key++) + if (key->type == KE_KEY && keycode == key->keycode) + return key; + + return NULL; +} +EXPORT_SYMBOL(sparse_keymap_entry_from_keycode); + +static int sparse_keymap_getkeycode(struct input_dev *dev, + int scancode, int *keycode) +{ + const struct key_entry *key = + sparse_keymap_entry_from_scancode(dev, scancode); + + if (key && key->type == KE_KEY) { + *keycode = key->keycode; + return 0; + } + + return -EINVAL; +} + +static int sparse_keymap_setkeycode(struct input_dev *dev, + int scancode, int keycode) +{ + struct key_entry *key; + int old_keycode; + + if (keycode < 0 || keycode > KEY_MAX) + return -EINVAL; + + key = sparse_keymap_entry_from_scancode(dev, scancode); + if (key && key->type == KE_KEY) { + old_keycode = key->keycode; + key->keycode = keycode; + set_bit(keycode, dev->keybit); + if (!sparse_keymap_entry_from_keycode(dev, old_keycode)) + clear_bit(old_keycode, dev->keybit); + return 0; + } + + return -EINVAL; +} + +/** + * sparse_keymap_setup - set up sparse keymap for an input device + * @dev: Input device + * @keymap: Keymap in form of array of &key_entry structures ending + * with %KE_END type entry + * @setup: Function that can be used to adjust keymap entries + * depending on device's deeds, may be %NULL + * + * The function calculates size and allocates copy of the original + * keymap after which sets up input device event bits appropriately. + * Before destroying input device allocated keymap should be freed + * with a call to sparse_keymap_free(). + */ +int sparse_keymap_setup(struct input_dev *dev, + const struct key_entry *keymap, + int (*setup)(struct input_dev *, struct key_entry *)) +{ + size_t map_size = 1; /* to account for the last KE_END entry */ + const struct key_entry *e; + struct key_entry *map, *entry; + int i; + int error; + + for (e = keymap; e->type != KE_END; e++) + map_size++; + + map = kcalloc(map_size, sizeof (struct key_entry), GFP_KERNEL); + if (!map) + return -ENOMEM; + + memcpy(map, keymap, map_size * sizeof (struct key_entry)); + + for (i = 0; i < map_size; i++) { + entry = &map[i]; + + if (setup) { + error = setup(dev, entry); + if (error) + goto err_out; + } + + switch (entry->type) { + case KE_KEY: + __set_bit(EV_KEY, dev->evbit); + __set_bit(entry->keycode, dev->keybit); + break; + + case KE_SW: + __set_bit(EV_SW, dev->evbit); + __set_bit(entry->sw.code, dev->swbit); + break; + } + } + + dev->keycode = map; + dev->keycodemax = map_size; + dev->getkeycode = sparse_keymap_getkeycode; + dev->setkeycode = sparse_keymap_setkeycode; + + return 0; + + err_out: + kfree(keymap); + return error; + +} +EXPORT_SYMBOL(sparse_keymap_setup); + +/** + * sparse_keymap_free - free memory allocated for sparse keymap + * @dev: Input device using sparse keymap + * + * This function is used to free memory allocated by sparse keymap + * in an input device that was set up by sparse_keymap_setup(). + */ +void sparse_keymap_free(struct input_dev *dev) +{ + kfree(dev->keycode); + dev->keycode = NULL; + dev->keycodemax = 0; + dev->getkeycode = NULL; + dev->setkeycode = NULL; +} +EXPORT_SYMBOL(sparse_keymap_free); + +/** + * sparse_keymap_report_entry - report event corresponding to given key entry + * @dev: Input device for which event should be reported + * @ke: key entry describing event + * @value: Value that should be reported (ignored by %KE_SW entries) + * @autorelease: Signals whether release event should be emitted for %KE_KEY + * entries right after reporting press event, ignored by all other + * entries + * + * This function is used to report input event described by given + * &struct key_entry. + */ +void sparse_keymap_report_entry(struct input_dev *dev, const struct key_entry *ke, + unsigned int value, bool autorelease) +{ + switch (ke->type) { + case KE_KEY: + input_report_key(dev, ke->keycode, value); + input_sync(dev); + if (value && autorelease) { + input_report_key(dev, ke->keycode, 0); + input_sync(dev); + } + break; + + case KE_SW: + value = ke->sw.value; + /* fall through */ + + case KE_VSW: + input_report_switch(dev, ke->sw.code, value); + break; + } +} +EXPORT_SYMBOL(sparse_keymap_report_entry); + +/** + * sparse_keymap_report_event - report event corresponding to given scancode + * @dev: Input device using sparse keymap + * @code: Scan code + * @value: Value that should be reported (ignored by %KE_SW entries) + * @autorelease: Signals whether release event should be emitted for %KE_KEY + * entries right after reporting press event, ignored by all other + * entries + * + * This function is used to perform lookup in an input device using sparse + * keymap and report corresponding event. Returns %true if lookup was + * successful and %false otherwise. + */ +bool sparse_keymap_report_event(struct input_dev *dev, unsigned int code, + unsigned int value, bool autorelease) +{ + const struct key_entry *ke = + sparse_keymap_entry_from_scancode(dev, code); + + if (ke) { + sparse_keymap_report_entry(dev, ke, value, autorelease); + return true; + } + + return false; +} +EXPORT_SYMBOL(sparse_keymap_report_event); + diff --git a/drivers/input/touchscreen/Kconfig b/drivers/input/touchscreen/Kconfig index 72e2712c7e2..32fc8ba039a 100644 --- a/drivers/input/touchscreen/Kconfig +++ b/drivers/input/touchscreen/Kconfig @@ -48,8 +48,8 @@ config TOUCHSCREEN_AD7879_I2C select TOUCHSCREEN_AD7879 help Say Y here if you have a touchscreen interface using the - AD7879-1 controller, and your board-specific initialization - code includes that in its table of I2C devices. + AD7879-1/AD7889-1 controller, and your board-specific + initialization code includes that in its table of I2C devices. If unsure, say N (but it's safe to say "Y"). @@ -62,7 +62,7 @@ config TOUCHSCREEN_AD7879_SPI select TOUCHSCREEN_AD7879 help Say Y here if you have a touchscreen interface using the - AD7879 controller, and your board-specific initialization + AD7879/AD7889 controller, and your board-specific initialization code includes that in its table of SPI devices. If unsure, say N (but it's safe to say "Y"). @@ -111,6 +111,18 @@ config TOUCHSCREEN_DA9034 Say Y here to enable the support for the touchscreen found on Dialog Semiconductor DA9034 PMIC. +config TOUCHSCREEN_DYNAPRO + tristate "Dynapro serial touchscreen" + select SERIO + help + Say Y here if you have a Dynapro serial touchscreen connected to + your system. + + If unsure, say N. + + To compile this driver as a module, choose M here: the + module will be called dynapro. + config TOUCHSCREEN_EETI tristate "EETI touchscreen panel support" depends on I2C @@ -133,6 +145,18 @@ config TOUCHSCREEN_FUJITSU To compile this driver as a module, choose M here: the module will be called fujitsu-ts. +config TOUCHSCREEN_S3C2410 + tristate "Samsung S3C2410 touchscreen input driver" + depends on ARCH_S3C2410 + select S3C24XX_ADC + help + Say Y here if you have the s3c2410 touchscreen. + + If unsure, say N. + + To compile this driver as a module, choose M here: the + module will be called s3c2410_ts. + config TOUCHSCREEN_GUNZE tristate "Gunze AHL-51S touchscreen" select SERIO @@ -169,6 +193,17 @@ config TOUCHSCREEN_WACOM_W8001 To compile this driver as a module, choose M here: the module will be called wacom_w8001. +config TOUCHSCREEN_MCS5000 + tristate "MELFAS MCS-5000 touchscreen" + depends on I2C + help + Say Y here if you have the MELFAS MCS-5000 touchscreen controller + chip in your system. + + If unsure, say N. + + To compile this driver as a module, choose M here: the + module will be called mcs5000_ts. config TOUCHSCREEN_MTOUCH tristate "MicroTouch serial touchscreens" @@ -286,7 +321,7 @@ config TOUCHSCREEN_TOUCHWIN config TOUCHSCREEN_ATMEL_TSADCC tristate "Atmel Touchscreen Interface" - depends on ARCH_AT91SAM9RL + depends on ARCH_AT91SAM9RL || ARCH_AT91SAM9G45 help Say Y here if you have a 4-wire touchscreen connected to the ADC Controller on your Atmel SoC (such as the AT91SAM9RL). @@ -366,11 +401,11 @@ config TOUCHSCREEN_WM97XX_ATMEL be called atmel-wm97xx. config TOUCHSCREEN_WM97XX_MAINSTONE - tristate "WM97xx Mainstone accelerated touch" + tristate "WM97xx Mainstone/Palm accelerated touch" depends on TOUCHSCREEN_WM97XX && ARCH_PXA help Say Y here for support for streaming mode with WM97xx touchscreens - on Mainstone systems. + on Mainstone, Palm Tungsten T5, TX and LifeDrive systems. If unsure, say N. @@ -406,6 +441,8 @@ config TOUCHSCREEN_USB_COMPOSITE - IRTOUCHSYSTEMS/UNITOP - IdealTEK URTC1000 - GoTop Super_Q2/GogoPen/PenPower tablets + - JASTEC USB Touch Controller/DigiTech DTR-02U + - Zytronic controllers Have a look at <http://linux.chapter7.ch/touchkit/> for a usage description and the required user-space stuff. @@ -468,6 +505,26 @@ config TOUCHSCREEN_USB_GOTOP bool "GoTop Super_Q2/GogoPen/PenPower tablet device support" if EMBEDDED depends on TOUCHSCREEN_USB_COMPOSITE +config TOUCHSCREEN_USB_JASTEC + default y + bool "JASTEC/DigiTech DTR-02U USB touch controller device support" if EMBEDDED + depends on TOUCHSCREEN_USB_COMPOSITE + +config TOUCHSCREEN_USB_E2I + default y + bool "e2i Touchscreen controller (e.g. from Mimo 740)" + depends on TOUCHSCREEN_USB_COMPOSITE + +config TOUCHSCREEN_USB_ZYTRONIC + default y + bool "Zytronic controller" if EMBEDDED + depends on TOUCHSCREEN_USB_COMPOSITE + +config TOUCHSCREEN_USB_ETT_TC5UH + default y + bool "ET&T TC5UH touchscreen controler support" if EMBEDDED + depends on TOUCHSCREEN_USB_COMPOSITE + config TOUCHSCREEN_TOUCHIT213 tristate "Sahara TouchIT-213 touchscreen" select SERIO @@ -492,10 +549,20 @@ config TOUCHSCREEN_TSC2007 config TOUCHSCREEN_W90X900 tristate "W90P910 touchscreen driver" + depends on HAVE_CLK help Say Y here if you have a W90P910 based touchscreen. To compile this driver as a module, choose M here: the module will be called w90p910_ts. +config TOUCHSCREEN_PCAP + tristate "Motorola PCAP touchscreen" + depends on EZX_PCAP + help + Say Y here if you have a Motorola EZX telephone and + want to enable support for the built-in touchscreen. + + To compile this driver as a module, choose M here: the + module will be called pcap_ts. endif diff --git a/drivers/input/touchscreen/Makefile b/drivers/input/touchscreen/Makefile index 3e1c5e0b952..f1f59c9e121 100644 --- a/drivers/input/touchscreen/Makefile +++ b/drivers/input/touchscreen/Makefile @@ -12,11 +12,13 @@ obj-$(CONFIG_TOUCHSCREEN_ADS7846) += ads7846.o obj-$(CONFIG_TOUCHSCREEN_ATMEL_TSADCC) += atmel_tsadcc.o obj-$(CONFIG_TOUCHSCREEN_BITSY) += h3600_ts_input.o obj-$(CONFIG_TOUCHSCREEN_CORGI) += corgi_ts.o +obj-$(CONFIG_TOUCHSCREEN_DYNAPRO) += dynapro.o obj-$(CONFIG_TOUCHSCREEN_GUNZE) += gunze.o obj-$(CONFIG_TOUCHSCREEN_EETI) += eeti_ts.o obj-$(CONFIG_TOUCHSCREEN_ELO) += elo.o obj-$(CONFIG_TOUCHSCREEN_FUJITSU) += fujitsu_ts.o obj-$(CONFIG_TOUCHSCREEN_INEXIO) += inexio.o +obj-$(CONFIG_TOUCHSCREEN_MCS5000) += mcs5000_ts.o obj-$(CONFIG_TOUCHSCREEN_MIGOR) += migor_ts.o obj-$(CONFIG_TOUCHSCREEN_MTOUCH) += mtouch.o obj-$(CONFIG_TOUCHSCREEN_MK712) += mk712.o @@ -24,7 +26,9 @@ obj-$(CONFIG_TOUCHSCREEN_HP600) += hp680_ts_input.o obj-$(CONFIG_TOUCHSCREEN_HP7XX) += jornada720_ts.o obj-$(CONFIG_TOUCHSCREEN_HTCPEN) += htcpen.o obj-$(CONFIG_TOUCHSCREEN_USB_COMPOSITE) += usbtouchscreen.o +obj-$(CONFIG_TOUCHSCREEN_PCAP) += pcap_ts.o obj-$(CONFIG_TOUCHSCREEN_PENMOUNT) += penmount.o +obj-$(CONFIG_TOUCHSCREEN_S3C2410) += s3c2410_ts.o obj-$(CONFIG_TOUCHSCREEN_TOUCHIT213) += touchit213.o obj-$(CONFIG_TOUCHSCREEN_TOUCHRIGHT) += touchright.o obj-$(CONFIG_TOUCHSCREEN_TOUCHWIN) += touchwin.o diff --git a/drivers/input/touchscreen/ad7877.c b/drivers/input/touchscreen/ad7877.c index ecaeb7e8e75..eb83939c705 100644 --- a/drivers/input/touchscreen/ad7877.c +++ b/drivers/input/touchscreen/ad7877.c @@ -842,3 +842,4 @@ module_exit(ad7877_exit); MODULE_AUTHOR("Michael Hennerich <hennerich@blackfin.uclinux.org>"); MODULE_DESCRIPTION("AD7877 touchscreen Driver"); MODULE_LICENSE("GPL"); +MODULE_ALIAS("spi:ad7877"); diff --git a/drivers/input/touchscreen/ad7879.c b/drivers/input/touchscreen/ad7879.c index 5d8a7039880..c21e6d3a884 100644 --- a/drivers/input/touchscreen/ad7879.c +++ b/drivers/input/touchscreen/ad7879.c @@ -1,7 +1,8 @@ /* - * Copyright (C) 2008 Michael Hennerich, Analog Devices Inc. + * Copyright (C) 2008-2009 Michael Hennerich, Analog Devices Inc. * - * Description: AD7879 based touchscreen, and GPIO driver (I2C/SPI Interface) + * Description: AD7879/AD7889 based touchscreen, and GPIO driver + * (I2C/SPI Interface) * * Bugs: Enter bugs at http://blackfin.uclinux.org/ * @@ -644,7 +645,7 @@ static int __devinit ad7879_probe(struct spi_device *spi) kfree(ts); } - return 0; + return error; } static int __devexit ad7879_remove(struct spi_device *spi) @@ -731,7 +732,7 @@ static int __devinit ad7879_probe(struct i2c_client *client, kfree(ts); } - return 0; + return error; } static int __devexit ad7879_remove(struct i2c_client *client) @@ -747,6 +748,7 @@ static int __devexit ad7879_remove(struct i2c_client *client) static const struct i2c_device_id ad7879_id[] = { { "ad7879", 0 }, + { "ad7889", 0 }, { } }; MODULE_DEVICE_TABLE(i2c, ad7879_id); @@ -779,3 +781,4 @@ module_exit(ad7879_exit); MODULE_AUTHOR("Michael Hennerich <hennerich@blackfin.uclinux.org>"); MODULE_DESCRIPTION("AD7879(-1) touchscreen Driver"); MODULE_LICENSE("GPL"); +MODULE_ALIAS("spi:ad7879"); diff --git a/drivers/input/touchscreen/ads7846.c b/drivers/input/touchscreen/ads7846.c index ba9d38c3f41..52d2ca147d8 100644 --- a/drivers/input/touchscreen/ads7846.c +++ b/drivers/input/touchscreen/ads7846.c @@ -29,10 +29,9 @@ #include <linux/spi/ads7846.h> #include <asm/irq.h> - /* * This code has been heavily tested on a Nokia 770, and lightly - * tested on other ads7846 devices (OSK/Mistral, Lubbock). + * tested on other ads7846 devices (OSK/Mistral, Lubbock, Spitz). * TSC2046 is just newer ads7846 silicon. * Support for ads7843 tested on Atmel at91sam926x-EK. * Support for ads7845 has only been stubbed in. @@ -43,7 +42,7 @@ * have to maintain our own SW IRQ disabled status. This should be * removed as soon as the affected platform's IRQ handling is fixed. * - * app note sbaa036 talks in more detail about accurate sampling... + * App note sbaa036 talks in more detail about accurate sampling... * that ought to help in situations like LCDs inducing noise (which * can also be helped by using synch signals) and more generally. * This driver tries to utilize the measures described in the app @@ -566,10 +565,8 @@ static void ads7846_rx(void *ads) * once more the measurement */ if (packet->tc.ignore || Rt > ts->pressure_max) { -#ifdef VERBOSE - pr_debug("%s: ignored %d pressure %d\n", - dev_name(&ts->spi->dev), packet->tc.ignore, Rt); -#endif + dev_vdbg(&ts->spi->dev, "ignored %d pressure %d\n", + packet->tc.ignore, Rt); hrtimer_start(&ts->timer, ktime_set(0, TS_POLL_PERIOD), HRTIMER_MODE_REL); return; @@ -598,9 +595,7 @@ static void ads7846_rx(void *ads) if (!ts->pendown) { input_report_key(input, BTN_TOUCH, 1); ts->pendown = 1; -#ifdef VERBOSE - dev_dbg(&ts->spi->dev, "DOWN\n"); -#endif + dev_vdbg(&ts->spi->dev, "DOWN\n"); } if (ts->swap_xy) @@ -608,12 +603,10 @@ static void ads7846_rx(void *ads) input_report_abs(input, ABS_X, x); input_report_abs(input, ABS_Y, y); - input_report_abs(input, ABS_PRESSURE, Rt); + input_report_abs(input, ABS_PRESSURE, ts->pressure_max - Rt); input_sync(input); -#ifdef VERBOSE - dev_dbg(&ts->spi->dev, "%4d/%4d/%4d\n", x, y, Rt); -#endif + dev_vdbg(&ts->spi->dev, "%4d/%4d/%4d\n", x, y, Rt); } hrtimer_start(&ts->timer, ktime_set(0, TS_POLL_PERIOD), @@ -723,9 +716,7 @@ static enum hrtimer_restart ads7846_timer(struct hrtimer *handle) input_sync(input); ts->pendown = 0; -#ifdef VERBOSE - dev_dbg(&ts->spi->dev, "UP\n"); -#endif + dev_vdbg(&ts->spi->dev, "UP\n"); } /* measurement cycle ended */ @@ -1256,3 +1247,4 @@ module_exit(ads7846_exit); MODULE_DESCRIPTION("ADS7846 TouchScreen Driver"); MODULE_LICENSE("GPL"); +MODULE_ALIAS("spi:ads7846"); diff --git a/drivers/input/touchscreen/atmel-wm97xx.c b/drivers/input/touchscreen/atmel-wm97xx.c index 35377f583e2..a12242f77e2 100644 --- a/drivers/input/touchscreen/atmel-wm97xx.c +++ b/drivers/input/touchscreen/atmel-wm97xx.c @@ -59,7 +59,7 @@ #define ATMEL_WM97XX_AC97C_IRQ (29) #define ATMEL_WM97XX_GPIO_DEFAULT (32+16) /* Pin 16 on port B. */ #else -#error Unkown CPU, this driver only supports AT32AP700X CPUs. +#error Unknown CPU, this driver only supports AT32AP700X CPUs. #endif struct continuous { diff --git a/drivers/input/touchscreen/atmel_tsadcc.c b/drivers/input/touchscreen/atmel_tsadcc.c index 055969e8be1..3d9b5166ebe 100644 --- a/drivers/input/touchscreen/atmel_tsadcc.c +++ b/drivers/input/touchscreen/atmel_tsadcc.c @@ -22,6 +22,8 @@ #include <linux/clk.h> #include <linux/platform_device.h> #include <linux/io.h> +#include <mach/board.h> +#include <mach/cpu.h> /* Register definitions based on AT91SAM9RL64 preliminary draft datasheet */ @@ -36,7 +38,9 @@ #define ATMEL_TSADCC_LOWRES (1 << 4) /* Resolution selection */ #define ATMEL_TSADCC_SLEEP (1 << 5) /* Sleep mode */ #define ATMEL_TSADCC_PENDET (1 << 6) /* Pen Detect selection */ +#define ATMEL_TSADCC_PRES (1 << 7) /* Pressure Measurement Selection */ #define ATMEL_TSADCC_PRESCAL (0x3f << 8) /* Prescalar Rate Selection */ +#define ATMEL_TSADCC_EPRESCAL (0xff << 8) /* Prescalar Rate Selection (Extended) */ #define ATMEL_TSADCC_STARTUP (0x7f << 16) /* Start Up time */ #define ATMEL_TSADCC_SHTIM (0xf << 24) /* Sample & Hold time */ #define ATMEL_TSADCC_PENDBC (0xf << 28) /* Pen Detect debouncing time */ @@ -84,7 +88,13 @@ #define ATMEL_TSADCC_CDR4 0x40 /* Channel Data 4 */ #define ATMEL_TSADCC_CDR5 0x44 /* Channel Data 5 */ -#define ADC_CLOCK 1000000 +#define ATMEL_TSADCC_XPOS 0x50 +#define ATMEL_TSADCC_Z1DAT 0x54 +#define ATMEL_TSADCC_Z2DAT 0x58 + +#define PRESCALER_VAL(x) ((x) >> 8) + +#define ADC_DEFAULT_CLOCK 100000 struct atmel_tsadcc { struct input_dev *input; @@ -172,6 +182,7 @@ static int __devinit atmel_tsadcc_probe(struct platform_device *pdev) struct atmel_tsadcc *ts_dev; struct input_dev *input_dev; struct resource *res; + struct at91_tsadcc_data *pdata = pdev->dev.platform_data; int err = 0; unsigned int prsc; unsigned int reg; @@ -204,14 +215,14 @@ static int __devinit atmel_tsadcc_probe(struct platform_device *pdev) goto err_free_dev; } - if (!request_mem_region(res->start, res->end - res->start + 1, + if (!request_mem_region(res->start, resource_size(res), "atmel tsadcc regs")) { dev_err(&pdev->dev, "resources is unavailable.\n"); err = -EBUSY; goto err_free_dev; } - tsc_base = ioremap(res->start, res->end - res->start + 1); + tsc_base = ioremap(res->start, resource_size(res)); if (!tsc_base) { dev_err(&pdev->dev, "failed to map registers.\n"); err = -ENOMEM; @@ -242,31 +253,49 @@ static int __devinit atmel_tsadcc_probe(struct platform_device *pdev) input_dev->phys = ts_dev->phys; 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); - + __set_bit(EV_ABS, input_dev->evbit); input_set_abs_params(input_dev, ABS_X, 0, 0x3FF, 0, 0); input_set_abs_params(input_dev, ABS_Y, 0, 0x3FF, 0, 0); + input_set_capability(input_dev, EV_KEY, BTN_TOUCH); + /* clk_enable() always returns 0, no need to check it */ clk_enable(ts_dev->clk); prsc = clk_get_rate(ts_dev->clk); dev_info(&pdev->dev, "Master clock is set at: %d Hz\n", prsc); - prsc = prsc / ADC_CLOCK / 2 - 1; + if (!pdata) + goto err_fail; + + if (!pdata->adc_clock) + pdata->adc_clock = ADC_DEFAULT_CLOCK; + + prsc = (prsc / (2 * pdata->adc_clock)) - 1; + + /* saturate if this value is too high */ + if (cpu_is_at91sam9rl()) { + if (prsc > PRESCALER_VAL(ATMEL_TSADCC_PRESCAL)) + prsc = PRESCALER_VAL(ATMEL_TSADCC_PRESCAL); + } else { + if (prsc > PRESCALER_VAL(ATMEL_TSADCC_EPRESCAL)) + prsc = PRESCALER_VAL(ATMEL_TSADCC_EPRESCAL); + } + + dev_info(&pdev->dev, "Prescaler is set at: %d\n", prsc); reg = ATMEL_TSADCC_TSAMOD_TS_ONLY_MODE | ((0x00 << 5) & ATMEL_TSADCC_SLEEP) | /* Normal Mode */ ((0x01 << 6) & ATMEL_TSADCC_PENDET) | /* Enable Pen Detect */ - ((prsc << 8) & ATMEL_TSADCC_PRESCAL) | /* PRESCAL */ - ((0x13 << 16) & ATMEL_TSADCC_STARTUP) | /* STARTUP */ - ((0x0F << 28) & ATMEL_TSADCC_PENDBC); /* PENDBC */ + (prsc << 8) | + ((0x26 << 16) & ATMEL_TSADCC_STARTUP) | + ((pdata->pendet_debounce << 28) & ATMEL_TSADCC_PENDBC); atmel_tsadcc_write(ATMEL_TSADCC_CR, ATMEL_TSADCC_SWRST); atmel_tsadcc_write(ATMEL_TSADCC_MR, reg); atmel_tsadcc_write(ATMEL_TSADCC_TRGR, ATMEL_TSADCC_TRGMOD_NONE); - atmel_tsadcc_write(ATMEL_TSADCC_TSR, (0x3 << 24) & ATMEL_TSADCC_TSSHTIM); + atmel_tsadcc_write(ATMEL_TSADCC_TSR, + (pdata->ts_sample_hold_time << 24) & ATMEL_TSADCC_TSSHTIM); atmel_tsadcc_read(ATMEL_TSADCC_SR); atmel_tsadcc_write(ATMEL_TSADCC_IER, ATMEL_TSADCC_PENCNT); @@ -286,7 +315,7 @@ err_free_irq: err_unmap_regs: iounmap(tsc_base); err_release_mem: - release_mem_region(res->start, res->end - res->start + 1); + release_mem_region(res->start, resource_size(res)); err_free_dev: input_free_device(ts_dev->input); err_free_mem: @@ -305,7 +334,7 @@ static int __devexit atmel_tsadcc_remove(struct platform_device *pdev) res = platform_get_resource(pdev, IORESOURCE_MEM, 0); iounmap(tsc_base); - release_mem_region(res->start, res->end - res->start + 1); + release_mem_region(res->start, resource_size(res)); clk_disable(ts_dev->clk); clk_put(ts_dev->clk); diff --git a/drivers/input/touchscreen/dynapro.c b/drivers/input/touchscreen/dynapro.c new file mode 100644 index 00000000000..455353908bd --- /dev/null +++ b/drivers/input/touchscreen/dynapro.c @@ -0,0 +1,206 @@ +/* + * Dynapro serial touchscreen driver + * + * Copyright (c) 2009 Tias Guns + * Based on the inexio driver (c) Vojtech Pavlik and Dan Streetman and + * Richard Lemon + * + */ + +/* + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 as published by + * the Free Software Foundation. + */ + +/* + * 2009/09/19 Tias Guns <tias@ulyssis.org> + * Copied inexio.c and edited for Dynapro protocol (from retired Xorg module) + */ + +#include <linux/errno.h> +#include <linux/kernel.h> +#include <linux/module.h> +#include <linux/slab.h> +#include <linux/input.h> +#include <linux/serio.h> +#include <linux/init.h> + +#define DRIVER_DESC "Dynapro serial touchscreen driver" + +MODULE_AUTHOR("Tias Guns <tias@ulyssis.org>"); +MODULE_DESCRIPTION(DRIVER_DESC); +MODULE_LICENSE("GPL"); + +/* + * Definitions & global arrays. + */ + +#define DYNAPRO_FORMAT_TOUCH_BIT 0x40 +#define DYNAPRO_FORMAT_LENGTH 3 +#define DYNAPRO_RESPONSE_BEGIN_BYTE 0x80 + +#define DYNAPRO_MIN_XC 0 +#define DYNAPRO_MAX_XC 0x3ff +#define DYNAPRO_MIN_YC 0 +#define DYNAPRO_MAX_YC 0x3ff + +#define DYNAPRO_GET_XC(data) (data[1] | ((data[0] & 0x38) << 4)) +#define DYNAPRO_GET_YC(data) (data[2] | ((data[0] & 0x07) << 7)) +#define DYNAPRO_GET_TOUCHED(data) (DYNAPRO_FORMAT_TOUCH_BIT & data[0]) + +/* + * Per-touchscreen data. + */ + +struct dynapro { + struct input_dev *dev; + struct serio *serio; + int idx; + unsigned char data[DYNAPRO_FORMAT_LENGTH]; + char phys[32]; +}; + +static void dynapro_process_data(struct dynapro *pdynapro) +{ + struct input_dev *dev = pdynapro->dev; + + if (DYNAPRO_FORMAT_LENGTH == ++pdynapro->idx) { + input_report_abs(dev, ABS_X, DYNAPRO_GET_XC(pdynapro->data)); + input_report_abs(dev, ABS_Y, DYNAPRO_GET_YC(pdynapro->data)); + input_report_key(dev, BTN_TOUCH, + DYNAPRO_GET_TOUCHED(pdynapro->data)); + input_sync(dev); + + pdynapro->idx = 0; + } +} + +static irqreturn_t dynapro_interrupt(struct serio *serio, + unsigned char data, unsigned int flags) +{ + struct dynapro *pdynapro = serio_get_drvdata(serio); + + pdynapro->data[pdynapro->idx] = data; + + if (DYNAPRO_RESPONSE_BEGIN_BYTE & pdynapro->data[0]) + dynapro_process_data(pdynapro); + else + dev_dbg(&serio->dev, "unknown/unsynchronized data: %x\n", + pdynapro->data[0]); + + return IRQ_HANDLED; +} + +static void dynapro_disconnect(struct serio *serio) +{ + struct dynapro *pdynapro = serio_get_drvdata(serio); + + input_get_device(pdynapro->dev); + input_unregister_device(pdynapro->dev); + serio_close(serio); + serio_set_drvdata(serio, NULL); + input_put_device(pdynapro->dev); + kfree(pdynapro); +} + +/* + * dynapro_connect() is the routine that is called when someone adds a + * new serio device that supports dynapro protocol and registers it as + * an input device. This is usually accomplished using inputattach. + */ + +static int dynapro_connect(struct serio *serio, struct serio_driver *drv) +{ + struct dynapro *pdynapro; + struct input_dev *input_dev; + int err; + + pdynapro = kzalloc(sizeof(struct dynapro), GFP_KERNEL); + input_dev = input_allocate_device(); + if (!pdynapro || !input_dev) { + err = -ENOMEM; + goto fail1; + } + + pdynapro->serio = serio; + pdynapro->dev = input_dev; + snprintf(pdynapro->phys, sizeof(pdynapro->phys), + "%s/input0", serio->phys); + + input_dev->name = "Dynapro Serial TouchScreen"; + input_dev->phys = pdynapro->phys; + input_dev->id.bustype = BUS_RS232; + input_dev->id.vendor = SERIO_DYNAPRO; + input_dev->id.product = 0; + input_dev->id.version = 0x0001; + input_dev->dev.parent = &serio->dev; + input_dev->evbit[0] = BIT_MASK(EV_KEY) | BIT_MASK(EV_ABS); + input_dev->keybit[BIT_WORD(BTN_TOUCH)] = BIT_MASK(BTN_TOUCH); + input_set_abs_params(pdynapro->dev, ABS_X, + DYNAPRO_MIN_XC, DYNAPRO_MAX_XC, 0, 0); + input_set_abs_params(pdynapro->dev, ABS_Y, + DYNAPRO_MIN_YC, DYNAPRO_MAX_YC, 0, 0); + + serio_set_drvdata(serio, pdynapro); + + err = serio_open(serio, drv); + if (err) + goto fail2; + + err = input_register_device(pdynapro->dev); + if (err) + goto fail3; + + return 0; + + fail3: serio_close(serio); + fail2: serio_set_drvdata(serio, NULL); + fail1: input_free_device(input_dev); + kfree(pdynapro); + return err; +} + +/* + * The serio driver structure. + */ + +static struct serio_device_id dynapro_serio_ids[] = { + { + .type = SERIO_RS232, + .proto = SERIO_DYNAPRO, + .id = SERIO_ANY, + .extra = SERIO_ANY, + }, + { 0 } +}; + +MODULE_DEVICE_TABLE(serio, dynapro_serio_ids); + +static struct serio_driver dynapro_drv = { + .driver = { + .name = "dynapro", + }, + .description = DRIVER_DESC, + .id_table = dynapro_serio_ids, + .interrupt = dynapro_interrupt, + .connect = dynapro_connect, + .disconnect = dynapro_disconnect, +}; + +/* + * The functions for inserting/removing us as a module. + */ + +static int __init dynapro_init(void) +{ + return serio_register_driver(&dynapro_drv); +} + +static void __exit dynapro_exit(void) +{ + serio_unregister_driver(&dynapro_drv); +} + +module_init(dynapro_init); +module_exit(dynapro_exit); diff --git a/drivers/input/touchscreen/eeti_ts.c b/drivers/input/touchscreen/eeti_ts.c index 3ab92222a52..9029bd3f34e 100644 --- a/drivers/input/touchscreen/eeti_ts.c +++ b/drivers/input/touchscreen/eeti_ts.c @@ -32,6 +32,7 @@ #include <linux/i2c.h> #include <linux/timer.h> #include <linux/gpio.h> +#include <linux/input/eeti_ts.h> static int flip_x; module_param(flip_x, bool, 0644); @@ -46,7 +47,7 @@ struct eeti_ts_priv { struct input_dev *input; struct work_struct work; struct mutex mutex; - int irq; + int irq, irq_active_high; }; #define EETI_TS_BITDEPTH (11) @@ -58,6 +59,11 @@ struct eeti_ts_priv { #define REPORT_BIT_HAS_PRESSURE (1 << 6) #define REPORT_RES_BITS(v) (((v) >> 1) + EETI_TS_BITDEPTH) +static inline int eeti_ts_irq_active(struct eeti_ts_priv *priv) +{ + return gpio_get_value(irq_to_gpio(priv->irq)) == priv->irq_active_high; +} + static void eeti_ts_read(struct work_struct *work) { char buf[6]; @@ -67,7 +73,7 @@ static void eeti_ts_read(struct work_struct *work) mutex_lock(&priv->mutex); - while (!gpio_get_value(irq_to_gpio(priv->irq)) && --to) + while (eeti_ts_irq_active(priv) && --to) i2c_master_recv(priv->client, buf, sizeof(buf)); if (!to) { @@ -140,8 +146,10 @@ static void eeti_ts_close(struct input_dev *dev) static int __devinit eeti_ts_probe(struct i2c_client *client, const struct i2c_device_id *idp) { + struct eeti_ts_platform_data *pdata; struct eeti_ts_priv *priv; struct input_dev *input; + unsigned int irq_flags; int err = -ENOMEM; /* In contrast to what's described in the datasheet, there seems @@ -180,6 +188,14 @@ static int __devinit eeti_ts_probe(struct i2c_client *client, priv->input = input; priv->irq = client->irq; + pdata = client->dev.platform_data; + + if (pdata) + priv->irq_active_high = pdata->irq_active_high; + + irq_flags = priv->irq_active_high ? + IRQF_TRIGGER_RISING : IRQF_TRIGGER_FALLING; + INIT_WORK(&priv->work, eeti_ts_read); i2c_set_clientdata(client, priv); input_set_drvdata(input, priv); @@ -188,7 +204,7 @@ static int __devinit eeti_ts_probe(struct i2c_client *client, if (err) goto err1; - err = request_irq(priv->irq, eeti_ts_isr, IRQF_TRIGGER_FALLING, + err = request_irq(priv->irq, eeti_ts_isr, irq_flags, client->name, priv); if (err) { dev_err(&client->dev, "Unable to request touchscreen IRQ.\n"); diff --git a/drivers/input/touchscreen/h3600_ts_input.c b/drivers/input/touchscreen/h3600_ts_input.c index 4d3139e2099..b4d7f63deff 100644 --- a/drivers/input/touchscreen/h3600_ts_input.c +++ b/drivers/input/touchscreen/h3600_ts_input.c @@ -148,9 +148,10 @@ unsigned int h3600_flite_power(struct input_dev *dev, enum flite_pwr pwr) struct h3600_dev *ts = input_get_drvdata(dev); /* Must be in this order */ - ts->serio->write(ts->serio, 1); - ts->serio->write(ts->serio, pwr); - ts->serio->write(ts->serio, brightness); + serio_write(ts->serio, 1); + serio_write(ts->serio, pwr); + serio_write(ts->serio, brightness); + return 0; } @@ -262,7 +263,7 @@ static int h3600ts_event(struct input_dev *dev, unsigned int type, switch (type) { case EV_LED: { - // ts->serio->write(ts->serio, SOME_CMD); + // serio_write(ts->serio, SOME_CMD); return 0; } } diff --git a/drivers/input/touchscreen/mainstone-wm97xx.c b/drivers/input/touchscreen/mainstone-wm97xx.c index 4cc047a5116..6cdcf2a6e03 100644 --- a/drivers/input/touchscreen/mainstone-wm97xx.c +++ b/drivers/input/touchscreen/mainstone-wm97xx.c @@ -14,7 +14,7 @@ * * Notes: * This is a wm97xx extended touch driver to capture touch - * data in a continuous manner on the Intel XScale archictecture + * data in a continuous manner on the Intel XScale architecture * * Features: * - codecs supported:- WM9705, WM9712, WM9713 @@ -31,9 +31,11 @@ #include <linux/interrupt.h> #include <linux/wm97xx.h> #include <linux/io.h> +#include <linux/gpio.h> + #include <mach/regs-ac97.h> -#define VERSION "0.13" +#include <asm/mach-types.h> struct continuous { u16 id; /* codec id */ @@ -62,6 +64,7 @@ static const struct continuous cinfo[] = { /* continuous speed index */ static int sp_idx; static u16 last, tries; +static int irq; /* * Pen sampling frequency (Hz) in continuous mode. @@ -128,7 +131,7 @@ static int wm97xx_acc_pen_down(struct wm97xx *wm) /* When the AC97 queue has been drained we need to allow time * to buffer up samples otherwise we end up spinning polling * for samples. The controller can't have a suitably low - * threashold set to use the notifications it gives. + * threshold set to use the notifications it gives. */ schedule_timeout_uninterruptible(1); @@ -171,7 +174,7 @@ up: static int wm97xx_acc_startup(struct wm97xx *wm) { - int idx = 0; + int idx = 0, ret = 0; /* check we have a codec */ if (wm->ac97 == NULL) @@ -191,18 +194,40 @@ static int wm97xx_acc_startup(struct wm97xx *wm) "mainstone accelerated touchscreen driver, %d samples/sec\n", cinfo[sp_idx].speed); + /* IRQ driven touchscreen is used on Palm hardware */ + if (machine_is_palmt5() || machine_is_palmtx() || machine_is_palmld()) { + pen_int = 1; + irq = 27; + /* There is some obscure mutant of WM9712 interbred with WM9713 + * used on Palm HW */ + wm->variant = WM97xx_WM1613; + } else if (machine_is_mainstone() && pen_int) + irq = 4; + + if (irq) { + ret = gpio_request(irq, "Touchscreen IRQ"); + if (ret) + goto out; + + ret = gpio_direction_input(irq); + if (ret) { + gpio_free(irq); + goto out; + } + + wm->pen_irq = gpio_to_irq(irq); + set_irq_type(wm->pen_irq, IRQ_TYPE_EDGE_BOTH); + } else /* pen irq not supported */ + pen_int = 0; + /* codec specific irq config */ if (pen_int) { switch (wm->id) { case WM9705_ID2: - wm->pen_irq = IRQ_GPIO(4); - set_irq_type(IRQ_GPIO(4), IRQ_TYPE_EDGE_BOTH); break; case WM9712_ID2: case WM9713_ID2: - /* enable pen down interrupt */ /* use PEN_DOWN GPIO 13 to assert IRQ on GPIO line 2 */ - wm->pen_irq = MAINSTONE_AC97_IRQ; wm97xx_config_gpio(wm, WM97XX_GPIO_13, WM97XX_GPIO_IN, WM97XX_GPIO_POL_HIGH, WM97XX_GPIO_STICKY, @@ -220,23 +245,17 @@ static int wm97xx_acc_startup(struct wm97xx *wm) } } - return 0; +out: + return ret; } static void wm97xx_acc_shutdown(struct wm97xx *wm) { /* codec specific deconfig */ if (pen_int) { - switch (wm->id & 0xffff) { - case WM9705_ID2: - wm->pen_irq = 0; - break; - case WM9712_ID2: - case WM9713_ID2: - /* disable interrupt */ - wm->pen_irq = 0; - break; - } + if (irq) + gpio_free(irq); + wm->pen_irq = 0; } } diff --git a/drivers/input/touchscreen/mcs5000_ts.c b/drivers/input/touchscreen/mcs5000_ts.c new file mode 100644 index 00000000000..4c28b89757f --- /dev/null +++ b/drivers/input/touchscreen/mcs5000_ts.c @@ -0,0 +1,318 @@ +/* + * mcs5000_ts.c - Touchscreen driver for MELFAS MCS-5000 controller + * + * Copyright (C) 2009 Samsung Electronics Co.Ltd + * Author: Joonyoung Shim <jy0922.shim@samsung.com> + * + * Based on wm97xx-core.c + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. + * + */ + +#include <linux/module.h> +#include <linux/init.h> +#include <linux/i2c.h> +#include <linux/i2c/mcs5000_ts.h> +#include <linux/interrupt.h> +#include <linux/input.h> +#include <linux/irq.h> + +/* Registers */ +#define MCS5000_TS_STATUS 0x00 +#define STATUS_OFFSET 0 +#define STATUS_NO (0 << STATUS_OFFSET) +#define STATUS_INIT (1 << STATUS_OFFSET) +#define STATUS_SENSING (2 << STATUS_OFFSET) +#define STATUS_COORD (3 << STATUS_OFFSET) +#define STATUS_GESTURE (4 << STATUS_OFFSET) +#define ERROR_OFFSET 4 +#define ERROR_NO (0 << ERROR_OFFSET) +#define ERROR_POWER_ON_RESET (1 << ERROR_OFFSET) +#define ERROR_INT_RESET (2 << ERROR_OFFSET) +#define ERROR_EXT_RESET (3 << ERROR_OFFSET) +#define ERROR_INVALID_REG_ADDRESS (8 << ERROR_OFFSET) +#define ERROR_INVALID_REG_VALUE (9 << ERROR_OFFSET) + +#define MCS5000_TS_OP_MODE 0x01 +#define RESET_OFFSET 0 +#define RESET_NO (0 << RESET_OFFSET) +#define RESET_EXT_SOFT (1 << RESET_OFFSET) +#define OP_MODE_OFFSET 1 +#define OP_MODE_SLEEP (0 << OP_MODE_OFFSET) +#define OP_MODE_ACTIVE (1 << OP_MODE_OFFSET) +#define GESTURE_OFFSET 4 +#define GESTURE_DISABLE (0 << GESTURE_OFFSET) +#define GESTURE_ENABLE (1 << GESTURE_OFFSET) +#define PROXIMITY_OFFSET 5 +#define PROXIMITY_DISABLE (0 << PROXIMITY_OFFSET) +#define PROXIMITY_ENABLE (1 << PROXIMITY_OFFSET) +#define SCAN_MODE_OFFSET 6 +#define SCAN_MODE_INTERRUPT (0 << SCAN_MODE_OFFSET) +#define SCAN_MODE_POLLING (1 << SCAN_MODE_OFFSET) +#define REPORT_RATE_OFFSET 7 +#define REPORT_RATE_40 (0 << REPORT_RATE_OFFSET) +#define REPORT_RATE_80 (1 << REPORT_RATE_OFFSET) + +#define MCS5000_TS_SENS_CTL 0x02 +#define MCS5000_TS_FILTER_CTL 0x03 +#define PRI_FILTER_OFFSET 0 +#define SEC_FILTER_OFFSET 4 + +#define MCS5000_TS_X_SIZE_UPPER 0x08 +#define MCS5000_TS_X_SIZE_LOWER 0x09 +#define MCS5000_TS_Y_SIZE_UPPER 0x0A +#define MCS5000_TS_Y_SIZE_LOWER 0x0B + +#define MCS5000_TS_INPUT_INFO 0x10 +#define INPUT_TYPE_OFFSET 0 +#define INPUT_TYPE_NONTOUCH (0 << INPUT_TYPE_OFFSET) +#define INPUT_TYPE_SINGLE (1 << INPUT_TYPE_OFFSET) +#define INPUT_TYPE_DUAL (2 << INPUT_TYPE_OFFSET) +#define INPUT_TYPE_PALM (3 << INPUT_TYPE_OFFSET) +#define INPUT_TYPE_PROXIMITY (7 << INPUT_TYPE_OFFSET) +#define GESTURE_CODE_OFFSET 3 +#define GESTURE_CODE_NO (0 << GESTURE_CODE_OFFSET) + +#define MCS5000_TS_X_POS_UPPER 0x11 +#define MCS5000_TS_X_POS_LOWER 0x12 +#define MCS5000_TS_Y_POS_UPPER 0x13 +#define MCS5000_TS_Y_POS_LOWER 0x14 +#define MCS5000_TS_Z_POS 0x15 +#define MCS5000_TS_WIDTH 0x16 +#define MCS5000_TS_GESTURE_VAL 0x17 +#define MCS5000_TS_MODULE_REV 0x20 +#define MCS5000_TS_FIRMWARE_VER 0x21 + +/* Touchscreen absolute values */ +#define MCS5000_MAX_XC 0x3ff +#define MCS5000_MAX_YC 0x3ff + +enum mcs5000_ts_read_offset { + READ_INPUT_INFO, + READ_X_POS_UPPER, + READ_X_POS_LOWER, + READ_Y_POS_UPPER, + READ_Y_POS_LOWER, + READ_BLOCK_SIZE, +}; + +/* Each client has this additional data */ +struct mcs5000_ts_data { + struct i2c_client *client; + struct input_dev *input_dev; + const struct mcs5000_ts_platform_data *platform_data; +}; + +static irqreturn_t mcs5000_ts_interrupt(int irq, void *dev_id) +{ + struct mcs5000_ts_data *data = dev_id; + struct i2c_client *client = data->client; + u8 buffer[READ_BLOCK_SIZE]; + int err; + int x; + int y; + + err = i2c_smbus_read_i2c_block_data(client, MCS5000_TS_INPUT_INFO, + READ_BLOCK_SIZE, buffer); + if (err < 0) { + dev_err(&client->dev, "%s, err[%d]\n", __func__, err); + goto out; + } + + switch (buffer[READ_INPUT_INFO]) { + case INPUT_TYPE_NONTOUCH: + input_report_key(data->input_dev, BTN_TOUCH, 0); + input_sync(data->input_dev); + break; + + case INPUT_TYPE_SINGLE: + x = (buffer[READ_X_POS_UPPER] << 8) | buffer[READ_X_POS_LOWER]; + y = (buffer[READ_Y_POS_UPPER] << 8) | buffer[READ_Y_POS_LOWER]; + + input_report_key(data->input_dev, BTN_TOUCH, 1); + input_report_abs(data->input_dev, ABS_X, x); + input_report_abs(data->input_dev, ABS_Y, y); + input_sync(data->input_dev); + break; + + case INPUT_TYPE_DUAL: + /* TODO */ + break; + + case INPUT_TYPE_PALM: + /* TODO */ + break; + + case INPUT_TYPE_PROXIMITY: + /* TODO */ + break; + + default: + dev_err(&client->dev, "Unknown ts input type %d\n", + buffer[READ_INPUT_INFO]); + break; + } + + out: + return IRQ_HANDLED; +} + +static void mcs5000_ts_phys_init(struct mcs5000_ts_data *data) +{ + const struct mcs5000_ts_platform_data *platform_data = + data->platform_data; + struct i2c_client *client = data->client; + + /* Touch reset & sleep mode */ + i2c_smbus_write_byte_data(client, MCS5000_TS_OP_MODE, + RESET_EXT_SOFT | OP_MODE_SLEEP); + + /* Touch size */ + i2c_smbus_write_byte_data(client, MCS5000_TS_X_SIZE_UPPER, + platform_data->x_size >> 8); + i2c_smbus_write_byte_data(client, MCS5000_TS_X_SIZE_LOWER, + platform_data->x_size & 0xff); + i2c_smbus_write_byte_data(client, MCS5000_TS_Y_SIZE_UPPER, + platform_data->y_size >> 8); + i2c_smbus_write_byte_data(client, MCS5000_TS_Y_SIZE_LOWER, + platform_data->y_size & 0xff); + + /* Touch active mode & 80 report rate */ + i2c_smbus_write_byte_data(data->client, MCS5000_TS_OP_MODE, + OP_MODE_ACTIVE | REPORT_RATE_80); +} + +static int __devinit mcs5000_ts_probe(struct i2c_client *client, + const struct i2c_device_id *id) +{ + struct mcs5000_ts_data *data; + struct input_dev *input_dev; + int ret; + + if (!client->dev.platform_data) + return -EINVAL; + + data = kzalloc(sizeof(struct mcs5000_ts_data), GFP_KERNEL); + input_dev = input_allocate_device(); + if (!data || !input_dev) { + dev_err(&client->dev, "Failed to allocate memory\n"); + ret = -ENOMEM; + goto err_free_mem; + } + + data->client = client; + data->input_dev = input_dev; + data->platform_data = client->dev.platform_data; + + input_dev->name = "MELPAS MCS-5000 Touchscreen"; + input_dev->id.bustype = BUS_I2C; + input_dev->dev.parent = &client->dev; + + __set_bit(EV_ABS, input_dev->evbit); + __set_bit(EV_KEY, input_dev->evbit); + __set_bit(BTN_TOUCH, input_dev->keybit); + input_set_abs_params(input_dev, ABS_X, 0, MCS5000_MAX_XC, 0, 0); + input_set_abs_params(input_dev, ABS_Y, 0, MCS5000_MAX_YC, 0, 0); + + input_set_drvdata(input_dev, data); + + if (data->platform_data->cfg_pin) + data->platform_data->cfg_pin(); + + ret = request_threaded_irq(client->irq, NULL, mcs5000_ts_interrupt, + IRQF_TRIGGER_LOW | IRQF_ONESHOT, "mcs5000_ts", data); + + if (ret < 0) { + dev_err(&client->dev, "Failed to register interrupt\n"); + goto err_free_mem; + } + + ret = input_register_device(data->input_dev); + if (ret < 0) + goto err_free_irq; + + mcs5000_ts_phys_init(data); + i2c_set_clientdata(client, data); + + return 0; + +err_free_irq: + free_irq(client->irq, data); +err_free_mem: + input_free_device(input_dev); + kfree(data); + return ret; +} + +static int __devexit mcs5000_ts_remove(struct i2c_client *client) +{ + struct mcs5000_ts_data *data = i2c_get_clientdata(client); + + free_irq(client->irq, data); + input_unregister_device(data->input_dev); + kfree(data); + i2c_set_clientdata(client, NULL); + + return 0; +} + +#ifdef CONFIG_PM +static int mcs5000_ts_suspend(struct i2c_client *client, pm_message_t mesg) +{ + /* Touch sleep mode */ + i2c_smbus_write_byte_data(client, MCS5000_TS_OP_MODE, OP_MODE_SLEEP); + + return 0; +} + +static int mcs5000_ts_resume(struct i2c_client *client) +{ + struct mcs5000_ts_data *data = i2c_get_clientdata(client); + + mcs5000_ts_phys_init(data); + + return 0; +} +#else +#define mcs5000_ts_suspend NULL +#define mcs5000_ts_resume NULL +#endif + +static const struct i2c_device_id mcs5000_ts_id[] = { + { "mcs5000_ts", 0 }, + { } +}; +MODULE_DEVICE_TABLE(i2c, mcs5000_ts_id); + +static struct i2c_driver mcs5000_ts_driver = { + .probe = mcs5000_ts_probe, + .remove = __devexit_p(mcs5000_ts_remove), + .suspend = mcs5000_ts_suspend, + .resume = mcs5000_ts_resume, + .driver = { + .name = "mcs5000_ts", + }, + .id_table = mcs5000_ts_id, +}; + +static int __init mcs5000_ts_init(void) +{ + return i2c_add_driver(&mcs5000_ts_driver); +} + +static void __exit mcs5000_ts_exit(void) +{ + i2c_del_driver(&mcs5000_ts_driver); +} + +module_init(mcs5000_ts_init); +module_exit(mcs5000_ts_exit); + +/* Module information */ +MODULE_AUTHOR("Joonyoung Shim <jy0922.shim@samsung.com>"); +MODULE_DESCRIPTION("Touchscreen driver for MELFAS MCS-5000 controller"); +MODULE_LICENSE("GPL"); diff --git a/drivers/input/touchscreen/pcap_ts.c b/drivers/input/touchscreen/pcap_ts.c new file mode 100644 index 00000000000..67fcd33595d --- /dev/null +++ b/drivers/input/touchscreen/pcap_ts.c @@ -0,0 +1,271 @@ +/* + * Driver for Motorola PCAP2 touchscreen as found in the EZX phone platform. + * + * Copyright (C) 2006 Harald Welte <laforge@openezx.org> + * Copyright (C) 2009 Daniel Ribeiro <drwyrm@gmail.com> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + */ + +#include <linux/module.h> +#include <linux/init.h> +#include <linux/fs.h> +#include <linux/string.h> +#include <linux/pm.h> +#include <linux/timer.h> +#include <linux/interrupt.h> +#include <linux/platform_device.h> +#include <linux/input.h> +#include <linux/mfd/ezx-pcap.h> + +struct pcap_ts { + struct pcap_chip *pcap; + struct input_dev *input; + struct delayed_work work; + u16 x, y; + u16 pressure; + u8 read_state; +}; + +#define SAMPLE_DELAY 20 /* msecs */ + +#define X_AXIS_MIN 0 +#define X_AXIS_MAX 1023 +#define Y_AXIS_MAX X_AXIS_MAX +#define Y_AXIS_MIN X_AXIS_MIN +#define PRESSURE_MAX X_AXIS_MAX +#define PRESSURE_MIN X_AXIS_MIN + +static void pcap_ts_read_xy(void *data, u16 res[2]) +{ + struct pcap_ts *pcap_ts = data; + + switch (pcap_ts->read_state) { + case PCAP_ADC_TS_M_PRESSURE: + /* pressure reading is unreliable */ + if (res[0] > PRESSURE_MIN && res[0] < PRESSURE_MAX) + pcap_ts->pressure = res[0]; + pcap_ts->read_state = PCAP_ADC_TS_M_XY; + schedule_delayed_work(&pcap_ts->work, 0); + break; + case PCAP_ADC_TS_M_XY: + pcap_ts->y = res[0]; + pcap_ts->x = res[1]; + if (pcap_ts->x <= X_AXIS_MIN || pcap_ts->x >= X_AXIS_MAX || + pcap_ts->y <= Y_AXIS_MIN || pcap_ts->y >= Y_AXIS_MAX) { + /* pen has been released */ + input_report_abs(pcap_ts->input, ABS_PRESSURE, 0); + input_report_key(pcap_ts->input, BTN_TOUCH, 0); + + pcap_ts->read_state = PCAP_ADC_TS_M_STANDBY; + schedule_delayed_work(&pcap_ts->work, 0); + } else { + /* pen is touching the screen */ + input_report_abs(pcap_ts->input, ABS_X, pcap_ts->x); + input_report_abs(pcap_ts->input, ABS_Y, pcap_ts->y); + input_report_key(pcap_ts->input, BTN_TOUCH, 1); + input_report_abs(pcap_ts->input, ABS_PRESSURE, + pcap_ts->pressure); + + /* switch back to pressure read mode */ + pcap_ts->read_state = PCAP_ADC_TS_M_PRESSURE; + schedule_delayed_work(&pcap_ts->work, + msecs_to_jiffies(SAMPLE_DELAY)); + } + input_sync(pcap_ts->input); + break; + default: + dev_warn(&pcap_ts->input->dev, + "pcap_ts: Warning, unhandled read_state %d\n", + pcap_ts->read_state); + break; + } +} + +static void pcap_ts_work(struct work_struct *work) +{ + struct delayed_work *dw = container_of(work, struct delayed_work, work); + struct pcap_ts *pcap_ts = container_of(dw, struct pcap_ts, work); + u8 ch[2]; + + pcap_set_ts_bits(pcap_ts->pcap, + pcap_ts->read_state << PCAP_ADC_TS_M_SHIFT); + + if (pcap_ts->read_state == PCAP_ADC_TS_M_STANDBY) + return; + + /* start adc conversion */ + ch[0] = PCAP_ADC_CH_TS_X1; + ch[1] = PCAP_ADC_CH_TS_Y1; + pcap_adc_async(pcap_ts->pcap, PCAP_ADC_BANK_1, 0, ch, + pcap_ts_read_xy, pcap_ts); +} + +static irqreturn_t pcap_ts_event_touch(int pirq, void *data) +{ + struct pcap_ts *pcap_ts = data; + + if (pcap_ts->read_state == PCAP_ADC_TS_M_STANDBY) { + pcap_ts->read_state = PCAP_ADC_TS_M_PRESSURE; + schedule_delayed_work(&pcap_ts->work, 0); + } + return IRQ_HANDLED; +} + +static int pcap_ts_open(struct input_dev *dev) +{ + struct pcap_ts *pcap_ts = input_get_drvdata(dev); + + pcap_ts->read_state = PCAP_ADC_TS_M_STANDBY; + schedule_delayed_work(&pcap_ts->work, 0); + + return 0; +} + +static void pcap_ts_close(struct input_dev *dev) +{ + struct pcap_ts *pcap_ts = input_get_drvdata(dev); + + cancel_delayed_work_sync(&pcap_ts->work); + + pcap_ts->read_state = PCAP_ADC_TS_M_NONTS; + pcap_set_ts_bits(pcap_ts->pcap, + pcap_ts->read_state << PCAP_ADC_TS_M_SHIFT); +} + +static int __devinit pcap_ts_probe(struct platform_device *pdev) +{ + struct input_dev *input_dev; + struct pcap_ts *pcap_ts; + int err = -ENOMEM; + + pcap_ts = kzalloc(sizeof(*pcap_ts), GFP_KERNEL); + if (!pcap_ts) + return err; + + pcap_ts->pcap = dev_get_drvdata(pdev->dev.parent); + platform_set_drvdata(pdev, pcap_ts); + + input_dev = input_allocate_device(); + if (!input_dev) + goto fail; + + INIT_DELAYED_WORK(&pcap_ts->work, pcap_ts_work); + + pcap_ts->read_state = PCAP_ADC_TS_M_NONTS; + pcap_set_ts_bits(pcap_ts->pcap, + pcap_ts->read_state << PCAP_ADC_TS_M_SHIFT); + + pcap_ts->input = input_dev; + input_set_drvdata(input_dev, pcap_ts); + + input_dev->name = "pcap-touchscreen"; + input_dev->phys = "pcap_ts/input0"; + input_dev->id.bustype = BUS_HOST; + input_dev->id.vendor = 0x0001; + input_dev->id.product = 0x0002; + input_dev->id.version = 0x0100; + input_dev->dev.parent = &pdev->dev; + input_dev->open = pcap_ts_open; + input_dev->close = pcap_ts_close; + + 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, X_AXIS_MIN, X_AXIS_MAX, 0, 0); + input_set_abs_params(input_dev, ABS_Y, Y_AXIS_MIN, Y_AXIS_MAX, 0, 0); + input_set_abs_params(input_dev, ABS_PRESSURE, PRESSURE_MIN, + PRESSURE_MAX, 0, 0); + + err = input_register_device(pcap_ts->input); + if (err) + goto fail_allocate; + + err = request_irq(pcap_to_irq(pcap_ts->pcap, PCAP_IRQ_TS), + pcap_ts_event_touch, 0, "Touch Screen", pcap_ts); + if (err) + goto fail_register; + + return 0; + +fail_register: + input_unregister_device(input_dev); + goto fail; +fail_allocate: + input_free_device(input_dev); +fail: + kfree(pcap_ts); + + return err; +} + +static int __devexit pcap_ts_remove(struct platform_device *pdev) +{ + struct pcap_ts *pcap_ts = platform_get_drvdata(pdev); + + free_irq(pcap_to_irq(pcap_ts->pcap, PCAP_IRQ_TS), pcap_ts); + cancel_delayed_work_sync(&pcap_ts->work); + + input_unregister_device(pcap_ts->input); + + kfree(pcap_ts); + + return 0; +} + +#ifdef CONFIG_PM +static int pcap_ts_suspend(struct device *dev) +{ + struct pcap_ts *pcap_ts = dev_get_drvdata(dev); + + pcap_set_ts_bits(pcap_ts->pcap, PCAP_ADC_TS_REF_LOWPWR); + return 0; +} + +static int pcap_ts_resume(struct device *dev) +{ + struct pcap_ts *pcap_ts = dev_get_drvdata(dev); + + pcap_set_ts_bits(pcap_ts->pcap, + pcap_ts->read_state << PCAP_ADC_TS_M_SHIFT); + return 0; +} + +static struct dev_pm_ops pcap_ts_pm_ops = { + .suspend = pcap_ts_suspend, + .resume = pcap_ts_resume, +}; +#define PCAP_TS_PM_OPS (&pcap_ts_pm_ops) +#else +#define PCAP_TS_PM_OPS NULL +#endif + +static struct platform_driver pcap_ts_driver = { + .probe = pcap_ts_probe, + .remove = __devexit_p(pcap_ts_remove), + .driver = { + .name = "pcap-ts", + .owner = THIS_MODULE, + .pm = PCAP_TS_PM_OPS, + }, +}; + +static int __init pcap_ts_init(void) +{ + return platform_driver_register(&pcap_ts_driver); +} + +static void __exit pcap_ts_exit(void) +{ + platform_driver_unregister(&pcap_ts_driver); +} + +module_init(pcap_ts_init); +module_exit(pcap_ts_exit); + +MODULE_DESCRIPTION("Motorola PCAP2 touchscreen driver"); +MODULE_AUTHOR("Daniel Ribeiro / Harald Welte"); +MODULE_LICENSE("GPL"); +MODULE_ALIAS("platform:pcap_ts"); diff --git a/drivers/input/touchscreen/s3c2410_ts.c b/drivers/input/touchscreen/s3c2410_ts.c new file mode 100644 index 00000000000..6386b441ef8 --- /dev/null +++ b/drivers/input/touchscreen/s3c2410_ts.c @@ -0,0 +1,457 @@ +/* + * Samsung S3C24XX touchscreen driver + * + * This program is free software; you can redistribute it and/or modify + * it under the term 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 + * + * Copyright 2004 Arnaud Patard <arnaud.patard@rtp-net.org> + * Copyright 2008 Ben Dooks <ben-linux@fluff.org> + * Copyright 2009 Simtec Electronics <linux@simtec.co.uk> + * + * Additional work by Herbert Pötzl <herbert@13thfloor.at> and + * Harald Welte <laforge@openmoko.org> + */ + +#include <linux/errno.h> +#include <linux/kernel.h> +#include <linux/module.h> +#include <linux/slab.h> +#include <linux/gpio.h> +#include <linux/input.h> +#include <linux/init.h> +#include <linux/delay.h> +#include <linux/interrupt.h> +#include <linux/platform_device.h> +#include <linux/clk.h> +#include <linux/io.h> + +#include <plat/adc.h> +#include <plat/regs-adc.h> + +#include <mach/regs-gpio.h> +#include <mach/ts.h> + +#define TSC_SLEEP (S3C2410_ADCTSC_PULL_UP_DISABLE | S3C2410_ADCTSC_XY_PST(0)) + +#define INT_DOWN (0) +#define INT_UP (1 << 8) + +#define WAIT4INT (S3C2410_ADCTSC_YM_SEN | \ + S3C2410_ADCTSC_YP_SEN | \ + S3C2410_ADCTSC_XP_SEN | \ + S3C2410_ADCTSC_XY_PST(3)) + +#define AUTOPST (S3C2410_ADCTSC_YM_SEN | \ + S3C2410_ADCTSC_YP_SEN | \ + S3C2410_ADCTSC_XP_SEN | \ + S3C2410_ADCTSC_AUTO_PST | \ + S3C2410_ADCTSC_XY_PST(0)) + +/* Per-touchscreen data. */ + +/** + * struct s3c2410ts - driver touchscreen state. + * @client: The ADC client we registered with the core driver. + * @dev: The device we are bound to. + * @input: The input device we registered with the input subsystem. + * @clock: The clock for the adc. + * @io: Pointer to the IO base. + * @xp: The accumulated X position data. + * @yp: The accumulated Y position data. + * @irq_tc: The interrupt number for pen up/down interrupt + * @count: The number of samples collected. + * @shift: The log2 of the maximum count to read in one go. + */ +struct s3c2410ts { + struct s3c_adc_client *client; + struct device *dev; + struct input_dev *input; + struct clk *clock; + void __iomem *io; + unsigned long xp; + unsigned long yp; + int irq_tc; + int count; + int shift; +}; + +static struct s3c2410ts ts; + +/** + * s3c2410_ts_connect - configure gpio for s3c2410 systems + * + * Configure the GPIO for the S3C2410 system, where we have external FETs + * connected to the device (later systems such as the S3C2440 integrate + * these into the device). +*/ +static inline void s3c2410_ts_connect(void) +{ + s3c2410_gpio_cfgpin(S3C2410_GPG(12), S3C2410_GPG12_XMON); + s3c2410_gpio_cfgpin(S3C2410_GPG(13), S3C2410_GPG13_nXPON); + s3c2410_gpio_cfgpin(S3C2410_GPG(14), S3C2410_GPG14_YMON); + s3c2410_gpio_cfgpin(S3C2410_GPG(15), S3C2410_GPG15_nYPON); +} + +/** + * get_down - return the down state of the pen + * @data0: The data read from ADCDAT0 register. + * @data1: The data read from ADCDAT1 register. + * + * Return non-zero if both readings show that the pen is down. + */ +static inline bool get_down(unsigned long data0, unsigned long data1) +{ + /* returns true if both data values show stylus down */ + return (!(data0 & S3C2410_ADCDAT0_UPDOWN) && + !(data1 & S3C2410_ADCDAT0_UPDOWN)); +} + +static void touch_timer_fire(unsigned long data) +{ + unsigned long data0; + unsigned long data1; + bool down; + + data0 = readl(ts.io + S3C2410_ADCDAT0); + data1 = readl(ts.io + S3C2410_ADCDAT1); + + down = get_down(data0, data1); + + if (ts.count == (1 << ts.shift)) { + ts.xp >>= ts.shift; + ts.yp >>= ts.shift; + + dev_dbg(ts.dev, "%s: X=%lu, Y=%lu, count=%d\n", + __func__, ts.xp, ts.yp, ts.count); + + input_report_abs(ts.input, ABS_X, ts.xp); + input_report_abs(ts.input, ABS_Y, ts.yp); + + input_report_key(ts.input, BTN_TOUCH, 1); + input_sync(ts.input); + + ts.xp = 0; + ts.yp = 0; + ts.count = 0; + } + + if (down) { + s3c_adc_start(ts.client, 0, 1 << ts.shift); + } else { + ts.count = 0; + + input_report_key(ts.input, BTN_TOUCH, 0); + input_sync(ts.input); + + writel(WAIT4INT | INT_DOWN, ts.io + S3C2410_ADCTSC); + } +} + +static DEFINE_TIMER(touch_timer, touch_timer_fire, 0, 0); + +/** + * stylus_irq - touchscreen stylus event interrupt + * @irq: The interrupt number + * @dev_id: The device ID. + * + * Called when the IRQ_TC is fired for a pen up or down event. + */ +static irqreturn_t stylus_irq(int irq, void *dev_id) +{ + unsigned long data0; + unsigned long data1; + bool down; + + data0 = readl(ts.io + S3C2410_ADCDAT0); + data1 = readl(ts.io + S3C2410_ADCDAT1); + + down = get_down(data0, data1); + + /* TODO we should never get an interrupt with down set while + * the timer is running, but maybe we ought to verify that the + * timer isn't running anyways. */ + + if (down) + s3c_adc_start(ts.client, 0, 1 << ts.shift); + else + dev_info(ts.dev, "%s: count=%d\n", __func__, ts.count); + + return IRQ_HANDLED; +} + +/** + * s3c24xx_ts_conversion - ADC conversion callback + * @client: The client that was registered with the ADC core. + * @data0: The reading from ADCDAT0. + * @data1: The reading from ADCDAT1. + * @left: The number of samples left. + * + * Called when a conversion has finished. + */ +static void s3c24xx_ts_conversion(struct s3c_adc_client *client, + unsigned data0, unsigned data1, + unsigned *left) +{ + dev_dbg(ts.dev, "%s: %d,%d\n", __func__, data0, data1); + + ts.xp += data0; + ts.yp += data1; + + ts.count++; + + /* From tests, it seems that it is unlikely to get a pen-up + * event during the conversion process which means we can + * ignore any pen-up events with less than the requisite + * count done. + * + * In several thousand conversions, no pen-ups where detected + * before count completed. + */ +} + +/** + * s3c24xx_ts_select - ADC selection callback. + * @client: The client that was registered with the ADC core. + * @select: The reason for select. + * + * Called when the ADC core selects (or deslects) us as a client. + */ +static void s3c24xx_ts_select(struct s3c_adc_client *client, unsigned select) +{ + if (select) { + writel(S3C2410_ADCTSC_PULL_UP_DISABLE | AUTOPST, + ts.io + S3C2410_ADCTSC); + } else { + mod_timer(&touch_timer, jiffies+1); + writel(WAIT4INT | INT_UP, ts.io + S3C2410_ADCTSC); + } +} + +/** + * s3c2410ts_probe - device core probe entry point + * @pdev: The device we are being bound to. + * + * Initialise, find and allocate any resources we need to run and then + * register with the ADC and input systems. + */ +static int __devinit s3c2410ts_probe(struct platform_device *pdev) +{ + struct s3c2410_ts_mach_info *info; + struct device *dev = &pdev->dev; + struct input_dev *input_dev; + struct resource *res; + int ret = -EINVAL; + + /* Initialise input stuff */ + memset(&ts, 0, sizeof(struct s3c2410ts)); + + ts.dev = dev; + + info = pdev->dev.platform_data; + if (!info) { + dev_err(dev, "no platform data, cannot attach\n"); + return -EINVAL; + } + + dev_dbg(dev, "initialising touchscreen\n"); + + ts.clock = clk_get(dev, "adc"); + if (IS_ERR(ts.clock)) { + dev_err(dev, "cannot get adc clock source\n"); + return -ENOENT; + } + + clk_enable(ts.clock); + dev_dbg(dev, "got and enabled clocks\n"); + + ts.irq_tc = ret = platform_get_irq(pdev, 0); + if (ret < 0) { + dev_err(dev, "no resource for interrupt\n"); + goto err_clk; + } + + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + if (!res) { + dev_err(dev, "no resource for registers\n"); + ret = -ENOENT; + goto err_clk; + } + + ts.io = ioremap(res->start, resource_size(res)); + if (ts.io == NULL) { + dev_err(dev, "cannot map registers\n"); + ret = -ENOMEM; + goto err_clk; + } + + /* Configure the touchscreen external FETs on the S3C2410 */ + if (!platform_get_device_id(pdev)->driver_data) + s3c2410_ts_connect(); + + ts.client = s3c_adc_register(pdev, s3c24xx_ts_select, + s3c24xx_ts_conversion, 1); + if (IS_ERR(ts.client)) { + dev_err(dev, "failed to register adc client\n"); + ret = PTR_ERR(ts.client); + goto err_iomap; + } + + /* Initialise registers */ + if ((info->delay & 0xffff) > 0) + writel(info->delay & 0xffff, ts.io + S3C2410_ADCDLY); + + writel(WAIT4INT | INT_DOWN, ts.io + S3C2410_ADCTSC); + + input_dev = input_allocate_device(); + if (!input_dev) { + dev_err(dev, "Unable to allocate the input device !!\n"); + ret = -ENOMEM; + goto err_iomap; + } + + ts.input = input_dev; + ts.input->evbit[0] = BIT_MASK(EV_KEY) | BIT_MASK(EV_ABS); + ts.input->keybit[BIT_WORD(BTN_TOUCH)] = BIT_MASK(BTN_TOUCH); + input_set_abs_params(ts.input, ABS_X, 0, 0x3FF, 0, 0); + input_set_abs_params(ts.input, ABS_Y, 0, 0x3FF, 0, 0); + + ts.input->name = "S3C24XX TouchScreen"; + ts.input->id.bustype = BUS_HOST; + ts.input->id.vendor = 0xDEAD; + ts.input->id.product = 0xBEEF; + ts.input->id.version = 0x0102; + + ts.shift = info->oversampling_shift; + + ret = request_irq(ts.irq_tc, stylus_irq, IRQF_DISABLED, + "s3c2410_ts_pen", ts.input); + if (ret) { + dev_err(dev, "cannot get TC interrupt\n"); + goto err_inputdev; + } + + dev_info(dev, "driver attached, registering input device\n"); + + /* All went ok, so register to the input system */ + ret = input_register_device(ts.input); + if (ret < 0) { + dev_err(dev, "failed to register input device\n"); + ret = -EIO; + goto err_tcirq; + } + + return 0; + + err_tcirq: + free_irq(ts.irq_tc, ts.input); + err_inputdev: + input_unregister_device(ts.input); + err_iomap: + iounmap(ts.io); + err_clk: + del_timer_sync(&touch_timer); + clk_put(ts.clock); + return ret; +} + +/** + * s3c2410ts_remove - device core removal entry point + * @pdev: The device we are being removed from. + * + * Free up our state ready to be removed. + */ +static int __devexit s3c2410ts_remove(struct platform_device *pdev) +{ + free_irq(ts.irq_tc, ts.input); + del_timer_sync(&touch_timer); + + clk_disable(ts.clock); + clk_put(ts.clock); + + input_unregister_device(ts.input); + iounmap(ts.io); + + return 0; +} + +#ifdef CONFIG_PM +static int s3c2410ts_suspend(struct device *dev) +{ + writel(TSC_SLEEP, ts.io + S3C2410_ADCTSC); + disable_irq(ts.irq_tc); + clk_disable(ts.clock); + + return 0; +} + +static int s3c2410ts_resume(struct device *dev) +{ + struct platform_device *pdev = to_platform_device(dev); + struct s3c2410_ts_mach_info *info = pdev->dev.platform_data; + + clk_enable(ts.clock); + + /* Initialise registers */ + if ((info->delay & 0xffff) > 0) + writel(info->delay & 0xffff, ts.io + S3C2410_ADCDLY); + + writel(WAIT4INT | INT_DOWN, ts.io + S3C2410_ADCTSC); + + return 0; +} + +static struct dev_pm_ops s3c_ts_pmops = { + .suspend = s3c2410ts_suspend, + .resume = s3c2410ts_resume, +}; +#endif + +static struct platform_device_id s3cts_driver_ids[] = { + { "s3c2410-ts", 0 }, + { "s3c2440-ts", 1 }, + { } +}; +MODULE_DEVICE_TABLE(platform, s3cts_driver_ids); + +static struct platform_driver s3c_ts_driver = { + .driver = { + .name = "s3c24xx-ts", + .owner = THIS_MODULE, +#ifdef CONFIG_PM + .pm = &s3c_ts_pmops, +#endif + }, + .id_table = s3cts_driver_ids, + .probe = s3c2410ts_probe, + .remove = __devexit_p(s3c2410ts_remove), +}; + +static int __init s3c2410ts_init(void) +{ + return platform_driver_register(&s3c_ts_driver); +} + +static void __exit s3c2410ts_exit(void) +{ + platform_driver_unregister(&s3c_ts_driver); +} + +module_init(s3c2410ts_init); +module_exit(s3c2410ts_exit); + +MODULE_AUTHOR("Arnaud Patard <arnaud.patard@rtp-net.org>, " + "Ben Dooks <ben@simtec.co.uk>, " + "Simtec Electronics <linux@simtec.co.uk>"); +MODULE_DESCRIPTION("S3C24XX Touchscreen driver"); +MODULE_LICENSE("GPL v2"); diff --git a/drivers/input/touchscreen/tsc2007.c b/drivers/input/touchscreen/tsc2007.c index 880f58c6a7c..7ef0d1420d3 100644 --- a/drivers/input/touchscreen/tsc2007.c +++ b/drivers/input/touchscreen/tsc2007.c @@ -21,15 +21,14 @@ */ #include <linux/module.h> -#include <linux/hrtimer.h> #include <linux/slab.h> #include <linux/input.h> #include <linux/interrupt.h> #include <linux/i2c.h> #include <linux/i2c/tsc2007.h> -#define TS_POLL_DELAY (10 * 1000) /* ns delay before the first sample */ -#define TS_POLL_PERIOD (5 * 1000) /* ns delay between samples */ +#define TS_POLL_DELAY 1 /* ms delay between samples */ +#define TS_POLL_PERIOD 1 /* ms delay between samples */ #define TSC2007_MEASURE_TEMP0 (0x0 << 4) #define TSC2007_MEASURE_AUX (0x2 << 4) @@ -70,17 +69,14 @@ struct ts_event { struct tsc2007 { struct input_dev *input; char phys[32]; - struct hrtimer timer; - struct ts_event tc; + struct delayed_work work; struct i2c_client *client; - spinlock_t lock; - u16 model; u16 x_plate_ohms; - unsigned pendown; + bool pendown; int irq; int (*get_pendown_state)(void); @@ -109,52 +105,96 @@ static inline int tsc2007_xfer(struct tsc2007 *tsc, u8 cmd) return val; } -static void tsc2007_send_event(void *tsc) +static void tsc2007_read_values(struct tsc2007 *tsc, struct ts_event *tc) { - struct tsc2007 *ts = tsc; - u32 rt; - u16 x, y, z1, z2; + /* y- still on; turn on only y+ (and ADC) */ + tc->y = tsc2007_xfer(tsc, READ_Y); + + /* turn y- off, x+ on, then leave in lowpower */ + tc->x = tsc2007_xfer(tsc, READ_X); + + /* turn y+ off, x- on; we'll use formula #1 */ + tc->z1 = tsc2007_xfer(tsc, READ_Z1); + tc->z2 = tsc2007_xfer(tsc, READ_Z2); - x = ts->tc.x; - y = ts->tc.y; - z1 = ts->tc.z1; - z2 = ts->tc.z2; + /* Prepare for next touch reading - power down ADC, enable PENIRQ */ + tsc2007_xfer(tsc, PWRDOWN); +} + +static u32 tsc2007_calculate_pressure(struct tsc2007 *tsc, struct ts_event *tc) +{ + u32 rt = 0; /* range filtering */ - if (x == MAX_12BIT) - x = 0; + if (tc->x == MAX_12BIT) + tc->x = 0; - if (likely(x && z1)) { + if (likely(tc->x && tc->z1)) { /* compute touch pressure resistance using equation #1 */ - rt = z2; - rt -= z1; - rt *= x; - rt *= ts->x_plate_ohms; - rt /= z1; + rt = tc->z2 - tc->z1; + rt *= tc->x; + rt *= tsc->x_plate_ohms; + rt /= tc->z1; rt = (rt + 2047) >> 12; - } else - rt = 0; + } + + return rt; +} + +static void tsc2007_send_up_event(struct tsc2007 *tsc) +{ + struct input_dev *input = tsc->input; - /* Sample found inconsistent by debouncing or pressure is beyond - * the maximum. Don't report it to user space, repeat at least - * once more the measurement + dev_dbg(&tsc->client->dev, "UP\n"); + + input_report_key(input, BTN_TOUCH, 0); + input_report_abs(input, ABS_PRESSURE, 0); + input_sync(input); +} + +static void tsc2007_work(struct work_struct *work) +{ + struct tsc2007 *ts = + container_of(to_delayed_work(work), struct tsc2007, work); + struct ts_event tc; + u32 rt; + + /* + * NOTE: We can't rely on the pressure to determine the pen down + * state, even though this controller has a pressure sensor. + * The pressure value can fluctuate for quite a while after + * lifting the pen and in some cases may not even settle at the + * expected value. + * + * The only safe way to check for the pen up condition is in the + * work function by reading the pen signal state (it's a GPIO + * and IRQ). Unfortunately such callback is not always available, + * in that case we have rely on the pressure anyway. */ + if (ts->get_pendown_state) { + if (unlikely(!ts->get_pendown_state())) { + tsc2007_send_up_event(ts); + ts->pendown = false; + goto out; + } + + dev_dbg(&ts->client->dev, "pen is still down\n"); + } + + tsc2007_read_values(ts, &tc); + + rt = tsc2007_calculate_pressure(ts, &tc); if (rt > MAX_12BIT) { + /* + * Sample found inconsistent by debouncing or pressure is + * beyond the maximum. Don't report it to user space, + * repeat at least once more the measurement. + */ dev_dbg(&ts->client->dev, "ignored pressure %d\n", rt); + goto out; - hrtimer_start(&ts->timer, ktime_set(0, TS_POLL_PERIOD), - HRTIMER_MODE_REL); - return; } - /* NOTE: We can't rely on the pressure to determine the pen down - * state, even this controller has a pressure sensor. The pressure - * value can fluctuate for quite a while after lifting the pen and - * in some cases may not even settle at the expected value. - * - * The only safe way to check for the pen up condition is in the - * timer by reading the pen signal state (it's a GPIO _and_ IRQ). - */ if (rt) { struct input_dev *input = ts->input; @@ -162,102 +202,74 @@ static void tsc2007_send_event(void *tsc) dev_dbg(&ts->client->dev, "DOWN\n"); input_report_key(input, BTN_TOUCH, 1); - ts->pendown = 1; + ts->pendown = true; } - input_report_abs(input, ABS_X, x); - input_report_abs(input, ABS_Y, y); + input_report_abs(input, ABS_X, tc.x); + input_report_abs(input, ABS_Y, tc.y); input_report_abs(input, ABS_PRESSURE, rt); input_sync(input); dev_dbg(&ts->client->dev, "point(%4d,%4d), pressure (%4u)\n", - x, y, rt); + tc.x, tc.y, rt); + + } else if (!ts->get_pendown_state && ts->pendown) { + /* + * We don't have callback to check pendown state, so we + * have to assume that since pressure reported is 0 the + * pen was lifted up. + */ + tsc2007_send_up_event(ts); + ts->pendown = false; } - hrtimer_start(&ts->timer, ktime_set(0, TS_POLL_PERIOD), - HRTIMER_MODE_REL); -} - -static int tsc2007_read_values(struct tsc2007 *tsc) -{ - /* y- still on; turn on only y+ (and ADC) */ - tsc->tc.y = tsc2007_xfer(tsc, READ_Y); - - /* turn y- off, x+ on, then leave in lowpower */ - tsc->tc.x = tsc2007_xfer(tsc, READ_X); - - /* turn y+ off, x- on; we'll use formula #1 */ - tsc->tc.z1 = tsc2007_xfer(tsc, READ_Z1); - tsc->tc.z2 = tsc2007_xfer(tsc, READ_Z2); - - /* power down */ - tsc2007_xfer(tsc, PWRDOWN); - - return 0; -} - -static enum hrtimer_restart tsc2007_timer(struct hrtimer *handle) -{ - struct tsc2007 *ts = container_of(handle, struct tsc2007, timer); - unsigned long flags; - - spin_lock_irqsave(&ts->lock, flags); - - if (unlikely(!ts->get_pendown_state() && ts->pendown)) { - struct input_dev *input = ts->input; - - dev_dbg(&ts->client->dev, "UP\n"); - - input_report_key(input, BTN_TOUCH, 0); - input_report_abs(input, ABS_PRESSURE, 0); - input_sync(input); - - ts->pendown = 0; + out: + if (ts->pendown) + schedule_delayed_work(&ts->work, + msecs_to_jiffies(TS_POLL_PERIOD)); + else enable_irq(ts->irq); - } else { - /* pen is still down, continue with the measurement */ - dev_dbg(&ts->client->dev, "pen is still down\n"); - - tsc2007_read_values(ts); - tsc2007_send_event(ts); - } - - spin_unlock_irqrestore(&ts->lock, flags); - - return HRTIMER_NORESTART; } static irqreturn_t tsc2007_irq(int irq, void *handle) { struct tsc2007 *ts = handle; - unsigned long flags; - - spin_lock_irqsave(&ts->lock, flags); - if (likely(ts->get_pendown_state())) { + if (!ts->get_pendown_state || likely(ts->get_pendown_state())) { disable_irq_nosync(ts->irq); - hrtimer_start(&ts->timer, ktime_set(0, TS_POLL_DELAY), - HRTIMER_MODE_REL); + schedule_delayed_work(&ts->work, + msecs_to_jiffies(TS_POLL_DELAY)); } if (ts->clear_penirq) ts->clear_penirq(); - spin_unlock_irqrestore(&ts->lock, flags); - return IRQ_HANDLED; } -static int tsc2007_probe(struct i2c_client *client, - const struct i2c_device_id *id) +static void tsc2007_free_irq(struct tsc2007 *ts) +{ + free_irq(ts->irq, ts); + if (cancel_delayed_work_sync(&ts->work)) { + /* + * Work was pending, therefore we need to enable + * IRQ here to balance the disable_irq() done in the + * interrupt handler. + */ + enable_irq(ts->irq); + } +} + +static int __devinit tsc2007_probe(struct i2c_client *client, + const struct i2c_device_id *id) { struct tsc2007 *ts; struct tsc2007_platform_data *pdata = pdata = client->dev.platform_data; struct input_dev *input_dev; int err; - if (!pdata || !pdata->get_pendown_state) { + if (!pdata) { dev_err(&client->dev, "platform data is required!\n"); return -EINVAL; } @@ -274,22 +286,15 @@ static int tsc2007_probe(struct i2c_client *client, } ts->client = client; - i2c_set_clientdata(client, ts); - + ts->irq = client->irq; ts->input = input_dev; - - hrtimer_init(&ts->timer, CLOCK_MONOTONIC, HRTIMER_MODE_REL); - ts->timer.function = tsc2007_timer; - - spin_lock_init(&ts->lock); + INIT_DELAYED_WORK(&ts->work, tsc2007_work); ts->model = pdata->model; ts->x_plate_ohms = pdata->x_plate_ohms; ts->get_pendown_state = pdata->get_pendown_state; ts->clear_penirq = pdata->clear_penirq; - pdata->init_platform_hw(); - snprintf(ts->phys, sizeof(ts->phys), "%s/input0", dev_name(&client->dev)); @@ -304,9 +309,8 @@ static int tsc2007_probe(struct i2c_client *client, 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); - tsc2007_read_values(ts); - - ts->irq = client->irq; + if (pdata->init_platform_hw) + pdata->init_platform_hw(); err = request_irq(ts->irq, tsc2007_irq, 0, client->dev.driver->name, ts); @@ -315,33 +319,39 @@ static int tsc2007_probe(struct i2c_client *client, goto err_free_mem; } + /* Prepare for touch readings - power down ADC and enable PENIRQ */ + err = tsc2007_xfer(ts, PWRDOWN); + if (err < 0) + goto err_free_irq; + err = input_register_device(input_dev); if (err) goto err_free_irq; - dev_info(&client->dev, "registered with irq (%d)\n", ts->irq); + i2c_set_clientdata(client, ts); return 0; err_free_irq: - free_irq(ts->irq, ts); - hrtimer_cancel(&ts->timer); + tsc2007_free_irq(ts); + if (pdata->exit_platform_hw) + pdata->exit_platform_hw(); err_free_mem: input_free_device(input_dev); kfree(ts); return err; } -static int tsc2007_remove(struct i2c_client *client) +static int __devexit tsc2007_remove(struct i2c_client *client) { struct tsc2007 *ts = i2c_get_clientdata(client); - struct tsc2007_platform_data *pdata; + struct tsc2007_platform_data *pdata = client->dev.platform_data; - pdata = client->dev.platform_data; - pdata->exit_platform_hw(); + tsc2007_free_irq(ts); + + if (pdata->exit_platform_hw) + pdata->exit_platform_hw(); - free_irq(ts->irq, ts); - hrtimer_cancel(&ts->timer); input_unregister_device(ts->input); kfree(ts); @@ -362,7 +372,7 @@ static struct i2c_driver tsc2007_driver = { }, .id_table = tsc2007_idtable, .probe = tsc2007_probe, - .remove = tsc2007_remove, + .remove = __devexit_p(tsc2007_remove), }; static int __init tsc2007_init(void) diff --git a/drivers/input/touchscreen/ucb1400_ts.c b/drivers/input/touchscreen/ucb1400_ts.c index 3a7a58222f8..89dcbe7b4b0 100644 --- a/drivers/input/touchscreen/ucb1400_ts.c +++ b/drivers/input/touchscreen/ucb1400_ts.c @@ -128,9 +128,10 @@ static inline unsigned int ucb1400_ts_read_yres(struct ucb1400_ts *ucb) return ucb1400_adc_read(ucb->ac97, 0, adcsync); } -static inline int ucb1400_ts_pen_down(struct snd_ac97 *ac97) +static inline int ucb1400_ts_pen_up(struct snd_ac97 *ac97) { unsigned short val = ucb1400_reg_read(ac97, UCB_TS_CR); + return val & (UCB_TS_CR_TSPX_LOW | UCB_TS_CR_TSMX_LOW); } @@ -209,7 +210,7 @@ static int ucb1400_ts_thread(void *_ucb) msleep(10); - if (ucb1400_ts_pen_down(ucb->ac97)) { + if (ucb1400_ts_pen_up(ucb->ac97)) { ucb1400_ts_irq_enable(ucb->ac97); /* @@ -354,10 +355,13 @@ static int ucb1400_ts_probe(struct platform_device *dev) goto err; } - error = ucb1400_ts_detect_irq(ucb); - if (error) { - printk(KERN_ERR "UCB1400: IRQ probe failed\n"); - goto err_free_devs; + /* Only in case the IRQ line wasn't supplied, try detecting it */ + if (ucb->irq < 0) { + error = ucb1400_ts_detect_irq(ucb); + if (error) { + printk(KERN_ERR "UCB1400: IRQ probe failed\n"); + goto err_free_devs; + } } init_waitqueue_head(&ucb->ts_wait); diff --git a/drivers/input/touchscreen/usbtouchscreen.c b/drivers/input/touchscreen/usbtouchscreen.c index fb7cb9bdfbd..09a5e7341bd 100644 --- a/drivers/input/touchscreen/usbtouchscreen.c +++ b/drivers/input/touchscreen/usbtouchscreen.c @@ -13,6 +13,8 @@ * - IdealTEK URTC1000 * - General Touch * - GoTop Super_Q2/GogoPen/PenPower tablets + * - JASTEC USB touch controller/DigiTech DTR-02U + * - Zytronic capacitive touchscreen * * Copyright (C) 2004-2007 by Daniel Ritz <daniel.ritz@gmx.ch> * Copyright (C) by Todd E. Johnson (mtouchusb.c) @@ -72,6 +74,15 @@ struct usbtouch_device_info { int min_press, max_press; int rept_size; + /* + * Always service the USB devices irq not just when the input device is + * open. This is useful when devices have a watchdog which prevents us + * from periodically polling the device. Leave this unset unless your + * touchscreen device requires it, as it does consume more of the USB + * bandwidth. + */ + bool irq_always; + void (*process_pkt) (struct usbtouch_usb *usbtouch, unsigned char *pkt, int len); /* @@ -118,6 +129,10 @@ enum { DEVTYPE_IDEALTEK, DEVTYPE_GENERAL_TOUCH, DEVTYPE_GOTOP, + DEVTYPE_JASTEC, + DEVTYPE_E2I, + DEVTYPE_ZYTRONIC, + DEVTYPE_TC5UH, }; #define USB_DEVICE_HID_CLASS(vend, prod) \ @@ -191,11 +206,60 @@ static struct usb_device_id usbtouch_devices[] = { {USB_DEVICE(0x08f2, 0x00f4), .driver_info = DEVTYPE_GOTOP}, #endif +#ifdef CONFIG_TOUCHSCREEN_USB_JASTEC + {USB_DEVICE(0x0f92, 0x0001), .driver_info = DEVTYPE_JASTEC}, +#endif + +#ifdef CONFIG_TOUCHSCREEN_USB_E2I + {USB_DEVICE(0x1ac7, 0x0001), .driver_info = DEVTYPE_E2I}, +#endif + +#ifdef CONFIG_TOUCHSCREEN_USB_ZYTRONIC + {USB_DEVICE(0x14c8, 0x0003), .driver_info = DEVTYPE_ZYTRONIC}, +#endif + +#ifdef CONFIG_TOUCHSCREEN_USB_ETT_TC5UH + {USB_DEVICE(0x0664, 0x0309), .driver_info = DEVTYPE_TC5UH}, +#endif + {} }; /***************************************************************************** + * e2i Part + */ + +#ifdef CONFIG_TOUCHSCREEN_USB_E2I +static int e2i_init(struct usbtouch_usb *usbtouch) +{ + int ret; + + ret = usb_control_msg(usbtouch->udev, usb_rcvctrlpipe(usbtouch->udev, 0), + 0x01, 0x02, 0x0000, 0x0081, + NULL, 0, USB_CTRL_SET_TIMEOUT); + + dbg("%s - usb_control_msg - E2I_RESET - bytes|err: %d", + __func__, ret); + return ret; +} + +static int e2i_read_data(struct usbtouch_usb *dev, unsigned char *pkt) +{ + int tmp = (pkt[0] << 8) | pkt[1]; + dev->x = (pkt[2] << 8) | pkt[3]; + dev->y = (pkt[4] << 8) | pkt[5]; + + tmp = tmp - 0xA000; + dev->touch = (tmp > 0); + dev->press = (tmp > 0 ? tmp : 0); + + return 1; +} +#endif + + +/***************************************************************************** * eGalax part */ @@ -495,6 +559,19 @@ static int irtouch_read_data(struct usbtouch_usb *dev, unsigned char *pkt) } #endif +/***************************************************************************** + * ET&T TC5UH part + */ +#ifdef CONFIG_TOUCHSCREEN_USB_ETT_TC5UH +static int tc5uh_read_data(struct usbtouch_usb *dev, unsigned char *pkt) +{ + dev->x = ((pkt[2] & 0x0F) << 8) | pkt[1]; + dev->y = ((pkt[4] & 0x0F) << 8) | pkt[3]; + dev->touch = pkt[0] & 0x01; + + return 1; +} +#endif /***************************************************************************** * IdealTEK URTC1000 Part @@ -559,10 +636,58 @@ static int gotop_read_data(struct usbtouch_usb *dev, unsigned char *pkt) dev->x = ((pkt[1] & 0x38) << 4) | pkt[2]; dev->y = ((pkt[1] & 0x07) << 7) | pkt[3]; dev->touch = pkt[0] & 0x01; + + return 1; +} +#endif + +/***************************************************************************** + * JASTEC Part + */ +#ifdef CONFIG_TOUCHSCREEN_USB_JASTEC +static int jastec_read_data(struct usbtouch_usb *dev, unsigned char *pkt) +{ + dev->x = ((pkt[0] & 0x3f) << 6) | (pkt[2] & 0x3f); + dev->y = ((pkt[1] & 0x3f) << 6) | (pkt[3] & 0x3f); + dev->touch = (pkt[0] & 0x40) >> 6; + return 1; } #endif +/***************************************************************************** + * Zytronic Part + */ +#ifdef CONFIG_TOUCHSCREEN_USB_ZYTRONIC +static int zytronic_read_data(struct usbtouch_usb *dev, unsigned char *pkt) +{ + switch (pkt[0]) { + case 0x3A: /* command response */ + dbg("%s: Command response %d", __func__, pkt[1]); + break; + + case 0xC0: /* down */ + dev->x = (pkt[1] & 0x7f) | ((pkt[2] & 0x07) << 7); + dev->y = (pkt[3] & 0x7f) | ((pkt[4] & 0x07) << 7); + dev->touch = 1; + dbg("%s: down %d,%d", __func__, dev->x, dev->y); + return 1; + + case 0x80: /* up */ + dev->x = (pkt[1] & 0x7f) | ((pkt[2] & 0x07) << 7); + dev->y = (pkt[3] & 0x7f) | ((pkt[4] & 0x07) << 7); + dev->touch = 0; + dbg("%s: up %d,%d", __func__, dev->x, dev->y); + return 1; + + default: + dbg("%s: Unknown return %d", __func__, pkt[0]); + break; + } + + return 0; +} +#endif /***************************************************************************** * the different device descriptors @@ -702,6 +827,52 @@ static struct usbtouch_device_info usbtouch_dev_info[] = { .read_data = gotop_read_data, }, #endif + +#ifdef CONFIG_TOUCHSCREEN_USB_JASTEC + [DEVTYPE_JASTEC] = { + .min_xc = 0x0, + .max_xc = 0x0fff, + .min_yc = 0x0, + .max_yc = 0x0fff, + .rept_size = 4, + .read_data = jastec_read_data, + }, +#endif + +#ifdef CONFIG_TOUCHSCREEN_USB_E2I + [DEVTYPE_E2I] = { + .min_xc = 0x0, + .max_xc = 0x7fff, + .min_yc = 0x0, + .max_yc = 0x7fff, + .rept_size = 6, + .init = e2i_init, + .read_data = e2i_read_data, + }, +#endif + +#ifdef CONFIG_TOUCHSCREEN_USB_ZYTRONIC + [DEVTYPE_ZYTRONIC] = { + .min_xc = 0x0, + .max_xc = 0x03ff, + .min_yc = 0x0, + .max_yc = 0x03ff, + .rept_size = 5, + .read_data = zytronic_read_data, + .irq_always = true, + }, +#endif + +#ifdef CONFIG_TOUCHSCREEN_USB_ETT_TC5UH + [DEVTYPE_TC5UH] = { + .min_xc = 0x0, + .max_xc = 0x0fff, + .min_yc = 0x0, + .max_yc = 0x0fff, + .rept_size = 5, + .read_data = tc5uh_read_data, + }, +#endif }; @@ -852,8 +1023,10 @@ static int usbtouch_open(struct input_dev *input) usbtouch->irq->dev = usbtouch->udev; - if (usb_submit_urb(usbtouch->irq, GFP_KERNEL)) - return -EIO; + if (!usbtouch->type->irq_always) { + if (usb_submit_urb(usbtouch->irq, GFP_KERNEL)) + return -EIO; + } return 0; } @@ -862,7 +1035,8 @@ static void usbtouch_close(struct input_dev *input) { struct usbtouch_usb *usbtouch = input_get_drvdata(input); - usb_kill_urb(usbtouch->irq); + if (!usbtouch->type->irq_always) + usb_kill_urb(usbtouch->irq); } @@ -985,6 +1159,9 @@ static int usbtouch_probe(struct usb_interface *intf, usb_set_intfdata(intf, usbtouch); + if (usbtouch->type->irq_always) + usb_submit_urb(usbtouch->irq, GFP_KERNEL); + return 0; out_free_buffers: @@ -1006,7 +1183,7 @@ static void usbtouch_disconnect(struct usb_interface *intf) dbg("%s - usbtouch is initialized, cleaning up", __func__); usb_set_intfdata(intf, NULL); - usb_kill_urb(usbtouch->irq); + /* this will stop IO via close */ input_unregister_device(usbtouch->input); usb_free_urb(usbtouch->irq); usbtouch_free_buffers(interface_to_usbdev(intf), usbtouch); diff --git a/drivers/input/touchscreen/w90p910_ts.c b/drivers/input/touchscreen/w90p910_ts.c index 6071f588257..6ccbdbbf33f 100644 --- a/drivers/input/touchscreen/w90p910_ts.c +++ b/drivers/input/touchscreen/w90p910_ts.c @@ -13,6 +13,7 @@ #include <linux/module.h> #include <linux/platform_device.h> #include <linux/io.h> +#include <linux/clk.h> #include <linux/input.h> #include <linux/interrupt.h> @@ -47,8 +48,8 @@ enum ts_state { struct w90p910_ts { struct input_dev *input; struct timer_list timer; + struct clk *clk; int irq_num; - void __iomem *clocken; void __iomem *ts_reg; spinlock_t lock; enum ts_state state; @@ -166,8 +167,7 @@ static int w90p910_open(struct input_dev *dev) unsigned long val; /* enable the ADC clock */ - val = __raw_readl(w90p910_ts->clocken); - __raw_writel(val | ADC_CLK_EN, w90p910_ts->clocken); + clk_enable(w90p910_ts->clk); __raw_writel(ADC_RST1, w90p910_ts->ts_reg); msleep(1); @@ -211,8 +211,7 @@ static void w90p910_close(struct input_dev *dev) del_timer_sync(&w90p910_ts->timer); /* stop the ADC clock */ - val = __raw_readl(w90p910_ts->clocken); - __raw_writel(val & ~ADC_CLK_EN, w90p910_ts->clocken); + clk_disable(w90p910_ts->clk); } static int __devinit w90x900ts_probe(struct platform_device *pdev) @@ -241,26 +240,24 @@ static int __devinit w90x900ts_probe(struct platform_device *pdev) goto fail1; } - if (!request_mem_region(res->start, res->end - res->start + 1, + if (!request_mem_region(res->start, resource_size(res), pdev->name)) { err = -EBUSY; goto fail1; } - w90p910_ts->ts_reg = ioremap(res->start, res->end - res->start + 1); + w90p910_ts->ts_reg = ioremap(res->start, resource_size(res)); if (!w90p910_ts->ts_reg) { err = -ENOMEM; goto fail2; } - res = platform_get_resource(pdev, IORESOURCE_MEM, 1); - if (!res) { - err = -ENXIO; + w90p910_ts->clk = clk_get(&pdev->dev, NULL); + if (IS_ERR(w90p910_ts->clk)) { + err = PTR_ERR(w90p910_ts->clk); goto fail3; } - w90p910_ts->clocken = (void __iomem *)res->start; - input_dev->name = "W90P910 TouchScreen"; input_dev->phys = "w90p910ts/event0"; input_dev->id.bustype = BUS_HOST; @@ -283,20 +280,21 @@ static int __devinit w90x900ts_probe(struct platform_device *pdev) if (request_irq(w90p910_ts->irq_num, w90p910_ts_interrupt, IRQF_DISABLED, "w90p910ts", w90p910_ts)) { err = -EBUSY; - goto fail3; + goto fail4; } err = input_register_device(w90p910_ts->input); if (err) - goto fail4; + goto fail5; platform_set_drvdata(pdev, w90p910_ts); return 0; -fail4: free_irq(w90p910_ts->irq_num, w90p910_ts); +fail5: free_irq(w90p910_ts->irq_num, w90p910_ts); +fail4: clk_put(w90p910_ts->clk); fail3: iounmap(w90p910_ts->ts_reg); -fail2: release_mem_region(res->start, res->end - res->start + 1); +fail2: release_mem_region(res->start, resource_size(res)); fail1: input_free_device(input_dev); kfree(w90p910_ts); return err; @@ -311,8 +309,10 @@ static int __devexit w90x900ts_remove(struct platform_device *pdev) del_timer_sync(&w90p910_ts->timer); iounmap(w90p910_ts->ts_reg); + clk_put(w90p910_ts->clk); + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); - release_mem_region(res->start, res->end - res->start + 1); + release_mem_region(res->start, resource_size(res)); input_unregister_device(w90p910_ts->input); kfree(w90p910_ts); @@ -326,7 +326,7 @@ static struct platform_driver w90x900ts_driver = { .probe = w90x900ts_probe, .remove = __devexit_p(w90x900ts_remove), .driver = { - .name = "w90x900-ts", + .name = "nuc900-ts", .owner = THIS_MODULE, }, }; @@ -347,4 +347,4 @@ module_exit(w90x900ts_exit); MODULE_AUTHOR("Wan ZongShun <mcuos.com@gmail.com>"); MODULE_DESCRIPTION("w90p910 touch screen driver!"); MODULE_LICENSE("GPL"); -MODULE_ALIAS("platform:w90p910-ts"); +MODULE_ALIAS("platform:nuc900-ts"); diff --git a/drivers/input/touchscreen/wacom_w8001.c b/drivers/input/touchscreen/wacom_w8001.c index 2f33a016764..56dc35c94bb 100644 --- a/drivers/input/touchscreen/wacom_w8001.c +++ b/drivers/input/touchscreen/wacom_w8001.c @@ -25,18 +25,16 @@ MODULE_AUTHOR("Jaya Kumar <jayakumar.lkml@gmail.com>"); MODULE_DESCRIPTION(DRIVER_DESC); MODULE_LICENSE("GPL"); -/* - * Definitions & global arrays. - */ - #define W8001_MAX_LENGTH 11 -#define W8001_PACKET_LEN 11 -#define W8001_LEAD_MASK 0x80 -#define W8001_LEAD_BYTE 0x80 -#define W8001_TAB_MASK 0x40 -#define W8001_TAB_BYTE 0x40 +#define W8001_LEAD_MASK 0x80 +#define W8001_LEAD_BYTE 0x80 +#define W8001_TAB_MASK 0x40 +#define W8001_TAB_BYTE 0x40 -#define W8001_QUERY_PACKET 0x20 +#define W8001_QUERY_PACKET 0x20 + +#define W8001_CMD_START '1' +#define W8001_CMD_QUERY '*' struct w8001_coord { u8 rdy; @@ -57,18 +55,19 @@ struct w8001_coord { struct w8001 { struct input_dev *dev; struct serio *serio; - struct mutex cmd_mutex; struct completion cmd_done; int id; int idx; - unsigned char expected_packet; + unsigned char response_type; + unsigned char response[W8001_MAX_LENGTH]; unsigned char data[W8001_MAX_LENGTH]; - unsigned char response[W8001_PACKET_LEN]; char phys[32]; }; -static int parse_data(u8 *data, struct w8001_coord *coord) +static void parse_data(u8 *data, struct w8001_coord *coord) { + memset(coord, 0, sizeof(*coord)); + coord->rdy = data[0] & 0x20; coord->tsw = data[0] & 0x01; coord->f1 = data[0] & 0x02; @@ -87,15 +86,15 @@ static int parse_data(u8 *data, struct w8001_coord *coord) coord->tilt_x = data[7] & 0x7F; coord->tilt_y = data[8] & 0x7F; - - return 0; } -static void w8001_process_data(struct w8001 *w8001, unsigned char data) +static irqreturn_t w8001_interrupt(struct serio *serio, + unsigned char data, unsigned int flags) { + struct w8001 *w8001 = serio_get_drvdata(serio); struct input_dev *dev = w8001->dev; - u8 tmp; struct w8001_coord coord; + unsigned char tmp; w8001->data[w8001->idx] = data; switch (w8001->idx++) { @@ -105,12 +104,13 @@ static void w8001_process_data(struct w8001 *w8001, unsigned char data) w8001->idx = 0; } break; + case 8: tmp = w8001->data[0] & W8001_TAB_MASK; if (unlikely(tmp == W8001_TAB_BYTE)) break; + w8001->idx = 0; - memset(&coord, 0, sizeof(coord)); parse_data(w8001->data, &coord); input_report_abs(dev, ABS_X, coord.x); input_report_abs(dev, ABS_Y, coord.y); @@ -118,86 +118,48 @@ static void w8001_process_data(struct w8001 *w8001, unsigned char data) input_report_key(dev, BTN_TOUCH, coord.tsw); input_sync(dev); break; + case 10: w8001->idx = 0; - memcpy(w8001->response, &w8001->data, W8001_PACKET_LEN); - w8001->expected_packet = W8001_QUERY_PACKET; + memcpy(w8001->response, w8001->data, W8001_MAX_LENGTH); + w8001->response_type = W8001_QUERY_PACKET; complete(&w8001->cmd_done); break; } -} - - -static irqreturn_t w8001_interrupt(struct serio *serio, - unsigned char data, unsigned int flags) -{ - struct w8001 *w8001 = serio_get_drvdata(serio); - - w8001_process_data(w8001, data); return IRQ_HANDLED; } -static int w8001_async_command(struct w8001 *w8001, unsigned char *packet, - int len) -{ - int rc = -1; - int i; - - mutex_lock(&w8001->cmd_mutex); - - for (i = 0; i < len; i++) { - if (serio_write(w8001->serio, packet[i])) - goto out; - } - rc = 0; - -out: - mutex_unlock(&w8001->cmd_mutex); - return rc; -} - -static int w8001_command(struct w8001 *w8001, unsigned char *packet, int len) +static int w8001_command(struct w8001 *w8001, unsigned char command, + bool wait_response) { - int rc = -1; - int i; + int rc; - mutex_lock(&w8001->cmd_mutex); - - serio_pause_rx(w8001->serio); + w8001->response_type = 0; init_completion(&w8001->cmd_done); - serio_continue_rx(w8001->serio); - - for (i = 0; i < len; i++) { - if (serio_write(w8001->serio, packet[i])) - goto out; - } - wait_for_completion_timeout(&w8001->cmd_done, HZ); + rc = serio_write(w8001->serio, command); + if (rc == 0 && wait_response) { - if (w8001->expected_packet == W8001_QUERY_PACKET) { - /* We are back in reporting mode, the query was ACKed */ - memcpy(packet, w8001->response, W8001_PACKET_LEN); - rc = 0; + wait_for_completion_timeout(&w8001->cmd_done, HZ); + if (w8001->response_type != W8001_QUERY_PACKET) + rc = -EIO; } -out: - mutex_unlock(&w8001->cmd_mutex); return rc; } static int w8001_setup(struct w8001 *w8001) { - struct w8001_coord coord; struct input_dev *dev = w8001->dev; - unsigned char start[1] = { '1' }; - unsigned char query[11] = { '*' }; + struct w8001_coord coord; + int error; - if (w8001_command(w8001, query, 1)) - return -1; + error = w8001_command(w8001, W8001_CMD_QUERY, true); + if (error) + return error; - memset(&coord, 0, sizeof(coord)); - parse_data(query, &coord); + parse_data(w8001->response, &coord); input_set_abs_params(dev, ABS_X, 0, coord.x, 0, 0); input_set_abs_params(dev, ABS_Y, 0, coord.y, 0, 0); @@ -205,10 +167,7 @@ static int w8001_setup(struct w8001 *w8001) input_set_abs_params(dev, ABS_TILT_X, 0, coord.tilt_x, 0, 0); input_set_abs_params(dev, ABS_TILT_Y, 0, coord.tilt_y, 0, 0); - if (w8001_async_command(w8001, start, 1)) - return -1; - - return 0; + return w8001_command(w8001, W8001_CMD_START, false); } /* @@ -249,7 +208,6 @@ static int w8001_connect(struct serio *serio, struct serio_driver *drv) w8001->serio = serio; w8001->id = serio->id.id; w8001->dev = input_dev; - mutex_init(&w8001->cmd_mutex); init_completion(&w8001->cmd_done); snprintf(w8001->phys, sizeof(w8001->phys), "%s/input0", serio->phys); @@ -269,7 +227,8 @@ static int w8001_connect(struct serio *serio, struct serio_driver *drv) if (err) goto fail2; - if (w8001_setup(w8001)) + err = w8001_setup(w8001); + if (err) goto fail3; err = input_register_device(w8001->dev); diff --git a/drivers/input/touchscreen/wm97xx-core.c b/drivers/input/touchscreen/wm97xx-core.c index 2957d48e004..f944918466e 100644 --- a/drivers/input/touchscreen/wm97xx-core.c +++ b/drivers/input/touchscreen/wm97xx-core.c @@ -204,7 +204,7 @@ void wm97xx_set_gpio(struct wm97xx *wm, u32 gpio, else reg &= ~gpio; - if (wm->id == WM9712_ID2) + if (wm->id == WM9712_ID2 && wm->variant != WM97xx_WM1613) wm97xx_reg_write(wm, AC97_GPIO_STATUS, reg << 1); else wm97xx_reg_write(wm, AC97_GPIO_STATUS, reg); @@ -307,7 +307,7 @@ static void wm97xx_pen_irq_worker(struct work_struct *work) WM97XX_GPIO_13); } - if (wm->id == WM9712_ID2) + if (wm->id == WM9712_ID2 && wm->variant != WM97xx_WM1613) wm97xx_reg_write(wm, AC97_GPIO_STATUS, (status & ~WM97XX_GPIO_13) << 1); else @@ -561,6 +561,7 @@ static void wm97xx_ts_input_close(struct input_dev *idev) static int wm97xx_probe(struct device *dev) { struct wm97xx *wm; + struct wm97xx_pdata *pdata = dev->platform_data; int ret = 0, id = 0; wm = kzalloc(sizeof(struct wm97xx), GFP_KERNEL); @@ -582,6 +583,8 @@ static int wm97xx_probe(struct device *dev) wm->id = wm97xx_reg_read(wm, AC97_VENDOR_ID2); + wm->variant = WM97xx_GENERIC; + dev_info(wm->dev, "detected a wm97%02x codec\n", wm->id & 0xff); switch (wm->id & 0xff) { @@ -656,6 +659,7 @@ static int wm97xx_probe(struct device *dev) } platform_set_drvdata(wm->battery_dev, wm); wm->battery_dev->dev.parent = dev; + wm->battery_dev->dev.platform_data = pdata; ret = platform_device_add(wm->battery_dev); if (ret < 0) goto batt_reg_err; @@ -669,6 +673,7 @@ static int wm97xx_probe(struct device *dev) } platform_set_drvdata(wm->touch_dev, wm); wm->touch_dev->dev.parent = dev; + wm->touch_dev->dev.platform_data = pdata; ret = platform_device_add(wm->touch_dev); if (ret < 0) goto touch_reg_err; diff --git a/drivers/input/touchscreen/zylonite-wm97xx.c b/drivers/input/touchscreen/zylonite-wm97xx.c index 41e4359c277..eca54dbdf49 100644 --- a/drivers/input/touchscreen/zylonite-wm97xx.c +++ b/drivers/input/touchscreen/zylonite-wm97xx.c @@ -96,7 +96,7 @@ static int wm97xx_acc_pen_down(struct wm97xx *wm) /* When the AC97 queue has been drained we need to allow time * to buffer up samples otherwise we end up spinning polling * for samples. The controller can't have a suitably low - * threashold set to use the notifications it gives. + * threshold set to use the notifications it gives. */ msleep(1); |