Merge branch 'for-linus' of git://git.kernel.org/pub/scm/linux/kernel/git/dtor/input

Pull input updates from Dmitry Torokhov:
 "Two new touchpad drivers - Cypress APA I2C Trackpad and Cypress PS/2
  touchpad and a big update to ALPS driver from Kevin Cernekee that adds
  support for "Rushmore" touchpads and paves way for adding support for
  "Dolphin" touchpads.

  There is also a new input driver for Goldfish emulator and also
  Android keyreset driver was folded into SysRq code.

  A few more drivers were updated with device tree bindings and others
  got some small cleanups and fixes."

* 'for-linus' of git://git.kernel.org/pub/scm/linux/kernel/git/dtor/input: (55 commits)
  Input: cyttsp-spi - remove duplicate MODULE_ALIAS()
  Input: tsc2005 - add MODULE_ALIAS
  Input: tegra-kbc - require CONFIG_OF, remove platform data
  Input: synaptics - initialize pointer emulation usage
  Input: MT - do not apply filtering on emulated events
  Input: bma150 - make some defines public and fix some comments
  Input: bma150 - fix checking pm_runtime_get_sync() return value
  Input: ALPS - enable trackstick on Rushmore touchpads
  Input: ALPS - add support for "Rushmore" touchpads
  Input: ALPS - make the V3 packet field decoder "pluggable"
  Input: ALPS - move pixel and bitmap info into alps_data struct
  Input: ALPS - fix command mode check
  Input: ALPS - rework detection of Pinnacle AGx touchpads
  Input: ALPS - move {addr,nibble}_command settings into alps_set_defaults()
  Input: ALPS - use function pointers for different protocol handlers
  Input: ALPS - rework detection sequence
  Input: ALPS - introduce helper function for repeated commands
  Input: ALPS - move alps_get_model() down below hw_init code
  Input: ALPS - copy "model" info into alps_data struct
  Input: ALPS - document the alps.h data structures
  ...
This commit is contained in:
Linus Torvalds 2013-02-20 11:00:43 -08:00
commit c6699b58f4
41 changed files with 3627 additions and 1201 deletions

View File

@ -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 */
};

View File

@ -1,7 +1,18 @@
* Tegra keyboard controller * Tegra keyboard controller
The key controller has maximum 24 pins to make matrix keypad. Any pin
can be configured as row or column. The maximum column pin can be 8
and maximum row pins can be 16 for Tegra20/Tegra30.
Required properties: Required properties:
- compatible: "nvidia,tegra20-kbc" - compatible: "nvidia,tegra20-kbc"
- reg: Register base address of KBC.
- interrupts: Interrupt number for the KBC.
- nvidia,kbc-row-pins: The KBC pins which are configured as row. This is an
array of pin numbers which is used as rows.
- nvidia,kbc-col-pins: The KBC pins which are configured as column. This is an
array of pin numbers which is used as column.
- linux,keymap: The keymap for keys as described in the binding document
devicetree/bindings/input/matrix-keymap.txt.
Optional properties, in addition to those specified by the shared Optional properties, in addition to those specified by the shared
matrix-keyboard bindings: matrix-keyboard bindings:
@ -19,5 +30,16 @@ Example:
keyboard: keyboard { keyboard: keyboard {
compatible = "nvidia,tegra20-kbc"; compatible = "nvidia,tegra20-kbc";
reg = <0x7000e200 0x100>; reg = <0x7000e200 0x100>;
interrupts = <0 85 0x04>;
nvidia,ghost-filter; nvidia,ghost-filter;
nvidia,debounce-delay-ms = <640>;
nvidia,kbc-row-pins = <0 1 2>; /* pin 0, 1, 2 as rows */
nvidia,kbc-col-pins = <11 12 13>; /* pin 11, 12, 13 as columns */
linux,keymap = <0x00000074
0x00010067
0x00020066
0x01010068
0x02000069
0x02010070
0x02020071>;
}; };

View File

@ -3,7 +3,7 @@
# #
menu "Input device support" menu "Input device support"
depends on !S390 && !UML depends on !UML
config INPUT config INPUT
tristate "Generic input layer (needed for keyboard, mouse, ...)" if EXPERT tristate "Generic input layer (needed for keyboard, mouse, ...)" if EXPERT

View File

@ -18,6 +18,7 @@ static void copy_abs(struct input_dev *dev, unsigned int dst, unsigned int src)
{ {
if (dev->absinfo && test_bit(src, dev->absbit)) { if (dev->absinfo && test_bit(src, dev->absbit)) {
dev->absinfo[dst] = dev->absinfo[src]; dev->absinfo[dst] = dev->absinfo[src];
dev->absinfo[dst].fuzz = 0;
dev->absbit[BIT_WORD(dst)] |= BIT_MASK(dst); dev->absbit[BIT_WORD(dst)] |= BIT_MASK(dst);
} }
} }

View File

@ -12,7 +12,7 @@
* the Free Software Foundation. * the Free Software Foundation.
*/ */
/* #define WK0701_DEBUG */ #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
#define RESERVE 20000 #define RESERVE 20000
#define SYNC_PULSE 1306000 #define SYNC_PULSE 1306000
@ -67,6 +67,7 @@ static inline void walkera0701_parse_frame(struct walkera_dev *w)
{ {
int i; int i;
int val1, val2, val3, val4, val5, val6, val7, val8; int val1, val2, val3, val4, val5, val6, val7, val8;
int magic, magic_bit;
int crc1, crc2; int crc1, crc2;
for (crc1 = crc2 = i = 0; i < 10; i++) { for (crc1 = crc2 = i = 0; i < 10; i++) {
@ -102,17 +103,12 @@ static inline void walkera0701_parse_frame(struct walkera_dev *w)
val8 = (w->buf[18] & 1) << 8 | (w->buf[19] << 4) | w->buf[20]; val8 = (w->buf[18] & 1) << 8 | (w->buf[19] << 4) | w->buf[20];
val8 *= (w->buf[18] & 2) - 1; /*sign */ val8 *= (w->buf[18] & 2) - 1; /*sign */
#ifdef WK0701_DEBUG
{
int magic, magic_bit;
magic = (w->buf[21] << 4) | w->buf[22]; magic = (w->buf[21] << 4) | w->buf[22];
magic_bit = (w->buf[24] & 8) >> 3; magic_bit = (w->buf[24] & 8) >> 3;
printk(KERN_DEBUG pr_debug("%4d %4d %4d %4d %4d %4d %4d %4d (magic %2x %d)\n",
"walkera0701: %4d %4d %4d %4d %4d %4d %4d %4d (magic %2x %d)\n", val1, val2, val3, val4, val5, val6, val7, val8,
val1, val2, val3, val4, val5, val6, val7, val8, magic, magic, magic_bit);
magic_bit);
}
#endif
input_report_abs(w->input_dev, ABS_X, val2); input_report_abs(w->input_dev, ABS_X, val2);
input_report_abs(w->input_dev, ABS_Y, val1); input_report_abs(w->input_dev, ABS_Y, val1);
input_report_abs(w->input_dev, ABS_Z, val6); input_report_abs(w->input_dev, ABS_Z, val6);
@ -187,6 +183,9 @@ static int walkera0701_open(struct input_dev *dev)
{ {
struct walkera_dev *w = input_get_drvdata(dev); struct walkera_dev *w = input_get_drvdata(dev);
if (parport_claim(w->pardevice))
return -EBUSY;
parport_enable_irq(w->parport); parport_enable_irq(w->parport);
return 0; return 0;
} }
@ -197,40 +196,51 @@ static void walkera0701_close(struct input_dev *dev)
parport_disable_irq(w->parport); parport_disable_irq(w->parport);
hrtimer_cancel(&w->timer); hrtimer_cancel(&w->timer);
parport_release(w->pardevice);
} }
static int walkera0701_connect(struct walkera_dev *w, int parport) static int walkera0701_connect(struct walkera_dev *w, int parport)
{ {
int err = -ENODEV; int error;
w->parport = parport_find_number(parport); w->parport = parport_find_number(parport);
if (w->parport == NULL) if (!w->parport) {
pr_err("parport %d does not exist\n", parport);
return -ENODEV; return -ENODEV;
if (w->parport->irq == -1) {
printk(KERN_ERR "walkera0701: parport without interrupt\n");
goto init_err;
} }
err = -EBUSY; if (w->parport->irq == -1) {
pr_err("parport %d does not have interrupt assigned\n",
parport);
error = -EINVAL;
goto err_put_parport;
}
w->pardevice = parport_register_device(w->parport, "walkera0701", w->pardevice = parport_register_device(w->parport, "walkera0701",
NULL, NULL, walkera0701_irq_handler, NULL, NULL, walkera0701_irq_handler,
PARPORT_DEV_EXCL, w); PARPORT_DEV_EXCL, w);
if (!w->pardevice) if (!w->pardevice) {
goto init_err; pr_err("failed to register parport device\n");
error = -EIO;
goto err_put_parport;
}
if (parport_negotiate(w->pardevice->port, IEEE1284_MODE_COMPAT)) if (parport_negotiate(w->pardevice->port, IEEE1284_MODE_COMPAT)) {
goto init_err1; pr_err("failed to negotiate parport mode\n");
error = -EIO;
if (parport_claim(w->pardevice)) goto err_unregister_device;
goto init_err1; }
hrtimer_init(&w->timer, CLOCK_MONOTONIC, HRTIMER_MODE_REL); hrtimer_init(&w->timer, CLOCK_MONOTONIC, HRTIMER_MODE_REL);
w->timer.function = timer_handler; w->timer.function = timer_handler;
w->input_dev = input_allocate_device(); w->input_dev = input_allocate_device();
if (!w->input_dev) if (!w->input_dev) {
goto init_err2; pr_err("failed to allocate input device\n");
error = -ENOMEM;
goto err_unregister_device;
}
input_set_drvdata(w->input_dev, w); input_set_drvdata(w->input_dev, w);
w->input_dev->name = "Walkera WK-0701 TX"; w->input_dev->name = "Walkera WK-0701 TX";
@ -241,6 +251,7 @@ static int walkera0701_connect(struct walkera_dev *w, int parport)
w->input_dev->id.vendor = 0x0001; w->input_dev->id.vendor = 0x0001;
w->input_dev->id.product = 0x0001; w->input_dev->id.product = 0x0001;
w->input_dev->id.version = 0x0100; w->input_dev->id.version = 0x0100;
w->input_dev->dev.parent = w->parport->dev;
w->input_dev->open = walkera0701_open; w->input_dev->open = walkera0701_open;
w->input_dev->close = walkera0701_close; w->input_dev->close = walkera0701_close;
@ -254,27 +265,26 @@ static int walkera0701_connect(struct walkera_dev *w, int parport)
input_set_abs_params(w->input_dev, ABS_RUDDER, -512, 512, 0, 0); input_set_abs_params(w->input_dev, ABS_RUDDER, -512, 512, 0, 0);
input_set_abs_params(w->input_dev, ABS_MISC, -512, 512, 0, 0); input_set_abs_params(w->input_dev, ABS_MISC, -512, 512, 0, 0);
err = input_register_device(w->input_dev); error = input_register_device(w->input_dev);
if (err) if (error) {
goto init_err3; pr_err("failed to register input device\n");
goto err_free_input_dev;
}
return 0; return 0;
init_err3: err_free_input_dev:
input_free_device(w->input_dev); input_free_device(w->input_dev);
init_err2: err_unregister_device:
parport_release(w->pardevice);
init_err1:
parport_unregister_device(w->pardevice); parport_unregister_device(w->pardevice);
init_err: err_put_parport:
parport_put_port(w->parport); parport_put_port(w->parport);
return err; return error;
} }
static void walkera0701_disconnect(struct walkera_dev *w) static void walkera0701_disconnect(struct walkera_dev *w)
{ {
input_unregister_device(w->input_dev); input_unregister_device(w->input_dev);
parport_release(w->pardevice);
parport_unregister_device(w->pardevice); parport_unregister_device(w->pardevice);
parport_put_port(w->parport); parport_put_port(w->parport);
} }

View File

@ -224,7 +224,7 @@ config KEYBOARD_TCA6416
config KEYBOARD_TCA8418 config KEYBOARD_TCA8418
tristate "TCA8418 Keypad Support" tristate "TCA8418 Keypad Support"
depends on I2C depends on I2C && GENERIC_HARDIRQS
select INPUT_MATRIXKMAP select INPUT_MATRIXKMAP
help help
This driver implements basic keypad functionality This driver implements basic keypad functionality
@ -303,7 +303,7 @@ config KEYBOARD_HP7XX
config KEYBOARD_LM8323 config KEYBOARD_LM8323
tristate "LM8323 keypad chip" tristate "LM8323 keypad chip"
depends on I2C depends on I2C && GENERIC_HARDIRQS
depends on LEDS_CLASS depends on LEDS_CLASS
help help
If you say yes here you get support for the National Semiconductor If you say yes here you get support for the National Semiconductor
@ -420,7 +420,7 @@ config KEYBOARD_NOMADIK
config KEYBOARD_TEGRA config KEYBOARD_TEGRA
tristate "NVIDIA Tegra internal matrix keyboard controller support" tristate "NVIDIA Tegra internal matrix keyboard controller support"
depends on ARCH_TEGRA depends on ARCH_TEGRA && OF
select INPUT_MATRIXKMAP select INPUT_MATRIXKMAP
help help
Say Y here if you want to use a matrix keyboard connected directly Say Y here if you want to use a matrix keyboard connected directly
@ -479,6 +479,16 @@ config KEYBOARD_SAMSUNG
To compile this driver as a module, choose M here: the To compile this driver as a module, choose M here: the
module will be called samsung-keypad. module will be called samsung-keypad.
config KEYBOARD_GOLDFISH_EVENTS
depends on GOLDFISH
tristate "Generic Input Event device for Goldfish"
help
Say Y here to get an input event device for the Goldfish virtual
device emulator.
To compile this driver as a module, choose M here: the
module will be called goldfish-events.
config KEYBOARD_STOWAWAY config KEYBOARD_STOWAWAY
tristate "Stowaway keyboard" tristate "Stowaway keyboard"
select SERIO select SERIO

View File

@ -13,6 +13,7 @@ obj-$(CONFIG_KEYBOARD_ATKBD) += atkbd.o
obj-$(CONFIG_KEYBOARD_BFIN) += bf54x-keys.o obj-$(CONFIG_KEYBOARD_BFIN) += bf54x-keys.o
obj-$(CONFIG_KEYBOARD_DAVINCI) += davinci_keyscan.o obj-$(CONFIG_KEYBOARD_DAVINCI) += davinci_keyscan.o
obj-$(CONFIG_KEYBOARD_EP93XX) += ep93xx_keypad.o obj-$(CONFIG_KEYBOARD_EP93XX) += ep93xx_keypad.o
obj-$(CONFIG_KEYBOARD_GOLDFISH_EVENTS) += goldfish_events.o
obj-$(CONFIG_KEYBOARD_GPIO) += gpio_keys.o obj-$(CONFIG_KEYBOARD_GPIO) += gpio_keys.o
obj-$(CONFIG_KEYBOARD_GPIO_POLLED) += gpio_keys_polled.o obj-$(CONFIG_KEYBOARD_GPIO_POLLED) += gpio_keys_polled.o
obj-$(CONFIG_KEYBOARD_TCA6416) += tca6416-keypad.o obj-$(CONFIG_KEYBOARD_TCA6416) += tca6416-keypad.o

View File

@ -676,6 +676,39 @@ static inline void atkbd_disable(struct atkbd *atkbd)
serio_continue_rx(atkbd->ps2dev.serio); serio_continue_rx(atkbd->ps2dev.serio);
} }
static int atkbd_activate(struct atkbd *atkbd)
{
struct ps2dev *ps2dev = &atkbd->ps2dev;
/*
* Enable the keyboard to receive keystrokes.
*/
if (ps2_command(ps2dev, NULL, ATKBD_CMD_ENABLE)) {
dev_err(&ps2dev->serio->dev,
"Failed to enable keyboard on %s\n",
ps2dev->serio->phys);
return -1;
}
return 0;
}
/*
* atkbd_deactivate() resets and disables the keyboard from sending
* keystrokes.
*/
static void atkbd_deactivate(struct atkbd *atkbd)
{
struct ps2dev *ps2dev = &atkbd->ps2dev;
if (ps2_command(ps2dev, NULL, ATKBD_CMD_RESET_DIS))
dev_err(&ps2dev->serio->dev,
"Failed to deactivate keyboard on %s\n",
ps2dev->serio->phys);
}
/* /*
* atkbd_probe() probes for an AT keyboard on a serio port. * atkbd_probe() probes for an AT keyboard on a serio port.
*/ */
@ -726,11 +759,17 @@ static int atkbd_probe(struct atkbd *atkbd)
if (atkbd->id == 0xaca1 && atkbd->translated) { if (atkbd->id == 0xaca1 && atkbd->translated) {
dev_err(&ps2dev->serio->dev, dev_err(&ps2dev->serio->dev,
"NCD terminal keyboards are only supported on non-translating controlelrs. " "NCD terminal keyboards are only supported on non-translating controllers. "
"Use i8042.direct=1 to disable translation.\n"); "Use i8042.direct=1 to disable translation.\n");
return -1; return -1;
} }
/*
* Make sure nothing is coming from the keyboard and disturbs our
* internal state.
*/
atkbd_deactivate(atkbd);
return 0; return 0;
} }
@ -825,24 +864,6 @@ static int atkbd_reset_state(struct atkbd *atkbd)
return 0; return 0;
} }
static int atkbd_activate(struct atkbd *atkbd)
{
struct ps2dev *ps2dev = &atkbd->ps2dev;
/*
* Enable the keyboard to receive keystrokes.
*/
if (ps2_command(ps2dev, NULL, ATKBD_CMD_ENABLE)) {
dev_err(&ps2dev->serio->dev,
"Failed to enable keyboard on %s\n",
ps2dev->serio->phys);
return -1;
}
return 0;
}
/* /*
* atkbd_cleanup() restores the keyboard state so that BIOS is happy after a * atkbd_cleanup() restores the keyboard state so that BIOS is happy after a
* reboot. * reboot.
@ -1150,7 +1171,6 @@ static int atkbd_connect(struct serio *serio, struct serio_driver *drv)
atkbd->set = atkbd_select_set(atkbd, atkbd_set, atkbd_extra); atkbd->set = atkbd_select_set(atkbd, atkbd_set, atkbd_extra);
atkbd_reset_state(atkbd); atkbd_reset_state(atkbd);
atkbd_activate(atkbd);
} else { } else {
atkbd->set = 2; atkbd->set = 2;
@ -1165,6 +1185,8 @@ static int atkbd_connect(struct serio *serio, struct serio_driver *drv)
goto fail3; goto fail3;
atkbd_enable(atkbd); atkbd_enable(atkbd);
if (serio->write)
atkbd_activate(atkbd);
err = input_register_device(atkbd->dev); err = input_register_device(atkbd->dev);
if (err) if (err)
@ -1208,8 +1230,6 @@ static int atkbd_reconnect(struct serio *serio)
if (atkbd->set != atkbd_select_set(atkbd, atkbd->set, atkbd->extra)) if (atkbd->set != atkbd_select_set(atkbd, atkbd->set, atkbd->extra))
goto out; goto out;
atkbd_activate(atkbd);
/* /*
* Restore LED state and repeat rate. While input core * Restore LED state and repeat rate. While input core
* will do this for us at resume time reconnect may happen * will do this for us at resume time reconnect may happen
@ -1223,7 +1243,17 @@ static int atkbd_reconnect(struct serio *serio)
} }
/*
* Reset our state machine in case reconnect happened in the middle
* of multi-byte scancode.
*/
atkbd->xl_bit = 0;
atkbd->emul = 0;
atkbd_enable(atkbd); atkbd_enable(atkbd);
if (atkbd->write)
atkbd_activate(atkbd);
retval = 0; retval = 0;
out: out:

