Merge tag 'v5.2' into next
[platform/kernel/linux-rpi.git] / drivers / input / mouse / elantech.c
index a434505..2d8434b 100644 (file)
@@ -227,6 +227,52 @@ static void elantech_packet_dump(struct psmouse *psmouse)
 }
 
 /*
+ * Advertise INPUT_PROP_BUTTONPAD for clickpads. The testing of bit 12 in
+ * fw_version for this is based on the following fw_version & caps table:
+ *
+ * Laptop-model:           fw_version:     caps:           buttons:
+ * Acer S3                 0x461f00        10, 13, 0e      clickpad
+ * Acer S7-392             0x581f01        50, 17, 0d      clickpad
+ * Acer V5-131             0x461f02        01, 16, 0c      clickpad
+ * Acer V5-551             0x461f00        ?               clickpad
+ * Asus K53SV              0x450f01        78, 15, 0c      2 hw buttons
+ * Asus G46VW              0x460f02        00, 18, 0c      2 hw buttons
+ * Asus G750JX             0x360f00        00, 16, 0c      2 hw buttons
+ * Asus TP500LN            0x381f17        10, 14, 0e      clickpad
+ * Asus X750JN             0x381f17        10, 14, 0e      clickpad
+ * Asus UX31               0x361f00        20, 15, 0e      clickpad
+ * Asus UX32VD             0x361f02        00, 15, 0e      clickpad
+ * Avatar AVIU-145A2       0x361f00        ?               clickpad
+ * Fujitsu CELSIUS H760    0x570f02        40, 14, 0c      3 hw buttons (**)
+ * Fujitsu CELSIUS H780    0x5d0f02        41, 16, 0d      3 hw buttons (**)
+ * Fujitsu LIFEBOOK E544   0x470f00        d0, 12, 09      2 hw buttons
+ * Fujitsu LIFEBOOK E546   0x470f00        50, 12, 09      2 hw buttons
+ * Fujitsu LIFEBOOK E547   0x470f00        50, 12, 09      2 hw buttons
+ * Fujitsu LIFEBOOK E554   0x570f01        40, 14, 0c      2 hw buttons
+ * Fujitsu LIFEBOOK E557   0x570f01        40, 14, 0c      2 hw buttons
+ * Fujitsu T725            0x470f01        05, 12, 09      2 hw buttons
+ * Fujitsu H730            0x570f00        c0, 14, 0c      3 hw buttons (**)
+ * Gigabyte U2442          0x450f01        58, 17, 0c      2 hw buttons
+ * Lenovo L430             0x350f02        b9, 15, 0c      2 hw buttons (*)
+ * Lenovo L530             0x350f02        b9, 15, 0c      2 hw buttons (*)
+ * Samsung NF210           0x150b00        78, 14, 0a      2 hw buttons
+ * Samsung NP770Z5E        0x575f01        10, 15, 0f      clickpad
+ * Samsung NP700Z5B        0x361f06        21, 15, 0f      clickpad
+ * Samsung NP900X3E-A02    0x575f03        ?               clickpad
+ * Samsung NP-QX410        0x851b00        19, 14, 0c      clickpad
+ * Samsung RC512           0x450f00        08, 15, 0c      2 hw buttons
+ * Samsung RF710           0x450f00        ?               2 hw buttons
+ * System76 Pangolin       0x250f01        ?               2 hw buttons
+ * (*) + 3 trackpoint buttons
+ * (**) + 0 trackpoint buttons
+ * Note: Lenovo L430 and Lenovo L530 have the same fw_version/caps
+ */
+static inline int elantech_is_buttonpad(struct elantech_device_info *info)
+{
+       return info->fw_version & 0x001000;
+}
+
+/*
  * Interpret complete data packets and report absolute mode input events for
  * hardware version 1. (4 byte packets)
  */
