- Update to 3.3-rc2.
[linux-flexiantxendom0-3.2.10.git] / drivers / input / mouse / synaptics.c
index 896a76a..94ab0e4 100644 (file)
@@ -270,19 +270,49 @@ static int synaptics_query_hardware(struct psmouse *psmouse)
        return 0;
 }
 
-static int synaptics_set_absolute_mode(struct psmouse *psmouse)
+static int synaptics_set_advanced_gesture_mode(struct psmouse *psmouse)
 {
+       static unsigned char param = 0xc8;
        struct synaptics_data *priv = psmouse->private;
 
-       priv->mode = SYN_BIT_ABSOLUTE_MODE;
-       if (SYN_ID_MAJOR(priv->identity) >= 4)
+       if (!SYN_CAP_ADV_GESTURE(priv->ext_cap_0c))
+               return 0;
+
+       if (psmouse_sliced_command(psmouse, SYN_QUE_MODEL))
+               return -1;
+
+       if (ps2_command(&psmouse->ps2dev, &param, PSMOUSE_CMD_SETRATE))
+               return -1;
+
+       /* Advanced gesture mode also sends multi finger data */
+       priv->capabilities |= BIT(1);
+
+       return 0;
+}
+
+static int synaptics_set_mode(struct psmouse *psmouse)
+{
+       struct synaptics_data *priv = psmouse->private;
+
+       priv->mode = 0;
+       if (priv->absolute_mode)
+               priv->mode |= SYN_BIT_ABSOLUTE_MODE;
+       if (priv->disable_gesture)
                priv->mode |= SYN_BIT_DISABLE_GESTURE;
+       if (psmouse->rate >= 80)
+               priv->mode |= SYN_BIT_HIGH_RATE;
        if (SYN_CAP_EXTENDED(priv->capabilities))
                priv->mode |= SYN_BIT_W_MODE;
 
        if (synaptics_mode_cmd(psmouse, priv->mode))
                return -1;
 
+       if (priv->absolute_mode &&
+           synaptics_set_advanced_gesture_mode(psmouse)) {
+               psmouse_err(psmouse, "Advanced gesture mode init failed.\n");
+               return -1;
+       }
+
        return 0;
 }
 
@@ -301,26 +331,6 @@ static void synaptics_set_rate(struct psmouse *psmouse, unsigned int rate)
        synaptics_mode_cmd(psmouse, priv->mode);
 }
 
-static int synaptics_set_advanced_gesture_mode(struct psmouse *psmouse)
-{
-       static unsigned char param = 0xc8;
-       struct synaptics_data *priv = psmouse->private;
-
-       if (!(SYN_CAP_ADV_GESTURE(priv->ext_cap_0c) ||
-                       SYN_CAP_IMAGE_SENSOR(priv->ext_cap_0c)))
-               return 0;
-
-       if (psmouse_sliced_command(psmouse, SYN_QUE_MODEL))
-               return -1;
-       if (ps2_command(&psmouse->ps2dev, &param, PSMOUSE_CMD_SETRATE))
-               return -1;
-
-       /* Advanced gesture mode also sends multi finger data */
-       priv->capabilities |= BIT(1);
-
-       return 0;
-}
-
 /*****************************************************************************
  *     Synaptics pass-through PS/2 port support
  ****************************************************************************/