View File

@ -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");

View File

@ -20,6 +20,7 @@
#include <linux/jiffies.h> #include <linux/jiffies.h>
#include <linux/kernel.h> #include <linux/kernel.h>
#include <linux/module.h> #include <linux/module.h>
#include <linux/of.h>
#include <linux/platform_device.h> #include <linux/platform_device.h>
#include <linux/slab.h> #include <linux/slab.h>
#include <linux/timer.h> #include <linux/timer.h>
@ -414,15 +415,23 @@ open_err:
return -EIO; return -EIO;
} }
#ifdef CONFIG_OF
static struct of_device_id imx_keypad_of_match[] = {
{ .compatible = "fsl,imx21-kpp", },
{ /* sentinel */ }
};
MODULE_DEVICE_TABLE(of, imx_keypad_of_match);
#endif
static int imx_keypad_probe(struct platform_device *pdev) static int imx_keypad_probe(struct platform_device *pdev)
{ {
const struct matrix_keymap_data *keymap_data = pdev->dev.platform_data; const struct matrix_keymap_data *keymap_data = pdev->dev.platform_data;
struct imx_keypad *keypad; struct imx_keypad *keypad;
struct input_dev *input_dev; struct input_dev *input_dev;
struct resource *res; struct resource *res;
int irq, error, i; int irq, error, i, row, col;
if (keymap_data == NULL) { if (!keymap_data && !pdev->dev.of_node) {
dev_err(&pdev->dev, "no keymap defined\n"); dev_err(&pdev->dev, "no keymap defined\n");
return -EINVAL; return -EINVAL;
} }
@ -480,22 +489,6 @@ static int imx_keypad_probe(struct platform_device *pdev)
goto failed_unmap; goto failed_unmap;
} }
/* Search for rows and cols enabled */
for (i = 0; i < keymap_data->keymap_size; i++) {
keypad->rows_en_mask |= 1 << KEY_ROW(keymap_data->keymap[i]);
keypad->cols_en_mask |= 1 << KEY_COL(keymap_data->keymap[i]);
}
if (keypad->rows_en_mask > ((1 << MAX_MATRIX_KEY_ROWS) - 1) ||
keypad->cols_en_mask > ((1 << MAX_MATRIX_KEY_COLS) - 1)) {
dev_err(&pdev->dev,
"invalid key data (too many rows or colums)\n");
error = -EINVAL;
goto failed_clock_put;
}
dev_dbg(&pdev->dev, "enabled rows mask: %x\n", keypad->rows_en_mask);
dev_dbg(&pdev->dev, "enabled cols mask: %x\n", keypad->cols_en_mask);
/* Init the Input device */ /* Init the Input device */
input_dev->name = pdev->name; input_dev->name = pdev->name;
input_dev->id.bustype = BUS_HOST; input_dev->id.bustype = BUS_HOST;
@ -512,6 +505,19 @@ static int imx_keypad_probe(struct platform_device *pdev)
goto failed_clock_put; goto failed_clock_put;
} }
/* Search for rows and cols enabled */
for (row = 0; row < MAX_MATRIX_KEY_ROWS; row++) {
for (col = 0; col < MAX_MATRIX_KEY_COLS; col++) {
i = MATRIX_SCAN_CODE(row, col, MATRIX_ROW_SHIFT);
if (keypad->keycodes[i] != KEY_RESERVED) {
keypad->rows_en_mask |= 1 << row;
keypad->cols_en_mask |= 1 << col;
}
}
}
dev_dbg(&pdev->dev, "enabled rows mask: %x\n", keypad->rows_en_mask);
dev_dbg(&pdev->dev, "enabled cols mask: %x\n", keypad->cols_en_mask);
__set_bit(EV_REP, input_dev->evbit); __set_bit(EV_REP, input_dev->evbit);
input_set_capability(input_dev, EV_MSC, MSC_SCAN); input_set_capability(input_dev, EV_MSC, MSC_SCAN);
input_set_drvdata(input_dev, keypad); input_set_drvdata(input_dev, keypad);
@ -631,6 +637,7 @@ static struct platform_driver imx_keypad_driver = {
.name = "imx-keypad", .name = "imx-keypad",
.owner = THIS_MODULE, .owner = THIS_MODULE,
.pm = &imx_kbd_pm_ops, .pm = &imx_kbd_pm_ops,
.of_match_table = of_match_ptr(imx_keypad_of_match),
}, },
.probe = imx_keypad_probe, .probe = imx_keypad_probe,
.remove = imx_keypad_remove, .remove = imx_keypad_remove,

View File

