mirror of
https://github.com/FEX-Emu/linux.git
synced 2024-11-30 11:02:19 +00:00
Merge branch 'for-linus' of git://git.kernel.org/pub/scm/linux/kernel/git/dtor/input
Pull input updates from Dmitry Torokhov: "Mostly existing driver fixes plus a new driver for game controllers directly connected to Nintendo 64, and an enhancement for keyboards driven by Chrome OS EC to communicate layout of the top row to userspace" * 'for-linus' of git://git.kernel.org/pub/scm/linux/kernel/git/dtor/input: (47 commits) Input: st1232 - fix NORMAL vs. IDLE state handling Input: aiptek - convert sysfs sprintf/snprintf family to sysfs_emit Input: alps - fix spelling of "positive" ARM: dts: cros-ec-keyboard: Use keymap macros dt-bindings: input: Fix the keymap for LOCK key dt-bindings: input: Create macros for cros-ec keymap Input: cros-ec-keyb - expose function row physical map to userspace dt-bindings: input: cros-ec-keyb: Add a new property describing top row Input: applespi - fix occasional crc errors under load. Input: applespi - don't wait for responses to commands indefinitely. Input: st1232 - add IDLE state as ready condition Input: zinitix - fix return type of zinitix_init_touch() Input: i8042 - add ASUS Zenbook Flip to noselftest list Input: add missing dependencies on CONFIG_HAS_IOMEM Input: joydev - prevent potential read overflow in ioctl Input: elo - fix an error code in elo_connect() Input: xpad - add support for PowerA Enhanced Wired Controller for Xbox Series X|S Input: sur40 - fix an error code in sur40_probe() Input: elants_i2c - detect enum overflow Input: zinitix - remove unneeded semicolon ...
This commit is contained in:
commit
e0fbd25bb3
@ -0,0 +1,6 @@
|
||||
What: /sys/class/input/input(x)/device/function_row_physmap
|
||||
Date: January 2021
|
||||
Contact: Philip Chen <philipchen@chromium.org>
|
||||
Description: A space separated list of scancodes for the top row keys,
|
||||
ordered by the physical positions of the keys, from left
|
||||
to right.
|
@ -31,6 +31,17 @@ properties:
|
||||
if the EC does not have its own logic or hardware for this.
|
||||
type: boolean
|
||||
|
||||
function-row-physmap:
|
||||
minItems: 1
|
||||
maxItems: 15
|
||||
description: |
|
||||
An ordered u32 array describing the rows/columns (in the scan matrix)
|
||||
of top row keys from physical left (KEY_F1) to right. Each entry
|
||||
encodes the row/column as:
|
||||
(((row) & 0xFF) << 24) | (((column) & 0xFF) << 16)
|
||||
where the lower 16 bits are reserved. This property is specified only
|
||||
when the keyboard has a custom design for the top row keys.
|
||||
|
||||
required:
|
||||
- compatible
|
||||
|
||||
@ -38,11 +49,24 @@ unevaluatedProperties: false
|
||||
|
||||
examples:
|
||||
- |
|
||||
#include <dt-bindings/input/input.h>
|
||||
cros-ec-keyb {
|
||||
compatible = "google,cros-ec-keyb";
|
||||
keypad,num-rows = <8>;
|
||||
keypad,num-columns = <13>;
|
||||
google,needs-ghost-filter;
|
||||
function-row-physmap = <
|
||||
MATRIX_KEY(0x00, 0x02, 0) /* T1 */
|
||||
MATRIX_KEY(0x03, 0x02, 0) /* T2 */
|
||||
MATRIX_KEY(0x02, 0x02, 0) /* T3 */
|
||||
MATRIX_KEY(0x01, 0x02, 0) /* T4 */
|
||||
MATRIX_KEY(0x03, 0x04, 0) /* T5 */
|
||||
MATRIX_KEY(0x02, 0x04, 0) /* T6 */
|
||||
MATRIX_KEY(0x01, 0x04, 0) /* T7 */
|
||||
MATRIX_KEY(0x02, 0x09, 0) /* T8 */
|
||||
MATRIX_KEY(0x01, 0x09, 0) /* T9 */
|
||||
MATRIX_KEY(0x00, 0x04, 0) /* T10 */
|
||||
>;
|
||||
/*
|
||||
* Keymap entries take the form of 0xRRCCKKKK where
|
||||
* RR=Row CC=Column KKKK=Key Code
|
||||
|
@ -6,103 +6,18 @@
|
||||
*/
|
||||
|
||||
#include <dt-bindings/input/input.h>
|
||||
#include <dt-bindings/input/cros-ec-keyboard.h>
|
||||
|
||||
&cros_ec {
|
||||
keyboard-controller {
|
||||
keyboard_controller: keyboard-controller {
|
||||
compatible = "google,cros-ec-keyb";
|
||||
keypad,num-rows = <8>;
|
||||
keypad,num-columns = <13>;
|
||||
google,needs-ghost-filter;
|
||||
|
||||
linux,keymap = <
|
||||
MATRIX_KEY(0x00, 0x01, KEY_LEFTMETA)
|
||||
MATRIX_KEY(0x00, 0x02, KEY_F1)
|
||||
MATRIX_KEY(0x00, 0x03, KEY_B)
|
||||
MATRIX_KEY(0x00, 0x04, KEY_F10)
|
||||
MATRIX_KEY(0x00, 0x05, KEY_RO)
|
||||
MATRIX_KEY(0x00, 0x06, KEY_N)
|
||||
MATRIX_KEY(0x00, 0x08, KEY_EQUAL)
|
||||
MATRIX_KEY(0x00, 0x0a, KEY_RIGHTALT)
|
||||
|
||||
MATRIX_KEY(0x01, 0x01, KEY_ESC)
|
||||
MATRIX_KEY(0x01, 0x02, KEY_F4)
|
||||
MATRIX_KEY(0x01, 0x03, KEY_G)
|
||||
MATRIX_KEY(0x01, 0x04, KEY_F7)
|
||||
MATRIX_KEY(0x01, 0x06, KEY_H)
|
||||
MATRIX_KEY(0x01, 0x08, KEY_APOSTROPHE)
|
||||
MATRIX_KEY(0x01, 0x09, KEY_F9)
|
||||
MATRIX_KEY(0x01, 0x0b, KEY_BACKSPACE)
|
||||
MATRIX_KEY(0x01, 0x0c, KEY_HENKAN)
|
||||
|
||||
MATRIX_KEY(0x02, 0x00, KEY_LEFTCTRL)
|
||||
MATRIX_KEY(0x02, 0x01, KEY_TAB)
|
||||
MATRIX_KEY(0x02, 0x02, KEY_F3)
|
||||
MATRIX_KEY(0x02, 0x03, KEY_T)
|
||||
MATRIX_KEY(0x02, 0x04, KEY_F6)
|
||||
MATRIX_KEY(0x02, 0x05, KEY_RIGHTBRACE)
|
||||
MATRIX_KEY(0x02, 0x06, KEY_Y)
|
||||
MATRIX_KEY(0x02, 0x07, KEY_102ND)
|
||||
MATRIX_KEY(0x02, 0x08, KEY_LEFTBRACE)
|
||||
MATRIX_KEY(0x02, 0x09, KEY_F8)
|
||||
MATRIX_KEY(0x02, 0x0a, KEY_YEN)
|
||||
|
||||
MATRIX_KEY(0x03, 0x00, KEY_LEFTMETA)
|
||||
MATRIX_KEY(0x03, 0x01, KEY_GRAVE)
|
||||
MATRIX_KEY(0x03, 0x02, KEY_F2)
|
||||
MATRIX_KEY(0x03, 0x03, KEY_5)
|
||||
MATRIX_KEY(0x03, 0x04, KEY_F5)
|
||||
MATRIX_KEY(0x03, 0x06, KEY_6)
|
||||
MATRIX_KEY(0x03, 0x08, KEY_MINUS)
|
||||
MATRIX_KEY(0x03, 0x09, KEY_F13)
|
||||
MATRIX_KEY(0x03, 0x0b, KEY_BACKSLASH)
|
||||
MATRIX_KEY(0x03, 0x0c, KEY_MUHENKAN)
|
||||
|
||||
MATRIX_KEY(0x04, 0x00, KEY_RIGHTCTRL)
|
||||
MATRIX_KEY(0x04, 0x01, KEY_A)
|
||||
MATRIX_KEY(0x04, 0x02, KEY_D)
|
||||
MATRIX_KEY(0x04, 0x03, KEY_F)
|
||||
MATRIX_KEY(0x04, 0x04, KEY_S)
|
||||
MATRIX_KEY(0x04, 0x05, KEY_K)
|
||||
MATRIX_KEY(0x04, 0x06, KEY_J)
|
||||
MATRIX_KEY(0x04, 0x08, KEY_SEMICOLON)
|
||||
MATRIX_KEY(0x04, 0x09, KEY_L)
|
||||
MATRIX_KEY(0x04, 0x0a, KEY_BACKSLASH)
|
||||
MATRIX_KEY(0x04, 0x0b, KEY_ENTER)
|
||||
|
||||
MATRIX_KEY(0x05, 0x01, KEY_Z)
|
||||
MATRIX_KEY(0x05, 0x02, KEY_C)
|
||||
MATRIX_KEY(0x05, 0x03, KEY_V)
|
||||
MATRIX_KEY(0x05, 0x04, KEY_X)
|
||||
MATRIX_KEY(0x05, 0x05, KEY_COMMA)
|
||||
MATRIX_KEY(0x05, 0x06, KEY_M)
|
||||
MATRIX_KEY(0x05, 0x07, KEY_LEFTSHIFT)
|
||||
MATRIX_KEY(0x05, 0x08, KEY_SLASH)
|
||||
MATRIX_KEY(0x05, 0x09, KEY_DOT)
|
||||
MATRIX_KEY(0x05, 0x0b, KEY_SPACE)
|
||||
|
||||
MATRIX_KEY(0x06, 0x01, KEY_1)
|
||||
MATRIX_KEY(0x06, 0x02, KEY_3)
|
||||
MATRIX_KEY(0x06, 0x03, KEY_4)
|
||||
MATRIX_KEY(0x06, 0x04, KEY_2)
|
||||
MATRIX_KEY(0x06, 0x05, KEY_8)
|
||||
MATRIX_KEY(0x06, 0x06, KEY_7)
|
||||
MATRIX_KEY(0x06, 0x08, KEY_0)
|
||||
MATRIX_KEY(0x06, 0x09, KEY_9)
|
||||
MATRIX_KEY(0x06, 0x0a, KEY_LEFTALT)
|
||||
MATRIX_KEY(0x06, 0x0b, KEY_DOWN)
|
||||
MATRIX_KEY(0x06, 0x0c, KEY_RIGHT)
|
||||
|
||||
MATRIX_KEY(0x07, 0x01, KEY_Q)
|
||||
MATRIX_KEY(0x07, 0x02, KEY_E)
|
||||
MATRIX_KEY(0x07, 0x03, KEY_R)
|
||||
MATRIX_KEY(0x07, 0x04, KEY_W)
|
||||
MATRIX_KEY(0x07, 0x05, KEY_I)
|
||||
MATRIX_KEY(0x07, 0x06, KEY_U)
|
||||
MATRIX_KEY(0x07, 0x07, KEY_RIGHTSHIFT)
|
||||
MATRIX_KEY(0x07, 0x08, KEY_P)
|
||||
MATRIX_KEY(0x07, 0x09, KEY_O)
|
||||
MATRIX_KEY(0x07, 0x0b, KEY_UP)
|
||||
MATRIX_KEY(0x07, 0x0c, KEY_LEFT)
|
||||
CROS_STD_TOP_ROW_KEYMAP
|
||||
CROS_STD_MAIN_KEYMAP
|
||||
>;
|
||||
};
|
||||
};
|
||||
|
@ -456,7 +456,7 @@ static int joydev_handle_JSIOCSAXMAP(struct joydev *joydev,
|
||||
if (IS_ERR(abspam))
|
||||
return PTR_ERR(abspam);
|
||||
|
||||
for (i = 0; i < joydev->nabs; i++) {
|
||||
for (i = 0; i < len && i < joydev->nabs; i++) {
|
||||
if (abspam[i] > ABS_MAX) {
|
||||
retval = -EINVAL;
|
||||
goto out;
|
||||
@ -480,6 +480,9 @@ static int joydev_handle_JSIOCSBTNMAP(struct joydev *joydev,
|
||||
int i;
|
||||
int retval = 0;
|
||||
|
||||
if (len % sizeof(*keypam))
|
||||
return -EINVAL;
|
||||
|
||||
len = min(len, sizeof(joydev->keypam));
|
||||
|
||||
/* Validate the map. */
|
||||
@ -487,7 +490,7 @@ static int joydev_handle_JSIOCSBTNMAP(struct joydev *joydev,
|
||||
if (IS_ERR(keypam))
|
||||
return PTR_ERR(keypam);
|
||||
|
||||
for (i = 0; i < joydev->nkey; i++) {
|
||||
for (i = 0; i < (len / 2) && i < joydev->nkey; i++) {
|
||||
if (keypam[i] > KEY_MAX || keypam[i] < BTN_MISC) {
|
||||
retval = -EINVAL;
|
||||
goto out;
|
||||
|
@ -382,4 +382,11 @@ config JOYSTICK_FSIA6B
|
||||
To compile this driver as a module, choose M here: the
|
||||
module will be called fsia6b.
|
||||
|
||||
config JOYSTICK_N64
|
||||
bool "N64 controller"
|
||||
depends on MACH_NINTENDO64
|
||||
help
|
||||
Say Y here if you want enable support for the four
|
||||
built-in controller ports on the Nintendo 64 console.
|
||||
|
||||
endif
|
||||
|
@ -24,6 +24,7 @@ obj-$(CONFIG_JOYSTICK_INTERACT) += interact.o
|
||||
obj-$(CONFIG_JOYSTICK_JOYDUMP) += joydump.o
|
||||
obj-$(CONFIG_JOYSTICK_MAGELLAN) += magellan.o
|
||||
obj-$(CONFIG_JOYSTICK_MAPLE) += maplecontrol.o
|
||||
obj-$(CONFIG_JOYSTICK_N64) += n64joy.o
|
||||
obj-$(CONFIG_JOYSTICK_PSXPAD_SPI) += psxpad-spi.o
|
||||
obj-$(CONFIG_JOYSTICK_PXRC) += pxrc.o
|
||||
obj-$(CONFIG_JOYSTICK_SIDEWINDER) += sidewinder.o
|
||||
@ -37,4 +38,3 @@ obj-$(CONFIG_JOYSTICK_WARRIOR) += warrior.o
|
||||
obj-$(CONFIG_JOYSTICK_WALKERA0701) += walkera0701.o
|
||||
obj-$(CONFIG_JOYSTICK_XPAD) += xpad.o
|
||||
obj-$(CONFIG_JOYSTICK_ZHENHUA) += zhenhua.o
|
||||
|
||||
|
345
drivers/input/joystick/n64joy.c
Normal file
345
drivers/input/joystick/n64joy.c
Normal file
@ -0,0 +1,345 @@
|
||||
// SPDX-License-Identifier: GPL-2.0
|
||||
/*
|
||||
* Support for the four N64 controllers.
|
||||
*
|
||||
* Copyright (c) 2021 Lauri Kasanen
|
||||
*/
|
||||
|
||||
#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
|
||||
|
||||
#include <linux/errno.h>
|
||||
#include <linux/init.h>
|
||||
#include <linux/input.h>
|
||||
#include <linux/limits.h>
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/mutex.h>
|
||||
#include <linux/platform_device.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/timer.h>
|
||||
|
||||
MODULE_AUTHOR("Lauri Kasanen <cand@gmx.com>");
|
||||
MODULE_DESCRIPTION("Driver for N64 controllers");
|
||||
MODULE_LICENSE("GPL");
|
||||
|
||||
#define PIF_RAM 0x1fc007c0
|
||||
|
||||
#define SI_DRAM_REG 0
|
||||
#define SI_READ_REG 1
|
||||
#define SI_WRITE_REG 4
|
||||
#define SI_STATUS_REG 6
|
||||
|
||||
#define SI_STATUS_DMA_BUSY BIT(0)
|
||||
#define SI_STATUS_IO_BUSY BIT(1)
|
||||
|
||||
#define N64_CONTROLLER_ID 0x0500
|
||||
|
||||
#define MAX_CONTROLLERS 4
|
||||
|
||||
static const char *n64joy_phys[MAX_CONTROLLERS] = {
|
||||
"n64joy/port0",
|
||||
"n64joy/port1",
|
||||
"n64joy/port2",
|
||||
"n64joy/port3",
|
||||
};
|
||||
|
||||
struct n64joy_priv {
|
||||
u64 si_buf[8] ____cacheline_aligned;
|
||||
struct timer_list timer;
|
||||
struct mutex n64joy_mutex;
|
||||
struct input_dev *n64joy_dev[MAX_CONTROLLERS];
|
||||
u32 __iomem *reg_base;
|
||||
u8 n64joy_opened;
|
||||
};
|
||||
|
||||
struct joydata {
|
||||
unsigned int: 16; /* unused */
|
||||
unsigned int err: 2;
|
||||
unsigned int: 14; /* unused */
|
||||
|
||||
union {
|
||||
u32 data;
|
||||
|
||||
struct {
|
||||
unsigned int a: 1;
|
||||
unsigned int b: 1;
|
||||
unsigned int z: 1;
|
||||
unsigned int start: 1;
|
||||
unsigned int up: 1;
|
||||
unsigned int down: 1;
|
||||
unsigned int left: 1;
|
||||
unsigned int right: 1;
|
||||
unsigned int: 2; /* unused */
|
||||
unsigned int l: 1;
|
||||
unsigned int r: 1;
|
||||
unsigned int c_up: 1;
|
||||
unsigned int c_down: 1;
|
||||
unsigned int c_left: 1;
|
||||
unsigned int c_right: 1;
|
||||
signed int x: 8;
|
||||
signed int y: 8;
|
||||
};
|
||||
};
|
||||
};
|
||||
|
||||
static void n64joy_write_reg(u32 __iomem *reg_base, const u8 reg, const u32 value)
|
||||
{
|
||||
writel(value, reg_base + reg);
|
||||
}
|
||||
|
||||
static u32 n64joy_read_reg(u32 __iomem *reg_base, const u8 reg)
|
||||
{
|
||||
return readl(reg_base + reg);
|
||||
}
|
||||
|
||||
static void n64joy_wait_si_dma(u32 __iomem *reg_base)
|
||||
{
|
||||
while (n64joy_read_reg(reg_base, SI_STATUS_REG) &
|
||||
(SI_STATUS_DMA_BUSY | SI_STATUS_IO_BUSY))
|
||||
cpu_relax();
|
||||
}
|
||||
|
||||
static void n64joy_exec_pif(struct n64joy_priv *priv, const u64 in[8])
|
||||
{
|
||||
unsigned long flags;
|
||||
|
||||
dma_cache_wback_inv((unsigned long) in, 8 * 8);
|
||||
dma_cache_inv((unsigned long) priv->si_buf, 8 * 8);
|
||||
|
||||
local_irq_save(flags);
|
||||
|
||||
n64joy_wait_si_dma(priv->reg_base);
|
||||
|
||||
barrier();
|
||||
n64joy_write_reg(priv->reg_base, SI_DRAM_REG, virt_to_phys(in));
|
||||
barrier();
|
||||
n64joy_write_reg(priv->reg_base, SI_WRITE_REG, PIF_RAM);
|
||||
barrier();
|
||||
|
||||
n64joy_wait_si_dma(priv->reg_base);
|
||||
|
||||
barrier();
|
||||
n64joy_write_reg(priv->reg_base, SI_DRAM_REG, virt_to_phys(priv->si_buf));
|
||||
barrier();
|
||||
n64joy_write_reg(priv->reg_base, SI_READ_REG, PIF_RAM);
|
||||
barrier();
|
||||
|
||||
n64joy_wait_si_dma(priv->reg_base);
|
||||
|
||||
local_irq_restore(flags);
|
||||
}
|
||||
|
||||
static const u64 polldata[] ____cacheline_aligned = {
|
||||
0xff010401ffffffff,
|
||||
0xff010401ffffffff,
|
||||
0xff010401ffffffff,
|
||||
0xff010401ffffffff,
|
||||
0xfe00000000000000,
|
||||
0,
|
||||
0,
|
||||
1
|
||||
};
|
||||
|
||||
static void n64joy_poll(struct timer_list *t)
|
||||
{
|
||||
const struct joydata *data;
|
||||
struct n64joy_priv *priv = container_of(t, struct n64joy_priv, timer);
|
||||
struct input_dev *dev;
|
||||
u32 i;
|
||||
|
||||
n64joy_exec_pif(priv, polldata);
|
||||
|
||||
data = (struct joydata *) priv->si_buf;
|
||||
|
||||
for (i = 0; i < MAX_CONTROLLERS; i++) {
|
||||
if (!priv->n64joy_dev[i])
|
||||
continue;
|
||||
|
||||
dev = priv->n64joy_dev[i];
|
||||
|
||||
/* d-pad */
|
||||
input_report_key(dev, BTN_DPAD_UP, data[i].up);
|
||||
input_report_key(dev, BTN_DPAD_DOWN, data[i].down);
|
||||
input_report_key(dev, BTN_DPAD_LEFT, data[i].left);
|
||||
input_report_key(dev, BTN_DPAD_RIGHT, data[i].right);
|
||||
|
||||
/* c buttons */
|
||||
input_report_key(dev, BTN_FORWARD, data[i].c_up);
|
||||
input_report_key(dev, BTN_BACK, data[i].c_down);
|
||||
input_report_key(dev, BTN_LEFT, data[i].c_left);
|
||||
input_report_key(dev, BTN_RIGHT, data[i].c_right);
|
||||
|
||||
/* matching buttons */
|
||||
input_report_key(dev, BTN_START, data[i].start);
|
||||
input_report_key(dev, BTN_Z, data[i].z);
|
||||
|
||||
/* remaining ones: a, b, l, r */
|
||||
input_report_key(dev, BTN_0, data[i].a);
|
||||
input_report_key(dev, BTN_1, data[i].b);
|
||||
input_report_key(dev, BTN_2, data[i].l);
|
||||
input_report_key(dev, BTN_3, data[i].r);
|
||||
|
||||
input_report_abs(dev, ABS_X, data[i].x);
|
||||
input_report_abs(dev, ABS_Y, data[i].y);
|
||||
|
||||
input_sync(dev);
|
||||
}
|
||||
|
||||
mod_timer(&priv->timer, jiffies + msecs_to_jiffies(16));
|
||||
}
|
||||
|
||||
static int n64joy_open(struct input_dev *dev)
|
||||
{
|
||||
struct n64joy_priv *priv = input_get_drvdata(dev);
|
||||
int err;
|
||||
|
||||
err = mutex_lock_interruptible(&priv->n64joy_mutex);
|
||||
if (err)
|
||||
return err;
|
||||
|
||||
if (!priv->n64joy_opened) {
|
||||
/*
|
||||
* We could use the vblank irq, but it's not important if
|
||||
* the poll point slightly changes.
|
||||
*/
|
||||
timer_setup(&priv->timer, n64joy_poll, 0);
|
||||
mod_timer(&priv->timer, jiffies + msecs_to_jiffies(16));
|
||||
}
|
||||
|
||||
priv->n64joy_opened++;
|
||||
|
||||
mutex_unlock(&priv->n64joy_mutex);
|
||||
return err;
|
||||
}
|
||||
|
||||
static void n64joy_close(struct input_dev *dev)
|
||||
{
|
||||
struct n64joy_priv *priv = input_get_drvdata(dev);
|
||||
|
||||
mutex_lock(&priv->n64joy_mutex);
|
||||
if (!--priv->n64joy_opened)
|
||||
del_timer_sync(&priv->timer);
|
||||
mutex_unlock(&priv->n64joy_mutex);
|
||||
}
|
||||
|
||||
static const u64 __initconst scandata[] ____cacheline_aligned = {
|
||||
0xff010300ffffffff,
|
||||
0xff010300ffffffff,
|
||||
0xff010300ffffffff,
|
||||
0xff010300ffffffff,
|
||||
0xfe00000000000000,
|
||||
0,
|
||||
0,
|
||||
1
|
||||
};
|
||||
|
||||
/*
|
||||
* The target device is embedded and RAM-constrained. We save RAM
|
||||
* by initializing in __init code that gets dropped late in boot.
|
||||
* For the same reason there is no module or unloading support.
|
||||
*/
|
||||
static int __init n64joy_probe(struct platform_device *pdev)
|
||||
{
|
||||
const struct joydata *data;
|
||||
struct n64joy_priv *priv;
|
||||
struct input_dev *dev;
|
||||
int err = 0;
|
||||
u32 i, j, found = 0;
|
||||
|
||||
priv = kzalloc(sizeof(struct n64joy_priv), GFP_KERNEL);
|
||||
if (!priv)
|
||||
return -ENOMEM;
|
||||
mutex_init(&priv->n64joy_mutex);
|
||||
|
||||
priv->reg_base = devm_platform_ioremap_resource(pdev, 0);
|
||||
if (!priv->reg_base) {
|
||||
err = -EINVAL;
|
||||
goto fail;
|
||||
}
|
||||
|
||||
/* The controllers are not hotpluggable, so we can scan in init */
|
||||
n64joy_exec_pif(priv, scandata);
|
||||
|
||||
data = (struct joydata *) priv->si_buf;
|
||||
|
||||
for (i = 0; i < MAX_CONTROLLERS; i++) {
|
||||
if (!data[i].err && data[i].data >> 16 == N64_CONTROLLER_ID) {
|
||||
found++;
|
||||
|
||||
dev = priv->n64joy_dev[i] = input_allocate_device();
|
||||
if (!priv->n64joy_dev[i]) {
|
||||
err = -ENOMEM;
|
||||
goto fail;
|
||||
}
|
||||
|
||||
input_set_drvdata(dev, priv);
|
||||
|
||||
dev->name = "N64 controller";
|
||||
dev->phys = n64joy_phys[i];
|
||||
dev->id.bustype = BUS_HOST;
|
||||
dev->id.vendor = 0;
|
||||
dev->id.product = data[i].data >> 16;
|
||||
dev->id.version = 0;
|
||||
dev->dev.parent = &pdev->dev;
|
||||
|
||||
dev->open = n64joy_open;
|
||||
dev->close = n64joy_close;
|
||||
|
||||
/* d-pad */
|
||||
input_set_capability(dev, EV_KEY, BTN_DPAD_UP);
|
||||
input_set_capability(dev, EV_KEY, BTN_DPAD_DOWN);
|
||||
input_set_capability(dev, EV_KEY, BTN_DPAD_LEFT);
|
||||
input_set_capability(dev, EV_KEY, BTN_DPAD_RIGHT);
|
||||
/* c buttons */
|
||||
input_set_capability(dev, EV_KEY, BTN_LEFT);
|
||||
input_set_capability(dev, EV_KEY, BTN_RIGHT);
|
||||
input_set_capability(dev, EV_KEY, BTN_FORWARD);
|
||||
input_set_capability(dev, EV_KEY, BTN_BACK);
|
||||
/* matching buttons */
|
||||
input_set_capability(dev, EV_KEY, BTN_START);
|
||||
input_set_capability(dev, EV_KEY, BTN_Z);
|
||||
/* remaining ones: a, b, l, r */
|
||||
input_set_capability(dev, EV_KEY, BTN_0);
|
||||
input_set_capability(dev, EV_KEY, BTN_1);
|
||||
input_set_capability(dev, EV_KEY, BTN_2);
|
||||
input_set_capability(dev, EV_KEY, BTN_3);
|
||||
|
||||
for (j = 0; j < 2; j++)
|
||||
input_set_abs_params(dev, ABS_X + j,
|
||||
S8_MIN, S8_MAX, 0, 0);
|
||||
|
||||
err = input_register_device(dev);
|
||||
if (err) {
|
||||
input_free_device(dev);
|
||||
goto fail;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pr_info("%u controller(s) connected\n", found);
|
||||
|
||||
if (!found)
|
||||
return -ENODEV;
|
||||
|
||||
return 0;
|
||||
fail:
|
||||
for (i = 0; i < MAX_CONTROLLERS; i++) {
|
||||
if (!priv->n64joy_dev[i])
|
||||
continue;
|
||||
input_unregister_device(priv->n64joy_dev[i]);
|
||||
}
|
||||
return err;
|
||||
}
|
||||
|
||||
static struct platform_driver n64joy_driver = {
|
||||
.driver = {
|
||||
.name = "n64joy",
|
||||
},
|
||||
};
|
||||
|
||||
static int __init n64joy_init(void)
|
||||
{
|
||||
return platform_driver_probe(&n64joy_driver, n64joy_probe);
|
||||
}
|
||||
|
||||
module_init(n64joy_init);
|
@ -305,6 +305,7 @@ static const struct xpad_device {
|
||||
{ 0x1bad, 0xfd00, "Razer Onza TE", 0, XTYPE_XBOX360 },
|
||||
{ 0x1bad, 0xfd01, "Razer Onza", 0, XTYPE_XBOX360 },
|
||||
{ 0x20d6, 0x2001, "BDA Xbox Series X Wired Controller", 0, XTYPE_XBOXONE },
|
||||
{ 0x20d6, 0x2009, "PowerA Enhanced Wired Controller for Xbox Series X|S", 0, XTYPE_XBOXONE },
|
||||
{ 0x20d6, 0x281f, "PowerA Wired Controller For Xbox 360", 0, XTYPE_XBOX360 },
|
||||
{ 0x2e24, 0x0652, "Hyperkin Duke X-Box One pad", 0, XTYPE_XBOXONE },
|
||||
{ 0x24c6, 0x5000, "Razer Atrox Arcade Stick", MAP_TRIGGERS_TO_BUTTONS, XTYPE_XBOX360 },
|
||||
|
@ -446,7 +446,7 @@ config KEYBOARD_MPR121
|
||||
|
||||
config KEYBOARD_SNVS_PWRKEY
|
||||
tristate "IMX SNVS Power Key Driver"
|
||||
depends on ARCH_MXC || COMPILE_TEST
|
||||
depends on ARCH_MXC || (COMPILE_TEST && HAS_IOMEM)
|
||||
depends on OF
|
||||
help
|
||||
This is the snvs powerkey driver for the Freescale i.MX application
|
||||
@ -685,7 +685,7 @@ config KEYBOARD_OMAP
|
||||
|
||||
config KEYBOARD_OMAP4
|
||||
tristate "TI OMAP4+ keypad support"
|
||||
depends on OF || ARCH_OMAP2PLUS
|
||||
depends on (OF && HAS_IOMEM) || ARCH_OMAP2PLUS
|
||||
select INPUT_MATRIXKMAP
|
||||
help
|
||||
Say Y here if you want to use the OMAP4+ keypad.
|
||||
@ -773,7 +773,7 @@ config KEYBOARD_CAP11XX
|
||||
|
||||
config KEYBOARD_BCM
|
||||
tristate "Broadcom keypad driver"
|
||||
depends on OF && HAVE_CLK
|
||||
depends on OF && HAVE_CLK && HAS_IOMEM
|
||||
select INPUT_MATRIXKMAP
|
||||
default ARCH_BCM_CYGNUS
|
||||
help
|
||||
|
@ -48,6 +48,7 @@
|
||||
#include <linux/efi.h>
|
||||
#include <linux/input.h>
|
||||
#include <linux/input/mt.h>
|
||||
#include <linux/ktime.h>
|
||||
#include <linux/leds.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/spinlock.h>
|
||||
@ -409,7 +410,7 @@ struct applespi_data {
|
||||
unsigned int cmd_msg_cntr;
|
||||
/* lock to protect the above parameters and flags below */
|
||||
spinlock_t cmd_msg_lock;
|
||||
bool cmd_msg_queued;
|
||||
ktime_t cmd_msg_queued;
|
||||
enum applespi_evt_type cmd_evt_type;
|
||||
|
||||
struct led_classdev backlight_info;
|
||||
@ -729,7 +730,7 @@ static void applespi_msg_complete(struct applespi_data *applespi,
|
||||
wake_up_all(&applespi->drain_complete);
|
||||
|
||||
if (is_write_msg) {
|
||||
applespi->cmd_msg_queued = false;
|
||||
applespi->cmd_msg_queued = 0;
|
||||
applespi_send_cmd_msg(applespi);
|
||||
}
|
||||
|
||||
@ -748,6 +749,8 @@ static void applespi_async_write_complete(void *context)
|
||||
applespi->tx_status,
|
||||
APPLESPI_STATUS_SIZE);
|
||||
|
||||
udelay(SPI_RW_CHG_DELAY_US);
|
||||
|
||||
if (!applespi_check_write_status(applespi, applespi->wr_m.status)) {
|
||||
/*
|
||||
* If we got an error, we presumably won't get the expected
|
||||
@ -771,8 +774,16 @@ static int applespi_send_cmd_msg(struct applespi_data *applespi)
|
||||
return 0;
|
||||
|
||||
/* check whether send is in progress */
|
||||
if (applespi->cmd_msg_queued)
|
||||
return 0;
|
||||
if (applespi->cmd_msg_queued) {
|
||||
if (ktime_ms_delta(ktime_get(), applespi->cmd_msg_queued) < 1000)
|
||||
return 0;
|
||||
|
||||
dev_warn(&applespi->spi->dev, "Command %d timed out\n",
|
||||
applespi->cmd_evt_type);
|
||||
|
||||
applespi->cmd_msg_queued = 0;
|
||||
applespi->write_active = false;
|
||||
}
|
||||
|
||||
/* set up packet */
|
||||
memset(packet, 0, APPLESPI_PACKET_SIZE);
|
||||
@ -869,7 +880,7 @@ static int applespi_send_cmd_msg(struct applespi_data *applespi)
|
||||
return sts;
|
||||
}
|
||||
|
||||
applespi->cmd_msg_queued = true;
|
||||
applespi->cmd_msg_queued = ktime_get_coarse();
|
||||
applespi->write_active = true;
|
||||
|
||||
return 0;
|
||||
@ -1921,7 +1932,7 @@ static int __maybe_unused applespi_resume(struct device *dev)
|
||||
applespi->drain = false;
|
||||
applespi->have_cl_led_on = false;
|
||||
applespi->have_bl_level = 0;
|
||||
applespi->cmd_msg_queued = false;
|
||||
applespi->cmd_msg_queued = 0;
|
||||
applespi->read_active = false;
|
||||
applespi->write_active = false;
|
||||
|
||||
|
@ -27,6 +27,8 @@
|
||||
|
||||
#include <asm/unaligned.h>
|
||||
|
||||
#define MAX_NUM_TOP_ROW_KEYS 15
|
||||
|
||||
/**
|
||||
* struct cros_ec_keyb - Structure representing EC keyboard device
|
||||
*
|
||||
@ -42,6 +44,9 @@
|
||||
* @idev: The input device for the matrix keys.
|
||||
* @bs_idev: The input device for non-matrix buttons and switches (or NULL).
|
||||
* @notifier: interrupt event notifier for transport devices
|
||||
* @function_row_physmap: An array of the encoded rows/columns for the top
|
||||
* row function keys, in an order from left to right
|
||||
* @num_function_row_keys: The number of top row keys in a custom keyboard
|
||||
*/
|
||||
struct cros_ec_keyb {
|
||||
unsigned int rows;
|
||||
@ -58,6 +63,9 @@ struct cros_ec_keyb {
|
||||
struct input_dev *idev;
|
||||
struct input_dev *bs_idev;
|
||||
struct notifier_block notifier;
|
||||
|
||||
u16 function_row_physmap[MAX_NUM_TOP_ROW_KEYS];
|
||||
size_t num_function_row_keys;
|
||||
};
|
||||
|
||||
/**
|
||||
@ -527,6 +535,11 @@ static int cros_ec_keyb_register_matrix(struct cros_ec_keyb *ckdev)
|
||||
struct input_dev *idev;
|
||||
const char *phys;
|
||||
int err;
|
||||
struct property *prop;
|
||||
const __be32 *p;
|
||||
u16 *physmap;
|
||||
u32 key_pos;
|
||||
int row, col;
|
||||
|
||||
err = matrix_keypad_parse_properties(dev, &ckdev->rows, &ckdev->cols);
|
||||
if (err)
|
||||
@ -578,6 +591,21 @@ static int cros_ec_keyb_register_matrix(struct cros_ec_keyb *ckdev)
|
||||
ckdev->idev = idev;
|
||||
cros_ec_keyb_compute_valid_keys(ckdev);
|
||||
|
||||
physmap = ckdev->function_row_physmap;
|
||||
of_property_for_each_u32(dev->of_node, "function-row-physmap",
|
||||
prop, p, key_pos) {
|
||||
if (ckdev->num_function_row_keys == MAX_NUM_TOP_ROW_KEYS) {
|
||||
dev_warn(dev, "Only support up to %d top row keys\n",
|
||||
MAX_NUM_TOP_ROW_KEYS);
|
||||
break;
|
||||
}
|
||||
row = KEY_ROW(key_pos);
|
||||
col = KEY_COL(key_pos);
|
||||
*physmap = MATRIX_SCAN_CODE(row, col, ckdev->row_shift);
|
||||
physmap++;
|
||||
ckdev->num_function_row_keys++;
|
||||
}
|
||||
|
||||
err = input_register_device(ckdev->idev);
|
||||
if (err) {
|
||||
dev_err(dev, "cannot register input device\n");
|
||||
@ -587,6 +615,51 @@ static int cros_ec_keyb_register_matrix(struct cros_ec_keyb *ckdev)
|
||||
return 0;
|
||||
}
|
||||
|
||||
static ssize_t function_row_physmap_show(struct device *dev,
|
||||
struct device_attribute *attr,
|
||||
char *buf)
|
||||
{
|
||||
ssize_t size = 0;
|
||||
int i;
|
||||
struct cros_ec_keyb *ckdev = dev_get_drvdata(dev);
|
||||
u16 *physmap = ckdev->function_row_physmap;
|
||||
|
||||
for (i = 0; i < ckdev->num_function_row_keys; i++)
|
||||
size += scnprintf(buf + size, PAGE_SIZE - size,
|
||||
"%s%02X", size ? " " : "", physmap[i]);
|
||||
if (size)
|
||||
size += scnprintf(buf + size, PAGE_SIZE - size, "\n");
|
||||
|
||||
return size;
|
||||
}
|
||||
|
||||
static DEVICE_ATTR_RO(function_row_physmap);
|
||||
|
||||
static struct attribute *cros_ec_keyb_attrs[] = {
|
||||
&dev_attr_function_row_physmap.attr,
|
||||
NULL,
|
||||
};
|
||||
|
||||
static umode_t cros_ec_keyb_attr_is_visible(struct kobject *kobj,
|
||||
struct attribute *attr,
|
||||
int n)
|
||||
{
|
||||
struct device *dev = container_of(kobj, struct device, kobj);
|
||||
struct cros_ec_keyb *ckdev = dev_get_drvdata(dev);
|
||||
|
||||
if (attr == &dev_attr_function_row_physmap.attr &&
|
||||
!ckdev->num_function_row_keys)
|
||||
return 0;
|
||||
|
||||
return attr->mode;
|
||||
}
|
||||
|
||||
static const struct attribute_group cros_ec_keyb_attr_group = {
|
||||
.is_visible = cros_ec_keyb_attr_is_visible,
|
||||
.attrs = cros_ec_keyb_attrs,
|
||||
};
|
||||
|
||||
|
||||
static int cros_ec_keyb_probe(struct platform_device *pdev)
|
||||
{
|
||||
struct cros_ec_device *ec = dev_get_drvdata(pdev->dev.parent);
|
||||
@ -617,6 +690,12 @@ static int cros_ec_keyb_probe(struct platform_device *pdev)
|
||||
return err;
|
||||
}
|
||||
|
||||
err = devm_device_add_group(dev, &cros_ec_keyb_attr_group);
|
||||
if (err) {
|
||||
dev_err(dev, "failed to create attributes. err=%d\n", err);
|
||||
return err;
|
||||
}
|
||||
|
||||
ckdev->notifier.notifier_call = cros_ec_keyb_work;
|
||||
err = blocking_notifier_chain_register(&ckdev->ec->event_notifier,
|
||||
&ckdev->notifier);
|
||||
|
@ -60,6 +60,8 @@
|
||||
((((dbms) * 1000) / ((1 << ((ptv) + 1)) * (1000000 / 32768))) - 1)
|
||||
#define OMAP4_VAL_DEBOUNCINGTIME_16MS \
|
||||
OMAP4_KEYPAD_DEBOUNCINGTIME_MS(16, OMAP4_KEYPAD_PTV_DIV_128)
|
||||
#define OMAP4_KEYPAD_AUTOIDLE_MS 50 /* Approximate measured time */
|
||||
#define OMAP4_KEYPAD_IDLE_CHECK_MS (OMAP4_KEYPAD_AUTOIDLE_MS / 2)
|
||||
|
||||
enum {
|
||||
KBD_REVISION_OMAP4 = 0,
|
||||
@ -71,6 +73,7 @@ struct omap4_keypad {
|
||||
|
||||
void __iomem *base;
|
||||
unsigned int irq;
|
||||
struct mutex lock; /* for key scan */
|
||||
|
||||
unsigned int rows;
|
||||
unsigned int cols;
|
||||
@ -78,7 +81,7 @@ struct omap4_keypad {
|
||||
u32 irqreg_offset;
|
||||
unsigned int row_shift;
|
||||
bool no_autorepeat;
|
||||
unsigned char key_state[8];
|
||||
u64 keys;
|
||||
unsigned short *keymap;
|
||||
};
|
||||
|
||||
@ -107,6 +110,55 @@ static void kbd_write_irqreg(struct omap4_keypad *keypad_data,
|
||||
keypad_data->base + keypad_data->irqreg_offset + offset);
|
||||
}
|
||||
|
||||
static int omap4_keypad_report_keys(struct omap4_keypad *keypad_data,
|
||||
u64 keys, bool down)
|
||||
{
|
||||
struct input_dev *input_dev = keypad_data->input;
|
||||
unsigned int col, row, code;
|
||||
DECLARE_BITMAP(mask, 64);
|
||||
unsigned long bit;
|
||||
int events = 0;
|
||||
|
||||
bitmap_from_u64(mask, keys);
|
||||
|
||||
for_each_set_bit(bit, mask, keypad_data->rows * BITS_PER_BYTE) {
|
||||
row = bit / BITS_PER_BYTE;
|
||||
col = bit % BITS_PER_BYTE;
|
||||
code = MATRIX_SCAN_CODE(row, col, keypad_data->row_shift);
|
||||
|
||||
input_event(input_dev, EV_MSC, MSC_SCAN, code);
|
||||
input_report_key(input_dev, keypad_data->keymap[code], down);
|
||||
|
||||
events++;
|
||||
}
|
||||
|
||||
if (events)
|
||||
input_sync(input_dev);
|
||||
|
||||
return events;
|
||||
}
|
||||
|
||||
static void omap4_keypad_scan_keys(struct omap4_keypad *keypad_data, u64 keys)
|
||||
{
|
||||
u64 changed;
|
||||
|
||||
mutex_lock(&keypad_data->lock);
|
||||
|
||||
changed = keys ^ keypad_data->keys;
|
||||
|
||||
/*
|
||||
* Report key up events separately and first. This matters in case we
|
||||
* lost key-up interrupt and just now catching up.
|
||||
*/
|
||||
omap4_keypad_report_keys(keypad_data, changed & ~keys, false);
|
||||
|
||||
/* Report key down events */
|
||||
omap4_keypad_report_keys(keypad_data, changed & keys, true);
|
||||
|
||||
keypad_data->keys = keys;
|
||||
|
||||
mutex_unlock(&keypad_data->lock);
|
||||
}
|
||||
|
||||
/* Interrupt handlers */
|
||||
static irqreturn_t omap4_keypad_irq_handler(int irq, void *dev_id)
|
||||
@ -122,48 +174,44 @@ static irqreturn_t omap4_keypad_irq_handler(int irq, void *dev_id)
|
||||
static irqreturn_t omap4_keypad_irq_thread_fn(int irq, void *dev_id)
|
||||
{
|
||||
struct omap4_keypad *keypad_data = dev_id;
|
||||
struct input_dev *input_dev = keypad_data->input;
|
||||
unsigned char key_state[ARRAY_SIZE(keypad_data->key_state)];
|
||||
unsigned int col, row, code, changed;
|
||||
u32 *new_state = (u32 *) key_state;
|
||||
struct device *dev = keypad_data->input->dev.parent;
|
||||
u32 low, high;
|
||||
int error;
|
||||
u64 keys;
|
||||
|
||||
*new_state = kbd_readl(keypad_data, OMAP4_KBD_FULLCODE31_0);
|
||||
*(new_state + 1) = kbd_readl(keypad_data, OMAP4_KBD_FULLCODE63_32);
|
||||
|
||||
for (row = 0; row < keypad_data->rows; row++) {
|
||||
changed = key_state[row] ^ keypad_data->key_state[row];
|
||||
if (!changed)
|
||||
continue;
|
||||
|
||||
for (col = 0; col < keypad_data->cols; col++) {
|
||||
if (changed & (1 << col)) {
|
||||
code = MATRIX_SCAN_CODE(row, col,
|
||||
keypad_data->row_shift);
|
||||
input_event(input_dev, EV_MSC, MSC_SCAN, code);
|
||||
input_report_key(input_dev,
|
||||
keypad_data->keymap[code],
|
||||
key_state[row] & (1 << col));
|
||||
}
|
||||
}
|
||||
error = pm_runtime_get_sync(dev);
|
||||
if (error < 0) {
|
||||
pm_runtime_put_noidle(dev);
|
||||
return IRQ_NONE;
|
||||
}
|
||||
|
||||
input_sync(input_dev);
|
||||
low = kbd_readl(keypad_data, OMAP4_KBD_FULLCODE31_0);
|
||||
high = kbd_readl(keypad_data, OMAP4_KBD_FULLCODE63_32);
|
||||
keys = low | (u64)high << 32;
|
||||
|
||||
memcpy(keypad_data->key_state, key_state,
|
||||
sizeof(keypad_data->key_state));
|
||||
omap4_keypad_scan_keys(keypad_data, keys);
|
||||
|
||||
/* clear pending interrupts */
|
||||
kbd_write_irqreg(keypad_data, OMAP4_KBD_IRQSTATUS,
|
||||
kbd_read_irqreg(keypad_data, OMAP4_KBD_IRQSTATUS));
|
||||
|
||||
pm_runtime_mark_last_busy(dev);
|
||||
pm_runtime_put_autosuspend(dev);
|
||||
|
||||
return IRQ_HANDLED;
|
||||
}
|
||||
|
||||
static int omap4_keypad_open(struct input_dev *input)
|
||||
{
|
||||
struct omap4_keypad *keypad_data = input_get_drvdata(input);
|
||||
struct device *dev = input->dev.parent;
|
||||
int error;
|
||||
|
||||
pm_runtime_get_sync(input->dev.parent);
|
||||
error = pm_runtime_get_sync(dev);
|
||||
if (error < 0) {
|
||||
pm_runtime_put_noidle(dev);
|
||||
return error;
|
||||
}
|
||||
|
||||
disable_irq(keypad_data->irq);
|
||||
|
||||
@ -176,13 +224,15 @@ static int omap4_keypad_open(struct input_dev *input)
|
||||
kbd_write_irqreg(keypad_data, OMAP4_KBD_IRQSTATUS,
|
||||
kbd_read_irqreg(keypad_data, OMAP4_KBD_IRQSTATUS));
|
||||
kbd_write_irqreg(keypad_data, OMAP4_KBD_IRQENABLE,
|
||||
OMAP4_DEF_IRQENABLE_EVENTEN |
|
||||
OMAP4_DEF_IRQENABLE_LONGKEY);
|
||||
OMAP4_DEF_IRQENABLE_EVENTEN);
|
||||
kbd_writel(keypad_data, OMAP4_KBD_WAKEUPENABLE,
|
||||
OMAP4_DEF_WUP_EVENT_ENA | OMAP4_DEF_WUP_LONG_KEY_ENA);
|
||||
OMAP4_DEF_WUP_EVENT_ENA);
|
||||
|
||||
enable_irq(keypad_data->irq);
|
||||
|
||||
pm_runtime_mark_last_busy(dev);
|
||||
pm_runtime_put_autosuspend(dev);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
@ -200,14 +250,20 @@ static void omap4_keypad_stop(struct omap4_keypad *keypad_data)
|
||||
|
||||
static void omap4_keypad_close(struct input_dev *input)
|
||||
{
|
||||
struct omap4_keypad *keypad_data;
|
||||
struct omap4_keypad *keypad_data = input_get_drvdata(input);
|
||||
struct device *dev = input->dev.parent;
|
||||
int error;
|
||||
|
||||
error = pm_runtime_get_sync(dev);
|
||||
if (error < 0)
|
||||
pm_runtime_put_noidle(dev);
|
||||
|
||||
keypad_data = input_get_drvdata(input);
|
||||
disable_irq(keypad_data->irq);
|
||||
omap4_keypad_stop(keypad_data);
|
||||
enable_irq(keypad_data->irq);
|
||||
|
||||
pm_runtime_put_sync(input->dev.parent);
|
||||
pm_runtime_mark_last_busy(dev);
|
||||
pm_runtime_put_autosuspend(dev);
|
||||
}
|
||||
|
||||
static int omap4_keypad_parse_dt(struct device *dev,
|
||||
@ -252,8 +308,41 @@ static int omap4_keypad_check_revision(struct device *dev,
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* Errata ID i689 "1.32 Keyboard Key Up Event Can Be Missed".
|
||||
* Interrupt may not happen for key-up events. We must clear stuck
|
||||
* key-up events after the keyboard hardware has auto-idled.
|
||||
*/
|
||||
static int __maybe_unused omap4_keypad_runtime_suspend(struct device *dev)
|
||||
{
|
||||
struct platform_device *pdev = to_platform_device(dev);
|
||||
struct omap4_keypad *keypad_data = platform_get_drvdata(pdev);
|
||||
u32 active;
|
||||
|
||||
active = kbd_readl(keypad_data, OMAP4_KBD_STATEMACHINE);
|
||||
if (active) {
|
||||
pm_runtime_mark_last_busy(dev);
|
||||
return -EBUSY;
|
||||
}
|
||||
|
||||
omap4_keypad_scan_keys(keypad_data, 0);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct dev_pm_ops omap4_keypad_pm_ops = {
|
||||
SET_RUNTIME_PM_OPS(omap4_keypad_runtime_suspend, NULL, NULL)
|
||||
};
|
||||
|
||||
static void omap4_disable_pm(void *d)
|
||||
{
|
||||
pm_runtime_dont_use_autosuspend(d);
|
||||
pm_runtime_disable(d);
|
||||
}
|
||||
|
||||
static int omap4_keypad_probe(struct platform_device *pdev)
|
||||
{
|
||||
struct device *dev = &pdev->dev;
|
||||
struct omap4_keypad *keypad_data;
|
||||
struct input_dev *input_dev;
|
||||
struct resource *res;
|
||||
@ -271,63 +360,62 @@ static int omap4_keypad_probe(struct platform_device *pdev)
|
||||
if (irq < 0)
|
||||
return irq;
|
||||
|
||||
keypad_data = kzalloc(sizeof(struct omap4_keypad), GFP_KERNEL);
|
||||
keypad_data = devm_kzalloc(dev, sizeof(*keypad_data), GFP_KERNEL);
|
||||
if (!keypad_data) {
|
||||
dev_err(&pdev->dev, "keypad_data memory allocation failed\n");
|
||||
dev_err(dev, "keypad_data memory allocation failed\n");
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
keypad_data->irq = irq;
|
||||
mutex_init(&keypad_data->lock);
|
||||
platform_set_drvdata(pdev, keypad_data);
|
||||
|
||||
error = omap4_keypad_parse_dt(&pdev->dev, keypad_data);
|
||||
error = omap4_keypad_parse_dt(dev, keypad_data);
|
||||
if (error)
|
||||
goto err_free_keypad;
|
||||
return error;
|
||||
|
||||
res = request_mem_region(res->start, resource_size(res), pdev->name);
|
||||
if (!res) {
|
||||
dev_err(&pdev->dev, "can't request mem region\n");
|
||||
error = -EBUSY;
|
||||
goto err_free_keypad;
|
||||
keypad_data->base = devm_ioremap_resource(dev, res);
|
||||
if (IS_ERR(keypad_data->base))
|
||||
return PTR_ERR(keypad_data->base);
|
||||
|
||||
pm_runtime_use_autosuspend(dev);
|
||||
pm_runtime_set_autosuspend_delay(dev, OMAP4_KEYPAD_IDLE_CHECK_MS);
|
||||
pm_runtime_enable(dev);
|
||||
|
||||
error = devm_add_action_or_reset(dev, omap4_disable_pm, dev);
|
||||
if (error) {
|
||||
dev_err(dev, "unable to register cleanup action\n");
|
||||
return error;
|
||||
}
|
||||
|
||||
keypad_data->base = ioremap(res->start, resource_size(res));
|
||||
if (!keypad_data->base) {
|
||||
dev_err(&pdev->dev, "can't ioremap mem resource\n");
|
||||
error = -ENOMEM;
|
||||
goto err_release_mem;
|
||||
}
|
||||
|
||||
pm_runtime_enable(&pdev->dev);
|
||||
|
||||
/*
|
||||
* Enable clocks for the keypad module so that we can read
|
||||
* revision register.
|
||||
*/
|
||||
error = pm_runtime_get_sync(&pdev->dev);
|
||||
error = pm_runtime_get_sync(dev);
|
||||
if (error) {
|
||||
dev_err(&pdev->dev, "pm_runtime_get_sync() failed\n");
|
||||
pm_runtime_put_noidle(&pdev->dev);
|
||||
} else {
|
||||
error = omap4_keypad_check_revision(&pdev->dev,
|
||||
keypad_data);
|
||||
if (!error) {
|
||||
/* Ensure device does not raise interrupts */
|
||||
omap4_keypad_stop(keypad_data);
|
||||
}
|
||||
pm_runtime_put_sync(&pdev->dev);
|
||||
dev_err(dev, "pm_runtime_get_sync() failed\n");
|
||||
pm_runtime_put_noidle(dev);
|
||||
return error;
|
||||
}
|
||||
|
||||
error = omap4_keypad_check_revision(dev, keypad_data);
|
||||
if (!error) {
|
||||
/* Ensure device does not raise interrupts */
|
||||
omap4_keypad_stop(keypad_data);
|
||||
}
|
||||
|
||||
pm_runtime_mark_last_busy(dev);
|
||||
pm_runtime_put_autosuspend(dev);
|
||||
if (error)
|
||||
goto err_pm_disable;
|
||||
return error;
|
||||
|
||||
/* input device allocation */
|
||||
keypad_data->input = input_dev = input_allocate_device();
|
||||
if (!input_dev) {
|
||||
error = -ENOMEM;
|
||||
goto err_pm_disable;
|
||||
}
|
||||
keypad_data->input = input_dev = devm_input_allocate_device(dev);
|
||||
if (!input_dev)
|
||||
return -ENOMEM;
|
||||
|
||||
input_dev->name = pdev->name;
|
||||
input_dev->dev.parent = &pdev->dev;
|
||||
input_dev->id.bustype = BUS_HOST;
|
||||
input_dev->id.vendor = 0x0001;
|
||||
input_dev->id.product = 0x0001;
|
||||
@ -344,84 +432,51 @@ static int omap4_keypad_probe(struct platform_device *pdev)
|
||||
|
||||
keypad_data->row_shift = get_count_order(keypad_data->cols);
|
||||
max_keys = keypad_data->rows << keypad_data->row_shift;
|
||||
keypad_data->keymap = kcalloc(max_keys,
|
||||
sizeof(keypad_data->keymap[0]),
|
||||
GFP_KERNEL);
|
||||
keypad_data->keymap = devm_kcalloc(dev,
|
||||
max_keys,
|
||||
sizeof(keypad_data->keymap[0]),
|
||||
GFP_KERNEL);
|
||||
if (!keypad_data->keymap) {
|
||||
dev_err(&pdev->dev, "Not enough memory for keymap\n");
|
||||
error = -ENOMEM;
|
||||
goto err_free_input;
|
||||
dev_err(dev, "Not enough memory for keymap\n");
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
error = matrix_keypad_build_keymap(NULL, NULL,
|
||||
keypad_data->rows, keypad_data->cols,
|
||||
keypad_data->keymap, input_dev);
|
||||
if (error) {
|
||||
dev_err(&pdev->dev, "failed to build keymap\n");
|
||||
goto err_free_keymap;
|
||||
dev_err(dev, "failed to build keymap\n");
|
||||
return error;
|
||||
}
|
||||
|
||||
error = request_threaded_irq(keypad_data->irq, omap4_keypad_irq_handler,
|
||||
omap4_keypad_irq_thread_fn, IRQF_ONESHOT,
|
||||
"omap4-keypad", keypad_data);
|
||||
error = devm_request_threaded_irq(dev, keypad_data->irq,
|
||||
omap4_keypad_irq_handler,
|
||||
omap4_keypad_irq_thread_fn,
|
||||
IRQF_ONESHOT,
|
||||
"omap4-keypad", keypad_data);
|
||||
if (error) {
|
||||
dev_err(&pdev->dev, "failed to register interrupt\n");
|
||||
goto err_free_keymap;
|
||||
dev_err(dev, "failed to register interrupt\n");
|
||||
return error;
|
||||
}
|
||||
|
||||
error = input_register_device(keypad_data->input);
|
||||
if (error < 0) {
|
||||
dev_err(&pdev->dev, "failed to register input device\n");
|
||||
goto err_free_irq;
|
||||
if (error) {
|
||||
dev_err(dev, "failed to register input device\n");
|
||||
return error;
|
||||
}
|
||||
|
||||
device_init_wakeup(&pdev->dev, true);
|
||||
error = dev_pm_set_wake_irq(&pdev->dev, keypad_data->irq);
|
||||
device_init_wakeup(dev, true);
|
||||
error = dev_pm_set_wake_irq(dev, keypad_data->irq);
|
||||
if (error)
|
||||
dev_warn(&pdev->dev,
|
||||
"failed to set up wakeup irq: %d\n", error);
|
||||
|
||||
platform_set_drvdata(pdev, keypad_data);
|
||||
dev_warn(dev, "failed to set up wakeup irq: %d\n", error);
|
||||
|
||||
return 0;
|
||||
|
||||
err_free_irq:
|
||||
free_irq(keypad_data->irq, keypad_data);
|
||||
err_free_keymap:
|
||||
kfree(keypad_data->keymap);
|
||||
err_free_input:
|
||||
input_free_device(input_dev);
|
||||
err_pm_disable:
|
||||
pm_runtime_disable(&pdev->dev);
|
||||
iounmap(keypad_data->base);
|
||||
err_release_mem:
|
||||
release_mem_region(res->start, resource_size(res));
|
||||
err_free_keypad:
|
||||
kfree(keypad_data);
|
||||
return error;
|
||||
}
|
||||
|
||||
static int omap4_keypad_remove(struct platform_device *pdev)
|
||||
{
|
||||
struct omap4_keypad *keypad_data = platform_get_drvdata(pdev);
|
||||
struct resource *res;
|
||||
|
||||
dev_pm_clear_wake_irq(&pdev->dev);
|
||||
|
||||
free_irq(keypad_data->irq, keypad_data);
|
||||
|
||||
pm_runtime_disable(&pdev->dev);
|
||||
|
||||
input_unregister_device(keypad_data->input);
|
||||
|
||||
iounmap(keypad_data->base);
|
||||
|
||||
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
|
||||
release_mem_region(res->start, resource_size(res));
|
||||
|
||||
kfree(keypad_data->keymap);
|
||||
kfree(keypad_data);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
@ -437,6 +492,7 @@ static struct platform_driver omap4_keypad_driver = {
|
||||
.driver = {
|
||||
.name = "omap4-keypad",
|
||||
.of_match_table = omap_keypad_dt_match,
|
||||
.pm = &omap4_keypad_pm_ops,
|
||||
},
|
||||
};
|
||||
module_platform_driver(omap4_keypad_driver);
|
||||
|
@ -863,6 +863,7 @@ static void da7280_parse_properties(struct device *dev,
|
||||
gpi_str3[7] = '0' + i;
|
||||
haptics->gpi_ctl[i].polarity = 0;
|
||||
error = device_property_read_string(dev, gpi_str3, &str);
|
||||
if (!error)
|
||||
haptics->gpi_ctl[i].polarity =
|
||||
da7280_haptic_of_gpi_pol_str(dev, str);
|
||||
}
|
||||
@ -1299,11 +1300,13 @@ static int __maybe_unused da7280_resume(struct device *dev)
|
||||
return retval;
|
||||
}
|
||||
|
||||
#ifdef CONFIG_OF
|
||||
static const struct of_device_id da7280_of_match[] = {
|
||||
{ .compatible = "dlg,da7280", },
|
||||
{ }
|
||||
};
|
||||
MODULE_DEVICE_TABLE(of, da7280_of_match);
|
||||
#endif
|
||||
|
||||
static const struct i2c_device_id da7280_i2c_id[] = {
|
||||
{ "da7280", },
|
||||
|
@ -986,7 +986,7 @@ static void alps_get_finger_coordinate_v7(struct input_mt_pos *mt,
|
||||
case V7_PACKET_ID_TWO:
|
||||
mt[1].x &= ~0x000F;
|
||||
mt[1].y |= 0x000F;
|
||||
/* Detect false-postive touches where x & y report max value */
|
||||
/* Detect false-positive touches where x & y report max value */
|
||||
if (mt[1].y == 0x7ff && mt[1].x == 0xff0) {
|
||||
mt[1].x = 0;
|
||||
/* y gets set to 0 at the end of this function */
|
||||
|
@ -1106,8 +1106,11 @@ static void synaptics_process_packet(struct psmouse *psmouse)
|
||||
num_fingers = hw.w + 2;
|
||||
break;
|
||||
case 2:
|
||||
if (SYN_MODEL_PEN(info->model_id))
|
||||
; /* Nothing, treat a pen as a single finger */
|
||||
/*
|
||||
* SYN_MODEL_PEN(info->model_id): even if
|
||||
* the device supports pen, we treat it as
|
||||
* a single finger.
|
||||
*/
|
||||
break;
|
||||
case 4 ... 15:
|
||||
if (SYN_CAP_PALMDETECT(info->capabilities))
|
||||
|
@ -255,7 +255,7 @@ config SERIO_ARC_PS2
|
||||
|
||||
config SERIO_APBPS2
|
||||
tristate "GRLIB APBPS2 PS/2 keyboard/mouse controller"
|
||||
depends on OF
|
||||
depends on OF && HAS_IOMEM
|
||||
help
|
||||
Say Y here if you want support for GRLIB APBPS2 peripherals used
|
||||
to connect to PS/2 keyboard and/or mouse.
|
||||
|
@ -588,6 +588,10 @@ static const struct dmi_system_id i8042_dmi_noselftest_table[] = {
|
||||
DMI_MATCH(DMI_SYS_VENDOR, "ASUSTeK COMPUTER INC."),
|
||||
DMI_MATCH(DMI_CHASSIS_TYPE, "10"), /* Notebook */
|
||||
},
|
||||
.matches = {
|
||||
DMI_MATCH(DMI_SYS_VENDOR, "ASUSTeK COMPUTER INC."),
|
||||
DMI_MATCH(DMI_CHASSIS_TYPE, "31"), /* Convertible Notebook */
|
||||
},
|
||||
},
|
||||
{ }
|
||||
};
|
||||
|
@ -1036,9 +1036,9 @@ static ssize_t show_tabletSize(struct device *dev, struct device_attribute *attr
|
||||
{
|
||||
struct aiptek *aiptek = dev_get_drvdata(dev);
|
||||
|
||||
return snprintf(buf, PAGE_SIZE, "%dx%d\n",
|
||||
input_abs_get_max(aiptek->inputdev, ABS_X) + 1,
|
||||
input_abs_get_max(aiptek->inputdev, ABS_Y) + 1);
|
||||
return sysfs_emit(buf, "%dx%d\n",
|
||||
input_abs_get_max(aiptek->inputdev, ABS_X) + 1,
|
||||
input_abs_get_max(aiptek->inputdev, ABS_Y) + 1);
|
||||
}
|
||||
|
||||
/* These structs define the sysfs files, param #1 is the name of the
|
||||
@ -1064,9 +1064,8 @@ static ssize_t show_tabletPointerMode(struct device *dev, struct device_attribut
|
||||
{
|
||||
struct aiptek *aiptek = dev_get_drvdata(dev);
|
||||
|
||||
return snprintf(buf, PAGE_SIZE, "%s\n",
|
||||
map_val_to_str(pointer_mode_map,
|
||||
aiptek->curSetting.pointerMode));
|
||||
return sysfs_emit(buf, "%s\n", map_val_to_str(pointer_mode_map,
|
||||
aiptek->curSetting.pointerMode));
|
||||
}
|
||||
|
||||
static ssize_t
|
||||
@ -1101,9 +1100,8 @@ static ssize_t show_tabletCoordinateMode(struct device *dev, struct device_attri
|
||||
{
|
||||
struct aiptek *aiptek = dev_get_drvdata(dev);
|
||||
|
||||
return snprintf(buf, PAGE_SIZE, "%s\n",
|
||||
map_val_to_str(coordinate_mode_map,
|
||||
aiptek->curSetting.coordinateMode));
|
||||
return sysfs_emit(buf, "%s\n", map_val_to_str(coordinate_mode_map,
|
||||
aiptek->curSetting.coordinateMode));
|
||||
}
|
||||
|
||||
static ssize_t
|
||||
@ -1143,9 +1141,8 @@ static ssize_t show_tabletToolMode(struct device *dev, struct device_attribute *
|
||||
{
|
||||
struct aiptek *aiptek = dev_get_drvdata(dev);
|
||||
|
||||
return snprintf(buf, PAGE_SIZE, "%s\n",
|
||||
map_val_to_str(tool_mode_map,
|
||||
aiptek->curSetting.toolMode));
|
||||
return sysfs_emit(buf, "%s\n", map_val_to_str(tool_mode_map,
|
||||
aiptek->curSetting.toolMode));
|
||||
}
|
||||
|
||||
static ssize_t
|
||||
@ -1174,10 +1171,9 @@ static ssize_t show_tabletXtilt(struct device *dev, struct device_attribute *att
|
||||
struct aiptek *aiptek = dev_get_drvdata(dev);
|
||||
|
||||
if (aiptek->curSetting.xTilt == AIPTEK_TILT_DISABLE) {
|
||||
return snprintf(buf, PAGE_SIZE, "disable\n");
|
||||
return sysfs_emit(buf, "disable\n");
|
||||
} else {
|
||||
return snprintf(buf, PAGE_SIZE, "%d\n",
|
||||
aiptek->curSetting.xTilt);
|
||||
return sysfs_emit(buf, "%d\n", aiptek->curSetting.xTilt);
|
||||
}
|
||||
}
|
||||
|
||||
@ -1216,10 +1212,9 @@ static ssize_t show_tabletYtilt(struct device *dev, struct device_attribute *att
|
||||
struct aiptek *aiptek = dev_get_drvdata(dev);
|
||||
|
||||
if (aiptek->curSetting.yTilt == AIPTEK_TILT_DISABLE) {
|
||||
return snprintf(buf, PAGE_SIZE, "disable\n");
|
||||
return sysfs_emit(buf, "disable\n");
|
||||
} else {
|
||||
return snprintf(buf, PAGE_SIZE, "%d\n",
|
||||
aiptek->curSetting.yTilt);
|
||||
return sysfs_emit(buf, "%d\n", aiptek->curSetting.yTilt);
|
||||
}
|
||||
}
|
||||
|
||||
@ -1257,7 +1252,7 @@ static ssize_t show_tabletJitterDelay(struct device *dev, struct device_attribut
|
||||
{
|
||||
struct aiptek *aiptek = dev_get_drvdata(dev);
|
||||
|
||||
return snprintf(buf, PAGE_SIZE, "%d\n", aiptek->curSetting.jitterDelay);
|
||||
return sysfs_emit(buf, "%d\n", aiptek->curSetting.jitterDelay);
|
||||
}
|
||||
|
||||
static ssize_t
|
||||
@ -1286,8 +1281,7 @@ static ssize_t show_tabletProgrammableDelay(struct device *dev, struct device_at
|
||||
{
|
||||
struct aiptek *aiptek = dev_get_drvdata(dev);
|
||||
|
||||
return snprintf(buf, PAGE_SIZE, "%d\n",
|
||||
aiptek->curSetting.programmableDelay);
|
||||
return sysfs_emit(buf, "%d\n", aiptek->curSetting.programmableDelay);
|
||||
}
|
||||
|
||||
static ssize_t
|
||||
@ -1316,7 +1310,7 @@ static ssize_t show_tabletEventsReceived(struct device *dev, struct device_attri
|
||||
{
|
||||
struct aiptek *aiptek = dev_get_drvdata(dev);
|
||||
|
||||
return snprintf(buf, PAGE_SIZE, "%ld\n", aiptek->eventCount);
|
||||
return sysfs_emit(buf, "%ld\n", aiptek->eventCount);
|
||||
}
|
||||
|
||||
static DEVICE_ATTR(event_count, S_IRUGO, show_tabletEventsReceived, NULL);
|
||||
@ -1355,7 +1349,7 @@ static ssize_t show_tabletDiagnosticMessage(struct device *dev, struct device_at
|
||||
default:
|
||||
return 0;
|
||||
}
|
||||
return snprintf(buf, PAGE_SIZE, retMsg);
|
||||
return sysfs_emit(buf, retMsg);
|
||||
}
|
||||
|
||||
static DEVICE_ATTR(diagnostic, S_IRUGO, show_tabletDiagnosticMessage, NULL);
|
||||
@ -1375,9 +1369,8 @@ static ssize_t show_tabletStylusUpper(struct device *dev, struct device_attribut
|
||||
{
|
||||
struct aiptek *aiptek = dev_get_drvdata(dev);
|
||||
|
||||
return snprintf(buf, PAGE_SIZE, "%s\n",
|
||||
map_val_to_str(stylus_button_map,
|
||||
aiptek->curSetting.stylusButtonUpper));
|
||||
return sysfs_emit(buf, "%s\n", map_val_to_str(stylus_button_map,
|
||||
aiptek->curSetting.stylusButtonUpper));
|
||||
}
|
||||
|
||||
static ssize_t
|
||||
@ -1406,9 +1399,8 @@ static ssize_t show_tabletStylusLower(struct device *dev, struct device_attribut
|
||||
{
|
||||
struct aiptek *aiptek = dev_get_drvdata(dev);
|
||||
|
||||
return snprintf(buf, PAGE_SIZE, "%s\n",
|
||||
map_val_to_str(stylus_button_map,
|
||||
aiptek->curSetting.stylusButtonLower));
|
||||
return sysfs_emit(buf, "%s\n", map_val_to_str(stylus_button_map,
|
||||
aiptek->curSetting.stylusButtonLower));
|
||||
}
|
||||
|
||||
static ssize_t
|
||||
@ -1444,9 +1436,8 @@ static ssize_t show_tabletMouseLeft(struct device *dev, struct device_attribute
|
||||
{
|
||||
struct aiptek *aiptek = dev_get_drvdata(dev);
|
||||
|
||||
return snprintf(buf, PAGE_SIZE, "%s\n",
|
||||
map_val_to_str(mouse_button_map,
|
||||
aiptek->curSetting.mouseButtonLeft));
|
||||
return sysfs_emit(buf, "%s\n", map_val_to_str(mouse_button_map,
|
||||
aiptek->curSetting.mouseButtonLeft));
|
||||
}
|
||||
|
||||
static ssize_t
|
||||
@ -1474,9 +1465,8 @@ static ssize_t show_tabletMouseMiddle(struct device *dev, struct device_attribut
|
||||
{
|
||||
struct aiptek *aiptek = dev_get_drvdata(dev);
|
||||
|
||||
return snprintf(buf, PAGE_SIZE, "%s\n",
|
||||
map_val_to_str(mouse_button_map,
|
||||
aiptek->curSetting.mouseButtonMiddle));
|
||||
return sysfs_emit(buf, "%s\n", map_val_to_str(mouse_button_map,
|
||||
aiptek->curSetting.mouseButtonMiddle));
|
||||
}
|
||||
|
||||
static ssize_t
|
||||
@ -1504,9 +1494,8 @@ static ssize_t show_tabletMouseRight(struct device *dev, struct device_attribute
|
||||
{
|
||||
struct aiptek *aiptek = dev_get_drvdata(dev);
|
||||
|
||||
return snprintf(buf, PAGE_SIZE, "%s\n",
|
||||
map_val_to_str(mouse_button_map,
|
||||
aiptek->curSetting.mouseButtonRight));
|
||||
return sysfs_emit(buf, "%s\n", map_val_to_str(mouse_button_map,
|
||||
aiptek->curSetting.mouseButtonRight));
|
||||
}
|
||||
|
||||
static ssize_t
|
||||
@ -1535,10 +1524,9 @@ static ssize_t show_tabletWheel(struct device *dev, struct device_attribute *att
|
||||
struct aiptek *aiptek = dev_get_drvdata(dev);
|
||||
|
||||
if (aiptek->curSetting.wheel == AIPTEK_WHEEL_DISABLE) {
|
||||
return snprintf(buf, PAGE_SIZE, "disable\n");
|
||||
return sysfs_emit(buf, "disable\n");
|
||||
} else {
|
||||
return snprintf(buf, PAGE_SIZE, "%d\n",
|
||||
aiptek->curSetting.wheel);
|
||||
return sysfs_emit(buf, "%d\n", aiptek->curSetting.wheel);
|
||||
}
|
||||
}
|
||||
|
||||
@ -1568,8 +1556,7 @@ static ssize_t show_tabletExecute(struct device *dev, struct device_attribute *a
|
||||
/* There is nothing useful to display, so a one-line manual
|
||||
* is in order...
|
||||
*/
|
||||
return snprintf(buf, PAGE_SIZE,
|
||||
"Write anything to this file to program your tablet.\n");
|
||||
return sysfs_emit(buf, "Write anything to this file to program your tablet.\n");
|
||||
}
|
||||
|
||||
static ssize_t
|
||||
@ -1600,7 +1587,7 @@ static ssize_t show_tabletODMCode(struct device *dev, struct device_attribute *a
|
||||
{
|
||||
struct aiptek *aiptek = dev_get_drvdata(dev);
|
||||
|
||||
return snprintf(buf, PAGE_SIZE, "0x%04x\n", aiptek->features.odmCode);
|
||||
return sysfs_emit(buf, "0x%04x\n", aiptek->features.odmCode);
|
||||
}
|
||||
|
||||
static DEVICE_ATTR(odm_code, S_IRUGO, show_tabletODMCode, NULL);
|
||||
@ -1613,7 +1600,7 @@ static ssize_t show_tabletModelCode(struct device *dev, struct device_attribute
|
||||
{
|
||||
struct aiptek *aiptek = dev_get_drvdata(dev);
|
||||
|
||||
return snprintf(buf, PAGE_SIZE, "0x%04x\n", aiptek->features.modelCode);
|
||||
return sysfs_emit(buf, "0x%04x\n", aiptek->features.modelCode);
|
||||
}
|
||||
|
||||
static DEVICE_ATTR(model_code, S_IRUGO, show_tabletModelCode, NULL);
|
||||
@ -1626,8 +1613,7 @@ static ssize_t show_firmwareCode(struct device *dev, struct device_attribute *at
|
||||
{
|
||||
struct aiptek *aiptek = dev_get_drvdata(dev);
|
||||
|
||||
return snprintf(buf, PAGE_SIZE, "%04x\n",
|
||||
aiptek->features.firmwareCode);
|
||||
return sysfs_emit(buf, "%04x\n", aiptek->features.firmwareCode);
|
||||
}
|
||||
|
||||
static DEVICE_ATTR(firmware_code, S_IRUGO, show_firmwareCode, NULL);
|
||||
|
@ -608,7 +608,7 @@ config TOUCHSCREEN_MTOUCH
|
||||
|
||||
config TOUCHSCREEN_IMX6UL_TSC
|
||||
tristate "Freescale i.MX6UL touchscreen controller"
|
||||
depends on (OF && GPIOLIB) || COMPILE_TEST
|
||||
depends on ((OF && GPIOLIB) || COMPILE_TEST) && HAS_IOMEM
|
||||
help
|
||||
Say Y here if you have a Freescale i.MX6UL, and want to
|
||||
use the internal touchscreen controller.
|
||||
|
@ -64,24 +64,13 @@
|
||||
|
||||
struct ads7846_buf {
|
||||
u8 cmd;
|
||||
/*
|
||||
* This union is a temporary hack. The driver does an in-place
|
||||
* endianness conversion. This will be cleaned up in the next
|
||||
* patch.
|
||||
*/
|
||||
union {
|
||||
__be16 data_be16;
|
||||
u16 data;
|
||||
};
|
||||
__be16 data;
|
||||
} __packed;
|
||||
|
||||
|
||||
struct ts_event {
|
||||
bool ignore;
|
||||
struct ads7846_buf x;
|
||||
struct ads7846_buf y;
|
||||
struct ads7846_buf z1;
|
||||
struct ads7846_buf z2;
|
||||
struct ads7846_buf_layout {
|
||||
unsigned int offset;
|
||||
unsigned int count;
|
||||
unsigned int skip;
|
||||
};
|
||||
|
||||
/*
|
||||
@ -90,12 +79,18 @@ struct ts_event {
|
||||
* systems where main memory is not DMA-coherent (most non-x86 boards).
|
||||
*/
|
||||
struct ads7846_packet {
|
||||
struct ts_event tc;
|
||||
struct ads7846_buf read_x_cmd;
|
||||
struct ads7846_buf read_y_cmd;
|
||||
struct ads7846_buf read_z1_cmd;
|
||||
struct ads7846_buf read_z2_cmd;
|
||||
unsigned int count;
|
||||
unsigned int count_skip;
|
||||
unsigned int cmds;
|
||||
unsigned int last_cmd_idx;
|
||||
struct ads7846_buf_layout l[5];
|
||||
struct ads7846_buf *rx;
|
||||
struct ads7846_buf *tx;
|
||||
|
||||
struct ads7846_buf pwrdown_cmd;
|
||||
|
||||
bool ignore;
|
||||
u16 x, y, z1, z2;
|
||||
};
|
||||
|
||||
struct ads7846 {
|
||||
@ -194,7 +189,6 @@ struct ads7846 {
|
||||
#define READ_Y(vref) (READ_12BIT_DFR(y, 1, vref))
|
||||
#define READ_Z1(vref) (READ_12BIT_DFR(z1, 1, vref))
|
||||
#define READ_Z2(vref) (READ_12BIT_DFR(z2, 1, vref))
|
||||
|
||||
#define READ_X(vref) (READ_12BIT_DFR(x, 1, vref))
|
||||
#define PWRDOWN (READ_12BIT_DFR(y, 0, 0)) /* LAST */
|
||||
|
||||
@ -207,6 +201,21 @@ struct ads7846 {
|
||||
#define REF_ON (READ_12BIT_DFR(x, 1, 1))
|
||||
#define REF_OFF (READ_12BIT_DFR(y, 0, 0))
|
||||
|
||||
/* Order commands in the most optimal way to reduce Vref switching and
|
||||
* settling time:
|
||||
* Measure: X; Vref: X+, X-; IN: Y+
|
||||
* Measure: Y; Vref: Y+, Y-; IN: X+
|
||||
* Measure: Z1; Vref: Y+, X-; IN: X+
|
||||
* Measure: Z2; Vref: Y+, X-; IN: Y-
|
||||
*/
|
||||
enum ads7846_cmds {
|
||||
ADS7846_X,
|
||||
ADS7846_Y,
|
||||
ADS7846_Z1,
|
||||
ADS7846_Z2,
|
||||
ADS7846_PWDOWN,
|
||||
};
|
||||
|
||||
static int get_pendown_state(struct ads7846 *ts)
|
||||
{
|
||||
if (ts->get_pendown_state)
|
||||
@ -689,26 +698,109 @@ static int ads7846_no_filter(void *ads, int data_idx, int *val)
|
||||
return ADS7846_FILTER_OK;
|
||||
}
|
||||
|
||||
static int ads7846_get_value(struct ads7846 *ts, struct spi_message *m)
|
||||
static int ads7846_get_value(struct ads7846_buf *buf)
|
||||
{
|
||||
int value;
|
||||
struct spi_transfer *t =
|
||||
list_entry(m->transfers.prev, struct spi_transfer, transfer_list);
|
||||
struct ads7846_buf *buf = t->rx_buf;
|
||||
|
||||
value = be16_to_cpup(&buf->data_be16);
|
||||
value = be16_to_cpup(&buf->data);
|
||||
|
||||
/* enforce ADC output is 12 bits width */
|
||||
return (value >> 3) & 0xfff;
|
||||
}
|
||||
|
||||
static void ads7846_update_value(struct spi_message *m, int val)
|
||||
static void ads7846_set_cmd_val(struct ads7846 *ts, enum ads7846_cmds cmd_idx,
|
||||
u16 val)
|
||||
{
|
||||
struct spi_transfer *t =
|
||||
list_entry(m->transfers.prev, struct spi_transfer, transfer_list);
|
||||
struct ads7846_buf *buf = t->rx_buf;
|
||||
struct ads7846_packet *packet = ts->packet;
|
||||
|
||||
buf->data = val;
|
||||
switch (cmd_idx) {
|
||||
case ADS7846_Y:
|
||||
packet->y = val;
|
||||
break;
|
||||
case ADS7846_X:
|
||||
packet->x = val;
|
||||
break;
|
||||
case ADS7846_Z1:
|
||||
packet->z1 = val;
|
||||
break;
|
||||
case ADS7846_Z2:
|
||||
packet->z2 = val;
|
||||
break;
|
||||
default:
|
||||
WARN_ON_ONCE(1);
|
||||
}
|
||||
}
|
||||
|
||||
static u8 ads7846_get_cmd(enum ads7846_cmds cmd_idx, int vref)
|
||||
{
|
||||
switch (cmd_idx) {
|
||||
case ADS7846_Y:
|
||||
return READ_Y(vref);
|
||||
case ADS7846_X:
|
||||
return READ_X(vref);
|
||||
|
||||
/* 7846 specific commands */
|
||||
case ADS7846_Z1:
|
||||
return READ_Z1(vref);
|
||||
case ADS7846_Z2:
|
||||
return READ_Z2(vref);
|
||||
case ADS7846_PWDOWN:
|
||||
return PWRDOWN;
|
||||
default:
|
||||
WARN_ON_ONCE(1);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static bool ads7846_cmd_need_settle(enum ads7846_cmds cmd_idx)
|
||||
{
|
||||
switch (cmd_idx) {
|
||||
case ADS7846_X:
|
||||
case ADS7846_Y:
|
||||
case ADS7846_Z1:
|
||||
case ADS7846_Z2:
|
||||
return true;
|
||||
case ADS7846_PWDOWN:
|
||||
return false;
|
||||
default:
|
||||
WARN_ON_ONCE(1);
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
static int ads7846_filter(struct ads7846 *ts)
|
||||
{
|
||||
struct ads7846_packet *packet = ts->packet;
|
||||
int action;
|
||||
int val;
|
||||
unsigned int cmd_idx, b;
|
||||
|
||||
packet->ignore = false;
|
||||
for (cmd_idx = packet->last_cmd_idx; cmd_idx < packet->cmds - 1; cmd_idx++) {
|
||||
struct ads7846_buf_layout *l = &packet->l[cmd_idx];
|
||||
|
||||
packet->last_cmd_idx = cmd_idx;
|
||||
|
||||
for (b = l->skip; b < l->count; b++) {
|
||||
val = ads7846_get_value(&packet->rx[l->offset + b]);
|
||||
|
||||
action = ts->filter(ts->filter_data, cmd_idx, &val);
|
||||
if (action == ADS7846_FILTER_REPEAT) {
|
||||
if (b == l->count - 1)
|
||||
return -EAGAIN;
|
||||
} else if (action == ADS7846_FILTER_OK) {
|
||||
ads7846_set_cmd_val(ts, cmd_idx, val);
|
||||
break;
|
||||
} else {
|
||||
packet->ignore = true;
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void ads7846_read_state(struct ads7846 *ts)
|
||||
@ -716,52 +808,26 @@ static void ads7846_read_state(struct ads7846 *ts)
|
||||
struct ads7846_packet *packet = ts->packet;
|
||||
struct spi_message *m;
|
||||
int msg_idx = 0;
|
||||
int val;
|
||||
int action;
|
||||
int error;
|
||||
|
||||
while (msg_idx < ts->msg_count) {
|
||||
packet->last_cmd_idx = 0;
|
||||
|
||||
while (true) {
|
||||
ts->wait_for_sync();
|
||||
|
||||
m = &ts->msg[msg_idx];
|
||||
error = spi_sync(ts->spi, m);
|
||||
if (error) {
|
||||
dev_err(&ts->spi->dev, "spi_sync --> %d\n", error);
|
||||
packet->tc.ignore = true;
|
||||
packet->ignore = true;
|
||||
return;
|
||||
}
|
||||
|
||||
/*
|
||||
* Last message is power down request, no need to convert
|
||||
* or filter the value.
|
||||
*/
|
||||
if (msg_idx < ts->msg_count - 1) {
|
||||
error = ads7846_filter(ts);
|
||||
if (error)
|
||||
continue;
|
||||
|
||||
val = ads7846_get_value(ts, m);
|
||||
|
||||
action = ts->filter(ts->filter_data, msg_idx, &val);
|
||||
switch (action) {
|
||||
case ADS7846_FILTER_REPEAT:
|
||||
continue;
|
||||
|
||||
case ADS7846_FILTER_IGNORE:
|
||||
packet->tc.ignore = true;
|
||||
msg_idx = ts->msg_count - 1;
|
||||
continue;
|
||||
|
||||
case ADS7846_FILTER_OK:
|
||||
ads7846_update_value(m, val);
|
||||
packet->tc.ignore = false;
|
||||
msg_idx++;
|
||||
break;
|
||||
|
||||
default:
|
||||
BUG();
|
||||
}
|
||||
} else {
|
||||
msg_idx++;
|
||||
}
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
@ -771,19 +837,14 @@ static void ads7846_report_state(struct ads7846 *ts)
|
||||
unsigned int Rt;
|
||||
u16 x, y, z1, z2;
|
||||
|
||||
/*
|
||||
* ads7846_get_value() does in-place conversion (including byte swap)
|
||||
* from on-the-wire format as part of debouncing to get stable
|
||||
* readings.
|
||||
*/
|
||||
x = packet->tc.x.data;
|
||||
y = packet->tc.y.data;
|
||||
x = packet->x;
|
||||
y = packet->y;
|
||||
if (ts->model == 7845) {
|
||||
z1 = 0;
|
||||
z2 = 0;
|
||||
} else {
|
||||
z1 = packet->tc.z1.data;
|
||||
z2 = packet->tc.z2.data;
|
||||
z1 = packet->z1;
|
||||
z2 = packet->z2;
|
||||
}
|
||||
|
||||
/* range filtering */
|
||||
@ -816,9 +877,9 @@ static void ads7846_report_state(struct ads7846 *ts)
|
||||
* the maximum. Don't report it to user space, repeat at least
|
||||
* once more the measurement
|
||||
*/
|
||||
if (packet->tc.ignore || Rt > ts->pressure_max) {
|
||||
if (packet->ignore || Rt > ts->pressure_max) {
|
||||
dev_vdbg(&ts->spi->dev, "ignored %d pressure %d\n",
|
||||
packet->tc.ignore, Rt);
|
||||
packet->ignore, Rt);
|
||||
return;
|
||||
}
|
||||
|
||||
@ -979,13 +1040,59 @@ static int ads7846_setup_pendown(struct spi_device *spi,
|
||||
* Set up the transfers to read touchscreen state; this assumes we
|
||||
* use formula #2 for pressure, not #3.
|
||||
*/
|
||||
static void ads7846_setup_spi_msg(struct ads7846 *ts,
|
||||
static int ads7846_setup_spi_msg(struct ads7846 *ts,
|
||||
const struct ads7846_platform_data *pdata)
|
||||
{
|
||||
struct spi_message *m = &ts->msg[0];
|
||||
struct spi_transfer *x = ts->xfer;
|
||||
struct ads7846_packet *packet = ts->packet;
|
||||
int vref = pdata->keep_vref_on;
|
||||
unsigned int count, offset = 0;
|
||||
unsigned int cmd_idx, b;
|
||||
unsigned long time;
|
||||
size_t size = 0;
|
||||
|
||||
/* time per bit */
|
||||
time = NSEC_PER_SEC / ts->spi->max_speed_hz;
|
||||
|
||||
count = pdata->settle_delay_usecs * NSEC_PER_USEC / time;
|
||||
packet->count_skip = DIV_ROUND_UP(count, 24);
|
||||
|
||||
if (ts->debounce_max && ts->debounce_rep)
|
||||
/* ads7846_debounce_filter() is making ts->debounce_rep + 2
|
||||
* reads. So we need to get all samples for normal case. */
|
||||
packet->count = ts->debounce_rep + 2;
|
||||
else
|
||||
packet->count = 1;
|
||||
|
||||
if (ts->model == 7846)
|
||||
packet->cmds = 5; /* x, y, z1, z2, pwdown */
|
||||
else
|
||||
packet->cmds = 3; /* x, y, pwdown */
|
||||
|
||||
for (cmd_idx = 0; cmd_idx < packet->cmds; cmd_idx++) {
|
||||
struct ads7846_buf_layout *l = &packet->l[cmd_idx];
|
||||
unsigned int max_count;
|
||||
|
||||
if (ads7846_cmd_need_settle(cmd_idx))
|
||||
max_count = packet->count + packet->count_skip;
|
||||
else
|
||||
max_count = packet->count;
|
||||
|
||||
l->offset = offset;
|
||||
offset += max_count;
|
||||
l->count = max_count;
|
||||
l->skip = packet->count_skip;
|
||||
size += sizeof(*packet->tx) * max_count;
|
||||
}
|
||||
|
||||
packet->tx = devm_kzalloc(&ts->spi->dev, size, GFP_KERNEL);
|
||||
if (!packet->tx)
|
||||
return -ENOMEM;
|
||||
|
||||
packet->rx = devm_kzalloc(&ts->spi->dev, size, GFP_KERNEL);
|
||||
if (!packet->rx)
|
||||
return -ENOMEM;
|
||||
|
||||
if (ts->model == 7873) {
|
||||
/*
|
||||
@ -1001,117 +1108,20 @@ static void ads7846_setup_spi_msg(struct ads7846 *ts,
|
||||
spi_message_init(m);
|
||||
m->context = ts;
|
||||
|
||||
packet->read_y_cmd.cmd = READ_Y(vref);
|
||||
x->tx_buf = &packet->read_y_cmd;
|
||||
x->rx_buf = &packet->tc.y;
|
||||
x->len = 3;
|
||||
spi_message_add_tail(x, m);
|
||||
for (cmd_idx = 0; cmd_idx < packet->cmds; cmd_idx++) {
|
||||
struct ads7846_buf_layout *l = &packet->l[cmd_idx];
|
||||
u8 cmd = ads7846_get_cmd(cmd_idx, vref);
|
||||
|
||||
/*
|
||||
* The first sample after switching drivers can be low quality;
|
||||
* optionally discard it, using a second one after the signals
|
||||
* have had enough time to stabilize.
|
||||
*/
|
||||
if (pdata->settle_delay_usecs) {
|
||||
x->delay.value = pdata->settle_delay_usecs;
|
||||
x->delay.unit = SPI_DELAY_UNIT_USECS;
|
||||
x++;
|
||||
|
||||
x->tx_buf = &packet->read_y_cmd;
|
||||
x->rx_buf = &packet->tc.y;
|
||||
x->len = 3;
|
||||
spi_message_add_tail(x, m);
|
||||
for (b = 0; b < l->count; b++)
|
||||
packet->tx[l->offset + b].cmd = cmd;
|
||||
}
|
||||
|
||||
ts->msg_count++;
|
||||
m++;
|
||||
spi_message_init(m);
|
||||
m->context = ts;
|
||||
|
||||
/* turn y- off, x+ on, then leave in lowpower */
|
||||
x++;
|
||||
packet->read_x_cmd.cmd = READ_X(vref);
|
||||
x->tx_buf = &packet->read_x_cmd;
|
||||
x->rx_buf = &packet->tc.x;
|
||||
x->len = 3;
|
||||
x->tx_buf = packet->tx;
|
||||
x->rx_buf = packet->rx;
|
||||
x->len = size;
|
||||
spi_message_add_tail(x, m);
|
||||
|
||||
/* ... maybe discard first sample ... */
|
||||
if (pdata->settle_delay_usecs) {
|
||||
x->delay.value = pdata->settle_delay_usecs;
|
||||
x->delay.unit = SPI_DELAY_UNIT_USECS;
|
||||
|
||||
x++;
|
||||
x->tx_buf = &packet->read_x_cmd;
|
||||
x->rx_buf = &packet->tc.x;
|
||||
x->len = 3;
|
||||
spi_message_add_tail(x, m);
|
||||
}
|
||||
|
||||
/* turn y+ off, x- on; we'll use formula #2 */
|
||||
if (ts->model == 7846) {
|
||||
ts->msg_count++;
|
||||
m++;
|
||||
spi_message_init(m);
|
||||
m->context = ts;
|
||||
|
||||
x++;
|
||||
packet->read_z1_cmd.cmd = READ_Z1(vref);
|
||||
x->tx_buf = &packet->read_z1_cmd;
|
||||
x->rx_buf = &packet->tc.z1;
|
||||
x->len = 3;
|
||||
spi_message_add_tail(x, m);
|
||||
|
||||
/* ... maybe discard first sample ... */
|
||||
if (pdata->settle_delay_usecs) {
|
||||
x->delay.value = pdata->settle_delay_usecs;
|
||||
x->delay.unit = SPI_DELAY_UNIT_USECS;
|
||||
|
||||
x++;
|
||||
x->tx_buf = &packet->read_z1_cmd;
|
||||
x->rx_buf = &packet->tc.z1;
|
||||
x->len = 3;
|
||||
spi_message_add_tail(x, m);
|
||||
}
|
||||
|
||||
ts->msg_count++;
|
||||
m++;
|
||||
spi_message_init(m);
|
||||
m->context = ts;
|
||||
|
||||
x++;
|
||||
packet->read_z2_cmd.cmd = READ_Z2(vref);
|
||||
x->tx_buf = &packet->read_z2_cmd;
|
||||
x->rx_buf = &packet->tc.z2;
|
||||
x->len = 3;
|
||||
spi_message_add_tail(x, m);
|
||||
|
||||
/* ... maybe discard first sample ... */
|
||||
if (pdata->settle_delay_usecs) {
|
||||
x->delay.value = pdata->settle_delay_usecs;
|
||||
x->delay.unit = SPI_DELAY_UNIT_USECS;
|
||||
|
||||
x++;
|
||||
x->tx_buf = &packet->read_z2_cmd;
|
||||
x->rx_buf = &packet->tc.z2;
|
||||
x->len = 3;
|
||||
spi_message_add_tail(x, m);
|
||||
}
|
||||
}
|
||||
|
||||
/* power down */
|
||||
ts->msg_count++;
|
||||
m++;
|
||||
spi_message_init(m);
|
||||
m->context = ts;
|
||||
|
||||
x++;
|
||||
packet->pwrdown_cmd.cmd = PWRDOWN;
|
||||
x->tx_buf = &packet->pwrdown_cmd;
|
||||
x->len = 3;
|
||||
|
||||
CS_CHANGE(*x);
|
||||
spi_message_add_tail(x, m);
|
||||
return 0;
|
||||
}
|
||||
|
||||
#ifdef CONFIG_OF
|
||||
|
@ -56,6 +56,7 @@
|
||||
#define QUEUE_HEADER_SINGLE 0x62
|
||||
#define QUEUE_HEADER_NORMAL 0X63
|
||||
#define QUEUE_HEADER_WAIT 0x64
|
||||
#define QUEUE_HEADER_NORMAL2 0x66
|
||||
|
||||
/* Command header definition */
|
||||
#define CMD_HEADER_WRITE 0x54
|
||||
@ -69,6 +70,7 @@
|
||||
#define CMD_HEADER_REK 0x66
|
||||
|
||||
/* FW position data */
|
||||
#define PACKET_SIZE_OLD 40
|
||||
#define PACKET_SIZE 55
|
||||
#define MAX_CONTACT_NUM 10
|
||||
#define FW_POS_HEADER 0
|
||||
@ -90,6 +92,8 @@
|
||||
/* FW read command, 0x53 0x?? 0x0, 0x01 */
|
||||
#define E_ELAN_INFO_FW_VER 0x00
|
||||
#define E_ELAN_INFO_BC_VER 0x10
|
||||
#define E_ELAN_INFO_X_RES 0x60
|
||||
#define E_ELAN_INFO_Y_RES 0x63
|
||||
#define E_ELAN_INFO_REK 0xD0
|
||||
#define E_ELAN_INFO_TEST_VER 0xE0
|
||||
#define E_ELAN_INFO_FW_ID 0xF0
|
||||
@ -112,6 +116,11 @@
|
||||
#define ELAN_POWERON_DELAY_USEC 500
|
||||
#define ELAN_RESET_DELAY_MSEC 20
|
||||
|
||||
enum elants_chip_id {
|
||||
EKTH3500,
|
||||
EKTF3624,
|
||||
};
|
||||
|
||||
enum elants_state {
|
||||
ELAN_STATE_NORMAL,
|
||||
ELAN_WAIT_QUEUE_HEADER,
|
||||
@ -143,9 +152,12 @@ struct elants_data {
|
||||
unsigned int y_res;
|
||||
unsigned int x_max;
|
||||
unsigned int y_max;
|
||||
unsigned int phy_x;
|
||||
unsigned int phy_y;
|
||||
struct touchscreen_properties prop;
|
||||
|
||||
enum elants_state state;
|
||||
enum elants_chip_id chip_id;
|
||||
enum elants_iap_mode iap_mode;
|
||||
|
||||
/* Guards against concurrent access to the device via sysfs */
|
||||
@ -433,7 +445,51 @@ static int elants_i2c_query_bc_version(struct elants_data *ts)
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int elants_i2c_query_ts_info(struct elants_data *ts)
|
||||
static int elants_i2c_query_ts_info_ektf(struct elants_data *ts)
|
||||
{
|
||||
struct i2c_client *client = ts->client;
|
||||
int error;
|
||||
u8 resp[4];
|
||||
u16 phy_x, phy_y;
|
||||
const u8 get_xres_cmd[] = {
|
||||
CMD_HEADER_READ, E_ELAN_INFO_X_RES, 0x00, 0x00
|
||||
};
|
||||
const u8 get_yres_cmd[] = {
|
||||
CMD_HEADER_READ, E_ELAN_INFO_Y_RES, 0x00, 0x00
|
||||
};
|
||||
|
||||
/* Get X/Y size in mm */
|
||||
error = elants_i2c_execute_command(client, get_xres_cmd,
|
||||
sizeof(get_xres_cmd),
|
||||
resp, sizeof(resp), 1,
|
||||
"get X size");
|
||||
if (error)
|
||||
return error;
|
||||
|
||||
phy_x = resp[2] | ((resp[3] & 0xF0) << 4);
|
||||
|
||||
error = elants_i2c_execute_command(client, get_yres_cmd,
|
||||
sizeof(get_yres_cmd),
|
||||
resp, sizeof(resp), 1,
|
||||
"get Y size");
|
||||
if (error)
|
||||
return error;
|
||||
|
||||
phy_y = resp[2] | ((resp[3] & 0xF0) << 4);
|
||||
|
||||
dev_dbg(&client->dev, "phy_x=%d, phy_y=%d\n", phy_x, phy_y);
|
||||
|
||||
ts->phy_x = phy_x;
|
||||
ts->phy_y = phy_y;
|
||||
|
||||
/* eKTF doesn't report max size, set it to default values */
|
||||
ts->x_max = 2240 - 1;
|
||||
ts->y_max = 1408 - 1;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int elants_i2c_query_ts_info_ekth(struct elants_data *ts)
|
||||
{
|
||||
struct i2c_client *client = ts->client;
|
||||
int error;
|
||||
@ -508,6 +564,8 @@ static int elants_i2c_query_ts_info(struct elants_data *ts)
|
||||
ts->x_res = DIV_ROUND_CLOSEST(ts->x_max, phy_x);
|
||||
ts->y_max = ELAN_TS_RESOLUTION(cols, osr);
|
||||
ts->y_res = DIV_ROUND_CLOSEST(ts->y_max, phy_y);
|
||||
ts->phy_x = phy_x;
|
||||
ts->phy_y = phy_y;
|
||||
}
|
||||
|
||||
return 0;
|
||||
@ -587,8 +645,19 @@ static int elants_i2c_initialize(struct elants_data *ts)
|
||||
error = elants_i2c_query_fw_version(ts);
|
||||
if (!error)
|
||||
error = elants_i2c_query_test_version(ts);
|
||||
if (!error)
|
||||
error = elants_i2c_query_ts_info(ts);
|
||||
|
||||
switch (ts->chip_id) {
|
||||
case EKTH3500:
|
||||
if (!error)
|
||||
error = elants_i2c_query_ts_info_ekth(ts);
|
||||
break;
|
||||
case EKTF3624:
|
||||
if (!error)
|
||||
error = elants_i2c_query_ts_info_ektf(ts);
|
||||
break;
|
||||
default:
|
||||
BUG();
|
||||
}
|
||||
|
||||
if (error)
|
||||
ts->iap_mode = ELAN_IAP_RECOVERY;
|
||||
@ -853,7 +922,8 @@ out:
|
||||
* Event reporting.
|
||||
*/
|
||||
|
||||
static void elants_i2c_mt_event(struct elants_data *ts, u8 *buf)
|
||||
static void elants_i2c_mt_event(struct elants_data *ts, u8 *buf,
|
||||
size_t packet_size)
|
||||
{
|
||||
struct input_dev *input = ts->input;
|
||||
unsigned int n_fingers;
|
||||
@ -880,8 +950,24 @@ static void elants_i2c_mt_event(struct elants_data *ts, u8 *buf)
|
||||
pos = &buf[FW_POS_XY + i * 3];
|
||||
x = (((u16)pos[0] & 0xf0) << 4) | pos[1];
|
||||
y = (((u16)pos[0] & 0x0f) << 8) | pos[2];
|
||||
p = buf[FW_POS_PRESSURE + i];
|
||||
w = buf[FW_POS_WIDTH + i];
|
||||
|
||||
/*
|
||||
* eKTF3624 may have use "old" touch-report format,
|
||||
* depending on a device and TS firmware version.
|
||||
* For example, ASUS Transformer devices use the "old"
|
||||
* format, while ASUS Nexus 7 uses the "new" formant.
|
||||
*/
|
||||
if (packet_size == PACKET_SIZE_OLD &&
|
||||
ts->chip_id == EKTF3624) {
|
||||
w = buf[FW_POS_WIDTH + i / 2];
|
||||
w >>= 4 * (~i & 1);
|
||||
w |= w << 4;
|
||||
w |= !w;
|
||||
p = w;
|
||||
} else {
|
||||
p = buf[FW_POS_PRESSURE + i];
|
||||
w = buf[FW_POS_WIDTH + i];
|
||||
}
|
||||
|
||||
dev_dbg(&ts->client->dev, "i=%d x=%d y=%d p=%d w=%d\n",
|
||||
i, x, y, p, w);
|
||||
@ -913,7 +999,8 @@ static u8 elants_i2c_calculate_checksum(u8 *buf)
|
||||
return checksum;
|
||||
}
|
||||
|
||||
static void elants_i2c_event(struct elants_data *ts, u8 *buf)
|
||||
static void elants_i2c_event(struct elants_data *ts, u8 *buf,
|
||||
size_t packet_size)
|
||||
{
|
||||
u8 checksum = elants_i2c_calculate_checksum(buf);
|
||||
|
||||
@ -927,7 +1014,7 @@ static void elants_i2c_event(struct elants_data *ts, u8 *buf)
|
||||
"%s: unknown packet type: %02x\n",
|
||||
__func__, buf[FW_POS_HEADER]);
|
||||
else
|
||||
elants_i2c_mt_event(ts, buf);
|
||||
elants_i2c_mt_event(ts, buf, packet_size);
|
||||
}
|
||||
|
||||
static irqreturn_t elants_i2c_irq(int irq, void *_dev)
|
||||
@ -970,7 +1057,6 @@ static irqreturn_t elants_i2c_irq(int irq, void *_dev)
|
||||
switch (ts->buf[FW_HDR_TYPE]) {
|
||||
case CMD_HEADER_HELLO:
|
||||
case CMD_HEADER_RESP:
|
||||
case CMD_HEADER_REK:
|
||||
break;
|
||||
|
||||
case QUEUE_HEADER_WAIT:
|
||||
@ -985,9 +1071,24 @@ static irqreturn_t elants_i2c_irq(int irq, void *_dev)
|
||||
break;
|
||||
|
||||
case QUEUE_HEADER_SINGLE:
|
||||
elants_i2c_event(ts, &ts->buf[HEADER_SIZE]);
|
||||
elants_i2c_event(ts, &ts->buf[HEADER_SIZE],
|
||||
ts->buf[FW_HDR_LENGTH]);
|
||||
break;
|
||||
|
||||
case QUEUE_HEADER_NORMAL2: /* CMD_HEADER_REK */
|
||||
/*
|
||||
* Depending on firmware version, eKTF3624 touchscreens
|
||||
* may utilize one of these opcodes for the touch events:
|
||||
* 0x63 (NORMAL) and 0x66 (NORMAL2). The 0x63 is used by
|
||||
* older firmware version and differs from 0x66 such that
|
||||
* touch pressure value needs to be adjusted. The 0x66
|
||||
* opcode of newer firmware is equal to 0x63 of eKTH3500.
|
||||
*/
|
||||
if (ts->chip_id != EKTF3624)
|
||||
break;
|
||||
|
||||
fallthrough;
|
||||
|
||||
case QUEUE_HEADER_NORMAL:
|
||||
report_count = ts->buf[FW_HDR_COUNT];
|
||||
if (report_count == 0 || report_count > 3) {
|
||||
@ -998,7 +1099,12 @@ static irqreturn_t elants_i2c_irq(int irq, void *_dev)
|
||||
}
|
||||
|
||||
report_len = ts->buf[FW_HDR_LENGTH] / report_count;
|
||||
if (report_len != PACKET_SIZE) {
|
||||
|
||||
if (report_len == PACKET_SIZE_OLD &&
|
||||
ts->chip_id == EKTF3624) {
|
||||
dev_dbg_once(&client->dev,
|
||||
"using old report format\n");
|
||||
} else if (report_len != PACKET_SIZE) {
|
||||
dev_err(&client->dev,
|
||||
"mismatching report length: %*ph\n",
|
||||
HEADER_SIZE, ts->buf);
|
||||
@ -1007,8 +1113,8 @@ static irqreturn_t elants_i2c_irq(int irq, void *_dev)
|
||||
|
||||
for (i = 0; i < report_count; i++) {
|
||||
u8 *buf = ts->buf + HEADER_SIZE +
|
||||
i * PACKET_SIZE;
|
||||
elants_i2c_event(ts, buf);
|
||||
i * report_len;
|
||||
elants_i2c_event(ts, buf, report_len);
|
||||
}
|
||||
break;
|
||||
|
||||
@ -1250,6 +1356,7 @@ static int elants_i2c_probe(struct i2c_client *client,
|
||||
init_completion(&ts->cmd_done);
|
||||
|
||||
ts->client = client;
|
||||
ts->chip_id = (enum elants_chip_id)id->driver_data;
|
||||
i2c_set_clientdata(client, ts);
|
||||
|
||||
ts->vcc33 = devm_regulator_get(&client->dev, "vcc33");
|
||||
@ -1331,13 +1438,20 @@ static int elants_i2c_probe(struct i2c_client *client,
|
||||
input_set_abs_params(ts->input, ABS_MT_PRESSURE, 0, 255, 0, 0);
|
||||
input_set_abs_params(ts->input, ABS_MT_TOOL_TYPE,
|
||||
0, MT_TOOL_PALM, 0, 0);
|
||||
|
||||
touchscreen_parse_properties(ts->input, true, &ts->prop);
|
||||
|
||||
if (ts->chip_id == EKTF3624) {
|
||||
/* calculate resolution from size */
|
||||
ts->x_res = DIV_ROUND_CLOSEST(ts->prop.max_x, ts->phy_x);
|
||||
ts->y_res = DIV_ROUND_CLOSEST(ts->prop.max_y, ts->phy_y);
|
||||
}
|
||||
|
||||
input_abs_set_res(ts->input, ABS_MT_POSITION_X, ts->x_res);
|
||||
input_abs_set_res(ts->input, ABS_MT_POSITION_Y, ts->y_res);
|
||||
if (ts->major_res > 0)
|
||||
input_abs_set_res(ts->input, ABS_MT_TOUCH_MAJOR, ts->major_res);
|
||||
|
||||
touchscreen_parse_properties(ts->input, true, &ts->prop);
|
||||
|
||||
error = input_mt_init_slots(ts->input, MAX_CONTACT_NUM,
|
||||
INPUT_MT_DIRECT | INPUT_MT_DROP_UNUSED);
|
||||
if (error) {
|
||||
@ -1466,14 +1580,16 @@ static SIMPLE_DEV_PM_OPS(elants_i2c_pm_ops,
|
||||
elants_i2c_suspend, elants_i2c_resume);
|
||||
|
||||
static const struct i2c_device_id elants_i2c_id[] = {
|
||||
{ DEVICE_NAME, 0 },
|
||||
{ DEVICE_NAME, EKTH3500 },
|
||||
{ "ekth3500", EKTH3500 },
|
||||
{ "ektf3624", EKTF3624 },
|
||||
{ }
|
||||
};
|
||||
MODULE_DEVICE_TABLE(i2c, elants_i2c_id);
|
||||
|
||||
#ifdef CONFIG_ACPI
|
||||
static const struct acpi_device_id elants_acpi_id[] = {
|
||||
{ "ELAN0001", 0 },
|
||||
{ "ELAN0001", EKTH3500 },
|
||||
{ }
|
||||
};
|
||||
MODULE_DEVICE_TABLE(acpi, elants_acpi_id);
|
||||
@ -1482,6 +1598,7 @@ MODULE_DEVICE_TABLE(acpi, elants_acpi_id);
|
||||
#ifdef CONFIG_OF
|
||||
static const struct of_device_id elants_of_match[] = {
|
||||
{ .compatible = "elan,ekth3500" },
|
||||
{ .compatible = "elan,ektf3624" },
|
||||
{ /* sentinel */ }
|
||||
};
|
||||
MODULE_DEVICE_TABLE(of, elants_of_match);
|
||||
|
@ -341,8 +341,10 @@ static int elo_connect(struct serio *serio, struct serio_driver *drv)
|
||||
switch (elo->id) {
|
||||
|
||||
case 0: /* 10-byte protocol */
|
||||
if (elo_setup_10(elo))
|
||||
if (elo_setup_10(elo)) {
|
||||
err = -EIO;
|
||||
goto fail3;
|
||||
}
|
||||
|
||||
break;
|
||||
|
||||
|
@ -2,8 +2,7 @@
|
||||
/*
|
||||
* Azoteq IQS550/572/525 Trackpad/Touchscreen Controller
|
||||
*
|
||||
* Copyright (C) 2018
|
||||
* Author: Jeff LaBundy <jeff@labundy.com>
|
||||
* Copyright (C) 2018 Jeff LaBundy <jeff@labundy.com>
|
||||
*
|
||||
* These devices require firmware exported from a PC-based configuration tool
|
||||
* made available by the vendor. Firmware files may be pushed to the device's
|
||||
@ -12,6 +11,7 @@
|
||||
* Link to PC-based configuration tool and data sheet: http://www.azoteq.com/
|
||||
*/
|
||||
|
||||
#include <linux/bits.h>
|
||||
#include <linux/delay.h>
|
||||
#include <linux/device.h>
|
||||
#include <linux/err.h>
|
||||
@ -30,9 +30,9 @@
|
||||
|
||||
#define IQS5XX_FW_FILE_LEN 64
|
||||
#define IQS5XX_NUM_RETRIES 10
|
||||
#define IQS5XX_NUM_POINTS 256
|
||||
#define IQS5XX_NUM_CONTACTS 5
|
||||
#define IQS5XX_WR_BYTES_MAX 2
|
||||
#define IQS5XX_XY_RES_MAX 0xFFFE
|
||||
|
||||
#define IQS5XX_PROD_NUM_IQS550 40
|
||||
#define IQS5XX_PROD_NUM_IQS572 58
|
||||
@ -41,28 +41,27 @@
|
||||
#define IQS5XX_PROJ_NUM_B000 15
|
||||
#define IQS5XX_MAJOR_VER_MIN 2
|
||||
|
||||
#define IQS5XX_RESUME 0x00
|
||||
#define IQS5XX_SUSPEND 0x01
|
||||
#define IQS5XX_SHOW_RESET BIT(7)
|
||||
#define IQS5XX_ACK_RESET BIT(7)
|
||||
|
||||
#define IQS5XX_SW_INPUT_EVENT 0x10
|
||||
#define IQS5XX_SETUP_COMPLETE 0x40
|
||||
#define IQS5XX_EVENT_MODE 0x01
|
||||
#define IQS5XX_TP_EVENT 0x04
|
||||
#define IQS5XX_SUSPEND BIT(0)
|
||||
#define IQS5XX_RESUME 0
|
||||
|
||||
#define IQS5XX_FLIP_X 0x01
|
||||
#define IQS5XX_FLIP_Y 0x02
|
||||
#define IQS5XX_SWITCH_XY_AXIS 0x04
|
||||
#define IQS5XX_SETUP_COMPLETE BIT(6)
|
||||
#define IQS5XX_WDT BIT(5)
|
||||
#define IQS5XX_ALP_REATI BIT(3)
|
||||
#define IQS5XX_REATI BIT(2)
|
||||
|
||||
#define IQS5XX_TP_EVENT BIT(2)
|
||||
#define IQS5XX_EVENT_MODE BIT(0)
|
||||
|
||||
#define IQS5XX_PROD_NUM 0x0000
|
||||
#define IQS5XX_ABS_X 0x0016
|
||||
#define IQS5XX_ABS_Y 0x0018
|
||||
#define IQS5XX_SYS_INFO0 0x000F
|
||||
#define IQS5XX_SYS_INFO1 0x0010
|
||||
#define IQS5XX_SYS_CTRL0 0x0431
|
||||
#define IQS5XX_SYS_CTRL1 0x0432
|
||||
#define IQS5XX_SYS_CFG0 0x058E
|
||||
#define IQS5XX_SYS_CFG1 0x058F
|
||||
#define IQS5XX_TOTAL_RX 0x063D
|
||||
#define IQS5XX_TOTAL_TX 0x063E
|
||||
#define IQS5XX_XY_CFG0 0x0669
|
||||
#define IQS5XX_X_RES 0x066E
|
||||
#define IQS5XX_Y_RES 0x0670
|
||||
#define IQS5XX_CHKSM 0x83C0
|
||||
@ -99,6 +98,7 @@ struct iqs5xx_private {
|
||||
struct i2c_client *client;
|
||||
struct input_dev *input;
|
||||
struct gpio_desc *reset_gpio;
|
||||
struct touchscreen_properties prop;
|
||||
struct mutex lock;
|
||||
u8 bl_status;
|
||||
};
|
||||
@ -126,6 +126,14 @@ struct iqs5xx_touch_data {
|
||||
u8 area;
|
||||
} __packed;
|
||||
|
||||
struct iqs5xx_status {
|
||||
u8 sys_info[2];
|
||||
u8 num_active;
|
||||
__be16 rel_x;
|
||||
__be16 rel_y;
|
||||
struct iqs5xx_touch_data touch_data[IQS5XX_NUM_CONTACTS];
|
||||
} __packed;
|
||||
|
||||
static int iqs5xx_read_burst(struct i2c_client *client,
|
||||
u16 reg, void *val, u16 len)
|
||||
{
|
||||
@ -182,11 +190,6 @@ static int iqs5xx_read_word(struct i2c_client *client, u16 reg, u16 *val)
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int iqs5xx_read_byte(struct i2c_client *client, u16 reg, u8 *val)
|
||||
{
|
||||
return iqs5xx_read_burst(client, reg, val, sizeof(*val));
|
||||
}
|
||||
|
||||
static int iqs5xx_write_burst(struct i2c_client *client,
|
||||
u16 reg, const void *val, u16 len)
|
||||
{
|
||||
@ -337,11 +340,16 @@ static int iqs5xx_bl_open(struct i2c_client *client)
|
||||
*/
|
||||
for (i = 0; i < IQS5XX_BL_ATTEMPTS; i++) {
|
||||
iqs5xx_reset(client);
|
||||
usleep_range(350, 400);
|
||||
|
||||
for (j = 0; j < IQS5XX_NUM_RETRIES; j++) {
|
||||
error = iqs5xx_bl_cmd(client, IQS5XX_BL_CMD_VER, 0);
|
||||
if (!error || error == -EINVAL)
|
||||
return error;
|
||||
if (!error)
|
||||
usleep_range(10000, 10100);
|
||||
else if (error != -EINVAL)
|
||||
continue;
|
||||
|
||||
return error;
|
||||
}
|
||||
}
|
||||
|
||||
@ -481,12 +489,10 @@ static void iqs5xx_close(struct input_dev *input)
|
||||
static int iqs5xx_axis_init(struct i2c_client *client)
|
||||
{
|
||||
struct iqs5xx_private *iqs5xx = i2c_get_clientdata(client);
|
||||
struct touchscreen_properties prop;
|
||||
struct touchscreen_properties *prop = &iqs5xx->prop;
|
||||
struct input_dev *input;
|
||||
u16 max_x, max_y;
|
||||
int error;
|
||||
u16 max_x, max_x_hw;
|
||||
u16 max_y, max_y_hw;
|
||||
u8 val;
|
||||
|
||||
if (!iqs5xx->input) {
|
||||
input = devm_input_allocate_device(&client->dev);
|
||||
@ -506,89 +512,39 @@ static int iqs5xx_axis_init(struct i2c_client *client)
|
||||
iqs5xx->input = input;
|
||||
}
|
||||
|
||||
touchscreen_parse_properties(iqs5xx->input, true, &prop);
|
||||
|
||||
error = iqs5xx_read_byte(client, IQS5XX_TOTAL_RX, &val);
|
||||
if (error)
|
||||
return error;
|
||||
max_x_hw = (val - 1) * IQS5XX_NUM_POINTS;
|
||||
|
||||
error = iqs5xx_read_byte(client, IQS5XX_TOTAL_TX, &val);
|
||||
if (error)
|
||||
return error;
|
||||
max_y_hw = (val - 1) * IQS5XX_NUM_POINTS;
|
||||
|
||||
error = iqs5xx_read_byte(client, IQS5XX_XY_CFG0, &val);
|
||||
error = iqs5xx_read_word(client, IQS5XX_X_RES, &max_x);
|
||||
if (error)
|
||||
return error;
|
||||
|
||||
if (val & IQS5XX_SWITCH_XY_AXIS)
|
||||
swap(max_x_hw, max_y_hw);
|
||||
|
||||
if (prop.swap_x_y)
|
||||
val ^= IQS5XX_SWITCH_XY_AXIS;
|
||||
|
||||
if (prop.invert_x)
|
||||
val ^= prop.swap_x_y ? IQS5XX_FLIP_Y : IQS5XX_FLIP_X;
|
||||
|
||||
if (prop.invert_y)
|
||||
val ^= prop.swap_x_y ? IQS5XX_FLIP_X : IQS5XX_FLIP_Y;
|
||||
|
||||
error = iqs5xx_write_byte(client, IQS5XX_XY_CFG0, val);
|
||||
error = iqs5xx_read_word(client, IQS5XX_Y_RES, &max_y);
|
||||
if (error)
|
||||
return error;
|
||||
|
||||
if (prop.max_x > max_x_hw) {
|
||||
input_abs_set_max(iqs5xx->input, ABS_MT_POSITION_X, max_x);
|
||||
input_abs_set_max(iqs5xx->input, ABS_MT_POSITION_Y, max_y);
|
||||
|
||||
touchscreen_parse_properties(iqs5xx->input, true, prop);
|
||||
|
||||
if (prop->max_x > IQS5XX_XY_RES_MAX) {
|
||||
dev_err(&client->dev, "Invalid maximum x-coordinate: %u > %u\n",
|
||||
prop.max_x, max_x_hw);
|
||||
prop->max_x, IQS5XX_XY_RES_MAX);
|
||||
return -EINVAL;
|
||||
} else if (prop.max_x == 0) {
|
||||
error = iqs5xx_read_word(client, IQS5XX_X_RES, &max_x);
|
||||
} else if (prop->max_x != max_x) {
|
||||
error = iqs5xx_write_word(client, IQS5XX_X_RES, prop->max_x);
|
||||
if (error)
|
||||
return error;
|
||||
|
||||
input_abs_set_max(iqs5xx->input,
|
||||
prop.swap_x_y ? ABS_MT_POSITION_Y :
|
||||
ABS_MT_POSITION_X,
|
||||
max_x);
|
||||
} else {
|
||||
max_x = (u16)prop.max_x;
|
||||
}
|
||||
|
||||
if (prop.max_y > max_y_hw) {
|
||||
if (prop->max_y > IQS5XX_XY_RES_MAX) {
|
||||
dev_err(&client->dev, "Invalid maximum y-coordinate: %u > %u\n",
|
||||
prop.max_y, max_y_hw);
|
||||
prop->max_y, IQS5XX_XY_RES_MAX);
|
||||
return -EINVAL;
|
||||
} else if (prop.max_y == 0) {
|
||||
error = iqs5xx_read_word(client, IQS5XX_Y_RES, &max_y);
|
||||
} else if (prop->max_y != max_y) {
|
||||
error = iqs5xx_write_word(client, IQS5XX_Y_RES, prop->max_y);
|
||||
if (error)
|
||||
return error;
|
||||
|
||||
input_abs_set_max(iqs5xx->input,
|
||||
prop.swap_x_y ? ABS_MT_POSITION_X :
|
||||
ABS_MT_POSITION_Y,
|
||||
max_y);
|
||||
} else {
|
||||
max_y = (u16)prop.max_y;
|
||||
}
|
||||
|
||||
/*
|
||||
* Write horizontal and vertical resolution to the device in case its
|
||||
* original defaults were overridden or swapped as per the properties
|
||||
* specified in the device tree.
|
||||
*/
|
||||
error = iqs5xx_write_word(client,
|
||||
prop.swap_x_y ? IQS5XX_Y_RES : IQS5XX_X_RES,
|
||||
max_x);
|
||||
if (error)
|
||||
return error;
|
||||
|
||||
error = iqs5xx_write_word(client,
|
||||
prop.swap_x_y ? IQS5XX_X_RES : IQS5XX_Y_RES,
|
||||
max_y);
|
||||
if (error)
|
||||
return error;
|
||||
|
||||
error = input_mt_init_slots(iqs5xx->input, IQS5XX_NUM_CONTACTS,
|
||||
INPUT_MT_DIRECT);
|
||||
if (error)
|
||||
@ -603,7 +559,6 @@ static int iqs5xx_dev_init(struct i2c_client *client)
|
||||
struct iqs5xx_private *iqs5xx = i2c_get_clientdata(client);
|
||||
struct iqs5xx_dev_id_info *dev_id_info;
|
||||
int error;
|
||||
u8 val;
|
||||
u8 buf[sizeof(*dev_id_info) + 1];
|
||||
|
||||
error = iqs5xx_read_burst(client, IQS5XX_PROD_NUM,
|
||||
@ -666,18 +621,18 @@ static int iqs5xx_dev_init(struct i2c_client *client)
|
||||
if (error)
|
||||
return error;
|
||||
|
||||
error = iqs5xx_read_byte(client, IQS5XX_SYS_CFG0, &val);
|
||||
error = iqs5xx_write_byte(client, IQS5XX_SYS_CTRL0, IQS5XX_ACK_RESET);
|
||||
if (error)
|
||||
return error;
|
||||
|
||||
val |= IQS5XX_SETUP_COMPLETE;
|
||||
val &= ~IQS5XX_SW_INPUT_EVENT;
|
||||
error = iqs5xx_write_byte(client, IQS5XX_SYS_CFG0, val);
|
||||
error = iqs5xx_write_byte(client, IQS5XX_SYS_CFG0,
|
||||
IQS5XX_SETUP_COMPLETE | IQS5XX_WDT |
|
||||
IQS5XX_ALP_REATI | IQS5XX_REATI);
|
||||
if (error)
|
||||
return error;
|
||||
|
||||
val = IQS5XX_TP_EVENT | IQS5XX_EVENT_MODE;
|
||||
error = iqs5xx_write_byte(client, IQS5XX_SYS_CFG1, val);
|
||||
error = iqs5xx_write_byte(client, IQS5XX_SYS_CFG1,
|
||||
IQS5XX_TP_EVENT | IQS5XX_EVENT_MODE);
|
||||
if (error)
|
||||
return error;
|
||||
|
||||
@ -688,13 +643,12 @@ static int iqs5xx_dev_init(struct i2c_client *client)
|
||||
iqs5xx->bl_status = dev_id_info->bl_status;
|
||||
|
||||
/*
|
||||
* Closure of the first communication window that appears following the
|
||||
* release of reset appears to kick off an initialization period during
|
||||
* which further communication is met with clock stretching. The return
|
||||
* from this function is delayed so that further communication attempts
|
||||
* avoid this period.
|
||||
* The following delay allows ATI to complete before the open and close
|
||||
* callbacks are free to elicit I2C communication. Any attempts to read
|
||||
* from or write to the device during this time may face extended clock
|
||||
* stretching and prompt the I2C controller to report an error.
|
||||
*/
|
||||
msleep(100);
|
||||
msleep(250);
|
||||
|
||||
return 0;
|
||||
}
|
||||
@ -702,7 +656,7 @@ static int iqs5xx_dev_init(struct i2c_client *client)
|
||||
static irqreturn_t iqs5xx_irq(int irq, void *data)
|
||||
{
|
||||
struct iqs5xx_private *iqs5xx = data;
|
||||
struct iqs5xx_touch_data touch_data[IQS5XX_NUM_CONTACTS];
|
||||
struct iqs5xx_status status;
|
||||
struct i2c_client *client = iqs5xx->client;
|
||||
struct input_dev *input = iqs5xx->input;
|
||||
int error, i;
|
||||
@ -715,21 +669,35 @@ static irqreturn_t iqs5xx_irq(int irq, void *data)
|
||||
if (iqs5xx->bl_status == IQS5XX_BL_STATUS_RESET)
|
||||
return IRQ_NONE;
|
||||
|
||||
error = iqs5xx_read_burst(client, IQS5XX_ABS_X,
|
||||
touch_data, sizeof(touch_data));
|
||||
error = iqs5xx_read_burst(client, IQS5XX_SYS_INFO0,
|
||||
&status, sizeof(status));
|
||||
if (error)
|
||||
return IRQ_NONE;
|
||||
|
||||
for (i = 0; i < ARRAY_SIZE(touch_data); i++) {
|
||||
u16 pressure = be16_to_cpu(touch_data[i].strength);
|
||||
if (status.sys_info[0] & IQS5XX_SHOW_RESET) {
|
||||
dev_err(&client->dev, "Unexpected device reset\n");
|
||||
|
||||
error = iqs5xx_dev_init(client);
|
||||
if (error) {
|
||||
dev_err(&client->dev,
|
||||
"Failed to re-initialize device: %d\n", error);
|
||||
return IRQ_NONE;
|
||||
}
|
||||
|
||||
return IRQ_HANDLED;
|
||||
}
|
||||
|
||||
for (i = 0; i < ARRAY_SIZE(status.touch_data); i++) {
|
||||
struct iqs5xx_touch_data *touch_data = &status.touch_data[i];
|
||||
u16 pressure = be16_to_cpu(touch_data->strength);
|
||||
|
||||
input_mt_slot(input, i);
|
||||
if (input_mt_report_slot_state(input, MT_TOOL_FINGER,
|
||||
pressure != 0)) {
|
||||
input_report_abs(input, ABS_MT_POSITION_X,
|
||||
be16_to_cpu(touch_data[i].abs_x));
|
||||
input_report_abs(input, ABS_MT_POSITION_Y,
|
||||
be16_to_cpu(touch_data[i].abs_y));
|
||||
touchscreen_report_pos(iqs5xx->input, &iqs5xx->prop,
|
||||
be16_to_cpu(touch_data->abs_x),
|
||||
be16_to_cpu(touch_data->abs_y),
|
||||
true);
|
||||
input_report_abs(input, ABS_MT_PRESSURE, pressure);
|
||||
}
|
||||
}
|
||||
@ -884,7 +852,7 @@ static int iqs5xx_fw_file_parse(struct i2c_client *client,
|
||||
static int iqs5xx_fw_file_write(struct i2c_client *client, const char *fw_file)
|
||||
{
|
||||
struct iqs5xx_private *iqs5xx = i2c_get_clientdata(client);
|
||||
int error;
|
||||
int error, error_bl = 0;
|
||||
u8 *pmap;
|
||||
|
||||
if (iqs5xx->bl_status == IQS5XX_BL_STATUS_NONE)
|
||||
@ -938,6 +906,7 @@ err_reset:
|
||||
usleep_range(10000, 10100);
|
||||
}
|
||||
|
||||
error_bl = error;
|
||||
error = iqs5xx_dev_init(client);
|
||||
if (!error && iqs5xx->bl_status == IQS5XX_BL_STATUS_RESET)
|
||||
error = -EINVAL;
|
||||
@ -949,11 +918,15 @@ err_reset:
|
||||
err_kfree:
|
||||
kfree(pmap);
|
||||
|
||||
if (error_bl)
|
||||
return error_bl;
|
||||
|
||||
return error;
|
||||
}
|
||||
|
||||
static ssize_t fw_file_store(struct device *dev, struct device_attribute *attr,
|
||||
const char *buf, size_t count)
|
||||
static ssize_t fw_file_store(struct device *dev,
|
||||
struct device_attribute *attr, const char *buf,
|
||||
size_t count)
|
||||
{
|
||||
struct iqs5xx_private *iqs5xx = dev_get_drvdata(dev);
|
||||
struct i2c_client *client = iqs5xx->client;
|
||||
@ -1012,7 +985,7 @@ static int __maybe_unused iqs5xx_suspend(struct device *dev)
|
||||
struct input_dev *input = iqs5xx->input;
|
||||
int error = 0;
|
||||
|
||||
if (!input)
|
||||
if (!input || device_may_wakeup(dev))
|
||||
return error;
|
||||
|
||||
mutex_lock(&input->mutex);
|
||||
@ -1031,7 +1004,7 @@ static int __maybe_unused iqs5xx_resume(struct device *dev)
|
||||
struct input_dev *input = iqs5xx->input;
|
||||
int error = 0;
|
||||
|
||||
if (!input)
|
||||
if (!input || device_may_wakeup(dev))
|
||||
return error;
|
||||
|
||||
mutex_lock(&input->mutex);
|
||||
|
@ -465,13 +465,13 @@ static void mip4_report_keys(struct mip4_ts *ts, u8 *packet)
|
||||
static void mip4_report_touch(struct mip4_ts *ts, u8 *packet)
|
||||
{
|
||||
int id;
|
||||
bool hover;
|
||||
bool palm;
|
||||
bool __always_unused hover;
|
||||
bool __always_unused palm;
|
||||
bool state;
|
||||
u16 x, y;
|
||||
u8 pressure_stage = 0;
|
||||
u8 __always_unused pressure_stage = 0;
|
||||
u8 pressure;
|
||||
u8 size;
|
||||
u8 __always_unused size;
|
||||
u8 touch_major;
|
||||
u8 touch_minor;
|
||||
|
||||
|
@ -445,6 +445,7 @@ static int raydium_i2c_write_object(struct i2c_client *client,
|
||||
enum raydium_bl_ack state)
|
||||
{
|
||||
int error;
|
||||
static const u8 cmd[] = { 0xFF, 0x39 };
|
||||
|
||||
error = raydium_i2c_send(client, RM_CMD_BOOT_WRT, data, len);
|
||||
if (error) {
|
||||
@ -453,7 +454,7 @@ static int raydium_i2c_write_object(struct i2c_client *client,
|
||||
return error;
|
||||
}
|
||||
|
||||
error = raydium_i2c_send(client, RM_CMD_BOOT_ACK, NULL, 0);
|
||||
error = raydium_i2c_send(client, RM_CMD_BOOT_ACK, cmd, sizeof(cmd));
|
||||
if (error) {
|
||||
dev_err(&client->dev, "Ack obj command failed: %d\n", error);
|
||||
return error;
|
||||
|
@ -94,8 +94,13 @@ static int st1232_ts_wait_ready(struct st1232_ts_data *ts)
|
||||
|
||||
for (retries = 10; retries; retries--) {
|
||||
error = st1232_ts_read_data(ts, REG_STATUS, 1);
|
||||
if (!error && ts->read_buf[0] == (STATUS_NORMAL | ERROR_NONE))
|
||||
return 0;
|
||||
if (!error) {
|
||||
switch (ts->read_buf[0]) {
|
||||
case STATUS_NORMAL | ERROR_NONE:
|
||||
case STATUS_IDLE | ERROR_NONE:
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
usleep_range(1000, 2000);
|
||||
}
|
||||
|
@ -52,6 +52,7 @@
|
||||
* @idev: registered input device
|
||||
* @work: a work item used to scan the device
|
||||
* @dev: a pointer back to the MFD cell struct device*
|
||||
* @prop: Touchscreen properties
|
||||
* @ave_ctrl: Sample average control
|
||||
* (0 -> 1 sample, 1 -> 2 samples, 2 -> 4 samples, 3 -> 8 samples)
|
||||
* @touch_det_delay: Touch detect interrupt delay
|
||||
|
@ -787,6 +787,7 @@ static int sur40_probe(struct usb_interface *interface,
|
||||
dev_err(&interface->dev,
|
||||
"Unable to register video controls.");
|
||||
v4l2_ctrl_handler_free(&sur40->hdl);
|
||||
error = sur40->hdl.error;
|
||||
goto err_unreg_v4l2;
|
||||
}
|
||||
|
||||
|
@ -94,9 +94,7 @@ static void surface3_spi_report_touch(struct surface3_ts_data *ts_data,
|
||||
|
||||
static void surface3_spi_process_touch(struct surface3_ts_data *ts_data, u8 *data)
|
||||
{
|
||||
u16 timestamp;
|
||||
unsigned int i;
|
||||
timestamp = get_unaligned_le16(&data[15]);
|
||||
|
||||
for (i = 0; i < 13; i++) {
|
||||
struct surface3_ts_data_finger *finger;
|
||||
|
@ -1044,6 +1044,7 @@ static void nexio_exit(struct usbtouch_usb *usbtouch)
|
||||
|
||||
static int nexio_read_data(struct usbtouch_usb *usbtouch, unsigned char *pkt)
|
||||
{
|
||||
struct device *dev = &usbtouch->interface->dev;
|
||||
struct nexio_touch_packet *packet = (void *) pkt;
|
||||
struct nexio_priv *priv = usbtouch->priv;
|
||||
unsigned int data_len = be16_to_cpu(packet->data_len);
|
||||
@ -1062,6 +1063,8 @@ static int nexio_read_data(struct usbtouch_usb *usbtouch, unsigned char *pkt)
|
||||
|
||||
/* send ACK */
|
||||
ret = usb_submit_urb(priv->ack, GFP_ATOMIC);
|
||||
if (ret)
|
||||
dev_warn(dev, "Failed to submit ACK URB: %d\n", ret);
|
||||
|
||||
if (!usbtouch->type->max_xc) {
|
||||
usbtouch->type->max_xc = 2 * x_len;
|
||||
|
@ -161,7 +161,7 @@ static int zinitix_read_data(struct i2c_client *client,
|
||||
|
||||
ret = i2c_master_recv(client, (u8 *)values, length);
|
||||
if (ret != length)
|
||||
return ret < 0 ? ret : -EIO; ;
|
||||
return ret < 0 ? ret : -EIO;
|
||||
|
||||
return 0;
|
||||
}
|
||||
@ -190,7 +190,7 @@ static int zinitix_write_cmd(struct i2c_client *client, u16 reg)
|
||||
return 0;
|
||||
}
|
||||
|
||||
static bool zinitix_init_touch(struct bt541_ts_data *bt541)
|
||||
static int zinitix_init_touch(struct bt541_ts_data *bt541)
|
||||
{
|
||||
struct i2c_client *client = bt541->client;
|
||||
int i;
|
||||
|
103
include/dt-bindings/input/cros-ec-keyboard.h
Normal file
103
include/dt-bindings/input/cros-ec-keyboard.h
Normal file
@ -0,0 +1,103 @@
|
||||
/* SPDX-License-Identifier: GPL-2.0 */
|
||||
/*
|
||||
* This header provides the constants of the standard Chrome OS key matrix
|
||||
* for cros-ec keyboard-controller bindings.
|
||||
*
|
||||
* Copyright (c) 2021 Google, Inc
|
||||
*/
|
||||
|
||||
#ifndef _CROS_EC_KEYBOARD_H
|
||||
#define _CROS_EC_KEYBOARD_H
|
||||
|
||||
#define CROS_STD_TOP_ROW_KEYMAP \
|
||||
MATRIX_KEY(0x00, 0x02, KEY_F1) \
|
||||
MATRIX_KEY(0x03, 0x02, KEY_F2) \
|
||||
MATRIX_KEY(0x02, 0x02, KEY_F3) \
|
||||
MATRIX_KEY(0x01, 0x02, KEY_F4) \
|
||||
MATRIX_KEY(0x03, 0x04, KEY_F5) \
|
||||
MATRIX_KEY(0x02, 0x04, KEY_F6) \
|
||||
MATRIX_KEY(0x01, 0x04, KEY_F7) \
|
||||
MATRIX_KEY(0x02, 0x09, KEY_F8) \
|
||||
MATRIX_KEY(0x01, 0x09, KEY_F9) \
|
||||
MATRIX_KEY(0x00, 0x04, KEY_F10)
|
||||
|
||||
#define CROS_STD_MAIN_KEYMAP \
|
||||
MATRIX_KEY(0x00, 0x01, KEY_LEFTMETA) \
|
||||
MATRIX_KEY(0x00, 0x03, KEY_B) \
|
||||
MATRIX_KEY(0x00, 0x05, KEY_RO) \
|
||||
MATRIX_KEY(0x00, 0x06, KEY_N) \
|
||||
MATRIX_KEY(0x00, 0x08, KEY_EQUAL) \
|
||||
MATRIX_KEY(0x00, 0x0a, KEY_RIGHTALT) \
|
||||
MATRIX_KEY(0x01, 0x01, KEY_ESC) \
|
||||
MATRIX_KEY(0x01, 0x03, KEY_G) \
|
||||
MATRIX_KEY(0x01, 0x06, KEY_H) \
|
||||
MATRIX_KEY(0x01, 0x08, KEY_APOSTROPHE) \
|
||||
MATRIX_KEY(0x01, 0x0b, KEY_BACKSPACE) \
|
||||
MATRIX_KEY(0x01, 0x0c, KEY_HENKAN) \
|
||||
\
|
||||
MATRIX_KEY(0x02, 0x00, KEY_LEFTCTRL) \
|
||||
MATRIX_KEY(0x02, 0x01, KEY_TAB) \
|
||||
MATRIX_KEY(0x02, 0x03, KEY_T) \
|
||||
MATRIX_KEY(0x02, 0x05, KEY_RIGHTBRACE) \
|
||||
MATRIX_KEY(0x02, 0x06, KEY_Y) \
|
||||
MATRIX_KEY(0x02, 0x07, KEY_102ND) \
|
||||
MATRIX_KEY(0x02, 0x08, KEY_LEFTBRACE) \
|
||||
MATRIX_KEY(0x02, 0x0a, KEY_YEN) \
|
||||
\
|
||||
MATRIX_KEY(0x03, 0x00, KEY_LEFTMETA) \
|
||||
MATRIX_KEY(0x03, 0x01, KEY_GRAVE) \
|
||||
MATRIX_KEY(0x03, 0x03, KEY_5) \
|
||||
MATRIX_KEY(0x03, 0x06, KEY_6) \
|
||||
MATRIX_KEY(0x03, 0x08, KEY_MINUS) \
|
||||
MATRIX_KEY(0x03, 0x09, KEY_SLEEP) \
|
||||
MATRIX_KEY(0x03, 0x0b, KEY_BACKSLASH) \
|
||||
MATRIX_KEY(0x03, 0x0c, KEY_MUHENKAN) \
|
||||
\
|
||||
MATRIX_KEY(0x04, 0x00, KEY_RIGHTCTRL) \
|
||||
MATRIX_KEY(0x04, 0x01, KEY_A) \
|
||||
MATRIX_KEY(0x04, 0x02, KEY_D) \
|
||||
MATRIX_KEY(0x04, 0x03, KEY_F) \
|
||||
MATRIX_KEY(0x04, 0x04, KEY_S) \
|
||||
MATRIX_KEY(0x04, 0x05, KEY_K) \
|
||||
MATRIX_KEY(0x04, 0x06, KEY_J) \
|
||||
MATRIX_KEY(0x04, 0x08, KEY_SEMICOLON) \
|
||||
MATRIX_KEY(0x04, 0x09, KEY_L) \
|
||||
MATRIX_KEY(0x04, 0x0a, KEY_BACKSLASH) \
|
||||
MATRIX_KEY(0x04, 0x0b, KEY_ENTER) \
|
||||
\
|
||||
MATRIX_KEY(0x05, 0x01, KEY_Z) \
|
||||
MATRIX_KEY(0x05, 0x02, KEY_C) \
|
||||
MATRIX_KEY(0x05, 0x03, KEY_V) \
|
||||
MATRIX_KEY(0x05, 0x04, KEY_X) \
|
||||
MATRIX_KEY(0x05, 0x05, KEY_COMMA) \
|
||||
MATRIX_KEY(0x05, 0x06, KEY_M) \
|
||||
MATRIX_KEY(0x05, 0x07, KEY_LEFTSHIFT) \
|
||||
MATRIX_KEY(0x05, 0x08, KEY_SLASH) \
|
||||
MATRIX_KEY(0x05, 0x09, KEY_DOT) \
|
||||
MATRIX_KEY(0x05, 0x0b, KEY_SPACE) \
|
||||
\
|
||||
MATRIX_KEY(0x06, 0x01, KEY_1) \
|
||||
MATRIX_KEY(0x06, 0x02, KEY_3) \
|
||||
MATRIX_KEY(0x06, 0x03, KEY_4) \
|
||||
MATRIX_KEY(0x06, 0x04, KEY_2) \
|
||||
MATRIX_KEY(0x06, 0x05, KEY_8) \
|
||||
MATRIX_KEY(0x06, 0x06, KEY_7) \
|
||||
MATRIX_KEY(0x06, 0x08, KEY_0) \
|
||||
MATRIX_KEY(0x06, 0x09, KEY_9) \
|
||||
MATRIX_KEY(0x06, 0x0a, KEY_LEFTALT) \
|
||||
MATRIX_KEY(0x06, 0x0b, KEY_DOWN) \
|
||||
MATRIX_KEY(0x06, 0x0c, KEY_RIGHT) \
|
||||
\
|
||||
MATRIX_KEY(0x07, 0x01, KEY_Q) \
|
||||
MATRIX_KEY(0x07, 0x02, KEY_E) \
|
||||
MATRIX_KEY(0x07, 0x03, KEY_R) \
|
||||
MATRIX_KEY(0x07, 0x04, KEY_W) \
|
||||
MATRIX_KEY(0x07, 0x05, KEY_I) \
|
||||
MATRIX_KEY(0x07, 0x06, KEY_U) \
|
||||
MATRIX_KEY(0x07, 0x07, KEY_RIGHTSHIFT) \
|
||||
MATRIX_KEY(0x07, 0x08, KEY_P) \
|
||||
MATRIX_KEY(0x07, 0x09, KEY_O) \
|
||||
MATRIX_KEY(0x07, 0x0b, KEY_UP) \
|
||||
MATRIX_KEY(0x07, 0x0c, KEY_LEFT)
|
||||
|
||||
#endif /* _CROS_EC_KEYBOARD_H */
|
Loading…
Reference in New Issue
Block a user