Input: elan_i2c - properly wake up touchpad on ASUS laptops
authorKT Liao <kt.liao@emc.com.tw>
Wed, 13 Jul 2016 18:12:12 +0000 (11:12 -0700)
committerGreg Kroah-Hartman <gregkh@linuxfoundation.org>
Sat, 20 Aug 2016 16:09:26 +0000 (18:09 +0200)
commit 2de4fcc64685def3e586856a2dc636df44532395 upstream.

Some ASUS laptops were shipped with touchpads that require to be woken up
first, before trying to switch them into absolute reporting mode, otherwise
touchpad would fail to work while flooding the logs with:

elan_i2c i2c-ELAN1000:00: invalid report id data (1)

Among affected devices are Asus E202SA, N552VW, X456UF, UX305CA, and
others. We detect such devices by checking the IC type and product ID
numbers and adjusting order of operations accordingly.

Signed-off-by: KT Liao <kt.liao@emc.com.tw>
Reported-by: Chris Chiu <chiu@endlessm.com>
Reported-by: Vlad Glagolev <stealth@vaygr.net>
Tested-by: Vlad Glagolev <stealth@vaygr.net>
Signed-off-by: Dmitry Torokhov <dmitry.torokhov@gmail.com>
Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
drivers/input/mouse/elan_i2c_core.c

index 2f589857a0395d8c48f685f47bd1c9ceb8445213..d15b338130213c53489baa600d6dcea1500790d4 100644 (file)
@@ -4,7 +4,8 @@
  * Copyright (c) 2013 ELAN Microelectronics Corp.
  *
  * Author: 林政維 (Duson Lin) <dusonlin@emc.com.tw>
- * Version: 1.6.0
+ * Author: KT Liao <kt.liao@emc.com.tw>
+ * Version: 1.6.2
  *
  * Based on cyapa driver:
  * copyright (c) 2011-2012 Cypress Semiconductor, Inc.
@@ -40,7 +41,7 @@
 #include "elan_i2c.h"
 
 #define DRIVER_NAME            "elan_i2c"
-#define ELAN_DRIVER_VERSION    "1.6.1"
+#define ELAN_DRIVER_VERSION    "1.6.2"
 #define ELAN_VENDOR_ID         0x04f3
 #define ETP_MAX_PRESSURE       255
 #define ETP_FWIDTH_REDUCE      90
@@ -199,9 +200,41 @@ static int elan_sleep(struct elan_tp_data *data)
        return error;
 }
 
+static int elan_query_product(struct elan_tp_data *data)
+{
+       int error;
+
+       error = data->ops->get_product_id(data->client, &data->product_id);
+       if (error)
+               return error;
+
+       error = data->ops->get_sm_version(data->client, &data->ic_type,
+                                         &data->sm_version);
+       if (error)
+               return error;
+
+       return 0;
+}
+
+static int elan_check_ASUS_special_fw(struct elan_tp_data *data)
+{
+       if (data->ic_type != 0x0E)
+               return false;
+
+       switch (data->product_id) {
+       case 0x05 ... 0x07:
+       case 0x09:
+       case 0x13:
+               return true;
+       default:
+               return false;
+       }
+}
+
 static int __elan_initialize(struct elan_tp_data *data)
 {
        struct i2c_client *client = data->client;
+       bool woken_up = false;
        int error;
 
        error = data->ops->initialize(client);
@@ -210,6 +243,27 @@ static int __elan_initialize(struct elan_tp_data *data)
                return error;
        }
 
+       error = elan_query_product(data);
+       if (error)
+               return error;
+
+       /*
+        * Some ASUS devices were shipped with firmware that requires
+        * touchpads to be woken up first, before attempting to switch
+        * them into absolute reporting mode.
+        */
+       if (elan_check_ASUS_special_fw(data)) {
+               error = data->ops->sleep_control(client, false);
+               if (error) {
+                       dev_err(&client->dev,
+                               "failed to wake device up: %d\n", error);
+                       return error;
+               }
+
+               msleep(200);
+               woken_up = true;
+       }
+
        data->mode |= ETP_ENABLE_ABS;
        error = data->ops->set_mode(client, data->mode);
        if (error) {
@@ -218,11 +272,13 @@ static int __elan_initialize(struct elan_tp_data *data)
                return error;
        }
 
-       error = data->ops->sleep_control(client, false);
-       if (error) {
-               dev_err(&client->dev,
-                       "failed to wake device up: %d\n", error);
-               return error;
+       if (!woken_up) {
+               error = data->ops->sleep_control(client, false);
+               if (error) {
+                       dev_err(&client->dev,
+                               "failed to wake device up: %d\n", error);
+                       return error;
+               }
        }
 
        return 0;
@@ -248,10 +304,6 @@ static int elan_query_device_info(struct elan_tp_data *data)
 {
        int error;
 
-       error = data->ops->get_product_id(data->client, &data->product_id);
-       if (error)
-               return error;
-
        error = data->ops->get_version(data->client, false, &data->fw_version);
        if (error)
                return error;
@@ -261,11 +313,6 @@ static int elan_query_device_info(struct elan_tp_data *data)
        if (error)
                return error;
 
-       error = data->ops->get_sm_version(data->client, &data->ic_type,
-                                         &data->sm_version);
-       if (error)
-               return error;
-
        error = data->ops->get_version(data->client, true, &data->iap_version);
        if (error)
                return error;