diff options
41 files changed, 3607 insertions, 1181 deletions
diff --git a/Documentation/devicetree/bindings/input/imx-keypad.txt b/Documentation/devicetree/bindings/input/imx-keypad.txt new file mode 100644 index 00000000000..2ebaf7d2684 --- /dev/null +++ b/Documentation/devicetree/bindings/input/imx-keypad.txt @@ -0,0 +1,53 @@ +* Freescale i.MX Keypad Port(KPP) device tree bindings + +The KPP is designed to interface with a keypad matrix with 2-point contact +or 3-point contact keys. The KPP is designed to simplify the software task +of scanning a keypad matrix. The KPP is capable of detecting, debouncing, +and decoding one or multiple keys pressed simultaneously on a keypad. + +Required SoC Specific Properties: +- compatible: Should be "fsl,<soc>-kpp". + +- reg: Physical base address of the KPP and length of memory mapped +  region. + +- interrupts: The KPP interrupt number to the CPU(s). + +- clocks: The clock provided by the SoC to the KPP. Some SoCs use dummy +clock(The clock for the KPP is provided by the SoCs automatically). + +Required Board Specific Properties: +- pinctrl-names: The definition can be found at +pinctrl/pinctrl-bindings.txt. + +- pinctrl-0: The definition can be found at +pinctrl/pinctrl-bindings.txt. + +- linux,keymap: The definition can be found at +bindings/input/matrix-keymap.txt. + +Example: +kpp: kpp@73f94000 { +	compatible = "fsl,imx51-kpp", "fsl,imx21-kpp"; +	reg = <0x73f94000 0x4000>; +	interrupts = <60>; +	clocks = <&clks 0>; +	pinctrl-names = "default"; +	pinctrl-0 = <&pinctrl_kpp_1>; +	linux,keymap = <0x00000067	/* KEY_UP */ +			0x0001006c	/* KEY_DOWN */ +			0x00020072	/* KEY_VOLUMEDOWN */ +			0x00030066	/* KEY_HOME */ +			0x0100006a	/* KEY_RIGHT */ +			0x01010069	/* KEY_LEFT */ +			0x0102001c	/* KEY_ENTER */ +			0x01030073	/* KEY_VOLUMEUP */ +			0x02000040	/* KEY_F6 */ +			0x02010042	/* KEY_F8 */ +			0x02020043	/* KEY_F9 */ +			0x02030044	/* KEY_F10 */ +			0x0300003b	/* KEY_F1 */ +			0x0301003c	/* KEY_F2 */ +			0x0302003d	/* KEY_F3 */ +			0x03030074>;	/* KEY_POWER */ +}; diff --git a/Documentation/devicetree/bindings/input/nvidia,tegra20-kbc.txt b/Documentation/devicetree/bindings/input/nvidia,tegra20-kbc.txt index 72683be6de3..2995fae7ee4 100644 --- a/Documentation/devicetree/bindings/input/nvidia,tegra20-kbc.txt +++ b/Documentation/devicetree/bindings/input/nvidia,tegra20-kbc.txt @@ -1,7 +1,18 @@  * Tegra keyboard controller +The key controller has maximum 24 pins to make matrix keypad. Any pin +can be configured as row or column. The maximum column pin can be 8 +and maximum row pins can be 16 for Tegra20/Tegra30.  Required properties:  - compatible: "nvidia,tegra20-kbc" +- reg: Register base address of KBC. +- interrupts: Interrupt number for the KBC. +- nvidia,kbc-row-pins: The KBC pins which are configured as row. This is an +  array of pin numbers which is used as rows. +- nvidia,kbc-col-pins: The KBC pins which are configured as column. This is an +  array of pin numbers which is used as column. +- linux,keymap: The keymap for keys as described in the binding document +  devicetree/bindings/input/matrix-keymap.txt.  Optional properties, in addition to those specified by the shared  matrix-keyboard bindings: @@ -19,5 +30,16 @@ Example:  keyboard: keyboard {  	compatible = "nvidia,tegra20-kbc";  	reg = <0x7000e200 0x100>; +	interrupts = <0 85 0x04>;  	nvidia,ghost-filter; +	nvidia,debounce-delay-ms = <640>; +	nvidia,kbc-row-pins = <0 1 2>;    /* pin 0, 1, 2 as rows */ +	nvidia,kbc-col-pins = <11 12 13>; /* pin 11, 12, 13 as columns */ +	linux,keymap = <0x00000074 +			0x00010067 +			0x00020066 +			0x01010068 +			0x02000069 +			0x02010070 +			0x02020071>;  }; diff --git a/drivers/input/Kconfig b/drivers/input/Kconfig index 55f7e57d4e4..38b523a1ece 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 47a6009dbf4..71db1930573 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 f8f892b076e..b76ac580703 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 5a240c60342..ac050066700 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 44e76002f54..49b16453d00 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 add5ffd9fe2..2626773ff29 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 00000000000..9f60a2ec88d --- /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 6d150e3e1f5..98f9113251d 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 3dc2b0f27b0..1c0ddad0a1c 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 c76f96872d3..d89e7d392d1 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 1cf72fe513e..0735de3a646 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 08ffcabd722..865c2f9d25b 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 78eb6b30580..68a5f33152a 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 71a28ee699f..0c2dfc8e969 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 558767d8ebf..caa2c4068f0 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 cd6268cf7cd..802bd6a72d7 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 46ba7556fd4..c25efdb3f28 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 e229fa3cad9..7b99fc7c943 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 ae1ac354c77..970480551b6 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 00000000000..b409c3d7d4f --- /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 00000000000..1673dc6c809 --- /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 00000000000..4720f21d2d7 --- /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 22fe2547e16..cff065f6261 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 fe1df231ba4..2f0b39d59a9 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 12d12ca3fee..2f78538e09d 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 4a4e182c33e..560c243bfca 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 264138f3217..41b6fbf6011 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: +	case TABLETPC2FG:  		if (features->device_type == BTN_TOOL_FINGER) { -			wacom_wac->slots = kmalloc(features->touch_max * -							sizeof(int), -						   GFP_KERNEL); -			if (!wacom_wac->slots) -				return -ENOMEM; +			unsigned int flags = INPUT_MT_DIRECT; -			for (i = 0; i < features->touch_max; i++) -				wacom_wac->slots[i] = -1; -		} -		/* fall through */ +			if (wacom_wac->features.type == TABLETPC2FG) +				flags = 0; -	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); +			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 9396d7769f8..5f9a7721e16 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 515cfe79054..f9a5fd89bc0 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 638e20310f1..861b7f77605 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 98841d8aa63..4a29ddf6bf1 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 84d884b4ec3..59e81b00f24 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 9c0cdc7ea44..7213e8b07e7 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 f88fab56178..6be2eb6a153 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 40e5b3919e2..814655ee2d6 100644 --- a/drivers/tty/sysrq.c +++ b/drivers/tty/sysrq.c @@ -42,6 +42,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> @@ -578,8 +579,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 = @@ -606,100 +670,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: @@ -787,7 +872,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) @@ -804,6 +902,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) diff --git a/include/linux/bma150.h b/include/linux/bma150.h index 7911fda23bb..97ade7cdc87 100644 --- a/include/linux/bma150.h +++ b/include/linux/bma150.h @@ -22,6 +22,18 @@  #define BMA150_DRIVER		"bma150" +#define BMA150_RANGE_2G		0 +#define BMA150_RANGE_4G		1 +#define BMA150_RANGE_8G		2 + +#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 +  struct bma150_cfg {  	bool any_motion_int;		/* Set to enable any-motion interrupt */  	bool hg_int;			/* Set to enable high-G interrupt */ @@ -34,8 +46,8 @@ struct bma150_cfg {  	unsigned char lg_hyst;		/* Low-G hysterisis */  	unsigned char lg_dur;		/* Low-G duration */  	unsigned char lg_thres;		/* Low-G threshold */ -	unsigned char range;		/* BMA0150_RANGE_xxx (in G) */ -	unsigned char bandwidth;	/* BMA0150_BW_xxx (in Hz) */ +	unsigned char range;		/* one of BMA0150_RANGE_xxx */ +	unsigned char bandwidth;	/* one of BMA0150_BW_xxx */  };  struct bma150_platform_data { diff --git a/include/linux/input/adxl34x.h b/include/linux/input/adxl34x.h index 57e01a7cb00..010d98175ef 100644 --- a/include/linux/input/adxl34x.h +++ b/include/linux/input/adxl34x.h @@ -13,6 +13,8 @@  #ifndef __LINUX_INPUT_ADXL34X_H__  #define __LINUX_INPUT_ADXL34X_H__ +#include <linux/input.h> +  struct adxl34x_platform_data {  	/* diff --git a/include/linux/input/tegra_kbc.h b/include/linux/input/tegra_kbc.h deleted file mode 100644 index a1302561293..00000000000 --- a/include/linux/input/tegra_kbc.h +++ /dev/null @@ -1,62 +0,0 @@ -/* - * Platform definitions for tegra-kbc keyboard input driver - * - * Copyright (c) 2010-2011, NVIDIA Corporation. - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License for - * more details. - * - * You should have received a copy of the GNU General Public License along - * with this program; if not, write to the Free Software Foundation, Inc., - * 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA. - */ - -#ifndef ASMARM_ARCH_TEGRA_KBC_H -#define ASMARM_ARCH_TEGRA_KBC_H - -#include <linux/types.h> -#include <linux/input/matrix_keypad.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) - -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_wake_key { -	u8 row:4; -	u8 col:4; -}; - -struct tegra_kbc_platform_data { -	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; - -	u32 wakeup_key; -	bool wakeup; -	bool use_fn_map; -	bool use_ghost_filter; -}; -#endif diff --git a/include/linux/libps2.h b/include/linux/libps2.h index 79603a6c356..4ad06e824f7 100644 --- a/include/linux/libps2.h +++ b/include/linux/libps2.h @@ -36,7 +36,7 @@ struct ps2dev {  	wait_queue_head_t wait;  	unsigned long flags; -	unsigned char cmdbuf[6]; +	unsigned char cmdbuf[8];  	unsigned char cmdcnt;  	unsigned char nak;  };  |