diff mbox

[RESEND] Lenovo Yoga 900 touchpad issues

Message ID 20151218153802.GL1762@lahna.fi.intel.com (mailing list archive)
State New, archived
Delegated to: Jiri Kosina
Headers show

Commit Message

Mika Westerberg Dec. 18, 2015, 3:38 p.m. UTC
On Fri, Dec 18, 2015 at 04:42:09PM +0200, Mika Westerberg wrote:
> On another occasion the faulty input report was received immediatelly
> after we call i2c_hid_set_power().
> 
> With below hack patch suspend/resume works fine but it is far from being
> suitable for merging. Still, it would be nice if you could try it out
> and see if it helps in your case.

Actually it looks like we can handle this by just ignoring input reports
while we are resetting the device. Following seems to work as well.

--
To unsubscribe from this list: send the line "unsubscribe linux-input" in
the body of a message to majordomo@vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html

Comments

Benjamin Tissoires Dec. 18, 2015, 4:10 p.m. UTC | #1
On Dec 18 2015 or thereabouts, Mika Westerberg wrote:
> On Fri, Dec 18, 2015 at 04:42:09PM +0200, Mika Westerberg wrote:
> > On another occasion the faulty input report was received immediatelly
> > after we call i2c_hid_set_power().
> > 
> > With below hack patch suspend/resume works fine but it is far from being
> > suitable for merging. Still, it would be nice if you could try it out
> > and see if it helps in your case.
> 
> Actually it looks like we can handle this by just ignoring input reports
> while we are resetting the device. Following seems to work as well.

Yes, I like this one better. I think, it is still prone to races, but
it's a much better option.

