diff options
Diffstat (limited to 'drivers/input')
44 files changed, 3675 insertions, 3496 deletions
diff --git a/drivers/input/Kconfig b/drivers/input/Kconfig index 07c2cd43109c..23e82e46656d 100644 --- a/drivers/input/Kconfig +++ b/drivers/input/Kconfig @@ -6,7 +6,7 @@ menu "Input device support" depends on !S390 config INPUT - tristate "Generic input layer (needed for keyboard, mouse, ...)" if EMBEDDED + tristate "Generic input layer (needed for keyboard, mouse, ...)" if EXPERT default y help Say Y here if you have any input device (mouse, keyboard, tablet, @@ -67,7 +67,7 @@ config INPUT_SPARSEKMAP comment "Userland interfaces" config INPUT_MOUSEDEV - tristate "Mouse interface" if EMBEDDED + tristate "Mouse interface" if EXPERT default y help Say Y here if you want your mouse to be accessible as char devices @@ -150,7 +150,7 @@ config INPUT_EVBUG module will be called evbug. config INPUT_APMPOWER - tristate "Input Power Event -> APM Bridge" if EMBEDDED + tristate "Input Power Event -> APM Bridge" if EXPERT depends on INPUT && APM_EMULATION help Say Y here if you want suspend key events to trigger a user @@ -161,16 +161,6 @@ config INPUT_APMPOWER To compile this driver as a module, choose M here: the module will be called apm-power. -config XEN_KBDDEV_FRONTEND - tristate "Xen virtual keyboard and mouse support" - depends on XEN_FBDEV_FRONTEND - default y - select XEN_XENBUS_FRONTEND - help - This driver implements the front-end of the Xen virtual - keyboard and mouse device driver. It communicates with a back-end - in another domain. - comment "Input Device Drivers" source "drivers/input/keyboard/Kconfig" diff --git a/drivers/input/Makefile b/drivers/input/Makefile index 09614ce74961..0c789490e0b3 100644 --- a/drivers/input/Makefile +++ b/drivers/input/Makefile @@ -24,5 +24,3 @@ obj-$(CONFIG_INPUT_TOUCHSCREEN) += touchscreen/ obj-$(CONFIG_INPUT_MISC) += misc/ obj-$(CONFIG_INPUT_APMPOWER) += apm-power.o - -obj-$(CONFIG_XEN_KBDDEV_FRONTEND) += xen-kbdfront.o diff --git a/drivers/input/gameport/gameport.c b/drivers/input/gameport/gameport.c index ce57ede6e981..5b8f59d6c3e8 100644 --- a/drivers/input/gameport/gameport.c +++ b/drivers/input/gameport/gameport.c @@ -121,7 +121,7 @@ static int gameport_measure_speed(struct gameport *gameport) } gameport_close(gameport); - return (cpu_data(raw_smp_processor_id()).loops_per_jiffy * + return (this_cpu_read(cpu_info.loops_per_jiffy) * (unsigned long)HZ / (1000 / 50)) / (tx < 1 ? 1 : tx); #else diff --git a/drivers/input/input-polldev.c b/drivers/input/input-polldev.c index 0559e309bac9..3037842a60d8 100644 --- a/drivers/input/input-polldev.c +++ b/drivers/input/input-polldev.c @@ -192,7 +192,7 @@ static struct attribute_group input_polldev_attribute_group = { }; /** - * input_allocate_polled_device - allocated memory polled device + * input_allocate_polled_device - allocate memory for polled device * * The function allocates memory for a polled device and also * for an input device associated with this polled device. @@ -239,7 +239,7 @@ EXPORT_SYMBOL(input_free_polled_device); * with input layer. The device should be allocated with call to * input_allocate_polled_device(). Callers should also set up poll() * method and set up capabilities (id, name, phys, bits) of the - * corresponing input_dev structure. + * corresponding input_dev structure. */ int input_register_polled_device(struct input_polled_dev *dev) { diff --git a/drivers/input/input.c b/drivers/input/input.c index b8894a059f87..d6e8bd8a851c 100644 --- a/drivers/input/input.c +++ b/drivers/input/input.c @@ -26,7 +26,6 @@ #include <linux/device.h> #include <linux/mutex.h> #include <linux/rcupdate.h> -#include <linux/smp_lock.h> #include "input-compat.h" MODULE_AUTHOR("Vojtech Pavlik <vojtech@suse.cz>"); @@ -792,22 +791,9 @@ int input_get_keycode(struct input_dev *dev, struct input_keymap_entry *ke) int retval; spin_lock_irqsave(&dev->event_lock, flags); - - if (dev->getkeycode) { - /* - * Support for legacy drivers, that don't implement the new - * ioctls - */ - u32 scancode = ke->index; - - memcpy(ke->scancode, &scancode, sizeof(scancode)); - ke->len = sizeof(scancode); - retval = dev->getkeycode(dev, scancode, &ke->keycode); - } else { - retval = dev->getkeycode_new(dev, ke); - } - + retval = dev->getkeycode(dev, ke); spin_unlock_irqrestore(&dev->event_lock, flags); + return retval; } EXPORT_SYMBOL(input_get_keycode); @@ -832,35 +818,7 @@ int input_set_keycode(struct input_dev *dev, spin_lock_irqsave(&dev->event_lock, flags); - if (dev->setkeycode) { - /* - * Support for legacy drivers, that don't implement the new - * ioctls - */ - unsigned int scancode; - - retval = input_scancode_to_scalar(ke, &scancode); - if (retval) - goto out; - - /* - * We need to know the old scancode, in order to generate a - * keyup effect, if the set operation happens successfully - */ - if (!dev->getkeycode) { - retval = -EINVAL; - goto out; - } - - retval = dev->getkeycode(dev, scancode, &old_keycode); - if (retval) - goto out; - - retval = dev->setkeycode(dev, scancode, ke->keycode); - } else { - retval = dev->setkeycode_new(dev, ke, &old_keycode); - } - + retval = dev->setkeycode(dev, ke, &old_keycode); if (retval) goto out; @@ -1847,11 +1805,11 @@ int input_register_device(struct input_dev *dev) dev->rep[REP_PERIOD] = 33; } - if (!dev->getkeycode && !dev->getkeycode_new) - dev->getkeycode_new = input_default_getkeycode; + if (!dev->getkeycode) + dev->getkeycode = input_default_getkeycode; - if (!dev->setkeycode && !dev->setkeycode_new) - dev->setkeycode_new = input_default_setkeycode; + if (!dev->setkeycode) + dev->setkeycode = input_default_setkeycode; dev_set_name(&dev->dev, "input%ld", (unsigned long) atomic_inc_return(&input_no) - 1); diff --git a/drivers/input/keyboard/Kconfig b/drivers/input/keyboard/Kconfig index b7dca74eb9be..b16bed038f72 100644 --- a/drivers/input/keyboard/Kconfig +++ b/drivers/input/keyboard/Kconfig @@ -2,7 +2,7 @@ # Input core configuration # menuconfig INPUT_KEYBOARD - bool "Keyboards" if EMBEDDED || !X86 + bool "Keyboards" if EXPERT || !X86 default y help Say Y here, and a list of supported keyboards will be displayed. @@ -57,7 +57,7 @@ config KEYBOARD_ATARI module will be called atakbd. config KEYBOARD_ATKBD - tristate "AT keyboard" if EMBEDDED || !X86 + tristate "AT keyboard" if EXPERT || !X86 default y select SERIO select SERIO_LIBPS2 @@ -112,6 +112,16 @@ config KEYBOARD_ATKBD_RDI_KEYCODES right-hand column will be interpreted as the key shown in the left-hand column. +config KEYBOARD_QT1070 + tristate "Atmel AT42QT1070 Touch Sensor Chip" + depends on I2C + help + Say Y here if you want to use Atmel AT42QT1070 QTouch + Sensor chip as input device. + + To compile this driver as a module, choose M here: + the module will be called qt1070 + config KEYBOARD_QT2160 tristate "Atmel AT42QT2160 Touch Sensor Chip" depends on I2C && EXPERIMENTAL @@ -468,6 +478,16 @@ config KEYBOARD_SPEAR To compile this driver as a module, choose M here: the module will be called spear-keboard. +config KEYBOARD_TC3589X + tristate "TC3589X Keypad support" + depends on MFD_TC3589X + help + Say Y here if you want to use the keypad controller on + TC35892/3 I/O expander. + + To compile this driver as a module, choose M here: the + module will be called tc3589x-keypad. + config KEYBOARD_TNETV107X tristate "TI TNETV107X keypad support" depends on ARCH_DAVINCI_TNETV107X diff --git a/drivers/input/keyboard/Makefile b/drivers/input/keyboard/Makefile index 3456b931e254..878e6c20deb0 100644 --- a/drivers/input/keyboard/Makefile +++ b/drivers/input/keyboard/Makefile @@ -34,6 +34,7 @@ obj-$(CONFIG_KEYBOARD_OMAP4) += omap4-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_QT1070) += qt1070.o obj-$(CONFIG_KEYBOARD_QT2160) += qt2160.o obj-$(CONFIG_KEYBOARD_SAMSUNG) += samsung-keypad.o obj-$(CONFIG_KEYBOARD_SH_KEYSC) += sh_keysc.o @@ -41,6 +42,7 @@ obj-$(CONFIG_KEYBOARD_SPEAR) += spear-keyboard.o obj-$(CONFIG_KEYBOARD_STMPE) += stmpe-keypad.o obj-$(CONFIG_KEYBOARD_STOWAWAY) += stowaway.o obj-$(CONFIG_KEYBOARD_SUNKBD) += sunkbd.o +obj-$(CONFIG_KEYBOARD_TC3589X) += tc3589x-keypad.o obj-$(CONFIG_KEYBOARD_TEGRA) += tegra-kbc.o obj-$(CONFIG_KEYBOARD_TNETV107X) += tnetv107x-keypad.o obj-$(CONFIG_KEYBOARD_TWL4030) += twl4030_keypad.o diff --git a/drivers/input/keyboard/lm8323.c b/drivers/input/keyboard/lm8323.c index f7c2a166576b..b732870ecc89 100644 --- a/drivers/input/keyboard/lm8323.c +++ b/drivers/input/keyboard/lm8323.c @@ -30,6 +30,7 @@ #include <linux/delay.h> #include <linux/input.h> #include <linux/leds.h> +#include <linux/pm.h> #include <linux/i2c/lm8323.h> #include <linux/slab.h> @@ -802,8 +803,9 @@ static int __devexit lm8323_remove(struct i2c_client *client) * We don't need to explicitly suspend the chip, as it already switches off * when there's no activity. */ -static int lm8323_suspend(struct i2c_client *client, pm_message_t mesg) +static int lm8323_suspend(struct device *dev) { + struct i2c_client *client = to_i2c_client(dev); struct lm8323_chip *lm = i2c_get_clientdata(client); int i; @@ -821,8 +823,9 @@ static int lm8323_suspend(struct i2c_client *client, pm_message_t mesg) return 0; } -static int lm8323_resume(struct i2c_client *client) +static int lm8323_resume(struct device *dev) { + struct i2c_client *client = to_i2c_client(dev); struct lm8323_chip *lm = i2c_get_clientdata(client); int i; @@ -839,11 +842,10 @@ static int lm8323_resume(struct i2c_client *client) return 0; } -#else -#define lm8323_suspend NULL -#define lm8323_resume NULL #endif +static SIMPLE_DEV_PM_OPS(lm8323_pm_ops, lm8323_suspend, lm8323_resume); + static const struct i2c_device_id lm8323_id[] = { { "lm8323", 0 }, { } @@ -852,11 +854,10 @@ static const struct i2c_device_id lm8323_id[] = { static struct i2c_driver lm8323_i2c_driver = { .driver = { .name = "lm8323", + .pm = &lm8323_pm_ops, }, .probe = lm8323_probe, .remove = __devexit_p(lm8323_remove), - .suspend = lm8323_suspend, - .resume = lm8323_resume, .id_table = lm8323_id, }; MODULE_DEVICE_TABLE(i2c, lm8323_id); diff --git a/drivers/input/keyboard/max7359_keypad.c b/drivers/input/keyboard/max7359_keypad.c index 9091ff5ea808..5afe35ad24d3 100644 --- a/drivers/input/keyboard/max7359_keypad.c +++ b/drivers/input/keyboard/max7359_keypad.c @@ -17,6 +17,7 @@ #include <linux/i2c.h> #include <linux/slab.h> #include <linux/interrupt.h> +#include <linux/pm.h> #include <linux/input.h> #include <linux/input/matrix_keypad.h> @@ -271,8 +272,10 @@ static int __devexit max7359_remove(struct i2c_client *client) } #ifdef CONFIG_PM -static int max7359_suspend(struct i2c_client *client, pm_message_t mesg) +static int max7359_suspend(struct device *dev) { + struct i2c_client *client = to_i2c_client(dev); + max7359_fall_deepsleep(client); if (device_may_wakeup(&client->dev)) @@ -281,8 +284,10 @@ static int max7359_suspend(struct i2c_client *client, pm_message_t mesg) return 0; } -static int max7359_resume(struct i2c_client *client) +static int max7359_resume(struct device *dev) { + struct i2c_client *client = to_i2c_client(dev); + if (device_may_wakeup(&client->dev)) disable_irq_wake(client->irq); @@ -291,11 +296,10 @@ static int max7359_resume(struct i2c_client *client) return 0; } -#else -#define max7359_suspend NULL -#define max7359_resume NULL #endif +static SIMPLE_DEV_PM_OPS(max7359_pm, max7359_suspend, max7359_resume); + static const struct i2c_device_id max7359_ids[] = { { "max7359", 0 }, { } @@ -305,11 +309,10 @@ MODULE_DEVICE_TABLE(i2c, max7359_ids); static struct i2c_driver max7359_i2c_driver = { .driver = { .name = "max7359", + .pm = &max7359_pm, }, .probe = max7359_probe, .remove = __devexit_p(max7359_remove), - .suspend = max7359_suspend, - .resume = max7359_resume, .id_table = max7359_ids, }; diff --git a/drivers/input/keyboard/mcs_touchkey.c b/drivers/input/keyboard/mcs_touchkey.c index 63b849d7e90b..af1aab324a4c 100644 --- a/drivers/input/keyboard/mcs_touchkey.c +++ b/drivers/input/keyboard/mcs_touchkey.c @@ -1,5 +1,5 @@ /* - * mcs_touchkey.c - Touchkey driver for MELFAS MCS5000/5080 controller + * Touchkey driver for MELFAS MCS5000/5080 controller * * Copyright (C) 2010 Samsung Electronics Co.Ltd * Author: HeungJun Kim <riverful.kim@samsung.com> @@ -19,6 +19,7 @@ #include <linux/input.h> #include <linux/irq.h> #include <linux/slab.h> +#include <linux/pm.h> /* MCS5000 Touchkey */ #define MCS5000_TOUCHKEY_STATUS 0x04 @@ -45,6 +46,8 @@ struct mcs_touchkey_chip { }; struct mcs_touchkey_data { + void (*poweron)(bool); + struct i2c_client *client; struct input_dev *input_dev; struct mcs_touchkey_chip chip; @@ -169,6 +172,11 @@ static int __devinit mcs_touchkey_probe(struct i2c_client *client, if (pdata->cfg_pin) pdata->cfg_pin(); + if (pdata->poweron) { + data->poweron = pdata->poweron; + data->poweron(true); + } + error = request_threaded_irq(client->irq, NULL, mcs_touchkey_interrupt, IRQF_TRIGGER_FALLING, client->dev.driver->name, data); if (error) { @@ -196,12 +204,57 @@ static int __devexit mcs_touchkey_remove(struct i2c_client *client) struct mcs_touchkey_data *data = i2c_get_clientdata(client); free_irq(client->irq, data); + if (data->poweron) + data->poweron(false); input_unregister_device(data->input_dev); kfree(data); return 0; } +static void mcs_touchkey_shutdown(struct i2c_client *client) +{ + struct mcs_touchkey_data *data = i2c_get_clientdata(client); + + if (data->poweron) + data->poweron(false); +} + +#ifdef CONFIG_PM_SLEEP +static int mcs_touchkey_suspend(struct device *dev) +{ + struct mcs_touchkey_data *data = dev_get_drvdata(dev); + struct i2c_client *client = data->client; + + /* Disable the work */ + disable_irq(client->irq); + + /* Finally turn off the power */ + if (data->poweron) + data->poweron(false); + + return 0; +} + +static int mcs_touchkey_resume(struct device *dev) +{ + struct mcs_touchkey_data *data = dev_get_drvdata(dev); + struct i2c_client *client = data->client; + + /* Enable the device first */ + if (data->poweron) + data->poweron(true); + + /* Enable irq again */ + enable_irq(client->irq); + + return 0; +} +#endif + +static SIMPLE_DEV_PM_OPS(mcs_touchkey_pm_ops, + mcs_touchkey_suspend, mcs_touchkey_resume); + static const struct i2c_device_id mcs_touchkey_id[] = { { "mcs5000_touchkey", MCS5000_TOUCHKEY }, { "mcs5080_touchkey", MCS5080_TOUCHKEY }, @@ -213,9 +266,11 @@ static struct i2c_driver mcs_touchkey_driver = { .driver = { .name = "mcs_touchkey", .owner = THIS_MODULE, + .pm = &mcs_touchkey_pm_ops, }, .probe = mcs_touchkey_probe, .remove = __devexit_p(mcs_touchkey_remove), + .shutdown = mcs_touchkey_shutdown, .id_table = mcs_touchkey_id, }; diff --git a/drivers/input/keyboard/omap-keypad.c b/drivers/input/keyboard/omap-keypad.c index a72e61ddca91..0e2a19cb43d8 100644 --- a/drivers/input/keyboard/omap-keypad.c +++ b/drivers/input/keyboard/omap-keypad.c @@ -65,7 +65,6 @@ struct omap_kp { static DECLARE_TASKLET_DISABLED(kp_tasklet, omap_kp_tasklet, 0); -static int *keymap; static unsigned int *row_gpios; static unsigned int *col_gpios; @@ -162,20 +161,11 @@ static void omap_kp_scan_keypad(struct omap_kp *omap_kp, unsigned char *state) } } -static inline int omap_kp_find_key(int col, int row) -{ - int i, key; - - key = KEY(col, row, 0); - for (i = 0; keymap[i] != 0; i++) - if ((keymap[i] & 0xff000000) == key) - return keymap[i] & 0x00ffffff; - return -1; -} - static void omap_kp_tasklet(unsigned long data) { struct omap_kp *omap_kp_data = (struct omap_kp *) data; + unsigned short *keycodes = omap_kp_data->input->keycode; + unsigned int row_shift = get_count_order(omap_kp_data->cols); unsigned char new_state[8], changed, key_down = 0; int col, row; int spurious = 0; @@ -199,7 +189,7 @@ static void omap_kp_tasklet(unsigned long data) row, (new_state[col] & (1 << row)) ? "pressed" : "released"); #else - key = omap_kp_find_key(col, row); + key = keycodes[MATRIX_SCAN_CODE(row, col, row_shift)]; if (key < 0) { printk(KERN_WARNING "omap-keypad: Spurious key event %d-%d\n", @@ -298,13 +288,18 @@ static int __devinit omap_kp_probe(struct platform_device *pdev) struct input_dev *input_dev; struct omap_kp_platform_data *pdata = pdev->dev.platform_data; int i, col_idx, row_idx, irq_idx, ret; + unsigned int row_shift, keycodemax; - if (!pdata->rows || !pdata->cols || !pdata->keymap) { - printk(KERN_ERR "No rows, cols or keymap from pdata\n"); + if (!pdata->rows || !pdata->cols || !pdata->keymap_data) { + printk(KERN_ERR "No rows, cols or keymap_data from pdata\n"); return -EINVAL; } - omap_kp = kzalloc(sizeof(struct omap_kp), GFP_KERNEL); + row_shift = get_count_order(pdata->cols); + keycodemax = pdata->rows << row_shift; + + omap_kp = kzalloc(sizeof(struct omap_kp) + + keycodemax * sizeof(unsigned short), GFP_KERNEL); input_dev = input_allocate_device(); if (!omap_kp || !input_dev) { kfree(omap_kp); @@ -320,7 +315,9 @@ static int __devinit omap_kp_probe(struct platform_device *pdev) if (!cpu_is_omap24xx()) omap_writew(1, OMAP1_MPUIO_BASE + OMAP_MPUIO_KBD_MASKIT); - keymap = pdata->keymap; + input_dev->keycode = &omap_kp[1]; + input_dev->keycodesize = sizeof(unsigned short); + input_dev->keycodemax = keycodemax; if (pdata->rep) __set_bit(EV_REP, input_dev->evbit); @@ -374,8 +371,8 @@ static int __devinit omap_kp_probe(struct platform_device *pdev) /* setup input device */ __set_bit(EV_KEY, input_dev->evbit); - for (i = 0; keymap[i] != 0; i++) - __set_bit(keymap[i] & KEY_MAX, input_dev->keybit); + matrix_keypad_build_keymap(pdata->keymap_data, row_shift, + input_dev->keycode, input_dev->keybit); input_dev->name = "omap-keypad"; input_dev->phys = "omap-keypad/input0"; input_dev->dev.parent = &pdev->dev; @@ -416,7 +413,7 @@ static int __devinit omap_kp_probe(struct platform_device *pdev) return 0; err5: for (i = irq_idx - 1; i >=0; i--) - free_irq(row_gpios[i], 0); + free_irq(row_gpios[i], NULL); err4: input_unregister_device(omap_kp->input); input_dev = NULL; @@ -447,11 +444,11 @@ static int __devexit omap_kp_remove(struct platform_device *pdev) gpio_free(col_gpios[i]); for (i = 0; i < omap_kp->rows; i++) { gpio_free(row_gpios[i]); - free_irq(gpio_to_irq(row_gpios[i]), 0); + free_irq(gpio_to_irq(row_gpios[i]), NULL); } } else { omap_writew(1, OMAP1_MPUIO_BASE + OMAP_MPUIO_KBD_MASKIT); - free_irq(omap_kp->irq, 0); + free_irq(omap_kp->irq, NULL); } del_timer_sync(&omap_kp->timer); diff --git a/drivers/input/keyboard/omap4-keypad.c b/drivers/input/keyboard/omap4-keypad.c index 45bd0977d006..c51a3c4a7feb 100644 --- a/drivers/input/keyboard/omap4-keypad.c +++ b/drivers/input/keyboard/omap4-keypad.c @@ -29,6 +29,7 @@ #include <linux/io.h> #include <linux/input.h> #include <linux/slab.h> +#include <linux/pm_runtime.h> #include <plat/omap4-keypad.h> @@ -80,20 +81,6 @@ struct omap4_keypad { unsigned short keymap[]; }; -static void __devinit omap4_keypad_config(struct omap4_keypad *keypad_data) -{ - __raw_writel(OMAP4_VAL_FUNCTIONALCFG, - keypad_data->base + OMAP4_KBD_CTRL); - __raw_writel(OMAP4_VAL_DEBOUNCINGTIME, - keypad_data->base + OMAP4_KBD_DEBOUNCINGTIME); - __raw_writel(OMAP4_VAL_IRQDISABLE, - keypad_data->base + OMAP4_KBD_IRQSTATUS); - __raw_writel(OMAP4_DEF_IRQENABLE_EVENTEN | OMAP4_DEF_IRQENABLE_LONGKEY, - keypad_data->base + OMAP4_KBD_IRQENABLE); - __raw_writel(OMAP4_DEF_WUP_EVENT_ENA | OMAP4_DEF_WUP_LONG_KEY_ENA, - keypad_data->base + OMAP4_KBD_WAKEUPENABLE); -} - /* Interrupt handler */ static irqreturn_t omap4_keypad_interrupt(int irq, void *dev_id) { @@ -144,6 +131,49 @@ static irqreturn_t omap4_keypad_interrupt(int irq, void *dev_id) return IRQ_HANDLED; } +static int omap4_keypad_open(struct input_dev *input) +{ + struct omap4_keypad *keypad_data = input_get_drvdata(input); + + pm_runtime_get_sync(input->dev.parent); + + disable_irq(keypad_data->irq); + + __raw_writel(OMAP4_VAL_FUNCTIONALCFG, + keypad_data->base + OMAP4_KBD_CTRL); + __raw_writel(OMAP4_VAL_DEBOUNCINGTIME, + keypad_data->base + OMAP4_KBD_DEBOUNCINGTIME); + __raw_writel(OMAP4_VAL_IRQDISABLE, + keypad_data->base + OMAP4_KBD_IRQSTATUS); + __raw_writel(OMAP4_DEF_IRQENABLE_EVENTEN | OMAP4_DEF_IRQENABLE_LONGKEY, + keypad_data->base + OMAP4_KBD_IRQENABLE); + __raw_writel(OMAP4_DEF_WUP_EVENT_ENA | OMAP4_DEF_WUP_LONG_KEY_ENA, + keypad_data->base + OMAP4_KBD_WAKEUPENABLE); + + enable_irq(keypad_data->irq); + + return 0; +} + +static void omap4_keypad_close(struct input_dev *input) +{ + struct omap4_keypad *keypad_data = input_get_drvdata(input); + + disable_irq(keypad_data->irq); + + /* Disable interrupts */ + __raw_writel(OMAP4_VAL_IRQDISABLE, + keypad_data->base + OMAP4_KBD_IRQENABLE); + + /* clear pending interrupts */ + __raw_writel(__raw_readl(keypad_data->base + OMAP4_KBD_IRQSTATUS), + keypad_data->base + OMAP4_KBD_IRQSTATUS); + + enable_irq(keypad_data->irq); + + pm_runtime_put_sync(input->dev.parent); +} + static int __devinit omap4_keypad_probe(struct platform_device *pdev) { const struct omap4_keypad_platform_data *pdata; @@ -225,6 +255,9 @@ static int __devinit omap4_keypad_probe(struct platform_device *pdev) input_dev->id.product = 0x0001; input_dev->id.version = 0x0001; + input_dev->open = omap4_keypad_open; + input_dev->close = omap4_keypad_close; + input_dev->keycode = keypad_data->keymap; input_dev->keycodesize = sizeof(keypad_data->keymap[0]); input_dev->keycodemax = max_keys; @@ -239,8 +272,6 @@ static int __devinit omap4_keypad_probe(struct platform_device *pdev) matrix_keypad_build_keymap(pdata->keymap_data, row_shift, input_dev->keycode, input_dev->keybit); - omap4_keypad_config(keypad_data); - error = request_irq(keypad_data->irq, omap4_keypad_interrupt, IRQF_TRIGGER_RISING, "omap4-keypad", keypad_data); @@ -249,17 +280,19 @@ static int __devinit omap4_keypad_probe(struct platform_device *pdev) goto err_free_input; } + pm_runtime_enable(&pdev->dev); + error = input_register_device(keypad_data->input); if (error < 0) { dev_err(&pdev->dev, "failed to register input device\n"); - goto err_free_irq; + goto err_pm_disable; } - platform_set_drvdata(pdev, keypad_data); return 0; -err_free_irq: +err_pm_disable: + pm_runtime_disable(&pdev->dev); free_irq(keypad_data->irq, keypad_data); err_free_input: input_free_device(input_dev); @@ -278,6 +311,9 @@ static int __devexit omap4_keypad_remove(struct platform_device *pdev) struct resource *res; free_irq(keypad_data->irq, keypad_data); + + pm_runtime_disable(&pdev->dev); + input_unregister_device(keypad_data->input); iounmap(keypad_data->base); diff --git a/drivers/input/keyboard/qt1070.c b/drivers/input/keyboard/qt1070.c new file mode 100644 index 000000000000..fba8404c7297 --- /dev/null +++ b/drivers/input/keyboard/qt1070.c @@ -0,0 +1,276 @@ +/* + * Atmel AT42QT1070 QTouch Sensor Controller + * + * Copyright (C) 2011 Atmel + * + * Authors: Bo Shen <voice.shen@atmel.com> + * + * Base on AT42QT2160 driver by: + * Raphael Derosso Pereira <raphaelpereira@gmail.com> + * Copyright (C) 2009 + * + * 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/module.h> +#include <linux/init.h> +#include <linux/i2c.h> +#include <linux/input.h> +#include <linux/slab.h> +#include <linux/irq.h> +#include <linux/interrupt.h> +#include <linux/jiffies.h> +#include <linux/delay.h> + +/* Address for each register */ +#define CHIP_ID 0x00 +#define QT1070_CHIP_ID 0x2E + +#define FW_VERSION 0x01 +#define QT1070_FW_VERSION 0x15 + +#define DET_STATUS 0x02 + +#define KEY_STATUS 0x03 + +/* Calibrate */ +#define CALIBRATE_CMD 0x38 +#define QT1070_CAL_TIME 200 + +/* Reset */ +#define RESET 0x39 +#define QT1070_RESET_TIME 255 + +/* AT42QT1070 support up to 7 keys */ +static const unsigned short qt1070_key2code[] = { + KEY_0, KEY_1, KEY_2, KEY_3, + KEY_4, KEY_5, KEY_6, +}; + +struct qt1070_data { + struct i2c_client *client; + struct input_dev *input; + unsigned int irq; + unsigned short keycodes[ARRAY_SIZE(qt1070_key2code)]; + u8 last_keys; +}; + +static int qt1070_read(struct i2c_client *client, u8 reg) +{ + int ret; + + ret = i2c_smbus_read_byte_data(client, reg); + if (ret < 0) + dev_err(&client->dev, + "can not read register, returned %d\n", ret); + + return ret; +} + +static int qt1070_write(struct i2c_client *client, u8 reg, u8 data) +{ + int ret; + + ret = i2c_smbus_write_byte_data(client, reg, data); + if (ret < 0) + dev_err(&client->dev, + "can not write register, returned %d\n", ret); + + return ret; +} + +static bool __devinit qt1070_identify(struct i2c_client *client) +{ + int id, ver; + + /* Read Chip ID */ + id = qt1070_read(client, CHIP_ID); + if (id != QT1070_CHIP_ID) { + dev_err(&client->dev, "ID %d not supported\n", id); + return false; + } + + /* Read firmware version */ + ver = qt1070_read(client, FW_VERSION); + if (ver < 0) { + dev_err(&client->dev, "could not read the firmware version\n"); + return false; + } + + dev_info(&client->dev, "AT42QT1070 firmware version %x\n", ver); + + return true; +} + +static irqreturn_t qt1070_interrupt(int irq, void *dev_id) +{ + struct qt1070_data *data = dev_id; + struct i2c_client *client = data->client; + struct input_dev *input = data->input; + int i; + u8 new_keys, keyval, mask = 0x01; + + /* Read the detected status register, thus clearing interrupt */ + qt1070_read(client, DET_STATUS); + + /* Read which key changed */ + new_keys = qt1070_read(client, KEY_STATUS); + + for (i = 0; i < ARRAY_SIZE(qt1070_key2code); i++) { + keyval = new_keys & mask; + if ((data->last_keys & mask) != keyval) + input_report_key(input, data->keycodes[i], keyval); + mask <<= 1; + } + input_sync(input); + + data->last_keys = new_keys; + return IRQ_HANDLED; +} + +static int __devinit qt1070_probe(struct i2c_client *client, + const struct i2c_device_id *id) +{ + struct qt1070_data *data; + struct input_dev *input; + int i; + int err; + + err = i2c_check_functionality(client->adapter, I2C_FUNC_SMBUS_BYTE); + if (!err) { + dev_err(&client->dev, "%s adapter not supported\n", + dev_driver_string(&client->adapter->dev)); + return -ENODEV; + } + + if (!client->irq) { + dev_err(&client->dev, "please assign the irq to this device\n"); + return -EINVAL; + } + + /* Identify the qt1070 chip */ + if (!qt1070_identify(client)) + return -ENODEV; + + data = kzalloc(sizeof(struct qt1070_data), GFP_KERNEL); + input = input_allocate_device(); + if (!data || !input) { + dev_err(&client->dev, "insufficient memory\n"); + err = -ENOMEM; + goto err_free_mem; + } + + data->client = client; + data->input = input; + data->irq = client->irq; + + input->name = "AT42QT1070 QTouch Sensor"; + input->dev.parent = &client->dev; + input->id.bustype = BUS_I2C; + + /* Add the keycode */ + input->keycode = data->keycodes; + input->keycodesize = sizeof(data->keycodes[0]); + input->keycodemax = ARRAY_SIZE(qt1070_key2code); + + __set_bit(EV_KEY, input->evbit); + + for (i = 0; i < ARRAY_SIZE(qt1070_key2code); i++) { + data->keycodes[i] = qt1070_key2code[i]; + __set_bit(qt1070_key2code[i], input->keybit); + } + + /* Calibrate device */ + qt1070_write(client, CALIBRATE_CMD, 1); + msleep(QT1070_CAL_TIME); + + /* Soft reset */ + qt1070_write(client, RESET, 1); + msleep(QT1070_RESET_TIME); + + err = request_threaded_irq(client->irq, NULL, qt1070_interrupt, + IRQF_TRIGGER_NONE, client->dev.driver->name, data); + if (err) { + dev_err(&client->dev, "fail to request irq\n"); + goto err_free_mem; + } + + /* Register the input device */ + err = input_register_device(data->input); + if (err) { + dev_err(&client->dev, "Failed to register input device\n"); + goto err_free_irq; + } + + i2c_set_clientdata(client, data); + + /* Read to clear the chang line */ + qt1070_read(client, DET_STATUS); + + return 0; + +err_free_irq: + free_irq(client->irq, data); +err_free_mem: + input_free_device(input); + kfree(data); + return err; +} + +static int __devexit qt1070_remove(struct i2c_client *client) +{ + struct qt1070_data *data = i2c_get_clientdata(client); + + /* Release IRQ */ + free_irq(client->irq, data); + + input_unregister_device(data->input); + kfree(data); + + i2c_set_clientdata(client, NULL); + + return 0; +} + +static const struct i2c_device_id qt1070_id[] = { + { "qt1070", 0 }, + { }, +}; + +static struct i2c_driver qt1070_driver = { + .driver = { + .name = "qt1070", + .owner = THIS_MODULE, + }, + .id_table = qt1070_id, + .probe = qt1070_probe, + .remove = __devexit_p(qt1070_remove), +}; + +static int __init qt1070_init(void) +{ + return i2c_add_driver(&qt1070_driver); +} +module_init(qt1070_init); + +static void __exit qt1070_exit(void) +{ + i2c_del_driver(&qt1070_driver); +} +module_exit(qt1070_exit); + +MODULE_AUTHOR("Bo Shen <voice.shen@atmel.com>"); +MODULE_DESCRIPTION("Driver for AT42QT1070 QTouch sensor"); +MODULE_LICENSE("GPL"); diff --git a/drivers/input/keyboard/tc3589x-keypad.c b/drivers/input/keyboard/tc3589x-keypad.c new file mode 100644 index 000000000000..99122f59e988 --- /dev/null +++ b/drivers/input/keyboard/tc3589x-keypad.c @@ -0,0 +1,472 @@ +/* + * Copyright (C) ST-Ericsson SA 2010 + * + * Author: Jayeeta Banerjee <jayeeta.banerjee@stericsson.com> + * Author: Sundar Iyer <sundar.iyer@stericsson.com> + * + * License Terms: GNU General Public License, version 2 + * + * TC35893 MFD Keypad Controller driver + */ + +#include <linux/module.h> +#include <linux/init.h> +#include <linux/interrupt.h> +#include <linux/input.h> +#include <linux/platform_device.h> +#include <linux/input/matrix_keypad.h> +#include <linux/i2c.h> +#include <linux/slab.h> +#include <linux/mfd/tc3589x.h> + +/* Maximum supported keypad matrix row/columns size */ +#define TC3589x_MAX_KPROW 8 +#define TC3589x_MAX_KPCOL 12 + +/* keypad related Constants */ +#define TC3589x_MAX_DEBOUNCE_SETTLE 0xFF +#define DEDICATED_KEY_VAL 0xFF + +/* Pull up/down masks */ +#define TC3589x_NO_PULL_MASK 0x0 +#define TC3589x_PULL_DOWN_MASK 0x1 +#define TC3589x_PULL_UP_MASK 0x2 +#define TC3589x_PULLUP_ALL_MASK 0xAA +#define TC3589x_IO_PULL_VAL(index, mask) ((mask)<<((index)%4)*2)) + +/* Bit masks for IOCFG register */ +#define IOCFG_BALLCFG 0x01 +#define IOCFG_IG 0x08 + +#define KP_EVCODE_COL_MASK 0x0F +#define KP_EVCODE_ROW_MASK 0x70 +#define KP_RELEASE_EVT_MASK 0x80 + +#define KP_ROW_SHIFT 4 + +#define KP_NO_VALID_KEY_MASK 0x7F + +/* bit masks for RESTCTRL register */ +#define TC3589x_KBDRST 0x2 +#define TC3589x_IRQRST 0x10 +#define TC3589x_RESET_ALL 0x1B + +/* KBDMFS register bit mask */ +#define TC3589x_KBDMFS_EN 0x1 + +/* CLKEN register bitmask */ +#define KPD_CLK_EN 0x1 + +/* RSTINTCLR register bit mask */ +#define IRQ_CLEAR 0x1 + +/* bit masks for keyboard interrupts*/ +#define TC3589x_EVT_LOSS_INT 0x8 +#define TC3589x_EVT_INT 0x4 +#define TC3589x_KBD_LOSS_INT 0x2 +#define TC3589x_KBD_INT 0x1 + +/* bit masks for keyboard interrupt clear*/ +#define TC3589x_EVT_INT_CLR 0x2 +#define TC3589x_KBD_INT_CLR 0x1 + +#define TC3589x_KBD_KEYMAP_SIZE 64 + +/** + * struct tc_keypad - data structure used by keypad driver + * @input: pointer to input device object + * @board: keypad platform device + * @krow: number of rows + * @kcol: number of coloumns + * @keymap: matrix scan code table for keycodes + */ +struct tc_keypad { + struct tc3589x *tc3589x; + struct input_dev *input; + const struct tc3589x_keypad_platform_data *board; + unsigned int krow; + unsigned int kcol; + unsigned short keymap[TC3589x_KBD_KEYMAP_SIZE]; + bool keypad_stopped; +}; + +static int __devinit tc3589x_keypad_init_key_hardware(struct tc_keypad *keypad) +{ + int ret; + struct tc3589x *tc3589x = keypad->tc3589x; + u8 settle_time = keypad->board->settle_time; + u8 dbounce_period = keypad->board->debounce_period; + u8 rows = keypad->board->krow & 0xf; /* mask out the nibble */ + u8 column = keypad->board->kcol & 0xf; /* mask out the nibble */ + + /* validate platform configurations */ + if (keypad->board->kcol > TC3589x_MAX_KPCOL || + keypad->board->krow > TC3589x_MAX_KPROW || + keypad->board->debounce_period > TC3589x_MAX_DEBOUNCE_SETTLE || + keypad->board->settle_time > TC3589x_MAX_DEBOUNCE_SETTLE) + return -EINVAL; + + /* configure KBDSIZE 4 LSbits for cols and 4 MSbits for rows */ + ret = tc3589x_reg_write(tc3589x, TC3589x_KBDSIZE, + (rows << KP_ROW_SHIFT) | column); + if (ret < 0) + return ret; + + /* configure dedicated key config, no dedicated key selected */ + ret = tc3589x_reg_write(tc3589x, TC3589x_KBCFG_LSB, DEDICATED_KEY_VAL); + if (ret < 0) + return ret; + + ret = tc3589x_reg_write(tc3589x, TC3589x_KBCFG_MSB, DEDICATED_KEY_VAL); + if (ret < 0) + return ret; + + /* Configure settle time */ + ret = tc3589x_reg_write(tc3589x, TC3589x_KBDSETTLE_REG, settle_time); + if (ret < 0) + return ret; + + /* Configure debounce time */ + ret = tc3589x_reg_write(tc3589x, TC3589x_KBDBOUNCE, dbounce_period); + if (ret < 0) + return ret; + + /* Start of initialise keypad GPIOs */ + ret = tc3589x_set_bits(tc3589x, TC3589x_IOCFG, 0x0, IOCFG_IG); + if (ret < 0) + return ret; + + /* Configure pull-up resistors for all row GPIOs */ + ret = tc3589x_reg_write(tc3589x, TC3589x_IOPULLCFG0_LSB, + TC3589x_PULLUP_ALL_MASK); + if (ret < 0) + return ret; + + ret = tc3589x_reg_write(tc3589x, TC3589x_IOPULLCFG0_MSB, + TC3589x_PULLUP_ALL_MASK); + if (ret < 0) + return ret; + + /* Configure pull-up resistors for all column GPIOs */ + ret = tc3589x_reg_write(tc3589x, TC3589x_IOPULLCFG1_LSB, + TC3589x_PULLUP_ALL_MASK); + if (ret < 0) + return ret; + + ret = tc3589x_reg_write(tc3589x, TC3589x_IOPULLCFG1_MSB, + TC3589x_PULLUP_ALL_MASK); + if (ret < 0) + return ret; + + ret = tc3589x_reg_write(tc3589x, TC3589x_IOPULLCFG2_LSB, + TC3589x_PULLUP_ALL_MASK); + + return ret; +} + +#define TC35893_DATA_REGS 4 +#define TC35893_KEYCODE_FIFO_EMPTY 0x7f +#define TC35893_KEYCODE_FIFO_CLEAR 0xff +#define TC35893_KEYPAD_ROW_SHIFT 0x3 + +static irqreturn_t tc3589x_keypad_irq(int irq, void *dev) +{ + struct tc_keypad *keypad = dev; + struct tc3589x *tc3589x = keypad->tc3589x; + u8 i, row_index, col_index, kbd_code, up; + u8 code; + + for (i = 0; i < TC35893_DATA_REGS * 2; i++) { + kbd_code = tc3589x_reg_read(tc3589x, TC3589x_EVTCODE_FIFO); + + /* loop till fifo is empty and no more keys are pressed */ + if (kbd_code == TC35893_KEYCODE_FIFO_EMPTY || + kbd_code == TC35893_KEYCODE_FIFO_CLEAR) + continue; + + /* valid key is found */ + col_index = kbd_code & KP_EVCODE_COL_MASK; + row_index = (kbd_code & KP_EVCODE_ROW_MASK) >> KP_ROW_SHIFT; + code = MATRIX_SCAN_CODE(row_index, col_index, + TC35893_KEYPAD_ROW_SHIFT); + up = kbd_code & KP_RELEASE_EVT_MASK; + + input_event(keypad->input, EV_MSC, MSC_SCAN, code); + input_report_key(keypad->input, keypad->keymap[code], !up); + input_sync(keypad->input); + } + + /* clear IRQ */ + tc3589x_set_bits(tc3589x, TC3589x_KBDIC, + 0x0, TC3589x_EVT_INT_CLR | TC3589x_KBD_INT_CLR); + /* enable IRQ */ + tc3589x_set_bits(tc3589x, TC3589x_KBDMSK, + 0x0, TC3589x_EVT_LOSS_INT | TC3589x_EVT_INT); + + return IRQ_HANDLED; +} + +static int tc3589x_keypad_enable(struct tc_keypad *keypad) +{ + struct tc3589x *tc3589x = keypad->tc3589x; + int ret; + + /* pull the keypad module out of reset */ + ret = tc3589x_set_bits(tc3589x, TC3589x_RSTCTRL, TC3589x_KBDRST, 0x0); + if (ret < 0) + return ret; + + /* configure KBDMFS */ + ret = tc3589x_set_bits(tc3589x, TC3589x_KBDMFS, 0x0, TC3589x_KBDMFS_EN); + if (ret < 0) + return ret; + + /* enable the keypad clock */ + ret = tc3589x_set_bits(tc3589x, TC3589x_CLKEN, 0x0, KPD_CLK_EN); + if (ret < 0) + return ret; + + /* clear pending IRQs */ + ret = tc3589x_set_bits(tc3589x, TC3589x_RSTINTCLR, 0x0, 0x1); + if (ret < 0) + return ret; + + /* enable the IRQs */ + ret = tc3589x_set_bits(tc3589x, TC3589x_KBDMSK, 0x0, + TC3589x_EVT_LOSS_INT | TC3589x_EVT_INT); + if (ret < 0) + return ret; + + keypad->keypad_stopped = false; + + return ret; +} + +static int tc3589x_keypad_disable(struct tc_keypad *keypad) +{ + struct tc3589x *tc3589x = keypad->tc3589x; + int ret; + + /* clear IRQ */ + ret = tc3589x_set_bits(tc3589x, TC3589x_KBDIC, + 0x0, TC3589x_EVT_INT_CLR | TC3589x_KBD_INT_CLR); + if (ret < 0) + return ret; + + /* disable all interrupts */ + ret = tc3589x_set_bits(tc3589x, TC3589x_KBDMSK, + ~(TC3589x_EVT_LOSS_INT | TC3589x_EVT_INT), 0x0); + if (ret < 0) + return ret; + + /* disable the keypad module */ + ret = tc3589x_set_bits(tc3589x, TC3589x_CLKEN, 0x1, 0x0); + if (ret < 0) + return ret; + + /* put the keypad module into reset */ + ret = tc3589x_set_bits(tc3589x, TC3589x_RSTCTRL, TC3589x_KBDRST, 0x1); + + keypad->keypad_stopped = true; + + return ret; +} + +static int tc3589x_keypad_open(struct input_dev *input) +{ + int error; + struct tc_keypad *keypad = input_get_drvdata(input); + + /* enable the keypad module */ + error = tc3589x_keypad_enable(keypad); + if (error < 0) { + dev_err(&input->dev, "failed to enable keypad module\n"); + return error; + } + + error = tc3589x_keypad_init_key_hardware(keypad); + if (error < 0) { + dev_err(&input->dev, "failed to configure keypad module\n"); + return error; + } + + return 0; +} + +static void tc3589x_keypad_close(struct input_dev *input) +{ + struct tc_keypad *keypad = input_get_drvdata(input); + + /* disable the keypad module */ + tc3589x_keypad_disable(keypad); +} + +static int __devinit tc3589x_keypad_probe(struct platform_device *pdev) +{ + struct tc3589x *tc3589x = dev_get_drvdata(pdev->dev.parent); + struct tc_keypad *keypad; + struct input_dev *input; + const struct tc3589x_keypad_platform_data *plat; + int error, irq; + + plat = tc3589x->pdata->keypad; + if (!plat) { + dev_err(&pdev->dev, "invalid keypad platform data\n"); + return -EINVAL; + } + + irq = platform_get_irq(pdev, 0); + if (irq < 0) + return irq; + + keypad = kzalloc(sizeof(struct tc_keypad), GFP_KERNEL); + input = input_allocate_device(); + if (!keypad || !input) { + dev_err(&pdev->dev, "failed to allocate keypad memory\n"); + error = -ENOMEM; + goto err_free_mem; + } + + keypad->board = plat; + keypad->input = input; + keypad->tc3589x = tc3589x; + + input->id.bustype = BUS_I2C; + input->name = pdev->name; + input->dev.parent = &pdev->dev; + + input->keycode = keypad->keymap; + input->keycodesize = sizeof(keypad->keymap[0]); + input->keycodemax = ARRAY_SIZE(keypad->keymap); + + input->open = tc3589x_keypad_open; + input->close = tc3589x_keypad_close; + + input_set_drvdata(input, keypad); + + input_set_capability(input, EV_MSC, MSC_SCAN); + + __set_bit(EV_KEY, input->evbit); + if (!plat->no_autorepeat) + __set_bit(EV_REP, input->evbit); + + matrix_keypad_build_keymap(plat->keymap_data, 0x3, + input->keycode, input->keybit); + + error = request_threaded_irq(irq, NULL, + tc3589x_keypad_irq, plat->irqtype, + "tc3589x-keypad", keypad); + if (error < 0) { + dev_err(&pdev->dev, + "Could not allocate irq %d,error %d\n", + irq, error); + goto err_free_mem; + } + + error = input_register_device(input); + if (error) { + dev_err(&pdev->dev, "Could not register input device\n"); + goto err_free_irq; + } + + /* let platform decide if keypad is a wakeup source or not */ + device_init_wakeup(&pdev->dev, plat->enable_wakeup); + device_set_wakeup_capable(&pdev->dev, plat->enable_wakeup); + + platform_set_drvdata(pdev, keypad); + + return 0; + +err_free_irq: + free_irq(irq, keypad); +err_free_mem: + input_free_device(input); + kfree(keypad); + return error; +} + +static int __devexit tc3589x_keypad_remove(struct platform_device *pdev) +{ + struct tc_keypad *keypad = platform_get_drvdata(pdev); + int irq = platform_get_irq(pdev, 0); + + if (!keypad->keypad_stopped) + tc3589x_keypad_disable(keypad); + + free_irq(irq, keypad); + + input_unregister_device(keypad->input); + + kfree(keypad); + + return 0; +} + +#ifdef CONFIG_PM_SLEEP +static int tc3589x_keypad_suspend(struct device *dev) +{ + struct platform_device *pdev = to_platform_device(dev); + struct tc_keypad *keypad = platform_get_drvdata(pdev); + int irq = platform_get_irq(pdev, 0); + + /* keypad is already off; we do nothing */ + if (keypad->keypad_stopped) + return 0; + + /* if device is not a wakeup source, disable it for powersave */ + if (!device_may_wakeup(&pdev->dev)) + tc3589x_keypad_disable(keypad); + else + enable_irq_wake(irq); + + return 0; +} + +static int tc3589x_keypad_resume(struct device *dev) +{ + struct platform_device *pdev = to_platform_device(dev); + struct tc_keypad *keypad = platform_get_drvdata(pdev); + int irq = platform_get_irq(pdev, 0); + + if (!keypad->keypad_stopped) + return 0; + + /* enable the device to resume normal operations */ + if (!device_may_wakeup(&pdev->dev)) + tc3589x_keypad_enable(keypad); + else + disable_irq_wake(irq); + + return 0; +} +#endif + +static SIMPLE_DEV_PM_OPS(tc3589x_keypad_dev_pm_ops, + tc3589x_keypad_suspend, tc3589x_keypad_resume); + +static struct platform_driver tc3589x_keypad_driver = { + .driver = { + .name = "tc3589x-keypad", + .owner = THIS_MODULE, + .pm = &tc3589x_keypad_dev_pm_ops, + }, + .probe = tc3589x_keypad_probe, + .remove = __devexit_p(tc3589x_keypad_remove), +}; + +static int __init tc3589x_keypad_init(void) +{ + return platform_driver_register(&tc3589x_keypad_driver); +} +module_init(tc3589x_keypad_init); + +static void __exit tc3589x_keypad_exit(void) +{ + return platform_driver_unregister(&tc3589x_keypad_driver); +} +module_exit(tc3589x_keypad_exit); + +MODULE_LICENSE("GPL v2"); +MODULE_AUTHOR("Jayeeta Banerjee/Sundar Iyer"); +MODULE_DESCRIPTION("TC35893 Keypad Driver"); +MODULE_ALIAS("platform:tc3589x-keypad"); diff --git a/drivers/input/keyboard/tca6416-keypad.c b/drivers/input/keyboard/tca6416-keypad.c index 800fbccf1f0f..3afea3f89718 100644 --- a/drivers/input/keyboard/tca6416-keypad.c +++ b/drivers/input/keyboard/tca6416-keypad.c @@ -297,6 +297,7 @@ static int __devinit tca6416_keypad_probe(struct i2c_client *client, } i2c_set_clientdata(client, chip); + device_init_wakeup(&client->dev, 1); return 0; @@ -326,10 +327,37 @@ static int __devexit tca6416_keypad_remove(struct i2c_client *client) return 0; } +#ifdef CONFIG_PM_SLEEP +static int tca6416_keypad_suspend(struct device *dev) +{ + struct i2c_client *client = to_i2c_client(dev); + struct tca6416_keypad_chip *chip = i2c_get_clientdata(client); + + if (device_may_wakeup(dev)) + enable_irq_wake(chip->irqnum); + + return 0; +} + +static int tca6416_keypad_resume(struct device *dev) +{ + struct i2c_client *client = to_i2c_client(dev); + struct tca6416_keypad_chip *chip = i2c_get_clientdata(client); + + if (device_may_wakeup(dev)) + disable_irq_wake(chip->irqnum); + + return 0; +} +#endif + +static SIMPLE_DEV_PM_OPS(tca6416_keypad_dev_pm_ops, + tca6416_keypad_suspend, tca6416_keypad_resume); static struct i2c_driver tca6416_keypad_driver = { .driver = { .name = "tca6416-keypad", + .pm = &tca6416_keypad_dev_pm_ops, }, .probe = tca6416_keypad_probe, .remove = __devexit_p(tca6416_keypad_remove), diff --git a/drivers/input/misc/Kconfig b/drivers/input/misc/Kconfig index f0d90172a65f..f9cf0881b0e3 100644 --- a/drivers/input/misc/Kconfig +++ b/drivers/input/misc/Kconfig @@ -294,24 +294,6 @@ config INPUT_SGI_BTNS To compile this driver as a module, choose M here: the module will be called sgi_btns. -config INPUT_WINBOND_CIR - tristate "Winbond IR remote control" - depends on X86 && PNP - select NEW_LEDS - select LEDS_CLASS - select LEDS_TRIGGERS - select BITREVERSE - help - Say Y here if you want to use the IR remote functionality found - in some Winbond SuperI/O chips. Currently only the WPCD376I - chip is supported (included in some Intel Media series motherboards). - - IR Receive and wake-on-IR from suspend and power-off is currently - supported. - - To compile this driver as a module, choose M here: the module will be - called winbond_cir. - config HP_SDC_RTC tristate "HP SDC Real Time Clock" depends on (GSC || HP300) && SERIO @@ -472,4 +454,17 @@ config INPUT_CMA3000_I2C To compile this driver as a module, choose M here: the module will be called cma3000_d0x_i2c. +config INPUT_XEN_KBDDEV_FRONTEND + tristate "Xen virtual keyboard and mouse support" + depends on XEN_FBDEV_FRONTEND + default y + select XEN_XENBUS_FRONTEND + help + This driver implements the front-end of the Xen virtual + keyboard and mouse device driver. It communicates with a back-end + in another domain. + + To compile this driver as a module, choose M here: the + module will be called xen-kbdfront. + endif diff --git a/drivers/input/misc/Makefile b/drivers/input/misc/Makefile index 35bcfe46555e..e3f7984e6274 100644 --- a/drivers/input/misc/Makefile +++ b/drivers/input/misc/Makefile @@ -40,8 +40,8 @@ obj-$(CONFIG_INPUT_SPARCSPKR) += sparcspkr.o obj-$(CONFIG_INPUT_TWL4030_PWRBUTTON) += twl4030-pwrbutton.o obj-$(CONFIG_INPUT_TWL4030_VIBRA) += twl4030-vibra.o obj-$(CONFIG_INPUT_UINPUT) += uinput.o -obj-$(CONFIG_INPUT_WINBOND_CIR) += winbond-cir.o obj-$(CONFIG_INPUT_WISTRON_BTNS) += wistron_btns.o obj-$(CONFIG_INPUT_WM831X_ON) += wm831x-on.o +obj-$(CONFIG_INPUT_XEN_KBDDEV_FRONTEND) += xen-kbdfront.o obj-$(CONFIG_INPUT_YEALINK) += yealink.o diff --git a/drivers/input/misc/ad714x-i2c.c b/drivers/input/misc/ad714x-i2c.c index 2bef8fa56c94..e21deb1baa8a 100644 --- a/drivers/input/misc/ad714x-i2c.c +++ b/drivers/input/misc/ad714x-i2c.c @@ -10,23 +10,23 @@ #include <linux/i2c.h> #include <linux/module.h> #include <linux/types.h> +#include <linux/pm.h> #include "ad714x.h" #ifdef CONFIG_PM -static int ad714x_i2c_suspend(struct i2c_client *client, pm_message_t message) +static int ad714x_i2c_suspend(struct device *dev) { - return ad714x_disable(i2c_get_clientdata(client)); + return ad714x_disable(i2c_get_clientdata(to_i2c_client(dev))); } -static int ad714x_i2c_resume(struct i2c_client *client) +static int ad714x_i2c_resume(struct device *dev) { - return ad714x_enable(i2c_get_clientdata(client)); + return ad714x_enable(i2c_get_clientdata(to_i2c_client(dev))); } -#else -# define ad714x_i2c_suspend NULL -# define ad714x_i2c_resume NULL #endif +static SIMPLE_DEV_PM_OPS(ad714x_i2c_pm, ad714x_i2c_suspend, ad714x_i2c_resume); + static int ad714x_i2c_write(struct device *dev, unsigned short reg, unsigned short data) { @@ -114,11 +114,10 @@ MODULE_DEVICE_TABLE(i2c, ad714x_id); static struct i2c_driver ad714x_i2c_driver = { .driver = { .name = "ad714x_captouch", + .pm = &ad714x_i2c_pm, }, .probe = ad714x_i2c_probe, .remove = __devexit_p(ad714x_i2c_remove), - .suspend = ad714x_i2c_suspend, - .resume = ad714x_i2c_resume, .id_table = ad714x_id, }; diff --git a/drivers/input/misc/ad714x-spi.c b/drivers/input/misc/ad714x-spi.c index 7f8dedfd1bfe..4120dd549305 100644 --- a/drivers/input/misc/ad714x-spi.c +++ b/drivers/input/misc/ad714x-spi.c @@ -9,6 +9,7 @@ #include <linux/input.h> /* BUS_I2C */ #include <linux/module.h> #include <linux/spi/spi.h> +#include <linux/pm.h> #include <linux/types.h> #include "ad714x.h" @@ -16,20 +17,19 @@ #define AD714x_SPI_READ BIT(10) #ifdef CONFIG_PM -static int ad714x_spi_suspend(struct spi_device *spi, pm_message_t message) +static int ad714x_spi_suspend(struct device *dev) { - return ad714x_disable(spi_get_drvdata(spi)); + return ad714x_disable(spi_get_drvdata(to_spi_device(dev))); } -static int ad714x_spi_resume(struct spi_device *spi) +static int ad714x_spi_resume(struct device *dev) { - return ad714x_enable(spi_get_drvdata(spi)); + return ad714x_enable(spi_get_drvdata(to_spi_device(dev))); } -#else -# define ad714x_spi_suspend NULL -# define ad714x_spi_resume NULL #endif +static SIMPLE_DEV_PM_OPS(ad714x_spi_pm, ad714x_spi_suspend, ad714x_spi_resume); + static int ad714x_spi_read(struct device *dev, unsigned short reg, unsigned short *data) { @@ -79,11 +79,10 @@ static struct spi_driver ad714x_spi_driver = { .driver = { .name = "ad714x_captouch", .owner = THIS_MODULE, + .pm = &ad714x_spi_pm, }, .probe = ad714x_spi_probe, .remove = __devexit_p(ad714x_spi_remove), - .suspend = ad714x_spi_suspend, - .resume = ad714x_spi_resume, }; static __init int ad714x_spi_init(void) diff --git a/drivers/input/misc/adxl34x-i2c.c b/drivers/input/misc/adxl34x-i2c.c index 0779724af7e7..ccacf2bb06a4 100644 --- a/drivers/input/misc/adxl34x-i2c.c +++ b/drivers/input/misc/adxl34x-i2c.c @@ -11,6 +11,7 @@ #include <linux/i2c.h> #include <linux/module.h> #include <linux/types.h> +#include <linux/pm.h> #include "adxl34x.h" static int adxl34x_smbus_read(struct device *dev, unsigned char reg) @@ -105,8 +106,9 @@ static int __devexit adxl34x_i2c_remove(struct i2c_client *client) } #ifdef CONFIG_PM -static int adxl34x_i2c_suspend(struct i2c_client *client, pm_message_t message) +static int adxl34x_i2c_suspend(struct device *dev) { + struct i2c_client *client = to_i2c_client(dev); struct adxl34x *ac = i2c_get_clientdata(client); adxl34x_suspend(ac); @@ -114,19 +116,20 @@ static int adxl34x_i2c_suspend(struct i2c_client *client, pm_message_t message) return 0; } -static int adxl34x_i2c_resume(struct i2c_client *client) +static int adxl34x_i2c_resume(struct device *dev) { + struct i2c_client *client = to_i2c_client(dev); struct adxl34x *ac = i2c_get_clientdata(client); adxl34x_resume(ac); return 0; } -#else -# define adxl34x_i2c_suspend NULL -# define adxl34x_i2c_resume NULL #endif +static SIMPLE_DEV_PM_OPS(adxl34x_i2c_pm, adxl34x_i2c_suspend, + adxl34x_i2c_resume); + static const struct i2c_device_id adxl34x_id[] = { { "adxl34x", 0 }, { } @@ -138,11 +141,10 @@ static struct i2c_driver adxl34x_driver = { .driver = { .name = "adxl34x", .owner = THIS_MODULE, + .pm = &adxl34x_i2c_pm, }, .probe = adxl34x_i2c_probe, .remove = __devexit_p(adxl34x_i2c_remove), - .suspend = adxl34x_i2c_suspend, - .resume = adxl34x_i2c_resume, .id_table = adxl34x_id, }; diff --git a/drivers/input/misc/adxl34x-spi.c b/drivers/input/misc/adxl34x-spi.c index 782de9e89828..f29de22fdda0 100644 --- a/drivers/input/misc/adxl34x-spi.c +++ b/drivers/input/misc/adxl34x-spi.c @@ -10,6 +10,7 @@ #include <linux/input.h> /* BUS_SPI */ #include <linux/module.h> #include <linux/spi/spi.h> +#include <linux/pm.h> #include <linux/types.h> #include "adxl34x.h" @@ -57,7 +58,7 @@ static int adxl34x_spi_read_block(struct device *dev, return (status < 0) ? status : 0; } -static const struct adxl34x_bus_ops adx134x_spi_bops = { +static const struct adxl34x_bus_ops adxl34x_spi_bops = { .bustype = BUS_SPI, .write = adxl34x_spi_write, .read = adxl34x_spi_read, @@ -76,7 +77,7 @@ static int __devinit adxl34x_spi_probe(struct spi_device *spi) ac = adxl34x_probe(&spi->dev, spi->irq, spi->max_speed_hz > MAX_FREQ_NO_FIFODELAY, - &adx134x_spi_bops); + &adxl34x_spi_bops); if (IS_ERR(ac)) return PTR_ERR(ac); @@ -94,8 +95,9 @@ static int __devexit adxl34x_spi_remove(struct spi_device *spi) } #ifdef CONFIG_PM -static int adxl34x_spi_suspend(struct spi_device *spi, pm_message_t message) +static int adxl34x_spi_suspend(struct device *dev) { + struct spi_device *spi = to_spi_device(dev); struct adxl34x *ac = dev_get_drvdata(&spi->dev); adxl34x_suspend(ac); @@ -103,29 +105,29 @@ static int adxl34x_spi_suspend(struct spi_device *spi, pm_message_t message) return 0; } -static int adxl34x_spi_resume(struct spi_device *spi) +static int adxl34x_spi_resume(struct device *dev) { + struct spi_device *spi = to_spi_device(dev); struct adxl34x *ac = dev_get_drvdata(&spi->dev); adxl34x_resume(ac); return 0; } -#else -# define adxl34x_spi_suspend NULL -# define adxl34x_spi_resume NULL #endif +static SIMPLE_DEV_PM_OPS(adxl34x_spi_pm, adxl34x_spi_suspend, + adxl34x_spi_resume); + static struct spi_driver adxl34x_driver = { .driver = { .name = "adxl34x", .bus = &spi_bus_type, .owner = THIS_MODULE, + .pm = &adxl34x_spi_pm, }, .probe = adxl34x_spi_probe, .remove = __devexit_p(adxl34x_spi_remove), - .suspend = adxl34x_spi_suspend, - .resume = adxl34x_spi_resume, }; static int __init adxl34x_spi_init(void) diff --git a/drivers/input/misc/ati_remote2.c b/drivers/input/misc/ati_remote2.c index 0b0e9be63542..9ccdb82d869a 100644 --- a/drivers/input/misc/ati_remote2.c +++ b/drivers/input/misc/ati_remote2.c @@ -612,8 +612,8 @@ static int ati_remote2_input_init(struct ati_remote2 *ar2) idev->open = ati_remote2_open; idev->close = ati_remote2_close; - idev->getkeycode_new = ati_remote2_getkeycode; - idev->setkeycode_new = ati_remote2_setkeycode; + idev->getkeycode = ati_remote2_getkeycode; + idev->setkeycode = ati_remote2_setkeycode; idev->name = ar2->name; idev->phys = ar2->phys; diff --git a/drivers/input/misc/uinput.c b/drivers/input/misc/uinput.c index 82542a1c1098..364bdf43a381 100644 --- a/drivers/input/misc/uinput.c +++ b/drivers/input/misc/uinput.c @@ -347,8 +347,7 @@ static int uinput_setup_device(struct uinput_device *udev, const char __user *bu { struct uinput_user_dev *user_dev; struct input_dev *dev; - char *name; - int i, size; + int i; int retval; if (count != sizeof(struct uinput_user_dev)) @@ -362,30 +361,25 @@ static int uinput_setup_device(struct uinput_device *udev, const char __user *bu dev = udev->dev; - user_dev = kmalloc(sizeof(struct uinput_user_dev), GFP_KERNEL); - if (!user_dev) - return -ENOMEM; - - if (copy_from_user(user_dev, buffer, sizeof(struct uinput_user_dev))) { - retval = -EFAULT; - goto exit; - } + user_dev = memdup_user(buffer, sizeof(struct uinput_user_dev)); + if (IS_ERR(user_dev)) + return PTR_ERR(user_dev); udev->ff_effects_max = user_dev->ff_effects_max; - size = strnlen(user_dev->name, UINPUT_MAX_NAME_SIZE) + 1; - if (!size) { + /* Ensure name is filled in */ + if (!user_dev->name[0]) { retval = -EINVAL; goto exit; } kfree(dev->name); - dev->name = name = kmalloc(size, GFP_KERNEL); - if (!name) { + dev->name = kstrndup(user_dev->name, UINPUT_MAX_NAME_SIZE, + GFP_KERNEL); + if (!dev->name) { retval = -ENOMEM; goto exit; } - strlcpy(name, user_dev->name, size); dev->id.bustype = user_dev->id.bustype; dev->id.vendor = user_dev->id.vendor; @@ -622,7 +616,6 @@ static long uinput_ioctl_handler(struct file *file, unsigned int cmd, struct uinput_ff_upload ff_up; struct uinput_ff_erase ff_erase; struct uinput_request *req; - int length; char *phys; retval = mutex_lock_interruptible(&udev->mutex); @@ -689,24 +682,15 @@ static long uinput_ioctl_handler(struct file *file, unsigned int cmd, retval = -EINVAL; goto out; } - length = strnlen_user(p, 1024); - if (length <= 0) { - retval = -EFAULT; - break; + + phys = strndup_user(p, 1024); + if (IS_ERR(phys)) { + retval = PTR_ERR(phys); + goto out; } + kfree(udev->dev->phys); - udev->dev->phys = phys = kmalloc(length, GFP_KERNEL); - if (!phys) { - retval = -ENOMEM; - break; - } - if (copy_from_user(phys, p, length)) { - udev->dev->phys = NULL; - kfree(phys); - retval = -EFAULT; - break; - } - phys[length - 1] = '\0'; + udev->dev->phys = phys; break; case UI_BEGIN_FF_UPLOAD: diff --git a/drivers/input/misc/winbond-cir.c b/drivers/input/misc/winbond-cir.c deleted file mode 100644 index 64f1de7960c6..000000000000 --- a/drivers/input/misc/winbond-cir.c +++ /dev/null @@ -1,1608 +0,0 @@ -/* - * winbond-cir.c - Driver for the Consumer IR functionality of Winbond - * SuperI/O chips. - * - * Currently supports the Winbond WPCD376i chip (PNP id WEC1022), but - * could probably support others (Winbond WEC102X, NatSemi, etc) - * with minor modifications. - * - * Original Author: David Härdeman <david@hardeman.nu> - * Copyright (C) 2009 David Härdeman <david@hardeman.nu> - * - * Dedicated to Matilda, my newborn daughter, without whose loving attention - * this driver would have been finished in half the time and with a fraction - * of the bugs. - * - * Written using: - * o Winbond WPCD376I datasheet helpfully provided by Jesse Barnes at Intel - * o NatSemi PC87338/PC97338 datasheet (for the serial port stuff) - * o DSDT dumps - * - * Supported features: - * o RC6 - * o Wake-On-CIR functionality - * - * To do: - * o Test NEC and RC5 - * - * Left as an exercise for the reader: - * o Learning (I have neither the hardware, nor the need) - * o IR Transmit (ibid) - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. - */ - -#include <linux/module.h> -#include <linux/pnp.h> -#include <linux/interrupt.h> -#include <linux/timer.h> -#include <linux/input.h> -#include <linux/leds.h> -#include <linux/list.h> -#include <linux/spinlock.h> -#include <linux/pci_ids.h> -#include <linux/io.h> -#include <linux/bitrev.h> -#include <linux/bitops.h> -#include <linux/slab.h> - -#define DRVNAME "winbond-cir" - -/* CEIR Wake-Up Registers, relative to data->wbase */ -#define WBCIR_REG_WCEIR_CTL 0x03 /* CEIR Receiver Control */ -#define WBCIR_REG_WCEIR_STS 0x04 /* CEIR Receiver Status */ -#define WBCIR_REG_WCEIR_EV_EN 0x05 /* CEIR Receiver Event Enable */ -#define WBCIR_REG_WCEIR_CNTL 0x06 /* CEIR Receiver Counter Low */ -#define WBCIR_REG_WCEIR_CNTH 0x07 /* CEIR Receiver Counter High */ -#define WBCIR_REG_WCEIR_INDEX 0x08 /* CEIR Receiver Index */ -#define WBCIR_REG_WCEIR_DATA 0x09 /* CEIR Receiver Data */ -#define WBCIR_REG_WCEIR_CSL 0x0A /* CEIR Re. Compare Strlen */ -#define WBCIR_REG_WCEIR_CFG1 0x0B /* CEIR Re. Configuration 1 */ -#define WBCIR_REG_WCEIR_CFG2 0x0C /* CEIR Re. Configuration 2 */ - -/* CEIR Enhanced Functionality Registers, relative to data->ebase */ -#define WBCIR_REG_ECEIR_CTS 0x00 /* Enhanced IR Control Status */ -#define WBCIR_REG_ECEIR_CCTL 0x01 /* Infrared Counter Control */ -#define WBCIR_REG_ECEIR_CNT_LO 0x02 /* Infrared Counter LSB */ -#define WBCIR_REG_ECEIR_CNT_HI 0x03 /* Infrared Counter MSB */ -#define WBCIR_REG_ECEIR_IREM 0x04 /* Infrared Emitter Status */ - -/* SP3 Banked Registers, relative to data->sbase */ -#define WBCIR_REG_SP3_BSR 0x03 /* Bank Select, all banks */ - /* Bank 0 */ -#define WBCIR_REG_SP3_RXDATA 0x00 /* FIFO RX data (r) */ -#define WBCIR_REG_SP3_TXDATA 0x00 /* FIFO TX data (w) */ -#define WBCIR_REG_SP3_IER 0x01 /* Interrupt Enable */ -#define WBCIR_REG_SP3_EIR 0x02 /* Event Identification (r) */ -#define WBCIR_REG_SP3_FCR 0x02 /* FIFO Control (w) */ -#define WBCIR_REG_SP3_MCR 0x04 /* Mode Control */ -#define WBCIR_REG_SP3_LSR 0x05 /* Link Status */ -#define WBCIR_REG_SP3_MSR 0x06 /* Modem Status */ -#define WBCIR_REG_SP3_ASCR 0x07 /* Aux Status and Control */ - /* Bank 2 */ -#define WBCIR_REG_SP3_BGDL 0x00 /* Baud Divisor LSB */ -#define WBCIR_REG_SP3_BGDH 0x01 /* Baud Divisor MSB */ -#define WBCIR_REG_SP3_EXCR1 0x02 /* Extended Control 1 */ -#define WBCIR_REG_SP3_EXCR2 0x04 /* Extended Control 2 */ -#define WBCIR_REG_SP3_TXFLV 0x06 /* TX FIFO Level */ -#define WBCIR_REG_SP3_RXFLV 0x07 /* RX FIFO Level */ - /* Bank 3 */ -#define WBCIR_REG_SP3_MRID 0x00 /* Module Identification */ -#define WBCIR_REG_SP3_SH_LCR 0x01 /* LCR Shadow */ -#define WBCIR_REG_SP3_SH_FCR 0x02 /* FCR Shadow */ - /* Bank 4 */ -#define WBCIR_REG_SP3_IRCR1 0x02 /* Infrared Control 1 */ - /* Bank 5 */ -#define WBCIR_REG_SP3_IRCR2 0x04 /* Infrared Control 2 */ - /* Bank 6 */ -#define WBCIR_REG_SP3_IRCR3 0x00 /* Infrared Control 3 */ -#define WBCIR_REG_SP3_SIR_PW 0x02 /* SIR Pulse Width */ - /* Bank 7 */ -#define WBCIR_REG_SP3_IRRXDC 0x00 /* IR RX Demod Control */ -#define WBCIR_REG_SP3_IRTXMC 0x01 /* IR TX Mod Control */ -#define WBCIR_REG_SP3_RCCFG 0x02 /* CEIR Config */ -#define WBCIR_REG_SP3_IRCFG1 0x04 /* Infrared Config 1 */ -#define WBCIR_REG_SP3_IRCFG4 0x07 /* Infrared Config 4 */ - -/* - * Magic values follow - */ - -/* No interrupts for WBCIR_REG_SP3_IER and WBCIR_REG_SP3_EIR */ -#define WBCIR_IRQ_NONE 0x00 -/* RX data bit for WBCIR_REG_SP3_IER and WBCIR_REG_SP3_EIR */ -#define WBCIR_IRQ_RX 0x01 -/* Over/Under-flow bit for WBCIR_REG_SP3_IER and WBCIR_REG_SP3_EIR */ -#define WBCIR_IRQ_ERR 0x04 -/* Led enable/disable bit for WBCIR_REG_ECEIR_CTS */ -#define WBCIR_LED_ENABLE 0x80 -/* RX data available bit for WBCIR_REG_SP3_LSR */ -#define WBCIR_RX_AVAIL 0x01 -/* RX disable bit for WBCIR_REG_SP3_ASCR */ -#define WBCIR_RX_DISABLE 0x20 -/* Extended mode enable bit for WBCIR_REG_SP3_EXCR1 */ -#define WBCIR_EXT_ENABLE 0x01 -/* Select compare register in WBCIR_REG_WCEIR_INDEX (bits 5 & 6) */ -#define WBCIR_REGSEL_COMPARE 0x10 -/* Select mask register in WBCIR_REG_WCEIR_INDEX (bits 5 & 6) */ -#define WBCIR_REGSEL_MASK 0x20 -/* Starting address of selected register in WBCIR_REG_WCEIR_INDEX */ -#define WBCIR_REG_ADDR0 0x00 - -/* Valid banks for the SP3 UART */ -enum wbcir_bank { - WBCIR_BANK_0 = 0x00, - WBCIR_BANK_1 = 0x80, - WBCIR_BANK_2 = 0xE0, - WBCIR_BANK_3 = 0xE4, - WBCIR_BANK_4 = 0xE8, - WBCIR_BANK_5 = 0xEC, - WBCIR_BANK_6 = 0xF0, - WBCIR_BANK_7 = 0xF4, -}; - -/* Supported IR Protocols */ -enum wbcir_protocol { - IR_PROTOCOL_RC5 = 0x0, - IR_PROTOCOL_NEC = 0x1, - IR_PROTOCOL_RC6 = 0x2, -}; - -/* Misc */ -#define WBCIR_NAME "Winbond CIR" -#define WBCIR_ID_FAMILY 0xF1 /* Family ID for the WPCD376I */ -#define WBCIR_ID_CHIP 0x04 /* Chip ID for the WPCD376I */ -#define IR_KEYPRESS_TIMEOUT 250 /* FIXME: should be per-protocol? */ -#define INVALID_SCANCODE 0x7FFFFFFF /* Invalid with all protos */ -#define WAKEUP_IOMEM_LEN 0x10 /* Wake-Up I/O Reg Len */ -#define EHFUNC_IOMEM_LEN 0x10 /* Enhanced Func I/O Reg Len */ -#define SP_IOMEM_LEN 0x08 /* Serial Port 3 (IR) Reg Len */ -#define WBCIR_MAX_IDLE_BYTES 10 - -static DEFINE_SPINLOCK(wbcir_lock); -static DEFINE_RWLOCK(keytable_lock); - -struct wbcir_key { - u32 scancode; - unsigned int keycode; -}; - -struct wbcir_keyentry { - struct wbcir_key key; - struct list_head list; -}; - -static struct wbcir_key rc6_def_keymap[] = { - { 0x800F0400, KEY_NUMERIC_0 }, - { 0x800F0401, KEY_NUMERIC_1 }, - { 0x800F0402, KEY_NUMERIC_2 }, - { 0x800F0403, KEY_NUMERIC_3 }, - { 0x800F0404, KEY_NUMERIC_4 }, - { 0x800F0405, KEY_NUMERIC_5 }, - { 0x800F0406, KEY_NUMERIC_6 }, - { 0x800F0407, KEY_NUMERIC_7 }, - { 0x800F0408, KEY_NUMERIC_8 }, - { 0x800F0409, KEY_NUMERIC_9 }, - { 0x800F041D, KEY_NUMERIC_STAR }, - { 0x800F041C, KEY_NUMERIC_POUND }, - { 0x800F0410, KEY_VOLUMEUP }, - { 0x800F0411, KEY_VOLUMEDOWN }, - { 0x800F0412, KEY_CHANNELUP }, - { 0x800F0413, KEY_CHANNELDOWN }, - { 0x800F040E, KEY_MUTE }, - { 0x800F040D, KEY_VENDOR }, /* Vista Logo Key */ - { 0x800F041E, KEY_UP }, - { 0x800F041F, KEY_DOWN }, - { 0x800F0420, KEY_LEFT }, - { 0x800F0421, KEY_RIGHT }, - { 0x800F0422, KEY_OK }, - { 0x800F0423, KEY_ESC }, - { 0x800F040F, KEY_INFO }, - { 0x800F040A, KEY_CLEAR }, - { 0x800F040B, KEY_ENTER }, - { 0x800F045B, KEY_RED }, - { 0x800F045C, KEY_GREEN }, - { 0x800F045D, KEY_YELLOW }, - { 0x800F045E, KEY_BLUE }, - { 0x800F045A, KEY_TEXT }, - { 0x800F0427, KEY_SWITCHVIDEOMODE }, - { 0x800F040C, KEY_POWER }, - { 0x800F0450, KEY_RADIO }, - { 0x800F0448, KEY_PVR }, - { 0x800F0447, KEY_AUDIO }, - { 0x800F0426, KEY_EPG }, - { 0x800F0449, KEY_CAMERA }, - { 0x800F0425, KEY_TV }, - { 0x800F044A, KEY_VIDEO }, - { 0x800F0424, KEY_DVD }, - { 0x800F0416, KEY_PLAY }, - { 0x800F0418, KEY_PAUSE }, - { 0x800F0419, KEY_STOP }, - { 0x800F0414, KEY_FASTFORWARD }, - { 0x800F041A, KEY_NEXT }, - { 0x800F041B, KEY_PREVIOUS }, - { 0x800F0415, KEY_REWIND }, - { 0x800F0417, KEY_RECORD }, -}; - -/* Registers and other state is protected by wbcir_lock */ -struct wbcir_data { - unsigned long wbase; /* Wake-Up Baseaddr */ - unsigned long ebase; /* Enhanced Func. Baseaddr */ - unsigned long sbase; /* Serial Port Baseaddr */ - unsigned int irq; /* Serial Port IRQ */ - - struct input_dev *input_dev; - struct timer_list timer_keyup; - struct led_trigger *rxtrigger; - struct led_trigger *txtrigger; - struct led_classdev led; - - u32 last_scancode; - unsigned int last_keycode; - u8 last_toggle; - u8 keypressed; - unsigned long keyup_jiffies; - unsigned int idle_count; - - /* RX irdata and parsing state */ - unsigned long irdata[30]; - unsigned int irdata_count; - unsigned int irdata_idle; - unsigned int irdata_off; - unsigned int irdata_error; - - /* Protected by keytable_lock */ - struct list_head keytable; -}; - -static enum wbcir_protocol protocol = IR_PROTOCOL_RC6; -module_param(protocol, uint, 0444); -MODULE_PARM_DESC(protocol, "IR protocol to use " - "(0 = RC5, 1 = NEC, 2 = RC6A, default)"); - -static int invert; /* default = 0 */ -module_param(invert, bool, 0444); -MODULE_PARM_DESC(invert, "Invert the signal from the IR receiver"); - -static unsigned int wake_sc = 0x800F040C; -module_param(wake_sc, uint, 0644); -MODULE_PARM_DESC(wake_sc, "Scancode of the power-on IR command"); - -static unsigned int wake_rc6mode = 6; -module_param(wake_rc6mode, uint, 0644); -MODULE_PARM_DESC(wake_rc6mode, "RC6 mode for the power-on command " - "(0 = 0, 6 = 6A, default)"); - - - -/***************************************************************************** - * - * UTILITY FUNCTIONS - * - *****************************************************************************/ - -/* Caller needs to hold wbcir_lock */ -static void -wbcir_set_bits(unsigned long addr, u8 bits, u8 mask) -{ - u8 val; - - val = inb(addr); - val = ((val & ~mask) | (bits & mask)); - outb(val, addr); -} - -/* Selects the register bank for the serial port */ -static inline void -wbcir_select_bank(struct wbcir_data *data, enum wbcir_bank bank) -{ - outb(bank, data->sbase + WBCIR_REG_SP3_BSR); -} - -static enum led_brightness -wbcir_led_brightness_get(struct led_classdev *led_cdev) -{ - struct wbcir_data *data = container_of(led_cdev, - struct wbcir_data, - led); - - if (inb(data->ebase + WBCIR_REG_ECEIR_CTS) & WBCIR_LED_ENABLE) - return LED_FULL; - else - return LED_OFF; -} - -static void -wbcir_led_brightness_set(struct led_classdev *led_cdev, - enum led_brightness brightness) -{ - struct wbcir_data *data = container_of(led_cdev, - struct wbcir_data, - led); - - wbcir_set_bits(data->ebase + WBCIR_REG_ECEIR_CTS, - brightness == LED_OFF ? 0x00 : WBCIR_LED_ENABLE, - WBCIR_LED_ENABLE); -} - -/* Manchester encodes bits to RC6 message cells (see wbcir_parse_rc6) */ -static u8 -wbcir_to_rc6cells(u8 val) -{ - u8 coded = 0x00; - int i; - - val &= 0x0F; - for (i = 0; i < 4; i++) { - if (val & 0x01) - coded |= 0x02 << (i * 2); - else - coded |= 0x01 << (i * 2); - val >>= 1; - } - - return coded; -} - - - -/***************************************************************************** - * - * INPUT FUNCTIONS - * - *****************************************************************************/ - -static unsigned int -wbcir_do_getkeycode(struct wbcir_data *data, u32 scancode) -{ - struct wbcir_keyentry *keyentry; - unsigned int keycode = KEY_RESERVED; - unsigned long flags; - - read_lock_irqsave(&keytable_lock, flags); - - list_for_each_entry(keyentry, &data->keytable, list) { - if (keyentry->key.scancode == scancode) { - keycode = keyentry->key.keycode; - break; - } - } - - read_unlock_irqrestore(&keytable_lock, flags); - return keycode; -} - -static int -wbcir_getkeycode(struct input_dev *dev, - unsigned int scancode, unsigned int *keycode) -{ - struct wbcir_data *data = input_get_drvdata(dev); - - *keycode = wbcir_do_getkeycode(data, scancode); - return 0; -} - -static int -wbcir_setkeycode(struct input_dev *dev, - unsigned int scancode, unsigned int keycode) -{ - struct wbcir_data *data = input_get_drvdata(dev); - struct wbcir_keyentry *keyentry; - struct wbcir_keyentry *new_keyentry; - unsigned long flags; - unsigned int old_keycode = KEY_RESERVED; - - new_keyentry = kmalloc(sizeof(*new_keyentry), GFP_KERNEL); - if (!new_keyentry) - return -ENOMEM; - - write_lock_irqsave(&keytable_lock, flags); - - list_for_each_entry(keyentry, &data->keytable, list) { - if (keyentry->key.scancode != scancode) - continue; - - old_keycode = keyentry->key.keycode; - keyentry->key.keycode = keycode; - - if (keyentry->key.keycode == KEY_RESERVED) { - list_del(&keyentry->list); - kfree(keyentry); - } - - break; - } - - set_bit(keycode, dev->keybit); - - if (old_keycode == KEY_RESERVED) { - new_keyentry->key.scancode = scancode; - new_keyentry->key.keycode = keycode; - list_add(&new_keyentry->list, &data->keytable); - } else { - kfree(new_keyentry); - clear_bit(old_keycode, dev->keybit); - list_for_each_entry(keyentry, &data->keytable, list) { - if (keyentry->key.keycode == old_keycode) { - set_bit(old_keycode, dev->keybit); - break; - } - } - } - - write_unlock_irqrestore(&keytable_lock, flags); - return 0; -} - -/* - * Timer function to report keyup event some time after keydown is - * reported by the ISR. - */ -static void -wbcir_keyup(unsigned long cookie) -{ - struct wbcir_data *data = (struct wbcir_data *)cookie; - unsigned long flags; - - /* - * data->keyup_jiffies is used to prevent a race condition if a - * hardware interrupt occurs at this point and the keyup timer - * event is moved further into the future as a result. - * - * The timer will then be reactivated and this function called - * again in the future. We need to exit gracefully in that case - * to allow the input subsystem to do its auto-repeat magic or - * a keyup event might follow immediately after the keydown. - */ - - spin_lock_irqsave(&wbcir_lock, flags); - - if (time_is_after_eq_jiffies(data->keyup_jiffies) && data->keypressed) { - data->keypressed = 0; - led_trigger_event(data->rxtrigger, LED_OFF); - input_report_key(data->input_dev, data->last_keycode, 0); - input_sync(data->input_dev); - } - - spin_unlock_irqrestore(&wbcir_lock, flags); -} - -static void -wbcir_keydown(struct wbcir_data *data, u32 scancode, u8 toggle) -{ - unsigned int keycode; - - /* Repeat? */ - if (data->last_scancode == scancode && - data->last_toggle == toggle && - data->keypressed) - goto set_timer; - data->last_scancode = scancode; - - /* Do we need to release an old keypress? */ - if (data->keypressed) { - input_report_key(data->input_dev, data->last_keycode, 0); - input_sync(data->input_dev); - data->keypressed = 0; - } - - /* Report scancode */ - input_event(data->input_dev, EV_MSC, MSC_SCAN, (int)scancode); - - /* Do we know this scancode? */ - keycode = wbcir_do_getkeycode(data, scancode); - if (keycode == KEY_RESERVED) - goto set_timer; - - /* Register a keypress */ - input_report_key(data->input_dev, keycode, 1); - data->keypressed = 1; - data->last_keycode = keycode; - data->last_toggle = toggle; - -set_timer: - input_sync(data->input_dev); - led_trigger_event(data->rxtrigger, - data->keypressed ? LED_FULL : LED_OFF); - data->keyup_jiffies = jiffies + msecs_to_jiffies(IR_KEYPRESS_TIMEOUT); - mod_timer(&data->timer_keyup, data->keyup_jiffies); -} - - - -/***************************************************************************** - * - * IR PARSING FUNCTIONS - * - *****************************************************************************/ - -/* Resets all irdata */ -static void -wbcir_reset_irdata(struct wbcir_data *data) -{ - memset(data->irdata, 0, sizeof(data->irdata)); - data->irdata_count = 0; - data->irdata_off = 0; - data->irdata_error = 0; - data->idle_count = 0; -} - -/* Adds one bit of irdata */ -static void -add_irdata_bit(struct wbcir_data *data, int set) -{ - if (data->irdata_count >= sizeof(data->irdata) * 8) { - data->irdata_error = 1; - return; - } - - if (set) - __set_bit(data->irdata_count, data->irdata); - data->irdata_count++; -} - -/* Gets count bits of irdata */ -static u16 -get_bits(struct wbcir_data *data, int count) -{ - u16 val = 0x0; - - if (data->irdata_count - data->irdata_off < count) { - data->irdata_error = 1; - return 0x0; - } - - while (count > 0) { - val <<= 1; - if (test_bit(data->irdata_off, data->irdata)) - val |= 0x1; - count--; - data->irdata_off++; - } - - return val; -} - -/* Reads 16 cells and converts them to a byte */ -static u8 -wbcir_rc6cells_to_byte(struct wbcir_data *data) -{ - u16 raw = get_bits(data, 16); - u8 val = 0x00; - int bit; - - for (bit = 0; bit < 8; bit++) { - switch (raw & 0x03) { - case 0x01: - break; - case 0x02: - val |= (0x01 << bit); - break; - default: - data->irdata_error = 1; - break; - } - raw >>= 2; - } - - return val; -} - -/* Decodes a number of bits from raw RC5 data */ -static u8 -wbcir_get_rc5bits(struct wbcir_data *data, unsigned int count) -{ - u16 raw = get_bits(data, count * 2); - u8 val = 0x00; - int bit; - - for (bit = 0; bit < count; bit++) { - switch (raw & 0x03) { - case 0x01: - val |= (0x01 << bit); - break; - case 0x02: - break; - default: - data->irdata_error = 1; - break; - } - raw >>= 2; - } - - return val; -} - -static void -wbcir_parse_rc6(struct device *dev, struct wbcir_data *data) -{ - /* - * Normal bits are manchester coded as follows: - * cell0 + cell1 = logic "0" - * cell1 + cell0 = logic "1" - * - * The IR pulse has the following components: - * - * Leader - 6 * cell1 - discarded - * Gap - 2 * cell0 - discarded - * Start bit - Normal Coding - always "1" - * Mode Bit 2 - 0 - Normal Coding - * Toggle bit - Normal Coding with double bit time, - * e.g. cell0 + cell0 + cell1 + cell1 - * means logic "0". - * - * The rest depends on the mode, the following modes are known: - * - * MODE 0: - * Address Bit 7 - 0 - Normal Coding - * Command Bit 7 - 0 - Normal Coding - * - * MODE 6: - * The above Toggle Bit is used as a submode bit, 0 = A, 1 = B. - * Submode B is for pointing devices, only remotes using submode A - * are supported. - * - * Customer range bit - 0 => Customer = 7 bits, 0...127 - * 1 => Customer = 15 bits, 32768...65535 - * Customer Bits - Normal Coding - * - * Customer codes are allocated by Philips. The rest of the bits - * are customer dependent. The following is commonly used (and the - * only supported config): - * - * Toggle Bit - Normal Coding - * Address Bit 6 - 0 - Normal Coding - * Command Bit 7 - 0 - Normal Coding - * - * All modes are followed by at least 6 * cell0. - * - * MODE 0 msglen: - * 1 * 2 (start bit) + 3 * 2 (mode) + 2 * 2 (toggle) + - * 8 * 2 (address) + 8 * 2 (command) = - * 44 cells - * - * MODE 6A msglen: - * 1 * 2 (start bit) + 3 * 2 (mode) + 2 * 2 (submode) + - * 1 * 2 (customer range bit) + 7/15 * 2 (customer bits) + - * 1 * 2 (toggle bit) + 7 * 2 (address) + 8 * 2 (command) = - * 60 - 76 cells - */ - u8 mode; - u8 toggle; - u16 customer = 0x0; - u8 address; - u8 command; - u32 scancode; - - /* Leader mark */ - while (get_bits(data, 1) && !data->irdata_error) - /* Do nothing */; - - /* Leader space */ - if (get_bits(data, 1)) { - dev_dbg(dev, "RC6 - Invalid leader space\n"); - return; - } - - /* Start bit */ - if (get_bits(data, 2) != 0x02) { - dev_dbg(dev, "RC6 - Invalid start bit\n"); - return; - } - - /* Mode */ - mode = get_bits(data, 6); - switch (mode) { - case 0x15: /* 010101 = b000 */ - mode = 0; - break; - case 0x29: /* 101001 = b110 */ - mode = 6; - break; - default: - dev_dbg(dev, "RC6 - Invalid mode\n"); - return; - } - - /* Toggle bit / Submode bit */ - toggle = get_bits(data, 4); - switch (toggle) { - case 0x03: - toggle = 0; - break; - case 0x0C: - toggle = 1; - break; - default: - dev_dbg(dev, "RC6 - Toggle bit error\n"); - break; - } - - /* Customer */ - if (mode == 6) { - if (toggle != 0) { - dev_dbg(dev, "RC6B - Not Supported\n"); - return; - } - - customer = wbcir_rc6cells_to_byte(data); - - if (customer & 0x80) { - /* 15 bit customer value */ - customer <<= 8; - customer |= wbcir_rc6cells_to_byte(data); - } - } - - /* Address */ - address = wbcir_rc6cells_to_byte(data); - if (mode == 6) { - toggle = address >> 7; - address &= 0x7F; - } - - /* Command */ - command = wbcir_rc6cells_to_byte(data); - - /* Create scancode */ - scancode = command; - scancode |= address << 8; - scancode |= customer << 16; - - /* Last sanity check */ - if (data->irdata_error) { - dev_dbg(dev, "RC6 - Cell error(s)\n"); - return; - } - - dev_dbg(dev, "IR-RC6 ad 0x%02X cm 0x%02X cu 0x%04X " - "toggle %u mode %u scan 0x%08X\n", - address, - command, - customer, - (unsigned int)toggle, - (unsigned int)mode, - scancode); - - wbcir_keydown(data, scancode, toggle); -} - -static void -wbcir_parse_rc5(struct device *dev, struct wbcir_data *data) -{ - /* - * Bits are manchester coded as follows: - * cell1 + cell0 = logic "0" - * cell0 + cell1 = logic "1" - * (i.e. the reverse of RC6) - * - * Start bit 1 - "1" - discarded - * Start bit 2 - Must be inverted to get command bit 6 - * Toggle bit - * Address Bit 4 - 0 - * Command Bit 5 - 0 - */ - u8 toggle; - u8 address; - u8 command; - u32 scancode; - - /* Start bit 1 */ - if (!get_bits(data, 1)) { - dev_dbg(dev, "RC5 - Invalid start bit\n"); - return; - } - - /* Start bit 2 */ - if (!wbcir_get_rc5bits(data, 1)) - command = 0x40; - else - command = 0x00; - - toggle = wbcir_get_rc5bits(data, 1); - address = wbcir_get_rc5bits(data, 5); - command |= wbcir_get_rc5bits(data, 6); - scancode = address << 7 | command; - - /* Last sanity check */ - if (data->irdata_error) { - dev_dbg(dev, "RC5 - Invalid message\n"); - return; - } - - dev_dbg(dev, "IR-RC5 ad %u cm %u t %u s %u\n", - (unsigned int)address, - (unsigned int)command, - (unsigned int)toggle, - (unsigned int)scancode); - - wbcir_keydown(data, scancode, toggle); -} - -static void -wbcir_parse_nec(struct device *dev, struct wbcir_data *data) -{ - /* - * Each bit represents 560 us. - * - * Leader - 9 ms burst - * Gap - 4.5 ms silence - * Address1 bit 0 - 7 - Address 1 - * Address2 bit 0 - 7 - Address 2 - * Command1 bit 0 - 7 - Command 1 - * Command2 bit 0 - 7 - Command 2 - * - * Note the bit order! - * - * With the old NEC protocol, Address2 was the inverse of Address1 - * and Command2 was the inverse of Command1 and were used as - * an error check. - * - * With NEC extended, Address1 is the LSB of the Address and - * Address2 is the MSB, Command parsing remains unchanged. - * - * A repeat message is coded as: - * Leader - 9 ms burst - * Gap - 2.25 ms silence - * Repeat - 560 us active - */ - u8 address1; - u8 address2; - u8 command1; - u8 command2; - u16 address; - u32 scancode; - - /* Leader mark */ - while (get_bits(data, 1) && !data->irdata_error) - /* Do nothing */; - - /* Leader space */ - if (get_bits(data, 4)) { - dev_dbg(dev, "NEC - Invalid leader space\n"); - return; - } - - /* Repeat? */ - if (get_bits(data, 1)) { - if (!data->keypressed) { - dev_dbg(dev, "NEC - Stray repeat message\n"); - return; - } - - dev_dbg(dev, "IR-NEC repeat s %u\n", - (unsigned int)data->last_scancode); - - wbcir_keydown(data, data->last_scancode, data->last_toggle); - return; - } - - /* Remaining leader space */ - if (get_bits(data, 3)) { - dev_dbg(dev, "NEC - Invalid leader space\n"); - return; - } - - address1 = bitrev8(get_bits(data, 8)); - address2 = bitrev8(get_bits(data, 8)); - command1 = bitrev8(get_bits(data, 8)); - command2 = bitrev8(get_bits(data, 8)); - - /* Sanity check */ - if (data->irdata_error) { - dev_dbg(dev, "NEC - Invalid message\n"); - return; - } - - /* Check command validity */ - if (command1 != ~command2) { - dev_dbg(dev, "NEC - Command bytes mismatch\n"); - return; - } - - /* Check for extended NEC protocol */ - address = address1; - if (address1 != ~address2) - address |= address2 << 8; - - scancode = address << 8 | command1; - - dev_dbg(dev, "IR-NEC ad %u cm %u s %u\n", - (unsigned int)address, - (unsigned int)command1, - (unsigned int)scancode); - - wbcir_keydown(data, scancode, !data->last_toggle); -} - - - -/***************************************************************************** - * - * INTERRUPT FUNCTIONS - * - *****************************************************************************/ - -static irqreturn_t -wbcir_irq_handler(int irqno, void *cookie) -{ - struct pnp_dev *device = cookie; - struct wbcir_data *data = pnp_get_drvdata(device); - struct device *dev = &device->dev; - u8 status; - unsigned long flags; - u8 irdata[8]; - int i; - unsigned int hw; - - spin_lock_irqsave(&wbcir_lock, flags); - - wbcir_select_bank(data, WBCIR_BANK_0); - - status = inb(data->sbase + WBCIR_REG_SP3_EIR); - - if (!(status & (WBCIR_IRQ_RX | WBCIR_IRQ_ERR))) { - spin_unlock_irqrestore(&wbcir_lock, flags); - return IRQ_NONE; - } - - if (status & WBCIR_IRQ_ERR) - data->irdata_error = 1; - - if (!(status & WBCIR_IRQ_RX)) - goto out; - - /* Since RXHDLEV is set, at least 8 bytes are in the FIFO */ - insb(data->sbase + WBCIR_REG_SP3_RXDATA, &irdata[0], 8); - - for (i = 0; i < sizeof(irdata); i++) { - hw = hweight8(irdata[i]); - if (hw > 4) - add_irdata_bit(data, 0); - else - add_irdata_bit(data, 1); - - if (hw == 8) - data->idle_count++; - else - data->idle_count = 0; - } - - if (data->idle_count > WBCIR_MAX_IDLE_BYTES) { - /* Set RXINACTIVE... */ - outb(WBCIR_RX_DISABLE, data->sbase + WBCIR_REG_SP3_ASCR); - - /* ...and drain the FIFO */ - while (inb(data->sbase + WBCIR_REG_SP3_LSR) & WBCIR_RX_AVAIL) - inb(data->sbase + WBCIR_REG_SP3_RXDATA); - - dev_dbg(dev, "IRDATA:\n"); - for (i = 0; i < data->irdata_count; i += BITS_PER_LONG) - dev_dbg(dev, "0x%08lX\n", data->irdata[i/BITS_PER_LONG]); - - switch (protocol) { - case IR_PROTOCOL_RC5: - wbcir_parse_rc5(dev, data); - break; - case IR_PROTOCOL_RC6: - wbcir_parse_rc6(dev, data); - break; - case IR_PROTOCOL_NEC: - wbcir_parse_nec(dev, data); - break; - } - - wbcir_reset_irdata(data); - } - -out: - spin_unlock_irqrestore(&wbcir_lock, flags); - return IRQ_HANDLED; -} - - - -/***************************************************************************** - * - * SETUP/INIT/SUSPEND/RESUME FUNCTIONS - * - *****************************************************************************/ - -static void -wbcir_shutdown(struct pnp_dev *device) -{ - struct device *dev = &device->dev; - struct wbcir_data *data = pnp_get_drvdata(device); - int do_wake = 1; - u8 match[11]; - u8 mask[11]; - u8 rc6_csl = 0; - int i; - - memset(match, 0, sizeof(match)); - memset(mask, 0, sizeof(mask)); - - if (wake_sc == INVALID_SCANCODE || !device_may_wakeup(dev)) { - do_wake = 0; - goto finish; - } - - switch (protocol) { - case IR_PROTOCOL_RC5: - if (wake_sc > 0xFFF) { - do_wake = 0; - dev_err(dev, "RC5 - Invalid wake scancode\n"); - break; - } - - /* Mask = 13 bits, ex toggle */ - mask[0] = 0xFF; - mask[1] = 0x17; - - match[0] = (wake_sc & 0x003F); /* 6 command bits */ - match[0] |= (wake_sc & 0x0180) >> 1; /* 2 address bits */ - match[1] = (wake_sc & 0x0E00) >> 9; /* 3 address bits */ - if (!(wake_sc & 0x0040)) /* 2nd start bit */ - match[1] |= 0x10; - - break; - - case IR_PROTOCOL_NEC: - if (wake_sc > 0xFFFFFF) { - do_wake = 0; - dev_err(dev, "NEC - Invalid wake scancode\n"); - break; - } - - mask[0] = mask[1] = mask[2] = mask[3] = 0xFF; - - match[1] = bitrev8((wake_sc & 0xFF)); - match[0] = ~match[1]; - - match[3] = bitrev8((wake_sc & 0xFF00) >> 8); - if (wake_sc > 0xFFFF) - match[2] = bitrev8((wake_sc & 0xFF0000) >> 16); - else - match[2] = ~match[3]; - - break; - - case IR_PROTOCOL_RC6: - - if (wake_rc6mode == 0) { - if (wake_sc > 0xFFFF) { - do_wake = 0; - dev_err(dev, "RC6 - Invalid wake scancode\n"); - break; - } - - /* Command */ - match[0] = wbcir_to_rc6cells(wake_sc >> 0); - mask[0] = 0xFF; - match[1] = wbcir_to_rc6cells(wake_sc >> 4); - mask[1] = 0xFF; - - /* Address */ - match[2] = wbcir_to_rc6cells(wake_sc >> 8); - mask[2] = 0xFF; - match[3] = wbcir_to_rc6cells(wake_sc >> 12); - mask[3] = 0xFF; - - /* Header */ - match[4] = 0x50; /* mode1 = mode0 = 0, ignore toggle */ - mask[4] = 0xF0; - match[5] = 0x09; /* start bit = 1, mode2 = 0 */ - mask[5] = 0x0F; - - rc6_csl = 44; - - } else if (wake_rc6mode == 6) { - i = 0; - - /* Command */ - match[i] = wbcir_to_rc6cells(wake_sc >> 0); - mask[i++] = 0xFF; - match[i] = wbcir_to_rc6cells(wake_sc >> 4); - mask[i++] = 0xFF; - - /* Address + Toggle */ - match[i] = wbcir_to_rc6cells(wake_sc >> 8); - mask[i++] = 0xFF; - match[i] = wbcir_to_rc6cells(wake_sc >> 12); - mask[i++] = 0x3F; - - /* Customer bits 7 - 0 */ - match[i] = wbcir_to_rc6cells(wake_sc >> 16); - mask[i++] = 0xFF; - match[i] = wbcir_to_rc6cells(wake_sc >> 20); - mask[i++] = 0xFF; - - if (wake_sc & 0x80000000) { - /* Customer range bit and bits 15 - 8 */ - match[i] = wbcir_to_rc6cells(wake_sc >> 24); - mask[i++] = 0xFF; - match[i] = wbcir_to_rc6cells(wake_sc >> 28); - mask[i++] = 0xFF; - rc6_csl = 76; - } else if (wake_sc <= 0x007FFFFF) { - rc6_csl = 60; - } else { - do_wake = 0; - dev_err(dev, "RC6 - Invalid wake scancode\n"); - break; - } - - /* Header */ - match[i] = 0x93; /* mode1 = mode0 = 1, submode = 0 */ - mask[i++] = 0xFF; - match[i] = 0x0A; /* start bit = 1, mode2 = 1 */ - mask[i++] = 0x0F; - - } else { - do_wake = 0; - dev_err(dev, "RC6 - Invalid wake mode\n"); - } - - break; - - default: - do_wake = 0; - break; - } - -finish: - if (do_wake) { - /* Set compare and compare mask */ - wbcir_set_bits(data->wbase + WBCIR_REG_WCEIR_INDEX, - WBCIR_REGSEL_COMPARE | WBCIR_REG_ADDR0, - 0x3F); - outsb(data->wbase + WBCIR_REG_WCEIR_DATA, match, 11); - wbcir_set_bits(data->wbase + WBCIR_REG_WCEIR_INDEX, - WBCIR_REGSEL_MASK | WBCIR_REG_ADDR0, - 0x3F); - outsb(data->wbase + WBCIR_REG_WCEIR_DATA, mask, 11); - - /* RC6 Compare String Len */ - outb(rc6_csl, data->wbase + WBCIR_REG_WCEIR_CSL); - - /* Clear status bits NEC_REP, BUFF, MSG_END, MATCH */ - wbcir_set_bits(data->wbase + WBCIR_REG_WCEIR_STS, 0x17, 0x17); - - /* Clear BUFF_EN, Clear END_EN, Set MATCH_EN */ - wbcir_set_bits(data->wbase + WBCIR_REG_WCEIR_EV_EN, 0x01, 0x07); - - /* Set CEIR_EN */ - wbcir_set_bits(data->wbase + WBCIR_REG_WCEIR_CTL, 0x01, 0x01); - - } else { - /* Clear BUFF_EN, Clear END_EN, Clear MATCH_EN */ - wbcir_set_bits(data->wbase + WBCIR_REG_WCEIR_EV_EN, 0x00, 0x07); - - /* Clear CEIR_EN */ - wbcir_set_bits(data->wbase + WBCIR_REG_WCEIR_CTL, 0x00, 0x01); - } - - /* Disable interrupts */ - wbcir_select_bank(data, WBCIR_BANK_0); - outb(WBCIR_IRQ_NONE, data->sbase + WBCIR_REG_SP3_IER); - - /* - * ACPI will set the HW disable bit for SP3 which means that the - * output signals are left in an undefined state which may cause - * spurious interrupts which we need to ignore until the hardware - * is reinitialized. - */ - disable_irq(data->irq); -} - -static int -wbcir_suspend(struct pnp_dev *device, pm_message_t state) -{ - wbcir_shutdown(device); - return 0; -} - -static void -wbcir_init_hw(struct wbcir_data *data) -{ - u8 tmp; - - /* Disable interrupts */ - wbcir_select_bank(data, WBCIR_BANK_0); - outb(WBCIR_IRQ_NONE, data->sbase + WBCIR_REG_SP3_IER); - - /* Set PROT_SEL, RX_INV, Clear CEIR_EN (needed for the led) */ - tmp = protocol << 4; - if (invert) - tmp |= 0x08; - outb(tmp, data->wbase + WBCIR_REG_WCEIR_CTL); - - /* Clear status bits NEC_REP, BUFF, MSG_END, MATCH */ - wbcir_set_bits(data->wbase + WBCIR_REG_WCEIR_STS, 0x17, 0x17); - - /* Clear BUFF_EN, Clear END_EN, Clear MATCH_EN */ - wbcir_set_bits(data->wbase + WBCIR_REG_WCEIR_EV_EN, 0x00, 0x07); - - /* Set RC5 cell time to correspond to 36 kHz */ - wbcir_set_bits(data->wbase + WBCIR_REG_WCEIR_CFG1, 0x4A, 0x7F); - - /* Set IRTX_INV */ - if (invert) - outb(0x04, data->ebase + WBCIR_REG_ECEIR_CCTL); - else - outb(0x00, data->ebase + WBCIR_REG_ECEIR_CCTL); - - /* - * Clear IR LED, set SP3 clock to 24Mhz - * set SP3_IRRX_SW to binary 01, helpfully not documented - */ - outb(0x10, data->ebase + WBCIR_REG_ECEIR_CTS); - - /* Enable extended mode */ - wbcir_select_bank(data, WBCIR_BANK_2); - outb(WBCIR_EXT_ENABLE, data->sbase + WBCIR_REG_SP3_EXCR1); - - /* - * Configure baud generator, IR data will be sampled at - * a bitrate of: (24Mhz * prescaler) / (divisor * 16). - * - * The ECIR registers include a flag to change the - * 24Mhz clock freq to 48Mhz. - * - * It's not documented in the specs, but fifo levels - * other than 16 seems to be unsupported. - */ - - /* prescaler 1.0, tx/rx fifo lvl 16 */ - outb(0x30, data->sbase + WBCIR_REG_SP3_EXCR2); - - /* Set baud divisor to generate one byte per bit/cell */ - switch (protocol) { - case IR_PROTOCOL_RC5: - outb(0xA7, data->sbase + WBCIR_REG_SP3_BGDL); - break; - case IR_PROTOCOL_RC6: - outb(0x53, data->sbase + WBCIR_REG_SP3_BGDL); - break; - case IR_PROTOCOL_NEC: - outb(0x69, data->sbase + WBCIR_REG_SP3_BGDL); - break; - } - outb(0x00, data->sbase + WBCIR_REG_SP3_BGDH); - - /* Set CEIR mode */ - wbcir_select_bank(data, WBCIR_BANK_0); - outb(0xC0, data->sbase + WBCIR_REG_SP3_MCR); - inb(data->sbase + WBCIR_REG_SP3_LSR); /* Clear LSR */ - inb(data->sbase + WBCIR_REG_SP3_MSR); /* Clear MSR */ - - /* Disable RX demod, run-length encoding/decoding, set freq span */ - wbcir_select_bank(data, WBCIR_BANK_7); - outb(0x10, data->sbase + WBCIR_REG_SP3_RCCFG); - - /* Disable timer */ - wbcir_select_bank(data, WBCIR_BANK_4); - outb(0x00, data->sbase + WBCIR_REG_SP3_IRCR1); - - /* Enable MSR interrupt, Clear AUX_IRX */ - wbcir_select_bank(data, WBCIR_BANK_5); - outb(0x00, data->sbase + WBCIR_REG_SP3_IRCR2); - - /* Disable CRC */ - wbcir_select_bank(data, WBCIR_BANK_6); - outb(0x20, data->sbase + WBCIR_REG_SP3_IRCR3); - - /* Set RX/TX (de)modulation freq, not really used */ - wbcir_select_bank(data, WBCIR_BANK_7); - outb(0xF2, data->sbase + WBCIR_REG_SP3_IRRXDC); - outb(0x69, data->sbase + WBCIR_REG_SP3_IRTXMC); - - /* Set invert and pin direction */ - if (invert) - outb(0x10, data->sbase + WBCIR_REG_SP3_IRCFG4); - else - outb(0x00, data->sbase + WBCIR_REG_SP3_IRCFG4); - - /* Set FIFO thresholds (RX = 8, TX = 3), reset RX/TX */ - wbcir_select_bank(data, WBCIR_BANK_0); - outb(0x97, data->sbase + WBCIR_REG_SP3_FCR); - - /* Clear AUX status bits */ - outb(0xE0, data->sbase + WBCIR_REG_SP3_ASCR); - - /* Enable interrupts */ - wbcir_reset_irdata(data); - outb(WBCIR_IRQ_RX | WBCIR_IRQ_ERR, data->sbase + WBCIR_REG_SP3_IER); -} - -static int -wbcir_resume(struct pnp_dev *device) -{ - struct wbcir_data *data = pnp_get_drvdata(device); - - wbcir_init_hw(data); - enable_irq(data->irq); - - return 0; -} - -static int __devinit -wbcir_probe(struct pnp_dev *device, const struct pnp_device_id *dev_id) -{ - struct device *dev = &device->dev; - struct wbcir_data *data; - int err; - - if (!(pnp_port_len(device, 0) == EHFUNC_IOMEM_LEN && - pnp_port_len(device, 1) == WAKEUP_IOMEM_LEN && - pnp_port_len(device, 2) == SP_IOMEM_LEN)) { - dev_err(dev, "Invalid resources\n"); - return -ENODEV; - } - - data = kzalloc(sizeof(*data), GFP_KERNEL); - if (!data) { - err = -ENOMEM; - goto exit; - } - - pnp_set_drvdata(device, data); - - data->ebase = pnp_port_start(device, 0); - data->wbase = pnp_port_start(device, 1); - data->sbase = pnp_port_start(device, 2); - data->irq = pnp_irq(device, 0); - - if (data->wbase == 0 || data->ebase == 0 || - data->sbase == 0 || data->irq == 0) { - err = -ENODEV; - dev_err(dev, "Invalid resources\n"); - goto exit_free_data; - } - - dev_dbg(&device->dev, "Found device " - "(w: 0x%lX, e: 0x%lX, s: 0x%lX, i: %u)\n", - data->wbase, data->ebase, data->sbase, data->irq); - - if (!request_region(data->wbase, WAKEUP_IOMEM_LEN, DRVNAME)) { - dev_err(dev, "Region 0x%lx-0x%lx already in use!\n", - data->wbase, data->wbase + WAKEUP_IOMEM_LEN - 1); - err = -EBUSY; - goto exit_free_data; - } - - if (!request_region(data->ebase, EHFUNC_IOMEM_LEN, DRVNAME)) { - dev_err(dev, "Region 0x%lx-0x%lx already in use!\n", - data->ebase, data->ebase + EHFUNC_IOMEM_LEN - 1); - err = -EBUSY; - goto exit_release_wbase; - } - - if (!request_region(data->sbase, SP_IOMEM_LEN, DRVNAME)) { - dev_err(dev, "Region 0x%lx-0x%lx already in use!\n", - data->sbase, data->sbase + SP_IOMEM_LEN - 1); - err = -EBUSY; - goto exit_release_ebase; - } - - err = request_irq(data->irq, wbcir_irq_handler, - IRQF_DISABLED, DRVNAME, device); - if (err) { - dev_err(dev, "Failed to claim IRQ %u\n", data->irq); - err = -EBUSY; - goto exit_release_sbase; - } - - led_trigger_register_simple("cir-tx", &data->txtrigger); - if (!data->txtrigger) { - err = -ENOMEM; - goto exit_free_irq; - } - - led_trigger_register_simple("cir-rx", &data->rxtrigger); - if (!data->rxtrigger) { - err = -ENOMEM; - goto exit_unregister_txtrigger; - } - - data->led.name = "cir::activity"; - data->led.default_trigger = "cir-rx"; - data->led.brightness_set = wbcir_led_brightness_set; - data->led.brightness_get = wbcir_led_brightness_get; - err = led_classdev_register(&device->dev, &data->led); - if (err) - goto exit_unregister_rxtrigger; - - data->input_dev = input_allocate_device(); - if (!data->input_dev) { - err = -ENOMEM; - goto exit_unregister_led; - } - - data->input_dev->evbit[0] = BIT(EV_KEY); - data->input_dev->name = WBCIR_NAME; - data->input_dev->phys = "wbcir/cir0"; - data->input_dev->id.bustype = BUS_HOST; - data->input_dev->id.vendor = PCI_VENDOR_ID_WINBOND; - data->input_dev->id.product = WBCIR_ID_FAMILY; - data->input_dev->id.version = WBCIR_ID_CHIP; - data->input_dev->getkeycode = wbcir_getkeycode; - data->input_dev->setkeycode = wbcir_setkeycode; - input_set_capability(data->input_dev, EV_MSC, MSC_SCAN); - input_set_drvdata(data->input_dev, data); - - err = input_register_device(data->input_dev); - if (err) - goto exit_free_input; - - data->last_scancode = INVALID_SCANCODE; - INIT_LIST_HEAD(&data->keytable); - setup_timer(&data->timer_keyup, wbcir_keyup, (unsigned long)data); - - /* Load default keymaps */ - if (protocol == IR_PROTOCOL_RC6) { - int i; - for (i = 0; i < ARRAY_SIZE(rc6_def_keymap); i++) { - err = wbcir_setkeycode(data->input_dev, - (int)rc6_def_keymap[i].scancode, - (int)rc6_def_keymap[i].keycode); - if (err) - goto exit_unregister_keys; - } - } - - device_init_wakeup(&device->dev, 1); - - wbcir_init_hw(data); - - return 0; - -exit_unregister_keys: - if (!list_empty(&data->keytable)) { - struct wbcir_keyentry *key; - struct wbcir_keyentry *keytmp; - - list_for_each_entry_safe(key, keytmp, &data->keytable, list) { - list_del(&key->list); - kfree(key); - } - } - input_unregister_device(data->input_dev); - /* Can't call input_free_device on an unregistered device */ - data->input_dev = NULL; -exit_free_input: - input_free_device(data->input_dev); -exit_unregister_led: - led_classdev_unregister(&data->led); -exit_unregister_rxtrigger: - led_trigger_unregister_simple(data->rxtrigger); -exit_unregister_txtrigger: - led_trigger_unregister_simple(data->txtrigger); -exit_free_irq: - free_irq(data->irq, device); -exit_release_sbase: - release_region(data->sbase, SP_IOMEM_LEN); -exit_release_ebase: - release_region(data->ebase, EHFUNC_IOMEM_LEN); -exit_release_wbase: - release_region(data->wbase, WAKEUP_IOMEM_LEN); -exit_free_data: - kfree(data); - pnp_set_drvdata(device, NULL); -exit: - return err; -} - -static void __devexit -wbcir_remove(struct pnp_dev *device) -{ - struct wbcir_data *data = pnp_get_drvdata(device); - struct wbcir_keyentry *key; - struct wbcir_keyentry *keytmp; - - /* Disable interrupts */ - wbcir_select_bank(data, WBCIR_BANK_0); - outb(WBCIR_IRQ_NONE, data->sbase + WBCIR_REG_SP3_IER); - - del_timer_sync(&data->timer_keyup); - - free_irq(data->irq, device); - - /* Clear status bits NEC_REP, BUFF, MSG_END, MATCH */ - wbcir_set_bits(data->wbase + WBCIR_REG_WCEIR_STS, 0x17, 0x17); - - /* Clear CEIR_EN */ - wbcir_set_bits(data->wbase + WBCIR_REG_WCEIR_CTL, 0x00, 0x01); - - /* Clear BUFF_EN, END_EN, MATCH_EN */ - wbcir_set_bits(data->wbase + WBCIR_REG_WCEIR_EV_EN, 0x00, 0x07); - - /* This will generate a keyup event if necessary */ - input_unregister_device(data->input_dev); - - led_trigger_unregister_simple(data->rxtrigger); - led_trigger_unregister_simple(data->txtrigger); - led_classdev_unregister(&data->led); - - /* This is ok since &data->led isn't actually used */ - wbcir_led_brightness_set(&data->led, LED_OFF); - - release_region(data->wbase, WAKEUP_IOMEM_LEN); - release_region(data->ebase, EHFUNC_IOMEM_LEN); - release_region(data->sbase, SP_IOMEM_LEN); - - list_for_each_entry_safe(key, keytmp, &data->keytable, list) { - list_del(&key->list); - kfree(key); - } - - kfree(data); - - pnp_set_drvdata(device, NULL); -} - -static const struct pnp_device_id wbcir_ids[] = { - { "WEC1022", 0 }, - { "", 0 } -}; -MODULE_DEVICE_TABLE(pnp, wbcir_ids); - -static struct pnp_driver wbcir_driver = { - .name = WBCIR_NAME, - .id_table = wbcir_ids, - .probe = wbcir_probe, - .remove = __devexit_p(wbcir_remove), - .suspend = wbcir_suspend, - .resume = wbcir_resume, - .shutdown = wbcir_shutdown -}; - -static int __init -wbcir_init(void) -{ - int ret; - - switch (protocol) { - case IR_PROTOCOL_RC5: - case IR_PROTOCOL_NEC: - case IR_PROTOCOL_RC6: - break; - default: - printk(KERN_ERR DRVNAME ": Invalid protocol argument\n"); - return -EINVAL; - } - - ret = pnp_register_driver(&wbcir_driver); - if (ret) - printk(KERN_ERR DRVNAME ": Unable to register driver\n"); - - return ret; -} - -static void __exit -wbcir_exit(void) -{ - pnp_unregister_driver(&wbcir_driver); -} - -MODULE_AUTHOR("David Härdeman <david@hardeman.nu>"); -MODULE_DESCRIPTION("Winbond SuperI/O Consumer IR Driver"); -MODULE_LICENSE("GPL"); - -module_init(wbcir_init); -module_exit(wbcir_exit); - - diff --git a/drivers/input/xen-kbdfront.c b/drivers/input/misc/xen-kbdfront.c index 7f85a862ad11..7077f9bf5ead 100644 --- a/drivers/input/xen-kbdfront.c +++ b/drivers/input/misc/xen-kbdfront.c @@ -11,12 +11,6 @@ * more details. */ -/* - * TODO: - * - * Switch to grant tables together with xen-fbfront.c. - */ - #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt #include <linux/kernel.h> @@ -30,6 +24,8 @@ #include <xen/xen.h> #include <xen/events.h> #include <xen/page.h> +#include <xen/grant_table.h> +#include <xen/interface/grant_table.h> #include <xen/interface/io/fbif.h> #include <xen/interface/io/kbdif.h> #include <xen/xenbus.h> @@ -38,6 +34,7 @@ struct xenkbd_info { struct input_dev *kbd; struct input_dev *ptr; struct xenkbd_page *page; + int gref; int irq; struct xenbus_device *xbdev; char phys[32]; @@ -110,7 +107,7 @@ static irqreturn_t input_handler(int rq, void *dev_id) static int __devinit xenkbd_probe(struct xenbus_device *dev, const struct xenbus_device_id *id) { - int ret, i; + int ret, i, abs; struct xenkbd_info *info; struct input_dev *kbd, *ptr; @@ -122,12 +119,18 @@ static int __devinit xenkbd_probe(struct xenbus_device *dev, dev_set_drvdata(&dev->dev, info); info->xbdev = dev; info->irq = -1; + info->gref = -1; snprintf(info->phys, sizeof(info->phys), "xenbus/%s", dev->nodename); info->page = (void *)__get_free_page(GFP_KERNEL | __GFP_ZERO); if (!info->page) goto error_nomem; + if (xenbus_scanf(XBT_NIL, dev->otherend, "feature-abs-pointer", "%d", &abs) < 0) + abs = 0; + if (abs) + xenbus_printf(XBT_NIL, dev->nodename, "request-abs-pointer", "1"); + /* keyboard */ kbd = input_allocate_device(); if (!kbd) @@ -137,11 +140,12 @@ static int __devinit xenkbd_probe(struct xenbus_device *dev, kbd->id.bustype = BUS_PCI; kbd->id.vendor = 0x5853; kbd->id.product = 0xffff; - kbd->evbit[0] = BIT(EV_KEY); + + __set_bit(EV_KEY, kbd->evbit); for (i = KEY_ESC; i < KEY_UNKNOWN; i++) - set_bit(i, kbd->keybit); + __set_bit(i, kbd->keybit); for (i = KEY_OK; i < KEY_MAX; i++) - set_bit(i, kbd->keybit); + __set_bit(i, kbd->keybit); ret = input_register_device(kbd); if (ret) { @@ -160,12 +164,20 @@ static int __devinit xenkbd_probe(struct xenbus_device *dev, ptr->id.bustype = BUS_PCI; ptr->id.vendor = 0x5853; ptr->id.product = 0xfffe; - ptr->evbit[0] = BIT(EV_KEY) | BIT(EV_REL) | BIT(EV_ABS); + + if (abs) { + __set_bit(EV_ABS, ptr->evbit); + input_set_abs_params(ptr, ABS_X, 0, XENFB_WIDTH, 0, 0); + input_set_abs_params(ptr, ABS_Y, 0, XENFB_HEIGHT, 0, 0); + } else { + input_set_capability(ptr, EV_REL, REL_X); + input_set_capability(ptr, EV_REL, REL_Y); + } + input_set_capability(ptr, EV_REL, REL_WHEEL); + + __set_bit(EV_KEY, ptr->evbit); for (i = BTN_LEFT; i <= BTN_TASK; i++) - set_bit(i, ptr->keybit); - ptr->relbit[0] = BIT(REL_X) | BIT(REL_Y) | BIT(REL_WHEEL); - input_set_abs_params(ptr, ABS_X, 0, XENFB_WIDTH, 0, 0); - input_set_abs_params(ptr, ABS_Y, 0, XENFB_HEIGHT, 0, 0); + __set_bit(i, ptr->keybit); ret = input_register_device(ptr); if (ret) { @@ -218,15 +230,20 @@ static int xenkbd_connect_backend(struct xenbus_device *dev, int ret, evtchn; struct xenbus_transaction xbt; + ret = gnttab_grant_foreign_access(dev->otherend_id, + virt_to_mfn(info->page), 0); + if (ret < 0) + return ret; + info->gref = ret; + ret = xenbus_alloc_evtchn(dev, &evtchn); if (ret) - return ret; + goto error_grant; ret = bind_evtchn_to_irqhandler(evtchn, input_handler, 0, dev->devicetype, info); if (ret < 0) { - xenbus_free_evtchn(dev, evtchn); xenbus_dev_fatal(dev, ret, "bind_evtchn_to_irqhandler"); - return ret; + goto error_evtchan; } info->irq = ret; @@ -234,12 +251,15 @@ static int xenkbd_connect_backend(struct xenbus_device *dev, ret = xenbus_transaction_start(&xbt); if (ret) { xenbus_dev_fatal(dev, ret, "starting transaction"); - return ret; + goto error_irqh; } ret = xenbus_printf(xbt, dev->nodename, "page-ref", "%lu", virt_to_mfn(info->page)); if (ret) goto error_xenbus; + ret = xenbus_printf(xbt, dev->nodename, "page-gref", "%u", info->gref); + if (ret) + goto error_xenbus; ret = xenbus_printf(xbt, dev->nodename, "event-channel", "%u", evtchn); if (ret) @@ -249,7 +269,7 @@ static int xenkbd_connect_backend(struct xenbus_device *dev, if (ret == -EAGAIN) goto again; xenbus_dev_fatal(dev, ret, "completing transaction"); - return ret; + goto error_irqh; } xenbus_switch_state(dev, XenbusStateInitialised); @@ -258,6 +278,14 @@ static int xenkbd_connect_backend(struct xenbus_device *dev, error_xenbus: xenbus_transaction_end(xbt, 1); xenbus_dev_fatal(dev, ret, "writing xenstore"); + error_irqh: + unbind_from_irqhandler(info->irq, info); + info->irq = -1; + error_evtchan: + xenbus_free_evtchn(dev, evtchn); + error_grant: + gnttab_end_foreign_access_ref(info->gref, 0); + info->gref = -1; return ret; } @@ -266,13 +294,16 @@ static void xenkbd_disconnect_backend(struct xenkbd_info *info) if (info->irq >= 0) unbind_from_irqhandler(info->irq, info); info->irq = -1; + if (info->gref >= 0) + gnttab_end_foreign_access_ref(info->gref, 0); + info->gref = -1; } static void xenkbd_backend_changed(struct xenbus_device *dev, enum xenbus_state backend_state) { struct xenkbd_info *info = dev_get_drvdata(&dev->dev); - int ret, val; + int val; switch (backend_state) { case XenbusStateInitialising: @@ -285,16 +316,6 @@ static void xenkbd_backend_changed(struct xenbus_device *dev, case XenbusStateInitWait: InitWait: - ret = xenbus_scanf(XBT_NIL, info->xbdev->otherend, - "feature-abs-pointer", "%d", &val); - if (ret < 0) - val = 0; - if (val) { - ret = xenbus_printf(XBT_NIL, info->xbdev->nodename, - "request-abs-pointer", "1"); - if (ret) - pr_warning("can't request abs-pointer\n"); - } xenbus_switch_state(dev, XenbusStateConnected); break; diff --git a/drivers/input/mouse/Kconfig b/drivers/input/mouse/Kconfig index bf5fd7f6a313..9c1e6ee83531 100644 --- a/drivers/input/mouse/Kconfig +++ b/drivers/input/mouse/Kconfig @@ -39,7 +39,7 @@ config MOUSE_PS2 module will be called psmouse. config MOUSE_PS2_ALPS - bool "ALPS PS/2 mouse protocol extension" if EMBEDDED + bool "ALPS PS/2 mouse protocol extension" if EXPERT default y depends on MOUSE_PS2 help @@ -49,7 +49,7 @@ config MOUSE_PS2_ALPS If unsure, say Y. config MOUSE_PS2_LOGIPS2PP - bool "Logitech PS/2++ mouse protocol extension" if EMBEDDED + bool "Logitech PS/2++ mouse protocol extension" if EXPERT default y depends on MOUSE_PS2 help @@ -59,7 +59,7 @@ config MOUSE_PS2_LOGIPS2PP If unsure, say Y. config MOUSE_PS2_SYNAPTICS - bool "Synaptics PS/2 mouse protocol extension" if EMBEDDED + bool "Synaptics PS/2 mouse protocol extension" if EXPERT default y depends on MOUSE_PS2 help @@ -69,7 +69,7 @@ config MOUSE_PS2_SYNAPTICS If unsure, say Y. config MOUSE_PS2_LIFEBOOK - bool "Fujitsu Lifebook PS/2 mouse protocol extension" if EMBEDDED + bool "Fujitsu Lifebook PS/2 mouse protocol extension" if EXPERT default y depends on MOUSE_PS2 && X86 && DMI help @@ -79,7 +79,7 @@ config MOUSE_PS2_LIFEBOOK If unsure, say Y. config MOUSE_PS2_TRACKPOINT - bool "IBM Trackpoint PS/2 mouse protocol extension" if EMBEDDED + bool "IBM Trackpoint PS/2 mouse protocol extension" if EXPERT default y depends on MOUSE_PS2 help diff --git a/drivers/input/mouse/bcm5974.c b/drivers/input/mouse/bcm5974.c index d24837210f04..3aead91bacc8 100644 --- a/drivers/input/mouse/bcm5974.c +++ b/drivers/input/mouse/bcm5974.c @@ -55,6 +55,18 @@ #define USB_DEVICE_ID_APPLE_WELLSPRING3_ANSI 0x0236 #define USB_DEVICE_ID_APPLE_WELLSPRING3_ISO 0x0237 #define USB_DEVICE_ID_APPLE_WELLSPRING3_JIS 0x0238 +/* MacbookAir3,2 (unibody), aka wellspring5 */ +#define USB_DEVICE_ID_APPLE_WELLSPRING4_ANSI 0x023f +#define USB_DEVICE_ID_APPLE_WELLSPRING4_ISO 0x0240 +#define USB_DEVICE_ID_APPLE_WELLSPRING4_JIS 0x0241 +/* MacbookAir3,1 (unibody), aka wellspring4 */ +#define USB_DEVICE_ID_APPLE_WELLSPRING4A_ANSI 0x0242 +#define USB_DEVICE_ID_APPLE_WELLSPRING4A_ISO 0x0243 +#define USB_DEVICE_ID_APPLE_WELLSPRING4A_JIS 0x0244 +/* Macbook8 (unibody, March 2011) */ +#define USB_DEVICE_ID_APPLE_WELLSPRING5_ANSI 0x0245 +#define USB_DEVICE_ID_APPLE_WELLSPRING5_ISO 0x0246 +#define USB_DEVICE_ID_APPLE_WELLSPRING5_JIS 0x0247 #define BCM5974_DEVICE(prod) { \ .match_flags = (USB_DEVICE_ID_MATCH_DEVICE | \ @@ -80,6 +92,18 @@ static const struct usb_device_id bcm5974_table[] = { BCM5974_DEVICE(USB_DEVICE_ID_APPLE_WELLSPRING3_ANSI), BCM5974_DEVICE(USB_DEVICE_ID_APPLE_WELLSPRING3_ISO), BCM5974_DEVICE(USB_DEVICE_ID_APPLE_WELLSPRING3_JIS), + /* MacbookAir3,2 */ + BCM5974_DEVICE(USB_DEVICE_ID_APPLE_WELLSPRING4_ANSI), + BCM5974_DEVICE(USB_DEVICE_ID_APPLE_WELLSPRING4_ISO), + BCM5974_DEVICE(USB_DEVICE_ID_APPLE_WELLSPRING4_JIS), + /* MacbookAir3,1 */ + BCM5974_DEVICE(USB_DEVICE_ID_APPLE_WELLSPRING4A_ANSI), + BCM5974_DEVICE(USB_DEVICE_ID_APPLE_WELLSPRING4A_ISO), + BCM5974_DEVICE(USB_DEVICE_ID_APPLE_WELLSPRING4A_JIS), + /* MacbookPro8 */ + BCM5974_DEVICE(USB_DEVICE_ID_APPLE_WELLSPRING5_ANSI), + BCM5974_DEVICE(USB_DEVICE_ID_APPLE_WELLSPRING5_ISO), + BCM5974_DEVICE(USB_DEVICE_ID_APPLE_WELLSPRING5_JIS), /* Terminating entry */ {} }; @@ -234,6 +258,42 @@ static const struct bcm5974_config bcm5974_config_table[] = { { DIM_X, DIM_X / SN_COORD, -4460, 5166 }, { DIM_Y, DIM_Y / SN_COORD, -75, 6700 } }, + { + USB_DEVICE_ID_APPLE_WELLSPRING4_ANSI, + USB_DEVICE_ID_APPLE_WELLSPRING4_ISO, + USB_DEVICE_ID_APPLE_WELLSPRING4_JIS, + HAS_INTEGRATED_BUTTON, + 0x84, sizeof(struct bt_data), + 0x81, TYPE2, FINGER_TYPE2, FINGER_TYPE2 + SIZEOF_ALL_FINGERS, + { DIM_PRESSURE, DIM_PRESSURE / SN_PRESSURE, 0, 300 }, + { DIM_WIDTH, DIM_WIDTH / SN_WIDTH, 0, 2048 }, + { DIM_X, DIM_X / SN_COORD, -4620, 5140 }, + { DIM_Y, DIM_Y / SN_COORD, -150, 6600 } + }, + { + USB_DEVICE_ID_APPLE_WELLSPRING4A_ANSI, + USB_DEVICE_ID_APPLE_WELLSPRING4A_ISO, + USB_DEVICE_ID_APPLE_WELLSPRING4A_JIS, + HAS_INTEGRATED_BUTTON, + 0x84, sizeof(struct bt_data), + 0x81, TYPE2, FINGER_TYPE2, FINGER_TYPE2 + SIZEOF_ALL_FINGERS, + { DIM_PRESSURE, DIM_PRESSURE / SN_PRESSURE, 0, 300 }, + { DIM_WIDTH, DIM_WIDTH / SN_WIDTH, 0, 2048 }, + { DIM_X, DIM_X / SN_COORD, -4616, 5112 }, + { DIM_Y, DIM_Y / SN_COORD, -142, 5234 } + }, + { + USB_DEVICE_ID_APPLE_WELLSPRING5_ANSI, + USB_DEVICE_ID_APPLE_WELLSPRING5_ISO, + USB_DEVICE_ID_APPLE_WELLSPRING5_JIS, + HAS_INTEGRATED_BUTTON, + 0x84, sizeof(struct bt_data), + 0x81, TYPE2, FINGER_TYPE2, FINGER_TYPE2 + SIZEOF_ALL_FINGERS, + { DIM_PRESSURE, DIM_PRESSURE / SN_PRESSURE, 0, 300 }, + { DIM_WIDTH, DIM_WIDTH / SN_WIDTH, 0, 2048 }, + { DIM_X, DIM_X / SN_COORD, -4415, 5050 }, + { DIM_Y, DIM_Y / SN_COORD, -55, 6680 } + }, {} }; diff --git a/drivers/input/mouse/synaptics_i2c.c b/drivers/input/mouse/synaptics_i2c.c index 0ae62f0bcb32..f6aa26d305ed 100644 --- a/drivers/input/mouse/synaptics_i2c.c +++ b/drivers/input/mouse/synaptics_i2c.c @@ -18,6 +18,7 @@ #include <linux/delay.h> #include <linux/workqueue.h> #include <linux/slab.h> +#include <linux/pm.h> #define DRIVER_NAME "synaptics_i2c" /* maximum product id is 15 characters */ @@ -619,8 +620,9 @@ static int __devexit synaptics_i2c_remove(struct i2c_client *client) } #ifdef CONFIG_PM -static int synaptics_i2c_suspend(struct i2c_client *client, pm_message_t mesg) +static int synaptics_i2c_suspend(struct device *dev) { + struct i2c_client *client = to_i2c_client(dev); struct synaptics_i2c *touch = i2c_get_clientdata(client); cancel_delayed_work_sync(&touch->dwork); @@ -631,9 +633,10 @@ static int synaptics_i2c_suspend(struct i2c_client *client, pm_message_t mesg) return 0; } -static int synaptics_i2c_resume(struct i2c_client *client) +static int synaptics_i2c_resume(struct device *dev) { int ret; + struct i2c_client *client = to_i2c_client(dev); struct synaptics_i2c *touch = i2c_get_clientdata(client); ret = synaptics_i2c_reset_config(client); @@ -645,11 +648,11 @@ static int synaptics_i2c_resume(struct i2c_client *client) return 0; } -#else -#define synaptics_i2c_suspend NULL -#define synaptics_i2c_resume NULL #endif +static SIMPLE_DEV_PM_OPS(synaptics_i2c_pm, synaptics_i2c_suspend, + synaptics_i2c_resume); + static const struct i2c_device_id synaptics_i2c_id_table[] = { { "synaptics_i2c", 0 }, { }, @@ -660,13 +663,12 @@ static struct i2c_driver synaptics_i2c_driver = { .driver = { .name = DRIVER_NAME, .owner = THIS_MODULE, + .pm = &synaptics_i2c_pm, }, .probe = synaptics_i2c_probe, .remove = __devexit_p(synaptics_i2c_remove), - .suspend = synaptics_i2c_suspend, - .resume = synaptics_i2c_resume, .id_table = synaptics_i2c_id_table, }; diff --git a/drivers/input/serio/Kconfig b/drivers/input/serio/Kconfig index 6256233d2bfb..55f2c2293ec6 100644 --- a/drivers/input/serio/Kconfig +++ b/drivers/input/serio/Kconfig @@ -2,7 +2,7 @@ # Input core configuration # config SERIO - tristate "Serial I/O support" if EMBEDDED || !X86 + tristate "Serial I/O support" if EXPERT || !X86 default y help Say Yes here if you have any input device that uses serial I/O to @@ -19,7 +19,7 @@ config SERIO if SERIO config SERIO_I8042 - tristate "i8042 PC Keyboard controller" if EMBEDDED || !X86 + tristate "i8042 PC Keyboard controller" if EXPERT || !X86 default y depends on !PARISC && (!ARM || ARCH_SHARK || FOOTBRIDGE_HOST) && \ (!SUPERH || SH_CAYMAN) && !M68K && !BLACKFIN @@ -168,7 +168,7 @@ config SERIO_MACEPS2 module will be called maceps2. config SERIO_LIBPS2 - tristate "PS/2 driver library" if EMBEDDED + tristate "PS/2 driver library" if EXPERT depends on SERIO_I8042 || SERIO_I8042=n help Say Y here if you are using a driver for device connected @@ -214,7 +214,6 @@ config SERIO_AMS_DELTA tristate "Amstrad Delta (E3) mailboard support" depends on MACH_AMS_DELTA default y - select AMS_DELTA_FIQ ---help--- Say Y here if you have an E3 and want to use its mailboard, or any standard AT keyboard connected to the mailboard port. @@ -230,7 +229,7 @@ config SERIO_PS2MULT tristate "TQC PS/2 multiplexer" help Say Y here if you have the PS/2 line multiplexer like the one - present on TQC boads. + present on TQC boards. To compile this driver as a module, choose M here: the module will be called ps2mult. diff --git a/drivers/input/serio/gscps2.c b/drivers/input/serio/gscps2.c index 3c287dd879d3..4225f5d6b15f 100644 --- a/drivers/input/serio/gscps2.c +++ b/drivers/input/serio/gscps2.c @@ -358,7 +358,7 @@ static int __devinit gscps2_probe(struct parisc_device *dev) gscps2_reset(ps2port); ps2port->id = readb(ps2port->addr + GSC_ID) & 0x0f; - snprintf(serio->name, sizeof(serio->name), "GSC PS/2 %s", + snprintf(serio->name, sizeof(serio->name), "gsc-ps2-%s", (ps2port->id == GSC_ID_KEYBOARD) ? "keyboard" : "mouse"); strlcpy(serio->phys, dev_name(&dev->dev), sizeof(serio->phys)); serio->id.type = SERIO_8042; diff --git a/drivers/input/serio/serio_raw.c b/drivers/input/serio/serio_raw.c index cd82bb125915..b7ba4597f7f0 100644 --- a/drivers/input/serio/serio_raw.c +++ b/drivers/input/serio/serio_raw.c @@ -11,7 +11,6 @@ #include <linux/sched.h> #include <linux/slab.h> -#include <linux/smp_lock.h> #include <linux/poll.h> #include <linux/module.h> #include <linux/serio.h> diff --git a/drivers/input/sparse-keymap.c b/drivers/input/sparse-keymap.c index 7729e547ba65..337bf51bc984 100644 --- a/drivers/input/sparse-keymap.c +++ b/drivers/input/sparse-keymap.c @@ -210,8 +210,8 @@ int sparse_keymap_setup(struct input_dev *dev, dev->keycode = map; dev->keycodemax = map_size; - dev->getkeycode_new = sparse_keymap_getkeycode; - dev->setkeycode_new = sparse_keymap_setkeycode; + dev->getkeycode = sparse_keymap_getkeycode; + dev->setkeycode = sparse_keymap_setkeycode; return 0; diff --git a/drivers/input/tablet/wacom_sys.c b/drivers/input/tablet/wacom_sys.c index cf8fb9f5d4a8..449c0a46dbac 100644 --- a/drivers/input/tablet/wacom_sys.c +++ b/drivers/input/tablet/wacom_sys.c @@ -193,16 +193,16 @@ static int wacom_parse_hid(struct usb_interface *intf, struct hid_descriptor *hi case HID_USAGE_X: if (usage == WCM_DESKTOP) { if (finger) { - features->device_type = BTN_TOOL_DOUBLETAP; + features->device_type = BTN_TOOL_FINGER; if (features->type == TABLETPC2FG) { /* need to reset back */ features->pktlen = WACOM_PKGLEN_TPC2FG; - features->device_type = BTN_TOOL_TRIPLETAP; + features->device_type = BTN_TOOL_DOUBLETAP; } if (features->type == BAMBOO_PT) { /* need to reset back */ features->pktlen = WACOM_PKGLEN_BBTOUCH; - features->device_type = BTN_TOOL_TRIPLETAP; + features->device_type = BTN_TOOL_DOUBLETAP; features->x_phy = get_unaligned_le16(&report[i + 5]); features->x_max = @@ -241,11 +241,11 @@ static int wacom_parse_hid(struct usb_interface *intf, struct hid_descriptor *hi case HID_USAGE_Y: if (usage == WCM_DESKTOP) { if (finger) { - features->device_type = BTN_TOOL_DOUBLETAP; + features->device_type = BTN_TOOL_FINGER; if (features->type == TABLETPC2FG) { /* need to reset back */ features->pktlen = WACOM_PKGLEN_TPC2FG; - features->device_type = BTN_TOOL_TRIPLETAP; + features->device_type = BTN_TOOL_DOUBLETAP; features->y_max = get_unaligned_le16(&report[i + 3]); features->y_phy = @@ -254,7 +254,7 @@ static int wacom_parse_hid(struct usb_interface *intf, struct hid_descriptor *hi } else if (features->type == BAMBOO_PT) { /* need to reset back */ features->pktlen = WACOM_PKGLEN_BBTOUCH; - features->device_type = BTN_TOOL_TRIPLETAP; + features->device_type = BTN_TOOL_DOUBLETAP; features->y_phy = get_unaligned_le16(&report[i + 3]); features->y_max = diff --git a/drivers/input/tablet/wacom_wac.c b/drivers/input/tablet/wacom_wac.c index 367fa82a607e..5597637cfd41 100644 --- a/drivers/input/tablet/wacom_wac.c +++ b/drivers/input/tablet/wacom_wac.c @@ -675,169 +675,87 @@ static int wacom_intuos_irq(struct wacom_wac *wacom) return 1; } - -static void wacom_tpc_finger_in(struct wacom_wac *wacom, char *data, int idx) +static int wacom_tpc_mt_touch(struct wacom_wac *wacom) { struct input_dev *input = wacom->input; - int finger = idx + 1; - int x = le16_to_cpup((__le16 *)&data[finger * 2]) & 0x7fff; - int y = le16_to_cpup((__le16 *)&data[4 + finger * 2]) & 0x7fff; + unsigned char *data = wacom->data; + int contact_with_no_pen_down_count = 0; + int i; - /* - * Work around input core suppressing "duplicate" events since - * we are abusing ABS_X/ABS_Y to transmit multi-finger data. - * This should go away once we switch to true multitouch - * protocol. - */ - if (wacom->last_finger != finger) { - if (x == input_abs_get_val(input, ABS_X)) - x++; + for (i = 0; i < 2; i++) { + int p = data[1] & (1 << i); + bool touch = p && !wacom->shared->stylus_in_proximity; + + input_mt_slot(input, i); + input_mt_report_slot_state(input, MT_TOOL_FINGER, touch); + if (touch) { + int x = le16_to_cpup((__le16 *)&data[i * 2 + 2]) & 0x7fff; + int y = le16_to_cpup((__le16 *)&data[i * 2 + 6]) & 0x7fff; - if (y == input_abs_get_val(input, ABS_Y)) - y++; + input_report_abs(input, ABS_MT_POSITION_X, x); + input_report_abs(input, ABS_MT_POSITION_Y, y); + contact_with_no_pen_down_count++; + } } - input_report_abs(input, ABS_X, x); - input_report_abs(input, ABS_Y, y); - input_report_abs(input, ABS_MISC, wacom->id[0]); - input_report_key(input, wacom->tool[finger], 1); - if (!idx) - input_report_key(input, BTN_TOUCH, 1); - input_event(input, EV_MSC, MSC_SERIAL, finger); - input_sync(input); + /* keep touch state for pen event */ + wacom->shared->touch_down = (contact_with_no_pen_down_count > 0); - wacom->last_finger = finger; -} + input_mt_report_pointer_emulation(input, true); -static void wacom_tpc_touch_out(struct wacom_wac *wacom, int idx) -{ - struct input_dev *input = wacom->input; - int finger = idx + 1; - - input_report_abs(input, ABS_X, 0); - input_report_abs(input, ABS_Y, 0); - input_report_abs(input, ABS_MISC, 0); - input_report_key(input, wacom->tool[finger], 0); - if (!idx) - input_report_key(input, BTN_TOUCH, 0); - input_event(input, EV_MSC, MSC_SERIAL, finger); - input_sync(input); + return 1; } -static void wacom_tpc_touch_in(struct wacom_wac *wacom, size_t len) +static int wacom_tpc_single_touch(struct wacom_wac *wacom, size_t len) { char *data = wacom->data; struct input_dev *input = wacom->input; + bool prox; + int x = 0, y = 0; - wacom->tool[1] = BTN_TOOL_DOUBLETAP; - wacom->id[0] = TOUCH_DEVICE_ID; - wacom->tool[2] = BTN_TOOL_TRIPLETAP; - - if (len != WACOM_PKGLEN_TPC1FG) { - - switch (data[0]) { + if (!wacom->shared->stylus_in_proximity) { + if (len == WACOM_PKGLEN_TPC1FG) { + prox = data[0] & 0x01; + x = get_unaligned_le16(&data[1]); + y = get_unaligned_le16(&data[3]); + } else { /* with capacity */ + prox = data[1] & 0x01; + x = le16_to_cpup((__le16 *)&data[2]); + y = le16_to_cpup((__le16 *)&data[4]); + } + } else + /* force touch out when pen is in prox */ + prox = 0; - case WACOM_REPORT_TPC1FG: - input_report_abs(input, ABS_X, le16_to_cpup((__le16 *)&data[2])); - input_report_abs(input, ABS_Y, le16_to_cpup((__le16 *)&data[4])); - input_report_abs(input, ABS_PRESSURE, le16_to_cpup((__le16 *)&data[6])); - input_report_key(input, BTN_TOUCH, le16_to_cpup((__le16 *)&data[6])); - input_report_abs(input, ABS_MISC, wacom->id[0]); - input_report_key(input, wacom->tool[1], 1); - input_sync(input); - break; + if (prox) { + input_report_abs(input, ABS_X, x); + input_report_abs(input, ABS_Y, y); + } + input_report_key(input, BTN_TOUCH, prox); - case WACOM_REPORT_TPC2FG: - if (data[1] & 0x01) - wacom_tpc_finger_in(wacom, data, 0); - else if (wacom->id[1] & 0x01) - wacom_tpc_touch_out(wacom, 0); + /* keep touch state for pen events */ + wacom->shared->touch_down = prox; - if (data[1] & 0x02) - wacom_tpc_finger_in(wacom, data, 1); - else if (wacom->id[1] & 0x02) - wacom_tpc_touch_out(wacom, 1); - break; - } - } else { - input_report_abs(input, ABS_X, get_unaligned_le16(&data[1])); - input_report_abs(input, ABS_Y, get_unaligned_le16(&data[3])); - input_report_key(input, BTN_TOUCH, 1); - input_report_abs(input, ABS_MISC, wacom->id[1]); - input_report_key(input, wacom->tool[1], 1); - input_sync(input); - } + return 1; } -static int wacom_tpc_irq(struct wacom_wac *wacom, size_t len) +static int wacom_tpc_pen(struct wacom_wac *wacom) { struct wacom_features *features = &wacom->features; char *data = wacom->data; struct input_dev *input = wacom->input; - int prox = 0, pressure; - int retval = 0; + int pressure; + bool prox = data[1] & 0x20; - dbg("wacom_tpc_irq: received report #%d", data[0]); - - if (len == WACOM_PKGLEN_TPC1FG || /* single touch */ - data[0] == WACOM_REPORT_TPC1FG || /* single touch */ - data[0] == WACOM_REPORT_TPC2FG) { /* 2FG touch */ - - if (wacom->shared->stylus_in_proximity) { - if (wacom->id[1] & 0x01) - wacom_tpc_touch_out(wacom, 0); - - if (wacom->id[1] & 0x02) - wacom_tpc_touch_out(wacom, 1); - - wacom->id[1] = 0; - return 0; - } - - if (len == WACOM_PKGLEN_TPC1FG) { /* with touch */ - prox = data[0] & 0x01; - } else { /* with capacity */ - if (data[0] == WACOM_REPORT_TPC1FG) - /* single touch */ - prox = data[1] & 0x01; - else - /* 2FG touch data */ - prox = data[1] & 0x03; - } - - if (prox) { - if (!wacom->id[1]) - wacom->last_finger = 1; - wacom_tpc_touch_in(wacom, len); - } else { - if (data[0] == WACOM_REPORT_TPC2FG) { - /* 2FGT out-prox */ - if (wacom->id[1] & 0x01) - wacom_tpc_touch_out(wacom, 0); + if (!wacom->shared->stylus_in_proximity) /* first in prox */ + /* Going into proximity select tool */ + wacom->tool[0] = (data[1] & 0x0c) ? BTN_TOOL_RUBBER : BTN_TOOL_PEN; - if (wacom->id[1] & 0x02) - wacom_tpc_touch_out(wacom, 1); - } else - /* one finger touch */ - wacom_tpc_touch_out(wacom, 0); + /* keep pen state for touch events */ + wacom->shared->stylus_in_proximity = prox; - wacom->id[0] = 0; - } - /* keep prox bit to send proper out-prox event */ - wacom->id[1] = prox; - } else if (data[0] == WACOM_REPORT_PENABLED) { /* Penabled */ - prox = data[1] & 0x20; - - if (!wacom->shared->stylus_in_proximity) { /* first in prox */ - /* Going into proximity select tool */ - wacom->tool[0] = (data[1] & 0x0c) ? BTN_TOOL_RUBBER : BTN_TOOL_PEN; - if (wacom->tool[0] == BTN_TOOL_PEN) - wacom->id[0] = STYLUS_DEVICE_ID; - else - wacom->id[0] = ERASER_DEVICE_ID; - - wacom->shared->stylus_in_proximity = true; - } + /* send pen events only when touch is up or forced out */ + if (!wacom->shared->touch_down) { input_report_key(input, BTN_STYLUS, data[1] & 0x02); input_report_key(input, BTN_STYLUS2, data[1] & 0x10); input_report_abs(input, ABS_X, le16_to_cpup((__le16 *)&data[2])); @@ -847,15 +765,27 @@ static int wacom_tpc_irq(struct wacom_wac *wacom, size_t len) pressure = features->pressure_max + pressure + 1; input_report_abs(input, ABS_PRESSURE, pressure); input_report_key(input, BTN_TOUCH, data[1] & 0x05); - if (!prox) { /* out-prox */ - wacom->id[0] = 0; - wacom->shared->stylus_in_proximity = false; - } input_report_key(input, wacom->tool[0], prox); - input_report_abs(input, ABS_MISC, wacom->id[0]); - retval = 1; + return 1; } - return retval; + + return 0; +} + +static int wacom_tpc_irq(struct wacom_wac *wacom, size_t len) +{ + char *data = wacom->data; + + dbg("wacom_tpc_irq: received report #%d", data[0]); + + if (len == WACOM_PKGLEN_TPC1FG || data[0] == WACOM_REPORT_TPC1FG) + return wacom_tpc_single_touch(wacom, len); + else if (data[0] == WACOM_REPORT_TPC2FG) + return wacom_tpc_mt_touch(wacom); + else if (data[0] == WACOM_REPORT_PENABLED) + return wacom_tpc_pen(wacom); + + return 0; } static int wacom_bpt_touch(struct wacom_wac *wacom) @@ -1078,7 +1008,7 @@ void wacom_setup_device_quirks(struct wacom_features *features) { /* touch device found but size is not defined. use default */ - if (features->device_type == BTN_TOOL_DOUBLETAP && !features->x_max) { + if (features->device_type == BTN_TOOL_FINGER && !features->x_max) { features->x_max = 1023; features->y_max = 1023; } @@ -1090,7 +1020,7 @@ void wacom_setup_device_quirks(struct wacom_features *features) /* quirks for bamboo touch */ if (features->type == BAMBOO_PT && - features->device_type == BTN_TOOL_TRIPLETAP) { + features->device_type == BTN_TOOL_DOUBLETAP) { features->x_max <<= 5; features->y_max <<= 5; features->x_fuzz <<= 5; @@ -1226,27 +1156,30 @@ void wacom_setup_input_capabilities(struct input_dev *input_dev, break; case TABLETPC2FG: - if (features->device_type == BTN_TOOL_TRIPLETAP) { - __set_bit(BTN_TOOL_TRIPLETAP, input_dev->keybit); - input_set_capability(input_dev, EV_MSC, MSC_SERIAL); + if (features->device_type == BTN_TOOL_DOUBLETAP) { + + input_mt_init_slots(input_dev, 2); + input_set_abs_params(input_dev, ABS_MT_TOOL_TYPE, + 0, MT_TOOL_MAX, 0, 0); + input_set_abs_params(input_dev, ABS_MT_POSITION_X, + 0, features->x_max, 0, 0); + input_set_abs_params(input_dev, ABS_MT_POSITION_Y, + 0, features->y_max, 0, 0); } /* fall through */ case TABLETPC: - if (features->device_type == BTN_TOOL_DOUBLETAP || - features->device_type == BTN_TOOL_TRIPLETAP) { + __clear_bit(ABS_MISC, input_dev->absbit); + + if (features->device_type != BTN_TOOL_PEN) { input_abs_set_res(input_dev, ABS_X, wacom_calculate_touch_res(features->x_max, features->x_phy)); input_abs_set_res(input_dev, ABS_Y, wacom_calculate_touch_res(features->y_max, features->y_phy)); - __set_bit(BTN_TOOL_DOUBLETAP, input_dev->keybit); - } - - if (features->device_type != BTN_TOOL_PEN) break; /* no need to process stylus stuff */ - + } /* fall through */ case PL: @@ -1264,7 +1197,7 @@ void wacom_setup_input_capabilities(struct input_dev *input_dev, case BAMBOO_PT: __clear_bit(ABS_MISC, input_dev->absbit); - if (features->device_type == BTN_TOOL_TRIPLETAP) { + if (features->device_type == BTN_TOOL_DOUBLETAP) { __set_bit(BTN_LEFT, input_dev->keybit); __set_bit(BTN_FORWARD, input_dev->keybit); __set_bit(BTN_BACK, input_dev->keybit); diff --git a/drivers/input/tablet/wacom_wac.h b/drivers/input/tablet/wacom_wac.h index b1310ec9720c..835f756b150c 100644 --- a/drivers/input/tablet/wacom_wac.h +++ b/drivers/input/tablet/wacom_wac.h @@ -88,15 +88,15 @@ struct wacom_features { struct wacom_shared { bool stylus_in_proximity; + bool touch_down; }; struct wacom_wac { char name[64]; unsigned char *data; - int tool[3]; - int id[3]; + int tool[2]; + int id[2]; __u32 serial[2]; - int last_finger; struct wacom_features features; struct wacom_shared *shared; struct input_dev *input; diff --git a/drivers/input/touchscreen/Kconfig b/drivers/input/touchscreen/Kconfig index 07ac77d393a4..112ec55f2939 100644 --- a/drivers/input/touchscreen/Kconfig +++ b/drivers/input/touchscreen/Kconfig @@ -86,6 +86,18 @@ config TOUCHSCREEN_AD7879_SPI To compile this driver as a module, choose M here: the module will be called ad7879-spi. +config TOUCHSCREEN_ATMEL_MXT + tristate "Atmel mXT I2C Touchscreen" + depends on I2C + help + Say Y here if you have Atmel mXT series I2C touchscreen, + such as AT42QT602240/ATMXT224, connected to your system. + + If unsure, say N. + + To compile this driver as a module, choose M here: the + module will be called atmel_mxt_ts. + config TOUCHSCREEN_BITSY tristate "Compaq iPAQ H3600 (Bitsy) touchscreen" depends on SA1100_BITSY @@ -339,18 +351,6 @@ config TOUCHSCREEN_PENMOUNT To compile this driver as a module, choose M here: the module will be called penmount. -config TOUCHSCREEN_QT602240 - tristate "QT602240 I2C Touchscreen" - depends on I2C - help - Say Y here if you have the AT42QT602240/ATMXT224 I2C touchscreen - connected to your system. - - If unsure, say N. - - To compile this driver as a module, choose M here: the - module will be called qt602240_ts. - config TOUCHSCREEN_MIGOR tristate "Renesas MIGO-R touchscreen" depends on SH_MIGOR && I2C @@ -423,6 +423,16 @@ config TOUCHSCREEN_UCB1400 To compile this driver as a module, choose M here: the module will be called ucb1400_ts. +config TOUCHSCREEN_WM831X + tristate "Support for WM831x touchscreen controllers" + depends on MFD_WM831X + help + This enables support for the touchscreen controller on the WM831x + series of PMICs. + + To compile this driver as a module, choose M here: the + module will be called wm831x-ts. + config TOUCHSCREEN_WM97XX tristate "Support for WM97xx AC97 touchscreen controllers" depends on AC97_BUS @@ -540,62 +550,62 @@ config TOUCHSCREEN_MC13783 config TOUCHSCREEN_USB_EGALAX default y - bool "eGalax, eTurboTouch CT-410/510/700 device support" if EMBEDDED + bool "eGalax, eTurboTouch CT-410/510/700 device support" if EXPERT depends on TOUCHSCREEN_USB_COMPOSITE config TOUCHSCREEN_USB_PANJIT default y - bool "PanJit device support" if EMBEDDED + bool "PanJit device support" if EXPERT depends on TOUCHSCREEN_USB_COMPOSITE config TOUCHSCREEN_USB_3M default y - bool "3M/Microtouch EX II series device support" if EMBEDDED + bool "3M/Microtouch EX II series device support" if EXPERT depends on TOUCHSCREEN_USB_COMPOSITE config TOUCHSCREEN_USB_ITM default y - bool "ITM device support" if EMBEDDED + bool "ITM device support" if EXPERT depends on TOUCHSCREEN_USB_COMPOSITE config TOUCHSCREEN_USB_ETURBO default y - bool "eTurboTouch (non-eGalax compatible) device support" if EMBEDDED + bool "eTurboTouch (non-eGalax compatible) device support" if EXPERT depends on TOUCHSCREEN_USB_COMPOSITE config TOUCHSCREEN_USB_GUNZE default y - bool "Gunze AHL61 device support" if EMBEDDED + bool "Gunze AHL61 device support" if EXPERT depends on TOUCHSCREEN_USB_COMPOSITE config TOUCHSCREEN_USB_DMC_TSC10 default y - bool "DMC TSC-10/25 device support" if EMBEDDED + bool "DMC TSC-10/25 device support" if EXPERT depends on TOUCHSCREEN_USB_COMPOSITE config TOUCHSCREEN_USB_IRTOUCH default y - bool "IRTOUCHSYSTEMS/UNITOP device support" if EMBEDDED + bool "IRTOUCHSYSTEMS/UNITOP device support" if EXPERT depends on TOUCHSCREEN_USB_COMPOSITE config TOUCHSCREEN_USB_IDEALTEK default y - bool "IdealTEK URTC1000 device support" if EMBEDDED + bool "IdealTEK URTC1000 device support" if EXPERT depends on TOUCHSCREEN_USB_COMPOSITE config TOUCHSCREEN_USB_GENERAL_TOUCH default y - bool "GeneralTouch Touchscreen device support" if EMBEDDED + bool "GeneralTouch Touchscreen device support" if EXPERT depends on TOUCHSCREEN_USB_COMPOSITE config TOUCHSCREEN_USB_GOTOP default y - bool "GoTop Super_Q2/GogoPen/PenPower tablet device support" if EMBEDDED + bool "GoTop Super_Q2/GogoPen/PenPower tablet device support" if EXPERT depends on TOUCHSCREEN_USB_COMPOSITE config TOUCHSCREEN_USB_JASTEC default y - bool "JASTEC/DigiTech DTR-02U USB touch controller device support" if EMBEDDED + bool "JASTEC/DigiTech DTR-02U USB touch controller device support" if EXPERT depends on TOUCHSCREEN_USB_COMPOSITE config TOUCHSCREEN_USB_E2I @@ -605,17 +615,17 @@ config TOUCHSCREEN_USB_E2I config TOUCHSCREEN_USB_ZYTRONIC default y - bool "Zytronic controller" if EMBEDDED + bool "Zytronic controller" if EXPERT depends on TOUCHSCREEN_USB_COMPOSITE config TOUCHSCREEN_USB_ETT_TC45USB default y - bool "ET&T USB series TC4UM/TC5UH touchscreen controler support" if EMBEDDED + bool "ET&T USB series TC4UM/TC5UH touchscreen controller support" if EXPERT depends on TOUCHSCREEN_USB_COMPOSITE config TOUCHSCREEN_USB_NEXIO default y - bool "NEXIO/iNexio device support" if EMBEDDED + bool "NEXIO/iNexio device support" if EXPERT depends on TOUCHSCREEN_USB_COMPOSITE config TOUCHSCREEN_TOUCHIT213 @@ -629,6 +639,17 @@ config TOUCHSCREEN_TOUCHIT213 To compile this driver as a module, choose M here: the module will be called touchit213. +config TOUCHSCREEN_TSC2005 + tristate "TSC2005 based touchscreens" + depends on SPI_MASTER + help + Say Y here if you have a TSC2005 based touchscreen. + + If unsure, say N. + + To compile this driver as a module, choose M here: the + module will be called tsc2005. + config TOUCHSCREEN_TSC2007 tristate "TSC2007 based touchscreens" depends on I2C diff --git a/drivers/input/touchscreen/Makefile b/drivers/input/touchscreen/Makefile index 718bcc814952..ca94098d4c92 100644 --- a/drivers/input/touchscreen/Makefile +++ b/drivers/input/touchscreen/Makefile @@ -12,6 +12,7 @@ obj-$(CONFIG_TOUCHSCREEN_AD7879) += ad7879.o obj-$(CONFIG_TOUCHSCREEN_AD7879_I2C) += ad7879-i2c.o obj-$(CONFIG_TOUCHSCREEN_AD7879_SPI) += ad7879-spi.o obj-$(CONFIG_TOUCHSCREEN_ADS7846) += ads7846.o +obj-$(CONFIG_TOUCHSCREEN_ATMEL_MXT) += atmel_mxt_ts.o obj-$(CONFIG_TOUCHSCREEN_ATMEL_TSADCC) += atmel_tsadcc.o obj-$(CONFIG_TOUCHSCREEN_BITSY) += h3600_ts_input.o obj-$(CONFIG_TOUCHSCREEN_BU21013) += bu21013_ts.o @@ -37,7 +38,6 @@ 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_QT602240) += qt602240_ts.o obj-$(CONFIG_TOUCHSCREEN_S3C2410) += s3c2410_ts.o obj-$(CONFIG_TOUCHSCREEN_ST1232) += st1232.o obj-$(CONFIG_TOUCHSCREEN_STMPE) += stmpe-ts.o @@ -45,9 +45,11 @@ obj-$(CONFIG_TOUCHSCREEN_TNETV107X) += tnetv107x-ts.o obj-$(CONFIG_TOUCHSCREEN_TOUCHIT213) += touchit213.o obj-$(CONFIG_TOUCHSCREEN_TOUCHRIGHT) += touchright.o obj-$(CONFIG_TOUCHSCREEN_TOUCHWIN) += touchwin.o +obj-$(CONFIG_TOUCHSCREEN_TSC2005) += tsc2005.o obj-$(CONFIG_TOUCHSCREEN_TSC2007) += tsc2007.o obj-$(CONFIG_TOUCHSCREEN_UCB1400) += ucb1400_ts.o obj-$(CONFIG_TOUCHSCREEN_WACOM_W8001) += wacom_w8001.o +obj-$(CONFIG_TOUCHSCREEN_WM831X) += wm831x-ts.o obj-$(CONFIG_TOUCHSCREEN_WM97XX) += wm97xx-ts.o wm97xx-ts-$(CONFIG_TOUCHSCREEN_WM9705) += wm9705.o wm97xx-ts-$(CONFIG_TOUCHSCREEN_WM9712) += wm9712.o diff --git a/drivers/input/touchscreen/ad7877.c b/drivers/input/touchscreen/ad7877.c index a1952fcc083e..714d4e0f9f95 100644 --- a/drivers/input/touchscreen/ad7877.c +++ b/drivers/input/touchscreen/ad7877.c @@ -41,6 +41,7 @@ #include <linux/delay.h> #include <linux/input.h> #include <linux/interrupt.h> +#include <linux/pm.h> #include <linux/slab.h> #include <linux/spi/spi.h> #include <linux/spi/ad7877.h> @@ -826,39 +827,37 @@ static int __devexit ad7877_remove(struct spi_device *spi) return 0; } -#ifdef CONFIG_PM -static int ad7877_suspend(struct spi_device *spi, pm_message_t message) +#ifdef CONFIG_PM_SLEEP +static int ad7877_suspend(struct device *dev) { - struct ad7877 *ts = dev_get_drvdata(&spi->dev); + struct ad7877 *ts = dev_get_drvdata(dev); ad7877_disable(ts); return 0; } -static int ad7877_resume(struct spi_device *spi) +static int ad7877_resume(struct device *dev) { - struct ad7877 *ts = dev_get_drvdata(&spi->dev); + struct ad7877 *ts = dev_get_drvdata(dev); ad7877_enable(ts); return 0; } -#else -#define ad7877_suspend NULL -#define ad7877_resume NULL #endif +static SIMPLE_DEV_PM_OPS(ad7877_pm, ad7877_suspend, ad7877_resume); + static struct spi_driver ad7877_driver = { .driver = { .name = "ad7877", .bus = &spi_bus_type, .owner = THIS_MODULE, + .pm = &ad7877_pm, }, .probe = ad7877_probe, .remove = __devexit_p(ad7877_remove), - .suspend = ad7877_suspend, - .resume = ad7877_resume, }; static int __init ad7877_init(void) diff --git a/drivers/input/touchscreen/ad7879-spi.c b/drivers/input/touchscreen/ad7879-spi.c index 59c6e68c4325..ddf732f3cafc 100644 --- a/drivers/input/touchscreen/ad7879-spi.c +++ b/drivers/input/touchscreen/ad7879-spi.c @@ -7,6 +7,7 @@ */ #include <linux/input.h> /* BUS_SPI */ +#include <linux/pm.h> #include <linux/spi/spi.h> #include "ad7879.h" @@ -20,9 +21,10 @@ #define AD7879_WRITECMD(reg) (AD7879_CMD(reg)) #define AD7879_READCMD(reg) (AD7879_CMD(reg) | AD7879_CMD_READ) -#ifdef CONFIG_PM -static int ad7879_spi_suspend(struct spi_device *spi, pm_message_t message) +#ifdef CONFIG_PM_SLEEP +static int ad7879_spi_suspend(struct device *dev) { + struct spi_device *spi = to_spi_device(dev); struct ad7879 *ts = spi_get_drvdata(spi); ad7879_suspend(ts); @@ -30,19 +32,19 @@ static int ad7879_spi_suspend(struct spi_device *spi, pm_message_t message) return 0; } -static int ad7879_spi_resume(struct spi_device *spi) +static int ad7879_spi_resume(struct device *dev) { + struct spi_device *spi = to_spi_device(dev); struct ad7879 *ts = spi_get_drvdata(spi); ad7879_resume(ts); return 0; } -#else -# define ad7879_spi_suspend NULL -# define ad7879_spi_resume NULL #endif +static SIMPLE_DEV_PM_OPS(ad7879_spi_pm, ad7879_spi_suspend, ad7879_spi_resume); + /* * ad7879_read/write are only used for initial setup and for sysfs controls. * The main traffic is done in ad7879_collect(). @@ -173,11 +175,10 @@ static struct spi_driver ad7879_spi_driver = { .name = "ad7879", .bus = &spi_bus_type, .owner = THIS_MODULE, + .pm = &ad7879_spi_pm, }, .probe = ad7879_spi_probe, .remove = __devexit_p(ad7879_spi_remove), - .suspend = ad7879_spi_suspend, - .resume = ad7879_spi_resume, }; static int __init ad7879_spi_init(void) diff --git a/drivers/input/touchscreen/ads7846.c b/drivers/input/touchscreen/ads7846.c index 4bf2316e3284..c24946f51256 100644 --- a/drivers/input/touchscreen/ads7846.c +++ b/drivers/input/touchscreen/ads7846.c @@ -26,6 +26,7 @@ #include <linux/input.h> #include <linux/interrupt.h> #include <linux/slab.h> +#include <linux/pm.h> #include <linux/gpio.h> #include <linux/spi/spi.h> #include <linux/spi/ads7846.h> @@ -892,9 +893,10 @@ static irqreturn_t ads7846_irq(int irq, void *handle) return IRQ_HANDLED; } -static int ads7846_suspend(struct spi_device *spi, pm_message_t message) +#ifdef CONFIG_PM_SLEEP +static int ads7846_suspend(struct device *dev) { - struct ads7846 *ts = dev_get_drvdata(&spi->dev); + struct ads7846 *ts = dev_get_drvdata(dev); mutex_lock(&ts->lock); @@ -914,9 +916,9 @@ static int ads7846_suspend(struct spi_device *spi, pm_message_t message) return 0; } -static int ads7846_resume(struct spi_device *spi) +static int ads7846_resume(struct device *dev) { - struct ads7846 *ts = dev_get_drvdata(&spi->dev); + struct ads7846 *ts = dev_get_drvdata(dev); mutex_lock(&ts->lock); @@ -935,6 +937,9 @@ static int ads7846_resume(struct spi_device *spi) return 0; } +#endif + +static SIMPLE_DEV_PM_OPS(ads7846_pm, ads7846_suspend, ads7846_resume); static int __devinit ads7846_setup_pendown(struct spi_device *spi, struct ads7846 *ts) { @@ -1408,11 +1413,10 @@ static struct spi_driver ads7846_driver = { .name = "ads7846", .bus = &spi_bus_type, .owner = THIS_MODULE, + .pm = &ads7846_pm, }, .probe = ads7846_probe, .remove = __devexit_p(ads7846_remove), - .suspend = ads7846_suspend, - .resume = ads7846_resume, }; static int __init ads7846_init(void) diff --git a/drivers/input/touchscreen/atmel_mxt_ts.c b/drivers/input/touchscreen/atmel_mxt_ts.c new file mode 100644 index 000000000000..4012436633b1 --- /dev/null +++ b/drivers/input/touchscreen/atmel_mxt_ts.c @@ -0,0 +1,1211 @@ +/* + * Atmel maXTouch Touchscreen driver + * + * Copyright (C) 2010 Samsung Electronics Co.Ltd + * Author: Joonyoung Shim <jy0922.shim@samsung.com> + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. + * + */ + +#include <linux/module.h> +#include <linux/init.h> +#include <linux/delay.h> +#include <linux/firmware.h> +#include <linux/i2c.h> +#include <linux/i2c/atmel_mxt_ts.h> +#include <linux/input.h> +#include <linux/interrupt.h> +#include <linux/slab.h> + +/* Version */ +#define MXT_VER_20 20 +#define MXT_VER_21 21 +#define MXT_VER_22 22 + +/* Slave addresses */ +#define MXT_APP_LOW 0x4a +#define MXT_APP_HIGH 0x4b +#define MXT_BOOT_LOW 0x24 +#define MXT_BOOT_HIGH 0x25 + +/* Firmware */ +#define MXT_FW_NAME "maxtouch.fw" + +/* Registers */ +#define MXT_FAMILY_ID 0x00 +#define MXT_VARIANT_ID 0x01 +#define MXT_VERSION 0x02 +#define MXT_BUILD 0x03 +#define MXT_MATRIX_X_SIZE 0x04 +#define MXT_MATRIX_Y_SIZE 0x05 +#define MXT_OBJECT_NUM 0x06 +#define MXT_OBJECT_START 0x07 + +#define MXT_OBJECT_SIZE 6 + +/* Object types */ +#define MXT_DEBUG_DIAGNOSTIC 37 +#define MXT_GEN_MESSAGE 5 +#define MXT_GEN_COMMAND 6 +#define MXT_GEN_POWER 7 +#define MXT_GEN_ACQUIRE 8 +#define MXT_TOUCH_MULTI 9 +#define MXT_TOUCH_KEYARRAY 15 +#define MXT_TOUCH_PROXIMITY 23 +#define MXT_PROCI_GRIPFACE 20 +#define MXT_PROCG_NOISE 22 +#define MXT_PROCI_ONETOUCH 24 +#define MXT_PROCI_TWOTOUCH 27 +#define MXT_PROCI_GRIP 40 +#define MXT_PROCI_PALM 41 +#define MXT_SPT_COMMSCONFIG 18 +#define MXT_SPT_GPIOPWM 19 +#define MXT_SPT_SELFTEST 25 +#define MXT_SPT_CTECONFIG 28 +#define MXT_SPT_USERDATA 38 +#define MXT_SPT_DIGITIZER 43 +#define MXT_SPT_MESSAGECOUNT 44 + +/* MXT_GEN_COMMAND field */ +#define MXT_COMMAND_RESET 0 +#define MXT_COMMAND_BACKUPNV 1 +#define MXT_COMMAND_CALIBRATE 2 +#define MXT_COMMAND_REPORTALL 3 +#define MXT_COMMAND_DIAGNOSTIC 5 + +/* MXT_GEN_POWER field */ +#define MXT_POWER_IDLEACQINT 0 +#define MXT_POWER_ACTVACQINT 1 +#define MXT_POWER_ACTV2IDLETO 2 + +/* MXT_GEN_ACQUIRE field */ +#define MXT_ACQUIRE_CHRGTIME 0 +#define MXT_ACQUIRE_TCHDRIFT 2 +#define MXT_ACQUIRE_DRIFTST 3 +#define MXT_ACQUIRE_TCHAUTOCAL 4 +#define MXT_ACQUIRE_SYNC 5 +#define MXT_ACQUIRE_ATCHCALST 6 +#define MXT_ACQUIRE_ATCHCALSTHR 7 + +/* MXT_TOUCH_MULTI field */ +#define MXT_TOUCH_CTRL 0 +#define MXT_TOUCH_XORIGIN 1 +#define MXT_TOUCH_YORIGIN 2 +#define MXT_TOUCH_XSIZE 3 +#define MXT_TOUCH_YSIZE 4 +#define MXT_TOUCH_BLEN 6 +#define MXT_TOUCH_TCHTHR 7 +#define MXT_TOUCH_TCHDI 8 +#define MXT_TOUCH_ORIENT 9 +#define MXT_TOUCH_MOVHYSTI 11 +#define MXT_TOUCH_MOVHYSTN 12 +#define MXT_TOUCH_NUMTOUCH 14 +#define MXT_TOUCH_MRGHYST 15 +#define MXT_TOUCH_MRGTHR 16 +#define MXT_TOUCH_AMPHYST 17 +#define MXT_TOUCH_XRANGE_LSB 18 +#define MXT_TOUCH_XRANGE_MSB 19 +#define MXT_TOUCH_YRANGE_LSB 20 +#define MXT_TOUCH_YRANGE_MSB 21 +#define MXT_TOUCH_XLOCLIP 22 +#define MXT_TOUCH_XHICLIP 23 +#define MXT_TOUCH_YLOCLIP 24 +#define MXT_TOUCH_YHICLIP 25 +#define MXT_TOUCH_XEDGECTRL 26 +#define MXT_TOUCH_XEDGEDIST 27 +#define MXT_TOUCH_YEDGECTRL 28 +#define MXT_TOUCH_YEDGEDIST 29 +#define MXT_TOUCH_JUMPLIMIT 30 + +/* MXT_PROCI_GRIPFACE field */ +#define MXT_GRIPFACE_CTRL 0 +#define MXT_GRIPFACE_XLOGRIP 1 +#define MXT_GRIPFACE_XHIGRIP 2 +#define MXT_GRIPFACE_YLOGRIP 3 +#define MXT_GRIPFACE_YHIGRIP 4 +#define MXT_GRIPFACE_MAXTCHS 5 +#define MXT_GRIPFACE_SZTHR1 7 +#define MXT_GRIPFACE_SZTHR2 8 +#define MXT_GRIPFACE_SHPTHR1 9 +#define MXT_GRIPFACE_SHPTHR2 10 +#define MXT_GRIPFACE_SUPEXTTO 11 + +/* MXT_PROCI_NOISE field */ +#define MXT_NOISE_CTRL 0 +#define MXT_NOISE_OUTFLEN 1 +#define MXT_NOISE_GCAFUL_LSB 3 +#define MXT_NOISE_GCAFUL_MSB 4 +#define MXT_NOISE_GCAFLL_LSB 5 +#define MXT_NOISE_GCAFLL_MSB 6 +#define MXT_NOISE_ACTVGCAFVALID 7 +#define MXT_NOISE_NOISETHR 8 +#define MXT_NOISE_FREQHOPSCALE 10 +#define MXT_NOISE_FREQ0 11 +#define MXT_NOISE_FREQ1 12 +#define MXT_NOISE_FREQ2 13 +#define MXT_NOISE_FREQ3 14 +#define MXT_NOISE_FREQ4 15 +#define MXT_NOISE_IDLEGCAFVALID 16 + +/* MXT_SPT_COMMSCONFIG */ +#define MXT_COMMS_CTRL 0 +#define MXT_COMMS_CMD 1 + +/* MXT_SPT_CTECONFIG field */ +#define MXT_CTE_CTRL 0 +#define MXT_CTE_CMD 1 +#define MXT_CTE_MODE 2 +#define MXT_CTE_IDLEGCAFDEPTH 3 +#define MXT_CTE_ACTVGCAFDEPTH 4 +#define MXT_CTE_VOLTAGE 5 + +#define MXT_VOLTAGE_DEFAULT 2700000 +#define MXT_VOLTAGE_STEP 10000 + +/* Define for MXT_GEN_COMMAND */ +#define MXT_BOOT_VALUE 0xa5 +#define MXT_BACKUP_VALUE 0x55 +#define MXT_BACKUP_TIME 25 /* msec */ +#define MXT_RESET_TIME 65 /* msec */ + +#define MXT_FWRESET_TIME 175 /* msec */ + +/* Command to unlock bootloader */ +#define MXT_UNLOCK_CMD_MSB 0xaa +#define MXT_UNLOCK_CMD_LSB 0xdc + +/* Bootloader mode status */ +#define MXT_WAITING_BOOTLOAD_CMD 0xc0 /* valid 7 6 bit only */ +#define MXT_WAITING_FRAME_DATA 0x80 /* valid 7 6 bit only */ +#define MXT_FRAME_CRC_CHECK 0x02 +#define MXT_FRAME_CRC_FAIL 0x03 +#define MXT_FRAME_CRC_PASS 0x04 +#define MXT_APP_CRC_FAIL 0x40 /* valid 7 8 bit only */ +#define MXT_BOOT_STATUS_MASK 0x3f + +/* Touch status */ +#define MXT_SUPPRESS (1 << 1) +#define MXT_AMP (1 << 2) +#define MXT_VECTOR (1 << 3) +#define MXT_MOVE (1 << 4) +#define MXT_RELEASE (1 << 5) +#define MXT_PRESS (1 << 6) +#define MXT_DETECT (1 << 7) + +/* Touchscreen absolute values */ +#define MXT_MAX_XC 0x3ff +#define MXT_MAX_YC 0x3ff +#define MXT_MAX_AREA 0xff + +#define MXT_MAX_FINGER 10 + +struct mxt_info { + u8 family_id; + u8 variant_id; + u8 version; + u8 build; + u8 matrix_xsize; + u8 matrix_ysize; + u8 object_num; +}; + +struct mxt_object { + u8 type; + u16 start_address; + u8 size; + u8 instances; + u8 num_report_ids; + + /* to map object and message */ + u8 max_reportid; +}; + +struct mxt_message { + u8 reportid; + u8 message[7]; + u8 checksum; +}; + +struct mxt_finger { + int status; + int x; + int y; + int area; +}; + +/* Each client has this additional data */ +struct mxt_data { + struct i2c_client *client; + struct input_dev *input_dev; + const struct mxt_platform_data *pdata; + struct mxt_object *object_table; + struct mxt_info info; + struct mxt_finger finger[MXT_MAX_FINGER]; + unsigned int irq; +}; + +static bool mxt_object_readable(unsigned int type) +{ + switch (type) { + case MXT_GEN_MESSAGE: + case MXT_GEN_COMMAND: + case MXT_GEN_POWER: + case MXT_GEN_ACQUIRE: + case MXT_TOUCH_MULTI: + case MXT_TOUCH_KEYARRAY: + case MXT_TOUCH_PROXIMITY: + case MXT_PROCI_GRIPFACE: + case MXT_PROCG_NOISE: + case MXT_PROCI_ONETOUCH: + case MXT_PROCI_TWOTOUCH: + case MXT_PROCI_GRIP: + case MXT_PROCI_PALM: + case MXT_SPT_COMMSCONFIG: + case MXT_SPT_GPIOPWM: + case MXT_SPT_SELFTEST: + case MXT_SPT_CTECONFIG: + case MXT_SPT_USERDATA: + return true; + default: + return false; + } +} + +static bool mxt_object_writable(unsigned int type) +{ + switch (type) { + case MXT_GEN_COMMAND: + case MXT_GEN_POWER: + case MXT_GEN_ACQUIRE: + case MXT_TOUCH_MULTI: + case MXT_TOUCH_KEYARRAY: + case MXT_TOUCH_PROXIMITY: + case MXT_PROCI_GRIPFACE: + case MXT_PROCG_NOISE: + case MXT_PROCI_ONETOUCH: + case MXT_PROCI_TWOTOUCH: + case MXT_PROCI_GRIP: + case MXT_PROCI_PALM: + case MXT_SPT_GPIOPWM: + case MXT_SPT_SELFTEST: + case MXT_SPT_CTECONFIG: + return true; + default: + return false; + } +} + +static void mxt_dump_message(struct device *dev, + struct mxt_message *message) +{ + dev_dbg(dev, "reportid:\t0x%x\n", message->reportid); + dev_dbg(dev, "message1:\t0x%x\n", message->message[0]); + dev_dbg(dev, "message2:\t0x%x\n", message->message[1]); + dev_dbg(dev, "message3:\t0x%x\n", message->message[2]); + dev_dbg(dev, "message4:\t0x%x\n", message->message[3]); + dev_dbg(dev, "message5:\t0x%x\n", message->message[4]); + dev_dbg(dev, "message6:\t0x%x\n", message->message[5]); + dev_dbg(dev, "message7:\t0x%x\n", message->message[6]); + dev_dbg(dev, "checksum:\t0x%x\n", message->checksum); +} + +static int mxt_check_bootloader(struct i2c_client *client, + unsigned int state) +{ + u8 val; + +recheck: + if (i2c_master_recv(client, &val, 1) != 1) { + dev_err(&client->dev, "%s: i2c recv failed\n", __func__); + return -EIO; + } + + switch (state) { + case MXT_WAITING_BOOTLOAD_CMD: + case MXT_WAITING_FRAME_DATA: + val &= ~MXT_BOOT_STATUS_MASK; + break; + case MXT_FRAME_CRC_PASS: + if (val == MXT_FRAME_CRC_CHECK) + goto recheck; + break; + default: + return -EINVAL; + } + + if (val != state) { + dev_err(&client->dev, "Unvalid bootloader mode state\n"); + return -EINVAL; + } + + return 0; +} + +static int mxt_unlock_bootloader(struct i2c_client *client) +{ + u8 buf[2]; + + buf[0] = MXT_UNLOCK_CMD_LSB; + buf[1] = MXT_UNLOCK_CMD_MSB; + + if (i2c_master_send(client, buf, 2) != 2) { + dev_err(&client->dev, "%s: i2c send failed\n", __func__); + return -EIO; + } + + return 0; +} + +static int mxt_fw_write(struct i2c_client *client, + const u8 *data, unsigned int frame_size) +{ + if (i2c_master_send(client, data, frame_size) != frame_size) { + dev_err(&client->dev, "%s: i2c send failed\n", __func__); + return -EIO; + } + + return 0; +} + +static int __mxt_read_reg(struct i2c_client *client, + u16 reg, u16 len, void *val) +{ + struct i2c_msg xfer[2]; + u8 buf[2]; + + buf[0] = reg & 0xff; + buf[1] = (reg >> 8) & 0xff; + + /* Write register */ + xfer[0].addr = client->addr; + xfer[0].flags = 0; + xfer[0].len = 2; + xfer[0].buf = buf; + + /* Read data */ + xfer[1].addr = client->addr; + xfer[1].flags = I2C_M_RD; + xfer[1].len = len; + xfer[1].buf = val; + + if (i2c_transfer(client->adapter, xfer, 2) != 2) { + dev_err(&client->dev, "%s: i2c transfer failed\n", __func__); + return -EIO; + } + + return 0; +} + +static int mxt_read_reg(struct i2c_client *client, u16 reg, u8 *val) +{ + return __mxt_read_reg(client, reg, 1, val); +} + +static int mxt_write_reg(struct i2c_client *client, u16 reg, u8 val) +{ + u8 buf[3]; + + buf[0] = reg & 0xff; + buf[1] = (reg >> 8) & 0xff; + buf[2] = val; + + if (i2c_master_send(client, buf, 3) != 3) { + dev_err(&client->dev, "%s: i2c send failed\n", __func__); + return -EIO; + } + + return 0; +} + +static int mxt_read_object_table(struct i2c_client *client, + u16 reg, u8 *object_buf) +{ + return __mxt_read_reg(client, reg, MXT_OBJECT_SIZE, + object_buf); +} + +static struct mxt_object * +mxt_get_object(struct mxt_data *data, u8 type) +{ + struct mxt_object *object; + int i; + + for (i = 0; i < data->info.object_num; i++) { + object = data->object_table + i; + if (object->type == type) + return object; + } + + dev_err(&data->client->dev, "Invalid object type\n"); + return NULL; +} + +static int mxt_read_message(struct mxt_data *data, + struct mxt_message *message) +{ + struct mxt_object *object; + u16 reg; + + object = mxt_get_object(data, MXT_GEN_MESSAGE); + if (!object) + return -EINVAL; + + reg = object->start_address; + return __mxt_read_reg(data->client, reg, + sizeof(struct mxt_message), message); +} + +static int mxt_read_object(struct mxt_data *data, + u8 type, u8 offset, u8 *val) +{ + struct mxt_object *object; + u16 reg; + + object = mxt_get_object(data, type); + if (!object) + return -EINVAL; + + reg = object->start_address; + return __mxt_read_reg(data->client, reg + offset, 1, val); +} + +static int mxt_write_object(struct mxt_data *data, + u8 type, u8 offset, u8 val) +{ + struct mxt_object *object; + u16 reg; + + object = mxt_get_object(data, type); + if (!object) + return -EINVAL; + + reg = object->start_address; + return mxt_write_reg(data->client, reg + offset, val); +} + +static void mxt_input_report(struct mxt_data *data, int single_id) +{ + struct mxt_finger *finger = data->finger; + struct input_dev *input_dev = data->input_dev; + int status = finger[single_id].status; + int finger_num = 0; + int id; + + for (id = 0; id < MXT_MAX_FINGER; id++) { + if (!finger[id].status) + continue; + + input_report_abs(input_dev, ABS_MT_TOUCH_MAJOR, + finger[id].status != MXT_RELEASE ? + finger[id].area : 0); + input_report_abs(input_dev, ABS_MT_POSITION_X, + finger[id].x); + input_report_abs(input_dev, ABS_MT_POSITION_Y, + finger[id].y); + input_mt_sync(input_dev); + + if (finger[id].status == MXT_RELEASE) + finger[id].status = 0; + else + finger_num++; + } + + input_report_key(input_dev, BTN_TOUCH, finger_num > 0); + + if (status != MXT_RELEASE) { + input_report_abs(input_dev, ABS_X, finger[single_id].x); + input_report_abs(input_dev, ABS_Y, finger[single_id].y); + } + + input_sync(input_dev); +} + +static void mxt_input_touchevent(struct mxt_data *data, + struct mxt_message *message, int id) +{ + struct mxt_finger *finger = data->finger; + struct device *dev = &data->client->dev; + u8 status = message->message[0]; + int x; + int y; + int area; + + /* Check the touch is present on the screen */ + if (!(status & MXT_DETECT)) { + if (status & MXT_RELEASE) { + dev_dbg(dev, "[%d] released\n", id); + + finger[id].status = MXT_RELEASE; + mxt_input_report(data, id); + } + return; + } + + /* Check only AMP detection */ + if (!(status & (MXT_PRESS | MXT_MOVE))) + return; + + x = (message->message[1] << 2) | ((message->message[3] & ~0x3f) >> 6); + y = (message->message[2] << 2) | ((message->message[3] & ~0xf3) >> 2); + area = message->message[4]; + + dev_dbg(dev, "[%d] %s x: %d, y: %d, area: %d\n", id, + status & MXT_MOVE ? "moved" : "pressed", + x, y, area); + + finger[id].status = status & MXT_MOVE ? + MXT_MOVE : MXT_PRESS; + finger[id].x = x; + finger[id].y = y; + finger[id].area = area; + + mxt_input_report(data, id); +} + +static irqreturn_t mxt_interrupt(int irq, void *dev_id) +{ + struct mxt_data *data = dev_id; + struct mxt_message message; + struct mxt_object *object; + struct device *dev = &data->client->dev; + int id; + u8 reportid; + u8 max_reportid; + u8 min_reportid; + + do { + if (mxt_read_message(data, &message)) { + dev_err(dev, "Failed to read message\n"); + goto end; + } + + reportid = message.reportid; + + /* whether reportid is thing of MXT_TOUCH_MULTI */ + object = mxt_get_object(data, MXT_TOUCH_MULTI); + if (!object) + goto end; + + max_reportid = object->max_reportid; + min_reportid = max_reportid - object->num_report_ids + 1; + id = reportid - min_reportid; + + if (reportid >= min_reportid && reportid <= max_reportid) + mxt_input_touchevent(data, &message, id); + else + mxt_dump_message(dev, &message); + } while (reportid != 0xff); + +end: + return IRQ_HANDLED; +} + +static int mxt_check_reg_init(struct mxt_data *data) +{ + const struct mxt_platform_data *pdata = data->pdata; + struct mxt_object *object; + struct device *dev = &data->client->dev; + int index = 0; + int i, j, config_offset; + + if (!pdata->config) { + dev_dbg(dev, "No cfg data defined, skipping reg init\n"); + return 0; + } + + for (i = 0; i < data->info.object_num; i++) { + object = data->object_table + i; + + if (!mxt_object_writable(object->type)) + continue; + + for (j = 0; j < object->size + 1; j++) { + config_offset = index + j; + if (config_offset > pdata->config_length) { + dev_err(dev, "Not enough config data!\n"); + return -EINVAL; + } + mxt_write_object(data, object->type, j, + pdata->config[config_offset]); + } + index += object->size + 1; + } + + return 0; +} + +static int mxt_make_highchg(struct mxt_data *data) +{ + struct device *dev = &data->client->dev; + struct mxt_message message; + int count = 10; + int error; + + /* Read dummy message to make high CHG pin */ + do { + error = mxt_read_message(data, &message); + if (error) + return error; + } while (message.reportid != 0xff && --count); + + if (!count) { + dev_err(dev, "CHG pin isn't cleared\n"); + return -EBUSY; + } + + return 0; +} + +static void mxt_handle_pdata(struct mxt_data *data) +{ + const struct mxt_platform_data *pdata = data->pdata; + u8 voltage; + + /* Set touchscreen lines */ + mxt_write_object(data, MXT_TOUCH_MULTI, MXT_TOUCH_XSIZE, + pdata->x_line); + mxt_write_object(data, MXT_TOUCH_MULTI, MXT_TOUCH_YSIZE, + pdata->y_line); + + /* Set touchscreen orient */ + mxt_write_object(data, MXT_TOUCH_MULTI, MXT_TOUCH_ORIENT, + pdata->orient); + + /* Set touchscreen burst length */ + mxt_write_object(data, MXT_TOUCH_MULTI, + MXT_TOUCH_BLEN, pdata->blen); + + /* Set touchscreen threshold */ + mxt_write_object(data, MXT_TOUCH_MULTI, + MXT_TOUCH_TCHTHR, pdata->threshold); + + /* Set touchscreen resolution */ + mxt_write_object(data, MXT_TOUCH_MULTI, + MXT_TOUCH_XRANGE_LSB, (pdata->x_size - 1) & 0xff); + mxt_write_object(data, MXT_TOUCH_MULTI, + MXT_TOUCH_XRANGE_MSB, (pdata->x_size - 1) >> 8); + mxt_write_object(data, MXT_TOUCH_MULTI, + MXT_TOUCH_YRANGE_LSB, (pdata->y_size - 1) & 0xff); + mxt_write_object(data, MXT_TOUCH_MULTI, + MXT_TOUCH_YRANGE_MSB, (pdata->y_size - 1) >> 8); + + /* Set touchscreen voltage */ + if (pdata->voltage) { + if (pdata->voltage < MXT_VOLTAGE_DEFAULT) { + voltage = (MXT_VOLTAGE_DEFAULT - pdata->voltage) / + MXT_VOLTAGE_STEP; + voltage = 0xff - voltage + 1; + } else + voltage = (pdata->voltage - MXT_VOLTAGE_DEFAULT) / + MXT_VOLTAGE_STEP; + + mxt_write_object(data, MXT_SPT_CTECONFIG, + MXT_CTE_VOLTAGE, voltage); + } +} + +static int mxt_get_info(struct mxt_data *data) +{ + struct i2c_client *client = data->client; + struct mxt_info *info = &data->info; + int error; + u8 val; + + error = mxt_read_reg(client, MXT_FAMILY_ID, &val); + if (error) + return error; + info->family_id = val; + + error = mxt_read_reg(client, MXT_VARIANT_ID, &val); + if (error) + return error; + info->variant_id = val; + + error = mxt_read_reg(client, MXT_VERSION, &val); + if (error) + return error; + info->version = val; + + error = mxt_read_reg(client, MXT_BUILD, &val); + if (error) + return error; + info->build = val; + + error = mxt_read_reg(client, MXT_OBJECT_NUM, &val); + if (error) + return error; + info->object_num = val; + + return 0; +} + +static int mxt_get_object_table(struct mxt_data *data) +{ + int error; + int i; + u16 reg; + u8 reportid = 0; + u8 buf[MXT_OBJECT_SIZE]; + + for (i = 0; i < data->info.object_num; i++) { + struct mxt_object *object = data->object_table + i; + + reg = MXT_OBJECT_START + MXT_OBJECT_SIZE * i; + error = mxt_read_object_table(data->client, reg, buf); + if (error) + return error; + + object->type = buf[0]; + object->start_address = (buf[2] << 8) | buf[1]; + object->size = buf[3]; + object->instances = buf[4]; + object->num_report_ids = buf[5]; + + if (object->num_report_ids) { + reportid += object->num_report_ids * + (object->instances + 1); + object->max_reportid = reportid; + } + } + + return 0; +} + +static int mxt_initialize(struct mxt_data *data) +{ + struct i2c_client *client = data->client; + struct mxt_info *info = &data->info; + int error; + u8 val; + + error = mxt_get_info(data); + if (error) + return error; + + data->object_table = kcalloc(info->object_num, + sizeof(struct mxt_object), + GFP_KERNEL); + if (!data->object_table) { + dev_err(&client->dev, "Failed to allocate memory\n"); + return -ENOMEM; + } + + /* Get object table information */ + error = mxt_get_object_table(data); + if (error) + return error; + + /* Check register init values */ + error = mxt_check_reg_init(data); + if (error) + return error; + + error = mxt_make_highchg(data); + if (error) + return error; + + mxt_handle_pdata(data); + + /* Backup to memory */ + mxt_write_object(data, MXT_GEN_COMMAND, + MXT_COMMAND_BACKUPNV, + MXT_BACKUP_VALUE); + msleep(MXT_BACKUP_TIME); + + /* Soft reset */ + mxt_write_object(data, MXT_GEN_COMMAND, + MXT_COMMAND_RESET, 1); + msleep(MXT_RESET_TIME); + + /* Update matrix size at info struct */ + error = mxt_read_reg(client, MXT_MATRIX_X_SIZE, &val); + if (error) + return error; + info->matrix_xsize = val; + + error = mxt_read_reg(client, MXT_MATRIX_Y_SIZE, &val); + if (error) + return error; + info->matrix_ysize = val; + + dev_info(&client->dev, + "Family ID: %d Variant ID: %d Version: %d Build: %d\n", + info->family_id, info->variant_id, info->version, + info->build); + + dev_info(&client->dev, + "Matrix X Size: %d Matrix Y Size: %d Object Num: %d\n", + info->matrix_xsize, info->matrix_ysize, + info->object_num); + + return 0; +} + +static ssize_t mxt_object_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct mxt_data *data = dev_get_drvdata(dev); + struct mxt_object *object; + int count = 0; + int i, j; + int error; + u8 val; + + for (i = 0; i < data->info.object_num; i++) { + object = data->object_table + i; + + count += sprintf(buf + count, + "Object Table Element %d(Type %d)\n", + i + 1, object->type); + + if (!mxt_object_readable(object->type)) { + count += sprintf(buf + count, "\n"); + continue; + } + + for (j = 0; j < object->size + 1; j++) { + error = mxt_read_object(data, + object->type, j, &val); + if (error) + return error; + + count += sprintf(buf + count, + " Byte %d: 0x%x (%d)\n", j, val, val); + } + + count += sprintf(buf + count, "\n"); + } + + return count; +} + +static int mxt_load_fw(struct device *dev, const char *fn) +{ + struct mxt_data *data = dev_get_drvdata(dev); + struct i2c_client *client = data->client; + const struct firmware *fw = NULL; + unsigned int frame_size; + unsigned int pos = 0; + int ret; + + ret = request_firmware(&fw, fn, dev); + if (ret) { + dev_err(dev, "Unable to open firmware %s\n", fn); + return ret; + } + + /* Change to the bootloader mode */ + mxt_write_object(data, MXT_GEN_COMMAND, + MXT_COMMAND_RESET, MXT_BOOT_VALUE); + msleep(MXT_RESET_TIME); + + /* Change to slave address of bootloader */ + if (client->addr == MXT_APP_LOW) + client->addr = MXT_BOOT_LOW; + else + client->addr = MXT_BOOT_HIGH; + + ret = mxt_check_bootloader(client, MXT_WAITING_BOOTLOAD_CMD); + if (ret) + goto out; + + /* Unlock bootloader */ + mxt_unlock_bootloader(client); + + while (pos < fw->size) { + ret = mxt_check_bootloader(client, + MXT_WAITING_FRAME_DATA); + if (ret) + goto out; + + frame_size = ((*(fw->data + pos) << 8) | *(fw->data + pos + 1)); + + /* We should add 2 at frame size as the the firmware data is not + * included the CRC bytes. + */ + frame_size += 2; + + /* Write one frame to device */ + mxt_fw_write(client, fw->data + pos, frame_size); + + ret = mxt_check_bootloader(client, + MXT_FRAME_CRC_PASS); + if (ret) + goto out; + + pos += frame_size; + + dev_dbg(dev, "Updated %d bytes / %zd bytes\n", pos, fw->size); + } + +out: + release_firmware(fw); + + /* Change to slave address of application */ + if (client->addr == MXT_BOOT_LOW) + client->addr = MXT_APP_LOW; + else + client->addr = MXT_APP_HIGH; + + return ret; +} + +static ssize_t mxt_update_fw_store(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t count) +{ + struct mxt_data *data = dev_get_drvdata(dev); + int error; + + disable_irq(data->irq); + + error = mxt_load_fw(dev, MXT_FW_NAME); + if (error) { + dev_err(dev, "The firmware update failed(%d)\n", error); + count = error; + } else { + dev_dbg(dev, "The firmware update succeeded\n"); + + /* Wait for reset */ + msleep(MXT_FWRESET_TIME); + + kfree(data->object_table); + data->object_table = NULL; + + mxt_initialize(data); + } + + enable_irq(data->irq); + + return count; +} + +static DEVICE_ATTR(object, 0444, mxt_object_show, NULL); +static DEVICE_ATTR(update_fw, 0664, NULL, mxt_update_fw_store); + +static struct attribute *mxt_attrs[] = { + &dev_attr_object.attr, + &dev_attr_update_fw.attr, + NULL +}; + +static const struct attribute_group mxt_attr_group = { + .attrs = mxt_attrs, +}; + +static void mxt_start(struct mxt_data *data) +{ + /* Touch enable */ + mxt_write_object(data, + MXT_TOUCH_MULTI, MXT_TOUCH_CTRL, 0x83); +} + +static void mxt_stop(struct mxt_data *data) +{ + /* Touch disable */ + mxt_write_object(data, + MXT_TOUCH_MULTI, MXT_TOUCH_CTRL, 0); +} + +static int mxt_input_open(struct input_dev *dev) +{ + struct mxt_data *data = input_get_drvdata(dev); + + mxt_start(data); + + return 0; +} + +static void mxt_input_close(struct input_dev *dev) +{ + struct mxt_data *data = input_get_drvdata(dev); + + mxt_stop(data); +} + +static int __devinit mxt_probe(struct i2c_client *client, + const struct i2c_device_id *id) +{ + const struct mxt_platform_data *pdata = client->dev.platform_data; + struct mxt_data *data; + struct input_dev *input_dev; + int error; + + if (!pdata) + return -EINVAL; + + data = kzalloc(sizeof(struct mxt_data), GFP_KERNEL); + input_dev = input_allocate_device(); + if (!data || !input_dev) { + dev_err(&client->dev, "Failed to allocate memory\n"); + error = -ENOMEM; + goto err_free_mem; + } + + input_dev->name = "Atmel maXTouch Touchscreen"; + input_dev->id.bustype = BUS_I2C; + input_dev->dev.parent = &client->dev; + input_dev->open = mxt_input_open; + input_dev->close = mxt_input_close; + + __set_bit(EV_ABS, input_dev->evbit); + __set_bit(EV_KEY, input_dev->evbit); + __set_bit(BTN_TOUCH, input_dev->keybit); + + /* For single touch */ + input_set_abs_params(input_dev, ABS_X, + 0, MXT_MAX_XC, 0, 0); + input_set_abs_params(input_dev, ABS_Y, + 0, MXT_MAX_YC, 0, 0); + + /* For multi touch */ + input_set_abs_params(input_dev, ABS_MT_TOUCH_MAJOR, + 0, MXT_MAX_AREA, 0, 0); + input_set_abs_params(input_dev, ABS_MT_POSITION_X, + 0, MXT_MAX_XC, 0, 0); + input_set_abs_params(input_dev, ABS_MT_POSITION_Y, + 0, MXT_MAX_YC, 0, 0); + + input_set_drvdata(input_dev, data); + + data->client = client; + data->input_dev = input_dev; + data->pdata = pdata; + data->irq = client->irq; + + i2c_set_clientdata(client, data); + + error = mxt_initialize(data); + if (error) + goto err_free_object; + + error = request_threaded_irq(client->irq, NULL, mxt_interrupt, + pdata->irqflags, client->dev.driver->name, data); + if (error) { + dev_err(&client->dev, "Failed to register interrupt\n"); + goto err_free_object; + } + + error = input_register_device(input_dev); + if (error) + goto err_free_irq; + + error = sysfs_create_group(&client->dev.kobj, &mxt_attr_group); + if (error) + goto err_unregister_device; + + return 0; + +err_unregister_device: + input_unregister_device(input_dev); + input_dev = NULL; +err_free_irq: + free_irq(client->irq, data); +err_free_object: + kfree(data->object_table); +err_free_mem: + input_free_device(input_dev); + kfree(data); + return error; +} + +static int __devexit mxt_remove(struct i2c_client *client) +{ + struct mxt_data *data = i2c_get_clientdata(client); + + sysfs_remove_group(&client->dev.kobj, &mxt_attr_group); + free_irq(data->irq, data); + input_unregister_device(data->input_dev); + kfree(data->object_table); + kfree(data); + + return 0; +} + +#ifdef CONFIG_PM +static int mxt_suspend(struct device *dev) +{ + struct i2c_client *client = to_i2c_client(dev); + struct mxt_data *data = i2c_get_clientdata(client); + struct input_dev *input_dev = data->input_dev; + + mutex_lock(&input_dev->mutex); + + if (input_dev->users) + mxt_stop(data); + + mutex_unlock(&input_dev->mutex); + + return 0; +} + +static int mxt_resume(struct device *dev) +{ + struct i2c_client *client = to_i2c_client(dev); + struct mxt_data *data = i2c_get_clientdata(client); + struct input_dev *input_dev = data->input_dev; + + /* Soft reset */ + mxt_write_object(data, MXT_GEN_COMMAND, + MXT_COMMAND_RESET, 1); + + msleep(MXT_RESET_TIME); + + mutex_lock(&input_dev->mutex); + + if (input_dev->users) + mxt_start(data); + + mutex_unlock(&input_dev->mutex); + + return 0; +} + +static const struct dev_pm_ops mxt_pm_ops = { + .suspend = mxt_suspend, + .resume = mxt_resume, +}; +#endif + +static const struct i2c_device_id mxt_id[] = { + { "qt602240_ts", 0 }, + { "atmel_mxt_ts", 0 }, + { "mXT224", 0 }, + { } +}; +MODULE_DEVICE_TABLE(i2c, mxt_id); + +static struct i2c_driver mxt_driver = { + .driver = { + .name = "atmel_mxt_ts", + .owner = THIS_MODULE, +#ifdef CONFIG_PM + .pm = &mxt_pm_ops, +#endif + }, + .probe = mxt_probe, + .remove = __devexit_p(mxt_remove), + .id_table = mxt_id, +}; + +static int __init mxt_init(void) +{ + return i2c_add_driver(&mxt_driver); +} + +static void __exit mxt_exit(void) +{ + i2c_del_driver(&mxt_driver); +} + +module_init(mxt_init); +module_exit(mxt_exit); + +/* Module information */ +MODULE_AUTHOR("Joonyoung Shim <jy0922.shim@samsung.com>"); +MODULE_DESCRIPTION("Atmel maXTouch Touchscreen driver"); +MODULE_LICENSE("GPL"); diff --git a/drivers/input/touchscreen/qt602240_ts.c b/drivers/input/touchscreen/qt602240_ts.c deleted file mode 100644 index 4dcb0e872f6a..000000000000 --- a/drivers/input/touchscreen/qt602240_ts.c +++ /dev/null @@ -1,1406 +0,0 @@ -/* - * AT42QT602240/ATMXT224 Touchscreen driver - * - * Copyright (C) 2010 Samsung Electronics Co.Ltd - * Author: Joonyoung Shim <jy0922.shim@samsung.com> - * - * This program is free software; you can redistribute it and/or modify it - * under the terms of the GNU General Public License as published by the - * Free Software Foundation; either version 2 of the License, or (at your - * option) any later version. - * - */ - -#include <linux/module.h> -#include <linux/init.h> -#include <linux/delay.h> -#include <linux/firmware.h> -#include <linux/i2c.h> -#include <linux/i2c/qt602240_ts.h> -#include <linux/input.h> -#include <linux/interrupt.h> -#include <linux/slab.h> - -/* Version */ -#define QT602240_VER_20 20 -#define QT602240_VER_21 21 -#define QT602240_VER_22 22 - -/* Slave addresses */ -#define QT602240_APP_LOW 0x4a -#define QT602240_APP_HIGH 0x4b -#define QT602240_BOOT_LOW 0x24 -#define QT602240_BOOT_HIGH 0x25 - -/* Firmware */ -#define QT602240_FW_NAME "qt602240.fw" - -/* Registers */ -#define QT602240_FAMILY_ID 0x00 -#define QT602240_VARIANT_ID 0x01 -#define QT602240_VERSION 0x02 -#define QT602240_BUILD 0x03 -#define QT602240_MATRIX_X_SIZE 0x04 -#define QT602240_MATRIX_Y_SIZE 0x05 -#define QT602240_OBJECT_NUM 0x06 -#define QT602240_OBJECT_START 0x07 - -#define QT602240_OBJECT_SIZE 6 - -/* Object types */ -#define QT602240_DEBUG_DIAGNOSTIC 37 -#define QT602240_GEN_MESSAGE 5 -#define QT602240_GEN_COMMAND 6 -#define QT602240_GEN_POWER 7 -#define QT602240_GEN_ACQUIRE 8 -#define QT602240_TOUCH_MULTI 9 -#define QT602240_TOUCH_KEYARRAY 15 -#define QT602240_TOUCH_PROXIMITY 23 -#define QT602240_PROCI_GRIPFACE 20 -#define QT602240_PROCG_NOISE 22 -#define QT602240_PROCI_ONETOUCH 24 -#define QT602240_PROCI_TWOTOUCH 27 -#define QT602240_SPT_COMMSCONFIG 18 /* firmware ver 21 over */ -#define QT602240_SPT_GPIOPWM 19 -#define QT602240_SPT_SELFTEST 25 -#define QT602240_SPT_CTECONFIG 28 -#define QT602240_SPT_USERDATA 38 /* firmware ver 21 over */ - -/* QT602240_GEN_COMMAND field */ -#define QT602240_COMMAND_RESET 0 -#define QT602240_COMMAND_BACKUPNV 1 -#define QT602240_COMMAND_CALIBRATE 2 -#define QT602240_COMMAND_REPORTALL 3 -#define QT602240_COMMAND_DIAGNOSTIC 5 - -/* QT602240_GEN_POWER field */ -#define QT602240_POWER_IDLEACQINT 0 -#define QT602240_POWER_ACTVACQINT 1 -#define QT602240_POWER_ACTV2IDLETO 2 - -/* QT602240_GEN_ACQUIRE field */ -#define QT602240_ACQUIRE_CHRGTIME 0 -#define QT602240_ACQUIRE_TCHDRIFT 2 -#define QT602240_ACQUIRE_DRIFTST 3 -#define QT602240_ACQUIRE_TCHAUTOCAL 4 -#define QT602240_ACQUIRE_SYNC 5 -#define QT602240_ACQUIRE_ATCHCALST 6 -#define QT602240_ACQUIRE_ATCHCALSTHR 7 - -/* QT602240_TOUCH_MULTI field */ -#define QT602240_TOUCH_CTRL 0 -#define QT602240_TOUCH_XORIGIN 1 -#define QT602240_TOUCH_YORIGIN 2 -#define QT602240_TOUCH_XSIZE 3 -#define QT602240_TOUCH_YSIZE 4 -#define QT602240_TOUCH_BLEN 6 -#define QT602240_TOUCH_TCHTHR 7 -#define QT602240_TOUCH_TCHDI 8 -#define QT602240_TOUCH_ORIENT 9 -#define QT602240_TOUCH_MOVHYSTI 11 -#define QT602240_TOUCH_MOVHYSTN 12 -#define QT602240_TOUCH_NUMTOUCH 14 -#define QT602240_TOUCH_MRGHYST 15 -#define QT602240_TOUCH_MRGTHR 16 -#define QT602240_TOUCH_AMPHYST 17 -#define QT602240_TOUCH_XRANGE_LSB 18 -#define QT602240_TOUCH_XRANGE_MSB 19 -#define QT602240_TOUCH_YRANGE_LSB 20 -#define QT602240_TOUCH_YRANGE_MSB 21 -#define QT602240_TOUCH_XLOCLIP 22 -#define QT602240_TOUCH_XHICLIP 23 -#define QT602240_TOUCH_YLOCLIP 24 -#define QT602240_TOUCH_YHICLIP 25 -#define QT602240_TOUCH_XEDGECTRL 26 -#define QT602240_TOUCH_XEDGEDIST 27 -#define QT602240_TOUCH_YEDGECTRL 28 -#define QT602240_TOUCH_YEDGEDIST 29 -#define QT602240_TOUCH_JUMPLIMIT 30 /* firmware ver 22 over */ - -/* QT602240_PROCI_GRIPFACE field */ -#define QT602240_GRIPFACE_CTRL 0 -#define QT602240_GRIPFACE_XLOGRIP 1 -#define QT602240_GRIPFACE_XHIGRIP 2 -#define QT602240_GRIPFACE_YLOGRIP 3 -#define QT602240_GRIPFACE_YHIGRIP 4 -#define QT602240_GRIPFACE_MAXTCHS 5 -#define QT602240_GRIPFACE_SZTHR1 7 -#define QT602240_GRIPFACE_SZTHR2 8 -#define QT602240_GRIPFACE_SHPTHR1 9 -#define QT602240_GRIPFACE_SHPTHR2 10 -#define QT602240_GRIPFACE_SUPEXTTO 11 - -/* QT602240_PROCI_NOISE field */ -#define QT602240_NOISE_CTRL 0 -#define QT602240_NOISE_OUTFLEN 1 -#define QT602240_NOISE_GCAFUL_LSB 3 -#define QT602240_NOISE_GCAFUL_MSB 4 -#define QT602240_NOISE_GCAFLL_LSB 5 -#define QT602240_NOISE_GCAFLL_MSB 6 -#define QT602240_NOISE_ACTVGCAFVALID 7 -#define QT602240_NOISE_NOISETHR 8 -#define QT602240_NOISE_FREQHOPSCALE 10 -#define QT602240_NOISE_FREQ0 11 -#define QT602240_NOISE_FREQ1 12 -#define QT602240_NOISE_FREQ2 13 -#define QT602240_NOISE_FREQ3 14 -#define QT602240_NOISE_FREQ4 15 -#define QT602240_NOISE_IDLEGCAFVALID 16 - -/* QT602240_SPT_COMMSCONFIG */ -#define QT602240_COMMS_CTRL 0 -#define QT602240_COMMS_CMD 1 - -/* QT602240_SPT_CTECONFIG field */ -#define QT602240_CTE_CTRL 0 -#define QT602240_CTE_CMD 1 -#define QT602240_CTE_MODE 2 -#define QT602240_CTE_IDLEGCAFDEPTH 3 -#define QT602240_CTE_ACTVGCAFDEPTH 4 -#define QT602240_CTE_VOLTAGE 5 /* firmware ver 21 over */ - -#define QT602240_VOLTAGE_DEFAULT 2700000 -#define QT602240_VOLTAGE_STEP 10000 - -/* Define for QT602240_GEN_COMMAND */ -#define QT602240_BOOT_VALUE 0xa5 -#define QT602240_BACKUP_VALUE 0x55 -#define QT602240_BACKUP_TIME 25 /* msec */ -#define QT602240_RESET_TIME 65 /* msec */ - -#define QT602240_FWRESET_TIME 175 /* msec */ - -/* Command to unlock bootloader */ -#define QT602240_UNLOCK_CMD_MSB 0xaa -#define QT602240_UNLOCK_CMD_LSB 0xdc - -/* Bootloader mode status */ -#define QT602240_WAITING_BOOTLOAD_CMD 0xc0 /* valid 7 6 bit only */ -#define QT602240_WAITING_FRAME_DATA 0x80 /* valid 7 6 bit only */ -#define QT602240_FRAME_CRC_CHECK 0x02 -#define QT602240_FRAME_CRC_FAIL 0x03 -#define QT602240_FRAME_CRC_PASS 0x04 -#define QT602240_APP_CRC_FAIL 0x40 /* valid 7 8 bit only */ -#define QT602240_BOOT_STATUS_MASK 0x3f - -/* Touch status */ -#define QT602240_SUPPRESS (1 << 1) -#define QT602240_AMP (1 << 2) -#define QT602240_VECTOR (1 << 3) -#define QT602240_MOVE (1 << 4) -#define QT602240_RELEASE (1 << 5) -#define QT602240_PRESS (1 << 6) -#define QT602240_DETECT (1 << 7) - -/* Touchscreen absolute values */ -#define QT602240_MAX_XC 0x3ff -#define QT602240_MAX_YC 0x3ff -#define QT602240_MAX_AREA 0xff - -#define QT602240_MAX_FINGER 10 - -/* Initial register values recommended from chip vendor */ -static const u8 init_vals_ver_20[] = { - /* QT602240_GEN_COMMAND(6) */ - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - /* QT602240_GEN_POWER(7) */ - 0x20, 0xff, 0x32, - /* QT602240_GEN_ACQUIRE(8) */ - 0x08, 0x05, 0x05, 0x00, 0x00, 0x00, 0x05, 0x14, - /* QT602240_TOUCH_MULTI(9) */ - 0x00, 0x00, 0x00, 0x11, 0x0a, 0x00, 0x00, 0x00, 0x02, 0x00, - 0x00, 0x01, 0x01, 0x0e, 0x0a, 0x0a, 0x0a, 0x0a, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x88, 0x64, - /* QT602240_TOUCH_KEYARRAY(15) */ - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, - /* QT602240_SPT_GPIOPWM(19) */ - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, - /* QT602240_PROCI_GRIPFACE(20) */ - 0x00, 0x64, 0x64, 0x64, 0x64, 0x00, 0x00, 0x1e, 0x14, 0x04, - 0x1e, 0x00, - /* QT602240_PROCG_NOISE(22) */ - 0x05, 0x00, 0x00, 0x19, 0x00, 0xe7, 0xff, 0x04, 0x32, 0x00, - 0x01, 0x0a, 0x0f, 0x14, 0x00, 0x00, 0xe8, - /* QT602240_TOUCH_PROXIMITY(23) */ - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, - /* QT602240_PROCI_ONETOUCH(24) */ - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - /* QT602240_SPT_SELFTEST(25) */ - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, - /* QT602240_PROCI_TWOTOUCH(27) */ - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - /* QT602240_SPT_CTECONFIG(28) */ - 0x00, 0x00, 0x00, 0x04, 0x08, -}; - -static const u8 init_vals_ver_21[] = { - /* QT602240_GEN_COMMAND(6) */ - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - /* QT602240_GEN_POWER(7) */ - 0x20, 0xff, 0x32, - /* QT602240_GEN_ACQUIRE(8) */ - 0x0a, 0x00, 0x05, 0x00, 0x00, 0x00, 0x09, 0x23, - /* QT602240_TOUCH_MULTI(9) */ - 0x00, 0x00, 0x00, 0x13, 0x0b, 0x00, 0x00, 0x00, 0x02, 0x00, - 0x00, 0x01, 0x01, 0x0e, 0x0a, 0x0a, 0x0a, 0x0a, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - /* QT602240_TOUCH_KEYARRAY(15) */ - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, - /* QT602240_SPT_GPIOPWM(19) */ - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - /* QT602240_PROCI_GRIPFACE(20) */ - 0x07, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x50, 0x28, 0x04, - 0x0f, 0x0a, - /* QT602240_PROCG_NOISE(22) */ - 0x05, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0x23, 0x00, - 0x00, 0x05, 0x0f, 0x19, 0x23, 0x2d, 0x03, - /* QT602240_TOUCH_PROXIMITY(23) */ - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, - /* QT602240_PROCI_ONETOUCH(24) */ - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - /* QT602240_SPT_SELFTEST(25) */ - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, - /* QT602240_PROCI_TWOTOUCH(27) */ - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - /* QT602240_SPT_CTECONFIG(28) */ - 0x00, 0x00, 0x00, 0x08, 0x10, 0x00, -}; - -static const u8 init_vals_ver_22[] = { - /* QT602240_GEN_COMMAND(6) */ - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - /* QT602240_GEN_POWER(7) */ - 0x20, 0xff, 0x32, - /* QT602240_GEN_ACQUIRE(8) */ - 0x0a, 0x00, 0x05, 0x00, 0x00, 0x00, 0x09, 0x23, - /* QT602240_TOUCH_MULTI(9) */ - 0x00, 0x00, 0x00, 0x13, 0x0b, 0x00, 0x00, 0x00, 0x02, 0x00, - 0x00, 0x01, 0x01, 0x0e, 0x0a, 0x0a, 0x0a, 0x0a, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, - /* QT602240_TOUCH_KEYARRAY(15) */ - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, - /* QT602240_SPT_GPIOPWM(19) */ - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - /* QT602240_PROCI_GRIPFACE(20) */ - 0x07, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x50, 0x28, 0x04, - 0x0f, 0x0a, - /* QT602240_PROCG_NOISE(22) */ - 0x05, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0x23, 0x00, - 0x00, 0x05, 0x0f, 0x19, 0x23, 0x2d, 0x03, - /* QT602240_TOUCH_PROXIMITY(23) */ - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, - /* QT602240_PROCI_ONETOUCH(24) */ - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - /* QT602240_SPT_SELFTEST(25) */ - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, - /* QT602240_PROCI_TWOTOUCH(27) */ - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - /* QT602240_SPT_CTECONFIG(28) */ - 0x00, 0x00, 0x00, 0x08, 0x10, 0x00, -}; - -struct qt602240_info { - u8 family_id; - u8 variant_id; - u8 version; - u8 build; - u8 matrix_xsize; - u8 matrix_ysize; - u8 object_num; -}; - -struct qt602240_object { - u8 type; - u16 start_address; - u8 size; - u8 instances; - u8 num_report_ids; - - /* to map object and message */ - u8 max_reportid; -}; - -struct qt602240_message { - u8 reportid; - u8 message[7]; - u8 checksum; -}; - -struct qt602240_finger { - int status; - int x; - int y; - int area; -}; - -/* Each client has this additional data */ -struct qt602240_data { - struct i2c_client *client; - struct input_dev *input_dev; - const struct qt602240_platform_data *pdata; - struct qt602240_object *object_table; - struct qt602240_info info; - struct qt602240_finger finger[QT602240_MAX_FINGER]; - unsigned int irq; -}; - -static bool qt602240_object_readable(unsigned int type) -{ - switch (type) { - case QT602240_GEN_MESSAGE: - case QT602240_GEN_COMMAND: - case QT602240_GEN_POWER: - case QT602240_GEN_ACQUIRE: - case QT602240_TOUCH_MULTI: - case QT602240_TOUCH_KEYARRAY: - case QT602240_TOUCH_PROXIMITY: - case QT602240_PROCI_GRIPFACE: - case QT602240_PROCG_NOISE: - case QT602240_PROCI_ONETOUCH: - case QT602240_PROCI_TWOTOUCH: - case QT602240_SPT_COMMSCONFIG: - case QT602240_SPT_GPIOPWM: - case QT602240_SPT_SELFTEST: - case QT602240_SPT_CTECONFIG: - case QT602240_SPT_USERDATA: - return true; - default: - return false; - } -} - -static bool qt602240_object_writable(unsigned int type) -{ - switch (type) { - case QT602240_GEN_COMMAND: - case QT602240_GEN_POWER: - case QT602240_GEN_ACQUIRE: - case QT602240_TOUCH_MULTI: - case QT602240_TOUCH_KEYARRAY: - case QT602240_TOUCH_PROXIMITY: - case QT602240_PROCI_GRIPFACE: - case QT602240_PROCG_NOISE: - case QT602240_PROCI_ONETOUCH: - case QT602240_PROCI_TWOTOUCH: - case QT602240_SPT_GPIOPWM: - case QT602240_SPT_SELFTEST: - case QT602240_SPT_CTECONFIG: - return true; - default: - return false; - } -} - -static void qt602240_dump_message(struct device *dev, - struct qt602240_message *message) -{ - dev_dbg(dev, "reportid:\t0x%x\n", message->reportid); - dev_dbg(dev, "message1:\t0x%x\n", message->message[0]); - dev_dbg(dev, "message2:\t0x%x\n", message->message[1]); - dev_dbg(dev, "message3:\t0x%x\n", message->message[2]); - dev_dbg(dev, "message4:\t0x%x\n", message->message[3]); - dev_dbg(dev, "message5:\t0x%x\n", message->message[4]); - dev_dbg(dev, "message6:\t0x%x\n", message->message[5]); - dev_dbg(dev, "message7:\t0x%x\n", message->message[6]); - dev_dbg(dev, "checksum:\t0x%x\n", message->checksum); -} - -static int qt602240_check_bootloader(struct i2c_client *client, - unsigned int state) -{ - u8 val; - -recheck: - if (i2c_master_recv(client, &val, 1) != 1) { - dev_err(&client->dev, "%s: i2c recv failed\n", __func__); - return -EIO; - } - - switch (state) { - case QT602240_WAITING_BOOTLOAD_CMD: - case QT602240_WAITING_FRAME_DATA: - val &= ~QT602240_BOOT_STATUS_MASK; - break; - case QT602240_FRAME_CRC_PASS: - if (val == QT602240_FRAME_CRC_CHECK) - goto recheck; - break; - default: - return -EINVAL; - } - - if (val != state) { - dev_err(&client->dev, "Unvalid bootloader mode state\n"); - return -EINVAL; - } - - return 0; -} - -static int qt602240_unlock_bootloader(struct i2c_client *client) -{ - u8 buf[2]; - - buf[0] = QT602240_UNLOCK_CMD_LSB; - buf[1] = QT602240_UNLOCK_CMD_MSB; - - if (i2c_master_send(client, buf, 2) != 2) { - dev_err(&client->dev, "%s: i2c send failed\n", __func__); - return -EIO; - } - - return 0; -} - -static int qt602240_fw_write(struct i2c_client *client, - const u8 *data, unsigned int frame_size) -{ - if (i2c_master_send(client, data, frame_size) != frame_size) { - dev_err(&client->dev, "%s: i2c send failed\n", __func__); - return -EIO; - } - - return 0; -} - -static int __qt602240_read_reg(struct i2c_client *client, - u16 reg, u16 len, void *val) -{ - struct i2c_msg xfer[2]; - u8 buf[2]; - - buf[0] = reg & 0xff; - buf[1] = (reg >> 8) & 0xff; - - /* Write register */ - xfer[0].addr = client->addr; - xfer[0].flags = 0; - xfer[0].len = 2; - xfer[0].buf = buf; - - /* Read data */ - xfer[1].addr = client->addr; - xfer[1].flags = I2C_M_RD; - xfer[1].len = len; - xfer[1].buf = val; - - if (i2c_transfer(client->adapter, xfer, 2) != 2) { - dev_err(&client->dev, "%s: i2c transfer failed\n", __func__); - return -EIO; - } - - return 0; -} - -static int qt602240_read_reg(struct i2c_client *client, u16 reg, u8 *val) -{ - return __qt602240_read_reg(client, reg, 1, val); -} - -static int qt602240_write_reg(struct i2c_client *client, u16 reg, u8 val) -{ - u8 buf[3]; - - buf[0] = reg & 0xff; - buf[1] = (reg >> 8) & 0xff; - buf[2] = val; - - if (i2c_master_send(client, buf, 3) != 3) { - dev_err(&client->dev, "%s: i2c send failed\n", __func__); - return -EIO; - } - - return 0; -} - -static int qt602240_read_object_table(struct i2c_client *client, - u16 reg, u8 *object_buf) -{ - return __qt602240_read_reg(client, reg, QT602240_OBJECT_SIZE, - object_buf); -} - -static struct qt602240_object * -qt602240_get_object(struct qt602240_data *data, u8 type) -{ - struct qt602240_object *object; - int i; - - for (i = 0; i < data->info.object_num; i++) { - object = data->object_table + i; - if (object->type == type) - return object; - } - - dev_err(&data->client->dev, "Invalid object type\n"); - return NULL; -} - -static int qt602240_read_message(struct qt602240_data *data, - struct qt602240_message *message) -{ - struct qt602240_object *object; - u16 reg; - - object = qt602240_get_object(data, QT602240_GEN_MESSAGE); - if (!object) - return -EINVAL; - - reg = object->start_address; - return __qt602240_read_reg(data->client, reg, - sizeof(struct qt602240_message), message); -} - -static int qt602240_read_object(struct qt602240_data *data, - u8 type, u8 offset, u8 *val) -{ - struct qt602240_object *object; - u16 reg; - - object = qt602240_get_object(data, type); - if (!object) - return -EINVAL; - - reg = object->start_address; - return __qt602240_read_reg(data->client, reg + offset, 1, val); -} - -static int qt602240_write_object(struct qt602240_data *data, - u8 type, u8 offset, u8 val) -{ - struct qt602240_object *object; - u16 reg; - - object = qt602240_get_object(data, type); - if (!object) - return -EINVAL; - - reg = object->start_address; - return qt602240_write_reg(data->client, reg + offset, val); -} - -static void qt602240_input_report(struct qt602240_data *data, int single_id) -{ - struct qt602240_finger *finger = data->finger; - struct input_dev *input_dev = data->input_dev; - int status = finger[single_id].status; - int finger_num = 0; - int id; - - for (id = 0; id < QT602240_MAX_FINGER; id++) { - if (!finger[id].status) - continue; - - input_report_abs(input_dev, ABS_MT_TOUCH_MAJOR, - finger[id].status != QT602240_RELEASE ? - finger[id].area : 0); - input_report_abs(input_dev, ABS_MT_POSITION_X, - finger[id].x); - input_report_abs(input_dev, ABS_MT_POSITION_Y, - finger[id].y); - input_mt_sync(input_dev); - - if (finger[id].status == QT602240_RELEASE) - finger[id].status = 0; - else - finger_num++; - } - - input_report_key(input_dev, BTN_TOUCH, finger_num > 0); - - if (status != QT602240_RELEASE) { - input_report_abs(input_dev, ABS_X, finger[single_id].x); - input_report_abs(input_dev, ABS_Y, finger[single_id].y); - } - - input_sync(input_dev); -} - -static void qt602240_input_touchevent(struct qt602240_data *data, - struct qt602240_message *message, int id) -{ - struct qt602240_finger *finger = data->finger; - struct device *dev = &data->client->dev; - u8 status = message->message[0]; - int x; - int y; - int area; - - /* Check the touch is present on the screen */ - if (!(status & QT602240_DETECT)) { - if (status & QT602240_RELEASE) { - dev_dbg(dev, "[%d] released\n", id); - - finger[id].status = QT602240_RELEASE; - qt602240_input_report(data, id); - } - return; - } - - /* Check only AMP detection */ - if (!(status & (QT602240_PRESS | QT602240_MOVE))) - return; - - x = (message->message[1] << 2) | ((message->message[3] & ~0x3f) >> 6); - y = (message->message[2] << 2) | ((message->message[3] & ~0xf3) >> 2); - area = message->message[4]; - - dev_dbg(dev, "[%d] %s x: %d, y: %d, area: %d\n", id, - status & QT602240_MOVE ? "moved" : "pressed", - x, y, area); - - finger[id].status = status & QT602240_MOVE ? - QT602240_MOVE : QT602240_PRESS; - finger[id].x = x; - finger[id].y = y; - finger[id].area = area; - - qt602240_input_report(data, id); -} - -static irqreturn_t qt602240_interrupt(int irq, void *dev_id) -{ - struct qt602240_data *data = dev_id; - struct qt602240_message message; - struct qt602240_object *object; - struct device *dev = &data->client->dev; - int id; - u8 reportid; - u8 max_reportid; - u8 min_reportid; - - do { - if (qt602240_read_message(data, &message)) { - dev_err(dev, "Failed to read message\n"); - goto end; - } - - reportid = message.reportid; - - /* whether reportid is thing of QT602240_TOUCH_MULTI */ - object = qt602240_get_object(data, QT602240_TOUCH_MULTI); - if (!object) - goto end; - - max_reportid = object->max_reportid; - min_reportid = max_reportid - object->num_report_ids + 1; - id = reportid - min_reportid; - - if (reportid >= min_reportid && reportid <= max_reportid) - qt602240_input_touchevent(data, &message, id); - else - qt602240_dump_message(dev, &message); - } while (reportid != 0xff); - -end: - return IRQ_HANDLED; -} - -static int qt602240_check_reg_init(struct qt602240_data *data) -{ - struct qt602240_object *object; - struct device *dev = &data->client->dev; - int index = 0; - int i, j; - u8 version = data->info.version; - u8 *init_vals; - - switch (version) { - case QT602240_VER_20: - init_vals = (u8 *)init_vals_ver_20; - break; - case QT602240_VER_21: - init_vals = (u8 *)init_vals_ver_21; - break; - case QT602240_VER_22: - init_vals = (u8 *)init_vals_ver_22; - break; - default: - dev_err(dev, "Firmware version %d doesn't support\n", version); - return -EINVAL; - } - - for (i = 0; i < data->info.object_num; i++) { - object = data->object_table + i; - - if (!qt602240_object_writable(object->type)) - continue; - - for (j = 0; j < object->size + 1; j++) - qt602240_write_object(data, object->type, j, - init_vals[index + j]); - - index += object->size + 1; - } - - return 0; -} - -static int qt602240_check_matrix_size(struct qt602240_data *data) -{ - const struct qt602240_platform_data *pdata = data->pdata; - struct device *dev = &data->client->dev; - int mode = -1; - int error; - u8 val; - - dev_dbg(dev, "Number of X lines: %d\n", pdata->x_line); - dev_dbg(dev, "Number of Y lines: %d\n", pdata->y_line); - - switch (pdata->x_line) { - case 0 ... 15: - if (pdata->y_line <= 14) - mode = 0; - break; - case 16: - if (pdata->y_line <= 12) - mode = 1; - if (pdata->y_line == 13 || pdata->y_line == 14) - mode = 0; - break; - case 17: - if (pdata->y_line <= 11) - mode = 2; - if (pdata->y_line == 12 || pdata->y_line == 13) - mode = 1; - break; - case 18: - if (pdata->y_line <= 10) - mode = 3; - if (pdata->y_line == 11 || pdata->y_line == 12) - mode = 2; - break; - case 19: - if (pdata->y_line <= 9) - mode = 4; - if (pdata->y_line == 10 || pdata->y_line == 11) - mode = 3; - break; - case 20: - mode = 4; - } - - if (mode < 0) { - dev_err(dev, "Invalid X/Y lines\n"); - return -EINVAL; - } - - error = qt602240_read_object(data, QT602240_SPT_CTECONFIG, - QT602240_CTE_MODE, &val); - if (error) - return error; - - if (mode == val) - return 0; - - /* Change the CTE configuration */ - qt602240_write_object(data, QT602240_SPT_CTECONFIG, - QT602240_CTE_CTRL, 1); - qt602240_write_object(data, QT602240_SPT_CTECONFIG, - QT602240_CTE_MODE, mode); - qt602240_write_object(data, QT602240_SPT_CTECONFIG, - QT602240_CTE_CTRL, 0); - - return 0; -} - -static int qt602240_make_highchg(struct qt602240_data *data) -{ - struct device *dev = &data->client->dev; - int count = 10; - int error; - u8 val; - - /* Read dummy message to make high CHG pin */ - do { - error = qt602240_read_object(data, QT602240_GEN_MESSAGE, 0, &val); - if (error) - return error; - } while ((val != 0xff) && --count); - - if (!count) { - dev_err(dev, "CHG pin isn't cleared\n"); - return -EBUSY; - } - - return 0; -} - -static void qt602240_handle_pdata(struct qt602240_data *data) -{ - const struct qt602240_platform_data *pdata = data->pdata; - u8 voltage; - - /* Set touchscreen lines */ - qt602240_write_object(data, QT602240_TOUCH_MULTI, QT602240_TOUCH_XSIZE, - pdata->x_line); - qt602240_write_object(data, QT602240_TOUCH_MULTI, QT602240_TOUCH_YSIZE, - pdata->y_line); - - /* Set touchscreen orient */ - qt602240_write_object(data, QT602240_TOUCH_MULTI, QT602240_TOUCH_ORIENT, - pdata->orient); - - /* Set touchscreen burst length */ - qt602240_write_object(data, QT602240_TOUCH_MULTI, - QT602240_TOUCH_BLEN, pdata->blen); - - /* Set touchscreen threshold */ - qt602240_write_object(data, QT602240_TOUCH_MULTI, - QT602240_TOUCH_TCHTHR, pdata->threshold); - - /* Set touchscreen resolution */ - qt602240_write_object(data, QT602240_TOUCH_MULTI, - QT602240_TOUCH_XRANGE_LSB, (pdata->x_size - 1) & 0xff); - qt602240_write_object(data, QT602240_TOUCH_MULTI, - QT602240_TOUCH_XRANGE_MSB, (pdata->x_size - 1) >> 8); - qt602240_write_object(data, QT602240_TOUCH_MULTI, - QT602240_TOUCH_YRANGE_LSB, (pdata->y_size - 1) & 0xff); - qt602240_write_object(data, QT602240_TOUCH_MULTI, - QT602240_TOUCH_YRANGE_MSB, (pdata->y_size - 1) >> 8); - - /* Set touchscreen voltage */ - if (data->info.version >= QT602240_VER_21 && pdata->voltage) { - if (pdata->voltage < QT602240_VOLTAGE_DEFAULT) { - voltage = (QT602240_VOLTAGE_DEFAULT - pdata->voltage) / - QT602240_VOLTAGE_STEP; - voltage = 0xff - voltage + 1; - } else - voltage = (pdata->voltage - QT602240_VOLTAGE_DEFAULT) / - QT602240_VOLTAGE_STEP; - - qt602240_write_object(data, QT602240_SPT_CTECONFIG, - QT602240_CTE_VOLTAGE, voltage); - } -} - -static int qt602240_get_info(struct qt602240_data *data) -{ - struct i2c_client *client = data->client; - struct qt602240_info *info = &data->info; - int error; - u8 val; - - error = qt602240_read_reg(client, QT602240_FAMILY_ID, &val); - if (error) - return error; - info->family_id = val; - - error = qt602240_read_reg(client, QT602240_VARIANT_ID, &val); - if (error) - return error; - info->variant_id = val; - - error = qt602240_read_reg(client, QT602240_VERSION, &val); - if (error) - return error; - info->version = val; - - error = qt602240_read_reg(client, QT602240_BUILD, &val); - if (error) - return error; - info->build = val; - - error = qt602240_read_reg(client, QT602240_OBJECT_NUM, &val); - if (error) - return error; - info->object_num = val; - - return 0; -} - -static int qt602240_get_object_table(struct qt602240_data *data) -{ - int error; - int i; - u16 reg; - u8 reportid = 0; - u8 buf[QT602240_OBJECT_SIZE]; - - for (i = 0; i < data->info.object_num; i++) { - struct qt602240_object *object = data->object_table + i; - - reg = QT602240_OBJECT_START + QT602240_OBJECT_SIZE * i; - error = qt602240_read_object_table(data->client, reg, buf); - if (error) - return error; - - object->type = buf[0]; - object->start_address = (buf[2] << 8) | buf[1]; - object->size = buf[3]; - object->instances = buf[4]; - object->num_report_ids = buf[5]; - - if (object->num_report_ids) { - reportid += object->num_report_ids * - (object->instances + 1); - object->max_reportid = reportid; - } - } - - return 0; -} - -static int qt602240_initialize(struct qt602240_data *data) -{ - struct i2c_client *client = data->client; - struct qt602240_info *info = &data->info; - int error; - u8 val; - - error = qt602240_get_info(data); - if (error) - return error; - - data->object_table = kcalloc(info->object_num, - sizeof(struct qt602240_object), - GFP_KERNEL); - if (!data->object_table) { - dev_err(&client->dev, "Failed to allocate memory\n"); - return -ENOMEM; - } - - /* Get object table information */ - error = qt602240_get_object_table(data); - if (error) - return error; - - /* Check register init values */ - error = qt602240_check_reg_init(data); - if (error) - return error; - - /* Check X/Y matrix size */ - error = qt602240_check_matrix_size(data); - if (error) - return error; - - error = qt602240_make_highchg(data); - if (error) - return error; - - qt602240_handle_pdata(data); - - /* Backup to memory */ - qt602240_write_object(data, QT602240_GEN_COMMAND, - QT602240_COMMAND_BACKUPNV, - QT602240_BACKUP_VALUE); - msleep(QT602240_BACKUP_TIME); - - /* Soft reset */ - qt602240_write_object(data, QT602240_GEN_COMMAND, - QT602240_COMMAND_RESET, 1); - msleep(QT602240_RESET_TIME); - - /* Update matrix size at info struct */ - error = qt602240_read_reg(client, QT602240_MATRIX_X_SIZE, &val); - if (error) - return error; - info->matrix_xsize = val; - - error = qt602240_read_reg(client, QT602240_MATRIX_Y_SIZE, &val); - if (error) - return error; - info->matrix_ysize = val; - - dev_info(&client->dev, - "Family ID: %d Variant ID: %d Version: %d Build: %d\n", - info->family_id, info->variant_id, info->version, - info->build); - - dev_info(&client->dev, - "Matrix X Size: %d Matrix Y Size: %d Object Num: %d\n", - info->matrix_xsize, info->matrix_ysize, - info->object_num); - - return 0; -} - -static ssize_t qt602240_object_show(struct device *dev, - struct device_attribute *attr, char *buf) -{ - struct qt602240_data *data = dev_get_drvdata(dev); - struct qt602240_object *object; - int count = 0; - int i, j; - int error; - u8 val; - - for (i = 0; i < data->info.object_num; i++) { - object = data->object_table + i; - - count += sprintf(buf + count, - "Object Table Element %d(Type %d)\n", - i + 1, object->type); - - if (!qt602240_object_readable(object->type)) { - count += sprintf(buf + count, "\n"); - continue; - } - - for (j = 0; j < object->size + 1; j++) { - error = qt602240_read_object(data, - object->type, j, &val); - if (error) - return error; - - count += sprintf(buf + count, - " Byte %d: 0x%x (%d)\n", j, val, val); - } - - count += sprintf(buf + count, "\n"); - } - - return count; -} - -static int qt602240_load_fw(struct device *dev, const char *fn) -{ - struct qt602240_data *data = dev_get_drvdata(dev); - struct i2c_client *client = data->client; - const struct firmware *fw = NULL; - unsigned int frame_size; - unsigned int pos = 0; - int ret; - - ret = request_firmware(&fw, fn, dev); - if (ret) { - dev_err(dev, "Unable to open firmware %s\n", fn); - return ret; - } - - /* Change to the bootloader mode */ - qt602240_write_object(data, QT602240_GEN_COMMAND, - QT602240_COMMAND_RESET, QT602240_BOOT_VALUE); - msleep(QT602240_RESET_TIME); - - /* Change to slave address of bootloader */ - if (client->addr == QT602240_APP_LOW) - client->addr = QT602240_BOOT_LOW; - else - client->addr = QT602240_BOOT_HIGH; - - ret = qt602240_check_bootloader(client, QT602240_WAITING_BOOTLOAD_CMD); - if (ret) - goto out; - - /* Unlock bootloader */ - qt602240_unlock_bootloader(client); - - while (pos < fw->size) { - ret = qt602240_check_bootloader(client, - QT602240_WAITING_FRAME_DATA); - if (ret) - goto out; - - frame_size = ((*(fw->data + pos) << 8) | *(fw->data + pos + 1)); - - /* We should add 2 at frame size as the the firmware data is not - * included the CRC bytes. - */ - frame_size += 2; - - /* Write one frame to device */ - qt602240_fw_write(client, fw->data + pos, frame_size); - - ret = qt602240_check_bootloader(client, - QT602240_FRAME_CRC_PASS); - if (ret) - goto out; - - pos += frame_size; - - dev_dbg(dev, "Updated %d bytes / %zd bytes\n", pos, fw->size); - } - -out: - release_firmware(fw); - - /* Change to slave address of application */ - if (client->addr == QT602240_BOOT_LOW) - client->addr = QT602240_APP_LOW; - else - client->addr = QT602240_APP_HIGH; - - return ret; -} - -static ssize_t qt602240_update_fw_store(struct device *dev, - struct device_attribute *attr, - const char *buf, size_t count) -{ - struct qt602240_data *data = dev_get_drvdata(dev); - unsigned int version; - int error; - - if (sscanf(buf, "%u", &version) != 1) { - dev_err(dev, "Invalid values\n"); - return -EINVAL; - } - - if (data->info.version < QT602240_VER_21 || version < QT602240_VER_21) { - dev_err(dev, "FW update supported starting with version 21\n"); - return -EINVAL; - } - - disable_irq(data->irq); - - error = qt602240_load_fw(dev, QT602240_FW_NAME); - if (error) { - dev_err(dev, "The firmware update failed(%d)\n", error); - count = error; - } else { - dev_dbg(dev, "The firmware update succeeded\n"); - - /* Wait for reset */ - msleep(QT602240_FWRESET_TIME); - - kfree(data->object_table); - data->object_table = NULL; - - qt602240_initialize(data); - } - - enable_irq(data->irq); - - return count; -} - -static DEVICE_ATTR(object, 0444, qt602240_object_show, NULL); -static DEVICE_ATTR(update_fw, 0664, NULL, qt602240_update_fw_store); - -static struct attribute *qt602240_attrs[] = { - &dev_attr_object.attr, - &dev_attr_update_fw.attr, - NULL -}; - -static const struct attribute_group qt602240_attr_group = { - .attrs = qt602240_attrs, -}; - -static void qt602240_start(struct qt602240_data *data) -{ - /* Touch enable */ - qt602240_write_object(data, - QT602240_TOUCH_MULTI, QT602240_TOUCH_CTRL, 0x83); -} - -static void qt602240_stop(struct qt602240_data *data) -{ - /* Touch disable */ - qt602240_write_object(data, - QT602240_TOUCH_MULTI, QT602240_TOUCH_CTRL, 0); -} - -static int qt602240_input_open(struct input_dev *dev) -{ - struct qt602240_data *data = input_get_drvdata(dev); - - qt602240_start(data); - - return 0; -} - -static void qt602240_input_close(struct input_dev *dev) -{ - struct qt602240_data *data = input_get_drvdata(dev); - - qt602240_stop(data); -} - -static int __devinit qt602240_probe(struct i2c_client *client, - const struct i2c_device_id *id) -{ - struct qt602240_data *data; - struct input_dev *input_dev; - int error; - - if (!client->dev.platform_data) - return -EINVAL; - - data = kzalloc(sizeof(struct qt602240_data), GFP_KERNEL); - input_dev = input_allocate_device(); - if (!data || !input_dev) { - dev_err(&client->dev, "Failed to allocate memory\n"); - error = -ENOMEM; - goto err_free_mem; - } - - input_dev->name = "AT42QT602240/ATMXT224 Touchscreen"; - input_dev->id.bustype = BUS_I2C; - input_dev->dev.parent = &client->dev; - input_dev->open = qt602240_input_open; - input_dev->close = qt602240_input_close; - - __set_bit(EV_ABS, input_dev->evbit); - __set_bit(EV_KEY, input_dev->evbit); - __set_bit(BTN_TOUCH, input_dev->keybit); - - /* For single touch */ - input_set_abs_params(input_dev, ABS_X, - 0, QT602240_MAX_XC, 0, 0); - input_set_abs_params(input_dev, ABS_Y, - 0, QT602240_MAX_YC, 0, 0); - - /* For multi touch */ - input_set_abs_params(input_dev, ABS_MT_TOUCH_MAJOR, - 0, QT602240_MAX_AREA, 0, 0); - input_set_abs_params(input_dev, ABS_MT_POSITION_X, - 0, QT602240_MAX_XC, 0, 0); - input_set_abs_params(input_dev, ABS_MT_POSITION_Y, - 0, QT602240_MAX_YC, 0, 0); - - input_set_drvdata(input_dev, data); - - data->client = client; - data->input_dev = input_dev; - data->pdata = client->dev.platform_data; - data->irq = client->irq; - - i2c_set_clientdata(client, data); - - error = qt602240_initialize(data); - if (error) - goto err_free_object; - - error = request_threaded_irq(client->irq, NULL, qt602240_interrupt, - IRQF_TRIGGER_FALLING, client->dev.driver->name, data); - if (error) { - dev_err(&client->dev, "Failed to register interrupt\n"); - goto err_free_object; - } - - error = input_register_device(input_dev); - if (error) - goto err_free_irq; - - error = sysfs_create_group(&client->dev.kobj, &qt602240_attr_group); - if (error) - goto err_unregister_device; - - return 0; - -err_unregister_device: - input_unregister_device(input_dev); - input_dev = NULL; -err_free_irq: - free_irq(client->irq, data); -err_free_object: - kfree(data->object_table); -err_free_mem: - input_free_device(input_dev); - kfree(data); - return error; -} - -static int __devexit qt602240_remove(struct i2c_client *client) -{ - struct qt602240_data *data = i2c_get_clientdata(client); - - sysfs_remove_group(&client->dev.kobj, &qt602240_attr_group); - free_irq(data->irq, data); - input_unregister_device(data->input_dev); - kfree(data->object_table); - kfree(data); - - return 0; -} - -#ifdef CONFIG_PM -static int qt602240_suspend(struct device *dev) -{ - struct i2c_client *client = to_i2c_client(dev); - struct qt602240_data *data = i2c_get_clientdata(client); - struct input_dev *input_dev = data->input_dev; - - mutex_lock(&input_dev->mutex); - - if (input_dev->users) - qt602240_stop(data); - - mutex_unlock(&input_dev->mutex); - - return 0; -} - -static int qt602240_resume(struct device *dev) -{ - struct i2c_client *client = to_i2c_client(dev); - struct qt602240_data *data = i2c_get_clientdata(client); - struct input_dev *input_dev = data->input_dev; - - /* Soft reset */ - qt602240_write_object(data, QT602240_GEN_COMMAND, - QT602240_COMMAND_RESET, 1); - - msleep(QT602240_RESET_TIME); - - mutex_lock(&input_dev->mutex); - - if (input_dev->users) - qt602240_start(data); - - mutex_unlock(&input_dev->mutex); - - return 0; -} - -static const struct dev_pm_ops qt602240_pm_ops = { - .suspend = qt602240_suspend, - .resume = qt602240_resume, -}; -#endif - -static const struct i2c_device_id qt602240_id[] = { - { "qt602240_ts", 0 }, - { } -}; -MODULE_DEVICE_TABLE(i2c, qt602240_id); - -static struct i2c_driver qt602240_driver = { - .driver = { - .name = "qt602240_ts", - .owner = THIS_MODULE, -#ifdef CONFIG_PM - .pm = &qt602240_pm_ops, -#endif - }, - .probe = qt602240_probe, - .remove = __devexit_p(qt602240_remove), - .id_table = qt602240_id, -}; - -static int __init qt602240_init(void) -{ - return i2c_add_driver(&qt602240_driver); -} - -static void __exit qt602240_exit(void) -{ - i2c_del_driver(&qt602240_driver); -} - -module_init(qt602240_init); -module_exit(qt602240_exit); - -/* Module information */ -MODULE_AUTHOR("Joonyoung Shim <jy0922.shim@samsung.com>"); -MODULE_DESCRIPTION("AT42QT602240/ATMXT224 Touchscreen driver"); -MODULE_LICENSE("GPL"); diff --git a/drivers/input/touchscreen/tsc2005.c b/drivers/input/touchscreen/tsc2005.c new file mode 100644 index 000000000000..87420616efa4 --- /dev/null +++ b/drivers/input/touchscreen/tsc2005.c @@ -0,0 +1,756 @@ +/* + * TSC2005 touchscreen driver + * + * Copyright (C) 2006-2010 Nokia Corporation + * + * Author: Lauri Leukkunen <lauri.leukkunen@nokia.com> + * based on TSC2301 driver by Klaus K. Pedersen <klaus.k.pedersen@nokia.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/input.h> +#include <linux/interrupt.h> +#include <linux/delay.h> +#include <linux/pm.h> +#include <linux/spi/spi.h> +#include <linux/spi/tsc2005.h> + +/* + * The touchscreen interface operates as follows: + * + * 1) Pen is pressed against the touchscreen. + * 2) TSC2005 performs AD conversion. + * 3) After the conversion is done TSC2005 drives DAV line down. + * 4) GPIO IRQ is received and tsc2005_irq_thread() is scheduled. + * 5) tsc2005_irq_thread() queues up an spi transfer to fetch the x, y, z1, z2 + * values. + * 6) tsc2005_irq_thread() reports coordinates to input layer and sets up + * tsc2005_penup_timer() to be called after TSC2005_PENUP_TIME_MS (40ms). + * 7) When the penup timer expires, there have not been touch or DAV interrupts + * during the last 40ms which means the pen has been lifted. + * + * ESD recovery via a hardware reset is done if the TSC2005 doesn't respond + * after a configurable period (in ms) of activity. If esd_timeout is 0, the + * watchdog is disabled. + */ + +/* control byte 1 */ +#define TSC2005_CMD 0x80 +#define TSC2005_CMD_NORMAL 0x00 +#define TSC2005_CMD_STOP 0x01 +#define TSC2005_CMD_12BIT 0x04 + +/* control byte 0 */ +#define TSC2005_REG_READ 0x0001 +#define TSC2005_REG_PND0 0x0002 +#define TSC2005_REG_X 0x0000 +#define TSC2005_REG_Y 0x0008 +#define TSC2005_REG_Z1 0x0010 +#define TSC2005_REG_Z2 0x0018 +#define TSC2005_REG_TEMP_HIGH 0x0050 +#define TSC2005_REG_CFR0 0x0060 +#define TSC2005_REG_CFR1 0x0068 +#define TSC2005_REG_CFR2 0x0070 + +/* configuration register 0 */ +#define TSC2005_CFR0_PRECHARGE_276US 0x0040 +#define TSC2005_CFR0_STABTIME_1MS 0x0300 +#define TSC2005_CFR0_CLOCK_1MHZ 0x1000 +#define TSC2005_CFR0_RESOLUTION12 0x2000 +#define TSC2005_CFR0_PENMODE 0x8000 +#define TSC2005_CFR0_INITVALUE (TSC2005_CFR0_STABTIME_1MS | \ + TSC2005_CFR0_CLOCK_1MHZ | \ + TSC2005_CFR0_RESOLUTION12 | \ + TSC2005_CFR0_PRECHARGE_276US | \ + TSC2005_CFR0_PENMODE) + +/* bits common to both read and write of configuration register 0 */ +#define TSC2005_CFR0_RW_MASK 0x3fff + +/* configuration register 1 */ +#define TSC2005_CFR1_BATCHDELAY_4MS 0x0003 +#define TSC2005_CFR1_INITVALUE TSC2005_CFR1_BATCHDELAY_4MS + +/* configuration register 2 */ +#define TSC2005_CFR2_MAVE_Z 0x0004 +#define TSC2005_CFR2_MAVE_Y 0x0008 +#define TSC2005_CFR2_MAVE_X 0x0010 +#define TSC2005_CFR2_AVG_7 0x0800 +#define TSC2005_CFR2_MEDIUM_15 0x3000 +#define TSC2005_CFR2_INITVALUE (TSC2005_CFR2_MAVE_X | \ + TSC2005_CFR2_MAVE_Y | \ + TSC2005_CFR2_MAVE_Z | \ + TSC2005_CFR2_MEDIUM_15 | \ + TSC2005_CFR2_AVG_7) + +#define MAX_12BIT 0xfff +#define TSC2005_SPI_MAX_SPEED_HZ 10000000 +#define TSC2005_PENUP_TIME_MS 40 + +struct tsc2005_spi_rd { + struct spi_transfer spi_xfer; + u32 spi_tx; + u32 spi_rx; +}; + +struct tsc2005 { + struct spi_device *spi; + + struct spi_message spi_read_msg; + struct tsc2005_spi_rd spi_x; + struct tsc2005_spi_rd spi_y; + struct tsc2005_spi_rd spi_z1; + struct tsc2005_spi_rd spi_z2; + + struct input_dev *idev; + char phys[32]; + + struct mutex mutex; + + /* raw copy of previous x,y,z */ + int in_x; + int in_y; + int in_z1; + int in_z2; + + spinlock_t lock; + struct timer_list penup_timer; + + unsigned int esd_timeout; + struct delayed_work esd_work; + unsigned long last_valid_interrupt; + + unsigned int x_plate_ohm; + + bool opened; + bool suspended; + + bool pen_down; + + void (*set_reset)(bool enable); +}; + +static int tsc2005_cmd(struct tsc2005 *ts, u8 cmd) +{ + u8 tx = TSC2005_CMD | TSC2005_CMD_12BIT | cmd; + struct spi_transfer xfer = { + .tx_buf = &tx, + .len = 1, + .bits_per_word = 8, + }; + struct spi_message msg; + int error; + + spi_message_init(&msg); + spi_message_add_tail(&xfer, &msg); + + error = spi_sync(ts->spi, &msg); + if (error) { + dev_err(&ts->spi->dev, "%s: failed, command: %x, error: %d\n", + __func__, cmd, error); + return error; + } + + return 0; +} + +static int tsc2005_write(struct tsc2005 *ts, u8 reg, u16 value) +{ + u32 tx = ((reg | TSC2005_REG_PND0) << 16) | value; + struct spi_transfer xfer = { + .tx_buf = &tx, + .len = 4, + .bits_per_word = 24, + }; + struct spi_message msg; + int error; + + spi_message_init(&msg); + spi_message_add_tail(&xfer, &msg); + + error = spi_sync(ts->spi, &msg); + if (error) { + dev_err(&ts->spi->dev, + "%s: failed, register: %x, value: %x, error: %d\n", + __func__, reg, value, error); + return error; + } + + return 0; +} + +static void tsc2005_setup_read(struct tsc2005_spi_rd *rd, u8 reg, bool last) +{ + memset(rd, 0, sizeof(*rd)); + + rd->spi_tx = (reg | TSC2005_REG_READ) << 16; + rd->spi_xfer.tx_buf = &rd->spi_tx; + rd->spi_xfer.rx_buf = &rd->spi_rx; + rd->spi_xfer.len = 4; + rd->spi_xfer.bits_per_word = 24; + rd->spi_xfer.cs_change = !last; +} + +static int tsc2005_read(struct tsc2005 *ts, u8 reg, u16 *value) +{ + struct tsc2005_spi_rd spi_rd; + struct spi_message msg; + int error; + + tsc2005_setup_read(&spi_rd, reg, true); + + spi_message_init(&msg); + spi_message_add_tail(&spi_rd.spi_xfer, &msg); + + error = spi_sync(ts->spi, &msg); + if (error) + return error; + + *value = spi_rd.spi_rx; + return 0; +} + +static void tsc2005_update_pen_state(struct tsc2005 *ts, + int x, int y, int pressure) +{ + if (pressure) { + input_report_abs(ts->idev, ABS_X, x); + input_report_abs(ts->idev, ABS_Y, y); + input_report_abs(ts->idev, ABS_PRESSURE, pressure); + if (!ts->pen_down) { + input_report_key(ts->idev, BTN_TOUCH, !!pressure); + ts->pen_down = true; + } + } else { + input_report_abs(ts->idev, ABS_PRESSURE, 0); + if (ts->pen_down) { + input_report_key(ts->idev, BTN_TOUCH, 0); + ts->pen_down = false; + } + } + input_sync(ts->idev); + dev_dbg(&ts->spi->dev, "point(%4d,%4d), pressure (%4d)\n", x, y, + pressure); +} + +static irqreturn_t tsc2005_irq_thread(int irq, void *_ts) +{ + struct tsc2005 *ts = _ts; + unsigned long flags; + unsigned int pressure; + u32 x, y; + u32 z1, z2; + int error; + + /* read the coordinates */ + error = spi_sync(ts->spi, &ts->spi_read_msg); + if (unlikely(error)) + goto out; + + x = ts->spi_x.spi_rx; + y = ts->spi_y.spi_rx; + z1 = ts->spi_z1.spi_rx; + z2 = ts->spi_z2.spi_rx; + + /* validate position */ + if (unlikely(x > MAX_12BIT || y > MAX_12BIT)) + goto out; + + /* Skip reading if the pressure components are out of range */ + if (unlikely(z1 == 0 || z2 > MAX_12BIT || z1 >= z2)) + goto out; + + /* + * Skip point if this is a pen down with the exact same values as + * the value before pen-up - that implies SPI fed us stale data + */ + if (!ts->pen_down && + ts->in_x == x && ts->in_y == y && + ts->in_z1 == z1 && ts->in_z2 == z2) { + goto out; + } + + /* + * At this point we are happy we have a valid and useful reading. + * Remember it for later comparisons. We may now begin downsampling. + */ + ts->in_x = x; + ts->in_y = y; + ts->in_z1 = z1; + ts->in_z2 = z2; + + /* Compute touch pressure resistance using equation #1 */ + pressure = x * (z2 - z1) / z1; + pressure = pressure * ts->x_plate_ohm / 4096; + if (unlikely(pressure > MAX_12BIT)) + goto out; + + spin_lock_irqsave(&ts->lock, flags); + + tsc2005_update_pen_state(ts, x, y, pressure); + mod_timer(&ts->penup_timer, + jiffies + msecs_to_jiffies(TSC2005_PENUP_TIME_MS)); + + spin_unlock_irqrestore(&ts->lock, flags); + + ts->last_valid_interrupt = jiffies; +out: + return IRQ_HANDLED; +} + +static void tsc2005_penup_timer(unsigned long data) +{ + struct tsc2005 *ts = (struct tsc2005 *)data; + unsigned long flags; + + spin_lock_irqsave(&ts->lock, flags); + tsc2005_update_pen_state(ts, 0, 0, 0); + spin_unlock_irqrestore(&ts->lock, flags); +} + +static void tsc2005_start_scan(struct tsc2005 *ts) +{ + tsc2005_write(ts, TSC2005_REG_CFR0, TSC2005_CFR0_INITVALUE); + tsc2005_write(ts, TSC2005_REG_CFR1, TSC2005_CFR1_INITVALUE); + tsc2005_write(ts, TSC2005_REG_CFR2, TSC2005_CFR2_INITVALUE); + tsc2005_cmd(ts, TSC2005_CMD_NORMAL); +} + +static void tsc2005_stop_scan(struct tsc2005 *ts) +{ + tsc2005_cmd(ts, TSC2005_CMD_STOP); +} + +/* must be called with ts->mutex held */ +static void __tsc2005_disable(struct tsc2005 *ts) +{ + tsc2005_stop_scan(ts); + + disable_irq(ts->spi->irq); + del_timer_sync(&ts->penup_timer); + + cancel_delayed_work_sync(&ts->esd_work); + + enable_irq(ts->spi->irq); +} + +/* must be called with ts->mutex held */ +static void __tsc2005_enable(struct tsc2005 *ts) +{ + tsc2005_start_scan(ts); + + if (ts->esd_timeout && ts->set_reset) { + ts->last_valid_interrupt = jiffies; + schedule_delayed_work(&ts->esd_work, + round_jiffies(jiffies + + msecs_to_jiffies(ts->esd_timeout))); + } + +} + +static ssize_t tsc2005_selftest_show(struct device *dev, + struct device_attribute *attr, + char *buf) +{ + struct spi_device *spi = to_spi_device(dev); + struct tsc2005 *ts = spi_get_drvdata(spi); + u16 temp_high; + u16 temp_high_orig; + u16 temp_high_test; + bool success = true; + int error; + + mutex_lock(&ts->mutex); + + /* + * Test TSC2005 communications via temp high register. + */ + __tsc2005_disable(ts); + + error = tsc2005_read(ts, TSC2005_REG_TEMP_HIGH, &temp_high_orig); + if (error) { + dev_warn(dev, "selftest failed: read error %d\n", error); + success = false; + goto out; + } + + temp_high_test = (temp_high_orig - 1) & MAX_12BIT; + + error = tsc2005_write(ts, TSC2005_REG_TEMP_HIGH, temp_high_test); + if (error) { + dev_warn(dev, "selftest failed: write error %d\n", error); + success = false; + goto out; + } + + error = tsc2005_read(ts, TSC2005_REG_TEMP_HIGH, &temp_high); + if (error) { + dev_warn(dev, "selftest failed: read error %d after write\n", + error); + success = false; + goto out; + } + + if (temp_high != temp_high_test) { + dev_warn(dev, "selftest failed: %d != %d\n", + temp_high, temp_high_test); + success = false; + } + + /* hardware reset */ + ts->set_reset(false); + usleep_range(100, 500); /* only 10us required */ + ts->set_reset(true); + + if (!success) + goto out; + + /* test that the reset really happened */ + error = tsc2005_read(ts, TSC2005_REG_TEMP_HIGH, &temp_high); + if (error) { + dev_warn(dev, "selftest failed: read error %d after reset\n", + error); + success = false; + goto out; + } + + if (temp_high != temp_high_orig) { + dev_warn(dev, "selftest failed after reset: %d != %d\n", + temp_high, temp_high_orig); + success = false; + } + +out: + __tsc2005_enable(ts); + mutex_unlock(&ts->mutex); + + return sprintf(buf, "%d\n", success); +} + +static DEVICE_ATTR(selftest, S_IRUGO, tsc2005_selftest_show, NULL); + +static struct attribute *tsc2005_attrs[] = { + &dev_attr_selftest.attr, + NULL +}; + +static mode_t tsc2005_attr_is_visible(struct kobject *kobj, + struct attribute *attr, int n) +{ + struct device *dev = container_of(kobj, struct device, kobj); + struct spi_device *spi = to_spi_device(dev); + struct tsc2005 *ts = spi_get_drvdata(spi); + mode_t mode = attr->mode; + + if (attr == &dev_attr_selftest.attr) { + if (!ts->set_reset) + mode = 0; + } + + return mode; +} + +static const struct attribute_group tsc2005_attr_group = { + .is_visible = tsc2005_attr_is_visible, + .attrs = tsc2005_attrs, +}; + +static void tsc2005_esd_work(struct work_struct *work) +{ + struct tsc2005 *ts = container_of(work, struct tsc2005, esd_work.work); + int error; + u16 r; + + mutex_lock(&ts->mutex); + + if (time_is_after_jiffies(ts->last_valid_interrupt + + msecs_to_jiffies(ts->esd_timeout))) + goto out; + + /* We should be able to read register without disabling interrupts. */ + error = tsc2005_read(ts, TSC2005_REG_CFR0, &r); + if (!error && + !((r ^ TSC2005_CFR0_INITVALUE) & TSC2005_CFR0_RW_MASK)) { + goto out; + } + + /* + * If we could not read our known value from configuration register 0 + * then we should reset the controller as if from power-up and start + * scanning again. + */ + dev_info(&ts->spi->dev, "TSC2005 not responding - resetting\n"); + + disable_irq(ts->spi->irq); + del_timer_sync(&ts->penup_timer); + + tsc2005_update_pen_state(ts, 0, 0, 0); + + ts->set_reset(false); + usleep_range(100, 500); /* only 10us required */ + ts->set_reset(true); + + enable_irq(ts->spi->irq); + tsc2005_start_scan(ts); + +out: + /* re-arm the watchdog */ + schedule_delayed_work(&ts->esd_work, + round_jiffies(jiffies + + msecs_to_jiffies(ts->esd_timeout))); + mutex_unlock(&ts->mutex); +} + +static int tsc2005_open(struct input_dev *input) +{ + struct tsc2005 *ts = input_get_drvdata(input); + + mutex_lock(&ts->mutex); + + if (!ts->suspended) + __tsc2005_enable(ts); + + ts->opened = true; + + mutex_unlock(&ts->mutex); + + return 0; +} + +static void tsc2005_close(struct input_dev *input) +{ + struct tsc2005 *ts = input_get_drvdata(input); + + mutex_lock(&ts->mutex); + + if (!ts->suspended) + __tsc2005_disable(ts); + + ts->opened = false; + + mutex_unlock(&ts->mutex); +} + +static void __devinit tsc2005_setup_spi_xfer(struct tsc2005 *ts) +{ + tsc2005_setup_read(&ts->spi_x, TSC2005_REG_X, false); + tsc2005_setup_read(&ts->spi_y, TSC2005_REG_Y, false); + tsc2005_setup_read(&ts->spi_z1, TSC2005_REG_Z1, false); + tsc2005_setup_read(&ts->spi_z2, TSC2005_REG_Z2, true); + + spi_message_init(&ts->spi_read_msg); + spi_message_add_tail(&ts->spi_x.spi_xfer, &ts->spi_read_msg); + spi_message_add_tail(&ts->spi_y.spi_xfer, &ts->spi_read_msg); + spi_message_add_tail(&ts->spi_z1.spi_xfer, &ts->spi_read_msg); + spi_message_add_tail(&ts->spi_z2.spi_xfer, &ts->spi_read_msg); +} + +static int __devinit tsc2005_probe(struct spi_device *spi) +{ + const struct tsc2005_platform_data *pdata = spi->dev.platform_data; + struct tsc2005 *ts; + struct input_dev *input_dev; + unsigned int max_x, max_y, max_p; + unsigned int fudge_x, fudge_y, fudge_p; + int error; + + if (!pdata) { + dev_dbg(&spi->dev, "no platform data\n"); + return -ENODEV; + } + + fudge_x = pdata->ts_x_fudge ? : 4; + fudge_y = pdata->ts_y_fudge ? : 8; + fudge_p = pdata->ts_pressure_fudge ? : 2; + max_x = pdata->ts_x_max ? : MAX_12BIT; + max_y = pdata->ts_y_max ? : MAX_12BIT; + max_p = pdata->ts_pressure_max ? : MAX_12BIT; + + if (spi->irq <= 0) { + dev_dbg(&spi->dev, "no irq\n"); + return -ENODEV; + } + + spi->mode = SPI_MODE_0; + spi->bits_per_word = 8; + if (!spi->max_speed_hz) + spi->max_speed_hz = TSC2005_SPI_MAX_SPEED_HZ; + + error = spi_setup(spi); + if (error) + return error; + + ts = kzalloc(sizeof(*ts), GFP_KERNEL); + input_dev = input_allocate_device(); + if (!ts || !input_dev) { + error = -ENOMEM; + goto err_free_mem; + } + + ts->spi = spi; + ts->idev = input_dev; + + ts->x_plate_ohm = pdata->ts_x_plate_ohm ? : 280; + ts->esd_timeout = pdata->esd_timeout_ms; + ts->set_reset = pdata->set_reset; + + mutex_init(&ts->mutex); + + spin_lock_init(&ts->lock); + setup_timer(&ts->penup_timer, tsc2005_penup_timer, (unsigned long)ts); + + INIT_DELAYED_WORK(&ts->esd_work, tsc2005_esd_work); + + tsc2005_setup_spi_xfer(ts); + + snprintf(ts->phys, sizeof(ts->phys), + "%s/input-ts", dev_name(&spi->dev)); + + input_dev->name = "TSC2005 touchscreen"; + input_dev->phys = ts->phys; + input_dev->id.bustype = BUS_SPI; + input_dev->dev.parent = &spi->dev; + input_dev->evbit[0] = BIT(EV_ABS) | BIT(EV_KEY); + input_dev->keybit[BIT_WORD(BTN_TOUCH)] = BIT_MASK(BTN_TOUCH); + + input_set_abs_params(input_dev, ABS_X, 0, max_x, fudge_x, 0); + input_set_abs_params(input_dev, ABS_Y, 0, max_y, fudge_y, 0); + input_set_abs_params(input_dev, ABS_PRESSURE, 0, max_p, fudge_p, 0); + + input_dev->open = tsc2005_open; + input_dev->close = tsc2005_close; + + input_set_drvdata(input_dev, ts); + + /* Ensure the touchscreen is off */ + tsc2005_stop_scan(ts); + + error = request_threaded_irq(spi->irq, NULL, tsc2005_irq_thread, + IRQF_TRIGGER_RISING, "tsc2005", ts); + if (error) { + dev_err(&spi->dev, "Failed to request irq, err: %d\n", error); + goto err_free_mem; + } + + spi_set_drvdata(spi, ts); + error = sysfs_create_group(&spi->dev.kobj, &tsc2005_attr_group); + if (error) { + dev_err(&spi->dev, + "Failed to create sysfs attributes, err: %d\n", error); + goto err_clear_drvdata; + } + + error = input_register_device(ts->idev); + if (error) { + dev_err(&spi->dev, + "Failed to register input device, err: %d\n", error); + goto err_remove_sysfs; + } + + set_irq_wake(spi->irq, 1); + return 0; + +err_remove_sysfs: + sysfs_remove_group(&spi->dev.kobj, &tsc2005_attr_group); +err_clear_drvdata: + spi_set_drvdata(spi, NULL); + free_irq(spi->irq, ts); +err_free_mem: + input_free_device(input_dev); + kfree(ts); + return error; +} + +static int __devexit tsc2005_remove(struct spi_device *spi) +{ + struct tsc2005 *ts = spi_get_drvdata(spi); + + sysfs_remove_group(&ts->spi->dev.kobj, &tsc2005_attr_group); + + free_irq(ts->spi->irq, ts); + input_unregister_device(ts->idev); + kfree(ts); + + spi_set_drvdata(spi, NULL); + return 0; +} + +#ifdef CONFIG_PM_SLEEP +static int tsc2005_suspend(struct device *dev) +{ + struct spi_device *spi = to_spi_device(dev); + struct tsc2005 *ts = spi_get_drvdata(spi); + + mutex_lock(&ts->mutex); + + if (!ts->suspended && ts->opened) + __tsc2005_disable(ts); + + ts->suspended = true; + + mutex_unlock(&ts->mutex); + + return 0; +} + +static int tsc2005_resume(struct device *dev) +{ + struct spi_device *spi = to_spi_device(dev); + struct tsc2005 *ts = spi_get_drvdata(spi); + + mutex_lock(&ts->mutex); + + if (ts->suspended && ts->opened) + __tsc2005_enable(ts); + + ts->suspended = false; + + mutex_unlock(&ts->mutex); + + return 0; +} +#endif + +static SIMPLE_DEV_PM_OPS(tsc2005_pm_ops, tsc2005_suspend, tsc2005_resume); + +static struct spi_driver tsc2005_driver = { + .driver = { + .name = "tsc2005", + .owner = THIS_MODULE, + .pm = &tsc2005_pm_ops, + }, + .probe = tsc2005_probe, + .remove = __devexit_p(tsc2005_remove), +}; + +static int __init tsc2005_init(void) +{ + return spi_register_driver(&tsc2005_driver); +} +module_init(tsc2005_init); + +static void __exit tsc2005_exit(void) +{ + spi_unregister_driver(&tsc2005_driver); +} +module_exit(tsc2005_exit); + +MODULE_AUTHOR("Lauri Leukkunen <lauri.leukkunen@nokia.com>"); +MODULE_DESCRIPTION("TSC2005 Touchscreen Driver"); +MODULE_LICENSE("GPL"); diff --git a/drivers/input/touchscreen/wm831x-ts.c b/drivers/input/touchscreen/wm831x-ts.c new file mode 100644 index 000000000000..6ae054f8e0aa --- /dev/null +++ b/drivers/input/touchscreen/wm831x-ts.c @@ -0,0 +1,368 @@ +/* + * Touchscreen driver for WM831x PMICs + * + * Copyright 2011 Wolfson Microelectronics plc. + * Author: Mark Brown <broonie@opensource.wolfsonmicro.com> + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. + */ + +#include <linux/module.h> +#include <linux/moduleparam.h> +#include <linux/kernel.h> +#include <linux/init.h> +#include <linux/string.h> +#include <linux/pm.h> +#include <linux/input.h> +#include <linux/interrupt.h> +#include <linux/io.h> +#include <linux/mfd/wm831x/core.h> +#include <linux/mfd/wm831x/irq.h> +#include <linux/mfd/wm831x/pdata.h> +#include <linux/platform_device.h> +#include <linux/slab.h> +#include <linux/types.h> + +/* + * R16424 (0x4028) - Touch Control 1 + */ +#define WM831X_TCH_ENA 0x8000 /* TCH_ENA */ +#define WM831X_TCH_CVT_ENA 0x4000 /* TCH_CVT_ENA */ +#define WM831X_TCH_SLPENA 0x1000 /* TCH_SLPENA */ +#define WM831X_TCH_Z_ENA 0x0400 /* TCH_Z_ENA */ +#define WM831X_TCH_Y_ENA 0x0200 /* TCH_Y_ENA */ +#define WM831X_TCH_X_ENA 0x0100 /* TCH_X_ENA */ +#define WM831X_TCH_DELAY_MASK 0x00E0 /* TCH_DELAY - [7:5] */ +#define WM831X_TCH_DELAY_SHIFT 5 /* TCH_DELAY - [7:5] */ +#define WM831X_TCH_DELAY_WIDTH 3 /* TCH_DELAY - [7:5] */ +#define WM831X_TCH_RATE_MASK 0x001F /* TCH_RATE - [4:0] */ +#define WM831X_TCH_RATE_SHIFT 0 /* TCH_RATE - [4:0] */ +#define WM831X_TCH_RATE_WIDTH 5 /* TCH_RATE - [4:0] */ + +/* + * R16425 (0x4029) - Touch Control 2 + */ +#define WM831X_TCH_PD_WK 0x2000 /* TCH_PD_WK */ +#define WM831X_TCH_5WIRE 0x1000 /* TCH_5WIRE */ +#define WM831X_TCH_PDONLY 0x0800 /* TCH_PDONLY */ +#define WM831X_TCH_ISEL 0x0100 /* TCH_ISEL */ +#define WM831X_TCH_RPU_MASK 0x000F /* TCH_RPU - [3:0] */ +#define WM831X_TCH_RPU_SHIFT 0 /* TCH_RPU - [3:0] */ +#define WM831X_TCH_RPU_WIDTH 4 /* TCH_RPU - [3:0] */ + +/* + * R16426-8 (0x402A-C) - Touch Data X/Y/X + */ +#define WM831X_TCH_PD 0x8000 /* TCH_PD1 */ +#define WM831X_TCH_DATA_MASK 0x0FFF /* TCH_DATA - [11:0] */ +#define WM831X_TCH_DATA_SHIFT 0 /* TCH_DATA - [11:0] */ +#define WM831X_TCH_DATA_WIDTH 12 /* TCH_DATA - [11:0] */ + +struct wm831x_ts { + struct input_dev *input_dev; + struct wm831x *wm831x; + unsigned int data_irq; + unsigned int pd_irq; + bool pressure; + bool pen_down; +}; + +static irqreturn_t wm831x_ts_data_irq(int irq, void *irq_data) +{ + struct wm831x_ts *wm831x_ts = irq_data; + struct wm831x *wm831x = wm831x_ts->wm831x; + static int data_types[] = { ABS_X, ABS_Y, ABS_PRESSURE }; + u16 data[3]; + int count; + int i, ret; + + if (wm831x_ts->pressure) + count = 3; + else + count = 2; + + wm831x_set_bits(wm831x, WM831X_INTERRUPT_STATUS_1, + WM831X_TCHDATA_EINT, WM831X_TCHDATA_EINT); + + ret = wm831x_bulk_read(wm831x, WM831X_TOUCH_DATA_X, count, + data); + if (ret != 0) { + dev_err(wm831x->dev, "Failed to read touch data: %d\n", + ret); + return IRQ_NONE; + } + + /* + * We get a pen down reading on every reading, report pen up if any + * individual reading does so. + */ + wm831x_ts->pen_down = true; + for (i = 0; i < count; i++) { + if (!(data[i] & WM831X_TCH_PD)) { + wm831x_ts->pen_down = false; + continue; + } + input_report_abs(wm831x_ts->input_dev, data_types[i], + data[i] & WM831X_TCH_DATA_MASK); + } + + if (!wm831x_ts->pen_down) { + disable_irq_nosync(wm831x_ts->data_irq); + + /* Don't need data any more */ + wm831x_set_bits(wm831x, WM831X_TOUCH_CONTROL_1, + WM831X_TCH_X_ENA | WM831X_TCH_Y_ENA | + WM831X_TCH_Z_ENA, 0); + + /* Flush any final samples that arrived while reading */ + wm831x_set_bits(wm831x, WM831X_INTERRUPT_STATUS_1, + WM831X_TCHDATA_EINT, WM831X_TCHDATA_EINT); + + wm831x_bulk_read(wm831x, WM831X_TOUCH_DATA_X, count, data); + + if (wm831x_ts->pressure) + input_report_abs(wm831x_ts->input_dev, + ABS_PRESSURE, 0); + + input_report_key(wm831x_ts->input_dev, BTN_TOUCH, 0); + } + + input_sync(wm831x_ts->input_dev); + + return IRQ_HANDLED; +} + +static irqreturn_t wm831x_ts_pen_down_irq(int irq, void *irq_data) +{ + struct wm831x_ts *wm831x_ts = irq_data; + struct wm831x *wm831x = wm831x_ts->wm831x; + int ena = 0; + + /* Start collecting data */ + if (wm831x_ts->pressure) + ena |= WM831X_TCH_Z_ENA; + + wm831x_set_bits(wm831x, WM831X_TOUCH_CONTROL_1, + WM831X_TCH_X_ENA | WM831X_TCH_Y_ENA | WM831X_TCH_Z_ENA, + WM831X_TCH_X_ENA | WM831X_TCH_Y_ENA | ena); + + input_report_key(wm831x_ts->input_dev, BTN_TOUCH, 1); + input_sync(wm831x_ts->input_dev); + + wm831x_set_bits(wm831x, WM831X_INTERRUPT_STATUS_1, + WM831X_TCHPD_EINT, WM831X_TCHPD_EINT); + + wm831x_ts->pen_down = true; + enable_irq(wm831x_ts->data_irq); + + return IRQ_HANDLED; +} + +static int wm831x_ts_input_open(struct input_dev *idev) +{ + struct wm831x_ts *wm831x_ts = input_get_drvdata(idev); + struct wm831x *wm831x = wm831x_ts->wm831x; + + wm831x_set_bits(wm831x, WM831X_TOUCH_CONTROL_1, + WM831X_TCH_ENA | WM831X_TCH_CVT_ENA | + WM831X_TCH_X_ENA | WM831X_TCH_Y_ENA | + WM831X_TCH_Z_ENA, WM831X_TCH_ENA); + + wm831x_set_bits(wm831x, WM831X_TOUCH_CONTROL_1, + WM831X_TCH_CVT_ENA, WM831X_TCH_CVT_ENA); + + return 0; +} + +static void wm831x_ts_input_close(struct input_dev *idev) +{ + struct wm831x_ts *wm831x_ts = input_get_drvdata(idev); + struct wm831x *wm831x = wm831x_ts->wm831x; + + wm831x_set_bits(wm831x, WM831X_TOUCH_CONTROL_1, + WM831X_TCH_ENA | WM831X_TCH_CVT_ENA | + WM831X_TCH_X_ENA | WM831X_TCH_Y_ENA | + WM831X_TCH_Z_ENA, 0); + + if (wm831x_ts->pen_down) + disable_irq(wm831x_ts->data_irq); +} + +static __devinit int wm831x_ts_probe(struct platform_device *pdev) +{ + struct wm831x_ts *wm831x_ts; + struct wm831x *wm831x = dev_get_drvdata(pdev->dev.parent); + struct wm831x_pdata *core_pdata = dev_get_platdata(pdev->dev.parent); + struct wm831x_touch_pdata *pdata = NULL; + struct input_dev *input_dev; + int error; + + if (core_pdata) + pdata = core_pdata->touch; + + wm831x_ts = kzalloc(sizeof(struct wm831x_ts), GFP_KERNEL); + input_dev = input_allocate_device(); + if (!wm831x_ts || !input_dev) { + error = -ENOMEM; + goto err_alloc; + } + + wm831x_ts->wm831x = wm831x; + wm831x_ts->input_dev = input_dev; + + /* + * If we have a direct IRQ use it, otherwise use the interrupt + * from the WM831x IRQ controller. + */ + if (pdata && pdata->data_irq) + wm831x_ts->data_irq = pdata->data_irq; + else + wm831x_ts->data_irq = platform_get_irq_byname(pdev, "TCHDATA"); + + if (pdata && pdata->pd_irq) + wm831x_ts->pd_irq = pdata->pd_irq; + else + wm831x_ts->pd_irq = platform_get_irq_byname(pdev, "TCHPD"); + + if (pdata) + wm831x_ts->pressure = pdata->pressure; + else + wm831x_ts->pressure = true; + + /* Five wire touchscreens can't report pressure */ + if (pdata && pdata->fivewire) { + wm831x_set_bits(wm831x, WM831X_TOUCH_CONTROL_2, + WM831X_TCH_5WIRE, WM831X_TCH_5WIRE); + + /* Pressure measurements are not possible for five wire mode */ + WARN_ON(pdata->pressure && pdata->fivewire); + wm831x_ts->pressure = false; + } else { + wm831x_set_bits(wm831x, WM831X_TOUCH_CONTROL_2, + WM831X_TCH_5WIRE, 0); + } + + if (pdata) { + switch (pdata->isel) { + default: + dev_err(&pdev->dev, "Unsupported ISEL setting: %d\n", + pdata->isel); + /* Fall through */ + case 200: + case 0: + wm831x_set_bits(wm831x, WM831X_TOUCH_CONTROL_2, + WM831X_TCH_ISEL, 0); + break; + case 400: + wm831x_set_bits(wm831x, WM831X_TOUCH_CONTROL_2, + WM831X_TCH_ISEL, WM831X_TCH_ISEL); + break; + } + } + + wm831x_set_bits(wm831x, WM831X_TOUCH_CONTROL_2, + WM831X_TCH_PDONLY, 0); + + /* Default to 96 samples/sec */ + wm831x_set_bits(wm831x, WM831X_TOUCH_CONTROL_1, + WM831X_TCH_RATE_MASK, 6); + + error = request_threaded_irq(wm831x_ts->data_irq, + NULL, wm831x_ts_data_irq, + IRQF_ONESHOT, + "Touchscreen data", wm831x_ts); + if (error) { + dev_err(&pdev->dev, "Failed to request data IRQ %d: %d\n", + wm831x_ts->data_irq, error); + goto err_alloc; + } + disable_irq(wm831x_ts->data_irq); + + error = request_threaded_irq(wm831x_ts->pd_irq, + NULL, wm831x_ts_pen_down_irq, + IRQF_ONESHOT, + "Touchscreen pen down", wm831x_ts); + if (error) { + dev_err(&pdev->dev, "Failed to request pen down IRQ %d: %d\n", + wm831x_ts->pd_irq, error); + goto err_data_irq; + } + + /* set up touch configuration */ + input_dev->name = "WM831x touchscreen"; + input_dev->phys = "wm831x"; + input_dev->open = wm831x_ts_input_open; + input_dev->close = wm831x_ts_input_close; + + __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, 4095, 5, 0); + input_set_abs_params(input_dev, ABS_Y, 0, 4095, 5, 0); + if (wm831x_ts->pressure) + input_set_abs_params(input_dev, ABS_PRESSURE, 0, 4095, 5, 0); + + input_set_drvdata(input_dev, wm831x_ts); + input_dev->dev.parent = &pdev->dev; + + error = input_register_device(input_dev); + if (error) + goto err_pd_irq; + + platform_set_drvdata(pdev, wm831x_ts); + return 0; + +err_pd_irq: + free_irq(wm831x_ts->pd_irq, wm831x_ts); +err_data_irq: + free_irq(wm831x_ts->data_irq, wm831x_ts); +err_alloc: + input_free_device(input_dev); + kfree(wm831x_ts); + + return error; +} + +static __devexit int wm831x_ts_remove(struct platform_device *pdev) +{ + struct wm831x_ts *wm831x_ts = platform_get_drvdata(pdev); + + free_irq(wm831x_ts->pd_irq, wm831x_ts); + free_irq(wm831x_ts->data_irq, wm831x_ts); + input_unregister_device(wm831x_ts->input_dev); + kfree(wm831x_ts); + + platform_set_drvdata(pdev, NULL); + return 0; +} + +static struct platform_driver wm831x_ts_driver = { + .driver = { + .name = "wm831x-touch", + .owner = THIS_MODULE, + }, + .probe = wm831x_ts_probe, + .remove = __devexit_p(wm831x_ts_remove), +}; + +static int __init wm831x_ts_init(void) +{ + return platform_driver_register(&wm831x_ts_driver); +} +module_init(wm831x_ts_init); + +static void __exit wm831x_ts_exit(void) +{ + platform_driver_unregister(&wm831x_ts_driver); +} +module_exit(wm831x_ts_exit); + +/* Module information */ +MODULE_AUTHOR("Mark Brown <broonie@opensource.wolfsonmicro.com>"); +MODULE_DESCRIPTION("WM831x PMIC touchscreen driver"); +MODULE_LICENSE("GPL"); +MODULE_ALIAS("platform:wm831x-touch"); |