diff options
author | Linus Torvalds <torvalds@linux-foundation.org> | 2013-09-07 10:38:46 -0700 |
---|---|---|
committer | Linus Torvalds <torvalds@linux-foundation.org> | 2013-09-07 10:38:46 -0700 |
commit | 8b8a7df9a1d87ba413fce246b11f54c636bb456a (patch) | |
tree | 797265037b52117fa633b32cbe3feacb1b1b0f07 | |
parent | b04c99e3b845892d754ee8052d6324c39c4040de (diff) | |
parent | 07176b988ebb20f46a317a60ee1d983fe1630203 (diff) |
Merge branch 'for-linus' of git://git.kernel.org/pub/scm/linux/kernel/git/dtor/input
Pull input updates from Dmitry Torokhov:
"A new driver for slidebar on Ideapad laptops and a bunch of assorted
driver fixes"
* 'for-linus' of git://git.kernel.org/pub/scm/linux/kernel/git/dtor/input: (32 commits)
Input: add SYN_MAX and SYN_CNT constants
Input: max11801_ts - convert to devm
Input: egalax-ts - fix typo and improve text
Input: MAINTAINERS - change maintainer for cyttsp driver
Input: cyttsp4 - kill 'defined but not used' compiler warnings
Input: add driver for slidebar on Lenovo IdeaPad laptops
Input: omap-keypad - set up irq type from DT
Input: omap-keypad - enable wakeup capability for keypad.
Input: omap-keypad - clear interrupts on open
Input: omap-keypad - convert to threaded IRQ
Input: omap-keypad - use bitfiled instead of hardcoded values
Input: cyttsp4 - remove useless NULL test from cyttsp4_watchdog_timer()
Input: wacom - fix error return code in wacom_probe()
Input: as5011 - fix error return code in as5011_probe()
Input: keyboard, serio - simplify use of devm_ioremap_resource
Input: tegra-kbc - simplify use of devm_ioremap_resource
Input: htcpen - fix incorrect placement of __initdata
Input: qt1070 - add power management ops
Input: wistron_btns - add MODULE_DEVICE_TABLE
Input: wistron_btns - mark the Medion MD96500 keymap as tested
...
33 files changed, 770 insertions, 321 deletions
diff --git a/Documentation/devicetree/bindings/input/input-reset.txt b/Documentation/devicetree/bindings/input/input-reset.txt new file mode 100644 index 00000000000..2bb2626fdb7 --- /dev/null +++ b/Documentation/devicetree/bindings/input/input-reset.txt @@ -0,0 +1,33 @@ +Input: sysrq reset sequence + +A simple binding to represent a set of keys as described in +include/uapi/linux/input.h. This is to communicate a sequence of keys to the +sysrq driver. Upon holding the keys for a specified amount of time (if +specified) the system is sync'ed and reset. + +Key sequences are global to the system but all the keys in a set must be coming +from the same input device. + +The /chosen node should contain a 'linux,sysrq-reset-seq' child node to define +a set of keys. + +Required property: +sysrq-reset-seq: array of Linux keycodes, one keycode per cell. + +Optional property: +timeout-ms: duration keys must be pressed together in milliseconds before +generating a sysrq. If omitted the system is rebooted immediately when a valid +sequence has been recognized. + +Example: + + chosen { + linux,sysrq-reset-seq { + keyset = <0x03 + 0x04 + 0x0a>; + timeout-ms = <3000>; + }; + }; + +Would represent KEY_2, KEY_3 and KEY_9. diff --git a/Documentation/devicetree/bindings/input/touchscreen/egalax-ts.txt b/Documentation/devicetree/bindings/input/touchscreen/egalax-ts.txt index df70318a617..49fa14ed155 100644 --- a/Documentation/devicetree/bindings/input/touchscreen/egalax-ts.txt +++ b/Documentation/devicetree/bindings/input/touchscreen/egalax-ts.txt @@ -6,7 +6,7 @@ Required properties: - interrupt-parent: the phandle for the interrupt controller - interrupts: touch controller interrupt - wakeup-gpios: the gpio pin to be used for waking up the controller - as well as uased as irq pin + and also used as irq pin Example: diff --git a/MAINTAINERS b/MAINTAINERS index 10533173e15..e775ecaf5d1 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -2494,9 +2494,9 @@ S: Maintained F: drivers/media/common/cypress_firmware* CYTTSP TOUCHSCREEN DRIVER -M: Javier Martinez Canillas <javier@dowhile0.org> +M: Ferruh Yigit <fery@cypress.com> L: linux-input@vger.kernel.org -S: Maintained +S: Supported F: drivers/input/touchscreen/cyttsp* F: include/linux/input/cyttsp.h @@ -4141,6 +4141,13 @@ W: http://launchpad.net/ideapad-laptop S: Maintained F: drivers/platform/x86/ideapad-laptop.c +IDEAPAD LAPTOP SLIDEBAR DRIVER +M: Andrey Moiseev <o2g.org.ru@gmail.com> +L: linux-input@vger.kernel.org +W: https://github.com/o2genum/ideapad-slidebar +S: Maintained +F: drivers/input/misc/ideapad_slidebar.c + IDE/ATAPI DRIVERS M: Borislav Petkov <bp@alien8.de> L: linux-ide@vger.kernel.org diff --git a/drivers/input/joystick/as5011.c b/drivers/input/joystick/as5011.c index 121cd63d333..005d852a06e 100644 --- a/drivers/input/joystick/as5011.c +++ b/drivers/input/joystick/as5011.c @@ -234,7 +234,7 @@ static int as5011_probe(struct i2c_client *client, int irq; int error; - plat_data = client->dev.platform_data; + plat_data = dev_get_platdata(&client->dev); if (!plat_data) return -EINVAL; @@ -288,6 +288,7 @@ static int as5011_probe(struct i2c_client *client, if (irq < 0) { dev_err(&client->dev, "Failed to get irq number for button gpio\n"); + error = irq; goto err_free_button_gpio; } diff --git a/drivers/input/joystick/maplecontrol.c b/drivers/input/joystick/maplecontrol.c index 59c10ec5a2a..8aa6e4c497d 100644 --- a/drivers/input/joystick/maplecontrol.c +++ b/drivers/input/joystick/maplecontrol.c @@ -61,7 +61,7 @@ static void dc_pad_callback(struct mapleq *mq) static int dc_pad_open(struct input_dev *dev) { - struct dc_pad *pad = dev->dev.platform_data; + struct dc_pad *pad = dev_get_platdata(&dev->dev); maple_getcond_callback(pad->mdev, dc_pad_callback, HZ/20, MAPLE_FUNC_CONTROLLER); @@ -71,7 +71,7 @@ static int dc_pad_open(struct input_dev *dev) static void dc_pad_close(struct input_dev *dev) { - struct dc_pad *pad = dev->dev.platform_data; + struct dc_pad *pad = dev_get_platdata(&dev->dev); maple_getcond_callback(pad->mdev, dc_pad_callback, 0, MAPLE_FUNC_CONTROLLER); diff --git a/drivers/input/keyboard/imx_keypad.c b/drivers/input/keyboard/imx_keypad.c index 03c8cc5cb6c..328cfc1eed9 100644 --- a/drivers/input/keyboard/imx_keypad.c +++ b/drivers/input/keyboard/imx_keypad.c @@ -442,12 +442,6 @@ static int imx_keypad_probe(struct platform_device *pdev) return -EINVAL; } - res = platform_get_resource(pdev, IORESOURCE_MEM, 0); - if (res == NULL) { - dev_err(&pdev->dev, "no I/O memory defined in platform data\n"); - return -EINVAL; - } - input_dev = devm_input_allocate_device(&pdev->dev); if (!input_dev) { dev_err(&pdev->dev, "failed to allocate the input device\n"); @@ -468,6 +462,7 @@ static int imx_keypad_probe(struct platform_device *pdev) setup_timer(&keypad->check_matrix_timer, imx_keypad_check_for_events, (unsigned long) keypad); + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); keypad->mmio_base = devm_ioremap_resource(&pdev->dev, res); if (IS_ERR(keypad->mmio_base)) return PTR_ERR(keypad->mmio_base); diff --git a/drivers/input/keyboard/max7359_keypad.c b/drivers/input/keyboard/max7359_keypad.c index 7c7af2b01e6..bc2cdaf563f 100644 --- a/drivers/input/keyboard/max7359_keypad.c +++ b/drivers/input/keyboard/max7359_keypad.c @@ -271,7 +271,7 @@ static int max7359_remove(struct i2c_client *client) return 0; } -#ifdef CONFIG_PM +#ifdef CONFIG_PM_SLEEP static int max7359_suspend(struct device *dev) { struct i2c_client *client = to_i2c_client(dev); diff --git a/drivers/input/keyboard/nspire-keypad.c b/drivers/input/keyboard/nspire-keypad.c index 20d872d6f60..b3e3edab6d9 100644 --- a/drivers/input/keyboard/nspire-keypad.c +++ b/drivers/input/keyboard/nspire-keypad.c @@ -171,12 +171,6 @@ static int nspire_keypad_probe(struct platform_device *pdev) return -EINVAL; } - res = platform_get_resource(pdev, IORESOURCE_MEM, 0); - if (!res) { - dev_err(&pdev->dev, "missing platform resources\n"); - return -EINVAL; - } - keypad = devm_kzalloc(&pdev->dev, sizeof(struct nspire_keypad), GFP_KERNEL); if (!keypad) { @@ -208,6 +202,7 @@ static int nspire_keypad_probe(struct platform_device *pdev) return PTR_ERR(keypad->clk); } + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); keypad->reg_base = devm_ioremap_resource(&pdev->dev, res); if (IS_ERR(keypad->reg_base)) return PTR_ERR(keypad->reg_base); diff --git a/drivers/input/keyboard/omap4-keypad.c b/drivers/input/keyboard/omap4-keypad.c index f4aa53a1fd6..30acfd49fa6 100644 --- a/drivers/input/keyboard/omap4-keypad.c +++ b/drivers/input/keyboard/omap4-keypad.c @@ -53,21 +53,17 @@ #define OMAP4_KBD_FULLCODE63_32 0x48 /* OMAP4 bit definitions */ -#define OMAP4_DEF_IRQENABLE_EVENTEN (1 << 0) -#define OMAP4_DEF_IRQENABLE_LONGKEY (1 << 1) -#define OMAP4_DEF_IRQENABLE_TIMEOUTEN (1 << 2) -#define OMAP4_DEF_WUP_EVENT_ENA (1 << 0) -#define OMAP4_DEF_WUP_LONG_KEY_ENA (1 << 1) -#define OMAP4_DEF_CTRL_NOSOFTMODE (1 << 1) -#define OMAP4_DEF_CTRLPTVVALUE (1 << 2) -#define OMAP4_DEF_CTRLPTV (1 << 1) +#define OMAP4_DEF_IRQENABLE_EVENTEN BIT(0) +#define OMAP4_DEF_IRQENABLE_LONGKEY BIT(1) +#define OMAP4_DEF_WUP_EVENT_ENA BIT(0) +#define OMAP4_DEF_WUP_LONG_KEY_ENA BIT(1) +#define OMAP4_DEF_CTRL_NOSOFTMODE BIT(1) +#define OMAP4_DEF_CTRL_PTV_SHIFT 2 /* OMAP4 values */ -#define OMAP4_VAL_IRQDISABLE 0x00 -#define OMAP4_VAL_DEBOUNCINGTIME 0x07 -#define OMAP4_VAL_FUNCTIONALCFG 0x1E - -#define OMAP4_MASK_IRQSTATUSDISABLE 0xFFFF +#define OMAP4_VAL_IRQDISABLE 0x0 +#define OMAP4_VAL_DEBOUNCINGTIME 0x7 +#define OMAP4_VAL_PVT 0x7 enum { KBD_REVISION_OMAP4 = 0, @@ -78,6 +74,7 @@ struct omap4_keypad { struct input_dev *input; void __iomem *base; + bool irq_wake_enabled; unsigned int irq; unsigned int rows; @@ -116,8 +113,22 @@ static void kbd_write_irqreg(struct omap4_keypad *keypad_data, } -/* Interrupt handler */ -static irqreturn_t omap4_keypad_interrupt(int irq, void *dev_id) +/* Interrupt handlers */ +static irqreturn_t omap4_keypad_irq_handler(int irq, void *dev_id) +{ + struct omap4_keypad *keypad_data = dev_id; + + if (kbd_read_irqreg(keypad_data, OMAP4_KBD_IRQSTATUS)) { + /* Disable interrupts */ + kbd_write_irqreg(keypad_data, OMAP4_KBD_IRQENABLE, + OMAP4_VAL_IRQDISABLE); + return IRQ_WAKE_THREAD; + } + + return IRQ_NONE; +} + +static irqreturn_t omap4_keypad_irq_thread_fn(int irq, void *dev_id) { struct omap4_keypad *keypad_data = dev_id; struct input_dev *input_dev = keypad_data->input; @@ -125,10 +136,6 @@ static irqreturn_t omap4_keypad_interrupt(int irq, void *dev_id) unsigned int col, row, code, changed; u32 *new_state = (u32 *) key_state; - /* Disable interrupts */ - kbd_write_irqreg(keypad_data, OMAP4_KBD_IRQENABLE, - OMAP4_VAL_IRQDISABLE); - *new_state = kbd_readl(keypad_data, OMAP4_KBD_FULLCODE31_0); *(new_state + 1) = kbd_readl(keypad_data, OMAP4_KBD_FULLCODE63_32); @@ -175,11 +182,13 @@ static int omap4_keypad_open(struct input_dev *input) disable_irq(keypad_data->irq); kbd_writel(keypad_data, OMAP4_KBD_CTRL, - OMAP4_VAL_FUNCTIONALCFG); + OMAP4_DEF_CTRL_NOSOFTMODE | + (OMAP4_VAL_PVT << OMAP4_DEF_CTRL_PTV_SHIFT)); kbd_writel(keypad_data, OMAP4_KBD_DEBOUNCINGTIME, OMAP4_VAL_DEBOUNCINGTIME); + /* clear pending interrupts */ kbd_write_irqreg(keypad_data, OMAP4_KBD_IRQSTATUS, - OMAP4_VAL_IRQDISABLE); + kbd_read_irqreg(keypad_data, OMAP4_KBD_IRQSTATUS)); kbd_write_irqreg(keypad_data, OMAP4_KBD_IRQENABLE, OMAP4_DEF_IRQENABLE_EVENTEN | OMAP4_DEF_IRQENABLE_LONGKEY); @@ -363,14 +372,15 @@ static int omap4_keypad_probe(struct platform_device *pdev) goto err_free_keymap; } - error = request_irq(keypad_data->irq, omap4_keypad_interrupt, - IRQF_TRIGGER_RISING, - "omap4-keypad", keypad_data); + error = request_threaded_irq(keypad_data->irq, omap4_keypad_irq_handler, + omap4_keypad_irq_thread_fn, 0, + "omap4-keypad", keypad_data); if (error) { dev_err(&pdev->dev, "failed to register interrupt\n"); goto err_free_input; } + device_init_wakeup(&pdev->dev, true); pm_runtime_put_sync(&pdev->dev); error = input_register_device(keypad_data->input); @@ -384,6 +394,7 @@ static int omap4_keypad_probe(struct platform_device *pdev) err_pm_disable: pm_runtime_disable(&pdev->dev); + device_init_wakeup(&pdev->dev, false); free_irq(keypad_data->irq, keypad_data); err_free_keymap: kfree(keypad_data->keymap); @@ -409,6 +420,8 @@ static int omap4_keypad_remove(struct platform_device *pdev) pm_runtime_disable(&pdev->dev); + device_init_wakeup(&pdev->dev, false); + input_unregister_device(keypad_data->input); iounmap(keypad_data->base); @@ -430,12 +443,46 @@ static const struct of_device_id omap_keypad_dt_match[] = { MODULE_DEVICE_TABLE(of, omap_keypad_dt_match); #endif +#ifdef CONFIG_PM_SLEEP +static int omap4_keypad_suspend(struct device *dev) +{ + struct platform_device *pdev = to_platform_device(dev); + struct omap4_keypad *keypad_data = platform_get_drvdata(pdev); + int error; + + if (device_may_wakeup(&pdev->dev)) { + error = enable_irq_wake(keypad_data->irq); + if (!error) + keypad_data->irq_wake_enabled = true; + } + + return 0; +} + +static int omap4_keypad_resume(struct device *dev) +{ + struct platform_device *pdev = to_platform_device(dev); + struct omap4_keypad *keypad_data = platform_get_drvdata(pdev); + + if (device_may_wakeup(&pdev->dev) && keypad_data->irq_wake_enabled) { + disable_irq_wake(keypad_data->irq); + keypad_data->irq_wake_enabled = false; + } + + return 0; +} +#endif + +static SIMPLE_DEV_PM_OPS(omap4_keypad_pm_ops, + omap4_keypad_suspend, omap4_keypad_resume); + static struct platform_driver omap4_keypad_driver = { .probe = omap4_keypad_probe, .remove = omap4_keypad_remove, .driver = { .name = "omap4-keypad", .owner = THIS_MODULE, + .pm = &omap4_keypad_pm_ops, .of_match_table = of_match_ptr(omap_keypad_dt_match), }, }; diff --git a/drivers/input/keyboard/qt1070.c b/drivers/input/keyboard/qt1070.c index 42b773b3125..6c561ec3cc0 100644 --- a/drivers/input/keyboard/qt1070.c +++ b/drivers/input/keyboard/qt1070.c @@ -243,6 +243,32 @@ static int qt1070_remove(struct i2c_client *client) return 0; } +#ifdef CONFIG_PM_SLEEP +static int qt1070_suspend(struct device *dev) +{ + struct i2c_client *client = to_i2c_client(dev); + struct qt1070_data *data = i2c_get_clientdata(client); + + if (device_may_wakeup(dev)) + enable_irq_wake(data->irq); + + return 0; +} + +static int qt1070_resume(struct device *dev) +{ + struct i2c_client *client = to_i2c_client(dev); + struct qt1070_data *data = i2c_get_clientdata(client); + + if (device_may_wakeup(dev)) + disable_irq_wake(data->irq); + + return 0; +} +#endif + +static SIMPLE_DEV_PM_OPS(qt1070_pm_ops, qt1070_suspend, qt1070_resume); + static const struct i2c_device_id qt1070_id[] = { { "qt1070", 0 }, { }, @@ -253,6 +279,7 @@ static struct i2c_driver qt1070_driver = { .driver = { .name = "qt1070", .owner = THIS_MODULE, + .pm = &qt1070_pm_ops, }, .id_table = qt1070_id, .probe = qt1070_probe, diff --git a/drivers/input/keyboard/spear-keyboard.c b/drivers/input/keyboard/spear-keyboard.c index 7111124b536..85ff530d9a9 100644 --- a/drivers/input/keyboard/spear-keyboard.c +++ b/drivers/input/keyboard/spear-keyboard.c @@ -191,12 +191,6 @@ static int spear_kbd_probe(struct platform_device *pdev) int irq; int error; - res = platform_get_resource(pdev, IORESOURCE_MEM, 0); - if (!res) { - dev_err(&pdev->dev, "no keyboard resource defined\n"); - return -EBUSY; - } - irq = platform_get_irq(pdev, 0); if (irq < 0) { dev_err(&pdev->dev, "not able to get irq for the device\n"); @@ -228,6 +222,7 @@ static int spear_kbd_probe(struct platform_device *pdev) kbd->suspended_rate = pdata->suspended_rate; } + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); kbd->io_base = devm_ioremap_resource(&pdev->dev, res); if (IS_ERR(kbd->io_base)) return PTR_ERR(kbd->io_base); diff --git a/drivers/input/keyboard/tegra-kbc.c b/drivers/input/keyboard/tegra-kbc.c index b46142f78ef..9cd20e6905a 100644 --- a/drivers/input/keyboard/tegra-kbc.c +++ b/drivers/input/keyboard/tegra-kbc.c @@ -638,12 +638,6 @@ static int tegra_kbc_probe(struct platform_device *pdev) if (!tegra_kbc_check_pin_cfg(kbc, &num_rows)) return -EINVAL; - res = platform_get_resource(pdev, IORESOURCE_MEM, 0); - if (!res) { - dev_err(&pdev->dev, "failed to get I/O memory\n"); - return -ENXIO; - } - kbc->irq = platform_get_irq(pdev, 0); if (kbc->irq < 0) { dev_err(&pdev->dev, "failed to get keyboard IRQ\n"); @@ -658,6 +652,7 @@ static int tegra_kbc_probe(struct platform_device *pdev) setup_timer(&kbc->timer, tegra_kbc_keypress_timer, (unsigned long)kbc); + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); kbc->mmio = devm_ioremap_resource(&pdev->dev, res); if (IS_ERR(kbc->mmio)) return PTR_ERR(kbc->mmio); diff --git a/drivers/input/misc/Kconfig b/drivers/input/misc/Kconfig index 0b541cdf9b8..aa51baaa9b1 100644 --- a/drivers/input/misc/Kconfig +++ b/drivers/input/misc/Kconfig @@ -647,4 +647,14 @@ config INPUT_SIRFSOC_ONKEY If unsure, say N. +config INPUT_IDEAPAD_SLIDEBAR + tristate "IdeaPad Laptop Slidebar" + depends on INPUT + depends on SERIO_I8042 + help + Say Y here if you have an IdeaPad laptop with a slidebar. + + To compile this driver as a module, choose M here: the + module will be called ideapad_slidebar. + endif diff --git a/drivers/input/misc/Makefile b/drivers/input/misc/Makefile index 829de43a242..0ebfb6dbf0f 100644 --- a/drivers/input/misc/Makefile +++ b/drivers/input/misc/Makefile @@ -61,3 +61,4 @@ obj-$(CONFIG_INPUT_WISTRON_BTNS) += wistron_btns.o obj-$(CONFIG_INPUT_WM831X_ON) += wm831x-on.o obj-$(CONFIG_INPUT_XEN_KBDDEV_FRONTEND) += xen-kbdfront.o obj-$(CONFIG_INPUT_YEALINK) += yealink.o +obj-$(CONFIG_INPUT_IDEAPAD_SLIDEBAR) += ideapad_slidebar.o diff --git a/drivers/input/misc/ideapad_slidebar.c b/drivers/input/misc/ideapad_slidebar.c new file mode 100644 index 00000000000..edfd6239f13 --- /dev/null +++ b/drivers/input/misc/ideapad_slidebar.c @@ -0,0 +1,358 @@ +/* + * Input driver for slidebars on some Lenovo IdeaPad laptops + * + * Copyright (C) 2013 Andrey Moiseev <o2g.org.ru@gmail.com> + * + * Reverse-engineered from Lenovo SlideNav software (SBarHook.dll). + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the Free + * Software Foundation; either version 2 of the License, or (at your option) + * any later version. + * + * Trademarks are the property of their respective owners. + */ + +/* + * Currently tested and works on: + * Lenovo IdeaPad Y550 + * Lenovo IdeaPad Y550P + * + * Other models can be added easily. To test, + * load with 'force' parameter set 'true'. + * + * LEDs blinking and input mode are managed via sysfs, + * (hex, unsigned byte value): + * /sys/devices/platform/ideapad_slidebar/slidebar_mode + * + * The value is in byte range, however, I only figured out + * how bits 0b10011001 work. Some other bits, probably, + * are meaningfull too. + * + * Possible states: + * + * STD_INT, ONMOV_INT, OFF_INT, LAST_POLL, OFF_POLL + * + * Meaning: + * released touched + * STD 'heartbeat' lights follow the finger + * ONMOV no lights lights follow the finger + * LAST at last pos lights follow the finger + * OFF no lights no lights + * + * INT all input events are generated, interrupts are used + * POLL no input events by default, to get them, + * send 0b10000000 (read below) + * + * Commands: write + * + * All | 0b01001 -> STD_INT + * possible | 0b10001 -> ONMOV_INT + * states | 0b01000 -> OFF_INT + * + * | 0b0 -> LAST_POLL + * STD_INT or ONMOV_INT | + * | 0b1 -> STD_INT + * + * | 0b0 -> OFF_POLL + * OFF_INT or OFF_POLL | + * | 0b1 -> OFF_INT + * + * Any state | 0b10000000 -> if the slidebar has updated data, + * produce one input event (last position), + * switch to respective POLL mode + * (like 0x0), if not in POLL mode yet. + * + * Get current state: read + * + * masked by 0x11 read value means: + * + * 0x00 LAST + * 0x01 STD + * 0x10 OFF + * 0x11 ONMOV + */ + +#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt + +#include <linux/module.h> +#include <linux/kernel.h> +#include <linux/dmi.h> +#include <linux/spinlock.h> +#include <linux/platform_device.h> +#include <linux/input.h> +#include <linux/io.h> +#include <linux/ioport.h> +#include <linux/i8042.h> +#include <linux/serio.h> + +#define IDEAPAD_BASE 0xff29 + +static bool force; +module_param(force, bool, 0); +MODULE_PARM_DESC(force, "Force driver load, ignore DMI data"); + +static DEFINE_SPINLOCK(io_lock); + +static struct input_dev *slidebar_input_dev; +static struct platform_device *slidebar_platform_dev; + +static u8 slidebar_pos_get(void) +{ + u8 res; + unsigned long flags; + + spin_lock_irqsave(&io_lock, flags); + outb(0xf4, 0xff29); + outb(0xbf, 0xff2a); + res = inb(0xff2b); + spin_unlock_irqrestore(&io_lock, flags); + + return res; +} + +static u8 slidebar_mode_get(void) +{ + u8 res; + unsigned long flags; + + spin_lock_irqsave(&io_lock, flags); + outb(0xf7, 0xff29); + outb(0x8b, 0xff2a); + res = inb(0xff2b); + spin_unlock_irqrestore(&io_lock, flags); + + return res; +} + +static void slidebar_mode_set(u8 mode) +{ + unsigned long flags; + + spin_lock_irqsave(&io_lock, flags); + outb(0xf7, 0xff29); + outb(0x8b, 0xff2a); + outb(mode, 0xff2b); + spin_unlock_irqrestore(&io_lock, flags); +} + +static bool slidebar_i8042_filter(unsigned char data, unsigned char str, + struct serio *port) +{ + static bool extended = false; + + /* We are only interested in data coming form KBC port */ + if (str & I8042_STR_AUXDATA) + return false; + + /* Scancodes: e03b on move, e0bb on release. */ + if (data == 0xe0) { + extended = true; + return true; + } + + if (!extended) + return false; + + extended = false; + + if (likely((data & 0x7f) != 0x3b)) { + serio_interrupt(port, 0xe0, 0); + return false; + } + + if (data & 0x80) { + input_report_key(slidebar_input_dev, BTN_TOUCH, 0); + } else { + input_report_key(slidebar_input_dev, BTN_TOUCH, 1); + input_report_abs(slidebar_input_dev, ABS_X, slidebar_pos_get()); + } + input_sync(slidebar_input_dev); + + return true; +} + +static ssize_t show_slidebar_mode(struct device *dev, + struct device_attribute *attr, + char *buf) +{ + return sprintf(buf, "%x\n", slidebar_mode_get()); +} + +static ssize_t store_slidebar_mode(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t count) +{ + u8 mode; + int error; + + error = kstrtou8(buf, 0, &mode); + if (error) + return error; + + slidebar_mode_set(mode); + + return count; +} + +static DEVICE_ATTR(slidebar_mode, S_IWUSR | S_IRUGO, + show_slidebar_mode, store_slidebar_mode); + +static struct attribute *ideapad_attrs[] = { + &dev_attr_slidebar_mode.attr, + NULL +}; + +static struct attribute_group ideapad_attr_group = { + .attrs = ideapad_attrs +}; + +static const struct attribute_group *ideapad_attr_groups[] = { + &ideapad_attr_group, + NULL +}; + +static int __init ideapad_probe(struct platform_device* pdev) +{ + int err; + + if (!request_region(IDEAPAD_BASE, 3, "ideapad_slidebar")) { + dev_err(&pdev->dev, "IO ports are busy\n"); + return -EBUSY; + } + + slidebar_input_dev = input_allocate_device(); + if (!slidebar_input_dev) { + dev_err(&pdev->dev, "Failed to allocate input device\n"); + err = -ENOMEM; + goto err_release_ports; + } + + slidebar_input_dev->name = "IdeaPad Slidebar"; + slidebar_input_dev->id.bustype = BUS_HOST; + slidebar_input_dev->dev.parent = &pdev->dev; + input_set_capability(slidebar_input_dev, EV_KEY, BTN_TOUCH); + input_set_capability(slidebar_input_dev, EV_ABS, ABS_X); + input_set_abs_params(slidebar_input_dev, ABS_X, 0, 0xff, 0, 0); + + err = i8042_install_filter(slidebar_i8042_filter); + if (err) { + dev_err(&pdev->dev, + "Failed to install i8042 filter: %d\n", err); + goto err_free_dev; + } + + err = input_register_device(slidebar_input_dev); + if (err) { + dev_err(&pdev->dev, + "Failed to register input device: %d\n", err); + goto err_remove_filter; + } + + return 0; + +err_remove_filter: + i8042_remove_filter(slidebar_i8042_filter); +err_free_dev: + input_free_device(slidebar_input_dev); +err_release_ports: + release_region(IDEAPAD_BASE, 3); + return err; +} + +static int ideapad_remove(struct platform_device *pdev) +{ + i8042_remove_filter(slidebar_i8042_filter); + input_unregister_device(slidebar_input_dev); + release_region(IDEAPAD_BASE, 3); + + return 0; +} + +static struct platform_driver slidebar_drv = { + .driver = { + .name = "ideapad_slidebar", + .owner = THIS_MODULE, + }, + .remove = ideapad_remove, +}; + +static int __init ideapad_dmi_check(const struct dmi_system_id *id) +{ + pr_info("Laptop model '%s'\n", id->ident); + return 1; +} + +static const struct dmi_system_id ideapad_dmi[] __initconst = { + { + .ident = "Lenovo IdeaPad Y550", + .matches = { + DMI_MATCH(DMI_SYS_VENDOR, "LENOVO"), + DMI_MATC |