@@ -1248,8 +1258,24 @@ static void set_input_params(struct input_dev *dev, struct synaptics_data *priv)
 {
        int i;
 
+       /* Things that apply to both modes */
        __set_bit(INPUT_PROP_POINTER, dev->propbit);
+       __set_bit(EV_KEY, dev->evbit);
+       __set_bit(BTN_LEFT, dev->keybit);
+       __set_bit(BTN_RIGHT, dev->keybit);
 
+       if (SYN_CAP_MIDDLE_BUTTON(priv->capabilities))
+               __set_bit(BTN_MIDDLE, dev->keybit);
+
+       if (!priv->absolute_mode) {
+               /* Relative mode */
+               __set_bit(EV_REL, dev->evbit);
+               __set_bit(REL_X, dev->relbit);
+               __set_bit(REL_Y, dev->relbit);
+               return;
+       }
+
+       /* Absolute mode */
        __set_bit(EV_ABS, dev->evbit);
        set_abs_position_params(dev, priv, ABS_X, ABS_Y);
        input_set_abs_params(dev, ABS_PRESSURE, 0, 255, 0, 0);
@@ -1275,20 +1301,14 @@ static void set_input_params(struct input_dev *dev, struct synaptics_data *priv)
        if (SYN_CAP_PALMDETECT(priv->capabilities))
                input_set_abs_params(dev, ABS_TOOL_WIDTH, 0, 15, 0, 0);
 
-       __set_bit(EV_KEY, dev->evbit);
        __set_bit(BTN_TOUCH, dev->keybit);
        __set_bit(BTN_TOOL_FINGER, dev->keybit);
-       __set_bit(BTN_LEFT, dev->keybit);
-       __set_bit(BTN_RIGHT, dev->keybit);
 
        if (SYN_CAP_MULTIFINGER(priv->capabilities)) {
                __set_bit(BTN_TOOL_DOUBLETAP, dev->keybit);
                __set_bit(BTN_TOOL_TRIPLETAP, dev->keybit);
        }
 
-       if (SYN_CAP_MIDDLE_BUTTON(priv->capabilities))
-               __set_bit(BTN_MIDDLE, dev->keybit);
-
        if (SYN_CAP_FOUR_BUTTON(priv->capabilities) ||
            SYN_CAP_MIDDLE_BUTTON(priv->capabilities)) {
                __set_bit(BTN_FORWARD, dev->keybit);
@@ -1310,11 +1330,59 @@ static void set_input_params(struct input_dev *dev, struct synaptics_data *priv)
        }
 }
 
+static ssize_t synaptics_show_disable_gesture(struct psmouse *psmouse,
+                                             void *data, char *buf)
+{
+       struct synaptics_data *priv = psmouse->private;
+
+       return sprintf(buf, "%c\n", priv->disable_gesture ? '1' : '0');
+}
+
+static ssize_t synaptics_set_disable_gesture(struct psmouse *psmouse,
+                                            void *data, const char *buf,
+                                            size_t len)
+{
+       struct synaptics_data *priv = psmouse->private;
+       unsigned int value;
+       int err;
+
+       err = kstrtouint(buf, 10, &value);
+       if (err)
+               return err;
+
+       if (value > 1)
+               return -EINVAL;
+
+       if (value == priv->disable_gesture)
+               return len;
+
+       priv->disable_gesture = value;
+       if (value)
+               priv->mode |= SYN_BIT_DISABLE_GESTURE;
+       else
+               priv->mode &= ~SYN_BIT_DISABLE_GESTURE;
+
+       if (synaptics_mode_cmd(psmouse, priv->mode))
+               return -EIO;
+
+       return len;
+}
+
+PSMOUSE_DEFINE_ATTR(disable_gesture, S_IWUSR | S_IRUGO, NULL,
+                   synaptics_show_disable_gesture,
+                   synaptics_set_disable_gesture);
+
 static void synaptics_disconnect(struct psmouse *psmouse)
 {
+       struct synaptics_data *priv = psmouse->private;
+
+       if (!priv->absolute_mode && SYN_ID_DISGEST_SUPPORTED(priv->identity))
+               device_remove_file(&psmouse->ps2dev.serio->dev,
+                                  &psmouse_attr_disable_gesture.dattr);
+
        synaptics_free_led(psmouse);
        synaptics_reset(psmouse);
-       kfree(psmouse->private);
+       kfree(priv);
        psmouse->private = NULL;
 }
 
@@ -1351,17 +1419,11 @@ static int synaptics_reconnect(struct psmouse *psmouse)
                return -1;
        }
 
