Merge branch 'for-linus' of git://git.kernel.org/pub/scm/linux/kernel/git/dtor/input
Pull input updates from Dmitry Torokhov: "Two new touchpad drivers - Cypress APA I2C Trackpad and Cypress PS/2 touchpad and a big update to ALPS driver from Kevin Cernekee that adds support for "Rushmore" touchpads and paves way for adding support for "Dolphin" touchpads. There is also a new input driver for Goldfish emulator and also Android keyreset driver was folded into SysRq code. A few more drivers were updated with device tree bindings and others got some small cleanups and fixes." * 'for-linus' of git://git.kernel.org/pub/scm/linux/kernel/git/dtor/input: (55 commits) Input: cyttsp-spi - remove duplicate MODULE_ALIAS() Input: tsc2005 - add MODULE_ALIAS Input: tegra-kbc - require CONFIG_OF, remove platform data Input: synaptics - initialize pointer emulation usage Input: MT - do not apply filtering on emulated events Input: bma150 - make some defines public and fix some comments Input: bma150 - fix checking pm_runtime_get_sync() return value Input: ALPS - enable trackstick on Rushmore touchpads Input: ALPS - add support for "Rushmore" touchpads Input: ALPS - make the V3 packet field decoder "pluggable" Input: ALPS - move pixel and bitmap info into alps_data struct Input: ALPS - fix command mode check Input: ALPS - rework detection of Pinnacle AGx touchpads Input: ALPS - move {addr,nibble}_command settings into alps_set_defaults() Input: ALPS - use function pointers for different protocol handlers Input: ALPS - rework detection sequence Input: ALPS - introduce helper function for repeated commands Input: ALPS - move alps_get_model() down below hw_init code Input: ALPS - copy "model" info into alps_data struct Input: ALPS - document the alps.h data structures ...
This commit is contained in:
commit
c6699b58f4
|
@ -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 */
|
||||||
|
};
|
|
@ -1,7 +1,18 @@
|
||||||
* Tegra keyboard controller
|
* 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:
|
Required properties:
|
||||||
- compatible: "nvidia,tegra20-kbc"
|
- 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
|
Optional properties, in addition to those specified by the shared
|
||||||
matrix-keyboard bindings:
|
matrix-keyboard bindings:
|
||||||
|
@ -19,5 +30,16 @@ Example:
|
||||||
keyboard: keyboard {
|
keyboard: keyboard {
|
||||||
compatible = "nvidia,tegra20-kbc";
|
compatible = "nvidia,tegra20-kbc";
|
||||||
reg = <0x7000e200 0x100>;
|
reg = <0x7000e200 0x100>;
|
||||||
|
interrupts = <0 85 0x04>;
|
||||||
nvidia,ghost-filter;
|
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>;
|
||||||
};
|
};
|
||||||
|
|
|
@ -3,7 +3,7 @@
|
||||||
#
|
#
|
||||||
|
|
||||||
menu "Input device support"
|
menu "Input device support"
|
||||||
depends on !S390 && !UML
|
depends on !UML
|
||||||
|
|
||||||
config INPUT
|
config INPUT
|
||||||
tristate "Generic input layer (needed for keyboard, mouse, ...)" if EXPERT
|
tristate "Generic input layer (needed for keyboard, mouse, ...)" if EXPERT
|
||||||
|
|
|
@ -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)) {
|
if (dev->absinfo && test_bit(src, dev->absbit)) {
|
||||||
dev->absinfo[dst] = dev->absinfo[src];
|
dev->absinfo[dst] = dev->absinfo[src];
|
||||||
|
dev->absinfo[dst].fuzz = 0;
|
||||||
dev->absbit[BIT_WORD(dst)] |= BIT_MASK(dst);
|
dev->absbit[BIT_WORD(dst)] |= BIT_MASK(dst);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -12,7 +12,7 @@
|
||||||
* the Free Software Foundation.
|
* the Free Software Foundation.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
/* #define WK0701_DEBUG */
|
#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
|
||||||
|
|
||||||
#define RESERVE 20000
|
#define RESERVE 20000
|
||||||
#define SYNC_PULSE 1306000
|
#define SYNC_PULSE 1306000
|
||||||
|
@ -67,6 +67,7 @@ static inline void walkera0701_parse_frame(struct walkera_dev *w)
|
||||||
{
|
{
|
||||||
int i;
|
int i;
|
||||||
int val1, val2, val3, val4, val5, val6, val7, val8;
|
int val1, val2, val3, val4, val5, val6, val7, val8;
|
||||||
|
int magic, magic_bit;
|
||||||
int crc1, crc2;
|
int crc1, crc2;
|
||||||
|
|
||||||
for (crc1 = crc2 = i = 0; i < 10; i++) {
|
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] & 1) << 8 | (w->buf[19] << 4) | w->buf[20];
|
||||||
val8 *= (w->buf[18] & 2) - 1; /*sign */
|
val8 *= (w->buf[18] & 2) - 1; /*sign */
|
||||||
|
|
||||||
#ifdef WK0701_DEBUG
|
|
||||||
{
|
|
||||||
int magic, magic_bit;
|
|
||||||
magic = (w->buf[21] << 4) | w->buf[22];
|
magic = (w->buf[21] << 4) | w->buf[22];
|
||||||
magic_bit = (w->buf[24] & 8) >> 3;
|
magic_bit = (w->buf[24] & 8) >> 3;
|
||||||
printk(KERN_DEBUG
|
pr_debug("%4d %4d %4d %4d %4d %4d %4d %4d (magic %2x %d)\n",
|
||||||
"walkera0701: %4d %4d %4d %4d %4d %4d %4d %4d (magic %2x %d)\n",
|
val1, val2, val3, val4, val5, val6, val7, val8,
|
||||||
val1, val2, val3, val4, val5, val6, val7, val8, magic,
|
magic, magic_bit);
|
||||||
magic_bit);
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
input_report_abs(w->input_dev, ABS_X, val2);
|
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_Y, val1);
|
||||||
input_report_abs(w->input_dev, ABS_Z, val6);
|
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);
|
struct walkera_dev *w = input_get_drvdata(dev);
|
||||||
|
|
||||||
|
if (parport_claim(w->pardevice))
|
||||||
|
return -EBUSY;
|
||||||
|
|
||||||
parport_enable_irq(w->parport);
|
parport_enable_irq(w->parport);
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
@ -197,40 +196,51 @@ static void walkera0701_close(struct input_dev *dev)
|
||||||
|
|
||||||
parport_disable_irq(w->parport);
|
parport_disable_irq(w->parport);
|
||||||
hrtimer_cancel(&w->timer);
|
hrtimer_cancel(&w->timer);
|
||||||
|
|
||||||
|
parport_release(w->pardevice);
|
||||||
}
|
}
|
||||||
|
|
||||||
static int walkera0701_connect(struct walkera_dev *w, int parport)
|
static int walkera0701_connect(struct walkera_dev *w, int parport)
|
||||||
{
|
{
|
||||||
int err = -ENODEV;
|
int error;
|
||||||
|
|
||||||
w->parport = parport_find_number(parport);
|
w->parport = parport_find_number(parport);
|
||||||
if (w->parport == NULL)
|
if (!w->parport) {
|
||||||
|
pr_err("parport %d does not exist\n", parport);
|
||||||
return -ENODEV;
|
return -ENODEV;
|
||||||
|
|
||||||
if (w->parport->irq == -1) {
|
|
||||||
printk(KERN_ERR "walkera0701: parport without interrupt\n");
|
|
||||||
goto init_err;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
err = -EBUSY;
|
if (w->parport->irq == -1) {
|
||||||
|
pr_err("parport %d does not have interrupt assigned\n",
|
||||||
|
parport);
|
||||||
|
error = -EINVAL;
|
||||||
|
goto err_put_parport;
|
||||||
|
}
|
||||||
|
|
||||||
w->pardevice = parport_register_device(w->parport, "walkera0701",
|
w->pardevice = parport_register_device(w->parport, "walkera0701",
|
||||||
NULL, NULL, walkera0701_irq_handler,
|
NULL, NULL, walkera0701_irq_handler,
|
||||||
PARPORT_DEV_EXCL, w);
|
PARPORT_DEV_EXCL, w);
|
||||||
if (!w->pardevice)
|
if (!w->pardevice) {
|
||||||
goto init_err;
|
pr_err("failed to register parport device\n");
|
||||||
|
error = -EIO;
|
||||||
|
goto err_put_parport;
|
||||||
|
}
|
||||||
|
|
||||||
if (parport_negotiate(w->pardevice->port, IEEE1284_MODE_COMPAT))
|
if (parport_negotiate(w->pardevice->port, IEEE1284_MODE_COMPAT)) {
|
||||||
goto init_err1;
|
pr_err("failed to negotiate parport mode\n");
|
||||||
|
error = -EIO;
|
||||||
if (parport_claim(w->pardevice))
|
goto err_unregister_device;
|
||||||
goto init_err1;
|
}
|
||||||
|
|
||||||
hrtimer_init(&w->timer, CLOCK_MONOTONIC, HRTIMER_MODE_REL);
|
hrtimer_init(&w->timer, CLOCK_MONOTONIC, HRTIMER_MODE_REL);
|
||||||
w->timer.function = timer_handler;
|
w->timer.function = timer_handler;
|
||||||
|
|
||||||
w->input_dev = input_allocate_device();
|
w->input_dev = input_allocate_device();
|
||||||
if (!w->input_dev)
|
if (!w->input_dev) {
|
||||||
goto init_err2;
|
pr_err("failed to allocate input device\n");
|
||||||
|
error = -ENOMEM;
|
||||||
|
goto err_unregister_device;
|
||||||
|
}
|
||||||
|
|
||||||
input_set_drvdata(w->input_dev, w);
|
input_set_drvdata(w->input_dev, w);
|
||||||
w->input_dev->name = "Walkera WK-0701 TX";
|
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.vendor = 0x0001;
|
||||||
w->input_dev->id.product = 0x0001;
|
w->input_dev->id.product = 0x0001;
|
||||||
w->input_dev->id.version = 0x0100;
|
w->input_dev->id.version = 0x0100;
|
||||||
|
w->input_dev->dev.parent = w->parport->dev;
|
||||||
w->input_dev->open = walkera0701_open;
|
w->input_dev->open = walkera0701_open;
|
||||||
w->input_dev->close = walkera0701_close;
|
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_RUDDER, -512, 512, 0, 0);
|
||||||
input_set_abs_params(w->input_dev, ABS_MISC, -512, 512, 0, 0);
|
input_set_abs_params(w->input_dev, ABS_MISC, -512, 512, 0, 0);
|
||||||
|
|
||||||
err = input_register_device(w->input_dev);
|
error = input_register_device(w->input_dev);
|
||||||
if (err)
|
if (error) {
|
||||||
goto init_err3;
|
pr_err("failed to register input device\n");
|
||||||
|
goto err_free_input_dev;
|
||||||
|
}
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
|
|
||||||
init_err3:
|
err_free_input_dev:
|
||||||
input_free_device(w->input_dev);
|
input_free_device(w->input_dev);
|
||||||
init_err2:
|
err_unregister_device:
|
||||||
parport_release(w->pardevice);
|
|
||||||
init_err1:
|
|
||||||
parport_unregister_device(w->pardevice);
|
parport_unregister_device(w->pardevice);
|
||||||
init_err:
|
err_put_parport:
|
||||||
parport_put_port(w->parport);
|
parport_put_port(w->parport);
|
||||||
return err;
|
return error;
|
||||||
}
|
}
|
||||||
|
|
||||||
static void walkera0701_disconnect(struct walkera_dev *w)
|
static void walkera0701_disconnect(struct walkera_dev *w)
|
||||||
{
|
{
|
||||||
input_unregister_device(w->input_dev);
|
input_unregister_device(w->input_dev);
|
||||||
parport_release(w->pardevice);
|
|
||||||
parport_unregister_device(w->pardevice);
|
parport_unregister_device(w->pardevice);
|
||||||
parport_put_port(w->parport);
|
parport_put_port(w->parport);
|
||||||
}
|
}
|
||||||
|
|
|
@ -224,7 +224,7 @@ config KEYBOARD_TCA6416
|
||||||
|
|
||||||
config KEYBOARD_TCA8418
|
config KEYBOARD_TCA8418
|
||||||
tristate "TCA8418 Keypad Support"
|
tristate "TCA8418 Keypad Support"
|
||||||
depends on I2C
|
depends on I2C && GENERIC_HARDIRQS
|
||||||
select INPUT_MATRIXKMAP
|
select INPUT_MATRIXKMAP
|
||||||
help
|
help
|
||||||
This driver implements basic keypad functionality
|
This driver implements basic keypad functionality
|
||||||
|
@ -303,7 +303,7 @@ config KEYBOARD_HP7XX
|
||||||
|
|
||||||
config KEYBOARD_LM8323
|
config KEYBOARD_LM8323
|
||||||
tristate "LM8323 keypad chip"
|
tristate "LM8323 keypad chip"
|
||||||
depends on I2C
|
depends on I2C && GENERIC_HARDIRQS
|
||||||
depends on LEDS_CLASS
|
depends on LEDS_CLASS
|
||||||
help
|
help
|
||||||
If you say yes here you get support for the National Semiconductor
|
If you say yes here you get support for the National Semiconductor
|
||||||
|
@ -420,7 +420,7 @@ config KEYBOARD_NOMADIK
|
||||||
|
|
||||||
config KEYBOARD_TEGRA
|
config KEYBOARD_TEGRA
|
||||||
tristate "NVIDIA Tegra internal matrix keyboard controller support"
|
tristate "NVIDIA Tegra internal matrix keyboard controller support"
|
||||||
depends on ARCH_TEGRA
|
depends on ARCH_TEGRA && OF
|
||||||
select INPUT_MATRIXKMAP
|
select INPUT_MATRIXKMAP
|
||||||
help
|
help
|
||||||
Say Y here if you want to use a matrix keyboard connected directly
|
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
|
To compile this driver as a module, choose M here: the
|
||||||
module will be called samsung-keypad.
|
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
|
config KEYBOARD_STOWAWAY
|
||||||
tristate "Stowaway keyboard"
|
tristate "Stowaway keyboard"
|
||||||
select SERIO
|
select SERIO
|
||||||
|
|
|
@ -13,6 +13,7 @@ obj-$(CONFIG_KEYBOARD_ATKBD) += atkbd.o
|
||||||
obj-$(CONFIG_KEYBOARD_BFIN) += bf54x-keys.o
|
obj-$(CONFIG_KEYBOARD_BFIN) += bf54x-keys.o
|
||||||
obj-$(CONFIG_KEYBOARD_DAVINCI) += davinci_keyscan.o
|
obj-$(CONFIG_KEYBOARD_DAVINCI) += davinci_keyscan.o
|
||||||
obj-$(CONFIG_KEYBOARD_EP93XX) += ep93xx_keypad.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) += gpio_keys.o
|
||||||
obj-$(CONFIG_KEYBOARD_GPIO_POLLED) += gpio_keys_polled.o
|
obj-$(CONFIG_KEYBOARD_GPIO_POLLED) += gpio_keys_polled.o
|
||||||
obj-$(CONFIG_KEYBOARD_TCA6416) += tca6416-keypad.o
|
obj-$(CONFIG_KEYBOARD_TCA6416) += tca6416-keypad.o
|
||||||
|
|
|
@ -676,6 +676,39 @@ static inline void atkbd_disable(struct atkbd *atkbd)
|
||||||
serio_continue_rx(atkbd->ps2dev.serio);
|
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.
|
* 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) {
|
if (atkbd->id == 0xaca1 && atkbd->translated) {
|
||||||
dev_err(&ps2dev->serio->dev,
|
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");
|
"Use i8042.direct=1 to disable translation.\n");
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Make sure nothing is coming from the keyboard and disturbs our
|
||||||
|
* internal state.
|
||||||
|
*/
|
||||||
|
atkbd_deactivate(atkbd);
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -825,24 +864,6 @@ static int atkbd_reset_state(struct atkbd *atkbd)
|
||||||
return 0;
|
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
|
* atkbd_cleanup() restores the keyboard state so that BIOS is happy after a
|
||||||
* reboot.
|
* 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->set = atkbd_select_set(atkbd, atkbd_set, atkbd_extra);
|
||||||
atkbd_reset_state(atkbd);
|
atkbd_reset_state(atkbd);
|
||||||
atkbd_activate(atkbd);
|
|
||||||
|
|
||||||
} else {
|
} else {
|
||||||
atkbd->set = 2;
|
atkbd->set = 2;
|
||||||
|
@ -1165,6 +1185,8 @@ static int atkbd_connect(struct serio *serio, struct serio_driver *drv)
|
||||||
goto fail3;
|
goto fail3;
|
||||||
|
|
||||||
atkbd_enable(atkbd);
|
atkbd_enable(atkbd);
|
||||||
|
if (serio->write)
|
||||||
|
atkbd_activate(atkbd);
|
||||||
|
|
||||||
err = input_register_device(atkbd->dev);
|
err = input_register_device(atkbd->dev);
|
||||||
if (err)
|
if (err)
|
||||||
|
@ -1208,8 +1230,6 @@ static int atkbd_reconnect(struct serio *serio)
|
||||||
if (atkbd->set != atkbd_select_set(atkbd, atkbd->set, atkbd->extra))
|
if (atkbd->set != atkbd_select_set(atkbd, atkbd->set, atkbd->extra))
|
||||||
goto out;
|
goto out;
|
||||||
|
|
||||||
atkbd_activate(atkbd);
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Restore LED state and repeat rate. While input core
|
* Restore LED state and repeat rate. While input core
|
||||||
* will do this for us at resume time reconnect may happen
|
* 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);
|
atkbd_enable(atkbd);
|
||||||
|
if (atkbd->write)
|
||||||
|
atkbd_activate(atkbd);
|
||||||
|
|
||||||
retval = 0;
|
retval = 0;
|
||||||
|
|
||||||
out:
|
out:
|
||||||
|
|
|
@ -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");
|
|
@ -20,6 +20,7 @@
|
||||||
#include <linux/jiffies.h>
|
#include <linux/jiffies.h>
|
||||||
#include <linux/kernel.h>
|
#include <linux/kernel.h>
|
||||||
#include <linux/module.h>
|
#include <linux/module.h>
|
||||||
|
#include <linux/of.h>
|
||||||
#include <linux/platform_device.h>
|
#include <linux/platform_device.h>
|
||||||
#include <linux/slab.h>
|
#include <linux/slab.h>
|
||||||
#include <linux/timer.h>
|
#include <linux/timer.h>
|
||||||
|
@ -414,15 +415,23 @@ open_err:
|
||||||
return -EIO;
|
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)
|
static int imx_keypad_probe(struct platform_device *pdev)
|
||||||
{
|
{
|
||||||
const struct matrix_keymap_data *keymap_data = pdev->dev.platform_data;
|
const struct matrix_keymap_data *keymap_data = pdev->dev.platform_data;
|
||||||
struct imx_keypad *keypad;
|
struct imx_keypad *keypad;
|
||||||
struct input_dev *input_dev;
|
struct input_dev *input_dev;
|
||||||
struct resource *res;
|
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");
|
dev_err(&pdev->dev, "no keymap defined\n");
|
||||||
return -EINVAL;
|
return -EINVAL;
|
||||||
}
|
}
|
||||||
|
@ -480,22 +489,6 @@ static int imx_keypad_probe(struct platform_device *pdev)
|
||||||
goto failed_unmap;
|
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 */
|
/* Init the Input device */
|
||||||
input_dev->name = pdev->name;
|
input_dev->name = pdev->name;
|
||||||
input_dev->id.bustype = BUS_HOST;
|
input_dev->id.bustype = BUS_HOST;
|
||||||
|
@ -512,6 +505,19 @@ static int imx_keypad_probe(struct platform_device *pdev)
|
||||||
goto failed_clock_put;
|
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);
|
__set_bit(EV_REP, input_dev->evbit);
|
||||||
input_set_capability(input_dev, EV_MSC, MSC_SCAN);
|
input_set_capability(input_dev, EV_MSC, MSC_SCAN);
|
||||||
input_set_drvdata(input_dev, keypad);
|
input_set_drvdata(input_dev, keypad);
|
||||||
|
@ -631,6 +637,7 @@ static struct platform_driver imx_keypad_driver = {
|
||||||
.name = "imx-keypad",
|
.name = "imx-keypad",
|
||||||
.owner = THIS_MODULE,
|
.owner = THIS_MODULE,
|
||||||
.pm = &imx_kbd_pm_ops,
|
.pm = &imx_kbd_pm_ops,
|
||||||
|
.of_match_table = of_match_ptr(imx_keypad_of_match),
|
||||||
},
|
},
|
||||||
.probe = imx_keypad_probe,
|
.probe = imx_keypad_probe,
|
||||||
.remove = imx_keypad_remove,
|
.remove = imx_keypad_remove,
|
||||||
|
|
|
@ -20,6 +20,7 @@
|
||||||
|
|
||||||
#include <linux/kernel.h>
|
#include <linux/kernel.h>
|
||||||
#include <linux/init.h>
|
#include <linux/init.h>
|
||||||
|
#include <linux/leds.h>
|
||||||
#include <linux/module.h>
|
#include <linux/module.h>
|
||||||
#include <linux/slab.h>
|
#include <linux/slab.h>
|
||||||
#include <linux/jiffies.h>
|
#include <linux/jiffies.h>
|
||||||
|
@ -39,6 +40,11 @@
|
||||||
#define QT2160_CMD_GPIOS 6
|
#define QT2160_CMD_GPIOS 6
|
||||||
#define QT2160_CMD_SUBVER 7
|
#define QT2160_CMD_SUBVER 7
|
||||||
#define QT2160_CMD_CALIBRATE 10
|
#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)
|
#define QT2160_CYCLE_INTERVAL (2*HZ)
|
||||||
|
|
||||||
|
@ -49,6 +55,17 @@ static unsigned char qt2160_key2code[] = {
|
||||||
KEY_C, KEY_D, KEY_E, KEY_F,
|
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 qt2160_data {
|
||||||
struct i2c_client *client;
|
struct i2c_client *client;
|
||||||
struct input_dev *input;
|
struct input_dev *input;
|
||||||
|
@ -56,8 +73,61 @@ struct qt2160_data {
|
||||||
spinlock_t lock; /* Protects canceling/rescheduling of dwork */
|
spinlock_t lock; /* Protects canceling/rescheduling of dwork */
|
||||||
unsigned short keycodes[ARRAY_SIZE(qt2160_key2code)];
|
unsigned short keycodes[ARRAY_SIZE(qt2160_key2code)];
|
||||||
u16 key_matrix;
|
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,
|
static int qt2160_read_block(struct i2c_client *client,
|
||||||
u8 inireg, u8 *buffer, unsigned int count)
|
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;
|
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)
|
static bool qt2160_identify(struct i2c_client *client)
|
||||||
{
|
{
|
||||||
|
@ -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);
|
error = input_register_device(qt2160->input);
|
||||||
if (error) {
|
if (error) {
|
||||||
dev_err(&client->dev,
|
dev_err(&client->dev,
|
||||||
"Failed to register input device\n");
|
"Failed to register input device\n");
|
||||||
goto err_free_irq;
|
goto err_unregister_leds;
|
||||||
}
|
}
|
||||||
|
|
||||||
i2c_set_clientdata(client, qt2160);
|
i2c_set_clientdata(client, qt2160);
|
||||||
|
@ -326,6 +459,8 @@ static int qt2160_probe(struct i2c_client *client,
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
|
|
||||||
|
err_unregister_leds:
|
||||||
|
qt2160_unregister_leds(qt2160);
|
||||||
err_free_irq:
|
err_free_irq:
|
||||||
if (client->irq)
|
if (client->irq)
|
||||||
free_irq(client->irq, qt2160);
|
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);
|
struct qt2160_data *qt2160 = i2c_get_clientdata(client);
|
||||||
|
|
||||||
|
qt2160_unregister_leds(qt2160);
|
||||||
|
|
||||||
/* Release IRQ so no queue will be scheduled */
|
/* Release IRQ so no queue will be scheduled */
|
||||||
if (client->irq)
|
if (client->irq)
|
||||||
free_irq(client->irq, qt2160);
|
free_irq(client->irq, qt2160);
|
||||||
|
|
|
@ -29,9 +29,16 @@
|
||||||
#include <linux/of.h>
|
#include <linux/of.h>
|
||||||
#include <linux/clk.h>
|
#include <linux/clk.h>
|
||||||
#include <linux/slab.h>
|
#include <linux/slab.h>
|
||||||
#include <linux/input/tegra_kbc.h>
|
#include <linux/input/matrix_keypad.h>
|
||||||
#include <mach/clk.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
|
#define KBC_MAX_DEBOUNCE_CNT 0x3ffu
|
||||||
|
|
||||||
/* KBC row scan time and delay for beginning the row scan. */
|
/* KBC row scan time and delay for beginning the row scan. */
|
||||||
|
@ -67,10 +74,27 @@
|
||||||
|
|
||||||
#define KBC_ROW_SHIFT 3
|
#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 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;
|
void __iomem *mmio;
|
||||||
struct input_dev *idev;
|
struct input_dev *idev;
|
||||||
unsigned int irq;
|
int irq;
|
||||||
spinlock_t lock;
|
spinlock_t lock;
|
||||||
unsigned int repoll_dly;
|
unsigned int repoll_dly;
|
||||||
unsigned long cp_dly_jiffies;
|
unsigned long cp_dly_jiffies;
|
||||||
|
@ -78,7 +102,6 @@ struct tegra_kbc {
|
||||||
bool use_fn_map;
|
bool use_fn_map;
|
||||||
bool use_ghost_filter;
|
bool use_ghost_filter;
|
||||||
bool keypress_caused_wake;
|
bool keypress_caused_wake;
|
||||||
const struct tegra_kbc_platform_data *pdata;
|
|
||||||
unsigned short keycode[KBC_MAX_KEY * 2];
|
unsigned short keycode[KBC_MAX_KEY * 2];
|
||||||
unsigned short current_keys[KBC_MAX_KPENT];
|
unsigned short current_keys[KBC_MAX_KPENT];
|
||||||
unsigned int num_pressed_keys;
|
unsigned int num_pressed_keys;
|
||||||
|
@ -87,147 +110,6 @@ struct tegra_kbc {
|
||||||
struct clk *clk;
|
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,
|
static void tegra_kbc_report_released_keys(struct input_dev *input,
|
||||||
unsigned short old_keycodes[],
|
unsigned short old_keycodes[],
|
||||||
unsigned int old_num_keys,
|
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);
|
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)
|
static void tegra_kbc_keypress_timer(unsigned long data)
|
||||||
{
|
{
|
||||||
struct tegra_kbc *kbc = (struct tegra_kbc *)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)
|
static void tegra_kbc_setup_wakekeys(struct tegra_kbc *kbc, bool filter)
|
||||||
{
|
{
|
||||||
const struct tegra_kbc_platform_data *pdata = kbc->pdata;
|
|
||||||
int i;
|
int i;
|
||||||
unsigned int rst_val;
|
unsigned int rst_val;
|
||||||
|
|
||||||
/* Either mask all keys or none. */
|
/* 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++)
|
for (i = 0; i < KBC_MAX_ROW; i++)
|
||||||
writel(rst_val, kbc->mmio + KBC_ROW0_MASK_0 + i * 4);
|
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)
|
static void tegra_kbc_config_pins(struct tegra_kbc *kbc)
|
||||||
{
|
{
|
||||||
const struct tegra_kbc_platform_data *pdata = kbc->pdata;
|
|
||||||
int i;
|
int i;
|
||||||
|
|
||||||
for (i = 0; i < KBC_MAX_GPIO; 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;
|
row_cfg &= ~r_mask;
|
||||||
col_cfg &= ~c_mask;
|
col_cfg &= ~c_mask;
|
||||||
|
|
||||||
switch (pdata->pin_cfg[i].type) {
|
switch (kbc->pin_cfg[i].type) {
|
||||||
case PIN_CFG_ROW:
|
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;
|
break;
|
||||||
|
|
||||||
case PIN_CFG_COL:
|
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;
|
break;
|
||||||
|
|
||||||
case PIN_CFG_IGNORE:
|
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)
|
static int tegra_kbc_start(struct tegra_kbc *kbc)
|
||||||
{
|
{
|
||||||
const struct tegra_kbc_platform_data *pdata = kbc->pdata;
|
|
||||||
unsigned int debounce_cnt;
|
unsigned int debounce_cnt;
|
||||||
u32 val = 0;
|
u32 val = 0;
|
||||||
|
|
||||||
|
@ -503,10 +370,10 @@ static int tegra_kbc_start(struct tegra_kbc *kbc)
|
||||||
tegra_kbc_config_pins(kbc);
|
tegra_kbc_config_pins(kbc);
|
||||||
tegra_kbc_setup_wakekeys(kbc, false);
|
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. */
|
/* 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_DEBOUNCE_CNT_SHIFT(debounce_cnt);
|
||||||
val |= KBC_FIFO_TH_CNT_SHIFT(1); /* set fifo interrupt threshold to 1 */
|
val |= KBC_FIFO_TH_CNT_SHIFT(1); /* set fifo interrupt threshold to 1 */
|
||||||
val |= KBC_CONTROL_FIFO_CNT_INT_EN; /* interrupt on FIFO threshold */
|
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);
|
return tegra_kbc_stop(kbc);
|
||||||
}
|
}
|
||||||
|
|
||||||
static bool
|
static bool tegra_kbc_check_pin_cfg(const struct tegra_kbc *kbc,
|
||||||
tegra_kbc_check_pin_cfg(const struct tegra_kbc_platform_data *pdata,
|
unsigned int *num_rows)
|
||||||
struct device *dev, unsigned int *num_rows)
|
|
||||||
{
|
{
|
||||||
int i;
|
int i;
|
||||||
|
|
||||||
*num_rows = 0;
|
*num_rows = 0;
|
||||||
|
|
||||||
for (i = 0; i < KBC_MAX_GPIO; i++) {
|
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) {
|
switch (pin_cfg->type) {
|
||||||
case PIN_CFG_ROW:
|
case PIN_CFG_ROW:
|
||||||
if (pin_cfg->num >= KBC_MAX_ROW) {
|
if (pin_cfg->num >= KBC_MAX_ROW) {
|
||||||
dev_err(dev,
|
dev_err(kbc->dev,
|
||||||
"pin_cfg[%d]: invalid row number %d\n",
|
"pin_cfg[%d]: invalid row number %d\n",
|
||||||
i, pin_cfg->num);
|
i, pin_cfg->num);
|
||||||
return false;
|
return false;
|
||||||
|
@ -597,7 +463,7 @@ tegra_kbc_check_pin_cfg(const struct tegra_kbc_platform_data *pdata,
|
||||||
|
|
||||||
case PIN_CFG_COL:
|
case PIN_CFG_COL:
|
||||||
if (pin_cfg->num >= KBC_MAX_COL) {
|
if (pin_cfg->num >= KBC_MAX_COL) {
|
||||||
dev_err(dev,
|
dev_err(kbc->dev,
|
||||||
"pin_cfg[%d]: invalid column number %d\n",
|
"pin_cfg[%d]: invalid column number %d\n",
|
||||||
i, pin_cfg->num);
|
i, pin_cfg->num);
|
||||||
return false;
|
return false;
|
||||||
|
@ -608,7 +474,7 @@ tegra_kbc_check_pin_cfg(const struct tegra_kbc_platform_data *pdata,
|
||||||
break;
|
break;
|
||||||
|
|
||||||
default:
|
default:
|
||||||
dev_err(dev,
|
dev_err(kbc->dev,
|
||||||
"pin_cfg[%d]: invalid entry type %d\n",
|
"pin_cfg[%d]: invalid entry type %d\n",
|
||||||
pin_cfg->type, pin_cfg->num);
|
pin_cfg->type, pin_cfg->num);
|
||||||
return false;
|
return false;
|
||||||
|
@ -618,154 +484,140 @@ tegra_kbc_check_pin_cfg(const struct tegra_kbc_platform_data *pdata,
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
#ifdef CONFIG_OF
|
static int tegra_kbc_parse_dt(struct tegra_kbc *kbc)
|
||||||
static struct tegra_kbc_platform_data *tegra_kbc_dt_parse_pdata(
|
|
||||||
struct platform_device *pdev)
|
|
||||||
{
|
{
|
||||||
struct tegra_kbc_platform_data *pdata;
|
struct device_node *np = kbc->dev->of_node;
|
||||||
struct device_node *np = pdev->dev.of_node;
|
|
||||||
u32 prop;
|
u32 prop;
|
||||||
int i;
|
int i;
|
||||||
|
u32 num_rows = 0;
|
||||||
if (!np)
|
u32 num_cols = 0;
|
||||||
return NULL;
|
u32 cols_cfg[KBC_MAX_GPIO];
|
||||||
|
u32 rows_cfg[KBC_MAX_GPIO];
|
||||||
pdata = kzalloc(sizeof(*pdata), GFP_KERNEL);
|
int proplen;
|
||||||
if (!pdata)
|
int ret;
|
||||||
return NULL;
|
|
||||||
|
|
||||||
if (!of_property_read_u32(np, "nvidia,debounce-delay-ms", &prop))
|
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))
|
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))
|
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))
|
if (of_find_property(np, "nvidia,wakeup-source", NULL))
|
||||||
pdata->wakeup = true;
|
kbc->wakeup = true;
|
||||||
|
|
||||||
/*
|
if (!of_get_property(np, "nvidia,kbc-row-pins", &proplen)) {
|
||||||
* All currently known keymaps with device tree support use the same
|
dev_err(kbc->dev, "property nvidia,kbc-row-pins not found\n");
|
||||||
* pin_cfg, so set it up here.
|
return -ENOENT;
|
||||||
*/
|
}
|
||||||
for (i = 0; i < KBC_MAX_ROW; i++) {
|
num_rows = proplen / sizeof(u32);
|
||||||
pdata->pin_cfg[i].num = i;
|
|
||||||
pdata->pin_cfg[i].type = PIN_CFG_ROW;
|
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);
|
||||||
|
|
||||||
|
if (!of_get_property(np, "linux,keymap", &proplen)) {
|
||||||
|
dev_err(kbc->dev, "property linux,keymap not found\n");
|
||||||
|
return -ENOENT;
|
||||||
}
|
}
|
||||||
|
|
||||||
for (i = 0; i < KBC_MAX_COL; i++) {
|
if (!num_rows || !num_cols || ((num_rows + num_cols) > KBC_MAX_GPIO)) {
|
||||||
pdata->pin_cfg[KBC_MAX_ROW + i].num = i;
|
dev_err(kbc->dev,
|
||||||
pdata->pin_cfg[KBC_MAX_ROW + i].type = PIN_CFG_COL;
|
"keypad rows/columns not porperly specified\n");
|
||||||
|
return -EINVAL;
|
||||||
}
|
}
|
||||||
|
|
||||||
return pdata;
|
/* Set all pins as non-configured */
|
||||||
}
|
for (i = 0; i < KBC_MAX_GPIO; i++)
|
||||||
#else
|
kbc->pin_cfg[i].type = PIN_CFG_IGNORE;
|
||||||
static inline struct tegra_kbc_platform_data *tegra_kbc_dt_parse_pdata(
|
|
||||||
struct platform_device *pdev)
|
|
||||||
{
|
|
||||||
return NULL;
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
|
|
||||||
static int tegra_kbd_setup_keymap(struct tegra_kbc *kbc)
|
ret = of_property_read_u32_array(np, "nvidia,kbc-row-pins",
|
||||||
{
|
rows_cfg, num_rows);
|
||||||
const struct tegra_kbc_platform_data *pdata = kbc->pdata;
|
if (ret < 0) {
|
||||||
const struct matrix_keymap_data *keymap_data = pdata->keymap_data;
|
dev_err(kbc->dev, "Rows configurations are not proper\n");
|
||||||
unsigned int keymap_rows = KBC_MAX_KEY;
|
return -EINVAL;
|
||||||
int retval;
|
|
||||||
|
|
||||||
if (keymap_data && pdata->use_fn_map)
|
|
||||||
keymap_rows *= 2;
|
|
||||||
|
|
||||||
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);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return retval;
|
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;
|
||||||
|
}
|
||||||
|
|
||||||
|
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)
|
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 tegra_kbc *kbc;
|
||||||
struct input_dev *input_dev;
|
|
||||||
struct resource *res;
|
struct resource *res;
|
||||||
int irq;
|
|
||||||
int err;
|
int err;
|
||||||
int num_rows = 0;
|
int num_rows = 0;
|
||||||
unsigned int debounce_cnt;
|
unsigned int debounce_cnt;
|
||||||
unsigned int scan_time_rows;
|
unsigned int scan_time_rows;
|
||||||
|
unsigned int keymap_rows = KBC_MAX_KEY;
|
||||||
|
|
||||||
if (!pdata)
|
kbc = devm_kzalloc(&pdev->dev, sizeof(*kbc), GFP_KERNEL);
|
||||||
pdata = tegra_kbc_dt_parse_pdata(pdev);
|
if (!kbc) {
|
||||||
|
dev_err(&pdev->dev, "failed to alloc memory for kbc\n");
|
||||||
if (!pdata)
|
return -ENOMEM;
|
||||||
return -EINVAL;
|
|
||||||
|
|
||||||
if (!tegra_kbc_check_pin_cfg(pdata, &pdev->dev, &num_rows)) {
|
|
||||||
err = -EINVAL;
|
|
||||||
goto err_free_pdata;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
kbc->dev = &pdev->dev;
|
||||||
|
spin_lock_init(&kbc->lock);
|
||||||
|
|
||||||
|
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);
|
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
|
||||||
if (!res) {
|
if (!res) {
|
||||||
dev_err(&pdev->dev, "failed to get I/O memory\n");
|
dev_err(&pdev->dev, "failed to get I/O memory\n");
|
||||||
err = -ENXIO;
|
return -ENXIO;
|
||||||
goto err_free_pdata;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
irq = platform_get_irq(pdev, 0);
|
kbc->irq = platform_get_irq(pdev, 0);
|
||||||
if (irq < 0) {
|
if (kbc->irq < 0) {
|
||||||
dev_err(&pdev->dev, "failed to get keyboard IRQ\n");
|
dev_err(&pdev->dev, "failed to get keyboard IRQ\n");
|
||||||
err = -ENXIO;
|
return -ENXIO;
|
||||||
goto err_free_pdata;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
kbc = kzalloc(sizeof(*kbc), GFP_KERNEL);
|
kbc->idev = devm_input_allocate_device(&pdev->dev);
|
||||||
input_dev = input_allocate_device();
|
if (!kbc->idev) {
|
||||||
if (!kbc || !input_dev) {
|
dev_err(&pdev->dev, "failed to allocate input device\n");
|
||||||
err = -ENOMEM;
|
return -ENOMEM;
|
||||||
goto err_free_mem;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
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);
|
setup_timer(&kbc->timer, tegra_kbc_keypress_timer, (unsigned long)kbc);
|
||||||
|
|
||||||
res = request_mem_region(res->start, resource_size(res), pdev->name);
|
kbc->mmio = devm_request_and_ioremap(&pdev->dev, res);
|
||||||
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));
|
|
||||||
if (!kbc->mmio) {
|
if (!kbc->mmio) {
|
||||||
dev_err(&pdev->dev, "failed to remap I/O memory\n");
|
dev_err(&pdev->dev, "Cannot request memregion/iomap address\n");
|
||||||
err = -ENXIO;
|
return -EBUSY;
|
||||||
goto err_free_mem_region;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
kbc->clk = clk_get(&pdev->dev, NULL);
|
kbc->clk = devm_clk_get(&pdev->dev, NULL);
|
||||||
if (IS_ERR(kbc->clk)) {
|
if (IS_ERR(kbc->clk)) {
|
||||||
dev_err(&pdev->dev, "failed to get keyboard clock\n");
|
dev_err(&pdev->dev, "failed to get keyboard clock\n");
|
||||||
err = PTR_ERR(kbc->clk);
|
return PTR_ERR(kbc->clk);
|
||||||
goto err_iounmap;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|
@ -774,37 +626,38 @@ static int tegra_kbc_probe(struct platform_device *pdev)
|
||||||
* the rows. There is an additional delay before the row scanning
|
* the rows. There is an additional delay before the row scanning
|
||||||
* starts. The repoll delay is computed in milliseconds.
|
* 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;
|
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->repoll_dly = DIV_ROUND_UP(kbc->repoll_dly, KBC_CYCLE_MS);
|
||||||
|
|
||||||
kbc->wakeup_key = pdata->wakeup_key;
|
kbc->idev->name = pdev->name;
|
||||||
kbc->use_fn_map = pdata->use_fn_map;
|
kbc->idev->id.bustype = BUS_HOST;
|
||||||
kbc->use_ghost_filter = pdata->use_ghost_filter;
|
kbc->idev->dev.parent = &pdev->dev;
|
||||||
|
kbc->idev->open = tegra_kbc_open;
|
||||||
|
kbc->idev->close = tegra_kbc_close;
|
||||||
|
|
||||||
input_dev->name = pdev->name;
|
if (kbc->keymap_data && kbc->use_fn_map)
|
||||||
input_dev->id.bustype = BUS_HOST;
|
keymap_rows *= 2;
|
||||||
input_dev->dev.parent = &pdev->dev;
|
|
||||||
input_dev->open = tegra_kbc_open;
|
|
||||||
input_dev->close = tegra_kbc_close;
|
|
||||||
|
|
||||||
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) {
|
if (err) {
|
||||||
dev_err(&pdev->dev, "failed to setup keymap\n");
|
dev_err(&pdev->dev, "failed to setup keymap\n");
|
||||||
goto err_put_clk;
|
return err;
|
||||||
}
|
}
|
||||||
|
|
||||||
__set_bit(EV_REP, input_dev->evbit);
|
__set_bit(EV_REP, kbc->idev->evbit);
|
||||||
input_set_capability(input_dev, EV_MSC, MSC_SCAN);
|
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);
|
IRQF_NO_SUSPEND | IRQF_TRIGGER_HIGH, pdev->name, kbc);
|
||||||
if (err) {
|
if (err) {
|
||||||
dev_err(&pdev->dev, "failed to request keyboard IRQ\n");
|
dev_err(&pdev->dev, "failed to request keyboard IRQ\n");
|
||||||
goto err_put_clk;
|
return err;
|
||||||
}
|
}
|
||||||
|
|
||||||
disable_irq(kbc->irq);
|
disable_irq(kbc->irq);
|
||||||
|
@ -812,60 +665,28 @@ static int tegra_kbc_probe(struct platform_device *pdev)
|
||||||
err = input_register_device(kbc->idev);
|
err = input_register_device(kbc->idev);
|
||||||
if (err) {
|
if (err) {
|
||||||
dev_err(&pdev->dev, "failed to register input device\n");
|
dev_err(&pdev->dev, "failed to register input device\n");
|
||||||
goto err_free_irq;
|
return err;
|
||||||
}
|
}
|
||||||
|
|
||||||
platform_set_drvdata(pdev, kbc);
|
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)
|
|
||||||
{
|
|
||||||
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);
|
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
#ifdef CONFIG_PM_SLEEP
|
#ifdef CONFIG_PM_SLEEP
|
||||||
|
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 int tegra_kbc_suspend(struct device *dev)
|
static int tegra_kbc_suspend(struct device *dev)
|
||||||
{
|
{
|
||||||
struct platform_device *pdev = to_platform_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 = {
|
static struct platform_driver tegra_kbc_driver = {
|
||||||
.probe = tegra_kbc_probe,
|
.probe = tegra_kbc_probe,
|
||||||
.remove = tegra_kbc_remove,
|
|
||||||
.driver = {
|
.driver = {
|
||||||
.name = "tegra-kbc",
|
.name = "tegra-kbc",
|
||||||
.owner = THIS_MODULE,
|
.owner = THIS_MODULE,
|
||||||
|
|
|
@ -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} */
|
.ev_code_tap = {BTN_TOUCH, BTN_TOUCH, BTN_TOUCH}, /* EV_KEY {x,y,z} */
|
||||||
.power_mode = ADXL_AUTO_SLEEP | ADXL_LINK,
|
.power_mode = ADXL_AUTO_SLEEP | ADXL_LINK,
|
||||||
.fifo_mode = FIFO_STREAM,
|
.fifo_mode = ADXL_FIFO_STREAM,
|
||||||
.watermark = 0,
|
.watermark = 0,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -732,7 +732,7 @@ struct adxl34x *adxl34x_probe(struct device *dev, int irq,
|
||||||
mutex_init(&ac->mutex);
|
mutex_init(&ac->mutex);
|
||||||
|
|
||||||
input_dev->name = "ADXL34x accelerometer";
|
input_dev->name = "ADXL34x accelerometer";
|
||||||
revid = ac->bops->read(dev, DEVID);
|
revid = AC_READ(ac, DEVID);
|
||||||
|
|
||||||
switch (revid) {
|
switch (revid) {
|
||||||
case ID_ADXL345:
|
case ID_ADXL345:
|
||||||
|
@ -809,7 +809,7 @@ struct adxl34x *adxl34x_probe(struct device *dev, int irq,
|
||||||
if (FIFO_MODE(pdata->fifo_mode) == FIFO_BYPASS)
|
if (FIFO_MODE(pdata->fifo_mode) == FIFO_BYPASS)
|
||||||
ac->fifo_delay = false;
|
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,
|
err = request_threaded_irq(ac->irq, NULL, adxl34x_irq,
|
||||||
IRQF_TRIGGER_HIGH | IRQF_ONESHOT,
|
IRQF_TRIGGER_HIGH | IRQF_ONESHOT,
|
||||||
|
@ -827,7 +827,6 @@ struct adxl34x *adxl34x_probe(struct device *dev, int irq,
|
||||||
if (err)
|
if (err)
|
||||||
goto err_remove_attr;
|
goto err_remove_attr;
|
||||||
|
|
||||||
AC_WRITE(ac, THRESH_TAP, pdata->tap_threshold);
|
|
||||||
AC_WRITE(ac, OFSX, pdata->x_axis_offset);
|
AC_WRITE(ac, OFSX, pdata->x_axis_offset);
|
||||||
ac->hwcal.x = pdata->x_axis_offset;
|
ac->hwcal.x = pdata->x_axis_offset;
|
||||||
AC_WRITE(ac, OFSY, pdata->y_axis_offset);
|
AC_WRITE(ac, OFSY, pdata->y_axis_offset);
|
||||||
|
|
|
@ -46,18 +46,6 @@
|
||||||
#define BMA150_POLL_MAX 200
|
#define BMA150_POLL_MAX 200
|
||||||
#define BMA150_POLL_MIN 0
|
#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_NORMAL 0
|
||||||
#define BMA150_MODE_SLEEP 2
|
#define BMA150_MODE_SLEEP 2
|
||||||
#define BMA150_MODE_WAKE_UP 3
|
#define BMA150_MODE_WAKE_UP 3
|
||||||
|
@ -372,7 +360,7 @@ static int bma150_open(struct bma150_data *bma150)
|
||||||
int error;
|
int error;
|
||||||
|
|
||||||
error = pm_runtime_get_sync(&bma150->client->dev);
|
error = pm_runtime_get_sync(&bma150->client->dev);
|
||||||
if (error && error != -ENOSYS)
|
if (error < 0 && error != -ENOSYS)
|
||||||
return error;
|
return error;
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|
|
|
@ -43,7 +43,6 @@ struct vibra_info {
|
||||||
struct device *dev;
|
struct device *dev;
|
||||||
struct input_dev *input_dev;
|
struct input_dev *input_dev;
|
||||||
|
|
||||||
struct workqueue_struct *workqueue;
|
|
||||||
struct work_struct play_work;
|
struct work_struct play_work;
|
||||||
|
|
||||||
bool enabled;
|
bool enabled;
|
||||||
|
@ -143,19 +142,7 @@ static int vibra_play(struct input_dev *input, void *data,
|
||||||
if (!info->speed)
|
if (!info->speed)
|
||||||
info->speed = effect->u.rumble.weak_magnitude >> 9;
|
info->speed = effect->u.rumble.weak_magnitude >> 9;
|
||||||
info->direction = effect->direction < EFFECT_DIR_180_DEG ? 0 : 1;
|
info->direction = effect->direction < EFFECT_DIR_180_DEG ? 0 : 1;
|
||||||
queue_work(info->workqueue, &info->play_work);
|
schedule_work(&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;
|
|
||||||
}
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -164,9 +151,6 @@ static void twl4030_vibra_close(struct input_dev *input)
|
||||||
struct vibra_info *info = input_get_drvdata(input);
|
struct vibra_info *info = input_get_drvdata(input);
|
||||||
|
|
||||||
cancel_work_sync(&info->play_work);
|
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)
|
if (info->enabled)
|
||||||
vibra_disable(info);
|
vibra_disable(info);
|
||||||
|
@ -219,7 +203,7 @@ static int twl4030_vibra_probe(struct platform_device *pdev)
|
||||||
return -EINVAL;
|
return -EINVAL;
|
||||||
}
|
}
|
||||||
|
|
||||||
info = kzalloc(sizeof(*info), GFP_KERNEL);
|
info = devm_kzalloc(&pdev->dev, sizeof(*info), GFP_KERNEL);
|
||||||
if (!info)
|
if (!info)
|
||||||
return -ENOMEM;
|
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);
|
info->coexist = twl4030_vibra_check_coexist(pdata, twl4030_core_node);
|
||||||
INIT_WORK(&info->play_work, vibra_play_work);
|
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) {
|
if (info->input_dev == NULL) {
|
||||||
dev_err(&pdev->dev, "couldn't allocate input device\n");
|
dev_err(&pdev->dev, "couldn't allocate input device\n");
|
||||||
ret = -ENOMEM;
|
return -ENOMEM;
|
||||||
goto err_kzalloc;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
input_set_drvdata(info->input_dev, info);
|
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->name = "twl4030:vibrator";
|
||||||
info->input_dev->id.version = 1;
|
info->input_dev->id.version = 1;
|
||||||
info->input_dev->dev.parent = pdev->dev.parent;
|
info->input_dev->dev.parent = pdev->dev.parent;
|
||||||
info->input_dev->open = twl4030_vibra_open;
|
|
||||||
info->input_dev->close = twl4030_vibra_close;
|
info->input_dev->close = twl4030_vibra_close;
|
||||||
__set_bit(FF_RUMBLE, info->input_dev->ffbit);
|
__set_bit(FF_RUMBLE, info->input_dev->ffbit);
|
||||||
|
|
||||||
ret = input_ff_create_memless(info->input_dev, NULL, vibra_play);
|
ret = input_ff_create_memless(info->input_dev, NULL, vibra_play);
|
||||||
if (ret < 0) {
|
if (ret < 0) {
|
||||||
dev_dbg(&pdev->dev, "couldn't register vibrator to FF\n");
|
dev_dbg(&pdev->dev, "couldn't register vibrator to FF\n");
|
||||||
goto err_ialloc;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
ret = input_register_device(info->input_dev);
|
ret = input_register_device(info->input_dev);
|
||||||
|
@ -262,28 +244,11 @@ static int twl4030_vibra_probe(struct platform_device *pdev)
|
||||||
|
|
||||||
err_iff:
|
err_iff:
|
||||||
input_ff_destroy(info->input_dev);
|
input_ff_destroy(info->input_dev);
|
||||||
err_ialloc:
|
|
||||||
input_free_device(info->input_dev);
|
|
||||||
err_kzalloc:
|
|
||||||
kfree(info);
|
|
||||||
return ret;
|
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 = {
|
static struct platform_driver twl4030_vibra_driver = {
|
||||||
.probe = twl4030_vibra_probe,
|
.probe = twl4030_vibra_probe,
|
||||||
.remove = twl4030_vibra_remove,
|
|
||||||
.driver = {
|
.driver = {
|
||||||
.name = "twl4030-vibra",
|
.name = "twl4030-vibra",
|
||||||
.owner = THIS_MODULE,
|
.owner = THIS_MODULE,
|
||||||
|
|
|
@ -275,7 +275,7 @@ static int twl6040_vibra_probe(struct platform_device *pdev)
|
||||||
return -EINVAL;
|
return -EINVAL;
|
||||||
}
|
}
|
||||||
|
|
||||||
info = kzalloc(sizeof(*info), GFP_KERNEL);
|
info = devm_kzalloc(&pdev->dev, sizeof(*info), GFP_KERNEL);
|
||||||
if (!info) {
|
if (!info) {
|
||||||
dev_err(&pdev->dev, "couldn't allocate memory\n");
|
dev_err(&pdev->dev, "couldn't allocate memory\n");
|
||||||
return -ENOMEM;
|
return -ENOMEM;
|
||||||
|
@ -309,24 +309,65 @@ static int twl6040_vibra_probe(struct platform_device *pdev)
|
||||||
if ((!info->vibldrv_res && !info->viblmotor_res) ||
|
if ((!info->vibldrv_res && !info->viblmotor_res) ||
|
||||||
(!info->vibrdrv_res && !info->vibrmotor_res)) {
|
(!info->vibrdrv_res && !info->vibrmotor_res)) {
|
||||||
dev_err(info->dev, "invalid vibra driver/motor resistance\n");
|
dev_err(info->dev, "invalid vibra driver/motor resistance\n");
|
||||||
ret = -EINVAL;
|
return -EINVAL;
|
||||||
goto err_kzalloc;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
info->irq = platform_get_irq(pdev, 0);
|
info->irq = platform_get_irq(pdev, 0);
|
||||||
if (info->irq < 0) {
|
if (info->irq < 0) {
|
||||||
dev_err(info->dev, "invalid irq\n");
|
dev_err(info->dev, "invalid irq\n");
|
||||||
ret = -EINVAL;
|
return -EINVAL;
|
||||||
goto err_kzalloc;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
mutex_init(&info->mutex);
|
mutex_init(&info->mutex);
|
||||||
|
|
||||||
|
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);
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
info->supplies[0].supply = "vddvibl";
|
||||||
|
info->supplies[1].supply = "vddvibr";
|
||||||
|
/*
|
||||||
|
* When booted with Device tree the regulators are attached to the
|
||||||
|
* parent device (twl6040 MFD core)
|
||||||
|
*/
|
||||||
|
ret = regulator_bulk_get(pdata ? info->dev : twl6040_core_dev,
|
||||||
|
ARRAY_SIZE(info->supplies), info->supplies);
|
||||||
|
if (ret) {
|
||||||
|
dev_err(info->dev, "couldn't get regulators %d\n", ret);
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (vddvibl_uV) {
|
||||||
|
ret = regulator_set_voltage(info->supplies[0].consumer,
|
||||||
|
vddvibl_uV, vddvibl_uV);
|
||||||
|
if (ret) {
|
||||||
|
dev_err(info->dev, "failed to set VDDVIBL volt %d\n",
|
||||||
|
ret);
|
||||||
|
goto err_regulator;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (vddvibr_uV) {
|
||||||
|
ret = regulator_set_voltage(info->supplies[1].consumer,
|
||||||
|
vddvibr_uV, vddvibr_uV);
|
||||||
|
if (ret) {
|
||||||
|
dev_err(info->dev, "failed to set VDDVIBR volt %d\n",
|
||||||
|
ret);
|
||||||
|
goto err_regulator;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
INIT_WORK(&info->play_work, vibra_play_work);
|
||||||
|
|
||||||
info->input_dev = input_allocate_device();
|
info->input_dev = input_allocate_device();
|
||||||
if (info->input_dev == NULL) {
|
if (info->input_dev == NULL) {
|
||||||
dev_err(info->dev, "couldn't allocate input device\n");
|
dev_err(info->dev, "couldn't allocate input device\n");
|
||||||
ret = -ENOMEM;
|
ret = -ENOMEM;
|
||||||
goto err_kzalloc;
|
goto err_regulator;
|
||||||
}
|
}
|
||||||
|
|
||||||
input_set_drvdata(info->input_dev, info);
|
input_set_drvdata(info->input_dev, info);
|
||||||
|
@ -351,70 +392,14 @@ static int twl6040_vibra_probe(struct platform_device *pdev)
|
||||||
|
|
||||||
platform_set_drvdata(pdev, info);
|
platform_set_drvdata(pdev, info);
|
||||||
|
|
||||||
ret = request_threaded_irq(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;
|
|
||||||
}
|
|
||||||
|
|
||||||
info->supplies[0].supply = "vddvibl";
|
|
||||||
info->supplies[1].supply = "vddvibr";
|
|
||||||
/*
|
|
||||||
* When booted with Device tree the regulators are attached to the
|
|
||||||
* parent device (twl6040 MFD core)
|
|
||||||
*/
|
|
||||||
ret = regulator_bulk_get(pdata ? info->dev : twl6040_core_dev,
|
|
||||||
ARRAY_SIZE(info->supplies), info->supplies);
|
|
||||||
if (ret) {
|
|
||||||
dev_err(info->dev, "couldn't get regulators %d\n", ret);
|
|
||||||
goto err_regulator;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (vddvibl_uV) {
|
|
||||||
ret = regulator_set_voltage(info->supplies[0].consumer,
|
|
||||||
vddvibl_uV, vddvibl_uV);
|
|
||||||
if (ret) {
|
|
||||||
dev_err(info->dev, "failed to set VDDVIBL volt %d\n",
|
|
||||||
ret);
|
|
||||||
goto err_voltage;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (vddvibr_uV) {
|
|
||||||
ret = regulator_set_voltage(info->supplies[1].consumer,
|
|
||||||
vddvibr_uV, vddvibr_uV);
|
|
||||||
if (ret) {
|
|
||||||
dev_err(info->dev, "failed to set VDDVIBR volt %d\n",
|
|
||||||
ret);
|
|
||||||
goto err_voltage;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
info->workqueue = alloc_workqueue("twl6040-vibra", 0, 0);
|
|
||||||
if (info->workqueue == NULL) {
|
|
||||||
dev_err(info->dev, "couldn't create workqueue\n");
|
|
||||||
ret = -ENOMEM;
|
|
||||||
goto err_voltage;
|
|
||||||
}
|
|
||||||
INIT_WORK(&info->play_work, vibra_play_work);
|
|
||||||
|
|
||||||
return 0;
|
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:
|
err_iff:
|
||||||
if (info->input_dev)
|
|
||||||
input_ff_destroy(info->input_dev);
|
input_ff_destroy(info->input_dev);
|
||||||
err_ialloc:
|
err_ialloc:
|
||||||
input_free_device(info->input_dev);
|
input_free_device(info->input_dev);
|
||||||
err_kzalloc:
|
err_regulator:
|
||||||
kfree(info);
|
regulator_bulk_free(ARRAY_SIZE(info->supplies), info->supplies);
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -423,10 +408,7 @@ static int twl6040_vibra_remove(struct platform_device *pdev)
|
||||||
struct vibra_info *info = platform_get_drvdata(pdev);
|
struct vibra_info *info = platform_get_drvdata(pdev);
|
||||||
|
|
||||||
input_unregister_device(info->input_dev);
|
input_unregister_device(info->input_dev);
|
||||||
free_irq(info->irq, info);
|
|
||||||
regulator_bulk_free(ARRAY_SIZE(info->supplies), info->supplies);
|
regulator_bulk_free(ARRAY_SIZE(info->supplies), info->supplies);
|
||||||
destroy_workqueue(info->workqueue);
|
|
||||||
kfree(info);
|
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
|
@ -86,7 +86,7 @@ static int wm831x_on_probe(struct platform_device *pdev)
|
||||||
wm831x_on->wm831x = wm831x;
|
wm831x_on->wm831x = wm831x;
|
||||||
INIT_DELAYED_WORK(&wm831x_on->work, wm831x_poll_on);
|
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) {
|
if (!wm831x_on->dev) {
|
||||||
dev_err(&pdev->dev, "Can't allocate input dev\n");
|
dev_err(&pdev->dev, "Can't allocate input dev\n");
|
||||||
ret = -ENOMEM;
|
ret = -ENOMEM;
|
||||||
|
@ -119,7 +119,6 @@ static int wm831x_on_probe(struct platform_device *pdev)
|
||||||
err_irq:
|
err_irq:
|
||||||
free_irq(irq, wm831x_on);
|
free_irq(irq, wm831x_on);
|
||||||
err_input_dev:
|
err_input_dev:
|
||||||
input_free_device(wm831x_on->dev);
|
|
||||||
err:
|
err:
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
@ -131,7 +130,6 @@ static int wm831x_on_remove(struct platform_device *pdev)
|
||||||
|
|
||||||
free_irq(irq, wm831x_on);
|
free_irq(irq, wm831x_on);
|
||||||
cancel_delayed_work_sync(&wm831x_on->work);
|
cancel_delayed_work_sync(&wm831x_on->work);
|
||||||
input_unregister_device(wm831x_on->dev);
|
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
|
@ -68,6 +68,16 @@ config MOUSE_PS2_SYNAPTICS
|
||||||
|
|
||||||
If unsure, say Y.
|
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
|
config MOUSE_PS2_LIFEBOOK
|
||||||
bool "Fujitsu Lifebook PS/2 mouse protocol extension" if EXPERT
|
bool "Fujitsu Lifebook PS/2 mouse protocol extension" if EXPERT
|
||||||
default y
|
default y
|
||||||
|
@ -193,6 +203,18 @@ config MOUSE_BCM5974
|
||||||
To compile this driver as a module, choose M here: the
|
To compile this driver as a module, choose M here: the
|
||||||
module will be called bcm5974.
|
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
|
config MOUSE_INPORT
|
||||||
tristate "InPort/MS/ATIXL busmouse"
|
tristate "InPort/MS/ATIXL busmouse"
|
||||||
depends on ISA
|
depends on ISA
|
||||||
|
|
|
@ -8,6 +8,7 @@ obj-$(CONFIG_MOUSE_AMIGA) += amimouse.o
|
||||||
obj-$(CONFIG_MOUSE_APPLETOUCH) += appletouch.o
|
obj-$(CONFIG_MOUSE_APPLETOUCH) += appletouch.o
|
||||||
obj-$(CONFIG_MOUSE_ATARI) += atarimouse.o
|
obj-$(CONFIG_MOUSE_ATARI) += atarimouse.o
|
||||||
obj-$(CONFIG_MOUSE_BCM5974) += bcm5974.o
|
obj-$(CONFIG_MOUSE_BCM5974) += bcm5974.o
|
||||||
|
obj-$(CONFIG_MOUSE_CYAPA) += cyapa.o
|
||||||
obj-$(CONFIG_MOUSE_GPIO) += gpio_mouse.o
|
obj-$(CONFIG_MOUSE_GPIO) += gpio_mouse.o
|
||||||
obj-$(CONFIG_MOUSE_INPORT) += inport.o
|
obj-$(CONFIG_MOUSE_INPORT) += inport.o
|
||||||
obj-$(CONFIG_MOUSE_LOGIBM) += logibm.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_SENTELIC) += sentelic.o
|
||||||
psmouse-$(CONFIG_MOUSE_PS2_TRACKPOINT) += trackpoint.o
|
psmouse-$(CONFIG_MOUSE_PS2_TRACKPOINT) += trackpoint.o
|
||||||
psmouse-$(CONFIG_MOUSE_PS2_TOUCHKIT) += touchkit_ps2.o
|
psmouse-$(CONFIG_MOUSE_PS2_TOUCHKIT) += touchkit_ps2.o
|
||||||
|
psmouse-$(CONFIG_MOUSE_PS2_CYPRESS) += cypress_ps2.o
|
||||||
|
|
File diff suppressed because it is too large
Load Diff
|
@ -12,35 +12,146 @@
|
||||||
#ifndef _ALPS_H
|
#ifndef _ALPS_H
|
||||||
#define _ALPS_H
|
#define _ALPS_H
|
||||||
|
|
||||||
#define ALPS_PROTO_V1 0
|
#define ALPS_PROTO_V1 1
|
||||||
#define ALPS_PROTO_V2 1
|
#define ALPS_PROTO_V2 2
|
||||||
#define ALPS_PROTO_V3 2
|
#define ALPS_PROTO_V3 3
|
||||||
#define ALPS_PROTO_V4 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 {
|
struct alps_model_info {
|
||||||
unsigned char signature[3];
|
unsigned char signature[3];
|
||||||
unsigned char command_mode_resp; /* v3/v4 only */
|
unsigned char command_mode_resp;
|
||||||
unsigned char proto_version;
|
unsigned char proto_version;
|
||||||
unsigned char byte0, mask0;
|
unsigned char byte0, mask0;
|
||||||
unsigned char flags;
|
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 {
|
struct alps_nibble_commands {
|
||||||
int command;
|
int command;
|
||||||
unsigned char data;
|
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 alps_data {
|
||||||
struct input_dev *dev2; /* Relative device */
|
struct input_dev *dev2;
|
||||||
char phys[32]; /* Phys */
|
char phys[32];
|
||||||
const struct alps_model_info *i;/* Info */
|
|
||||||
|
/* these are autodetected when the device is identified */
|
||||||
const struct alps_nibble_commands *nibble_commands;
|
const struct alps_nibble_commands *nibble_commands;
|
||||||
int addr_command; /* Command to set register address */
|
int addr_command;
|
||||||
int prev_fin; /* Finger bit from previous packet */
|
unsigned char proto_version;
|
||||||
int multi_packet; /* Multi-packet data in progress */
|
unsigned char byte0, mask0;
|
||||||
unsigned char multi_data[6]; /* Saved multi-packet data */
|
unsigned char flags;
|
||||||
int x1, x2, y1, y2; /* Coordinates from last MT report */
|
int x_max;
|
||||||
int fingers; /* Number of fingers from MT report */
|
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;
|
u8 quirks;
|
||||||
struct timer_list timer;
|
struct timer_list timer;
|
||||||
};
|
};
|
||||||
|
|
|
@ -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");
|
|
@ -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;
|
||||||
|
}
|
|
@ -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 */
|
|
@ -34,6 +34,7 @@
|
||||||
#include "touchkit_ps2.h"
|
#include "touchkit_ps2.h"
|
||||||
#include "elantech.h"
|
#include "elantech.h"
|
||||||
#include "sentelic.h"
|
#include "sentelic.h"
|
||||||
|
#include "cypress_ps2.h"
|
||||||
|
|
||||||
#define DRIVER_DESC "PS/2 mouse driver"
|
#define DRIVER_DESC "PS/2 mouse driver"
|
||||||
|
|
||||||
|
@ -758,6 +759,28 @@ static int psmouse_extensions(struct psmouse *psmouse,
|
||||||
synaptics_reset(psmouse);
|
synaptics_reset(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
|
* Try ALPS TouchPad
|
||||||
*/
|
*/
|
||||||
|
@ -896,6 +919,15 @@ static const struct psmouse_protocol psmouse_protocols[] = {
|
||||||
.alias = "thinkps",
|
.alias = "thinkps",
|
||||||
.detect = thinking_detect,
|
.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,
|
.type = PSMOUSE_GENPS,
|
||||||
.name = "GenPS/2",
|
.name = "GenPS/2",
|
||||||
|
|
|
@ -95,6 +95,7 @@ enum psmouse_type {
|
||||||
PSMOUSE_ELANTECH,
|
PSMOUSE_ELANTECH,
|
||||||
PSMOUSE_FSP,
|
PSMOUSE_FSP,
|
||||||
PSMOUSE_SYNAPTICS_RELATIVE,
|
PSMOUSE_SYNAPTICS_RELATIVE,
|
||||||
|
PSMOUSE_CYPRESS,
|
||||||
PSMOUSE_AUTO /* This one should always be last */
|
PSMOUSE_AUTO /* This one should always be last */
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
@ -722,11 +722,13 @@ static void synaptics_report_mt_data(struct psmouse *psmouse,
|
||||||
default:
|
default:
|
||||||
/*
|
/*
|
||||||
* If the finger slot contained in SGM is valid, and either
|
* 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.
|
* Otherwise, empty MTB slot 0.
|
||||||
*/
|
*/
|
||||||
if (mt_state->sgm != -1 &&
|
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);
|
synaptics_report_slot(dev, 0, sgm);
|
||||||
else
|
else
|
||||||
synaptics_report_slot(dev, 0, NULL);
|
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
|
* If the finger slot contained in AGM is valid, and either
|
||||||
* hasn't changed, or is new, then report AGM in MTB slot 1.
|
* hasn't changed, or is new, then report AGM in MTB slot 1.
|
||||||
* Otherwise, empty 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 &&
|
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);
|
synaptics_report_slot(dev, 1, agm);
|
||||||
else
|
else
|
||||||
synaptics_report_slot(dev, 1, NULL);
|
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);
|
input_set_abs_params(dev, ABS_PRESSURE, 0, 255, 0, 0);
|
||||||
|
|
||||||
if (SYN_CAP_IMAGE_SENSOR(priv->ext_cap_0c)) {
|
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,
|
set_abs_position_params(dev, priv, ABS_MT_POSITION_X,
|
||||||
ABS_MT_POSITION_Y);
|
ABS_MT_POSITION_Y);
|
||||||
/* Image sensors can report per-contact pressure */
|
/* Image sensors can report per-contact pressure */
|
||||||
input_set_abs_params(dev, ABS_MT_PRESSURE, 0, 255, 0, 0);
|
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 */
|
/* Image sensors can signal 4 and 5 finger clicks */
|
||||||
__set_bit(BTN_TOOL_QUADTAP, dev->keybit);
|
__set_bit(BTN_TOOL_QUADTAP, dev->keybit);
|
||||||
|
|
|
@ -236,6 +236,7 @@ config SERIO_PS2MULT
|
||||||
|
|
||||||
config SERIO_ARC_PS2
|
config SERIO_ARC_PS2
|
||||||
tristate "ARC PS/2 support"
|
tristate "ARC PS/2 support"
|
||||||
|
depends on GENERIC_HARDIRQS
|
||||||
help
|
help
|
||||||
Say Y here if you have an ARC FPGA platform with a PS/2
|
Say Y here if you have an ARC FPGA platform with a PS/2
|
||||||
controller in it.
|
controller in it.
|
||||||
|
|
|
@ -359,6 +359,7 @@ static int wacom_intuos_inout(struct wacom_wac *wacom)
|
||||||
case 0x802: /* Intuos4 General Pen */
|
case 0x802: /* Intuos4 General Pen */
|
||||||
case 0x804: /* Intuos4 Marker Pen */
|
case 0x804: /* Intuos4 Marker Pen */
|
||||||
case 0x40802: /* Intuos4 Classic Pen */
|
case 0x40802: /* Intuos4 Classic Pen */
|
||||||
|
case 0x18803: /* DTH2242 Grip Pen */
|
||||||
case 0x022:
|
case 0x022:
|
||||||
wacom->tool[idx] = BTN_TOOL_PEN;
|
wacom->tool[idx] = BTN_TOOL_PEN;
|
||||||
break;
|
break;
|
||||||
|
@ -538,6 +539,13 @@ static int wacom_intuos_irq(struct wacom_wac *wacom)
|
||||||
input_report_key(input, wacom->tool[1], 0);
|
input_report_key(input, wacom->tool[1], 0);
|
||||||
input_report_abs(input, ABS_MISC, 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) {
|
} else if (features->type == WACOM_24HD) {
|
||||||
input_report_key(input, BTN_0, (data[6] & 0x01));
|
input_report_key(input, BTN_0, (data[6] & 0x01));
|
||||||
input_report_key(input, BTN_1, (data[6] & 0x02));
|
input_report_key(input, BTN_1, (data[6] & 0x02));
|
||||||
|
@ -785,25 +793,6 @@ static int wacom_intuos_irq(struct wacom_wac *wacom)
|
||||||
return 1;
|
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)
|
static int int_dist(int x1, int y1, int x2, int y2)
|
||||||
{
|
{
|
||||||
int x = x2 - x1;
|
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++) {
|
for (i = 0; i < contacts_to_send; i++) {
|
||||||
int offset = (WACOM_BYTES_PER_24HDT_PACKET * i) + 1;
|
int offset = (WACOM_BYTES_PER_24HDT_PACKET * i) + 1;
|
||||||
bool touch = data[offset] & 0x1 && !wacom->shared->stylus_in_proximity;
|
bool touch = data[offset] & 0x1 && !wacom->shared->stylus_in_proximity;
|
||||||
int id = data[offset + 1];
|
int slot = input_mt_get_slot_by_key(input, data[offset + 1]);
|
||||||
int slot = find_slot_from_contactid(wacom, id);
|
|
||||||
|
|
||||||
if (slot < 0)
|
if (slot < 0)
|
||||||
continue;
|
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_WIDTH_MINOR, min(w, h));
|
||||||
input_report_abs(input, ABS_MT_ORIENTATION, w > h);
|
input_report_abs(input, ABS_MT_ORIENTATION, w > h);
|
||||||
}
|
}
|
||||||
wacom->slots[slot] = touch ? id : -1;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
input_mt_report_pointer_emulation(input, true);
|
input_mt_report_pointer_emulation(input, true);
|
||||||
|
|
||||||
wacom->num_contacts_left -= contacts_to_send;
|
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;
|
int offset = (WACOM_BYTES_PER_MT_PACKET + x_offset) * i + 3;
|
||||||
bool touch = data[offset] & 0x1;
|
bool touch = data[offset] & 0x1;
|
||||||
int id = le16_to_cpup((__le16 *)&data[offset + 1]);
|
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)
|
if (slot < 0)
|
||||||
continue;
|
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_X, x);
|
||||||
input_report_abs(input, ABS_MT_POSITION_Y, y);
|
input_report_abs(input, ABS_MT_POSITION_Y, y);
|
||||||
}
|
}
|
||||||
wacom->slots[slot] = touch ? id : -1;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
input_mt_report_pointer_emulation(input, true);
|
input_mt_report_pointer_emulation(input, true);
|
||||||
|
|
||||||
wacom->num_contacts_left -= contacts_to_send;
|
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++;
|
contact_with_no_pen_down_count++;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
input_mt_report_pointer_emulation(input, true);
|
||||||
|
|
||||||
/* keep touch state for pen event */
|
/* keep touch state for pen event */
|
||||||
wacom->shared->touch_down = (contact_with_no_pen_down_count > 0);
|
wacom->shared->touch_down = (contact_with_no_pen_down_count > 0);
|
||||||
|
|
||||||
input_mt_report_pointer_emulation(input, true);
|
|
||||||
|
|
||||||
return 1;
|
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)
|
static void wacom_bpt3_touch_msg(struct wacom_wac *wacom, unsigned char *data)
|
||||||
{
|
{
|
||||||
struct input_dev *input = wacom->input;
|
struct input_dev *input = wacom->input;
|
||||||
int slot_id = data[0] - 2; /* data[0] is between 2 and 17 */
|
|
||||||
bool touch = data[1] & 0x80;
|
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;
|
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);
|
input_mt_report_slot_state(input, MT_TOOL_FINGER, touch);
|
||||||
|
|
||||||
if (touch) {
|
if (touch) {
|
||||||
|
@ -1162,7 +1148,6 @@ static int wacom_bpt3_touch(struct wacom_wac *wacom)
|
||||||
wacom_bpt3_button_msg(wacom, data + offset);
|
wacom_bpt3_button_msg(wacom, data + offset);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
input_mt_report_pointer_emulation(input, true);
|
input_mt_report_pointer_emulation(input, true);
|
||||||
|
|
||||||
input_sync(input);
|
input_sync(input);
|
||||||
|
@ -1319,6 +1304,7 @@ void wacom_wac_irq(struct wacom_wac *wacom_wac, size_t len)
|
||||||
case WACOM_21UX2:
|
case WACOM_21UX2:
|
||||||
case WACOM_22HD:
|
case WACOM_22HD:
|
||||||
case WACOM_24HD:
|
case WACOM_24HD:
|
||||||
|
case DTK:
|
||||||
sync = wacom_intuos_irq(wacom_wac);
|
sync = wacom_intuos_irq(wacom_wac);
|
||||||
break;
|
break;
|
||||||
|
|
||||||
|
@ -1444,6 +1430,51 @@ static unsigned int wacom_calculate_touch_res(unsigned int logical_max,
|
||||||
return (logical_max * 100) / physical_max;
|
return (logical_max * 100) / physical_max;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static void wacom_abs_set_axis(struct input_dev *input_dev,
|
||||||
|
struct wacom_wac *wacom_wac)
|
||||||
|
{
|
||||||
|
struct wacom_features *features = &wacom_wac->features;
|
||||||
|
|
||||||
|
if (features->device_type == BTN_TOOL_PEN) {
|
||||||
|
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 {
|
||||||
|
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,
|
int wacom_setup_input_capabilities(struct input_dev *input_dev,
|
||||||
struct wacom_wac *wacom_wac)
|
struct wacom_wac *wacom_wac)
|
||||||
{
|
{
|
||||||
|
@ -1453,30 +1484,10 @@ int wacom_setup_input_capabilities(struct input_dev *input_dev,
|
||||||
input_dev->evbit[0] |= BIT_MASK(EV_KEY) | BIT_MASK(EV_ABS);
|
input_dev->evbit[0] |= BIT_MASK(EV_KEY) | BIT_MASK(EV_ABS);
|
||||||
|
|
||||||
__set_bit(BTN_TOUCH, input_dev->keybit);
|
__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);
|
|
||||||
|
|
||||||
/* 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));
|
|
||||||
}
|
|
||||||
|
|
||||||
__set_bit(ABS_MISC, input_dev->absbit);
|
__set_bit(ABS_MISC, input_dev->absbit);
|
||||||
|
|
||||||
|
wacom_abs_set_axis(input_dev, wacom_wac);
|
||||||
|
|
||||||
switch (wacom_wac->features.type) {
|
switch (wacom_wac->features.type) {
|
||||||
case WACOM_MO:
|
case WACOM_MO:
|
||||||
input_set_abs_params(input_dev, ABS_WHEEL, 0, 71, 0, 0);
|
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_Y, input_dev->keybit);
|
||||||
__set_bit(BTN_Z, 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(BTN_0 + i, input_dev->keybit);
|
||||||
|
|
||||||
__set_bit(KEY_PROG1, input_dev->keybit);
|
__set_bit(KEY_PROG1, input_dev->keybit);
|
||||||
__set_bit(KEY_PROG2, input_dev->keybit);
|
__set_bit(KEY_PROG2, input_dev->keybit);
|
||||||
__set_bit(KEY_PROG3, 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_Z, -900, 899, 0, 0);
|
||||||
input_set_abs_params(input_dev, ABS_THROTTLE, 0, 71, 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) {
|
} else if (features->device_type == BTN_TOOL_FINGER) {
|
||||||
__clear_bit(ABS_MISC, input_dev->absbit);
|
__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,
|
input_set_abs_params(input_dev, ABS_MT_TOUCH_MAJOR,
|
||||||
0, features->x_max, 0, 0);
|
0, features->x_max, 0, 0);
|
||||||
input_set_abs_params(input_dev, ABS_MT_TOUCH_MINOR,
|
input_set_abs_params(input_dev, ABS_MT_TOUCH_MINOR,
|
||||||
0, features->y_max, 0, 0);
|
0, features->y_max, 0, 0);
|
||||||
|
input_mt_init_slots(input_dev, features->touch_max, INPUT_MT_POINTER);
|
||||||
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);
|
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
|
|
||||||
|
@ -1662,27 +1665,14 @@ int wacom_setup_input_capabilities(struct input_dev *input_dev,
|
||||||
|
|
||||||
case MTSCREEN:
|
case MTSCREEN:
|
||||||
case MTTPC:
|
case MTTPC:
|
||||||
if (features->device_type == BTN_TOOL_FINGER) {
|
|
||||||
wacom_wac->slots = kmalloc(features->touch_max *
|
|
||||||
sizeof(int),
|
|
||||||
GFP_KERNEL);
|
|
||||||
if (!wacom_wac->slots)
|
|
||||||
return -ENOMEM;
|
|
||||||
|
|
||||||
for (i = 0; i < features->touch_max; i++)
|
|
||||||
wacom_wac->slots[i] = -1;
|
|
||||||
}
|
|
||||||
/* fall through */
|
|
||||||
|
|
||||||
case TABLETPC2FG:
|
case TABLETPC2FG:
|
||||||
if (features->device_type == BTN_TOOL_FINGER) {
|
if (features->device_type == BTN_TOOL_FINGER) {
|
||||||
input_mt_init_slots(input_dev, features->touch_max, 0);
|
unsigned int flags = INPUT_MT_DIRECT;
|
||||||
input_set_abs_params(input_dev, ABS_MT_TOOL_TYPE,
|
|
||||||
0, MT_TOOL_MAX, 0, 0);
|
if (wacom_wac->features.type == TABLETPC2FG)
|
||||||
input_set_abs_params(input_dev, ABS_MT_POSITION_X,
|
flags = 0;
|
||||||
0, features->x_max, 0, 0);
|
|
||||||
input_set_abs_params(input_dev, ABS_MT_POSITION_Y,
|
input_mt_init_slots(input_dev, features->touch_max, flags);
|
||||||
0, features->y_max, 0, 0);
|
|
||||||
}
|
}
|
||||||
/* fall through */
|
/* fall through */
|
||||||
|
|
||||||
|
@ -1725,35 +1715,26 @@ int wacom_setup_input_capabilities(struct input_dev *input_dev,
|
||||||
__set_bit(INPUT_PROP_POINTER, input_dev->propbit);
|
__set_bit(INPUT_PROP_POINTER, input_dev->propbit);
|
||||||
|
|
||||||
if (features->device_type == BTN_TOOL_FINGER) {
|
if (features->device_type == BTN_TOOL_FINGER) {
|
||||||
|
unsigned int flags = INPUT_MT_POINTER;
|
||||||
|
|
||||||
__set_bit(BTN_LEFT, input_dev->keybit);
|
__set_bit(BTN_LEFT, input_dev->keybit);
|
||||||
__set_bit(BTN_FORWARD, input_dev->keybit);
|
__set_bit(BTN_FORWARD, input_dev->keybit);
|
||||||
__set_bit(BTN_BACK, input_dev->keybit);
|
__set_bit(BTN_BACK, input_dev->keybit);
|
||||||
__set_bit(BTN_RIGHT, 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) {
|
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,
|
input_set_abs_params(input_dev,
|
||||||
ABS_MT_TOUCH_MAJOR,
|
ABS_MT_TOUCH_MAJOR,
|
||||||
0, features->x_max, 0, 0);
|
0, features->x_max, 0, 0);
|
||||||
input_set_abs_params(input_dev,
|
input_set_abs_params(input_dev,
|
||||||
ABS_MT_TOUCH_MINOR,
|
ABS_MT_TOUCH_MINOR,
|
||||||
0, features->y_max, 0, 0);
|
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_mt_init_slots(input_dev, features->touch_max, flags);
|
||||||
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);
|
|
||||||
} else if (features->device_type == BTN_TOOL_PEN) {
|
} else if (features->device_type == BTN_TOOL_PEN) {
|
||||||
__set_bit(BTN_TOOL_RUBBER, input_dev->keybit);
|
__set_bit(BTN_TOOL_RUBBER, input_dev->keybit);
|
||||||
__set_bit(BTN_TOOL_PEN, 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 =
|
static const struct wacom_features wacom_features_0xF0 =
|
||||||
{ "Wacom DTU1631", WACOM_PKGLEN_GRAPHIRE, 34623, 19553, 511,
|
{ "Wacom DTU1631", WACOM_PKGLEN_GRAPHIRE, 34623, 19553, 511,
|
||||||
0, DTU, WACOM_INTUOS_RES, WACOM_INTUOS_RES };
|
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 =
|
static const struct wacom_features wacom_features_0xCC =
|
||||||
{ "Wacom Cintiq 21UX2", WACOM_PKGLEN_INTUOS, 87200, 65600, 2047,
|
{ "Wacom Cintiq 21UX2", WACOM_PKGLEN_INTUOS, 87200, 65600, 2047,
|
||||||
63, WACOM_21UX2, WACOM_INTUOS3_RES, WACOM_INTUOS3_RES };
|
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(0x43) },
|
||||||
{ USB_DEVICE_WACOM(0x44) },
|
{ USB_DEVICE_WACOM(0x44) },
|
||||||
{ USB_DEVICE_WACOM(0x45) },
|
{ USB_DEVICE_WACOM(0x45) },
|
||||||
|
{ USB_DEVICE_WACOM(0x59) },
|
||||||
|
{ USB_DEVICE_WACOM(0x5D) },
|
||||||
{ USB_DEVICE_WACOM(0xB0) },
|
{ USB_DEVICE_WACOM(0xB0) },
|
||||||
{ USB_DEVICE_WACOM(0xB1) },
|
{ USB_DEVICE_WACOM(0xB1) },
|
||||||
{ USB_DEVICE_WACOM(0xB2) },
|
{ USB_DEVICE_WACOM(0xB2) },
|
||||||
|
|
|
@ -78,6 +78,7 @@ enum {
|
||||||
INTUOS5L,
|
INTUOS5L,
|
||||||
WACOM_21UX2,
|
WACOM_21UX2,
|
||||||
WACOM_22HD,
|
WACOM_22HD,
|
||||||
|
DTK,
|
||||||
WACOM_24HD,
|
WACOM_24HD,
|
||||||
CINTIQ,
|
CINTIQ,
|
||||||
WACOM_BEE,
|
WACOM_BEE,
|
||||||
|
@ -135,7 +136,6 @@ struct wacom_wac {
|
||||||
int pid;
|
int pid;
|
||||||
int battery_capacity;
|
int battery_capacity;
|
||||||
int num_contacts_left;
|
int num_contacts_left;
|
||||||
int *slots;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|
|
@ -359,7 +359,7 @@ config TOUCHSCREEN_MCS5000
|
||||||
|
|
||||||
config TOUCHSCREEN_MMS114
|
config TOUCHSCREEN_MMS114
|
||||||
tristate "MELFAS MMS114 touchscreen"
|
tristate "MELFAS MMS114 touchscreen"
|
||||||
depends on I2C
|
depends on I2C && GENERIC_HARDIRQS
|
||||||
help
|
help
|
||||||
Say Y here if you have the MELFAS MMS114 touchscreen controller
|
Say Y here if you have the MELFAS MMS114 touchscreen controller
|
||||||
chip in your system.
|
chip in your system.
|
||||||
|
|
|
@ -193,7 +193,6 @@ static struct spi_driver cyttsp_spi_driver = {
|
||||||
|
|
||||||
module_spi_driver(cyttsp_spi_driver);
|
module_spi_driver(cyttsp_spi_driver);
|
||||||
|
|
||||||
MODULE_ALIAS("spi:cyttsp");
|
|
||||||
MODULE_LICENSE("GPL");
|
MODULE_LICENSE("GPL");
|
||||||
MODULE_DESCRIPTION("Cypress TrueTouch(R) Standard Product (TTSP) SPI driver");
|
MODULE_DESCRIPTION("Cypress TrueTouch(R) Standard Product (TTSP) SPI driver");
|
||||||
MODULE_AUTHOR("Cypress");
|
MODULE_AUTHOR("Cypress");
|
||||||
|
|
|
@ -429,12 +429,12 @@ static int mms114_probe(struct i2c_client *client,
|
||||||
return -ENODEV;
|
return -ENODEV;
|
||||||
}
|
}
|
||||||
|
|
||||||
data = kzalloc(sizeof(struct mms114_data), GFP_KERNEL);
|
data = devm_kzalloc(&client->dev, sizeof(struct mms114_data),
|
||||||
input_dev = input_allocate_device();
|
GFP_KERNEL);
|
||||||
|
input_dev = devm_input_allocate_device(&client->dev);
|
||||||
if (!data || !input_dev) {
|
if (!data || !input_dev) {
|
||||||
dev_err(&client->dev, "Failed to allocate memory\n");
|
dev_err(&client->dev, "Failed to allocate memory\n");
|
||||||
error = -ENOMEM;
|
return -ENOMEM;
|
||||||
goto err_free_mem;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
data->client = client;
|
data->client = client;
|
||||||
|
@ -466,57 +466,36 @@ static int mms114_probe(struct i2c_client *client,
|
||||||
input_set_drvdata(input_dev, data);
|
input_set_drvdata(input_dev, data);
|
||||||
i2c_set_clientdata(client, 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)) {
|
if (IS_ERR(data->core_reg)) {
|
||||||
error = PTR_ERR(data->core_reg);
|
error = PTR_ERR(data->core_reg);
|
||||||
dev_err(&client->dev,
|
dev_err(&client->dev,
|
||||||
"Unable to get the Core regulator (%d)\n", error);
|
"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)) {
|
if (IS_ERR(data->io_reg)) {
|
||||||
error = PTR_ERR(data->io_reg);
|
error = PTR_ERR(data->io_reg);
|
||||||
dev_err(&client->dev,
|
dev_err(&client->dev,
|
||||||
"Unable to get the IO regulator (%d)\n", error);
|
"Unable to get the IO regulator (%d)\n", error);
|
||||||
goto err_core_reg;
|
return error;
|
||||||
}
|
}
|
||||||
|
|
||||||
error = request_threaded_irq(client->irq, NULL, mms114_interrupt,
|
error = devm_request_threaded_irq(&client->dev, client->irq, NULL,
|
||||||
IRQF_TRIGGER_FALLING | IRQF_ONESHOT, "mms114", data);
|
mms114_interrupt, IRQF_TRIGGER_FALLING | IRQF_ONESHOT,
|
||||||
|
dev_name(&client->dev), data);
|
||||||
if (error) {
|
if (error) {
|
||||||
dev_err(&client->dev, "Failed to register interrupt\n");
|
dev_err(&client->dev, "Failed to register interrupt\n");
|
||||||
goto err_io_reg;
|
return error;
|
||||||
}
|
}
|
||||||
disable_irq(client->irq);
|
disable_irq(client->irq);
|
||||||
|
|
||||||
error = input_register_device(data->input_dev);
|
error = input_register_device(data->input_dev);
|
||||||
if (error)
|
if (error) {
|
||||||
goto err_free_irq;
|
dev_err(&client->dev, "Failed to register input device\n");
|
||||||
|
|
||||||
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;
|
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);
|
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
@ -590,7 +569,6 @@ static struct i2c_driver mms114_driver = {
|
||||||
.of_match_table = of_match_ptr(mms114_dt_match),
|
.of_match_table = of_match_ptr(mms114_dt_match),
|
||||||
},
|
},
|
||||||
.probe = mms114_probe,
|
.probe = mms114_probe,
|
||||||
.remove = mms114_remove,
|
|
||||||
.id_table = mms114_id,
|
.id_table = mms114_id,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
@ -120,6 +120,7 @@ static void stmpe_work(struct work_struct *work)
|
||||||
__stmpe_reset_fifo(ts->stmpe);
|
__stmpe_reset_fifo(ts->stmpe);
|
||||||
|
|
||||||
input_report_abs(ts->idev, ABS_PRESSURE, 0);
|
input_report_abs(ts->idev, ABS_PRESSURE, 0);
|
||||||
|
input_report_key(ts->idev, BTN_TOUCH, 0);
|
||||||
input_sync(ts->idev);
|
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_X, x);
|
||||||
input_report_abs(ts->idev, ABS_Y, y);
|
input_report_abs(ts->idev, ABS_Y, y);
|
||||||
input_report_abs(ts->idev, ABS_PRESSURE, z);
|
input_report_abs(ts->idev, ABS_PRESSURE, z);
|
||||||
|
input_report_key(ts->idev, BTN_TOUCH, 1);
|
||||||
input_sync(ts->idev);
|
input_sync(ts->idev);
|
||||||
|
|
||||||
/* flush the FIFO after we have read out our values. */
|
/* flush the FIFO after we have read out our values. */
|
||||||
|
|
|
@ -753,3 +753,4 @@ module_spi_driver(tsc2005_driver);
|
||||||
MODULE_AUTHOR("Lauri Leukkunen <lauri.leukkunen@nokia.com>");
|
MODULE_AUTHOR("Lauri Leukkunen <lauri.leukkunen@nokia.com>");
|
||||||
MODULE_DESCRIPTION("TSC2005 Touchscreen Driver");
|
MODULE_DESCRIPTION("TSC2005 Touchscreen Driver");
|
||||||
MODULE_LICENSE("GPL");
|
MODULE_LICENSE("GPL");
|
||||||
|
MODULE_ALIAS("spi:tsc2005");
|
||||||
|
|
|
@ -247,7 +247,7 @@ static int wm831x_ts_probe(struct platform_device *pdev)
|
||||||
|
|
||||||
wm831x_ts = devm_kzalloc(&pdev->dev, sizeof(struct wm831x_ts),
|
wm831x_ts = devm_kzalloc(&pdev->dev, sizeof(struct wm831x_ts),
|
||||||
GFP_KERNEL);
|
GFP_KERNEL);
|
||||||
input_dev = input_allocate_device();
|
input_dev = devm_input_allocate_device(&pdev->dev);
|
||||||
if (!wm831x_ts || !input_dev) {
|
if (!wm831x_ts || !input_dev) {
|
||||||
error = -ENOMEM;
|
error = -ENOMEM;
|
||||||
goto err_alloc;
|
goto err_alloc;
|
||||||
|
@ -376,7 +376,6 @@ err_pd_irq:
|
||||||
err_data_irq:
|
err_data_irq:
|
||||||
free_irq(wm831x_ts->data_irq, wm831x_ts);
|
free_irq(wm831x_ts->data_irq, wm831x_ts);
|
||||||
err_alloc:
|
err_alloc:
|
||||||
input_free_device(input_dev);
|
|
||||||
|
|
||||||
return error;
|
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->pd_irq, wm831x_ts);
|
||||||
free_irq(wm831x_ts->data_irq, wm831x_ts);
|
free_irq(wm831x_ts->data_irq, wm831x_ts);
|
||||||
input_unregister_device(wm831x_ts->input_dev);
|
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
|
@ -42,6 +42,7 @@
|
||||||
#include <linux/slab.h>
|
#include <linux/slab.h>
|
||||||
#include <linux/input.h>
|
#include <linux/input.h>
|
||||||
#include <linux/uaccess.h>
|
#include <linux/uaccess.h>
|
||||||
|
#include <linux/moduleparam.h>
|
||||||
|
|
||||||
#include <asm/ptrace.h>
|
#include <asm/ptrace.h>
|
||||||
#include <asm/irq_regs.h>
|
#include <asm/irq_regs.h>
|
||||||
|
@ -578,8 +579,71 @@ struct sysrq_state {
|
||||||
bool active;
|
bool active;
|
||||||
bool need_reinject;
|
bool need_reinject;
|
||||||
bool reinjecting;
|
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)
|
static void sysrq_reinject_alt_sysrq(struct work_struct *work)
|
||||||
{
|
{
|
||||||
struct sysrq_state *sysrq =
|
struct sysrq_state *sysrq =
|
||||||
|
@ -606,27 +670,12 @@ static void sysrq_reinject_alt_sysrq(struct work_struct *work)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static bool sysrq_filter(struct input_handle *handle,
|
static bool sysrq_handle_keypress(struct sysrq_state *sysrq,
|
||||||
unsigned int type, unsigned int code, int value)
|
unsigned int code, int value)
|
||||||
{
|
{
|
||||||
struct sysrq_state *sysrq = handle->private;
|
|
||||||
bool was_active = sysrq->active;
|
bool was_active = sysrq->active;
|
||||||
bool suppress;
|
bool suppress;
|
||||||
|
|
||||||
/*
|
|
||||||
* 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:
|
|
||||||
switch (code) {
|
switch (code) {
|
||||||
|
|
||||||
case KEY_LEFTALT:
|
case KEY_LEFTALT:
|
||||||
|
@ -664,7 +713,7 @@ static bool sysrq_filter(struct input_handle *handle,
|
||||||
* triggering print screen function.
|
* triggering print screen function.
|
||||||
*/
|
*/
|
||||||
if (sysrq->active)
|
if (sysrq->active)
|
||||||
clear_bit(KEY_SYSRQ, handle->dev->key);
|
clear_bit(KEY_SYSRQ, sysrq->handle.dev->key);
|
||||||
|
|
||||||
break;
|
break;
|
||||||
|
|
||||||
|
@ -679,6 +728,13 @@ static bool sysrq_filter(struct input_handle *handle,
|
||||||
suppress = sysrq->active;
|
suppress = sysrq->active;
|
||||||
|
|
||||||
if (!sysrq->active) {
|
if (!sysrq->active) {
|
||||||
|
|
||||||
|
/*
|
||||||
|
* See if reset sequence has changed since the last time.
|
||||||
|
*/
|
||||||
|
if (sysrq->reset_seq_version != sysrq_reset_seq_version)
|
||||||
|
sysrq_parse_reset_sequence(sysrq);
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* If we are not suppressing key presses keep track of
|
* If we are not suppressing key presses keep track of
|
||||||
* keyboard state so we can release keys that have been
|
* keyboard state so we can release keys that have been
|
||||||
|
@ -692,14 +748,43 @@ static bool sysrq_filter(struct input_handle *handle,
|
||||||
if (was_active)
|
if (was_active)
|
||||||
schedule_work(&sysrq->reinject_work);
|
schedule_work(&sysrq->reinject_work);
|
||||||
|
|
||||||
} else if (value == 0 &&
|
if (sysrq_detect_reset_sequence(sysrq, code, value)) {
|
||||||
test_and_clear_bit(code, sysrq->key_down)) {
|
/* Force emergency reboot */
|
||||||
|
__handle_sysrq(sysrq_xlate[KEY_B], false);
|
||||||
|
}
|
||||||
|
|
||||||
|
} else if (value == 0 && test_and_clear_bit(code, sysrq->key_down)) {
|
||||||
/*
|
/*
|
||||||
* Pass on release events for keys that was pressed before
|
* Pass on release events for keys that was pressed before
|
||||||
* entering SysRq mode.
|
* entering SysRq mode.
|
||||||
*/
|
*/
|
||||||
suppress = false;
|
suppress = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
return suppress;
|
||||||
|
}
|
||||||
|
|
||||||
|
static bool sysrq_filter(struct input_handle *handle,
|
||||||
|
unsigned int type, unsigned int code, int value)
|
||||||
|
{
|
||||||
|
struct sysrq_state *sysrq = handle->private;
|
||||||
|
bool suppress;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* 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;
|
break;
|
||||||
|
|
||||||
default:
|
default:
|
||||||
|
@ -787,7 +872,20 @@ static bool sysrq_handler_registered;
|
||||||
|
|
||||||
static inline void sysrq_register_handler(void)
|
static inline void sysrq_register_handler(void)
|
||||||
{
|
{
|
||||||
|
extern unsigned short platform_sysrq_reset_seq[] __weak;
|
||||||
|
unsigned short key;
|
||||||
int error;
|
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);
|
error = input_register_handler(&sysrq_handler);
|
||||||
if (error)
|
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
|
#else
|
||||||
|
|
||||||
static inline void sysrq_register_handler(void)
|
static inline void sysrq_register_handler(void)
|
||||||
|
|
|
@ -22,6 +22,18 @@
|
||||||
|
|
||||||
#define BMA150_DRIVER "bma150"
|
#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 {
|
struct bma150_cfg {
|
||||||
bool any_motion_int; /* Set to enable any-motion interrupt */
|
bool any_motion_int; /* Set to enable any-motion interrupt */
|
||||||
bool hg_int; /* Set to enable high-G 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_hyst; /* Low-G hysterisis */
|
||||||
unsigned char lg_dur; /* Low-G duration */
|
unsigned char lg_dur; /* Low-G duration */
|
||||||
unsigned char lg_thres; /* Low-G threshold */
|
unsigned char lg_thres; /* Low-G threshold */
|
||||||
unsigned char range; /* BMA0150_RANGE_xxx (in G) */
|
unsigned char range; /* one of BMA0150_RANGE_xxx */
|
||||||
unsigned char bandwidth; /* BMA0150_BW_xxx (in Hz) */
|
unsigned char bandwidth; /* one of BMA0150_BW_xxx */
|
||||||
};
|
};
|
||||||
|
|
||||||
struct bma150_platform_data {
|
struct bma150_platform_data {
|
||||||
|
|
|
@ -13,6 +13,8 @@
|
||||||
#ifndef __LINUX_INPUT_ADXL34X_H__
|
#ifndef __LINUX_INPUT_ADXL34X_H__
|
||||||
#define __LINUX_INPUT_ADXL34X_H__
|
#define __LINUX_INPUT_ADXL34X_H__
|
||||||
|
|
||||||
|
#include <linux/input.h>
|
||||||
|
|
||||||
struct adxl34x_platform_data {
|
struct adxl34x_platform_data {
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|
|
|
@ -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
|
|
|
@ -36,7 +36,7 @@ struct ps2dev {
|
||||||
wait_queue_head_t wait;
|
wait_queue_head_t wait;
|
||||||
|
|
||||||
unsigned long flags;
|
unsigned long flags;
|
||||||
unsigned char cmdbuf[6];
|
unsigned char cmdbuf[8];
|
||||||
unsigned char cmdcnt;
|
unsigned char cmdcnt;
|
||||||
unsigned char nak;
|
unsigned char nak;
|
||||||
};
|
};
|
||||||
|
|
Loading…
Reference in New Issue