diff options
Diffstat (limited to 'drivers/input')
-rw-r--r-- | drivers/input/gameport/fm801-gp.c | 1 | ||||
-rw-r--r-- | drivers/input/gameport/gameport.c | 19 | ||||
-rw-r--r-- | drivers/input/keyboard/Kconfig | 10 | ||||
-rw-r--r-- | drivers/input/keyboard/Makefile | 1 | ||||
-rw-r--r-- | drivers/input/keyboard/ep93xx_keypad.c | 470 | ||||
-rw-r--r-- | drivers/input/keyboard/gpio_keys.c | 3 | ||||
-rw-r--r-- | drivers/input/misc/Kconfig | 19 | ||||
-rw-r--r-- | drivers/input/misc/Makefile | 2 | ||||
-rw-r--r-- | drivers/input/misc/dm355evm_keys.c | 329 | ||||
-rw-r--r-- | drivers/input/misc/rotary_encoder.c | 61 | ||||
-rw-r--r-- | drivers/input/misc/twl4030-pwrbutton.c | 145 | ||||
-rw-r--r-- | drivers/input/serio/serio.c | 21 | ||||
-rw-r--r-- | drivers/input/tablet/gtco.c | 1 | ||||
-rw-r--r-- | drivers/input/touchscreen/Kconfig | 15 | ||||
-rw-r--r-- | drivers/input/touchscreen/Makefile | 1 | ||||
-rw-r--r-- | drivers/input/touchscreen/atmel-wm97xx.c | 446 | ||||
-rw-r--r-- | drivers/input/touchscreen/wm97xx-core.c | 2 |
17 files changed, 1502 insertions, 44 deletions
diff --git a/drivers/input/gameport/fm801-gp.c b/drivers/input/gameport/fm801-gp.c index 1dec00e20db..8a1810f88b9 100644 --- a/drivers/input/gameport/fm801-gp.c +++ b/drivers/input/gameport/fm801-gp.c @@ -167,5 +167,6 @@ module_exit(fm801_gp_exit); MODULE_DEVICE_TABLE(pci, fm801_gp_id_table); +MODULE_DESCRIPTION("FM801 gameport driver"); MODULE_AUTHOR("Takashi Iwai <tiwai@suse.de>"); MODULE_LICENSE("GPL"); diff --git a/drivers/input/gameport/gameport.c b/drivers/input/gameport/gameport.c index 2d175b5928f..0279d6983cc 100644 --- a/drivers/input/gameport/gameport.c +++ b/drivers/input/gameport/gameport.c @@ -30,16 +30,6 @@ MODULE_AUTHOR("Vojtech Pavlik <vojtech@ucw.cz>"); MODULE_DESCRIPTION("Generic gameport layer"); MODULE_LICENSE("GPL"); -EXPORT_SYMBOL(__gameport_register_port); -EXPORT_SYMBOL(gameport_unregister_port); -EXPORT_SYMBOL(__gameport_register_driver); -EXPORT_SYMBOL(gameport_unregister_driver); -EXPORT_SYMBOL(gameport_open); -EXPORT_SYMBOL(gameport_close); -EXPORT_SYMBOL(gameport_set_phys); -EXPORT_SYMBOL(gameport_start_polling); -EXPORT_SYMBOL(gameport_stop_polling); - /* * gameport_mutex protects entire gameport subsystem and is taken * every time gameport port or driver registrered or unregistered. @@ -162,6 +152,7 @@ void gameport_start_polling(struct gameport *gameport) spin_unlock(&gameport->timer_lock); } +EXPORT_SYMBOL(gameport_start_polling); void gameport_stop_polling(struct gameport *gameport) { @@ -172,6 +163,7 @@ void gameport_stop_polling(struct gameport *gameport) spin_unlock(&gameport->timer_lock); } +EXPORT_SYMBOL(gameport_stop_polling); static void gameport_run_poll_handler(unsigned long d) { @@ -516,6 +508,7 @@ void gameport_set_phys(struct gameport *gameport, const char *fmt, ...) vsnprintf(gameport->phys, sizeof(gameport->phys), fmt, args); va_end(args); } +EXPORT_SYMBOL(gameport_set_phys); /* * Prepare gameport port for registration. @@ -658,6 +651,7 @@ void __gameport_register_port(struct gameport *gameport, struct module *owner) gameport_init_port(gameport); gameport_queue_event(gameport, owner, GAMEPORT_REGISTER_PORT); } +EXPORT_SYMBOL(__gameport_register_port); /* * Synchronously unregisters gameport port. @@ -669,6 +663,7 @@ void gameport_unregister_port(struct gameport *gameport) gameport_destroy_port(gameport); mutex_unlock(&gameport_mutex); } +EXPORT_SYMBOL(gameport_unregister_port); /* @@ -750,6 +745,7 @@ int __gameport_register_driver(struct gameport_driver *drv, struct module *owner return 0; } +EXPORT_SYMBOL(__gameport_register_driver); void gameport_unregister_driver(struct gameport_driver *drv) { @@ -774,6 +770,7 @@ start_over: mutex_unlock(&gameport_mutex); } +EXPORT_SYMBOL(gameport_unregister_driver); static int gameport_bus_match(struct device *dev, struct device_driver *drv) { @@ -812,6 +809,7 @@ int gameport_open(struct gameport *gameport, struct gameport_driver *drv, int mo gameport_set_drv(gameport, drv); return 0; } +EXPORT_SYMBOL(gameport_open); void gameport_close(struct gameport *gameport) { @@ -822,6 +820,7 @@ void gameport_close(struct gameport *gameport) if (gameport->close) gameport->close(gameport); } +EXPORT_SYMBOL(gameport_close); static int __init gameport_init(void) { diff --git a/drivers/input/keyboard/Kconfig b/drivers/input/keyboard/Kconfig index ea2638b4198..54775aaa7be 100644 --- a/drivers/input/keyboard/Kconfig +++ b/drivers/input/keyboard/Kconfig @@ -332,4 +332,14 @@ config KEYBOARD_SH_KEYSC To compile this driver as a module, choose M here: the module will be called sh_keysc. ++ +config KEYBOARD_EP93XX + tristate "EP93xx Matrix Keypad support" + depends on ARCH_EP93XX + help + Say Y here to enable the matrix keypad on the Cirrus EP93XX. + + To compile this driver as a module, choose M here: the + module will be called ep93xx_keypad. + endif diff --git a/drivers/input/keyboard/Makefile b/drivers/input/keyboard/Makefile index 36351e1190f..13ba9c95493 100644 --- a/drivers/input/keyboard/Makefile +++ b/drivers/input/keyboard/Makefile @@ -28,3 +28,4 @@ obj-$(CONFIG_KEYBOARD_HP7XX) += jornada720_kbd.o obj-$(CONFIG_KEYBOARD_MAPLE) += maple_keyb.o obj-$(CONFIG_KEYBOARD_BFIN) += bf54x-keys.o obj-$(CONFIG_KEYBOARD_SH_KEYSC) += sh_keysc.o +obj-$(CONFIG_KEYBOARD_EP93XX) += ep93xx_keypad.o diff --git a/drivers/input/keyboard/ep93xx_keypad.c b/drivers/input/keyboard/ep93xx_keypad.c new file mode 100644 index 00000000000..181d30e3018 --- /dev/null +++ b/drivers/input/keyboard/ep93xx_keypad.c @@ -0,0 +1,470 @@ +/* + * Driver for the Cirrus EP93xx matrix keypad controller. + * + * Copyright (c) 2008 H Hartley Sweeten <hsweeten@visionengravers.com> + * + * Based on the pxa27x matrix keypad controller by Rodolfo Giometti. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * NOTE: + * + * The 3-key reset is triggered by pressing the 3 keys in + * Row 0, Columns 2, 4, and 7 at the same time. This action can + * be disabled by setting the EP93XX_KEYPAD_DISABLE_3_KEY flag. + * + * Normal operation for the matrix does not autorepeat the key press. + * This action can be enabled by setting the EP93XX_KEYPAD_AUTOREPEAT + * flag. + */ + +#include <linux/platform_device.h> +#include <linux/interrupt.h> +#include <linux/input.h> +#include <linux/clk.h> + +#include <mach/hardware.h> +#include <mach/gpio.h> +#include <mach/ep93xx_keypad.h> + +/* + * Keypad Interface Register offsets + */ +#define KEY_INIT 0x00 /* Key Scan Initialization register */ +#define KEY_DIAG 0x04 /* Key Scan Diagnostic register */ +#define KEY_REG 0x08 /* Key Value Capture register */ + +/* Key Scan Initialization Register bit defines */ +#define KEY_INIT_DBNC_MASK (0x00ff0000) +#define KEY_INIT_DBNC_SHIFT (16) +#define KEY_INIT_DIS3KY (1<<15) +#define KEY_INIT_DIAG (1<<14) +#define KEY_INIT_BACK (1<<13) +#define KEY_INIT_T2 (1<<12) +#define KEY_INIT_PRSCL_MASK (0x000003ff) +#define KEY_INIT_PRSCL_SHIFT (0) + +/* Key Scan Diagnostic Register bit defines */ +#define KEY_DIAG_MASK (0x0000003f) +#define KEY_DIAG_SHIFT (0) + +/* Key Value Capture Register bit defines */ +#define KEY_REG_K (1<<15) +#define KEY_REG_INT (1<<14) +#define KEY_REG_2KEYS (1<<13) +#define KEY_REG_1KEY (1<<12) +#define KEY_REG_KEY2_MASK (0x00000fc0) +#define KEY_REG_KEY2_SHIFT (6) +#define KEY_REG_KEY1_MASK (0x0000003f) +#define KEY_REG_KEY1_SHIFT (0) + +#define keypad_readl(off) __raw_readl(keypad->mmio_base + (off)) +#define keypad_writel(v, off) __raw_writel((v), keypad->mmio_base + (off)) + +#define MAX_MATRIX_KEY_NUM (MAX_MATRIX_KEY_ROWS * MAX_MATRIX_KEY_COLS) + +struct ep93xx_keypad { + struct ep93xx_keypad_platform_data *pdata; + + struct clk *clk; + struct input_dev *input_dev; + void __iomem *mmio_base; + + int irq; + int enabled; + + int key1; + int key2; + + unsigned int matrix_keycodes[MAX_MATRIX_KEY_NUM]; +}; + +static void ep93xx_keypad_build_keycode(struct ep93xx_keypad *keypad) +{ + struct ep93xx_keypad_platform_data *pdata = keypad->pdata; + struct input_dev *input_dev = keypad->input_dev; + int i; + + for (i = 0; i < pdata->matrix_key_map_size; i++) { + unsigned int key = pdata->matrix_key_map[i]; + int row = (key >> 28) & 0xf; + int col = (key >> 24) & 0xf; + int code = key & 0xffffff; + + keypad->matrix_keycodes[(row << 3) + col] = code; + __set_bit(code, input_dev->keybit); + } +} + +static irqreturn_t ep93xx_keypad_irq_handler(int irq, void *dev_id) +{ + struct ep93xx_keypad *keypad = dev_id; + struct input_dev *input_dev = keypad->input_dev; + unsigned int status = keypad_readl(KEY_REG); + int keycode, key1, key2; + + keycode = (status & KEY_REG_KEY1_MASK) >> KEY_REG_KEY1_SHIFT; + key1 = keypad->matrix_keycodes[keycode]; + + keycode = (status & KEY_REG_KEY2_MASK) >> KEY_REG_KEY2_SHIFT; + key2 = keypad->matrix_keycodes[keycode]; + + if (status & KEY_REG_2KEYS) { + if (keypad->key1 && key1 != keypad->key1 && key2 != keypad->key1) + input_report_key(input_dev, keypad->key1, 0); + + if (keypad->key2 && key1 != keypad->key2 && key2 != keypad->key2) + input_report_key(input_dev, keypad->key2, 0); + + input_report_key(input_dev, key1, 1); + input_report_key(input_dev, key2, 1); + + keypad->key1 = key1; + keypad->key2 = key2; + + } else if (status & KEY_REG_1KEY) { + if (keypad->key1 && key1 != keypad->key1) + input_report_key(input_dev, keypad->key1, 0); + + if (keypad->key2 && key1 != keypad->key2) + input_report_key(input_dev, keypad->key2, 0); + + input_report_key(input_dev, key1, 1); + + keypad->key1 = key1; + keypad->key2 = 0; + + } else { + input_report_key(input_dev, keypad->key1, 0); + input_report_key(input_dev, keypad->key2, 0); + + keypad->key1 = keypad->key2 = 0; + } + input_sync(input_dev); + + return IRQ_HANDLED; +} + +static void ep93xx_keypad_config(struct ep93xx_keypad *keypad) +{ + struct ep93xx_keypad_platform_data *pdata = keypad->pdata; + unsigned int val = 0; + + clk_set_rate(keypad->clk, pdata->flags & EP93XX_KEYPAD_KDIV); + + if (pdata->flags & EP93XX_KEYPAD_DISABLE_3_KEY) + val |= KEY_INIT_DIS3KY; + if (pdata->flags & EP93XX_KEYPAD_DIAG_MODE) + val |= KEY_INIT_DIAG; + if (pdata->flags & EP93XX_KEYPAD_BACK_DRIVE) + val |= KEY_INIT_BACK; + if (pdata->flags & EP93XX_KEYPAD_TEST_MODE) + val |= KEY_INIT_T2; + + val |= ((pdata->debounce << KEY_INIT_DBNC_SHIFT) & KEY_INIT_DBNC_MASK); + + val |= ((pdata->prescale << KEY_INIT_PRSCL_SHIFT) & KEY_INIT_PRSCL_MASK); + + keypad_writel(val, KEY_INIT); +} + +static int ep93xx_keypad_open(struct input_dev *pdev) +{ + struct ep93xx_keypad *keypad = input_get_drvdata(pdev); + + if (!keypad->enabled) { + ep93xx_keypad_config(keypad); + clk_enable(keypad->clk); + keypad->enabled = 1; + } + + return 0; +} + +static void ep93xx_keypad_close(struct input_dev *pdev) +{ + struct ep93xx_keypad *keypad = input_get_drvdata(pdev); + + if (keypad->enabled) { + clk_disable(keypad->clk); + keypad->enabled = 0; + } +} + + +#ifdef CONFIG_PM +/* + * NOTE: I don't know if this is correct, or will work on the ep93xx. + * + * None of the existing ep93xx drivers have power management support. + * But, this is basically what the pxa27x_keypad driver does. + */ +static int ep93xx_keypad_suspend(struct platform_device *pdev, + pm_message_t state) +{ + struct ep93xx_keypad *keypad = platform_get_drvdata(pdev); + struct input_dev *input_dev = keypad->input_dev; + + mutex_lock(&input_dev->mutex); + + if (keypad->enabled) { + clk_disable(keypad->clk); + keypad->enabled = 0; + } + + mutex_unlock(&input_dev->mutex); + + if (device_may_wakeup(&pdev->dev)) + enable_irq_wake(keypad->irq); + + return 0; +} + +static int ep93xx_keypad_resume(struct platform_device *pdev) +{ + struct ep93xx_keypad *keypad = platform_get_drvdata(pdev); + struct input_dev *input_dev = keypad->input_dev; + + if (device_may_wakeup(&pdev->dev)) + disable_irq_wake(keypad->irq); + + mutex_lock(&input_dev->mutex); + + if (input_dev->users) { + if (!keypad->enabled) { + ep93xx_keypad_config(keypad); + clk_enable(keypad->clk); + keypad->enabled = 1; + } + } + + mutex_unlock(&input_dev->mutex); + + return 0; +} +#else /* !CONFIG_PM */ +#define ep93xx_keypad_suspend NULL +#define ep93xx_keypad_resume NULL +#endif /* !CONFIG_PM */ + +static int __devinit ep93xx_keypad_probe(struct platform_device *pdev) +{ + struct ep93xx_keypad *keypad; + struct ep93xx_keypad_platform_data *pdata = pdev->dev.platform_data; + struct input_dev *input_dev; + struct resource *res; + int irq, err, i, gpio; + + if (!pdata || + !pdata->matrix_key_rows || + pdata->matrix_key_rows > MAX_MATRIX_KEY_ROWS || + !pdata->matrix_key_cols || + pdata->matrix_key_cols > MAX_MATRIX_KEY_COLS) { + dev_err(&pdev->dev, "invalid or missing platform data\n"); + return -EINVAL; + } + + keypad = kzalloc(sizeof(struct ep93xx_keypad), GFP_KERNEL); + if (!keypad) { + dev_err(&pdev->dev, "failed to allocate driver data\n"); + return -ENOMEM; + } + + keypad->pdata = pdata; + + irq = platform_get_irq(pdev, 0); + if (irq < 0) { + dev_err(&pdev->dev, "failed to get keypad irq\n"); + err = -ENXIO; + goto failed_free; + } + + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + if (!res) { + dev_err(&pdev->dev, "failed to get I/O memory\n"); + err = -ENXIO; + goto failed_free; + } + + res = request_mem_region(res->start, resource_size(res), pdev->name); + if (!res) { + dev_err(&pdev->dev, "failed to request I/O memory\n"); + err = -EBUSY; + goto failed_free; + } + + keypad->mmio_base = ioremap(res->start, resource_size(res)); + if (keypad->mmio_base == NULL) { + dev_err(&pdev->dev, "failed to remap I/O memory\n"); + err = -ENXIO; + goto failed_free_mem; + } + + /* Request the needed GPIO's */ + gpio = EP93XX_GPIO_LINE_ROW0; + for (i = 0; i < keypad->pdata->matrix_key_rows; i++, gpio++) { + err = gpio_request(gpio, pdev->name); + if (err) { + dev_err(&pdev->dev, "failed to request gpio-%d\n", + gpio); + goto failed_free_rows; + } + } + + gpio = EP93XX_GPIO_LINE_COL0; + for (i = 0; i < keypad->pdata->matrix_key_cols; i++, gpio++) { + err = gpio_request(gpio, pdev->name); + if (err) { + dev_err(&pdev->dev, "failed to request gpio-%d\n", + gpio); + goto failed_free_cols; + } + } + + keypad->clk = clk_get(&pdev->dev, "key_clk"); + if (IS_ERR(keypad->clk)) { + dev_err(&pdev->dev, "failed to get keypad clock\n"); + err = PTR_ERR(keypad->clk); + goto failed_free_io; + } + + /* Create and register the input driver */ + input_dev = input_allocate_device(); + if (!input_dev) { + dev_err(&pdev->dev, "failed to allocate input device\n"); + err = -ENOMEM; + goto failed_put_clk; + } + + keypad->input_dev = input_dev; + + input_dev->name = pdev->name; + input_dev->id.bustype = BUS_HOST; + input_dev->open = ep93xx_keypad_open; + input_dev->close = ep93xx_keypad_close; + input_dev->dev.parent = &pdev->dev; + input_dev->keycode = keypad->matrix_keycodes; + input_dev->keycodesize = sizeof(keypad->matrix_keycodes[0]); + input_dev->keycodemax = ARRAY_SIZE(keypad->matrix_keycodes); + + input_set_drvdata(input_dev, keypad); + + input_dev->evbit[0] = BIT_MASK(EV_KEY); + if (keypad->pdata->flags & EP93XX_KEYPAD_AUTOREPEAT) + input_dev->evbit[0] |= BIT_MASK(EV_REP); + + ep93xx_keypad_build_keycode(keypad); + platform_set_drvdata(pdev, keypad); + + err = request_irq(irq, ep93xx_keypad_irq_handler, IRQF_DISABLED, + pdev->name, keypad); + if (err) { + dev_err(&pdev->dev, "failed to request IRQ\n"); + goto failed_free_dev; + } + + keypad->irq = irq; + + /* Register the input device */ + err = input_register_device(input_dev); + if (err) { + dev_err(&pdev->dev, "failed to register input device\n"); + goto failed_free_irq; + } + + device_init_wakeup(&pdev->dev, 1); + + return 0; + +failed_free_irq: + free_irq(irq, pdev); + platform_set_drvdata(pdev, NULL); +failed_free_dev: + input_free_device(input_dev); +failed_put_clk: + clk_put(keypad->clk); +failed_free_io: + i = keypad->pdata->matrix_key_cols - 1; + gpio = EP93XX_GPIO_LINE_COL0 + i; +failed_free_cols: + for ( ; i >= 0; i--, gpio--) + gpio_free(gpio); + i = keypad->pdata->matrix_key_rows - 1; + gpio = EP93XX_GPIO_LINE_ROW0 + i; +failed_free_rows: + for ( ; i >= 0; i--, gpio--) + gpio_free(gpio); + iounmap(keypad->mmio_base); +failed_free_mem: + release_mem_region(res->start, resource_size(res)); +failed_free: + kfree(keypad); + return err; +} + +static int __devexit ep93xx_keypad_remove(struct platform_device *pdev) +{ + struct ep93xx_keypad *keypad = platform_get_drvdata(pdev); + struct resource *res; + int i, gpio; + + free_irq(keypad->irq, pdev); + + platform_set_drvdata(pdev, NULL); + + if (keypad->enabled) + clk_disable(keypad->clk); + clk_put(keypad->clk); + + input_unregister_device(keypad->input_dev); + + i = keypad->pdata->matrix_key_cols - 1; + gpio = EP93XX_GPIO_LINE_COL0 + i; + for ( ; i >= 0; i--, gpio--) + gpio_free(gpio); + + i = keypad->pdata->matrix_key_rows - 1; + gpio = EP93XX_GPIO_LINE_ROW0 + i; + for ( ; i >= 0; i--, gpio--) + gpio_free(gpio); + + iounmap(keypad->mmio_base); + + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + release_mem_region(res->start, resource_size(res)); + + kfree(keypad); + + return 0; +} + +static struct platform_driver ep93xx_keypad_driver = { + .driver = { + .name = "ep93xx-keypad", + .owner = THIS_MODULE, + }, + .probe = ep93xx_keypad_probe, + .remove = __devexit_p(ep93xx_keypad_remove), + .suspend = ep93xx_keypad_suspend, + .resume = ep93xx_keypad_resume, +}; + +static int __init ep93xx_keypad_init(void) +{ + return platform_driver_register(&ep93xx_keypad_driver); +} + +static void __exit ep93xx_keypad_exit(void) +{ + platform_driver_unregister(&ep93xx_keypad_driver); +} + +module_init(ep93xx_keypad_init); +module_exit(ep93xx_keypad_exit); + +MODULE_LICENSE("GPL"); +MODULE_AUTHOR("H Hartley Sweeten <hsweeten@visionengravers.com>"); +MODULE_DESCRIPTION("EP93xx Matrix Keypad Controller"); +MODULE_ALIAS("platform:ep93xx-keypad"); diff --git a/drivers/input/keyboard/gpio_keys.c b/drivers/input/keyboard/gpio_keys.c index ad67d763fdb..9767213b6c8 100644 --- a/drivers/input/keyboard/gpio_keys.c +++ b/drivers/input/keyboard/gpio_keys.c @@ -142,8 +142,7 @@ static int __devinit gpio_keys_probe(struct platform_device *pdev) } error = request_irq(irq, gpio_keys_isr, - IRQF_SAMPLE_RANDOM | IRQF_TRIGGER_RISING | - IRQF_TRIGGER_FALLING, + IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING, button->desc ? button->desc : "gpio_keys", bdata); if (error) { diff --git a/drivers/input/misc/Kconfig b/drivers/input/misc/Kconfig index 5c0a631d145..4399f54c043 100644 --- a/drivers/input/misc/Kconfig +++ b/drivers/input/misc/Kconfig @@ -193,6 +193,16 @@ config INPUT_CM109 To compile this driver as a module, choose M here: the module will be called cm109. +config INPUT_TWL4030_PWRBUTTON + tristate "TWL4030 Power button Driver" + depends on TWL4030_CORE + help + Say Y here if you want to enable power key reporting via the + TWL4030 family of chips. + + To compile this driver as a module, choose M here. The module will + be called twl4030_pwrbutton. + config INPUT_UINPUT tristate "User level driver support" help @@ -250,4 +260,13 @@ config INPUT_RB532_BUTTON To compile this driver as a module, choose M here: the module will be called rb532_button. +config INPUT_DM355EVM + tristate "TI DaVinci DM355 EVM Keypad and IR Remote" + depends on MFD_DM355EVM_MSP + help + Supports the pushbuttons and IR remote used with + the DM355 EVM board. + + To compile this driver as a module, choose M here: the + module will be called dm355evm_keys. endif diff --git a/drivers/input/misc/Makefile b/drivers/input/misc/Makefile index eb3f407baed..0d979fd4cd5 100644 --- a/drivers/input/misc/Makefile +++ b/drivers/input/misc/Makefile @@ -10,6 +10,7 @@ obj-$(CONFIG_INPUT_ATI_REMOTE2) += ati_remote2.o obj-$(CONFIG_INPUT_ATLAS_BTNS) += atlas_btns.o obj-$(CONFIG_INPUT_CM109) += cm109.o obj-$(CONFIG_INPUT_COBALT_BTNS) += cobalt_btns.o +obj-$(CONFIG_INPUT_DM355EVM) += dm355evm_keys.o obj-$(CONFIG_HP_SDC_RTC) += hp_sdc_rtc.o obj-$(CONFIG_INPUT_IXP4XX_BEEPER) += ixp4xx-beeper.o obj-$(CONFIG_INPUT_KEYSPAN_REMOTE) += keyspan_remote.o @@ -21,6 +22,7 @@ obj-$(CONFIG_INPUT_RB532_BUTTON) += rb532_button.o obj-$(CONFIG_INPUT_GPIO_ROTARY_ENCODER) += rotary_encoder.o obj-$(CONFIG_INPUT_SGI_BTNS) += sgi_btns.o obj-$(CONFIG_INPUT_SPARCSPKR) += sparcspkr.o +obj-$(CONFIG_INPUT_TWL4030_PWRBUTTON) += twl4030-pwrbutton.o obj-$(CONFIG_INPUT_UINPUT) += uinput.o obj-$(CONFIG_INPUT_WISTRON_BTNS) += wistron_btns.o obj-$(CONFIG_INPUT_YEALINK) += yealink.o diff --git a/drivers/input/misc/dm355evm_keys.c b/drivers/input/misc/dm355evm_keys.c new file mode 100644 index 00000000000..a63315ce4a6 --- /dev/null +++ b/drivers/input/misc/dm355evm_keys.c @@ -0,0 +1,329 @@ +/* + * dm355evm_keys.c - support buttons and IR remote on DM355 EVM board + * + * Copyright (c) 2008 by David Brownell + * + * 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/kernel.h> +#include <linux/init.h> +#include <linux/input.h> +#include <linux/platform_device.h> +#include <linux/interrupt.h> + +#include <linux/i2c/dm355evm_msp.h> + + +/* + * The MSP430 firmware on the DM355 EVM monitors on-board pushbuttons + * and an IR receptor used for the remote control. When any key is + * pressed, or its autorepeat kicks in, an event is sent. This driver + * read those events from the small (32 event) queue and reports them. + * + * Because we communicate with the MSP430 using I2C, and all I2C calls + * in Linux sleep, we need to cons up a kind of threaded IRQ handler + * using a work_struct. The IRQ is active low, but we use it through + * the GPIO controller so we can trigger on falling edges. + * + * Note that physically there can only be one of these devices. + * + * This driver was tested with firmware revision A4. + */ +struct dm355evm_keys { + struct work_struct work; + struct input_dev *input; + struct device *dev; + int irq; +}; + +static irqreturn_t dm355evm_keys_irq(int irq, void *_keys) +{ + struct dm355evm_keys *keys = _keys; + + schedule_work(&keys->work); + return IRQ_HANDLED; +} + +/* These initial keycodes can be remapped by dm355evm_setkeycode(). */ +static struct { + u16 event; + u16 keycode; +} dm355evm_keys[] = { + + /* + * Pushbuttons on the EVM board ... note that the labels for these + * are SW10/SW11/etc on the PC board. The left/right orientation + * comes only from the firmware's documentation, and presumes the + * power connector is immediately in front of you and the IR sensor + * is to the right. (That is, rotate the board counter-clockwise + * by 90 degrees from the SW10/etc and "DM355 EVM" labels.) + */ + { 0x00d8, KEY_OK, }, /* SW12 */ + { 0x00b8, KEY_UP, }, /* SW13 */ + { 0x00e8, KEY_DOWN, }, /* SW11 */ + { 0x0078, KEY_LEFT, }, /* SW14 */ + { 0x00f0, KEY_RIGHT, }, /* SW10 */ + + /* + * IR buttons ... codes assigned to match the universal remote + * provided with the EVM (Philips PM4S) using DVD code 0020. + * + * These event codes match firmware documentation, but other + * remote controls could easily send more RC5-encoded events. + * The PM4S manual was used in several cases to help select + * a keycode reflecting the intended usage. + * + * RC5 codes are 14 bits, with two start bits (0x3 prefix) + * and a toggle bit (masked out below). + */ + { 0x300c, KEY_POWER, }, /* NOTE: docs omit this */ + { 0x3000, KEY_NUMERIC_0, }, + { 0x3001, KEY_NUMERIC_1, }, + { 0x3002, KEY_NUMERIC_2, }, + { 0x3003, KEY_NUMERIC_3, }, + { 0x3004, KEY_NUMERIC_4, }, + { 0x3005, KEY_NUMERIC_5, }, + { 0x3006, KEY_NUMERIC_6, }, + { 0x3007, KEY_NUMERIC_7, }, + { 0x3008, KEY_NUMERIC_8, }, + { 0x3009, KEY_NUMERIC_9, }, + { 0x3022, KEY_ENTER, }, + { 0x30ec, KEY_MODE, }, /* "tv/vcr/..." */ + { 0x300f, KEY_SELECT, }, /* "info" */ + { 0x3020, KEY_CHANNELUP, }, /* "up" */ + { 0x302e, KEY_MENU, }, /* "in/out" */ + { 0x3011, KEY_VOLUMEDOWN, }, /* "left" */ + { 0x300d, KEY_MUTE, }, /* "ok" */ + { 0x3010, KEY_VOLUMEUP, }, /* "right" */ + { 0x301e, KEY_SUBTITLE, }, /* "cc" */ + { 0x3021, KEY_CHANNELDOWN, }, /* "down" */ + { 0x3022, KEY_PREVIOUS, }, + { 0x3026, KEY_SLEEP, }, + { 0x3172, KEY_REWIND, }, /* NOTE: docs wrongly say 0x30ca */ + { 0x3175, KEY_PLAY, }, + { 0x3174, KEY_FASTFORWARD, }, + { 0x3177, KEY_RECORD, }, + { 0x3176, KEY_STOP, }, + { 0x3169, KEY_PAUSE, }, +}; + +static void dm355evm_keys_work(struct work_struct *work) +{ + struct dm355evm_keys *keys; + int status; + + keys = container_of(work, struct dm355evm_keys, work); + + /* For simplicity we ignore INPUT_COUNT and just read + * events until we get the "queue empty" indicator. + * Reading INPUT_LOW decrements the count. + */ + for (;;) { + static u16 last_event; + u16 event; + int keycode; + int i; + + status = dm355evm_msp_read(DM355EVM_MSP_INPUT_HIGH); + if (status < 0) { + dev_dbg(keys->dev, "input high err %d\n", + status); + break; + } + event = status << 8; + + status = dm355evm_msp_read(DM355EVM_MSP_INPUT_LOW); + if (status < 0) { + dev_dbg(keys->dev, "input low err %d\n", + status); + break; + } + event |= status; + if (event == 0xdead) + break; + + /* Press and release a button: two events, same code. + * Press and hold (autorepeat), then release: N events + * (N > 2), same code. For RC5 buttons the toggle bits + * distinguish (for example) "1-autorepeat" from "1 1"; + * but PCB buttons don't support that bit. + * + * So we must synthesize release events. We do that by + * mapping events to a press/release event pair; then + * to avoid adding extra events, skip the second event + * of each pair. + */ + if (event == last_event) { + last_event = 0; + continue; + } + last_event = event; + + /* ignore the RC5 toggle bit */ + event &= ~0x0800; + + /* find the key, or leave it as unknown */ + keycode = KEY_UNKNOWN; + for (i = 0; i < ARRAY_SIZE(dm355evm_keys); i++) { + if (dm355evm_keys[i].event != event) + continue; + keycode = dm355evm_keys[i].keycode; + break; + } + dev_dbg(keys->dev, + "input event 0x%04x--> keycode %d\n", + event, keycode); + + /* report press + release */ + input_report_key(keys->input, keycode, 1); + input_sync(keys->input); + input_report_key(keys->input, keycode, 0); + input_sync(keys->input); + } +} + +static int dm355evm_setkeycode(struct input_dev *dev, int index, int keycode) +{ + u16 old_keycode; + unsigned i; + + if (((unsigned)index) >= ARRAY_SIZE(dm355evm_keys)) + return -EINVAL; + + old_keycode = dm355evm_keys[index].keycode; + dm355evm_keys[index].keycode = keycode; + set_bit(keycode, dev->keybit); + + for (i = 0; i < ARRAY_SIZE(dm355evm_keys); i++) { + if (dm355evm_keys[index].keycode == old_keycode) + goto done; + } + clear_bit(old_keycode, dev->keybit); +done: + return 0; +} + +static int dm355evm_getkeycode(struct input_dev *dev, int index, int *keycode) +{ + if (((unsigned)index) >= ARRAY_SIZE(dm355evm_keys)) + return -EINVAL; + + return dm355evm_keys[index].keycode; +} + +/*----------------------------------------------------------------------*/ + +static int __devinit dm355evm_keys_probe(struct platform_device *pdev) +{ + struct dm355evm_keys *keys; + struct input_dev *input; + int status; + int i; + + /* allocate instance struct and input dev */ + keys = kzalloc(sizeof *keys, GFP_KERNEL); + input = input_allocate_device(); + if (!keys || !input) { + status = -ENOMEM; + goto fail1; + } + + keys->dev = &pdev->dev; + keys->input = input; + INIT_WORK(&keys->work, dm355evm_keys_work); + + /* set up "threaded IRQ handler" */ + status = platform_get_irq(pdev, 0); + if (status < 0) + goto fail1; + keys->irq = status; + + input_set_drvdata(input, keys); + + input->name = "DM355 EVM Controls"; + input->phys = "dm355evm/input0"; + input->dev.parent = &pdev->dev; + + input->id.bustype = BUS_I2C; + input->id.product = 0x0355; + input->id.version = dm355evm_msp_read(DM355EVM_MSP_FIRMREV); + + input->evbit[0] = BIT(EV_KEY); + for (i = 0; i < ARRAY_SIZE(dm355evm_keys); i++) + __set_bit(dm355evm_keys[i].keycode, input->keybit); + + input->setkeycode = dm355evm_setkeycode; + input->getkeycode = dm355evm_getkeycode; + + /* REVISIT: flush the event queue? */ + + status = request_irq(keys->irq, dm355evm_keys_irq, + IRQF_TRIGGER_FALLING, + dev_name(&pdev->dev), keys); + if (status < 0) + goto fail1; + + /* register */ + status = input_register_device(input); + if (status < 0) + goto fail2; + + platform_set_drvdata(pdev, keys); + + return 0; + +fail2: + free_irq(keys->irq, keys); +fail1: + input_free_device(input); + kfree(keys); + dev_err(&pdev->dev, "can't register, err %d\n", status); + + return status; +} + +static int __devexit dm355evm_keys_remove(struct platform_device *pdev) +{ + struct dm355evm_keys *keys = platform_get_drvdata(pdev); + + free_irq(keys->irq, keys); + input_unregister_device(keys->input); + kfree(keys); + + return 0; +} + +/* REVISIT: add suspend/resume when DaVinci supports it. The IRQ should + * be able to wake up the system. When device_may_wakeup(&pdev->dev), call + * enable_irq_wake() on suspend, and disable_irq_wake() on resume. + */ + +/* + * I2C is used to talk to the MSP430, but this platform device is + * exposed by an MFD driver that manages I2C communications. + */ +static struct platform_driver dm355evm_keys_driver = { + .probe = dm355evm_keys_probe, + .remove = __devexit_p(dm355evm_keys_remove), + .driver = { + .owner = THIS_MODULE, + .name = "dm355evm_keys", + }, +}; + +static int __init dm355evm_keys_init(void) +{ + return platform_driver_register(&dm355evm_keys_driver); +} +module_init(dm355evm_keys_init); + +static void __exit dm355evm_keys_exit(void) +{ + platform_driver_unregister(&dm355evm_keys_driver); +} +module_exit(dm355evm_keys_exit); + +MODULE_LICENSE("GPL"); diff --git a/drivers/input/misc/rotary_encoder.c b/drivers/input/misc/rotary_encoder.c index 5bb3ab51b8c..c806fbf1e17 100644 --- a/drivers/input/misc/rotary_encoder.c +++ b/drivers/input/misc/rotary_encoder.c @@ -26,13 +26,17 @@ #define DRV_NAME "rotary-encoder" struct rotary_encoder { - unsigned int irq_a; - unsigned int irq_b; - unsigned int pos; - unsigned int armed; - unsigned int dir; struct input_dev *input; struct rotary_encoder_platform_data *pdata; + + unsigned int axis; + unsigned int pos; + + unsigned int irq_a; + unsigned int irq_b; + + bool armed; + unsigned char dir; /* 0 - clockwise, 1 - CCW */ }; static irqreturn_t rotary_encoder_irq(int irq, void *dev_id) @@ -53,21 +57,32 @@ static irqreturn_t rotary_encoder_irq(int irq, void *dev_id) if (!encoder->armed) break; - if (encoder->dir) { - /* turning counter-clockwise */ - encoder->pos += pdata->steps; - encoder->pos--; - encoder->pos %= pdata->steps; + if (pdata->relative_axis) { + input_report_rel(encoder->input, pdata->axis, + encoder->dir ? -1 : 1); } else { - /* turning clockwise */ - encoder->pos++; - encoder->pos %= pdata->steps; + unsigned int pos = encoder->pos; + + if (encoder->dir) { + /* turning counter-clockwise */ + if (pdata->rollover) + pos += pdata->steps; + if (pos) + pos--; + } else { + /* turning clockwise */ + if (pdata->rollover || pos < pdata->steps) + pos++; + } + if (pdata->rollover) + pos %= pdata->steps; + encoder->pos = pos; + input_report_abs(encoder->input, pdata->axis, + encoder->pos); } - - input_report_abs(encoder->input, pdata->axis, encoder->pos); input_sync(encoder->input); - encoder->armed = 0; + encoder->armed = false; break; case 0x1: @@ -77,7 +92,7 @@ static irqreturn_t rotary_encoder_irq(int irq, void *dev_id) break; case 0x3: - encoder->armed = 1; + encoder->armed = true; break; } @@ -113,9 +128,15 @@ static int __devinit rotary_encoder_probe(struct platform_device *pdev) input->name = pdev->name; input->id.bustype = BUS_HOST; input->dev.parent = &pdev->dev; - input->evbit[0] = BIT_MASK(EV_ABS); - input_set_abs_params(encoder->input, - pdata->axis, 0, pdata->steps, 0, 1); + + if (pdata->relative_axis) { + input->evbit[0] = BIT_MASK(EV_REL); + input->relbit[0] = BIT_MASK(pdata->axis); + } else { + input->evbit[0] = BIT_MASK(EV_ABS); + input_set_abs_params(encoder->input, + pdata->axis, 0, pdata->steps, 0, 1); + } err = input_register_device(input); if (err) { diff --git a/drivers/input/misc/twl4030-pwrbutton.c b/drivers/input/misc/twl4030-pwrbutton.c new file mode 100644 index 00000000000..f5fc9974a11 --- /dev/null +++ b/drivers/input/misc/twl4030-pwrbutton.c @@ -0,0 +1,145 @@ +/** + * twl4030-pwrbutton.c - TWL4030 Power Button Input Driver + * + * Copyright (C) 2008-2009 Nokia Corporation + * + * Written by Peter De Schrijver <peter.de-schrijver@nokia.com> + * Several fixes by Felipe Balbi <felipe.balbi@nokia.com> + * + * This file is subject to the terms and conditions of the GNU General + * Public License. See the file "COPYING" in the main directory of this + * archive for more details. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#include <linux/module.h> +#include <linux/init.h> +#include <linux/kernel.h> +#include <linux/errno.h> +#include <linux/input.h> +#include <linux/interrupt.h> +#include <linux/platform_device.h> +#include <linux/i2c/twl4030.h> + +#define PWR_PWRON_IRQ (1 << 0) + +#define STS_HW_CONDITIONS 0xf + +static irqreturn_t powerbutton_irq(int irq, void *_pwr) +{ + struct input_dev *pwr = _pwr; + int err; + u8 value; + +#ifdef CONFIG_LOCKDEP + /* WORKAROUND for lockdep forcing IRQF_DISABLED on us, which + * we don't want and can't tolerate since this is a threaded + * IRQ and can sleep due to the i2c reads it has to issue. + * Although it might be friendlier not to borrow this thread + * context... + */ + local_irq_enable(); +#endif + + err = twl4030_i2c_read_u8(TWL4030_MODULE_PM_MASTER, &value, + STS_HW_CONDITIONS); + if (!err) { + input_report_key(pwr, KEY_POWER, value & PWR_PWRON_IRQ); + input_sync(pwr); + } else { + dev_err(pwr->dev.parent, "twl4030: i2c error %d while reading" + " TWL4030 PM_MASTER STS_HW_CONDITIONS register\n", err); + } + + return IRQ_HANDLED; +} + +static int __devinit twl4030_pwrbutton_probe(struct platform_device *pdev) +{ + struct input_dev *pwr; + int irq = platform_get_irq(pdev, 0); + int err; + + pwr = input_allocate_device(); + if (!pwr) { + dev_dbg(&pdev->dev, "Can't allocate power button\n"); + return -ENOMEM; + } + + pwr->evbit[0] = BIT_MASK(EV_KEY); + pwr->keybit[BIT_WORD(KEY_POWER)] = BIT_MASK(KEY_POWER); + pwr->name = "twl4030_pwrbutton"; + pwr->phys = "twl4030_pwrbutton/input0"; + pwr->dev.parent = &pdev->dev; + + err = request_irq(irq, powerbutton_irq, + IRQF_TRIGGER_FALLING | IRQF_TRIGGER_RISING, + "twl4030_pwrbutton", pwr); + if (err < 0) { + dev_dbg(&pdev->dev, "Can't get IRQ for pwrbutton: %d\n", err); + goto free_input_dev; + } + + err = input_register_device(pwr); + if (err) { + dev_dbg(&pdev->dev, "Can't register power button: %d\n", err); + goto free_irq; + } + + platform_set_drvdata(pdev, pwr); + + return 0; + +free_irq: + free_irq(irq, NULL); +free_input_dev: + input_free_device(pwr); + return err; +} + +static int __devexit twl4030_pwrbutton_remove(struct platform_device *pdev) +{ + struct input_dev *pwr = platform_get_drvdata(pdev); + int irq = platform_get_irq(pdev, 0); + + free_irq(irq, pwr); + input_unregister_device(pwr); + + return 0; +} + +struct platform_driver twl4030_pwrbutton_driver = { + .probe = twl4030_pwrbutton_probe, + .remove = __devexit_p(twl4030_pwrbutton_remove), + .driver = { + .name = "twl4030_pwrbutton", + .owner = THIS_MODULE, + }, +}; + +static int __init twl4030_pwrbutton_init(void) +{ + return platform_driver_register(&twl4030_pwrbutton_driver); +} +module_init(twl4030_pwrbutton_init); + +static void __exit twl4030_pwrbutton_exit(void) +{ + platform_driver_unregister(&twl4030_pwrbutton_driver); +} +module_exit(twl4030_pwrbutton_exit); + +MODULE_ALIAS("platform:twl4030_pwrbutton"); +MODULE_DESCRIPTION("Triton2 Power Button"); +MODULE_LICENSE("GPL"); +MODULE_AUTHOR("Peter De Schrijver <peter.de-schrijver@nokia.com>"); +MODULE_AUTHOR("Felipe Balbi <felipe.balbi@nokia.com>"); + diff --git a/drivers/input/serio/serio.c b/drivers/input/serio/serio.c index bc033250dfc..8d2df5d2d5a 100644 --- a/drivers/input/serio/serio.c +++ b/drivers/input/serio/serio.c @@ -41,17 +41,6 @@ MODULE_AUTHOR("Vojtech Pavlik <vojtech@ucw.cz>"); MODULE_DESCRIPTION("Serio abstraction core"); MODULE_LICENSE("GPL"); -EXPORT_SYMBOL(serio_interrupt); -EXPORT_SYMBOL(__serio_register_port); -EXPORT_SYMBOL(serio_unregister_port); -EXPORT_SYMBOL(serio_unregister_child_port); -EXPORT_SYMBOL(__serio_register_driver); -EXPORT_SYMBOL(serio_unregister_driver); -EXPORT_SYMBOL(serio_open); -EXPORT_SYMBOL(serio_close); -EXPORT_SYMBOL(serio_rescan); -EXPORT_SYMBOL(serio_reconnect); - /* * serio_mutex protects entire serio subsystem and is taken every time * serio port or driver registrered or unregistered. @@ -692,11 +681,13 @@ void serio_rescan(struct serio *serio) { serio_queue_event(serio, NULL, SERIO_RESCAN_PORT); } +EXPORT_SYMBOL(serio_rescan); void serio_reconnect(struct serio *serio) { serio_queue_event(serio, NULL, SERIO_RECONNECT_CHAIN); } +EXPORT_SYMBOL(serio_reconnect); /* * Submits register request to kseriod for subsequent execution. @@ -707,6 +698,7 @@ void __serio_register_port(struct serio *serio, struct module *owner) serio_init_port(serio); serio_queue_event(serio, owner, SERIO_REGISTER_PORT); } +EXPORT_SYMBOL(__serio_register_port); /* * Synchronously unregisters serio port. @@ -718,6 +710,7 @@ void serio_unregister_port(struct serio *serio) serio_destroy_port(serio); mutex_unlock(&serio_mutex); } +EXPORT_SYMBOL(serio_unregister_port); /* * Safely unregisters child port if one is present. @@ -731,6 +724,7 @@ void serio_unregister_child_port(struct serio *serio) } mutex_unlock(&serio_mutex); } +EXPORT_SYMBOL(serio_unregister_child_port); /* @@ -854,6 +848,7 @@ int __serio_register_driver(struct serio_driver *drv, struct module *owner, cons return 0; } +EXPORT_SYMBOL(__serio_register_driver); void serio_unregister_driver(struct serio_driver *drv) { @@ -877,6 +872,7 @@ start_over: driver_unregister(&drv->driver); mutex_unlock(&serio_mutex); } +EXPORT_SYMBOL(serio_unregister_driver); static void serio_set_drv(struct serio *serio, struct serio_driver *drv) { @@ -974,6 +970,7 @@ int serio_open(struct serio *serio, struct serio_driver *drv) } return 0; } +EXPORT_SYMBOL(serio_open); /* called from serio_driver->connect/disconnect methods under serio_mutex */ void serio_close(struct serio *serio) @@ -983,6 +980,7 @@ void serio_close(struct serio *serio) serio_set_drv(serio, NULL); } +EXPORT_SYMBOL(serio_close); irqreturn_t serio_interrupt(struct serio *serio, unsigned char data, unsigned int dfl) @@ -1003,6 +1001,7 @@ irqreturn_t serio_interrupt(struct serio *serio, return ret; } +EXPORT_SYMBOL(serio_interrupt); static struct bus_type serio_bus = { .name = "serio", diff --git a/drivers/input/tablet/gtco.c b/drivers/input/tablet/gtco.c index 2e18a1c0c35..3d32d3f4e48 100644 --- a/drivers/input/tablet/gtco.c +++ b/drivers/input/tablet/gtco.c @@ -1050,4 +1050,5 @@ static void __exit gtco_exit(void) module_init(gtco_init); module_exit(gtco_exit); +MODULE_DESCRIPTION("GTCO digitizer USB driver"); MODULE_LICENSE("GPL"); diff --git a/drivers/input/touchscreen/Kconfig b/drivers/input/touchscreen/Kconfig index b01fd61dadc..82c388e0fe3 100644 --- a/drivers/input/touchscreen/Kconfig +++ b/drivers/input/touchscreen/Kconfig @@ -341,6 +341,21 @@ config TOUCHSCREEN_WM9713 Say Y here to enable support for the Wolfson Microelectronics WM9713 touchscreen controller. +config TOUCHSCREEN_WM97XX_ATMEL + tristate "WM97xx Atmel accelerated touch" + depends on TOUCHSCREEN_WM97XX && (AVR32 || ARCH_AT91) + help + Say Y here for support for streaming mode with WM97xx touchscreens + on Atmel AT91 or AVR32 systems with an AC97C module. + + Be aware that this will use channel B in the controller for + streaming data, this must not conflict with other AC97C drivers. + + If unsure, say N. + + To compile this driver as a module, choose M here: the module will + be called atmel-wm97xx. + config TOUCHSCREEN_WM97XX_MAINSTONE tristate "WM97xx Mainstone accelerated touch" depends on TOUCHSCREEN_WM97XX && ARCH_PXA diff --git a/drivers/input/touchscreen/Makefile b/drivers/input/touchscreen/Makefile index 6700f7b9d16..bef74152295 100644 --- a/drivers/input/touchscreen/Makefile +++ b/drivers/input/touchscreen/Makefile @@ -35,5 +35,6 @@ obj-$(CONFIG_TOUCHSCREEN_DA9034) += da9034-ts.o wm97xx-ts-$(CONFIG_TOUCHSCREEN_WM9705) += wm9705.o wm97xx-ts-$(CONFIG_TOUCHSCREEN_WM9712) += wm9712.o wm97xx-ts-$(CONFIG_TOUCHSCREEN_WM9713) += wm9713.o +obj-$(CONFIG_TOUCHSCREEN_WM97XX_ATMEL) += atmel-wm97xx.o obj-$(CONFIG_TOUCHSCREEN_WM97XX_MAINSTONE) += mainstone-wm97xx.o obj-$(CONFIG_TOUCHSCREEN_WM97XX_ZYLONITE) += zylonite-wm97xx.o diff --git a/drivers/input/touchscreen/atmel-wm97xx.c b/drivers/input/touchscreen/atmel-wm97xx.c new file mode 100644 index 00000000000..35377f583e2 --- /dev/null +++ b/drivers/input/touchscreen/atmel-wm97xx.c @@ -0,0 +1,446 @@ +/* + * Atmel AT91 and AVR32 continuous touch screen driver for Wolfson WM97xx AC97 + * codecs. + * + * Copyright (C) 2008 - 2009 Atmel Corporation + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 as published by + * the Free Software Foundation. + */ +#include <linux/module.h> +#include <linux/moduleparam.h> +#include <linux/kernel.h> +#include <linux/init.h> +#include <linux/delay.h> +#include <linux/irq.h> +#include <linux/interrupt.h> +#include <linux/wm97xx.h> +#include <linux/timer.h> +#include <linux/gpio.h> +#include <linux/io.h> + +#define AC97C_ICA 0x10 +#define AC97C_CBRHR 0x30 +#define AC97C_CBSR 0x38 +#define AC97C_CBMR 0x3c +#define AC97C_IER 0x54 +#define AC97C_IDR 0x58 + +#define AC97C_RXRDY (1 << 4) +#define AC97C_OVRUN (1 << 5) + +#define AC97C_CMR_SIZE_20 (0 << 16) +#define AC97C_CMR_SIZE_18 (1 << 16) +#define AC97C_CMR_SIZE_16 (2 << 16) +#define AC97C_CMR_SIZE_10 (3 << 16) +#define AC97C_CMR_CEM_LITTLE (1 << 18) +#define AC97C_CMR_CEM_BIG (0 << 18) +#define AC97C_CMR_CENA (1 << 21) + +#define AC97C_INT_CBEVT (1 << 4) + +#define AC97C_SR_CAEVT (1 << 3) + +#define AC97C_CH_MASK(slot) \ + (0x7 << (3 * (slot - 3))) +#define AC97C_CH_ASSIGN(slot, channel) \ + (AC97C_CHANNEL_##channel << (3 * (slot - 3))) +#define AC97C_CHANNEL_NONE 0x0 +#define AC97C_CHANNEL_B 0x2 + +#define ac97c_writel(chip, reg, val) \ + __raw_writel((val), (chip)->regs + AC97C_##reg) +#define ac97c_readl(chip, reg) \ + __raw_readl((chip)->regs + AC97C_##reg) + +#ifdef CONFIG_CPU_AT32AP700X +#define ATMEL_WM97XX_AC97C_IOMEM (0xfff02800) +#define ATMEL_WM97XX_AC97C_IRQ (29) +#define ATMEL_WM97XX_GPIO_DEFAULT (32+16) /* Pin 16 on port B. */ +#else +#error Unkown CPU, this driver only supports AT32AP700X CPUs. +#endif + +struct continuous { + u16 id; /* codec id */ + u8 code; /* continuous code */ + u8 reads; /* number of coord reads per read cycle */ + u32 speed; /* number of coords per second */ +}; + +#define WM_READS(sp) ((sp / HZ) + 1) + +static const struct continuous cinfo[] = { + {WM9705_ID2, 0, WM_READS(94), 94}, + {WM9705_ID2, 1, WM_READS(188), 188}, + {WM9705_ID2, 2, WM_READS(375), 375}, + {WM9705_ID2, 3, WM_READS(750), 750}, + {WM9712_ID2, 0, WM_READS(94), 94}, + {WM9712_ID2, 1, WM_READS(188), 188}, + {WM9712_ID2, 2, WM_READS(375), 375}, + {WM9712_ID2, 3, WM_READS(750), 750}, + {WM9713_ID2, 0, WM_READS(94), 94}, + {WM9713_ID2, 1, WM_READS(120), 120}, + {WM9713_ID2, 2, WM_READS(154), 154}, + {WM9713_ID2, 3, WM_READS(188), 188}, +}; + +/* Continuous speed index. */ +static int sp_idx; + +/* + * Pen sampling frequency (Hz) in continuous mode. + */ +static int cont_rate = 188; +module_param(cont_rate, int, 0); +MODULE_PARM_DESC(cont_rate, "Sampling rate in continuous mode (Hz)"); + +/* + * Pen down detection. + * + * This driver can either poll or use an interrupt to indicate a pen down + * event. If the irq request fails then it will fall back to polling mode. + */ +static int pen_int = 1; +module_param(pen_int, int, 0); +MODULE_PARM_DESC(pen_int, "Pen down detection (1 = interrupt, 0 = polling)"); + +/* + * Pressure readback. + * + * Set to 1 to read back pen down pressure. + */ +static int pressure; +module_param(pressure, int, 0); +MODULE_PARM_DESC(pressure, "Pressure readback (1 = pressure, 0 = no pressure)"); + +/* + * AC97 touch data slot. + * + * Touch screen readback data ac97 slot. + */ +static int ac97_touch_slot = 5; +module_param(ac97_touch_slot, int, 0); +MODULE_PARM_DESC(ac97_touch_slot, "Touch screen data slot AC97 number"); + +/* + * GPIO line number. + * + * Set to GPIO number where the signal from the WM97xx device is hooked up. + */ +static int atmel_gpio_line = ATMEL_WM97XX_GPIO_DEFAULT; +module_param(atmel_gpio_line, int, 0); +MODULE_PARM_DESC(atmel_gpio_line, "GPIO line number connected to WM97xx"); + +struct atmel_wm97xx { + struct wm97xx *wm; + struct timer_list pen_timer; + void __iomem *regs; + unsigned long ac97c_irq; + unsigned long gpio_pen; + unsigned long gpio_irq; + unsigned short x; + unsigned short y; +}; + +static irqreturn_t atmel_wm97xx_channel_b_interrupt(int irq, void *dev_id) +{ + struct atmel_wm97xx *atmel_wm97xx = dev_id; + struct wm97xx *wm = atmel_wm97xx->wm; + int status = ac97c_readl(atmel_wm97xx, CBSR); + irqreturn_t retval = IRQ_NONE; + + if (status & AC97C_OVRUN) { + dev_dbg(&wm->touch_dev->dev, "AC97C overrun\n"); + ac97c_readl(atmel_wm97xx, CBRHR); + retval = IRQ_HANDLED; + } else if (status & AC97C_RXRDY) { + u16 data; + u16 value; + u16 source; + u16 pen_down; + + data = ac97c_readl(atmel_wm97xx, CBRHR); + value = data & 0x0fff; + source = data & WM97XX_ADCSRC_MASK; + pen_down = (data & WM97XX_PEN_DOWN) >> 8; + + if (source == WM97XX_ADCSEL_X) + atmel_wm97xx->x = value; + if (source == WM97XX_ADCSEL_Y) + atmel_wm97xx->y = value; + + if (!pressure && source == WM97XX_ADCSEL_Y) { + input_report_abs(wm->input_dev, ABS_X, atmel_wm97xx->x); + input_report_abs(wm->input_dev, ABS_Y, atmel_wm97xx->y); + input_report_key(wm->input_dev, BTN_TOUCH, pen_down); + input_sync(wm->input_dev); + } else if (pressure && source == WM97XX_ADCSEL_PRES) { + input_report_abs(wm->input_dev, ABS_X, atmel_wm97xx->x); + input_report_abs(wm->input_dev, ABS_Y, atmel_wm97xx->y); + input_report_abs(wm->input_dev, ABS_PRESSURE, value); + input_report_key(wm->input_dev, BTN_TOUCH, value); + input_sync(wm->input_dev); + } + + retval = IRQ_HANDLED; + } + + return retval; +} + +static void atmel_wm97xx_acc_pen_up(struct wm97xx *wm) +{ + struct atmel_wm97xx *atmel_wm97xx = platform_get_drvdata(wm->touch_dev); + struct input_dev *input_dev = wm->input_dev; + int pen_down = gpio_get_value(atmel_wm97xx->gpio_pen); + + if (pen_down != 0) { + mod_timer(&atmel_wm97xx->pen_timer, + jiffies + msecs_to_jiffies(1)); + } else { + if (pressure) + input_report_abs(input_dev, ABS_PRESSURE, 0); + input_report_key(input_dev, BTN_TOUCH, 0); + input_sync(input_dev); + } +} + +static void atmel_wm97xx_pen_timer(unsigned long data) +{ + atmel_wm97xx_acc_pen_up((struct wm97xx *)data); +} + +static int atmel_wm97xx_acc_startup(struct wm97xx *wm) +{ + struct atmel_wm97xx *atmel_wm97xx = platform_get_drvdata(wm->touch_dev); + int idx = 0; + + if (wm->ac97 == NULL) + return -ENODEV; + + for (idx = 0; idx < ARRAY_SIZE(cinfo); idx++) { + if (wm->id != cinfo[idx].id) + continue; + + sp_idx = idx; + + if (cont_rate <= cinfo[idx].speed) + break; + } + + wm->acc_rate = cinfo[sp_idx].code; + wm->acc_slot = ac97_touch_slot; + dev_info(&wm->touch_dev->dev, "atmel accelerated touchscreen driver, " + "%d samples/sec\n", cinfo[sp_idx].speed); + + if (pen_int) { + unsigned long reg; + + wm->pen_irq = atmel_wm97xx->gpio_irq; + + switch (wm->id) { + case WM9712_ID2: /* Fall through. */ + case WM9713_ID2: + /* + * Use GPIO 13 (PEN_DOWN) to assert GPIO line 3 + * (PENDOWN). + */ + wm97xx_config_gpio(wm, WM97XX_GPIO_13, WM97XX_GPIO_IN, + WM97XX_GPIO_POL_HIGH, + WM97XX_GPIO_STICKY, + WM97XX_GPIO_WAKE); + wm97xx_config_gpio(wm, WM97XX_GPIO_3, WM97XX_GPIO_OUT, + WM97XX_GPIO_POL_HIGH, + WM97XX_GPIO_NOTSTICKY, + WM97XX_GPIO_NOWAKE); + case WM9705_ID2: /* Fall through. */ + /* + * Enable touch data slot in AC97 controller channel B. + */ + reg = ac97c_readl(atmel_wm97xx, ICA); + reg &= ~AC97C_CH_MASK(wm->acc_slot); + reg |= AC97C_CH_ASSIGN(wm->acc_slot, B); + ac97c_writel(atmel_wm97xx, ICA, reg); + + /* + * Enable channel and interrupt for RXRDY and OVERRUN. + */ + ac97c_writel(atmel_wm97xx, CBMR, AC97C_CMR_CENA + | AC97C_CMR_CEM_BIG + | AC97C_CMR_SIZE_16 + | AC97C_OVRUN + | AC97C_RXRDY); + /* Dummy read to empty RXRHR. */ + ac97c_readl(atmel_wm97xx, CBRHR); + /* + * Enable interrupt for channel B in the AC97 + * controller. + */ + ac97c_writel(atmel_wm97xx, IER, AC97C_INT_CBEVT); + break; + default: + dev_err(&wm->touch_dev->dev, "pen down irq not " + "supported on this device\n"); + pen_int = 0; + break; + } + } + + return 0; +} + +static void atmel_wm97xx_acc_shutdown(struct wm97xx *wm) +{ + if (pen_int) { + struct atmel_wm97xx *atmel_wm97xx = + platform_get_drvdata(wm->touch_dev); + unsigned long ica; + + switch (wm->id & 0xffff) { + case WM9705_ID2: /* Fall through. */ + case WM9712_ID2: /* Fall through. */ + case WM9713_ID2: + /* Disable slot and turn off channel B interrupts. */ + ica = ac97c_readl(atmel_wm97xx, ICA); + ica &= ~AC97C_CH_MASK(wm->acc_slot); + ac97c_writel(atmel_wm97xx, ICA, ica); + ac97c_writel(atmel_wm97xx, IDR, AC97C_INT_CBEVT); + ac97c_writel(atmel_wm97xx, CBMR, 0); + wm->pen_irq = 0; + break; + default: + dev_err(&wm->touch_dev->dev, "unknown codec\n"); + break; + } + } +} + +static void atmel_wm97xx_irq_enable(struct wm97xx *wm, int enable) +{ + /* Intentionally left empty. */ +} + +static struct wm97xx_mach_ops atmel_mach_ops = { + .acc_enabled = 1, + .acc_pen_up = atmel_wm97xx_acc_pen_up, + .acc_startup = atmel_wm97xx_acc_startup, + .acc_shutdown = atmel_wm97xx_acc_shutdown, + .irq_enable = atmel_wm97xx_irq_enable, + .irq_gpio = WM97XX_GPIO_3, +}; + +static int __init atmel_wm97xx_probe(struct platform_device *pdev) +{ + struct wm97xx *wm = platform_get_drvdata(pdev); + struct atmel_wm97xx *atmel_wm97xx; + int ret; + + atmel_wm97xx = kzalloc(sizeof(struct atmel_wm97xx), GFP_KERNEL); + if (!atmel_wm97xx) { + dev_dbg(&pdev->dev, "out of memory\n"); + return -ENOMEM; + } + + atmel_wm97xx->wm = wm; + atmel_wm97xx->regs = (void *)ATMEL_WM97XX_AC97C_IOMEM; + atmel_wm97xx->ac97c_irq = ATMEL_WM97XX_AC97C_IRQ; + atmel_wm97xx->gpio_pen = atmel_gpio_line; + atmel_wm97xx->gpio_irq = gpio_to_irq(atmel_wm97xx->gpio_pen); + + setup_timer(&atmel_wm97xx->pen_timer, atmel_wm97xx_pen_timer, + (unsigned long)wm); + + ret = request_irq(atmel_wm97xx->ac97c_irq, + atmel_wm97xx_channel_b_interrupt, + IRQF_SHARED, "atmel-wm97xx-ch-b", atmel_wm97xx); + if (ret) { + dev_dbg(&pdev->dev, "could not request ac97c irq\n"); + goto err; + } + + platform_set_drvdata(pdev, atmel_wm97xx); + + ret = wm97xx_register_mach_ops(wm, &atmel_mach_ops); + if (ret) + goto err_irq; + + return ret; + +err_irq: + free_irq(atmel_wm97xx->ac97c_irq, atmel_wm97xx); +err: + platform_set_drvdata(pdev, NULL); + kfree(atmel_wm97xx); + return ret; +} + +static int __exit atmel_wm97xx_remove(struct platform_device *pdev) +{ + struct atmel_wm97xx *atmel_wm97xx = platform_get_drvdata(pdev); + struct wm97xx *wm = atmel_wm97xx->wm; + + ac97c_writel(atmel_wm97xx, IDR, AC97C_INT_CBEVT); + free_irq(atmel_wm97xx->ac97c_irq, atmel_wm97xx); + del_timer_sync(&atmel_wm97xx->pen_timer); + wm97xx_unregister_mach_ops(wm); + platform_set_drvdata(pdev, NULL); + kfree(atmel_wm97xx); + + return 0; +} + +#ifdef CONFIG_PM +static int atmel_wm97xx_suspend(struct platform_device *pdev, pm_message_t msg) +{ + struct atmel_wm97xx *atmel_wm97xx = platform_get_drvdata(pdev); + + ac97c_writel(atmel_wm97xx, IDR, AC97C_INT_CBEVT); + disable_irq(atmel_wm97xx->gpio_irq); + del_timer_sync(&atmel_wm97xx->pen_timer); + + return 0; +} + +static int atmel_wm97xx_resume(struct platform_device *pdev) +{ + struct atmel_wm97xx *atmel_wm97xx = platform_get_drvdata(pdev); + struct wm97xx *wm = atmel_wm97xx->wm; + + if (wm->input_dev->users) { + enable_irq(atmel_wm97xx->gpio_irq); + ac97c_writel(atmel_wm97xx, IER, AC97C_INT_CBEVT); + } + + return 0; +} +#else +#define atmel_wm97xx_suspend NULL +#define atmel_wm97xx_resume NULL +#endif + +static struct platform_driver atmel_wm97xx_driver = { + .remove = __exit_p(atmel_wm97xx_remove), + .driver = { + .name = "wm97xx-touch", + }, + .suspend = atmel_wm97xx_suspend, + .resume = atmel_wm97xx_resume, +}; + +static int __init atmel_wm97xx_init(void) +{ + return platform_driver_probe(&atmel_wm97xx_driver, atmel_wm97xx_probe); +} +module_init(atmel_wm97xx_init); + +static void __exit atmel_wm97xx_exit(void) +{ + platform_driver_unregister(&atmel_wm97xx_driver); +} +module_exit(atmel_wm97xx_exit); + +MODULE_AUTHOR("Hans-Christian Egtvedt <hans-christian.egtvedt@atmel.com>"); +MODULE_DESCRIPTION("wm97xx continuous touch driver for Atmel AT91 and AVR32"); +MODULE_LICENSE("GPL"); diff --git a/drivers/input/touchscreen/wm97xx-core.c b/drivers/input/touchscreen/wm97xx-core.c index 69af8385ab1..2957d48e004 100644 --- a/drivers/input/touchscreen/wm97xx-core.c +++ b/drivers/input/touchscreen/wm97xx-core.c @@ -569,7 +569,7 @@ static int wm97xx_probe(struct device *dev) mutex_init(&wm->codec_mutex); wm->dev = dev; - dev->driver_data = wm; + dev_set_drvdata(dev, wm); wm->ac97 = to_ac97_t(dev); /* check that we have a supported codec */ |