Merge branch 'next' into for-linus
Prepare first set of updates for 3.9 merge window.
This commit is contained in:
Коммит
2d9f0d964b
|
@ -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
|
||||
The key controller has maximum 24 pins to make matrix keypad. Any pin
|
||||
can be configured as row or column. The maximum column pin can be 8
|
||||
and maximum row pins can be 16 for Tegra20/Tegra30.
|
||||
|
||||
Required properties:
|
||||
- compatible: "nvidia,tegra20-kbc"
|
||||
- reg: Register base address of KBC.
|
||||
- interrupts: Interrupt number for the KBC.
|
||||
- nvidia,kbc-row-pins: The KBC pins which are configured as row. This is an
|
||||
array of pin numbers which is used as rows.
|
||||
- nvidia,kbc-col-pins: The KBC pins which are configured as column. This is an
|
||||
array of pin numbers which is used as column.
|
||||
- linux,keymap: The keymap for keys as described in the binding document
|
||||
devicetree/bindings/input/matrix-keymap.txt.
|
||||
|
||||
Optional properties, in addition to those specified by the shared
|
||||
matrix-keyboard bindings:
|
||||
|
@ -19,5 +30,16 @@ Example:
|
|||
keyboard: keyboard {
|
||||
compatible = "nvidia,tegra20-kbc";
|
||||
reg = <0x7000e200 0x100>;
|
||||
interrupts = <0 85 0x04>;
|
||||
nvidia,ghost-filter;
|
||||
nvidia,debounce-delay-ms = <640>;
|
||||
nvidia,kbc-row-pins = <0 1 2>; /* pin 0, 1, 2 as rows */
|
||||
nvidia,kbc-col-pins = <11 12 13>; /* pin 11, 12, 13 as columns */
|
||||
linux,keymap = <0x00000074
|
||||
0x00010067
|
||||
0x00020066
|
||||
0x01010068
|
||||
0x02000069
|
||||
0x02010070
|
||||
0x02020071>;
|
||||
};
|
||||
|
|
|
@ -3,7 +3,7 @@
|
|||
#
|
||||
|
||||
menu "Input device support"
|
||||
depends on !S390 && !UML
|
||||
depends on !UML
|
||||
|
||||
config INPUT
|
||||
tristate "Generic input layer (needed for keyboard, mouse, ...)" if EXPERT
|
||||
|
|
|
@ -18,6 +18,7 @@ static void copy_abs(struct input_dev *dev, unsigned int dst, unsigned int src)
|
|||
{
|
||||
if (dev->absinfo && test_bit(src, dev->absbit)) {
|
||||
dev->absinfo[dst] = dev->absinfo[src];
|
||||
dev->absinfo[dst].fuzz = 0;
|
||||
dev->absbit[BIT_WORD(dst)] |= BIT_MASK(dst);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -12,7 +12,7 @@
|
|||
* the Free Software Foundation.
|
||||
*/
|
||||
|
||||
/* #define WK0701_DEBUG */
|
||||
#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
|
||||
|
||||
#define RESERVE 20000
|
||||
#define SYNC_PULSE 1306000
|
||||
|
@ -67,6 +67,7 @@ static inline void walkera0701_parse_frame(struct walkera_dev *w)
|
|||
{
|
||||
int i;
|
||||
int val1, val2, val3, val4, val5, val6, val7, val8;
|
||||
int magic, magic_bit;
|
||||
int crc1, crc2;
|
||||
|
||||
for (crc1 = crc2 = i = 0; i < 10; i++) {
|
||||
|
@ -102,17 +103,12 @@ static inline void walkera0701_parse_frame(struct walkera_dev *w)
|
|||
val8 = (w->buf[18] & 1) << 8 | (w->buf[19] << 4) | w->buf[20];
|
||||
val8 *= (w->buf[18] & 2) - 1; /*sign */
|
||||
|
||||
#ifdef WK0701_DEBUG
|
||||
{
|
||||
int magic, magic_bit;
|
||||
magic = (w->buf[21] << 4) | w->buf[22];
|
||||
magic_bit = (w->buf[24] & 8) >> 3;
|
||||
printk(KERN_DEBUG
|
||||
"walkera0701: %4d %4d %4d %4d %4d %4d %4d %4d (magic %2x %d)\n",
|
||||
val1, val2, val3, val4, val5, val6, val7, val8, magic,
|
||||
magic_bit);
|
||||
}
|
||||
#endif
|
||||
magic = (w->buf[21] << 4) | w->buf[22];
|
||||
magic_bit = (w->buf[24] & 8) >> 3;
|
||||
pr_debug("%4d %4d %4d %4d %4d %4d %4d %4d (magic %2x %d)\n",
|
||||
val1, val2, val3, val4, val5, val6, val7, val8,
|
||||
magic, magic_bit);
|
||||
|
||||
input_report_abs(w->input_dev, ABS_X, val2);
|
||||
input_report_abs(w->input_dev, ABS_Y, val1);
|
||||
input_report_abs(w->input_dev, ABS_Z, val6);
|
||||
|
@ -187,6 +183,9 @@ static int walkera0701_open(struct input_dev *dev)
|
|||
{
|
||||
struct walkera_dev *w = input_get_drvdata(dev);
|
||||
|
||||
if (parport_claim(w->pardevice))
|
||||
return -EBUSY;
|
||||
|
||||
parport_enable_irq(w->parport);
|
||||
return 0;
|
||||
}
|
||||
|
@ -197,40 +196,51 @@ static void walkera0701_close(struct input_dev *dev)
|
|||
|
||||
parport_disable_irq(w->parport);
|
||||
hrtimer_cancel(&w->timer);
|
||||
|
||||
parport_release(w->pardevice);
|
||||
}
|
||||
|
||||
static int walkera0701_connect(struct walkera_dev *w, int parport)
|
||||
{
|
||||
int err = -ENODEV;
|
||||
int error;
|
||||
|
||||
w->parport = parport_find_number(parport);
|
||||
if (w->parport == NULL)
|
||||
if (!w->parport) {
|
||||
pr_err("parport %d does not exist\n", parport);
|
||||
return -ENODEV;
|
||||
|
||||
if (w->parport->irq == -1) {
|
||||
printk(KERN_ERR "walkera0701: parport without interrupt\n");
|
||||
goto init_err;
|
||||
}
|
||||
|
||||
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",
|
||||
NULL, NULL, walkera0701_irq_handler,
|
||||
PARPORT_DEV_EXCL, w);
|
||||
if (!w->pardevice)
|
||||
goto init_err;
|
||||
if (!w->pardevice) {
|
||||
pr_err("failed to register parport device\n");
|
||||
error = -EIO;
|
||||
goto err_put_parport;
|
||||
}
|
||||
|
||||
if (parport_negotiate(w->pardevice->port, IEEE1284_MODE_COMPAT))
|
||||
goto init_err1;
|
||||
|
||||
if (parport_claim(w->pardevice))
|
||||
goto init_err1;
|
||||
if (parport_negotiate(w->pardevice->port, IEEE1284_MODE_COMPAT)) {
|
||||
pr_err("failed to negotiate parport mode\n");
|
||||
error = -EIO;
|
||||
goto err_unregister_device;
|
||||
}
|
||||
|
||||
hrtimer_init(&w->timer, CLOCK_MONOTONIC, HRTIMER_MODE_REL);
|
||||
w->timer.function = timer_handler;
|
||||
|
||||
w->input_dev = input_allocate_device();
|
||||
if (!w->input_dev)
|
||||
goto init_err2;
|
||||
if (!w->input_dev) {
|
||||
pr_err("failed to allocate input device\n");
|
||||
error = -ENOMEM;
|
||||
goto err_unregister_device;
|
||||
}
|
||||
|
||||
input_set_drvdata(w->input_dev, w);
|
||||
w->input_dev->name = "Walkera WK-0701 TX";
|
||||
|
@ -241,6 +251,7 @@ static int walkera0701_connect(struct walkera_dev *w, int parport)
|
|||
w->input_dev->id.vendor = 0x0001;
|
||||
w->input_dev->id.product = 0x0001;
|
||||
w->input_dev->id.version = 0x0100;
|
||||
w->input_dev->dev.parent = w->parport->dev;
|
||||
w->input_dev->open = walkera0701_open;
|
||||
w->input_dev->close = walkera0701_close;
|
||||
|
||||
|
@ -254,27 +265,26 @@ static int walkera0701_connect(struct walkera_dev *w, int parport)
|
|||
input_set_abs_params(w->input_dev, ABS_RUDDER, -512, 512, 0, 0);
|
||||
input_set_abs_params(w->input_dev, ABS_MISC, -512, 512, 0, 0);
|
||||
|
||||
err = input_register_device(w->input_dev);
|
||||
if (err)
|
||||
goto init_err3;
|
||||
error = input_register_device(w->input_dev);
|
||||
if (error) {
|
||||
pr_err("failed to register input device\n");
|
||||
goto err_free_input_dev;
|
||||
}
|
||||
|
||||
return 0;
|
||||
|
||||
init_err3:
|
||||
err_free_input_dev:
|
||||
input_free_device(w->input_dev);
|
||||
init_err2:
|
||||
parport_release(w->pardevice);
|
||||
init_err1:
|
||||
err_unregister_device:
|
||||
parport_unregister_device(w->pardevice);
|
||||
init_err:
|
||||
err_put_parport:
|
||||
parport_put_port(w->parport);
|
||||
return err;
|
||||
return error;
|
||||
}
|
||||
|
||||
static void walkera0701_disconnect(struct walkera_dev *w)
|
||||
{
|
||||
input_unregister_device(w->input_dev);
|
||||
parport_release(w->pardevice);
|
||||
parport_unregister_device(w->pardevice);
|
||||
parport_put_port(w->parport);
|
||||
}
|
||||
|
|
|
@ -224,7 +224,7 @@ config KEYBOARD_TCA6416
|
|||
|
||||
config KEYBOARD_TCA8418
|
||||
tristate "TCA8418 Keypad Support"
|
||||
depends on I2C
|
||||
depends on I2C && GENERIC_HARDIRQS
|
||||
select INPUT_MATRIXKMAP
|
||||
help
|
||||
This driver implements basic keypad functionality
|
||||
|
@ -303,7 +303,7 @@ config KEYBOARD_HP7XX
|
|||
|
||||
config KEYBOARD_LM8323
|
||||
tristate "LM8323 keypad chip"
|
||||
depends on I2C
|
||||
depends on I2C && GENERIC_HARDIRQS
|
||||
depends on LEDS_CLASS
|
||||
help
|
||||
If you say yes here you get support for the National Semiconductor
|
||||
|
@ -420,7 +420,7 @@ config KEYBOARD_NOMADIK
|
|||
|
||||
config KEYBOARD_TEGRA
|
||||
tristate "NVIDIA Tegra internal matrix keyboard controller support"
|
||||
depends on ARCH_TEGRA
|
||||
depends on ARCH_TEGRA && OF
|
||||
select INPUT_MATRIXKMAP
|
||||
help
|
||||
Say Y here if you want to use a matrix keyboard connected directly
|
||||
|
@ -479,6 +479,16 @@ config KEYBOARD_SAMSUNG
|
|||
To compile this driver as a module, choose M here: the
|
||||
module will be called samsung-keypad.
|
||||
|
||||
config KEYBOARD_GOLDFISH_EVENTS
|
||||
depends on GOLDFISH
|
||||
tristate "Generic Input Event device for Goldfish"
|
||||
help
|
||||
Say Y here to get an input event device for the Goldfish virtual
|
||||
device emulator.
|
||||
|
||||
To compile this driver as a module, choose M here: the
|
||||
module will be called goldfish-events.
|
||||
|
||||
config KEYBOARD_STOWAWAY
|
||||
tristate "Stowaway keyboard"
|
||||
select SERIO
|
||||
|
|
|
@ -13,6 +13,7 @@ obj-$(CONFIG_KEYBOARD_ATKBD) += atkbd.o
|
|||
obj-$(CONFIG_KEYBOARD_BFIN) += bf54x-keys.o
|
||||
obj-$(CONFIG_KEYBOARD_DAVINCI) += davinci_keyscan.o
|
||||
obj-$(CONFIG_KEYBOARD_EP93XX) += ep93xx_keypad.o
|
||||
obj-$(CONFIG_KEYBOARD_GOLDFISH_EVENTS) += goldfish_events.o
|
||||
obj-$(CONFIG_KEYBOARD_GPIO) += gpio_keys.o
|
||||
obj-$(CONFIG_KEYBOARD_GPIO_POLLED) += gpio_keys_polled.o
|
||||
obj-$(CONFIG_KEYBOARD_TCA6416) += tca6416-keypad.o
|
||||
|
|
|
@ -676,6 +676,39 @@ static inline void atkbd_disable(struct atkbd *atkbd)
|
|||
serio_continue_rx(atkbd->ps2dev.serio);
|
||||
}
|
||||
|
||||
static int atkbd_activate(struct atkbd *atkbd)
|
||||
{
|
||||
struct ps2dev *ps2dev = &atkbd->ps2dev;
|
||||
|
||||
/*
|
||||
* Enable the keyboard to receive keystrokes.
|
||||
*/
|
||||
|
||||
if (ps2_command(ps2dev, NULL, ATKBD_CMD_ENABLE)) {
|
||||
dev_err(&ps2dev->serio->dev,
|
||||
"Failed to enable keyboard on %s\n",
|
||||
ps2dev->serio->phys);
|
||||
return -1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* atkbd_deactivate() resets and disables the keyboard from sending
|
||||
* keystrokes.
|
||||
*/
|
||||
|
||||
static void atkbd_deactivate(struct atkbd *atkbd)
|
||||
{
|
||||
struct ps2dev *ps2dev = &atkbd->ps2dev;
|
||||
|
||||
if (ps2_command(ps2dev, NULL, ATKBD_CMD_RESET_DIS))
|
||||
dev_err(&ps2dev->serio->dev,
|
||||
"Failed to deactivate keyboard on %s\n",
|
||||
ps2dev->serio->phys);
|
||||
}
|
||||
|
||||
/*
|
||||
* atkbd_probe() probes for an AT keyboard on a serio port.
|
||||
*/
|
||||
|
@ -726,11 +759,17 @@ static int atkbd_probe(struct atkbd *atkbd)
|
|||
|
||||
if (atkbd->id == 0xaca1 && atkbd->translated) {
|
||||
dev_err(&ps2dev->serio->dev,
|
||||
"NCD terminal keyboards are only supported on non-translating controlelrs. "
|
||||
"NCD terminal keyboards are only supported on non-translating controllers. "
|
||||
"Use i8042.direct=1 to disable translation.\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
/*
|
||||
* Make sure nothing is coming from the keyboard and disturbs our
|
||||
* internal state.
|
||||
*/
|
||||
atkbd_deactivate(atkbd);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
@ -825,24 +864,6 @@ static int atkbd_reset_state(struct atkbd *atkbd)
|
|||
return 0;
|
||||
}
|
||||
|
||||
static int atkbd_activate(struct atkbd *atkbd)
|
||||
{
|
||||
struct ps2dev *ps2dev = &atkbd->ps2dev;
|
||||
|
||||
/*
|
||||
* Enable the keyboard to receive keystrokes.
|
||||
*/
|
||||
|
||||
if (ps2_command(ps2dev, NULL, ATKBD_CMD_ENABLE)) {
|
||||
dev_err(&ps2dev->serio->dev,
|
||||
"Failed to enable keyboard on %s\n",
|
||||
ps2dev->serio->phys);
|
||||
return -1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* atkbd_cleanup() restores the keyboard state so that BIOS is happy after a
|
||||
* reboot.
|
||||
|
@ -1150,7 +1171,6 @@ static int atkbd_connect(struct serio *serio, struct serio_driver *drv)
|
|||
|
||||
atkbd->set = atkbd_select_set(atkbd, atkbd_set, atkbd_extra);
|
||||
atkbd_reset_state(atkbd);
|
||||
atkbd_activate(atkbd);
|
||||
|
||||
} else {
|
||||
atkbd->set = 2;
|
||||
|
@ -1165,6 +1185,8 @@ static int atkbd_connect(struct serio *serio, struct serio_driver *drv)
|
|||
goto fail3;
|
||||
|
||||
atkbd_enable(atkbd);
|
||||
if (serio->write)
|
||||
atkbd_activate(atkbd);
|
||||
|
||||
err = input_register_device(atkbd->dev);
|
||||
if (err)
|
||||
|
@ -1208,8 +1230,6 @@ static int atkbd_reconnect(struct serio *serio)
|
|||
if (atkbd->set != atkbd_select_set(atkbd, atkbd->set, atkbd->extra))
|
||||
goto out;
|
||||
|
||||
atkbd_activate(atkbd);
|
||||
|
||||
/*
|
||||
* Restore LED state and repeat rate. While input core
|
||||
* will do this for us at resume time reconnect may happen
|
||||
|
@ -1223,7 +1243,17 @@ static int atkbd_reconnect(struct serio *serio)
|
|||
|
||||
}
|
||||
|
||||
/*
|
||||
* Reset our state machine in case reconnect happened in the middle
|
||||
* of multi-byte scancode.
|
||||
*/
|
||||
atkbd->xl_bit = 0;
|
||||
atkbd->emul = 0;
|
||||
|
||||
atkbd_enable(atkbd);
|
||||
if (atkbd->write)
|
||||
atkbd_activate(atkbd);
|
||||
|
||||
retval = 0;
|
||||
|
||||
out:
|
||||
|
|
|
@ -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/kernel.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/of.h>
|
||||
#include <linux/platform_device.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/timer.h>
|
||||
|
@ -414,15 +415,23 @@ open_err:
|
|||
return -EIO;
|
||||
}
|
||||
|
||||
#ifdef CONFIG_OF
|
||||
static struct of_device_id imx_keypad_of_match[] = {
|
||||
{ .compatible = "fsl,imx21-kpp", },
|
||||
{ /* sentinel */ }
|
||||
};
|
||||
MODULE_DEVICE_TABLE(of, imx_keypad_of_match);
|
||||
#endif
|
||||
|
||||
static int imx_keypad_probe(struct platform_device *pdev)
|
||||
{
|
||||
const struct matrix_keymap_data *keymap_data = pdev->dev.platform_data;
|
||||
struct imx_keypad *keypad;
|
||||
struct input_dev *input_dev;
|
||||
struct resource *res;
|
||||
int irq, error, i;
|
||||
int irq, error, i, row, col;
|
||||
|
||||
if (keymap_data == NULL) {
|
||||
if (!keymap_data && !pdev->dev.of_node) {
|
||||
dev_err(&pdev->dev, "no keymap defined\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
@ -480,22 +489,6 @@ static int imx_keypad_probe(struct platform_device *pdev)
|
|||
goto failed_unmap;
|
||||
}
|
||||
|
||||
/* Search for rows and cols enabled */
|
||||
for (i = 0; i < keymap_data->keymap_size; i++) {
|
||||
keypad->rows_en_mask |= 1 << KEY_ROW(keymap_data->keymap[i]);
|
||||
keypad->cols_en_mask |= 1 << KEY_COL(keymap_data->keymap[i]);
|
||||
}
|
||||
|
||||
if (keypad->rows_en_mask > ((1 << MAX_MATRIX_KEY_ROWS) - 1) ||
|
||||
keypad->cols_en_mask > ((1 << MAX_MATRIX_KEY_COLS) - 1)) {
|
||||
dev_err(&pdev->dev,
|
||||
"invalid key data (too many rows or colums)\n");
|
||||
error = -EINVAL;
|
||||
goto failed_clock_put;
|
||||
}
|
||||
dev_dbg(&pdev->dev, "enabled rows mask: %x\n", keypad->rows_en_mask);
|
||||
dev_dbg(&pdev->dev, "enabled cols mask: %x\n", keypad->cols_en_mask);
|
||||
|
||||
/* Init the Input device */
|
||||
input_dev->name = pdev->name;
|
||||
input_dev->id.bustype = BUS_HOST;
|
||||
|
@ -512,6 +505,19 @@ static int imx_keypad_probe(struct platform_device *pdev)
|
|||
goto failed_clock_put;
|
||||
}
|
||||
|
||||
/* Search for rows and cols enabled */
|
||||
for (row = 0; row < MAX_MATRIX_KEY_ROWS; row++) {
|
||||
for (col = 0; col < MAX_MATRIX_KEY_COLS; col++) {
|
||||
i = MATRIX_SCAN_CODE(row, col, MATRIX_ROW_SHIFT);
|
||||
if (keypad->keycodes[i] != KEY_RESERVED) {
|
||||
keypad->rows_en_mask |= 1 << row;
|
||||
keypad->cols_en_mask |= 1 << col;
|
||||
}
|
||||
}
|
||||
}
|
||||
dev_dbg(&pdev->dev, "enabled rows mask: %x\n", keypad->rows_en_mask);
|
||||
dev_dbg(&pdev->dev, "enabled cols mask: %x\n", keypad->cols_en_mask);
|
||||
|
||||
__set_bit(EV_REP, input_dev->evbit);
|
||||
input_set_capability(input_dev, EV_MSC, MSC_SCAN);
|
||||
input_set_drvdata(input_dev, keypad);
|
||||
|
@ -631,6 +637,7 @@ static struct platform_driver imx_keypad_driver = {
|
|||
.name = "imx-keypad",
|
||||
.owner = THIS_MODULE,
|
||||
.pm = &imx_kbd_pm_ops,
|
||||
.of_match_table = of_match_ptr(imx_keypad_of_match),
|
||||
},
|
||||
.probe = imx_keypad_probe,
|
||||
.remove = imx_keypad_remove,
|
||||
|
|
|
@ -20,6 +20,7 @@
|
|||
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/init.h>
|
||||
#include <linux/leds.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/jiffies.h>
|
||||
|
@ -39,6 +40,11 @@
|
|||
#define QT2160_CMD_GPIOS 6
|
||||
#define QT2160_CMD_SUBVER 7
|
||||
#define QT2160_CMD_CALIBRATE 10
|
||||
#define QT2160_CMD_DRIVE_X 70
|
||||
#define QT2160_CMD_PWMEN_X 74
|
||||
#define QT2160_CMD_PWM_DUTY 76
|
||||
|
||||
#define QT2160_NUM_LEDS_X 8
|
||||
|
||||
#define QT2160_CYCLE_INTERVAL (2*HZ)
|
||||
|
||||
|
@ -49,6 +55,17 @@ static unsigned char qt2160_key2code[] = {
|
|||
KEY_C, KEY_D, KEY_E, KEY_F,
|
||||
};
|
||||
|
||||
#ifdef CONFIG_LEDS_CLASS
|
||||
struct qt2160_led {
|
||||
struct qt2160_data *qt2160;
|
||||
struct led_classdev cdev;
|
||||
struct work_struct work;
|
||||
char name[32];
|
||||
int id;
|
||||
enum led_brightness new_brightness;
|
||||
};
|
||||
#endif
|
||||
|
||||
struct qt2160_data {
|
||||
struct i2c_client *client;
|
||||
struct input_dev *input;
|
||||
|
@ -56,8 +73,61 @@ struct qt2160_data {
|
|||
spinlock_t lock; /* Protects canceling/rescheduling of dwork */
|
||||
unsigned short keycodes[ARRAY_SIZE(qt2160_key2code)];
|
||||
u16 key_matrix;
|
||||
#ifdef CONFIG_LEDS_CLASS
|
||||
struct qt2160_led leds[QT2160_NUM_LEDS_X];
|
||||
struct mutex led_lock;
|
||||
#endif
|
||||
};
|
||||
|
||||
static int qt2160_read(struct i2c_client *client, u8 reg);
|
||||
static int qt2160_write(struct i2c_client *client, u8 reg, u8 data);
|
||||
|
||||
#ifdef CONFIG_LEDS_CLASS
|
||||
|
||||
static void qt2160_led_work(struct work_struct *work)
|
||||
{
|
||||
struct qt2160_led *led = container_of(work, struct qt2160_led, work);
|
||||
struct qt2160_data *qt2160 = led->qt2160;
|
||||
struct i2c_client *client = qt2160->client;
|
||||
int value = led->new_brightness;
|
||||
u32 drive, pwmen;
|
||||
|
||||
mutex_lock(&qt2160->led_lock);
|
||||
|
||||
drive = qt2160_read(client, QT2160_CMD_DRIVE_X);
|
||||
pwmen = qt2160_read(client, QT2160_CMD_PWMEN_X);
|
||||
if (value != LED_OFF) {
|
||||
drive |= (1 << led->id);
|
||||
pwmen |= (1 << led->id);
|
||||
|
||||
} else {
|
||||
drive &= ~(1 << led->id);
|
||||
pwmen &= ~(1 << led->id);
|
||||
}
|
||||
qt2160_write(client, QT2160_CMD_DRIVE_X, drive);
|
||||
qt2160_write(client, QT2160_CMD_PWMEN_X, pwmen);
|
||||
|
||||
/*
|
||||
* Changing this register will change the brightness
|
||||
* of every LED in the qt2160. It's a HW limitation.
|
||||
*/
|
||||
if (value != LED_OFF)
|
||||
qt2160_write(client, QT2160_CMD_PWM_DUTY, value);
|
||||
|
||||
mutex_unlock(&qt2160->led_lock);
|
||||
}
|
||||
|
||||
static void qt2160_led_set(struct led_classdev *cdev,
|
||||
enum led_brightness value)
|
||||
{
|
||||
struct qt2160_led *led = container_of(cdev, struct qt2160_led, cdev);
|
||||
|
||||
led->new_brightness = value;
|
||||
schedule_work(&led->work);
|
||||
}
|
||||
|
||||
#endif /* CONFIG_LEDS_CLASS */
|
||||
|
||||
static int qt2160_read_block(struct i2c_client *client,
|
||||
u8 inireg, u8 *buffer, unsigned int count)
|
||||
{
|
||||
|
@ -216,6 +286,63 @@ static int qt2160_write(struct i2c_client *client, u8 reg, u8 data)
|
|||
return ret;
|
||||
}
|
||||
|
||||
#ifdef CONFIG_LEDS_CLASS
|
||||
|
||||
static int qt2160_register_leds(struct qt2160_data *qt2160)
|
||||
{
|
||||
struct i2c_client *client = qt2160->client;
|
||||
int ret;
|
||||
int i;
|
||||
|
||||
mutex_init(&qt2160->led_lock);
|
||||
|
||||
for (i = 0; i < QT2160_NUM_LEDS_X; i++) {
|
||||
struct qt2160_led *led = &qt2160->leds[i];
|
||||
|
||||
snprintf(led->name, sizeof(led->name), "qt2160:x%d", i);
|
||||
led->cdev.name = led->name;
|
||||
led->cdev.brightness_set = qt2160_led_set;
|
||||
led->cdev.brightness = LED_OFF;
|
||||
led->id = i;
|
||||
led->qt2160 = qt2160;
|
||||
|
||||
INIT_WORK(&led->work, qt2160_led_work);
|
||||
|
||||
ret = led_classdev_register(&client->dev, &led->cdev);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
}
|
||||
|
||||
/* Tur off LEDs */
|
||||
qt2160_write(client, QT2160_CMD_DRIVE_X, 0);
|
||||
qt2160_write(client, QT2160_CMD_PWMEN_X, 0);
|
||||
qt2160_write(client, QT2160_CMD_PWM_DUTY, 0);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void qt2160_unregister_leds(struct qt2160_data *qt2160)
|
||||
{
|
||||
int i;
|
||||
|
||||
for (i = 0; i < QT2160_NUM_LEDS_X; i++) {
|
||||
led_classdev_unregister(&qt2160->leds[i].cdev);
|
||||
cancel_work_sync(&qt2160->leds[i].work);
|
||||
}
|
||||
}
|
||||
|
||||
#else
|
||||
|
||||
static inline int qt2160_register_leds(struct qt2160_data *qt2160)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
static inline void qt2160_unregister_leds(struct qt2160_data *qt2160)
|
||||
{
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
static bool qt2160_identify(struct i2c_client *client)
|
||||
{
|
||||
|
@ -249,7 +376,7 @@ static bool qt2160_identify(struct i2c_client *client)
|
|||
}
|
||||
|
||||
static int qt2160_probe(struct i2c_client *client,
|
||||
const struct i2c_device_id *id)
|
||||
const struct i2c_device_id *id)
|
||||
{
|
||||
struct qt2160_data *qt2160;
|
||||
struct input_dev *input;
|
||||
|
@ -314,11 +441,17 @@ static int qt2160_probe(struct i2c_client *client,
|
|||
}
|
||||
}
|
||||
|
||||
error = qt2160_register_leds(qt2160);
|
||||
if (error) {
|
||||
dev_err(&client->dev, "Failed to register leds\n");
|
||||
goto err_free_irq;
|
||||
}
|
||||
|
||||
error = input_register_device(qt2160->input);
|
||||
if (error) {
|
||||
dev_err(&client->dev,
|
||||
"Failed to register input device\n");
|
||||
goto err_free_irq;
|
||||
goto err_unregister_leds;
|
||||
}
|
||||
|
||||
i2c_set_clientdata(client, qt2160);
|
||||
|
@ -326,6 +459,8 @@ static int qt2160_probe(struct i2c_client *client,
|
|||
|
||||
return 0;
|
||||
|
||||
err_unregister_leds:
|
||||
qt2160_unregister_leds(qt2160);
|
||||
err_free_irq:
|
||||
if (client->irq)
|
||||
free_irq(client->irq, qt2160);
|
||||
|
@ -339,6 +474,8 @@ static int qt2160_remove(struct i2c_client *client)
|
|||
{
|
||||
struct qt2160_data *qt2160 = i2c_get_clientdata(client);
|
||||
|
||||
qt2160_unregister_leds(qt2160);
|
||||
|
||||
/* Release IRQ so no queue will be scheduled */
|
||||
if (client->irq)
|
||||
free_irq(client->irq, qt2160);
|
||||
|
|
|
@ -29,9 +29,16 @@
|
|||
#include <linux/of.h>
|
||||
#include <linux/clk.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/input/tegra_kbc.h>
|
||||
#include <linux/input/matrix_keypad.h>
|
||||
#include <mach/clk.h>
|
||||
|
||||
#define KBC_MAX_GPIO 24
|
||||
#define KBC_MAX_KPENT 8
|
||||
|
||||
#define KBC_MAX_ROW 16
|
||||
#define KBC_MAX_COL 8
|
||||
#define KBC_MAX_KEY (KBC_MAX_ROW * KBC_MAX_COL)
|
||||
|
||||
#define KBC_MAX_DEBOUNCE_CNT 0x3ffu
|
||||
|
||||
/* KBC row scan time and delay for beginning the row scan. */
|
||||
|
@ -67,10 +74,27 @@
|
|||
|
||||
#define KBC_ROW_SHIFT 3
|
||||
|
||||
enum tegra_pin_type {
|
||||
PIN_CFG_IGNORE,
|
||||
PIN_CFG_COL,
|
||||
PIN_CFG_ROW,
|
||||
};
|
||||
|
||||
struct tegra_kbc_pin_cfg {
|
||||
enum tegra_pin_type type;
|
||||
unsigned char num;
|
||||
};
|
||||
|
||||
struct tegra_kbc {
|
||||
struct device *dev;
|
||||
unsigned int debounce_cnt;
|
||||
unsigned int repeat_cnt;
|
||||
struct tegra_kbc_pin_cfg pin_cfg[KBC_MAX_GPIO];
|
||||
const struct matrix_keymap_data *keymap_data;
|
||||
bool wakeup;
|
||||
void __iomem *mmio;
|
||||
struct input_dev *idev;
|
||||
unsigned int irq;
|
||||
int irq;
|
||||
spinlock_t lock;
|
||||
unsigned int repoll_dly;
|
||||
unsigned long cp_dly_jiffies;
|
||||
|
@ -78,7 +102,6 @@ struct tegra_kbc {
|
|||
bool use_fn_map;
|
||||
bool use_ghost_filter;
|
||||
bool keypress_caused_wake;
|
||||
const struct tegra_kbc_platform_data *pdata;
|
||||
unsigned short keycode[KBC_MAX_KEY * 2];
|
||||
unsigned short current_keys[KBC_MAX_KPENT];
|
||||
unsigned int num_pressed_keys;
|
||||
|
@ -87,147 +110,6 @@ struct tegra_kbc {
|
|||
struct clk *clk;
|
||||
};
|
||||
|
||||
static const u32 tegra_kbc_default_keymap[] = {
|
||||
KEY(0, 2, KEY_W),
|
||||
KEY(0, 3, KEY_S),
|
||||
KEY(0, 4, KEY_A),
|
||||
KEY(0, 5, KEY_Z),
|
||||
KEY(0, 7, KEY_FN),
|
||||
|
||||
KEY(1, 7, KEY_LEFTMETA),
|
||||
|
||||
KEY(2, 6, KEY_RIGHTALT),
|
||||
KEY(2, 7, KEY_LEFTALT),
|
||||
|
||||
KEY(3, 0, KEY_5),
|
||||
KEY(3, 1, KEY_4),
|
||||
KEY(3, 2, KEY_R),
|
||||
KEY(3, 3, KEY_E),
|
||||
KEY(3, 4, KEY_F),
|
||||
KEY(3, 5, KEY_D),
|
||||
KEY(3, 6, KEY_X),
|
||||
|
||||
KEY(4, 0, KEY_7),
|
||||
KEY(4, 1, KEY_6),
|
||||
KEY(4, 2, KEY_T),
|
||||
KEY(4, 3, KEY_H),
|
||||
KEY(4, 4, KEY_G),
|
||||
KEY(4, 5, KEY_V),
|
||||
KEY(4, 6, KEY_C),
|
||||
KEY(4, 7, KEY_SPACE),
|
||||
|
||||
KEY(5, 0, KEY_9),
|
||||
KEY(5, 1, KEY_8),
|
||||
KEY(5, 2, KEY_U),
|
||||
KEY(5, 3, KEY_Y),
|
||||
KEY(5, 4, KEY_J),
|
||||
KEY(5, 5, KEY_N),
|
||||
KEY(5, 6, KEY_B),
|
||||
KEY(5, 7, KEY_BACKSLASH),
|
||||
|
||||
KEY(6, 0, KEY_MINUS),
|
||||
KEY(6, 1, KEY_0),
|
||||
KEY(6, 2, KEY_O),
|
||||
KEY(6, 3, KEY_I),
|
||||
KEY(6, 4, KEY_L),
|
||||
KEY(6, 5, KEY_K),
|
||||
KEY(6, 6, KEY_COMMA),
|
||||
KEY(6, 7, KEY_M),
|
||||
|
||||
KEY(7, 1, KEY_EQUAL),
|
||||
KEY(7, 2, KEY_RIGHTBRACE),
|
||||
KEY(7, 3, KEY_ENTER),
|
||||
KEY(7, 7, KEY_MENU),
|
||||
|
||||
KEY(8, 4, KEY_RIGHTSHIFT),
|
||||
KEY(8, 5, KEY_LEFTSHIFT),
|
||||
|
||||
KEY(9, 5, KEY_RIGHTCTRL),
|
||||
KEY(9, 7, KEY_LEFTCTRL),
|
||||
|
||||
KEY(11, 0, KEY_LEFTBRACE),
|
||||
KEY(11, 1, KEY_P),
|
||||
KEY(11, 2, KEY_APOSTROPHE),
|
||||
KEY(11, 3, KEY_SEMICOLON),
|
||||
KEY(11, 4, KEY_SLASH),
|
||||
KEY(11, 5, KEY_DOT),
|
||||
|
||||
KEY(12, 0, KEY_F10),
|
||||
KEY(12, 1, KEY_F9),
|
||||
KEY(12, 2, KEY_BACKSPACE),
|
||||
KEY(12, 3, KEY_3),
|
||||
KEY(12, 4, KEY_2),
|
||||
KEY(12, 5, KEY_UP),
|
||||
KEY(12, 6, KEY_PRINT),
|
||||
KEY(12, 7, KEY_PAUSE),
|
||||
|
||||
KEY(13, 0, KEY_INSERT),
|
||||
KEY(13, 1, KEY_DELETE),
|
||||
KEY(13, 3, KEY_PAGEUP),
|
||||
KEY(13, 4, KEY_PAGEDOWN),
|
||||
KEY(13, 5, KEY_RIGHT),
|
||||
KEY(13, 6, KEY_DOWN),
|
||||
KEY(13, 7, KEY_LEFT),
|
||||
|
||||
KEY(14, 0, KEY_F11),
|
||||
KEY(14, 1, KEY_F12),
|
||||
KEY(14, 2, KEY_F8),
|
||||
KEY(14, 3, KEY_Q),
|
||||
KEY(14, 4, KEY_F4),
|
||||
KEY(14, 5, KEY_F3),
|
||||
KEY(14, 6, KEY_1),
|
||||
KEY(14, 7, KEY_F7),
|
||||
|
||||
KEY(15, 0, KEY_ESC),
|
||||
KEY(15, 1, KEY_GRAVE),
|
||||
KEY(15, 2, KEY_F5),
|
||||
KEY(15, 3, KEY_TAB),
|
||||
KEY(15, 4, KEY_F1),
|
||||
KEY(15, 5, KEY_F2),
|
||||
KEY(15, 6, KEY_CAPSLOCK),
|
||||
KEY(15, 7, KEY_F6),
|
||||
|
||||
/* Software Handled Function Keys */
|
||||
KEY(20, 0, KEY_KP7),
|
||||
|
||||
KEY(21, 0, KEY_KP9),
|
||||
KEY(21, 1, KEY_KP8),
|
||||
KEY(21, 2, KEY_KP4),
|
||||
KEY(21, 4, KEY_KP1),
|
||||
|
||||
KEY(22, 1, KEY_KPSLASH),
|
||||
KEY(22, 2, KEY_KP6),
|
||||
KEY(22, 3, KEY_KP5),
|
||||
KEY(22, 4, KEY_KP3),
|
||||
KEY(22, 5, KEY_KP2),
|
||||
KEY(22, 7, KEY_KP0),
|
||||
|
||||
KEY(27, 1, KEY_KPASTERISK),
|
||||
KEY(27, 3, KEY_KPMINUS),
|
||||
KEY(27, 4, KEY_KPPLUS),
|
||||
KEY(27, 5, KEY_KPDOT),
|
||||
|
||||
KEY(28, 5, KEY_VOLUMEUP),
|
||||
|
||||
KEY(29, 3, KEY_HOME),
|
||||
KEY(29, 4, KEY_END),
|
||||
KEY(29, 5, KEY_BRIGHTNESSDOWN),
|
||||
KEY(29, 6, KEY_VOLUMEDOWN),
|
||||
KEY(29, 7, KEY_BRIGHTNESSUP),
|
||||
|
||||
KEY(30, 0, KEY_NUMLOCK),
|
||||
KEY(30, 1, KEY_SCROLLLOCK),
|
||||
KEY(30, 2, KEY_MUTE),
|
||||
|
||||
KEY(31, 4, KEY_HELP),
|
||||
};
|
||||
|
||||
static const
|
||||
struct matrix_keymap_data tegra_kbc_default_keymap_data = {
|
||||
.keymap = tegra_kbc_default_keymap,
|
||||
.keymap_size = ARRAY_SIZE(tegra_kbc_default_keymap),
|
||||
};
|
||||
|
||||
static void tegra_kbc_report_released_keys(struct input_dev *input,
|
||||
unsigned short old_keycodes[],
|
||||
unsigned int old_num_keys,
|
||||
|
@ -357,18 +239,6 @@ static void tegra_kbc_set_fifo_interrupt(struct tegra_kbc *kbc, bool enable)
|
|||
writel(val, kbc->mmio + KBC_CONTROL_0);
|
||||
}
|
||||
|
||||
static void tegra_kbc_set_keypress_interrupt(struct tegra_kbc *kbc, bool enable)
|
||||
{
|
||||
u32 val;
|
||||
|
||||
val = readl(kbc->mmio + KBC_CONTROL_0);
|
||||
if (enable)
|
||||
val |= KBC_CONTROL_KEYPRESS_INT_EN;
|
||||
else
|
||||
val &= ~KBC_CONTROL_KEYPRESS_INT_EN;
|
||||
writel(val, kbc->mmio + KBC_CONTROL_0);
|
||||
}
|
||||
|
||||
static void tegra_kbc_keypress_timer(unsigned long data)
|
||||
{
|
||||
struct tegra_kbc *kbc = (struct tegra_kbc *)data;
|
||||
|
@ -439,12 +309,11 @@ static irqreturn_t tegra_kbc_isr(int irq, void *args)
|
|||
|
||||
static void tegra_kbc_setup_wakekeys(struct tegra_kbc *kbc, bool filter)
|
||||
{
|
||||
const struct tegra_kbc_platform_data *pdata = kbc->pdata;
|
||||
int i;
|
||||
unsigned int rst_val;
|
||||
|
||||
/* Either mask all keys or none. */
|
||||
rst_val = (filter && !pdata->wakeup) ? ~0 : 0;
|
||||
rst_val = (filter && !kbc->wakeup) ? ~0 : 0;
|
||||
|
||||
for (i = 0; i < KBC_MAX_ROW; i++)
|
||||
writel(rst_val, kbc->mmio + KBC_ROW0_MASK_0 + i * 4);
|
||||
|
@ -452,7 +321,6 @@ static void tegra_kbc_setup_wakekeys(struct tegra_kbc *kbc, bool filter)
|
|||
|
||||
static void tegra_kbc_config_pins(struct tegra_kbc *kbc)
|
||||
{
|
||||
const struct tegra_kbc_platform_data *pdata = kbc->pdata;
|
||||
int i;
|
||||
|
||||
for (i = 0; i < KBC_MAX_GPIO; i++) {
|
||||
|
@ -468,13 +336,13 @@ static void tegra_kbc_config_pins(struct tegra_kbc *kbc)
|
|||
row_cfg &= ~r_mask;
|
||||
col_cfg &= ~c_mask;
|
||||
|
||||
switch (pdata->pin_cfg[i].type) {
|
||||
switch (kbc->pin_cfg[i].type) {
|
||||
case PIN_CFG_ROW:
|
||||
row_cfg |= ((pdata->pin_cfg[i].num << 1) | 1) << r_shft;
|
||||
row_cfg |= ((kbc->pin_cfg[i].num << 1) | 1) << r_shft;
|
||||
break;
|
||||
|
||||
case PIN_CFG_COL:
|
||||
col_cfg |= ((pdata->pin_cfg[i].num << 1) | 1) << c_shft;
|
||||
col_cfg |= ((kbc->pin_cfg[i].num << 1) | 1) << c_shft;
|
||||
break;
|
||||
|
||||
case PIN_CFG_IGNORE:
|
||||
|
@ -488,7 +356,6 @@ static void tegra_kbc_config_pins(struct tegra_kbc *kbc)
|
|||
|
||||
static int tegra_kbc_start(struct tegra_kbc *kbc)
|
||||
{
|
||||
const struct tegra_kbc_platform_data *pdata = kbc->pdata;
|
||||
unsigned int debounce_cnt;
|
||||
u32 val = 0;
|
||||
|
||||
|
@ -503,10 +370,10 @@ static int tegra_kbc_start(struct tegra_kbc *kbc)
|
|||
tegra_kbc_config_pins(kbc);
|
||||
tegra_kbc_setup_wakekeys(kbc, false);
|
||||
|
||||
writel(pdata->repeat_cnt, kbc->mmio + KBC_RPT_DLY_0);
|
||||
writel(kbc->repeat_cnt, kbc->mmio + KBC_RPT_DLY_0);
|
||||
|
||||
/* Keyboard debounce count is maximum of 12 bits. */
|
||||
debounce_cnt = min(pdata->debounce_cnt, KBC_MAX_DEBOUNCE_CNT);
|
||||
debounce_cnt = min(kbc->debounce_cnt, KBC_MAX_DEBOUNCE_CNT);
|
||||
val = KBC_DEBOUNCE_CNT_SHIFT(debounce_cnt);
|
||||
val |= KBC_FIFO_TH_CNT_SHIFT(1); /* set fifo interrupt threshold to 1 */
|
||||
val |= KBC_CONTROL_FIFO_CNT_INT_EN; /* interrupt on FIFO threshold */
|
||||
|
@ -573,21 +440,20 @@ static void tegra_kbc_close(struct input_dev *dev)
|
|||
return tegra_kbc_stop(kbc);
|
||||
}
|
||||
|
||||
static bool
|
||||
tegra_kbc_check_pin_cfg(const struct tegra_kbc_platform_data *pdata,
|
||||
struct device *dev, unsigned int *num_rows)
|
||||
static bool tegra_kbc_check_pin_cfg(const struct tegra_kbc *kbc,
|
||||
unsigned int *num_rows)
|
||||
{
|
||||
int i;
|
||||
|
||||
*num_rows = 0;
|
||||
|
||||
for (i = 0; i < KBC_MAX_GPIO; i++) {
|
||||
const struct tegra_kbc_pin_cfg *pin_cfg = &pdata->pin_cfg[i];
|
||||
const struct tegra_kbc_pin_cfg *pin_cfg = &kbc->pin_cfg[i];
|
||||
|
||||
switch (pin_cfg->type) {
|
||||
case PIN_CFG_ROW:
|
||||
if (pin_cfg->num >= KBC_MAX_ROW) {
|
||||
dev_err(dev,
|
||||
dev_err(kbc->dev,
|
||||
"pin_cfg[%d]: invalid row number %d\n",
|
||||
i, pin_cfg->num);
|
||||
return false;
|
||||
|
@ -597,7 +463,7 @@ tegra_kbc_check_pin_cfg(const struct tegra_kbc_platform_data *pdata,
|
|||
|
||||
case PIN_CFG_COL:
|
||||
if (pin_cfg->num >= KBC_MAX_COL) {
|
||||
dev_err(dev,
|
||||
dev_err(kbc->dev,
|
||||
"pin_cfg[%d]: invalid column number %d\n",
|
||||
i, pin_cfg->num);
|
||||
return false;
|
||||
|
@ -608,7 +474,7 @@ tegra_kbc_check_pin_cfg(const struct tegra_kbc_platform_data *pdata,
|
|||
break;
|
||||
|
||||
default:
|
||||
dev_err(dev,
|
||||
dev_err(kbc->dev,
|
||||
"pin_cfg[%d]: invalid entry type %d\n",
|
||||
pin_cfg->type, pin_cfg->num);
|
||||
return false;
|
||||
|
@ -618,154 +484,140 @@ tegra_kbc_check_pin_cfg(const struct tegra_kbc_platform_data *pdata,
|
|||
return true;
|
||||
}
|
||||
|
||||
#ifdef CONFIG_OF
|
||||
static struct tegra_kbc_platform_data *tegra_kbc_dt_parse_pdata(
|
||||
struct platform_device *pdev)
|
||||
static int tegra_kbc_parse_dt(struct tegra_kbc *kbc)
|
||||
{
|
||||
struct tegra_kbc_platform_data *pdata;
|
||||
struct device_node *np = pdev->dev.of_node;
|
||||
struct device_node *np = kbc->dev->of_node;
|
||||
u32 prop;
|
||||
int i;
|
||||
|
||||
if (!np)
|
||||
return NULL;
|
||||
|
||||
pdata = kzalloc(sizeof(*pdata), GFP_KERNEL);
|
||||
if (!pdata)
|
||||
return NULL;
|
||||
u32 num_rows = 0;
|
||||
u32 num_cols = 0;
|
||||
u32 cols_cfg[KBC_MAX_GPIO];
|
||||
u32 rows_cfg[KBC_MAX_GPIO];
|
||||
int proplen;
|
||||
int ret;
|
||||
|
||||
if (!of_property_read_u32(np, "nvidia,debounce-delay-ms", &prop))
|
||||
pdata->debounce_cnt = prop;
|
||||
kbc->debounce_cnt = prop;
|
||||
|
||||
if (!of_property_read_u32(np, "nvidia,repeat-delay-ms", &prop))
|
||||
pdata->repeat_cnt = prop;
|
||||
kbc->repeat_cnt = prop;
|
||||
|
||||
if (of_find_property(np, "nvidia,needs-ghost-filter", NULL))
|
||||
pdata->use_ghost_filter = true;
|
||||
kbc->use_ghost_filter = true;
|
||||
|
||||
if (of_find_property(np, "nvidia,wakeup-source", NULL))
|
||||
pdata->wakeup = true;
|
||||
kbc->wakeup = true;
|
||||
|
||||
/*
|
||||
* All currently known keymaps with device tree support use the same
|
||||
* pin_cfg, so set it up here.
|
||||
*/
|
||||
for (i = 0; i < KBC_MAX_ROW; i++) {
|
||||
pdata->pin_cfg[i].num = i;
|
||||
pdata->pin_cfg[i].type = PIN_CFG_ROW;
|
||||
if (!of_get_property(np, "nvidia,kbc-row-pins", &proplen)) {
|
||||
dev_err(kbc->dev, "property nvidia,kbc-row-pins not found\n");
|
||||
return -ENOENT;
|
||||
}
|
||||
num_rows = proplen / sizeof(u32);
|
||||
|
||||
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++) {
|
||||
pdata->pin_cfg[KBC_MAX_ROW + i].num = i;
|
||||
pdata->pin_cfg[KBC_MAX_ROW + i].type = PIN_CFG_COL;
|
||||
if (!num_rows || !num_cols || ((num_rows + num_cols) > KBC_MAX_GPIO)) {
|
||||
dev_err(kbc->dev,
|
||||
"keypad rows/columns not porperly specified\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
return pdata;
|
||||
}
|
||||
#else
|
||||
static inline struct tegra_kbc_platform_data *tegra_kbc_dt_parse_pdata(
|
||||
struct platform_device *pdev)
|
||||
{
|
||||
return NULL;
|
||||
}
|
||||
#endif
|
||||
/* Set all pins as non-configured */
|
||||
for (i = 0; i < KBC_MAX_GPIO; i++)
|
||||
kbc->pin_cfg[i].type = PIN_CFG_IGNORE;
|
||||
|
||||
static int tegra_kbd_setup_keymap(struct tegra_kbc *kbc)
|
||||
{
|
||||
const struct tegra_kbc_platform_data *pdata = kbc->pdata;
|
||||
const struct matrix_keymap_data *keymap_data = pdata->keymap_data;
|
||||
unsigned int keymap_rows = KBC_MAX_KEY;
|
||||
int retval;
|
||||
|
||||
if (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);
|
||||
ret = of_property_read_u32_array(np, "nvidia,kbc-row-pins",
|
||||
rows_cfg, num_rows);
|
||||
if (ret < 0) {
|
||||
dev_err(kbc->dev, "Rows configurations are not proper\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
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)
|
||||
{
|
||||
const struct tegra_kbc_platform_data *pdata = pdev->dev.platform_data;
|
||||
struct tegra_kbc *kbc;
|
||||
struct input_dev *input_dev;
|
||||
struct resource *res;
|
||||
int irq;
|
||||
int err;
|
||||
int num_rows = 0;
|
||||
unsigned int debounce_cnt;
|
||||
unsigned int scan_time_rows;
|
||||
unsigned int keymap_rows = KBC_MAX_KEY;
|
||||
|
||||
if (!pdata)
|
||||
pdata = tegra_kbc_dt_parse_pdata(pdev);
|
||||
|
||||
if (!pdata)
|
||||
return -EINVAL;
|
||||
|
||||
if (!tegra_kbc_check_pin_cfg(pdata, &pdev->dev, &num_rows)) {
|
||||
err = -EINVAL;
|
||||
goto err_free_pdata;
|
||||
kbc = devm_kzalloc(&pdev->dev, sizeof(*kbc), GFP_KERNEL);
|
||||
if (!kbc) {
|
||||
dev_err(&pdev->dev, "failed to alloc memory for kbc\n");
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
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);
|
||||
if (!res) {
|
||||
dev_err(&pdev->dev, "failed to get I/O memory\n");
|
||||
err = -ENXIO;
|
||||
goto err_free_pdata;
|
||||
return -ENXIO;
|
||||
}
|
||||
|
||||
irq = platform_get_irq(pdev, 0);
|
||||
if (irq < 0) {
|
||||
kbc->irq = platform_get_irq(pdev, 0);
|
||||
if (kbc->irq < 0) {
|
||||
dev_err(&pdev->dev, "failed to get keyboard IRQ\n");
|
||||
err = -ENXIO;
|
||||
goto err_free_pdata;
|
||||
return -ENXIO;
|
||||
}
|
||||
|
||||
kbc = kzalloc(sizeof(*kbc), GFP_KERNEL);
|
||||
input_dev = input_allocate_device();
|
||||
if (!kbc || !input_dev) {
|
||||
err = -ENOMEM;
|
||||
goto err_free_mem;
|
||||
kbc->idev = devm_input_allocate_device(&pdev->dev);
|
||||
if (!kbc->idev) {
|
||||
dev_err(&pdev->dev, "failed to allocate input device\n");
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
kbc->pdata = pdata;
|
||||
kbc->idev = input_dev;
|
||||
kbc->irq = irq;
|
||||
spin_lock_init(&kbc->lock);
|
||||
setup_timer(&kbc->timer, tegra_kbc_keypress_timer, (unsigned long)kbc);
|
||||
|
||||
res = request_mem_region(res->start, resource_size(res), pdev->name);
|
||||
if (!res) {
|
||||
dev_err(&pdev->dev, "failed to request I/O memory\n");
|
||||
err = -EBUSY;
|
||||
goto err_free_mem;
|
||||
}
|
||||
|
||||
kbc->mmio = ioremap(res->start, resource_size(res));
|
||||
kbc->mmio = devm_request_and_ioremap(&pdev->dev, res);
|
||||
if (!kbc->mmio) {
|
||||
dev_err(&pdev->dev, "failed to remap I/O memory\n");
|
||||
err = -ENXIO;
|
||||
goto err_free_mem_region;
|
||||
dev_err(&pdev->dev, "Cannot request memregion/iomap address\n");
|
||||
return -EBUSY;
|
||||
}
|
||||
|
||||
kbc->clk = clk_get(&pdev->dev, NULL);
|
||||
kbc->clk = devm_clk_get(&pdev->dev, NULL);
|
||||
if (IS_ERR(kbc->clk)) {
|
||||
dev_err(&pdev->dev, "failed to get keyboard clock\n");
|
||||
err = PTR_ERR(kbc->clk);
|
||||
goto err_iounmap;
|
||||
return PTR_ERR(kbc->clk);
|
||||
}
|
||||
|
||||
/*
|
||||
|
@ -774,37 +626,38 @@ static int tegra_kbc_probe(struct platform_device *pdev)
|
|||
* the rows. There is an additional delay before the row scanning
|
||||
* starts. The repoll delay is computed in milliseconds.
|
||||
*/
|
||||
debounce_cnt = min(pdata->debounce_cnt, KBC_MAX_DEBOUNCE_CNT);
|
||||
debounce_cnt = min(kbc->debounce_cnt, KBC_MAX_DEBOUNCE_CNT);
|
||||
scan_time_rows = (KBC_ROW_SCAN_TIME + debounce_cnt) * num_rows;
|
||||
kbc->repoll_dly = KBC_ROW_SCAN_DLY + scan_time_rows + pdata->repeat_cnt;
|
||||
kbc->repoll_dly = KBC_ROW_SCAN_DLY + scan_time_rows + kbc->repeat_cnt;
|
||||
kbc->repoll_dly = DIV_ROUND_UP(kbc->repoll_dly, KBC_CYCLE_MS);
|
||||
|
||||
kbc->wakeup_key = pdata->wakeup_key;
|
||||
kbc->use_fn_map = pdata->use_fn_map;
|
||||
kbc->use_ghost_filter = pdata->use_ghost_filter;
|
||||
kbc->idev->name = pdev->name;
|
||||
kbc->idev->id.bustype = BUS_HOST;
|
||||
kbc->idev->dev.parent = &pdev->dev;
|
||||
kbc->idev->open = tegra_kbc_open;
|
||||
kbc->idev->close = tegra_kbc_close;
|
||||
|
||||
input_dev->name = pdev->name;
|
||||
input_dev->id.bustype = BUS_HOST;
|
||||
input_dev->dev.parent = &pdev->dev;
|
||||
input_dev->open = tegra_kbc_open;
|
||||
input_dev->close = tegra_kbc_close;
|
||||
if (kbc->keymap_data && kbc->use_fn_map)
|
||||
keymap_rows *= 2;
|
||||
|
||||
err = tegra_kbd_setup_keymap(kbc);
|
||||
err = matrix_keypad_build_keymap(kbc->keymap_data, NULL,
|
||||
keymap_rows, KBC_MAX_COL,
|
||||
kbc->keycode, kbc->idev);
|
||||
if (err) {
|
||||
dev_err(&pdev->dev, "failed to setup keymap\n");
|
||||
goto err_put_clk;
|
||||
return err;
|
||||
}
|
||||
|
||||
__set_bit(EV_REP, input_dev->evbit);
|
||||
input_set_capability(input_dev, EV_MSC, MSC_SCAN);
|
||||
__set_bit(EV_REP, kbc->idev->evbit);
|
||||
input_set_capability(kbc->idev, EV_MSC, MSC_SCAN);
|
||||
|
||||
input_set_drvdata(input_dev, kbc);
|
||||
input_set_drvdata(kbc->idev, kbc);
|
||||
|
||||
err = request_irq(kbc->irq, tegra_kbc_isr,
|
||||
err = devm_request_irq(&pdev->dev, kbc->irq, tegra_kbc_isr,
|
||||
IRQF_NO_SUSPEND | IRQF_TRIGGER_HIGH, pdev->name, kbc);
|
||||
if (err) {
|
||||
dev_err(&pdev->dev, "failed to request keyboard IRQ\n");
|
||||
goto err_put_clk;
|
||||
return err;
|
||||
}
|
||||
|
||||
disable_irq(kbc->irq);
|
||||
|
@ -812,60 +665,28 @@ static int tegra_kbc_probe(struct platform_device *pdev)
|
|||
err = input_register_device(kbc->idev);
|
||||
if (err) {
|
||||
dev_err(&pdev->dev, "failed to register input device\n");
|
||||
goto err_free_irq;
|
||||
return err;
|
||||
}
|
||||
|
||||
platform_set_drvdata(pdev, kbc);
|
||||
device_init_wakeup(&pdev->dev, pdata->wakeup);
|
||||
|
||||
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);
|
||||
device_init_wakeup(&pdev->dev, kbc->wakeup);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
#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)
|
||||
{
|
||||
struct platform_device *pdev = to_platform_device(dev);
|
||||
|
@ -954,7 +775,6 @@ MODULE_DEVICE_TABLE(of, tegra_kbc_of_match);
|
|||
|
||||
static struct platform_driver tegra_kbc_driver = {
|
||||
.probe = tegra_kbc_probe,
|
||||
.remove = tegra_kbc_remove,
|
||||
.driver = {
|
||||
.name = "tegra-kbc",
|
||||
.owner = THIS_MODULE,
|
||||
|
|
|
@ -232,7 +232,7 @@ static const struct adxl34x_platform_data adxl34x_default_init = {
|
|||
|
||||
.ev_code_tap = {BTN_TOUCH, BTN_TOUCH, BTN_TOUCH}, /* EV_KEY {x,y,z} */
|
||||
.power_mode = ADXL_AUTO_SLEEP | ADXL_LINK,
|
||||
.fifo_mode = FIFO_STREAM,
|
||||
.fifo_mode = ADXL_FIFO_STREAM,
|
||||
.watermark = 0,
|
||||
};
|
||||
|
||||
|
@ -732,7 +732,7 @@ struct adxl34x *adxl34x_probe(struct device *dev, int irq,
|
|||
mutex_init(&ac->mutex);
|
||||
|
||||
input_dev->name = "ADXL34x accelerometer";
|
||||
revid = ac->bops->read(dev, DEVID);
|
||||
revid = AC_READ(ac, DEVID);
|
||||
|
||||
switch (revid) {
|
||||
case ID_ADXL345:
|
||||
|
@ -809,7 +809,7 @@ struct adxl34x *adxl34x_probe(struct device *dev, int irq,
|
|||
if (FIFO_MODE(pdata->fifo_mode) == FIFO_BYPASS)
|
||||
ac->fifo_delay = false;
|
||||
|
||||
ac->bops->write(dev, POWER_CTL, 0);
|
||||
AC_WRITE(ac, POWER_CTL, 0);
|
||||
|
||||
err = request_threaded_irq(ac->irq, NULL, adxl34x_irq,
|
||||
IRQF_TRIGGER_HIGH | IRQF_ONESHOT,
|
||||
|
@ -827,7 +827,6 @@ struct adxl34x *adxl34x_probe(struct device *dev, int irq,
|
|||
if (err)
|
||||
goto err_remove_attr;
|
||||
|
||||
AC_WRITE(ac, THRESH_TAP, pdata->tap_threshold);
|
||||
AC_WRITE(ac, OFSX, pdata->x_axis_offset);
|
||||
ac->hwcal.x = pdata->x_axis_offset;
|
||||
AC_WRITE(ac, OFSY, pdata->y_axis_offset);
|
||||
|
|
|
@ -46,18 +46,6 @@
|
|||
#define BMA150_POLL_MAX 200
|
||||
#define BMA150_POLL_MIN 0
|
||||
|
||||
#define BMA150_BW_25HZ 0
|
||||
#define BMA150_BW_50HZ 1
|
||||
#define BMA150_BW_100HZ 2
|
||||
#define BMA150_BW_190HZ 3
|
||||
#define BMA150_BW_375HZ 4
|
||||
#define BMA150_BW_750HZ 5
|
||||
#define BMA150_BW_1500HZ 6
|
||||
|
||||
#define BMA150_RANGE_2G 0
|
||||
#define BMA150_RANGE_4G 1
|
||||
#define BMA150_RANGE_8G 2
|
||||
|
||||
#define BMA150_MODE_NORMAL 0
|
||||
#define BMA150_MODE_SLEEP 2
|
||||
#define BMA150_MODE_WAKE_UP 3
|
||||
|
@ -372,7 +360,7 @@ static int bma150_open(struct bma150_data *bma150)
|
|||
int error;
|
||||
|
||||
error = pm_runtime_get_sync(&bma150->client->dev);
|
||||
if (error && error != -ENOSYS)
|
||||
if (error < 0 && error != -ENOSYS)
|
||||
return error;
|
||||
|
||||
/*
|
||||
|
|
|
@ -43,7 +43,6 @@ struct vibra_info {
|
|||
struct device *dev;
|
||||
struct input_dev *input_dev;
|
||||
|
||||
struct workqueue_struct *workqueue;
|
||||
struct work_struct play_work;
|
||||
|
||||
bool enabled;
|
||||
|
@ -143,19 +142,7 @@ static int vibra_play(struct input_dev *input, void *data,
|
|||
if (!info->speed)
|
||||
info->speed = effect->u.rumble.weak_magnitude >> 9;
|
||||
info->direction = effect->direction < EFFECT_DIR_180_DEG ? 0 : 1;
|
||||
queue_work(info->workqueue, &info->play_work);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int twl4030_vibra_open(struct input_dev *input)
|
||||
{
|
||||
struct vibra_info *info = input_get_drvdata(input);
|
||||
|
||||
info->workqueue = create_singlethread_workqueue("vibra");
|
||||
if (info->workqueue == NULL) {
|
||||
dev_err(&input->dev, "couldn't create workqueue\n");
|
||||
return -ENOMEM;
|
||||
}
|
||||
schedule_work(&info->play_work);
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
@ -164,9 +151,6 @@ static void twl4030_vibra_close(struct input_dev *input)
|
|||
struct vibra_info *info = input_get_drvdata(input);
|
||||
|
||||
cancel_work_sync(&info->play_work);
|
||||
INIT_WORK(&info->play_work, vibra_play_work); /* cleanup */
|
||||
destroy_workqueue(info->workqueue);
|
||||
info->workqueue = NULL;
|
||||
|
||||
if (info->enabled)
|
||||
vibra_disable(info);
|
||||
|
@ -219,7 +203,7 @@ static int twl4030_vibra_probe(struct platform_device *pdev)
|
|||
return -EINVAL;
|
||||
}
|
||||
|
||||
info = kzalloc(sizeof(*info), GFP_KERNEL);
|
||||
info = devm_kzalloc(&pdev->dev, sizeof(*info), GFP_KERNEL);
|
||||
if (!info)
|
||||
return -ENOMEM;
|
||||
|
||||
|
@ -227,11 +211,10 @@ static int twl4030_vibra_probe(struct platform_device *pdev)
|
|||
info->coexist = twl4030_vibra_check_coexist(pdata, twl4030_core_node);
|
||||
INIT_WORK(&info->play_work, vibra_play_work);
|
||||
|
||||
info->input_dev = input_allocate_device();
|
||||
info->input_dev = devm_input_allocate_device(&pdev->dev);
|
||||
if (info->input_dev == NULL) {
|
||||
dev_err(&pdev->dev, "couldn't allocate input device\n");
|
||||
ret = -ENOMEM;
|
||||
goto err_kzalloc;
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
input_set_drvdata(info->input_dev, info);
|
||||
|
@ -239,14 +222,13 @@ static int twl4030_vibra_probe(struct platform_device *pdev)
|
|||
info->input_dev->name = "twl4030:vibrator";
|
||||
info->input_dev->id.version = 1;
|
||||
info->input_dev->dev.parent = pdev->dev.parent;
|
||||
info->input_dev->open = twl4030_vibra_open;
|
||||
info->input_dev->close = twl4030_vibra_close;
|
||||
__set_bit(FF_RUMBLE, info->input_dev->ffbit);
|
||||
|
||||
ret = input_ff_create_memless(info->input_dev, NULL, vibra_play);
|
||||
if (ret < 0) {
|
||||
dev_dbg(&pdev->dev, "couldn't register vibrator to FF\n");
|
||||
goto err_ialloc;
|
||||
return ret;
|
||||
}
|
||||
|
||||
ret = input_register_device(info->input_dev);
|
||||
|
@ -262,28 +244,11 @@ static int twl4030_vibra_probe(struct platform_device *pdev)
|
|||
|
||||
err_iff:
|
||||
input_ff_destroy(info->input_dev);
|
||||
err_ialloc:
|
||||
input_free_device(info->input_dev);
|
||||
err_kzalloc:
|
||||
kfree(info);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int twl4030_vibra_remove(struct platform_device *pdev)
|
||||
{
|
||||
struct vibra_info *info = platform_get_drvdata(pdev);
|
||||
|
||||
/* this also free ff-memless and calls close if needed */
|
||||
input_unregister_device(info->input_dev);
|
||||
kfree(info);
|
||||
platform_set_drvdata(pdev, NULL);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static struct platform_driver twl4030_vibra_driver = {
|
||||
.probe = twl4030_vibra_probe,
|
||||
.remove = twl4030_vibra_remove,
|
||||
.driver = {
|
||||
.name = "twl4030-vibra",
|
||||
.owner = THIS_MODULE,
|
||||
|
|
|
@ -275,7 +275,7 @@ static int twl6040_vibra_probe(struct platform_device *pdev)
|
|||
return -EINVAL;
|
||||
}
|
||||
|
||||
info = kzalloc(sizeof(*info), GFP_KERNEL);
|
||||
info = devm_kzalloc(&pdev->dev, sizeof(*info), GFP_KERNEL);
|
||||
if (!info) {
|
||||
dev_err(&pdev->dev, "couldn't allocate memory\n");
|
||||
return -ENOMEM;
|
||||
|
@ -309,24 +309,65 @@ static int twl6040_vibra_probe(struct platform_device *pdev)
|
|||
if ((!info->vibldrv_res && !info->viblmotor_res) ||
|
||||
(!info->vibrdrv_res && !info->vibrmotor_res)) {
|
||||
dev_err(info->dev, "invalid vibra driver/motor resistance\n");
|
||||
ret = -EINVAL;
|
||||
goto err_kzalloc;
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
info->irq = platform_get_irq(pdev, 0);
|
||||
if (info->irq < 0) {
|
||||
dev_err(info->dev, "invalid irq\n");
|
||||
ret = -EINVAL;
|
||||
goto err_kzalloc;
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
mutex_init(&info->mutex);
|
||||
|
||||
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();
|
||||
if (info->input_dev == NULL) {
|
||||
dev_err(info->dev, "couldn't allocate input device\n");
|
||||
ret = -ENOMEM;
|
||||
goto err_kzalloc;
|
||||
goto err_regulator;
|
||||
}
|
||||
|
||||
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);
|
||||
|
||||
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;
|
||||
|
||||
err_voltage:
|
||||
regulator_bulk_free(ARRAY_SIZE(info->supplies), info->supplies);
|
||||
err_regulator:
|
||||
free_irq(info->irq, info);
|
||||
err_irq:
|
||||
input_unregister_device(info->input_dev);
|
||||
info->input_dev = NULL;
|
||||
err_iff:
|
||||
if (info->input_dev)
|
||||
input_ff_destroy(info->input_dev);
|
||||
input_ff_destroy(info->input_dev);
|
||||
err_ialloc:
|
||||
input_free_device(info->input_dev);
|
||||
err_kzalloc:
|
||||
kfree(info);
|
||||
err_regulator:
|
||||
regulator_bulk_free(ARRAY_SIZE(info->supplies), info->supplies);
|
||||
return ret;
|
||||
}
|
||||
|
||||
|
@ -423,10 +408,7 @@ static int twl6040_vibra_remove(struct platform_device *pdev)
|
|||
struct vibra_info *info = platform_get_drvdata(pdev);
|
||||
|
||||
input_unregister_device(info->input_dev);
|
||||
free_irq(info->irq, info);
|
||||
regulator_bulk_free(ARRAY_SIZE(info->supplies), info->supplies);
|
||||
destroy_workqueue(info->workqueue);
|
||||
kfree(info);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
|
|
@ -86,7 +86,7 @@ static int wm831x_on_probe(struct platform_device *pdev)
|
|||
wm831x_on->wm831x = wm831x;
|
||||
INIT_DELAYED_WORK(&wm831x_on->work, wm831x_poll_on);
|
||||
|
||||
wm831x_on->dev = input_allocate_device();
|
||||
wm831x_on->dev = devm_input_allocate_device(&pdev->dev);
|
||||
if (!wm831x_on->dev) {
|
||||
dev_err(&pdev->dev, "Can't allocate input dev\n");
|
||||
ret = -ENOMEM;
|
||||
|
@ -119,7 +119,6 @@ static int wm831x_on_probe(struct platform_device *pdev)
|
|||
err_irq:
|
||||
free_irq(irq, wm831x_on);
|
||||
err_input_dev:
|
||||
input_free_device(wm831x_on->dev);
|
||||
err:
|
||||
return ret;
|
||||
}
|
||||
|
@ -131,7 +130,6 @@ static int wm831x_on_remove(struct platform_device *pdev)
|
|||
|
||||
free_irq(irq, wm831x_on);
|
||||
cancel_delayed_work_sync(&wm831x_on->work);
|
||||
input_unregister_device(wm831x_on->dev);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
|
|
@ -68,6 +68,16 @@ config MOUSE_PS2_SYNAPTICS
|
|||
|
||||
If unsure, say Y.
|
||||
|
||||
config MOUSE_PS2_CYPRESS
|
||||
bool "Cypress PS/2 mouse protocol extension" if EXPERT
|
||||
default y
|
||||
depends on MOUSE_PS2
|
||||
help
|
||||
Say Y here if you have a Cypress PS/2 Trackpad connected to
|
||||
your system.
|
||||
|
||||
If unsure, say Y.
|
||||
|
||||
config MOUSE_PS2_LIFEBOOK
|
||||
bool "Fujitsu Lifebook PS/2 mouse protocol extension" if EXPERT
|
||||
default y
|
||||
|
@ -193,6 +203,18 @@ config MOUSE_BCM5974
|
|||
To compile this driver as a module, choose M here: the
|
||||
module will be called bcm5974.
|
||||
|
||||
config MOUSE_CYAPA
|
||||
tristate "Cypress APA I2C Trackpad support"
|
||||
depends on I2C
|
||||
help
|
||||
This driver adds support for Cypress All Points Addressable (APA)
|
||||
I2C Trackpads, including the ones used in 2012 Samsung Chromebooks.
|
||||
|
||||
Say Y here if you have a Cypress APA I2C Trackpad.
|
||||
|
||||
To compile this driver as a module, choose M here: the module will be
|
||||
called cyapa.
|
||||
|
||||
config MOUSE_INPORT
|
||||
tristate "InPort/MS/ATIXL busmouse"
|
||||
depends on ISA
|
||||
|
|
|
@ -8,6 +8,7 @@ obj-$(CONFIG_MOUSE_AMIGA) += amimouse.o
|
|||
obj-$(CONFIG_MOUSE_APPLETOUCH) += appletouch.o
|
||||
obj-$(CONFIG_MOUSE_ATARI) += atarimouse.o
|
||||
obj-$(CONFIG_MOUSE_BCM5974) += bcm5974.o
|
||||
obj-$(CONFIG_MOUSE_CYAPA) += cyapa.o
|
||||
obj-$(CONFIG_MOUSE_GPIO) += gpio_mouse.o
|
||||
obj-$(CONFIG_MOUSE_INPORT) += inport.o
|
||||
obj-$(CONFIG_MOUSE_LOGIBM) += logibm.o
|
||||
|
@ -32,3 +33,4 @@ psmouse-$(CONFIG_MOUSE_PS2_LIFEBOOK) += lifebook.o
|
|||
psmouse-$(CONFIG_MOUSE_PS2_SENTELIC) += sentelic.o
|
||||
psmouse-$(CONFIG_MOUSE_PS2_TRACKPOINT) += trackpoint.o
|
||||
psmouse-$(CONFIG_MOUSE_PS2_TOUCHKIT) += touchkit_ps2.o
|
||||
psmouse-$(CONFIG_MOUSE_PS2_CYPRESS) += cypress_ps2.o
|
||||
|
|
Разница между файлами не показана из-за своего большого размера
Загрузить разницу
|
@ -12,35 +12,146 @@
|
|||
#ifndef _ALPS_H
|
||||
#define _ALPS_H
|
||||
|
||||
#define ALPS_PROTO_V1 0
|
||||
#define ALPS_PROTO_V2 1
|
||||
#define ALPS_PROTO_V3 2
|
||||
#define ALPS_PROTO_V4 3
|
||||
#define ALPS_PROTO_V1 1
|
||||
#define ALPS_PROTO_V2 2
|
||||
#define ALPS_PROTO_V3 3
|
||||
#define ALPS_PROTO_V4 4
|
||||
|
||||
/**
|
||||
* struct alps_model_info - touchpad ID table
|
||||
* @signature: E7 response string to match.
|
||||
* @command_mode_resp: For V3/V4 touchpads, the final byte of the EC response
|
||||
* (aka command mode response) identifies the firmware minor version. This
|
||||
* can be used to distinguish different hardware models which are not
|
||||
* uniquely identifiable through their E7 responses.
|
||||
* @proto_version: Indicates V1/V2/V3/...
|
||||
* @byte0: Helps figure out whether a position report packet matches the
|
||||
* known format for this model. The first byte of the report, ANDed with
|
||||
* mask0, should match byte0.
|
||||
* @mask0: The mask used to check the first byte of the report.
|
||||
* @flags: Additional device capabilities (passthrough port, trackstick, etc.).
|
||||
*
|
||||
* Many (but not all) ALPS touchpads can be identified by looking at the
|
||||
* values returned in the "E7 report" and/or the "EC report." This table
|
||||
* lists a number of such touchpads.
|
||||
*/
|
||||
struct alps_model_info {
|
||||
unsigned char signature[3];
|
||||
unsigned char command_mode_resp; /* v3/v4 only */
|
||||
unsigned char signature[3];
|
||||
unsigned char command_mode_resp;
|
||||
unsigned char proto_version;
|
||||
unsigned char byte0, mask0;
|
||||
unsigned char flags;
|
||||
unsigned char byte0, mask0;
|
||||
unsigned char flags;
|
||||
};
|
||||
|
||||
/**
|
||||
* struct alps_nibble_commands - encodings for register accesses
|
||||
* @command: PS/2 command used for the nibble
|
||||
* @data: Data supplied as an argument to the PS/2 command, if applicable
|
||||
*
|
||||
* The ALPS protocol uses magic sequences to transmit binary data to the
|
||||
* touchpad, as it is generally not OK to send arbitrary bytes out the
|
||||
* PS/2 port. Each of the sequences in this table sends one nibble of the
|
||||
* register address or (write) data. Different versions of the ALPS protocol
|
||||
* use slightly different encodings.
|
||||
*/
|
||||
struct alps_nibble_commands {
|
||||
int command;
|
||||
unsigned char data;
|
||||
};
|
||||
|
||||
/**
|
||||
* struct alps_fields - decoded version of the report packet
|
||||
* @x_map: Bitmap of active X positions for MT.
|
||||
* @y_map: Bitmap of active Y positions for MT.
|
||||
* @fingers: Number of fingers for MT.
|
||||
* @x: X position for ST.
|
||||
* @y: Y position for ST.
|
||||
* @z: Z position for ST.
|
||||
* @first_mp: Packet is the first of a multi-packet report.
|
||||
* @is_mp: Packet is part of a multi-packet report.
|
||||
* @left: Left touchpad button is active.
|
||||
* @right: Right touchpad button is active.
|
||||
* @middle: Middle touchpad button is active.
|
||||
* @ts_left: Left trackstick button is active.
|
||||
* @ts_right: Right trackstick button is active.
|
||||
* @ts_middle: Middle trackstick button is active.
|
||||
*/
|
||||
struct alps_fields {
|
||||
unsigned int x_map;
|
||||
unsigned int y_map;
|
||||
unsigned int fingers;
|
||||
unsigned int x;
|
||||
unsigned int y;
|
||||
unsigned int z;
|
||||
unsigned int first_mp:1;
|
||||
unsigned int is_mp:1;
|
||||
|
||||
unsigned int left:1;
|
||||
unsigned int right:1;
|
||||
unsigned int middle:1;
|
||||
|
||||
unsigned int ts_left:1;
|
||||
unsigned int ts_right:1;
|
||||
unsigned int ts_middle:1;
|
||||
};
|
||||
|
||||
/**
|
||||
* struct alps_data - private data structure for the ALPS driver
|
||||
* @dev2: "Relative" device used to report trackstick or mouse activity.
|
||||
* @phys: Physical path for the relative device.
|
||||
* @nibble_commands: Command mapping used for touchpad register accesses.
|
||||
* @addr_command: Command used to tell the touchpad that a register address
|
||||
* follows.
|
||||
* @proto_version: Indicates V1/V2/V3/...
|
||||
* @byte0: Helps figure out whether a position report packet matches the
|
||||
* known format for this model. The first byte of the report, ANDed with
|
||||
* mask0, should match byte0.
|
||||
* @mask0: The mask used to check the first byte of the report.
|
||||
* @flags: Additional device capabilities (passthrough port, trackstick, etc.).
|
||||
* @x_max: Largest possible X position value.
|
||||
* @y_max: Largest possible Y position value.
|
||||
* @x_bits: Number of X bits in the MT bitmap.
|
||||
* @y_bits: Number of Y bits in the MT bitmap.
|
||||
* @hw_init: Protocol-specific hardware init function.
|
||||
* @process_packet: Protocol-specific function to process a report packet.
|
||||
* @decode_fields: Protocol-specific function to read packet bitfields.
|
||||
* @set_abs_params: Protocol-specific function to configure the input_dev.
|
||||
* @prev_fin: Finger bit from previous packet.
|
||||
* @multi_packet: Multi-packet data in progress.
|
||||
* @multi_data: Saved multi-packet data.
|
||||
* @x1: First X coordinate from last MT report.
|
||||
* @x2: Second X coordinate from last MT report.
|
||||
* @y1: First Y coordinate from last MT report.
|
||||
* @y2: Second Y coordinate from last MT report.
|
||||
* @fingers: Number of fingers from last MT report.
|
||||
* @quirks: Bitmap of ALPS_QUIRK_*.
|
||||
* @timer: Timer for flushing out the final report packet in the stream.
|
||||
*/
|
||||
struct alps_data {
|
||||
struct input_dev *dev2; /* Relative device */
|
||||
char phys[32]; /* Phys */
|
||||
const struct alps_model_info *i;/* Info */
|
||||
struct input_dev *dev2;
|
||||
char phys[32];
|
||||
|
||||
/* these are autodetected when the device is identified */
|
||||
const struct alps_nibble_commands *nibble_commands;
|
||||
int addr_command; /* Command to set register address */
|
||||
int prev_fin; /* Finger bit from previous packet */
|
||||
int multi_packet; /* Multi-packet data in progress */
|
||||
unsigned char multi_data[6]; /* Saved multi-packet data */
|
||||
int x1, x2, y1, y2; /* Coordinates from last MT report */
|
||||
int fingers; /* Number of fingers from MT report */
|
||||
int addr_command;
|
||||
unsigned char proto_version;
|
||||
unsigned char byte0, mask0;
|
||||
unsigned char flags;
|
||||
int x_max;
|
||||
int y_max;
|
||||
int x_bits;
|
||||
int y_bits;
|
||||
|
||||
int (*hw_init)(struct psmouse *psmouse);
|
||||
void (*process_packet)(struct psmouse *psmouse);
|
||||
void (*decode_fields)(struct alps_fields *f, unsigned char *p);
|
||||
void (*set_abs_params)(struct alps_data *priv, struct input_dev *dev1);
|
||||
|
||||
int prev_fin;
|
||||
int multi_packet;
|
||||
unsigned char multi_data[6];
|
||||
int x1, x2, y1, y2;
|
||||
int fingers;
|
||||
u8 quirks;
|
||||
struct timer_list timer;
|
||||
};
|
||||
|
|
|
@ -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 "elantech.h"
|
||||
#include "sentelic.h"
|
||||
#include "cypress_ps2.h"
|
||||
|
||||
#define DRIVER_DESC "PS/2 mouse driver"
|
||||
|
||||
|
@ -758,6 +759,28 @@ static int psmouse_extensions(struct psmouse *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
|
||||
*/
|
||||
|
@ -896,6 +919,15 @@ static const struct psmouse_protocol psmouse_protocols[] = {
|
|||
.alias = "thinkps",
|
||||
.detect = thinking_detect,
|
||||
},
|
||||
#ifdef CONFIG_MOUSE_PS2_CYPRESS
|
||||
{
|
||||
.type = PSMOUSE_CYPRESS,
|
||||
.name = "CyPS/2",
|
||||
.alias = "cypress",
|
||||
.detect = cypress_detect,
|
||||
.init = cypress_init,
|
||||
},
|
||||
#endif
|
||||
{
|
||||
.type = PSMOUSE_GENPS,
|
||||
.name = "GenPS/2",
|
||||
|
|
|
@ -95,6 +95,7 @@ enum psmouse_type {
|
|||
PSMOUSE_ELANTECH,
|
||||
PSMOUSE_FSP,
|
||||
PSMOUSE_SYNAPTICS_RELATIVE,
|
||||
PSMOUSE_CYPRESS,
|
||||
PSMOUSE_AUTO /* This one should always be last */
|
||||
};
|
||||
|
||||
|
|
|
@ -722,11 +722,13 @@ static void synaptics_report_mt_data(struct psmouse *psmouse,
|
|||
default:
|
||||
/*
|
||||
* If the finger slot contained in SGM is valid, and either
|
||||
* hasn't changed, or is new, then report SGM in MTB slot 0.
|
||||
* hasn't changed, or is new, or the old SGM has now moved to
|
||||
* AGM, then report SGM in MTB slot 0.
|
||||
* Otherwise, empty MTB slot 0.
|
||||
*/
|
||||
if (mt_state->sgm != -1 &&
|
||||
(mt_state->sgm == old->sgm || old->sgm == -1))
|
||||
(mt_state->sgm == old->sgm ||
|
||||
old->sgm == -1 || mt_state->agm == old->sgm))
|
||||
synaptics_report_slot(dev, 0, sgm);
|
||||
else
|
||||
synaptics_report_slot(dev, 0, NULL);
|
||||
|
@ -735,9 +737,31 @@ static void synaptics_report_mt_data(struct psmouse *psmouse,
|
|||
* If the finger slot contained in AGM is valid, and either
|
||||
* hasn't changed, or is new, then report AGM in MTB slot 1.
|
||||
* Otherwise, empty MTB slot 1.
|
||||
*
|
||||
* However, in the case where the AGM is new, make sure that
|
||||
* that it is either the same as the old SGM, or there was no
|
||||
* SGM.
|
||||
*
|
||||
* Otherwise, if the SGM was just 1, and the new AGM is 2, then
|
||||
* the new AGM will keep the old SGM's tracking ID, which can
|
||||
* cause apparent drumroll. This happens if in the following
|
||||
* valid finger sequence:
|
||||
*
|
||||
* Action SGM AGM (MTB slot:Contact)
|
||||
* 1. Touch contact 0 (0:0)
|
||||
* 2. Touch contact 1 (0:0, 1:1)
|
||||
* 3. Lift contact 0 (1:1)
|
||||
* 4. Touch contacts 2,3 (0:2, 1:3)
|
||||
*
|
||||
* In step 4, contact 3, in AGM must not be given the same
|
||||
* tracking ID as contact 1 had in step 3. To avoid this,
|
||||
* the first agm with contact 3 is dropped and slot 1 is
|
||||
* invalidated (tracking ID = -1).
|
||||
*/
|
||||
if (mt_state->agm != -1 &&
|
||||
(mt_state->agm == old->agm || old->agm == -1))
|
||||
(mt_state->agm == old->agm ||
|
||||
(old->agm == -1 &&
|
||||
(old->sgm == -1 || mt_state->agm == old->sgm))))
|
||||
synaptics_report_slot(dev, 1, agm);
|
||||
else
|
||||
synaptics_report_slot(dev, 1, NULL);
|
||||
|
@ -1247,11 +1271,11 @@ static void set_input_params(struct input_dev *dev, struct synaptics_data *priv)
|
|||
input_set_abs_params(dev, ABS_PRESSURE, 0, 255, 0, 0);
|
||||
|
||||
if (SYN_CAP_IMAGE_SENSOR(priv->ext_cap_0c)) {
|
||||
input_mt_init_slots(dev, 2, 0);
|
||||
set_abs_position_params(dev, priv, ABS_MT_POSITION_X,
|
||||
ABS_MT_POSITION_Y);
|
||||
/* Image sensors can report per-contact pressure */
|
||||
input_set_abs_params(dev, ABS_MT_PRESSURE, 0, 255, 0, 0);
|
||||
input_mt_init_slots(dev, 2, INPUT_MT_POINTER);
|
||||
|
||||
/* Image sensors can signal 4 and 5 finger clicks */
|
||||
__set_bit(BTN_TOOL_QUADTAP, dev->keybit);
|
||||
|
|
|
@ -236,6 +236,7 @@ config SERIO_PS2MULT
|
|||
|
||||
config SERIO_ARC_PS2
|
||||
tristate "ARC PS/2 support"
|
||||
depends on GENERIC_HARDIRQS
|
||||
help
|
||||
Say Y here if you have an ARC FPGA platform with a PS/2
|
||||
controller in it.
|
||||
|
|
|
@ -359,6 +359,7 @@ static int wacom_intuos_inout(struct wacom_wac *wacom)
|
|||
case 0x802: /* Intuos4 General Pen */
|
||||
case 0x804: /* Intuos4 Marker Pen */
|
||||
case 0x40802: /* Intuos4 Classic Pen */
|
||||
case 0x18803: /* DTH2242 Grip Pen */
|
||||
case 0x022:
|
||||
wacom->tool[idx] = BTN_TOOL_PEN;
|
||||
break;
|
||||
|
@ -538,6 +539,13 @@ static int wacom_intuos_irq(struct wacom_wac *wacom)
|
|||
input_report_key(input, wacom->tool[1], 0);
|
||||
input_report_abs(input, ABS_MISC, 0);
|
||||
}
|
||||
} else if (features->type == DTK) {
|
||||
input_report_key(input, BTN_0, (data[6] & 0x01));
|
||||
input_report_key(input, BTN_1, (data[6] & 0x02));
|
||||
input_report_key(input, BTN_2, (data[6] & 0x04));
|
||||
input_report_key(input, BTN_3, (data[6] & 0x08));
|
||||
input_report_key(input, BTN_4, (data[6] & 0x10));
|
||||
input_report_key(input, BTN_5, (data[6] & 0x20));
|
||||
} else if (features->type == WACOM_24HD) {
|
||||
input_report_key(input, BTN_0, (data[6] & 0x01));
|
||||
input_report_key(input, BTN_1, (data[6] & 0x02));
|
||||
|
@ -785,25 +793,6 @@ static int wacom_intuos_irq(struct wacom_wac *wacom)
|
|||
return 1;
|
||||
}
|
||||
|
||||
static int find_slot_from_contactid(struct wacom_wac *wacom, int contactid)
|
||||
{
|
||||
int touch_max = wacom->features.touch_max;
|
||||
int i;
|
||||
|
||||
if (!wacom->slots)
|
||||
return -1;
|
||||
|
||||
for (i = 0; i < touch_max; ++i) {
|
||||
if (wacom->slots[i] == contactid)
|
||||
return i;
|
||||
}
|
||||
for (i = 0; i < touch_max; ++i) {
|
||||
if (wacom->slots[i] == -1)
|
||||
return i;
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
static int int_dist(int x1, int y1, int x2, int y2)
|
||||
{
|
||||
int x = x2 - x1;
|
||||
|
@ -833,8 +822,7 @@ static int wacom_24hdt_irq(struct wacom_wac *wacom)
|
|||
for (i = 0; i < contacts_to_send; i++) {
|
||||
int offset = (WACOM_BYTES_PER_24HDT_PACKET * i) + 1;
|
||||
bool touch = data[offset] & 0x1 && !wacom->shared->stylus_in_proximity;
|
||||
int id = data[offset + 1];
|
||||
int slot = find_slot_from_contactid(wacom, id);
|
||||
int slot = input_mt_get_slot_by_key(input, data[offset + 1]);
|
||||
|
||||
if (slot < 0)
|
||||
continue;
|
||||
|
@ -856,9 +844,7 @@ static int wacom_24hdt_irq(struct wacom_wac *wacom)
|
|||
input_report_abs(input, ABS_MT_WIDTH_MINOR, min(w, h));
|
||||
input_report_abs(input, ABS_MT_ORIENTATION, w > h);
|
||||
}
|
||||
wacom->slots[slot] = touch ? id : -1;
|
||||
}
|
||||
|
||||
input_mt_report_pointer_emulation(input, true);
|
||||
|
||||
wacom->num_contacts_left -= contacts_to_send;
|
||||
|
@ -895,7 +881,7 @@ static int wacom_mt_touch(struct wacom_wac *wacom)
|
|||
int offset = (WACOM_BYTES_PER_MT_PACKET + x_offset) * i + 3;
|
||||
bool touch = data[offset] & 0x1;
|
||||
int id = le16_to_cpup((__le16 *)&data[offset + 1]);
|
||||
int slot = find_slot_from_contactid(wacom, id);
|
||||
int slot = input_mt_get_slot_by_key(input, id);
|
||||
|
||||
if (slot < 0)
|
||||
continue;
|
||||
|
@ -908,9 +894,7 @@ static int wacom_mt_touch(struct wacom_wac *wacom)
|
|||
input_report_abs(input, ABS_MT_POSITION_X, x);
|
||||
input_report_abs(input, ABS_MT_POSITION_Y, y);
|
||||
}
|
||||
wacom->slots[slot] = touch ? id : -1;
|
||||
}
|
||||
|
||||
input_mt_report_pointer_emulation(input, true);
|
||||
|
||||
wacom->num_contacts_left -= contacts_to_send;
|
||||
|
@ -942,12 +926,11 @@ static int wacom_tpc_mt_touch(struct wacom_wac *wacom)
|
|||
contact_with_no_pen_down_count++;
|
||||
}
|
||||
}
|
||||
input_mt_report_pointer_emulation(input, true);
|
||||
|
||||
/* keep touch state for pen event */
|
||||
wacom->shared->touch_down = (contact_with_no_pen_down_count > 0);
|
||||
|
||||
input_mt_report_pointer_emulation(input, true);
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
|
@ -1104,12 +1087,15 @@ static int wacom_bpt_touch(struct wacom_wac *wacom)
|
|||
static void wacom_bpt3_touch_msg(struct wacom_wac *wacom, unsigned char *data)
|
||||
{
|
||||
struct input_dev *input = wacom->input;
|
||||
int slot_id = data[0] - 2; /* data[0] is between 2 and 17 */
|
||||
bool touch = data[1] & 0x80;
|
||||
int slot = input_mt_get_slot_by_key(input, data[0]);
|
||||
|
||||
if (slot < 0)
|
||||
return;
|
||||
|
||||
touch = touch && !wacom->shared->stylus_in_proximity;
|
||||
|
||||
input_mt_slot(input, slot_id);
|
||||
input_mt_slot(input, slot);
|
||||
input_mt_report_slot_state(input, MT_TOOL_FINGER, touch);
|
||||
|
||||
if (touch) {
|
||||
|
@ -1162,7 +1148,6 @@ static int wacom_bpt3_touch(struct wacom_wac *wacom)
|
|||
wacom_bpt3_button_msg(wacom, data + offset);
|
||||
|
||||
}
|
||||
|
||||
input_mt_report_pointer_emulation(input, true);
|
||||
|
||||
input_sync(input);
|
||||
|
@ -1319,6 +1304,7 @@ void wacom_wac_irq(struct wacom_wac *wacom_wac, size_t len)
|
|||
case WACOM_21UX2:
|
||||
case WACOM_22HD:
|
||||
case WACOM_24HD:
|
||||
case DTK:
|
||||
sync = wacom_intuos_irq(wacom_wac);
|
||||
break;
|
||||
|
||||
|
@ -1444,6 +1430,51 @@ static unsigned int wacom_calculate_touch_res(unsigned int logical_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,
|
||||
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);
|
||||
|
||||
__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);
|
||||
|
||||
wacom_abs_set_axis(input_dev, wacom_wac);
|
||||
|
||||
switch (wacom_wac->features.type) {
|
||||
case WACOM_MO:
|
||||
input_set_abs_params(input_dev, ABS_WHEEL, 0, 71, 0, 0);
|
||||
|
@ -1513,12 +1524,17 @@ int wacom_setup_input_capabilities(struct input_dev *input_dev,
|
|||
__set_bit(BTN_Y, input_dev->keybit);
|
||||
__set_bit(BTN_Z, input_dev->keybit);
|
||||
|
||||
for (i = 0; i < 10; i++)
|
||||
for (i = 6; i < 10; i++)
|
||||
__set_bit(BTN_0 + i, input_dev->keybit);
|
||||
|
||||
__set_bit(KEY_PROG1, input_dev->keybit);
|
||||
__set_bit(KEY_PROG2, input_dev->keybit);
|
||||
__set_bit(KEY_PROG3, input_dev->keybit);
|
||||
/* fall through */
|
||||
|
||||
case DTK:
|
||||
for (i = 0; i < 6; i++)
|
||||
__set_bit(BTN_0 + i, input_dev->keybit);
|
||||
|
||||
input_set_abs_params(input_dev, ABS_Z, -900, 899, 0, 0);
|
||||
input_set_abs_params(input_dev, ABS_THROTTLE, 0, 71, 0, 0);
|
||||
|
@ -1614,24 +1630,11 @@ int wacom_setup_input_capabilities(struct input_dev *input_dev,
|
|||
} else if (features->device_type == BTN_TOOL_FINGER) {
|
||||
__clear_bit(ABS_MISC, input_dev->absbit);
|
||||
|
||||
__set_bit(BTN_TOOL_FINGER, input_dev->keybit);
|
||||
__set_bit(BTN_TOOL_DOUBLETAP, input_dev->keybit);
|
||||
__set_bit(BTN_TOOL_TRIPLETAP, input_dev->keybit);
|
||||
__set_bit(BTN_TOOL_QUADTAP, input_dev->keybit);
|
||||
|
||||
input_mt_init_slots(input_dev, features->touch_max, 0);
|
||||
|
||||
input_set_abs_params(input_dev, ABS_MT_TOUCH_MAJOR,
|
||||
0, features->x_max, 0, 0);
|
||||
input_set_abs_params(input_dev, ABS_MT_TOUCH_MINOR,
|
||||
0, features->y_max, 0, 0);
|
||||
|
||||
input_set_abs_params(input_dev, ABS_MT_POSITION_X,
|
||||
0, features->x_max,
|
||||
features->x_fuzz, 0);
|
||||
input_set_abs_params(input_dev, ABS_MT_POSITION_Y,
|
||||
0, features->y_max,
|
||||
features->y_fuzz, 0);
|
||||
input_mt_init_slots(input_dev, features->touch_max, INPUT_MT_POINTER);
|
||||
}
|
||||
break;
|
||||
|
||||
|
@ -1662,27 +1665,14 @@ int wacom_setup_input_capabilities(struct input_dev *input_dev,
|
|||
|
||||
case MTSCREEN:
|
||||
case MTTPC:
|
||||
if (features->device_type == BTN_TOOL_FINGER) {
|
||||
wacom_wac->slots = kmalloc(features->touch_max *
|
||||
sizeof(int),
|
||||
GFP_KERNEL);
|
||||
if (!wacom_wac->slots)
|
||||
return -ENOMEM;
|
||||
|
||||
for (i = 0; i < features->touch_max; i++)
|
||||
wacom_wac->slots[i] = -1;
|
||||
}
|
||||
/* fall through */
|
||||
|
||||
case TABLETPC2FG:
|
||||
if (features->device_type == BTN_TOOL_FINGER) {
|
||||
input_mt_init_slots(input_dev, features->touch_max, 0);
|
||||
input_set_abs_params(input_dev, ABS_MT_TOOL_TYPE,
|
||||
0, MT_TOOL_MAX, 0, 0);
|
||||
input_set_abs_params(input_dev, ABS_MT_POSITION_X,
|
||||
0, features->x_max, 0, 0);
|
||||
input_set_abs_params(input_dev, ABS_MT_POSITION_Y,
|
||||
0, features->y_max, 0, 0);
|
||||
unsigned int flags = INPUT_MT_DIRECT;
|
||||
|
||||
if (wacom_wac->features.type == TABLETPC2FG)
|
||||
flags = 0;
|
||||
|
||||
input_mt_init_slots(input_dev, features->touch_max, flags);
|
||||
}
|
||||
/* fall through */
|
||||
|
||||
|
@ -1725,35 +1715,26 @@ int wacom_setup_input_capabilities(struct input_dev *input_dev,
|
|||
__set_bit(INPUT_PROP_POINTER, input_dev->propbit);
|
||||
|
||||
if (features->device_type == BTN_TOOL_FINGER) {
|
||||
unsigned int flags = INPUT_MT_POINTER;
|
||||
|
||||
__set_bit(BTN_LEFT, input_dev->keybit);
|
||||
__set_bit(BTN_FORWARD, input_dev->keybit);
|
||||
__set_bit(BTN_BACK, input_dev->keybit);
|
||||
__set_bit(BTN_RIGHT, input_dev->keybit);
|
||||
|
||||
__set_bit(BTN_TOOL_FINGER, input_dev->keybit);
|
||||
__set_bit(BTN_TOOL_DOUBLETAP, input_dev->keybit);
|
||||
input_mt_init_slots(input_dev, features->touch_max, 0);
|
||||
|
||||
if (features->pktlen == WACOM_PKGLEN_BBTOUCH3) {
|
||||
__set_bit(BTN_TOOL_TRIPLETAP,
|
||||
input_dev->keybit);
|
||||
__set_bit(BTN_TOOL_QUADTAP,
|
||||
input_dev->keybit);
|
||||
|
||||
input_set_abs_params(input_dev,
|
||||
ABS_MT_TOUCH_MAJOR,
|
||||
0, features->x_max, 0, 0);
|
||||
input_set_abs_params(input_dev,
|
||||
ABS_MT_TOUCH_MINOR,
|
||||
0, features->y_max, 0, 0);
|
||||
} else {
|
||||
__set_bit(BTN_TOOL_FINGER, input_dev->keybit);
|
||||
__set_bit(BTN_TOOL_DOUBLETAP, input_dev->keybit);
|
||||
flags = 0;
|
||||
}
|
||||
|
||||
input_set_abs_params(input_dev, ABS_MT_POSITION_X,
|
||||
0, features->x_max,
|
||||
features->x_fuzz, 0);
|
||||
input_set_abs_params(input_dev, ABS_MT_POSITION_Y,
|
||||
0, features->y_max,
|
||||
features->y_fuzz, 0);
|
||||
input_mt_init_slots(input_dev, features->touch_max, flags);
|
||||
} else if (features->device_type == BTN_TOOL_PEN) {
|
||||
__set_bit(BTN_TOOL_RUBBER, input_dev->keybit);
|
||||
__set_bit(BTN_TOOL_PEN, input_dev->keybit);
|
||||
|
@ -1978,6 +1959,13 @@ static const struct wacom_features wacom_features_0xCE =
|
|||
static const struct wacom_features wacom_features_0xF0 =
|
||||
{ "Wacom DTU1631", WACOM_PKGLEN_GRAPHIRE, 34623, 19553, 511,
|
||||
0, DTU, WACOM_INTUOS_RES, WACOM_INTUOS_RES };
|
||||
static const struct wacom_features wacom_features_0x59 = /* Pen */
|
||||
{ "Wacom DTH2242", WACOM_PKGLEN_INTUOS, 95840, 54260, 2047,
|
||||
63, DTK, WACOM_INTUOS3_RES, WACOM_INTUOS3_RES,
|
||||
.oVid = USB_VENDOR_ID_WACOM, .oPid = 0x5D };
|
||||
static const struct wacom_features wacom_features_0x5D = /* Touch */
|
||||
{ "Wacom DTH2242", .type = WACOM_24HDT,
|
||||
.oVid = USB_VENDOR_ID_WACOM, .oPid = 0x59, .touch_max = 10 };
|
||||
static const struct wacom_features wacom_features_0xCC =
|
||||
{ "Wacom Cintiq 21UX2", WACOM_PKGLEN_INTUOS, 87200, 65600, 2047,
|
||||
63, WACOM_21UX2, WACOM_INTUOS3_RES, WACOM_INTUOS3_RES };
|
||||
|
@ -2152,6 +2140,8 @@ const struct usb_device_id wacom_ids[] = {
|
|||
{ USB_DEVICE_WACOM(0x43) },
|
||||
{ USB_DEVICE_WACOM(0x44) },
|
||||
{ USB_DEVICE_WACOM(0x45) },
|
||||
{ USB_DEVICE_WACOM(0x59) },
|
||||
{ USB_DEVICE_WACOM(0x5D) },
|
||||
{ USB_DEVICE_WACOM(0xB0) },
|
||||
{ USB_DEVICE_WACOM(0xB1) },
|
||||
{ USB_DEVICE_WACOM(0xB2) },
|
||||
|
|
|
@ -78,6 +78,7 @@ enum {
|
|||
INTUOS5L,
|
||||
WACOM_21UX2,
|
||||
WACOM_22HD,
|
||||
DTK,
|
||||
WACOM_24HD,
|
||||
CINTIQ,
|
||||
WACOM_BEE,
|
||||
|
@ -135,7 +136,6 @@ struct wacom_wac {
|
|||
int pid;
|
||||
int battery_capacity;
|
||||
int num_contacts_left;
|
||||
int *slots;
|
||||
};
|
||||
|
||||
#endif
|
||||
|
|
|
@ -359,7 +359,7 @@ config TOUCHSCREEN_MCS5000
|
|||
|
||||
config TOUCHSCREEN_MMS114
|
||||
tristate "MELFAS MMS114 touchscreen"
|
||||
depends on I2C
|
||||
depends on I2C && GENERIC_HARDIRQS
|
||||
help
|
||||
Say Y here if you have the MELFAS MMS114 touchscreen controller
|
||||
chip in your system.
|
||||
|
|
|
@ -193,7 +193,6 @@ static struct spi_driver cyttsp_spi_driver = {
|
|||
|
||||
module_spi_driver(cyttsp_spi_driver);
|
||||
|
||||
MODULE_ALIAS("spi:cyttsp");
|
||||
MODULE_LICENSE("GPL");
|
||||
MODULE_DESCRIPTION("Cypress TrueTouch(R) Standard Product (TTSP) SPI driver");
|
||||
MODULE_AUTHOR("Cypress");
|
||||
|
|
|
@ -429,12 +429,12 @@ static int mms114_probe(struct i2c_client *client,
|
|||
return -ENODEV;
|
||||
}
|
||||
|
||||
data = kzalloc(sizeof(struct mms114_data), GFP_KERNEL);
|
||||
input_dev = input_allocate_device();
|
||||
data = devm_kzalloc(&client->dev, sizeof(struct mms114_data),
|
||||
GFP_KERNEL);
|
||||
input_dev = devm_input_allocate_device(&client->dev);
|
||||
if (!data || !input_dev) {
|
||||
dev_err(&client->dev, "Failed to allocate memory\n");
|
||||
error = -ENOMEM;
|
||||
goto err_free_mem;
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
data->client = client;
|
||||
|
@ -466,57 +466,36 @@ static int mms114_probe(struct i2c_client *client,
|
|||
input_set_drvdata(input_dev, data);
|
||||
i2c_set_clientdata(client, data);
|
||||
|
||||
data->core_reg = regulator_get(&client->dev, "avdd");
|
||||
data->core_reg = devm_regulator_get(&client->dev, "avdd");
|
||||
if (IS_ERR(data->core_reg)) {
|
||||
error = PTR_ERR(data->core_reg);
|
||||
dev_err(&client->dev,
|
||||
"Unable to get the Core regulator (%d)\n", error);
|
||||
goto err_free_mem;
|
||||
return error;
|
||||
}
|
||||
|
||||
data->io_reg = regulator_get(&client->dev, "vdd");
|
||||
data->io_reg = devm_regulator_get(&client->dev, "vdd");
|
||||
if (IS_ERR(data->io_reg)) {
|
||||
error = PTR_ERR(data->io_reg);
|
||||
dev_err(&client->dev,
|
||||
"Unable to get the IO regulator (%d)\n", error);
|
||||
goto err_core_reg;
|
||||
return error;
|
||||
}
|
||||
|
||||
error = request_threaded_irq(client->irq, NULL, mms114_interrupt,
|
||||
IRQF_TRIGGER_FALLING | IRQF_ONESHOT, "mms114", data);
|
||||
error = devm_request_threaded_irq(&client->dev, client->irq, NULL,
|
||||
mms114_interrupt, IRQF_TRIGGER_FALLING | IRQF_ONESHOT,
|
||||
dev_name(&client->dev), data);
|
||||
if (error) {
|
||||
dev_err(&client->dev, "Failed to register interrupt\n");
|
||||
goto err_io_reg;
|
||||
return error;
|
||||
}
|
||||
disable_irq(client->irq);
|
||||
|
||||
error = input_register_device(data->input_dev);
|
||||
if (error)
|
||||
goto err_free_irq;
|
||||
|
||||
return 0;
|
||||
|
||||
err_free_irq:
|
||||
free_irq(client->irq, data);
|
||||
err_io_reg:
|
||||
regulator_put(data->io_reg);
|
||||
err_core_reg:
|
||||
regulator_put(data->core_reg);
|
||||
err_free_mem:
|
||||
input_free_device(input_dev);
|
||||
kfree(data);
|
||||
return error;
|
||||
}
|
||||
|
||||
static int mms114_remove(struct i2c_client *client)
|
||||
{
|
||||
struct mms114_data *data = i2c_get_clientdata(client);
|
||||
|
||||
free_irq(client->irq, data);
|
||||
regulator_put(data->io_reg);
|
||||
regulator_put(data->core_reg);
|
||||
input_unregister_device(data->input_dev);
|
||||
kfree(data);
|
||||
if (error) {
|
||||
dev_err(&client->dev, "Failed to register input device\n");
|
||||
return error;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
@ -590,7 +569,6 @@ static struct i2c_driver mms114_driver = {
|
|||
.of_match_table = of_match_ptr(mms114_dt_match),
|
||||
},
|
||||
.probe = mms114_probe,
|
||||
.remove = mms114_remove,
|
||||
.id_table = mms114_id,
|
||||
};
|
||||
|
||||
|
|
|
@ -120,6 +120,7 @@ static void stmpe_work(struct work_struct *work)
|
|||
__stmpe_reset_fifo(ts->stmpe);
|
||||
|
||||
input_report_abs(ts->idev, ABS_PRESSURE, 0);
|
||||
input_report_key(ts->idev, BTN_TOUCH, 0);
|
||||
input_sync(ts->idev);
|
||||
}
|
||||
|
||||
|
@ -153,6 +154,7 @@ static irqreturn_t stmpe_ts_handler(int irq, void *data)
|
|||
input_report_abs(ts->idev, ABS_X, x);
|
||||
input_report_abs(ts->idev, ABS_Y, y);
|
||||
input_report_abs(ts->idev, ABS_PRESSURE, z);
|
||||
input_report_key(ts->idev, BTN_TOUCH, 1);
|
||||
input_sync(ts->idev);
|
||||
|
||||
/* flush the FIFO after we have read out our values. */
|
||||
|
|
|
@ -753,3 +753,4 @@ module_spi_driver(tsc2005_driver);
|
|||
MODULE_AUTHOR("Lauri Leukkunen <lauri.leukkunen@nokia.com>");
|
||||
MODULE_DESCRIPTION("TSC2005 Touchscreen Driver");
|
||||
MODULE_LICENSE("GPL");
|
||||
MODULE_ALIAS("spi:tsc2005");
|
||||
|
|
|
@ -247,7 +247,7 @@ static int wm831x_ts_probe(struct platform_device *pdev)
|
|||
|
||||
wm831x_ts = devm_kzalloc(&pdev->dev, sizeof(struct wm831x_ts),
|
||||
GFP_KERNEL);
|
||||
input_dev = input_allocate_device();
|
||||
input_dev = devm_input_allocate_device(&pdev->dev);
|
||||
if (!wm831x_ts || !input_dev) {
|
||||
error = -ENOMEM;
|
||||
goto err_alloc;
|
||||
|
@ -376,7 +376,6 @@ err_pd_irq:
|
|||
err_data_irq:
|
||||
free_irq(wm831x_ts->data_irq, wm831x_ts);
|
||||
err_alloc:
|
||||
input_free_device(input_dev);
|
||||
|
||||
return error;
|
||||
}
|
||||
|
@ -387,7 +386,6 @@ static int wm831x_ts_remove(struct platform_device *pdev)
|
|||
|
||||
free_irq(wm831x_ts->pd_irq, wm831x_ts);
|
||||
free_irq(wm831x_ts->data_irq, wm831x_ts);
|
||||
input_unregister_device(wm831x_ts->input_dev);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
|
|
@ -41,6 +41,7 @@
|
|||
#include <linux/slab.h>
|
||||
#include <linux/input.h>
|
||||
#include <linux/uaccess.h>
|
||||
#include <linux/moduleparam.h>
|
||||
|
||||
#include <asm/ptrace.h>
|
||||
#include <asm/irq_regs.h>
|
||||
|
@ -576,8 +577,71 @@ struct sysrq_state {
|
|||
bool active;
|
||||
bool need_reinject;
|
||||
bool reinjecting;
|
||||
|
||||
/* reset sequence handling */
|
||||
bool reset_canceled;
|
||||
unsigned long reset_keybit[BITS_TO_LONGS(KEY_CNT)];
|
||||
int reset_seq_len;
|
||||
int reset_seq_cnt;
|
||||
int reset_seq_version;
|
||||
};
|
||||
|
||||
#define SYSRQ_KEY_RESET_MAX 20 /* Should be plenty */
|
||||
static unsigned short sysrq_reset_seq[SYSRQ_KEY_RESET_MAX];
|
||||
static unsigned int sysrq_reset_seq_len;
|
||||
static unsigned int sysrq_reset_seq_version = 1;
|
||||
|
||||
static void sysrq_parse_reset_sequence(struct sysrq_state *state)
|
||||
{
|
||||
int i;
|
||||
unsigned short key;
|
||||
|
||||
state->reset_seq_cnt = 0;
|
||||
|
||||
for (i = 0; i < sysrq_reset_seq_len; i++) {
|
||||
key = sysrq_reset_seq[i];
|
||||
|
||||
if (key == KEY_RESERVED || key > KEY_MAX)
|
||||
break;
|
||||
|
||||
__set_bit(key, state->reset_keybit);
|
||||
state->reset_seq_len++;
|
||||
|
||||
if (test_bit(key, state->key_down))
|
||||
state->reset_seq_cnt++;
|
||||
}
|
||||
|
||||
/* Disable reset until old keys are not released */
|
||||
state->reset_canceled = state->reset_seq_cnt != 0;
|
||||
|
||||
state->reset_seq_version = sysrq_reset_seq_version;
|
||||
}
|
||||
|
||||
static bool sysrq_detect_reset_sequence(struct sysrq_state *state,
|
||||
unsigned int code, int value)
|
||||
{
|
||||
if (!test_bit(code, state->reset_keybit)) {
|
||||
/*
|
||||
* Pressing any key _not_ in reset sequence cancels
|
||||
* the reset sequence.
|
||||
*/
|
||||
if (value && state->reset_seq_cnt)
|
||||
state->reset_canceled = true;
|
||||
} else if (value == 0) {
|
||||
/* key release */
|
||||
if (--state->reset_seq_cnt == 0)
|
||||
state->reset_canceled = false;
|
||||
} else if (value == 1) {
|
||||
/* key press, not autorepeat */
|
||||
if (++state->reset_seq_cnt == state->reset_seq_len &&
|
||||
!state->reset_canceled) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
static void sysrq_reinject_alt_sysrq(struct work_struct *work)
|
||||
{
|
||||
struct sysrq_state *sysrq =
|
||||
|
@ -604,11 +668,104 @@ static void sysrq_reinject_alt_sysrq(struct work_struct *work)
|
|||
}
|
||||
}
|
||||
|
||||
static bool sysrq_handle_keypress(struct sysrq_state *sysrq,
|
||||
unsigned int code, int value)
|
||||
{
|
||||
bool was_active = sysrq->active;
|
||||
bool suppress;
|
||||
|
||||
switch (code) {
|
||||
|
||||
case KEY_LEFTALT:
|
||||
case KEY_RIGHTALT:
|
||||
if (!value) {
|
||||
/* One of ALTs is being released */
|
||||
if (sysrq->active && code == sysrq->alt_use)
|
||||
sysrq->active = false;
|
||||
|
||||
sysrq->alt = KEY_RESERVED;
|
||||
|
||||
} else if (value != 2) {
|
||||
sysrq->alt = code;
|
||||
sysrq->need_reinject = false;
|
||||
}
|
||||
break;
|
||||
|
||||
case KEY_SYSRQ:
|
||||
if (value == 1 && sysrq->alt != KEY_RESERVED) {
|
||||
sysrq->active = true;
|
||||
sysrq->alt_use = sysrq->alt;
|
||||
/*
|
||||
* If nothing else will be pressed we'll need
|
||||
* to re-inject Alt-SysRq keysroke.
|
||||
*/
|
||||
sysrq->need_reinject = true;
|
||||
}
|
||||
|
||||
/*
|
||||
* Pretend that sysrq was never pressed at all. This
|
||||
* is needed to properly handle KGDB which will try
|
||||
* to release all keys after exiting debugger. If we
|
||||
* do not clear key bit it KGDB will end up sending
|
||||
* release events for Alt and SysRq, potentially
|
||||
* triggering print screen function.
|
||||
*/
|
||||
if (sysrq->active)
|
||||
clear_bit(KEY_SYSRQ, sysrq->handle.dev->key);
|
||||
|
||||
break;
|
||||
|
||||
default:
|
||||
if (sysrq->active && value && value != 2) {
|
||||
sysrq->need_reinject = false;
|
||||
__handle_sysrq(sysrq_xlate[code], true);
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
suppress = 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
|
||||
* keyboard state so we can release keys that have been
|
||||
* pressed before entering SysRq mode.
|
||||
*/
|
||||
if (value)
|
||||
set_bit(code, sysrq->key_down);
|
||||
else
|
||||
clear_bit(code, sysrq->key_down);
|
||||
|
||||
if (was_active)
|
||||
schedule_work(&sysrq->reinject_work);
|
||||
|
||||
if (sysrq_detect_reset_sequence(sysrq, code, value)) {
|
||||
/* Force emergency reboot */
|
||||
__handle_sysrq(sysrq_xlate[KEY_B], false);
|
||||
}
|
||||
|
||||
} else if (value == 0 && test_and_clear_bit(code, sysrq->key_down)) {
|
||||
/*
|
||||
* Pass on release events for keys that was pressed before
|
||||
* entering SysRq mode.
|
||||
*/
|
||||
suppress = false;
|
||||
}
|
||||
|
||||
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 was_active = sysrq->active;
|
||||
bool suppress;
|
||||
|
||||
/*
|
||||
|
@ -625,79 +782,7 @@ static bool sysrq_filter(struct input_handle *handle,
|
|||
break;
|
||||
|
||||
case EV_KEY:
|
||||
switch (code) {
|
||||
|
||||
case KEY_LEFTALT:
|
||||
case KEY_RIGHTALT:
|
||||
if (!value) {
|
||||
/* One of ALTs is being released */
|
||||
if (sysrq->active && code == sysrq->alt_use)
|
||||
sysrq->active = false;
|
||||
|
||||
sysrq->alt = KEY_RESERVED;
|
||||
|
||||
} else if (value != 2) {
|
||||
sysrq->alt = code;
|
||||
sysrq->need_reinject = false;
|
||||
}
|
||||
break;
|
||||
|
||||
case KEY_SYSRQ:
|
||||
if (value == 1 && sysrq->alt != KEY_RESERVED) {
|
||||
sysrq->active = true;
|
||||
sysrq->alt_use = sysrq->alt;
|
||||
/*
|
||||
* If nothing else will be pressed we'll need
|
||||
* to re-inject Alt-SysRq keysroke.
|
||||
*/
|
||||
sysrq->need_reinject = true;
|
||||
}
|
||||
|
||||
/*
|
||||
* Pretend that sysrq was never pressed at all. This
|
||||
* is needed to properly handle KGDB which will try
|
||||
* to release all keys after exiting debugger. If we
|
||||
* do not clear key bit it KGDB will end up sending
|
||||
* release events for Alt and SysRq, potentially
|
||||
* triggering print screen function.
|
||||
*/
|
||||
if (sysrq->active)
|
||||
clear_bit(KEY_SYSRQ, handle->dev->key);
|
||||
|
||||
break;
|
||||
|
||||
default:
|
||||
if (sysrq->active && value && value != 2) {
|
||||
sysrq->need_reinject = false;
|
||||
__handle_sysrq(sysrq_xlate[code], true);
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
suppress = sysrq->active;
|
||||
|
||||
if (!sysrq->active) {
|
||||
/*
|
||||
* If we are not suppressing key presses keep track of
|
||||
* keyboard state so we can release keys that have been
|
||||
* pressed before entering SysRq mode.
|
||||
*/
|
||||
if (value)
|
||||
set_bit(code, sysrq->key_down);
|
||||
else
|
||||
clear_bit(code, sysrq->key_down);
|
||||
|
||||
if (was_active)
|
||||
schedule_work(&sysrq->reinject_work);
|
||||
|
||||
} else if (value == 0 &&
|
||||
test_and_clear_bit(code, sysrq->key_down)) {
|
||||
/*
|
||||
* Pass on release events for keys that was pressed before
|
||||
* entering SysRq mode.
|
||||
*/
|
||||
suppress = false;
|
||||
}
|
||||
suppress = sysrq_handle_keypress(sysrq, code, value);
|
||||
break;
|
||||
|
||||
default:
|
||||
|
@ -785,7 +870,20 @@ static bool sysrq_handler_registered;
|
|||
|
||||
static inline void sysrq_register_handler(void)
|
||||
{
|
||||
extern unsigned short platform_sysrq_reset_seq[] __weak;
|
||||
unsigned short key;
|
||||
int error;
|
||||
int i;
|
||||
|
||||
if (platform_sysrq_reset_seq) {
|
||||
for (i = 0; i < ARRAY_SIZE(sysrq_reset_seq); i++) {
|
||||
key = platform_sysrq_reset_seq[i];
|
||||
if (key == KEY_RESERVED || key > KEY_MAX)
|
||||
break;
|
||||
|
||||
sysrq_reset_seq[sysrq_reset_seq_len++] = key;
|
||||
}
|
||||
}
|
||||
|
||||
error = input_register_handler(&sysrq_handler);
|
||||
if (error)
|
||||
|
@ -802,6 +900,36 @@ static inline void sysrq_unregister_handler(void)
|
|||
}
|
||||
}
|
||||
|
||||
static int sysrq_reset_seq_param_set(const char *buffer,
|
||||
const struct kernel_param *kp)
|
||||
{
|
||||
unsigned long val;
|
||||
int error;
|
||||
|
||||
error = strict_strtoul(buffer, 0, &val);
|
||||
if (error < 0)
|
||||
return error;
|
||||
|
||||
if (val > KEY_MAX)
|
||||
return -EINVAL;
|
||||
|
||||
*((unsigned short *)kp->arg) = val;
|
||||
sysrq_reset_seq_version++;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static struct kernel_param_ops param_ops_sysrq_reset_seq = {
|
||||
.get = param_get_ushort,
|
||||
.set = sysrq_reset_seq_param_set,
|
||||
};
|
||||
|
||||
#define param_check_sysrq_reset_seq(name, p) \
|
||||
__param_check(name, p, unsigned short)
|
||||
|
||||
module_param_array_named(reset_seq, sysrq_reset_seq, sysrq_reset_seq,
|
||||
&sysrq_reset_seq_len, 0644);
|
||||
|
||||
#else
|
||||
|
||||
static inline void sysrq_register_handler(void)
|
||||
|
|
|
@ -22,6 +22,18 @@
|
|||
|
||||
#define BMA150_DRIVER "bma150"
|
||||
|
||||
#define BMA150_RANGE_2G 0
|
||||
#define BMA150_RANGE_4G 1
|
||||
#define BMA150_RANGE_8G 2
|
||||
|
||||
#define BMA150_BW_25HZ 0
|
||||
#define BMA150_BW_50HZ 1
|
||||
#define BMA150_BW_100HZ 2
|
||||
#define BMA150_BW_190HZ 3
|
||||
#define BMA150_BW_375HZ 4
|
||||
#define BMA150_BW_750HZ 5
|
||||
#define BMA150_BW_1500HZ 6
|
||||
|
||||
struct bma150_cfg {
|
||||
bool any_motion_int; /* Set to enable any-motion interrupt */
|
||||
bool hg_int; /* Set to enable high-G interrupt */
|
||||
|
@ -34,8 +46,8 @@ struct bma150_cfg {
|
|||
unsigned char lg_hyst; /* Low-G hysterisis */
|
||||
unsigned char lg_dur; /* Low-G duration */
|
||||
unsigned char lg_thres; /* Low-G threshold */
|
||||
unsigned char range; /* BMA0150_RANGE_xxx (in G) */
|
||||
unsigned char bandwidth; /* BMA0150_BW_xxx (in Hz) */
|
||||
unsigned char range; /* one of BMA0150_RANGE_xxx */
|
||||
unsigned char bandwidth; /* one of BMA0150_BW_xxx */
|
||||
};
|
||||
|
||||
struct bma150_platform_data {
|
||||
|
|
|
@ -13,6 +13,8 @@
|
|||
#ifndef __LINUX_INPUT_ADXL34X_H__
|
||||
#define __LINUX_INPUT_ADXL34X_H__
|
||||
|
||||
#include <linux/input.h>
|
||||
|
||||
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;
|
||||
|
||||
unsigned long flags;
|
||||
unsigned char cmdbuf[6];
|
||||
unsigned char cmdbuf[8];
|
||||
unsigned char cmdcnt;
|
||||
unsigned char nak;
|
||||
};
|
||||
|
|
Загрузка…
Ссылка в новой задаче