]> git.kernelconcepts.de Git - karo-tx-linux.git/blobdiff - drivers/input/mouse/alps.c
Input: alps - only the Dell Latitude D420/430/620/630 have separate stick button...
[karo-tx-linux.git] / drivers / input / mouse / alps.c
index a353b7de6d22e91a52378cd4c106b17cafc26a07..41e6cb501e6a1d5b07801463460fe4ebbaace887 100644 (file)
@@ -20,6 +20,7 @@
 #include <linux/input/mt.h>
 #include <linux/serio.h>
 #include <linux/libps2.h>
+#include <linux/dmi.h>
 
 #include "psmouse.h"
 #include "alps.h"
@@ -99,6 +100,7 @@ static const struct alps_nibble_commands alps_v6_nibble_commands[] = {
 #define ALPS_FOUR_BUTTONS      0x40    /* 4 direction button present */
 #define ALPS_PS2_INTERLEAVED   0x80    /* 3-byte PS/2 packet interleaved with
                                           6-byte ALPS packet */
+#define ALPS_STICK_BITS                0x100   /* separate stick button bits */
 #define ALPS_BUTTONPAD         0x200   /* device is a clickpad */
 
 static const struct alps_model_info alps_model_data[] = {
@@ -157,10 +159,47 @@ static const struct alps_protocol_info alps_v8_protocol_data = {
        ALPS_PROTO_V8, 0x18, 0x18, 0
 };
 
+/*
+ * Some v2 models report the stick buttons in separate bits
+ */
+static const struct dmi_system_id alps_dmi_has_separate_stick_buttons[] = {
+#if defined(CONFIG_DMI) && defined(CONFIG_X86)
+       {
+               /* Extrapolated from other entries */
+               .matches = {
+                       DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc."),
+                       DMI_MATCH(DMI_PRODUCT_NAME, "Latitude D420"),
+               },
+       },
+       {
+               /* Reported-by: Hans de Bruin <jmdebruin@xmsnet.nl> */
+               .matches = {
+                       DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc."),
+                       DMI_MATCH(DMI_PRODUCT_NAME, "Latitude D430"),
+               },
+       },
+       {
+               /* Reported-by: Hans de Goede <hdegoede@redhat.com> */
+               .matches = {
+                       DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc."),
+                       DMI_MATCH(DMI_PRODUCT_NAME, "Latitude D620"),
+               },
+       },
+       {
+               /* Extrapolated from other entries */
+               .matches = {
+                       DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc."),
+                       DMI_MATCH(DMI_PRODUCT_NAME, "Latitude D630"),
+               },
+       },
+#endif
+       { }
+};
+
 static void alps_set_abs_params_st(struct alps_data *priv,
                                   struct input_dev *dev1);
-static void alps_set_abs_params_mt(struct alps_data *priv,
-                                  struct input_dev *dev1);
+static void alps_set_abs_params_semi_mt(struct alps_data *priv,
+                                       struct input_dev *dev1);
 static void alps_set_abs_params_v7(struct alps_data *priv,
                                   struct input_dev *dev1);
 static void alps_set_abs_params_ss4_v2(struct alps_data *priv,
@@ -251,9 +290,8 @@ static void alps_process_packet_v1_v2(struct psmouse *psmouse)
                return;
        }
 
-       /* Non interleaved V2 dualpoint has separate stick button bits */
-       if (priv->proto_version == ALPS_PROTO_V2 &&
-           priv->flags == (ALPS_PASS | ALPS_DUALPOINT)) {
+       /* Some models have separate stick button bits */
+       if (priv->flags & ALPS_STICK_BITS) {
                left |= packet[0] & 1;
                right |= packet[0] & 2;
                middle |= packet[0] & 4;
@@ -310,53 +348,6 @@ static void alps_process_packet_v1_v2(struct psmouse *psmouse)
        input_sync(dev);
 }
 
-/*
- * Process bitmap data for V5 protocols. Return value is null.
- *
- * The bitmaps don't have enough data to track fingers, so this function
- * only generates points representing a bounding box of at most two contacts.
- * These two points are returned in fields->mt.
- */
-static void alps_process_bitmap_dolphin(struct alps_data *priv,
-                                       struct alps_fields *fields)
-{
-       int box_middle_x, box_middle_y;
-       unsigned int x_map, y_map;
-       unsigned char start_bit, end_bit;
-       unsigned char x_msb, x_lsb, y_msb, y_lsb;
-
-       x_map = fields->x_map;
-       y_map = fields->y_map;
-
-       if (!x_map || !y_map)
-               return;
-
-       /* Get Most-significant and Least-significant bit */
-       x_msb = fls(x_map);
-       x_lsb = ffs(x_map);
-       y_msb = fls(y_map);
-       y_lsb = ffs(y_map);
-
-       /* Most-significant bit should never exceed max sensor line number */
-       if (x_msb > priv->x_bits || y_msb > priv->y_bits)
-               return;
-
-       if (fields->fingers > 1) {
-               start_bit = priv->x_bits - x_msb;
-               end_bit = priv->x_bits - x_lsb;
-               box_middle_x = (priv->x_max * (start_bit + end_bit)) /
-                               (2 * (priv->x_bits - 1));
-
-               start_bit = y_lsb - 1;
-               end_bit = y_msb - 1;
-               box_middle_y = (priv->y_max * (start_bit + end_bit)) /
-                               (2 * (priv->y_bits - 1));
-               fields->mt[0] = fields->st;
-               fields->mt[1].x = 2 * box_middle_x - fields->mt[0].x;
-               fields->mt[1].y = 2 * box_middle_y - fields->mt[0].y;
-       }
-}
-
 static void alps_get_bitmap_points(unsigned int map,
                                   struct alps_bitmap_point *low,
                                   struct alps_bitmap_point *high,
@@ -384,7 +375,7 @@ static void alps_get_bitmap_points(unsigned int map,
 }
 
 /*
- * Process bitmap data from v3 and v4 protocols. Returns the number of
+ * Process bitmap data from semi-mt protocols. Returns the number of
  * fingers detected. A return value of 0 means at least one of the
  * bitmaps was empty.
  *
@@ -396,9 +387,10 @@ static void alps_get_bitmap_points(unsigned int map,
 static int alps_process_bitmap(struct alps_data *priv,
                               struct alps_fields *fields)
 {
-       int i, fingers_x = 0, fingers_y = 0, fingers;
+       int i, fingers_x = 0, fingers_y = 0, fingers, closest;
        struct alps_bitmap_point x_low = {0,}, x_high = {0,};
        struct alps_bitmap_point y_low = {0,}, y_high = {0,};
+       struct input_mt_pos corner[4];
 
        if (!fields->x_map || !fields->y_map)
                return 0;
@@ -429,26 +421,76 @@ static int alps_process_bitmap(struct alps_data *priv,
                y_high.num_bits = max(i, 1);
        }
 
-       fields->mt[0].x =
+       /* top-left corner */
+       corner[0].x =
                (priv->x_max * (2 * x_low.start_bit + x_low.num_bits - 1)) /
                (2 * (priv->x_bits - 1));
-       fields->mt[0].y =
+       corner[0].y =
                (priv->y_max * (2 * y_low.start_bit + y_low.num_bits - 1)) /
                (2 * (priv->y_bits - 1));
 
-       fields->mt[1].x =
+       /* top-right corner */
+       corner[1].x =
                (priv->x_max * (2 * x_high.start_bit + x_high.num_bits - 1)) /
                (2 * (priv->x_bits - 1));
-       fields->mt[1].y =
+       corner[1].y =
+               (priv->y_max * (2 * y_low.start_bit + y_low.num_bits - 1)) /
+               (2 * (priv->y_bits - 1));
+
+       /* bottom-right corner */
+       corner[2].x =
+               (priv->x_max * (2 * x_high.start_bit + x_high.num_bits - 1)) /
+               (2 * (priv->x_bits - 1));
+       corner[2].y =
+               (priv->y_max * (2 * y_high.start_bit + y_high.num_bits - 1)) /
+               (2 * (priv->y_bits - 1));
+
+       /* bottom-left corner */
+       corner[3].x =
+               (priv->x_max * (2 * x_low.start_bit + x_low.num_bits - 1)) /
+               (2 * (priv->x_bits - 1));
+       corner[3].y =
                (priv->y_max * (2 * y_high.start_bit + y_high.num_bits - 1)) /
                (2 * (priv->y_bits - 1));
 
-       /* y-bitmap order is reversed, except on rushmore */
-       if (priv->proto_version != ALPS_PROTO_V3_RUSHMORE) {
-               fields->mt[0].y = priv->y_max - fields->mt[0].y;
-               fields->mt[1].y = priv->y_max - fields->mt[1].y;
+       /* x-bitmap order is reversed on v5 touchpads  */
+       if (priv->proto_version == ALPS_PROTO_V5) {
+               for (i = 0; i < 4; i++)
+                       corner[i].x = priv->x_max - corner[i].x;
+       }
+
+       /* y-bitmap order is reversed on v3 and v4 touchpads  */
+       if (priv->proto_version == ALPS_PROTO_V3 ||
+           priv->proto_version == ALPS_PROTO_V4) {
+               for (i = 0; i < 4; i++)
+                       corner[i].y = priv->y_max - corner[i].y;
+       }
+
+       /*
+        * We only select a corner for the second touch once per 2 finger
+        * touch sequence to avoid the chosen corner (and thus the coordinates)
+        * jumping around when the first touch is in the middle.
+        */
+       if (priv->second_touch == -1) {
+               /* Find corner closest to our st coordinates */
+               closest = 0x7fffffff;
+               for (i = 0; i < 4; i++) {
+                       int dx = fields->st.x - corner[i].x;
+                       int dy = fields->st.y - corner[i].y;
+                       int distance = dx * dx + dy * dy;
+
+                       if (distance < closest) {
+                               priv->second_touch = i;
+                               closest = distance;
+                       }
+               }
+               /* And select the opposite corner to use for the 2nd touch */
+               priv->second_touch = (priv->second_touch + 2) % 4;
        }
 
+       fields->mt[0] = fields->st;
+       fields->mt[1] = corner[priv->second_touch];
+
        return fingers;
 }
 
@@ -485,9 +527,14 @@ static void alps_report_semi_mt_data(struct psmouse *psmouse, int fingers)
                f->mt[0].x = f->st.x;
                f->mt[0].y = f->st.y;
                fingers = f->pressure > 0 ? 1 : 0;
+               priv->second_touch = -1;
        }
 
-       alps_report_mt_data(psmouse, (fingers <= 2) ? fingers : 2);
+       if (fingers >= 1)
+               alps_set_slot(dev, 0, f->mt[0].x, f->mt[0].y);
+       if (fingers >= 2)
+               alps_set_slot(dev, 1, f->mt[1].x, f->mt[1].y);
+       input_mt_sync_frame(dev);
 
        input_mt_report_finger_count(dev, fingers);
 
@@ -584,20 +631,22 @@ static int alps_decode_pinnacle(struct alps_fields *f, unsigned char *p,
        f->first_mp = !!(p[4] & 0x40);
        f->is_mp = !!(p[0] & 0x40);
 
-       f->fingers = (p[5] & 0x3) + 1;
-       f->x_map = ((p[4] & 0x7e) << 8) |
-                  ((p[1] & 0x7f) << 2) |
-                  ((p[0] & 0x30) >> 4);
-       f->y_map = ((p[3] & 0x70) << 4) |
-                  ((p[2] & 0x7f) << 1) |
-                  (p[4] & 0x01);
-
-       f->st.x = ((p[1] & 0x7f) << 4) | ((p[4] & 0x30) >> 2) |
-              ((p[0] & 0x30) >> 4);
-       f->st.y = ((p[2] & 0x7f) << 4) | (p[4] & 0x0f);
-       f->pressure = p[5] & 0x7f;
+       if (f->is_mp) {
+               f->fingers = (p[5] & 0x3) + 1;
+               f->x_map = ((p[4] & 0x7e) << 8) |
+                          ((p[1] & 0x7f) << 2) |
+                          ((p[0] & 0x30) >> 4);
+               f->y_map = ((p[3] & 0x70) << 4) |
+                          ((p[2] & 0x7f) << 1) |
+                          (p[4] & 0x01);
+       } else {
+               f->st.x = ((p[1] & 0x7f) << 4) | ((p[4] & 0x30) >> 2) |
+                      ((p[0] & 0x30) >> 4);
+               f->st.y = ((p[2] & 0x7f) << 4) | (p[4] & 0x0f);
+               f->pressure = p[5] & 0x7f;
 
-       alps_decode_buttons_v3(f, p);
+               alps_decode_buttons_v3(f, p);
+       }
 
        return 0;
 }
@@ -605,13 +654,27 @@ static int alps_decode_pinnacle(struct alps_fields *f, unsigned char *p,
 static int alps_decode_rushmore(struct alps_fields *f, unsigned char *p,
                                 struct psmouse *psmouse)
 {
-       alps_decode_pinnacle(f, p, psmouse);
-
-       /* Rushmore's packet decode has a bit difference with Pinnacle's */
+       f->first_mp = !!(p[4] & 0x40);
        f->is_mp = !!(p[5] & 0x40);
-       f->fingers = max((p[5] & 0x3), ((p[5] >> 2) & 0x3)) + 1;
-       f->x_map |= (p[5] & 0x10) << 11;
-       f->y_map |= (p[5] & 0x20) << 6;
+
+       if (f->is_mp) {
+               f->fingers = max((p[5] & 0x3), ((p[5] >> 2) & 0x3)) + 1;
+               f->x_map = ((p[5] & 0x10) << 11) |
+                          ((p[4] & 0x7e) << 8) |
+                          ((p[1] & 0x7f) << 2) |
+                          ((p[0] & 0x30) >> 4);
+               f->y_map = ((p[5] & 0x20) << 6) |
+                          ((p[3] & 0x70) << 4) |
+                          ((p[2] & 0x7f) << 1) |
+                          (p[4] & 0x01);
+       } else {
+               f->st.x = ((p[1] & 0x7f) << 4) | ((p[4] & 0x30) >> 2) |
+                      ((p[0] & 0x30) >> 4);
+               f->st.y = ((p[2] & 0x7f) << 4) | (p[4] & 0x0f);
+               f->pressure = p[5] & 0x7f;
+
+               alps_decode_buttons_v3(f, p);
+       }
 
        return 0;
 }
@@ -680,30 +743,13 @@ static void alps_process_touchpad_packet_v3_v5(struct psmouse *psmouse)
                 */
                if (f->is_mp) {
                        fingers = f->fingers;
-                       if (priv->proto_version == ALPS_PROTO_V3 ||
-                           priv->proto_version == ALPS_PROTO_V3_RUSHMORE) {
-                               if (alps_process_bitmap(priv, f) == 0)
-                                       fingers = 0; /* Use st data */
-
-                               /* Now process position packet */
-                               priv->decode_fields(f, priv->multi_data,
-                                                   psmouse);
-                       } else {
-                               /*
-                                * Because Dolphin uses position packet's
-                                * coordinate data as Pt1 and uses it to
-                                * calculate Pt2, so we need to do position
-                                * packet decode first.
-                                */
-                               priv->decode_fields(f, priv->multi_data,
-                                                   psmouse);
-
-                               /*
-                                * Since Dolphin's finger number is reliable,
-                                * there is no need to compare with bmap_fn.
-                                */
-                               alps_process_bitmap_dolphin(priv, f);
-                       }
+                       /*
+                        * Bitmap processing uses position packet's coordinate
+                        * data, so we need to do decode it first.
+                        */
+                       priv->decode_fields(f, priv->multi_data, psmouse);
+                       if (alps_process_bitmap(priv, f) == 0)
+                               fingers = 0; /* Use st data */
                } else {
                        priv->multi_packet = 0;
                }
@@ -865,6 +911,14 @@ static void alps_process_packet_v4(struct psmouse *psmouse)
        priv->multi_data[offset] = packet[6];
        priv->multi_data[offset + 1] = packet[7];
 
+       f->left = !!(packet[4] & 0x01);
+       f->right = !!(packet[4] & 0x02);
+
+       f->st.x = ((packet[1] & 0x7f) << 4) | ((packet[3] & 0x30) >> 2) |
+                 ((packet[0] & 0x30) >> 4);
+       f->st.y = ((packet[2] & 0x7f) << 4) | (packet[3] & 0x0f);
+       f->pressure = packet[5] & 0x7f;
+
        if (++priv->multi_packet > 2) {
                priv->multi_packet = 0;
 
@@ -879,14 +933,6 @@ static void alps_process_packet_v4(struct psmouse *psmouse)
                f->fingers = alps_process_bitmap(priv, f);
        }
 
-       f->left = !!(packet[4] & 0x01);
-       f->right = !!(packet[4] & 0x02);
-
-       f->st.x = ((packet[1] & 0x7f) << 4) | ((packet[3] & 0x30) >> 2) |
-                 ((packet[0] & 0x30) >> 4);
-       f->st.y = ((packet[2] & 0x7f) << 4) | (packet[3] & 0x0f);
-       f->pressure = packet[5] & 0x7f;
-
        alps_report_semi_mt_data(psmouse, f->fingers);
 }
 
@@ -2556,12 +2602,14 @@ static int alps_set_protocol(struct psmouse *psmouse,
                priv->set_abs_params = alps_set_abs_params_st;
                priv->x_max = 1023;
                priv->y_max = 767;
+               if (dmi_check_system(alps_dmi_has_separate_stick_buttons))
+                       priv->flags |= ALPS_STICK_BITS;
                break;
 
        case ALPS_PROTO_V3:
                priv->hw_init = alps_hw_init_v3;
                priv->process_packet = alps_process_packet_v3;
-               priv->set_abs_params = alps_set_abs_params_mt;
+               priv->set_abs_params = alps_set_abs_params_semi_mt;
                priv->decode_fields = alps_decode_pinnacle;
                priv->nibble_commands = alps_v3_nibble_commands;
                priv->addr_command = PSMOUSE_CMD_RESET_WRAP;
@@ -2570,7 +2618,7 @@ static int alps_set_protocol(struct psmouse *psmouse,
        case ALPS_PROTO_V3_RUSHMORE:
                priv->hw_init = alps_hw_init_rushmore_v3;
                priv->process_packet = alps_process_packet_v3;
-               priv->set_abs_params = alps_set_abs_params_mt;
+               priv->set_abs_params = alps_set_abs_params_semi_mt;
                priv->decode_fields = alps_decode_rushmore;
                priv->nibble_commands = alps_v3_nibble_commands;
                priv->addr_command = PSMOUSE_CMD_RESET_WRAP;
@@ -2586,7 +2634,7 @@ static int alps_set_protocol(struct psmouse *psmouse,
        case ALPS_PROTO_V4:
                priv->hw_init = alps_hw_init_v4;
                priv->process_packet = alps_process_packet_v4;
-               priv->set_abs_params = alps_set_abs_params_mt;
+               priv->set_abs_params = alps_set_abs_params_semi_mt;
                priv->nibble_commands = alps_v4_nibble_commands;
                priv->addr_command = PSMOUSE_CMD_DISABLE;
                break;
@@ -2595,7 +2643,7 @@ static int alps_set_protocol(struct psmouse *psmouse,
                priv->hw_init = alps_hw_init_dolphin_v1;
                priv->process_packet = alps_process_touchpad_packet_v3_v5;
                priv->decode_fields = alps_decode_dolphin;
-               priv->set_abs_params = alps_set_abs_params_mt;
+               priv->set_abs_params = alps_set_abs_params_semi_mt;
                priv->nibble_commands = alps_v3_nibble_commands;
                priv->addr_command = PSMOUSE_CMD_RESET_WRAP;
                priv->x_bits = 23;
@@ -2777,15 +2825,15 @@ static void alps_set_abs_params_mt_common(struct alps_data *priv,
        set_bit(BTN_TOOL_QUADTAP, dev1->keybit);
 }
 
-static void alps_set_abs_params_mt(struct alps_data *priv,
-                                  struct input_dev *dev1)
+static void alps_set_abs_params_semi_mt(struct alps_data *priv,
+                                       struct input_dev *dev1)
 {
        alps_set_abs_params_mt_common(priv, dev1);
        input_set_abs_params(dev1, ABS_PRESSURE, 0, 127, 0, 0);
 
        input_mt_init_slots(dev1, MAX_TOUCHES,
                            INPUT_MT_POINTER | INPUT_MT_DROP_UNUSED |
-                               INPUT_MT_TRACK | INPUT_MT_SEMI_MT);
+                               INPUT_MT_SEMI_MT);
 }
 
 static void alps_set_abs_params_v7(struct alps_data *priv,