From mboxrd@z Thu Jan 1 00:00:00 1970 Return-Path: Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1754020AbcHRJls (ORCPT ); Thu, 18 Aug 2016 05:41:48 -0400 Received: from mx1.redhat.com ([209.132.183.28]:43139 "EHLO mx1.redhat.com" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1753240AbcHRJlo (ORCPT ); Thu, 18 Aug 2016 05:41:44 -0400 From: Benjamin Tissoires To: Dmitry Torokhov , Lyude Paul , Andrew Duggan , Christopher Heiny , Dennis Wassenberg Cc: Peter Hutterer , linux-kernel@vger.kernel.org, linux-input@vger.kernel.org Subject: [PATCH 10/11] Input: synaptics-rmi4 - smbus: call psmouse_deactivate before binding/resume Date: Thu, 18 Aug 2016 11:24:48 +0200 Message-Id: <1471512289-10648-11-git-send-email-benjamin.tissoires@redhat.com> In-Reply-To: <1471512289-10648-1-git-send-email-benjamin.tissoires@redhat.com> References: <1471512289-10648-1-git-send-email-benjamin.tissoires@redhat.com> X-Greylist: Sender IP whitelisted, not delayed by milter-greylist-4.5.16 (mx1.redhat.com [10.5.110.32]); Thu, 18 Aug 2016 09:25:18 +0000 (UTC) Sender: linux-kernel-owner@vger.kernel.org List-ID: X-Mailing-List: linux-kernel@vger.kernel.org The SMBus Synaptics devices enumerated as PS/2 devices have the problem of being deaf to I2C if the touchpad has been fully initialized over PS/2 (psmouse_activate being called). A simple PS/2 deactivate command is enough to make it back alive. To make sure the pass-through device does not interfere, we also remove it from serio while using InterTouch. Add a platform_data callback to reset the PS/2 device and prevent it to be asynchronously re-enabled at resume. Signed-off-by: Benjamin Tissoires --- drivers/input/mouse/psmouse-base.c | 3 +++ drivers/input/mouse/psmouse.h | 1 + drivers/input/mouse/synaptics.c | 32 ++++++++++++++++++++++++++++++++ drivers/input/rmi4/rmi_smbus.c | 30 ++++++++++++++++++++---------- include/linux/rmi.h | 13 +++++++++++++ 5 files changed, 69 insertions(+), 10 deletions(-) diff --git a/drivers/input/mouse/psmouse-base.c b/drivers/input/mouse/psmouse-base.c index fa2d700..5d84e0c 100644 --- a/drivers/input/mouse/psmouse-base.c +++ b/drivers/input/mouse/psmouse-base.c @@ -1607,6 +1607,9 @@ static int psmouse_reconnect(struct serio *serio) unsigned char type; int rc = -1; + if (psmouse->ignore_reconnect) + return 0; + mutex_lock(&psmouse_mutex); if (serio->parent && serio->id.type == SERIO_PS_PSTHRU) { diff --git a/drivers/input/mouse/psmouse.h b/drivers/input/mouse/psmouse.h index e0ca6cd..e9dc1a1 100644 --- a/drivers/input/mouse/psmouse.h +++ b/drivers/input/mouse/psmouse.h @@ -81,6 +81,7 @@ struct psmouse { void (*pt_activate)(struct psmouse *psmouse); void (*pt_deactivate)(struct psmouse *psmouse); + bool ignore_reconnect; }; enum psmouse_type { diff --git a/drivers/input/mouse/synaptics.c b/drivers/input/mouse/synaptics.c index eba6358..e1a84a6 100644 --- a/drivers/input/mouse/synaptics.c +++ b/drivers/input/mouse/synaptics.c @@ -245,6 +245,30 @@ static const char * const smbus_pnp_ids[] = { static int rmi4_id; +static void synaptics_pt_create(struct psmouse *psmouse); + +static int synaptics_intertouch_enable(void *data, bool value) +{ + struct psmouse *psmouse = data; + struct synaptics_data *priv = psmouse->private; + + if (!psmouse) + return 0; + + psmouse->ignore_reconnect = value; + + if (value) { + serio_unregister_child_port(psmouse->ps2dev.serio); + psmouse_deactivate(psmouse); + } else { + psmouse_activate(psmouse); + if (SYN_CAP_PASS_THROUGH(priv->capabilities)) + synaptics_pt_create(psmouse); + } + + return 0; +} + static int synaptics_create_intertouch(struct psmouse *psmouse) { struct synaptics_data *priv = psmouse->private; @@ -261,6 +285,8 @@ static int synaptics_create_intertouch(struct psmouse *psmouse) .trackstick_buttons = !!SYN_CAP_EXT_BUTTONS_STICK(priv->ext_cap_10), }, + .transport_enable = synaptics_intertouch_enable, + .transport_data = psmouse, }; if (priv->intertouch) @@ -1397,6 +1423,12 @@ static void synaptics_disconnect(struct psmouse *psmouse) &psmouse_attr_disable_gesture.dattr); if (priv->intertouch) { + struct rmi_device_platform_data *pdata; + + /* reset transport_data as it will get eventually freed */ + pdata = priv->intertouch->dev.platform_data; + pdata->transport_data = NULL; + platform_device_unregister(priv->intertouch); priv->intertouch = NULL; } diff --git a/drivers/input/rmi4/rmi_smbus.c b/drivers/input/rmi4/rmi_smbus.c index 4d6f228..023dbd5 100644 --- a/drivers/input/rmi4/rmi_smbus.c +++ b/drivers/input/rmi4/rmi_smbus.c @@ -244,8 +244,15 @@ static void rmi_smb_clear_state(struct rmi_smb_xport *rmi_smb) static int rmi_smb_enable_smbus_mode(struct rmi_smb_xport *rmi_smb) { + struct rmi_device_platform_data *pdata; int retval; + pdata = dev_get_platdata(&rmi_smb->client->dev); + + retval = rmi_transport_enable(pdata, true); + if (retval) + return retval; + /* we need to get the smbus version to activate the touchpad */ retval = rmi_smb_get_version(rmi_smb); if (retval < 0) @@ -261,13 +268,6 @@ static int rmi_smb_reset(struct rmi_transport_dev *xport, u16 reset_addr) rmi_smb_clear_state(rmi_smb); - /* - * we do not call the actual reset command, it has to be handled in - * PS/2 or there will be races between PS/2 and SMBus. - * PS/2 should ensure that a psmouse_reset is called before - * intializing the device and after it has been removed to be in a known - * state. - */ return rmi_smb_enable_smbus_mode(rmi_smb); } @@ -334,9 +334,13 @@ static int rmi_smb_probe(struct i2c_client *client, rmi_smb->xport.proto_name = "smb2"; rmi_smb->xport.ops = &rmi_smb_ops; + retval = rmi_transport_enable(pdata, true); + if (retval) + return retval; + retval = rmi_smb_get_version(rmi_smb); if (retval < 0) - return retval; + goto fail; smbus_version = retval; rmi_dbg(RMI_DEBUG_XPORT, &client->dev, "Smbus version is %d", @@ -345,7 +349,8 @@ static int rmi_smb_probe(struct i2c_client *client, if (smbus_version != 2) { dev_err(&client->dev, "Unrecognized SMB version %d.\n", smbus_version); - return -ENODEV; + retval = -ENODEV; + goto fail; } i2c_set_clientdata(client, rmi_smb); @@ -355,20 +360,25 @@ static int rmi_smb_probe(struct i2c_client *client, dev_err(&client->dev, "Failed to register transport driver at 0x%.2X.\n", client->addr); i2c_set_clientdata(client, NULL); - return retval; + goto fail; } dev_info(&client->dev, "registered rmi smb driver at %#04x.\n", client->addr); return 0; +fail: + rmi_transport_enable(pdata, false); + return retval; } static int rmi_smb_remove(struct i2c_client *client) { + struct rmi_device_platform_data *pdata = dev_get_platdata(&client->dev); struct rmi_smb_xport *rmi_smb = i2c_get_clientdata(client); rmi_unregister_transport_device(&rmi_smb->xport); + rmi_transport_enable(pdata, false); return 0; } diff --git a/include/linux/rmi.h b/include/linux/rmi.h index 4a071e8..02e1dae 100644 --- a/include/linux/rmi.h +++ b/include/linux/rmi.h @@ -214,6 +214,9 @@ struct rmi_device_platform_data { struct rmi_2d_sensor_platform_data sensor_pdata; struct rmi_f01_power_management power_management; struct rmi_f30_data f30_data; + + int (*transport_enable)(void*, bool); + void *transport_data; }; /** @@ -350,6 +353,16 @@ struct rmi_driver_data { void *data; }; +static inline int rmi_transport_enable(struct rmi_device_platform_data *pdata, + bool value) +{ + if (!pdata->transport_enable) + return 0; + + return pdata->transport_enable(pdata->transport_data, value); +} + + int rmi_register_transport_device(struct rmi_transport_dev *xport); void rmi_unregister_transport_device(struct rmi_transport_dev *xport); int rmi_process_interrupt_requests(struct rmi_device *rmi_dev); -- 2.5.5