summaryrefslogtreecommitdiff
path: root/drivers/input
diff options
context:
space:
mode:
authorDmitry Torokhov <dmitry.torokhov@gmail.com>2021-02-22 21:35:15 -0800
committerDmitry Torokhov <dmitry.torokhov@gmail.com>2021-02-22 21:35:15 -0800
commitcbecf716ca618fd44feda6bd9a64a8179d031fc5 (patch)
tree186c9f69f0d11f773253c440dac85087f67288b7 /drivers/input
parent6524d8eac258452e547f8a49c8a965ac6dd8a161 (diff)
parent4c47097f8514e4b35a31e04e33172d0193cb38ed (diff)
downloadlinux-cbecf716ca618fd44feda6bd9a64a8179d031fc5.tar.gz
linux-cbecf716ca618fd44feda6bd9a64a8179d031fc5.tar.bz2
linux-cbecf716ca618fd44feda6bd9a64a8179d031fc5.zip
Merge branch 'next' into for-linus
Prepare input updates for 5.12 merge window.
Diffstat (limited to 'drivers/input')
-rw-r--r--drivers/input/joydev.c7
-rw-r--r--drivers/input/joystick/Kconfig7
-rw-r--r--drivers/input/joystick/Makefile2
-rw-r--r--drivers/input/joystick/n64joy.c345
-rw-r--r--drivers/input/keyboard/Kconfig6
-rw-r--r--drivers/input/keyboard/applespi.c23
-rw-r--r--drivers/input/keyboard/cros_ec_keyb.c81
-rw-r--r--drivers/input/keyboard/omap4-keypad.c302
-rw-r--r--drivers/input/misc/da7280.c3
-rw-r--r--drivers/input/mouse/alps.c2
-rw-r--r--drivers/input/mouse/synaptics.c7
-rw-r--r--drivers/input/serio/Kconfig2
-rw-r--r--drivers/input/serio/hil_mlc.c21
-rw-r--r--drivers/input/serio/hp_sdc_mlc.c8
-rw-r--r--drivers/input/serio/hyperv-keyboard.c4
-rw-r--r--drivers/input/serio/i8042-x86ia64io.h4
-rw-r--r--drivers/input/tablet/aiptek.c80
-rw-r--r--drivers/input/touchscreen/Kconfig2
-rw-r--r--drivers/input/touchscreen/ads7846.c379
-rw-r--r--drivers/input/touchscreen/elants_i2c.c151
-rw-r--r--drivers/input/touchscreen/elo.c4
-rw-r--r--drivers/input/touchscreen/iqs5xx.c209
-rw-r--r--drivers/input/touchscreen/melfas_mip4.c8
-rw-r--r--drivers/input/touchscreen/raydium_i2c_ts.c3
-rw-r--r--drivers/input/touchscreen/s3c2410_ts.c37
-rw-r--r--drivers/input/touchscreen/stmpe-ts.c1
-rw-r--r--drivers/input/touchscreen/sur40.c1
-rw-r--r--drivers/input/touchscreen/surface3_spi.c2
-rw-r--r--drivers/input/touchscreen/usbtouchscreen.c3
-rw-r--r--drivers/input/touchscreen/zinitix.c4
30 files changed, 1180 insertions, 528 deletions
diff --git a/drivers/input/joydev.c b/drivers/input/joydev.c
index a2b5fbba2d3b..430dc6975004 100644
--- a/drivers/input/joydev.c
+++ b/drivers/input/joydev.c
@@ -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;
diff --git a/drivers/input/joystick/Kconfig b/drivers/input/joystick/Kconfig
index b080f0cfb068..5e38899058c1 100644
--- a/drivers/input/joystick/Kconfig
+++ b/drivers/input/joystick/Kconfig
@@ -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
diff --git a/drivers/input/joystick/Makefile b/drivers/input/joystick/Makefile
index 58232b3057d3..31d720c9e493 100644
--- a/drivers/input/joystick/Makefile
+++ b/drivers/input/joystick/Makefile
@@ -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
-
diff --git a/drivers/input/joystick/n64joy.c b/drivers/input/joystick/n64joy.c
new file mode 100644
index 000000000000..8bcc529942bc
--- /dev/null
+++ b/drivers/input/joystick/n64joy.c
@@ -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);
diff --git a/drivers/input/keyboard/Kconfig b/drivers/input/keyboard/Kconfig
index 2b321c17054a..32d15809ae58 100644
--- a/drivers/input/keyboard/Kconfig
+++ b/drivers/input/keyboard/Kconfig
@@ -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
diff --git a/drivers/input/keyboard/applespi.c b/drivers/input/keyboard/applespi.c
index d22223154177..eda1b23002b5 100644
--- a/drivers/input/keyboard/applespi.c
+++ b/drivers/input/keyboard/applespi.c
@@ -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;
diff --git a/drivers/input/keyboard/cros_ec_keyb.c b/drivers/input/keyboard/cros_ec_keyb.c
index 354d74d62f05..38457d9641bd 100644
--- a/drivers/input/keyboard/cros_ec_keyb.c
+++ b/drivers/input/keyboard/cros_ec_keyb.c
@@ -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;
};
/**
@@ -350,7 +358,7 @@ static int cros_ec_keyb_info(struct cros_ec_device *ec_dev,
params->event_type = event_type;
ret = cros_ec_cmd_xfer_status(ec_dev, msg);
- if (ret == -ENOTSUPP) {
+ if (ret == -ENOPROTOOPT) {
/* With older ECs we just return 0 for everything */
memset(result, 0, result_size);
ret = 0;
@@ -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);
diff --git a/drivers/input/keyboard/omap4-keypad.c b/drivers/input/keyboard/omap4-keypad.c
index b17ac2a295b9..43375b38ee59 100644
--- a/drivers/input/keyboard/omap4-keypad.c
+++ b/drivers/input/keyboard/omap4-keypad.c
@@ -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;
-
- *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));
- }
- }
+ struct device *dev = keypad_data->input->dev.parent;
+ u32 low, high;
+ int error;
+ u64 keys;
+
+ 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);