@ -20,6 +20,7 @@
#include <linux/kernel.h> #include <linux/kernel.h>
#include <linux/init.h> #include <linux/init.h>
#include <linux/leds.h>
#include <linux/module.h> #include <linux/module.h>
#include <linux/slab.h> #include <linux/slab.h>
#include <linux/jiffies.h> #include <linux/jiffies.h>
@ -39,6 +40,11 @@
#define QT2160_CMD_GPIOS 6 #define QT2160_CMD_GPIOS 6
#define QT2160_CMD_SUBVER 7 #define QT2160_CMD_SUBVER 7
#define QT2160_CMD_CALIBRATE 10 #define QT2160_CMD_CALIBRATE 10
#define QT2160_CMD_DRIVE_X 70
#define QT2160_CMD_PWMEN_X 74
#define QT2160_CMD_PWM_DUTY 76
#define QT2160_NUM_LEDS_X 8
#define QT2160_CYCLE_INTERVAL (2*HZ) #define QT2160_CYCLE_INTERVAL (2*HZ)
@ -49,6 +55,17 @@ static unsigned char qt2160_key2code[] = {
KEY_C, KEY_D, KEY_E, KEY_F, KEY_C, KEY_D, KEY_E, KEY_F,
}; };
#ifdef CONFIG_LEDS_CLASS
struct qt2160_led {
struct qt2160_data *qt2160;
struct led_classdev cdev;
struct work_struct work;
char name[32];
int id;
enum led_brightness new_brightness;
};
#endif
struct qt2160_data { struct qt2160_data {
struct i2c_client *client; struct i2c_client *client;
struct input_dev *input; struct input_dev *input;
@ -56,8 +73,61 @@ struct qt2160_data {
spinlock_t lock; /* Protects canceling/rescheduling of dwork */ spinlock_t lock; /* Protects canceling/rescheduling of dwork */
unsigned short keycodes[ARRAY_SIZE(qt2160_key2code)]; unsigned short keycodes[ARRAY_SIZE(qt2160_key2code)];
u16 key_matrix; u16 key_matrix;
#ifdef CONFIG_LEDS_CLASS
struct qt2160_led leds[QT2160_NUM_LEDS_X];
struct mutex led_lock;
#endif
}; };
static int qt2160_read(struct i2c_client *client, u8 reg);
static int qt2160_write(struct i2c_client *client, u8 reg, u8 data);
#ifdef CONFIG_LEDS_CLASS
static void qt2160_led_work(struct work_struct *work)
{
struct qt2160_led *led = container_of(work, struct qt2160_led, work);
struct qt2160_data *qt2160 = led->qt2160;
struct i2c_client *client = qt2160->client;
int value = led->new_brightness;
u32 drive, pwmen;
mutex_lock(&qt2160->led_lock);
drive = qt2160_read(client, QT2160_CMD_DRIVE_X);
pwmen = qt2160_read(client, QT2160_CMD_PWMEN_X);
if (value != LED_OFF) {
drive |= (1 << led->id);
pwmen |= (1 << led->id);
} else {
drive &= ~(1 << led->id);
pwmen &= ~(1 << led->id);
}
qt2160_write(client, QT2160_CMD_DRIVE_X, drive);
qt2160_write(client, QT2160_CMD_PWMEN_X, pwmen);
/*
* Changing this register will change the brightness
* of every LED in the qt2160. It's a HW limitation.
*/
if (value != LED_OFF)
qt2160_write(client, QT2160_CMD_PWM_DUTY, value);
mutex_unlock(&qt2160->led_lock);
}
static void qt2160_led_set(struct led_classdev *cdev,
enum led_brightness value)
{
struct qt2160_led *led = container_of(cdev, struct qt2160_led, cdev);
led->new_brightness = value;
schedule_work(&led->work);
}
#endif /* CONFIG_LEDS_CLASS */
static int qt2160_read_block(struct i2c_client *client, static int qt2160_read_block(struct i2c_client *client,
u8 inireg, u8 *buffer, unsigned int count) u8 inireg, u8 *buffer, unsigned int count)
{ {
@ -216,6 +286,63 @@ static int qt2160_write(struct i2c_client *client, u8 reg, u8 data)
return ret; return ret;
} }
#ifdef CONFIG_LEDS_CLASS
static int qt2160_register_leds(struct qt2160_data *qt2160)
{
struct i2c_client *client = qt2160->client;
int ret;
int i;
mutex_init(&qt2160->led_lock);
for (i = 0; i < QT2160_NUM_LEDS_X; i++) {
struct qt2160_led *led = &qt2160->leds[i];
snprintf(led->name, sizeof(led->name), "qt2160:x%d", i);
led->cdev.name = led->name;
led->cdev.brightness_set = qt2160_led_set;
led->cdev.brightness = LED_OFF;
led->id = i;
led->qt2160 = qt2160;
INIT_WORK(&led->work, qt2160_led_work);
ret = led_classdev_register(&client->dev, &led->cdev);
if (ret < 0)
return ret;
}
/* Tur off LEDs */
qt2160_write(client, QT2160_CMD_DRIVE_X, 0);
qt2160_write(client, QT2160_CMD_PWMEN_X, 0);
qt2160_write(client, QT2160_CMD_PWM_DUTY, 0);
return 0;
}
static void qt2160_unregister_leds(struct qt2160_data *qt2160)
{
int i;
for (i = 0; i < QT2160_NUM_LEDS_X; i++) {
led_classdev_unregister(&qt2160->leds[i].cdev);
cancel_work_sync(&qt2160->leds[i].work);
}
}
#else
static inline int qt2160_register_leds(struct qt2160_data *qt2160)
{
return 0;
}
static inline void qt2160_unregister_leds(struct qt2160_data *qt2160)
{
}
#endif
static bool qt2160_identify(struct i2c_client *client) static bool qt2160_identify(struct i2c_client *client)
{ {
@ -314,11 +441,17 @@ static int qt2160_probe(struct i2c_client *client,
} }
} }
error = qt2160_register_leds(qt2160);
if (error) {
dev_err(&client->dev, "Failed to register leds\n");
goto err_free_irq;
}
error = input_register_device(qt2160->input); error = input_register_device(qt2160->input);
if (error) { if (error) {
dev_err(&client->dev, dev_err(&client->dev,
"Failed to register input device\n"); "Failed to register input device\n");
goto err_free_irq; goto err_unregister_leds;
} }
i2c_set_clientdata(client, qt2160); i2c_set_clientdata(client, qt2160);
@ -326,6 +459,8 @@ static int qt2160_probe(struct i2c_client *client,
return 0; return 0;
err_unregister_leds:
qt2160_unregister_leds(qt2160);
err_free_irq: err_free_irq:
if (client->irq) if (client->irq)
free_irq(client->irq, qt2160); free_irq(client->irq, qt2160);
@ -339,6 +474,8 @@ static int qt2160_remove(struct i2c_client *client)
{ {
struct qt2160_data *qt2160 = i2c_get_clientdata(client); struct qt2160_data *qt2160 = i2c_get_clientdata(client);
qt2160_unregister_leds(qt2160);
/* Release IRQ so no queue will be scheduled */ /* Release IRQ so no queue will be scheduled */
if (client->irq) if (client->irq)
free_irq(client->irq, qt2160); free_irq(client->irq, qt2160);

View File

@ -29,9 +29,16 @@
#include <linux/of.h> #include <linux/of.h>
#include <linux/clk.h> #include <linux/clk.h>
#include <linux/slab.h> #include <linux/slab.h>
#include <linux/input/tegra_kbc.h> #include <linux/input/matrix_keypad.h>
#include <mach/clk.h> #include <mach/clk.h>
#define KBC_MAX_GPIO 24
#define KBC_MAX_KPENT 8
#define KBC_MAX_ROW 16
#define KBC_MAX_COL 8
#define KBC_MAX_KEY (KBC_MAX_ROW * KBC_MAX_COL)
#define KBC_MAX_DEBOUNCE_CNT 0x3ffu #define KBC_MAX_DEBOUNCE_CNT 0x3ffu
/* KBC row scan time and delay for beginning the row scan. */ /* KBC row scan time and delay for beginning the row scan. */
@ -67,10 +74,27 @@
#define KBC_ROW_SHIFT 3 #define KBC_ROW_SHIFT 3
enum tegra_pin_type {
PIN_CFG_IGNORE,
PIN_CFG_COL,
PIN_CFG_ROW,
};
struct tegra_kbc_pin_cfg {
enum tegra_pin_type type;
unsigned char num;
};
struct tegra_kbc { struct tegra_kbc {
struct device *dev;
unsigned int debounce_cnt;
unsigned int repeat_cnt;
struct tegra_kbc_pin_cfg pin_cfg[KBC_MAX_GPIO];
const struct matrix_keymap_data *keymap_data;
bool wakeup;
void __iomem *mmio; void __iomem *mmio;
struct input_dev *idev; struct input_dev *idev;
unsigned int irq; int irq;
spinlock_t lock; spinlock_t lock;
unsigned int repoll_dly; unsigned int repoll_dly;
unsigned long cp_dly_jiffies; unsigned long cp_dly_jiffies;
@ -78,7 +102,6 @@ struct tegra_kbc {
bool use_fn_map; bool use_fn_map;
bool use_ghost_filter; bool use_ghost_filter;
bool keypress_caused_wake; bool keypress_caused_wake;
const struct tegra_kbc_platform_data *pdata;
unsigned short keycode[KBC_MAX_KEY * 2]; unsigned short keycode[KBC_MAX_KEY * 2];
unsigned short current_keys[KBC_MAX_KPENT]; unsigned short current_keys[KBC_MAX_KPENT];
unsigned int num_pressed_keys; unsigned int num_pressed_keys;
@ -87,147 +110,6 @@ struct tegra_kbc {
struct clk *clk; struct clk *clk;
}; };
static const u32 tegra_kbc_default_keymap[] = {
KEY(0, 2, KEY_W),
KEY(0, 3, KEY_S),
KEY(0, 4, KEY_A),
KEY(0, 5, KEY_Z),
KEY(0, 7, KEY_FN),
KEY(1, 7, KEY_LEFTMETA),
KEY(2, 6, KEY_RIGHTALT),
KEY(2, 7, KEY_LEFTALT),
KEY(3, 0, KEY_5),
KEY(3, 1, KEY_4),
KEY(3, 2, KEY_R),
KEY(3, 3, KEY_E),
KEY(3, 4, KEY_F),
KEY(3, 5, KEY_D),
KEY(3, 6, KEY_X),
KEY(4, 0, KEY_7),
KEY(4, 1, KEY_6),
KEY(4, 2, KEY_T),
KEY(4, 3, KEY_H),
KEY(4, 4, KEY_G),
KEY(4, 5, KEY_V),
KEY(4, 6, KEY_C),
KEY(4, 7, KEY_SPACE),
KEY(5, 0, KEY_9),
KEY(5, 1, KEY_8),
KEY(5, 2, KEY_U),
KEY(5, 3, KEY_Y),
KEY(5, 4, KEY_J),
KEY(5, 5, KEY_N),
KEY(5, 6, KEY_B),
KEY(5, 7, KEY_BACKSLASH),
KEY(6, 0, KEY_MINUS),
KEY(6, 1, KEY_0),
KEY(6, 2, KEY_O),
KEY(6, 3, KEY_I),
KEY(6, 4, KEY_L),
KEY(6, 5, KEY_K),
KEY(6, 6, KEY_COMMA),
KEY(6, 7, KEY_M),
KEY(7, 1, KEY_EQUAL),
KEY(7, 2, KEY_RIGHTBRACE),
KEY(7, 3, KEY_ENTER),
KEY(7, 7, KEY_MENU),
KEY(8, 4, KEY_RIGHTSHIFT),
KEY(8, 5, KEY_LEFTSHIFT),
KEY(9, 5, KEY_RIGHTCTRL),
KEY(9, 7, KEY_LEFTCTRL),
KEY(11, 0, KEY_LEFTBRACE),
KEY(11, 1, KEY_P),
KEY(11, 2, KEY_APOSTROPHE),
KEY(11, 3, KEY_SEMICOLON),
KEY(11, 4, KEY_SLASH),
KEY(11, 5, KEY_DOT),
KEY(12, 0, KEY_F10),
KEY(12, 1, KEY_F9),
KEY(12, 2, KEY_BACKSPACE),
KEY(12, 3, KEY_3),
KEY(12, 4, KEY_2),
KEY(12, 5, KEY_UP),
KEY(12, 6, KEY_PRINT),
KEY(12, 7, KEY_PAUSE),
KEY(13, 0, KEY_INSERT),
KEY(13, 1, KEY_DELETE),
KEY(13, 3, KEY_PAGEUP),
KEY(13, 4, KEY_PAGEDOWN),
KEY(13, 5, KEY_RIGHT),
KEY(13, 6, KEY_DOWN),
KEY(13, 7, KEY_LEFT),
KEY(14, 0, KEY_F11),
KEY(14, 1, KEY_F12),
KEY(14, 2, KEY_F8),
KEY(14, 3, KEY_Q),
KEY(14, 4, KEY_F4),
KEY(14, 5, KEY_F3),
KEY(14, 6, KEY_1),
KEY(14, 7, KEY_F7),
KEY(15, 0, KEY_ESC),
KEY(15, 1, KEY_GRAVE),
KEY(15, 2, KEY_F5),
KEY(15, 3, KEY_TAB),
KEY(15, 4, KEY_F1),
KEY(15, 5, KEY_F2),
KEY(15, 6, KEY_CAPSLOCK),
KEY(15, 7, KEY_F6),
/* Software Handled Function Keys */
KEY(20, 0, KEY_KP7),
KEY(21, 0, KEY_KP9),
KEY(21, 1, KEY_KP8),
KEY(21, 2, KEY_KP4),
KEY(21, 4, KEY_KP1),
KEY(22, 1, KEY_KPSLASH),
KEY(22, 2, KEY_KP6),
KEY(22, 3, KEY_KP5),
KEY(22, 4, KEY_KP3),
KEY(22, 5, KEY_KP2),
KEY(22, 7, KEY_KP0),
KEY(27, 1, KEY_KPASTERISK),
KEY(27, 3, KEY_KPMINUS),
KEY(27, 4, KEY_KPPLUS),
KEY(27, 5, KEY_KPDOT),
KEY(28, 5, KEY_VOLUMEUP),
KEY(29, 3, KEY_HOME),
KEY(29, 4, KEY_END),
KEY(29, 5, KEY_BRIGHTNESSDOWN),
KEY(29, 6, KEY_VOLUMEDOWN),
KEY(29, 7, KEY_BRIGHTNESSUP),
KEY(30, 0, KEY_NUMLOCK),
KEY(30, 1, KEY_SCROLLLOCK),
KEY(30, 2, KEY_MUTE),
KEY(31, 4, KEY_HELP),
};
static const
struct matrix_keymap_data tegra_kbc_default_keymap_data = {
.keymap = tegra_kbc_default_keymap,
.keymap_size = ARRAY_SIZE(tegra_kbc_default_keymap),
};
static void tegra_kbc_report_released_keys(struct input_dev *input, static void tegra_kbc_report_released_keys(struct input_dev *input,
unsigned short old_keycodes[], unsigned short old_keycodes[],
unsigned int old_num_keys, unsigned int old_num_keys,
@ -357,18 +239,6 @@ static void tegra_kbc_set_fifo_interrupt(struct tegra_kbc *kbc, bool enable)
writel(val, kbc->mmio + KBC_CONTROL_0); writel(val, kbc->mmio + KBC_CONTROL_0);
} }
static void tegra_kbc_set_keypress_interrupt(struct tegra_kbc *kbc, bool enable)
{
u32 val;
val = readl(kbc->mmio + KBC_CONTROL_0);
if (enable)
val |= KBC_CONTROL_KEYPRESS_INT_EN;
else
val &= ~KBC_CONTROL_KEYPRESS_INT_EN;
writel(val, kbc->mmio + KBC_CONTROL_0);
}
static void tegra_kbc_keypress_timer(unsigned long data) static void tegra_kbc_keypress_timer(unsigned long data)
{ {
struct tegra_kbc *kbc = (struct tegra_kbc *)data; struct tegra_kbc *kbc = (struct tegra_kbc *)data;
@ -439,12 +309,11 @@ static irqreturn_t tegra_kbc_isr(int irq, void *args)
static void tegra_kbc_setup_wakekeys(struct tegra_kbc *kbc, bool filter) static void tegra_kbc_setup_wakekeys(struct tegra_kbc *kbc, bool filter)
{ {
const struct tegra_kbc_platform_data *pdata = kbc->pdata;
int i; int i;
unsigned int rst_val; unsigned int rst_val;
/* Either mask all keys or none. */ /* Either mask all keys or none. */
rst_val = (filter && !pdata->wakeup) ? ~0 : 0; rst_val = (filter && !kbc->wakeup) ? ~0 : 0;
for (i = 0; i < KBC_MAX_ROW; i++) for (i = 0; i < KBC_MAX_ROW; i++)
writel(rst_val, kbc->mmio + KBC_ROW0_MASK_0 + i * 4); writel(rst_val, kbc->mmio + KBC_ROW0_MASK_0 + i * 4);
@ -452,7 +321,6 @@ static void tegra_kbc_setup_wakekeys(struct tegra_kbc *kbc, bool filter)
static void tegra_kbc_config_pins(struct tegra_kbc *kbc) static void tegra_kbc_config_pins(struct tegra_kbc *kbc)
{ {
const struct tegra_kbc_platform_data *pdata = kbc->pdata;
int i; int i;
for (i = 0; i < KBC_MAX_GPIO; i++) { for (i = 0; i < KBC_MAX_GPIO; i++) {
@ -468,13 +336,13 @@ static void tegra_kbc_config_pins(struct tegra_kbc *kbc)
row_cfg &= ~r_mask; row_cfg &= ~r_mask;
col_cfg &= ~c_mask; col_cfg &= ~c_mask;
switch (pdata->pin_cfg[i].type) { switch (kbc->pin_cfg[i].type) {
case PIN_CFG_ROW: case PIN_CFG_ROW:
row_cfg |= ((pdata->pin_cfg[i].num << 1) | 1) << r_shft; row_cfg |= ((kbc->pin_cfg[i].num << 1) | 1) << r_shft;
break; break;
case PIN_CFG_COL: case PIN_CFG_COL:
col_cfg |= ((pdata->pin_cfg[i].num << 1) | 1) << c_shft; col_cfg |= ((kbc->pin_cfg[i].num << 1) | 1) << c_shft;
break; break;
case PIN_CFG_IGNORE: case PIN_CFG_IGNORE:
@ -488,7 +356,6 @@ static void tegra_kbc_config_pins(struct tegra_kbc *kbc)
static int tegra_kbc_start(struct tegra_kbc *kbc) static int tegra_kbc_start(struct tegra_kbc *kbc)
{ {
const struct tegra_kbc_platform_data *pdata = kbc->pdata;
unsigned int debounce_cnt; unsigned int debounce_cnt;
u32 val = 0; u32 val = 0;
@ -503,10 +370,10 @@ static int tegra_kbc_start(struct tegra_kbc *kbc)
tegra_kbc_config_pins(kbc); tegra_kbc_config_pins(kbc);
tegra_kbc_setup_wakekeys(kbc, false); tegra_kbc_setup_wakekeys(kbc, false);
writel(pdata->repeat_cnt, kbc->mmio + KBC_RPT_DLY_0); writel(kbc->repeat_cnt, kbc->mmio + KBC_RPT_DLY_0);
/* Keyboard debounce count is maximum of 12 bits. */ /* Keyboard debounce count is maximum of 12 bits. */
debounce_cnt = min(pdata->debounce_cnt, KBC_MAX_DEBOUNCE_CNT); debounce_cnt = min(kbc->debounce_cnt, KBC_MAX_DEBOUNCE_CNT);
val = KBC_DEBOUNCE_CNT_SHIFT(debounce_cnt); val = KBC_DEBOUNCE_CNT_SHIFT(debounce_cnt);
val |= KBC_FIFO_TH_CNT_SHIFT(1); /* set fifo interrupt threshold to 1 */ val |= KBC_FIFO_TH_CNT_SHIFT(1); /* set fifo interrupt threshold to 1 */
val |= KBC_CONTROL_FIFO_CNT_INT_EN; /* interrupt on FIFO threshold */ val |= KBC_CONTROL_FIFO_CNT_INT_EN; /* interrupt on FIFO threshold */
@ -573,21 +440,20 @@ static void tegra_kbc_close(struct input_dev *dev)
return tegra_kbc_stop(kbc); return tegra_kbc_stop(kbc);
} }
static bool static bool tegra_kbc_check_pin_cfg(const struct tegra_kbc *kbc,
tegra_kbc_check_pin_cfg(const struct tegra_kbc_platform_data *pdata, unsigned int *num_rows)
struct device *dev, unsigned int *num_rows)
{ {
int i; int i;
*num_rows = 0; *num_rows = 0;
for (i = 0; i < KBC_MAX_GPIO; i++) { for (i = 0; i < KBC_MAX_GPIO; i++) {
const struct tegra_kbc_pin_cfg *pin_cfg = &pdata->pin_cfg[i]; const struct tegra_kbc_pin_cfg *pin_cfg = &kbc->pin_cfg[i];
switch (pin_cfg->type) { switch (pin_cfg->type) {
case PIN_CFG_ROW: case PIN_CFG_ROW:
if (pin_cfg->num >= KBC_MAX_ROW) { if (pin_cfg->num >= KBC_MAX_ROW) {
dev_err(dev, dev_err(kbc->dev,
"pin_cfg[%d]: invalid row number %d\n", "pin_cfg[%d]: invalid row number %d\n",
i, pin_cfg->num); i, pin_cfg->num);
return false; return false;
@ -597,7 +463,7 @@ tegra_kbc_check_pin_cfg(const struct tegra_kbc_platform_data *pdata,
case PIN_CFG_COL: case PIN_CFG_COL:
if (pin_cfg->num >= KBC_MAX_COL) { if (pin_cfg->num >= KBC_MAX_COL) {
dev_err(dev, dev_err(kbc->dev,
"pin_cfg[%d]: invalid column number %d\n", "pin_cfg[%d]: invalid column number %d\n",
i, pin_cfg->num); i, pin_cfg->num);
return false; return false;
@ -608,7 +474,7 @@ tegra_kbc_check_pin_cfg(const struct tegra_kbc_platform_data *pdata,
break; break;
default: default:
dev_err(dev, dev_err(kbc->dev,
"pin_cfg[%d]: invalid entry type %d\n", "pin_cfg[%d]: invalid entry type %d\n",
pin_cfg->type, pin_cfg->num); pin_cfg->type, pin_cfg->num);
return false; return false;
@ -618,154 +484,140 @@ tegra_kbc_check_pin_cfg(const struct tegra_kbc_platform_data *pdata,
return true; return true;
} }
#ifdef CONFIG_OF static int tegra_kbc_parse_dt(struct tegra_kbc *kbc)
static struct tegra_kbc_platform_data *tegra_kbc_dt_parse_pdata(
struct platform_device *pdev)
{ {
struct tegra_kbc_platform_data *pdata; struct device_node *np = kbc->dev->of_node;
struct device_node *np = pdev->dev.of_node;
u32 prop; u32 prop;
int i; int i;
u32 num_rows = 0;
if (!np) u32 num_cols = 0;
return NULL; u32 cols_cfg[KBC_MAX_GPIO];
u32 rows_cfg[KBC_MAX_GPIO];
pdata = kzalloc(sizeof(*pdata), GFP_KERNEL); int proplen;
if (!pdata) int ret;
return NULL;
if (!of_property_read_u32(np, "nvidia,debounce-delay-ms", &prop)) if (!of_property_read_u32(np, "nvidia,debounce-delay-ms", &prop))
pdata->debounce_cnt = prop; kbc->debounce_cnt = prop;
if (!of_property_read_u32(np, "nvidia,repeat-delay-ms", &prop)) if (!of_property_read_u32(np, "nvidia,repeat-delay-ms", &prop))
pdata->repeat_cnt = prop; kbc->repeat_cnt = prop;
if (of_find_property(np, "nvidia,needs-ghost-filter", NULL)) if (of_find_property(np, "nvidia,needs-ghost-filter", NULL))
pdata->use_ghost_filter = true; kbc->use_ghost_filter = true;
if (of_find_property(np, "nvidia,wakeup-source", NULL)) if (of_find_property(np, "nvidia,wakeup-source", NULL))
pdata->wakeup = true; kbc->wakeup = true;
/* if (!of_get_property(np, "nvidia,kbc-row-pins", &proplen)) {
* All currently known keymaps with device tree support use the same dev_err(kbc->dev, "property nvidia,kbc-row-pins not found\n");
* pin_cfg, so set it up here. return -ENOENT;
*/ }
for (i = 0; i < KBC_MAX_ROW; i++) { num_rows = proplen / sizeof(u32);
pdata->pin_cfg[i].num = i;
pdata->pin_cfg[i].type = PIN_CFG_ROW; if (!of_get_property(np, "nvidia,kbc-col-pins", &proplen)) {
dev_err(kbc->dev, "property nvidia,kbc-col-pins not found\n");
return -ENOENT;
}
num_cols = proplen / sizeof(u32);
if (!of_get_property(np, "linux,keymap", &proplen)) {
dev_err(kbc->dev, "property linux,keymap not found\n");
return -ENOENT;
} }
for (i = 0; i < KBC_MAX_COL; i++) { if (!num_rows || !num_cols || ((num_rows + num_cols) > KBC_MAX_GPIO)) {
pdata->pin_cfg[KBC_MAX_ROW + i].num = i; dev_err(kbc->dev,
pdata->pin_cfg[KBC_MAX_ROW + i].type = PIN_CFG_COL; "keypad rows/columns not porperly specified\n");
return -EINVAL;
} }
return pdata; /* Set all pins as non-configured */
} for (i = 0; i < KBC_MAX_GPIO; i++)
#else kbc->pin_cfg[i].type = PIN_CFG_IGNORE;
static inline struct tegra_kbc_platform_data *tegra_kbc_dt_parse_pdata(
struct platform_device *pdev)
{
return NULL;
}
#endif
static int tegra_kbd_setup_keymap(struct tegra_kbc *kbc) ret = of_property_read_u32_array(np, "nvidia,kbc-row-pins",
{ rows_cfg, num_rows);
const struct tegra_kbc_platform_data *pdata = kbc->pdata; if (ret < 0) {
const struct matrix_keymap_data *keymap_data = pdata->keymap_data; dev_err(kbc->dev, "Rows configurations are not proper\n");
unsigned int keymap_rows = KBC_MAX_KEY; return -EINVAL;
int retval;
if (keymap_data && pdata->use_fn_map)
keymap_rows *= 2;
retval = matrix_keypad_build_keymap(keymap_data, NULL,
keymap_rows, KBC_MAX_COL,
kbc->keycode, kbc->idev);
if (retval == -ENOSYS || retval == -ENOENT) {
/*
* If there is no OF support in kernel or keymap
* property is missing, use default keymap.
*/
retval = matrix_keypad_build_keymap(
&tegra_kbc_default_keymap_data, NULL,
keymap_rows, KBC_MAX_COL,
kbc->keycode, kbc->idev);
} }
return retval; ret = of_property_read_u32_array(np, "nvidia,kbc-col-pins",
cols_cfg, num_cols);
if (ret < 0) {
dev_err(kbc->dev, "Cols configurations are not proper\n");
return -EINVAL;
}
for (i = 0; i < num_rows; i++) {
kbc->pin_cfg[rows_cfg[i]].type = PIN_CFG_ROW;
kbc->pin_cfg[rows_cfg[i]].num = i;
}
for (i = 0; i < num_cols; i++) {
kbc->pin_cfg[cols_cfg[i]].type = PIN_CFG_COL;
kbc->pin_cfg[cols_cfg[i]].num = i;
}
return 0;
} }
static int tegra_kbc_probe(struct platform_device *pdev) static int tegra_kbc_probe(struct platform_device *pdev)
{ {
const struct tegra_kbc_platform_data *pdata = pdev->dev.platform_data;
struct tegra_kbc *kbc; struct tegra_kbc *kbc;
struct input_dev *input_dev;
struct resource *res; struct resource *res;
int irq;
int err; int err;
int num_rows = 0; int num_rows = 0;
unsigned int debounce_cnt; unsigned int debounce_cnt;
unsigned int scan_time_rows; unsigned int scan_time_rows;
unsigned int keymap_rows = KBC_MAX_KEY;
if (!pdata) kbc = devm_kzalloc(&pdev->dev, sizeof(*kbc), GFP_KERNEL);
pdata = tegra_kbc_dt_parse_pdata(pdev); if (!kbc) {
dev_err(&pdev->dev, "failed to alloc memory for kbc\n");
if (!pdata) return -ENOMEM;
return -EINVAL;
if (!tegra_kbc_check_pin_cfg(pdata, &pdev->dev, &num_rows)) {
err = -EINVAL;
goto err_free_pdata;
} }
kbc->dev = &pdev->dev;
spin_lock_init(&kbc->lock);
err = tegra_kbc_parse_dt(kbc);
if (err)
return err;
if (!tegra_kbc_check_pin_cfg(kbc, &num_rows))
return -EINVAL;
res = platform_get_resource(pdev, IORESOURCE_MEM, 0); res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
if (!res) { if (!res) {
dev_err(&pdev->dev, "failed to get I/O memory\n"); dev_err(&pdev->dev, "failed to get I/O memory\n");
err = -ENXIO; return -ENXIO;
goto err_free_pdata;
} }
irq = platform_get_irq(pdev, 0); kbc->irq = platform_get_irq(pdev, 0);
if (irq < 0) { if (kbc->irq < 0) {
dev_err(&pdev->dev, "failed to get keyboard IRQ\n"); dev_err(&pdev->dev, "failed to get keyboard IRQ\n");
err = -ENXIO; return -ENXIO;
goto err_free_pdata;
} }
kbc = kzalloc(sizeof(*kbc), GFP_KERNEL); kbc->idev = devm_input_allocate_device(&pdev->dev);
input_dev = input_allocate_device(); if (!kbc->idev) {
if (!kbc || !input_dev) { dev_err(&pdev->dev, "failed to allocate input device\n");
err = -ENOMEM; return -ENOMEM;
goto err_free_mem;
} }
kbc->pdata = pdata;
kbc->idev = input_dev;
kbc->irq = irq;
spin_lock_init(&kbc->lock);
setup_timer(&kbc->timer, tegra_kbc_keypress_timer, (unsigned long)kbc); setup_timer(&kbc->timer, tegra_kbc_keypress_timer, (unsigned long)kbc);
res = request_mem_region(res->start, resource_size(res), pdev->name); kbc->mmio = devm_request_and_ioremap(&pdev->dev, res);
if (!res) {
dev_err(&pdev->dev, "failed to request I/O memory\n");
err = -EBUSY;
goto err_free_mem;
}
kbc->mmio = ioremap(res->start, resource_size(res));
if (!kbc->mmio) { if (!kbc->mmio) {
dev_err(&pdev->dev, "failed to remap I/O memory\n"); dev_err(&pdev->dev, "Cannot request memregion/iomap address\n");
err = -ENXIO; return -EBUSY;
goto err_free_mem_region;
} }
kbc->clk = clk_get(&pdev->dev, NULL); kbc->clk = devm_clk_get(&pdev->dev, NULL);
if (IS_ERR(kbc->clk)) { if (IS_ERR(kbc->clk)) {
dev_err(&pdev->dev, "failed to get keyboard clock\n"); dev_err(&pdev->dev, "failed to get keyboard clock\n");
err = PTR_ERR(kbc->clk); return PTR_ERR(kbc->clk);
goto err_iounmap;
} }
/* /*
@ -774,37 +626,38 @@ static int tegra_kbc_probe(struct platform_device *pdev)
* the rows. There is an additional delay before the row scanning * the rows. There is an additional delay before the row scanning
* starts. The repoll delay is computed in milliseconds. * starts. The repoll delay is computed in milliseconds.
*/ */
debounce_cnt = min(pdata->debounce_cnt, KBC_MAX_DEBOUNCE_CNT); debounce_cnt = min(kbc->debounce_cnt, KBC_MAX_DEBOUNCE_CNT);
scan_time_rows = (KBC_ROW_SCAN_TIME + debounce_cnt) * num_rows; scan_time_rows = (KBC_ROW_SCAN_TIME + debounce_cnt) * num_rows;
kbc->repoll_dly = KBC_ROW_SCAN_DLY + scan_time_rows + pdata->repeat_cnt; kbc->repoll_dly = KBC_ROW_SCAN_DLY + scan_time_rows + kbc->repeat_cnt;
kbc->repoll_dly = DIV_ROUND_UP(kbc->repoll_dly, KBC_CYCLE_MS); kbc->repoll_dly = DIV_ROUND_UP(kbc->repoll_dly, KBC_CYCLE_MS);
kbc->wakeup_key = pdata->wakeup_key; kbc->idev->name = pdev->name;
kbc->use_fn_map = pdata->use_fn_map; kbc->idev->id.bustype = BUS_HOST;
kbc->use_ghost_filter = pdata->use_ghost_filter; kbc->idev->dev.parent = &pdev->dev;
kbc->idev->open = tegra_kbc_open;
kbc->idev->close = tegra_kbc_close;
input_dev->name = pdev->name; if (kbc->keymap_data && kbc->use_fn_map)
input_dev->id.bustype = BUS_HOST; keymap_rows *= 2;
input_dev->dev.parent = &pdev->dev;
input_dev->open = tegra_kbc_open;
input_dev->close = tegra_kbc_close;
err = tegra_kbd_setup_keymap(kbc); err = matrix_keypad_build_keymap(kbc->keymap_data, NULL,
keymap_rows, KBC_MAX_COL,
kbc->keycode, kbc->idev);
if (err) { if (err) {
dev_err(&pdev->dev, "failed to setup keymap\n"); dev_err(&pdev->dev, "failed to setup keymap\n");
goto err_put_clk; return err;
} }
__set_bit(EV_REP, input_dev->evbit); __set_bit(EV_REP, kbc->idev->evbit);
input_set_capability(input_dev, EV_MSC, MSC_SCAN); input_set_capability(kbc->idev, EV_MSC, MSC_SCAN);
input_set_drvdata(input_dev, kbc); input_set_drvdata(kbc->idev, kbc);
err = request_irq(kbc->irq, tegra_kbc_isr, err = devm_request_irq(&pdev->dev, kbc->irq, tegra_kbc_isr,
IRQF_NO_SUSPEND | IRQF_TRIGGER_HIGH, pdev->name, kbc); IRQF_NO_SUSPEND | IRQF_TRIGGER_HIGH, pdev->name, kbc);
if (err) { if (err) {
dev_err(&pdev->dev, "failed to request keyboard IRQ\n"); dev_err(&pdev->dev, "failed to request keyboard IRQ\n");
goto err_put_clk; return err;
} }
disable_irq(kbc->irq); disable_irq(kbc->irq);
@ -812,60 +665,28 @@ static int tegra_kbc_probe(struct platform_device *pdev)
err = input_register_device(kbc->idev); err = input_register_device(kbc->idev);
if (err) { if (err) {
dev_err(&pdev->dev, "failed to register input device\n"); dev_err(&pdev->dev, "failed to register input device\n");
goto err_free_irq; return err;
} }
platform_set_drvdata(pdev, kbc); platform_set_drvdata(pdev, kbc);
device_init_wakeup(&pdev->dev, pdata->wakeup); device_init_wakeup(&pdev->dev, kbc->wakeup);
return 0;
err_free_irq:
free_irq(kbc->irq, pdev);
err_put_clk:
clk_put(kbc->clk);
err_iounmap:
iounmap(kbc->mmio);
err_free_mem_region:
release_mem_region(res->start, resource_size(res));
err_free_mem:
input_free_device(input_dev);
kfree(kbc);
err_free_pdata:
if (!pdev->dev.platform_data)
kfree(pdata);
return err;
}
static int tegra_kbc_remove(struct platform_device *pdev)
{
struct tegra_kbc *kbc = platform_get_drvdata(pdev);
struct resource *res;
platform_set_drvdata(pdev, NULL);
free_irq(kbc->irq, pdev);
clk_put(kbc->clk);
input_unregister_device(kbc->idev);
iounmap(kbc->mmio);
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
release_mem_region(res->start, resource_size(res));
/*
* If we do not have platform data attached to the device we
* allocated it ourselves and thus need to free it.
*/
if (!pdev->dev.platform_data)
kfree(kbc->pdata);
kfree(kbc);
return 0; return 0;
} }
#ifdef CONFIG_PM_SLEEP #ifdef CONFIG_PM_SLEEP
static void tegra_kbc_set_keypress_interrupt(struct tegra_kbc *kbc, bool enable)
{
u32 val;
val = readl(kbc->mmio + KBC_CONTROL_0);
if (enable)
val |= KBC_CONTROL_KEYPRESS_INT_EN;
else
val &= ~KBC_CONTROL_KEYPRESS_INT_EN;
writel(val, kbc->mmio + KBC_CONTROL_0);
}
static int tegra_kbc_suspend(struct device *dev) static int tegra_kbc_suspend(struct device *dev)
{ {
struct platform_device *pdev = to_platform_device(dev); struct platform_device *pdev = to_platform_device(dev);
@ -954,7 +775,6 @@ MODULE_DEVICE_TABLE(of, tegra_kbc_of_match);
static struct platform_driver tegra_kbc_driver = { static struct platform_driver tegra_kbc_driver = {
.probe = tegra_kbc_probe, .probe = tegra_kbc_probe,
.remove = tegra_kbc_remove,
.driver = { .driver = {
.name = "tegra-kbc", .name = "tegra-kbc",
.owner = THIS_MODULE, .owner = THIS_MODULE,

View File

@ -232,7 +232,7 @@ static const struct adxl34x_platform_data adxl34x_default_init = {
.ev_code_tap = {BTN_TOUCH, BTN_TOUCH, BTN_TOUCH}, /* EV_KEY {x,y,z} */ .ev_code_tap = {BTN_TOUCH, BTN_TOUCH, BTN_TOUCH}, /* EV_KEY {x,y,z} */
.power_mode = ADXL_AUTO_SLEEP | ADXL_LINK, .power_mode = ADXL_AUTO_SLEEP | ADXL_LINK,
.fifo_mode = FIFO_STREAM, .fifo_mode = ADXL_FIFO_STREAM,
.watermark = 0, .watermark = 0,
}; };
@ -732,7 +732,7 @@ struct adxl34x *adxl34x_probe(struct device *dev, int irq,
mutex_init(&ac->mutex); mutex_init(&ac->mutex);
input_dev->name = "ADXL34x accelerometer"; input_dev->name = "ADXL34x accelerometer";
revid = ac->bops->read(dev, DEVID); revid = AC_READ(ac, DEVID);
switch (revid) { switch (revid) {
case ID_ADXL345: case ID_ADXL345:
@ -809,7 +809,7 @@ struct adxl34x *adxl34x_probe(struct device *dev, int irq,
if (FIFO_MODE(pdata->fifo_mode) == FIFO_BYPASS) if (FIFO_MODE(pdata->fifo_mode) == FIFO_BYPASS)
ac->fifo_delay = false; ac->fifo_delay = false;
ac->bops->write(dev, POWER_CTL, 0); AC_WRITE(ac, POWER_CTL, 0);
err = request_threaded_irq(ac->irq, NULL, adxl34x_irq, err = request_threaded_irq(ac->irq, NULL, adxl34x_irq,
IRQF_TRIGGER_HIGH | IRQF_ONESHOT, IRQF_TRIGGER_HIGH | IRQF_ONESHOT,
@ -827,7 +827,6 @@ struct adxl34x *adxl34x_probe(struct device *dev, int irq,
if (err) if (err)
goto err_remove_attr; goto err_remove_attr;
AC_WRITE(ac, THRESH_TAP, pdata->tap_threshold);
AC_WRITE(ac, OFSX, pdata->x_axis_offset); AC_WRITE(ac, OFSX, pdata->x_axis_offset);
ac->hwcal.x = pdata->x_axis_offset; ac->hwcal.x = pdata->x_axis_offset;
AC_WRITE(ac, OFSY, pdata->y_axis_offset); AC_WRITE(ac, OFSY, pdata->y_axis_offset);

View File

@ -46,18 +46,6 @@
#define BMA150_POLL_MAX 200 #define BMA150_POLL_MAX 200
#define BMA150_POLL_MIN 0 #define BMA150_POLL_MIN 0
#define BMA150_BW_25HZ 0
#define BMA150_BW_50HZ 1
#define BMA150_BW_100HZ 2
#define BMA150_BW_190HZ 3
#define BMA150_BW_375HZ 4
#define BMA150_BW_750HZ 5
#define BMA150_BW_1500HZ 6
#define BMA150_RANGE_2G 0
#define BMA150_RANGE_4G 1
#define BMA150_RANGE_8G 2
#define BMA150_MODE_NORMAL 0 #define BMA150_MODE_NORMAL 0
#define BMA150_MODE_SLEEP 2 #define BMA150_MODE_SLEEP 2
#define BMA150_MODE_WAKE_UP 3 #define BMA150_MODE_WAKE_UP 3
@ -372,7 +360,7 @@ static int bma150_open(struct bma150_data *bma150)
int error; int error;
error = pm_runtime_get_sync(&bma150->client->dev); error = pm_runtime_get_sync(&bma150->client->dev);
if (error && error != -ENOSYS) if (error < 0 && error != -ENOSYS)
return error; return error;
/* /*

View File

@ -43,7 +43,6 @@ struct vibra_info {
struct device *dev; struct device *dev;
struct input_dev *input_dev; struct input_dev *input_dev;
struct workqueue_struct *workqueue;
struct work_struct play_work; struct work_struct play_work;
bool enabled; bool enabled;
@ -143,19 +142,7 @@ static int vibra_play(struct input_dev *input, void *data,
if (!info->speed) if (!info->speed)
info->speed = effect->u.rumble.weak_magnitude >> 9; info->speed = effect->u.rumble.weak_magnitude >> 9;
info->direction = effect->direction < EFFECT_DIR_180_DEG ? 0 : 1; info->direction = effect->direction < EFFECT_DIR_180_DEG ? 0 : 1;
queue_work(info->workqueue, &info->play_work); schedule_work(&info->play_work);
return 0;
}
static int twl4030_vibra_open(struct input_dev *input)
{
struct vibra_info *info = input_get_drvdata(input);
info->workqueue = create_singlethread_workqueue("vibra");
if (info->workqueue == NULL) {
dev_err(&input->dev, "couldn't create workqueue\n");
return -ENOMEM;
}
return 0; return 0;
} }
@ -164,9 +151,6 @@ static void twl4030_vibra_close(struct input_dev *input)
struct vibra_info *info = input_get_drvdata(input); struct vibra_info *info = input_get_drvdata(input);
cancel_work_sync(&info->play_work); cancel_work_sync(&info->play_work);
INIT_WORK(&info->play_work, vibra_play_work); /* cleanup */
destroy_workqueue(info->workqueue);
info->workqueue = NULL;
if (info->enabled) if (info->enabled)
vibra_disable(info); vibra_disable(info);
@ -219,7 +203,7 @@ static int twl4030_vibra_probe(struct platform_device *pdev)
return -EINVAL; return -EINVAL;
} }
info = kzalloc(sizeof(*info), GFP_KERNEL); info = devm_kzalloc(&pdev->dev, sizeof(*info), GFP_KERNEL);
if (!info) if (!info)
return -ENOMEM; return -ENOMEM;
@ -227,11 +211,10 @@ static int twl4030_vibra_probe(struct platform_device *pdev)
info->coexist = twl4030_vibra_check_coexist(pdata, twl4030_core_node); info->coexist = twl4030_vibra_check_coexist(pdata, twl4030_core_node);
INIT_WORK(&info->play_work, vibra_play_work); INIT_WORK(&info->play_work, vibra_play_work);
info->input_dev = input_allocate_device(); info->input_dev = devm_input_allocate_device(&pdev->dev);
if (info->input_dev == NULL) { if (info->input_dev == NULL) {
dev_err(&pdev->dev, "couldn't allocate input device\n"); dev_err(&pdev->dev, "couldn't allocate input device\n");
ret = -ENOMEM; return -ENOMEM;
goto err_kzalloc;
} }
input_set_drvdata(info->input_dev, info); input_set_drvdata(info->input_dev, info);
@ -239,14 +222,13 @@ static int twl4030_vibra_probe(struct platform_device *pdev)
info->input_dev->name = "twl4030:vibrator"; info->input_dev->name = "twl4030:vibrator";
info->input_dev->id.version = 1; info->input_dev->id.version = 1;
info->input_dev->dev.parent = pdev->dev.parent; info->input_dev->dev.parent = pdev->dev.parent;
info->input_dev->open = twl4030_vibra_open;
info->input_dev->close = twl4030_vibra_close; info->input_dev->close = twl4030_vibra_close;
__set_bit(FF_RUMBLE, info->input_dev->ffbit); __set_bit(FF_RUMBLE, info->input_dev->ffbit);
ret = input_ff_create_memless(info->input_dev, NULL, vibra_play); ret = input_ff_create_memless(info->input_dev, NULL, vibra_play);
if (ret < 0) { if (ret < 0) {
dev_dbg(&pdev->dev, "couldn't register vibrator to FF\n"); dev_dbg(&pdev->dev, "couldn't register vibrator to FF\n");
goto err_ialloc; return ret;
} }
ret = input_register_device(info->input_dev); ret = input_register_device(info->input_dev);
@ -262,28 +244,11 @@ static int twl4030_vibra_probe(struct platform_device *pdev)
err_iff: err_iff:
input_ff_destroy(info->input_dev); input_ff_destroy(info->input_dev);
err_ialloc:
input_free_device(info->input_dev);
err_kzalloc:
kfree(info);
return ret; return ret;
} }
static int twl4030_vibra_remove(struct platform_device *pdev)
{
struct vibra_info *info = platform_get_drvdata(pdev);
/* this also free ff-memless and calls close if needed */
input_unregister_device(info->input_dev);
kfree(info);
platform_set_drvdata(pdev, NULL);
return 0;
}
static struct platform_driver twl4030_vibra_driver = { static struct platform_driver twl4030_vibra_driver = {
.probe = twl4030_vibra_probe, .probe = twl4030_vibra_probe,
.remove = twl4030_vibra_remove,
.driver = { .driver = {
.name = "twl4030-vibra", .name = "twl4030-vibra",
.owner = THIS_MODULE, .owner = THIS_MODULE,

View File

@ -275,7 +275,7 @@ static int twl6040_vibra_probe(struct platform_device *pdev)
return -EINVAL; return -EINVAL;
} }
info = kzalloc(sizeof(*info), GFP_KERNEL); info = devm_kzalloc(&pdev->dev, sizeof(*info), GFP_KERNEL);
if (!info) { if (!info) {
dev_err(&pdev->dev, "couldn't allocate memory\n"); dev_err(&pdev->dev, "couldn't allocate memory\n");
return -ENOMEM; return -ENOMEM;
@ -309,24 +309,65 @@ static int twl6040_vibra_probe(struct platform_device *pdev)
if ((!info->vibldrv_res && !info->viblmotor_res) || if ((!info->vibldrv_res && !info->viblmotor_res) ||
(!info->vibrdrv_res && !info->vibrmotor_res)) { (!info->vibrdrv_res && !info->vibrmotor_res)) {
dev_err(info->dev, "invalid vibra driver/motor resistance\n"); dev_err(info->dev, "invalid vibra driver/motor resistance\n");
ret = -EINVAL; return -EINVAL;
goto err_kzalloc;
} }
info->irq = platform_get_irq(pdev, 0); info->irq = platform_get_irq(pdev, 0);
if (info->irq < 0) { if (info->irq < 0) {
dev_err(info->dev, "invalid irq\n"); dev_err(info->dev, "invalid irq\n");
ret = -EINVAL; return -EINVAL;
goto err_kzalloc;
} }
mutex_init(&info->mutex); mutex_init(&info->mutex);
ret = devm_request_threaded_irq(&pdev->dev, info->irq, NULL,
twl6040_vib_irq_handler, 0,
"twl6040_irq_vib", info);
if (ret) {
dev_err(info->dev, "VIB IRQ request failed: %d\n", ret);
return ret;
}
info->supplies[0].supply = "vddvibl";
info->supplies[1].supply = "vddvibr";
/*
* When booted with Device tree the regulators are attached to the
* parent device (twl6040 MFD core)
*/
ret = regulator_bulk_get(pdata ? info->dev : twl6040_core_dev,
ARRAY_SIZE(info->supplies), info->supplies);
if (ret) {
dev_err(info->dev, "couldn't get regulators %d\n", ret);
return ret;
}
if (vddvibl_uV) {
ret = regulator_set_voltage(info->supplies[0].consumer,
vddvibl_uV, vddvibl_uV);
if (ret) {
dev_err(info->dev, "failed to set VDDVIBL volt %d\n",
ret);
goto err_regulator;
}
}
if (vddvibr_uV) {
ret = regulator_set_voltage(info->supplies[1].consumer,
vddvibr_uV, vddvibr_uV);
if (ret) {
dev_err(info->dev, "failed to set VDDVIBR volt %d\n",
ret);
goto err_regulator;
}
}
INIT_WORK(&info->play_work, vibra_play_work);
info->input_dev = input_allocate_device(); info->input_dev = input_allocate_device();
if (info->input_dev == NULL) { if (info->input_dev == NULL) {
dev_err(info->dev, "couldn't allocate input device\n"); dev_err(info->dev, "couldn't allocate input device\n");
ret = -ENOMEM; ret = -ENOMEM;
goto err_kzalloc; goto err_regulator;
} }
input_set_drvdata(info->input_dev, info); input_set_drvdata(info->input_dev, info);
@ -351,70 +392,14 @@ static int twl6040_vibra_probe(struct platform_device *pdev)
platform_set_drvdata(pdev, info); platform_set_drvdata(pdev, info);
ret = request_threaded_irq(info->irq, NULL, twl6040_vib_irq_handler, 0,
"twl6040_irq_vib", info);
if (ret) {
dev_err(info->dev, "VIB IRQ request failed: %d\n", ret);
goto err_irq;
}
info->supplies[0].supply = "vddvibl";
info->supplies[1].supply = "vddvibr";
/*
* When booted with Device tree the regulators are attached to the
* parent device (twl6040 MFD core)
*/
ret = regulator_bulk_get(pdata ? info->dev : twl6040_core_dev,
ARRAY_SIZE(info->supplies), info->supplies);
if (ret) {
dev_err(info->dev, "couldn't get regulators %d\n", ret);
goto err_regulator;
}
if (vddvibl_uV) {
ret = regulator_set_voltage(info->supplies[0].consumer,
vddvibl_uV, vddvibl_uV);
if (ret) {
dev_err(info->dev, "failed to set VDDVIBL volt %d\n",
ret);
goto err_voltage;
}
}
if (vddvibr_uV) {
ret = regulator_set_voltage(info->supplies[1].consumer,
vddvibr_uV, vddvibr_uV);
if (ret) {
dev_err(info->dev, "failed to set VDDVIBR volt %d\n",
ret);
goto err_voltage;
}
}
info->workqueue = alloc_workqueue("twl6040-vibra", 0, 0);
if (info->workqueue == NULL) {
dev_err(info->dev, "couldn't create workqueue\n");
ret = -ENOMEM;
goto err_voltage;
}
INIT_WORK(&info->play_work, vibra_play_work);
return 0; return 0;
err_voltage:
regulator_bulk_free(ARRAY_SIZE(info->supplies), info->supplies);
err_regulator:
free_irq(info->irq, info);
err_irq:
input_unregister_device(info->input_dev);
info->input_dev = NULL;
err_iff: err_iff:
if (info->input_dev)
input_ff_destroy(info->input_dev); input_ff_destroy(info->input_dev);
err_ialloc: err_ialloc:
input_free_device(info->input_dev); input_free_device(info->input_dev);
err_kzalloc: err_regulator:
kfree(info); regulator_bulk_free(ARRAY_SIZE(info->supplies), info->supplies);
return ret; return ret;
} }
@ -423,10 +408,7 @@ static int twl6040_vibra_remove(struct platform_device *pdev)
struct vibra_info *info = platform_get_drvdata(pdev); struct vibra_info *info = platform_get_drvdata(pdev);
input_unregister_device(info->input_dev); input_unregister_device(info->input_dev);
free_irq(info->irq, info);
regulator_bulk_free(ARRAY_SIZE(info->supplies), info->supplies); regulator_bulk_free(ARRAY_SIZE(info->supplies), info->supplies);
destroy_workqueue(info->workqueue);
kfree(info);
return 0; return 0;
} }

View File

@ -86,7 +86,7 @@ static int wm831x_on_probe(struct platform_device *pdev)
wm831x_on->wm831x = wm831x; wm831x_on->wm831x = wm831x;
INIT_DELAYED_WORK(&wm831x_on->work, wm831x_poll_on); INIT_DELAYED_WORK(&wm831x_on->work, wm831x_poll_on);
wm831x_on->dev = input_allocate_device(); wm831x_on->dev = devm_input_allocate_device(&pdev->dev);
if (!wm831x_on->dev) { if (!wm831x_on->dev) {
dev_err(&pdev->dev, "Can't allocate input dev\n"); dev_err(&pdev->dev, "Can't allocate input dev\n");
ret = -ENOMEM; ret = -ENOMEM;
@ -119,7 +119,6 @@ static int wm831x_on_probe(struct platform_device *pdev)
err_irq: err_irq:
free_irq(irq, wm831x_on); free_irq(irq, wm831x_on);
err_input_dev: err_input_dev:
input_free_device(wm831x_on->dev);
err: err:
return ret; return ret;
} }
@ -131,7 +130,6 @@ static int wm831x_on_remove(struct platform_device *pdev)
free_irq(irq, wm831x_on); free_irq(irq, wm831x_on);
cancel_delayed_work_sync(&wm831x_on->work); cancel_delayed_work_sync(&wm831x_on->work);
input_unregister_device(wm831x_on->dev);
return 0; return 0;
} }

View File

@ -68,6 +68,16 @@ config MOUSE_PS2_SYNAPTICS
If unsure, say Y. If unsure, say Y.
config MOUSE_PS2_CYPRESS
bool "Cypress PS/2 mouse protocol extension" if EXPERT
default y
depends on MOUSE_PS2
help
Say Y here if you have a Cypress PS/2 Trackpad connected to
your system.
If unsure, say Y.
config MOUSE_PS2_LIFEBOOK config MOUSE_PS2_LIFEBOOK
bool "Fujitsu Lifebook PS/2 mouse protocol extension" if EXPERT bool "Fujitsu Lifebook PS/2 mouse protocol extension" if EXPERT
default y default y
@ -193,6 +203,18 @@ config MOUSE_BCM5974
To compile this driver as a module, choose M here: the To compile this driver as a module, choose M here: the
module will be called bcm5974. module will be called bcm5974.
config MOUSE_CYAPA
tristate "Cypress APA I2C Trackpad support"
depends on I2C
help
This driver adds support for Cypress All Points Addressable (APA)
I2C Trackpads, including the ones used in 2012 Samsung Chromebooks.
Say Y here if you have a Cypress APA I2C Trackpad.
To compile this driver as a module, choose M here: the module will be
called cyapa.
config MOUSE_INPORT config MOUSE_INPORT
tristate "InPort/MS/ATIXL busmouse" tristate "InPort/MS/ATIXL busmouse"
depends on ISA depends on ISA

View File

@ -8,6 +8,7 @@ obj-$(CONFIG_MOUSE_AMIGA) += amimouse.o
obj-$(CONFIG_MOUSE_APPLETOUCH) += appletouch.o obj-$(CONFIG_MOUSE_APPLETOUCH) += appletouch.o
obj-$(CONFIG_MOUSE_ATARI) += atarimouse.o obj-$(CONFIG_MOUSE_ATARI) += atarimouse.o
obj-$(CONFIG_MOUSE_BCM5974) += bcm5974.o obj-$(CONFIG_MOUSE_BCM5974) += bcm5974.o
obj-$(CONFIG_MOUSE_CYAPA) += cyapa.o
obj-$(CONFIG_MOUSE_GPIO) += gpio_mouse.o obj-$(CONFIG_MOUSE_GPIO) += gpio_mouse.o
obj-$(CONFIG_MOUSE_INPORT) += inport.o obj-$(CONFIG_MOUSE_INPORT) += inport.o
obj-$(CONFIG_MOUSE_LOGIBM) += logibm.o obj-$(CONFIG_MOUSE_LOGIBM) += logibm.o
@ -32,3 +33,4 @@ psmouse-$(CONFIG_MOUSE_PS2_LIFEBOOK) += lifebook.o
psmouse-$(CONFIG_MOUSE_PS2_SENTELIC) += sentelic.o psmouse-$(CONFIG_MOUSE_PS2_SENTELIC) += sentelic.o
psmouse-$(CONFIG_MOUSE_PS2_TRACKPOINT) += trackpoint.o psmouse-$(CONFIG_MOUSE_PS2_TRACKPOINT) += trackpoint.o
psmouse-$(CONFIG_MOUSE_PS2_TOUCHKIT) += touchkit_ps2.o psmouse-$(CONFIG_MOUSE_PS2_TOUCHKIT) += touchkit_ps2.o
psmouse-$(CONFIG_MOUSE_PS2_CYPRESS) += cypress_ps2.o

File diff suppressed because it is too large Load Diff

View File

@ -12,35 +12,146 @@
#ifndef _ALPS_H #ifndef _ALPS_H
#define _ALPS_H #define _ALPS_H
#define ALPS_PROTO_V1 0 #define ALPS_PROTO_V1 1
#define ALPS_PROTO_V2 1 #define ALPS_PROTO_V2 2
#define ALPS_PROTO_V3 2 #define ALPS_PROTO_V3 3
#define ALPS_PROTO_V4 3 #define ALPS_PROTO_V4 4
/**
* struct alps_model_info - touchpad ID table
* @signature: E7 response string to match.
* @command_mode_resp: For V3/V4 touchpads, the final byte of the EC response
* (aka command mode response) identifies the firmware minor version. This
* can be used to distinguish different hardware models which are not
* uniquely identifiable through their E7 responses.
* @proto_version: Indicates V1/V2/V3/...
* @byte0: Helps figure out whether a position report packet matches the
* known format for this model. The first byte of the report, ANDed with
* mask0, should match byte0.
* @mask0: The mask used to check the first byte of the report.
* @flags: Additional device capabilities (passthrough port, trackstick, etc.).
*
* Many (but not all) ALPS touchpads can be identified by looking at the
* values returned in the "E7 report" and/or the "EC report." This table
* lists a number of such touchpads.
*/
struct alps_model_info { struct alps_model_info {
unsigned char signature[3]; unsigned char signature[3];
unsigned char command_mode_resp; /* v3/v4 only */ unsigned char command_mode_resp;
unsigned char proto_version; unsigned char proto_version;
unsigned char byte0, mask0; unsigned char byte0, mask0;
unsigned char flags; unsigned char flags;
}; };
/**
* struct alps_nibble_commands - encodings for register accesses
* @command: PS/2 command used for the nibble
* @data: Data supplied as an argument to the PS/2 command, if applicable
*
* The ALPS protocol uses magic sequences to transmit binary data to the
* touchpad, as it is generally not OK to send arbitrary bytes out the
* PS/2 port. Each of the sequences in this table sends one nibble of the
* register address or (write) data. Different versions of the ALPS protocol
* use slightly different encodings.
*/
struct alps_nibble_commands { struct alps_nibble_commands {
int command; int command;
unsigned char data; unsigned char data;
}; };
/**
* struct alps_fields - decoded version of the report packet
* @x_map: Bitmap of active X positions for MT.
* @y_map: Bitmap of active Y positions for MT.
* @fingers: Number of fingers for MT.
* @x: X position for ST.
* @y: Y position for ST.
* @z: Z position for ST.
* @first_mp: Packet is the first of a multi-packet report.
* @is_mp: Packet is part of a multi-packet report.
* @left: Left touchpad button is active.
* @right: Right touchpad button is active.
* @middle: Middle touchpad button is active.
* @ts_left: Left trackstick button is active.
* @ts_right: Right trackstick button is active.
* @ts_middle: Middle trackstick button is active.
*/
struct alps_fields {
unsigned int x_map;
unsigned int y_map;
unsigned int fingers;
unsigned int x;
unsigned int y;
unsigned int z;
unsigned int first_mp:1;
unsigned int is_mp:1;
unsigned int left:1;
unsigned int right:1;
unsigned int middle:1;
unsigned int ts_left:1;
unsigned int ts_right:1;
unsigned int ts_middle:1;
};
/**
* struct alps_data - private data structure for the ALPS driver
* @dev2: "Relative" device used to report trackstick or mouse activity.
* @phys: Physical path for the relative device.
* @nibble_commands: Command mapping used for touchpad register accesses.
* @addr_command: Command used to tell the touchpad that a register address
* follows.
* @proto_version: Indicates V1/V2/V3/...
* @byte0: Helps figure out whether a position report packet matches the
* known format for this model. The first byte of the report, ANDed with
* mask0, should match byte0.
* @mask0: The mask used to check the first byte of the report.
* @flags: Additional device capabilities (passthrough port, trackstick, etc.).
* @x_max: Largest possible X position value.
* @y_max: Largest possible Y position value.
* @x_bits: Number of X bits in the MT bitmap.
* @y_bits: Number of Y bits in the MT bitmap.
* @hw_init: Protocol-specific hardware init function.
* @process_packet: Protocol-specific function to process a report packet.
* @decode_fields: Protocol-specific function to read packet bitfields.
* @set_abs_params: Protocol-specific function to configure the input_dev.
* @prev_fin: Finger bit from previous packet.
* @multi_packet: Multi-packet data in progress.
* @multi_data: Saved multi-packet data.
* @x1: First X coordinate from last MT report.
* @x2: Second X coordinate from last MT report.
* @y1: First Y coordinate from last MT report.
* @y2: Second Y coordinate from last MT report.
* @fingers: Number of fingers from last MT report.
* @quirks: Bitmap of ALPS_QUIRK_*.
* @timer: Timer for flushing out the final report packet in the stream.
*/
struct alps_data { struct alps_data {
struct input_dev *dev2; /* Relative device */ struct input_dev *dev2;
char phys[32]; /* Phys */ char phys[32];
const struct alps_model_info *i;/* Info */
/* these are autodetected when the device is identified */
const struct alps_nibble_commands *nibble_commands; const struct alps_nibble_commands *nibble_commands;
int addr_command; /* Command to set register address */ int addr_command;
int prev_fin; /* Finger bit from previous packet */ unsigned char proto_version;
int multi_packet; /* Multi-packet data in progress */ unsigned char byte0, mask0;
unsigned char multi_data[6]; /* Saved multi-packet data */ unsigned char flags;
int x1, x2, y1, y2; /* Coordinates from last MT report */ int x_max;
int fingers; /* Number of fingers from MT report */ int y_max;
int x_bits;
int y_bits;
int (*hw_init)(struct psmouse *psmouse);
void (*process_packet)(struct psmouse *psmouse);
void (*decode_fields)(struct alps_fields *f, unsigned char *p);
void (*set_abs_params)(struct alps_data *priv, struct input_dev *dev1);
int prev_fin;
int multi_packet;
unsigned char multi_data[6];
int x1, x2, y1, y2;
int fingers;
u8 quirks; u8 quirks;
struct timer_list timer; struct timer_list timer;
}; };

973
drivers/input/mouse/cyapa.c Normal file
View File

@ -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");

View File

@ -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;
}

View File

@ -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 */

View File

@ -34,6 +34,7 @@
#include "touchkit_ps2.h" #include "touchkit_ps2.h"
#include "elantech.h" #include "elantech.h"
#include "sentelic.h" #include "sentelic.h"
#include "cypress_ps2.h"
#define DRIVER_DESC "PS/2 mouse driver" #define DRIVER_DESC "PS/2 mouse driver"
@ -758,6 +759,28 @@ static int psmouse_extensions(struct psmouse *psmouse,
synaptics_reset(psmouse); synaptics_reset(psmouse);
} }
/*
* Try Cypress Trackpad.
* Must try it before Finger Sensing Pad because Finger Sensing Pad probe
* upsets some modules of Cypress Trackpads.
*/
if (max_proto > PSMOUSE_IMEX &&
cypress_detect(psmouse, set_properties) == 0) {
if (cypress_supported()) {
if (cypress_init(psmouse) == 0)
return PSMOUSE_CYPRESS;
/*
* Finger Sensing Pad probe upsets some modules of
* Cypress Trackpad, must avoid Finger Sensing Pad
* probe if Cypress Trackpad device detected.
*/
return PSMOUSE_PS2;
}
max_proto = PSMOUSE_IMEX;
}
/* /*
* Try ALPS TouchPad * Try ALPS TouchPad
*/ */
@ -896,6 +919,15 @@ static const struct psmouse_protocol psmouse_protocols[] = {
.alias = "thinkps", .alias = "thinkps",
.detect = thinking_detect, .detect = thinking_detect,
}, },
#ifdef CONFIG_MOUSE_PS2_CYPRESS
{
.type = PSMOUSE_CYPRESS,
.name = "CyPS/2",
.alias = "cypress",
.detect = cypress_detect,
.init = cypress_init,
},
#endif
{ {
.type = PSMOUSE_GENPS, .type = PSMOUSE_GENPS,
.name = "GenPS/2", .name = "GenPS/2",

View File

@ -95,6 +95,7 @@ enum psmouse_type {
PSMOUSE_ELANTECH, PSMOUSE_ELANTECH,
PSMOUSE_FSP, PSMOUSE_FSP,
PSMOUSE_SYNAPTICS_RELATIVE, PSMOUSE_SYNAPTICS_RELATIVE,
PSMOUSE_CYPRESS,
PSMOUSE_AUTO /* This one should always be last */ PSMOUSE_AUTO /* This one should always be last */
}; };

View File

@ -722,11 +722,13 @@ static void synaptics_report_mt_data(struct psmouse *psmouse,
default: default:
/* /*
* If the finger slot contained in SGM is valid, and either * If the finger slot contained in SGM is valid, and either
* hasn't changed, or is new, then report SGM in MTB slot 0. * hasn't changed, or is new, or the old SGM has now moved to
* AGM, then report SGM in MTB slot 0.
* Otherwise, empty MTB slot 0. * Otherwise, empty MTB slot 0.
*/ */
if (mt_state->sgm != -1 && if (mt_state->sgm != -1 &&
(mt_state->sgm == old->sgm || old->sgm == -1)) (mt_state->sgm == old->sgm ||
old->sgm == -1 || mt_state->agm == old->sgm))
synaptics_report_slot(dev, 0, sgm); synaptics_report_slot(dev, 0, sgm);
else else
synaptics_report_slot(dev, 0, NULL); synaptics_report_slot(dev, 0, NULL);
@ -735,9 +737,31 @@ static void synaptics_report_mt_data(struct psmouse *psmouse,
* If the finger slot contained in AGM is valid, and either * If the finger slot contained in AGM is valid, and either
* hasn't changed, or is new, then report AGM in MTB slot 1. * hasn't changed, or is new, then report AGM in MTB slot 1.
* Otherwise, empty MTB slot 1. * Otherwise, empty MTB slot 1.
*
* However, in the case where the AGM is new, make sure that
* that it is either the same as the old SGM, or there was no
* SGM.
*
* Otherwise, if the SGM was just 1, and the new AGM is 2, then
* the new AGM will keep the old SGM's tracking ID, which can
* cause apparent drumroll. This happens if in the following
* valid finger sequence:
*
* Action SGM AGM (MTB slot:Contact)
* 1. Touch contact 0 (0:0)
* 2. Touch contact 1 (0:0, 1:1)
* 3. Lift contact 0 (1:1)
* 4. Touch contacts 2,3 (0:2, 1:3)
*
* In step 4, contact 3, in AGM must not be given the same
* tracking ID as contact 1 had in step 3. To avoid this,
* the first agm with contact 3 is dropped and slot 1 is
* invalidated (tracking ID = -1).
*/ */
if (mt_state->agm != -1 && if (mt_state->agm != -1 &&
(mt_state->agm == old->agm || old->agm == -1)) (mt_state->agm == old->agm ||
(old->agm == -1 &&
(old->sgm == -1 || mt_state->agm == old->sgm))))
synaptics_report_slot(dev, 1, agm); synaptics_report_slot(dev, 1, agm);
else else
synaptics_report_slot(dev, 1, NULL); synaptics_report_slot(dev, 1, NULL);
@ -1247,11 +1271,11 @@ static void set_input_params(struct input_dev *dev, struct synaptics_data *priv)
input_set_abs_params(dev, ABS_PRESSURE, 0, 255, 0, 0); input_set_abs_params(dev, ABS_PRESSURE, 0, 255, 0, 0);
if (SYN_CAP_IMAGE_SENSOR(priv->ext_cap_0c)) { if (SYN_CAP_IMAGE_SENSOR(priv->ext_cap_0c)) {
input_mt_init_slots(dev, 2, 0);
set_abs_position_params(dev, priv, ABS_MT_POSITION_X, set_abs_position_params(dev, priv, ABS_MT_POSITION_X,
ABS_MT_POSITION_Y); ABS_MT_POSITION_Y);
/* Image sensors can report per-contact pressure */ /* Image sensors can report per-contact pressure */
input_set_abs_params(dev, ABS_MT_PRESSURE, 0, 255, 0, 0); input_set_abs_params(dev, ABS_MT_PRESSURE, 0, 255, 0, 0);
input_mt_init_slots(dev, 2, INPUT_MT_POINTER);
/* Image sensors can signal 4 and 5 finger clicks */ /* Image sensors can signal 4 and 5 finger clicks */
__set_bit(BTN_TOOL_QUADTAP, dev->keybit); __set_bit(BTN_TOOL_QUADTAP, dev->keybit);

View File

@ -236,6 +236,7 @@ config SERIO_PS2MULT
config SERIO_ARC_PS2 config SERIO_ARC_PS2
tristate "ARC PS/2 support" tristate "ARC PS/2 support"
depends on GENERIC_HARDIRQS
help help
Say Y here if you have an ARC FPGA platform with a PS/2 Say Y here if you have an ARC FPGA platform with a PS/2
controller in it. controller in it.

View File

@ -359,6 +359,7 @@ static int wacom_intuos_inout(struct wacom_wac *wacom)
case 0x802: /* Intuos4 General Pen */ case 0x802: /* Intuos4 General Pen */
case 0x804: /* Intuos4 Marker Pen */ case 0x804: /* Intuos4 Marker Pen */
case 0x40802: /* Intuos4 Classic Pen */ case 0x40802: /* Intuos4 Classic Pen */
case 0x18803: /* DTH2242 Grip Pen */
case 0x022: case 0x022:
wacom->tool[idx] = BTN_TOOL_PEN; wacom->tool[idx] = BTN_TOOL_PEN;
break; break;
@ -538,6 +539,13 @@ static int wacom_intuos_irq(struct wacom_wac *wacom)
input_report_key(input, wacom->tool[1], 0); input_report_key(input, wacom->tool[1], 0);
input_report_abs(input, ABS_MISC, 0); input_report_abs(input, ABS_MISC, 0);
} }
} else if (features->type == DTK) {
input_report_key(input, BTN_0, (data[6] & 0x01));
input_report_key(input, BTN_1, (data[6] & 0x02));
input_report_key(input, BTN_2, (data[6] & 0x04));
input_report_key(input, BTN_3, (data[6] & 0x08));
input_report_key(input, BTN_4, (data[6] & 0x10));
input_report_key(input, BTN_5, (data[6] & 0x20));
} else if (features->type == WACOM_24HD) { } else if (features->type == WACOM_24HD) {
input_report_key(input, BTN_0, (data[6] & 0x01)); input_report_key(input, BTN_0, (data[6] & 0x01));
input_report_key(input, BTN_1, (data[6] & 0x02)); input_report_key(input, BTN_1, (data[6] & 0x02));
@ -785,25 +793,6 @@ static int wacom_intuos_irq(struct wacom_wac *wacom)
return 1; return 1;
} }
static int find_slot_from_contactid(struct wacom_wac *wacom, int contactid)
{
int touch_max = wacom->features.touch_max;
int i;
if (!wacom->slots)
return -1;
for (i = 0; i < touch_max; ++i) {
if (wacom->slots[i] == contactid)
return i;
}
for (i = 0; i < touch_max; ++i) {
if (wacom->slots[i] == -1)
return i;
}
return -1;
}
static int int_dist(int x1, int y1, int x2, int y2) static int int_dist(int x1, int y1, int x2, int y2)
{ {
int x = x2 - x1; int x = x2 - x1;
@ -833,8 +822,7 @@ static int wacom_24hdt_irq(struct wacom_wac *wacom)
for (i = 0; i < contacts_to_send; i++) { for (i = 0; i < contacts_to_send; i++) {
int offset = (WACOM_BYTES_PER_24HDT_PACKET * i) + 1; int offset = (WACOM_BYTES_PER_24HDT_PACKET * i) + 1;
bool touch = data[offset] & 0x1 && !wacom->shared->stylus_in_proximity; bool touch = data[offset] & 0x1 && !wacom->shared->stylus_in_proximity;
int id = data[offset + 1]; int slot = input_mt_get_slot_by_key(input, data[offset + 1]);
int slot = find_slot_from_contactid(wacom, id);
if (slot < 0) if (slot < 0)
continue; continue;
@ -856,9 +844,7 @@ static int wacom_24hdt_irq(struct wacom_wac *wacom)
input_report_abs(input, ABS_MT_WIDTH_MINOR, min(w, h)); input_report_abs(input, ABS_MT_WIDTH_MINOR, min(w, h));
input_report_abs(input, ABS_MT_ORIENTATION, w > h); input_report_abs(input, ABS_MT_ORIENTATION, w > h);
} }
wacom->slots[slot] = touch ? id : -1;
} }
input_mt_report_pointer_emulation(input, true); input_mt_report_pointer_emulation(input, true);
wacom->num_contacts_left -= contacts_to_send; wacom->num_contacts_left -= contacts_to_send;
@ -895,7 +881,7 @@ static int wacom_mt_touch(struct wacom_wac *wacom)
int offset = (WACOM_BYTES_PER_MT_PACKET + x_offset) * i + 3; int offset = (WACOM_BYTES_PER_MT_PACKET + x_offset) * i + 3;
bool touch = data[offset] & 0x1; bool touch = data[offset] & 0x1;
int id = le16_to_cpup((__le16 *)&data[offset + 1]); int id = le16_to_cpup((__le16 *)&data[offset + 1]);
int slot = find_slot_from_contactid(wacom, id); int slot = input_mt_get_slot_by_key(input, id);
if (slot < 0) if (slot < 0)
continue; continue;
@ -908,9 +894,7 @@ static int wacom_mt_touch(struct wacom_wac *wacom)
input_report_abs(input, ABS_MT_POSITION_X, x); input_report_abs(input, ABS_MT_POSITION_X, x);
input_report_abs(input, ABS_MT_POSITION_Y, y); input_report_abs(input, ABS_MT_POSITION_Y, y);
} }
wacom->slots[slot] = touch ? id : -1;
} }
input_mt_report_pointer_emulation(input, true); input_mt_report_pointer_emulation(input, true);
wacom->num_contacts_left -= contacts_to_send; wacom->num_contacts_left -= contacts_to_send;
@ -942,12 +926,11 @@ static int wacom_tpc_mt_touch(struct wacom_wac *wacom)
contact_with_no_pen_down_count++; contact_with_no_pen_down_count++;
} }
} }
input_mt_report_pointer_emulation(input, true);
/* keep touch state for pen event */ /* keep touch state for pen event */
wacom->shared->touch_down = (contact_with_no_pen_down_count > 0); wacom->shared->touch_down = (contact_with_no_pen_down_count > 0);
input_mt_report_pointer_emulation(input, true);
return 1; return 1;
} }
@ -1104,12 +1087,15 @@ static int wacom_bpt_touch(struct wacom_wac *wacom)
static void wacom_bpt3_touch_msg(struct wacom_wac *wacom, unsigned char *data) static void wacom_bpt3_touch_msg(struct wacom_wac *wacom, unsigned char *data)
{ {
struct input_dev *input = wacom->input; struct input_dev *input = wacom->input;
int slot_id = data[0] - 2; /* data[0] is between 2 and 17 */
bool touch = data[1] & 0x80; bool touch = data[1] & 0x80;
int slot = input_mt_get_slot_by_key(input, data[0]);
if (slot < 0)
return;
touch = touch && !wacom->shared->stylus_in_proximity; touch = touch && !wacom->shared->stylus_in_proximity;
input_mt_slot(input, slot_id); input_mt_slot(input, slot);
input_mt_report_slot_state(input, MT_TOOL_FINGER, touch); input_mt_report_slot_state(input, MT_TOOL_FINGER, touch);
if (touch) { if (touch) {
@ -1162,7 +1148,6 @@ static int wacom_bpt3_touch(struct wacom_wac *wacom)
wacom_bpt3_button_msg(wacom, data + offset); wacom_bpt3_button_msg(wacom, data + offset);
} }
input_mt_report_pointer_emulation(input, true); input_mt_report_pointer_emulation(input, true);
input_sync(input); input_sync(input);
@ -1319,6 +1304,7 @@ void wacom_wac_irq(struct wacom_wac *wacom_wac, size_t len)
case WACOM_21UX2: case WACOM_21UX2:
case WACOM_22HD: case WACOM_22HD:
case WACOM_24HD: case WACOM_24HD:
case DTK:
sync = wacom_intuos_irq(wacom_wac); sync = wacom_intuos_irq(wacom_wac);
break; break;
@ -1444,6 +1430,51 @@ static unsigned int wacom_calculate_touch_res(unsigned int logical_max,
return (logical_max * 100) / physical_max; return (logical_max * 100) / physical_max;
} }
static void wacom_abs_set_axis(struct input_dev *input_dev,
struct wacom_wac *wacom_wac)
{
struct wacom_features *features = &wacom_wac->features;
if (features->device_type == BTN_TOOL_PEN) {
input_set_abs_params(input_dev, ABS_X, 0, features->x_max,
features->x_fuzz, 0);
input_set_abs_params(input_dev, ABS_Y, 0, features->y_max,
features->y_fuzz, 0);
input_set_abs_params(input_dev, ABS_PRESSURE, 0,
features->pressure_max, features->pressure_fuzz, 0);
/* penabled devices have fixed resolution for each model */
input_abs_set_res(input_dev, ABS_X, features->x_resolution);
input_abs_set_res(input_dev, ABS_Y, features->y_resolution);
} else {
if (features->touch_max <= 2) {
input_set_abs_params(input_dev, ABS_X, 0,
features->x_max, features->x_fuzz, 0);
input_set_abs_params(input_dev, ABS_Y, 0,
features->y_max, features->y_fuzz, 0);
input_abs_set_res(input_dev, ABS_X,
wacom_calculate_touch_res(features->x_max,
features->x_phy));
input_abs_set_res(input_dev, ABS_Y,
wacom_calculate_touch_res(features->y_max,
features->y_phy));
}
if (features->touch_max > 1) {
input_set_abs_params(input_dev, ABS_MT_POSITION_X, 0,
features->x_max, features->x_fuzz, 0);
input_set_abs_params(input_dev, ABS_MT_POSITION_Y, 0,
features->y_max, features->y_fuzz, 0);
input_abs_set_res(input_dev, ABS_MT_POSITION_X,
wacom_calculate_touch_res(features->x_max,
features->x_phy));
input_abs_set_res(input_dev, ABS_MT_POSITION_Y,
wacom_calculate_touch_res(features->y_max,
features->y_phy));
}
}
}
int wacom_setup_input_capabilities(struct input_dev *input_dev, int wacom_setup_input_capabilities(struct input_dev *input_dev,
struct wacom_wac *wacom_wac) struct wacom_wac *wacom_wac)
{ {
@ -1453,30 +1484,10 @@ int wacom_setup_input_capabilities(struct input_dev *input_dev,
input_dev->evbit[0] |= BIT_MASK(EV_KEY) | BIT_MASK(EV_ABS); input_dev->evbit[0] |= BIT_MASK(EV_KEY) | BIT_MASK(EV_ABS);
__set_bit(BTN_TOUCH, input_dev->keybit); __set_bit(BTN_TOUCH, input_dev->keybit);
input_set_abs_params(input_dev, ABS_X, 0, features->x_max,
features->x_fuzz, 0);
input_set_abs_params(input_dev, ABS_Y, 0, features->y_max,
features->y_fuzz, 0);
if (features->device_type == BTN_TOOL_PEN) {
input_set_abs_params(input_dev, ABS_PRESSURE, 0, features->pressure_max,
features->pressure_fuzz, 0);
/* penabled devices have fixed resolution for each model */
input_abs_set_res(input_dev, ABS_X, features->x_resolution);
input_abs_set_res(input_dev, ABS_Y, features->y_resolution);
} else {
input_abs_set_res(input_dev, ABS_X,
wacom_calculate_touch_res(features->x_max,
features->x_phy));
input_abs_set_res(input_dev, ABS_Y,
wacom_calculate_touch_res(features->y_max,
features->y_phy));
}
__set_bit(ABS_MISC, input_dev->absbit); __set_bit(ABS_MISC, input_dev->absbit);
wacom_abs_set_axis(input_dev, wacom_wac);
switch (wacom_wac->features.type) { switch (wacom_wac->features.type) {
case WACOM_MO: case WACOM_MO:
input_set_abs_params(input_dev, ABS_WHEEL, 0, 71, 0, 0); input_set_abs_params(input_dev, ABS_WHEEL, 0, 71, 0, 0);
@ -1513,12 +1524,17 @@ int wacom_setup_input_capabilities(struct input_dev *input_dev,
__set_bit(BTN_Y, input_dev->keybit); __set_bit(BTN_Y, input_dev->keybit);
__set_bit(BTN_Z, input_dev->keybit); __set_bit(BTN_Z, input_dev->keybit);
for (i = 0; i < 10; i++) for (i = 6; i < 10; i++)
__set_bit(BTN_0 + i, input_dev->keybit); __set_bit(BTN_0 + i, input_dev->keybit);
__set_bit(KEY_PROG1, input_dev->keybit); __set_bit(KEY_PROG1, input_dev->keybit);
__set_bit(KEY_PROG2, input_dev->keybit); __set_bit(KEY_PROG2, input_dev->keybit);
__set_bit(KEY_PROG3, input_dev->keybit); __set_bit(KEY_PROG3, input_dev->keybit);
/* fall through */
case DTK:
for (i = 0; i < 6; i++)
__set_bit(BTN_0 + i, input_dev->keybit);
input_set_abs_params(input_dev, ABS_Z, -900, 899, 0, 0); input_set_abs_params(input_dev, ABS_Z, -900, 899, 0, 0);
input_set_abs_params(input_dev, ABS_THROTTLE, 0, 71, 0, 0); input_set_abs_params(input_dev, ABS_THROTTLE, 0, 71, 0, 0);
@ -1614,24 +1630,11 @@ int wacom_setup_input_capabilities(struct input_dev *input_dev,
} else if (features->device_type == BTN_TOOL_FINGER) { } else if (features->device_type == BTN_TOOL_FINGER) {
__clear_bit(ABS_MISC, input_dev->absbit); __clear_bit(ABS_MISC, input_dev->absbit);
__set_bit(BTN_TOOL_FINGER, input_dev->keybit);
__set_bit(BTN_TOOL_DOUBLETAP, input_dev->keybit);
__set_bit(BTN_TOOL_TRIPLETAP, input_dev->keybit);
__set_bit(BTN_TOOL_QUADTAP, input_dev->keybit);
input_mt_init_slots(input_dev, features->touch_max, 0);
input_set_abs_params(input_dev, ABS_MT_TOUCH_MAJOR, input_set_abs_params(input_dev, ABS_MT_TOUCH_MAJOR,
0, features->x_max, 0, 0); 0, features->x_max, 0, 0);
input_set_abs_params(input_dev, ABS_MT_TOUCH_MINOR, input_set_abs_params(input_dev, ABS_MT_TOUCH_MINOR,
0, features->y_max, 0, 0); 0, features->y_max, 0, 0);
input_mt_init_slots(input_dev, features->touch_max, INPUT_MT_POINTER);
input_set_abs_params(input_dev, ABS_MT_POSITION_X,
0, features->x_max,
features->x_fuzz, 0);
input_set_abs_params(input_dev, ABS_MT_POSITION_Y,
0, features->y_max,
features->y_fuzz, 0);
} }
break; break;
@ -1662,27 +1665,14 @@ int wacom_setup_input_capabilities(struct input_dev *input_dev,
case MTSCREEN: case MTSCREEN:
case MTTPC: case MTTPC:
if (features->device_type == BTN_TOOL_FINGER) {
wacom_wac->slots = kmalloc(features->touch_max *
sizeof(int),
GFP_KERNEL);
if (!wacom_wac->slots)
return -ENOMEM;
for (i = 0; i < features->touch_max; i++)
wacom_wac->slots[i] = -1;
}
/* fall through */
case TABLETPC2FG: case TABLETPC2FG:
if (features->device_type == BTN_TOOL_FINGER) { if (features->device_type == BTN_TOOL_FINGER) {
input_mt_init_slots(input_dev, features->touch_max, 0); unsigned int flags = INPUT_MT_DIRECT;
input_set_abs_params(input_dev, ABS_MT_TOOL_TYPE,
0, MT_TOOL_MAX, 0, 0); if (wacom_wac->features.type == TABLETPC2FG)
input_set_abs_params(input_dev, ABS_MT_POSITION_X, flags = 0;
0, features->x_max, 0, 0);
input_set_abs_params(input_dev, ABS_MT_POSITION_Y, input_mt_init_slots(input_dev, features->touch_max, flags);
0, features->y_max, 0, 0);
} }
/* fall through */ /* fall through */
@ -1725,35 +1715,26 @@ int wacom_setup_input_capabilities(struct input_dev *input_dev,
__set_bit(INPUT_PROP_POINTER, input_dev->propbit); __set_bit(INPUT_PROP_POINTER, input_dev->propbit);
if (features->device_type == BTN_TOOL_FINGER) { if (features->device_type == BTN_TOOL_FINGER) {
unsigned int flags = INPUT_MT_POINTER;
__set_bit(BTN_LEFT, input_dev->keybit); __set_bit(BTN_LEFT, input_dev->keybit);
__set_bit(BTN_FORWARD, input_dev->keybit); __set_bit(BTN_FORWARD, input_dev->keybit);
__set_bit(BTN_BACK, input_dev->keybit); __set_bit(BTN_BACK, input_dev->keybit);
__set_bit(BTN_RIGHT, input_dev->keybit); __set_bit(BTN_RIGHT, input_dev->keybit);
__set_bit(BTN_TOOL_FINGER, input_dev->keybit);
__set_bit(BTN_TOOL_DOUBLETAP, input_dev->keybit);
input_mt_init_slots(input_dev, features->touch_max, 0);
if (features->pktlen == WACOM_PKGLEN_BBTOUCH3) { if (features->pktlen == WACOM_PKGLEN_BBTOUCH3) {
__set_bit(BTN_TOOL_TRIPLETAP,
input_dev->keybit);
__set_bit(BTN_TOOL_QUADTAP,
input_dev->keybit);
input_set_abs_params(input_dev, input_set_abs_params(input_dev,
ABS_MT_TOUCH_MAJOR, ABS_MT_TOUCH_MAJOR,
0, features->x_max, 0, 0); 0, features->x_max, 0, 0);
input_set_abs_params(input_dev, input_set_abs_params(input_dev,
ABS_MT_TOUCH_MINOR, ABS_MT_TOUCH_MINOR,
0, features->y_max, 0, 0); 0, features->y_max, 0, 0);
} else {
__set_bit(BTN_TOOL_FINGER, input_dev->keybit);
__set_bit(BTN_TOOL_DOUBLETAP, input_dev->keybit);
flags = 0;
} }
input_mt_init_slots(input_dev, features->touch_max, flags);
input_set_abs_params(input_dev, ABS_MT_POSITION_X,
0, features->x_max,
features->x_fuzz, 0);
input_set_abs_params(input_dev, ABS_MT_POSITION_Y,
0, features->y_max,
features->y_fuzz, 0);
} else if (features->device_type == BTN_TOOL_PEN) { } else if (features->device_type == BTN_TOOL_PEN) {
__set_bit(BTN_TOOL_RUBBER, input_dev->keybit); __set_bit(BTN_TOOL_RUBBER, input_dev->keybit);
__set_bit(BTN_TOOL_PEN, input_dev->keybit); __set_bit(BTN_TOOL_PEN, input_dev->keybit);
@ -1978,6 +1959,13 @@ static const struct wacom_features wacom_features_0xCE =
static const struct wacom_features wacom_features_0xF0 = static const struct wacom_features wacom_features_0xF0 =
{ "Wacom DTU1631", WACOM_PKGLEN_GRAPHIRE, 34623, 19553, 511, { "Wacom DTU1631", WACOM_PKGLEN_GRAPHIRE, 34623, 19553, 511,
0, DTU, WACOM_INTUOS_RES, WACOM_INTUOS_RES }; 0, DTU, WACOM_INTUOS_RES, WACOM_INTUOS_RES };
static const struct wacom_features wacom_features_0x59 = /* Pen */
{ "Wacom DTH2242", WACOM_PKGLEN_INTUOS, 95840, 54260, 2047,
63, DTK, WACOM_INTUOS3_RES, WACOM_INTUOS3_RES,
.oVid = USB_VENDOR_ID_WACOM, .oPid = 0x5D };
static const struct wacom_features wacom_features_0x5D = /* Touch */
{ "Wacom DTH2242", .type = WACOM_24HDT,
.oVid = USB_VENDOR_ID_WACOM, .oPid = 0x59, .touch_max = 10 };
static const struct wacom_features wacom_features_0xCC = static const struct wacom_features wacom_features_0xCC =
{ "Wacom Cintiq 21UX2", WACOM_PKGLEN_INTUOS, 87200, 65600, 2047, { "Wacom Cintiq 21UX2", WACOM_PKGLEN_INTUOS, 87200, 65600, 2047,
63, WACOM_21UX2, WACOM_INTUOS3_RES, WACOM_INTUOS3_RES }; 63, WACOM_21UX2, WACOM_INTUOS3_RES, WACOM_INTUOS3_RES };
@ -2152,6 +2140,8 @@ const struct usb_device_id wacom_ids[] = {
{ USB_DEVICE_WACOM(0x43) }, { USB_DEVICE_WACOM(0x43) },
{ USB_DEVICE_WACOM(0x44) }, { USB_DEVICE_WACOM(0x44) },
{ USB_DEVICE_WACOM(0x45) }, { USB_DEVICE_WACOM(0x45) },
{ USB_DEVICE_WACOM(0x59) },
{ USB_DEVICE_WACOM(0x5D) },
{ USB_DEVICE_WACOM(0xB0) }, { USB_DEVICE_WACOM(0xB0) },
{ USB_DEVICE_WACOM(0xB1) }, { USB_DEVICE_WACOM(0xB1) },
{ USB_DEVICE_WACOM(0xB2) }, { USB_DEVICE_WACOM(0xB2) },

View File

@ -78,6 +78,7 @@ enum {
INTUOS5L, INTUOS5L,
WACOM_21UX2, WACOM_21UX2,
WACOM_22HD, WACOM_22HD,
DTK,
WACOM_24HD, WACOM_24HD,
CINTIQ, CINTIQ,
WACOM_BEE, WACOM_BEE,
@ -135,7 +136,6 @@ struct wacom_wac {
int pid; int pid;
int battery_capacity; int battery_capacity;
int num_contacts_left; int num_contacts_left;
int *slots;
}; };
#endif #endif

View File

@ -359,7 +359,7 @@ config TOUCHSCREEN_MCS5000
config TOUCHSCREEN_MMS114 config TOUCHSCREEN_MMS114
tristate "MELFAS MMS114 touchscreen" tristate "MELFAS MMS114 touchscreen"
depends on I2C depends on I2C && GENERIC_HARDIRQS
help help
Say Y here if you have the MELFAS MMS114 touchscreen controller Say Y here if you have the MELFAS MMS114 touchscreen controller
chip in your system. chip in your system.

View File

@ -193,7 +193,6 @@ static struct spi_driver cyttsp_spi_driver = {
module_spi_driver(cyttsp_spi_driver); module_spi_driver(cyttsp_spi_driver);
MODULE_ALIAS("spi:cyttsp");
MODULE_LICENSE("GPL"); MODULE_LICENSE("GPL");
MODULE_DESCRIPTION("Cypress TrueTouch(R) Standard Product (TTSP) SPI driver"); MODULE_DESCRIPTION("Cypress TrueTouch(R) Standard Product (TTSP) SPI driver");
MODULE_AUTHOR("Cypress"); MODULE_AUTHOR("Cypress");

View File

@ -429,12 +429,12 @@ static int mms114_probe(struct i2c_client *client,
return -ENODEV; return -ENODEV;
} }
data = kzalloc(sizeof(struct mms114_data), GFP_KERNEL); data = devm_kzalloc(&client->dev, sizeof(struct mms114_data),
input_dev = input_allocate_device(); GFP_KERNEL);
input_dev = devm_input_allocate_device(&client->dev);
if (!data || !input_dev) { if (!data || !input_dev) {
dev_err(&client->dev, "Failed to allocate memory\n"); dev_err(&client->dev, "Failed to allocate memory\n");
error = -ENOMEM; return -ENOMEM;
goto err_free_mem;
} }
data->client = client; data->client = client;
@ -466,57 +466,36 @@ static int mms114_probe(struct i2c_client *client,
input_set_drvdata(input_dev, data); input_set_drvdata(input_dev, data);
i2c_set_clientdata(client, data); i2c_set_clientdata(client, data);
data->core_reg = regulator_get(&client->dev, "avdd"); data->core_reg = devm_regulator_get(&client->dev, "avdd");
if (IS_ERR(data->core_reg)) { if (IS_ERR(data->core_reg)) {
error = PTR_ERR(data->core_reg); error = PTR_ERR(data->core_reg);
dev_err(&client->dev, dev_err(&client->dev,
"Unable to get the Core regulator (%d)\n", error); "Unable to get the Core regulator (%d)\n", error);
goto err_free_mem; return error;
} }
data->io_reg = regulator_get(&client->dev, "vdd"); data->io_reg = devm_regulator_get(&client->dev, "vdd");
if (IS_ERR(data->io_reg)) { if (IS_ERR(data->io_reg)) {
error = PTR_ERR(data->io_reg); error = PTR_ERR(data->io_reg);
dev_err(&client->dev, dev_err(&client->dev,
"Unable to get the IO regulator (%d)\n", error); "Unable to get the IO regulator (%d)\n", error);
goto err_core_reg; return error;
} }
error = request_threaded_irq(client->irq, NULL, mms114_interrupt, error = devm_request_threaded_irq(&client->dev, client->irq, NULL,
IRQF_TRIGGER_FALLING | IRQF_ONESHOT, "mms114", data); mms114_interrupt, IRQF_TRIGGER_FALLING | IRQF_ONESHOT,
dev_name(&client->dev), data);
if (error) { if (error) {
dev_err(&client->dev, "Failed to register interrupt\n"); dev_err(&client->dev, "Failed to register interrupt\n");
goto err_io_reg; return error;
} }
disable_irq(client->irq); disable_irq(client->irq);
error = input_register_device(data->input_dev); error = input_register_device(data->input_dev);
if (error) if (error) {
goto err_free_irq; dev_err(&client->dev, "Failed to register input device\n");
return 0;
err_free_irq:
free_irq(client->irq, data);
err_io_reg:
regulator_put(data->io_reg);
err_core_reg:
regulator_put(data->core_reg);
err_free_mem:
input_free_device(input_dev);
kfree(data);
return error; return error;
} }
static int mms114_remove(struct i2c_client *client)
{
struct mms114_data *data = i2c_get_clientdata(client);
free_irq(client->irq, data);
regulator_put(data->io_reg);
regulator_put(data->core_reg);
input_unregister_device(data->input_dev);
kfree(data);
return 0; return 0;
} }
@ -590,7 +569,6 @@ static struct i2c_driver mms114_driver = {
.of_match_table = of_match_ptr(mms114_dt_match), .of_match_table = of_match_ptr(mms114_dt_match),
}, },
.probe = mms114_probe, .probe = mms114_probe,
.remove = mms114_remove,
.id_table = mms114_id, .id_table = mms114_id,
}; };

View File

@ -120,6 +120,7 @@ static void stmpe_work(struct work_struct *work)
__stmpe_reset_fifo(ts->stmpe); __stmpe_reset_fifo(ts->stmpe);
input_report_abs(ts->idev, ABS_PRESSURE, 0); input_report_abs(ts->idev, ABS_PRESSURE, 0);
input_report_key(ts->idev, BTN_TOUCH, 0);
input_sync(ts->idev); input_sync(ts->idev);
} }
@ -153,6 +154,7 @@ static irqreturn_t stmpe_ts_handler(int irq, void *data)
input_report_abs(ts->idev, ABS_X, x); input_report_abs(ts->idev, ABS_X, x);
input_report_abs(ts->idev, ABS_Y, y); input_report_abs(ts->idev, ABS_Y, y);
input_report_abs(ts->idev, ABS_PRESSURE, z); input_report_abs(ts->idev, ABS_PRESSURE, z);
input_report_key(ts->idev, BTN_TOUCH, 1);
input_sync(ts->idev); input_sync(ts->idev);
/* flush the FIFO after we have read out our values. */ /* flush the FIFO after we have read out our values. */

View File

@ -753,3 +753,4 @@ module_spi_driver(tsc2005_driver);
MODULE_AUTHOR("Lauri Leukkunen <lauri.leukkunen@nokia.com>"); MODULE_AUTHOR("Lauri Leukkunen <lauri.leukkunen@nokia.com>");
MODULE_DESCRIPTION("TSC2005 Touchscreen Driver"); MODULE_DESCRIPTION("TSC2005 Touchscreen Driver");
MODULE_LICENSE("GPL"); MODULE_LICENSE("GPL");
MODULE_ALIAS("spi:tsc2005");

View File

@ -247,7 +247,7 @@ static int wm831x_ts_probe(struct platform_device *pdev)
wm831x_ts = devm_kzalloc(&pdev->dev, sizeof(struct wm831x_ts), wm831x_ts = devm_kzalloc(&pdev->dev, sizeof(struct wm831x_ts),
GFP_KERNEL); GFP_KERNEL);
input_dev = input_allocate_device(); input_dev = devm_input_allocate_device(&pdev->dev);
if (!wm831x_ts || !input_dev) { if (!wm831x_ts || !input_dev) {
error = -ENOMEM; error = -ENOMEM;
goto err_alloc; goto err_alloc;
@ -376,7 +376,6 @@ err_pd_irq:
err_data_irq: err_data_irq:
free_irq(wm831x_ts->data_irq, wm831x_ts); free_irq(wm831x_ts->data_irq, wm831x_ts);
err_alloc: err_alloc:
input_free_device(input_dev);
return error; return error;
} }
@ -387,7 +386,6 @@ static int wm831x_ts_remove(struct platform_device *pdev)
free_irq(wm831x_ts->pd_irq, wm831x_ts); free_irq(wm831x_ts->pd_irq, wm831x_ts);
free_irq(wm831x_ts->data_irq, wm831x_ts); free_irq(wm831x_ts->data_irq, wm831x_ts);
input_unregister_device(wm831x_ts->input_dev);
return 0; return 0;
} }

View File

@ -42,6 +42,7 @@
#include <linux/slab.h> #include <linux/slab.h>
#include <linux/input.h> #include <linux/input.h>
#include <linux/uaccess.h> #include <linux/uaccess.h>
#include <linux/moduleparam.h>
#include <asm/ptrace.h> #include <asm/ptrace.h>
#include <asm/irq_regs.h> #include <asm/irq_regs.h>
@ -578,8 +579,71 @@ struct sysrq_state {
bool active; bool active;
bool need_reinject; bool need_reinject;
bool reinjecting; bool reinjecting;
/* reset sequence handling */
bool reset_canceled;
unsigned long reset_keybit[BITS_TO_LONGS(KEY_CNT)];
int reset_seq_len;
int reset_seq_cnt;
int reset_seq_version;
}; };
#define SYSRQ_KEY_RESET_MAX 20 /* Should be plenty */
static unsigned short sysrq_reset_seq[SYSRQ_KEY_RESET_MAX];
static unsigned int sysrq_reset_seq_len;
static unsigned int sysrq_reset_seq_version = 1;
static void sysrq_parse_reset_sequence(struct sysrq_state *state)
{
int i;
unsigned short key;
state->reset_seq_cnt = 0;
for (i = 0; i < sysrq_reset_seq_len; i++) {
key = sysrq_reset_seq[i];
if (key == KEY_RESERVED || key > KEY_MAX)
break;
__set_bit(key, state->reset_keybit);
state->reset_seq_len++;
if (test_bit(key, state->key_down))
state->reset_seq_cnt++;
}
/* Disable reset until old keys are not released */
state->reset_canceled = state->reset_seq_cnt != 0;
state->reset_seq_version = sysrq_reset_seq_version;
}
static bool sysrq_detect_reset_sequence(struct sysrq_state *state,
unsigned int code, int value)
{
if (!test_bit(code, state->reset_keybit)) {
/*
* Pressing any key _not_ in reset sequence cancels
* the reset sequence.
*/
if (value && state->reset_seq_cnt)
state->reset_canceled = true;
} else if (value == 0) {
/* key release */
if (--state->reset_seq_cnt == 0)
state->reset_canceled = false;
} else if (value == 1) {
/* key press, not autorepeat */
if (++state->reset_seq_cnt == state->reset_seq_len &&
!state->reset_canceled) {
return true;
}
}
return false;
}
static void sysrq_reinject_alt_sysrq(struct work_struct *work) static void sysrq_reinject_alt_sysrq(struct work_struct *work)
{ {
struct sysrq_state *sysrq = struct sysrq_state *sysrq =
@ -606,27 +670,12 @@ static void sysrq_reinject_alt_sysrq(struct work_struct *work)
} }
} }
static bool sysrq_filter(struct input_handle *handle, static bool sysrq_handle_keypress(struct sysrq_state *sysrq,
unsigned int type, unsigned int code, int value) unsigned int code, int value)
{ {
struct sysrq_state *sysrq = handle->private;
bool was_active = sysrq->active; bool was_active = sysrq->active;
bool suppress; bool suppress;
/*
* Do not filter anything if we are in the process of re-injecting
* Alt+SysRq combination.
*/
if (sysrq->reinjecting)
return false;
switch (type) {
case EV_SYN:
suppress = false;
break;
case EV_KEY:
switch (code) { switch (code) {
case KEY_LEFTALT: case KEY_LEFTALT:
@ -664,7 +713,7 @@ static bool sysrq_filter(struct input_handle *handle,
* triggering print screen function. * triggering print screen function.
*/ */
if (sysrq->active) if (sysrq->active)
clear_bit(KEY_SYSRQ, handle->dev->key); clear_bit(KEY_SYSRQ, sysrq->handle.dev->key);
break; break;
@ -679,6 +728,13 @@ static bool sysrq_filter(struct input_handle *handle,
suppress = sysrq->active; suppress = sysrq->active;
if (!sysrq->active) { if (!sysrq->active) {
/*
* See if reset sequence has changed since the last time.
*/
if (sysrq->reset_seq_version != sysrq_reset_seq_version)
sysrq_parse_reset_sequence(sysrq);
/* /*
* If we are not suppressing key presses keep track of * If we are not suppressing key presses keep track of
* keyboard state so we can release keys that have been * keyboard state so we can release keys that have been
@ -692,14 +748,43 @@ static bool sysrq_filter(struct input_handle *handle,
if (was_active) if (was_active)
schedule_work(&sysrq->reinject_work); schedule_work(&sysrq->reinject_work);
} else if (value == 0 && if (sysrq_detect_reset_sequence(sysrq, code, value)) {
test_and_clear_bit(code, sysrq->key_down)) { /* Force emergency reboot */
__handle_sysrq(sysrq_xlate[KEY_B], false);
}
} else if (value == 0 && test_and_clear_bit(code, sysrq->key_down)) {
/* /*
* Pass on release events for keys that was pressed before * Pass on release events for keys that was pressed before
* entering SysRq mode. * entering SysRq mode.
*/ */
suppress = false; suppress = false;
} }
return suppress;
}
static bool sysrq_filter(struct input_handle *handle,
unsigned int type, unsigned int code, int value)
{
struct sysrq_state *sysrq = handle->private;
bool suppress;
/*
* Do not filter anything if we are in the process of re-injecting
* Alt+SysRq combination.
*/
if (sysrq->reinjecting)
return false;
switch (type) {
case EV_SYN:
suppress = false;
break;
case EV_KEY:
suppress = sysrq_handle_keypress(sysrq, code, value);
break; break;
default: default:
@ -787,7 +872,20 @@ static bool sysrq_handler_registered;
static inline void sysrq_register_handler(void) static inline void sysrq_register_handler(void)
{ {
extern unsigned short platform_sysrq_reset_seq[] __weak;
unsigned short key;
int error; int error;
int i;
if (platform_sysrq_reset_seq) {
for (i = 0; i < ARRAY_SIZE(sysrq_reset_seq); i++) {
key = platform_sysrq_reset_seq[i];
if (key == KEY_RESERVED || key > KEY_MAX)
break;
sysrq_reset_seq[sysrq_reset_seq_len++] = key;
}
}
error = input_register_handler(&sysrq_handler); error = input_register_handler(&sysrq_handler);
if (error) if (error)
@ -804,6 +902,36 @@ static inline void sysrq_unregister_handler(void)
} }
} }
static int sysrq_reset_seq_param_set(const char *buffer,
const struct kernel_param *kp)
{
unsigned long val;
int error;
error = strict_strtoul(buffer, 0, &val);
if (error < 0)
return error;
if (val > KEY_MAX)
return -EINVAL;
*((unsigned short *)kp->arg) = val;
sysrq_reset_seq_version++;
return 0;
}
static struct kernel_param_ops param_ops_sysrq_reset_seq = {
.get = param_get_ushort,
.set = sysrq_reset_seq_param_set,
};
#define param_check_sysrq_reset_seq(name, p) \
__param_check(name, p, unsigned short)
module_param_array_named(reset_seq, sysrq_reset_seq, sysrq_reset_seq,
&sysrq_reset_seq_len, 0644);
#else #else
static inline void sysrq_register_handler(void) static inline void sysrq_register_handler(void)

View File

@ -22,6 +22,18 @@
#define BMA150_DRIVER "bma150" #define BMA150_DRIVER "bma150"
#define BMA150_RANGE_2G 0
#define BMA150_RANGE_4G 1
#define BMA150_RANGE_8G 2
#define BMA150_BW_25HZ 0
#define BMA150_BW_50HZ 1
#define BMA150_BW_100HZ 2
#define BMA150_BW_190HZ 3
#define BMA150_BW_375HZ 4
#define BMA150_BW_750HZ 5
#define BMA150_BW_1500HZ 6
struct bma150_cfg { struct bma150_cfg {
bool any_motion_int; /* Set to enable any-motion interrupt */ bool any_motion_int; /* Set to enable any-motion interrupt */
bool hg_int; /* Set to enable high-G interrupt */ bool hg_int; /* Set to enable high-G interrupt */
@ -34,8 +46,8 @@ struct bma150_cfg {
unsigned char lg_hyst; /* Low-G hysterisis */ unsigned char lg_hyst; /* Low-G hysterisis */
unsigned char lg_dur; /* Low-G duration */ unsigned char lg_dur; /* Low-G duration */
unsigned char lg_thres; /* Low-G threshold */ unsigned char lg_thres; /* Low-G threshold */
unsigned char range; /* BMA0150_RANGE_xxx (in G) */ unsigned char range; /* one of BMA0150_RANGE_xxx */
unsigned char bandwidth; /* BMA0150_BW_xxx (in Hz) */ unsigned char bandwidth; /* one of BMA0150_BW_xxx */
}; };
struct bma150_platform_data { struct bma150_platform_data {

View File

@ -13,6 +13,8 @@
#ifndef __LINUX_INPUT_ADXL34X_H__ #ifndef __LINUX_INPUT_ADXL34X_H__
#define __LINUX_INPUT_ADXL34X_H__ #define __LINUX_INPUT_ADXL34X_H__
#include <linux/input.h>
struct adxl34x_platform_data { struct adxl34x_platform_data {
/* /*

View File

@ -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

View File

@ -36,7 +36,7 @@ struct ps2dev {
wait_queue_head_t wait; wait_queue_head_t wait;
unsigned long flags; unsigned long flags;
unsigned char cmdbuf[6]; unsigned char cmdbuf[8];
unsigned char cmdcnt; unsigned char cmdcnt;
unsigned char nak; unsigned char nak;
}; };