@@ -523,7 +569,7 @@ static void elantech_report_absolute_v3(struct psmouse *psmouse,
        input_report_key(dev, BTN_TOOL_TRIPLETAP, fingers == 3);
 
        /* For clickpads map both buttons to BTN_LEFT */
-       if (etd->info.fw_version & 0x001000)
+       if (elantech_is_buttonpad(&etd->info))
                input_report_key(dev, BTN_LEFT, packet[0] & 0x03);
        else
                psmouse_report_standard_buttons(dev, packet[0]);
@@ -541,7 +587,7 @@ static void elantech_input_sync_v4(struct psmouse *psmouse)
        unsigned char *packet = psmouse->packet;
 
        /* For clickpads map both buttons to BTN_LEFT */
-       if (etd->info.fw_version & 0x001000)
+       if (elantech_is_buttonpad(&etd->info))
                input_report_key(dev, BTN_LEFT, packet[0] & 0x03);
        else
                psmouse_report_standard_buttons(dev, packet[0]);
@@ -991,88 +1037,6 @@ static int elantech_set_absolute_mode(struct psmouse *psmouse)
        return rc;
 }
 
-static int elantech_set_range(struct psmouse *psmouse,
-                             unsigned int *x_min, unsigned int *y_min,
-                             unsigned int *x_max, unsigned int *y_max,
-                             unsigned int *width)
-{
-       struct elantech_data *etd = psmouse->private;
-       struct elantech_device_info *info = &etd->info;
-       unsigned char param[3];
-       unsigned char traces;
-
-       switch (info->hw_version) {
-       case 1:
-               *x_min = ETP_XMIN_V1;
-               *y_min = ETP_YMIN_V1;
-               *x_max = ETP_XMAX_V1;
-               *y_max = ETP_YMAX_V1;
-               break;
-
-       case 2:
-               if (info->fw_version == 0x020800 ||
-                   info->fw_version == 0x020b00 ||
-                   info->fw_version == 0x020030) {
-                       *x_min = ETP_XMIN_V2;
-                       *y_min = ETP_YMIN_V2;
-                       *x_max = ETP_XMAX_V2;
-                       *y_max = ETP_YMAX_V2;
-               } else {
-                       int i;
-                       int fixed_dpi;
-
-                       i = (info->fw_version > 0x020800 &&
-                            info->fw_version < 0x020900) ? 1 : 2;
-
-                       if (info->send_cmd(psmouse, ETP_FW_ID_QUERY, param))
-                               return -1;
-
-                       fixed_dpi = param[1] & 0x10;
-
-                       if (((info->fw_version >> 16) == 0x14) && fixed_dpi) {
-                               if (info->send_cmd(psmouse, ETP_SAMPLE_QUERY, param))
-                                       return -1;
-
-                               *x_max = (info->capabilities[1] - i) * param[1] / 2;
-                               *y_max = (info->capabilities[2] - i) * param[2] / 2;
-                       } else if (info->fw_version == 0x040216) {
-                               *x_max = 819;
-                               *y_max = 405;
-                       } else if (info->fw_version == 0x040219 || info->fw_version == 0x040215) {
-                               *x_max = 900;
-                               *y_max = 500;
-                       } else {
-                               *x_max = (info->capabilities[1] - i) * 64;
-                               *y_max = (info->capabilities[2] - i) * 64;
-                       }
-               }
-               break;
-
-       case 3:
-               if (info->send_cmd(psmouse, ETP_FW_ID_QUERY, param))
-                       return -1;
-
-               *x_max = (0x0f & param[0]) << 8 | param[1];
-               *y_max = (0xf0 & param[0]) << 4 | param[2];
-               break;
-
-       case 4:
-               if (info->send_cmd(psmouse, ETP_FW_ID_QUERY, param))
-                       return -1;
-
-               *x_max = (0x0f & param[0]) << 8 | param[1];
-               *y_max = (0xf0 & param[0]) << 4 | param[2];
-               traces = info->capabilities[1];
-               if ((traces < 2) || (traces > *x_max))
-                       return -1;
-
-               *width = *x_max / (traces - 1);
-               break;
-       }
-
-       return 0;
-}
-
 /*
  * (value from firmware) * 10 + 790 = dpi
  * we also have to convert dpi to dots/mm (*10/254 to avoid floating point)
@@ -1099,53 +1063,12 @@ static int elantech_get_resolution_v4(struct psmouse *psmouse,
        return 0;
 }
 
-/*
- * Advertise INPUT_PROP_BUTTONPAD for clickpads. The testing of bit 12 in
- * fw_version for this is based on the following fw_version & caps table:
- *
- * Laptop-model:           fw_version:     caps:           buttons:
- * Acer S3                 0x461f00        10, 13, 0e      clickpad
- * Acer S7-392             0x581f01        50, 17, 0d      clickpad
- * Acer V5-131             0x461f02        01, 16, 0c      clickpad
- * Acer V5-551             0x461f00        ?               clickpad
- * Asus K53SV              0x450f01        78, 15, 0c      2 hw buttons
- * Asus G46VW              0x460f02        00, 18, 0c      2 hw buttons
- * Asus G750JX             0x360f00        00, 16, 0c      2 hw buttons
- * Asus TP500LN            0x381f17        10, 14, 0e      clickpad
- * Asus X750JN             0x381f17        10, 14, 0e      clickpad
- * Asus UX31               0x361f00        20, 15, 0e      clickpad
- * Asus UX32VD             0x361f02        00, 15, 0e      clickpad
- * Avatar AVIU-145A2       0x361f00        ?               clickpad
- * Fujitsu CELSIUS H760    0x570f02        40, 14, 0c      3 hw buttons (**)
- * Fujitsu CELSIUS H780    0x5d0f02        41, 16, 0d      3 hw buttons (**)
- * Fujitsu LIFEBOOK E544   0x470f00        d0, 12, 09      2 hw buttons
- * Fujitsu LIFEBOOK E546   0x470f00        50, 12, 09      2 hw buttons
- * Fujitsu LIFEBOOK E547   0x470f00        50, 12, 09      2 hw buttons
- * Fujitsu LIFEBOOK E554   0x570f01        40, 14, 0c      2 hw buttons
- * Fujitsu LIFEBOOK E557   0x570f01        40, 14, 0c      2 hw buttons
- * Fujitsu T725            0x470f01        05, 12, 09      2 hw buttons
- * Fujitsu H730            0x570f00        c0, 14, 0c      3 hw buttons (**)
- * Gigabyte U2442          0x450f01        58, 17, 0c      2 hw buttons
- * Lenovo L430             0x350f02        b9, 15, 0c      2 hw buttons (*)
- * Lenovo L530             0x350f02        b9, 15, 0c      2 hw buttons (*)
- * Samsung NF210           0x150b00        78, 14, 0a      2 hw buttons
- * Samsung NP770Z5E        0x575f01        10, 15, 0f      clickpad
- * Samsung NP700Z5B        0x361f06        21, 15, 0f      clickpad
- * Samsung NP900X3E-A02    0x575f03        ?               clickpad
- * Samsung NP-QX410        0x851b00        19, 14, 0c      clickpad
- * Samsung RC512           0x450f00        08, 15, 0c      2 hw buttons
- * Samsung RF710           0x450f00        ?               2 hw buttons
- * System76 Pangolin       0x250f01        ?               2 hw buttons
- * (*) + 3 trackpoint buttons
- * (**) + 0 trackpoint buttons
- * Note: Lenovo L430 and Lenovo L530 have the same fw_version/caps
- */
 static void elantech_set_buttonpad_prop(struct psmouse *psmouse)
 {
        struct input_dev *dev = psmouse->dev;
        struct elantech_data *etd = psmouse->private;
 
-       if (etd->info.fw_version & 0x001000) {
+       if (elantech_is_buttonpad(&etd->info)) {
                __set_bit(INPUT_PROP_BUTTONPAD, dev->propbit);
                __clear_bit(BTN_RIGHT, dev->keybit);
        }
@@ -1181,16 +1104,6 @@ static const struct dmi_system_id elantech_dmi_has_middle_button[] = {
        { }
 };
 
-static const char * const middle_button_pnp_ids[] = {
-       "LEN2131", /* ThinkPad P52 w/ NFC */
-       "LEN2132", /* ThinkPad P52 */
-       "LEN2133", /* ThinkPad P72 w/ NFC */
-       "LEN2134", /* ThinkPad P72 */
-       "LEN0407",
-       "LEN0408",
-       NULL
-};
-
 /*
  * Set the appropriate event bits for the input subsystem
  */
@@ -1199,10 +1112,9 @@ static int elantech_set_input_params(struct psmouse *psmouse)
        struct input_dev *dev = psmouse->dev;
        struct elantech_data *etd = psmouse->private;
        struct elantech_device_info *info = &etd->info;
-       unsigned int x_min = 0, y_min = 0, x_max = 0, y_max = 0, width = 0;
-
-       if (elantech_set_range(psmouse, &x_min, &y_min, &x_max, &y_max, &width))
-               return -1;
+       unsigned int x_min = info->x_min, y_min = info->y_min,
+                    x_max = info->x_max, y_max = info->y_max,
+                    width = info->width;
 
        __set_bit(INPUT_PROP_POINTER, dev->propbit);
        __set_bit(EV_KEY, dev->evbit);
@@ -1210,8 +1122,7 @@ static int elantech_set_input_params(struct psmouse *psmouse)
        __clear_bit(EV_REL, dev->evbit);
 
        __set_bit(BTN_LEFT, dev->keybit);
-       if (dmi_check_system(elantech_dmi_has_middle_button) ||
-                       psmouse_matches_pnp_id(psmouse, middle_button_pnp_ids))
+       if (info->has_middle_button)
                __set_bit(BTN_MIDDLE, dev->keybit);
        __set_bit(BTN_RIGHT, dev->keybit);
 
@@ -1686,6 +1597,7 @@ static int elantech_query_info(struct psmouse *psmouse,
                               struct elantech_device_info *info)
 {
        unsigned char param[3];
+       unsigned char traces;
 
        memset(info, 0, sizeof(*info));
 
@@ -1754,6 +1666,90 @@ static int elantech_query_info(struct psmouse *psmouse,
                }
        }
 
+       /* query range information */
+       switch (info->hw_version) {
+       case 1:
+               info->x_min = ETP_XMIN_V1;
+               info->y_min = ETP_YMIN_V1;
+               info->x_max = ETP_XMAX_V1;
+               info->y_max = ETP_YMAX_V1;
+               break;
+
+       case 2:
+               if (info->fw_version == 0x020800 ||
+                   info->fw_version == 0x020b00 ||
+                   info->fw_version == 0x020030) {
+                       info->x_min = ETP_XMIN_V2;
+                       info->y_min = ETP_YMIN_V2;
+                       info->x_max = ETP_XMAX_V2;
+                       info->y_max = ETP_YMAX_V2;
+               } else {
+                       int i;
+                       int fixed_dpi;
+
+                       i = (info->fw_version > 0x020800 &&
+                            info->fw_version < 0x020900) ? 1 : 2;
+
+                       if (info->send_cmd(psmouse, ETP_FW_ID_QUERY, param))
+                               return -EINVAL;
+
+                       fixed_dpi = param[1] & 0x10;
+
+                       if (((info->fw_version >> 16) == 0x14) && fixed_dpi) {
+                               if (info->send_cmd(psmouse, ETP_SAMPLE_QUERY, param))
+                                       return -EINVAL;
+
+                               info->x_max = (info->capabilities[1] - i) * param[1] / 2;
+                               info->y_max = (info->capabilities[2] - i) * param[2] / 2;
+                       } else if (info->fw_version == 0x040216) {
+                               info->x_max = 819;
+                               info->y_max = 405;
+                       } else if (info->fw_version == 0x040219 || info->fw_version == 0x040215) {
+                               info->x_max = 900;
+                               info->y_max = 500;
+                       } else {
+                               info->x_max = (info->capabilities[1] - i) * 64;
+                               info->y_max = (info->capabilities[2] - i) * 64;
+                       }
+               }
+               break;
+
+       case 3:
+               if (info->send_cmd(psmouse, ETP_FW_ID_QUERY, param))
+                       return -EINVAL;
+
+               info->x_max = (0x0f & param[0]) << 8 | param[1];
+               info->y_max = (0xf0 & param[0]) << 4 | param[2];
+               break;
+
+       case 4:
+               if (info->send_cmd(psmouse, ETP_FW_ID_QUERY, param))
+                       return -EINVAL;
+
+               info->x_max = (0x0f & param[0]) << 8 | param[1];
+               info->y_max = (0xf0 & param[0]) << 4 | param[2];
+               traces = info->capabilities[1];
+               if ((traces < 2) || (traces > info->x_max))
+                       return -EINVAL;
+
+               info->width = info->x_max / (traces - 1);
+
+               /* column number of traces */
+               info->x_traces = traces;
+
+               /* row number of traces */
+               traces = info->capabilities[2];
+               if ((traces >= 2) && (traces <= info->y_max))
+                       info->y_traces = traces;
+
+               break;
+       }
+
+       /* check for the middle button: DMI matching or new v4 firmwares */
+       info->has_middle_button = dmi_check_system(elantech_dmi_has_middle_button) ||
+                                 (ETP_NEW_IC_SMBUS_HOST_NOTIFY(info->fw_version) &&
+                                  !elantech_is_buttonpad(info));
+
        return 0;
 }
 
@@ -1780,10 +1776,6 @@ static const char * const i2c_blacklist_pnp_ids[] = {
         * These are known to not be working properly as bits are missing
         * in elan_i2c.
         */
-       "LEN2131", /* ThinkPad P52 w/ NFC */
-       "LEN2132", /* ThinkPad P52 */
-       "LEN2133", /* ThinkPad P72 w/ NFC */
-       "LEN2134", /* ThinkPad P72 */
        NULL
 };
 
@@ -1791,17 +1783,45 @@ static int elantech_create_smbus(struct psmouse *psmouse,
                                 struct elantech_device_info *info,
                                 bool leave_breadcrumbs)
 {
-       const struct property_entry i2c_properties[] = {
-               PROPERTY_ENTRY_BOOL("elan,trackpoint"),
-               { },
-       };
+       struct property_entry i2c_props[11] = {};
        struct i2c_board_info smbus_board = {
                I2C_BOARD_INFO("elan_i2c", 0x15),
                .flags = I2C_CLIENT_HOST_NOTIFY,
        };
+       unsigned int idx = 0;
+
+       smbus_board.properties = i2c_props;
+
+       i2c_props[idx++] = PROPERTY_ENTRY_U32("touchscreen-size-x",
+                                                  info->x_max + 1);
+       i2c_props[idx++] = PROPERTY_ENTRY_U32("touchscreen-size-y",
+                                                  info->y_max + 1);
+       i2c_props[idx++] = PROPERTY_ENTRY_U32("touchscreen-min-x",
+                                                  info->x_min);
+       i2c_props[idx++] = PROPERTY_ENTRY_U32("touchscreen-min-y",
+                                                  info->y_min);
+       if (info->x_res)
+               i2c_props[idx++] = PROPERTY_ENTRY_U32("touchscreen-x-mm",
+                                                     (info->x_max + 1) / info->x_res);
+       if (info->y_res)
+               i2c_props[idx++] = PROPERTY_ENTRY_U32("touchscreen-y-mm",
+                                                     (info->y_max + 1) / info->y_res);
 
        if (info->has_trackpoint)
-               smbus_board.properties = i2c_properties;
+               i2c_props[idx++] = PROPERTY_ENTRY_BOOL("elan,trackpoint");
+
+       if (info->has_middle_button)
+               i2c_props[idx++] = PROPERTY_ENTRY_BOOL("elan,middle-button");
+
+       if (info->x_traces)
+               i2c_props[idx++] = PROPERTY_ENTRY_U32("elan,x_traces",
+                                                     info->x_traces);
+       if (info->y_traces)
+               i2c_props[idx++] = PROPERTY_ENTRY_U32("elan,y_traces",
+                                                     info->y_traces);
+
+       if (elantech_is_buttonpad(info))
+               i2c_props[idx++] = PROPERTY_ENTRY_BOOL("elan,clickpad");
 
        return psmouse_smbus_init(psmouse, &smbus_board, NULL, 0, false,
                                  leave_breadcrumbs);