Input: elantech - add more IC body types to the list
[pandora-kernel.git] / drivers / input / mouse / elantech.c
index e2a9867..537692d 100644 (file)
@@ -11,6 +11,7 @@
  */
 
 #include <linux/delay.h>
+#include <linux/dmi.h>
 #include <linux/slab.h>
 #include <linux/module.h>
 #include <linux/input.h>
@@ -295,7 +296,7 @@ static void elantech_report_semi_mt_data(struct input_dev *dev,
                                         unsigned int x2, unsigned int y2)
 {
        elantech_set_slot(dev, 0, num_fingers != 0, x1, y1);
-       elantech_set_slot(dev, 1, num_fingers == 2, x2, y2);
+       elantech_set_slot(dev, 1, num_fingers >= 2, x2, y2);
 }
 
 /*
@@ -454,8 +455,15 @@ static void elantech_report_absolute_v3(struct psmouse *psmouse,
        input_report_key(dev, BTN_TOOL_FINGER, fingers == 1);
        input_report_key(dev, BTN_TOOL_DOUBLETAP, fingers == 2);
        input_report_key(dev, BTN_TOOL_TRIPLETAP, fingers == 3);
-       input_report_key(dev, BTN_LEFT, packet[0] & 0x01);
-       input_report_key(dev, BTN_RIGHT, packet[0] & 0x02);
+
+       /* For clickpads map both buttons to BTN_LEFT */
+       if (etd->fw_version & 0x001000) {
+               input_report_key(dev, BTN_LEFT, packet[0] & 0x03);
+       } else {
+               input_report_key(dev, BTN_LEFT, packet[0] & 0x01);
+               input_report_key(dev, BTN_RIGHT, packet[0] & 0x02);
+       }
+
        input_report_abs(dev, ABS_PRESSURE, pres);
        input_report_abs(dev, ABS_TOOL_WIDTH, width);
 
@@ -465,10 +473,17 @@ static void elantech_report_absolute_v3(struct psmouse *psmouse,
 static void elantech_input_sync_v4(struct psmouse *psmouse)
 {
        struct input_dev *dev = psmouse->dev;
+       struct elantech_data *etd = psmouse->private;
        unsigned char *packet = psmouse->packet;
 
-       input_report_key(dev, BTN_LEFT, packet[0] & 0x01);
-       input_report_key(dev, BTN_RIGHT, packet[0] & 0x02);
+       /* For clickpads map both buttons to BTN_LEFT */
+       if (etd->fw_version & 0x001000) {
+               input_report_key(dev, BTN_LEFT, packet[0] & 0x03);
+       } else {
+               input_report_key(dev, BTN_LEFT, packet[0] & 0x01);
+               input_report_key(dev, BTN_RIGHT, packet[0] & 0x02);
+       }
+
        input_mt_report_pointer_emulation(dev, true);
        input_sync(dev);
 }
@@ -677,18 +692,18 @@ static int elantech_packet_check_v3(struct psmouse *psmouse)
 static int elantech_packet_check_v4(struct psmouse *psmouse)
 {
        unsigned char *packet = psmouse->packet;
+       unsigned char packet_type = packet[3] & 0x03;
+
+       switch (packet_type) {
+       case 0:
+               return PACKET_V4_STATUS;
 
-       if ((packet[0] & 0x0c) == 0x04 &&
-           (packet[3] & 0x1f) == 0x11)
+       case 1:
                return PACKET_V4_HEAD;
 
-       if ((packet[0] & 0x0c) == 0x04 &&
-           (packet[3] & 0x1f) == 0x12)
+       case 2:
                return PACKET_V4_MOTION;
-
-       if ((packet[0] & 0x0c) == 0x04 &&
-           (packet[3] & 0x1f) == 0x10)
-               return PACKET_V4_STATUS;
+       }
 
        return PACKET_UNKNOWN;
 }
@@ -750,6 +765,21 @@ static psmouse_ret_t elantech_process_byte(struct psmouse *psmouse)
        return PSMOUSE_FULL_PACKET;
 }
 
+/*
+ * This writes the reg_07 value again to the hardware at the end of every
+ * set_rate call because the register loses its value. reg_07 allows setting
+ * absolute mode on v4 hardware
+ */
+static void elantech_set_rate_restore_reg_07(struct psmouse *psmouse,
+               unsigned int rate)
+{
+       struct elantech_data *etd = psmouse->private;
+
+       etd->original_set_rate(psmouse, rate);
+       if (elantech_write_reg(psmouse, 0x07, etd->reg_07))
+               psmouse_err(psmouse, "restoring reg_07 failed\n");
+}
+
 /*
  * Put the touchpad into absolute mode
  */
@@ -783,7 +813,11 @@ static int elantech_set_absolute_mode(struct psmouse *psmouse)
                break;
 
        case 3:
-               etd->reg_10 = 0x0b;
+               if (etd->set_hw_resolution)
+                       etd->reg_10 = 0x0b;
+               else
+                       etd->reg_10 = 0x01;
+
                if (elantech_write_reg(psmouse, 0x10, etd->reg_10))
                        rc = -1;
 
@@ -1111,6 +1145,14 @@ static bool elantech_is_signature_valid(const unsigned char *param)
        if (param[1] == 0)
                return true;
 
+       /*
+        * Some hw_version >= 4 models have a revision higher then 20. Meaning
+        * that param[2] may be 10 or 20, skip the rates check for these.
+        */
+       if ((param[0] & 0x0f) >= 0x06 && (param[1] & 0xaf) == 0x0f &&
+           param[2] < 40)
+               return true;
+
        for (i = 0; i < ARRAY_SIZE(rates); i++)
                if (param[2] == rates[i])
                        return false;
@@ -1205,6 +1247,23 @@ static int elantech_reconnect(struct psmouse *psmouse)
        return 0;
 }
 
+/*
+ * Some hw_version 3 models go into error state when we try to set
+ * bit 3 and/or bit 1 of r10.
+ */
+static const struct dmi_system_id no_hw_res_dmi_table[] = {
+#if defined(CONFIG_DMI) && defined(CONFIG_X86)
+       {
+               /* Gigabyte U2442 */
+               .matches = {
+                       DMI_MATCH(DMI_SYS_VENDOR, "GIGABYTE"),
+                       DMI_MATCH(DMI_PRODUCT_NAME, "U2442"),
+               },
+       },
+#endif
+       { }
+};
+
 /*
  * determine hardware version and set some properties according to it.
  */
@@ -1225,7 +1284,7 @@ static int elantech_set_properties(struct elantech_data *etd)
                case 5:
                        etd->hw_version = 3;
                        break;
-               case 6:
+               case 6 ... 14:
                        etd->hw_version = 4;
                        break;
                default:
@@ -1254,6 +1313,9 @@ static int elantech_set_properties(struct elantech_data *etd)
                        etd->reports_pressure = true;
        }
 
+       /* Enable real hardware resolution on hw_version 3 ? */
+       etd->set_hw_resolution = !dmi_check_system(no_hw_res_dmi_table);
+
        return 0;
 }
 
@@ -1307,6 +1369,11 @@ int elantech_init(struct psmouse *psmouse)
                goto init_fail;
        }
 
+       if (etd->fw_version == 0x381f17) {
+               etd->original_set_rate = psmouse->set_rate;
+               psmouse->set_rate = elantech_set_rate_restore_reg_07;
+       }
+
        if (elantech_set_input_params(psmouse)) {
                psmouse_err(psmouse, "failed to query touchpad range.\n");
                goto init_fail;