After turning this around, I think I finally got what was going on (and
yes, it's basically a race that should have been caught long ago):
- during resume, the i2c-hid driver calls reset, which is a long (few
  ms) operation.
- hid-multitouch is also called during resume, and right after i2c-hid
- hid-multiotuch immediately emits for touchpads a set input mode (this
  was unseen with touchscreens because they do not have to be switched
  into a proper mode)
- there is a race between hid-multitouch accessing the features while
  the device is still resetting. And our reset interrupt never gets to
  us because there was an other operation in progress to request/set
  reports

So the actual fix would be to make hid-multitouch wait until we reset
i2c-hid.

This should be done by either a mutex or a spinlock in
i2c_hid_output_raw_report() and i2c_hid_resume() that would protect a
flag set during suspend and cleared after resume.

(not sure if this is clear :-P )

Cheers,
Benjamin

> 
> diff --git a/drivers/hid/i2c-hid/i2c-hid.c b/drivers/hid/i2c-hid/i2c-hid.c
> index 10bd8e6..eeaf33a 100644
> --- a/drivers/hid/i2c-hid/i2c-hid.c
> +++ b/drivers/hid/i2c-hid/i2c-hid.c
> @@ -352,13 +352,24 @@ static int i2c_hid_set_power(struct i2c_client *client, int power_state)
>  static int i2c_hid_hwreset(struct i2c_client *client)
>  {
>  	struct i2c_hid *ihid = i2c_get_clientdata(client);
> +	bool started;
>  	int ret;
>  
>  	i2c_hid_dbg(ihid, "%s\n", __func__);
>  
> +	/*
> +	 * Some touchpads seem to send input reports once their power is
> +	 * turned back on after resume. This confuses our reset logic
> +	 * below.
> +	 *
> +	 * Prevent handling any input reports while we are resetting the
> +	 * device.
> +	 */
> +	started = test_and_clear_bit(I2C_HID_STARTED, &ihid->flags);
> +
>  	ret = i2c_hid_set_power(client, I2C_HID_PWR_ON);
>  	if (ret)
> -		return ret;
> +		goto out;
>  
>  	i2c_hid_dbg(ihid, "resetting...\n");
>  
> @@ -366,10 +377,14 @@ static int i2c_hid_hwreset(struct i2c_client *client)
>  	if (ret) {
>  		dev_err(&client->dev, "failed to reset device.\n");
>  		i2c_hid_set_power(client, I2C_HID_PWR_SLEEP);
> -		return ret;
> +		goto out;
>  	}
>  
> -	return 0;
> +out:
> +	if (started)
> +		set_bit(I2C_HID_STARTED, &ihid->flags);
> +
> +	return ret;
>  }
>  
>  static void i2c_hid_get_input(struct i2c_hid *ihid)
--
To unsubscribe from this list: send the line "unsubscribe linux-input" in
the body of a message to majordomo@vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html
Nish Aravamudan Dec. 18, 2015, 5:54 p.m. UTC | #2
On Fri, Dec 18, 2015 at 7:38 AM, Mika Westerberg
<mika.westerberg@linux.intel.com> wrote:
> On Fri, Dec 18, 2015 at 04:42:09PM +0200, Mika Westerberg wrote:
>> On another occasion the faulty input report was received immediatelly
>> after we call i2c_hid_set_power().
>>
>> With below hack patch suspend/resume works fine but it is far from being
>> suitable for merging. Still, it would be nice if you could try it out
>> and see if it helps in your case.
>
> Actually it looks like we can handle this by just ignoring input reports
> while we are resetting the device. Following seems to work as well.

FWIW,

Tested-by: Nishanth Aravamudan <nish.aravamudan@gmail.com>

and if you can Cc me on any follow-up/alternative patches, I will be
happy to test those too!

Thanks!
-Nish

> diff --git a/drivers/hid/i2c-hid/i2c-hid.c b/drivers/hid/i2c-hid/i2c-hid.c
> index 10bd8e6..eeaf33a 100644
> --- a/drivers/hid/i2c-hid/i2c-hid.c
> +++ b/drivers/hid/i2c-hid/i2c-hid.c
> @@ -352,13 +352,24 @@ static int i2c_hid_set_power(struct i2c_client *client, int power_state)
>  static int i2c_hid_hwreset(struct i2c_client *client)
>  {
>         struct i2c_hid *ihid = i2c_get_clientdata(client);
> +       bool started;
>         int ret;
>
>         i2c_hid_dbg(ihid, "%s\n", __func__);
>
> +       /*
> +        * Some touchpads seem to send input reports once their power is
> +        * turned back on after resume. This confuses our reset logic
> +        * below.
> +        *
> +        * Prevent handling any input reports while we are resetting the
> +        * device.
> +        */
> +       started = test_and_clear_bit(I2C_HID_STARTED, &ihid->flags);
> +
>         ret = i2c_hid_set_power(client, I2C_HID_PWR_ON);
>         if (ret)
> -               return ret;
> +               goto out;
>
>         i2c_hid_dbg(ihid, "resetting...\n");
>
> @@ -366,10 +377,14 @@ static int i2c_hid_hwreset(struct i2c_client *client)
>         if (ret) {
>                 dev_err(&client->dev, "failed to reset device.\n");
>                 i2c_hid_set_power(client, I2C_HID_PWR_SLEEP);
> -               return ret;
> +               goto out;
>         }
>
> -       return 0;
> +out:
> +       if (started)
> +               set_bit(I2C_HID_STARTED, &ihid->flags);
> +
> +       return ret;
>  }
>
>  static void i2c_hid_get_input(struct i2c_hid *ihid)
--
To unsubscribe from this list: send the line "unsubscribe linux-input" in
the body of a message to majordomo@vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html
Mika Westerberg Dec. 21, 2015, 11:32 a.m. UTC | #3
On Fri, Dec 18, 2015 at 05:10:31PM +0100, Benjamin Tissoires wrote:
> After turning this around, I think I finally got what was going on (and
> yes, it's basically a race that should have been caught long ago):
> - during resume, the i2c-hid driver calls reset, which is a long (few
>   ms) operation.
> - hid-multitouch is also called during resume, and right after i2c-hid
> - hid-multiotuch immediately emits for touchpads a set input mode (this
>   was unseen with touchscreens because they do not have to be switched
>   into a proper mode)
> - there is a race between hid-multitouch accessing the features while
>   the device is still resetting. And our reset interrupt never gets to
>   us because there was an other operation in progress to request/set
>   reports
> 
> So the actual fix would be to make hid-multitouch wait until we reset
> i2c-hid.

The driver used for touchpad is actually hid-rmi but you are right, it
tries to set rmi mode (or something) during its rmi_post_reset() that
then confuses the i2c-hid driver.

Thanks for the analysis :-)

> This should be done by either a mutex or a spinlock in
> i2c_hid_output_raw_report() and i2c_hid_resume() that would protect a
> flag set during suspend and cleared after resume.

Makes sense. I'll make a patch that does this and submit it soon.
--
To unsubscribe from this list: send the line "unsubscribe linux-input" in
the body of a message to majordomo@vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html
diff mbox

Patch

diff --git a/drivers/hid/i2c-hid/i2c-hid.c b/drivers/hid/i2c-hid/i2c-hid.c
index 10bd8e6..eeaf33a 100644
--- a/drivers/hid/i2c-hid/i2c-hid.c
+++ b/drivers/hid/i2c-hid/i2c-hid.c
@@ -352,13 +352,24 @@  static int i2c_hid_set_power(struct i2c_client *client, int power_state)
 static int i2c_hid_hwreset(struct i2c_client *client)
 {
 	struct i2c_hid *ihid = i2c_get_clientdata(client);
+	bool started;
 	int ret;
 
 	i2c_hid_dbg(ihid, "%s\n", __func__);
 
+	/*
+	 * Some touchpads seem to send input reports once their power is
+	 * turned back on after resume. This confuses our reset logic
+	 * below.
+	 *
+	 * Prevent handling any input reports while we are resetting the
+	 * device.
+	 */
+	started = test_and_clear_bit(I2C_HID_STARTED, &ihid->flags);
+
 	ret = i2c_hid_set_power(client, I2C_HID_PWR_ON);
 	if (ret)
-		return ret;
+		goto out;
 
 	i2c_hid_dbg(ihid, "resetting...\n");
 
@@ -366,10 +377,14 @@  static int i2c_hid_hwreset(struct i2c_client *client)
 	if (ret) {
 		dev_err(&client->dev, "failed to reset device.\n");
 		i2c_hid_set_power(client, I2C_HID_PWR_SLEEP);
-		return ret;
+		goto out;
 	}
 
-	return 0;
+out:
+	if (started)
+		set_bit(I2C_HID_STARTED, &ihid->flags);
+
+	return ret;
 }
 
 static void i2c_hid_get_input(struct i2c_hid *ihid)