---
Hi,
This is all v4.12 material, but it should benefit all f26 and rawhide
users.
Since the Lenovo T440s, the Synaptics touchpads used by Lenovo are stuck
into the PS/2 mode with heaps of issues that we tried to fix over time.
Under Windows, they did not see such issues because they are using the
SMBus bus as provided by this series.
The main difference with the current protocol is that now we will reliably
report 5 touches on the touchpad instead of 2 (and a half, the FW gives
some hints on the number of touches).
This will allow users of the Lenovo Thinkpad series to actually make
use of finger gestures :)
Cheers,
Benjamin
Note: I know there are still a few bugs there open that require this
change, but I can't seem to find them (we must have closed a bunch
while waiting for upstream)
One bug I can remember is that if you are using the touchpad over PS/2,
have 2 or more fingers touching it, then from time to time the physical
buttons on the T450s are not reported. This is solved by switching to
RMI4.
---
kernel.spec | 14 +
...-psmouse-add-support-for-SMBus-companions.patch | 455 ++++++++++
...-psmouse-fix-cleaning-up-SMBus-companions.patch | 97 +++
...t-psmouse-implement-fast-reconnect-option.patch | 123 +++
...ouse-introduce-notion-of-SMBus-companions.patch | 239 ++++++
...psmouse-store-pointer-to-current-protocol.patch | 278 +++++++
rmi4-Input-serio-add-fast-reconnect-option.patch | 83 ++
...aptics-add-support-for-Intertouch-devices.patch | 836 +++++++++++++++++++
...tics-do-not-mix-logical-and-bitwise-opera.patch | 70 ++
...tics-split-device-info-into-a-separate-st.patch | 924 +++++++++++++++++++++
10 files changed, 3119 insertions(+)
create mode 100644 rmi4-Input-psmouse-add-support-for-SMBus-companions.patch
create mode 100644 rmi4-Input-psmouse-fix-cleaning-up-SMBus-companions.patch
create mode 100644 rmi4-Input-psmouse-implement-fast-reconnect-option.patch
create mode 100644 rmi4-Input-psmouse-introduce-notion-of-SMBus-companions.patch
create mode 100644 rmi4-Input-psmouse-store-pointer-to-current-protocol.patch
create mode 100644 rmi4-Input-serio-add-fast-reconnect-option.patch
create mode 100644 rmi4-Input-synaptics-add-support-for-Intertouch-devices.patch
create mode 100644 rmi4-Input-synaptics-do-not-mix-logical-and-bitwise-opera.patch
create mode 100644 rmi4-Input-synaptics-split-device-info-into-a-separate-st.patch
diff --git a/kernel.spec b/kernel.spec
index dc70f0d..44b4029 100644
--- a/kernel.spec
+++ b/kernel.spec
@@ -605,6 +605,17 @@ Patch665: netfilter-x_tables-deal-with-bogus-nextoffset-values.patch
#CVE-2017-7261 rhbz 1435719 1435740
Patch670: vmwgfx-check-that-number-of-mip-levels-is-above-zero.patch
+# Support of RMI4 over SMBus for Synaptics touchpads
+Patch701: rmi4-Input-synaptics-do-not-mix-logical-and-bitwise-opera.patch
+Patch702: rmi4-Input-serio-add-fast-reconnect-option.patch
+Patch703: rmi4-Input-psmouse-implement-fast-reconnect-option.patch
+Patch704: rmi4-Input-psmouse-store-pointer-to-current-protocol.patch
+Patch705: rmi4-Input-psmouse-introduce-notion-of-SMBus-companions.patch
+Patch706: rmi4-Input-psmouse-add-support-for-SMBus-companions.patch
+Patch707: rmi4-Input-synaptics-split-device-info-into-a-separate-st.patch
+Patch708: rmi4-Input-synaptics-add-support-for-Intertouch-devices.patch
+Patch709: rmi4-Input-psmouse-fix-cleaning-up-SMBus-companions.patch
+
# END OF PATCH DEFINITIONS
%endif
@@ -2169,6 +2180,9 @@ fi
#
#
%changelog
+* Wed Apr 05 2017 Benjamin Tissoires <benjamin.tissoires(a)redhat.com>
+- add Synaptics RMI4 Intertouch patches
+
* Mon Apr 03 2017 Laura Abbott <labbott(a)fedoraproject.org> - 4.11.0-0.rc5.git0.1
- Linux v4.11-rc5
- Disable 64K pages on aarch64
diff --git a/rmi4-Input-psmouse-add-support-for-SMBus-companions.patch b/rmi4-Input-psmouse-add-support-for-SMBus-companions.patch
new file mode 100644
index 0000000..85e5e9b
--- /dev/null
+++ b/rmi4-Input-psmouse-add-support-for-SMBus-companions.patch
@@ -0,0 +1,455 @@
+From c8a1e66242c490cceea8df45513a087890eb8564 Mon Sep 17 00:00:00 2001
+From: Benjamin Tissoires <benjamin.tissoires(a)redhat.com>
+Date: Wed, 5 Apr 2017 14:38:20 +0200
+Subject: [PATCH 6/9] Input: psmouse - add support for SMBus companions
+
+Upstream: scheduled for v4.12 in the input tree:
+https://git.kernel.org/pub/scm/linux/kernel/git/dtor/input.git/log/?h=next
+
+commit 8eb92e5c91338eb19f86ffb2232258337ebf905b
+Author: Benjamin Tissoires <benjamin.tissoires(a)redhat.com>
+Date: Thu Mar 2 10:48:23 2017 -0800
+
+ Input: psmouse - add support for SMBus companions
+
+ This provides glue between PS/2 devices that enumerate the RMI4 devices
+ and Elan touchpads to the RMI4 (or Elan) SMBus driver.
+
+ The SMBus devices keep their PS/2 connection alive. If the initialization
+ process goes too far (psmouse_activate called), the device disconnects
+ from the I2C bus and stays on the PS/2 bus, that is why we explicitly
+ disable PS/2 device reporting (by calling psmouse_deactivate) before
+ trying to register SMBus companion device.
+
+ The HID over I2C devices are enumerated through the ACPI DSDT, and
+ their PS/2 device also exports the InterTouch bit in the extended
+ capability 0x0C. However, the firmware keeps its I2C connection open
+ even after going further in the PS/2 initialization. We don't need
+ to take extra precautions with those device, especially because they
+ block their PS/2 communication when HID over I2C is used.
+
+ Signed-off-by: Benjamin Tissoires <benjamin.tissoires(a)redhat.com>
+ Signed-off-by: Dmitry Torokhov <dmitry.torokhov(a)gmail.com>
+
+Signed-off-by: Benjamin Tissoires <benjamin.tissoires(a)redhat.com>
+---
+ drivers/input/mouse/Kconfig | 4 +
+ drivers/input/mouse/Makefile | 2 +
+ drivers/input/mouse/psmouse-base.c | 16 +-
+ drivers/input/mouse/psmouse-smbus.c | 294 ++++++++++++++++++++++++++++++++++++
+ drivers/input/mouse/psmouse.h | 29 ++++
+ 5 files changed, 343 insertions(+), 2 deletions(-)
+ create mode 100644 drivers/input/mouse/psmouse-smbus.c
+
+diff --git a/drivers/input/mouse/Kconfig b/drivers/input/mouse/Kconfig
+index 096abb4..87bde8a 100644
+--- a/drivers/input/mouse/Kconfig
++++ b/drivers/input/mouse/Kconfig
+@@ -171,6 +171,10 @@ config MOUSE_PS2_VMMOUSE
+
+ If unsure, say N.
+
++config MOUSE_PS2_SMBUS
++ bool
++ depends on MOUSE_PS2
++
+ config MOUSE_SERIAL
+ tristate "Serial mouse"
+ select SERIO
+diff --git a/drivers/input/mouse/Makefile b/drivers/input/mouse/Makefile
+index 6168b13..56bf0ad 100644
+--- a/drivers/input/mouse/Makefile
++++ b/drivers/input/mouse/Makefile
+@@ -39,6 +39,8 @@ psmouse-$(CONFIG_MOUSE_PS2_TOUCHKIT) += touchkit_ps2.o
+ psmouse-$(CONFIG_MOUSE_PS2_CYPRESS) += cypress_ps2.o
+ psmouse-$(CONFIG_MOUSE_PS2_VMMOUSE) += vmmouse.o
+
++psmouse-$(CONFIG_MOUSE_PS2_SMBUS) += psmouse-smbus.o
++
+ elan_i2c-objs := elan_i2c_core.o
+ elan_i2c-$(CONFIG_MOUSE_ELAN_I2C_I2C) += elan_i2c_i2c.o
+ elan_i2c-$(CONFIG_MOUSE_ELAN_I2C_SMBUS) += elan_i2c_smbus.o
+diff --git a/drivers/input/mouse/psmouse-base.c b/drivers/input/mouse/psmouse-base.c
+index a84f8ed..ab9bfe2 100644
+--- a/drivers/input/mouse/psmouse-base.c
++++ b/drivers/input/mouse/psmouse-base.c
+@@ -1999,16 +1999,27 @@ static int __init psmouse_init(void)
+ synaptics_module_init();
+ hgpk_module_init();
+
++ err = psmouse_smbus_module_init();
++ if (err)
++ return err;
++
+ kpsmoused_wq = alloc_ordered_workqueue("kpsmoused", 0);
+ if (!kpsmoused_wq) {
+ pr_err("failed to create kpsmoused workqueue\n");
+- return -ENOMEM;
++ err = -ENOMEM;
++ goto err_smbus_exit;
+ }
+
+ err = serio_register_driver(&psmouse_drv);
+ if (err)
+- destroy_workqueue(kpsmoused_wq);
++ goto err_destroy_wq;
+
++ return 0;
++
++err_destroy_wq:
++ destroy_workqueue(kpsmoused_wq);
++err_smbus_exit:
++ psmouse_smbus_module_exit();
+ return err;
+ }
+
+@@ -2016,6 +2027,7 @@ static void __exit psmouse_exit(void)
+ {
+ serio_unregister_driver(&psmouse_drv);
+ destroy_workqueue(kpsmoused_wq);
++ psmouse_smbus_module_exit();
+ }
+
+ module_init(psmouse_init);
+diff --git a/drivers/input/mouse/psmouse-smbus.c b/drivers/input/mouse/psmouse-smbus.c
+new file mode 100644
+index 0000000..061c1cc
+--- /dev/null
++++ b/drivers/input/mouse/psmouse-smbus.c
+@@ -0,0 +1,294 @@
++/*
++ * Copyright (c) 2017 Red Hat, Inc
++ *
++ * This program is free software; you can redistribute it and/or modify it
++ * under the terms of the GNU General Public License version 2 as published by
++ * the Free Software Foundation.
++ */
++
++#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
++
++#include <linux/kernel.h>
++#include <linux/module.h>
++#include <linux/libps2.h>
++#include <linux/i2c.h>
++#include <linux/serio.h>
++#include <linux/slab.h>
++#include <linux/workqueue.h>
++#include "psmouse.h"
++
++struct psmouse_smbus_dev {
++ struct i2c_board_info board;
++ struct psmouse *psmouse;
++ struct i2c_client *client;
++ struct list_head node;
++ bool dead;
++};
++
++static LIST_HEAD(psmouse_smbus_list);
++static DEFINE_MUTEX(psmouse_smbus_mutex);
++
++static void psmouse_smbus_check_adapter(struct i2c_adapter *adapter)
++{
++ struct psmouse_smbus_dev *smbdev;
++
++ if (!i2c_check_functionality(adapter, I2C_FUNC_SMBUS_HOST_NOTIFY))
++ return;
++
++ mutex_lock(&psmouse_smbus_mutex);
++
++ list_for_each_entry(smbdev, &psmouse_smbus_list, node) {
++ if (smbdev->dead)
++ continue;
++
++ if (smbdev->client)
++ continue;
++
++ /*
++ * Here would be a good place to check if device is actually
++ * present, but it seems that SMBus will not respond unless we
++ * fully reset PS/2 connection. So cross our fingers, and try
++ * to switch over, hopefully our system will not have too many
++ * "host notify" I2C adapters.
++ */
++ psmouse_dbg(smbdev->psmouse,
++ "SMBus candidate adapter appeared, triggering rescan\n");
++ serio_rescan(smbdev->psmouse->ps2dev.serio);
++ }
++
++ mutex_unlock(&psmouse_smbus_mutex);
++}
++
++static void psmouse_smbus_detach_i2c_client(struct i2c_client *client)
++{
++ struct psmouse_smbus_dev *smbdev;
++
++ mutex_lock(&psmouse_smbus_mutex);
++
++ list_for_each_entry(smbdev, &psmouse_smbus_list, node) {
++ if (smbdev->client == client) {
++ psmouse_dbg(smbdev->psmouse,
++ "Marking SMBus companion %s as gone\n",
++ dev_name(&smbdev->client->dev));
++ smbdev->client = NULL;
++ smbdev->dead = true;
++ serio_rescan(smbdev->psmouse->ps2dev.serio);
++ }
++ }
++
++ kfree(client->dev.platform_data);
++ client->dev.platform_data = NULL;
++
++ mutex_unlock(&psmouse_smbus_mutex);
++}
++
++static int psmouse_smbus_notifier_call(struct notifier_block *nb,
++ unsigned long action, void *data)
++{
++ struct device *dev = data;
++
++ switch (action) {
++ case BUS_NOTIFY_ADD_DEVICE:
++ if (dev->type == &i2c_adapter_type)
++ psmouse_smbus_check_adapter(to_i2c_adapter(dev));
++ break;
++
++ case BUS_NOTIFY_REMOVED_DEVICE:
++ if (dev->type != &i2c_adapter_type)
++ psmouse_smbus_detach_i2c_client(to_i2c_client(dev));
++ break;
++ }
++
++ return 0;
++}
++
++static struct notifier_block psmouse_smbus_notifier = {
++ .notifier_call = psmouse_smbus_notifier_call,
++};
++
++static psmouse_ret_t psmouse_smbus_process_byte(struct psmouse *psmouse)
++{
++ return PSMOUSE_FULL_PACKET;
++}
++
++static int psmouse_smbus_reconnect(struct psmouse *psmouse)
++{
++ psmouse_deactivate(psmouse);
++
++ return 0;
++}
++
++struct psmouse_smbus_removal_work {
++ struct work_struct work;
++ struct i2c_client *client;
++};
++
++static void psmouse_smbus_remove_i2c_device(struct work_struct *work)
++{
++ struct psmouse_smbus_removal_work *rwork =
++ container_of(work, struct psmouse_smbus_removal_work, work);
++
++ dev_dbg(&rwork->client->dev, "destroying SMBus companion device\n");
++ i2c_unregister_device(rwork->client);
++
++ kfree(rwork);
++}
++
++/*
++ * This schedules removal of SMBus companion device. We have to do
++ * it in a separate tread to avoid deadlocking on psmouse_mutex in
++ * case the device has a trackstick (which is also driven by psmouse).
++ *
++ * Note that this may be racing with i2c adapter removal, but we
++ * can't do anything about that: i2c automatically destroys clients
++ * attached to an adapter that is being removed. This has to be
++ * fixed in i2c core.
++ */
++static void psmouse_smbus_schedule_remove(struct i2c_client *client)
++{
++ struct psmouse_smbus_removal_work *rwork;
++
++ rwork = kzalloc(sizeof(*rwork), GFP_KERNEL);
++ if (rwork) {
++ INIT_WORK(&rwork->work, psmouse_smbus_remove_i2c_device);
++ rwork->client = client;
++
++ schedule_work(&rwork->work);
++ }
++}
++
++static void psmouse_smbus_disconnect(struct psmouse *psmouse)
++{
++ struct psmouse_smbus_dev *smbdev = psmouse->private;
++
++ mutex_lock(&psmouse_smbus_mutex);
++ list_del(&smbdev->node);
++ mutex_unlock(&psmouse_smbus_mutex);
++
++ if (smbdev->client) {
++ psmouse_dbg(smbdev->psmouse,
++ "posting removal request for SMBus companion %s\n",
++ dev_name(&smbdev->client->dev));
++ psmouse_smbus_schedule_remove(smbdev->client);
++ }
++
++ kfree(smbdev);
++ psmouse->private = NULL;
++}
++
++static int psmouse_smbus_create_companion(struct device *dev, void *data)
++{
++ struct psmouse_smbus_dev *smbdev = data;
++ unsigned short addr_list[] = { smbdev->board.addr, I2C_CLIENT_END };
++ struct i2c_adapter *adapter;
++
++ adapter = i2c_verify_adapter(dev);
++ if (!adapter)
++ return 0;
++
++ if (!i2c_check_functionality(adapter, I2C_FUNC_SMBUS_HOST_NOTIFY))
++ return 0;
++
++ smbdev->client = i2c_new_probed_device(adapter, &smbdev->board,
++ addr_list, NULL);
++ if (!smbdev->client)
++ return 0;
++
++ /* We have our(?) device, stop iterating i2c bus. */
++ return 1;
++}
++
++void psmouse_smbus_cleanup(struct psmouse *psmouse)
++{
++ struct psmouse_smbus_dev *smbdev, *tmp;
++
++ mutex_lock(&psmouse_smbus_mutex);
++
++ list_for_each_entry_safe(smbdev, tmp, &psmouse_smbus_list, node) {
++ if (psmouse == smbdev->psmouse) {
++ list_del(&smbdev->node);
++ kfree(smbdev);
++ }
++ }
++
++ mutex_unlock(&psmouse_smbus_mutex);
++}
++
++int psmouse_smbus_init(struct psmouse *psmouse,
++ const struct i2c_board_info *board,
++ const void *pdata, size_t pdata_size,
++ bool leave_breadcrumbs)
++{
++ struct psmouse_smbus_dev *smbdev;
++ int error;
++
++ smbdev = kzalloc(sizeof(*smbdev), GFP_KERNEL);
++ if (!smbdev)
++ return -ENOMEM;
++
++ smbdev->psmouse = psmouse;
++ smbdev->board = *board;
++
++ smbdev->board.platform_data = kmemdup(pdata, pdata_size, GFP_KERNEL);
++ if (!smbdev->board.platform_data) {
++ kfree(smbdev);
++ return -ENOMEM;
++ }
++
++ psmouse->private = smbdev;
++ psmouse->protocol_handler = psmouse_smbus_process_byte;
++ psmouse->reconnect = psmouse_smbus_reconnect;
++ psmouse->fast_reconnect = psmouse_smbus_reconnect;
++ psmouse->disconnect = psmouse_smbus_disconnect;
++ psmouse->resync_time = 0;
++
++ psmouse_deactivate(psmouse);
++
++ mutex_lock(&psmouse_smbus_mutex);
++ list_add_tail(&smbdev->node, &psmouse_smbus_list);
++ mutex_unlock(&psmouse_smbus_mutex);
++
++ /* Bind to already existing adapters right away */
++ error = i2c_for_each_dev(smbdev, psmouse_smbus_create_companion);
++
++ if (smbdev->client) {
++ /* We have our companion device */
++ return 0;
++ }
++
++ /*
++ * If we did not create i2c device we will not need platform
++ * data even if we are leaving breadcrumbs.
++ */
++ kfree(smbdev->board.platform_data);
++ smbdev->board.platform_data = NULL;
++
++ if (error < 0 || !leave_breadcrumbs) {
++ mutex_lock(&psmouse_smbus_mutex);
++ list_del(&smbdev->node);
++ mutex_unlock(&psmouse_smbus_mutex);
++
++ kfree(smbdev);
++ }
++
++ return error < 0 ? error : -EAGAIN;
++}
++
++int __init psmouse_smbus_module_init(void)
++{
++ int error;
++
++ error = bus_register_notifier(&i2c_bus_type, &psmouse_smbus_notifier);
++ if (error) {
++ pr_err("failed to register i2c bus notifier: %d\n", error);
++ return error;
++ }
++
++ return 0;
++}
++
++void psmouse_smbus_module_exit(void)
++{
++ bus_unregister_notifier(&i2c_bus_type, &psmouse_smbus_notifier);
++ flush_scheduled_work();
++}
+diff --git a/drivers/input/mouse/psmouse.h b/drivers/input/mouse/psmouse.h
+index e853dee..0511083 100644
+--- a/drivers/input/mouse/psmouse.h
++++ b/drivers/input/mouse/psmouse.h
+@@ -209,5 +209,34 @@ static struct psmouse_attribute psmouse_attr_##_name = { \
+ &(psmouse)->ps2dev.serio->dev, \
+ psmouse_fmt(format), ##__VA_ARGS__)
+
++#ifdef CONFIG_MOUSE_PS2_SMBUS
++
++int psmouse_smbus_module_init(void);
++void psmouse_smbus_module_exit(void);
++
++struct i2c_board_info;
++
++int psmouse_smbus_init(struct psmouse *psmouse,
++ const struct i2c_board_info *board,
++ const void *pdata, size_t pdata_size,
++ bool leave_breadcrumbs);
++void psmouse_smbus_cleanup(struct psmouse *psmouse);
++
++#else /* !CONFIG_MOUSE_PS2_SMBUS */
++
++static inline int psmouse_smbus_module_init(void)
++{
++ return 0;
++}
++
++static inline void psmouse_smbus_module_exit(void)
++{
++}
++
++static inline void psmouse_smbus_cleanup(struct psmouse *psmouse)
++{
++}
++
++#endif /* CONFIG_MOUSE_PS2_SMBUS */
+
+ #endif /* _PSMOUSE_H */
+--
+2.9.3
+
diff --git a/rmi4-Input-psmouse-fix-cleaning-up-SMBus-companions.patch b/rmi4-Input-psmouse-fix-cleaning-up-SMBus-companions.patch
new file mode 100644
index 0000000..6904fcb
--- /dev/null
+++ b/rmi4-Input-psmouse-fix-cleaning-up-SMBus-companions.patch
@@ -0,0 +1,97 @@
+From bf39e3cd092bd27918ab8a06adf8c238ce2c0cf3 Mon Sep 17 00:00:00 2001
+From: Benjamin Tissoires <benjamin.tissoires(a)redhat.com>
+Date: Wed, 5 Apr 2017 14:38:39 +0200
+Subject: [PATCH 9/9] Input: psmouse - fix cleaning up SMBus companions
+
+Upstream: scheduled for v4.12 in the input tree:
+https://git.kernel.org/pub/scm/linux/kernel/git/dtor/input.git/log/?h=next
+
+commit f6f08c55cced6885f1e1448cd0806098e836c5f5
+Author: Dmitry Torokhov <dmitry.torokhov(a)gmail.com>
+Date: Sat Apr 1 11:33:11 2017 -0700
+
+ Input: psmouse - fix cleaning up SMBus companions
+
+ When trying to destroy platform data after destruction of SMbus companion,
+ we need to make sure that we are actually dealing with an SMB companion
+ device, and not some random I2C client device.
+
+ Fixes: 8eb92e5c9133 ("Input: psmouse - add support for SMBus companions")
+ Reported-by: Benjamin Tissoires <benjamin.tissoires(a)redhat.com>
+ Tested-by: Benjamin Tissoires <benjamin.tissoires(a)redhat.com>
+ Signed-off-by: Dmitry Torokhov <dmitry.torokhov(a)gmail.com>
+
+Signed-off-by: Benjamin Tissoires <benjamin.tissoires(a)redhat.com>
+---
+ drivers/input/mouse/psmouse-smbus.c | 30 +++++++++++++++++++-----------
+ 1 file changed, 19 insertions(+), 11 deletions(-)
+
+diff --git a/drivers/input/mouse/psmouse-smbus.c b/drivers/input/mouse/psmouse-smbus.c
+index 061c1cc..d2b1ea3 100644
+--- a/drivers/input/mouse/psmouse-smbus.c
++++ b/drivers/input/mouse/psmouse-smbus.c
+@@ -61,24 +61,29 @@ static void psmouse_smbus_check_adapter(struct i2c_adapter *adapter)
+
+ static void psmouse_smbus_detach_i2c_client(struct i2c_client *client)
+ {
+- struct psmouse_smbus_dev *smbdev;
++ struct psmouse_smbus_dev *smbdev, *tmp;
+
+ mutex_lock(&psmouse_smbus_mutex);
+
+- list_for_each_entry(smbdev, &psmouse_smbus_list, node) {
+- if (smbdev->client == client) {
++ list_for_each_entry_safe(smbdev, tmp, &psmouse_smbus_list, node) {
++ if (smbdev->client != client)
++ continue;
++
++ kfree(client->dev.platform_data);
++ client->dev.platform_data = NULL;
++
++ if (!smbdev->dead) {
+ psmouse_dbg(smbdev->psmouse,
+ "Marking SMBus companion %s as gone\n",
+ dev_name(&smbdev->client->dev));
+- smbdev->client = NULL;
+ smbdev->dead = true;
+ serio_rescan(smbdev->psmouse->ps2dev.serio);
++ } else {
++ list_del(&smbdev->node);
++ kfree(smbdev);
+ }
+ }
+
+- kfree(client->dev.platform_data);
+- client->dev.platform_data = NULL;
+-
+ mutex_unlock(&psmouse_smbus_mutex);
+ }
+
+@@ -162,17 +167,20 @@ static void psmouse_smbus_disconnect(struct psmouse *psmouse)
+ struct psmouse_smbus_dev *smbdev = psmouse->private;
+
+ mutex_lock(&psmouse_smbus_mutex);
+- list_del(&smbdev->node);
+- mutex_unlock(&psmouse_smbus_mutex);
+
+- if (smbdev->client) {
++ if (smbdev->dead) {
++ list_del(&smbdev->node);
++ kfree(smbdev);
++ } else {
++ smbdev->dead = true;
+ psmouse_dbg(smbdev->psmouse,
+ "posting removal request for SMBus companion %s\n",
+ dev_name(&smbdev->client->dev));
+ psmouse_smbus_schedule_remove(smbdev->client);
+ }
+
+- kfree(smbdev);
++ mutex_unlock(&psmouse_smbus_mutex);
++
+ psmouse->private = NULL;
+ }
+
+--
+2.9.3
+
diff --git a/rmi4-Input-psmouse-implement-fast-reconnect-option.patch b/rmi4-Input-psmouse-implement-fast-reconnect-option.patch
new file mode 100644
index 0000000..6557650
--- /dev/null
+++ b/rmi4-Input-psmouse-implement-fast-reconnect-option.patch
@@ -0,0 +1,123 @@
+From 23bbf21f4e2434c4ad7248d433339be0ffcd0f5a Mon Sep 17 00:00:00 2001
+From: Benjamin Tissoires <benjamin.tissoires(a)redhat.com>
+Date: Wed, 5 Apr 2017 14:37:58 +0200
+Subject: [PATCH 3/9] Input: psmouse - implement fast reconnect option
+
+Upstream: scheduled for v4.12 in the input tree:
+https://git.kernel.org/pub/scm/linux/kernel/git/dtor/input.git/log/?h=next
+
+commit 0ab3fa57425023f42e8822a293d9b87a3ad4e2b3
+Author: Dmitry Torokhov <dmitry.torokhov(a)gmail.com>
+Date: Sun Mar 5 23:19:22 2017 -0800
+
+ Input: psmouse - implement fast reconnect option
+
+ Make use of serio's fast reconnect option and allow psmouse protocol
+ handler's to implement fast reconnect handlers that will be called during
+ system resume.
+
+ Reviewed-by: Benjamin Tissoires <benjamin.tissoires(a)redhat.com>
+ Signed-off-by: Dmitry Torokhov <dmitry.torokhov(a)gmail.com>
+
+Signed-off-by: Benjamin Tissoires <benjamin.tissoires(a)redhat.com>
+---
+ drivers/input/mouse/psmouse-base.c | 30 +++++++++++++++++++++++++++---
+ drivers/input/mouse/psmouse.h | 1 +
+ 2 files changed, 28 insertions(+), 3 deletions(-)
+
+diff --git a/drivers/input/mouse/psmouse-base.c b/drivers/input/mouse/psmouse-base.c
+index a598b72..47fd297 100644
+--- a/drivers/input/mouse/psmouse-base.c
++++ b/drivers/input/mouse/psmouse-base.c
+@@ -966,6 +966,7 @@ static void psmouse_apply_defaults(struct psmouse *psmouse)
+ psmouse->protocol_handler = psmouse_process_byte;
+ psmouse->pktsize = 3;
+ psmouse->reconnect = NULL;
++ psmouse->fast_reconnect = NULL;
+ psmouse->disconnect = NULL;
+ psmouse->cleanup = NULL;
+ psmouse->pt_activate = NULL;
+@@ -1628,15 +1629,26 @@ static int psmouse_connect(struct serio *serio, struct serio_driver *drv)
+ goto out;
+ }
+
+-static int psmouse_reconnect(struct serio *serio)
++static int __psmouse_reconnect(struct serio *serio, bool fast_reconnect)
+ {
+ struct psmouse *psmouse = serio_get_drvdata(serio);
+ struct psmouse *parent = NULL;
++ int (*reconnect_handler)(struct psmouse *);
+ unsigned char type;
+ int rc = -1;
+
+ mutex_lock(&psmouse_mutex);
+
++ if (fast_reconnect) {
++ reconnect_handler = psmouse->fast_reconnect;
++ if (!reconnect_handler) {
++ rc = -ENOENT;
++ goto out_unlock;
++ }
++ } else {
++ reconnect_handler = psmouse->reconnect;
++ }
++
+ if (serio->parent && serio->id.type == SERIO_PS_PSTHRU) {
+ parent = serio_get_drvdata(serio->parent);
+ psmouse_deactivate(parent);
+@@ -1644,8 +1656,8 @@ static int psmouse_reconnect(struct serio *serio)
+
+ psmouse_set_state(psmouse, PSMOUSE_INITIALIZING);
+
+- if (psmouse->reconnect) {
+- if (psmouse->reconnect(psmouse))
++ if (reconnect_handler) {
++ if (reconnect_handler(psmouse))
+ goto out;
+ } else {
+ psmouse_reset(psmouse);
+@@ -1677,10 +1689,21 @@ static int psmouse_reconnect(struct serio *serio)
+ if (parent)
+ psmouse_activate(parent);
+
++out_unlock:
+ mutex_unlock(&psmouse_mutex);
+ return rc;
+ }
+
++static int psmouse_reconnect(struct serio *serio)
++{
++ return __psmouse_reconnect(serio, false);
++}
++
++static int psmouse_fast_reconnect(struct serio *serio)
++{
++ return __psmouse_reconnect(serio, true);
++}
++
+ static struct serio_device_id psmouse_serio_ids[] = {
+ {
+ .type = SERIO_8042,
+@@ -1708,6 +1731,7 @@ static struct serio_driver psmouse_drv = {
+ .interrupt = psmouse_interrupt,
+ .connect = psmouse_connect,
+ .reconnect = psmouse_reconnect,
++ .fast_reconnect = psmouse_fast_reconnect,
+ .disconnect = psmouse_disconnect,
+ .cleanup = psmouse_cleanup,
+ };
+diff --git a/drivers/input/mouse/psmouse.h b/drivers/input/mouse/psmouse.h
+index 8c83b8e..bc76e77 100644
+--- a/drivers/input/mouse/psmouse.h
++++ b/drivers/input/mouse/psmouse.h
+@@ -80,6 +80,7 @@ struct psmouse {
+ void (*set_scale)(struct psmouse *psmouse, enum psmouse_scale scale);
+
+ int (*reconnect)(struct psmouse *psmouse);
++ int (*fast_reconnect)(struct psmouse *psmouse);
+ void (*disconnect)(struct psmouse *psmouse);
+ void (*cleanup)(struct psmouse *psmouse);
+ int (*poll)(struct psmouse *psmouse);
+--
+2.9.3
+
diff --git a/rmi4-Input-psmouse-introduce-notion-of-SMBus-companions.patch b/rmi4-Input-psmouse-introduce-notion-of-SMBus-companions.patch
new file mode 100644
index 0000000..2f92def
--- /dev/null
+++ b/rmi4-Input-psmouse-introduce-notion-of-SMBus-companions.patch
@@ -0,0 +1,239 @@
+From ef6663d1fb80ab38e391d23da77802ceede7f6cc Mon Sep 17 00:00:00 2001
+From: Benjamin Tissoires <benjamin.tissoires(a)redhat.com>
+Date: Wed, 5 Apr 2017 14:38:13 +0200
+Subject: [PATCH 5/9] Input: psmouse - introduce notion of SMBus companions
+
+Upstream: scheduled for v4.12 in the input tree:
+https://git.kernel.org/pub/scm/linux/kernel/git/dtor/input.git/log/?h=next
+
+commit c774326a219536ab615d68a22875673f6f608b62
+Author: Dmitry Torokhov <dmitry.torokhov(a)gmail.com>
+Date: Thu Mar 2 15:50:56 2017 -0800
+
+ Input: psmouse - introduce notion of SMBus companions
+
+ Prepare PS/2 mouse drivers to work with devices that are accessible both
+ via PS/2 and SMBus, which provides higher bandwidth, and thus suits better
+ for modern multi-touch devices.
+
+ We expect that SMBus drivers will take control over the device, so when
+ we detect SMBus "protocol" we forego registering input device, or enabling
+ PS/2 device reports (as it usually makes device unresponsive to access over
+ SMBus).
+
+ Reviewed-by: Benjamin Tissoires <benjamin.tissoires(a)redhat.com>
+ Signed-off-by: Dmitry Torokhov <dmitry.torokhov(a)gmail.com>
+
+Signed-off-by: Benjamin Tissoires <benjamin.tissoires(a)redhat.com>
+---
+ drivers/input/mouse/psmouse-base.c | 103 +++++++++++++++++++++++++------------
+ drivers/input/mouse/psmouse.h | 1 +
+ 2 files changed, 71 insertions(+), 33 deletions(-)
+
+diff --git a/drivers/input/mouse/psmouse-base.c b/drivers/input/mouse/psmouse-base.c
+index bb5d164..a84f8ed 100644
+--- a/drivers/input/mouse/psmouse-base.c
++++ b/drivers/input/mouse/psmouse-base.c
+@@ -1424,9 +1424,8 @@ static void psmouse_cleanup(struct serio *serio)
+ */
+ static void psmouse_disconnect(struct serio *serio)
+ {
+- struct psmouse *psmouse, *parent = NULL;
+-
+- psmouse = serio_get_drvdata(serio);
++ struct psmouse *psmouse = serio_get_drvdata(serio);
++ struct psmouse *parent = NULL;
+
+ sysfs_remove_group(&serio->dev.kobj, &psmouse_attribute_group);
+
+@@ -1454,7 +1453,10 @@ static void psmouse_disconnect(struct serio *serio)
+
+ serio_close(serio);
+ serio_set_drvdata(serio, NULL);
+- input_unregister_device(psmouse->dev);
++
++ if (psmouse->dev)
++ input_unregister_device(psmouse->dev);
++
+ kfree(psmouse);
+
+ if (parent)
+@@ -1575,12 +1577,18 @@ static int psmouse_connect(struct serio *serio, struct serio_driver *drv)
+
+ psmouse_switch_protocol(psmouse, NULL);
+
+- psmouse_set_state(psmouse, PSMOUSE_CMD_MODE);
+- psmouse_initialize(psmouse);
++ if (!psmouse->protocol->smbus_companion) {
++ psmouse_set_state(psmouse, PSMOUSE_CMD_MODE);
++ psmouse_initialize(psmouse);
+
+- error = input_register_device(psmouse->dev);
+- if (error)
+- goto err_protocol_disconnect;
++ error = input_register_device(input_dev);
++ if (error)
++ goto err_protocol_disconnect;
++ } else {
++ /* Smbus companion will be reporting events, not us. */
++ input_free_device(input_dev);
++ psmouse->dev = input_dev = NULL;
++ }
+
+ if (parent && parent->pt_activate)
+ parent->pt_activate(parent);
+@@ -1589,7 +1597,12 @@ static int psmouse_connect(struct serio *serio, struct serio_driver *drv)
+ if (error)
+ goto err_pt_deactivate;
+
+- psmouse_activate(psmouse);
++ /*
++ * PS/2 devices having SMBus companions should stay disabled
++ * on PS/2 side, in order to have SMBus part operable.
++ */
++ if (!psmouse->protocol->smbus_companion)
++ psmouse_activate(psmouse);
+
+ out:
+ /* If this is a pass-through port the parent needs to be re-activated */
+@@ -1602,8 +1615,10 @@ static int psmouse_connect(struct serio *serio, struct serio_driver *drv)
+ err_pt_deactivate:
+ if (parent && parent->pt_deactivate)
+ parent->pt_deactivate(parent);
+- input_unregister_device(psmouse->dev);
+- input_dev = NULL; /* so we don't try to free it below */
++ if (input_dev) {
++ input_unregister_device(input_dev);
++ input_dev = NULL; /* so we don't try to free it below */
++ }
+ err_protocol_disconnect:
+ if (psmouse->disconnect)
+ psmouse->disconnect(psmouse);
+@@ -1665,14 +1680,21 @@ static int __psmouse_reconnect(struct serio *serio, bool fast_reconnect)
+ * OK, the device type (and capabilities) match the old one,
+ * we can continue using it, complete initialization
+ */
+- psmouse_set_state(psmouse, PSMOUSE_CMD_MODE);
+-
+- psmouse_initialize(psmouse);
++ if (!psmouse->protocol->smbus_companion) {
++ psmouse_set_state(psmouse, PSMOUSE_CMD_MODE);
++ psmouse_initialize(psmouse);
++ }
+
+ if (parent && parent->pt_activate)
+ parent->pt_activate(parent);
+
+- psmouse_activate(psmouse);
++ /*
++ * PS/2 devices having SMBus companions should stay disabled
++ * on PS/2 side, in order to have SMBus part operable.
++ */
++ if (!psmouse->protocol->smbus_companion)
++ psmouse_activate(psmouse);
++
+ rc = 0;
+
+ out:
+@@ -1732,9 +1754,11 @@ ssize_t psmouse_attr_show_helper(struct device *dev, struct device_attribute *de
+ {
+ struct serio *serio = to_serio_port(dev);
+ struct psmouse_attribute *attr = to_psmouse_attr(devattr);
+- struct psmouse *psmouse;
++ struct psmouse *psmouse = serio_get_drvdata(serio);
+
+- psmouse = serio_get_drvdata(serio);
++ if (psmouse->protocol->smbus_companion &&
++ devattr != &psmouse_attr_protocol.dattr)
++ return -ENOENT;
+
+ return attr->show(psmouse, attr->data, buf);
+ }
+@@ -1753,6 +1777,12 @@ ssize_t psmouse_attr_set_helper(struct device *dev, struct device_attribute *dev
+
+ psmouse = serio_get_drvdata(serio);
+
++ if (psmouse->protocol->smbus_companion &&
++ devattr != &psmouse_attr_protocol.dattr) {
++ retval = -ENOENT;
++ goto out_unlock;
++ }
++
+ if (attr->protect) {
+ if (psmouse->state == PSMOUSE_IGNORE) {
+ retval = -ENODEV;
+@@ -1764,13 +1794,14 @@ ssize_t psmouse_attr_set_helper(struct device *dev, struct device_attribute *dev
+ psmouse_deactivate(parent);
+ }
+
+- psmouse_deactivate(psmouse);
++ if (!psmouse->protocol->smbus_companion)
++ psmouse_deactivate(psmouse);
+ }
+
+ retval = attr->set(psmouse, attr->data, buf, count);
+
+ if (attr->protect) {
+- if (retval != -ENODEV)
++ if (retval != -ENODEV && !psmouse->protocol->smbus_companion)
+ psmouse_activate(psmouse);
+
+ if (parent)
+@@ -1879,23 +1910,29 @@ static ssize_t psmouse_attr_set_protocol(struct psmouse *psmouse, void *data, co
+ psmouse_initialize(psmouse);
+ psmouse_set_state(psmouse, PSMOUSE_CMD_MODE);
+
+- error = input_register_device(psmouse->dev);
+- if (error) {
+- if (psmouse->disconnect)
+- psmouse->disconnect(psmouse);
++ if (psmouse->protocol->smbus_companion) {
++ input_free_device(psmouse->dev);
++ psmouse->dev = NULL;
++ } else {
++ error = input_register_device(psmouse->dev);
++ if (error) {
++ if (psmouse->disconnect)
++ psmouse->disconnect(psmouse);
+
+- psmouse_set_state(psmouse, PSMOUSE_IGNORE);
+- input_free_device(new_dev);
+- psmouse->dev = old_dev;
+- psmouse_set_state(psmouse, PSMOUSE_INITIALIZING);
+- psmouse_switch_protocol(psmouse, old_proto);
+- psmouse_initialize(psmouse);
+- psmouse_set_state(psmouse, PSMOUSE_CMD_MODE);
++ psmouse_set_state(psmouse, PSMOUSE_IGNORE);
++ input_free_device(new_dev);
++ psmouse->dev = old_dev;
++ psmouse_set_state(psmouse, PSMOUSE_INITIALIZING);
++ psmouse_switch_protocol(psmouse, old_proto);
++ psmouse_initialize(psmouse);
++ psmouse_set_state(psmouse, PSMOUSE_CMD_MODE);
+
+- return error;
++ return error;
++ }
+ }
+
+- input_unregister_device(old_dev);
++ if (old_dev)
++ input_unregister_device(old_dev);
+
+ if (parent && parent->pt_activate)
+ parent->pt_activate(parent);
+diff --git a/drivers/input/mouse/psmouse.h b/drivers/input/mouse/psmouse.h
+index 36bd421..e853dee 100644
+--- a/drivers/input/mouse/psmouse.h
++++ b/drivers/input/mouse/psmouse.h
+@@ -76,6 +76,7 @@ struct psmouse_protocol {
+ bool maxproto;
+ bool ignore_parity; /* Protocol should ignore parity errors from KBC */
+ bool try_passthru; /* Try protocol also on passthrough ports */
++ bool smbus_companion; /* "Protocol" is a stub, device is on SMBus */
+ const char *name;
+ const char *alias;
+ int (*detect)(struct psmouse *, bool);
+--
+2.9.3
+
diff --git a/rmi4-Input-psmouse-store-pointer-to-current-protocol.patch b/rmi4-Input-psmouse-store-pointer-to-current-protocol.patch
new file mode 100644
index 0000000..7e8b5cd
--- /dev/null
+++ b/rmi4-Input-psmouse-store-pointer-to-current-protocol.patch
@@ -0,0 +1,278 @@
+From 2f1e5e11e43d08bf500a9be610648619491b17b5 Mon Sep 17 00:00:00 2001
+From: Benjamin Tissoires <benjamin.tissoires(a)redhat.com>
+Date: Wed, 5 Apr 2017 14:38:06 +0200
+Subject: [PATCH 4/9] Input: psmouse - store pointer to current protocol
+
+Upstream: scheduled for v4.12 in the input tree:
+https://git.kernel.org/pub/scm/linux/kernel/git/dtor/input.git/log/?h=next
+
+commit 085fa80dfdd60ac58137a5b5d231e70456126fda
+Author: Dmitry Torokhov <dmitry.torokhov(a)gmail.com>
+Date: Fri Mar 3 15:29:00 2017 -0800
+
+ Input: psmouse - store pointer to current protocol
+
+ Instead of storing only protocol "type" in pmsouse structure, store pointer
+ to the protocol structure, so that we have access to more data without
+ having to copy it over to psmouse structure.
+
+ Reviewed-by: Benjamin Tissoires <benjamin.tissoires(a)redhat.com>
+ Signed-off-by: Dmitry Torokhov <dmitry.torokhov(a)gmail.com>
+
+Signed-off-by: Benjamin Tissoires <benjamin.tissoires(a)redhat.com>
+---
+ drivers/input/mouse/psmouse-base.c | 43 +++++++++--------------
+ drivers/input/mouse/psmouse.h | 70 ++++++++++++++++++++++----------------
+ 2 files changed, 58 insertions(+), 55 deletions(-)
+
+diff --git a/drivers/input/mouse/psmouse-base.c b/drivers/input/mouse/psmouse-base.c
+index 47fd297..bb5d164 100644
+--- a/drivers/input/mouse/psmouse-base.c
++++ b/drivers/input/mouse/psmouse-base.c
+@@ -116,17 +116,6 @@ static DEFINE_MUTEX(psmouse_mutex);
+
+ static struct workqueue_struct *kpsmoused_wq;
+
+-struct psmouse_protocol {
+- enum psmouse_type type;
+- bool maxproto;
+- bool ignore_parity; /* Protocol should ignore parity errors from KBC */
+- bool try_passthru; /* Try protocol also on passthrough ports */
+- const char *name;
+- const char *alias;
+- int (*detect)(struct psmouse *, bool);
+- int (*init)(struct psmouse *);
+-};
+-
+ static void psmouse_report_standard_buttons(struct input_dev *dev, u8 buttons)
+ {
+ input_report_key(dev, BTN_LEFT, buttons & BIT(0));
+@@ -148,7 +137,7 @@ psmouse_ret_t psmouse_process_byte(struct psmouse *psmouse)
+
+ /* Full packet accumulated, process it */
+
+- switch (psmouse->type) {
++ switch (psmouse->protocol->type) {
+ case PSMOUSE_IMPS:
+ /* IntelliMouse has scroll wheel */
+ input_report_rel(dev, REL_WHEEL, -(signed char) packet[3]);
+@@ -325,7 +314,8 @@ static irqreturn_t psmouse_interrupt(struct serio *serio,
+ goto out;
+
+ if (unlikely((flags & SERIO_TIMEOUT) ||
+- ((flags & SERIO_PARITY) && !psmouse->ignore_parity))) {
++ ((flags & SERIO_PARITY) &&
++ !psmouse->protocol->ignore_parity))) {
+
+ if (psmouse->state == PSMOUSE_ACTIVATED)
+ psmouse_warn(psmouse,
+@@ -372,7 +362,7 @@ static irqreturn_t psmouse_interrupt(struct serio *serio,
+ }
+
+ if (psmouse->packet[1] == PSMOUSE_RET_ID ||
+- (psmouse->type == PSMOUSE_HGPK &&
++ (psmouse->protocol->type == PSMOUSE_HGPK &&
+ psmouse->packet[1] == PSMOUSE_RET_BAT)) {
+ __psmouse_set_state(psmouse, PSMOUSE_IGNORE);
+ serio_reconnect(serio);
+@@ -959,6 +949,8 @@ static void psmouse_apply_defaults(struct psmouse *psmouse)
+
+ __set_bit(INPUT_PROP_POINTER, input_dev->propbit);
+
++ psmouse->protocol = &psmouse_protocols[0];
++
+ psmouse->set_rate = psmouse_set_rate;
+ psmouse->set_resolution = psmouse_set_resolution;
+ psmouse->set_scale = psmouse_set_scale;
+@@ -1476,6 +1468,7 @@ static int psmouse_switch_protocol(struct psmouse *psmouse,
+ {
+ const struct psmouse_protocol *selected_proto;
+ struct input_dev *input_dev = psmouse->dev;
++ enum psmouse_type type;
+
+ input_dev->dev.parent = &psmouse->ps2dev.serio->dev;
+
+@@ -1488,15 +1481,13 @@ static int psmouse_switch_protocol(struct psmouse *psmouse,
+ if (proto->init && proto->init(psmouse) < 0)
+ return -1;
+
+- psmouse->type = proto->type;
+ selected_proto = proto;
+ } else {
+- psmouse->type = psmouse_extensions(psmouse,
+- psmouse_max_proto, true);
+- selected_proto = psmouse_protocol_by_type(psmouse->type);
++ type = psmouse_extensions(psmouse, psmouse_max_proto, true);
++ selected_proto = psmouse_protocol_by_type(type);
+ }
+
+- psmouse->ignore_parity = selected_proto->ignore_parity;
++ psmouse->protocol = selected_proto;
+
+ /*
+ * If mouse's packet size is 3 there is no point in polling the
+@@ -1522,7 +1513,7 @@ static int psmouse_switch_protocol(struct psmouse *psmouse,
+ input_dev->phys = psmouse->phys;
+ input_dev->id.bustype = BUS_I8042;
+ input_dev->id.vendor = 0x0002;
+- input_dev->id.product = psmouse->type;
++ input_dev->id.product = psmouse->protocol->type;
+ input_dev->id.version = psmouse->model;
+
+ return 0;
+@@ -1634,7 +1625,7 @@ static int __psmouse_reconnect(struct serio *serio, bool fast_reconnect)
+ struct psmouse *psmouse = serio_get_drvdata(serio);
+ struct psmouse *parent = NULL;
+ int (*reconnect_handler)(struct psmouse *);
+- unsigned char type;
++ enum psmouse_type type;
+ int rc = -1;
+
+ mutex_lock(&psmouse_mutex);
+@@ -1666,7 +1657,7 @@ static int __psmouse_reconnect(struct serio *serio, bool fast_reconnect)
+ goto out;
+
+ type = psmouse_extensions(psmouse, psmouse_max_proto, false);
+- if (psmouse->type != type)
++ if (psmouse->protocol->type != type)
+ goto out;
+ }
+
+@@ -1816,7 +1807,7 @@ static ssize_t psmouse_set_int_attr(struct psmouse *psmouse, void *offset, const
+
+ static ssize_t psmouse_attr_show_protocol(struct psmouse *psmouse, void *data, char *buf)
+ {
+- return sprintf(buf, "%s\n", psmouse_protocol_by_type(psmouse->type)->name);
++ return sprintf(buf, "%s\n", psmouse->protocol->name);
+ }
+
+ static ssize_t psmouse_attr_set_protocol(struct psmouse *psmouse, void *data, const char *buf, size_t count)
+@@ -1832,7 +1823,7 @@ static ssize_t psmouse_attr_set_protocol(struct psmouse *psmouse, void *data, co
+ if (!proto)
+ return -EINVAL;
+
+- if (psmouse->type == proto->type)
++ if (psmouse->protocol == proto)
+ return count;
+
+ new_dev = input_allocate_device();
+@@ -1856,7 +1847,7 @@ static ssize_t psmouse_attr_set_protocol(struct psmouse *psmouse, void *data, co
+ return -ENODEV;
+ }
+
+- if (psmouse->type == proto->type) {
++ if (psmouse->protocol == proto) {
+ input_free_device(new_dev);
+ return count; /* switched by other thread */
+ }
+@@ -1869,7 +1860,7 @@ static ssize_t psmouse_attr_set_protocol(struct psmouse *psmouse, void *data, co
+ }
+
+ old_dev = psmouse->dev;
+- old_proto = psmouse_protocol_by_type(psmouse->type);
++ old_proto = psmouse->protocol;
+
+ if (psmouse->disconnect)
+ psmouse->disconnect(psmouse);
+diff --git a/drivers/input/mouse/psmouse.h b/drivers/input/mouse/psmouse.h
+index bc76e77..36bd421 100644
+--- a/drivers/input/mouse/psmouse.h
++++ b/drivers/input/mouse/psmouse.h
+@@ -44,21 +44,58 @@ enum psmouse_scale {
+ PSMOUSE_SCALE21
+ };
+
++enum psmouse_type {
++ PSMOUSE_NONE,
++ PSMOUSE_PS2,
++ PSMOUSE_PS2PP,
++ PSMOUSE_THINKPS,
++ PSMOUSE_GENPS,
++ PSMOUSE_IMPS,
++ PSMOUSE_IMEX,
++ PSMOUSE_SYNAPTICS,
++ PSMOUSE_ALPS,
++ PSMOUSE_LIFEBOOK,
++ PSMOUSE_TRACKPOINT,
++ PSMOUSE_TOUCHKIT_PS2,
++ PSMOUSE_CORTRON,
++ PSMOUSE_HGPK,
++ PSMOUSE_ELANTECH,
++ PSMOUSE_FSP,
++ PSMOUSE_SYNAPTICS_RELATIVE,
++ PSMOUSE_CYPRESS,
++ PSMOUSE_FOCALTECH,
++ PSMOUSE_VMMOUSE,
++ PSMOUSE_BYD,
++ PSMOUSE_AUTO /* This one should always be last */
++};
++
++struct psmouse;
++
++struct psmouse_protocol {
++ enum psmouse_type type;
++ bool maxproto;
++ bool ignore_parity; /* Protocol should ignore parity errors from KBC */
++ bool try_passthru; /* Try protocol also on passthrough ports */
++ const char *name;
++ const char *alias;
++ int (*detect)(struct psmouse *, bool);
++ int (*init)(struct psmouse *);
++};
++
+ struct psmouse {
+ void *private;
+ struct input_dev *dev;
+ struct ps2dev ps2dev;
+ struct delayed_work resync_work;
+- char *vendor;
+- char *name;
++ const char *vendor;
++ const char *name;
++ const struct psmouse_protocol *protocol;
+ unsigned char packet[8];
+ unsigned char badbyte;
+ unsigned char pktcnt;
+ unsigned char pktsize;
+- unsigned char type;
+ unsigned char oob_data_type;
+ unsigned char extra_buttons;
+- bool ignore_parity;
+ bool acks_disable_command;
+ unsigned int model;
+ unsigned long last;
+@@ -89,31 +126,6 @@ struct psmouse {
+ void (*pt_deactivate)(struct psmouse *psmouse);
+ };
+
+-enum psmouse_type {
+- PSMOUSE_NONE,
+- PSMOUSE_PS2,
+- PSMOUSE_PS2PP,
+- PSMOUSE_THINKPS,
+- PSMOUSE_GENPS,
+- PSMOUSE_IMPS,
+- PSMOUSE_IMEX,
+- PSMOUSE_SYNAPTICS,
+- PSMOUSE_ALPS,
+- PSMOUSE_LIFEBOOK,
+- PSMOUSE_TRACKPOINT,
+- PSMOUSE_TOUCHKIT_PS2,
+- PSMOUSE_CORTRON,
+- PSMOUSE_HGPK,
+- PSMOUSE_ELANTECH,
+- PSMOUSE_FSP,
+- PSMOUSE_SYNAPTICS_RELATIVE,
+- PSMOUSE_CYPRESS,
+- PSMOUSE_FOCALTECH,
+- PSMOUSE_VMMOUSE,
+- PSMOUSE_BYD,
+- PSMOUSE_AUTO /* This one should always be last */
+-};
+-
+ void psmouse_queue_work(struct psmouse *psmouse, struct delayed_work *work,
+ unsigned long delay);
+ int psmouse_sliced_command(struct psmouse *psmouse, unsigned char command);
+--
+2.9.3
+
diff --git a/rmi4-Input-serio-add-fast-reconnect-option.patch b/rmi4-Input-serio-add-fast-reconnect-option.patch
new file mode 100644
index 0000000..4406542
--- /dev/null
+++ b/rmi4-Input-serio-add-fast-reconnect-option.patch
@@ -0,0 +1,83 @@
+From de058b1340b7c9868a51578ea1900e1bb6451d52 Mon Sep 17 00:00:00 2001
+From: Benjamin Tissoires <benjamin.tissoires(a)redhat.com>
+Date: Wed, 5 Apr 2017 15:47:05 +0200
+Subject: [PATCH 2/9] Input: serio - add fast reconnect option
+
+Upstream: scheduled for v4.12 in the input tree:
+https://git.kernel.org/pub/scm/linux/kernel/git/dtor/input.git/log/?h=next
+
+commit 5ea1320653359dd2ade7ff2ad81e37b790eb1f1f
+Author: Dmitry Torokhov <dmitry.torokhov(a)gmail.com>
+Date: Fri Mar 3 11:47:40 2017 -0800
+
+ Input: serio - add fast reconnect option
+
+ Devices connected to serio bus are quite slow, and to improve apparent
+ speed of resume process, serio core resumes (reconnects) its devices
+ asynchronously, by posting port reconnect requests to a workqueue.
+ Unfortunately this means that if there is a dependent device of a given
+ serio port (for example SMBus part of touchpad connected via both PS/2 and
+ SMBus), we do not have a good way of ensuring resume order.
+
+ This change allows drivers to define "fast reconnect" handlers that would
+ be called in-line during system resume. Drivers need to ensure that these
+ handlers are truly "fast".
+
+ Reviewed-by: Benjamin Tissoires <benjamin.tissoires(a)redhat.com>
+ Signed-off-by: Dmitry Torokhov <dmitry.torokhov(a)gmail.com>
+
+Signed-off-by: Benjamin Tissoires <benjamin.tissoires(a)redhat.com>
+---
+ drivers/input/serio/serio.c | 22 +++++++++++++++++-----
+ include/linux/serio.h | 1 +
+ 2 files changed, 18 insertions(+), 5 deletions(-)
+
+diff --git a/drivers/input/serio/serio.c b/drivers/input/serio/serio.c
+index 1ca7f55..34793ce 100644
+--- a/drivers/input/serio/serio.c
++++ b/drivers/input/serio/serio.c
+@@ -953,12 +953,24 @@ static int serio_suspend(struct device *dev)
+ static int serio_resume(struct device *dev)
+ {
+ struct serio *serio = to_serio_port(dev);
++ int error = -ENOENT;
+
+- /*
+- * Driver reconnect can take a while, so better let kseriod
+- * deal with it.
+- */
+- serio_queue_event(serio, NULL, SERIO_RECONNECT_PORT);
++ mutex_lock(&serio->drv_mutex);
++ if (serio->drv && serio->drv->fast_reconnect) {
++ error = serio->drv->fast_reconnect(serio);
++ if (error && error != -ENOENT)
++ dev_warn(dev, "fast reconnect failed with error %d\n",
++ error);
++ }
++ mutex_unlock(&serio->drv_mutex);
++
++ if (error) {
++ /*
++ * Driver reconnect can take a while, so better let
++ * kseriod deal with it.
++ */
++ serio_queue_event(serio, NULL, SERIO_RECONNECT_PORT);
++ }
+
+ return 0;
+ }
+diff --git a/include/linux/serio.h b/include/linux/serio.h
+index c733cff..138a5ef 100644
+--- a/include/linux/serio.h
++++ b/include/linux/serio.h
+@@ -77,6 +77,7 @@ struct serio_driver {
+ irqreturn_t (*interrupt)(struct serio *, unsigned char, unsigned int);
+ int (*connect)(struct serio *, struct serio_driver *drv);
+ int (*reconnect)(struct serio *);
++ int (*fast_reconnect)(struct serio *);
+ void (*disconnect)(struct serio *);
+ void (*cleanup)(struct serio *);
+
+--
+2.9.3
+
diff --git a/rmi4-Input-synaptics-add-support-for-Intertouch-devices.patch b/rmi4-Input-synaptics-add-support-for-Intertouch-devices.patch
new file mode 100644
index 0000000..870cb27
--- /dev/null
+++ b/rmi4-Input-synaptics-add-support-for-Intertouch-devices.patch
@@ -0,0 +1,836 @@
+From 60301475fd73e5242649b73c99f13b2597fc362c Mon Sep 17 00:00:00 2001
+From: Benjamin Tissoires <benjamin.tissoires(a)redhat.com>
+Date: Wed, 5 Apr 2017 14:38:33 +0200
+Subject: [PATCH 8/9] Input: synaptics - add support for Intertouch devices
+
+Upstream: scheduled for v4.12 in the input tree:
+https://git.kernel.org/pub/scm/linux/kernel/git/dtor/input.git/log/?h=next
+
+commit e839ffab028981ac77f650faf8c84f16e1719738
+Author: Benjamin Tissoires <benjamin.tissoires(a)redhat.com>
+Date: Thu Mar 2 14:13:53 2017 -0800
+
+ Input: synaptics - add support for Intertouch devices
+
+ Most of the Synaptics devices are connected through PS/2 and a different
+ bus (SMBus or HID over I2C). The secondary bus capability is indicated by
+ the InterTouch bit in extended capability 0x0C.
+
+ We only enable the InterTouch device to be created for the laptops
+ registered with the top software button property or those we know that are
+ functional. In the future, we might change the default to always rely on
+ the InterTouch bus. Currently, users can enable/disable the feature with
+ the psmouse parameter synaptics_intertouch.
+
+ Signed-off-by: Benjamin Tissoires <benjamin.tissoires(a)redhat.com>
+ Signed-off-by: Dmitry Torokhov <dmitry.torokhov(a)gmail.com>
+
+Signed-off-by: Benjamin Tissoires <benjamin.tissoires(a)redhat.com>
+---
+ drivers/input/mouse/Kconfig | 12 +
+ drivers/input/mouse/psmouse-base.c | 24 +-
+ drivers/input/mouse/psmouse.h | 1 +
+ drivers/input/mouse/synaptics.c | 554 +++++++++++++++++++++++++------------
+ drivers/input/mouse/synaptics.h | 5 +-
+ 5 files changed, 421 insertions(+), 175 deletions(-)
+
+diff --git a/drivers/input/mouse/Kconfig b/drivers/input/mouse/Kconfig
+index 87bde8a..89ebb8f 100644
+--- a/drivers/input/mouse/Kconfig
++++ b/drivers/input/mouse/Kconfig
+@@ -78,6 +78,18 @@ config MOUSE_PS2_SYNAPTICS
+
+ If unsure, say Y.
+
++config MOUSE_PS2_SYNAPTICS_SMBUS
++ bool "Synaptics PS/2 SMbus companion" if EXPERT
++ default y
++ depends on MOUSE_PS2
++ depends on I2C=y || I2C=MOUSE_PS2
++ select MOUSE_PS2_SMBUS
++ help
++ Say Y here if you have a Synaptics RMI4 touchpad connected to
++ to an SMBus, but enumerated through PS/2.
++
++ If unsure, say Y.
++
+ config MOUSE_PS2_CYPRESS
+ bool "Cypress PS/2 mouse protocol extension" if EXPERT
+ default y
+diff --git a/drivers/input/mouse/psmouse-base.c b/drivers/input/mouse/psmouse-base.c
+index ab9bfe2..f73b47b 100644
+--- a/drivers/input/mouse/psmouse-base.c
++++ b/drivers/input/mouse/psmouse-base.c
+@@ -773,7 +773,7 @@ static const struct psmouse_protocol psmouse_protocols[] = {
+ .name = "SynPS/2",
+ .alias = "synaptics",
+ .detect = synaptics_detect,
+- .init = synaptics_init,
++ .init = synaptics_init_absolute,
+ },
+ {
+ .type = PSMOUSE_SYNAPTICS_RELATIVE,
+@@ -783,6 +783,16 @@ static const struct psmouse_protocol psmouse_protocols[] = {
+ .init = synaptics_init_relative,
+ },
+ #endif
++#ifdef CONFIG_MOUSE_PS2_SYNAPTICS_SMBUS
++ {
++ .type = PSMOUSE_SYNAPTICS_SMBUS,
++ .name = "SynSMBus",
++ .alias = "synaptics-smbus",
++ .detect = synaptics_detect,
++ .init = synaptics_init_smbus,
++ .smbus_companion = true,
++ },
++#endif
+ #ifdef CONFIG_MOUSE_PS2_ALPS
+ {
+ .type = PSMOUSE_ALPS,
+@@ -1011,6 +1021,7 @@ static int psmouse_extensions(struct psmouse *psmouse,
+ unsigned int max_proto, bool set_properties)
+ {
+ bool synaptics_hardware = false;
++ int ret;
+
+ /*
+ * Always check for focaltech, this is safe as it uses pnp-id
+@@ -1073,9 +1084,14 @@ static int psmouse_extensions(struct psmouse *psmouse,
+ * enabled first, since we try detecting Synaptics
+ * even when protocol is disabled.
+ */
+- if (IS_ENABLED(CONFIG_MOUSE_PS2_SYNAPTICS) &&
+- (!set_properties || synaptics_init(psmouse) == 0)) {
+- return PSMOUSE_SYNAPTICS;
++ if (IS_ENABLED(CONFIG_MOUSE_PS2_SYNAPTICS) ||
++ IS_ENABLED(CONFIG_MOUSE_PS2_SYNAPTICS_SMBUS)) {
++ if (!set_properties)
++ return PSMOUSE_SYNAPTICS;
++
++ ret = synaptics_init(psmouse);
++ if (ret >= 0)
++ return ret;
+ }
+
+ /*
+diff --git a/drivers/input/mouse/psmouse.h b/drivers/input/mouse/psmouse.h
+index 0511083..38855e4 100644
+--- a/drivers/input/mouse/psmouse.h
++++ b/drivers/input/mouse/psmouse.h
+@@ -66,6 +66,7 @@ enum psmouse_type {
+ PSMOUSE_FOCALTECH,
+ PSMOUSE_VMMOUSE,
+ PSMOUSE_BYD,
++ PSMOUSE_SYNAPTICS_SMBUS,
+ PSMOUSE_AUTO /* This one should always be last */
+ };
+
+diff --git a/drivers/input/mouse/synaptics.c b/drivers/input/mouse/synaptics.c
+index 621cf3f..c991d63 100644
+--- a/drivers/input/mouse/synaptics.c
++++ b/drivers/input/mouse/synaptics.c
+@@ -29,6 +29,8 @@
+ #include <linux/input/mt.h>
+ #include <linux/serio.h>
+ #include <linux/libps2.h>
++#include <linux/rmi.h>
++#include <linux/i2c.h>
+ #include <linux/slab.h>
+ #include "psmouse.h"
+ #include "synaptics.h"
+@@ -119,59 +121,8 @@ void synaptics_reset(struct psmouse *psmouse)
+ synaptics_mode_cmd(psmouse, 0);
+ }
+
+-#ifdef CONFIG_MOUSE_PS2_SYNAPTICS
+-
+-static bool cr48_profile_sensor;
+-
+-#define ANY_BOARD_ID 0
+-struct min_max_quirk {
+- const char * const *pnp_ids;
+- struct {
+- u32 min, max;
+- } board_id;
+- u32 x_min, x_max, y_min, y_max;
+-};
+-
+-static const struct min_max_quirk min_max_pnpid_table[] = {
+- {
+- (const char * const []){"LEN0033", NULL},
+- {ANY_BOARD_ID, ANY_BOARD_ID},
+- 1024, 5052, 2258, 4832
+- },
+- {
+- (const char * const []){"LEN0042", NULL},
+- {ANY_BOARD_ID, ANY_BOARD_ID},
+- 1232, 5710, 1156, 4696
+- },
+- {
+- (const char * const []){"LEN0034", "LEN0036", "LEN0037",
+- "LEN0039", "LEN2002", "LEN2004",
+- NULL},
+- {ANY_BOARD_ID, 2961},
+- 1024, 5112, 2024, 4832
+- },
+- {
+- (const char * const []){"LEN2000", NULL},
+- {ANY_BOARD_ID, ANY_BOARD_ID},
+- 1024, 5113, 2021, 4832
+- },
+- {
+- (const char * const []){"LEN2001", NULL},
+- {ANY_BOARD_ID, ANY_BOARD_ID},
+- 1024, 5022, 2508, 4832
+- },
+- {
+- (const char * const []){"LEN2006", NULL},
+- {2691, 2691},
+- 1024, 5045, 2457, 4832
+- },
+- {
+- (const char * const []){"LEN2006", NULL},
+- {ANY_BOARD_ID, ANY_BOARD_ID},
+- 1264, 5675, 1171, 4688
+- },
+- { }
+-};
++#if defined(CONFIG_MOUSE_PS2_SYNAPTICS) || \
++ defined(CONFIG_MOUSE_PS2_SYNAPTICS_SMBUS)
+
+ /* This list has been kindly provided by Synaptics. */
+ static const char * const topbuttonpad_pnp_ids[] = {
+@@ -211,37 +162,50 @@ static const char * const topbuttonpad_pnp_ids[] = {
+ NULL
+ };
+
+-/* This list has been kindly provided by Synaptics. */
+-static const char * const forcepad_pnp_ids[] = {
+- "SYN300D",
+- "SYN3014",
++static const char * const smbus_pnp_ids[] = {
++ /* all of the topbuttonpad_pnp_ids are valid, we just add some extras */
++ "LEN0048", /* X1 Carbon 3 */
++ "LEN0046", /* X250 */
++ "LEN004a", /* W541 */
++ "LEN200f", /* T450s */
+ NULL
+ };
+
+-/*****************************************************************************
+- * Synaptics communications functions
+- ****************************************************************************/
+-
+ /*
+- * Synaptics touchpads report the y coordinate from bottom to top, which is
+- * opposite from what userspace expects.
+- * This function is used to invert y before reporting.
++ * Send a command to the synpatics touchpad by special commands
+ */
+-static int synaptics_invert_y(int y)
++static int synaptics_send_cmd(struct psmouse *psmouse,
++ unsigned char c, unsigned char *param)
+ {
+- return YMAX_NOMINAL + YMIN_NOMINAL - y;
++ int error;
++
++ error = psmouse_sliced_command(psmouse, c);
++ if (error)
++ return error;
++
++ error = ps2_command(&psmouse->ps2dev, param, PSMOUSE_CMD_GETINFO);
++ if (error)
++ return error;
++
++ return 0;
+ }
+
+ /*
+- * Send a command to the synpatics touchpad by special commands
++ * Identify Touchpad
++ * See also the SYN_ID_* macros
+ */
+-static int synaptics_send_cmd(struct psmouse *psmouse, unsigned char c, unsigned char *param)
++static int synaptics_identify(struct psmouse *psmouse,
++ struct synaptics_device_info *info)
+ {
+- if (psmouse_sliced_command(psmouse, c))
+- return -1;
+- if (ps2_command(&psmouse->ps2dev, param, PSMOUSE_CMD_GETINFO))
+- return -1;
+- return 0;
++ unsigned char id[3];
++ int error;
++
++ error = synaptics_send_cmd(psmouse, SYN_QUE_IDENTIFY, id);
++ if (error)
++ return error;
++
++ info->identity = (id[0] << 16) | (id[1] << 8) | id[2];
++ return SYN_ID_IS_SYNAPTICS(info->identity) ? 0 : -ENXIO;
+ }
+
+ /*
+@@ -262,6 +226,23 @@ static int synaptics_model_id(struct psmouse *psmouse,
+ return 0;
+ }
+
++/*
++ * Read the firmware id from the touchpad
++ */
++static int synaptics_firmware_id(struct psmouse *psmouse,
++ struct synaptics_device_info *info)
++{
++ unsigned char fwid[3];
++ int error;
++
++ error = synaptics_send_cmd(psmouse, SYN_QUE_FIRMWARE_ID, fwid);
++ if (error)
++ return error;
++
++ info->firmware_id = (fwid[0] << 16) | (fwid[1] << 8) | fwid[2];
++ return 0;
++}
++
+ static int synaptics_more_extended_queries(struct psmouse *psmouse,
+ struct synaptics_device_info *info)
+ {
+@@ -303,23 +284,6 @@ static int synaptics_query_modes(struct psmouse *psmouse,
+ }
+
+ /*
+- * Read the firmware id from the touchpad
+- */
+-static int synaptics_firmware_id(struct psmouse *psmouse,
+- struct synaptics_device_info *info)
+-{
+- unsigned char fwid[3];
+- int error;
+-
+- error = synaptics_send_cmd(psmouse, SYN_QUE_FIRMWARE_ID, fwid);
+- if (error)
+- return error;
+-
+- info->firmware_id = (fwid[0] << 16) | (fwid[1] << 8) | fwid[2];
+- return 0;
+-}
+-
+-/*
+ * Read the capability-bits from the touchpad
+ * see also the SYN_CAP_* macros
+ */
+@@ -380,28 +344,9 @@ static int synaptics_capability(struct psmouse *psmouse,
+ }
+
+ /*
+- * Identify Touchpad
+- * See also the SYN_ID_* macros
+- */
+-static int synaptics_identify(struct psmouse *psmouse,
+- struct synaptics_device_info *info)
+-{
+- unsigned char id[3];
+- int error;
+-
+- error = synaptics_send_cmd(psmouse, SYN_QUE_IDENTIFY, id);
+- if (error)
+- return error;
+-
+- info->identity = (id[0] << 16) | (id[1] << 8) | id[2];
+- return SYN_ID_IS_SYNAPTICS(info->identity) ? 0 : -ENXIO;
+-}
+-
+-/*
+ * Read touchpad resolution and maximum reported coordinates
+ * Resolution is left zero if touchpad does not support the query
+ */
+-
+ static int synaptics_resolution(struct psmouse *psmouse,
+ struct synaptics_device_info *info)
+ {
+@@ -460,10 +405,118 @@ static int synaptics_resolution(struct psmouse *psmouse,
+ return 0;
+ }
+
++static int synaptics_query_hardware(struct psmouse *psmouse,
++ struct synaptics_device_info *info)
++{
++ int error;
++
++ error = synaptics_identify(psmouse, info);
++ if (error)
++ return error;
++
++ error = synaptics_model_id(psmouse, info);
++ if (error)
++ return error;
++
++ error = synaptics_firmware_id(psmouse, info);
++ if (error)
++ return error;
++
++ error = synaptics_query_modes(psmouse, info);
++ if (error)
++ return error;
++
++ error = synaptics_capability(psmouse, info);
++ if (error)
++ return error;
++
++ error = synaptics_resolution(psmouse, info);
++ if (error)
++ return error;
++
++ return 0;
++}
++
++#endif /* CONFIG_MOUSE_PS2_SYNAPTICS || CONFIG_MOUSE_PS2_SYNAPTICS_SMBUS */
++
++#ifdef CONFIG_MOUSE_PS2_SYNAPTICS
++
++static bool cr48_profile_sensor;
++
++#define ANY_BOARD_ID 0
++struct min_max_quirk {
++ const char * const *pnp_ids;
++ struct {
++ u32 min, max;
++ } board_id;
++ u32 x_min, x_max, y_min, y_max;
++};
++
++static const struct min_max_quirk min_max_pnpid_table[] = {
++ {
++ (const char * const []){"LEN0033", NULL},
++ {ANY_BOARD_ID, ANY_BOARD_ID},
++ 1024, 5052, 2258, 4832
++ },
++ {
++ (const char * const []){"LEN0042", NULL},
++ {ANY_BOARD_ID, ANY_BOARD_ID},
++ 1232, 5710, 1156, 4696
++ },
++ {
++ (const char * const []){"LEN0034", "LEN0036", "LEN0037",
++ "LEN0039", "LEN2002", "LEN2004",
++ NULL},
++ {ANY_BOARD_ID, 2961},
++ 1024, 5112, 2024, 4832
++ },
++ {
++ (const char * const []){"LEN2000", NULL},
++ {ANY_BOARD_ID, ANY_BOARD_ID},
++ 1024, 5113, 2021, 4832
++ },
++ {
++ (const char * const []){"LEN2001", NULL},
++ {ANY_BOARD_ID, ANY_BOARD_ID},
++ 1024, 5022, 2508, 4832
++ },
++ {
++ (const char * const []){"LEN2006", NULL},
++ {2691, 2691},
++ 1024, 5045, 2457, 4832
++ },
++ {
++ (const char * const []){"LEN2006", NULL},
++ {ANY_BOARD_ID, ANY_BOARD_ID},
++ 1264, 5675, 1171, 4688
++ },
++ { }
++};
++
++/* This list has been kindly provided by Synaptics. */
++static const char * const forcepad_pnp_ids[] = {
++ "SYN300D",
++ "SYN3014",
++ NULL
++};
++
++/*****************************************************************************
++ * Synaptics communications functions
++ ****************************************************************************/
++
++/*
++ * Synaptics touchpads report the y coordinate from bottom to top, which is
++ * opposite from what userspace expects.
++ * This function is used to invert y before reporting.
++ */
++static int synaptics_invert_y(int y)
++{
++ return YMAX_NOMINAL + YMIN_NOMINAL - y;
++}
++
+ /*
+ * Apply quirk(s) if the hardware matches
+ */
+-
+ static void synaptics_apply_quirks(struct psmouse *psmouse,
+ struct synaptics_device_info *info)
+ {
+@@ -494,38 +547,6 @@ static void synaptics_apply_quirks(struct psmouse *psmouse,
+ }
+ }
+
+-static int synaptics_query_hardware(struct psmouse *psmouse,
+- struct synaptics_device_info *info)
+-{
+- int error;
+-
+- error = synaptics_identify(psmouse, info);
+- if (error)
+- return error;
+-
+- error = synaptics_model_id(psmouse, info);
+- if (error)
+- return error;
+-
+- error = synaptics_firmware_id(psmouse, info);
+- if (error)
+- return error;
+-
+- error = synaptics_query_modes(psmouse, info);
+- if (error)
+- return error;
+-
+- error = synaptics_capability(psmouse, info);
+- if (error)
+- return error;
+-
+- error = synaptics_resolution(psmouse, info);
+- if (error)
+- return error;
+-
+- return 0;
+-}
+-
+ static int synaptics_set_advanced_gesture_mode(struct psmouse *psmouse)
+ {
+ static unsigned char param = 0xc8;
+@@ -1343,6 +1364,12 @@ static void synaptics_disconnect(struct psmouse *psmouse)
+ {
+ struct synaptics_data *priv = psmouse->private;
+
++ /*
++ * We might have left a breadcrumb when trying to
++ * set up SMbus companion.
++ */
++ psmouse_smbus_cleanup(psmouse);
++
+ if (!priv->absolute_mode &&
+ SYN_ID_DISGEST_SUPPORTED(priv->info.identity))
+ device_remove_file(&psmouse->ps2dev.serio->dev,
+@@ -1483,39 +1510,20 @@ void __init synaptics_module_init(void)
+ cr48_profile_sensor = dmi_check_system(cr48_dmi_table);
+ }
+
+-static int __synaptics_init(struct psmouse *psmouse, bool absolute_mode)
++static int synaptics_init_ps2(struct psmouse *psmouse,
++ struct synaptics_device_info *info,
++ bool absolute_mode)
+ {
+ struct synaptics_data *priv;
+- struct synaptics_device_info *info;
+- int err = -1;
++ int err;
+
+- /*
+- * The OLPC XO has issues with Synaptics' absolute mode; the constant
+- * packet spew overloads the EC such that key presses on the keyboard
+- * are missed. Given that, don't even attempt to use Absolute mode.
+- * Relative mode seems to work just fine.
+- */
+- if (absolute_mode && broken_olpc_ec) {
+- psmouse_info(psmouse,
+- "OLPC XO detected, not enabling Synaptics protocol.\n");
+- return -ENODEV;
+- }
++ synaptics_apply_quirks(psmouse, info);
+
+ psmouse->private = priv = kzalloc(sizeof(struct synaptics_data), GFP_KERNEL);
+ if (!priv)
+ return -ENOMEM;
+
+- info = &priv->info;
+-
+- psmouse_reset(psmouse);
+-
+- if (synaptics_query_hardware(psmouse, info)) {
+- psmouse_err(psmouse, "Unable to query device.\n");
+- goto init_fail;
+- }
+-
+- synaptics_apply_quirks(psmouse, info);
+-
++ priv->info = *info;
+ priv->absolute_mode = absolute_mode;
+ if (SYN_ID_DISGEST_SUPPORTED(info->identity))
+ priv->disable_gesture = true;
+@@ -1526,7 +1534,8 @@ static int __synaptics_init(struct psmouse *psmouse, bool absolute_mode)
+ */
+ priv->is_forcepad = psmouse_matches_pnp_id(psmouse, forcepad_pnp_ids);
+
+- if (synaptics_set_mode(psmouse)) {
++ err = synaptics_set_mode(psmouse);
++ if (err) {
+ psmouse_err(psmouse, "Unable to initialize device.\n");
+ goto init_fail;
+ }
+@@ -1603,7 +1612,23 @@ static int __synaptics_init(struct psmouse *psmouse, bool absolute_mode)
+ return err;
+ }
+
+-int synaptics_init(struct psmouse *psmouse)
++static int __synaptics_init(struct psmouse *psmouse, bool absolute_mode)
++{
++ struct synaptics_device_info info;
++ int error;
++
++ psmouse_reset(psmouse);
++
++ error = synaptics_query_hardware(psmouse, &info);
++ if (error) {
++ psmouse_err(psmouse, "Unable to query device: %d\n", error);
++ return error;
++ }
++
++ return synaptics_init_ps2(psmouse, &info, absolute_mode);
++}
++
++int synaptics_init_absolute(struct psmouse *psmouse)
+ {
+ return __synaptics_init(psmouse, true);
+ }
+@@ -1613,15 +1638,204 @@ int synaptics_init_relative(struct psmouse *psmouse)
+ return __synaptics_init(psmouse, false);
+ }
+
++static int synaptics_setup_ps2(struct psmouse *psmouse,
++ struct synaptics_device_info *info)
++{
++ bool absolute_mode = true;
++ int error;
++
++ /*
++ * The OLPC XO has issues with Synaptics' absolute mode; the constant
++ * packet spew overloads the EC such that key presses on the keyboard
++ * are missed. Given that, don't even attempt to use Absolute mode.
++ * Relative mode seems to work just fine.
++ */
++ if (broken_olpc_ec) {
++ psmouse_info(psmouse,
++ "OLPC XO detected, forcing relative protocol.\n");
++ absolute_mode = false;
++ }
++
++ error = synaptics_init_ps2(psmouse, info, absolute_mode);
++ if (error)
++ return error;
++
++ return absolute_mode ? PSMOUSE_SYNAPTICS : PSMOUSE_SYNAPTICS_RELATIVE;
++}
++
+ #else /* CONFIG_MOUSE_PS2_SYNAPTICS */
+
+ void __init synaptics_module_init(void)
+ {
+ }
+
+-int synaptics_init(struct psmouse *psmouse)
++static int __maybe_unused
++synaptics_setup_ps2(struct psmouse *psmouse,
++ struct synaptics_device_info *info)
+ {
+ return -ENOSYS;
+ }
+
+ #endif /* CONFIG_MOUSE_PS2_SYNAPTICS */
++
++#ifdef CONFIG_MOUSE_PS2_SYNAPTICS_SMBUS
++
++/*
++ * The newest Synaptics device can use a secondary bus (called InterTouch) which
++ * provides a better bandwidth and allow a better control of the touchpads.
++ * This is used to decide if we need to use this bus or not.
++ */
++enum {
++ SYNAPTICS_INTERTOUCH_NOT_SET = -1,
++ SYNAPTICS_INTERTOUCH_OFF,
++ SYNAPTICS_INTERTOUCH_ON,
++};
++
++static int synaptics_intertouch = SYNAPTICS_INTERTOUCH_NOT_SET;
++module_param_named(synaptics_intertouch, synaptics_intertouch, int, 0644);
++MODULE_PARM_DESC(synaptics_intertouch, "Use a secondary bus for the Synaptics device.");
++
++static int synaptics_create_intertouch(struct psmouse *psmouse,
++ struct synaptics_device_info *info,
++ bool leave_breadcrumbs)
++{
++ bool topbuttonpad =
++ psmouse_matches_pnp_id(psmouse, topbuttonpad_pnp_ids) &&
++ !SYN_CAP_EXT_BUTTONS_STICK(info->ext_cap_10);
++ const struct rmi_device_platform_data pdata = {
++ .sensor_pdata = {
++ .sensor_type = rmi_sensor_touchpad,
++ .axis_align.flip_y = true,
++ /* to prevent cursors jumps: */
++ .kernel_tracking = true,
++ .topbuttonpad = topbuttonpad,
++ },
++ .f30_data = {
++ .buttonpad = SYN_CAP_CLICKPAD(info->ext_cap_0c),
++ .trackstick_buttons =
++ !!SYN_CAP_EXT_BUTTONS_STICK(info->ext_cap_10),
++ },
++ };
++ const struct i2c_board_info intertouch_board = {
++ I2C_BOARD_INFO("rmi4_smbus", 0x2c),
++ .flags = I2C_CLIENT_HOST_NOTIFY,
++ };
++
++ return psmouse_smbus_init(psmouse, &intertouch_board,
++ &pdata, sizeof(pdata),
++ leave_breadcrumbs);
++}
++
++/**
++ * synaptics_setup_intertouch - called once the PS/2 devices are enumerated
++ * and decides to instantiate a SMBus InterTouch device.
++ */
++static int synaptics_setup_intertouch(struct psmouse *psmouse,
++ struct synaptics_device_info *info,
++ bool leave_breadcrumbs)
++{
++ int error;
++
++ if (synaptics_intertouch == SYNAPTICS_INTERTOUCH_OFF)
++ return -ENXIO;
++
++ if (synaptics_intertouch == SYNAPTICS_INTERTOUCH_NOT_SET) {
++ if (!psmouse_matches_pnp_id(psmouse, topbuttonpad_pnp_ids) &&
++ !psmouse_matches_pnp_id(psmouse, smbus_pnp_ids))
++ return -ENXIO;
++ }
++
++ psmouse_info(psmouse, "Trying to set up SMBus access\n");
++
++ error = synaptics_create_intertouch(psmouse, info, leave_breadcrumbs);
++ if (error) {
++ if (error == -EAGAIN)
++ psmouse_info(psmouse, "SMbus companion is not ready yet\n");
++ else
++ psmouse_err(psmouse, "unable to create intertouch device\n");
++
++ return error;
++ }
++
++ return 0;
++}
++
++int synaptics_init_smbus(struct psmouse *psmouse)
++{
++ struct synaptics_device_info info;
++ int error;
++
++ psmouse_reset(psmouse);
++
++ error = synaptics_query_hardware(psmouse, &info);
++ if (error) {
++ psmouse_err(psmouse, "Unable to query device: %d\n", error);
++ return error;
++ }
++
++ if (!SYN_CAP_INTERTOUCH(info.ext_cap_0c))
++ return -ENXIO;
++
++ return synaptics_create_intertouch(psmouse, &info, false);
++}
++
++#else /* CONFIG_MOUSE_PS2_SYNAPTICS_SMBUS */
++
++static int __maybe_unused
++synaptics_setup_intertouch(struct psmouse *psmouse,
++ struct synaptics_device_info *info,
++ bool leave_breadcrumbs)
++{
++ return -ENOSYS;
++}
++
++int synaptics_init_smbus(struct psmouse *psmouse)
++{
++ return -ENOSYS;
++}
++
++#endif /* CONFIG_MOUSE_PS2_SYNAPTICS_SMBUS */
++
++#if defined(CONFIG_MOUSE_PS2_SYNAPTICS) || \
++ defined(CONFIG_MOUSE_PS2_SYNAPTICS_SMBUS)
++
++int synaptics_init(struct psmouse *psmouse)
++{
++ struct synaptics_device_info info;
++ int error;
++ int retval;
++
++ psmouse_reset(psmouse);
++
++ error = synaptics_query_hardware(psmouse, &info);
++ if (error) {
++ psmouse_err(psmouse, "Unable to query device: %d\n", error);
++ return error;
++ }
++
++ if (SYN_CAP_INTERTOUCH(info.ext_cap_0c)) {
++ error = synaptics_setup_intertouch(psmouse, &info, true);
++ if (!error)
++ return PSMOUSE_SYNAPTICS_SMBUS;
++ }
++
++ retval = synaptics_setup_ps2(psmouse, &info);
++ if (retval < 0) {
++ /*
++ * Not using any flavor of Synaptics support, so clean up
++ * SMbus breadcrumbs, if any.
++ */
++ psmouse_smbus_cleanup(psmouse);
++ }
++
++ return retval;
++}
++
++#else /* CONFIG_MOUSE_PS2_SYNAPTICS || CONFIG_MOUSE_PS2_SYNAPTICS_SMBUS */
++
++int synaptics_init(struct psmouse *psmouse)
++{
++ return -ENOSYS;
++}
++
++#endif /* CONFIG_MOUSE_PS2_SYNAPTICS || CONFIG_MOUSE_PS2_SYNAPTICS_SMBUS */
+diff --git a/drivers/input/mouse/synaptics.h b/drivers/input/mouse/synaptics.h
+index d9b824f..31652d9 100644
+--- a/drivers/input/mouse/synaptics.h
++++ b/drivers/input/mouse/synaptics.h
+@@ -90,6 +90,7 @@
+ #define SYN_CAP_ADV_GESTURE(ex0c) ((ex0c) & 0x080000)
+ #define SYN_CAP_REDUCED_FILTERING(ex0c) ((ex0c) & 0x000400)
+ #define SYN_CAP_IMAGE_SENSOR(ex0c) ((ex0c) & 0x000800)
++#define SYN_CAP_INTERTOUCH(ex0c) ((ex0c) & 0x004000)
+
+ /*
+ * The following descibes response for the 0x10 query.
+@@ -204,8 +205,10 @@ struct synaptics_data {
+
+ void synaptics_module_init(void);
+ int synaptics_detect(struct psmouse *psmouse, bool set_properties);
+-int synaptics_init(struct psmouse *psmouse);
++int synaptics_init_absolute(struct psmouse *psmouse);
+ int synaptics_init_relative(struct psmouse *psmouse);
++int synaptics_init_smbus(struct psmouse *psmouse);
++int synaptics_init(struct psmouse *psmouse);
+ void synaptics_reset(struct psmouse *psmouse);
+
+ #endif /* _SYNAPTICS_H */
+--
+2.9.3
+
diff --git a/rmi4-Input-synaptics-do-not-mix-logical-and-bitwise-opera.patch b/rmi4-Input-synaptics-do-not-mix-logical-and-bitwise-opera.patch
new file mode 100644
index 0000000..e606b77
--- /dev/null
+++ b/rmi4-Input-synaptics-do-not-mix-logical-and-bitwise-opera.patch
@@ -0,0 +1,70 @@
+From 0dc84d09c42670c423098ee91acdc0290a5122a8 Mon Sep 17 00:00:00 2001
+From: Benjamin Tissoires <benjamin.tissoires(a)redhat.com>
+Date: Wed, 5 Apr 2017 14:37:18 +0200
+Subject: [PATCH 1/9] Input: synaptics - do not mix logical and bitwise
+ operations
+
+Upstream: scheduled for v4.12 in the input tree:
+https://git.kernel.org/pub/scm/linux/kernel/git/dtor/input.git/log/?h=next
+
+commit 996b9eedd061752bfa0f3a10381515d67db26b3e
+Author: Dmitry Torokhov <dmitry.torokhov(a)gmail.com>
+Date: Thu Mar 23 10:02:50 2017 -0700
+
+ Input: synaptics - do not mix logical and bitwise operations
+
+ Let's stop using !!x to reduce value of trackstick button expression to 0/1
+ and use shift instead. This removes the following sparse warning:
+
+ CHECK drivers/input/mouse/synaptics.c
+ drivers/input/mouse/synaptics.c:943:79: warning: dubious: !x | y
+
+ Also, the bits we are testing are not capabilities, so lets drop "_CAP"
+ suffix from macro names.
+
+ Reviewed-by: Benjamin Tissoires <benjamin.tissoires(a)redhat.com>
+ Signed-off-by: Dmitry Torokhov <dmitry.torokhov(a)gmail.com>
+
+Signed-off-by: Benjamin Tissoires <benjamin.tissoires(a)redhat.com>
+---
+ drivers/input/mouse/synaptics.c | 6 +++---
+ drivers/input/mouse/synaptics.h | 6 +++---
+ 2 files changed, 6 insertions(+), 6 deletions(-)
+
+diff --git a/drivers/input/mouse/synaptics.c b/drivers/input/mouse/synaptics.c
+index 411784f..705fc78 100644
+--- a/drivers/input/mouse/synaptics.c
++++ b/drivers/input/mouse/synaptics.c
+@@ -884,9 +884,9 @@ static void synaptics_report_ext_buttons(struct psmouse *psmouse,
+ u8 pt_buttons;
+
+ /* The trackstick expects at most 3 buttons */
+- pt_buttons = SYN_CAP_EXT_BUTTON_STICK_L(hw->ext_buttons) |
+- SYN_CAP_EXT_BUTTON_STICK_R(hw->ext_buttons) << 1 |
+- SYN_CAP_EXT_BUTTON_STICK_M(hw->ext_buttons) << 2;
++ pt_buttons = SYN_EXT_BUTTON_STICK_L(hw->ext_buttons) |
++ SYN_EXT_BUTTON_STICK_R(hw->ext_buttons) << 1 |
++ SYN_EXT_BUTTON_STICK_M(hw->ext_buttons) << 2;
+
+ serio_interrupt(priv->pt_port,
+ PSMOUSE_OOB_EXTRA_BTNS, SERIO_OOB_DATA);
+diff --git a/drivers/input/mouse/synaptics.h b/drivers/input/mouse/synaptics.h
+index 116ae25..b76bb7a 100644
+--- a/drivers/input/mouse/synaptics.h
++++ b/drivers/input/mouse/synaptics.h
+@@ -111,9 +111,9 @@
+ #define SYN_CAP_EXT_BUTTONS_STICK(ex10) ((ex10) & 0x010000)
+ #define SYN_CAP_SECUREPAD(ex10) ((ex10) & 0x020000)
+
+-#define SYN_CAP_EXT_BUTTON_STICK_L(eb) (!!((eb) & 0x01))
+-#define SYN_CAP_EXT_BUTTON_STICK_M(eb) (!!((eb) & 0x02))
+-#define SYN_CAP_EXT_BUTTON_STICK_R(eb) (!!((eb) & 0x04))
++#define SYN_EXT_BUTTON_STICK_L(eb) (((eb) & BIT(0)) >> 0)
++#define SYN_EXT_BUTTON_STICK_M(eb) (((eb) & BIT(1)) >> 1)
++#define SYN_EXT_BUTTON_STICK_R(eb) (((eb) & BIT(2)) >> 2)
+
+ /* synaptics modes query bits */
+ #define SYN_MODE_ABSOLUTE(m) ((m) & (1 << 7))
+--
+2.9.3
+
diff --git a/rmi4-Input-synaptics-split-device-info-into-a-separate-st.patch b/rmi4-Input-synaptics-split-device-info-into-a-separate-st.patch
new file mode 100644
index 0000000..445eeb0
--- /dev/null
+++ b/rmi4-Input-synaptics-split-device-info-into-a-separate-st.patch
@@ -0,0 +1,924 @@
+From dc5abb2d1b1424b1fb203c77245e04e2a9fee820 Mon Sep 17 00:00:00 2001
+From: Benjamin Tissoires <benjamin.tissoires(a)redhat.com>
+Date: Wed, 5 Apr 2017 14:38:27 +0200
+Subject: [PATCH 7/9] Input: synaptics - split device info into a separate
+ structure
+
+Upstream: scheduled for v4.12 in the input tree:
+https://git.kernel.org/pub/scm/linux/kernel/git/dtor/input.git/log/?h=next
+
+commit 6c53694fb2223746738d1d0cea71456ca88c8fb2
+Author: Dmitry Torokhov <dmitry.torokhov(a)gmail.com>
+Date: Sun Mar 5 15:51:33 2017 -0800
+
+ Input: synaptics - split device info into a separate structure
+
+ In preparation for SMBus/Intertouch device support, move static device
+ information that we query form the touchpad upon initialization into
+ separate structure. This will allow us to query the device without
+ allocating memory first.
+
+ Also stop using "unsigned long", everything fits into 32 bit chunks.
+
+ Reviewed-by: Benjamin Tissoires <benjamin.tissoires(a)redhat.com>
+ Signed-off-by: Dmitry Torokhov <dmitry.torokhov(a)gmail.com>
+
+Signed-off-by: Benjamin Tissoires <benjamin.tissoires(a)redhat.com>
+---
+ drivers/input/mouse/synaptics.c | 392 ++++++++++++++++++++++------------------
+ drivers/input/mouse/synaptics.h | 28 +--
+ 2 files changed, 233 insertions(+), 187 deletions(-)
+
+diff --git a/drivers/input/mouse/synaptics.c b/drivers/input/mouse/synaptics.c
+index 705fc78..621cf3f 100644
+--- a/drivers/input/mouse/synaptics.c
++++ b/drivers/input/mouse/synaptics.c
+@@ -127,9 +127,9 @@ static bool cr48_profile_sensor;
+ struct min_max_quirk {
+ const char * const *pnp_ids;
+ struct {
+- unsigned long int min, max;
++ u32 min, max;
+ } board_id;
+- int x_min, x_max, y_min, y_max;
++ u32 x_min, x_max, y_min, y_max;
+ };
+
+ static const struct min_max_quirk min_max_pnpid_table[] = {
+@@ -248,27 +248,31 @@ static int synaptics_send_cmd(struct psmouse *psmouse, unsigned char c, unsigned
+ * Read the model-id bytes from the touchpad
+ * see also SYN_MODEL_* macros
+ */
+-static int synaptics_model_id(struct psmouse *psmouse)
++static int synaptics_model_id(struct psmouse *psmouse,
++ struct synaptics_device_info *info)
+ {
+- struct synaptics_data *priv = psmouse->private;
+ unsigned char mi[3];
++ int error;
+
+- if (synaptics_send_cmd(psmouse, SYN_QUE_MODEL, mi))
+- return -1;
+- priv->model_id = (mi[0]<<16) | (mi[1]<<8) | mi[2];
++ error = synaptics_send_cmd(psmouse, SYN_QUE_MODEL, mi);
++ if (error)
++ return error;
++
++ info->model_id = (mi[0] << 16) | (mi[1] << 8) | mi[2];
+ return 0;
+ }
+
+-static int synaptics_more_extended_queries(struct psmouse *psmouse)
++static int synaptics_more_extended_queries(struct psmouse *psmouse,
++ struct synaptics_device_info *info)
+ {
+- struct synaptics_data *priv = psmouse->private;
+ unsigned char buf[3];
++ int error;
+
+- if (synaptics_send_cmd(psmouse, SYN_QUE_MEXT_CAPAB_10, buf))
+- return -1;
+-
+- priv->ext_cap_10 = (buf[0]<<16) | (buf[1]<<8) | buf[2];
++ error = synaptics_send_cmd(psmouse, SYN_QUE_MEXT_CAPAB_10, buf);
++ if (error)
++ return error;
+
++ info->ext_cap_10 = (buf[0] << 16) | (buf[1] << 8) | buf[2];
+ return 0;
+ }
+
+@@ -276,21 +280,24 @@ static int synaptics_more_extended_queries(struct psmouse *psmouse)
+ * Read the board id and the "More Extended Queries" from the touchpad
+ * The board id is encoded in the "QUERY MODES" response
+ */
+-static int synaptics_query_modes(struct psmouse *psmouse)
++static int synaptics_query_modes(struct psmouse *psmouse,
++ struct synaptics_device_info *info)
+ {
+- struct synaptics_data *priv = psmouse->private;
+ unsigned char bid[3];
++ int error;
+
+ /* firmwares prior 7.5 have no board_id encoded */
+- if (SYN_ID_FULL(priv->identity) < 0x705)
++ if (SYN_ID_FULL(info->identity) < 0x705)
+ return 0;
+
+- if (synaptics_send_cmd(psmouse, SYN_QUE_MODES, bid))
+- return -1;
+- priv->board_id = ((bid[0] & 0xfc) << 6) | bid[1];
++ error = synaptics_send_cmd(psmouse, SYN_QUE_MODES, bid);
++ if (error)
++ return error;
++
++ info->board_id = ((bid[0] & 0xfc) << 6) | bid[1];
+
+ if (SYN_MEXT_CAP_BIT(bid[0]))
+- return synaptics_more_extended_queries(psmouse);
++ return synaptics_more_extended_queries(psmouse, info);
+
+ return 0;
+ }
+@@ -298,14 +305,17 @@ static int synaptics_query_modes(struct psmouse *psmouse)
+ /*
+ * Read the firmware id from the touchpad
+ */
+-static int synaptics_firmware_id(struct psmouse *psmouse)
++static int synaptics_firmware_id(struct psmouse *psmouse,
++ struct synaptics_device_info *info)
+ {
+- struct synaptics_data *priv = psmouse->private;
+ unsigned char fwid[3];
++ int error;
+
+- if (synaptics_send_cmd(psmouse, SYN_QUE_FIRMWARE_ID, fwid))
+- return -1;
+- priv->firmware_id = (fwid[0] << 16) | (fwid[1] << 8) | fwid[2];
++ error = synaptics_send_cmd(psmouse, SYN_QUE_FIRMWARE_ID, fwid);
++ if (error)
++ return error;
++
++ info->firmware_id = (fwid[0] << 16) | (fwid[1] << 8) | fwid[2];
+ return 0;
+ }
+
+@@ -313,53 +323,57 @@ static int synaptics_firmware_id(struct psmouse *psmouse)
+ * Read the capability-bits from the touchpad
+ * see also the SYN_CAP_* macros
+ */
+-static int synaptics_capability(struct psmouse *psmouse)
++static int synaptics_capability(struct psmouse *psmouse,
++ struct synaptics_device_info *info)
+ {
+- struct synaptics_data *priv = psmouse->private;
+ unsigned char cap[3];
++ int error;
+
+- if (synaptics_send_cmd(psmouse, SYN_QUE_CAPABILITIES, cap))
+- return -1;
+- priv->capabilities = (cap[0] << 16) | (cap[1] << 8) | cap[2];
+- priv->ext_cap = priv->ext_cap_0c = 0;
++ error = synaptics_send_cmd(psmouse, SYN_QUE_CAPABILITIES, cap);
++ if (error)
++ return error;
++
++ info->capabilities = (cap[0] << 16) | (cap[1] << 8) | cap[2];
++ info->ext_cap = info->ext_cap_0c = 0;
+
+ /*
+ * Older firmwares had submodel ID fixed to 0x47
+ */
+- if (SYN_ID_FULL(priv->identity) < 0x705 &&
+- SYN_CAP_SUBMODEL_ID(priv->capabilities) != 0x47) {
+- return -1;
++ if (SYN_ID_FULL(info->identity) < 0x705 &&
++ SYN_CAP_SUBMODEL_ID(info->capabilities) != 0x47) {
++ return -ENXIO;
+ }
+
+ /*
+ * Unless capExtended is set the rest of the flags should be ignored
+ */
+- if (!SYN_CAP_EXTENDED(priv->capabilities))
+- priv->capabilities = 0;
++ if (!SYN_CAP_EXTENDED(info->capabilities))
++ info->capabilities = 0;
+
+- if (SYN_EXT_CAP_REQUESTS(priv->capabilities) >= 1) {
++ if (SYN_EXT_CAP_REQUESTS(info->capabilities) >= 1) {
+ if (synaptics_send_cmd(psmouse, SYN_QUE_EXT_CAPAB, cap)) {
+ psmouse_warn(psmouse,
+ "device claims to have extended capabilities, but I'm not able to read them.\n");
+ } else {
+- priv->ext_cap = (cap[0] << 16) | (cap[1] << 8) | cap[2];
++ info->ext_cap = (cap[0] << 16) | (cap[1] << 8) | cap[2];
+
+ /*
+ * if nExtBtn is greater than 8 it should be considered
+ * invalid and treated as 0
+ */
+- if (SYN_CAP_MULTI_BUTTON_NO(priv->ext_cap) > 8)
+- priv->ext_cap &= 0xff0fff;
++ if (SYN_CAP_MULTI_BUTTON_NO(info->ext_cap) > 8)
++ info->ext_cap &= 0xff0fff;
+ }
+ }
+
+- if (SYN_EXT_CAP_REQUESTS(priv->capabilities) >= 4) {
+- if (synaptics_send_cmd(psmouse, SYN_QUE_EXT_CAPAB_0C, cap)) {
++ if (SYN_EXT_CAP_REQUESTS(info->capabilities) >= 4) {
++ error = synaptics_send_cmd(psmouse, SYN_QUE_EXT_CAPAB_0C, cap);
++ if (error)
+ psmouse_warn(psmouse,
+ "device claims to have extended capability 0x0c, but I'm not able to read it.\n");
+- } else {
+- priv->ext_cap_0c = (cap[0] << 16) | (cap[1] << 8) | cap[2];
+- }
++ else
++ info->ext_cap_0c =
++ (cap[0] << 16) | (cap[1] << 8) | cap[2];
+ }
+
+ return 0;
+@@ -369,17 +383,18 @@ static int synaptics_capability(struct psmouse *psmouse)
+ * Identify Touchpad
+ * See also the SYN_ID_* macros
+ */
+-static int synaptics_identify(struct psmouse *psmouse)
++static int synaptics_identify(struct psmouse *psmouse,
++ struct synaptics_device_info *info)
+ {
+- struct synaptics_data *priv = psmouse->private;
+ unsigned char id[3];
++ int error;
+
+- if (synaptics_send_cmd(psmouse, SYN_QUE_IDENTIFY, id))
+- return -1;
+- priv->identity = (id[0]<<16) | (id[1]<<8) | id[2];
+- if (SYN_ID_IS_SYNAPTICS(priv->identity))
+- return 0;
+- return -1;
++ error = synaptics_send_cmd(psmouse, SYN_QUE_IDENTIFY, id);
++ if (error)
++ return error;
++
++ info->identity = (id[0] << 16) | (id[1] << 8) | id[2];
++ return SYN_ID_IS_SYNAPTICS(info->identity) ? 0 : -ENXIO;
+ }
+
+ /*
+@@ -387,52 +402,58 @@ static int synaptics_identify(struct psmouse *psmouse)
+ * Resolution is left zero if touchpad does not support the query
+ */
+
+-static int synaptics_resolution(struct psmouse *psmouse)
++static int synaptics_resolution(struct psmouse *psmouse,
++ struct synaptics_device_info *info)
+ {
+- struct synaptics_data *priv = psmouse->private;
+ unsigned char resp[3];
++ int error;
+
+- if (SYN_ID_MAJOR(priv->identity) < 4)
++ if (SYN_ID_MAJOR(info->identity) < 4)
+ return 0;
+
+- if (synaptics_send_cmd(psmouse, SYN_QUE_RESOLUTION, resp) == 0) {
++ error = synaptics_send_cmd(psmouse, SYN_QUE_RESOLUTION, resp);
++ if (!error) {
+ if (resp[0] != 0 && (resp[1] & 0x80) && resp[2] != 0) {
+- priv->x_res = resp[0]; /* x resolution in units/mm */
+- priv->y_res = resp[2]; /* y resolution in units/mm */
++ info->x_res = resp[0]; /* x resolution in units/mm */
++ info->y_res = resp[2]; /* y resolution in units/mm */
+ }
+ }
+
+- if (SYN_EXT_CAP_REQUESTS(priv->capabilities) >= 5 &&
+- SYN_CAP_MAX_DIMENSIONS(priv->ext_cap_0c)) {
+- if (synaptics_send_cmd(psmouse, SYN_QUE_EXT_MAX_COORDS, resp)) {
++ if (SYN_EXT_CAP_REQUESTS(info->capabilities) >= 5 &&
++ SYN_CAP_MAX_DIMENSIONS(info->ext_cap_0c)) {
++ error = synaptics_send_cmd(psmouse,
++ SYN_QUE_EXT_MAX_COORDS, resp);
++ if (error) {
+ psmouse_warn(psmouse,
+ "device claims to have max coordinates query, but I'm not able to read it.\n");
+ } else {
+- priv->x_max = (resp[0] << 5) | ((resp[1] & 0x0f) << 1);
+- priv->y_max = (resp[2] << 5) | ((resp[1] & 0xf0) >> 3);
++ info->x_max = (resp[0] << 5) | ((resp[1] & 0x0f) << 1);
++ info->y_max = (resp[2] << 5) | ((resp[1] & 0xf0) >> 3);
+ psmouse_info(psmouse,
+ "queried max coordinates: x [..%d], y [..%d]\n",
+- priv->x_max, priv->y_max);
++ info->x_max, info->y_max);
+ }
+ }
+
+- if (SYN_CAP_MIN_DIMENSIONS(priv->ext_cap_0c) &&
+- (SYN_EXT_CAP_REQUESTS(priv->capabilities) >= 7 ||
++ if (SYN_CAP_MIN_DIMENSIONS(info->ext_cap_0c) &&
++ (SYN_EXT_CAP_REQUESTS(info->capabilities) >= 7 ||
+ /*
+ * Firmware v8.1 does not report proper number of extended
+ * capabilities, but has been proven to report correct min
+ * coordinates.
+ */
+- SYN_ID_FULL(priv->identity) == 0x801)) {
+- if (synaptics_send_cmd(psmouse, SYN_QUE_EXT_MIN_COORDS, resp)) {
++ SYN_ID_FULL(info->identity) == 0x801)) {
++ error = synaptics_send_cmd(psmouse,
++ SYN_QUE_EXT_MIN_COORDS, resp);
++ if (error) {
+ psmouse_warn(psmouse,
+ "device claims to have min coordinates query, but I'm not able to read it.\n");
+ } else {
+- priv->x_min = (resp[0] << 5) | ((resp[1] & 0x0f) << 1);
+- priv->y_min = (resp[2] << 5) | ((resp[1] & 0xf0) >> 3);
++ info->x_min = (resp[0] << 5) | ((resp[1] & 0x0f) << 1);
++ info->y_min = (resp[2] << 5) | ((resp[1] & 0xf0) >> 3);
+ psmouse_info(psmouse,
+ "queried min coordinates: x [%d..], y [%d..]\n",
+- priv->x_min, priv->y_min);
++ info->x_min, info->y_min);
+ }
+ }
+
+@@ -443,9 +464,9 @@ static int synaptics_resolution(struct psmouse *psmouse)
+ * Apply quirk(s) if the hardware matches
+ */
+
+-static void synaptics_apply_quirks(struct psmouse *psmouse)
++static void synaptics_apply_quirks(struct psmouse *psmouse,
++ struct synaptics_device_info *info)
+ {
+- struct synaptics_data *priv = psmouse->private;
+ int i;
+
+ for (i = 0; min_max_pnpid_table[i].pnp_ids; i++) {
+@@ -454,41 +475,53 @@ static void synaptics_apply_quirks(struct psmouse *psmouse)
+ continue;
+
+ if (min_max_pnpid_table[i].board_id.min != ANY_BOARD_ID &&
+- priv->board_id < min_max_pnpid_table[i].board_id.min)
++ info->board_id < min_max_pnpid_table[i].board_id.min)
+ continue;
+
+ if (min_max_pnpid_table[i].board_id.max != ANY_BOARD_ID &&
+- priv->board_id > min_max_pnpid_table[i].board_id.max)
++ info->board_id > min_max_pnpid_table[i].board_id.max)
+ continue;
+
+- priv->x_min = min_max_pnpid_table[i].x_min;
+- priv->x_max = min_max_pnpid_table[i].x_max;
+- priv->y_min = min_max_pnpid_table[i].y_min;
+- priv->y_max = min_max_pnpid_table[i].y_max;
++ info->x_min = min_max_pnpid_table[i].x_min;
++ info->x_max = min_max_pnpid_table[i].x_max;
++ info->y_min = min_max_pnpid_table[i].y_min;
++ info->y_max = min_max_pnpid_table[i].y_max;
+ psmouse_info(psmouse,
+ "quirked min/max coordinates: x [%d..%d], y [%d..%d]\n",
+- priv->x_min, priv->x_max,
+- priv->y_min, priv->y_max);
++ info->x_min, info->x_max,
++ info->y_min, info->y_max);
+ break;
+ }
+ }
+
+-static int synaptics_query_hardware(struct psmouse *psmouse)
++static int synaptics_query_hardware(struct psmouse *psmouse,
++ struct synaptics_device_info *info)
+ {
+- if (synaptics_identify(psmouse))
+- return -1;
+- if (synaptics_model_id(psmouse))
+- return -1;
+- if (synaptics_firmware_id(psmouse))
+- return -1;
+- if (synaptics_query_modes(psmouse))
+- return -1;
+- if (synaptics_capability(psmouse))
+- return -1;
+- if (synaptics_resolution(psmouse))
+- return -1;
++ int error;
++
++ error = synaptics_identify(psmouse, info);
++ if (error)
++ return error;
++
++ error = synaptics_model_id(psmouse, info);
++ if (error)
++ return error;
++
++ error = synaptics_firmware_id(psmouse, info);
++ if (error)
++ return error;
+
+- synaptics_apply_quirks(psmouse);
++ error = synaptics_query_modes(psmouse, info);
++ if (error)
++ return error;
++
++ error = synaptics_capability(psmouse, info);
++ if (error)
++ return error;
++
++ error = synaptics_resolution(psmouse, info);
++ if (error)
++ return error;
+
+ return 0;
+ }
+@@ -498,8 +531,8 @@ static int synaptics_set_advanced_gesture_mode(struct psmouse *psmouse)
+ static unsigned char param = 0xc8;
+ struct synaptics_data *priv = psmouse->private;
+
+- if (!(SYN_CAP_ADV_GESTURE(priv->ext_cap_0c) ||
+- SYN_CAP_IMAGE_SENSOR(priv->ext_cap_0c)))
++ if (!(SYN_CAP_ADV_GESTURE(priv->info.ext_cap_0c) ||
++ SYN_CAP_IMAGE_SENSOR(priv->info.ext_cap_0c)))
+ return 0;
+
+ if (psmouse_sliced_command(psmouse, SYN_QUE_MODEL))
+@@ -509,7 +542,7 @@ static int synaptics_set_advanced_gesture_mode(struct psmouse *psmouse)
+ return -1;
+
+ /* Advanced gesture mode also sends multi finger data */
+- priv->capabilities |= BIT(1);
++ priv->info.capabilities |= BIT(1);
+
+ return 0;
+ }
+@@ -525,7 +558,7 @@ static int synaptics_set_mode(struct psmouse *psmouse)
+ priv->mode |= SYN_BIT_DISABLE_GESTURE;
+ if (psmouse->rate >= 80)
+ priv->mode |= SYN_BIT_HIGH_RATE;
+- if (SYN_CAP_EXTENDED(priv->capabilities))
++ if (SYN_CAP_EXTENDED(priv->info.capabilities))
+ priv->mode |= SYN_BIT_W_MODE;
+
+ if (synaptics_mode_cmd(psmouse, priv->mode))
+@@ -693,7 +726,7 @@ static void synaptics_parse_ext_buttons(const unsigned char buf[],
+ struct synaptics_hw_state *hw)
+ {
+ unsigned int ext_bits =
+- (SYN_CAP_MULTI_BUTTON_NO(priv->ext_cap) + 1) >> 1;
++ (SYN_CAP_MULTI_BUTTON_NO(priv->info.ext_cap) + 1) >> 1;
+ unsigned int ext_mask = GENMASK(ext_bits - 1, 0);
+
+ hw->ext_buttons = buf[4] & ext_mask;
+@@ -706,13 +739,13 @@ static int synaptics_parse_hw_state(const unsigned char buf[],
+ {
+ memset(hw, 0, sizeof(struct synaptics_hw_state));
+
+- if (SYN_MODEL_NEWABS(priv->model_id)) {
++ if (SYN_MODEL_NEWABS(priv->info.model_id)) {
+ hw->w = (((buf[0] & 0x30) >> 2) |
+ ((buf[0] & 0x04) >> 1) |
+ ((buf[3] & 0x04) >> 2));
+
+- if ((SYN_CAP_ADV_GESTURE(priv->ext_cap_0c) ||
+- SYN_CAP_IMAGE_SENSOR(priv->ext_cap_0c)) &&
++ if ((SYN_CAP_ADV_GESTURE(priv->info.ext_cap_0c) ||
++ SYN_CAP_IMAGE_SENSOR(priv->info.ext_cap_0c)) &&
+ hw->w == 2) {
+ synaptics_parse_agm(buf, priv, hw);
+ return 1;
+@@ -765,7 +798,7 @@ static int synaptics_parse_hw_state(const unsigned char buf[],
+
+ hw->left = priv->report_press;
+
+- } else if (SYN_CAP_CLICKPAD(priv->ext_cap_0c)) {
++ } else if (SYN_CAP_CLICKPAD(priv->info.ext_cap_0c)) {
+ /*
+ * Clickpad's button is transmitted as middle button,
+ * however, since it is primary button, we will report
+@@ -773,18 +806,18 @@ static int synaptics_parse_hw_state(const unsigned char buf[],
+ */
+ hw->left = ((buf[0] ^ buf[3]) & 0x01) ? 1 : 0;
+
+- } else if (SYN_CAP_MIDDLE_BUTTON(priv->capabilities)) {
++ } else if (SYN_CAP_MIDDLE_BUTTON(priv->info.capabilities)) {
+ hw->middle = ((buf[0] ^ buf[3]) & 0x01) ? 1 : 0;
+ if (hw->w == 2)
+ hw->scroll = (signed char)(buf[1]);
+ }
+
+- if (SYN_CAP_FOUR_BUTTON(priv->capabilities)) {
++ if (SYN_CAP_FOUR_BUTTON(priv->info.capabilities)) {
+ hw->up = ((buf[0] ^ buf[3]) & 0x01) ? 1 : 0;
+ hw->down = ((buf[0] ^ buf[3]) & 0x02) ? 1 : 0;
+ }
+
+- if (SYN_CAP_MULTI_BUTTON_NO(priv->ext_cap) > 0 &&
++ if (SYN_CAP_MULTI_BUTTON_NO(priv->info.ext_cap) > 0 &&
+ ((buf[0] ^ buf[3]) & 0x02)) {
+ synaptics_parse_ext_buttons(buf, priv, hw);
+ }
+@@ -853,19 +886,19 @@ static void synaptics_report_ext_buttons(struct psmouse *psmouse,
+ {
+ struct input_dev *dev = psmouse->dev;
+ struct synaptics_data *priv = psmouse->private;
+- int ext_bits = (SYN_CAP_MULTI_BUTTON_NO(priv->ext_cap) + 1) >> 1;
++ int ext_bits = (SYN_CAP_MULTI_BUTTON_NO(priv->info.ext_cap) + 1) >> 1;
+ int i;
+
+- if (!SYN_CAP_MULTI_BUTTON_NO(priv->ext_cap))
++ if (!SYN_CAP_MULTI_BUTTON_NO(priv->info.ext_cap))
+ return;
+
+ /* Bug in FW 8.1 & 8.2, buttons are reported only when ExtBit is 1 */
+- if ((SYN_ID_FULL(priv->identity) == 0x801 ||
+- SYN_ID_FULL(priv->identity) == 0x802) &&
++ if ((SYN_ID_FULL(priv->info.identity) == 0x801 ||
++ SYN_ID_FULL(priv->info.identity) == 0x802) &&
+ !((psmouse->packet[0] ^ psmouse->packet[3]) & 0x02))
+ return;
+
+- if (!SYN_CAP_EXT_BUTTONS_STICK(priv->ext_cap_10)) {
++ if (!SYN_CAP_EXT_BUTTONS_STICK(priv->info.ext_cap_10)) {
+ for (i = 0; i < ext_bits; i++) {
+ input_report_key(dev, BTN_0 + 2 * i,
+ hw->ext_buttons & (1 << i));
+@@ -903,10 +936,10 @@ static void synaptics_report_buttons(struct psmouse *psmouse,
+ input_report_key(dev, BTN_LEFT, hw->left);
+ input_report_key(dev, BTN_RIGHT, hw->right);
+
+- if (SYN_CAP_MIDDLE_BUTTON(priv->capabilities))
++ if (SYN_CAP_MIDDLE_BUTTON(priv->info.capabilities))
+ input_report_key(dev, BTN_MIDDLE, hw->middle);
+
+- if (SYN_CAP_FOUR_BUTTON(priv->capabilities)) {
++ if (SYN_CAP_FOUR_BUTTON(priv->info.capabilities)) {
+ input_report_key(dev, BTN_FORWARD, hw->up);
+ input_report_key(dev, BTN_BACK, hw->down);
+ }
+@@ -931,7 +964,7 @@ static void synaptics_report_mt_data(struct psmouse *psmouse,
+ pos[i].y = synaptics_invert_y(hw[i]->y);
+ }
+
+- input_mt_assign_slots(dev, slot, pos, nsemi, DMAX * priv->x_res);
++ input_mt_assign_slots(dev, slot, pos, nsemi, DMAX * priv->info.x_res);
+
+ for (i = 0; i < nsemi; i++) {
+ input_mt_slot(dev, slot[i]);
+@@ -993,6 +1026,7 @@ static void synaptics_process_packet(struct psmouse *psmouse)
+ {
+ struct input_dev *dev = psmouse->dev;
+ struct synaptics_data *priv = psmouse->private;
++ struct synaptics_device_info *info = &priv->info;
+ struct synaptics_hw_state hw;
+ int num_fingers;
+ int finger_width;
+@@ -1000,7 +1034,7 @@ static void synaptics_process_packet(struct psmouse *psmouse)
+ if (synaptics_parse_hw_state(psmouse->packet, priv, &hw))
+ return;
+
+- if (SYN_CAP_IMAGE_SENSOR(priv->ext_cap_0c)) {
++ if (SYN_CAP_IMAGE_SENSOR(info->ext_cap_0c)) {
+ synaptics_image_sensor_process(psmouse, &hw);
+ return;
+ }
+@@ -1028,18 +1062,18 @@ static void synaptics_process_packet(struct psmouse *psmouse)
+ if (hw.z > 0 && hw.x > 1) {
+ num_fingers = 1;
+ finger_width = 5;
+- if (SYN_CAP_EXTENDED(priv->capabilities)) {
++ if (SYN_CAP_EXTENDED(info->capabilities)) {
+ switch (hw.w) {
+ case 0 ... 1:
+- if (SYN_CAP_MULTIFINGER(priv->capabilities))
++ if (SYN_CAP_MULTIFINGER(info->capabilities))
+ num_fingers = hw.w + 2;
+ break;
+ case 2:
+- if (SYN_MODEL_PEN(priv->model_id))
++ if (SYN_MODEL_PEN(info->model_id))
+ ; /* Nothing, treat a pen as a single finger */
+ break;
+ case 4 ... 15:
+- if (SYN_CAP_PALMDETECT(priv->capabilities))
++ if (SYN_CAP_PALMDETECT(info->capabilities))
+ finger_width = hw.w;
+ break;
+ }
+@@ -1054,7 +1088,7 @@ static void synaptics_process_packet(struct psmouse *psmouse)
+ return;
+ }
+
+- if (SYN_CAP_ADV_GESTURE(priv->ext_cap_0c))
++ if (SYN_CAP_ADV_GESTURE(info->ext_cap_0c))
+ synaptics_report_semi_mt_data(dev, &hw, &priv->agm,
+ num_fingers);
+
+@@ -1071,11 +1105,11 @@ static void synaptics_process_packet(struct psmouse *psmouse)
+ }
+ input_report_abs(dev, ABS_PRESSURE, hw.z);
+
+- if (SYN_CAP_PALMDETECT(priv->capabilities))
++ if (SYN_CAP_PALMDETECT(info->capabilities))
+ input_report_abs(dev, ABS_TOOL_WIDTH, finger_width);
+
+ input_report_key(dev, BTN_TOOL_FINGER, num_fingers == 1);
+- if (SYN_CAP_MULTIFINGER(priv->capabilities)) {
++ if (SYN_CAP_MULTIFINGER(info->capabilities)) {
+ input_report_key(dev, BTN_TOOL_DOUBLETAP, num_fingers == 2);
+ input_report_key(dev, BTN_TOOL_TRIPLETAP, num_fingers == 3);
+ }
+@@ -1137,7 +1171,7 @@ static psmouse_ret_t synaptics_process_byte(struct psmouse *psmouse)
+ if (unlikely(priv->pkt_type == SYN_NEWABS))
+ priv->pkt_type = synaptics_detect_pkt_type(psmouse);
+
+- if (SYN_CAP_PASS_THROUGH(priv->capabilities) &&
++ if (SYN_CAP_PASS_THROUGH(priv->info.capabilities) &&
+ synaptics_is_pt_packet(psmouse->packet)) {
+ if (priv->pt_port)
+ synaptics_pass_pt_packet(priv->pt_port,
+@@ -1156,26 +1190,27 @@ static psmouse_ret_t synaptics_process_byte(struct psmouse *psmouse)
+ * Driver initialization/cleanup functions
+ ****************************************************************************/
+ static void set_abs_position_params(struct input_dev *dev,
+- struct synaptics_data *priv, int x_code,
+- int y_code)
++ struct synaptics_device_info *info,
++ int x_code, int y_code)
+ {
+- int x_min = priv->x_min ?: XMIN_NOMINAL;
+- int x_max = priv->x_max ?: XMAX_NOMINAL;
+- int y_min = priv->y_min ?: YMIN_NOMINAL;
+- int y_max = priv->y_max ?: YMAX_NOMINAL;
+- int fuzz = SYN_CAP_REDUCED_FILTERING(priv->ext_cap_0c) ?
++ int x_min = info->x_min ?: XMIN_NOMINAL;
++ int x_max = info->x_max ?: XMAX_NOMINAL;
++ int y_min = info->y_min ?: YMIN_NOMINAL;
++ int y_max = info->y_max ?: YMAX_NOMINAL;
++ int fuzz = SYN_CAP_REDUCED_FILTERING(info->ext_cap_0c) ?
+ SYN_REDUCED_FILTER_FUZZ : 0;
+
+ input_set_abs_params(dev, x_code, x_min, x_max, fuzz, 0);
+ input_set_abs_params(dev, y_code, y_min, y_max, fuzz, 0);
+- input_abs_set_res(dev, x_code, priv->x_res);
+- input_abs_set_res(dev, y_code, priv->y_res);
++ input_abs_set_res(dev, x_code, info->x_res);
++ input_abs_set_res(dev, y_code, info->y_res);
+ }
+
+ static void set_input_params(struct psmouse *psmouse,
+ struct synaptics_data *priv)
+ {
+ struct input_dev *dev = psmouse->dev;
++ struct synaptics_device_info *info = &priv->info;
+ int i;
+
+ /* Things that apply to both modes */
+@@ -1184,7 +1219,7 @@ static void set_input_params(struct psmouse *psmouse,
+ __set_bit(BTN_LEFT, dev->keybit);
+ __set_bit(BTN_RIGHT, dev->keybit);
+
+- if (SYN_CAP_MIDDLE_BUTTON(priv->capabilities))
++ if (SYN_CAP_MIDDLE_BUTTON(info->capabilities))
+ __set_bit(BTN_MIDDLE, dev->keybit);
+
+ if (!priv->absolute_mode) {
+@@ -1197,15 +1232,15 @@ static void set_input_params(struct psmouse *psmouse,
+
+ /* Absolute mode */
+ __set_bit(EV_ABS, dev->evbit);
+- set_abs_position_params(dev, priv, ABS_X, ABS_Y);
++ set_abs_position_params(dev, &priv->info, ABS_X, ABS_Y);
+ input_set_abs_params(dev, ABS_PRESSURE, 0, 255, 0, 0);
+
+ if (cr48_profile_sensor)
+ input_set_abs_params(dev, ABS_MT_PRESSURE, 0, 255, 0, 0);
+
+- if (SYN_CAP_IMAGE_SENSOR(priv->ext_cap_0c)) {
+- set_abs_position_params(dev, priv, ABS_MT_POSITION_X,
+- ABS_MT_POSITION_Y);
++ if (SYN_CAP_IMAGE_SENSOR(info->ext_cap_0c)) {
++ set_abs_position_params(dev, info,
++ ABS_MT_POSITION_X, ABS_MT_POSITION_Y);
+ /* Image sensors can report per-contact pressure */
+ input_set_abs_params(dev, ABS_MT_PRESSURE, 0, 255, 0, 0);
+ input_mt_init_slots(dev, 2, INPUT_MT_POINTER | INPUT_MT_TRACK);
+@@ -1213,9 +1248,9 @@ static void set_input_params(struct psmouse *psmouse,
+ /* Image sensors can signal 4 and 5 finger clicks */
+ __set_bit(BTN_TOOL_QUADTAP, dev->keybit);
+ __set_bit(BTN_TOOL_QUINTTAP, dev->keybit);
+- } else if (SYN_CAP_ADV_GESTURE(priv->ext_cap_0c)) {
+- set_abs_position_params(dev, priv, ABS_MT_POSITION_X,
+- ABS_MT_POSITION_Y);
++ } else if (SYN_CAP_ADV_GESTURE(info->ext_cap_0c)) {
++ set_abs_position_params(dev, info,
++ ABS_MT_POSITION_X, ABS_MT_POSITION_Y);
+ /*
+ * Profile sensor in CR-48 tracks contacts reasonably well,
+ * other non-image sensors with AGM use semi-mt.
+@@ -1226,35 +1261,35 @@ static void set_input_params(struct psmouse *psmouse,
+ INPUT_MT_TRACK : INPUT_MT_SEMI_MT));
+ }
+
+- if (SYN_CAP_PALMDETECT(priv->capabilities))
++ if (SYN_CAP_PALMDETECT(info->capabilities))
+ input_set_abs_params(dev, ABS_TOOL_WIDTH, 0, 15, 0, 0);
+
+ __set_bit(BTN_TOUCH, dev->keybit);
+ __set_bit(BTN_TOOL_FINGER, dev->keybit);
+
+- if (SYN_CAP_MULTIFINGER(priv->capabilities)) {
++ if (SYN_CAP_MULTIFINGER(info->capabilities)) {
+ __set_bit(BTN_TOOL_DOUBLETAP, dev->keybit);
+ __set_bit(BTN_TOOL_TRIPLETAP, dev->keybit);
+ }
+
+- if (SYN_CAP_FOUR_BUTTON(priv->capabilities) ||
+- SYN_CAP_MIDDLE_BUTTON(priv->capabilities)) {
++ if (SYN_CAP_FOUR_BUTTON(info->capabilities) ||
++ SYN_CAP_MIDDLE_BUTTON(info->capabilities)) {
+ __set_bit(BTN_FORWARD, dev->keybit);
+ __set_bit(BTN_BACK, dev->keybit);
+ }
+
+- if (!SYN_CAP_EXT_BUTTONS_STICK(priv->ext_cap_10))
+- for (i = 0; i < SYN_CAP_MULTI_BUTTON_NO(priv->ext_cap); i++)
++ if (!SYN_CAP_EXT_BUTTONS_STICK(info->ext_cap_10))
++ for (i = 0; i < SYN_CAP_MULTI_BUTTON_NO(info->ext_cap); i++)
+ __set_bit(BTN_0 + i, dev->keybit);
+
+ __clear_bit(EV_REL, dev->evbit);
+ __clear_bit(REL_X, dev->relbit);
+ __clear_bit(REL_Y, dev->relbit);
+
+- if (SYN_CAP_CLICKPAD(priv->ext_cap_0c)) {
++ if (SYN_CAP_CLICKPAD(info->ext_cap_0c)) {
+ __set_bit(INPUT_PROP_BUTTONPAD, dev->propbit);
+ if (psmouse_matches_pnp_id(psmouse, topbuttonpad_pnp_ids) &&
+- !SYN_CAP_EXT_BUTTONS_STICK(priv->ext_cap_10))
++ !SYN_CAP_EXT_BUTTONS_STICK(info->ext_cap_10))
+ __set_bit(INPUT_PROP_TOPBUTTONPAD, dev->propbit);
+ /* Clickpads report only left button */
+ __clear_bit(BTN_RIGHT, dev->keybit);
+@@ -1308,7 +1343,8 @@ static void synaptics_disconnect(struct psmouse *psmouse)
+ {
+ struct synaptics_data *priv = psmouse->private;
+
+- if (!priv->absolute_mode && SYN_ID_DISGEST_SUPPORTED(priv->identity))
++ if (!priv->absolute_mode &&
++ SYN_ID_DISGEST_SUPPORTED(priv->info.identity))
+ device_remove_file(&psmouse->ps2dev.serio->dev,
+ &psmouse_attr_disable_gesture.dattr);
+
+@@ -1320,7 +1356,7 @@ static void synaptics_disconnect(struct psmouse *psmouse)
+ static int synaptics_reconnect(struct psmouse *psmouse)
+ {
+ struct synaptics_data *priv = psmouse->private;
+- struct synaptics_data old_priv = *priv;
++ struct synaptics_device_info info;
+ unsigned char param[2];
+ int retry = 0;
+ int error;
+@@ -1347,7 +1383,7 @@ static int synaptics_reconnect(struct psmouse *psmouse)
+ if (retry > 1)
+ psmouse_dbg(psmouse, "reconnected after %d tries\n", retry);
+
+- if (synaptics_query_hardware(psmouse)) {
++ if (synaptics_query_hardware(psmouse, &info)) {
+ psmouse_err(psmouse, "Unable to query device.\n");
+ return -1;
+ }
+@@ -1357,16 +1393,16 @@ static int synaptics_reconnect(struct psmouse *psmouse)
+ return -1;
+ }
+
+- if (old_priv.identity != priv->identity ||
+- old_priv.model_id != priv->model_id ||
+- old_priv.capabilities != priv->capabilities ||
+- old_priv.ext_cap != priv->ext_cap) {
++ if (info.identity != priv->info.identity ||
++ info.model_id != priv->info.model_id ||
++ info.capabilities != priv->info.capabilities ||
++ info.ext_cap != priv->info.ext_cap) {
+ psmouse_err(psmouse,
+- "hardware appears to be different: id(%ld-%ld), model(%ld-%ld), caps(%lx-%lx), ext(%lx-%lx).\n",
+- old_priv.identity, priv->identity,
+- old_priv.model_id, priv->model_id,
+- old_priv.capabilities, priv->capabilities,
+- old_priv.ext_cap, priv->ext_cap);
++ "hardware appears to be different: id(%u-%u), model(%u-%u), caps(%x-%x), ext(%x-%x).\n",
++ priv->info.identity, info.identity,
++ priv->info.model_id, info.model_id,
++ priv->info.capabilities, info.capabilities,
++ priv->info.ext_cap, info.ext_cap);
+ return -1;
+ }
+
+@@ -1450,6 +1486,7 @@ void __init synaptics_module_init(void)
+ static int __synaptics_init(struct psmouse *psmouse, bool absolute_mode)
+ {
+ struct synaptics_data *priv;
++ struct synaptics_device_info *info;
+ int err = -1;
+
+ /*
+@@ -1468,15 +1505,19 @@ static int __synaptics_init(struct psmouse *psmouse, bool absolute_mode)
+ if (!priv)
+ return -ENOMEM;
+
++ info = &priv->info;
++
+ psmouse_reset(psmouse);
+
+- if (synaptics_query_hardware(psmouse)) {
++ if (synaptics_query_hardware(psmouse, info)) {
+ psmouse_err(psmouse, "Unable to query device.\n");
+ goto init_fail;
+ }
+
++ synaptics_apply_quirks(psmouse, info);
++
+ priv->absolute_mode = absolute_mode;
+- if (SYN_ID_DISGEST_SUPPORTED(priv->identity))
++ if (SYN_ID_DISGEST_SUPPORTED(info->identity))
+ priv->disable_gesture = true;
+
+ /*
+@@ -1490,15 +1531,16 @@ static int __synaptics_init(struct psmouse *psmouse, bool absolute_mode)
+ goto init_fail;
+ }
+
+- priv->pkt_type = SYN_MODEL_NEWABS(priv->model_id) ? SYN_NEWABS : SYN_OLDABS;
++ priv->pkt_type = SYN_MODEL_NEWABS(info->model_id) ?
++ SYN_NEWABS : SYN_OLDABS;
+
+ psmouse_info(psmouse,
+- "Touchpad model: %ld, fw: %ld.%ld, id: %#lx, caps: %#lx/%#lx/%#lx/%#lx, board id: %lu, fw id: %lu\n",
+- SYN_ID_MODEL(priv->identity),
+- SYN_ID_MAJOR(priv->identity), SYN_ID_MINOR(priv->identity),
+- priv->model_id,
+- priv->capabilities, priv->ext_cap, priv->ext_cap_0c,
+- priv->ext_cap_10, priv->board_id, priv->firmware_id);
++ "Touchpad model: %u, fw: %u.%u, id: %#x, caps: %#x/%#x/%#x/%#x, board id: %u, fw id: %u\n",
++ SYN_ID_MODEL(info->identity),
++ SYN_ID_MAJOR(info->identity), SYN_ID_MINOR(info->identity),
++ info->model_id,
++ info->capabilities, info->ext_cap, info->ext_cap_0c,
++ info->ext_cap_10, info->board_id, info->firmware_id);
+
+ set_input_params(psmouse, priv);
+
+@@ -1509,8 +1551,8 @@ static int __synaptics_init(struct psmouse *psmouse, bool absolute_mode)
+ * Hardware info bits seem to be good candidates as they
+ * are documented to be for Synaptics corp. internal use.
+ */
+- psmouse->model = ((priv->model_id & 0x00ff0000) >> 8) |
+- (priv->model_id & 0x000000ff);
++ psmouse->model = ((info->model_id & 0x00ff0000) >> 8) |
++ (info->model_id & 0x000000ff);
+
+ if (absolute_mode) {
+ psmouse->protocol_handler = synaptics_process_byte;
+@@ -1528,7 +1570,7 @@ static int __synaptics_init(struct psmouse *psmouse, bool absolute_mode)
+ /* Synaptics can usually stay in sync without extra help */
+ psmouse->resync_time = 0;
+
+- if (SYN_CAP_PASS_THROUGH(priv->capabilities))
++ if (SYN_CAP_PASS_THROUGH(info->capabilities))
+ synaptics_pt_create(psmouse);
+
+ /*
+@@ -1543,7 +1585,7 @@ static int __synaptics_init(struct psmouse *psmouse, bool absolute_mode)
+ psmouse->rate = 40;
+ }
+
+- if (!priv->absolute_mode && SYN_ID_DISGEST_SUPPORTED(priv->identity)) {
++ if (!priv->absolute_mode && SYN_ID_DISGEST_SUPPORTED(info->identity)) {
+ err = device_create_file(&psmouse->ps2dev.serio->dev,
+ &psmouse_attr_disable_gesture.dattr);
+ if (err) {
+diff --git a/drivers/input/mouse/synaptics.h b/drivers/input/mouse/synaptics.h
+index b76bb7a..d9b824f 100644
+--- a/drivers/input/mouse/synaptics.h
++++ b/drivers/input/mouse/synaptics.h
+@@ -161,19 +161,23 @@ struct synaptics_hw_state {
+ signed char scroll;
+ };
+
++/* Data read from the touchpad */
++struct synaptics_device_info {
++ u32 model_id; /* Model-ID */
++ u32 firmware_id; /* Firmware-ID */
++ u32 board_id; /* Board-ID */
++ u32 capabilities; /* Capabilities */
++ u32 ext_cap; /* Extended Capabilities */
++ u32 ext_cap_0c; /* Ext Caps from 0x0c query */
++ u32 ext_cap_10; /* Ext Caps from 0x10 query */
++ u32 identity; /* Identification */
++ u32 x_res, y_res; /* X/Y resolution in units/mm */
++ u32 x_max, y_max; /* Max coordinates (from FW) */
++ u32 x_min, y_min; /* Min coordinates (from FW) */
++};
++
+ struct synaptics_data {
+- /* Data read from the touchpad */
+- unsigned long int model_id; /* Model-ID */
+- unsigned long int firmware_id; /* Firmware-ID */
+- unsigned long int board_id; /* Board-ID */
+- unsigned long int capabilities; /* Capabilities */
+- unsigned long int ext_cap; /* Extended Capabilities */
+- unsigned long int ext_cap_0c; /* Ext Caps from 0x0c query */
+- unsigned long int ext_cap_10; /* Ext Caps from 0x10 query */
+- unsigned long int identity; /* Identification */
+- unsigned int x_res, y_res; /* X/Y resolution in units/mm */
+- unsigned int x_max, y_max; /* Max coordinates (from FW) */
+- unsigned int x_min, y_min; /* Min coordinates (from FW) */
++ struct synaptics_device_info info;
+
+ unsigned char pkt_type; /* packet type - old, new, etc */
+ unsigned char mode; /* current mode byte */
+--
+2.9.3
+
--
2.9.3