summaryrefslogtreecommitdiff
path: root/drivers
diff options
context:
space:
mode:
authorDmitry Torokhov <dmitry.torokhov@gmail.com>2013-02-20 10:05:39 +0400
committerDmitry Torokhov <dmitry.torokhov@gmail.com>2013-02-20 10:05:39 +0400
commit2d9f0d964be94fd51c7303288c6f9c88bf2381fe (patch)
tree2631c3e82abc145a6e9c8e2c18687833c71de012 /drivers
parent9937c026820baabd1e908a9c1e6bdc846293000a (diff)
parent005a69d632cd8694061c2dd27492fe874780b5ee (diff)
downloadlinux-2d9f0d964be94fd51c7303288c6f9c88bf2381fe.tar.xz
Merge branch 'next' into for-linus
Prepare first set of updates for 3.9 merge window.
Diffstat (limited to 'drivers')
-rw-r--r--drivers/input/Kconfig2
-rw-r--r--drivers/input/input-mt.c1
-rw-r--r--drivers/input/joystick/walkera0701.c82
-rw-r--r--drivers/input/keyboard/Kconfig16
-rw-r--r--drivers/input/keyboard/Makefile1
-rw-r--r--drivers/input/keyboard/atkbd.c74
-rw-r--r--drivers/input/keyboard/goldfish_events.c194
-rw-r--r--drivers/input/keyboard/imx_keypad.c43
-rw-r--r--drivers/input/keyboard/qt2160.c141
-rw-r--r--drivers/input/keyboard/tegra-kbc.c478
-rw-r--r--drivers/input/misc/adxl34x.c7
-rw-r--r--drivers/input/misc/bma150.c14
-rw-r--r--drivers/input/misc/twl4030-vibra.c45
-rw-r--r--drivers/input/misc/twl6040-vibra.c100
-rw-r--r--drivers/input/misc/wm831x-on.c4
-rw-r--r--drivers/input/mouse/Kconfig22
-rw-r--r--drivers/input/mouse/Makefile2
-rw-r--r--drivers/input/mouse/alps.c773
-rw-r--r--drivers/input/mouse/alps.h145
-rw-r--r--drivers/input/mouse/cyapa.c973
-rw-r--r--drivers/input/mouse/cypress_ps2.c725
-rw-r--r--drivers/input/mouse/cypress_ps2.h191
-rw-r--r--drivers/input/mouse/psmouse-base.c32
-rw-r--r--drivers/input/mouse/psmouse.h1
-rw-r--r--drivers/input/mouse/synaptics.c32
-rw-r--r--drivers/input/serio/Kconfig1
-rw-r--r--drivers/input/tablet/wacom_wac.c192
-rw-r--r--drivers/input/tablet/wacom_wac.h2
-rw-r--r--drivers/input/touchscreen/Kconfig2
-rw-r--r--drivers/input/touchscreen/cyttsp_spi.c1
-rw-r--r--drivers/input/touchscreen/mms114.c54
-rw-r--r--drivers/input/touchscreen/stmpe-ts.c2
-rw-r--r--drivers/input/touchscreen/tsc2005.c1
-rw-r--r--drivers/input/touchscreen/wm831x-ts.c4
-rw-r--r--drivers/tty/sysrq.c276
35 files changed, 3516 insertions, 1117 deletions
diff --git a/drivers/input/Kconfig b/drivers/input/Kconfig
index 55f7e57d4e42..38b523a1ece0 100644
--- a/drivers/input/Kconfig
+++ b/drivers/input/Kconfig
@@ -3,7 +3,7 @@
#
menu "Input device support"
- depends on !S390 && !UML
+ depends on !UML
config INPUT
tristate "Generic input layer (needed for keyboard, mouse, ...)" if EXPERT
diff --git a/drivers/input/input-mt.c b/drivers/input/input-mt.c
index c2f436ce7f5b..53eaf89f2db5 100644
--- a/drivers/input/input-mt.c
+++ b/drivers/input/input-mt.c
@@ -18,6 +18,7 @@ static void copy_abs(struct input_dev *dev, unsigned int dst, unsigned int src)
{
if (dev->absinfo && test_bit(src, dev->absbit)) {
dev->absinfo[dst] = dev->absinfo[src];
+ dev->absinfo[dst].fuzz = 0;
dev->absbit[BIT_WORD(dst)] |= BIT_MASK(dst);
}
}
diff --git a/drivers/input/joystick/walkera0701.c b/drivers/input/joystick/walkera0701.c
index f8f892b076e8..b76ac580703c 100644
--- a/drivers/input/joystick/walkera0701.c
+++ b/drivers/input/joystick/walkera0701.c
@@ -12,7 +12,7 @@
* the Free Software Foundation.
*/
-/* #define WK0701_DEBUG */
+#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
#define RESERVE 20000
#define SYNC_PULSE 1306000
@@ -67,6 +67,7 @@ static inline void walkera0701_parse_frame(struct walkera_dev *w)
{
int i;
int val1, val2, val3, val4, val5, val6, val7, val8;
+ int magic, magic_bit;
int crc1, crc2;
for (crc1 = crc2 = i = 0; i < 10; i++) {
@@ -102,17 +103,12 @@ static inline void walkera0701_parse_frame(struct walkera_dev *w)
val8 = (w->buf[18] & 1) << 8 | (w->buf[19] << 4) | w->buf[20];
val8 *= (w->buf[18] & 2) - 1; /*sign */
-#ifdef WK0701_DEBUG
- {
- int magic, magic_bit;
- magic = (w->buf[21] << 4) | w->buf[22];
- magic_bit = (w->buf[24] & 8) >> 3;
- printk(KERN_DEBUG
- "walkera0701: %4d %4d %4d %4d %4d %4d %4d %4d (magic %2x %d)\n",
- val1, val2, val3, val4, val5, val6, val7, val8, magic,
- magic_bit);
- }
-#endif
+ magic = (w->buf[21] << 4) | w->buf[22];
+ magic_bit = (w->buf[24] & 8) >> 3;
+ pr_debug("%4d %4d %4d %4d %4d %4d %4d %4d (magic %2x %d)\n",
+ val1, val2, val3, val4, val5, val6, val7, val8,
+ magic, magic_bit);
+
input_report_abs(w->input_dev, ABS_X, val2);
input_report_abs(w->input_dev, ABS_Y, val1);
input_report_abs(w->input_dev, ABS_Z, val6);
@@ -187,6 +183,9 @@ static int walkera0701_open(struct input_dev *dev)
{
struct walkera_dev *w = input_get_drvdata(dev);
+ if (parport_claim(w->pardevice))
+ return -EBUSY;
+
parport_enable_irq(w->parport);
return 0;
}
@@ -197,40 +196,51 @@ static void walkera0701_close(struct input_dev *dev)
parport_disable_irq(w->parport);
hrtimer_cancel(&w->timer);
+
+ parport_release(w->pardevice);
}
static int walkera0701_connect(struct walkera_dev *w, int parport)
{
- int err = -ENODEV;
+ int error;
w->parport = parport_find_number(parport);
- if (w->parport == NULL)
+ if (!w->parport) {
+ pr_err("parport %d does not exist\n", parport);
return -ENODEV;
+ }
if (w->parport->irq == -1) {
- printk(KERN_ERR "walkera0701: parport without interrupt\n");
- goto init_err;
+ pr_err("parport %d does not have interrupt assigned\n",
+ parport);
+ error = -EINVAL;
+ goto err_put_parport;
}
- err = -EBUSY;
w->pardevice = parport_register_device(w->parport, "walkera0701",
NULL, NULL, walkera0701_irq_handler,
PARPORT_DEV_EXCL, w);
- if (!w->pardevice)
- goto init_err;
-
- if (parport_negotiate(w->pardevice->port, IEEE1284_MODE_COMPAT))
- goto init_err1;
+ if (!w->pardevice) {
+ pr_err("failed to register parport device\n");
+ error = -EIO;
+ goto err_put_parport;
+ }
- if (parport_claim(w->pardevice))
- goto init_err1;
+ if (parport_negotiate(w->pardevice->port, IEEE1284_MODE_COMPAT)) {
+ pr_err("failed to negotiate parport mode\n");
+ error = -EIO;
+ goto err_unregister_device;
+ }
hrtimer_init(&w->timer, CLOCK_MONOTONIC, HRTIMER_MODE_REL);
w->timer.function = timer_handler;
w->input_dev = input_allocate_device();
- if (!w->input_dev)
- goto init_err2;
+ if (!w->input_dev) {
+ pr_err("failed to allocate input device\n");
+ error = -ENOMEM;
+ goto err_unregister_device;
+ }
input_set_drvdata(w->input_dev, w);
w->input_dev->name = "Walkera WK-0701 TX";
@@ -241,6 +251,7 @@ static int walkera0701_connect(struct walkera_dev *w, int parport)
w->input_dev->id.vendor = 0x0001;
w->input_dev->id.product = 0x0001;
w->input_dev->id.version = 0x0100;
+ w->input_dev->dev.parent = w->parport->dev;
w->input_dev->open = walkera0701_open;
w->input_dev->close = walkera0701_close;
@@ -254,27 +265,26 @@ static int walkera0701_connect(struct walkera_dev *w, int parport)
input_set_abs_params(w->input_dev, ABS_RUDDER, -512, 512, 0, 0);
input_set_abs_params(w->input_dev, ABS_MISC, -512, 512, 0, 0);
- err = input_register_device(w->input_dev);
- if (err)
- goto init_err3;
+ error = input_register_device(w->input_dev);
+ if (error) {
+ pr_err("failed to register input device\n");
+ goto err_free_input_dev;
+ }
return 0;
- init_err3:
+err_free_input_dev:
input_free_device(w->input_dev);
- init_err2:
- parport_release(w->pardevice);
- init_err1:
+err_unregister_device:
parport_unregister_device(w->pardevice);
- init_err:
+err_put_parport:
parport_put_port(w->parport);
- return err;
+ return error;
}
static void walkera0701_disconnect(struct walkera_dev *w)
{
input_unregister_device(w->input_dev);
- parport_release(w->pardevice);
parport_unregister_device(w->pardevice);
parport_put_port(w->parport);
}
diff --git a/drivers/input/keyboard/Kconfig b/drivers/input/keyboard/Kconfig
index 48309641b1b2..992137cf3a64 100644
--- a/drivers/input/keyboard/Kconfig
+++ b/drivers/input/keyboard/Kconfig
@@ -224,7 +224,7 @@ config KEYBOARD_TCA6416
config KEYBOARD_TCA8418
tristate "TCA8418 Keypad Support"
- depends on I2C
+ depends on I2C && GENERIC_HARDIRQS
select INPUT_MATRIXKMAP
help
This driver implements basic keypad functionality
@@ -303,7 +303,7 @@ config KEYBOARD_HP7XX
config KEYBOARD_LM8323
tristate "LM8323 keypad chip"
- depends on I2C
+ depends on I2C && GENERIC_HARDIRQS
depends on LEDS_CLASS
help
If you say yes here you get support for the National Semiconductor
@@ -420,7 +420,7 @@ config KEYBOARD_NOMADIK
config KEYBOARD_TEGRA
tristate "NVIDIA Tegra internal matrix keyboard controller support"
- depends on ARCH_TEGRA
+ depends on ARCH_TEGRA && OF
select INPUT_MATRIXKMAP
help
Say Y here if you want to use a matrix keyboard connected directly
@@ -479,6 +479,16 @@ config KEYBOARD_SAMSUNG
To compile this driver as a module, choose M here: the
module will be called samsung-keypad.
+config KEYBOARD_GOLDFISH_EVENTS
+ depends on GOLDFISH
+ tristate "Generic Input Event device for Goldfish"
+ help
+ Say Y here to get an input event device for the Goldfish virtual
+ device emulator.
+
+ To compile this driver as a module, choose M here: the
+ module will be called goldfish-events.
+
config KEYBOARD_STOWAWAY
tristate "Stowaway keyboard"
select SERIO
diff --git a/drivers/input/keyboard/Makefile b/drivers/input/keyboard/Makefile
index 44e76002f54b..49b16453d00e 100644
--- a/drivers/input/keyboard/Makefile
+++ b/drivers/input/keyboard/Makefile
@@ -13,6 +13,7 @@ obj-$(CONFIG_KEYBOARD_ATKBD) += atkbd.o
obj-$(CONFIG_KEYBOARD_BFIN) += bf54x-keys.o
obj-$(CONFIG_KEYBOARD_DAVINCI) += davinci_keyscan.o
obj-$(CONFIG_KEYBOARD_EP93XX) += ep93xx_keypad.o
+obj-$(CONFIG_KEYBOARD_GOLDFISH_EVENTS) += goldfish_events.o
obj-$(CONFIG_KEYBOARD_GPIO) += gpio_keys.o
obj-$(CONFIG_KEYBOARD_GPIO_POLLED) += gpio_keys_polled.o
obj-$(CONFIG_KEYBOARD_TCA6416) += tca6416-keypad.o
diff --git a/drivers/input/keyboard/atkbd.c b/drivers/input/keyboard/atkbd.c
index add5ffd9fe26..2626773ff29b 100644
--- a/drivers/input/keyboard/atkbd.c
+++ b/drivers/input/keyboard/atkbd.c
@@ -676,6 +676,39 @@ static inline void atkbd_disable(struct atkbd *atkbd)
serio_continue_rx(atkbd->ps2dev.serio);
}
+static int atkbd_activate(struct atkbd *atkbd)
+{
+ struct ps2dev *ps2dev = &atkbd->ps2dev;
+
+/*
+ * Enable the keyboard to receive keystrokes.
+ */
+
+ if (ps2_command(ps2dev, NULL, ATKBD_CMD_ENABLE)) {
+ dev_err(&ps2dev->serio->dev,
+ "Failed to enable keyboard on %s\n",
+ ps2dev->serio->phys);
+ return -1;
+ }
+
+ return 0;
+}
+
+/*
+ * atkbd_deactivate() resets and disables the keyboard from sending
+ * keystrokes.
+ */
+
+static void atkbd_deactivate(struct atkbd *atkbd)
+{
+ struct ps2dev *ps2dev = &atkbd->ps2dev;
+
+ if (ps2_command(ps2dev, NULL, ATKBD_CMD_RESET_DIS))
+ dev_err(&ps2dev->serio->dev,
+ "Failed to deactivate keyboard on %s\n",
+ ps2dev->serio->phys);
+}
+
/*
* atkbd_probe() probes for an AT keyboard on a serio port.
*/
@@ -726,11 +759,17 @@ static int atkbd_probe(struct atkbd *atkbd)
if (atkbd->id == 0xaca1 && atkbd->translated) {
dev_err(&ps2dev->serio->dev,
- "NCD terminal keyboards are only supported on non-translating controlelrs. "
+ "NCD terminal keyboards are only supported on non-translating controllers. "
"Use i8042.direct=1 to disable translation.\n");
return -1;
}
+/*
+ * Make sure nothing is coming from the keyboard and disturbs our
+ * internal state.
+ */
+ atkbd_deactivate(atkbd);
+
return 0;
}
@@ -825,24 +864,6 @@ static int atkbd_reset_state(struct atkbd *atkbd)
return 0;
}
-static int atkbd_activate(struct atkbd *atkbd)
-{
- struct ps2dev *ps2dev = &atkbd->ps2dev;
-
-/*
- * Enable the keyboard to receive keystrokes.
- */
-
- if (ps2_command(ps2dev, NULL, ATKBD_CMD_ENABLE)) {
- dev_err(&ps2dev->serio->dev,
- "Failed to enable keyboard on %s\n",
- ps2dev->serio->phys);
- return -1;
- }
-
- return 0;
-}
-
/*
* atkbd_cleanup() restores the keyboard state so that BIOS is happy after a
* reboot.
@@ -1150,7 +1171,6 @@ static int atkbd_connect(struct serio *serio, struct serio_driver *drv)
atkbd->set = atkbd_select_set(atkbd, atkbd_set, atkbd_extra);
atkbd_reset_state(atkbd);
- atkbd_activate(atkbd);
} else {
atkbd->set = 2;
@@ -1165,6 +1185,8 @@ static int atkbd_connect(struct serio *serio, struct serio_driver *drv)
goto fail3;
atkbd_enable(atkbd);
+ if (serio->write)
+ atkbd_activate(atkbd);
err = input_register_device(atkbd->dev);
if (err)
@@ -1208,8 +1230,6 @@ static int atkbd_reconnect(struct serio *serio)
if (atkbd->set != atkbd_select_set(atkbd, atkbd->set, atkbd->extra))
goto out;
- atkbd_activate(atkbd);
-
/*
* Restore LED state and repeat rate. While input core
* will do this for us at resume time reconnect may happen
@@ -1223,7 +1243,17 @@ static int atkbd_reconnect(struct serio *serio)
}
+ /*
+ * Reset our state machine in case reconnect happened in the middle
+ * of multi-byte scancode.
+ */
+ atkbd->xl_bit = 0;
+ atkbd->emul = 0;
+
atkbd_enable(atkbd);
+ if (atkbd->write)
+ atkbd_activate(atkbd);
+
retval = 0;
out:
diff --git a/drivers/input/keyboard/goldfish_events.c b/drivers/input/keyboard/goldfish_events.c
new file mode 100644
index 000000000000..9f60a2ec88db
--- /dev/null
+++ b/drivers/input/keyboard/goldfish_events.c
@@ -0,0 +1,194 @@
+/*
+ * Copyright (C) 2007 Google, Inc.
+ * Copyright (C) 2012 Intel, Inc.
+ *
+ * This software is licensed under the terms of the GNU General Public
+ * License version 2, as published by the Free Software Foundation, and
+ * may be copied, distributed, and modified under those terms.
+ *
+ * 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.
+ *
+ */
+
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/interrupt.h>
+#include <linux/types.h>
+#include <linux/input.h>
+#include <linux/kernel.h>
+#include <linux/platform_device.h>
+#include <linux/slab.h>
+#include <linux/irq.h>
+#include <linux/io.h>
+
+enum {
+ REG_READ = 0x00,
+ REG_SET_PAGE = 0x00,
+ REG_LEN = 0x04,
+ REG_DATA = 0x08,
+
+ PAGE_NAME = 0x00000,
+ PAGE_EVBITS = 0x10000,
+ PAGE_ABSDATA = 0x20000 | EV_ABS,
+};
+
+struct event_dev {
+ struct input_dev *input;
+ int irq;
+ void __iomem *addr;
+ char name[0];
+};
+
+static irqreturn_t events_interrupt(int irq, void *dev_id)
+{
+ struct event_dev *edev = dev_id;
+ unsigned type, code, value;
+
+ type = __raw_readl(edev->addr + REG_READ);
+ code = __raw_readl(edev->addr + REG_READ);
+ value = __raw_readl(edev->addr + REG_READ);
+
+ input_event(edev->input, type, code, value);
+ input_sync(edev->input);
+ return IRQ_HANDLED;
+}
+
+static void events_import_bits(struct event_dev *edev,
+ unsigned long bits[], unsigned type, size_t count)
+{
+ void __iomem *addr = edev->addr;
+ int i, j;
+ size_t size;
+ uint8_t val;
+
+ __raw_writel(PAGE_EVBITS | type, addr + REG_SET_PAGE);
+
+ size = __raw_readl(addr + REG_LEN) * 8;
+ if (size < count)
+ count = size;
+
+ addr += REG_DATA;
+ for (i = 0; i < count; i += 8) {
+ val = __raw_readb(addr++);
+ for (j = 0; j < 8; j++)
+ if (val & 1 << j)
+ set_bit(i + j, bits);
+ }
+}
+
+static void events_import_abs_params(struct event_dev *edev)
+{
+ struct input_dev *input_dev = edev->input;
+ void __iomem *addr = edev->addr;
+ u32 val[4];
+ int count;
+ int i, j;
+
+ __raw_writel(PAGE_ABSDATA, addr + REG_SET_PAGE);
+
+ count = __raw_readl(addr + REG_LEN) / sizeof(val);
+ if (count > ABS_MAX)
+ count = ABS_MAX;
+
+ for (i = 0; i < count; i++) {
+ if (!test_bit(i, input_dev->absbit))
+ continue;
+
+ for (j = 0; j < ARRAY_SIZE(val); j++) {
+ int offset = (i * ARRAY_SIZE(val) + j) * sizeof(u32);
+ val[j] = __raw_readl(edev->addr + REG_DATA + offset);
+ }
+
+ input_set_abs_params(input_dev, i,
+ val[0], val[1], val[2], val[3]);
+ }
+}
+
+static int events_probe(struct platform_device *pdev)
+{
+ struct input_dev *input_dev;
+ struct event_dev *edev;
+ struct resource *res;
+ unsigned keymapnamelen;
+ void __iomem *addr;
+ int irq;
+ int i;
+ int error;
+
+ irq = platform_get_irq(pdev, 0);
+ if (irq < 0)
+ return -EINVAL;
+
+ res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+ if (!res)
+ return -EINVAL;
+
+ addr = devm_ioremap(&pdev->dev, res->start, 4096);
+ if (!addr)
+ return -ENOMEM;
+
+ __raw_writel(PAGE_NAME, addr + REG_SET_PAGE);
+ keymapnamelen = __raw_readl(addr + REG_LEN);
+
+ edev = devm_kzalloc(&pdev->dev,
+ sizeof(struct event_dev) + keymapnamelen + 1,
+ GFP_KERNEL);
+ if (!edev)
+ return -ENOMEM;
+
+ input_dev = devm_input_allocate_device(&pdev->dev);
+ if (!input_dev)
+ return -ENOMEM;
+
+ edev->input = input_dev;
+ edev->addr = addr;
+ edev->irq = irq;
+
+ for (i = 0; i < keymapnamelen; i++)
+ edev->name[i] = __raw_readb(edev->addr + REG_DATA + i);
+
+ pr_debug("events_probe() keymap=%s\n", edev->name);
+
+ input_dev->name = edev->name;
+ input_dev->id.bustype = BUS_HOST;
+
+ events_import_bits(edev, input_dev->evbit, EV_SYN, EV_MAX);
+ events_import_bits(edev, input_dev->keybit, EV_KEY, KEY_MAX);
+ events_import_bits(edev, input_dev->relbit, EV_REL, REL_MAX);
+ events_import_bits(edev, input_dev->absbit, EV_ABS, ABS_MAX);
+ events_import_bits(edev, input_dev->mscbit, EV_MSC, MSC_MAX);
+ events_import_bits(edev, input_dev->ledbit, EV_LED, LED_MAX);
+ events_import_bits(edev, input_dev->sndbit, EV_SND, SND_MAX);
+ events_import_bits(edev, input_dev->ffbit, EV_FF, FF_MAX);
+ events_import_bits(edev, input_dev->swbit, EV_SW, SW_MAX);
+
+ events_import_abs_params(edev);
+
+ error = devm_request_irq(&pdev->dev, edev->irq, events_interrupt, 0,
+ "goldfish-events-keypad", edev);
+ if (error)
+ return error;
+
+ error = input_register_device(input_dev);
+ if (error)
+ return error;
+
+ return 0;
+}
+
+static struct platform_driver events_driver = {
+ .probe = events_probe,
+ .driver = {
+ .owner = THIS_MODULE,
+ .name = "goldfish_events",
+ },
+};
+
+module_platform_driver(events_driver);
+
+MODULE_AUTHOR("Brian Swetland");
+MODULE_DESCRIPTION("Goldfish Event Device");
+MODULE_LICENSE("GPL");
diff --git a/drivers/input/keyboard/imx_keypad.c b/drivers/input/keyboard/imx_keypad.c
index 6d150e3e1f55..98f9113251d2 100644
--- a/drivers/input/keyboard/imx_keypad.c
+++ b/drivers/input/keyboard/imx_keypad.c
@@ -20,6 +20,7 @@
#include <linux/jiffies.h>
#include <linux/kernel.h>
#include <linux/module.h>
+#include <linux/of.h>
#include <linux/platform_device.h>
#include <linux/slab.h>
#include <linux/timer.h>
@@ -414,15 +415,23 @@ open_err:
return -EIO;
}
+#ifdef CONFIG_OF
+static struct of_device_id imx_keypad_of_match[] = {
+ { .compatible = "fsl,imx21-kpp", },
+ { /* sentinel */ }
+};
+MODULE_DEVICE_TABLE(of, imx_keypad_of_match);
+#endif
+
static int imx_keypad_probe(struct platform_device *pdev)
{
const struct matrix_keymap_data *keymap_data = pdev->dev.platform_data;
struct imx_keypad *keypad;
struct input_dev *input_dev;
struct resource *res;
- int irq, error, i;
+ int irq, error, i, row, col;
- if (keymap_data == NULL) {
+ if (!keymap_data && !pdev->dev.of_node) {
dev_err(&pdev->dev, "no keymap defined\n");
return -EINVAL;
}
@@ -480,22 +489,6 @@ static int imx_keypad_probe(struct platform_device *pdev)
goto failed_unmap;
}
- /* Search for rows and cols enabled */
- for (i = 0; i < keymap_data->keymap_size; i++) {
- keypad->rows_en_mask |= 1 << KEY_ROW(keymap_data->keymap[i]);
- keypad->cols_en_mask |= 1 << KEY_COL(keymap_data->keymap[i]);
- }
-
- if (keypad->rows_en_mask > ((1 << MAX_MATRIX_KEY_ROWS) - 1) ||
- keypad->cols_en_mask > ((1 << MAX_MATRIX_KEY_COLS) - 1)) {
- dev_err(&pdev->dev,
- "invalid key data (too many rows or colums)\n");
- error = -EINVAL;
- goto failed_clock_put;
- }
- dev_dbg(&pdev->dev, "enabled rows mask: %x\n", keypad->rows_en_mask);
- dev_dbg(&pdev->dev, "enabled cols mask: %x\n", keypad->cols_en_mask);
-
/* Init the Input device */
input_dev->name = pdev->name;
input_dev->id.bustype = BUS_HOST;
@@ -512,6 +505,19 @@ static int imx_keypad_probe(struct platform_device *pdev)
goto failed_clock_put;
}
+ /* Search for rows and cols enabled */
+ for (row = 0; row < MAX_MATRIX_KEY_ROWS; row++) {
+ for (col = 0; col < MAX_MATRIX_KEY_COLS; col++) {
+ i = MATRIX_SCAN_CODE(row, col, MATRIX_ROW_SHIFT);
+ if (keypad->keycodes[i] != KEY_RESERVED) {
+ keypad->rows_en_mask |= 1 << row;
+ keypad->cols_en_mask |= 1 << col;
+ }
+ }
+ }
+ dev_dbg(&pdev->dev, "enabled rows mask: %x\n", keypad->rows_en_mask);
+ dev_dbg(&pdev->dev, "enabled cols mask: %x\n", keypad->cols_en_mask);
+
__set_bit(EV_REP, input_dev->evbit);
input_set_capability(input_dev, EV_MSC, MSC_SCAN);
input_set_drvdata(input_dev, keypad);
@@ -631,6 +637,7 @@ static struct platform_driver imx_keypad_driver = {
.name = "imx-keypad",
.owner = THIS_MODULE,
.pm = &imx_kbd_pm_ops,
+ .of_match_table = of_match_ptr(imx_keypad_of_match),
},
.probe = imx_keypad_probe,
.remove = imx_keypad_remove,
diff --git a/drivers/input/keyboard/qt2160.c b/drivers/input/keyboard/qt2160.c
index 3dc2b0f27b0c..1c0ddad0a1cc 100644
--- a/drivers/input/keyboard/qt2160.c
+++ b/drivers/input/keyboard/qt2160.c
@@ -20,6 +20,7 @@
#include <linux/kernel.h>
#include <linux/init.h>
+#include <linux/leds.h>
#include <linux/module.h>
#include <linux/slab.h>
#include <linux/jiffies.h>
@@ -39,6 +40,11 @@
#define QT2160_CMD_GPIOS 6
#define QT2160_CMD_SUBVER 7
#define QT2160_CMD_CALIBRATE 10
+#define QT2160_CMD_DRIVE_X 70
+#define QT2160_CMD_PWMEN_X 74
+#define QT2160_CMD_PWM_DUTY 76
+
+#define QT2160_NUM_LEDS_X 8
#define QT2160_CYCLE_INTERVAL (2*HZ)
@@ -49,6 +55,17 @@ static unsigned char qt2160_key2code[] = {
KEY_C, KEY_D, KEY_E, KEY_F,
};
+#ifdef CONFIG_LEDS_CLASS
+struct qt2160_led {
+ struct qt2160_data *qt2160;
+ struct led_classdev cdev;
+ struct work_struct work;
+ char name[32];
+ int id;
+ enum led_brightness new_brightness;
+};
+#endif
+
struct qt2160_data {
struct i2c_client *client;
struct input_dev *input;
@@ -56,8 +73,61 @@ struct qt2160_data {
spinlock_t lock; /* Protects canceling/rescheduling of dwork */
unsigned short keycodes[ARRAY_SIZE(qt2160_key2code)];
u16 key_matrix;
+#ifdef CONFIG_LEDS_CLASS
+ struct qt2160_led leds[QT2160_NUM_LEDS_X];
+ struct mutex led_lock;
+#endif
};
+static int qt2160_read(struct i2c_client *client, u8 reg);
+static int qt2160_write(struct i2c_client *client, u8 reg, u8 data);
+
+#ifdef CONFIG_LEDS_CLASS
+
+static void qt2160_led_work(struct work_struct *work)
+{
+ struct qt2160_led *led = container_of(work, struct qt2160_led, work);
+ struct qt2160_data *qt2160 = led->qt2160;
+ struct i2c_client *client = qt2160->client;
+ int value = led->new_brightness;
+ u32 drive, pwmen;
+
+ mutex_lock(&qt2160->led_lock);
+
+ drive = qt2160_read(client, QT2160_CMD_DRIVE_X);
+ pwmen = qt2160_read(client, QT2160_CMD_PWMEN_X);
+ if (value != LED_OFF) {
+ drive |= (1 << led->id);
+ pwmen |= (1 << led->id);
+
+ } else {
+ drive &= ~(1 << led->id);
+ pwmen &= ~(1 << led->id);
+ }
+ qt2160_write(client, QT2160_CMD_DRIVE_X, drive);
+ qt2160_write(client, QT2160_CMD_PWMEN_X, pwmen);
+
+ /*
+ * Changing this register will change the brightness
+ * of every LED in the qt2160. It's a HW limitation.
+ */
+ if (value != LED_OFF)
+ qt2160_write(client, QT2160_CMD_PWM_DUTY, value);
+
+ mutex_unlock(&qt2160->led_lock);
+}
+
+static void qt2160_led_set(struct led_classdev *cdev,
+ enum led_brightness value)
+{
+ struct qt2160_led *led = container_of(cdev, struct qt2160_led, cdev);
+
+ led->new_brightness = value;
+ schedule_work(&led->work);
+}
+
+#endif /* CONFIG_LEDS_CLASS */
+
static int qt2160_read_block(struct i2c_client *client,
u8 inireg, u8 *buffer, unsigned int count)
{
@@ -216,6 +286,63 @@ static int qt2160_write(struct i2c_client *client, u8 reg, u8 data)
return ret;
}
+#ifdef CONFIG_LEDS_CLASS
+
+static int qt2160_register_leds(struct qt2160_data *qt2160)
+{
+ struct i2c_client *client = qt2160->client;
+ int ret;
+ int i;
+
+ mutex_init(&qt2160->led_lock);
+
+ for (i = 0; i < QT2160_NUM_LEDS_X; i++) {
+ struct qt2160_led *led = &qt2160->leds[i];
+
+ snprintf(led->name, sizeof(led->name), "qt2160:x%d", i);
+ led->cdev.name = led->name;
+ led->cdev.brightness_set = qt2160_led_set;
+ led->cdev.brightness = LED_OFF;
+ led->id = i;
+ led->qt2160 = qt2160;
+
+ INIT_WORK(&led->work, qt2160_led_work);
+
+ ret = led_classdev_register(&client->dev, &led->cdev);
+ if (ret < 0)
+ return ret;
+ }
+
+ /* Tur off LEDs */
+ qt2160_write(client, QT2160_CMD_DRIVE_X, 0);
+ qt2160_write(client, QT2160_CMD_PWMEN_X, 0);
+ qt2160_write(client, QT2160_CMD_PWM_DUTY, 0);
+
+ return 0;
+}
+
+static void qt2160_unregister_leds(struct qt2160_data *qt2160)
+{
+ int i;
+
+ for (i = 0; i < QT2160_NUM_LEDS_X; i++) {
+ led_classdev_unregister(&qt2160->leds[i].cdev);
+ cancel_work_sync(&qt2160->leds[i].work);
+ }
+}
+
+#else
+
+static inline int qt2160_register_leds(struct qt2160_data *qt2160)
+{
+ return 0;
+}
+
+static inline void qt2160_unregister_leds(struct qt2160_data *qt2160)
+{
+}
+
+#endif
static bool qt2160_identify(struct i2c_client *client)
{
@@ -249,7 +376,7 @@ static bool qt2160_identify(struct i2c_client *client)
}
static int qt2160_probe(struct i2c_client *client,
- const struct i2c_device_id *id)
+ const struct i2c_device_id *id)
{
struct qt2160_data *qt2160;
struct input_dev *input;
@@ -314,11 +441,17 @@ static int qt2160_probe(struct i2c_client *client,
}
}
+ error = qt2160_register_leds(qt2160);
+ if (error) {
+ dev_err(&client->dev, "Failed to register leds\n");
+ goto err_free_irq;
+ }
+
error = input_register_device(qt2160->input);
if (error) {
dev_err(&client->dev,
"Failed to register input device\n");
- goto err_free_irq;
+ goto err_unregister_leds;
}
i2c_set_clientdata(client, qt2160);
@@ -326,6 +459,8 @@ static int qt2160_probe(struct i2c_client *client,
return 0;
+err_unregister_leds:
+ qt2160_unregister_leds(qt2160);
err_free_irq:
if (client->irq)
free_irq(client->irq, qt2160);
@@ -339,6 +474,8 @@ static int qt2160_remove(struct i2c_client *client)
{
struct qt2160_data *qt2160 = i2c_get_clientdata(client);
+ qt2160_unregister_leds(qt2160);
+
/* Release IRQ so no queue will be scheduled */
if (client->irq)
free_irq(client->irq, qt2160);
diff --git a/drivers/input/keyboard/tegra-kbc.c b/drivers/input/keyboard/tegra-kbc.c
index c76f96872d31..d89e7d392d1e 100644
--- a/drivers/input/keyboard/tegra-kbc.c
+++ b/drivers/input/keyboard/tegra-kbc.c
@@ -29,9 +29,16 @@
#include <linux/of.h>
#include <linux/clk.h>
#include <linux/slab.h>
-#include <linux/input/tegra_kbc.h>
+#include <linux/input/matrix_keypad.h>
#include <mach/clk.h>
+#define KBC_MAX_GPIO 24
+#define KBC_MAX_KPENT 8
+
+#define KBC_MAX_ROW 16
+#define KBC_MAX_COL 8
+#define KBC_MAX_KEY (KBC_MAX_ROW * KBC_MAX_COL)
+
#define KBC_MAX_DEBOUNCE_CNT 0x3ffu
/* KBC row scan time and delay for beginning the row scan. */
@@ -67,10 +74,27 @@
#define KBC_ROW_SHIFT 3
+enum tegra_pin_type {
+ PIN_CFG_IGNORE,
+ PIN_CFG_COL,
+ PIN_CFG_ROW,
+};
+
+struct tegra_kbc_pin_cfg {
+ enum tegra_pin_type type;
+ unsigned char num;
+};
+
struct tegra_kbc {
+ struct device *dev;
+ unsigned int debounce_cnt;
+ unsigned int repeat_cnt;
+ struct tegra_kbc_pin_cfg pin_cfg[KBC_MAX_GPIO];
+ const struct matrix_keymap_data *keymap_data;
+ bool wakeup;
void __iomem *mmio;
struct input_dev *idev;
- unsigned int irq;
+ int irq;
spinlock_t lock;
unsigned int repoll_dly;
unsigned long cp_dly_jiffies;
@@ -78,7 +102,6 @@ struct tegra_kbc {
bool use_fn_map;
bool use_ghost_filter;
bool keypress_caused_wake;
- const struct tegra_kbc_platform_data *pdata;
unsigned short keycode[KBC_MAX_KEY * 2];
unsigned short current_keys[KBC_MAX_KPENT];
unsigned int num_pressed_keys;
@@ -87,147 +110,6 @@ struct tegra_kbc {
struct clk *clk;
};
-static const u32 tegra_kbc_default_keymap[] = {
- KEY(0, 2, KEY_W),
- KEY(0, 3, KEY_S),
- KEY(0, 4, KEY_A),
- KEY(0, 5, KEY_Z),
- KEY(0, 7, KEY_FN),
-
- KEY(1, 7, KEY_LEFTMETA),
-
- KEY(2, 6, KEY_RIGHTALT),
- KEY(2, 7, KEY_LEFTALT),
-
- KEY(3, 0, KEY_5),
- KEY(3, 1, KEY_4),
- KEY(3, 2, KEY_R),
- KEY(3, 3, KEY_E),
- KEY(3, 4, KEY_F),
- KEY(3, 5, KEY_D),
- KEY(3, 6, KEY_X),
-
- KEY(4, 0, KEY_7),
- KEY(4, 1, KEY_6),
- KEY(4, 2, KEY_T),
- KEY(4, 3, KEY_H),
- KEY(4, 4, KEY_G),
- KEY(4, 5, KEY_V),
- KEY(4, 6, KEY_C),
- KEY(4, 7, KEY_SPACE),
-
- KEY(5, 0, KEY_9),
- KEY(5, 1, KEY_8),
- KEY(5, 2, KEY_U),
- KEY(5, 3, KEY_Y),
- KEY(5, 4, KEY_J),
- KEY(5, 5, KEY_N),
- KEY(5, 6, KEY_B),
- KEY(5, 7, KEY_BACKSLASH),
-
- KEY(6, 0, KEY_MINUS),
- KEY(6, 1, KEY_0),
- KEY(6, 2, KEY_O),
- KEY(6, 3, KEY_I),
- KEY(6, 4, KEY_L),
- KEY(6, 5, KEY_K),
- KEY(6, 6, KEY_COMMA),
- KEY(6, 7, KEY_M),
-
- KEY(7, 1, KEY_EQUAL),
- KEY(7, 2, KEY_RIGHTBRACE),
- KEY(7, 3, KEY_ENTER),
- KEY(7, 7, KEY_MENU),
-
- KEY(8, 4, KEY_RIGHTSHIFT),
- KEY(8, 5, KEY_LEFTSHIFT),
-
- KEY(9, 5, KEY_RIGHTCTRL),
- KEY(9, 7, KEY_LEFTCTRL),
-
- KEY(11, 0, KEY_LEFTBRACE),
- KEY(11, 1, KEY_P),
- KEY(11, 2, KEY_APOSTROPHE),
- KEY(11, 3, KEY_SEMICOLON),
- KEY(11, 4, KEY_SLASH),
- KEY(11, 5, KEY_DOT),
-
- KEY(12, 0, KEY_F10),
- KEY(12, 1, KEY_F9),
- KEY(12, 2, KEY_BACKSPACE),
- KEY(12, 3, KEY_3),
- KEY(12, 4, KEY_2),
- KEY(12, 5, KEY_UP),
- KEY(12, 6, KEY_PRINT),
- KEY(12, 7, KEY_PAUSE),
-
- KEY(13, 0, KEY_INSERT),
- KEY(13, 1, KEY_DELETE),
- KEY(13, 3, KEY_PAGEUP),
- KEY(13, 4, KEY_PAGEDOWN),
- KEY(13, 5, KEY_RIGHT),
- KEY(13, 6, KEY_DOWN),
- KEY(13, 7, KEY_LEFT),
-
- KEY(14, 0, KEY_F11),
- KEY(14, 1, KEY_F12),
- KEY(14, 2, KEY_F8),
- KEY(14, 3, KEY_Q),
- KEY(14, 4, KEY_F4),
- KEY(14, 5, KEY_F3),
- KEY(14, 6, KEY_1),
- KEY(14, 7, KEY_F7),
-
- KEY(15, 0, KEY_ESC),
- KEY(15, 1, KEY_GRAVE),
- KEY(15, 2, KEY_F5),
- KEY(15, 3, KEY_TAB),
- KEY(15, 4, KEY_F1),
- KEY(15, 5, KEY_F2),
- KEY(15, 6, KEY_CAPSLOCK),
- KEY(15, 7, KEY_F6),
-
- /* Software Handled Function Keys */
- KEY(20, 0, KEY_KP7),
-
- KEY(21, 0, KEY_KP9),
- KEY(21, 1, KEY_KP8),
- KEY(21, 2, KEY_KP4),
- KEY(21, 4, KEY_KP1),
-
- KEY(22, 1, KEY_KPSLASH),
- KEY(22, 2, KEY_KP6),
- KEY(22, 3, KEY_KP5),
- KEY(22, 4, KEY_KP3),
- KEY(22, 5, KEY_KP2),
- KEY(22, 7, KEY_KP0),
-
- KEY(27, 1, KEY_KPASTERISK),
- KEY(27, 3, KEY_KPMINUS),
- KEY(27, 4, KEY_KPPLUS),
- KEY(27, 5, KEY_KPDOT),
-
- KEY(28, 5, KEY_VOLUMEUP),
-
- KEY(29, 3, KEY_HOME),
- KEY(29, 4, KEY_END),
- KEY(29, 5, KEY_BRIGHTNESSDOWN),
- KEY(29, 6, KEY_VOLUMEDOWN),
- KEY(29, 7, KEY_BRIGHTNESSUP),
-
- KEY(30, 0, KEY_NUMLOCK),
- KEY(30, 1, KEY_SCROLLLOCK),
- KEY(30, 2, KEY_MUTE),
-
- KEY(31, 4, KEY_HELP),
-};
-
-static const
-struct matrix_keymap_data tegra_kbc_default_keymap_data = {
- .keymap = tegra_kbc_default_keymap,
- .keymap_size = ARRAY_SIZE(tegra_kbc_default_keymap),
-};
-
static void tegra_kbc_report_released_keys(struct input_dev *input,
unsigned short old_keycodes[],
unsigned int old_num_keys,
@@ -357,18 +239,6 @@ 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;
@@ -439,12 +309,11 @@ static irqreturn_t tegra_kbc_isr(int irq, void *args)
static void tegra_kbc_setup_wakekeys(struct tegra_kbc *kbc, bool filter)
{
- const struct tegra_kbc_platform_data *pdata = kbc->pdata;
int i;
unsigned int rst_val;
/* Either mask all keys or none. */
- rst_val = (filter && !pdata->wakeup) ? ~0 : 0;
+ rst_val = (filter && !kbc->wakeup) ? ~0 : 0;
for (i = 0; i < KBC_MAX_ROW; i++)
writel(rst_val, kbc->mmio + KBC_ROW0_MASK_0 + i * 4);
@@ -452,7 +321,6 @@ static void tegra_kbc_setup_wakekeys(struct tegra_kbc *kbc, bool filter)
static void tegra_kbc_config_pins(struct tegra_kbc *kbc)
{
- const struct tegra_kbc_platform_data *pdata = kbc->pdata;
int i;
for (i = 0; i < KBC_MAX_GPIO; i++) {
@@ -468,13 +336,13 @@ static void tegra_kbc_config_pins(struct tegra_kbc *kbc)
row_cfg &= ~r_mask;
col_cfg &= ~c_mask;
- switch (pdata->pin_cfg[i].type) {
+ switch (kbc->pin_cfg[i].type) {
case PIN_CFG_ROW:
- row_cfg |= ((pdata->pin_cfg[i].num << 1) | 1) << r_shft;
+ row_cfg |= ((kbc->pin_cfg[i].num << 1) | 1) << r_shft;
break;
case PIN_CFG_COL:
- col_cfg |= ((pdata->pin_cfg[i].num << 1) | 1) << c_shft;
+ col_cfg |= ((kbc->pin_cfg[i].num << 1) | 1) << c_shft;
break;
case PIN_CFG_IGNORE:
@@ -488,7 +356,6 @@ static void tegra_kbc_config_pins(struct tegra_kbc *kbc)
static int tegra_kbc_start(struct tegra_kbc *kbc)
{
- const struct tegra_kbc_platform_data *pdata = kbc->pdata;
unsigned int debounce_cnt;
u32 val = 0;
@@ -503,10 +370,10 @@ static int tegra_kbc_start(struct tegra_kbc *kbc)
tegra_kbc_config_pins(kbc);
tegra_kbc_setup_wakekeys(kbc, false);
- writel(pdata->repeat_cnt, kbc->mmio + KBC_RPT_DLY_0);
+ writel(kbc->repeat_cnt, kbc->mmio + KBC_RPT_DLY_0);
/* Keyboard debounce count is maximum of 12 bits. */
- debounce_cnt = min(pdata->debounce_cnt, KBC_MAX_DEBOUNCE_CNT);
+ debounce_cnt = min(kbc->debounce_cnt, KBC_MAX_DEBOUNCE_CNT);
val = KBC_DEBOUNCE_CNT_SHIFT(debounce_cnt);
val |= KBC_FIFO_TH_CNT_SHIFT(1); /* set fifo interrupt threshold to 1 */
val |= KBC_CONTROL_FIFO_CNT_INT_EN; /* interrupt on FIFO threshold */
@@ -573,21 +440,20 @@ static void tegra_kbc_close(struct input_dev *dev)
return tegra_kbc_stop(kbc);
}
-static bool
-tegra_kbc_check_pin_cfg(const struct tegra_kbc_platform_data *pdata,
- struct device *dev, unsigned int *num_rows)
+static bool tegra_kbc_check_pin_cfg(const struct tegra_kbc *kbc,
+ unsigned int *num_rows)
{
int i;
*num_rows = 0;
for (i = 0; i < KBC_MAX_GPIO; i++) {
- const struct tegra_kbc_pin_cfg *pin_cfg = &pdata->pin_cfg[i];
+ const struct tegra_kbc_pin_cfg *pin_cfg = &kbc->pin_cfg[i];
switch (pin_cfg->type) {
case PIN_CFG_ROW:
if (pin_cfg->num >= KBC_MAX_ROW) {
- dev_err(dev,
+ dev_err(kbc->dev,
"pin_cfg[%d]: invalid row number %d\n",
i, pin_cfg->num);
return false;
@@ -597,7 +463,7 @@ tegra_kbc_check_pin_cfg(const struct tegra_kbc_platform_data *pdata,
case PIN_CFG_COL:
if (pin_cfg->num >= KBC_MAX_COL) {
- dev_err(dev,
+ dev_err(kbc->dev,
"pin_cfg[%d]: invalid column number %d\n",
i, pin_cfg->num);
return false;
@@ -608,7 +474,7 @@ tegra_kbc_check_pin_cfg(const struct tegra_kbc_platform_data *pdata,
break;
default:
- dev_err(dev,
+ dev_err(kbc->dev,
"pin_cfg[%d]: invalid entry type %d\n",
pin_cfg->type, pin_cfg->num);
return false;
@@ -618,154 +484,140 @@ tegra_kbc_check_pin_cfg(const struct tegra_kbc_platform_data *pdata,
return true;
}
-#ifdef CONFIG_OF
-static struct tegra_kbc_platform_data *tegra_kbc_dt_parse_pdata(
- struct platform_device *pdev)
+static int tegra_kbc_parse_dt(struct tegra_kbc *kbc)
{
- struct tegra_kbc_platform_data *pdata;
- struct device_node *np = pdev->dev.of_node;
+ struct device_node *np = kbc->dev->of_node;
u32 prop;
int i;
-
- if (!np)
- return NULL;
-
- pdata = kzalloc(sizeof(*pdata), GFP_KERNEL);
- if (!pdata)
- return NULL;
+ u32 num_rows = 0;
+ u32 num_cols = 0;
+ u32 cols_cfg[KBC_MAX_GPIO];
+ u32 rows_cfg[KBC_MAX_GPIO];
+ int proplen;
+ int ret;
if (!of_property_read_u32(np, "nvidia,debounce-delay-ms", &prop))
- pdata->debounce_cnt = prop;
+ kbc->debounce_cnt = prop;
if (!of_property_read_u32(np, "nvidia,repeat-delay-ms", &prop))
- pdata->repeat_cnt = prop;
+ kbc->repeat_cnt = prop;
if (of_find_property(np, "nvidia,needs-ghost-filter", NULL))
- pdata->use_ghost_filter = true;
+ kbc->use_ghost_filter = true;
if (of_find_property(np, "nvidia,wakeup-source", NULL))
- pdata->wakeup = true;
+ kbc->wakeup = true;
- /*
- * All currently known keymaps with device tree support use the same
- * pin_cfg, so set it up here.
- */
- for (i = 0; i < KBC_MAX_ROW; i++) {
- pdata->pin_cfg[i].num = i;
- pdata->pin_cfg[i].type = PIN_CFG_ROW;
+ if (!of_get_property(np, "nvidia,kbc-row-pins", &proplen)) {
+ dev_err(kbc->dev, "property nvidia,kbc-row-pins not found\n");
+ return -ENOENT;
}
+ num_rows = proplen / sizeof(u32);
- for (i = 0; i < KBC_MAX_COL; i++) {
- pdata->pin_cfg[KBC_MAX_ROW + i].num = i;
- pdata->pin_cfg[KBC_MAX_ROW + i].type = PIN_CFG_COL;
+ if (!of_get_property(np, "nvidia,kbc-col-pins", &proplen)) {
+ dev_err(kbc->dev, "property nvidia,kbc-col-pins not found\n");
+ return -ENOENT;
}
+ num_cols = proplen / sizeof(u32);
- return pdata;
-}
-#else
-static inline struct tegra_kbc_platform_data *tegra_kbc_dt_parse_pdata(
- struct platform_device *pdev)
-{
- return NULL;
-}
-#endif
+ if (!of_get_property(np, "linux,keymap", &proplen)) {
+ dev_err(kbc->dev, "property linux,keymap not found\n");
+ return -ENOENT;
+ }
-static int tegra_kbd_setup_keymap(struct tegra_kbc *kbc)
-{
- const struct tegra_kbc_platform_data *pdata = kbc->pdata;
- const struct matrix_keymap_data *keymap_data = pdata->keymap_data;
- unsigned int keymap_rows = KBC_MAX_KEY;
- int retval;
+ if (!num_rows || !num_cols || ((num_rows + num_cols) > KBC_MAX_GPIO)) {
+ dev_err(kbc->dev,
+ "keypad rows/columns not porperly specified\n");
+ return -EINVAL;
+ }
- if (keymap_data && pdata->use_fn_map)
- keymap_rows *= 2;
+ /* Set all pins as non-configured */
+ for (i = 0; i < KBC_MAX_GPIO; i++)
+ kbc->pin_cfg[i].type = PIN_CFG_IGNORE;
- retval = matrix_keypad_build_keymap(keymap_data, NULL,
- keymap_rows, KBC_MAX_COL,
- kbc->keycode, kbc->idev);
- if (retval == -ENOSYS || retval == -ENOENT) {
- /*
- * If there is no OF support in kernel or keymap
- * property is missing, use default keymap.
- */
- retval = matrix_keypad_build_keymap(
- &tegra_kbc_default_keymap_data, NULL,
- keymap_rows, KBC_MAX_COL,
- kbc->keycode, kbc->idev);
+ ret = of_property_read_u32_array(np, "nvidia,kbc-row-pins",
+ rows_cfg, num_rows);
+ if (ret < 0) {
+ dev_err(kbc->dev, "Rows configurations are not proper\n");
+ return -EINVAL;
+ }
+
+ ret = of_property_read_u32_array(np, "nvidia,kbc-col-pins",
+ cols_cfg, num_cols);
+ if (ret < 0) {
+ dev_err(kbc->dev, "Cols configurations are not proper\n");
+ return -EINVAL;
+ }
+
+ for (i = 0; i < num_rows; i++) {
+ kbc->pin_cfg[rows_cfg[i]].type = PIN_CFG_ROW;
+ kbc->pin_cfg[rows_cfg[i]].num = i;
}
- return retval;
+ for (i = 0; i < num_cols; i++) {
+ kbc->pin_cfg[cols_cfg[i]].type = PIN_CFG_COL;
+ kbc->pin_cfg[cols_cfg[i]].num = i;
+ }
+
+ return 0;
}
static int tegra_kbc_probe(struct platform_device *pdev)
{
- const struct tegra_kbc_platform_data *pdata = pdev->dev.platform_data;
struct tegra_kbc *kbc;
- struct input_dev *input_dev;
struct resource *res;
- int irq;
int err;
int num_rows = 0;
unsigned int debounce_cnt;
unsigned int scan_time_rows;
+ unsigned int keymap_rows = KBC_MAX_KEY;
- if (!pdata)
- pdata = tegra_kbc_dt_parse_pdata(pdev);
+ kbc = devm_kzalloc(&pdev->dev, sizeof(*kbc), GFP_KERNEL);
+ if (!kbc) {
+ dev_err(&pdev->dev, "failed to alloc memory for kbc\n");
+ return -ENOMEM;
+ }
- if (!pdata)
- return -EINVAL;
+ kbc->dev = &pdev->dev;
+ spin_lock_init(&kbc->lock);
- if (!tegra_kbc_check_pin_cfg(pdata, &pdev->dev, &num_rows)) {
- err = -EINVAL;
- goto err_free_pdata;
- }
+ err = tegra_kbc_parse_dt(kbc);
+ if (err)
+ return err;
+
+ 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");
- err = -ENXIO;
- goto err_free_pdata;
+ return -ENXIO;
}
- irq = platform_get_irq(pdev, 0);
- if (irq < 0) {
+ kbc->irq = platform_get_irq(pdev, 0);
+ if (kbc->irq < 0) {
dev_err(&pdev->dev, "failed to get keyboard IRQ\n");
- err = -ENXIO;
- goto err_free_pdata;
+ return -ENXIO;
}
- kbc = kzalloc(sizeof(*kbc), GFP_KERNEL);
- input_dev = input_allocate_device();
- if (!kbc || !input_dev) {
- err = -ENOMEM;
- goto err_free_mem;
+ kbc->idev = devm_input_allocate_device(&pdev->dev);
+ if (!kbc->idev) {
+ dev_err(&pdev->dev, "failed to allocate input device\n");
+ return -ENOMEM;
}
- kbc->pdata = pdata;
- kbc->idev = input_dev;
- kbc->irq = irq;
- spin_lock_init(&kbc->lock);
setup_timer(&kbc->timer, tegra_kbc_keypress_timer, (unsigned long)kbc);
- 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 err_free_mem;
- }
-
- kbc->mmio = ioremap(res->start, resource_size(res));
+ kbc->mmio = devm_request_and_ioremap(&pdev->dev, res);
if (!kbc->mmio) {
- dev_err(&pdev->dev, "failed to remap I/O memory\n");
- err = -ENXIO;
- goto err_free_mem_region;
+ dev_err(&pdev->dev, "Cannot request memregion/iomap address\n");
+ return -EBUSY;
}
- kbc->clk = clk_get(&pdev->dev, NULL);
+ kbc->clk = devm_clk_get(&pdev->dev, NULL);
if (IS_ERR(kbc->clk)) {
dev_err(&pdev->dev, "failed to get keyboard clock\n");
- err = PTR_ERR(kbc->clk);
- goto err_iounmap;
+ return PTR_ERR(kbc->clk);
}
/*
@@ -774,37 +626,38 @@ static int tegra_kbc_probe(struct platform_device *pdev)
* the rows. There is an additional delay before the row scanning
* starts. The repoll delay is computed in milliseconds.
*/
- debounce_cnt = min(pdata->debounce_cnt, KBC_MAX_DEBOUNCE_CNT);
+ debounce_cnt = min(kbc->debounce_cnt, KBC_MAX_DEBOUNCE_CNT);
scan_time_rows = (KBC_ROW_SCAN_TIME + debounce_cnt) * num_rows;
- kbc->repoll_dly = KBC_ROW_SCAN_DLY + scan_time_rows + pdata->repeat_cnt;
+ kbc->repoll_dly = KBC_ROW_SCAN_DLY + scan_time_rows + kbc->repeat_cnt;
kbc->repoll_dly = DIV_ROUND_UP(kbc->repoll_dly, KBC_CYCLE_MS);
- kbc->wakeup_key = pdata->wakeup_key;
- kbc->use_fn_map = pdata->use_fn_map;
- kbc->use_ghost_filter = pdata->use_ghost_filter;
+ kbc->idev->name = pdev->name;
+ kbc->idev->id.bustype = BUS_HOST;
+ kbc->idev->dev.parent = &pdev->dev;
+ kbc->idev->open = tegra_kbc_open;
+ kbc->idev->close = tegra_kbc_close;
- input_dev->name = pdev->name;
- input_dev->id.bustype = BUS_HOST;
- input_dev->dev.parent = &pdev->dev;
- input_dev->open = tegra_kbc_open;
- input_dev->close = tegra_kbc_close;
+ if (kbc->keymap_data && kbc->use_fn_map)
+ keymap_rows *= 2;
- err = tegra_kbd_setup_keymap(kbc);
+ err = matrix_keypad_build_keymap(kbc->keymap_data, NULL,
+ keymap_rows, KBC_MAX_COL,
+ kbc->keycode, kbc->idev);
if (err) {
dev_err(&pdev->dev, "failed to setup keymap\n");
- goto err_put_clk;
+ return err;
}
- __set_bit(EV_REP, input_dev->evbit);
- input_set_capability(input_dev, EV_MSC, MSC_SCAN);
+ __set_bit(EV_REP, kbc->idev->evbit);
+ input_set_capability(kbc->idev, EV_MSC, MSC_SCAN);
- input_set_drvdata(input_dev, kbc);
+ input_set_drvdata(kbc->idev, kbc);
- err = request_irq(kbc->irq, tegra_kbc_isr,
+ err = devm_request_irq(&pdev->dev, kbc->irq, tegra_kbc_isr,
IRQF_NO_SUSPEND | IRQF_TRIGGER_HIGH, pdev->name, kbc);
if (err) {
dev_err(&pdev->dev, "failed to request keyboard IRQ\n");
- goto err_put_clk;
+ return err;
}
disable_irq(kbc->irq);
@@ -812,60 +665,28 @@ static int tegra_kbc_probe(struct platform_device *pdev)
err = input_register_device(kbc->idev);
if (err) {
dev_err(&pdev->dev, "failed to register input device\n");
- goto err_free_irq;
+ return err;
}
platform_set_drvdata(pdev, kbc);
- device_init_wakeup(&pdev->dev, pdata->wakeup);
+ device_init_wakeup(&pdev->dev, kbc->wakeup);
return 0;
-
-err_free_irq:
- free_irq(kbc->irq, pdev);
-err_put_clk:
- clk_put(kbc->clk);
-err_iounmap:
- iounmap(kbc->mmio);
-err_free_mem_region:
- release_mem_region(res->start, resource_size(res));
-err_free_mem:
- input_free_device(input_dev);
- kfree(kbc);
-err_free_pdata:
- if (!pdev->dev.platform_data)
- kfree(pdata);
-
- return err;
}
-static int tegra_kbc_remove(struct platform_device *pdev)
+#ifdef CONFIG_PM_SLEEP
+static void tegra_kbc_set_keypress_interrupt(struct tegra_kbc *kbc, bool enable)
{
- struct tegra_kbc *kbc = platform_get_drvdata(pdev);
- struct resource *res;
-
- platform_set_drvdata(pdev, NULL);
-
- free_irq(kbc->irq, pdev);
- clk_put(kbc->clk);
-
- input_unregister_device(kbc->idev);
- iounmap(kbc->mmio);
- res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
- release_mem_region(res->start, resource_size(res));
-
- /*
- * If we do not have platform data attached to the device we
- * allocated it ourselves and thus need to free it.
- */
- if (!pdev->dev.platform_data)
- kfree(kbc->pdata);
-
- kfree(kbc);
+ u32 val;
- return 0;
+ 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);
}
-#ifdef CONFIG_PM_SLEEP
static int tegra_kbc_suspend(struct device *dev)
{
struct platform_device *pdev = to_platform_device(dev);
@@ -954,7 +775,6 @@ MODULE_DEVICE_TABLE(of, tegra_kbc_of_match);
static struct platform_driver tegra_kbc_driver = {
.probe = tegra_kbc_probe,
- .remove = tegra_kbc_remove,
.driver = {
.name = "tegra-kbc",
.owner = THIS_MODULE,
diff --git a/drivers/input/misc/adxl34x.c b/drivers/input/misc/adxl34x.c
index 1cf72fe513e6..0735de3a6468 100644
--- a/drivers/input/misc/adxl34x.c
+++ b/drivers/input/misc/adxl34x.c
@@ -232,7 +232,7 @@ static const struct adxl34x_platform_data adxl34x_default_init = {
.ev_code_tap = {BTN_TOUCH, BTN_TOUCH, BTN_TOUCH}, /* EV_KEY {x,y,z} */
.power_mode = ADXL_AUTO_SLEEP | ADXL_LINK,
- .fifo_mode = FIFO_STREAM,
+ .fifo_mode = ADXL_FIFO_STREAM,
.watermark = 0,
};
@@ -732,7 +732,7 @@ struct adxl34x *adxl34x_probe(struct device *dev, int irq,
mutex_init(&ac->mutex);
input_dev->name = "ADXL34x accelerometer";
- revid = ac->bops->read(dev, DEVID);
+ revid = AC_READ(ac, DEVID);
switch (revid) {
case ID_ADXL345:
@@ -809,7 +809,7 @@ struct adxl34x *adxl34x_probe(struct device *dev, int irq,
if (FIFO_MODE(pdata->fifo_mode) == FIFO_BYPASS)
ac->fifo_delay = false;
- ac->bops->write(dev, POWER_CTL, 0);
+ AC_WRITE(ac, POWER_CTL, 0);
err = request_threaded_irq(ac->irq, NULL, adxl34x_irq,
IRQF_TRIGGER_HIGH | IRQF_ONESHOT,
@@ -827,7 +827,6 @@ struct adxl34x *adxl34x_probe(struct device *dev, int irq,
if (err)
goto err_remove_attr;
- AC_WRITE(ac, THRESH_TAP, pdata->tap_threshold);
AC_WRITE(ac, OFSX, pdata->x_axis_offset);
ac->hwcal.x = pdata->x_axis_offset;
AC_WRITE(ac, OFSY, pdata->y_axis_offset);
diff --git a/drivers/input/misc/bma150.c b/drivers/input/misc/bma150.c
index 08ffcabd7220..865c2f9d25b9 100644
--- a/drivers/input/misc/bma150.c
+++ b/drivers/input/misc/bma150.c
@@ -46,18 +46,6 @@
#define BMA150_POLL_MAX 200
#define BMA150_POLL_MIN 0
-#define BMA150_BW_25HZ 0
-#define BMA150_BW_50HZ 1
-#define BMA150_BW_100HZ 2
-#define BMA150_BW_190HZ 3
-#define BMA150_BW_375HZ 4
-#define BMA150_BW_750HZ 5
-#define BMA150_BW_1500HZ 6
-
-#define BMA150_RANGE_2G 0
-#define BMA150_RANGE_4G 1
-#define BMA150_RANGE_8G 2
-
#define BMA150_MODE_NORMAL 0
#define BMA150_MODE_SLEEP 2
#define BMA150_MODE_WAKE_UP 3
@@ -372,7 +360,7 @@ static int bma150_open(struct bma150_data *bma150)
int error;
error = pm_runtime_get_sync(&bma150->client->dev);
- if (error && error != -ENOSYS)
+ if (error < 0 && error != -ENOSYS)
return error;
/*
diff --git a/drivers/input/misc/twl4030-vibra.c b/drivers/input/misc/twl4030-vibra.c
index 78eb6b30580a..68a5f33152a8 100644
--- a/drivers/input/misc/twl4030-vibra.c
+++ b/drivers/input/misc/twl4030-vibra.c
@@ -43,7 +43,6 @@ struct vibra_info {
struct device *dev;
struct input_dev *input_dev;
- struct workqueue_struct *workqueue;
struct work_struct play_work;
bool enabled;
@@ -143,19 +142,7 @@ static int vibra_play(struct input_dev *input, void *data,
if (!info->speed)
info->speed = effect->u.rumble.weak_magnitude >> 9;
info->direction = effect->direction < EFFECT_DIR_180_DEG ? 0 : 1;
- queue_work(info->workqueue, &info->play_work);
- return 0;
-}
-
-static int twl4030_vibra_open(struct input_dev *input)
-{
- struct vibra_info *info = input_get_drvdata(input);
-
- info->workqueue = create_singlethread_workqueue("vibra");
- if (info->workqueue == NULL) {
- dev_err(&input->dev, "couldn't create workqueue\n");
- return -ENOMEM;
- }
+ schedule_work(&info->play_work);
return 0;
}
@@ -164,9 +151,6 @@ static void twl4030_vibra_close(struct input_dev *input)
struct vibra_info *info = input_get_drvdata(input);
cancel_work_sync(&info->play_work);
- INIT_WORK(&info->play_work, vibra_play_work); /* cleanup */
- destroy_workqueue(info->workqueue);
- info->workqueue = NULL;
if (info->enabled)
vibra_disable(info);
@@ -219,7 +203,7 @@ static int twl4030_vibra_probe(struct platform_device *pdev)
return -EINVAL;
}
- info = kzalloc(sizeof(*info), GFP_KERNEL);
+ info = devm_kzalloc(&pdev->dev, sizeof(*info), GFP_KERNEL);
if (!info)
return -ENOMEM;
@@ -227,11 +211,10 @@ static int twl4030_vibra_probe(struct platform_device *pdev)
info->coexist = twl4030_vibra_check_coexist(pdata, twl4030_core_node);
INIT_WORK(&info->play_work, vibra_play_work);
- info->input_dev = input_allocate_device();
+ info->input_dev = devm_input_allocate_device(&pdev->dev);
if (info->input_dev == NULL) {
dev_err(&pdev->dev, "couldn't allocate input device\n");
- ret = -ENOMEM;
- goto err_kzalloc;
+ return -ENOMEM;
}
input_set_drvdata(info->input_dev, info);
@@ -239,14 +222,13 @@ static int twl4030_vibra_probe(struct platform_device *pdev)
info->input_dev->name = "twl4030:vibrator";
info->input_dev->id.version = 1;
info->input_dev->dev.parent = pdev->dev.parent;
- info->input_dev->open = twl4030_vibra_open;
info->input_dev->close = twl4030_vibra_close;
__set_bit(FF_RUMBLE, info->input_dev->ffbit);
ret = input_ff_create_memless(info->input_dev, NULL, vibra_play);
if (ret < 0) {
dev_dbg(&pdev->dev, "couldn't register vibrator to FF\n");
- goto err_ialloc;
+ return ret;
}
ret = input_register_device(info->input_dev);
@@ -262,28 +244,11 @@ static int twl4030_vibra_probe(struct platform_device *pdev)
err_iff:
input_ff_destroy(info->input_dev);
-err_ialloc:
- input_free_device(info->input_dev);
-err_kzalloc:
- kfree(info);
return ret;
}
-static int twl4030_vibra_remove(struct platform_device *pdev)
-{
- struct vibra_info *info = platform_get_drvdata(pdev);
-
- /* this also free ff-memless and calls close if needed */
- input_unregister_device(info->input_dev);
- kfree(info);
- platform_set_drvdata(pdev, NULL);
-
- return 0;
-}
-
static struct platform_driver twl4030_vibra_driver = {
.probe = twl4030_vibra_probe,
- .remove = twl4030_vibra_remove,
.driver = {
.name = "twl4030-vibra",
.owner = THIS_MODULE,
diff --git a/drivers/input/misc/twl6040-vibra.c b/drivers/input/misc/twl6040-vibra.c
index 71a28ee699f3..0c2dfc8e9691 100644
--- a/drivers/input/misc/twl6040-vibra.c
+++ b/drivers/input/misc/twl6040-vibra.c
@@ -275,7 +275,7 @@ static int twl6040_vibra_probe(struct platform_device *pdev)
return -EINVAL;
}
- info = kzalloc(sizeof(*info), GFP_KERNEL);
+ info = devm_kzalloc(&pdev->dev, sizeof(*info), GFP_KERNEL);
if (!info) {
dev_err(&pdev->dev, "couldn't allocate memory\n");
return -ENOMEM;
@@ -309,53 +309,23 @@ static int twl6040_vibra_probe(struct platform_device *pdev)
if ((!info->vibldrv_res && !info->viblmotor_res) ||
(!info->vibrdrv_res && !info->vibrmotor_res)) {
dev_err(info->dev, "invalid vibra driver/motor resistance\n");
- ret = -EINVAL;
- goto err_kzalloc;
+ return -EINVAL;
}
info->irq = platform_get_irq(pdev, 0);
if (info->irq < 0) {
dev_err(info->dev, "invalid irq\n");
- ret = -EINVAL;
- goto err_kzalloc;
+ return -EINVAL;
}
mutex_init(&info->mutex);
- info->input_dev = input_allocate_device();
- if (info->input_dev == NULL) {
- dev_err(info->dev, "couldn't allocate input device\n");
- ret = -ENOMEM;
- goto err_kzalloc;
- }
-
- input_set_drvdata(info->input_dev, info);
-
- info->input_dev->name = "twl6040:vibrator";
- info->input_dev->id.version = 1;
- info->input_dev->dev.parent = pdev->dev.parent;
- info->input_dev->close = twl6040_vibra_close;
- __set_bit(FF_RUMBLE, info->input_dev->ffbit);
-
- ret = input_ff_create_memless(info->input_dev, NULL, vibra_play);
- if (ret < 0) {
- dev_err(info->dev, "couldn't register vibrator to FF\n");
- goto err_ialloc;
- }
-
- ret = input_register_device(info->input_dev);
- if (ret < 0) {
- dev_err(info->dev, "couldn't register input device\n");
- goto err_iff;
- }
-
- platform_set_drvdata(pdev, info);
-
- ret = request_threaded_irq(info->irq, NULL, twl6040_vib_irq_handler, 0,
- "twl6040_irq_vib", info);
+ ret = devm_request_threaded_irq(&pdev->dev, info->irq, NULL,
+ twl6040_vib_irq_handler, 0,
+ "twl6040_irq_vib", info);
if (ret) {
dev_err(info->dev, "VIB IRQ request failed: %d\n", ret);
- goto err_irq;
+ return ret;
}
info->supplies[0].supply = "vddvibl";
@@ -368,7 +338,7 @@ static int twl6040_vibra_probe(struct platform_device *pdev)
ARRAY_SIZE(info->supplies), info->supplies);
if (ret) {
dev_err(info->dev, "couldn't get regulators %d\n", ret);
- goto err_regulator;
+ return ret;
}
if (vddvibl_uV) {
@@ -377,7 +347,7 @@ static int twl6040_vibra_probe(struct platform_device *pdev)
if (ret) {
dev_err(info->dev, "failed to set VDDVIBL volt %d\n",
ret);
- goto err_voltage;
+ goto err_regulator;
}
}
@@ -387,34 +357,49 @@ static int twl6040_vibra_probe(struct platform_device *pdev)
if (ret) {
dev_err(info->dev, "failed to set VDDVIBR volt %d\n",
ret);
- goto err_voltage;
+ goto err_regulator;
}
}
- info->workqueue = alloc_workqueue("twl6040-vibra", 0, 0);
- if (info->workqueue == NULL) {
- dev_err(info->dev, "couldn't create workqueue\n");
+ INIT_WORK(&info->play_work, vibra_play_work);
+
+ info->input_dev = input_allocate_device();
+ if (info->input_dev == NULL) {
+ dev_err(info->dev, "couldn't allocate input device\n");
ret = -ENOMEM;
- goto err_voltage;
+ goto err_regulator;
}
- INIT_WORK(&info->play_work, vibra_play_work);
+
+ input_set_drvdata(info->input_dev, info);
+
+ info->input_dev->name = "twl6040:vibrator";
+ info->input_dev->id.version = 1;
+ info->input_dev->dev.parent = pdev->dev.parent;
+ info->input_dev->close = twl6040_vibra_close;
+ __set_bit(FF_RUMBLE, info->input_dev->ffbit);
+
+ ret = input_ff_create_memless(info->input_dev, NULL, vibra_play);
+ if (ret < 0) {
+ dev_err(info->dev, "couldn't register vibrator to FF\n");
+ goto err_ialloc;
+ }
+
+ ret = input_register_device(info->input_dev);
+ if (ret < 0) {
+ dev_err(info->dev, "couldn't register input device\n");
+ goto err_iff;
+ }
+
+ platform_set_drvdata(pdev, info);
return 0;
-err_voltage:
- regulator_bulk_free(ARRAY_SIZE(info->supplies), info->supplies);
-err_regulator:
- free_irq(info->irq, info);
-err_irq:
- input_unregister_device(info->input_dev);
- info->input_dev = NULL;
err_iff:
- if (info->input_dev)
- input_ff_destroy(info->input_dev);
+ input_ff_destroy(info->input_dev);
err_ialloc:
input_free_device(info->input_dev);
-err_kzalloc:
- kfree(info);
+err_regulator:
+ regulator_bulk_free(ARRAY_SIZE(info->supplies), info->supplies);
return ret;
}
@@ -423,10 +408,7 @@ static int twl6040_vibra_remove(struct platform_device *pdev)
struct vibra_info *info = platform_get_drvdata(pdev);
input_unregister_device(info->input_dev);
- free_irq(info->irq, info);
regulator_bulk_free(ARRAY_SIZE(info->supplies), info->supplies);
- destroy_workqueue(info->workqueue);
- kfree(info);
return 0;
}
diff --git a/drivers/input/misc/wm831x-on.c b/drivers/input/misc/wm831x-on.c
index 558767d8ebf4..caa2c4068f09 100644
--- a/drivers/input/misc/wm831x-on.c
+++ b/drivers/input/misc/wm831x-on.c
@@ -86,7 +86,7 @@ static int wm831x_on_probe(struct platform_device *pdev)
wm831x_on->wm831x = wm831x;
INIT_DELAYED_WORK(&wm831x_on->work, wm831x_poll_on);
- wm831x_on->dev = input_allocate_device();
+ wm831x_on->dev = devm_input_allocate_device(&pdev->dev);
if (!wm831x_on->dev) {
dev_err(&pdev->dev, "Can't allocate input dev\n");
ret = -ENOMEM;
@@ -119,7 +119,6 @@ static int wm831x_on_probe(struct platform_device *pdev)
err_irq:
free_irq(irq, wm831x_on);
err_input_dev:
- input_free_device(wm831x_on->dev);
err:
return ret;
}
@@ -131,7 +130,6 @@ static int wm831x_on_remove(struct platform_device *pdev)
free_irq(irq, wm831x_on);
cancel_delayed_work_sync(&wm831x_on->work);
- input_unregister_device(wm831x_on->dev);
return 0;
}
diff --git a/drivers/input/mouse/Kconfig b/drivers/input/mouse/Kconfig
index cd6268cf7cd5..802bd6a72d73 100644
--- a/drivers/input/mouse/Kconfig
+++ b/drivers/input/mouse/Kconfig
@@ -68,6 +68,16 @@ config MOUSE_PS2_SYNAPTICS
If unsure, say Y.
+config MOUSE_PS2_CYPRESS
+ bool "Cypress PS/2 mouse protocol extension" if EXPERT
+ default y
+ depends on MOUSE_PS2
+ help
+ Say Y here if you have a Cypress PS/2 Trackpad connected to
+ your system.
+
+ If unsure, say Y.
+
config MOUSE_PS2_LIFEBOOK
bool "Fujitsu Lifebook PS/2 mouse protocol extension" if EXPERT
default y
@@ -193,6 +203,18 @@ config MOUSE_BCM5974
To compile this driver as a module, choose M here: the
module will be called bcm5974.
+config MOUSE_CYAPA
+ tristate "Cypress APA I2C Trackpad support"
+ depends on I2C
+ help
+ This driver adds support for Cypress All Points Addressable (APA)
+ I2C Trackpads, including the ones used in 2012 Samsung Chromebooks.
+
+ Say Y here if you have a Cypress APA I2C Trackpad.
+
+ To compile this driver as a module, choose M here: the module will be
+ called cyapa.
+
config MOUSE_INPORT
tristate "InPort/MS/ATIXL busmouse"
depends on ISA
diff --git a/drivers/input/mouse/Makefile b/drivers/input/mouse/Makefile
index 46ba7556fd4f..c25efdb3f288 100644
--- a/drivers/input/mouse/Makefile
+++ b/drivers/input/mouse/Makefile
@@ -8,6 +8,7 @@ obj-$(CONFIG_MOUSE_AMIGA) += amimouse.o
obj-$(CONFIG_MOUSE_APPLETOUCH) += appletouch.o
obj-$(CONFIG_MOUSE_ATARI) += atarimouse.o
obj-$(CONFIG_MOUSE_BCM5974) += bcm5974.o
+obj-$(CONFIG_MOUSE_CYAPA) += cyapa.o
obj-$(CONFIG_MOUSE_GPIO) += gpio_mouse.o
obj-$(CONFIG_MOUSE_INPORT) += inport.o
obj-$(CONFIG_MOUSE_LOGIBM) += logibm.o
@@ -32,3 +33,4 @@ psmouse-$(CONFIG_MOUSE_PS2_LIFEBOOK) += lifebook.o
psmouse-$(CONFIG_MOUSE_PS2_SENTELIC) += sentelic.o
psmouse-$(CONFIG_MOUSE_PS2_TRACKPOINT) += trackpoint.o
psmouse-$(CONFIG_MOUSE_PS2_TOUCHKIT) += touchkit_ps2.o
+psmouse-$(CONFIG_MOUSE_PS2_CYPRESS) += cypress_ps2.o
diff --git a/drivers/input/mouse/alps.c b/drivers/input/mouse/alps.c
index e229fa3cad96..7b99fc7c9438 100644
--- a/drivers/input/mouse/alps.c
+++ b/drivers/input/mouse/alps.c
@@ -27,14 +27,11 @@
/*
* Definitions for ALPS version 3 and 4 command mode protocol
*/
-#define ALPS_V3_X_MAX 2000
-#define ALPS_V3_Y_MAX 1400
-
-#define ALPS_BITMAP_X_BITS 15
-#define ALPS_BITMAP_Y_BITS 11
-
#define ALPS_CMD_NIBBLE_10 0x01f2
+#define ALPS_REG_BASE_RUSHMORE 0xc2c0
+#define ALPS_REG_BASE_PINNACLE 0x0000
+
static const struct alps_nibble_commands alps_v3_nibble_commands[] = {
{ PSMOUSE_CMD_SETPOLL, 0x00 }, /* 0 */
{ PSMOUSE_CMD_RESET_DIS, 0x00 }, /* 1 */
@@ -109,11 +106,14 @@ static const struct alps_model_info alps_model_data[] = {
{ { 0x73, 0x02, 0x50 }, 0x00, ALPS_PROTO_V2, 0xcf, 0xcf, ALPS_FOUR_BUTTONS }, /* Dell Vostro 1400 */
{ { 0x52, 0x01, 0x14 }, 0x00, ALPS_PROTO_V2, 0xff, 0xff,
ALPS_PASS | ALPS_DUALPOINT | ALPS_PS2_INTERLEAVED }, /* Toshiba Tecra A11-11L */
- { { 0x73, 0x02, 0x64 }, 0x9b, ALPS_PROTO_V3, 0x8f, 0x8f, ALPS_DUALPOINT },
- { { 0x73, 0x02, 0x64 }, 0x9d, ALPS_PROTO_V3, 0x8f, 0x8f, ALPS_DUALPOINT },
{ { 0x73, 0x02, 0x64 }, 0x8a, ALPS_PROTO_V4, 0x8f, 0x8f, 0 },
};
+static void alps_set_abs_params_st(struct alps_data *priv,
+ struct input_dev *dev1);
+static void alps_set_abs_params_mt(struct alps_data *priv,
+ struct input_dev *dev1);
+
/*
* XXX - this entry is suspicious. First byte has zero lower nibble,
* which is what a normal mouse would report. Also, the value 0x0e
@@ -122,10 +122,10 @@ static const struct alps_model_info alps_model_data[] = {
/* Packet formats are described in Documentation/input/alps.txt */
-static bool alps_is_valid_first_byte(const struct alps_model_info *model,
+static bool alps_is_valid_first_byte(struct alps_data *priv,
unsigned char data)
{
- return (data & model->mask0) == model->byte0;
+ return (data & priv->mask0) == priv->byte0;
}
static void alps_report_buttons(struct psmouse *psmouse,
@@ -158,14 +158,13 @@ static void alps_report_buttons(struct psmouse *psmouse,
static void alps_process_packet_v1_v2(struct psmouse *psmouse)
{
struct alps_data *priv = psmouse->private;
- const struct alps_model_info *model = priv->i;
unsigned char *packet = psmouse->packet;
struct input_dev *dev = psmouse->dev;
struct input_dev *dev2 = priv->dev2;
int x, y, z, ges, fin, left, right, middle;
int back = 0, forward = 0;
- if (model->proto_version == ALPS_PROTO_V1) {
+ if (priv->proto_version == ALPS_PROTO_V1) {
left = packet[2] & 0x10;
right = packet[2] & 0x08;
middle = 0;
@@ -181,12 +180,12 @@ static void alps_process_packet_v1_v2(struct psmouse *psmouse)
z = packet[5];
}
- if (model->flags & ALPS_FW_BK_1) {
+ if (priv->flags & ALPS_FW_BK_1) {
back = packet[0] & 0x10;
forward = packet[2] & 4;
}
- if (model->flags & ALPS_FW_BK_2) {
+ if (priv->flags & ALPS_FW_BK_2) {
back = packet[3] & 4;
forward = packet[2] & 4;
if ((middle = forward && back))
@@ -196,7 +195,7 @@ static void alps_process_packet_v1_v2(struct psmouse *psmouse)
ges = packet[2] & 1;
fin = packet[2] & 2;
- if ((model->flags & ALPS_DUALPOINT) && z == 127) {
+ if ((priv->flags & ALPS_DUALPOINT) && z == 127) {
input_report_rel(dev2, REL_X, (x > 383 ? (x - 768) : x));
input_report_rel(dev2, REL_Y, -(y > 255 ? (y - 512) : y));
@@ -239,15 +238,15 @@ static void alps_process_packet_v1_v2(struct psmouse *psmouse)
input_report_abs(dev, ABS_PRESSURE, z);
input_report_key(dev, BTN_TOOL_FINGER, z > 0);
- if (model->flags & ALPS_WHEEL)
+ if (priv->flags & ALPS_WHEEL)
input_report_rel(dev, REL_WHEEL, ((packet[2] << 1) & 0x08) - ((packet[0] >> 4) & 0x07));
- if (model->flags & (ALPS_FW_BK_1 | ALPS_FW_BK_2)) {
+ if (priv->flags & (ALPS_FW_BK_1 | ALPS_FW_BK_2)) {
input_report_key(dev, BTN_FORWARD, forward);
input_report_key(dev, BTN_BACK, back);
}
- if (model->flags & ALPS_FOUR_BUTTONS) {
+ if (priv->flags & ALPS_FOUR_BUTTONS) {
input_report_key(dev, BTN_0, packet[2] & 4);
input_report_key(dev, BTN_1, packet[0] & 0x10);
input_report_key(dev, BTN_2, packet[3] & 4);
@@ -267,7 +266,8 @@ static void alps_process_packet_v1_v2(struct psmouse *psmouse)
* These points are returned in x1, y1, x2, and y2 when the return value
* is greater than 0.
*/
-static int alps_process_bitmap(unsigned int x_map, unsigned int y_map,
+static int alps_process_bitmap(struct alps_data *priv,
+ unsigned int x_map, unsigned int y_map,
int *x1, int *y1, int *x2, int *y2)
{
struct alps_bitmap_point {
@@ -309,7 +309,7 @@ static int alps_process_bitmap(unsigned int x_map, unsigned int y_map,
* y bitmap is reversed for what we need (lower positions are in
* higher bits), so we process from the top end.
*/
- y_map = y_map << (sizeof(y_map) * BITS_PER_BYTE - ALPS_BITMAP_Y_BITS);
+ y_map = y_map << (sizeof(y_map) * BITS_PER_BYTE - priv->y_bits);
prev_bit = 0;
point = &y_low;
for (i = 0; y_map != 0; i++, y_map <<= 1) {
@@ -355,16 +355,18 @@ static int alps_process_bitmap(unsigned int x_map, unsigned int y_map,
}
}
- *x1 = (ALPS_V3_X_MAX * (2 * x_low.start_bit + x_low.num_bits - 1)) /
- (2 * (ALPS_BITMAP_X_BITS - 1));
- *y1 = (ALPS_V3_Y_MAX * (2 * y_low.start_bit + y_low.num_bits - 1)) /
- (2 * (ALPS_BITMAP_Y_BITS - 1));
+ *x1 = (priv->x_max * (2 * x_low.start_bit + x_low.num_bits - 1)) /
+ (2 * (priv->x_bits - 1));
+ *y1 = (priv->y_max * (2 * y_low.start_bit + y_low.num_bits - 1)) /
+ (2 * (priv->y_bits - 1));
if (fingers > 1) {
- *x2 = (ALPS_V3_X_MAX * (2 * x_high.start_bit + x_high.num_bits - 1)) /
- (2 * (ALPS_BITMAP_X_BITS - 1));
- *y2 = (ALPS_V3_Y_MAX * (2 * y_high.start_bit + y_high.num_bits - 1)) /
- (2 * (ALPS_BITMAP_Y_BITS - 1));
+ *x2 = (priv->x_max *
+ (2 * x_high.start_bit + x_high.num_bits - 1)) /
+ (2 * (priv->x_bits - 1));
+ *y2 = (priv->y_max *
+ (2 * y_high.start_bit + y_high.num_bits - 1)) /
+ (2 * (priv->y_bits - 1));
}
return fingers;
@@ -448,17 +450,57 @@ static void alps_process_trackstick_packet_v3(struct psmouse *psmouse)
return;
}
+static void alps_decode_buttons_v3(struct alps_fields *f, unsigned char *p)
+{
+ f->left = !!(p[3] & 0x01);
+ f->right = !!(p[3] & 0x02);
+ f->middle = !!(p[3] & 0x04);
+
+ f->ts_left = !!(p[3] & 0x10);
+ f->ts_right = !!(p[3] & 0x20);
+ f->ts_middle = !!(p[3] & 0x40);
+}
+
+static void alps_decode_pinnacle(struct alps_fields *f, unsigned char *p)
+{
+ f->first_mp = !!(p[4] & 0x40);
+ f->is_mp = !!(p[0] & 0x40);
+
+ f->fingers = (p[5] & 0x3) + 1;
+ f->x_map = ((p[4] & 0x7e) << 8) |
+ ((p[1] & 0x7f) << 2) |
+ ((p[0] & 0x30) >> 4);
+ f->y_map = ((p[3] & 0x70) << 4) |
+ ((p[2] & 0x7f) << 1) |
+ (p[4] & 0x01);
+
+ f->x = ((p[1] & 0x7f) << 4) | ((p[4] & 0x30) >> 2) |
+ ((p[0] & 0x30) >> 4);
+ f->y = ((p[2] & 0x7f) << 4) | (p[4] & 0x0f);
+ f->z = p[5] & 0x7f;
+
+ alps_decode_buttons_v3(f, p);
+}
+
+static void alps_decode_rushmore(struct alps_fields *f, unsigned char *p)
+{
+ alps_decode_pinnacle(f, p);
+
+ f->x_map |= (p[5] & 0x10) << 11;
+ f->y_map |= (p[5] & 0x20) << 6;
+}
+
static void alps_process_touchpad_packet_v3(struct psmouse *psmouse)
{
struct alps_data *priv = psmouse->private;
unsigned char *packet = psmouse->packet;
struct input_dev *dev = psmouse->dev;
struct input_dev *dev2 = priv->dev2;
- int x, y, z;
- int left, right, middle;
int x1 = 0, y1 = 0, x2 = 0, y2 = 0;
int fingers = 0, bmap_fingers;
- unsigned int x_bitmap, y_bitmap;
+ struct alps_fields f;
+
+ priv->decode_fields(&f, packet);
/*
* There's no single feature of touchpad position and bitmap packets
@@ -473,16 +515,10 @@ static void alps_process_touchpad_packet_v3(struct psmouse *psmouse)
* packet. Check for this, and when it happens process the
* position packet as usual.
*/
- if (packet[0] & 0x40) {
- fingers = (packet[5] & 0x3) + 1;
- x_bitmap = ((packet[4] & 0x7e) << 8) |
- ((packet[1] & 0x7f) << 2) |
- ((packet[0] & 0x30) >> 4);
- y_bitmap = ((packet[3] & 0x70) << 4) |
- ((packet[2] & 0x7f) << 1) |
- (packet[4] & 0x01);
-
- bmap_fingers = alps_process_bitmap(x_bitmap, y_bitmap,
+ if (f.is_mp) {
+ fingers = f.fingers;
+ bmap_fingers = alps_process_bitmap(priv,
+ f.x_map, f.y_map,
&x1, &y1, &x2, &y2);
/*
@@ -493,7 +529,7 @@ static void alps_process_touchpad_packet_v3(struct psmouse *psmouse)
fingers = bmap_fingers;
/* Now process position packet */
- packet = priv->multi_data;
+ priv->decode_fields(&f, priv->multi_data);
} else {
priv->multi_packet = 0;
}
@@ -507,10 +543,10 @@ static void alps_process_touchpad_packet_v3(struct psmouse *psmouse)
* out misidentified bitmap packets, we reject anything with this
* bit set.
*/
- if (packet[0] & 0x40)
+ if (f.is_mp)
return;
- if (!priv->multi_packet && (packet[4] & 0x40)) {
+ if (!priv->multi_packet && f.first_mp) {
priv->multi_packet = 1;
memcpy(priv->multi_data, packet, sizeof(priv->multi_data));
return;
@@ -518,22 +554,13 @@ static void alps_process_touchpad_packet_v3(struct psmouse *psmouse)
priv->multi_packet = 0;
- left = packet[3] & 0x01;
- right = packet[3] & 0x02;
- middle = packet[3] & 0x04;
-
- x = ((packet[1] & 0x7f) << 4) | ((packet[4] & 0x30) >> 2) |
- ((packet[0] & 0x30) >> 4);
- y = ((packet[2] & 0x7f) << 4) | (packet[4] & 0x0f);
- z = packet[5] & 0x7f;
-
/*
* Sometimes the hardware sends a single packet with z = 0
* in the middle of a stream. Real releases generate packets
* with x, y, and z all zero, so these seem to be flukes.
* Ignore them.
*/
- if (x && y && !z)
+ if (f.x && f.y && !f.z)
return;
/*
@@ -541,12 +568,12 @@ static void alps_process_touchpad_packet_v3(struct psmouse *psmouse)
* to rely on ST data.
*/
if (!fingers) {
- x1 = x;
- y1 = y;
- fingers = z > 0 ? 1 : 0;
+ x1 = f.x;
+ y1 = f.y;
+ fingers = f.z > 0 ? 1 : 0;
}
- if (z >= 64)
+ if (f.z >= 64)
input_report_key(dev, BTN_TOUCH, 1);
else
input_report_key(dev, BTN_TOUCH, 0);
@@ -555,26 +582,22 @@ static void alps_process_touchpad_packet_v3(struct psmouse *psmouse)
input_mt_report_finger_count(dev, fingers);
- input_report_key(dev, BTN_LEFT, left);
- input_report_key(dev, BTN_RIGHT, right);
- input_report_key(dev, BTN_MIDDLE, middle);
+ input_report_key(dev, BTN_LEFT, f.left);
+ input_report_key(dev, BTN_RIGHT, f.right);
+ input_report_key(dev, BTN_MIDDLE, f.middle);
- if (z > 0) {
- input_report_abs(dev, ABS_X, x);
- input_report_abs(dev, ABS_Y, y);
+ if (f.z > 0) {
+ input_report_abs(dev, ABS_X, f.x);
+ input_report_abs(dev, ABS_Y, f.y);
}
- input_report_abs(dev, ABS_PRESSURE, z);
+ input_report_abs(dev, ABS_PRESSURE, f.z);
input_sync(dev);
if (!(priv->quirks & ALPS_QUIRK_TRACKSTICK_BUTTONS)) {
- left = packet[3] & 0x10;
- right = packet[3] & 0x20;
- middle = packet[3] & 0x40;
-
- input_report_key(dev2, BTN_LEFT, left);
- input_report_key(dev2, BTN_RIGHT, right);
- input_report_key(dev2, BTN_MIDDLE, middle);
+ input_report_key(dev2, BTN_LEFT, f.ts_left);
+ input_report_key(dev2, BTN_RIGHT, f.ts_right);
+ input_report_key(dev2, BTN_MIDDLE, f.ts_middle);
input_sync(dev2);
}
}
@@ -639,7 +662,7 @@ static void alps_process_packet_v4(struct psmouse *psmouse)
((priv->multi_data[3] & 0x1f) << 5) |
(priv->multi_data[1] & 0x1f);
- fingers = alps_process_bitmap(x_bitmap, y_bitmap,
+ fingers = alps_process_bitmap(priv, x_bitmap, y_bitmap,
&x1, &y1, &x2, &y2);
/* Store MT data.*/
@@ -696,25 +719,6 @@ static void alps_process_packet_v4(struct psmouse *psmouse)
input_sync(dev);
}
-static void alps_process_packet(struct psmouse *psmouse)
-{
- struct alps_data *priv = psmouse->private;
- const struct alps_model_info *model = priv->i;
-
- switch (model->proto_version) {
- case ALPS_PROTO_V1:
- case ALPS_PROTO_V2:
- alps_process_packet_v1_v2(psmouse);
- break;
- case ALPS_PROTO_V3:
- alps_process_packet_v3(psmouse);
- break;
- case ALPS_PROTO_V4:
- alps_process_packet_v4(psmouse);
- break;
- }
-}
-
static void alps_report_bare_ps2_packet(struct psmouse *psmouse,
unsigned char packet[],
bool report_buttons)
@@ -765,14 +769,14 @@ static psmouse_ret_t alps_handle_interleaved_ps2(struct psmouse *psmouse)
if (((psmouse->packet[3] |
psmouse->packet[4] |
psmouse->packet[5]) & 0x80) ||
- (!alps_is_valid_first_byte(priv->i, psmouse->packet[6]))) {
+ (!alps_is_valid_first_byte(priv, psmouse->packet[6]))) {
psmouse_dbg(psmouse,
"refusing packet %4ph (suspected interleaved ps/2)\n",
psmouse->packet + 3);
return PSMOUSE_BAD_DATA;
}
- alps_process_packet(psmouse);
+ priv->process_packet(psmouse);
/* Continue with the next packet */
psmouse->packet[0] = psmouse->packet[6];
@@ -816,6 +820,7 @@ static psmouse_ret_t alps_handle_interleaved_ps2(struct psmouse *psmouse)
static void alps_flush_packet(unsigned long data)
{
struct psmouse *psmouse = (struct psmouse *)data;
+ struct alps_data *priv = psmouse->private;
serio_pause_rx(psmouse->ps2dev.serio);
@@ -833,7 +838,7 @@ static void alps_flush_packet(unsigned long data)
"refusing packet %3ph (suspected interleaved ps/2)\n",
psmouse->packet + 3);
} else {
- alps_process_packet(psmouse);
+ priv->process_packet(psmouse);
}
psmouse->pktcnt = 0;
}
@@ -844,7 +849,6 @@ static void alps_flush_packet(unsigned long data)
static psmouse_ret_t alps_process_byte(struct psmouse *psmouse)
{
struct alps_data *priv = psmouse->private;
- const struct alps_model_info *model = priv->i;
if ((psmouse->packet[0] & 0xc8) == 0x08) { /* PS/2 packet */
if (psmouse->pktcnt == 3) {
@@ -857,15 +861,15 @@ static psmouse_ret_t alps_process_byte(struct psmouse *psmouse)
/* Check for PS/2 packet stuffed in the middle of ALPS packet. */
- if ((model->flags & ALPS_PS2_INTERLEAVED) &&
+ if ((priv->flags & ALPS_PS2_INTERLEAVED) &&
psmouse->pktcnt >= 4 && (psmouse->packet[3] & 0x0f) == 0x0f) {
return alps_handle_interleaved_ps2(psmouse);
}
- if (!alps_is_valid_first_byte(model, psmouse->packet[0])) {
+ if (!alps_is_valid_first_byte(priv, psmouse->packet[0])) {
psmouse_dbg(psmouse,
"refusing packet[0] = %x (mask0 = %x, byte0 = %x)\n",
- psmouse->packet[0], model->mask0, model->byte0);
+ psmouse->packet[0], priv->mask0, priv->byte0);
return PSMOUSE_BAD_DATA;
}
@@ -879,7 +883,7 @@ static psmouse_ret_t alps_process_byte(struct psmouse *psmouse)
}
if (psmouse->pktcnt == psmouse->pktsize) {
- alps_process_packet(psmouse);
+ priv->process_packet(psmouse);
return PSMOUSE_FULL_PACKET;
}
@@ -967,24 +971,42 @@ static int alps_command_mode_write_reg(struct psmouse *psmouse, int addr,
return __alps_command_mode_write_reg(psmouse, value);
}
+static int alps_rpt_cmd(struct psmouse *psmouse, int init_command,
+ int repeated_command, unsigned char *param)
+{
+ struct ps2dev *ps2dev = &psmouse->ps2dev;
+
+ param[0] = 0;
+ if (init_command && ps2_command(ps2dev, param, init_command))
+ return -EIO;
+
+ if (ps2_command(ps2dev, NULL, repeated_command) ||
+ ps2_command(ps2dev, NULL, repeated_command) ||
+ ps2_command(ps2dev, NULL, repeated_command))
+ return -EIO;
+
+ param[0] = param[1] = param[2] = 0xff;
+ if (ps2_command(ps2dev, param, PSMOUSE_CMD_GETINFO))
+ return -EIO;
+
+ psmouse_dbg(psmouse, "%2.2X report: %2.2x %2.2x %2.2x\n",
+ repeated_command, param[0], param[1], param[2]);
+ return 0;
+}
+
static int alps_enter_command_mode(struct psmouse *psmouse,
unsigned char *resp)
{
unsigned char param[4];
- struct ps2dev *ps2dev = &psmouse->ps2dev;
- if (ps2_command(ps2dev, NULL, PSMOUSE_CMD_RESET_WRAP) ||
- ps2_command(ps2dev, NULL, PSMOUSE_CMD_RESET_WRAP) ||
- ps2_command(ps2dev, NULL, PSMOUSE_CMD_RESET_WRAP) ||
- ps2_command(ps2dev, param, PSMOUSE_CMD_GETINFO)) {
+ if (alps_rpt_cmd(psmouse, 0, PSMOUSE_CMD_RESET_WRAP, param)) {
psmouse_err(psmouse, "failed to enter command mode\n");
return -1;
}
- if (param[0] != 0x88 && param[1] != 0x07) {
+ if (param[0] != 0x88 || (param[1] != 0x07 && param[1] != 0x08)) {
psmouse_dbg(psmouse,
- "unknown response while entering command mode: %2.2x %2.2x %2.2x\n",
- param[0], param[1], param[2]);
+ "unknown response while entering command mode\n");
return -1;
}
@@ -1001,99 +1023,6 @@ static inline int alps_exit_command_mode(struct psmouse *psmouse)
return 0;
}
-static const struct alps_model_info *alps_get_model(struct psmouse *psmouse, int *version)
-{
- struct ps2dev *ps2dev = &psmouse->ps2dev;
- static const unsigned char rates[] = { 0, 10, 20, 40, 60, 80, 100, 200 };
- unsigned char param[4];
- const struct alps_model_info *model = NULL;
- int i;
-
- /*
- * First try "E6 report".
- * ALPS should return 0,0,10 or 0,0,100 if no buttons are pressed.
- * The bits 0-2 of the first byte will be 1s if some buttons are
- * pressed.
- */
- param[0] = 0;
- if (ps2_command(ps2dev, param, PSMOUSE_CMD_SETRES) ||
- ps2_command(ps2dev, NULL, PSMOUSE_CMD_SETSCALE11) ||
- ps2_command(ps2dev, NULL, PSMOUSE_CMD_SETSCALE11) ||
- ps2_command(ps2dev, NULL, PSMOUSE_CMD_SETSCALE11))
- return NULL;
-
- param[0] = param[1] = param[2] = 0xff;
- if (ps2_command(ps2dev, param, PSMOUSE_CMD_GETINFO))
- return NULL;
-
- psmouse_dbg(psmouse, "E6 report: %2.2x %2.2x %2.2x",
- param[0], param[1], param[2]);
-
- if ((param[0] & 0xf8) != 0 || param[1] != 0 ||
- (param[2] != 10 && param[2] != 100))
- return NULL;
-
- /*
- * Now try "E7 report". Allowed responses are in
- * alps_model_data[].signature
- */
- param[0] = 0;
- if (ps2_command(ps2dev, param, PSMOUSE_CMD_SETRES) ||
- ps2_command(ps2dev, NULL, PSMOUSE_CMD_SETSCALE21) ||
- ps2_command(ps2dev, NULL, PSMOUSE_CMD_SETSCALE21) ||
- ps2_command(ps2dev, NULL, PSMOUSE_CMD_SETSCALE21))
- return NULL;
-
- param[0] = param[1] = param[2] = 0xff;
- if (ps2_command(ps2dev, param, PSMOUSE_CMD_GETINFO))
- return NULL;
-
- psmouse_dbg(psmouse, "E7 report: %2.2x %2.2x %2.2x",
- param[0], param[1], param[2]);
-
- if (version) {
- for (i = 0; i < ARRAY_SIZE(rates) && param[2] != rates[i]; i++)
- /* empty */;
- *version = (param[0] << 8) | (param[1] << 4) | i;
- }
-
- for (i = 0; i < ARRAY_SIZE(alps_model_data); i++) {
- if (!memcmp(param, alps_model_data[i].signature,
- sizeof(alps_model_data[i].signature))) {
- model = alps_model_data + i;
- break;
- }
- }
-
- if (model && model->proto_version > ALPS_PROTO_V2) {
- /*
- * Need to check command mode response to identify
- * model
- */
- model = NULL;
- if (alps_enter_command_mode(psmouse, param)) {
- psmouse_warn(psmouse,
- "touchpad failed to enter command mode\n");
- } else {
- for (i = 0; i < ARRAY_SIZE(alps_model_data); i++) {
- if (alps_model_data[i].proto_version > ALPS_PROTO_V2 &&
- alps_model_data[i].command_mode_resp == param[0]) {
- model = alps_model_data + i;
- break;
- }
- }
- alps_exit_command_mode(psmouse);
-
- if (!model)
- psmouse_dbg(psmouse,
- "Unknown command mode response %2.2x\n",
- param[0]);
- }
- }
-
- return model;
-}
-
/*
* For DualPoint devices select the device that should respond to
* subsequent commands. It looks like glidepad is behind stickpointer,
@@ -1137,18 +1066,10 @@ static int alps_absolute_mode_v1_v2(struct psmouse *psmouse)
static int alps_get_status(struct psmouse *psmouse, char *param)
{
- struct ps2dev *ps2dev = &psmouse->ps2dev;
-
/* Get status: 0xF5 0xF5 0xF5 0xE9 */
- if (ps2_command(ps2dev, NULL, PSMOUSE_CMD_DISABLE) ||
- ps2_command(ps2dev, NULL, PSMOUSE_CMD_DISABLE) ||
- ps2_command(ps2dev, NULL, PSMOUSE_CMD_DISABLE) ||
- ps2_command(ps2dev, param, PSMOUSE_CMD_GETINFO))
+ if (alps_rpt_cmd(psmouse, 0, PSMOUSE_CMD_DISABLE, param))
return -1;
- psmouse_dbg(psmouse, "Status: %2.2x %2.2x %2.2x",
- param[0], param[1], param[2]);
-
return 0;
}
@@ -1190,16 +1111,16 @@ static int alps_poll(struct psmouse *psmouse)
unsigned char buf[sizeof(psmouse->packet)];
bool poll_failed;
- if (priv->i->flags & ALPS_PASS)
+ if (priv->flags & ALPS_PASS)
alps_passthrough_mode_v2(psmouse, true);
poll_failed = ps2_command(&psmouse->ps2dev, buf,
PSMOUSE_CMD_POLL | (psmouse->pktsize << 8)) < 0;
- if (priv->i->flags & ALPS_PASS)
+ if (priv->flags & ALPS_PASS)
alps_passthrough_mode_v2(psmouse, false);
- if (poll_failed || (buf[0] & priv->i->mask0) != priv->i->byte0)
+ if (poll_failed || (buf[0] & priv->mask0) != priv->byte0)
return -1;
if ((psmouse->badbyte & 0xc8) == 0x08) {
@@ -1217,9 +1138,8 @@ static int alps_poll(struct psmouse *psmouse)
static int alps_hw_init_v1_v2(struct psmouse *psmouse)
{
struct alps_data *priv = psmouse->private;
- const struct alps_model_info *model = priv->i;
- if ((model->flags & ALPS_PASS) &&
+ if ((priv->flags & ALPS_PASS) &&
alps_passthrough_mode_v2(psmouse, true)) {
return -1;
}
@@ -1234,7 +1154,7 @@ static int alps_hw_init_v1_v2(struct psmouse *psmouse)
return -1;
}
- if ((model->flags & ALPS_PASS) &&
+ if ((priv->flags & ALPS_PASS) &&
alps_passthrough_mode_v2(psmouse, false)) {
return -1;
}
@@ -1249,26 +1169,31 @@ static int alps_hw_init_v1_v2(struct psmouse *psmouse)
}
/*
- * Enable or disable passthrough mode to the trackstick. Must be in
- * command mode when calling this function.
+ * Enable or disable passthrough mode to the trackstick.
*/
-static int alps_passthrough_mode_v3(struct psmouse *psmouse, bool enable)
+static int alps_passthrough_mode_v3(struct psmouse *psmouse,
+ int reg_base, bool enable)
{
- int reg_val;
+ int reg_val, ret = -1;
- reg_val = alps_command_mode_read_reg(psmouse, 0x0008);
- if (reg_val == -1)
+ if (alps_enter_command_mode(psmouse, NULL))
return -1;
+ reg_val = alps_command_mode_read_reg(psmouse, reg_base + 0x0008);
+ if (reg_val == -1)
+ goto error;
+
if (enable)
reg_val |= 0x01;
else
reg_val &= ~0x01;
- if (__alps_command_mode_write_reg(psmouse, reg_val))
- return -1;
+ ret = __alps_command_mode_write_reg(psmouse, reg_val);
- return 0;
+error:
+ if (alps_exit_command_mode(psmouse))
+ ret = -1;
+ return ret;
}
/* Must be in command mode when calling this function */
@@ -1287,73 +1212,102 @@ static int alps_absolute_mode_v3(struct psmouse *psmouse)
return 0;
}
-static int alps_hw_init_v3(struct psmouse *psmouse)
+static int alps_probe_trackstick_v3(struct psmouse *psmouse, int reg_base)
{
- struct alps_data *priv = psmouse->private;
- struct ps2dev *ps2dev = &psmouse->ps2dev;
- int reg_val;
- unsigned char param[4];
-
- priv->nibble_commands = alps_v3_nibble_commands;
- priv->addr_command = PSMOUSE_CMD_RESET_WRAP;
+ int ret = -EIO, reg_val;
if (alps_enter_command_mode(psmouse, NULL))
goto error;
- /* Check for trackstick */
- reg_val = alps_command_mode_read_reg(psmouse, 0x0008);
+ reg_val = alps_command_mode_read_reg(psmouse, reg_base + 0x08);
if (reg_val == -1)
goto error;
- if (reg_val & 0x80) {
- if (alps_passthrough_mode_v3(psmouse, true))
- goto error;
- if (alps_exit_command_mode(psmouse))
- goto error;
+
+ /* bit 7: trackstick is present */
+ ret = reg_val & 0x80 ? 0 : -ENODEV;
+
+error:
+ alps_exit_command_mode(psmouse);
+ return ret;
+}
+
+static int alps_setup_trackstick_v3(struct psmouse *psmouse, int reg_base)
+{
+ struct ps2dev *ps2dev = &psmouse->ps2dev;
+ int ret = 0;
+ unsigned char param[4];
+
+ if (alps_passthrough_mode_v3(psmouse, reg_base, true))
+ return -EIO;
+
+ /*
+ * E7 report for the trackstick
+ *
+ * There have been reports of failures to seem to trace back
+ * to the above trackstick check failing. When these occur
+ * this E7 report fails, so when that happens we continue
+ * with the assumption that there isn't a trackstick after
+ * all.
+ */
+ if (alps_rpt_cmd(psmouse, 0, PSMOUSE_CMD_SETSCALE21, param)) {
+ psmouse_warn(psmouse, "trackstick E7 report failed\n");
+ ret = -ENODEV;
+ } else {
+ psmouse_dbg(psmouse,
+ "trackstick E7 report: %2.2x %2.2x %2.2x\n",
+ param[0], param[1], param[2]);
/*
- * E7 report for the trackstick
- *
- * There have been reports of failures to seem to trace back
- * to the above trackstick check failing. When these occur
- * this E7 report fails, so when that happens we continue
- * with the assumption that there isn't a trackstick after
- * all.
+ * Not sure what this does, but it is absolutely
+ * essential. Without it, the touchpad does not
+ * work at all and the trackstick just emits normal
+ * PS/2 packets.
*/
- param[0] = 0x64;
- if (ps2_command(ps2dev, NULL, PSMOUSE_CMD_SETSCALE21) ||
- ps2_command(ps2dev, NULL, PSMOUSE_CMD_SETSCALE21) ||
- ps2_command(ps2dev, NULL, PSMOUSE_CMD_SETSCALE21) ||
- ps2_command(ps2dev, param, PSMOUSE_CMD_GETINFO)) {
- psmouse_warn(psmouse, "trackstick E7 report failed\n");
- } else {
- psmouse_dbg(psmouse,
- "trackstick E7 report: %2.2x %2.2x %2.2x\n",
- param[0], param[1], param[2]);
-
- /*
- * Not sure what this does, but it is absolutely
- * essential. Without it, the touchpad does not
- * work at all and the trackstick just emits normal
- * PS/2 packets.
- */
- if (ps2_command(ps2dev, NULL, PSMOUSE_CMD_SETSCALE11) ||
- ps2_command(ps2dev, NULL, PSMOUSE_CMD_SETSCALE11) ||
- ps2_command(ps2dev, NULL, PSMOUSE_CMD_SETSCALE11) ||
- alps_command_mode_send_nibble(psmouse, 0x9) ||
- alps_command_mode_send_nibble(psmouse, 0x4)) {
- psmouse_err(psmouse,
- "Error sending magic E6 sequence\n");
- goto error_passthrough;
- }
+ if (ps2_command(ps2dev, NULL, PSMOUSE_CMD_SETSCALE11) ||
+ ps2_command(ps2dev, NULL, PSMOUSE_CMD_SETSCALE11) ||
+ ps2_command(ps2dev, NULL, PSMOUSE_CMD_SETSCALE11) ||
+ alps_command_mode_send_nibble(psmouse, 0x9) ||
+ alps_command_mode_send_nibble(psmouse, 0x4)) {
+ psmouse_err(psmouse,
+ "Error sending magic E6 sequence\n");
+ ret = -EIO;
+ goto error;
}
- if (alps_enter_command_mode(psmouse, NULL))
- goto error_passthrough;
- if (alps_passthrough_mode_v3(psmouse, false))
- goto error;
+ /*
+ * This ensures the trackstick packets are in the format
+ * supported by this driver. If bit 1 isn't set the packet
+ * format is different.
+ */
+ if (alps_enter_command_mode(psmouse, NULL) ||
+ alps_command_mode_write_reg(psmouse,
+ reg_base + 0x08, 0x82) ||
+ alps_exit_command_mode(psmouse))
+ ret = -EIO;
}
- if (alps_absolute_mode_v3(psmouse)) {
+error:
+ if (alps_passthrough_mode_v3(psmouse, reg_base, false))
+ ret = -EIO;
+
+ return ret;
+}
+
+static int alps_hw_init_v3(struct psmouse *psmouse)
+{
+ struct ps2dev *ps2dev = &psmouse->ps2dev;
+ int reg_val;
+ unsigned char param[4];
+
+ reg_val = alps_probe_trackstick_v3(psmouse, ALPS_REG_BASE_PINNACLE);
+ if (reg_val == -EIO)
+ goto error;
+ if (reg_val == 0 &&
+ alps_setup_trackstick_v3(psmouse, ALPS_REG_BASE_PINNACLE) == -EIO)
+ goto error;
+
+ if (alps_enter_command_mode(psmouse, NULL) ||
+ alps_absolute_mode_v3(psmouse)) {
psmouse_err(psmouse, "Failed to enter absolute mode\n");
goto error;
}
@@ -1390,14 +1344,6 @@ static int alps_hw_init_v3(struct psmouse *psmouse)
if (alps_command_mode_write_reg(psmouse, 0x0162, 0x04))
goto error;
- /*
- * This ensures the trackstick packets are in the format
- * supported by this driver. If bit 1 isn't set the packet
- * format is different.
- */
- if (alps_command_mode_write_reg(psmouse, 0x0008, 0x82))
- goto error;
-
alps_exit_command_mode(psmouse);
/* Set rate and enable data reporting */
@@ -1410,10 +1356,6 @@ static int alps_hw_init_v3(struct psmouse *psmouse)
return 0;
-error_passthrough:
- /* Something failed while in passthrough mode, so try to get out */
- if (!alps_enter_command_mode(psmouse, NULL))
- alps_passthrough_mode_v3(psmouse, false);
error:
/*
* Leaving the touchpad in command mode will essentially render
@@ -1424,6 +1366,50 @@ error:
return -1;
}
+static int alps_hw_init_rushmore_v3(struct psmouse *psmouse)
+{
+ struct alps_data *priv = psmouse->private;
+ struct ps2dev *ps2dev = &psmouse->ps2dev;
+ int reg_val, ret = -1;
+
+ if (priv->flags & ALPS_DUALPOINT) {
+ reg_val = alps_setup_trackstick_v3(psmouse,
+ ALPS_REG_BASE_RUSHMORE);
+ if (reg_val == -EIO)
+ goto error;
+ if (reg_val == -ENODEV)
+ priv->flags &= ~ALPS_DUALPOINT;
+ }
+
+ if (alps_enter_command_mode(psmouse, NULL) ||
+ alps_command_mode_read_reg(psmouse, 0xc2d9) == -1 ||
+ alps_command_mode_write_reg(psmouse, 0xc2cb, 0x00))
+ goto error;
+
+ reg_val = alps_command_mode_read_reg(psmouse, 0xc2c6);
+ if (reg_val == -1)
+ goto error;
+ if (__alps_command_mode_write_reg(psmouse, reg_val & 0xfd))
+ goto error;
+
+ if (alps_command_mode_write_reg(psmouse, 0xc2c9, 0x64))
+ goto error;
+
+ /* enter absolute mode */
+ reg_val = alps_command_mode_read_reg(psmouse, 0xc2c4);
+ if (reg_val == -1)
+ goto error;
+ if (__alps_command_mode_write_reg(psmouse, reg_val | 0x02))
+ goto error;
+
+ alps_exit_command_mode(psmouse);
+ return ps2_command(ps2dev, NULL, PSMOUSE_CMD_ENABLE);
+
+error:
+ alps_exit_command_mode(psmouse);
+ return ret;
+}
+
/* Must be in command mode when calling this function */
static int alps_absolute_mode_v4(struct psmouse *psmouse)
{
@@ -1442,13 +1428,9 @@ static int alps_absolute_mode_v4(struct psmouse *psmouse)
static int alps_hw_init_v4(struct psmouse *psmouse)
{
- struct alps_data *priv = psmouse->private;
struct ps2dev *ps2dev = &psmouse->ps2dev;
unsigned char param[4];
- priv->nibble_commands = alps_v4_nibble_commands;
- priv->addr_command = PSMOUSE_CMD_DISABLE;
-
if (alps_enter_command_mode(psmouse, NULL))
goto error;
@@ -1517,39 +1499,140 @@ error:
return -1;
}
-static int alps_hw_init(struct psmouse *psmouse)
+static void alps_set_defaults(struct alps_data *priv)
{
- struct alps_data *priv = psmouse->private;
- const struct alps_model_info *model = priv->i;
- int ret = -1;
+ priv->byte0 = 0x8f;
+ priv->mask0 = 0x8f;
+ priv->flags = ALPS_DUALPOINT;
+
+ priv->x_max = 2000;
+ priv->y_max = 1400;
+ priv->x_bits = 15;
+ priv->y_bits = 11;
- switch (model->proto_version) {
+ switch (priv->proto_version) {
case ALPS_PROTO_V1:
case ALPS_PROTO_V2:
- ret = alps_hw_init_v1_v2(psmouse);
+ priv->hw_init = alps_hw_init_v1_v2;
+ priv->process_packet = alps_process_packet_v1_v2;
+ priv->set_abs_params = alps_set_abs_params_st;
break;
case ALPS_PROTO_V3:
- ret = alps_hw_init_v3(psmouse);
+ priv->hw_init = alps_hw_init_v3;
+ priv->process_packet = alps_process_packet_v3;
+ priv->set_abs_params = alps_set_abs_params_mt;
+ priv->decode_fields = alps_decode_pinnacle;
+ priv->nibble_commands = alps_v3_nibble_commands;
+ priv->addr_command = PSMOUSE_CMD_RESET_WRAP;
break;
case ALPS_PROTO_V4:
- ret = alps_hw_init_v4(psmouse);
+ priv->hw_init = alps_hw_init_v4;
+ priv->process_packet = alps_process_packet_v4;
+ priv->set_abs_params = alps_set_abs_params_mt;
+ priv->nibble_commands = alps_v4_nibble_commands;
+ priv->addr_command = PSMOUSE_CMD_DISABLE;
break;
}
+}
- return ret;
+static int alps_match_table(struct psmouse *psmouse, struct alps_data *priv,
+ unsigned char *e7, unsigned char *ec)
+{
+ const struct alps_model_info *model;
+ int i;
+
+ for (i = 0; i < ARRAY_SIZE(alps_model_data); i++) {
+ model = &alps_model_data[i];
+
+ if (!memcmp(e7, model->signature, sizeof(model->signature)) &&
+ (!model->command_mode_resp ||
+ model->command_mode_resp == ec[2])) {
+
+ priv->proto_version = model->proto_version;
+ alps_set_defaults(priv);
+
+ priv->flags = model->flags;
+ priv->byte0 = model->byte0;
+ priv->mask0 = model->mask0;
+
+ return 0;
+ }
+ }
+
+ return -EINVAL;
+}
+
+static int alps_identify(struct psmouse *psmouse, struct alps_data *priv)
+{
+ unsigned char e6[4], e7[4], ec[4];
+
+ /*
+ * First try "E6 report".
+ * ALPS should return 0,0,10 or 0,0,100 if no buttons are pressed.
+ * The bits 0-2 of the first byte will be 1s if some buttons are
+ * pressed.
+ */
+ if (alps_rpt_cmd(psmouse, PSMOUSE_CMD_SETRES,
+ PSMOUSE_CMD_SETSCALE11, e6))
+ return -EIO;
+
+ if ((e6[0] & 0xf8) != 0 || e6[1] != 0 || (e6[2] != 10 && e6[2] != 100))
+ return -EINVAL;
+
+ /*
+ * Now get the "E7" and "EC" reports. These will uniquely identify
+ * most ALPS touchpads.
+ */
+ if (alps_rpt_cmd(psmouse, PSMOUSE_CMD_SETRES,
+ PSMOUSE_CMD_SETSCALE21, e7) ||
+ alps_rpt_cmd(psmouse, PSMOUSE_CMD_SETRES,
+ PSMOUSE_CMD_RESET_WRAP, ec) ||
+ alps_exit_command_mode(psmouse))
+ return -EIO;
+
+ if (alps_match_table(psmouse, priv, e7, ec) == 0) {
+ return 0;
+ } else if (ec[0] == 0x88 && ec[1] == 0x08) {
+ priv->proto_version = ALPS_PROTO_V3;
+ alps_set_defaults(priv);
+
+ priv->hw_init = alps_hw_init_rushmore_v3;
+ priv->decode_fields = alps_decode_rushmore;
+ priv->x_bits = 16;
+ priv->y_bits = 12;
+
+ /* hack to make addr_command, nibble_command available */
+ psmouse->private = priv;
+
+ if (alps_probe_trackstick_v3(psmouse, ALPS_REG_BASE_RUSHMORE))
+ priv->flags &= ~ALPS_DUALPOINT;
+
+ return 0;
+ } else if (ec[0] == 0x88 && ec[1] == 0x07 &&
+ ec[2] >= 0x90 && ec[2] <= 0x9d) {
+ priv->proto_version = ALPS_PROTO_V3;
+ alps_set_defaults(priv);
+
+ return 0;
+ }
+
+ psmouse_info(psmouse,
+ "Unknown ALPS touchpad: E7=%2.2x %2.2x %2.2x, EC=%2.2x %2.2x %2.2x\n",
+ e7[0], e7[1], e7[2], ec[0], ec[1], ec[2]);
+
+ return -EINVAL;
}
static int alps_reconnect(struct psmouse *psmouse)
{
- const struct alps_model_info *model;
+ struct alps_data *priv = psmouse->private;
psmouse_reset(psmouse);
- model = alps_get_model(psmouse, NULL);
- if (!model)
+ if (alps_identify(psmouse, priv) < 0)
return -1;
- return alps_hw_init(psmouse);
+ return priv->hw_init(psmouse);
}
static void alps_disconnect(struct psmouse *psmouse)
@@ -1562,12 +1645,33 @@ static void alps_disconnect(struct psmouse *psmouse)
kfree(priv);
}
+static void alps_set_abs_params_st(struct alps_data *priv,
+ struct input_dev *dev1)
+{
+ input_set_abs_params(dev1, ABS_X, 0, 1023, 0, 0);
+ input_set_abs_params(dev1, ABS_Y, 0, 767, 0, 0);
+}
+
+static void alps_set_abs_params_mt(struct alps_data *priv,
+ struct input_dev *dev1)
+{
+ set_bit(INPUT_PROP_SEMI_MT, dev1->propbit);
+ input_mt_init_slots(dev1, 2, 0);
+ input_set_abs_params(dev1, ABS_MT_POSITION_X, 0, priv->x_max, 0, 0);
+ input_set_abs_params(dev1, ABS_MT_POSITION_Y, 0, priv->y_max, 0, 0);
+
+ set_bit(BTN_TOOL_DOUBLETAP, dev1->keybit);
+ set_bit(BTN_TOOL_TRIPLETAP, dev1->keybit);
+ set_bit(BTN_TOOL_QUADTAP, dev1->keybit);
+
+ input_set_abs_params(dev1, ABS_X, 0, priv->x_max, 0, 0);
+ input_set_abs_params(dev1, ABS_Y, 0, priv->y_max, 0, 0);
+}
+
int alps_init(struct psmouse *psmouse)
{
struct alps_data *priv;
- const struct alps_model_info *model;
struct input_dev *dev1 = psmouse->dev, *dev2;
- int version;
priv = kzalloc(sizeof(struct alps_data), GFP_KERNEL);
dev2 = input_allocate_device();
@@ -1581,13 +1685,10 @@ int alps_init(struct psmouse *psmouse)
psmouse_reset(psmouse);
- model = alps_get_model(psmouse, &version);
- if (!model)
+ if (alps_identify(psmouse, priv) < 0)
goto init_fail;
- priv->i = model;
-
- if (alps_hw_init(psmouse))
+ if (priv->hw_init(psmouse))
goto init_fail;
/*
@@ -1609,41 +1710,20 @@ int alps_init(struct psmouse *psmouse)
dev1->evbit[BIT_WORD(EV_ABS)] |= BIT_MASK(EV_ABS);
- switch (model->proto_version) {
- case ALPS_PROTO_V1:
- case ALPS_PROTO_V2:
- input_set_abs_params(dev1, ABS_X, 0, 1023, 0, 0);
- input_set_abs_params(dev1, ABS_Y, 0, 767, 0, 0);
- break;
- case ALPS_PROTO_V3:
- case ALPS_PROTO_V4:
- set_bit(INPUT_PROP_SEMI_MT, dev1->propbit);
- input_mt_init_slots(dev1, 2, 0);
- input_set_abs_params(dev1, ABS_MT_POSITION_X, 0, ALPS_V3_X_MAX, 0, 0);
- input_set_abs_params(dev1, ABS_MT_POSITION_Y, 0, ALPS_V3_Y_MAX, 0, 0);
-
- set_bit(BTN_TOOL_DOUBLETAP, dev1->keybit);
- set_bit(BTN_TOOL_TRIPLETAP, dev1->keybit);
- set_bit(BTN_TOOL_QUADTAP, dev1->keybit);
-
- input_set_abs_params(dev1, ABS_X, 0, ALPS_V3_X_MAX, 0, 0);
- input_set_abs_params(dev1, ABS_Y, 0, ALPS_V3_Y_MAX, 0, 0);
- break;
- }
-
+ priv->set_abs_params(priv, dev1);
input_set_abs_params(dev1, ABS_PRESSURE, 0, 127, 0, 0);
- if (model->flags & ALPS_WHEEL) {
+ if (priv->flags & ALPS_WHEEL) {
dev1->evbit[BIT_WORD(EV_REL)] |= BIT_MASK(EV_REL);
dev1->relbit[BIT_WORD(REL_WHEEL)] |= BIT_MASK(REL_WHEEL);
}
- if (model->flags & (ALPS_FW_BK_1 | ALPS_FW_BK_2)) {
+ if (priv->flags & (ALPS_FW_BK_1 | ALPS_FW_BK_2)) {
dev1->keybit[BIT_WORD(BTN_FORWARD)] |= BIT_MASK(BTN_FORWARD);
dev1->keybit[BIT_WORD(BTN_BACK)] |= BIT_MASK(BTN_BACK);
}
- if (model->flags & ALPS_FOUR_BUTTONS) {
+ if (priv->flags & ALPS_FOUR_BUTTONS) {
dev1->keybit[BIT_WORD(BTN_0)] |= BIT_MASK(BTN_0);
dev1->keybit[BIT_WORD(BTN_1)] |= BIT_MASK(BTN_1);
dev1->keybit[BIT_WORD(BTN_2)] |= BIT_MASK(BTN_2);
@@ -1654,7 +1734,8 @@ int alps_init(struct psmouse *psmouse)
snprintf(priv->phys, sizeof(priv->phys), "%s/input1", psmouse->ps2dev.serio->phys);
dev2->phys = priv->phys;
- dev2->name = (model->flags & ALPS_DUALPOINT) ? "DualPoint Stick" : "PS/2 Mouse";
+ dev2->name = (priv->flags & ALPS_DUALPOINT) ?
+ "DualPoint Stick" : "PS/2 Mouse";
dev2->id.bustype = BUS_I8042;
dev2->id.vendor = 0x0002;
dev2->id.product = PSMOUSE_ALPS;
@@ -1673,7 +1754,7 @@ int alps_init(struct psmouse *psmouse)
psmouse->poll = alps_poll;
psmouse->disconnect = alps_disconnect;
psmouse->reconnect = alps_reconnect;
- psmouse->pktsize = model->proto_version == ALPS_PROTO_V4 ? 8 : 6;
+ psmouse->pktsize = priv->proto_version == ALPS_PROTO_V4 ? 8 : 6;
/* We are having trouble resyncing ALPS touchpads so disable it for now */
psmouse->resync_time = 0;
@@ -1690,18 +1771,16 @@ init_fail:
int alps_detect(struct psmouse *psmouse, bool set_properties)
{
- int version;
- const struct alps_model_info *model;
+ struct alps_data dummy;
- model = alps_get_model(psmouse, &version);
- if (!model)
+ if (alps_identify(psmouse, &dummy) < 0)
return -1;
if (set_properties) {
psmouse->vendor = "ALPS";
- psmouse->name = model->flags & ALPS_DUALPOINT ?
+ psmouse->name = dummy.flags & ALPS_DUALPOINT ?
"DualPoint TouchPad" : "GlidePoint";
- psmouse->model = version;
+ psmouse->model = dummy.proto_version << 8;
}
return 0;
}
diff --git a/drivers/input/mouse/alps.h b/drivers/input/mouse/alps.h
index ae1ac354c778..970480551b6e 100644
--- a/drivers/input/mouse/alps.h
+++ b/drivers/input/mouse/alps.h
@@ -12,35 +12,146 @@
#ifndef _ALPS_H
#define _ALPS_H
-#define ALPS_PROTO_V1 0
-#define ALPS_PROTO_V2 1
-#define ALPS_PROTO_V3 2
-#define ALPS_PROTO_V4 3
+#define ALPS_PROTO_V1 1
+#define ALPS_PROTO_V2 2
+#define ALPS_PROTO_V3 3
+#define ALPS_PROTO_V4 4
+/**
+ * struct alps_model_info - touchpad ID table
+ * @signature: E7 response string to match.
+ * @command_mode_resp: For V3/V4 touchpads, the final byte of the EC response
+ * (aka command mode response) identifies the firmware minor version. This
+ * can be used to distinguish different hardware models which are not
+ * uniquely identifiable through their E7 responses.
+ * @proto_version: Indicates V1/V2/V3/...
+ * @byte0: Helps figure out whether a position report packet matches the
+ * known format for this model. The first byte of the report, ANDed with
+ * mask0, should match byte0.
+ * @mask0: The mask used to check the first byte of the report.
+ * @flags: Additional device capabilities (passthrough port, trackstick, etc.).
+ *
+ * Many (but not all) ALPS touchpads can be identified by looking at the
+ * values returned in the "E7 report" and/or the "EC report." This table
+ * lists a number of such touchpads.
+ */
struct alps_model_info {
- unsigned char signature[3];
- unsigned char command_mode_resp; /* v3/v4 only */
+ unsigned char signature[3];
+ unsigned char command_mode_resp;
unsigned char proto_version;
- unsigned char byte0, mask0;
- unsigned char flags;
+ unsigned char byte0, mask0;
+ unsigned char flags;
};
+/**
+ * struct alps_nibble_commands - encodings for register accesses
+ * @command: PS/2 command used for the nibble
+ * @data: Data supplied as an argument to the PS/2 command, if applicable
+ *
+ * The ALPS protocol uses magic sequences to transmit binary data to the
+ * touchpad, as it is generally not OK to send arbitrary bytes out the
+ * PS/2 port. Each of the sequences in this table sends one nibble of the
+ * register address or (write) data. Different versions of the ALPS protocol
+ * use slightly different encodings.
+ */
struct alps_nibble_commands {
int command;
unsigned char data;
};
+/**
+ * struct alps_fields - decoded version of the report packet
+ * @x_map: Bitmap of active X positions for MT.
+ * @y_map: Bitmap of active Y positions for MT.
+ * @fingers: Number of fingers for MT.
+ * @x: X position for ST.
+ * @y: Y position for ST.
+ * @z: Z position for ST.
+ * @first_mp: Packet is the first of a multi-packet report.
+ * @is_mp: Packet is part of a multi-packet report.
+ * @left: Left touchpad button is active.
+ * @right: Right touchpad button is active.
+ * @middle: Middle touchpad button is active.
+ * @ts_left: Left trackstick button is active.
+ * @ts_right: Right trackstick button is active.
+ * @ts_middle: Middle trackstick button is active.
+ */
+struct alps_fields {
+ unsigned int x_map;
+ unsigned int y_map;
+ unsigned int fingers;
+ unsigned int x;
+ unsigned int y;
+ unsigned int z;
+ unsigned int first_mp:1;
+ unsigned int is_mp:1;
+
+ unsigned int left:1;
+ unsigned int right:1;
+ unsigned int middle:1;
+
+ unsigned int ts_left:1;
+ unsigned int ts_right:1;
+ unsigned int ts_middle:1;
+};
+
+/**
+ * struct alps_data - private data structure for the ALPS driver
+ * @dev2: "Relative" device used to report trackstick or mouse activity.
+ * @phys: Physical path for the relative device.
+ * @nibble_commands: Command mapping used for touchpad register accesses.
+ * @addr_command: Command used to tell the touchpad that a register address
+ * follows.
+ * @proto_version: Indicates V1/V2/V3/...
+ * @byte0: Helps figure out whether a position report packet matches the
+ * known format for this model. The first byte of the report, ANDed with
+ * mask0, should match byte0.
+ * @mask0: The mask used to check the first byte of the report.
+ * @flags: Additional device capabilities (passthrough port, trackstick, etc.).
+ * @x_max: Largest possible X position value.
+ * @y_max: Largest possible Y position value.
+ * @x_bits: Number of X bits in the MT bitmap.
+ * @y_bits: Number of Y bits in the MT bitmap.
+ * @hw_init: Protocol-specific hardware init function.
+ * @process_packet: Protocol-specific function to process a report packet.
+ * @decode_fields: Protocol-specific function to read packet bitfields.
+ * @set_abs_params: Protocol-specific function to configure the input_dev.
+ * @prev_fin: Finger bit from previous packet.
+ * @multi_packet: Multi-packet data in progress.
+ * @multi_data: Saved multi-packet data.
+ * @x1: First X coordinate from last MT report.
+ * @x2: Second X coordinate from last MT report.
+ * @y1: First Y coordinate from last MT report.
+ * @y2: Second Y coordinate from last MT report.
+ * @fingers: Number of fingers from last MT report.
+ * @quirks: Bitmap of ALPS_QUIRK_*.
+ * @timer: Timer for flushing out the final report packet in the stream.
+ */
struct alps_data {
- struct input_dev *dev2; /* Relative device */
- char phys[32]; /* Phys */
- const struct alps_model_info *i;/* Info */
+ struct input_dev *dev2;
+ char phys[32];
+
+ /* these are autodetected when the device is identified */
const struct alps_nibble_commands *nibble_commands;
- int addr_command; /* Command to set register address */
- int prev_fin; /* Finger bit from previous packet */
- int multi_packet; /* Multi-packet data in progress */
- unsigned char multi_data[6]; /* Saved multi-packet data */
- int x1, x2, y1, y2; /* Coordinates from last MT report */
- int fingers; /* Number of fingers from MT report */
+ int addr_command;
+ unsigned char proto_version;
+ unsigned char byte0, mask0;
+ unsigned char flags;
+ int x_max;
+ int y_max;
+ int x_bits;
+ int y_bits;
+
+ int (*hw_init)(struct psmouse *psmouse);
+ void (*process_packet)(struct psmouse *psmouse);
+ void (*decode_fields)(struct alps_fields *f, unsigned char *p);
+ void (*set_abs_params)(struct alps_data *priv, struct input_dev *dev1);
+
+ int prev_fin;
+ int multi_packet;
+ unsigned char multi_data[6];
+ int x1, x2, y1, y2;
+ int fingers;
u8 quirks;
struct timer_list timer;
};
diff --git a/drivers/input/mouse/cyapa.c b/drivers/input/mouse/cyapa.c
new file mode 100644
index 000000000000..b409c3d7d4fb
--- /dev/null
+++ b/drivers/input/mouse/cyapa.c
@@ -0,0 +1,973 @@
+/*
+ * Cypress APA trackpad with I2C interface
+ *
+ * Author: Dudley Du <dudl@cypress.com>
+ * Further cleanup and restructuring by:
+ * Daniel Kurtz <djkurtz@chromium.org>
+ * Benson Leung <bleung@chromium.org>
+ *
+ * Copyright (C) 2011-2012 Cypress Semiconductor, Inc.
+ * Copyright (C) 2011-2012 Google, Inc.
+ *
+ * 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.
+ */
+
+#include <linux/delay.h>
+#include <linux/i2c.h>
+#include <linux/input.h>
+#include <linux/input/mt.h>
+#include <linux/interrupt.h>
+#include <linux/module.h>
+#include <linux/slab.h>
+
+/* APA trackpad firmware generation */
+#define CYAPA_GEN3 0x03 /* support MT-protocol B with tracking ID. */
+
+#define CYAPA_NAME "Cypress APA Trackpad (cyapa)"
+
+/* commands for read/write registers of Cypress trackpad */
+#define CYAPA_CMD_SOFT_RESET 0x00
+#define CYAPA_CMD_POWER_MODE 0x01
+#define CYAPA_CMD_DEV_STATUS 0x02
+#define CYAPA_CMD_GROUP_DATA 0x03
+#define CYAPA_CMD_GROUP_CMD 0x04
+#define CYAPA_CMD_GROUP_QUERY 0x05
+#define CYAPA_CMD_BL_STATUS 0x06
+#define CYAPA_CMD_BL_HEAD 0x07
+#define CYAPA_CMD_BL_CMD 0x08
+#define CYAPA_CMD_BL_DATA 0x09
+#define CYAPA_CMD_BL_ALL 0x0a
+#define CYAPA_CMD_BLK_PRODUCT_ID 0x0b
+#define CYAPA_CMD_BLK_HEAD 0x0c
+
+/* report data start reg offset address. */
+#define DATA_REG_START_OFFSET 0x0000
+
+#define BL_HEAD_OFFSET 0x00
+#define BL_DATA_OFFSET 0x10
+
+/*
+ * Operational Device Status Register
+ *
+ * bit 7: Valid interrupt source
+ * bit 6 - 4: Reserved
+ * bit 3 - 2: Power status
+ * bit 1 - 0: Device status
+ */
+#define REG_OP_STATUS 0x00
+#define OP_STATUS_SRC 0x80
+#define OP_STATUS_POWER 0x0c
+#define OP_STATUS_DEV 0x03
+#define OP_STATUS_MASK (OP_STATUS_SRC | OP_STATUS_POWER | OP_STATUS_DEV)
+
+/*
+ * Operational Finger Count/Button Flags Register
+ *
+ * bit 7 - 4: Number of touched finger
+ * bit 3: Valid data
+ * bit 2: Middle Physical Button
+ * bit 1: Right Physical Button
+ * bit 0: Left physical Button
+ */
+#define REG_OP_DATA1 0x01
+#define OP_DATA_VALID 0x08
+#define OP_DATA_MIDDLE_BTN 0x04
+#define OP_DATA_RIGHT_BTN 0x02
+#define OP_DATA_LEFT_BTN 0x01
+#define OP_DATA_BTN_MASK (OP_DATA_MIDDLE_BTN | OP_DATA_RIGHT_BTN | \
+ OP_DATA_LEFT_BTN)
+
+/*
+ * Bootloader Status Register
+ *
+ * bit 7: Busy
+ * bit 6 - 5: Reserved
+ * bit 4: Bootloader running
+ * bit 3 - 1: Reserved
+ * bit 0: Checksum valid
+ */
+#define REG_BL_STATUS 0x01
+#define BL_STATUS_BUSY 0x80
+#define BL_STATUS_RUNNING 0x10
+#define BL_STATUS_DATA_VALID 0x08
+#define BL_STATUS_CSUM_VALID 0x01
+
+/*
+ * Bootloader Error Register
+ *
+ * bit 7: Invalid
+ * bit 6: Invalid security key
+ * bit 5: Bootloading
+ * bit 4: Command checksum
+ * bit 3: Flash protection error
+ * bit 2: Flash checksum error
+ * bit 1 - 0: Reserved
+ */
+#define REG_BL_ERROR 0x02
+#define BL_ERROR_INVALID 0x80
+#define BL_ERROR_INVALID_KEY 0x40
+#define BL_ERROR_BOOTLOADING 0x20
+#define BL_ERROR_CMD_CSUM 0x10
+#define BL_ERROR_FLASH_PROT 0x08
+#define BL_ERROR_FLASH_CSUM 0x04
+
+#define BL_STATUS_SIZE 3 /* length of bootloader status registers */
+#define BLK_HEAD_BYTES 32
+
+#define PRODUCT_ID_SIZE 16
+#define QUERY_DATA_SIZE 27
+#define REG_PROTOCOL_GEN_QUERY_OFFSET 20
+
+#define REG_OFFSET_DATA_BASE 0x0000
+#define REG_OFFSET_COMMAND_BASE 0x0028
+#define REG_OFFSET_QUERY_BASE 0x002a
+
+#define CAPABILITY_LEFT_BTN_MASK (0x01 << 3)
+#define CAPABILITY_RIGHT_BTN_MASK (0x01 << 4)
+#define CAPABILITY_MIDDLE_BTN_MASK (0x01 << 5)
+#define CAPABILITY_BTN_MASK (CAPABILITY_LEFT_BTN_MASK | \
+ CAPABILITY_RIGHT_BTN_MASK | \
+ CAPABILITY_MIDDLE_BTN_MASK)
+
+#define CYAPA_OFFSET_SOFT_RESET REG_OFFSET_COMMAND_BASE
+
+#define REG_OFFSET_POWER_MODE (REG_OFFSET_COMMAND_BASE + 1)
+
+#define PWR_MODE_MASK 0xfc
+#define PWR_MODE_FULL_ACTIVE (0x3f << 2)
+#define PWR_MODE_IDLE (0x05 << 2) /* default sleep time is 50 ms. */
+#define PWR_MODE_OFF (0x00 << 2)
+
+#define PWR_STATUS_MASK 0x0c
+#define PWR_STATUS_ACTIVE (0x03 << 2)
+#define PWR_STATUS_IDLE (0x02 << 2)
+#define PWR_STATUS_OFF (0x00 << 2)
+
+/*
+ * CYAPA trackpad device states.
+ * Used in register 0x00, bit1-0, DeviceStatus field.
+ * Other values indicate device is in an abnormal state and must be reset.
+ */
+#define CYAPA_DEV_NORMAL 0x03
+#define CYAPA_DEV_BUSY 0x01
+
+enum cyapa_state {
+ CYAPA_STATE_OP,
+ CYAPA_STATE_BL_IDLE,
+ CYAPA_STATE_BL_ACTIVE,
+ CYAPA_STATE_BL_BUSY,
+ CYAPA_STATE_NO_DEVICE,
+};
+
+
+struct cyapa_touch {
+ /*
+ * high bits or x/y position value
+ * bit 7 - 4: high 4 bits of x position value
+ * bit 3 - 0: high 4 bits of y position value
+ */
+ u8 xy_hi;
+ u8 x_lo; /* low 8 bits of x position value. */
+ u8 y_lo; /* low 8 bits of y position value. */
+ u8 pressure;
+ /* id range is 1 - 15. It is incremented with every new touch. */
+ u8 id;
+} __packed;
+
+/* The touch.id is used as the MT slot id, thus max MT slot is 15 */
+#define CYAPA_MAX_MT_SLOTS 15
+
+struct cyapa_reg_data {
+ /*
+ * bit 0 - 1: device status
+ * bit 3 - 2: power mode
+ * bit 6 - 4: reserved
+ * bit 7: interrupt valid bit
+ */
+ u8 device_status;
+ /*
+ * bit 7 - 4: number of fingers currently touching pad
+ * bit 3: valid data check bit
+ * bit 2: middle mechanism button state if exists
+ * bit 1: right mechanism button state if exists
+ * bit 0: left mechanism button state if exists
+ */
+ u8 finger_btn;
+ /* CYAPA reports up to 5 touches per packet. */
+ struct cyapa_touch touches[5];
+} __packed;
+
+/* The main device structure */
+struct cyapa {
+ enum cyapa_state state;
+
+ struct i2c_client *client;
+ struct input_dev *input;
+ char phys[32]; /* device physical location */
+ int irq;
+ bool irq_wake; /* irq wake is enabled */
+ bool smbus;
+
+ /* read from query data region. */
+ char product_id[16];
+ u8 btn_capability;
+ u8 gen;
+ int max_abs_x;
+ int max_abs_y;
+ int physical_size_x;
+ int physical_size_y;
+};
+
+static const u8 bl_deactivate[] = { 0x00, 0xff, 0x3b, 0x00, 0x01, 0x02, 0x03,
+ 0x04, 0x05, 0x06, 0x07 };
+static const u8 bl_exit[] = { 0x00, 0xff, 0xa5, 0x00, 0x01, 0x02, 0x03, 0x04,
+ 0x05, 0x06, 0x07 };
+
+struct cyapa_cmd_len {
+ u8 cmd;
+ u8 len;
+};
+
+#define CYAPA_ADAPTER_FUNC_NONE 0
+#define CYAPA_ADAPTER_FUNC_I2C 1
+#define CYAPA_ADAPTER_FUNC_SMBUS 2
+#define CYAPA_ADAPTER_FUNC_BOTH 3
+
+/*
+ * macros for SMBus communication
+ */
+#define SMBUS_READ 0x01
+#define SMBUS_WRITE 0x00
+#define SMBUS_ENCODE_IDX(cmd, idx) ((cmd) | (((idx) & 0x03) << 1))
+#define SMBUS_ENCODE_RW(cmd, rw) ((cmd) | ((rw) & 0x01))
+#define SMBUS_BYTE_BLOCK_CMD_MASK 0x80
+#define SMBUS_GROUP_BLOCK_CMD_MASK 0x40
+
+ /* for byte read/write command */
+#define CMD_RESET 0
+#define CMD_POWER_MODE 1
+#define CMD_DEV_STATUS 2
+#define SMBUS_BYTE_CMD(cmd) (((cmd) & 0x3f) << 1)
+#define CYAPA_SMBUS_RESET SMBUS_BYTE_CMD(CMD_RESET)
+#define CYAPA_SMBUS_POWER_MODE SMBUS_BYTE_CMD(CMD_POWER_MODE)
+#define CYAPA_SMBUS_DEV_STATUS SMBUS_BYTE_CMD(CMD_DEV_STATUS)
+
+ /* for group registers read/write command */
+#define REG_GROUP_DATA 0
+#define REG_GROUP_CMD 2
+#define REG_GROUP_QUERY 3
+#define SMBUS_GROUP_CMD(grp) (0x80 | (((grp) & 0x07) << 3))
+#define CYAPA_SMBUS_GROUP_DATA SMBUS_GROUP_CMD(REG_GROUP_DATA)
+#define CYAPA_SMBUS_GROUP_CMD SMBUS_GROUP_CMD(REG_GROUP_CMD)
+#define CYAPA_SMBUS_GROUP_QUERY SMBUS_GROUP_CMD(REG_GROUP_QUERY)
+
+ /* for register block read/write command */
+#define CMD_BL_STATUS 0
+#define CMD_BL_HEAD 1
+#define CMD_BL_CMD 2
+#define CMD_BL_DATA 3
+#define CMD_BL_ALL 4
+#define CMD_BLK_PRODUCT_ID 5
+#define CMD_BLK_HEAD 6
+#define SMBUS_BLOCK_CMD(cmd) (0xc0 | (((cmd) & 0x1f) << 1))
+
+/* register block read/write command in bootloader mode */
+#define CYAPA_SMBUS_BL_STATUS SMBUS_BLOCK_CMD(CMD_BL_STATUS)
+#define CYAPA_SMBUS_BL_HEAD SMBUS_BLOCK_CMD(CMD_BL_HEAD)
+#define CYAPA_SMBUS_BL_CMD SMBUS_BLOCK_CMD(CMD_BL_CMD)
+#define CYAPA_SMBUS_BL_DATA SMBUS_BLOCK_CMD(CMD_BL_DATA)
+#define CYAPA_SMBUS_BL_ALL SMBUS_BLOCK_CMD(CMD_BL_ALL)
+
+/* register block read/write command in operational mode */
+#define CYAPA_SMBUS_BLK_PRODUCT_ID SMBUS_BLOCK_CMD(CMD_BLK_PRODUCT_ID)
+#define CYAPA_SMBUS_BLK_HEAD SMBUS_BLOCK_CMD(CMD_BLK_HEAD)
+
+static const struct cyapa_cmd_len cyapa_i2c_cmds[] = {
+ { CYAPA_OFFSET_SOFT_RESET, 1 },
+ { REG_OFFSET_COMMAND_BASE + 1, 1 },
+ { REG_OFFSET_DATA_BASE, 1 },
+ { REG_OFFSET_DATA_BASE, sizeof(struct cyapa_reg_data) },
+ { REG_OFFSET_COMMAND_BASE, 0 },
+ { REG_OFFSET_QUERY_BASE, QUERY_DATA_SIZE },
+ { BL_HEAD_OFFSET, 3 },
+ { BL_HEAD_OFFSET, 16 },
+ { BL_HEAD_OFFSET, 16 },
+ { BL_DATA_OFFSET, 16 },
+ { BL_HEAD_OFFSET, 32 },
+ { REG_OFFSET_QUERY_BASE, PRODUCT_ID_SIZE },
+ { REG_OFFSET_DATA_BASE, 32 }
+};
+
+static const struct cyapa_cmd_len cyapa_smbus_cmds[] = {
+ { CYAPA_SMBUS_RESET, 1 },
+ { CYAPA_SMBUS_POWER_MODE, 1 },
+ { CYAPA_SMBUS_DEV_STATUS, 1 },
+ { CYAPA_SMBUS_GROUP_DATA, sizeof(struct cyapa_reg_data) },
+ { CYAPA_SMBUS_GROUP_CMD, 2 },
+ { CYAPA_SMBUS_GROUP_QUERY, QUERY_DATA_SIZE },
+ { CYAPA_SMBUS_BL_STATUS, 3 },
+ { CYAPA_SMBUS_BL_HEAD, 16 },
+ { CYAPA_SMBUS_BL_CMD, 16 },
+ { CYAPA_SMBUS_BL_DATA, 16 },
+ { CYAPA_SMBUS_BL_ALL, 32 },
+ { CYAPA_SMBUS_BLK_PRODUCT_ID, PRODUCT_ID_SIZE },
+ { CYAPA_SMBUS_BLK_HEAD, 16 },
+};
+
+static ssize_t cyapa_i2c_reg_read_block(struct cyapa *cyapa, u8 reg, size_t len,
+ u8 *values)
+{
+ return i2c_smbus_read_i2c_block_data(cyapa->client, reg, len, values);
+}
+
+static ssize_t cyapa_i2c_reg_write_block(struct cyapa *cyapa, u8 reg,
+ size_t len, const u8 *values)
+{
+ return i2c_smbus_write_i2c_block_data(cyapa->client, reg, len, values);
+}
+
+/*
+ * cyapa_smbus_read_block - perform smbus block read command
+ * @cyapa - private data structure of the driver
+ * @cmd - the properly encoded smbus command
+ * @len - expected length of smbus command result
+ * @values - buffer to store smbus command result
+ *
+ * Returns negative errno, else the number of bytes written.
+ *
+ * Note:
+ * In trackpad device, the memory block allocated for I2C register map
+ * is 256 bytes, so the max read block for I2C bus is 256 bytes.
+ */
+static ssize_t cyapa_smbus_read_block(struct cyapa *cyapa, u8 cmd, size_t len,
+ u8 *values)
+{
+ ssize_t ret;
+ u8 index;
+ u8 smbus_cmd;
+ u8 *buf;
+ struct i2c_client *client = cyapa->client;
+
+ if (!(SMBUS_BYTE_BLOCK_CMD_MASK & cmd))
+ return -EINVAL;
+
+ if (SMBUS_GROUP_BLOCK_CMD_MASK & cmd) {
+ /* read specific block registers command. */
+ smbus_cmd = SMBUS_ENCODE_RW(cmd, SMBUS_READ);
+ ret = i2c_smbus_read_block_data(client, smbus_cmd, values);
+ goto out;
+ }
+
+ ret = 0;
+ for (index = 0; index * I2C_SMBUS_BLOCK_MAX < len; index++) {
+ smbus_cmd = SMBUS_ENCODE_IDX(cmd, index);
+ smbus_cmd = SMBUS_ENCODE_RW(smbus_cmd, SMBUS_READ);
+ buf = values + I2C_SMBUS_BLOCK_MAX * index;
+ ret = i2c_smbus_read_block_data(client, smbus_cmd, buf);
+ if (ret < 0)
+ goto out;
+ }
+
+out:
+ return ret > 0 ? len : ret;
+}
+
+static s32 cyapa_read_byte(struct cyapa *cyapa, u8 cmd_idx)
+{
+ u8 cmd;
+
+ if (cyapa->smbus) {
+ cmd = cyapa_smbus_cmds[cmd_idx].cmd;
+ cmd = SMBUS_ENCODE_RW(cmd, SMBUS_READ);
+ } else {
+ cmd = cyapa_i2c_cmds[cmd_idx].cmd;
+ }
+ return i2c_smbus_read_byte_data(cyapa->client, cmd);
+}
+
+static s32 cyapa_write_byte(struct cyapa *cyapa, u8 cmd_idx, u8 value)
+{
+ u8 cmd;
+
+ if (cyapa->smbus) {
+ cmd = cyapa_smbus_cmds[cmd_idx].cmd;
+ cmd = SMBUS_ENCODE_RW(cmd, SMBUS_WRITE);
+ } else {
+ cmd = cyapa_i2c_cmds[cmd_idx].cmd;
+ }
+ return i2c_smbus_write_byte_data(cyapa->client, cmd, value);
+}
+
+static ssize_t cyapa_read_block(struct cyapa *cyapa, u8 cmd_idx, u8 *values)
+{
+ u8 cmd;
+ size_t len;
+
+ if (cyapa->smbus) {
+ cmd = cyapa_smbus_cmds[cmd_idx].cmd;
+ len = cyapa_smbus_cmds[cmd_idx].len;
+ return cyapa_smbus_read_block(cyapa, cmd, len, values);
+ } else {
+ cmd = cyapa_i2c_cmds[cmd_idx].cmd;
+ len = cyapa_i2c_cmds[cmd_idx].len;
+ return cyapa_i2c_reg_read_block(cyapa, cmd, len, values);
+ }
+}
+
+/*
+ * Query device for its current operating state.
+ *
+ */
+static int cyapa_get_state(struct cyapa *cyapa)
+{
+ int ret;
+ u8 status[BL_STATUS_SIZE];
+
+ cyapa->state = CYAPA_STATE_NO_DEVICE;
+
+ /*
+ * Get trackpad status by reading 3 registers starting from 0.
+ * If the device is in the bootloader, this will be BL_HEAD.
+ * If the device is in operation mode, this will be the DATA regs.
+ *
+ */
+ ret = cyapa_i2c_reg_read_block(cyapa, BL_HEAD_OFFSET, BL_STATUS_SIZE,
+ status);
+
+ /*
+ * On smbus systems in OP mode, the i2c_reg_read will fail with
+ * -ETIMEDOUT. In this case, try again using the smbus equivalent
+ * command. This should return a BL_HEAD indicating CYAPA_STATE_OP.
+ */
+ if (cyapa->smbus && (ret == -ETIMEDOUT || ret == -ENXIO))
+ ret = cyapa_read_block(cyapa, CYAPA_CMD_BL_STATUS, status);
+
+ if (ret != BL_STATUS_SIZE)
+ goto error;
+
+ if ((status[REG_OP_STATUS] & OP_STATUS_SRC) == OP_STATUS_SRC) {
+ switch (status[REG_OP_STATUS] & OP_STATUS_DEV) {
+ case CYAPA_DEV_NORMAL:
+ case CYAPA_DEV_BUSY:
+ cyapa->state = CYAPA_STATE_OP;
+ break;
+ default:
+ ret = -EAGAIN;
+ goto error;
+ }
+ } else {
+ if (status[REG_BL_STATUS] & BL_STATUS_BUSY)
+ cyapa->state = CYAPA_STATE_BL_BUSY;
+ else if (status[REG_BL_ERROR] & BL_ERROR_BOOTLOADING)
+ cyapa->state = CYAPA_STATE_BL_ACTIVE;
+ else
+ cyapa->state = CYAPA_STATE_BL_IDLE;
+ }
+
+ return 0;
+error:
+ return (ret < 0) ? ret : -EAGAIN;
+}
+
+/*
+ * Poll device for its status in a loop, waiting up to timeout for a response.
+ *
+ * When the device switches state, it usually takes ~300 ms.
+ * However, when running a new firmware image, the device must calibrate its
+ * sensors, which can take as long as 2 seconds.
+ *
+ * Note: The timeout has granularity of the polling rate, which is 100 ms.
+ *
+ * Returns:
+ * 0 when the device eventually responds with a valid non-busy state.
+ * -ETIMEDOUT if device never responds (too many -EAGAIN)
+ * < 0 other errors
+ */
+static int cyapa_poll_state(struct cyapa *cyapa, unsigned int timeout)
+{
+ int ret;
+ int tries = timeout / 100;
+
+ ret = cyapa_get_state(cyapa);
+ while ((ret || cyapa->state >= CYAPA_STATE_BL_BUSY) && tries--) {
+ msleep(100);
+ ret = cyapa_get_state(cyapa);
+ }
+ return (ret == -EAGAIN || ret == -ETIMEDOUT) ? -ETIMEDOUT : ret;
+}
+
+static int cyapa_bl_deactivate(struct cyapa *cyapa)
+{
+ int ret;
+
+ ret = cyapa_i2c_reg_write_block(cyapa, 0, sizeof(bl_deactivate),
+ bl_deactivate);
+ if (ret < 0)
+ return ret;
+
+ /* wait for bootloader to switch to idle state; should take < 100ms */
+ msleep(100);
+ ret = cyapa_poll_state(cyapa, 500);
+ if (ret < 0)
+ return ret;
+ if (cyapa->state != CYAPA_STATE_BL_IDLE)
+ return -EAGAIN;
+ return 0;
+}
+
+/*
+ * Exit bootloader
+ *
+ * Send bl_exit command, then wait 50 - 100 ms to let device transition to
+ * operational mode. If this is the first time the device's firmware is
+ * running, it can take up to 2 seconds to calibrate its sensors. So, poll
+ * the device's new state for up to 2 seconds.
+ *
+ * Returns:
+ * -EIO failure while reading from device
+ * -EAGAIN device is stuck in bootloader, b/c it has invalid firmware
+ * 0 device is supported and in operational mode
+ */
+static int cyapa_bl_exit(struct cyapa *cyapa)
+{
+ int ret;
+
+ ret = cyapa_i2c_reg_write_block(cyapa, 0, sizeof(bl_exit), bl_exit);
+ if (ret < 0)
+ return ret;
+
+ /*
+ * Wait for bootloader to exit, and operation mode to start.
+ * Normally, this takes at least 50 ms.
+ */
+ usleep_range(50000, 100000);
+ /*
+ * In addition, when a device boots for the first time after being
+ * updated to new firmware, it must first calibrate its sensors, which
+ * can take up to an additional 2 seconds.
+ */
+ ret = cyapa_poll_state(cyapa, 2000);
+ if (ret < 0)
+ return ret;
+ if (cyapa->state != CYAPA_STATE_OP)
+ return -EAGAIN;
+
+ return 0;
+}
+
+/*
+ * Set device power mode
+ *
+ */
+static int cyapa_set_power_mode(struct cyapa *cyapa, u8 power_mode)
+{
+ struct device *dev = &cyapa->client->dev;
+ int ret;
+ u8 power;
+
+ if (cyapa->state != CYAPA_STATE_OP)
+ return 0;
+
+ ret = cyapa_read_byte(cyapa, CYAPA_CMD_POWER_MODE);
+ if (ret < 0)
+ return ret;
+
+ power = ret & ~PWR_MODE_MASK;
+ power |= power_mode & PWR_MODE_MASK;
+ ret = cyapa_write_byte(cyapa, CYAPA_CMD_POWER_MODE, power);
+ if (ret < 0)
+ dev_err(dev, "failed to set power_mode 0x%02x err = %d\n",
+ power_mode, ret);
+ return ret;
+}
+
+static int cyapa_get_query_data(struct cyapa *cyapa)
+{
+ u8 query_data[QUERY_DATA_SIZE];
+ int ret;
+
+ if (cyapa->state != CYAPA_STATE_OP)
+ return -EBUSY;
+
+ ret = cyapa_read_block(cyapa, CYAPA_CMD_GROUP_QUERY, query_data);
+ if (ret < 0)
+ return ret;
+ if (ret != QUERY_DATA_SIZE)
+ return -EIO;
+
+ memcpy(&cyapa->product_id[0], &query_data[0], 5);
+ cyapa->product_id[5] = '-';
+ memcpy(&cyapa->product_id[6], &query_data[5], 6);
+ cyapa->product_id[12] = '-';
+ memcpy(&cyapa->product_id[13], &query_data[11], 2);
+ cyapa->product_id[15] = '\0';
+
+ cyapa->btn_capability = query_data[19] & CAPABILITY_BTN_MASK;
+
+ cyapa->gen = query_data[20] & 0x0f;
+
+ cyapa->max_abs_x = ((query_data[21] & 0xf0) << 4) | query_data[22];
+ cyapa->max_abs_y = ((query_data[21] & 0x0f) << 8) | query_data[23];
+
+ cyapa->physical_size_x =
+ ((query_data[24] & 0xf0) << 4) | query_data[25];
+ cyapa->physical_size_y =
+ ((query_data[24] & 0x0f) << 8) | query_data[26];
+
+ return 0;
+}
+
+/*
+ * Check if device is operational.
+ *
+ * An operational device is responding, has exited bootloader, and has
+ * firmware supported by this driver.
+ *
+ * Returns:
+ * -EBUSY no device or in bootloader
+ * -EIO failure while reading from device
+ * -EAGAIN device is still in bootloader
+ * if ->state = CYAPA_STATE_BL_IDLE, device has invalid firmware
+ * -EINVAL device is in operational mode, but not supported by this driver
+ * 0 device is supported
+ */
+static int cyapa_check_is_operational(struct cyapa *cyapa)
+{
+ struct device *dev = &cyapa->client->dev;
+ static const char unique_str[] = "CYTRA";
+ int ret;
+
+ ret = cyapa_poll_state(cyapa, 2000);
+ if (ret < 0)
+ return ret;
+ switch (cyapa->state) {
+ case CYAPA_STATE_BL_ACTIVE:
+ ret = cyapa_bl_deactivate(cyapa);
+ if (ret)
+ return ret;
+
+ /* Fallthrough state */
+ case CYAPA_STATE_BL_IDLE:
+ ret = cyapa_bl_exit(cyapa);
+ if (ret)
+ return ret;
+
+ /* Fallthrough state */
+ case CYAPA_STATE_OP:
+ ret = cyapa_get_query_data(cyapa);
+ if (ret < 0)
+ return ret;
+
+ /* only support firmware protocol gen3 */
+ if (cyapa->gen != CYAPA_GEN3) {
+ dev_err(dev, "unsupported protocol version (%d)",
+ cyapa->gen);
+ return -EINVAL;
+ }
+
+ /* only support product ID starting with CYTRA */
+ if (memcmp(cyapa->product_id, unique_str,
+ sizeof(unique_str) - 1) != 0) {
+ dev_err(dev, "unsupported product ID (%s)\n",
+ cyapa->product_id);
+ return -EINVAL;
+ }
+ return 0;
+
+ default:
+ return -EIO;
+ }
+ return 0;
+}
+
+static irqreturn_t cyapa_irq(int irq, void *dev_id)
+{
+ struct cyapa *cyapa = dev_id;
+ struct device *dev = &cyapa->client->dev;
+ struct input_dev *input = cyapa->input;
+ struct cyapa_reg_data data;
+ int i;
+ int ret;
+ int num_fingers;
+
+ if (device_may_wakeup(dev))
+ pm_wakeup_event(dev, 0);
+
+ ret = cyapa_read_block(cyapa, CYAPA_CMD_GROUP_DATA, (u8 *)&data);
+ if (ret != sizeof(data))
+ goto out;
+
+ if ((data.device_status & OP_STATUS_SRC) != OP_STATUS_SRC ||
+ (data.device_status & OP_STATUS_DEV) != CYAPA_DEV_NORMAL ||
+ (data.finger_btn & OP_DATA_VALID) != OP_DATA_VALID) {
+ goto out;
+ }
+
+ num_fingers = (data.finger_btn >> 4) & 0x0f;
+ for (i = 0; i < num_fingers; i++) {
+ const struct cyapa_touch *touch = &data.touches[i];
+ /* Note: touch->id range is 1 to 15; slots are 0 to 14. */
+ int slot = touch->id - 1;
+
+ input_mt_slot(input, slot);
+ input_mt_report_slot_state(input, MT_TOOL_FINGER, true);
+ input_report_abs(input, ABS_MT_POSITION_X,
+ ((touch->xy_hi & 0xf0) << 4) | touch->x_lo);
+ input_report_abs(input, ABS_MT_POSITION_Y,
+ ((touch->xy_hi & 0x0f) << 8) | touch->y_lo);
+ input_report_abs(input, ABS_MT_PRESSURE, touch->pressure);
+ }
+
+ input_mt_sync_frame(input);
+
+ if (cyapa->btn_capability & CAPABILITY_LEFT_BTN_MASK)
+ input_report_key(input, BTN_LEFT,
+ data.finger_btn & OP_DATA_LEFT_BTN);
+
+ if (cyapa->btn_capability & CAPABILITY_MIDDLE_BTN_MASK)
+ input_report_key(input, BTN_MIDDLE,
+ data.finger_btn & OP_DATA_MIDDLE_BTN);
+
+ if (cyapa->btn_capability & CAPABILITY_RIGHT_BTN_MASK)
+ input_report_key(input, BTN_RIGHT,
+ data.finger_btn & OP_DATA_RIGHT_BTN);
+
+ input_sync(input);
+
+out:
+ return IRQ_HANDLED;
+}
+
+static u8 cyapa_check_adapter_functionality(struct i2c_client *client)
+{
+ u8 ret = CYAPA_ADAPTER_FUNC_NONE;
+
+ if (i2c_check_functionality(client->adapter, I2C_FUNC_I2C))
+ ret |= CYAPA_ADAPTER_FUNC_I2C;
+ if (i2c_check_functionality(client->adapter, I2C_FUNC_SMBUS_BYTE_DATA |
+ I2C_FUNC_SMBUS_BLOCK_DATA |
+ I2C_FUNC_SMBUS_I2C_BLOCK))
+ ret |= CYAPA_ADAPTER_FUNC_SMBUS;
+ return ret;
+}
+
+static int cyapa_create_input_dev(struct cyapa *cyapa)
+{
+ struct device *dev = &cyapa->client->dev;
+ int ret;
+ struct input_dev *input;
+
+ if (!cyapa->physical_size_x || !cyapa->physical_size_y)
+ return -EINVAL;
+
+ input = cyapa->input = input_allocate_device();
+ if (!input) {
+ dev_err(dev, "allocate memory for input device failed\n");
+ return -ENOMEM;
+ }
+
+ input->name = CYAPA_NAME;
+ input->phys = cyapa->phys;
+ input->id.bustype = BUS_I2C;
+ input->id.version = 1;
+ input->id.product = 0; /* means any product in eventcomm. */
+ input->dev.parent = &cyapa->client->dev;
+
+ input_set_drvdata(input, cyapa);
+
+ __set_bit(EV_ABS, input->evbit);
+
+ /* finger position */
+ input_set_abs_params(input, ABS_MT_POSITION_X, 0, cyapa->max_abs_x, 0,
+ 0);
+ input_set_abs_params(input, ABS_MT_POSITION_Y, 0, cyapa->max_abs_y, 0,
+ 0);
+ input_set_abs_params(input, ABS_MT_PRESSURE, 0, 255, 0, 0);
+
+ input_abs_set_res(input, ABS_MT_POSITION_X,
+ cyapa->max_abs_x / cyapa->physical_size_x);
+ input_abs_set_res(input, ABS_MT_POSITION_Y,
+ cyapa->max_abs_y / cyapa->physical_size_y);
+
+ if (cyapa->btn_capability & CAPABILITY_LEFT_BTN_MASK)
+ __set_bit(BTN_LEFT, input->keybit);
+ if (cyapa->btn_capability & CAPABILITY_MIDDLE_BTN_MASK)
+ __set_bit(BTN_MIDDLE, input->keybit);
+ if (cyapa->btn_capability & CAPABILITY_RIGHT_BTN_MASK)
+ __set_bit(BTN_RIGHT, input->keybit);
+
+ if (cyapa->btn_capability == CAPABILITY_LEFT_BTN_MASK)
+ __set_bit(INPUT_PROP_BUTTONPAD, input->propbit);
+
+ /* handle pointer emulation and unused slots in core */
+ ret = input_mt_init_slots(input, CYAPA_MAX_MT_SLOTS,
+ INPUT_MT_POINTER | INPUT_MT_DROP_UNUSED);
+ if (ret) {
+ dev_err(dev, "allocate memory for MT slots failed, %d\n", ret);
+ goto err_free_device;
+ }
+
+ /* Register the device in input subsystem */
+ ret = input_register_device(input);
+ if (ret) {
+ dev_err(dev, "input device register failed, %d\n", ret);
+ goto err_free_device;
+ }
+ return 0;
+
+err_free_device:
+ input_free_device(input);
+ cyapa->input = NULL;
+ return ret;
+}
+
+static int cyapa_probe(struct i2c_client *client,
+ const struct i2c_device_id *dev_id)
+{
+ int ret;
+ u8 adapter_func;
+ struct cyapa *cyapa;
+ struct device *dev = &client->dev;
+
+ adapter_func = cyapa_check_adapter_functionality(client);
+ if (adapter_func == CYAPA_ADAPTER_FUNC_NONE) {
+ dev_err(dev, "not a supported I2C/SMBus adapter\n");
+ return -EIO;
+ }
+
+ cyapa = kzalloc(sizeof(struct cyapa), GFP_KERNEL);
+ if (!cyapa) {
+ dev_err(dev, "allocate memory for cyapa failed\n");
+ return -ENOMEM;
+ }
+
+ cyapa->gen = CYAPA_GEN3;
+ cyapa->client = client;
+ i2c_set_clientdata(client, cyapa);
+ sprintf(cyapa->phys, "i2c-%d-%04x/input0", client->adapter->nr,
+ client->addr);
+
+ /* i2c isn't supported, use smbus */
+ if (adapter_func == CYAPA_ADAPTER_FUNC_SMBUS)
+ cyapa->smbus = true;
+ cyapa->state = CYAPA_STATE_NO_DEVICE;
+ ret = cyapa_check_is_operational(cyapa);
+ if (ret) {
+ dev_err(dev, "device not operational, %d\n", ret);
+ goto err_mem_free;
+ }
+
+ ret = cyapa_create_input_dev(cyapa);
+ if (ret) {
+ dev_err(dev, "create input_dev instance failed, %d\n", ret);
+ goto err_mem_free;
+ }
+
+ ret = cyapa_set_power_mode(cyapa, PWR_MODE_FULL_ACTIVE);
+ if (ret) {
+ dev_err(dev, "set active power failed, %d\n", ret);
+ goto err_unregister_device;
+ }
+
+ cyapa->irq = client->irq;
+ ret = request_threaded_irq(cyapa->irq,
+ NULL,
+ cyapa_irq,
+ IRQF_TRIGGER_FALLING | IRQF_ONESHOT,
+ "cyapa",
+ cyapa);
+ if (ret) {
+ dev_err(dev, "IRQ request failed: %d\n, ", ret);
+ goto err_unregister_device;
+ }
+
+ return 0;
+
+err_unregister_device:
+ input_unregister_device(cyapa->input);
+err_mem_free:
+ kfree(cyapa);
+
+ return ret;
+}
+
+static int cyapa_remove(struct i2c_client *client)
+{
+ struct cyapa *cyapa = i2c_get_clientdata(client);
+
+ free_irq(cyapa->irq, cyapa);
+ input_unregister_device(cyapa->input);
+ cyapa_set_power_mode(cyapa, PWR_MODE_OFF);
+ kfree(cyapa);
+
+ return 0;
+}
+
+#ifdef CONFIG_PM_SLEEP
+static int cyapa_suspend(struct device *dev)
+{
+ int ret;
+ u8 power_mode;
+ struct cyapa *cyapa = dev_get_drvdata(dev);
+
+ disable_irq(cyapa->irq);
+
+ /*
+ * Set trackpad device to idle mode if wakeup is allowed,
+ * otherwise turn off.
+ */
+ power_mode = device_may_wakeup(dev) ? PWR_MODE_IDLE
+ : PWR_MODE_OFF;
+ ret = cyapa_set_power_mode(cyapa, power_mode);
+ if (ret < 0)
+ dev_err(dev, "set power mode failed, %d\n", ret);
+
+ if (device_may_wakeup(dev))
+ cyapa->irq_wake = (enable_irq_wake(cyapa->irq) == 0);
+ return 0;
+}
+
+static int cyapa_resume(struct device *dev)
+{
+ int ret;
+ struct cyapa *cyapa = dev_get_drvdata(dev);
+
+ if (device_may_wakeup(dev) && cyapa->irq_wake)
+ disable_irq_wake(cyapa->irq);
+
+ ret = cyapa_set_power_mode(cyapa, PWR_MODE_FULL_ACTIVE);
+ if (ret)
+ dev_warn(dev, "resume active power failed, %d\n", ret);
+
+ enable_irq(cyapa->irq);
+ return 0;
+}
+#endif /* CONFIG_PM_SLEEP */
+
+static SIMPLE_DEV_PM_OPS(cyapa_pm_ops, cyapa_suspend, cyapa_resume);
+
+static const struct i2c_device_id cyapa_id_table[] = {
+ { "cyapa", 0 },
+ { },
+};
+MODULE_DEVICE_TABLE(i2c, cyapa_id_table);
+
+static struct i2c_driver cyapa_driver = {
+ .driver = {
+ .name = "cyapa",
+ .owner = THIS_MODULE,
+ .pm = &cyapa_pm_ops,
+ },
+
+ .probe = cyapa_probe,
+ .remove = cyapa_remove,
+ .id_table = cyapa_id_table,
+};
+
+module_i2c_driver(cyapa_driver);
+
+MODULE_DESCRIPTION("Cypress APA I2C Trackpad Driver");
+MODULE_AUTHOR("Dudley Du <dudl@cypress.com>");
+MODULE_LICENSE("GPL");
diff --git a/drivers/input/mouse/cypress_ps2.c b/drivers/input/mouse/cypress_ps2.c
new file mode 100644
index 000000000000..1673dc6c8092
--- /dev/null
+++ b/drivers/input/mouse/cypress_ps2.c
@@ -0,0 +1,725 @@
+/*
+ * Cypress Trackpad PS/2 mouse driver
+ *
+ * Copyright (c) 2012 Cypress Semiconductor Corporation.
+ *
+ * Author:
+ * Dudley Du <dudl@cypress.com>
+ *
+ * Additional contributors include:
+ * Kamal Mostafa <kamal@canonical.com>
+ * Kyle Fazzari <git@status.e4ward.com>
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 as published by
+ * the Free Software Foundation.
+ */
+
+#include <linux/init.h>
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/slab.h>
+#include <linux/serio.h>
+#include <linux/libps2.h>
+#include <linux/input.h>
+#include <linux/input/mt.h>
+#include <linux/sched.h>
+#include <linux/wait.h>
+
+#include "cypress_ps2.h"
+
+#undef CYTP_DEBUG_VERBOSE /* define this and DEBUG for more verbose dump */
+
+static void cypress_set_packet_size(struct psmouse *psmouse, unsigned int n)
+{
+ struct cytp_data *cytp = psmouse->private;
+ cytp->pkt_size = n;
+}
+
+static const unsigned char cytp_rate[] = {10, 20, 40, 60, 100, 200};
+static const unsigned char cytp_resolution[] = {0x00, 0x01, 0x02, 0x03};
+
+static int cypress_ps2_sendbyte(struct psmouse *psmouse, int value)
+{
+ struct ps2dev *ps2dev = &psmouse->ps2dev;
+
+ if (ps2_sendbyte(ps2dev, value & 0xff, CYTP_CMD_TIMEOUT) < 0) {
+ psmouse_dbg(psmouse,
+ "sending command 0x%02x failed, resp 0x%02x\n",
+ value & 0xff, ps2dev->nak);
+ if (ps2dev->nak == CYTP_PS2_RETRY)
+ return CYTP_PS2_RETRY;
+ else
+ return CYTP_PS2_ERROR;
+ }
+
+#ifdef CYTP_DEBUG_VERBOSE
+ psmouse_dbg(psmouse, "sending command 0x%02x succeeded, resp 0xfa\n",
+ value & 0xff);
+#endif
+
+ return 0;
+}
+
+static int cypress_ps2_ext_cmd(struct psmouse *psmouse, unsigned short cmd,
+ unsigned char data)
+{
+ struct ps2dev *ps2dev = &psmouse->ps2dev;
+ int tries = CYTP_PS2_CMD_TRIES;
+ int rc;
+
+ ps2_begin_command(ps2dev);
+
+ do {
+ /*
+ * Send extension command byte (0xE8 or 0xF3).
+ * If sending the command fails, send recovery command
+ * to make the device return to the ready state.
+ */
+ rc = cypress_ps2_sendbyte(psmouse, cmd & 0xff);
+ if (rc == CYTP_PS2_RETRY) {
+ rc = cypress_ps2_sendbyte(psmouse, 0x00);
+ if (rc == CYTP_PS2_RETRY)
+ rc = cypress_ps2_sendbyte(psmouse, 0x0a);
+ }
+ if (rc == CYTP_PS2_ERROR)
+ continue;
+
+ rc = cypress_ps2_sendbyte(psmouse, data);
+ if (rc == CYTP_PS2_RETRY)
+ rc = cypress_ps2_sendbyte(psmouse, data);
+ if (rc == CYTP_PS2_ERROR)
+ continue;
+ else
+ break;
+ } while (--tries > 0);
+
+ ps2_end_command(ps2dev);
+
+ return rc;
+}
+
+static int cypress_ps2_read_cmd_status(struct psmouse *psmouse,
+ unsigned char cmd,
+ unsigned char *param)
+{
+ int rc;
+ struct ps2dev *ps2dev = &psmouse->ps2dev;
+ enum psmouse_state old_state;
+ int pktsize;
+
+ ps2_begin_command(&psmouse->ps2dev);
+
+ old_state = psmouse->state;
+ psmouse->state = PSMOUSE_CMD_MODE;
+ psmouse->pktcnt = 0;
+
+ pktsize = (cmd == CYTP_CMD_READ_TP_METRICS) ? 8 : 3;
+ memset(param, 0, pktsize);
+
+ rc = cypress_ps2_sendbyte(psmouse, 0xe9);
+ if (rc < 0)
+ goto out;
+
+ wait_event_timeout(ps2dev->wait,
+ (psmouse->pktcnt >= pktsize),
+ msecs_to_jiffies(CYTP_CMD_TIMEOUT));
+
+ memcpy(param, psmouse->packet, pktsize);
+
+ psmouse_dbg(psmouse, "Command 0x%02x response data (0x): %*ph\n",
+ cmd, pktsize, param);
+
+out:
+ psmouse->state = old_state;
+ psmouse->pktcnt = 0;
+
+ ps2_end_command(&psmouse->ps2dev);
+
+ return rc;
+}
+
+static bool cypress_verify_cmd_state(struct psmouse *psmouse,
+ unsigned char cmd, unsigned char *param)
+{
+ bool rate_match = false;
+ bool resolution_match = false;
+ int i;
+
+ /* callers will do further checking. */
+ if (cmd == CYTP_CMD_READ_CYPRESS_ID ||
+ cmd == CYTP_CMD_STANDARD_MODE ||
+ cmd == CYTP_CMD_READ_TP_METRICS)
+ return true;
+
+ if ((~param[0] & DFLT_RESP_BITS_VALID) == DFLT_RESP_BITS_VALID &&
+ (param[0] & DFLT_RESP_BIT_MODE) == DFLT_RESP_STREAM_MODE) {
+ for (i = 0; i < sizeof(cytp_resolution); i++)
+ if (cytp_resolution[i] == param[1])
+ resolution_match = true;
+
+ for (i = 0; i < sizeof(cytp_rate); i++)
+ if (cytp_rate[i] == param[2])
+ rate_match = true;
+
+ if (resolution_match && rate_match)
+ return true;
+ }
+
+ psmouse_dbg(psmouse, "verify cmd state failed.\n");
+ return false;
+}
+
+static int cypress_send_ext_cmd(struct psmouse *psmouse, unsigned char cmd,
+ unsigned char *param)
+{
+ int tries = CYTP_PS2_CMD_TRIES;
+ int rc;
+
+ psmouse_dbg(psmouse, "send extension cmd 0x%02x, [%d %d %d %d]\n",
+ cmd, DECODE_CMD_AA(cmd), DECODE_CMD_BB(cmd),
+ DECODE_CMD_CC(cmd), DECODE_CMD_DD(cmd));
+
+ do {
+ cypress_ps2_ext_cmd(psmouse,
+ PSMOUSE_CMD_SETRES, DECODE_CMD_DD(cmd));
+ cypress_ps2_ext_cmd(psmouse,
+ PSMOUSE_CMD_SETRES, DECODE_CMD_CC(cmd));
+ cypress_ps2_ext_cmd(psmouse,
+ PSMOUSE_CMD_SETRES, DECODE_CMD_BB(cmd));
+ cypress_ps2_ext_cmd(psmouse,
+ PSMOUSE_CMD_SETRES, DECODE_CMD_AA(cmd));
+
+ rc = cypress_ps2_read_cmd_status(psmouse, cmd, param);
+ if (rc)
+ continue;
+
+ if (cypress_verify_cmd_state(psmouse, cmd, param))
+ return 0;
+
+ } while (--tries > 0);
+
+ return -EIO;
+}
+
+int cypress_detect(struct psmouse *psmouse, bool set_properties)
+{
+ unsigned char param[3];
+
+ if (cypress_send_ext_cmd(psmouse, CYTP_CMD_READ_CYPRESS_ID, param))
+ return -ENODEV;
+
+ /* Check for Cypress Trackpad signature bytes: 0x33 0xCC */
+ if (param[0] != 0x33 || param[1] != 0xCC)
+ return -ENODEV;
+
+ if (set_properties) {
+ psmouse->vendor = "Cypress";
+ psmouse->name = "Trackpad";
+ }
+
+ return 0;
+}
+
+static int cypress_read_fw_version(struct psmouse *psmouse)
+{
+ struct cytp_data *cytp = psmouse->private;
+ unsigned char param[3];
+
+ if (cypress_send_ext_cmd(psmouse, CYTP_CMD_READ_CYPRESS_ID, param))
+ return -ENODEV;
+
+ /* Check for Cypress Trackpad signature bytes: 0x33 0xCC */
+ if (param[0] != 0x33 || param[1] != 0xCC)
+ return -ENODEV;
+
+ cytp->fw_version = param[2] & FW_VERSION_MASX;
+ cytp->tp_metrics_supported = (param[2] & TP_METRICS_MASK) ? 1 : 0;
+
+ psmouse_dbg(psmouse, "cytp->fw_version = %d\n", cytp->fw_version);
+ psmouse_dbg(psmouse, "cytp->tp_metrics_supported = %d\n",
+ cytp->tp_metrics_supported);
+
+ return 0;
+}
+
+static int cypress_read_tp_metrics(struct psmouse *psmouse)
+{
+ struct cytp_data *cytp = psmouse->private;
+ unsigned char param[8];
+
+ /* set default values for tp metrics. */
+ cytp->tp_width = CYTP_DEFAULT_WIDTH;
+ cytp->tp_high = CYTP_DEFAULT_HIGH;
+ cytp->tp_max_abs_x = CYTP_ABS_MAX_X;
+ cytp->tp_max_abs_y = CYTP_ABS_MAX_Y;
+ cytp->tp_min_pressure = CYTP_MIN_PRESSURE;
+ cytp->tp_max_pressure = CYTP_MAX_PRESSURE;
+ cytp->tp_res_x = cytp->tp_max_abs_x / cytp->tp_width;
+ cytp->tp_res_y = cytp->tp_max_abs_y / cytp->tp_high;
+
+ memset(param, 0, sizeof(param));
+ if (cypress_send_ext_cmd(psmouse, CYTP_CMD_READ_TP_METRICS, param) == 0) {
+ /* Update trackpad parameters. */
+ cytp->tp_max_abs_x = (param[1] << 8) | param[0];
+ cytp->tp_max_abs_y = (param[3] << 8) | param[2];
+ cytp->tp_min_pressure = param[4];
+ cytp->tp_max_pressure = param[5];
+ }
+
+ if (!cytp->tp_max_pressure ||
+ cytp->tp_max_pressure < cytp->tp_min_pressure ||
+ !cytp->tp_width || !cytp->tp_high ||
+ !cytp->tp_max_abs_x ||
+ cytp->tp_max_abs_x < cytp->tp_width ||
+ !cytp->tp_max_abs_y ||
+ cytp->tp_max_abs_y < cytp->tp_high)
+ return -EINVAL;
+
+ cytp->tp_res_x = cytp->tp_max_abs_x / cytp->tp_width;
+ cytp->tp_res_y = cytp->tp_max_abs_y / cytp->tp_high;
+
+#ifdef CYTP_DEBUG_VERBOSE
+ psmouse_dbg(psmouse, "Dump trackpad hardware configuration as below:\n");
+ psmouse_dbg(psmouse, "cytp->tp_width = %d\n", cytp->tp_width);
+ psmouse_dbg(psmouse, "cytp->tp_high = %d\n", cytp->tp_high);
+ psmouse_dbg(psmouse, "cytp->tp_max_abs_x = %d\n", cytp->tp_max_abs_x);
+ psmouse_dbg(psmouse, "cytp->tp_max_abs_y = %d\n", cytp->tp_max_abs_y);
+ psmouse_dbg(psmouse, "cytp->tp_min_pressure = %d\n", cytp->tp_min_pressure);
+ psmouse_dbg(psmouse, "cytp->tp_max_pressure = %d\n", cytp->tp_max_pressure);
+ psmouse_dbg(psmouse, "cytp->tp_res_x = %d\n", cytp->tp_res_x);
+ psmouse_dbg(psmouse, "cytp->tp_res_y = %d\n", cytp->tp_res_y);
+
+ psmouse_dbg(psmouse, "tp_type_APA = %d\n",
+ (param[6] & TP_METRICS_BIT_APA) ? 1 : 0);
+ psmouse_dbg(psmouse, "tp_type_MTG = %d\n",
+ (param[6] & TP_METRICS_BIT_MTG) ? 1 : 0);
+ psmouse_dbg(psmouse, "tp_palm = %d\n",
+ (param[6] & TP_METRICS_BIT_PALM) ? 1 : 0);
+ psmouse_dbg(psmouse, "tp_stubborn = %d\n",
+ (param[6] & TP_METRICS_BIT_STUBBORN) ? 1 : 0);
+ psmouse_dbg(psmouse, "tp_1f_jitter = %d\n",
+ (param[6] & TP_METRICS_BIT_1F_JITTER) >> 2);
+ psmouse_dbg(psmouse, "tp_2f_jitter = %d\n",
+ (param[6] & TP_METRICS_BIT_2F_JITTER) >> 4);
+ psmouse_dbg(psmouse, "tp_1f_spike = %d\n",
+ param[7] & TP_METRICS_BIT_1F_SPIKE);
+ psmouse_dbg(psmouse, "tp_2f_spike = %d\n",
+ (param[7] & TP_METRICS_BIT_2F_SPIKE) >> 2);
+ psmouse_dbg(psmouse, "tp_abs_packet_format_set = %d\n",
+ (param[7] & TP_METRICS_BIT_ABS_PKT_FORMAT_SET) >> 4);
+#endif
+
+ return 0;
+}
+
+static int cypress_query_hardware(struct psmouse *psmouse)
+{
+ struct cytp_data *cytp = psmouse->private;
+ int ret;
+
+ ret = cypress_read_fw_version(psmouse);
+ if (ret)
+ return ret;
+
+ if (cytp->tp_metrics_supported) {
+ ret = cypress_read_tp_metrics(psmouse);
+ if (ret)
+ return ret;
+ }
+
+ return 0;
+}
+
+static int cypress_set_absolute_mode(struct psmouse *psmouse)
+{
+ struct cytp_data *cytp = psmouse->private;
+ unsigned char param[3];
+
+ if (cypress_send_ext_cmd(psmouse, CYTP_CMD_ABS_WITH_PRESSURE_MODE, param) < 0)
+ return -1;
+
+ cytp->mode = (cytp->mode & ~CYTP_BIT_ABS_REL_MASK)
+ | CYTP_BIT_ABS_PRESSURE;
+ cypress_set_packet_size(psmouse, 5);
+
+ return 0;
+}
+
+/*
+ * Reset trackpad device.
+ * This is also the default mode when trackpad powered on.
+ */
+static void cypress_reset(struct psmouse *psmouse)
+{
+ struct cytp_data *cytp = psmouse->private;
+
+ cytp->mode = 0;
+
+ psmouse_reset(psmouse);
+}
+
+static int cypress_set_input_params(struct input_dev *input,
+ struct cytp_data *cytp)
+{
+ int ret;
+
+ if (!cytp->tp_res_x || !cytp->tp_res_y)
+ return -EINVAL;
+
+ __set_bit(EV_ABS, input->evbit);
+ input_set_abs_params(input, ABS_X, 0, cytp->tp_max_abs_x, 0, 0);
+ input_set_abs_params(input, ABS_Y, 0, cytp->tp_max_abs_y, 0, 0);
+ input_set_abs_params(input, ABS_PRESSURE,
+ cytp->tp_min_pressure, cytp->tp_max_pressure, 0, 0);
+ input_set_abs_params(input, ABS_TOOL_WIDTH, 0, 255, 0, 0);
+
+ /* finger position */
+ input_set_abs_params(input, ABS_MT_POSITION_X, 0, cytp->tp_max_abs_x, 0, 0);
+ input_set_abs_params(input, ABS_MT_POSITION_Y, 0, cytp->tp_max_abs_y, 0, 0);
+ input_set_abs_params(input, ABS_MT_PRESSURE, 0, 255, 0, 0);
+
+ ret = input_mt_init_slots(input, CYTP_MAX_MT_SLOTS,
+ INPUT_MT_DROP_UNUSED|INPUT_MT_TRACK);
+ if (ret < 0)
+ return ret;
+
+ __set_bit(INPUT_PROP_SEMI_MT, input->propbit);
+
+ input_abs_set_res(input, ABS_X, cytp->tp_res_x);
+ input_abs_set_res(input, ABS_Y, cytp->tp_res_y);
+
+ input_abs_set_res(input, ABS_MT_POSITION_X, cytp->tp_res_x);
+ input_abs_set_res(input, ABS_MT_POSITION_Y, cytp->tp_res_y);
+
+ __set_bit(BTN_TOUCH, input->keybit);
+ __set_bit(BTN_TOOL_FINGER, input->keybit);
+ __set_bit(BTN_TOOL_DOUBLETAP, input->keybit);
+ __set_bit(BTN_TOOL_TRIPLETAP, input->keybit);
+ __set_bit(BTN_TOOL_QUADTAP, input->keybit);
+ __set_bit(BTN_TOOL_QUINTTAP, input->keybit);
+
+ __clear_bit(EV_REL, input->evbit);
+ __clear_bit(REL_X, input->relbit);
+ __clear_bit(REL_Y, input->relbit);
+
+ __set_bit(INPUT_PROP_BUTTONPAD, input->propbit);
+ __set_bit(EV_KEY, input->evbit);
+ __set_bit(BTN_LEFT, input->keybit);
+ __set_bit(BTN_RIGHT, input->keybit);
+ __set_bit(BTN_MIDDLE, input->keybit);
+
+ input_set_drvdata(input, cytp);
+
+ return 0;
+}
+
+static int cypress_get_finger_count(unsigned char header_byte)
+{
+ unsigned char bits6_7;
+ int finger_count;
+
+ bits6_7 = header_byte >> 6;
+ finger_count = bits6_7 & 0x03;
+
+ if (finger_count == 1)
+ return 1;
+
+ if (header_byte & ABS_HSCROLL_BIT) {
+ /* HSCROLL gets added on to 0 finger count. */
+ switch (finger_count) {
+ case 0: return 4;
+ case 2: return 5;
+ default:
+ /* Invalid contact (e.g. palm). Ignore it. */
+ return -1;
+ }
+ }
+
+ return finger_count;
+}
+
+
+static int cypress_parse_packet(struct psmouse *psmouse,
+ struct cytp_data *cytp, struct cytp_report_data *report_data)
+{
+ unsigned char *packet = psmouse->packet;
+ unsigned char header_byte = packet[0];
+ int contact_cnt;
+
+ memset(report_data, 0, sizeof(struct cytp_report_data));
+
+ contact_cnt = cypress_get_finger_count(header_byte);
+
+ if (contact_cnt < 0) /* e.g. palm detect */
+ return -EINVAL;
+
+ report_data->contact_cnt = contact_cnt;
+
+ report_data->tap = (header_byte & ABS_MULTIFINGER_TAP) ? 1 : 0;
+
+ if (report_data->contact_cnt == 1) {
+ report_data->contacts[0].x =
+ ((packet[1] & 0x70) << 4) | packet[2];
+ report_data->contacts[0].y =
+ ((packet[1] & 0x07) << 8) | packet[3];
+ if (cytp->mode & CYTP_BIT_ABS_PRESSURE)
+ report_data->contacts[0].z = packet[4];
+
+ } else if (report_data->contact_cnt >= 2) {
+ report_data->contacts[0].x =
+ ((packet[1] & 0x70) << 4) | packet[2];
+ report_data->contacts[0].y =
+ ((packet[1] & 0x07) << 8) | packet[3];
+ if (cytp->mode & CYTP_BIT_ABS_PRESSURE)
+ report_data->contacts[0].z = packet[4];
+
+ report_data->contacts[1].x =
+ ((packet[5] & 0xf0) << 4) | packet[6];
+ report_data->contacts[1].y =
+ ((packet[5] & 0x0f) << 8) | packet[7];
+ if (cytp->mode & CYTP_BIT_ABS_PRESSURE)
+ report_data->contacts[1].z = report_data->contacts[0].z;
+ }
+
+ report_data->left = (header_byte & BTN_LEFT_BIT) ? 1 : 0;
+ report_data->right = (header_byte & BTN_RIGHT_BIT) ? 1 : 0;
+
+ /*
+ * This is only true if one of the mouse buttons were tapped. Make
+ * sure it doesn't turn into a click. The regular tap-to-click
+ * functionality will handle that on its own. If we don't do this,
+ * disabling tap-to-click won't affect the mouse button zones.
+ */
+ if (report_data->tap)
+ report_data->left = 0;
+
+#ifdef CYTP_DEBUG_VERBOSE
+ {
+ int i;
+ int n = report_data->contact_cnt;
+ psmouse_dbg(psmouse, "Dump parsed report data as below:\n");
+ psmouse_dbg(psmouse, "contact_cnt = %d\n",
+ report_data->contact_cnt);
+ if (n > CYTP_MAX_MT_SLOTS)
+ n = CYTP_MAX_MT_SLOTS;
+ for (i = 0; i < n; i++)
+ psmouse_dbg(psmouse, "contacts[%d] = {%d, %d, %d}\n", i,
+ report_data->contacts[i].x,
+ report_data->contacts[i].y,
+ report_data->contacts[i].z);
+ psmouse_dbg(psmouse, "left = %d\n", report_data->left);
+ psmouse_dbg(psmouse, "right = %d\n", report_data->right);
+ psmouse_dbg(psmouse, "middle = %d\n", report_data->middle);
+ }
+#endif
+
+ return 0;
+}
+
+static void cypress_process_packet(struct psmouse *psmouse, bool zero_pkt)
+{
+ int i;
+ struct input_dev *input = psmouse->dev;
+ struct cytp_data *cytp = psmouse->private;
+ struct cytp_report_data report_data;
+ struct cytp_contact *contact;
+ struct input_mt_pos pos[CYTP_MAX_MT_SLOTS];
+ int slots[CYTP_MAX_MT_SLOTS];
+ int n;
+
+ if (cypress_parse_packet(psmouse, cytp, &report_data))
+ return;
+
+ n = report_data.contact_cnt;
+
+ if (n > CYTP_MAX_MT_SLOTS)
+ n = CYTP_MAX_MT_SLOTS;
+
+ for (i = 0; i < n; i++) {
+ contact = &report_data.contacts[i];
+ pos[i].x = contact->x;
+ pos[i].y = contact->y;
+ }
+
+ input_mt_assign_slots(input, slots, pos, n);
+
+ for (i = 0; i < n; i++) {
+ contact = &report_data.contacts[i];
+ input_mt_slot(input, slots[i]);
+ input_mt_report_slot_state(input, MT_TOOL_FINGER, true);
+ input_report_abs(input, ABS_MT_POSITION_X, contact->x);
+ input_report_abs(input, ABS_MT_POSITION_Y, contact->y);
+ input_report_abs(input, ABS_MT_PRESSURE, contact->z);
+ }
+
+ input_mt_sync_frame(input);
+
+ input_mt_report_finger_count(input, report_data.contact_cnt);
+
+ input_report_key(input, BTN_LEFT, report_data.left);
+ input_report_key(input, BTN_RIGHT, report_data.right);
+ input_report_key(input, BTN_MIDDLE, report_data.middle);
+
+ input_sync(input);
+}
+
+static psmouse_ret_t cypress_validate_byte(struct psmouse *psmouse)
+{
+ int contact_cnt;
+ int index = psmouse->pktcnt - 1;
+ unsigned char *packet = psmouse->packet;
+ struct cytp_data *cytp = psmouse->private;
+
+ if (index < 0 || index > cytp->pkt_size)
+ return PSMOUSE_BAD_DATA;
+
+ if (index == 0 && (packet[0] & 0xfc) == 0) {
+ /* call packet process for reporting finger leave. */
+ cypress_process_packet(psmouse, 1);
+ return PSMOUSE_FULL_PACKET;
+ }
+
+ /*
+ * Perform validation (and adjust packet size) based only on the
+ * first byte; allow all further bytes through.
+ */
+ if (index != 0)
+ return PSMOUSE_GOOD_DATA;
+
+ /*
+ * If absolute/relative mode bit has not been set yet, just pass
+ * the byte through.
+ */
+ if ((cytp->mode & CYTP_BIT_ABS_REL_MASK) == 0)
+ return PSMOUSE_GOOD_DATA;
+
+ if ((packet[0] & 0x08) == 0x08)
+ return PSMOUSE_BAD_DATA;
+
+ contact_cnt = cypress_get_finger_count(packet[0]);
+
+ if (contact_cnt < 0)
+ return PSMOUSE_BAD_DATA;
+
+ if (cytp->mode & CYTP_BIT_ABS_NO_PRESSURE)
+ cypress_set_packet_size(psmouse, contact_cnt == 2 ? 7 : 4);
+ else
+ cypress_set_packet_size(psmouse, contact_cnt == 2 ? 8 : 5);
+
+ return PSMOUSE_GOOD_DATA;
+}
+
+static psmouse_ret_t cypress_protocol_handler(struct psmouse *psmouse)
+{
+ struct cytp_data *cytp = psmouse->private;
+
+ if (psmouse->pktcnt >= cytp->pkt_size) {
+ cypress_process_packet(psmouse, 0);
+ return PSMOUSE_FULL_PACKET;
+ }
+
+ return cypress_validate_byte(psmouse);
+}
+
+static void cypress_set_rate(struct psmouse *psmouse, unsigned int rate)
+{
+ struct cytp_data *cytp = psmouse->private;
+
+ if (rate >= 80) {
+ psmouse->rate = 80;
+ cytp->mode |= CYTP_BIT_HIGH_RATE;
+ } else {
+ psmouse->rate = 40;
+ cytp->mode &= ~CYTP_BIT_HIGH_RATE;
+ }
+
+ ps2_command(&psmouse->ps2dev, (unsigned char *)&psmouse->rate,
+ PSMOUSE_CMD_SETRATE);
+}
+
+static void cypress_disconnect(struct psmouse *psmouse)
+{
+ cypress_reset(psmouse);
+ kfree(psmouse->private);
+ psmouse->private = NULL;
+}
+
+static int cypress_reconnect(struct psmouse *psmouse)
+{
+ int tries = CYTP_PS2_CMD_TRIES;
+ int rc;
+
+ do {
+ cypress_reset(psmouse);
+ rc = cypress_detect(psmouse, false);
+ } while (rc && (--tries > 0));
+
+ if (rc) {
+ psmouse_err(psmouse, "Reconnect: unable to detect trackpad.\n");
+ return -1;
+ }
+
+ if (cypress_set_absolute_mode(psmouse)) {
+ psmouse_err(psmouse, "Reconnect: Unable to initialize Cypress absolute mode.\n");
+ return -1;
+ }
+
+ return 0;
+}
+
+int cypress_init(struct psmouse *psmouse)
+{
+ struct cytp_data *cytp;
+
+ cytp = (struct cytp_data *)kzalloc(sizeof(struct cytp_data), GFP_KERNEL);
+ psmouse->private = (void *)cytp;
+ if (cytp == NULL)
+ return -ENOMEM;
+
+ cypress_reset(psmouse);
+
+ psmouse->pktsize = 8;
+
+ if (cypress_query_hardware(psmouse)) {
+ psmouse_err(psmouse, "Unable to query Trackpad hardware.\n");
+ goto err_exit;
+ }
+
+ if (cypress_set_absolute_mode(psmouse)) {
+ psmouse_err(psmouse, "init: Unable to initialize Cypress absolute mode.\n");
+ goto err_exit;
+ }
+
+ if (cypress_set_input_params(psmouse->dev, cytp) < 0) {
+ psmouse_err(psmouse, "init: Unable to set input params.\n");
+ goto err_exit;
+ }
+
+ psmouse->model = 1;
+ psmouse->protocol_handler = cypress_protocol_handler;
+ psmouse->set_rate = cypress_set_rate;
+ psmouse->disconnect = cypress_disconnect;
+ psmouse->reconnect = cypress_reconnect;
+ psmouse->cleanup = cypress_reset;
+ psmouse->resync_time = 0;
+
+ return 0;
+
+err_exit:
+ /*
+ * Reset Cypress Trackpad as a standard mouse. Then
+ * let psmouse driver commmunicating with it as default PS2 mouse.
+ */
+ cypress_reset(psmouse);
+
+ psmouse->private = NULL;
+ kfree(cytp);
+
+ return -1;
+}
+
+bool cypress_supported(void)
+{
+ return true;
+}
diff --git a/drivers/input/mouse/cypress_ps2.h b/drivers/input/mouse/cypress_ps2.h
new file mode 100644
index 000000000000..4720f21d2d70
--- /dev/null
+++ b/drivers/input/mouse/cypress_ps2.h
@@ -0,0 +1,191 @@
+#ifndef _CYPRESS_PS2_H
+#define _CYPRESS_PS2_H
+
+#include "psmouse.h"
+
+#define CMD_BITS_MASK 0x03
+#define COMPOSIT(x, s) (((x) & CMD_BITS_MASK) << (s))
+
+#define ENCODE_CMD(aa, bb, cc, dd) \
+ (COMPOSIT((aa), 6) | COMPOSIT((bb), 4) | COMPOSIT((cc), 2) | COMPOSIT((dd), 0))
+#define CYTP_CMD_ABS_NO_PRESSURE_MODE ENCODE_CMD(0, 1, 0, 0)
+#define CYTP_CMD_ABS_WITH_PRESSURE_MODE ENCODE_CMD(0, 1, 0, 1)
+#define CYTP_CMD_SMBUS_MODE ENCODE_CMD(0, 1, 1, 0)
+#define CYTP_CMD_STANDARD_MODE ENCODE_CMD(0, 2, 0, 0) /* not implemented yet. */
+#define CYTP_CMD_CYPRESS_REL_MODE ENCODE_CMD(1, 1, 1, 1) /* not implemented yet. */
+#define CYTP_CMD_READ_CYPRESS_ID ENCODE_CMD(0, 0, 0, 0)
+#define CYTP_CMD_READ_TP_METRICS ENCODE_CMD(0, 0, 0, 1)
+#define CYTP_CMD_SET_HSCROLL_WIDTH(w) ENCODE_CMD(1, 1, 0, (w))
+#define CYTP_CMD_SET_HSCROLL_MASK ENCODE_CMD(1, 1, 0, 0)
+#define CYTP_CMD_SET_VSCROLL_WIDTH(w) ENCODE_CMD(1, 2, 0, (w))
+#define CYTP_CMD_SET_VSCROLL_MASK ENCODE_CMD(1, 2, 0, 0)
+#define CYTP_CMD_SET_PALM_GEOMETRY(e) ENCODE_CMD(1, 2, 1, (e))
+#define CYTP_CMD_PALM_GEMMETRY_MASK ENCODE_CMD(1, 2, 1, 0)
+#define CYTP_CMD_SET_PALM_SENSITIVITY(s) ENCODE_CMD(1, 2, 2, (s))
+#define CYTP_CMD_PALM_SENSITIVITY_MASK ENCODE_CMD(1, 2, 2, 0)
+#define CYTP_CMD_SET_MOUSE_SENSITIVITY(s) ENCODE_CMD(1, 3, ((s) >> 2), (s))
+#define CYTP_CMD_MOUSE_SENSITIVITY_MASK ENCODE_CMD(1, 3, 0, 0)
+#define CYTP_CMD_REQUEST_BASELINE_STATUS ENCODE_CMD(2, 0, 0, 1)
+#define CYTP_CMD_REQUEST_RECALIBRATION ENCODE_CMD(2, 0, 0, 3)
+
+#define DECODE_CMD_AA(x) (((x) >> 6) & CMD_BITS_MASK)
+#define DECODE_CMD_BB(x) (((x) >> 4) & CMD_BITS_MASK)
+#define DECODE_CMD_CC(x) (((x) >> 2) & CMD_BITS_MASK)
+#define DECODE_CMD_DD(x) ((x) & CMD_BITS_MASK)
+
+/* Cypress trackpad working mode. */
+#define CYTP_BIT_ABS_PRESSURE (1 << 3)
+#define CYTP_BIT_ABS_NO_PRESSURE (1 << 2)
+#define CYTP_BIT_CYPRESS_REL (1 << 1)
+#define CYTP_BIT_STANDARD_REL (1 << 0)
+#define CYTP_BIT_REL_MASK (CYTP_BIT_CYPRESS_REL | CYTP_BIT_STANDARD_REL)
+#define CYTP_BIT_ABS_MASK (CYTP_BIT_ABS_PRESSURE | CYTP_BIT_ABS_NO_PRESSURE)
+#define CYTP_BIT_ABS_REL_MASK (CYTP_BIT_ABS_MASK | CYTP_BIT_REL_MASK)
+
+#define CYTP_BIT_HIGH_RATE (1 << 4)
+/*
+ * report mode bit is set, firmware working in Remote Mode.
+ * report mode bit is cleared, firmware working in Stream Mode.
+ */
+#define CYTP_BIT_REPORT_MODE (1 << 5)
+
+/* scrolling width values for set HSCROLL and VSCROLL width command. */
+#define SCROLL_WIDTH_NARROW 1
+#define SCROLL_WIDTH_NORMAL 2
+#define SCROLL_WIDTH_WIDE 3
+
+#define PALM_GEOMETRY_ENABLE 1
+#define PALM_GEOMETRY_DISABLE 0
+
+#define TP_METRICS_MASK 0x80
+#define FW_VERSION_MASX 0x7f
+#define FW_VER_HIGH_MASK 0x70
+#define FW_VER_LOW_MASK 0x0f
+
+/* Times to retry a ps2_command and millisecond delay between tries. */
+#define CYTP_PS2_CMD_TRIES 3
+#define CYTP_PS2_CMD_DELAY 500
+
+/* time out for PS/2 command only in milliseconds. */
+#define CYTP_CMD_TIMEOUT 200
+#define CYTP_DATA_TIMEOUT 30
+
+#define CYTP_EXT_CMD 0xe8
+#define CYTP_PS2_RETRY 0xfe
+#define CYTP_PS2_ERROR 0xfc
+
+#define CYTP_RESP_RETRY 0x01
+#define CYTP_RESP_ERROR 0xfe
+
+
+#define CYTP_105001_WIDTH 97 /* Dell XPS 13 */
+#define CYTP_105001_HIGH 59
+#define CYTP_DEFAULT_WIDTH (CYTP_105001_WIDTH)
+#define CYTP_DEFAULT_HIGH (CYTP_105001_HIGH)
+
+#define CYTP_ABS_MAX_X 1600
+#define CYTP_ABS_MAX_Y 900
+#define CYTP_MAX_PRESSURE 255
+#define CYTP_MIN_PRESSURE 0
+
+/* header byte bits of relative package. */
+#define BTN_LEFT_BIT 0x01
+#define BTN_RIGHT_BIT 0x02
+#define BTN_MIDDLE_BIT 0x04
+#define REL_X_SIGN_BIT 0x10
+#define REL_Y_SIGN_BIT 0x20
+
+/* header byte bits of absolute package. */
+#define ABS_VSCROLL_BIT 0x10
+#define ABS_HSCROLL_BIT 0x20
+#define ABS_MULTIFINGER_TAP 0x04
+#define ABS_EDGE_MOTION_MASK 0x80
+
+#define DFLT_RESP_BITS_VALID 0x88 /* SMBus bit should not be set. */
+#define DFLT_RESP_SMBUS_BIT 0x80
+#define DFLT_SMBUS_MODE 0x80
+#define DFLT_PS2_MODE 0x00
+#define DFLT_RESP_BIT_MODE 0x40
+#define DFLT_RESP_REMOTE_MODE 0x40
+#define DFLT_RESP_STREAM_MODE 0x00
+#define DFLT_RESP_BIT_REPORTING 0x20
+#define DFLT_RESP_BIT_SCALING 0x10
+
+#define TP_METRICS_BIT_PALM 0x80
+#define TP_METRICS_BIT_STUBBORN 0x40
+#define TP_METRICS_BIT_2F_JITTER 0x30
+#define TP_METRICS_BIT_1F_JITTER 0x0c
+#define TP_METRICS_BIT_APA 0x02
+#define TP_METRICS_BIT_MTG 0x01
+#define TP_METRICS_BIT_ABS_PKT_FORMAT_SET 0xf0
+#define TP_METRICS_BIT_2F_SPIKE 0x0c
+#define TP_METRICS_BIT_1F_SPIKE 0x03
+
+/* bits of first byte response of E9h-Status Request command. */
+#define RESP_BTN_RIGHT_BIT 0x01
+#define RESP_BTN_MIDDLE_BIT 0x02
+#define RESP_BTN_LEFT_BIT 0x04
+#define RESP_SCALING_BIT 0x10
+#define RESP_ENABLE_BIT 0x20
+#define RESP_REMOTE_BIT 0x40
+#define RESP_SMBUS_BIT 0x80
+
+#define CYTP_MAX_MT_SLOTS 2
+
+struct cytp_contact {
+ int x;
+ int y;
+ int z; /* also named as touch pressure. */
+};
+
+/* The structure of Cypress Trackpad event data. */
+struct cytp_report_data {
+ int contact_cnt;
+ struct cytp_contact contacts[CYTP_MAX_MT_SLOTS];
+ unsigned int left:1;
+ unsigned int right:1;
+ unsigned int middle:1;
+ unsigned int tap:1; /* multi-finger tap detected. */
+};
+
+/* The structure of Cypress Trackpad device private data. */
+struct cytp_data {
+ int fw_version;
+
+ int pkt_size;
+ int mode;
+
+ int tp_min_pressure;
+ int tp_max_pressure;
+ int tp_width; /* X direction physical size in mm. */
+ int tp_high; /* Y direction physical size in mm. */
+ int tp_max_abs_x; /* Max X absolute units that can be reported. */
+ int tp_max_abs_y; /* Max Y absolute units that can be reported. */
+
+ int tp_res_x; /* X resolution in units/mm. */
+ int tp_res_y; /* Y resolution in units/mm. */
+
+ int tp_metrics_supported;
+};
+
+
+#ifdef CONFIG_MOUSE_PS2_CYPRESS
+int cypress_detect(struct psmouse *psmouse, bool set_properties);
+int cypress_init(struct psmouse *psmouse);
+bool cypress_supported(void);
+#else
+inline int cypress_detect(struct psmouse *psmouse, bool set_properties)
+{
+ return -ENOSYS;
+}
+inline int cypress_init(struct psmouse *psmouse)
+{
+ return -ENOSYS;
+}
+inline bool cypress_supported(void)
+{
+ return 0;
+}
+#endif /* CONFIG_MOUSE_PS2_CYPRESS */
+
+#endif /* _CYPRESS_PS2_H */
diff --git a/drivers/input/mouse/psmouse-base.c b/drivers/input/mouse/psmouse-base.c
index 22fe2547e169..cff065f6261c 100644
--- a/drivers/input/mouse/psmouse-base.c
+++ b/drivers/input/mouse/psmouse-base.c
@@ -34,6 +34,7 @@
#include "touchkit_ps2.h"
#include "elantech.h"
#include "sentelic.h"
+#include "cypress_ps2.h"
#define DRIVER_DESC "PS/2 mouse driver"
@@ -759,6 +760,28 @@ static int psmouse_extensions(struct psmouse *psmouse,
}
/*
+ * Try Cypress Trackpad.
+ * Must try it before Finger Sensing Pad because Finger Sensing Pad probe
+ * upsets some modules of Cypress Trackpads.
+ */
+ if (max_proto > PSMOUSE_IMEX &&
+ cypress_detect(psmouse, set_properties) == 0) {
+ if (cypress_supported()) {
+ if (cypress_init(psmouse) == 0)
+ return PSMOUSE_CYPRESS;
+
+ /*
+ * Finger Sensing Pad probe upsets some modules of
+ * Cypress Trackpad, must avoid Finger Sensing Pad
+ * probe if Cypress Trackpad device detected.
+ */
+ return PSMOUSE_PS2;
+ }
+
+ max_proto = PSMOUSE_IMEX;
+ }
+
+/*
* Try ALPS TouchPad
*/
if (max_proto > PSMOUSE_IMEX) {
@@ -896,6 +919,15 @@ static const struct psmouse_protocol psmouse_protocols[] = {
.alias = "thinkps",
.detect = thinking_detect,
},
+#ifdef CONFIG_MOUSE_PS2_CYPRESS
+ {
+ .type = PSMOUSE_CYPRESS,
+ .name = "CyPS/2",
+ .alias = "cypress",
+ .detect = cypress_detect,
+ .init = cypress_init,
+ },
+#endif
{
.type = PSMOUSE_GENPS,
.name = "GenPS/2",
diff --git a/drivers/input/mouse/psmouse.h b/drivers/input/mouse/psmouse.h
index fe1df231ba4c..2f0b39d59a9b 100644
--- a/drivers/input/mouse/psmouse.h
+++ b/drivers/input/mouse/psmouse.h
@@ -95,6 +95,7 @@ enum psmouse_type {
PSMOUSE_ELANTECH,
PSMOUSE_FSP,
PSMOUSE_SYNAPTICS_RELATIVE,
+ PSMOUSE_CYPRESS,
PSMOUSE_AUTO /* This one should always be last */
};
diff --git a/drivers/input/mouse/synaptics.c b/drivers/input/mouse/synaptics.c
index 12d12ca3fee0..2f78538e09d0 100644
--- a/drivers/input/mouse/synaptics.c
+++ b/drivers/input/mouse/synaptics.c
@@ -722,11 +722,13 @@ static void synaptics_report_mt_data(struct psmouse *psmouse,
default:
/*
* If the finger slot contained in SGM is valid, and either
- * hasn't changed, or is new, then report SGM in MTB slot 0.
+ * hasn't changed, or is new, or the old SGM has now moved to
+ * AGM, then report SGM in MTB slot 0.
* Otherwise, empty MTB slot 0.
*/
if (mt_state->sgm != -1 &&
- (mt_state->sgm == old->sgm || old->sgm == -1))
+ (mt_state->sgm == old->sgm ||
+ old->sgm == -1 || mt_state->agm == old->sgm))
synaptics_report_slot(dev, 0, sgm);
else
synaptics_report_slot(dev, 0, NULL);
@@ -735,9 +737,31 @@ static void synaptics_report_mt_data(struct psmouse *psmouse,
* If the finger slot contained in AGM is valid, and either
* hasn't changed, or is new, then report AGM in MTB slot 1.
* Otherwise, empty MTB slot 1.
+ *
+ * However, in the case where the AGM is new, make sure that
+ * that it is either the same as the old SGM, or there was no
+ * SGM.
+ *
+ * Otherwise, if the SGM was just 1, and the new AGM is 2, then
+ * the new AGM will keep the old SGM's tracking ID, which can
+ * cause apparent drumroll. This happens if in the following
+ * valid finger sequence:
+ *
+ * Action SGM AGM (MTB slot:Contact)
+ * 1. Touch contact 0 (0:0)
+ * 2. Touch contact 1 (0:0, 1:1)
+ * 3. Lift contact 0 (1:1)
+ * 4. Touch contacts 2,3 (0:2, 1:3)
+ *
+ * In step 4, contact 3, in AGM must not be given the same
+ * tracking ID as contact 1 had in step 3. To avoid this,
+ * the first agm with contact 3 is dropped and slot 1 is
+ * invalidated (tracking ID = -1).
*/
if (mt_state->agm != -1 &&
- (mt_state->agm == old->agm || old->agm == -1))
+ (mt_state->agm == old->agm ||
+ (old->agm == -1 &&
+ (old->sgm == -1 || mt_state->agm == old->sgm))))
synaptics_report_slot(dev, 1, agm);
else
synaptics_report_slot(dev, 1, NULL);
@@ -1247,11 +1271,11 @@ static void set_input_params(struct input_dev *dev, struct synaptics_data *priv)
input_set_abs_params(dev, ABS_PRESSURE, 0, 255, 0, 0);
if (SYN_CAP_IMAGE_SENSOR(priv->ext_cap_0c)) {
- input_mt_init_slots(dev, 2, 0);
set_abs_position_params(dev, priv, ABS_MT_POSITION_X,
ABS_MT_POSITION_Y);
/* Image sensors can report per-contact pressure */
input_set_abs_params(dev, ABS_MT_PRESSURE, 0, 255, 0, 0);
+ input_mt_init_slots(dev, 2, INPUT_MT_POINTER);
/* Image sensors can signal 4 and 5 finger clicks */
__set_bit(BTN_TOOL_QUADTAP, dev->keybit);
diff --git a/drivers/input/serio/Kconfig b/drivers/input/serio/Kconfig
index 4a4e182c33e7..560c243bfcaf 100644
--- a/drivers/input/serio/Kconfig
+++ b/drivers/input/serio/Kconfig
@@ -236,6 +236,7 @@ config SERIO_PS2MULT
config SERIO_ARC_PS2
tristate "ARC PS/2 support"
+ depends on GENERIC_HARDIRQS
help
Say Y here if you have an ARC FPGA platform with a PS/2
controller in it.
diff --git a/drivers/input/tablet/wacom_wac.c b/drivers/input/tablet/wacom_wac.c
index 264138f3217e..41b6fbf60112 100644
--- a/drivers/input/tablet/wacom_wac.c
+++ b/drivers/input/tablet/wacom_wac.c
@@ -359,6 +359,7 @@ static int wacom_intuos_inout(struct wacom_wac *wacom)
case 0x802: /* Intuos4 General Pen */
case 0x804: /* Intuos4 Marker Pen */
case 0x40802: /* Intuos4 Classic Pen */
+ case 0x18803: /* DTH2242 Grip Pen */
case 0x022:
wacom->tool[idx] = BTN_TOOL_PEN;
break;
@@ -538,6 +539,13 @@ static int wacom_intuos_irq(struct wacom_wac *wacom)
input_report_key(input, wacom->tool[1], 0);
input_report_abs(input, ABS_MISC, 0);
}
+ } else if (features->type == DTK) {
+ input_report_key(input, BTN_0, (data[6] & 0x01));
+ input_report_key(input, BTN_1, (data[6] & 0x02));
+ input_report_key(input, BTN_2, (data[6] & 0x04));
+ input_report_key(input, BTN_3, (data[6] & 0x08));
+ input_report_key(input, BTN_4, (data[6] & 0x10));
+ input_report_key(input, BTN_5, (data[6] & 0x20));
} else if (features->type == WACOM_24HD) {
input_report_key(input, BTN_0, (data[6] & 0x01));
input_report_key(input, BTN_1, (data[6] & 0x02));
@@ -785,25 +793,6 @@ static int wacom_intuos_irq(struct wacom_wac *wacom)
return 1;
}
-static int find_slot_from_contactid(struct wacom_wac *wacom, int contactid)
-{
- int touch_max = wacom->features.touch_max;
- int i;
-
- if (!wacom->slots)
- return -1;
-
- for (i = 0; i < touch_max; ++i) {
- if (wacom->slots[i] == contactid)
- return i;
- }
- for (i = 0; i < touch_max; ++i) {
- if (wacom->slots[i] == -1)
- return i;
- }
- return -1;
-}
-
static int int_dist(int x1, int y1, int x2, int y2)
{
int x = x2 - x1;
@@ -833,8 +822,7 @@ static int wacom_24hdt_irq(struct wacom_wac *wacom)
for (i = 0; i < contacts_to_send; i++) {
int offset = (WACOM_BYTES_PER_24HDT_PACKET * i) + 1;
bool touch = data[offset] & 0x1 && !wacom->shared->stylus_in_proximity;
- int id = data[offset + 1];
- int slot = find_slot_from_contactid(wacom, id);
+ int slot = input_mt_get_slot_by_key(input, data[offset + 1]);
if (slot < 0)
continue;
@@ -856,9 +844,7 @@ static int wacom_24hdt_irq(struct wacom_wac *wacom)
input_report_abs(input, ABS_MT_WIDTH_MINOR, min(w, h));
input_report_abs(input, ABS_MT_ORIENTATION, w > h);
}
- wacom->slots[slot] = touch ? id : -1;
}
-
input_mt_report_pointer_emulation(input, true);
wacom->num_contacts_left -= contacts_to_send;
@@ -895,7 +881,7 @@ static int wacom_mt_touch(struct wacom_wac *wacom)
int offset = (WACOM_BYTES_PER_MT_PACKET + x_offset) * i + 3;
bool touch = data[offset] & 0x1;
int id = le16_to_cpup((__le16 *)&data[offset + 1]);
- int slot = find_slot_from_contactid(wacom, id);
+ int slot = input_mt_get_slot_by_key(input, id);
if (slot < 0)
continue;
@@ -908,9 +894,7 @@ static int wacom_mt_touch(struct wacom_wac *wacom)
input_report_abs(input, ABS_MT_POSITION_X, x);
input_report_abs(input, ABS_MT_POSITION_Y, y);
}
- wacom->slots[slot] = touch ? id : -1;
}
-
input_mt_report_pointer_emulation(input, true);
wacom->num_contacts_left -= contacts_to_send;
@@ -942,12 +926,11 @@ static int wacom_tpc_mt_touch(struct wacom_wac *wacom)
contact_with_no_pen_down_count++;
}
}
+ input_mt_report_pointer_emulation(input, true);
/* keep touch state for pen event */
wacom->shared->touch_down = (contact_with_no_pen_down_count > 0);
- input_mt_report_pointer_emulation(input, true);
-
return 1;
}
@@ -1104,12 +1087,15 @@ static int wacom_bpt_touch(struct wacom_wac *wacom)
static void wacom_bpt3_touch_msg(struct wacom_wac *wacom, unsigned char *data)
{
struct input_dev *input = wacom->input;
- int slot_id = data[0] - 2; /* data[0] is between 2 and 17 */
bool touch = data[1] & 0x80;
+ int slot = input_mt_get_slot_by_key(input, data[0]);
+
+ if (slot < 0)
+ return;
touch = touch && !wacom->shared->stylus_in_proximity;
- input_mt_slot(input, slot_id);
+ input_mt_slot(input, slot);
input_mt_report_slot_state(input, MT_TOOL_FINGER, touch);
if (touch) {
@@ -1162,7 +1148,6 @@ static int wacom_bpt3_touch(struct wacom_wac *wacom)
wacom_bpt3_button_msg(wacom, data + offset);
}
-
input_mt_report_pointer_emulation(input, true);
input_sync(input);
@@ -1319,6 +1304,7 @@ void wacom_wac_irq(struct wacom_wac *wacom_wac, size_t len)
case WACOM_21UX2:
case WACOM_22HD:
case WACOM_24HD:
+ case DTK:
sync = wacom_intuos_irq(wacom_wac);
break;
@@ -1444,39 +1430,64 @@ static unsigned int wacom_calculate_touch_res(unsigned int logical_max,
return (logical_max * 100) / physical_max;
}
-int wacom_setup_input_capabilities(struct input_dev *input_dev,
- struct wacom_wac *wacom_wac)
+static void wacom_abs_set_axis(struct input_dev *input_dev,
+ struct wacom_wac *wacom_wac)
{
struct wacom_features *features = &wacom_wac->features;
- int i;
-
- input_dev->evbit[0] |= BIT_MASK(EV_KEY) | BIT_MASK(EV_ABS);
-
- __set_bit(BTN_TOUCH, input_dev->keybit);
-
- input_set_abs_params(input_dev, ABS_X, 0, features->x_max,
- features->x_fuzz, 0);
- input_set_abs_params(input_dev, ABS_Y, 0, features->y_max,
- features->y_fuzz, 0);
if (features->device_type == BTN_TOOL_PEN) {
- input_set_abs_params(input_dev, ABS_PRESSURE, 0, features->pressure_max,
- features->pressure_fuzz, 0);
+ input_set_abs_params(input_dev, ABS_X, 0, features->x_max,
+ features->x_fuzz, 0);
+ input_set_abs_params(input_dev, ABS_Y, 0, features->y_max,
+ features->y_fuzz, 0);
+ input_set_abs_params(input_dev, ABS_PRESSURE, 0,
+ features->pressure_max, features->pressure_fuzz, 0);
/* penabled devices have fixed resolution for each model */
input_abs_set_res(input_dev, ABS_X, features->x_resolution);
input_abs_set_res(input_dev, ABS_Y, features->y_resolution);
} else {
- input_abs_set_res(input_dev, ABS_X,
- wacom_calculate_touch_res(features->x_max,
- features->x_phy));
- input_abs_set_res(input_dev, ABS_Y,
- wacom_calculate_touch_res(features->y_max,
- features->y_phy));
+ if (features->touch_max <= 2) {
+ input_set_abs_params(input_dev, ABS_X, 0,
+ features->x_max, features->x_fuzz, 0);
+ input_set_abs_params(input_dev, ABS_Y, 0,
+ features->y_max, features->y_fuzz, 0);
+ input_abs_set_res(input_dev, ABS_X,
+ wacom_calculate_touch_res(features->x_max,
+ features->x_phy));
+ input_abs_set_res(input_dev, ABS_Y,
+ wacom_calculate_touch_res(features->y_max,
+ features->y_phy));
+ }
+
+ if (features->touch_max > 1) {
+ input_set_abs_params(input_dev, ABS_MT_POSITION_X, 0,
+ features->x_max, features->x_fuzz, 0);
+ input_set_abs_params(input_dev, ABS_MT_POSITION_Y, 0,
+ features->y_max, features->y_fuzz, 0);
+ input_abs_set_res(input_dev, ABS_MT_POSITION_X,
+ wacom_calculate_touch_res(features->x_max,
+ features->x_phy));
+ input_abs_set_res(input_dev, ABS_MT_POSITION_Y,
+ wacom_calculate_touch_res(features->y_max,
+ features->y_phy));
+ }
}
+}
+int wacom_setup_input_capabilities(struct input_dev *input_dev,
+ struct wacom_wac *wacom_wac)
+{
+ struct wacom_features *features = &wacom_wac->features;
+ int i;
+
+ input_dev->evbit[0] |= BIT_MASK(EV_KEY) | BIT_MASK(EV_ABS);
+
+ __set_bit(BTN_TOUCH, input_dev->keybit);
__set_bit(ABS_MISC, input_dev->absbit);
+ wacom_abs_set_axis(input_dev, wacom_wac);
+
switch (wacom_wac->features.type) {
case WACOM_MO:
input_set_abs_params(input_dev, ABS_WHEEL, 0, 71, 0, 0);
@@ -1513,12 +1524,17 @@ int wacom_setup_input_capabilities(struct input_dev *input_dev,
__set_bit(BTN_Y, input_dev->keybit);
__set_bit(BTN_Z, input_dev->keybit);
- for (i = 0; i < 10; i++)
+ for (i = 6; i < 10; i++)
__set_bit(BTN_0 + i, input_dev->keybit);
__set_bit(KEY_PROG1, input_dev->keybit);
__set_bit(KEY_PROG2, input_dev->keybit);
__set_bit(KEY_PROG3, input_dev->keybit);
+ /* fall through */
+
+ case DTK:
+ for (i = 0; i < 6; i++)
+ __set_bit(BTN_0 + i, input_dev->keybit);
input_set_abs_params(input_dev, ABS_Z, -900, 899, 0, 0);
input_set_abs_params(input_dev, ABS_THROTTLE, 0, 71, 0, 0);
@@ -1614,24 +1630,11 @@ int wacom_setup_input_capabilities(struct input_dev *input_dev,
} else if (features->device_type == BTN_TOOL_FINGER) {
__clear_bit(ABS_MISC, input_dev->absbit);
- __set_bit(BTN_TOOL_FINGER, input_dev->keybit);
- __set_bit(BTN_TOOL_DOUBLETAP, input_dev->keybit);
- __set_bit(BTN_TOOL_TRIPLETAP, input_dev->keybit);
- __set_bit(BTN_TOOL_QUADTAP, input_dev->keybit);
-
- input_mt_init_slots(input_dev, features->touch_max, 0);
-
input_set_abs_params(input_dev, ABS_MT_TOUCH_MAJOR,
0, features->x_max, 0, 0);
input_set_abs_params(input_dev, ABS_MT_TOUCH_MINOR,
0, features->y_max, 0, 0);
-
- input_set_abs_params(input_dev, ABS_MT_POSITION_X,
- 0, features->x_max,
- features->x_fuzz, 0);
- input_set_abs_params(input_dev, ABS_MT_POSITION_Y,
- 0, features->y_max,
- features->y_fuzz, 0);
+ input_mt_init_slots(input_dev, features->touch_max, INPUT_MT_POINTER);
}
break;
@@ -1662,27 +1665,14 @@ int wacom_setup_input_capabilities(struct input_dev *input_dev,
case MTSCREEN:
case MTTPC:
- if (features->device_type == BTN_TOOL_FINGER) {
- wacom_wac->slots = kmalloc(features->touch_max *
- sizeof(int),
- GFP_KERNEL);
- if (!wacom_wac->slots)
- return -ENOMEM;
-
- for (i = 0; i < features->touch_max; i++)
- wacom_wac->slots[i] = -1;
- }
- /* fall through */
-
case TABLETPC2FG:
if (features->device_type == BTN_TOOL_FINGER) {
- input_mt_init_slots(input_dev, features->touch_max, 0);
- input_set_abs_params(input_dev, ABS_MT_TOOL_TYPE,
- 0, MT_TOOL_MAX, 0, 0);
- input_set_abs_params(input_dev, ABS_MT_POSITION_X,
- 0, features->x_max, 0, 0);
- input_set_abs_params(input_dev, ABS_MT_POSITION_Y,
- 0, features->y_max, 0, 0);
+ unsigned int flags = INPUT_MT_DIRECT;
+
+ if (wacom_wac->features.type == TABLETPC2FG)
+ flags = 0;
+
+ input_mt_init_slots(input_dev, features->touch_max, flags);
}
/* fall through */
@@ -1725,35 +1715,26 @@ int wacom_setup_input_capabilities(struct input_dev *input_dev,
__set_bit(INPUT_PROP_POINTER, input_dev->propbit);
if (features->device_type == BTN_TOOL_FINGER) {
+ unsigned int flags = INPUT_MT_POINTER;
+
__set_bit(BTN_LEFT, input_dev->keybit);
__set_bit(BTN_FORWARD, input_dev->keybit);
__set_bit(BTN_BACK, input_dev->keybit);
__set_bit(BTN_RIGHT, input_dev->keybit);
- __set_bit(BTN_TOOL_FINGER, input_dev->keybit);
- __set_bit(BTN_TOOL_DOUBLETAP, input_dev->keybit);
- input_mt_init_slots(input_dev, features->touch_max, 0);
-
if (features->pktlen == WACOM_PKGLEN_BBTOUCH3) {
- __set_bit(BTN_TOOL_TRIPLETAP,
- input_dev->keybit);
- __set_bit(BTN_TOOL_QUADTAP,
- input_dev->keybit);
-
input_set_abs_params(input_dev,
ABS_MT_TOUCH_MAJOR,
0, features->x_max, 0, 0);
input_set_abs_params(input_dev,
ABS_MT_TOUCH_MINOR,
0, features->y_max, 0, 0);
+ } else {
+ __set_bit(BTN_TOOL_FINGER, input_dev->keybit);
+ __set_bit(BTN_TOOL_DOUBLETAP, input_dev->keybit);
+ flags = 0;
}
-
- input_set_abs_params(input_dev, ABS_MT_POSITION_X,
- 0, features->x_max,
- features->x_fuzz, 0);
- input_set_abs_params(input_dev, ABS_MT_POSITION_Y,
- 0, features->y_max,
- features->y_fuzz, 0);
+ input_mt_init_slots(input_dev, features->touch_max, flags);
} else if (features->device_type == BTN_TOOL_PEN) {
__set_bit(BTN_TOOL_RUBBER, input_dev->keybit);
__set_bit(BTN_TOOL_PEN, input_dev->keybit);
@@ -1978,6 +1959,13 @@ static const struct wacom_features wacom_features_0xCE =
static const struct wacom_features wacom_features_0xF0 =
{ "Wacom DTU1631", WACOM_PKGLEN_GRAPHIRE, 34623, 19553, 511,
0, DTU, WACOM_INTUOS_RES, WACOM_INTUOS_RES };
+static const struct wacom_features wacom_features_0x59 = /* Pen */
+ { "Wacom DTH2242", WACOM_PKGLEN_INTUOS, 95840, 54260, 2047,
+ 63, DTK, WACOM_INTUOS3_RES, WACOM_INTUOS3_RES,
+ .oVid = USB_VENDOR_ID_WACOM, .oPid = 0x5D };
+static const struct wacom_features wacom_features_0x5D = /* Touch */
+ { "Wacom DTH2242", .type = WACOM_24HDT,
+ .oVid = USB_VENDOR_ID_WACOM, .oPid = 0x59, .touch_max = 10 };
static const struct wacom_features wacom_features_0xCC =
{ "Wacom Cintiq 21UX2", WACOM_PKGLEN_INTUOS, 87200, 65600, 2047,
63, WACOM_21UX2, WACOM_INTUOS3_RES, WACOM_INTUOS3_RES };
@@ -2152,6 +2140,8 @@ const struct usb_device_id wacom_ids[] = {
{ USB_DEVICE_WACOM(0x43) },
{ USB_DEVICE_WACOM(0x44) },
{ USB_DEVICE_WACOM(0x45) },
+ { USB_DEVICE_WACOM(0x59) },
+ { USB_DEVICE_WACOM(0x5D) },
{ USB_DEVICE_WACOM(0xB0) },
{ USB_DEVICE_WACOM(0xB1) },
{ USB_DEVICE_WACOM(0xB2) },
diff --git a/drivers/input/tablet/wacom_wac.h b/drivers/input/tablet/wacom_wac.h
index 9396d7769f86..5f9a7721e16c 100644
--- a/drivers/input/tablet/wacom_wac.h
+++ b/drivers/input/tablet/wacom_wac.h
@@ -78,6 +78,7 @@ enum {
INTUOS5L,
WACOM_21UX2,
WACOM_22HD,
+ DTK,
WACOM_24HD,
CINTIQ,
WACOM_BEE,
@@ -135,7 +136,6 @@ struct wacom_wac {
int pid;
int battery_capacity;
int num_contacts_left;
- int *slots;
};
#endif
diff --git a/drivers/input/touchscreen/Kconfig b/drivers/input/touchscreen/Kconfig
index b93b5988ace8..9a647ee1136a 100644
--- a/drivers/input/touchscreen/Kconfig
+++ b/drivers/input/touchscreen/Kconfig
@@ -359,7 +359,7 @@ config TOUCHSCREEN_MCS5000
config TOUCHSCREEN_MMS114
tristate "MELFAS MMS114 touchscreen"
- depends on I2C
+ depends on I2C && GENERIC_HARDIRQS
help
Say Y here if you have the MELFAS MMS114 touchscreen controller
chip in your system.
diff --git a/drivers/input/touchscreen/cyttsp_spi.c b/drivers/input/touchscreen/cyttsp_spi.c
index 638e20310f12..861b7f77605b 100644
--- a/drivers/input/touchscreen/cyttsp_spi.c
+++ b/drivers/input/touchscreen/cyttsp_spi.c
@@ -193,7 +193,6 @@ static struct spi_driver cyttsp_spi_driver = {
module_spi_driver(cyttsp_spi_driver);
-MODULE_ALIAS("spi:cyttsp");
MODULE_LICENSE("GPL");
MODULE_DESCRIPTION("Cypress TrueTouch(R) Standard Product (TTSP) SPI driver");
MODULE_AUTHOR("Cypress");
diff --git a/drivers/input/touchscreen/mms114.c b/drivers/input/touchscreen/mms114.c
index 98841d8aa635..4a29ddf6bf1e 100644
--- a/drivers/input/touchscreen/mms114.c
+++ b/drivers/input/touchscreen/mms114.c
@@ -429,12 +429,12 @@ static int mms114_probe(struct i2c_client *client,
return -ENODEV;
}
- data = kzalloc(sizeof(struct mms114_data), GFP_KERNEL);
- input_dev = input_allocate_device();
+ data = devm_kzalloc(&client->dev, sizeof(struct mms114_data),
+ GFP_KERNEL);
+ input_dev = devm_input_allocate_device(&client->dev);
if (!data || !input_dev) {
dev_err(&client->dev, "Failed to allocate memory\n");
- error = -ENOMEM;
- goto err_free_mem;
+ return -ENOMEM;
}
data->client = client;
@@ -466,57 +466,36 @@ static int mms114_probe(struct i2c_client *client,
input_set_drvdata(input_dev, data);
i2c_set_clientdata(client, data);
- data->core_reg = regulator_get(&client->dev, "avdd");
+ data->core_reg = devm_regulator_get(&client->dev, "avdd");
if (IS_ERR(data->core_reg)) {
error = PTR_ERR(data->core_reg);
dev_err(&client->dev,
"Unable to get the Core regulator (%d)\n", error);
- goto err_free_mem;
+ return error;
}
- data->io_reg = regulator_get(&client->dev, "vdd");
+ data->io_reg = devm_regulator_get(&client->dev, "vdd");
if (IS_ERR(data->io_reg)) {
error = PTR_ERR(data->io_reg);
dev_err(&client->dev,
"Unable to get the IO regulator (%d)\n", error);
- goto err_core_reg;
+ return error;
}
- error = request_threaded_irq(client->irq, NULL, mms114_interrupt,
- IRQF_TRIGGER_FALLING | IRQF_ONESHOT, "mms114", data);
+ error = devm_request_threaded_irq(&client->dev, client->irq, NULL,
+ mms114_interrupt, IRQF_TRIGGER_FALLING | IRQF_ONESHOT,
+ dev_name(&client->dev), data);
if (error) {
dev_err(&client->dev, "Failed to register interrupt\n");
- goto err_io_reg;
+ return error;
}
disable_irq(client->irq);
error = input_register_device(data->input_dev);
- if (error)
- goto err_free_irq;
-
- return 0;
-
-err_free_irq:
- free_irq(client->irq, data);
-err_io_reg:
- regulator_put(data->io_reg);
-err_core_reg:
- regulator_put(data->core_reg);
-err_free_mem:
- input_free_device(input_dev);
- kfree(data);
- return error;
-}
-
-static int mms114_remove(struct i2c_client *client)
-{
- struct mms114_data *data = i2c_get_clientdata(client);
-
- free_irq(client->irq, data);
- regulator_put(data->io_reg);
- regulator_put(data->core_reg);
- input_unregister_device(data->input_dev);
- kfree(data);
+ if (error) {
+ dev_err(&client->dev, "Failed to register input device\n");
+ return error;
+ }
return 0;
}
@@ -590,7 +569,6 @@ static struct i2c_driver mms114_driver = {
.of_match_table = of_match_ptr(mms114_dt_match),
},
.probe = mms114_probe,
- .remove = mms114_remove,
.id_table = mms114_id,
};
diff --git a/drivers/input/touchscreen/stmpe-ts.c b/drivers/input/touchscreen/stmpe-ts.c
index 84d884b4ec3e..59e81b00f244 100644
--- a/drivers/input/touchscreen/stmpe-ts.c
+++ b/drivers/input/touchscreen/stmpe-ts.c
@@ -120,6 +120,7 @@ static void stmpe_work(struct work_struct *work)
__stmpe_reset_fifo(ts->stmpe);
input_report_abs(ts->idev, ABS_PRESSURE, 0);
+ input_report_key(ts->idev, BTN_TOUCH, 0);
input_sync(ts->idev);
}
@@ -153,6 +154,7 @@ static irqreturn_t stmpe_ts_handler(int irq, void *data)
input_report_abs(ts->idev, ABS_X, x);
input_report_abs(ts->idev, ABS_Y, y);
input_report_abs(ts->idev, ABS_PRESSURE, z);
+ input_report_key(ts->idev, BTN_TOUCH, 1);
input_sync(ts->idev);
/* flush the FIFO after we have read out our values. */
diff --git a/drivers/input/touchscreen/tsc2005.c b/drivers/input/touchscreen/tsc2005.c
index 9c0cdc7ea449..7213e8b07e79 100644
--- a/drivers/input/touchscreen/tsc2005.c
+++ b/drivers/input/touchscreen/tsc2005.c
@@ -753,3 +753,4 @@ module_spi_driver(tsc2005_driver);
MODULE_AUTHOR("Lauri Leukkunen <lauri.leukkunen@nokia.com>");
MODULE_DESCRIPTION("TSC2005 Touchscreen Driver");
MODULE_LICENSE("GPL");
+MODULE_ALIAS("spi:tsc2005");
diff --git a/drivers/input/touchscreen/wm831x-ts.c b/drivers/input/touchscreen/wm831x-ts.c
index f88fab56178c..6be2eb6a153a 100644
--- a/drivers/input/touchscreen/wm831x-ts.c
+++ b/drivers/input/touchscreen/wm831x-ts.c
@@ -247,7 +247,7 @@ static int wm831x_ts_probe(struct platform_device *pdev)
wm831x_ts = devm_kzalloc(&pdev->dev, sizeof(struct wm831x_ts),
GFP_KERNEL);
- input_dev = input_allocate_device();
+ input_dev = devm_input_allocate_device(&pdev->dev);
if (!wm831x_ts || !input_dev) {
error = -ENOMEM;
goto err_alloc;
@@ -376,7 +376,6 @@ err_pd_irq:
err_data_irq:
free_irq(wm831x_ts->data_irq, wm831x_ts);
err_alloc:
- input_free_device(input_dev);
return error;
}
@@ -387,7 +386,6 @@ static int wm831x_ts_remove(struct platform_device *pdev)
free_irq(wm831x_ts->pd_irq, wm831x_ts);
free_irq(wm831x_ts->data_irq, wm831x_ts);
- input_unregister_device(wm831x_ts->input_dev);
return 0;
}
diff --git a/drivers/tty/sysrq.c b/drivers/tty/sysrq.c
index 16ee6cee07da..77fcad4371ce 100644
--- a/drivers/tty/sysrq.c
+++ b/drivers/tty/sysrq.c
@@ -41,6 +41,7 @@
#include <linux/slab.h>
#include <linux/input.h>
#include <linux/uaccess.h>
+#include <linux/moduleparam.h>
#include <asm/ptrace.h>
#include <asm/irq_regs.h>
@@ -576,8 +577,71 @@ struct sysrq_state {
bool active;
bool need_reinject;
bool reinjecting;
+
+ /* reset sequence handling */
+ bool reset_canceled;
+ unsigned long reset_keybit[BITS_TO_LONGS(KEY_CNT)];
+ int reset_seq_len;
+ int reset_seq_cnt;
+ int reset_seq_version;
};
+#define SYSRQ_KEY_RESET_MAX 20 /* Should be plenty */
+static unsigned short sysrq_reset_seq[SYSRQ_KEY_RESET_MAX];
+static unsigned int sysrq_reset_seq_len;
+static unsigned int sysrq_reset_seq_version = 1;
+
+static void sysrq_parse_reset_sequence(struct sysrq_state *state)
+{
+ int i;
+ unsigned short key;
+
+ state->reset_seq_cnt = 0;
+
+ for (i = 0; i < sysrq_reset_seq_len; i++) {
+ key = sysrq_reset_seq[i];
+
+ if (key == KEY_RESERVED || key > KEY_MAX)
+ break;
+
+ __set_bit(key, state->reset_keybit);
+ state->reset_seq_len++;
+
+ if (test_bit(key, state->key_down))
+ state->reset_seq_cnt++;
+ }
+
+ /* Disable reset until old keys are not released */
+ state->reset_canceled = state->reset_seq_cnt != 0;
+
+ state->reset_seq_version = sysrq_reset_seq_version;
+}
+
+static bool sysrq_detect_reset_sequence(struct sysrq_state *state,
+ unsigned int code, int value)
+{
+ if (!test_bit(code, state->reset_keybit)) {
+ /*
+ * Pressing any key _not_ in reset sequence cancels
+ * the reset sequence.
+ */
+ if (value && state->reset_seq_cnt)
+ state->reset_canceled = true;
+ } else if (value == 0) {
+ /* key release */
+ if (--state->reset_seq_cnt == 0)
+ state->reset_canceled = false;
+ } else if (value == 1) {
+ /* key press, not autorepeat */
+ if (++state->reset_seq_cnt == state->reset_seq_len &&
+ !state->reset_canceled) {
+ return true;
+ }
+ }
+
+ return false;
+}
+
static void sysrq_reinject_alt_sysrq(struct work_struct *work)
{
struct sysrq_state *sysrq =
@@ -604,100 +668,121 @@ static void sysrq_reinject_alt_sysrq(struct work_struct *work)
}
}
-static bool sysrq_filter(struct input_handle *handle,
- unsigned int type, unsigned int code, int value)
+static bool sysrq_handle_keypress(struct sysrq_state *sysrq,
+ unsigned int code, int value)
{
- struct sysrq_state *sysrq = handle->private;
bool was_active = sysrq->active;
bool suppress;
- /*
- * Do not filter anything if we are in the process of re-injecting
- * Alt+SysRq combination.
- */
- if (sysrq->reinjecting)
- return false;
+ switch (code) {
- switch (type) {
+ case KEY_LEFTALT:
+ case KEY_RIGHTALT:
+ if (!value) {
+ /* One of ALTs is being released */
+ if (sysrq->active && code == sysrq->alt_use)
+ sysrq->active = false;
- case EV_SYN:
- suppress = false;
+ sysrq->alt = KEY_RESERVED;
+
+ } else if (value != 2) {
+ sysrq->alt = code;
+ sysrq->need_reinject = false;
+ }
break;
- case EV_KEY:
- switch (code) {
+ case KEY_SYSRQ:
+ if (value == 1 && sysrq->alt != KEY_RESERVED) {
+ sysrq->active = true;
+ sysrq->alt_use = sysrq->alt;
+ /*
+ * If nothing else will be pressed we'll need
+ * to re-inject Alt-SysRq keysroke.
+ */
+ sysrq->need_reinject = true;
+ }
- case KEY_LEFTALT:
- case KEY_RIGHTALT:
- if (!value) {
- /* One of ALTs is being released */
- if (sysrq->active && code == sysrq->alt_use)
- sysrq->active = false;
+ /*
+ * Pretend that sysrq was never pressed at all. This
+ * is needed to properly handle KGDB which will try
+ * to release all keys after exiting debugger. If we
+ * do not clear key bit it KGDB will end up sending
+ * release events for Alt and SysRq, potentially
+ * triggering print screen function.
+ */
+ if (sysrq->active)
+ clear_bit(KEY_SYSRQ, sysrq->handle.dev->key);
- sysrq->alt = KEY_RESERVED;
+ break;
- } else if (value != 2) {
- sysrq->alt = code;
- sysrq->need_reinject = false;
- }
- break;
+ default:
+ if (sysrq->active && value && value != 2) {
+ sysrq->need_reinject = false;
+ __handle_sysrq(sysrq_xlate[code], true);
+ }
+ break;
+ }
- case KEY_SYSRQ:
- if (value == 1 && sysrq->alt != KEY_RESERVED) {
- sysrq->active = true;
- sysrq->alt_use = sysrq->alt;
- /*
- * If nothing else will be pressed we'll need
- * to re-inject Alt-SysRq keysroke.
- */
- sysrq->need_reinject = true;
- }
+ suppress = sysrq->active;
- /*
- * Pretend that sysrq was never pressed at all. This
- * is needed to properly handle KGDB which will try
- * to release all keys after exiting debugger. If we
- * do not clear key bit it KGDB will end up sending
- * release events for Alt and SysRq, potentially
- * triggering print screen function.
- */
- if (sysrq->active)
- clear_bit(KEY_SYSRQ, handle->dev->key);
+ if (!sysrq->active) {
- break;
+ /*
+ * See if reset sequence has changed since the last time.
+ */
+ if (sysrq->reset_seq_version != sysrq_reset_seq_version)
+ sysrq_parse_reset_sequence(sysrq);
- default:
- if (sysrq->active && value && value != 2) {
- sysrq->need_reinject = false;
- __handle_sysrq(sysrq_xlate[code], true);
- }
- break;
+ /*
+ * If we are not suppressing key presses keep track of
+ * keyboard state so we can release keys that have been
+ * pressed before entering SysRq mode.
+ */
+ if (value)
+ set_bit(code, sysrq->key_down);
+ else
+ clear_bit(code, sysrq->key_down);
+
+ if (was_active)
+ schedule_work(&sysrq->reinject_work);
+
+ if (sysrq_detect_reset_sequence(sysrq, code, value)) {
+ /* Force emergency reboot */
+ __handle_sysrq(sysrq_xlate[KEY_B], false);
}
- suppress = sysrq->active;
+ } else if (value == 0 && test_and_clear_bit(code, sysrq->key_down)) {
+ /*
+ * Pass on release events for keys that was pressed before
+ * entering SysRq mode.
+ */
+ suppress = false;
+ }
- if (!sysrq->active) {
- /*
- * If we are not suppressing key presses keep track of
- * keyboard state so we can release keys that have been
- * pressed before entering SysRq mode.
- */
- if (value)
- set_bit(code, sysrq->key_down);
- else
- clear_bit(code, sysrq->key_down);
+ return suppress;
+}
- if (was_active)
- schedule_work(&sysrq->reinject_work);
+static bool sysrq_filter(struct input_handle *handle,
+ unsigned int type, unsigned int code, int value)
+{
+ struct sysrq_state *sysrq = handle->private;
+ bool suppress;
- } else if (value == 0 &&
- test_and_clear_bit(code, sysrq->key_down)) {
- /*
- * Pass on release events for keys that was pressed before
- * entering SysRq mode.
- */
- suppress = false;
- }
+ /*
+ * Do not filter anything if we are in the process of re-injecting
+ * Alt+SysRq combination.
+ */
+ if (sysrq->reinjecting)
+ return false;
+
+ switch (type) {
+
+ case EV_SYN:
+ suppress = false;
+ break;
+
+ case EV_KEY:
+ suppress = sysrq_handle_keypress(sysrq, code, value);
break;
default:
@@ -785,7 +870,20 @@ static bool sysrq_handler_registered;
static inline void sysrq_register_handler(void)
{
+ extern unsigned short platform_sysrq_reset_seq[] __weak;
+ unsigned short key;
int error;
+ int i;
+
+ if (platform_sysrq_reset_seq) {
+ for (i = 0; i < ARRAY_SIZE(sysrq_reset_seq); i++) {
+ key = platform_sysrq_reset_seq[i];
+ if (key == KEY_RESERVED || key > KEY_MAX)
+ break;
+
+ sysrq_reset_seq[sysrq_reset_seq_len++] = key;
+ }
+ }
error = input_register_handler(&sysrq_handler);
if (error)
@@ -802,6 +900,36 @@ static inline void sysrq_unregister_handler(void)
}
}
+static int sysrq_reset_seq_param_set(const char *buffer,
+ const struct kernel_param *kp)
+{
+ unsigned long val;
+ int error;
+
+ error = strict_strtoul(buffer, 0, &val);
+ if (error < 0)
+ return error;
+
+ if (val > KEY_MAX)
+ return -EINVAL;
+
+ *((unsigned short *)kp->arg) = val;
+ sysrq_reset_seq_version++;
+
+ return 0;
+}
+
+static struct kernel_param_ops param_ops_sysrq_reset_seq = {
+ .get = param_get_ushort,
+ .set = sysrq_reset_seq_param_set,
+};
+
+#define param_check_sysrq_reset_seq(name, p) \
+ __param_check(name, p, unsigned short)
+
+module_param_array_named(reset_seq, sysrq_reset_seq, sysrq_reset_seq,
+ &sysrq_reset_seq_len, 0644);
+
#else
static inline void sysrq_register_handler(void)