-       if (synaptics_set_absolute_mode(psmouse)) {
+       if (synaptics_set_mode(psmouse)) {
                psmouse_err(psmouse, "Unable to initialize device.\n");
                return -1;
        }
 
-       if (synaptics_set_advanced_gesture_mode(psmouse)) {
-               psmouse_err(psmouse,
-                           "Advanced gesture mode reconnect failed.\n");
-               return -1;
-       }
-
        if (old_priv.identity != priv->identity ||
            old_priv.model_id != priv->model_id ||
            old_priv.capabilities != priv->capabilities ||
@@ -1440,20 +1502,18 @@ void __init synaptics_module_init(void)
        broken_olpc_ec = dmi_check_system(olpc_dmi_table);
 }
 
-int synaptics_init(struct psmouse *psmouse)
+static int __synaptics_init(struct psmouse *psmouse, bool absolute_mode)
 {
        struct synaptics_data *priv;
+       int err = -1;
 
        /*
-        * The OLPC XO has issues with Synaptics' absolute mode; similarly to
-        * the HGPK, it quickly degrades and the hardware becomes jumpy and
-        * overly sensitive.  Not only that, but the constant packet spew
-        * (even at a lowered 40pps rate) overloads the EC such that key
-        * presses on the keyboard are missed.  Given all of that, don't
-        * even attempt to use Synaptics mode.  Relative mode seems to work
-        * just fine.
+        * The OLPC XO has issues with Synaptics' absolute mode; the constant
+        * packet spew overloads the EC such that key presses on the keyboard
+        * are missed.  Given that, don't even attempt to use Absolute mode.
+        * Relative mode seems to work just fine.
         */
-       if (broken_olpc_ec) {
+       if (absolute_mode && broken_olpc_ec) {
                psmouse_info(psmouse,
                             "OLPC XO detected, not enabling Synaptics protocol.\n");
                return -ENODEV;
@@ -1470,13 +1530,12 @@ int synaptics_init(struct psmouse *psmouse)
                goto init_fail;
        }
 
-       if (synaptics_set_absolute_mode(psmouse)) {
-               psmouse_err(psmouse, "Unable to initialize device.\n");
-               goto init_fail;
-       }
+       priv->absolute_mode = absolute_mode;
+       if (SYN_ID_DISGEST_SUPPORTED(priv->identity))
+               priv->disable_gesture = true;
 
-       if (synaptics_set_advanced_gesture_mode(psmouse)) {
-               psmouse_err(psmouse, "Advanced gesture mode init failed.\n");
+       if (synaptics_set_mode(psmouse)) {
+               psmouse_err(psmouse, "Unable to initialize device.\n");
                goto init_fail;
        }
 
@@ -1504,12 +1563,19 @@ int synaptics_init(struct psmouse *psmouse)
        psmouse->model = ((priv->model_id & 0x00ff0000) >> 8) |
                          (priv->model_id & 0x000000ff);
 
-       psmouse->protocol_handler = synaptics_process_byte;
+       if (absolute_mode) {
+               psmouse->protocol_handler = synaptics_process_byte;
+               psmouse->pktsize = 6;
+       } else {
+               /* Relative mode follows standard PS/2 mouse protocol */
+               psmouse->protocol_handler = psmouse_process_byte;
+               psmouse->pktsize = 3;
+       }
+
        psmouse->set_rate = synaptics_set_rate;
        psmouse->disconnect = synaptics_disconnect;
        psmouse->reconnect = synaptics_reconnect;
        psmouse->cleanup = synaptics_reset;
-       psmouse->pktsize = 6;
        /* Synaptics can usually stay in sync without extra help */
        psmouse->resync_time = 0;
 
@@ -1528,11 +1594,32 @@ int synaptics_init(struct psmouse *psmouse)
                psmouse->rate = 40;
        }
 
+       if (!priv->absolute_mode && SYN_ID_DISGEST_SUPPORTED(priv->identity)) {
+               err = device_create_file(&psmouse->ps2dev.serio->dev,
+                                        &psmouse_attr_disable_gesture.dattr);
+               if (err) {
+                       psmouse_err(psmouse,
+                                   "Failed to create disable_gesture attribute (%d)",
+                                   err);
+                       goto init_fail;
+               }
+       }
+
        return 0;
 
  init_fail:
        kfree(priv);
-       return -1;
+       return err;
+}
+
+int synaptics_init(struct psmouse *psmouse)
+{
+       return __synaptics_init(psmouse, true);
+}
+
+int synaptics_init_relative(struct psmouse *psmouse)
+{
+       return __synaptics_init(psmouse, false);
 }
 
 bool synaptics_supported(void)