From db3dbd093a7cbb201f169ace35f6cdff562e5a77 Mon Sep 17 00:00:00 2001 From: Dmitry Torokhov Date: Sun, 22 Jan 2012 23:27:29 -0800 Subject: Input: nomadik-ske-keypad - do not assign driver's probe() method Because we are using platform_device_probe() to register the driver we do not need to assign driver's probe method. We also can mark ske_keypad_probe(), together with ske_keypad_chip_init(), as __init instead of __devinit. Acked-by: Linus Walleij Signed-off-by: Dmitry Torokhov --- drivers/input/keyboard/nomadik-ske-keypad.c | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/drivers/input/keyboard/nomadik-ske-keypad.c b/drivers/input/keyboard/nomadik-ske-keypad.c index 5a71e55c9c54..a804f7b815bb 100644 --- a/drivers/input/keyboard/nomadik-ske-keypad.c +++ b/drivers/input/keyboard/nomadik-ske-keypad.c @@ -88,7 +88,7 @@ static void ske_keypad_set_bits(struct ske_keypad *keypad, u16 addr, * * Enable Multi key press detection, auto scan mode */ -static int __devinit ske_keypad_chip_init(struct ske_keypad *keypad) +static int __init ske_keypad_chip_init(struct ske_keypad *keypad) { u32 value; int timeout = 50; @@ -198,7 +198,7 @@ static irqreturn_t ske_keypad_irq(int irq, void *dev_id) return IRQ_HANDLED; } -static int __devinit ske_keypad_probe(struct platform_device *pdev) +static int __init ske_keypad_probe(struct platform_device *pdev) { const struct ske_keypad_platform_data *plat = pdev->dev.platform_data; struct ske_keypad *keypad; @@ -387,7 +387,6 @@ static struct platform_driver ske_keypad_driver = { .pm = &ske_keypad_dev_pm_ops, #endif }, - .probe = ske_keypad_probe, .remove = __devexit_p(ske_keypad_remove), }; module_platform_driver(ske_keypad_driver); -- cgit v1.2.3 From 89f0f170fbec6290637c3172cb08ddf31f211ef0 Mon Sep 17 00:00:00 2001 From: Dmitry Torokhov Date: Sun, 22 Jan 2012 23:27:54 -0800 Subject: Input: nomadik-ske-keypad - convert to using SIMPLE_DEV_PM_OPS Also proper guard for system suspend/resume methods is CONFIG_PM_SLEEP, not CONFIG_PM. Acked-by: Linus Walleij Signed-off-by: Dmitry Torokhov --- drivers/input/keyboard/nomadik-ske-keypad.c | 12 ++++-------- 1 file changed, 4 insertions(+), 8 deletions(-) diff --git a/drivers/input/keyboard/nomadik-ske-keypad.c b/drivers/input/keyboard/nomadik-ske-keypad.c index a804f7b815bb..91c2fcb8ca4b 100644 --- a/drivers/input/keyboard/nomadik-ske-keypad.c +++ b/drivers/input/keyboard/nomadik-ske-keypad.c @@ -344,7 +344,7 @@ static int __devexit ske_keypad_remove(struct platform_device *pdev) return 0; } -#ifdef CONFIG_PM +#ifdef CONFIG_PM_SLEEP static int ske_keypad_suspend(struct device *dev) { struct platform_device *pdev = to_platform_device(dev); @@ -372,20 +372,16 @@ static int ske_keypad_resume(struct device *dev) return 0; } - -static const struct dev_pm_ops ske_keypad_dev_pm_ops = { - .suspend = ske_keypad_suspend, - .resume = ske_keypad_resume, -}; #endif +static SIMPLE_DEV_PM_OPS(ske_keypad_dev_pm_ops, + ske_keypad_suspend, ske_keypad_resume); + static struct platform_driver ske_keypad_driver = { .driver = { .name = "nmk-ske-keypad", .owner = THIS_MODULE, -#ifdef CONFIG_PM .pm = &ske_keypad_dev_pm_ops, -#endif }, .remove = __devexit_p(ske_keypad_remove), }; -- cgit v1.2.3 From 3e8040b0a93cadeead148938188212ac7422a6bc Mon Sep 17 00:00:00 2001 From: Dmitry Torokhov Date: Sun, 22 Jan 2012 23:27:54 -0800 Subject: Input: at32psif - convert to dev_pm_ops Convert driver to use dev_pm_ops instead of legacy PM infrastructure. Also make 'open' a bool since it is really a boolean. Signed-off-by: Dmitry Torokhov --- drivers/input/serio/at32psif.c | 22 +++++++++++----------- 1 file changed, 11 insertions(+), 11 deletions(-) diff --git a/drivers/input/serio/at32psif.c b/drivers/input/serio/at32psif.c index 421a7442e464..d0d861fb5b8b 100644 --- a/drivers/input/serio/at32psif.c +++ b/drivers/input/serio/at32psif.c @@ -98,9 +98,9 @@ struct psif { struct serio *io; void __iomem *regs; unsigned int irq; - unsigned int open; /* Prevent concurrent writes to PSIF THR. */ spinlock_t lock; + bool open; }; static irqreturn_t psif_interrupt(int irq, void *_ptr) @@ -164,7 +164,7 @@ static int psif_open(struct serio *io) psif_writel(psif, CR, PSIF_BIT(CR_TXEN) | PSIF_BIT(CR_RXEN)); psif_writel(psif, IER, PSIF_BIT(RXRDY)); - psif->open = 1; + psif->open = true; out: return retval; } @@ -173,7 +173,7 @@ static void psif_close(struct serio *io) { struct psif *psif = io->port_data; - psif->open = 0; + psif->open = false; psif_writel(psif, IDR, ~0UL); psif_writel(psif, CR, PSIF_BIT(CR_TXDIS) | PSIF_BIT(CR_RXDIS)); @@ -319,9 +319,10 @@ static int __exit psif_remove(struct platform_device *pdev) return 0; } -#ifdef CONFIG_PM -static int psif_suspend(struct platform_device *pdev, pm_message_t state) +#ifdef CONFIG_PM_SLEEP +static int psif_suspend(struct device *dev) { + struct platform_device *pdev = to_platform_device(dev); struct psif *psif = platform_get_drvdata(pdev); if (psif->open) { @@ -332,8 +333,9 @@ static int psif_suspend(struct platform_device *pdev, pm_message_t state) return 0; } -static int psif_resume(struct platform_device *pdev) +static int psif_resume(struct device *dev) { + struct platform_device *pdev = to_platform_device(dev); struct psif *psif = platform_get_drvdata(pdev); if (psif->open) { @@ -344,19 +346,17 @@ static int psif_resume(struct platform_device *pdev) return 0; } -#else -#define psif_suspend NULL -#define psif_resume NULL #endif +static SIMPLE_DEV_PM_OPS(psif_pm_ops, psif_suspend, psif_resume); + static struct platform_driver psif_driver = { .remove = __exit_p(psif_remove), .driver = { .name = "atmel_psif", .owner = THIS_MODULE, + .pm = &psif_pm_ops, }, - .suspend = psif_suspend, - .resume = psif_resume, }; module_platform_driver(psif_driver); -- cgit v1.2.3 From 409e15442fc7f7ae9d025f3ea3fdf3c60070314f Mon Sep 17 00:00:00 2001 From: Dmitry Torokhov Date: Sun, 22 Jan 2012 23:27:54 -0800 Subject: Input: q40kbd - convert driver to the split model Convert the driver to standard spilt model arch-specific code registers platform device to which driver code can bind later. Also request IRQ immediately upon binding to the device instead of doing this when serio port is being opened. Acked-by: Geert Uytterhoeven Signed-off-by: Dmitry Torokhov --- arch/m68k/q40/config.c | 13 ++++ drivers/input/serio/q40kbd.c | 139 ++++++++++++++++++++++++------------------- 2 files changed, 92 insertions(+), 60 deletions(-) diff --git a/arch/m68k/q40/config.c b/arch/m68k/q40/config.c index ad10fecec2fe..be936480b964 100644 --- a/arch/m68k/q40/config.c +++ b/arch/m68k/q40/config.c @@ -24,6 +24,7 @@ #include #include #include +#include #include #include @@ -329,3 +330,15 @@ static int q40_set_rtc_pll(struct rtc_pll_info *pll) } else return -EINVAL; } + +static __init int q40_add_kbd_device(void) +{ + struct platform_device *pdev; + + pdev = platform_device_register_simple("q40kbd", -1, NULL, 0); + if (IS_ERR(pdev)) + return PTR_ERR(pdev); + + return 0; +} +arch_initcall(q40_add_kbd_device); diff --git a/drivers/input/serio/q40kbd.c b/drivers/input/serio/q40kbd.c index 5eb84b3b67fb..0c0df7f73802 100644 --- a/drivers/input/serio/q40kbd.c +++ b/drivers/input/serio/q40kbd.c @@ -44,26 +44,31 @@ #include #include +#define DRV_NAME "q40kbd" + MODULE_AUTHOR("Vojtech Pavlik "); MODULE_DESCRIPTION("Q40 PS/2 keyboard controller driver"); MODULE_LICENSE("GPL"); +MODULE_ALIAS("platform:" DRV_NAME); -static DEFINE_SPINLOCK(q40kbd_lock); -static struct serio *q40kbd_port; -static struct platform_device *q40kbd_device; +struct q40kbd { + struct serio *port; + spinlock_t lock; +}; static irqreturn_t q40kbd_interrupt(int irq, void *dev_id) { + struct q40kbd *q40kbd = dev_id; unsigned long flags; - spin_lock_irqsave(&q40kbd_lock, flags); + spin_lock_irqsave(&q40kbd->lock, flags); if (Q40_IRQ_KEYB_MASK & master_inb(INTERRUPT_REG)) - serio_interrupt(q40kbd_port, master_inb(KEYCODE_REG), 0); + serio_interrupt(q40kbd->port, master_inb(KEYCODE_REG), 0); master_outb(-1, KEYBOARD_UNLOCK_REG); - spin_unlock_irqrestore(&q40kbd_lock, flags); + spin_unlock_irqrestore(&q40kbd->lock, flags); return IRQ_HANDLED; } @@ -72,17 +77,23 @@ static irqreturn_t q40kbd_interrupt(int irq, void *dev_id) * q40kbd_flush() flushes all data that may be in the keyboard buffers */ -static void q40kbd_flush(void) +static void q40kbd_flush(struct q40kbd *q40kbd) { int maxread = 100; unsigned long flags; - spin_lock_irqsave(&q40kbd_lock, flags); + spin_lock_irqsave(&q40kbd->lock, flags); while (maxread-- && (Q40_IRQ_KEYB_MASK & master_inb(INTERRUPT_REG))) master_inb(KEYCODE_REG); - spin_unlock_irqrestore(&q40kbd_lock, flags); + spin_unlock_irqrestore(&q40kbd->lock, flags); +} + +static void q40kbd_stop(void) +{ + master_outb(0, KEY_IRQ_ENABLE_REG); + master_outb(-1, KEYBOARD_UNLOCK_REG); } /* @@ -92,12 +103,9 @@ static void q40kbd_flush(void) static int q40kbd_open(struct serio *port) { - q40kbd_flush(); + struct q40kbd *q40kbd = port->port_data; - if (request_irq(Q40_IRQ_KEYBOARD, q40kbd_interrupt, 0, "q40kbd", NULL)) { - printk(KERN_ERR "q40kbd.c: Can't get irq %d.\n", Q40_IRQ_KEYBOARD); - return -EBUSY; - } + q40kbd_flush(q40kbd); /* off we go */ master_outb(-1, KEYBOARD_UNLOCK_REG); @@ -108,36 +116,72 @@ static int q40kbd_open(struct serio *port) static void q40kbd_close(struct serio *port) { - master_outb(0, KEY_IRQ_ENABLE_REG); - master_outb(-1, KEYBOARD_UNLOCK_REG); - free_irq(Q40_IRQ_KEYBOARD, NULL); + struct q40kbd *q40kbd = port->port_data; - q40kbd_flush(); + q40kbd_stop(); + q40kbd_flush(q40kbd); } -static int __devinit q40kbd_probe(struct platform_device *dev) +static int __devinit q40kbd_probe(struct platform_device *pdev) { - q40kbd_port = kzalloc(sizeof(struct serio), GFP_KERNEL); - if (!q40kbd_port) - return -ENOMEM; - - q40kbd_port->id.type = SERIO_8042; - q40kbd_port->open = q40kbd_open; - q40kbd_port->close = q40kbd_close; - q40kbd_port->dev.parent = &dev->dev; - strlcpy(q40kbd_port->name, "Q40 Kbd Port", sizeof(q40kbd_port->name)); - strlcpy(q40kbd_port->phys, "Q40", sizeof(q40kbd_port->phys)); - - serio_register_port(q40kbd_port); + struct q40kbd *q40kbd; + struct serio *port; + int error; + + q40kbd = kzalloc(sizeof(struct q40kbd), GFP_KERNEL); + port = kzalloc(sizeof(struct serio), GFP_KERNEL); + if (!q40kbd || !port) { + error = -ENOMEM; + goto err_free_mem; + } + + q40kbd->port = port; + spin_lock_init(&q40kbd->lock); + + port->id.type = SERIO_8042; + port->open = q40kbd_open; + port->close = q40kbd_close; + port->port_data = q40kbd; + port->dev.parent = &pdev->dev; + strlcpy(port->name, "Q40 Kbd Port", sizeof(port->name)); + strlcpy(port->phys, "Q40", sizeof(port->phys)); + + q40kbd_stop(); + + error = request_irq(Q40_IRQ_KEYBOARD, q40kbd_interrupt, 0, + DRV_NAME, q40kbd); + if (error) { + dev_err(&pdev->dev, "Can't get irq %d.\n", Q40_IRQ_KEYBOARD); + goto err_free_mem; + } + + serio_register_port(q40kbd->port); + + platform_set_drvdata(pdev, q40kbd); printk(KERN_INFO "serio: Q40 kbd registered\n"); return 0; + +err_free_mem: + kfree(port); + kfree(q40kbd); + return error; } -static int __devexit q40kbd_remove(struct platform_device *dev) +static int __devexit q40kbd_remove(struct platform_device *pdev) { - serio_unregister_port(q40kbd_port); - + struct q40kbd *q40kbd = platform_get_drvdata(pdev); + + /* + * q40kbd_close() will be called as part of unregistering + * and will ensure that IRQ is turned off, so it is safe + * to unregister port first and free IRQ later. + */ + serio_unregister_port(q40kbd->port); + free_irq(Q40_IRQ_KEYBOARD, q40kbd); + kfree(q40kbd); + + platform_set_drvdata(pdev, NULL); return 0; } @@ -146,41 +190,16 @@ static struct platform_driver q40kbd_driver = { .name = "q40kbd", .owner = THIS_MODULE, }, - .probe = q40kbd_probe, .remove = __devexit_p(q40kbd_remove), }; static int __init q40kbd_init(void) { - int error; - - if (!MACH_IS_Q40) - return -ENODEV; - - error = platform_driver_register(&q40kbd_driver); - if (error) - return error; - - q40kbd_device = platform_device_alloc("q40kbd", -1); - if (!q40kbd_device) - goto err_unregister_driver; - - error = platform_device_add(q40kbd_device); - if (error) - goto err_free_device; - - return 0; - - err_free_device: - platform_device_put(q40kbd_device); - err_unregister_driver: - platform_driver_unregister(&q40kbd_driver); - return error; + return platform_driver_probe(&q40kbd_driver, q40kbd_probe); } static void __exit q40kbd_exit(void) { - platform_device_unregister(q40kbd_device); platform_driver_unregister(&q40kbd_driver); } -- cgit v1.2.3 From 69898e512a4c27017aec43796bef8fe1dd1ec661 Mon Sep 17 00:00:00 2001 From: Mark Brown Date: Sun, 22 Jan 2012 23:27:54 -0800 Subject: Input: samsung-keypad - don't synchronise with runtime PM put We don't actually care if the device has been runtime suspended immediately so we can just drop the reference without waiting for any state change to be implemented. This may allow us to avoid some suspend/resume cycles and is a bit more friendly to the rest of the system. Signed-off-by: Mark Brown Signed-off-by: Dmitry Torokhov --- drivers/input/keyboard/samsung-keypad.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/drivers/input/keyboard/samsung-keypad.c b/drivers/input/keyboard/samsung-keypad.c index b746fce2d120..395b3af9f73e 100644 --- a/drivers/input/keyboard/samsung-keypad.c +++ b/drivers/input/keyboard/samsung-keypad.c @@ -178,7 +178,7 @@ static irqreturn_t samsung_keypad_irq(int irq, void *dev_id) } while (key_down && !keypad->stopped); - pm_runtime_put_sync(&keypad->pdev->dev); + pm_runtime_put(&keypad->pdev->dev); return IRQ_HANDLED; } @@ -202,7 +202,7 @@ static void samsung_keypad_start(struct samsung_keypad *keypad) /* KEYIFCOL reg clear. */ writel(0, keypad->base + SAMSUNG_KEYIFCOL); - pm_runtime_put_sync(&keypad->pdev->dev); + pm_runtime_put(&keypad->pdev->dev); } static void samsung_keypad_stop(struct samsung_keypad *keypad) @@ -232,7 +232,7 @@ static void samsung_keypad_stop(struct samsung_keypad *keypad) */ enable_irq(keypad->irq); - pm_runtime_put_sync(&keypad->pdev->dev); + pm_runtime_put(&keypad->pdev->dev); } static int samsung_keypad_open(struct input_dev *input_dev) -- cgit v1.2.3 From 82f6aba8eacf9dec23459832c1a0c789db28faa3 Mon Sep 17 00:00:00 2001 From: Rakesh Iyer Date: Sun, 22 Jan 2012 23:27:54 -0800 Subject: Input: tegra-kbc - remove pre-Tegra20 definitions Add support for Tegra30 by correcting definitions. This is necessary to make driver useful in Tegra30. Signed-off-by: Rakesh Iyer Acked-by: Stephen Warren: Signed-off-by: Dmitry Torokhov --- arch/arm/mach-tegra/include/mach/kbc.h | 5 ----- 1 file changed, 5 deletions(-) diff --git a/arch/arm/mach-tegra/include/mach/kbc.h b/arch/arm/mach-tegra/include/mach/kbc.h index 20bb0545f992..d34ecd1bea63 100644 --- a/arch/arm/mach-tegra/include/mach/kbc.h +++ b/arch/arm/mach-tegra/include/mach/kbc.h @@ -24,13 +24,8 @@ #include #include -#ifdef CONFIG_ARCH_TEGRA_2x_SOC #define KBC_MAX_GPIO 24 #define KBC_MAX_KPENT 8 -#else -#define KBC_MAX_GPIO 20 -#define KBC_MAX_KPENT 7 -#endif #define KBC_MAX_ROW 16 #define KBC_MAX_COL 8 -- cgit v1.2.3 From b6834b02e476ff0e99b6814665839e37affa31f0 Mon Sep 17 00:00:00 2001 From: Rakesh Iyer Date: Sun, 22 Jan 2012 23:27:54 -0800 Subject: Input: tegra-kbc - enable key interrupt for wakeup Enable keypress interrupt to support wakeup from low power state. Signed-off-by: Rakesh Iyer Signed-off-by: Dmitry Torokhov --- drivers/input/keyboard/tegra-kbc.c | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) diff --git a/drivers/input/keyboard/tegra-kbc.c b/drivers/input/keyboard/tegra-kbc.c index a136e2e832be..b307a46ecef1 100644 --- a/drivers/input/keyboard/tegra-kbc.c +++ b/drivers/input/keyboard/tegra-kbc.c @@ -48,6 +48,7 @@ #define KBC_FIFO_TH_CNT_SHIFT(cnt) (cnt << 14) #define KBC_DEBOUNCE_CNT_SHIFT(cnt) (cnt << 4) #define KBC_CONTROL_FIFO_CNT_INT_EN (1 << 3) +#define KBC_CONTROL_KEYPRESS_INT_EN (1 << 1) #define KBC_CONTROL_KBC_EN (1 << 0) /* KBC Interrupt Register */ @@ -356,6 +357,18 @@ static void tegra_kbc_set_fifo_interrupt(struct tegra_kbc *kbc, bool enable) writel(val, kbc->mmio + KBC_CONTROL_0); } +static void tegra_kbc_set_keypress_interrupt(struct tegra_kbc *kbc, bool enable) +{ + u32 val; + + val = readl(kbc->mmio + KBC_CONTROL_0); + if (enable) + val |= KBC_CONTROL_KEYPRESS_INT_EN; + else + val &= ~KBC_CONTROL_KEYPRESS_INT_EN; + writel(val, kbc->mmio + KBC_CONTROL_0); +} + static void tegra_kbc_keypress_timer(unsigned long data) { struct tegra_kbc *kbc = (struct tegra_kbc *)data; @@ -831,6 +844,8 @@ static int tegra_kbc_suspend(struct device *dev) msleep(30); kbc->keypress_caused_wake = false; + /* Enable keypress interrupt before going into suspend. */ + tegra_kbc_set_keypress_interrupt(kbc, true); enable_irq(kbc->irq); enable_irq_wake(kbc->irq); } else { @@ -852,6 +867,8 @@ static int tegra_kbc_resume(struct device *dev) if (device_may_wakeup(&pdev->dev)) { disable_irq_wake(kbc->irq); tegra_kbc_setup_wakekeys(kbc, false); + /* We will use fifo interrupts for key detection. */ + tegra_kbc_set_keypress_interrupt(kbc, false); /* Restore the resident time of continuous polling mode. */ writel(kbc->cp_to_wkup_dly, kbc->mmio + KBC_TO_CNT_0); -- cgit v1.2.3 From 393f9ffb7956c0ab8edb971d2c98d94aad9eeef8 Mon Sep 17 00:00:00 2001 From: Masanari Iida Date: Tue, 31 Jan 2012 00:07:33 -0800 Subject: Input: pcf8574_keypad - fix typo in Kconfig Correct spelling "connetced" to "connected" in pcf8574_keypad description in drivers/input/misc/Kconfig. Signed-off-by: Masanari Iida Signed-off-by: Dmitry Torokhov --- drivers/input/misc/Kconfig | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/input/misc/Kconfig b/drivers/input/misc/Kconfig index 7b46781c30c9..c528deadd63c 100644 --- a/drivers/input/misc/Kconfig +++ b/drivers/input/misc/Kconfig @@ -415,7 +415,7 @@ config INPUT_PCF8574 tristate "PCF8574 Keypad input device" depends on I2C && EXPERIMENTAL help - Say Y here if you want to support a keypad connetced via I2C + Say Y here if you want to support a keypad connected via I2C with a PCF8574. To compile this driver as a module, choose M here: the -- cgit v1.2.3 From 8b4a0c1fe3b03c0cfe829413481d69c2e6fd844c Mon Sep 17 00:00:00 2001 From: Ping Cheng Date: Tue, 31 Jan 2012 00:07:33 -0800 Subject: Input: wacom - use BTN_TOOL_FINGER to indicate touch device type Tested-by: Chris Bagwell Reviewed-by: Chris Bagwell Signed-off-by: Ping Cheng Signed-off-by: Dmitry Torokhov --- drivers/input/tablet/wacom_sys.c | 6 +----- drivers/input/tablet/wacom_wac.c | 4 ++-- 2 files changed, 3 insertions(+), 7 deletions(-) diff --git a/drivers/input/tablet/wacom_sys.c b/drivers/input/tablet/wacom_sys.c index 7e63183a6c68..c9588eececfb 100644 --- a/drivers/input/tablet/wacom_sys.c +++ b/drivers/input/tablet/wacom_sys.c @@ -176,7 +176,7 @@ static int wacom_parse_logical_collection(unsigned char *report, /* Logical collection is only used by 3rd gen Bamboo Touch */ features->pktlen = WACOM_PKGLEN_BBTOUCH3; - features->device_type = BTN_TOOL_DOUBLETAP; + features->device_type = BTN_TOOL_FINGER; /* * Stylus and Touch have same active area @@ -286,12 +286,10 @@ static int wacom_parse_hid(struct usb_interface *intf, if (features->type == TABLETPC2FG) { /* need to reset back */ features->pktlen = WACOM_PKGLEN_TPC2FG; - features->device_type = BTN_TOOL_DOUBLETAP; } if (features->type == BAMBOO_PT) { /* need to reset back */ features->pktlen = WACOM_PKGLEN_BBTOUCH; - features->device_type = BTN_TOOL_DOUBLETAP; features->x_phy = get_unaligned_le16(&report[i + 5]); features->x_max = @@ -325,7 +323,6 @@ static int wacom_parse_hid(struct usb_interface *intf, if (features->type == TABLETPC2FG) { /* need to reset back */ features->pktlen = WACOM_PKGLEN_TPC2FG; - features->device_type = BTN_TOOL_DOUBLETAP; features->y_max = get_unaligned_le16(&report[i + 3]); features->y_phy = @@ -334,7 +331,6 @@ static int wacom_parse_hid(struct usb_interface *intf, } else if (features->type == BAMBOO_PT) { /* need to reset back */ features->pktlen = WACOM_PKGLEN_BBTOUCH; - features->device_type = BTN_TOOL_DOUBLETAP; features->y_phy = get_unaligned_le16(&report[i + 3]); features->y_max = diff --git a/drivers/input/tablet/wacom_wac.c b/drivers/input/tablet/wacom_wac.c index a22e7789d91b..e18f36232682 100644 --- a/drivers/input/tablet/wacom_wac.c +++ b/drivers/input/tablet/wacom_wac.c @@ -1317,7 +1317,7 @@ void wacom_setup_input_capabilities(struct input_dev *input_dev, break; case TABLETPC2FG: - if (features->device_type == BTN_TOOL_DOUBLETAP) { + if (features->device_type == BTN_TOOL_FINGER) { input_mt_init_slots(input_dev, 2); input_set_abs_params(input_dev, ABS_MT_TOOL_TYPE, @@ -1366,7 +1366,7 @@ void wacom_setup_input_capabilities(struct input_dev *input_dev, __set_bit(INPUT_PROP_POINTER, input_dev->propbit); - if (features->device_type == BTN_TOOL_DOUBLETAP) { + if (features->device_type == BTN_TOOL_FINGER) { __set_bit(BTN_LEFT, input_dev->keybit); __set_bit(BTN_FORWARD, input_dev->keybit); __set_bit(BTN_BACK, input_dev->keybit); -- cgit v1.2.3 From 31175a8348af76aea2f557857c90467d13632dc3 Mon Sep 17 00:00:00 2001 From: Ping Cheng Date: Tue, 31 Jan 2012 00:07:33 -0800 Subject: Input: wacom - use switch statement for wacom_tpc_irq() And add two new data formats. Tested-by: Chris Bagwell Reviewed-by: Chris Bagwell Signed-off-by: Ping Cheng Signed-off-by: Dmitry Torokhov --- drivers/input/tablet/wacom_wac.c | 24 ++++++++++++++++++------ drivers/input/tablet/wacom_wac.h | 2 ++ 2 files changed, 20 insertions(+), 6 deletions(-) diff --git a/drivers/input/tablet/wacom_wac.c b/drivers/input/tablet/wacom_wac.c index e18f36232682..9283507b13af 100644 --- a/drivers/input/tablet/wacom_wac.c +++ b/drivers/input/tablet/wacom_wac.c @@ -832,12 +832,24 @@ static int wacom_tpc_irq(struct wacom_wac *wacom, size_t len) dbg("wacom_tpc_irq: received report #%d", data[0]); - if (len == WACOM_PKGLEN_TPC1FG || data[0] == WACOM_REPORT_TPC1FG) - return wacom_tpc_single_touch(wacom, len); - else if (data[0] == WACOM_REPORT_TPC2FG) - return wacom_tpc_mt_touch(wacom); - else if (data[0] == WACOM_REPORT_PENABLED) - return wacom_tpc_pen(wacom); + switch (len) { + case WACOM_PKGLEN_TPC1FG: + return wacom_tpc_single_touch(wacom, len); + + case WACOM_PKGLEN_TPC2FG: + return wacom_tpc_mt_touch(wacom); + + default: + switch (data[0]) { + case WACOM_REPORT_TPC1FG: + case WACOM_REPORT_TPCHID: + case WACOM_REPORT_TPCST: + return wacom_tpc_single_touch(wacom, len); + + case WACOM_REPORT_PENABLED: + return wacom_tpc_pen(wacom); + } + } return 0; } diff --git a/drivers/input/tablet/wacom_wac.h b/drivers/input/tablet/wacom_wac.h index 050acaefee7d..4f0ba21b0196 100644 --- a/drivers/input/tablet/wacom_wac.h +++ b/drivers/input/tablet/wacom_wac.h @@ -39,6 +39,8 @@ #define WACOM_REPORT_INTUOSPAD 12 #define WACOM_REPORT_TPC1FG 6 #define WACOM_REPORT_TPC2FG 13 +#define WACOM_REPORT_TPCHID 15 +#define WACOM_REPORT_TPCST 16 /* device quirks */ #define WACOM_QUIRK_MULTI_INPUT 0x0001 -- cgit v1.2.3 From 4065d1e7b2164cff4af57b58fac887df2fe75d2a Mon Sep 17 00:00:00 2001 From: Javier Martinez Canillas Date: Tue, 31 Jan 2012 00:18:00 -0800 Subject: Input: add Cypress TTSP capacitive multi-touch screen support Cypress TrueTouch(tm) Standard Product controllers are found in a wide range of embedded devices. This driver add support for a variety of TTSP controllers. Since the hardware is capable of tracking identifiable contacts, multi-touch protocol type B (stateful) is used to report contact information. The driver is composed of a core driver that process the data sent by the contacts and a set of bus specific interface modules. This patch adds the base core TTSP driver. Signed-off-by: Javier Martinez Canillas Reviewed-by: Henrik Rydberg Signed-off-by: Dmitry Torokhov --- drivers/input/touchscreen/Kconfig | 32 +- drivers/input/touchscreen/Makefile | 5 +- drivers/input/touchscreen/cyttsp_core.c | 625 ++++++++++++++++++++++++++++++++ drivers/input/touchscreen/cyttsp_core.h | 149 ++++++++ drivers/input/touchscreen/cyttsp_i2c.c | 146 ++++++++ drivers/input/touchscreen/cyttsp_spi.c | 210 +++++++++++ include/linux/input/cyttsp.h | 58 +++ 7 files changed, 1223 insertions(+), 2 deletions(-) create mode 100644 drivers/input/touchscreen/cyttsp_core.c create mode 100644 drivers/input/touchscreen/cyttsp_core.h create mode 100644 drivers/input/touchscreen/cyttsp_i2c.c create mode 100644 drivers/input/touchscreen/cyttsp_spi.c create mode 100644 include/linux/input/cyttsp.h diff --git a/drivers/input/touchscreen/Kconfig b/drivers/input/touchscreen/Kconfig index 4af2a18eb3ba..2b21a7066d36 100644 --- a/drivers/input/touchscreen/Kconfig +++ b/drivers/input/touchscreen/Kconfig @@ -139,7 +139,6 @@ config TOUCHSCREEN_CY8CTMG110 tristate "cy8ctmg110 touchscreen" depends on I2C depends on GPIOLIB - help Say Y here if you have a cy8ctmg110 capacitive touchscreen on an AAVA device. @@ -149,6 +148,37 @@ config TOUCHSCREEN_CY8CTMG110 To compile this driver as a module, choose M here: the module will be called cy8ctmg110_ts. +config TOUCHSCREEN_CYTTSP_CORE + tristate "Cypress TTSP touchscreen" + help + Say Y here if you have a touchscreen using controller from + the Cypress TrueTouch(tm) Standard Product family connected + to your system. You will also need to select appropriate + bus connection below. + + If unsure, say N. + + To compile this driver as a module, choose M here: the + module will be called cyttsp_core. + +config TOUCHSCREEN_CYTTSP_I2C + tristate "support I2C bus connection" + depends on TOUCHSCREEN_CYTTSP_CORE && I2C + help + Say Y here if the touchscreen is connected via I2C bus. + + To compile this driver as a module, choose M here: the + module will be called cyttsp_i2c. + +config TOUCHSCREEN_CYTTSP_SPI + tristate "support SPI bus connection" + depends on TOUCHSCREEN_CYTTSP_CORE && SPI_MASTER + help + Say Y here if the touchscreen is connected via SPI bus. + + To compile this driver as a module, choose M here: the + module will be called cyttsp_spi. + config TOUCHSCREEN_DA9034 tristate "Touchscreen support for Dialog Semiconductor DA9034" depends on PMIC_DA903X diff --git a/drivers/input/touchscreen/Makefile b/drivers/input/touchscreen/Makefile index 496091e88460..d9acbdcda3d9 100644 --- a/drivers/input/touchscreen/Makefile +++ b/drivers/input/touchscreen/Makefile @@ -16,8 +16,11 @@ obj-$(CONFIG_TOUCHSCREEN_ATMEL_MXT) += atmel_mxt_ts.o obj-$(CONFIG_TOUCHSCREEN_ATMEL_TSADCC) += atmel_tsadcc.o obj-$(CONFIG_TOUCHSCREEN_AUO_PIXCIR) += auo-pixcir-ts.o obj-$(CONFIG_TOUCHSCREEN_BITSY) += h3600_ts_input.o -obj-$(CONFIG_TOUCHSCREEN_BU21013) += bu21013_ts.o +obj-$(CONFIG_TOUCHSCREEN_BU21013) += bu21013_ts.o obj-$(CONFIG_TOUCHSCREEN_CY8CTMG110) += cy8ctmg110_ts.o +obj-$(CONFIG_TOUCHSCREEN_CYTTSP_CORE) += cyttsp_core.o +obj-$(CONFIG_TOUCHSCREEN_CYTTSP_I2C) += cyttsp_i2c.o +obj-$(CONFIG_TOUCHSCREEN_CYTTSP_SPI) += cyttsp_spi.o obj-$(CONFIG_TOUCHSCREEN_DA9034) += da9034-ts.o obj-$(CONFIG_TOUCHSCREEN_DYNAPRO) += dynapro.o obj-$(CONFIG_TOUCHSCREEN_HAMPSHIRE) += hampshire.o diff --git a/drivers/input/touchscreen/cyttsp_core.c b/drivers/input/touchscreen/cyttsp_core.c new file mode 100644 index 000000000000..8be22479b41c --- /dev/null +++ b/drivers/input/touchscreen/cyttsp_core.c @@ -0,0 +1,625 @@ +/* + * Core Source for: + * Cypress TrueTouch(TM) Standard Product (TTSP) touchscreen drivers. + * For use with Cypress Txx3xx parts. + * Supported parts include: + * CY8CTST341 + * CY8CTMA340 + * + * Copyright (C) 2009, 2010, 2011 Cypress Semiconductor, Inc. + * Copyright (C) 2012 Javier Martinez Canillas + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * version 2, and only version 2, as published by the + * Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + * Contact Cypress Semiconductor at www.cypress.com + * + */ + +#include +#include +#include +#include +#include +#include + +#include "cyttsp_core.h" + +/* Bootloader number of command keys */ +#define CY_NUM_BL_KEYS 8 + +/* helpers */ +#define GET_NUM_TOUCHES(x) ((x) & 0x0F) +#define IS_LARGE_AREA(x) (((x) & 0x10) >> 4) +#define IS_BAD_PKT(x) ((x) & 0x20) +#define IS_VALID_APP(x) ((x) & 0x01) +#define IS_OPERATIONAL_ERR(x) ((x) & 0x3F) +#define GET_HSTMODE(reg) (((reg) & 0x70) >> 4) +#define GET_BOOTLOADERMODE(reg) (((reg) & 0x10) >> 4) + +#define CY_REG_BASE 0x00 +#define CY_REG_ACT_DIST 0x1E +#define CY_REG_ACT_INTRVL 0x1D +#define CY_REG_TCH_TMOUT (CY_REG_ACT_INTRVL + 1) +#define CY_REG_LP_INTRVL (CY_REG_TCH_TMOUT + 1) +#define CY_MAXZ 255 +#define CY_DELAY_DFLT 20 /* ms */ +#define CY_DELAY_MAX 500 +#define CY_ACT_DIST_DFLT 0xF8 +#define CY_HNDSHK_BIT 0x80 +/* device mode bits */ +#define CY_OPERATE_MODE 0x00 +#define CY_SYSINFO_MODE 0x10 +/* power mode select bits */ +#define CY_SOFT_RESET_MODE 0x01 /* return to Bootloader mode */ +#define CY_DEEP_SLEEP_MODE 0x02 +#define CY_LOW_POWER_MODE 0x04 + +/* Slots management */ +#define CY_MAX_FINGER 4 +#define CY_MAX_ID 16 + +static const u8 bl_command[] = { + 0x00, /* file offset */ + 0xFF, /* command */ + 0xA5, /* exit bootloader command */ + 0, 1, 2, 3, 4, 5, 6, 7 /* default keys */ +}; + +static int ttsp_read_block_data(struct cyttsp *ts, u8 command, + u8 length, void *buf) +{ + int error; + int tries; + + for (tries = 0; tries < CY_NUM_RETRY; tries++) { + error = ts->bus_ops->read(ts, command, length, buf); + if (!error) + return 0; + + msleep(CY_DELAY_DFLT); + } + + return -EIO; +} + +static int ttsp_write_block_data(struct cyttsp *ts, u8 command, + u8 length, void *buf) +{ + int error; + int tries; + + for (tries = 0; tries < CY_NUM_RETRY; tries++) { + error = ts->bus_ops->write(ts, command, length, buf); + if (!error) + return 0; + + msleep(CY_DELAY_DFLT); + } + + return -EIO; +} + +static int ttsp_send_command(struct cyttsp *ts, u8 cmd) +{ + return ttsp_write_block_data(ts, CY_REG_BASE, sizeof(cmd), &cmd); +} + +static int cyttsp_load_bl_regs(struct cyttsp *ts) +{ + memset(&ts->bl_data, 0, sizeof(ts->bl_data)); + ts->bl_data.bl_status = 0x10; + + return ttsp_read_block_data(ts, CY_REG_BASE, + sizeof(ts->bl_data), &ts->bl_data); +} + +static int cyttsp_exit_bl_mode(struct cyttsp *ts) +{ + int error; + u8 bl_cmd[sizeof(bl_command)]; + + memcpy(bl_cmd, bl_command, sizeof(bl_command)); + if (ts->pdata->bl_keys) + memcpy(&bl_cmd[sizeof(bl_command) - CY_NUM_BL_KEYS], + ts->pdata->bl_keys, sizeof(bl_command)); + + error = ttsp_write_block_data(ts, CY_REG_BASE, + sizeof(bl_cmd), bl_cmd); + if (error) + return error; + + /* wait for TTSP Device to complete the operation */ + msleep(CY_DELAY_DFLT); + + error = cyttsp_load_bl_regs(ts); + if (error) + return error; + + if (GET_BOOTLOADERMODE(ts->bl_data.bl_status)) + return -EIO; + + return 0; +} + +static int cyttsp_set_operational_mode(struct cyttsp *ts) +{ + int error; + + error = ttsp_send_command(ts, CY_OPERATE_MODE); + if (error) + return error; + + /* wait for TTSP Device to complete switch to Operational mode */ + error = ttsp_read_block_data(ts, CY_REG_BASE, + sizeof(ts->xy_data), &ts->xy_data); + if (error) + return error; + + return ts->xy_data.act_dist == CY_ACT_DIST_DFLT ? -EIO : 0; +} + +static int cyttsp_set_sysinfo_mode(struct cyttsp *ts) +{ + int error; + + memset(&ts->sysinfo_data, 0, sizeof(ts->sysinfo_data)); + + /* switch to sysinfo mode */ + error = ttsp_send_command(ts, CY_SYSINFO_MODE); + if (error) + return error; + + /* read sysinfo registers */ + msleep(CY_DELAY_DFLT); + error = ttsp_read_block_data(ts, CY_REG_BASE, sizeof(ts->sysinfo_data), + &ts->sysinfo_data); + if (error) + return error; + + if (!ts->sysinfo_data.tts_verh && !ts->sysinfo_data.tts_verl) + return -EIO; + + return 0; +} + +static int cyttsp_set_sysinfo_regs(struct cyttsp *ts) +{ + int retval = 0; + + if (ts->pdata->act_intrvl != CY_ACT_INTRVL_DFLT || + ts->pdata->tch_tmout != CY_TCH_TMOUT_DFLT || + ts->pdata->lp_intrvl != CY_LP_INTRVL_DFLT) { + + u8 intrvl_ray[] = { + ts->pdata->act_intrvl, + ts->pdata->tch_tmout, + ts->pdata->lp_intrvl + }; + + /* set intrvl registers */ + retval = ttsp_write_block_data(ts, CY_REG_ACT_INTRVL, + sizeof(intrvl_ray), intrvl_ray); + msleep(CY_DELAY_DFLT); + } + + return retval; +} + +static int cyttsp_soft_reset(struct cyttsp *ts) +{ + unsigned long timeout; + int retval; + + /* wait for interrupt to set ready completion */ + INIT_COMPLETION(ts->bl_ready); + ts->state = CY_BL_STATE; + + enable_irq(ts->irq); + + retval = ttsp_send_command(ts, CY_SOFT_RESET_MODE); + if (retval) + goto out; + + timeout = wait_for_completion_timeout(&ts->bl_ready, + msecs_to_jiffies(CY_DELAY_DFLT * CY_DELAY_MAX)); + retval = timeout ? 0 : -EIO; + +out: + ts->state = CY_IDLE_STATE; + disable_irq(ts->irq); + return retval; +} + +static int cyttsp_act_dist_setup(struct cyttsp *ts) +{ + u8 act_dist_setup = ts->pdata->act_dist; + + /* Init gesture; active distance setup */ + return ttsp_write_block_data(ts, CY_REG_ACT_DIST, + sizeof(act_dist_setup), &act_dist_setup); +} + +static void cyttsp_extract_track_ids(struct cyttsp_xydata *xy_data, int *ids) +{ + ids[0] = xy_data->touch12_id >> 4; + ids[1] = xy_data->touch12_id & 0xF; + ids[2] = xy_data->touch34_id >> 4; + ids[3] = xy_data->touch34_id & 0xF; +} + +static const struct cyttsp_tch *cyttsp_get_tch(struct cyttsp_xydata *xy_data, + int idx) +{ + switch (idx) { + case 0: + return &xy_data->tch1; + case 1: + return &xy_data->tch2; + case 2: + return &xy_data->tch3; + case 3: + return &xy_data->tch4; + default: + return NULL; + } +} + +static void cyttsp_report_tchdata(struct cyttsp *ts) +{ + struct cyttsp_xydata *xy_data = &ts->xy_data; + struct input_dev *input = ts->input; + int num_tch = GET_NUM_TOUCHES(xy_data->tt_stat); + const struct cyttsp_tch *tch; + int ids[CY_MAX_ID]; + int i; + DECLARE_BITMAP(used, CY_MAX_ID); + + if (IS_LARGE_AREA(xy_data->tt_stat) == 1) { + /* terminate all active tracks */ + num_tch = 0; + dev_dbg(ts->dev, "%s: Large area detected\n", __func__); + } else if (num_tch > CY_MAX_FINGER) { + /* terminate all active tracks */ + num_tch = 0; + dev_dbg(ts->dev, "%s: Num touch error detected\n", __func__); + } else if (IS_BAD_PKT(xy_data->tt_mode)) { + /* terminate all active tracks */ + num_tch = 0; + dev_dbg(ts->dev, "%s: Invalid buffer detected\n", __func__); + } + + cyttsp_extract_track_ids(xy_data, ids); + + bitmap_zero(used, CY_MAX_ID); + + for (i = 0; i < num_tch; i++) { + tch = cyttsp_get_tch(xy_data, i); + + input_mt_slot(input, ids[i]); + input_mt_report_slot_state(input, MT_TOOL_FINGER, true); + input_report_abs(input, ABS_MT_POSITION_X, be16_to_cpu(tch->x)); + input_report_abs(input, ABS_MT_POSITION_Y, be16_to_cpu(tch->y)); + input_report_abs(input, ABS_MT_TOUCH_MAJOR, tch->z); + + __set_bit(ids[i], used); + } + + for (i = 0; i < CY_MAX_ID; i++) { + if (test_bit(i, used)) + continue; + + input_mt_slot(input, i); + input_mt_report_slot_state(input, MT_TOOL_FINGER, false); + } + + input_sync(input); +} + +static irqreturn_t cyttsp_irq(int irq, void *handle) +{ + struct cyttsp *ts = handle; + int error; + + if (unlikely(ts->state == CY_BL_STATE)) { + complete(&ts->bl_ready); + goto out; + } + + /* Get touch data from CYTTSP device */ + error = ttsp_read_block_data(ts, CY_REG_BASE, + sizeof(struct cyttsp_xydata), &ts->xy_data); + if (error) + goto out; + + /* provide flow control handshake */ + if (ts->pdata->use_hndshk) { + error = ttsp_send_command(ts, + ts->xy_data.hst_mode ^ CY_HNDSHK_BIT); + if (error) + goto out; + } + + if (unlikely(ts->state == CY_IDLE_STATE)) + goto out; + + if (GET_BOOTLOADERMODE(ts->xy_data.tt_mode)) { + /* + * TTSP device has reset back to bootloader mode. + * Restore to operational mode. + */ + error = cyttsp_exit_bl_mode(ts); + if (error) { + dev_err(ts->dev, + "Could not return to operational mode, err: %d\n", + error); + ts->state = CY_IDLE_STATE; + } + } else { + cyttsp_report_tchdata(ts); + } + +out: + return IRQ_HANDLED; +} + +static int cyttsp_power_on(struct cyttsp *ts) +{ + int error; + + error = cyttsp_soft_reset(ts); + if (error) + return error; + + error = cyttsp_load_bl_regs(ts); + if (error) + return error; + + if (GET_BOOTLOADERMODE(ts->bl_data.bl_status) && + IS_VALID_APP(ts->bl_data.bl_status)) { + error = cyttsp_exit_bl_mode(ts); + if (error) + return error; + } + + if (GET_HSTMODE(ts->bl_data.bl_file) != CY_OPERATE_MODE || + IS_OPERATIONAL_ERR(ts->bl_data.bl_status)) { + return -ENODEV; + } + + error = cyttsp_set_sysinfo_mode(ts); + if (error) + return error; + + error = cyttsp_set_sysinfo_regs(ts); + if (error) + return error; + + error = cyttsp_set_operational_mode(ts); + if (error) + return error; + + /* init active distance */ + error = cyttsp_act_dist_setup(ts); + if (error) + return error; + + ts->state = CY_ACTIVE_STATE; + + return 0; +} + +static int cyttsp_enable(struct cyttsp *ts) +{ + int error; + + /* + * The device firmware can wake on an I2C or SPI memory slave + * address match. So just reading a register is sufficient to + * wake up the device. The first read attempt will fail but it + * will wake it up making the second read attempt successful. + */ + error = ttsp_read_block_data(ts, CY_REG_BASE, + sizeof(ts->xy_data), &ts->xy_data); + if (error) + return error; + + if (GET_HSTMODE(ts->xy_data.hst_mode)) + return -EIO; + + enable_irq(ts->irq); + + return 0; +} + +static int cyttsp_disable(struct cyttsp *ts) +{ + int error; + + error = ttsp_send_command(ts, CY_LOW_POWER_MODE); + if (error) + return error; + + disable_irq(ts->irq); + + return 0; +} + +#ifdef CONFIG_PM_SLEEP +static int cyttsp_suspend(struct device *dev) +{ + struct cyttsp *ts = dev_get_drvdata(dev); + int retval = 0; + + mutex_lock(&ts->input->mutex); + + if (ts->input->users) { + retval = cyttsp_disable(ts); + if (retval == 0) + ts->suspended = true; + } + + mutex_unlock(&ts->input->mutex); + + return retval; +} + +static int cyttsp_resume(struct device *dev) +{ + struct cyttsp *ts = dev_get_drvdata(dev); + + mutex_lock(&ts->input->mutex); + + if (ts->input->users) + cyttsp_enable(ts); + + ts->suspended = false; + + mutex_unlock(&ts->input->mutex); + + return 0; +} + +#endif + +SIMPLE_DEV_PM_OPS(cyttsp_pm_ops, cyttsp_suspend, cyttsp_resume); +EXPORT_SYMBOL_GPL(cyttsp_pm_ops); + +static int cyttsp_open(struct input_dev *dev) +{ + struct cyttsp *ts = input_get_drvdata(dev); + int retval = 0; + + if (!ts->suspended) + retval = cyttsp_enable(ts); + + return retval; +} + +static void cyttsp_close(struct input_dev *dev) +{ + struct cyttsp *ts = input_get_drvdata(dev); + + if (!ts->suspended) + cyttsp_disable(ts); +} + +struct cyttsp *cyttsp_probe(const struct cyttsp_bus_ops *bus_ops, + struct device *dev, int irq, size_t xfer_buf_size) +{ + const struct cyttsp_platform_data *pdata = dev->platform_data; + struct cyttsp *ts; + struct input_dev *input_dev; + int error; + + if (!dev || !bus_ops || !pdata || !pdata->name || irq <= 0) { + error = -EINVAL; + goto err_out; + } + + ts = kzalloc(sizeof(*ts) + xfer_buf_size, GFP_KERNEL); + input_dev = input_allocate_device(); + if (!ts || !input_dev) { + error = -ENOMEM; + goto err_free_mem; + } + + ts->dev = dev; + ts->input = input_dev; + ts->pdata = dev->platform_data; + ts->bus_ops = bus_ops; + ts->irq = irq; + + init_completion(&ts->bl_ready); + snprintf(ts->phys, sizeof(ts->phys), "%s/input0", dev_name(dev)); + + if (pdata->init) { + error = pdata->init(); + if (error) { + dev_err(ts->dev, "platform init failed, err: %d\n", + error); + goto err_free_mem; + } + } + + input_dev->name = pdata->name; + input_dev->phys = ts->phys; + input_dev->id.bustype = bus_ops->bustype; + input_dev->dev.parent = ts->dev; + + input_dev->open = cyttsp_open; + input_dev->close = cyttsp_close; + + input_set_drvdata(input_dev, ts); + + __set_bit(EV_ABS, input_dev->evbit); + input_set_abs_params(input_dev, ABS_MT_POSITION_X, + 0, pdata->maxx, 0, 0); + input_set_abs_params(input_dev, ABS_MT_POSITION_Y, + 0, pdata->maxy, 0, 0); + input_set_abs_params(input_dev, ABS_MT_TOUCH_MAJOR, + 0, CY_MAXZ, 0, 0); + + input_mt_init_slots(input_dev, CY_MAX_ID); + + error = request_threaded_irq(ts->irq, NULL, cyttsp_irq, + IRQF_TRIGGER_FALLING | IRQF_ONESHOT, + pdata->name, ts); + if (error) { + dev_err(ts->dev, "failed to request IRQ %d, err: %d\n", + ts->irq, error); + goto err_platform_exit; + } + + disable_irq(ts->irq); + + error = cyttsp_power_on(ts); + if (error) + goto err_free_irq; + + error = input_register_device(input_dev); + if (error) { + dev_err(ts->dev, "failed to register input device: %d\n", + error); + goto err_free_irq; + } + + return ts; + +err_free_irq: + free_irq(ts->irq, ts); +err_platform_exit: + if (pdata->exit) + pdata->exit(); +err_free_mem: + input_free_device(input_dev); + kfree(ts); +err_out: + return ERR_PTR(error); +} +EXPORT_SYMBOL_GPL(cyttsp_probe); + +void cyttsp_remove(struct cyttsp *ts) +{ + free_irq(ts->irq, ts); + input_unregister_device(ts->input); + if (ts->pdata->exit) + ts->pdata->exit(); + kfree(ts); +} +EXPORT_SYMBOL_GPL(cyttsp_remove); + +MODULE_LICENSE("GPL"); +MODULE_DESCRIPTION("Cypress TrueTouch(R) Standard touchscreen driver core"); +MODULE_AUTHOR("Cypress"); diff --git a/drivers/input/touchscreen/cyttsp_core.h b/drivers/input/touchscreen/cyttsp_core.h new file mode 100644 index 000000000000..1aa3c6967e70 --- /dev/null +++ b/drivers/input/touchscreen/cyttsp_core.h @@ -0,0 +1,149 @@ +/* + * Header file for: + * Cypress TrueTouch(TM) Standard Product (TTSP) touchscreen drivers. + * For use with Cypress Txx3xx parts. + * Supported parts include: + * CY8CTST341 + * CY8CTMA340 + * + * Copyright (C) 2009, 2010, 2011 Cypress Semiconductor, Inc. + * Copyright (C) 2012 Javier Martinez Canillas + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * version 2, and only version 2, as published by the + * Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + * Contact Cypress Semiconductor at www.cypress.com + * + */ + + +#ifndef __CYTTSP_CORE_H__ +#define __CYTTSP_CORE_H__ + +#include +#include +#include +#include +#include +#include + +#define CY_NUM_RETRY 16 /* max number of retries for read ops */ + +struct cyttsp_tch { + __be16 x, y; + u8 z; +} __packed; + +/* TrueTouch Standard Product Gen3 interface definition */ +struct cyttsp_xydata { + u8 hst_mode; + u8 tt_mode; + u8 tt_stat; + struct cyttsp_tch tch1; + u8 touch12_id; + struct cyttsp_tch tch2; + u8 gest_cnt; + u8 gest_id; + struct cyttsp_tch tch3; + u8 touch34_id; + struct cyttsp_tch tch4; + u8 tt_undef[3]; + u8 act_dist; + u8 tt_reserved; +} __packed; + + +/* TTSP System Information interface definition */ +struct cyttsp_sysinfo_data { + u8 hst_mode; + u8 mfg_cmd; + u8 mfg_stat; + u8 cid[3]; + u8 tt_undef1; + u8 uid[8]; + u8 bl_verh; + u8 bl_verl; + u8 tts_verh; + u8 tts_verl; + u8 app_idh; + u8 app_idl; + u8 app_verh; + u8 app_verl; + u8 tt_undef[5]; + u8 scn_typ; + u8 act_intrvl; + u8 tch_tmout; + u8 lp_intrvl; +}; + +/* TTSP Bootloader Register Map interface definition */ +#define CY_BL_CHKSUM_OK 0x01 +struct cyttsp_bootloader_data { + u8 bl_file; + u8 bl_status; + u8 bl_error; + u8 blver_hi; + u8 blver_lo; + u8 bld_blver_hi; + u8 bld_blver_lo; + u8 ttspver_hi; + u8 ttspver_lo; + u8 appid_hi; + u8 appid_lo; + u8 appver_hi; + u8 appver_lo; + u8 cid_0; + u8 cid_1; + u8 cid_2; +}; + +struct cyttsp; + +struct cyttsp_bus_ops { + u16 bustype; + int (*write)(struct cyttsp *ts, + u8 addr, u8 length, const void *values); + int (*read)(struct cyttsp *ts, u8 addr, u8 length, void *values); +}; + +enum cyttsp_state { + CY_IDLE_STATE, + CY_ACTIVE_STATE, + CY_BL_STATE, +}; + +struct cyttsp { + struct device *dev; + int irq; + struct input_dev *input; + char phys[32]; + const struct cyttsp_platform_data *pdata; + const struct cyttsp_bus_ops *bus_ops; + struct cyttsp_bootloader_data bl_data; + struct cyttsp_sysinfo_data sysinfo_data; + struct cyttsp_xydata xy_data; + struct completion bl_ready; + enum cyttsp_state state; + bool suspended; + + u8 xfer_buf[] ____cacheline_aligned; +}; + +struct cyttsp *cyttsp_probe(const struct cyttsp_bus_ops *bus_ops, + struct device *dev, int irq, size_t xfer_buf_size); +void cyttsp_remove(struct cyttsp *ts); + +extern const struct dev_pm_ops cyttsp_pm_ops; + +#endif /* __CYTTSP_CORE_H__ */ diff --git a/drivers/input/touchscreen/cyttsp_i2c.c b/drivers/input/touchscreen/cyttsp_i2c.c new file mode 100644 index 000000000000..c7110cc06b9d --- /dev/null +++ b/drivers/input/touchscreen/cyttsp_i2c.c @@ -0,0 +1,146 @@ +/* + * Source for: + * Cypress TrueTouch(TM) Standard Product (TTSP) I2C touchscreen driver. + * For use with Cypress Txx3xx parts. + * Supported parts include: + * CY8CTST341 + * CY8CTMA340 + * + * Copyright (C) 2009, 2010, 2011 Cypress Semiconductor, Inc. + * Copyright (C) 2012 Javier Martinez Canillas + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * version 2, and only version 2, as published by the + * Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + * Contact Cypress Semiconductor at www.cypress.com + * + */ + +#include "cyttsp_core.h" + +#include +#include + +#define CY_I2C_DATA_SIZE 128 + +static int cyttsp_i2c_read_block_data(struct cyttsp *ts, + u8 addr, u8 length, void *values) +{ + struct i2c_client *client = to_i2c_client(ts->dev); + struct i2c_msg msgs[] = { + { + .addr = client->addr, + .flags = 0, + .len = 1, + .buf = &addr, + }, + { + .addr = client->addr, + .flags = I2C_M_RD, + .len = length, + .buf = values, + }, + }; + int retval; + + retval = i2c_transfer(client->adapter, msgs, ARRAY_SIZE(msgs)); + if (retval < 0) + return retval; + + return retval != ARRAY_SIZE(msgs) ? -EIO : 0; +} + +static int cyttsp_i2c_write_block_data(struct cyttsp *ts, + u8 addr, u8 length, const void *values) +{ + struct i2c_client *client = to_i2c_client(ts->dev); + int retval; + + ts->xfer_buf[0] = addr; + memcpy(&ts->xfer_buf[1], values, length); + + retval = i2c_master_send(client, ts->xfer_buf, length + 1); + + return retval < 0 ? retval : 0; +} + +static const struct cyttsp_bus_ops cyttsp_i2c_bus_ops = { + .bustype = BUS_I2C, + .write = cyttsp_i2c_write_block_data, + .read = cyttsp_i2c_read_block_data, +}; + +static int __devinit cyttsp_i2c_probe(struct i2c_client *client, + const struct i2c_device_id *id) +{ + struct cyttsp *ts; + + if (!i2c_check_functionality(client->adapter, I2C_FUNC_I2C)) { + dev_err(&client->dev, "I2C functionality not Supported\n"); + return -EIO; + } + + ts = cyttsp_probe(&cyttsp_i2c_bus_ops, &client->dev, client->irq, + CY_I2C_DATA_SIZE); + + if (IS_ERR(ts)) + return PTR_ERR(ts); + + i2c_set_clientdata(client, ts); + + return 0; +} + +static int __devexit cyttsp_i2c_remove(struct i2c_client *client) +{ + struct cyttsp *ts = i2c_get_clientdata(client); + + cyttsp_remove(ts); + + return 0; +} + +static const struct i2c_device_id cyttsp_i2c_id[] = { + { CY_I2C_NAME, 0 }, + { } +}; +MODULE_DEVICE_TABLE(i2c, cyttsp_i2c_id); + +static struct i2c_driver cyttsp_i2c_driver = { + .driver = { + .name = CY_I2C_NAME, + .owner = THIS_MODULE, + .pm = &cyttsp_pm_ops, + }, + .probe = cyttsp_i2c_probe, + .remove = __devexit_p(cyttsp_i2c_remove), + .id_table = cyttsp_i2c_id, +}; + +static int __init cyttsp_i2c_init(void) +{ + return i2c_add_driver(&cyttsp_i2c_driver); +} +module_init(cyttsp_i2c_init); + +static void __exit cyttsp_i2c_exit(void) +{ + return i2c_del_driver(&cyttsp_i2c_driver); +} +module_exit(cyttsp_i2c_exit); + +MODULE_LICENSE("GPL"); +MODULE_DESCRIPTION("Cypress TrueTouch(R) Standard Product (TTSP) I2C driver"); +MODULE_AUTHOR("Cypress"); +MODULE_ALIAS("i2c:cyttsp"); diff --git a/drivers/input/touchscreen/cyttsp_spi.c b/drivers/input/touchscreen/cyttsp_spi.c new file mode 100644 index 000000000000..9db5f8754d10 --- /dev/null +++ b/drivers/input/touchscreen/cyttsp_spi.c @@ -0,0 +1,210 @@ +/* + * Source for: + * Cypress TrueTouch(TM) Standard Product (TTSP) SPI touchscreen driver. + * For use with Cypress Txx3xx parts. + * Supported parts include: + * CY8CTST341 + * CY8CTMA340 + * + * Copyright (C) 2009, 2010, 2011 Cypress Semiconductor, Inc. + * Copyright (C) 2012 Javier Martinez Canillas + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * version 2, and only version 2, as published by the + * Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + * Contact Cypress Semiconductor at www.cypress.com + * + */ + +#include "cyttsp_core.h" + +#include +#include +#include + +#define CY_SPI_WR_OP 0x00 /* r/~w */ +#define CY_SPI_RD_OP 0x01 +#define CY_SPI_CMD_BYTES 4 +#define CY_SPI_SYNC_BYTE 2 +#define CY_SPI_SYNC_ACK1 0x62 /* from protocol v.2 */ +#define CY_SPI_SYNC_ACK2 0x9D /* from protocol v.2 */ +#define CY_SPI_DATA_SIZE 128 +#define CY_SPI_DATA_BUF_SIZE (CY_SPI_CMD_BYTES + CY_SPI_DATA_SIZE) +#define CY_SPI_BITS_PER_WORD 8 + +static int cyttsp_spi_xfer(struct cyttsp *ts, + u8 op, u8 reg, u8 *buf, int length) +{ + struct spi_device *spi = to_spi_device(ts->dev); + struct spi_message msg; + struct spi_transfer xfer[2]; + u8 *wr_buf = &ts->xfer_buf[0]; + u8 *rd_buf = &ts->xfer_buf[CY_SPI_DATA_BUF_SIZE]; + int retval; + int i; + + if (length > CY_SPI_DATA_SIZE) { + dev_err(ts->dev, "%s: length %d is too big.\n", + __func__, length); + return -EINVAL; + } + + memset(wr_buf, 0, CY_SPI_DATA_BUF_SIZE); + memset(rd_buf, 0, CY_SPI_DATA_BUF_SIZE); + + wr_buf[0] = 0x00; /* header byte 0 */ + wr_buf[1] = 0xFF; /* header byte 1 */ + wr_buf[2] = reg; /* reg index */ + wr_buf[3] = op; /* r/~w */ + if (op == CY_SPI_WR_OP) + memcpy(wr_buf + CY_SPI_CMD_BYTES, buf, length); + + memset(xfer, 0, sizeof(xfer)); + spi_message_init(&msg); + + /* + We set both TX and RX buffers because Cypress TTSP + requires full duplex operation. + */ + xfer[0].tx_buf = wr_buf; + xfer[0].rx_buf = rd_buf; + switch (op) { + case CY_SPI_WR_OP: + xfer[0].len = length + CY_SPI_CMD_BYTES; + spi_message_add_tail(&xfer[0], &msg); + break; + + case CY_SPI_RD_OP: + xfer[0].len = CY_SPI_CMD_BYTES; + spi_message_add_tail(&xfer[0], &msg); + + xfer[1].rx_buf = buf; + xfer[1].len = length; + spi_message_add_tail(&xfer[1], &msg); + break; + + default: + dev_err(ts->dev, "%s: bad operation code=%d\n", __func__, op); + return -EINVAL; + } + + retval = spi_sync(spi, &msg); + if (retval < 0) { + dev_dbg(ts->dev, "%s: spi_sync() error %d, len=%d, op=%d\n", + __func__, retval, xfer[1].len, op); + + /* + * do not return here since was a bad ACK sequence + * let the following ACK check handle any errors and + * allow silent retries + */ + } + + if (rd_buf[CY_SPI_SYNC_BYTE] != CY_SPI_SYNC_ACK1 || + rd_buf[CY_SPI_SYNC_BYTE + 1] != CY_SPI_SYNC_ACK2) { + + dev_dbg(ts->dev, "%s: operation %d failed\n", __func__, op); + + for (i = 0; i < CY_SPI_CMD_BYTES; i++) + dev_dbg(ts->dev, "%s: test rd_buf[%d]:0x%02x\n", + __func__, i, rd_buf[i]); + for (i = 0; i < length; i++) + dev_dbg(ts->dev, "%s: test buf[%d]:0x%02x\n", + __func__, i, buf[i]); + + return -EIO; + } + + return 0; +} + +static int cyttsp_spi_read_block_data(struct cyttsp *ts, + u8 addr, u8 length, void *data) +{ + return cyttsp_spi_xfer(ts, CY_SPI_RD_OP, addr, data, length); +} + +static int cyttsp_spi_write_block_data(struct cyttsp *ts, + u8 addr, u8 length, const void *data) +{ + return cyttsp_spi_xfer(ts, CY_SPI_WR_OP, addr, (void *)data, length); +} + +static const struct cyttsp_bus_ops cyttsp_spi_bus_ops = { + .bustype = BUS_SPI, + .write = cyttsp_spi_write_block_data, + .read = cyttsp_spi_read_block_data, +}; + +static int __devinit cyttsp_spi_probe(struct spi_device *spi) +{ + struct cyttsp *ts; + int error; + + /* Set up SPI*/ + spi->bits_per_word = CY_SPI_BITS_PER_WORD; + spi->mode = SPI_MODE_0; + error = spi_setup(spi); + if (error < 0) { + dev_err(&spi->dev, "%s: SPI setup error %d\n", + __func__, error); + return error; + } + + ts = cyttsp_probe(&cyttsp_spi_bus_ops, &spi->dev, spi->irq, + CY_SPI_DATA_BUF_SIZE * 2); + if (IS_ERR(ts)) + return PTR_ERR(ts); + + spi_set_drvdata(spi, ts); + + return 0; +} + +static int __devexit cyttsp_spi_remove(struct spi_device *spi) +{ + struct cyttsp *ts = spi_get_drvdata(spi); + + cyttsp_remove(ts); + + return 0; +} + +static struct spi_driver cyttsp_spi_driver = { + .driver = { + .name = CY_SPI_NAME, + .owner = THIS_MODULE, + .pm = &cyttsp_pm_ops, + }, + .probe = cyttsp_spi_probe, + .remove = __devexit_p(cyttsp_spi_remove), +}; + +static int __init cyttsp_spi_init(void) +{ + return spi_register_driver(&cyttsp_spi_driver); +} +module_init(cyttsp_spi_init); + +static void __exit cyttsp_spi_exit(void) +{ + spi_unregister_driver(&cyttsp_spi_driver); +} +module_exit(cyttsp_spi_exit); + +MODULE_ALIAS("spi:cyttsp"); +MODULE_LICENSE("GPL"); +MODULE_DESCRIPTION("Cypress TrueTouch(R) Standard Product (TTSP) SPI driver"); +MODULE_AUTHOR("Cypress"); +MODULE_ALIAS("spi:cyttsp"); diff --git a/include/linux/input/cyttsp.h b/include/linux/input/cyttsp.h new file mode 100644 index 000000000000..5af7c66f1fca --- /dev/null +++ b/include/linux/input/cyttsp.h @@ -0,0 +1,58 @@ +/* + * Header file for: + * Cypress TrueTouch(TM) Standard Product (TTSP) touchscreen drivers. + * For use with Cypress Txx3xx parts. + * Supported parts include: + * CY8CTST341 + * CY8CTMA340 + * + * Copyright (C) 2009, 2010, 2011 Cypress Semiconductor, Inc. + * Copyright (C) 2012 Javier Martinez Canillas + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * version 2, and only version 2, as published by the + * Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + * Contact Cypress Semiconductor at www.cypress.com (kev@cypress.com) + * + */ +#ifndef _CYTTSP_H_ +#define _CYTTSP_H_ + +#define CY_SPI_NAME "cyttsp-spi" +#define CY_I2C_NAME "cyttsp-i2c" +/* Active Power state scanning/processing refresh interval */ +#define CY_ACT_INTRVL_DFLT 0x00 /* ms */ +/* touch timeout for the Active power */ +#define CY_TCH_TMOUT_DFLT 0xFF /* ms */ +/* Low Power state scanning/processing refresh interval */ +#define CY_LP_INTRVL_DFLT 0x0A /* ms */ +/* Active distance in pixels for a gesture to be reported */ +#define CY_ACT_DIST_DFLT 0xF8 /* pixels */ + +struct cyttsp_platform_data { + u32 maxx; + u32 maxy; + bool use_hndshk; + u8 act_dist; /* Active distance */ + u8 act_intrvl; /* Active refresh interval; ms */ + u8 tch_tmout; /* Active touch timeout; ms */ + u8 lp_intrvl; /* Low power refresh interval; ms */ + int (*init)(void); + void (*exit)(void); + char *name; + s16 irq_gpio; + u8 *bl_keys; +}; + +#endif /* _CYTTSP_H_ */ -- cgit v1.2.3 From b89529a10c954f14191367355da2a6053c49abb9 Mon Sep 17 00:00:00 2001 From: Henrik Rydberg Date: Thu, 12 Jan 2012 19:40:34 +0100 Subject: Input: Use accessor for MT values The current MT accessor function does not distinguish between the MT values and the slot specification event. Add an accessor function for the values only, and use it where appropriate. Signed-off-by: Henrik Rydberg --- drivers/input/input.c | 2 +- include/linux/input/mt.h | 8 ++++++-- 2 files changed, 7 insertions(+), 3 deletions(-) diff --git a/drivers/input/input.c b/drivers/input/input.c index 1f78c957a75a..8921c6180c51 100644 --- a/drivers/input/input.c +++ b/drivers/input/input.c @@ -180,7 +180,7 @@ static int input_handle_abs_event(struct input_dev *dev, return INPUT_IGNORE_EVENT; } - is_mt_event = code >= ABS_MT_FIRST && code <= ABS_MT_LAST; + is_mt_event = input_is_mt_value(code); if (!is_mt_event) { pold = &dev->absinfo[code].value; diff --git a/include/linux/input/mt.h b/include/linux/input/mt.h index 318bb82325a6..f86737586e19 100644 --- a/include/linux/input/mt.h +++ b/include/linux/input/mt.h @@ -48,10 +48,14 @@ static inline void input_mt_slot(struct input_dev *dev, int slot) input_event(dev, EV_ABS, ABS_MT_SLOT, slot); } +static inline bool input_is_mt_value(int axis) +{ + return axis >= ABS_MT_FIRST && axis <= ABS_MT_LAST; +} + static inline bool input_is_mt_axis(int axis) { - return axis == ABS_MT_SLOT || - (axis >= ABS_MT_FIRST && axis <= ABS_MT_LAST); + return axis == ABS_MT_SLOT || input_is_mt_value(axis); } void input_mt_report_slot_state(struct input_dev *dev, -- cgit v1.2.3 From a80b83b7b8456e9b475346c2e01d7e210883208c Mon Sep 17 00:00:00 2001 From: John Stultz Date: Fri, 3 Feb 2012 00:19:07 -0800 Subject: Input: add infrastructure for selecting clockid for event time stamps As noted by Arve and others, since wall time can jump backwards, it is difficult to use for input because one cannot determine if one event occurred before another or for how long a key was pressed. However, the timestamp field is part of the kernel ABI, and cannot be changed without possibly breaking existing users. This patch adds a new IOCTL that allows a clockid to be set in the evdev_client struct that will specify which time base to use for event timestamps (ie: CLOCK_MONOTONIC instead of CLOCK_REALTIME). For now we only support CLOCK_MONOTONIC and CLOCK_REALTIME, but in the future we could support other clockids if appropriate. The default remains CLOCK_REALTIME, so we don't change the ABI. Signed-off-by: John Stultz Reviewed-by: Daniel Kurtz Signed-off-by: Dmitry Torokhov --- drivers/input/evdev.c | 25 +++++++++++++++++++++---- include/linux/input.h | 2 ++ kernel/time/timekeeping.c | 2 ++ 3 files changed, 25 insertions(+), 4 deletions(-) diff --git a/drivers/input/evdev.c b/drivers/input/evdev.c index 76457d50bc34..c17409742999 100644 --- a/drivers/input/evdev.c +++ b/drivers/input/evdev.c @@ -46,6 +46,7 @@ struct evdev_client { struct fasync_struct *fasync; struct evdev *evdev; struct list_head node; + int clkid; unsigned int bufsize; struct input_event buffer[]; }; @@ -54,8 +55,12 @@ static struct evdev *evdev_table[EVDEV_MINORS]; static DEFINE_MUTEX(evdev_table_mutex); static void evdev_pass_event(struct evdev_client *client, - struct input_event *event) + struct input_event *event, + ktime_t mono, ktime_t real) { + event->time = ktime_to_timeval(client->clkid == CLOCK_MONOTONIC ? + mono : real); + /* Interrupts are disabled, just acquire the lock. */ spin_lock(&client->buffer_lock); @@ -94,8 +99,11 @@ static void evdev_event(struct input_handle *handle, struct evdev *evdev = handle->private; struct evdev_client *client; struct input_event event; + ktime_t time_mono, time_real; + + time_mono = ktime_get(); + time_real = ktime_sub(time_mono, ktime_get_monotonic_offset()); - do_gettimeofday(&event.time); event.type = type; event.code = code; event.value = value; @@ -103,11 +111,12 @@ static void evdev_event(struct input_handle *handle, rcu_read_lock(); client = rcu_dereference(evdev->grab); + if (client) - evdev_pass_event(client, &event); + evdev_pass_event(client, &event, time_mono, time_real); else list_for_each_entry_rcu(client, &evdev->client_list, node) - evdev_pass_event(client, &event); + evdev_pass_event(client, &event, time_mono, time_real); rcu_read_unlock(); @@ -685,6 +694,14 @@ static long evdev_do_ioctl(struct file *file, unsigned int cmd, else return evdev_ungrab(evdev, client); + case EVIOCSCLOCKID: + if (copy_from_user(&i, p, sizeof(unsigned int))) + return -EFAULT; + if (i != CLOCK_MONOTONIC && i != CLOCK_REALTIME) + return -EINVAL; + client->clkid = i; + return 0; + case EVIOCGKEYCODE: return evdev_handle_get_keycode(dev, p); diff --git a/include/linux/input.h b/include/linux/input.h index 3862e32c4eeb..177261ea6f52 100644 --- a/include/linux/input.h +++ b/include/linux/input.h @@ -129,6 +129,8 @@ struct input_keymap_entry { #define EVIOCGRAB _IOW('E', 0x90, int) /* Grab/Release device */ +#define EVIOCSCLOCKID _IOW('E', 0xa0, int) /* Set clockid to be used for timestamps */ + /* * Device properties and quirks */ diff --git a/kernel/time/timekeeping.c b/kernel/time/timekeeping.c index 2b021b0e8507..169479994755 100644 --- a/kernel/time/timekeeping.c +++ b/kernel/time/timekeeping.c @@ -