]> asedeno.scripts.mit.edu Git - linux.git/commitdiff
Merge branch 'for-linus' of git://git.kernel.org/pub/scm/linux/kernel/git/dtor/input
authorLinus Torvalds <torvalds@linux-foundation.org>
Tue, 21 Apr 2015 19:54:08 +0000 (12:54 -0700)
committerLinus Torvalds <torvalds@linux-foundation.org>
Tue, 21 Apr 2015 19:54:08 +0000 (12:54 -0700)
Pull more input subsystem updates from Dmitry Torokhov:

 - an update to Atmel MXT driver that makes it functional on Google
   Pixel 2 boxes (both touchpad and touchscreen)

 - a new VMware VMMouse driver that should allow us drop X vmmouse
   driver that requires root privileges (since it accesses ioports)

 - XBox One controllers now support force feedback (rumble)

* 'for-linus' of git://git.kernel.org/pub/scm/linux/kernel/git/dtor/input:
  Input: lm8333 - fix broken email address
  Input: cyapa - fix setting suspend scan rate
  Input: elan_i2c - fix calculating number of x and y traces.
  Input: elan_i2c - report hovering contacts
  Input: elants_i2c - zero-extend hardware ID in firmware name
  Input: alps - document separate pointstick button bits for V2 devices
  Input: atmel_mxt_ts - add support for Google Pixel 2
  Input: xpad - add rumble support for Xbox One controller
  Input: ff-core - use new debug macros
  Input: add vmmouse driver
  Input: elan_i2c - adjust for newer firmware pressure reporting

18 files changed:
Documentation/input/alps.txt
MAINTAINERS
drivers/input/ff-core.c
drivers/input/joystick/xpad.c
drivers/input/keyboard/lm8333.c
drivers/input/mouse/Kconfig
drivers/input/mouse/Makefile
drivers/input/mouse/cyapa.c
drivers/input/mouse/elan_i2c.h
drivers/input/mouse/elan_i2c_core.c
drivers/input/mouse/elan_i2c_i2c.c
drivers/input/mouse/elan_i2c_smbus.c
drivers/input/mouse/psmouse-base.c
drivers/input/mouse/psmouse.h
drivers/input/mouse/vmmouse.c [new file with mode: 0644]
drivers/input/mouse/vmmouse.h [new file with mode: 0644]
drivers/input/touchscreen/atmel_mxt_ts.c
drivers/input/touchscreen/elants_i2c.c

index b9d229fee6b95b7d4836b9781c6b5650cb013435..c86f2f1ae4f6aa2d9af3e3987e8be06fd237dbef 100644 (file)
@@ -94,6 +94,10 @@ PS/2 packet format
 
 Note that the device never signals overflow condition.
 
+For protocol version 2 devices when the trackpoint is used, and no fingers
+are on the touchpad, the M R L bits signal the combined status of both the
+pointingstick and touchpad buttons.
+
 ALPS Absolute Mode - Protocol Version 1
 --------------------------------------
 
@@ -107,7 +111,7 @@ ALPS Absolute Mode - Protocol Version 1
 ALPS Absolute Mode - Protocol Version 2
 ---------------------------------------
 
- byte 0:  1    ?    ?    ?    1    ?    ?    ?
+ byte 0:  1    ?    ?    ?    1  PSM  PSR  PSL
  byte 1:  0   x6   x5   x4   x3   x2   x1   x0
  byte 2:  0  x10   x9   x8   x7    ?  fin  ges
  byte 3:  0   y9   y8   y7    1    M    R    L
@@ -115,7 +119,8 @@ ALPS Absolute Mode - Protocol Version 2
  byte 5:  0   z6   z5   z4   z3   z2   z1   z0
 
 Protocol Version 2 DualPoint devices send standard PS/2 mouse packets for
-the DualPoint Stick.
+the DualPoint Stick. For non interleaved dualpoint devices the pointingstick
+buttons get reported separately in the PSM, PSR and PSL bits.
 
 Dualpoint device -- interleaved packet format
 ---------------------------------------------
index 1af6b9a9a6d5773b84c98052773d737a89a7d708..b4b131a0b9399fab4dc194b0f88b485bc0d516b7 100644 (file)
@@ -10597,6 +10597,14 @@ L:     linux-kernel@vger.kernel.org
 S:     Maintained
 F:     drivers/misc/vmw_balloon.c
 
+VMWARE VMMOUSE SUBDRIVER
+M:     "VMware Graphics" <linux-graphics-maintainer@vmware.com>
+M:     "VMware, Inc." <pv-drivers@vmware.com>
+L:     linux-input@vger.kernel.org
+S:     Maintained
+F:     drivers/input/mouse/vmmouse.c
+F:     drivers/input/mouse/vmmouse.h
+
 VMWARE VMXNET3 ETHERNET DRIVER
 M:     Shreyas Bhatewara <sbhatewara@vmware.com>
 M:     "VMware, Inc." <pv-drivers@vmware.com>
index f50f6dd92274c3d25f686c02e5f710d71a9f4102..b81c88c434521020d46d7fe4d9337fc2b5921579 100644 (file)
@@ -23,8 +23,6 @@
 
 /* #define DEBUG */
 
-#define pr_fmt(fmt) KBUILD_BASENAME ": " fmt
-
 #include <linux/input.h>
 #include <linux/module.h>
 #include <linux/mutex.h>
@@ -116,7 +114,7 @@ int input_ff_upload(struct input_dev *dev, struct ff_effect *effect,
 
        if (effect->type < FF_EFFECT_MIN || effect->type > FF_EFFECT_MAX ||
            !test_bit(effect->type, dev->ffbit)) {
-               pr_debug("invalid or not supported effect type in upload\n");
+               dev_dbg(&dev->dev, "invalid or not supported effect type in upload\n");
                return -EINVAL;
        }
 
@@ -124,7 +122,7 @@ int input_ff_upload(struct input_dev *dev, struct ff_effect *effect,
            (effect->u.periodic.waveform < FF_WAVEFORM_MIN ||
             effect->u.periodic.waveform > FF_WAVEFORM_MAX ||
             !test_bit(effect->u.periodic.waveform, dev->ffbit))) {
-               pr_debug("invalid or not supported wave form in upload\n");
+               dev_dbg(&dev->dev, "invalid or not supported wave form in upload\n");
                return -EINVAL;
        }
 
@@ -246,7 +244,7 @@ static int flush_effects(struct input_dev *dev, struct file *file)
        struct ff_device *ff = dev->ff;
        int i;
 
-       pr_debug("flushing now\n");
+       dev_dbg(&dev->dev, "flushing now\n");
 
        mutex_lock(&ff->mutex);
 
@@ -316,7 +314,7 @@ int input_ff_create(struct input_dev *dev, unsigned int max_effects)
        int i;
 
        if (!max_effects) {
-               pr_err("cannot allocate device without any effects\n");
+               dev_err(&dev->dev, "cannot allocate device without any effects\n");
                return -EINVAL;
        }
 
index 3aa2f3f3da5bcb2b3e8ad781b0fd3d07fd2c66e3..61c7611563712d9cfc829217701061af4bcdd3fa 100644 (file)
  *  - the iForce driver    drivers/char/joystick/iforce.c
  *  - the skeleton-driver  drivers/usb/usb-skeleton.c
  *  - Xbox 360 information http://www.free60.org/wiki/Gamepad
+ *  - Xbox One information https://github.com/quantus/xbox-one-controller-protocol
  *
  * Thanks to:
  *  - ITO Takayuki for providing essential xpad information on his website
  *  - Vojtech Pavlik     - iforce driver / input subsystem
  *  - Greg Kroah-Hartman - usb-skeleton driver
  *  - XBOX Linux project - extra USB id's
+ *  - Pekka Pöyry (quantus) - Xbox One controller reverse engineering
  *
  * TODO:
  *  - fine tune axes (especially trigger axes)
@@ -828,6 +830,23 @@ static int xpad_play_effect(struct input_dev *dev, void *data, struct ff_effect
 
                        return usb_submit_urb(xpad->irq_out, GFP_ATOMIC);
 
+               case XTYPE_XBOXONE:
+                       xpad->odata[0] = 0x09; /* activate rumble */
+                       xpad->odata[1] = 0x08;
+                       xpad->odata[2] = 0x00;
+                       xpad->odata[3] = 0x08; /* continuous effect */
+                       xpad->odata[4] = 0x00; /* simple rumble mode */
+                       xpad->odata[5] = 0x03; /* L and R actuator only */
+                       xpad->odata[6] = 0x00; /* TODO: LT actuator */
+                       xpad->odata[7] = 0x00; /* TODO: RT actuator */
+                       xpad->odata[8] = strong / 256;  /* left actuator */
+                       xpad->odata[9] = weak / 256;    /* right actuator */
+                       xpad->odata[10] = 0x80; /* length of pulse */
+                       xpad->odata[11] = 0x00; /* stop period of pulse */
+                       xpad->irq_out->transfer_buffer_length = 12;
+
+                       return usb_submit_urb(xpad->irq_out, GFP_ATOMIC);
+
                default:
                        dev_dbg(&xpad->dev->dev,
                                "%s - rumble command sent to unsupported xpad type: %d\n",
@@ -841,7 +860,7 @@ static int xpad_play_effect(struct input_dev *dev, void *data, struct ff_effect
 
 static int xpad_init_ff(struct usb_xpad *xpad)
 {
-       if (xpad->xtype == XTYPE_UNKNOWN || xpad->xtype == XTYPE_XBOXONE)
+       if (xpad->xtype == XTYPE_UNKNOWN)
                return 0;
 
        input_set_capability(xpad->dev, EV_FF, FF_RUMBLE);
index 9081cbef11ea126d4c46097416dce9cff21aa799..0ad422b8a2607c3cae027ca7c54b3cf0331094c3 100644 (file)
@@ -1,6 +1,6 @@
 /*
  * LM8333 keypad driver
- * Copyright (C) 2012 Wolfram Sang, Pengutronix <w.sang@pengutronix.de>
+ * Copyright (C) 2012 Wolfram Sang, Pengutronix <kernel@pengutronix.de>
  *
  * This program is free software; you can redistribute it and/or modify
  * it under the terms of the GNU General Public License as published by
@@ -231,6 +231,6 @@ static struct i2c_driver lm8333_driver = {
 };
 module_i2c_driver(lm8333_driver);
 
-MODULE_AUTHOR("Wolfram Sang <w.sang@pengutronix.de>");
+MODULE_AUTHOR("Wolfram Sang <kernel@pengutronix.de>");
 MODULE_DESCRIPTION("LM8333 keyboard driver");
 MODULE_LICENSE("GPL v2");
index 4658b5d41dd715360e959f88c8810182673a20cc..7462d2fc8cfed8d4bf11d77fb2bb5e853d9f00eb 100644 (file)
@@ -149,6 +149,18 @@ config MOUSE_PS2_FOCALTECH
 
          If unsure, say Y.
 
+config MOUSE_PS2_VMMOUSE
+       bool "Virtual mouse (vmmouse)"
+       depends on MOUSE_PS2 && X86 && HYPERVISOR_GUEST
+       help
+         Say Y here if you are running under control of VMware hypervisor
+         (ESXi, Workstation or Fusion). Also make sure that when you enable
+         this option, you remove the xf86-input-vmmouse user-space driver
+         or upgrade it to at least xf86-input-vmmouse 13.0.1, which doesn't
+         load in the presence of an in-kernel vmmouse driver.
+
+         If unsure, say N.
+
 config MOUSE_SERIAL
        tristate "Serial mouse"
        select SERIO
index 8a9c98e76d9c148cfe7e424c975f02c47478f945..793300bfbddda44aba927fc4ce28650da3389b0b 100644 (file)
@@ -36,6 +36,7 @@ psmouse-$(CONFIG_MOUSE_PS2_SENTELIC)  += sentelic.o
 psmouse-$(CONFIG_MOUSE_PS2_TRACKPOINT) += trackpoint.o
 psmouse-$(CONFIG_MOUSE_PS2_TOUCHKIT)   += touchkit_ps2.o
 psmouse-$(CONFIG_MOUSE_PS2_CYPRESS)    += cypress_ps2.o
+psmouse-$(CONFIG_MOUSE_PS2_VMMOUSE)    += vmmouse.o
 
 elan_i2c-objs := elan_i2c_core.o
 elan_i2c-$(CONFIG_MOUSE_ELAN_I2C_I2C)  += elan_i2c_i2c.o
index 58f4f6fa4857c34eb6a831154f039e063653992a..efe148474e7f6b4dcf758ac628bf2f081a18d6c4 100644 (file)
@@ -723,7 +723,7 @@ static ssize_t cyapa_update_suspend_scanrate(struct device *dev,
        } else if (sysfs_streq(buf, OFF_MODE_NAME)) {
                cyapa->suspend_power_mode = PWR_MODE_OFF;
        } else if (!kstrtou16(buf, 10, &sleep_time)) {
-               cyapa->suspend_sleep_time = max_t(u16, sleep_time, 1000);
+               cyapa->suspend_sleep_time = min_t(u16, sleep_time, 1000);
                cyapa->suspend_power_mode =
                        cyapa_sleep_time_to_pwr_cmd(cyapa->suspend_sleep_time);
        } else {
@@ -840,7 +840,7 @@ static ssize_t cyapa_update_rt_suspend_scanrate(struct device *dev,
        if (error)
                return error;
 
-       cyapa->runtime_suspend_sleep_time = max_t(u16, time, 1000);
+       cyapa->runtime_suspend_sleep_time = min_t(u16, time, 1000);
        cyapa->runtime_suspend_power_mode =
                cyapa_sleep_time_to_pwr_cmd(cyapa->runtime_suspend_sleep_time);
 
index 9b2dc015f20c8a24d65bb1019a2e39e8e1bf1279..6d5f8a4c1748861b547d218b4eda3ab14de5da5e 100644 (file)
@@ -25,6 +25,7 @@
 #define ETP_ENABLE_CALIBRATE   0x0002
 #define ETP_DISABLE_CALIBRATE  0x0000
 #define ETP_DISABLE_POWER      0x0001
+#define ETP_PRESSURE_OFFSET    25
 
 /* IAP Firmware handling */
 #define ETP_FW_NAME            "elan_i2c.bin"
@@ -79,6 +80,8 @@ struct elan_transport_ops {
                                struct completion *reset_done);
 
        int (*get_report)(struct i2c_client *client, u8 *report);
+       int (*get_pressure_adjustment)(struct i2c_client *client,
+                                      int *adjustment);
 };
 
 extern const struct elan_transport_ops elan_smbus_ops, elan_i2c_ops;
index 375d98f47483d309f35a561de11a376982713b17..fd5068b2542db7d58df4900058376f7285243c1e 100644 (file)
@@ -4,7 +4,7 @@
  * Copyright (c) 2013 ELAN Microelectronics Corp.
  *
  * Author: ćž—政維 (Duson Lin) <dusonlin@emc.com.tw>
- * Version: 1.5.6
+ * Version: 1.5.7
  *
  * Based on cyapa driver:
  * copyright (c) 2011-2012 Cypress Semiconductor, Inc.
@@ -40,8 +40,7 @@
 #include "elan_i2c.h"
 
 #define DRIVER_NAME            "elan_i2c"
-#define ELAN_DRIVER_VERSION    "1.5.6"
-#define ETP_PRESSURE_OFFSET    25
+#define ELAN_DRIVER_VERSION    "1.5.7"
 #define ETP_MAX_PRESSURE       255
 #define ETP_FWIDTH_REDUCE      90
 #define ETP_FINGER_WIDTH       15
@@ -53,6 +52,7 @@
 #define ETP_REPORT_ID_OFFSET   2
 #define ETP_TOUCH_INFO_OFFSET  3
 #define ETP_FINGER_DATA_OFFSET 4
+#define ETP_HOVER_INFO_OFFSET  30
 #define ETP_MAX_REPORT_LEN     34
 
 /* The main device structure */
@@ -81,7 +81,7 @@ struct elan_tp_data {
        u8                      sm_version;
        u8                      iap_version;
        u16                     fw_checksum;
-
+       int                     pressure_adjustment;
        u8                      mode;
 
        bool                    irq_wake;
@@ -229,6 +229,11 @@ static int elan_query_device_info(struct elan_tp_data *data)
        if (error)
                return error;
 
+       error = data->ops->get_pressure_adjustment(data->client,
+                                                  &data->pressure_adjustment);
+       if (error)
+               return error;
+
        return 0;
 }
 
@@ -721,13 +726,13 @@ static const struct attribute_group *elan_sysfs_groups[] = {
  */
 static void elan_report_contact(struct elan_tp_data *data,
                                int contact_num, bool contact_valid,
-                               u8 *finger_data)
+                               bool hover_event, u8 *finger_data)
 {
        struct input_dev *input = data->input;
        unsigned int pos_x, pos_y;
        unsigned int pressure, mk_x, mk_y;
-       unsigned int area_x, area_y, major, minor, new_pressure;
-
+       unsigned int area_x, area_y, major, minor;
+       unsigned int scaled_pressure;
 
        if (contact_valid) {
                pos_x = ((finger_data[0] & 0xf0) << 4) |
@@ -756,15 +761,18 @@ static void elan_report_contact(struct elan_tp_data *data,
                major = max(area_x, area_y);
                minor = min(area_x, area_y);
 
-               new_pressure = pressure + ETP_PRESSURE_OFFSET;
-               if (new_pressure > ETP_MAX_PRESSURE)
-                       new_pressure = ETP_MAX_PRESSURE;
+               scaled_pressure = pressure + data->pressure_adjustment;
+
+               if (scaled_pressure > ETP_MAX_PRESSURE)
+                       scaled_pressure = ETP_MAX_PRESSURE;
 
                input_mt_slot(input, contact_num);
                input_mt_report_slot_state(input, MT_TOOL_FINGER, true);
                input_report_abs(input, ABS_MT_POSITION_X, pos_x);
                input_report_abs(input, ABS_MT_POSITION_Y, data->max_y - pos_y);
-               input_report_abs(input, ABS_MT_PRESSURE, new_pressure);
+               input_report_abs(input, ABS_MT_DISTANCE, hover_event);
+               input_report_abs(input, ABS_MT_PRESSURE,
+                                hover_event ? 0 : scaled_pressure);
                input_report_abs(input, ABS_TOOL_WIDTH, mk_x);
                input_report_abs(input, ABS_MT_TOUCH_MAJOR, major);
                input_report_abs(input, ABS_MT_TOUCH_MINOR, minor);
@@ -780,11 +788,14 @@ static void elan_report_absolute(struct elan_tp_data *data, u8 *packet)
        u8 *finger_data = &packet[ETP_FINGER_DATA_OFFSET];
        int i;
        u8 tp_info = packet[ETP_TOUCH_INFO_OFFSET];
-       bool contact_valid;
+       u8 hover_info = packet[ETP_HOVER_INFO_OFFSET];
+       bool contact_valid, hover_event;
 
+       hover_event = hover_info & 0x40;
        for (i = 0; i < ETP_MAX_FINGERS; i++) {
                contact_valid = tp_info & (1U << (3 + i));
-               elan_report_contact(data, i, contact_valid, finger_data);
+               elan_report_contact(data, i, contact_valid, hover_event,
+                                   finger_data);
 
                if (contact_valid)
                        finger_data += ETP_FINGER_DATA_LEN;
@@ -878,6 +889,7 @@ static int elan_setup_input_device(struct elan_tp_data *data)
                             ETP_FINGER_WIDTH * max_width, 0, 0);
        input_set_abs_params(input, ABS_MT_TOUCH_MINOR, 0,
                             ETP_FINGER_WIDTH * min_width, 0, 0);
+       input_set_abs_params(input, ABS_MT_DISTANCE, 0, 1, 0, 0);
 
        data->input = input;
 
index 6cf0def6d35ee764cb686bb5c9d2f90bb9f81a86..a0acbbf83bfd4faaa7d14a09dbe332848dc648e4 100644 (file)
@@ -41,6 +41,7 @@
 #define ETP_I2C_MAX_X_AXIS_CMD         0x0106
 #define ETP_I2C_MAX_Y_AXIS_CMD         0x0107
 #define ETP_I2C_RESOLUTION_CMD         0x0108
+#define ETP_I2C_PRESSURE_CMD           0x010A
 #define ETP_I2C_IAP_VERSION_CMD                0x0110
 #define ETP_I2C_SET_CMD                        0x0300
 #define ETP_I2C_POWER_CMD              0x0307
@@ -364,8 +365,29 @@ static int elan_i2c_get_num_traces(struct i2c_client *client,
                return error;
        }
 
-       *x_traces = val[0] - 1;
-       *y_traces = val[1] - 1;
+       *x_traces = val[0];
+       *y_traces = val[1];
+
+       return 0;
+}
+
+static int elan_i2c_get_pressure_adjustment(struct i2c_client *client,
+                                           int *adjustment)
+{
+       int error;
+       u8 val[3];
+
+       error = elan_i2c_read_cmd(client, ETP_I2C_PRESSURE_CMD, val);
+       if (error) {
+               dev_err(&client->dev, "failed to get pressure format: %d\n",
+                       error);
+               return error;
+       }
+
+       if ((val[0] >> 4) & 0x1)
+               *adjustment = 0;
+       else
+               *adjustment = ETP_PRESSURE_OFFSET;
 
        return 0;
 }
@@ -602,6 +624,7 @@ const struct elan_transport_ops elan_i2c_ops = {
        .get_sm_version         = elan_i2c_get_sm_version,
        .get_product_id         = elan_i2c_get_product_id,
        .get_checksum           = elan_i2c_get_checksum,
+       .get_pressure_adjustment = elan_i2c_get_pressure_adjustment,
 
        .get_max                = elan_i2c_get_max,
        .get_resolution         = elan_i2c_get_resolution,
index 06a2bcd1cda267f87cfe11b8eb09fe4d8bb52aaf..30ab80dbcdd6e013d6854c63fb7bbe1874169596 100644 (file)
@@ -268,12 +268,19 @@ static int elan_smbus_get_num_traces(struct i2c_client *client,
                return error;
        }
 
-       *x_traces = val[1] - 1;
-       *y_traces = val[2] - 1;
+       *x_traces = val[1];
+       *y_traces = val[2];
 
        return 0;
 }
 
+static int elan_smbus_get_pressure_adjustment(struct i2c_client *client,
+                                             int *adjustment)
+{
+       *adjustment = ETP_PRESSURE_OFFSET;
+       return 0;
+}
+
 static int elan_smbus_iap_get_mode(struct i2c_client *client,
                                   enum tp_mode *mode)
 {
@@ -497,6 +504,7 @@ const struct elan_transport_ops elan_smbus_ops = {
        .get_sm_version         = elan_smbus_get_sm_version,
        .get_product_id         = elan_smbus_get_product_id,
        .get_checksum           = elan_smbus_get_checksum,
+       .get_pressure_adjustment = elan_smbus_get_pressure_adjustment,
 
        .get_max                = elan_smbus_get_max,
        .get_resolution         = elan_smbus_get_resolution,
index 27057df7ba74eeeba06f24f0e24fcf6d4387eda3..5bb1658f60c746f29efd4f5d7008068701f4d08d 100644 (file)
@@ -36,6 +36,7 @@
 #include "sentelic.h"
 #include "cypress_ps2.h"
 #include "focaltech.h"
+#include "vmmouse.h"
 
 #define DRIVER_DESC    "PS/2 mouse driver"
 
@@ -790,6 +791,13 @@ static int psmouse_extensions(struct psmouse *psmouse,
                }
        }
 
+       if (psmouse_do_detect(vmmouse_detect, psmouse, set_properties) == 0) {
+               if (max_proto > PSMOUSE_IMEX) {
+                       if (!set_properties || vmmouse_init(psmouse) == 0)
+                               return PSMOUSE_VMMOUSE;
+               }
+       }
+
 /*
  * Try Kensington ThinkingMouse (we try first, because synaptics probe
  * upsets the thinkingmouse).
@@ -1112,6 +1120,15 @@ static const struct psmouse_protocol psmouse_protocols[] = {
                .detect         = focaltech_detect,
                .init           = focaltech_init,
        },
+#endif
+#ifdef CONFIG_MOUSE_PS2_VMMOUSE
+       {
+               .type           = PSMOUSE_VMMOUSE,
+               .name           = VMMOUSE_PSNAME,
+               .alias          = "vmmouse",
+               .detect         = vmmouse_detect,
+               .init           = vmmouse_init,
+       },
 #endif
        {
                .type           = PSMOUSE_AUTO,
index d02e1bdc9ae4934d56e5290f8adedcb3a2297566..ad5a5a1ea872cd17389d623e109e0299b90f1429 100644 (file)
@@ -103,6 +103,7 @@ enum psmouse_type {
        PSMOUSE_SYNAPTICS_RELATIVE,
        PSMOUSE_CYPRESS,
        PSMOUSE_FOCALTECH,
+       PSMOUSE_VMMOUSE,
        PSMOUSE_AUTO            /* This one should always be last */
 };
 
diff --git a/drivers/input/mouse/vmmouse.c b/drivers/input/mouse/vmmouse.c
new file mode 100644 (file)
index 0000000..e272f06
--- /dev/null
@@ -0,0 +1,508 @@
+/*
+ * Driver for Virtual PS/2 Mouse on VMware and QEMU hypervisors.
+ *
+ * Copyright (C) 2014, VMware, Inc. All Rights Reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 as published by
+ * the Free Software Foundation.
+ *
+ * Twin device code is hugely inspired by the ALPS driver.
+ * Authors:
+ *   Dmitry Torokhov <dmitry.torokhov@gmail.com>
+ *   Thomas Hellstrom <thellstrom@vmware.com>
+ */
+
+#include <linux/input.h>
+#include <linux/serio.h>
+#include <linux/libps2.h>
+#include <linux/slab.h>
+#include <linux/module.h>
+#include <asm/hypervisor.h>
+
+#include "psmouse.h"
+#include "vmmouse.h"
+
+#define VMMOUSE_PROTO_MAGIC                    0x564D5868U
+#define VMMOUSE_PROTO_PORT                     0x5658
+
+/*
+ * Main commands supported by the vmmouse hypervisor port.
+ */
+#define VMMOUSE_PROTO_CMD_GETVERSION           10
+#define VMMOUSE_PROTO_CMD_ABSPOINTER_DATA      39
+#define VMMOUSE_PROTO_CMD_ABSPOINTER_STATUS    40
+#define VMMOUSE_PROTO_CMD_ABSPOINTER_COMMAND   41
+#define VMMOUSE_PROTO_CMD_ABSPOINTER_RESTRICT   86
+
+/*
+ * Subcommands for VMMOUSE_PROTO_CMD_ABSPOINTER_COMMAND
+ */
+#define VMMOUSE_CMD_ENABLE                     0x45414552U
+#define VMMOUSE_CMD_DISABLE                    0x000000f5U
+#define VMMOUSE_CMD_REQUEST_RELATIVE           0x4c455252U
+#define VMMOUSE_CMD_REQUEST_ABSOLUTE           0x53424152U
+
+#define VMMOUSE_ERROR                          0xffff0000U
+
+#define VMMOUSE_VERSION_ID                     0x3442554aU
+
+#define VMMOUSE_RELATIVE_PACKET                        0x00010000U
+
+#define VMMOUSE_LEFT_BUTTON                    0x20
+#define VMMOUSE_RIGHT_BUTTON                   0x10
+#define VMMOUSE_MIDDLE_BUTTON                  0x08
+
+/*
+ * VMMouse Restrict command
+ */
+#define VMMOUSE_RESTRICT_ANY                    0x00
+#define VMMOUSE_RESTRICT_CPL0                   0x01
+#define VMMOUSE_RESTRICT_IOPL                   0x02
+
+#define VMMOUSE_MAX_X                           0xFFFF
+#define VMMOUSE_MAX_Y                           0xFFFF
+
+#define VMMOUSE_VENDOR "VMware"
+#define VMMOUSE_NAME   "VMMouse"
+
+/**
+ * struct vmmouse_data - private data structure for the vmmouse driver
+ *
+ * @abs_dev: "Absolute" device used to report absolute mouse movement.
+ * @phys: Physical path for the absolute device.
+ * @dev_name: Name attribute name for the absolute device.
+ */
+struct vmmouse_data {
+       struct input_dev *abs_dev;
+       char phys[32];
+       char dev_name[128];
+};
+
+/**
+ * Hypervisor-specific bi-directional communication channel
+ * implementing the vmmouse protocol. Should never execute on
+ * bare metal hardware.
+ */
+#define VMMOUSE_CMD(cmd, in1, out1, out2, out3, out4)  \
+({                                                     \
+       unsigned long __dummy1, __dummy2;               \
+       __asm__ __volatile__ ("inl %%dx" :              \
+               "=a"(out1),                             \
+               "=b"(out2),                             \
+               "=c"(out3),                             \
+               "=d"(out4),                             \
+               "=S"(__dummy1),                         \
+               "=D"(__dummy2) :                        \
+               "a"(VMMOUSE_PROTO_MAGIC),               \
+               "b"(in1),                               \
+               "c"(VMMOUSE_PROTO_CMD_##cmd),           \
+               "d"(VMMOUSE_PROTO_PORT) :               \
+               "memory");                              \
+})
+
+/**
+ * vmmouse_report_button - report button state on the correct input device
+ *
+ * @psmouse:  Pointer to the psmouse struct
+ * @abs_dev:  The absolute input device
+ * @rel_dev:  The relative input device
+ * @pref_dev: The preferred device for reporting
+ * @code:     Button code
+ * @value:    Button value
+ *
+ * Report @value and @code on @pref_dev, unless the button is already
+ * pressed on the other device, in which case the state is reported on that
+ * device.
+ */
+static void vmmouse_report_button(struct psmouse *psmouse,
+                                 struct input_dev *abs_dev,
+                                 struct input_dev *rel_dev,
+                                 struct input_dev *pref_dev,
+                                 unsigned int code, int value)
+{
+       if (test_bit(code, abs_dev->key))
+               pref_dev = abs_dev;
+       else if (test_bit(code, rel_dev->key))
+               pref_dev = rel_dev;
+
+       input_report_key(pref_dev, code, value);
+}
+
+/**
+ * vmmouse_report_events - process events on the vmmouse communications channel
+ *
+ * @psmouse: Pointer to the psmouse struct
+ *
+ * This function pulls events from the vmmouse communications channel and
+ * reports them on the correct (absolute or relative) input device. When the
+ * communications channel is drained, or if we've processed more than 255
+ * psmouse commands, the function returns PSMOUSE_FULL_PACKET. If there is a
+ * host- or synchronization error, the function returns PSMOUSE_BAD_DATA in
+ * the hope that the caller will reset the communications channel.
+ */
+static psmouse_ret_t vmmouse_report_events(struct psmouse *psmouse)
+{
+       struct input_dev *rel_dev = psmouse->dev;
+       struct vmmouse_data *priv = psmouse->private;
+       struct input_dev *abs_dev = priv->abs_dev;
+       struct input_dev *pref_dev;
+       u32 status, x, y, z;
+       u32 dummy1, dummy2, dummy3;
+       unsigned int queue_length;
+       unsigned int count = 255;
+
+       while (count--) {
+               /* See if we have motion data. */
+               VMMOUSE_CMD(ABSPOINTER_STATUS, 0,
+                           status, dummy1, dummy2, dummy3);
+               if ((status & VMMOUSE_ERROR) == VMMOUSE_ERROR) {
+                       psmouse_err(psmouse, "failed to fetch status data\n");
+                       /*
+                        * After a few attempts this will result in
+                        * reconnect.
+                        */
+                       return PSMOUSE_BAD_DATA;
+               }
+
+               queue_length = status & 0xffff;
+               if (queue_length == 0)
+                       break;
+
+               if (queue_length % 4) {
+                       psmouse_err(psmouse, "invalid queue length\n");
+                       return PSMOUSE_BAD_DATA;
+               }
+
+               /* Now get it */
+               VMMOUSE_CMD(ABSPOINTER_DATA, 4, status, x, y, z);
+
+               /*
+                * And report what we've got. Prefer to report button
+                * events on the same device where we report motion events.
+                * This doesn't work well with the mouse wheel, though. See
+                * below. Ideally we would want to report that on the
+                * preferred device as well.
+                */
+               if (status & VMMOUSE_RELATIVE_PACKET) {
+                       pref_dev = rel_dev;
+                       input_report_rel(rel_dev, REL_X, (s32)x);
+                       input_report_rel(rel_dev, REL_Y, -(s32)y);
+               } else {
+                       pref_dev = abs_dev;
+                       input_report_abs(abs_dev, ABS_X, x);
+                       input_report_abs(abs_dev, ABS_Y, y);
+               }
+
+               /* Xorg seems to ignore wheel events on absolute devices */
+               input_report_rel(rel_dev, REL_WHEEL, -(s8)((u8) z));
+
+               vmmouse_report_button(psmouse, abs_dev, rel_dev,
+                                     pref_dev, BTN_LEFT,
+                                     status & VMMOUSE_LEFT_BUTTON);
+               vmmouse_report_button(psmouse, abs_dev, rel_dev,
+                                     pref_dev, BTN_RIGHT,
+                                     status & VMMOUSE_RIGHT_BUTTON);
+               vmmouse_report_button(psmouse, abs_dev, rel_dev,
+                                     pref_dev, BTN_MIDDLE,
+                                     status & VMMOUSE_MIDDLE_BUTTON);
+               input_sync(abs_dev);
+               input_sync(rel_dev);
+       }
+
+       return PSMOUSE_FULL_PACKET;
+}
+
+/**
+ * vmmouse_process_byte - process data on the ps/2 channel
+ *
+ * @psmouse: Pointer to the psmouse struct
+ *
+ * When the ps/2 channel indicates that there is vmmouse data available,
+ * call vmmouse channel processing. Otherwise, continue to accept bytes. If
+ * there is a synchronization or communication data error, return
+ * PSMOUSE_BAD_DATA in the hope that the caller will reset the mouse.
+ */
+static psmouse_ret_t vmmouse_process_byte(struct psmouse *psmouse)
+{
+       unsigned char *packet = psmouse->packet;
+
+       switch (psmouse->pktcnt) {
+       case 1:
+               return (packet[0] & 0x8) == 0x8 ?
+                       PSMOUSE_GOOD_DATA : PSMOUSE_BAD_DATA;
+
+       case 2:
+               return PSMOUSE_GOOD_DATA;
+
+       default:
+               return vmmouse_report_events(psmouse);
+       }
+}
+
+/**
+ * vmmouse_disable - Disable vmmouse
+ *
+ * @psmouse: Pointer to the psmouse struct
+ *
+ * Tries to disable vmmouse mode.
+ */
+static void vmmouse_disable(struct psmouse *psmouse)
+{
+       u32 status;
+       u32 dummy1, dummy2, dummy3, dummy4;
+
+       VMMOUSE_CMD(ABSPOINTER_COMMAND, VMMOUSE_CMD_DISABLE,
+                   dummy1, dummy2, dummy3, dummy4);
+
+       VMMOUSE_CMD(ABSPOINTER_STATUS, 0,
+                   status, dummy1, dummy2, dummy3);
+
+       if ((status & VMMOUSE_ERROR) != VMMOUSE_ERROR)
+               psmouse_warn(psmouse, "failed to disable vmmouse device\n");
+}
+
+/**
+ * vmmouse_enable - Enable vmmouse and request absolute mode.
+ *
+ * @psmouse: Pointer to the psmouse struct
+ *
+ * Tries to enable vmmouse mode. Performs basic checks and requests
+ * absolute vmmouse mode.
+ * Returns 0 on success, -ENODEV on failure.
+ */
+static int vmmouse_enable(struct psmouse *psmouse)
+{
+       u32 status, version;
+       u32 dummy1, dummy2, dummy3, dummy4;
+
+       /*
+        * Try enabling the device. If successful, we should be able to
+        * read valid version ID back from it.
+        */
+       VMMOUSE_CMD(ABSPOINTER_COMMAND, VMMOUSE_CMD_ENABLE,
+                   dummy1, dummy2, dummy3, dummy4);
+
+       /*
+        * See if version ID can be retrieved.
+        */
+       VMMOUSE_CMD(ABSPOINTER_STATUS, 0, status, dummy1, dummy2, dummy3);
+       if ((status & 0x0000ffff) == 0) {
+               psmouse_dbg(psmouse, "empty flags - assuming no device\n");
+               return -ENXIO;
+       }
+
+       VMMOUSE_CMD(ABSPOINTER_DATA, 1 /* single item */,
+                   version, dummy1, dummy2, dummy3);
+       if (version != VMMOUSE_VERSION_ID) {
+               psmouse_dbg(psmouse, "Unexpected version value: %u vs %u\n",
+                           (unsigned) version, VMMOUSE_VERSION_ID);
+               vmmouse_disable(psmouse);
+               return -ENXIO;
+       }
+
+       /*
+        * Restrict ioport access, if possible.
+        */
+       VMMOUSE_CMD(ABSPOINTER_RESTRICT, VMMOUSE_RESTRICT_CPL0,
+                   dummy1, dummy2, dummy3, dummy4);
+
+       VMMOUSE_CMD(ABSPOINTER_COMMAND, VMMOUSE_CMD_REQUEST_ABSOLUTE,
+                   dummy1, dummy2, dummy3, dummy4);
+
+       return 0;
+}
+
+/*
+ * Array of supported hypervisors.
+ */
+static const struct hypervisor_x86 *vmmouse_supported_hypervisors[] = {
+       &x86_hyper_vmware,
+#ifdef CONFIG_KVM_GUEST
+       &x86_hyper_kvm,
+#endif
+};
+
+/**
+ * vmmouse_check_hypervisor - Check if we're running on a supported hypervisor
+ */
+static bool vmmouse_check_hypervisor(void)
+{
+       int i;
+
+       for (i = 0; i < ARRAY_SIZE(vmmouse_supported_hypervisors); i++)
+               if (vmmouse_supported_hypervisors[i] == x86_hyper)
+                       return true;
+
+       return false;
+}
+
+/**
+ * vmmouse_detect - Probe whether vmmouse is available
+ *
+ * @psmouse: Pointer to the psmouse struct
+ * @set_properties: Whether to set psmouse name and vendor
+ *
+ * Returns 0 if vmmouse channel is available. Negative error code if not.
+ */
+int vmmouse_detect(struct psmouse *psmouse, bool set_properties)
+{
+       u32 response, version, dummy1, dummy2;
+
+       if (!vmmouse_check_hypervisor()) {
+               psmouse_dbg(psmouse,
+                           "VMMouse not running on supported hypervisor.\n");
+               return -ENXIO;
+       }
+
+       if (!request_region(VMMOUSE_PROTO_PORT, 4, "vmmouse")) {
+               psmouse_dbg(psmouse, "VMMouse port in use.\n");
+               return -EBUSY;
+       }
+
+       /* Check if the device is present */
+       response = ~VMMOUSE_PROTO_MAGIC;
+       VMMOUSE_CMD(GETVERSION, 0, version, response, dummy1, dummy2);
+       if (response != VMMOUSE_PROTO_MAGIC || version == 0xffffffffU) {
+               release_region(VMMOUSE_PROTO_PORT, 4);
+               return -ENXIO;
+       }
+
+       if (set_properties) {
+               psmouse->vendor = VMMOUSE_VENDOR;
+               psmouse->name = VMMOUSE_NAME;
+               psmouse->model = version;
+       }
+
+       release_region(VMMOUSE_PROTO_PORT, 4);
+
+       return 0;
+}
+
+/**
+ * vmmouse_disconnect - Take down vmmouse driver
+ *
+ * @psmouse: Pointer to the psmouse struct
+ *
+ * Takes down vmmouse driver and frees resources set up in vmmouse_init().
+ */
+static void vmmouse_disconnect(struct psmouse *psmouse)
+{
+       struct vmmouse_data *priv = psmouse->private;
+
+       vmmouse_disable(psmouse);
+       psmouse_reset(psmouse);
+       input_unregister_device(priv->abs_dev);
+       kfree(priv);
+       release_region(VMMOUSE_PROTO_PORT, 4);
+}
+
+/**
+ * vmmouse_reconnect - Reset the ps/2 - and vmmouse connections
+ *
+ * @psmouse: Pointer to the psmouse struct
+ *
+ * Attempts to reset the mouse connections. Returns 0 on success and
+ * -1 on failure.
+ */
+static int vmmouse_reconnect(struct psmouse *psmouse)
+{
+       int error;
+
+       psmouse_reset(psmouse);
+       vmmouse_disable(psmouse);
+       error = vmmouse_enable(psmouse);
+       if (error) {
+               psmouse_err(psmouse,
+                           "Unable to re-enable mouse when reconnecting, err: %d\n",
+                           error);
+               return error;
+       }
+
+       return 0;
+}
+
+/**
+ * vmmouse_init - Initialize the vmmouse driver
+ *
+ * @psmouse: Pointer to the psmouse struct
+ *
+ * Requests the device and tries to enable vmmouse mode.
+ * If successful, sets up the input device for relative movement events.
+ * It also allocates another input device and sets it up for absolute motion
+ * events. Returns 0 on success and -1 on failure.
+ */
+int vmmouse_init(struct psmouse *psmouse)
+{
+       struct vmmouse_data *priv;
+       struct input_dev *rel_dev = psmouse->dev, *abs_dev;
+       int error;
+
+       if (!request_region(VMMOUSE_PROTO_PORT, 4, "vmmouse")) {
+               psmouse_dbg(psmouse, "VMMouse port in use.\n");
+               return -EBUSY;
+       }
+
+       psmouse_reset(psmouse);
+       error = vmmouse_enable(psmouse);
+       if (error)
+               goto release_region;
+
+       priv = kzalloc(sizeof(*priv), GFP_KERNEL);
+       abs_dev = input_allocate_device();
+       if (!priv || !abs_dev) {
+               error = -ENOMEM;
+               goto init_fail;
+       }
+
+       priv->abs_dev = abs_dev;
+       psmouse->private = priv;
+
+       input_set_capability(rel_dev, EV_REL, REL_WHEEL);
+
+       /* Set up and register absolute device */
+       snprintf(priv->phys, sizeof(priv->phys), "%s/input1",
+                psmouse->ps2dev.serio->phys);
+
+       /* Mimic name setup for relative device in psmouse-base.c */
+       snprintf(priv->dev_name, sizeof(priv->dev_name), "%s %s %s",
+                VMMOUSE_PSNAME, VMMOUSE_VENDOR, VMMOUSE_NAME);
+       abs_dev->phys = priv->phys;
+       abs_dev->name = priv->dev_name;
+       abs_dev->id.bustype = BUS_I8042;
+       abs_dev->id.vendor = 0x0002;
+       abs_dev->id.product = PSMOUSE_VMMOUSE;
+       abs_dev->id.version = psmouse->model;
+       abs_dev->dev.parent = &psmouse->ps2dev.serio->dev;
+
+       error = input_register_device(priv->abs_dev);
+       if (error)
+               goto init_fail;
+
+       /* Set absolute device capabilities */
+       input_set_capability(abs_dev, EV_KEY, BTN_LEFT);
+       input_set_capability(abs_dev, EV_KEY, BTN_RIGHT);
+       input_set_capability(abs_dev, EV_KEY, BTN_MIDDLE);
+       input_set_capability(abs_dev, EV_ABS, ABS_X);
+       input_set_capability(abs_dev, EV_ABS, ABS_Y);
+       input_set_abs_params(abs_dev, ABS_X, 0, VMMOUSE_MAX_X, 0, 0);
+       input_set_abs_params(abs_dev, ABS_Y, 0, VMMOUSE_MAX_Y, 0, 0);
+
+       psmouse->protocol_handler = vmmouse_process_byte;
+       psmouse->disconnect = vmmouse_disconnect;
+       psmouse->reconnect = vmmouse_reconnect;
+
+       return 0;
+
+init_fail:
+       vmmouse_disable(psmouse);
+       psmouse_reset(psmouse);
+       input_free_device(abs_dev);
+       kfree(priv);
+       psmouse->private = NULL;
+
+release_region:
+       release_region(VMMOUSE_PROTO_PORT, 4);
+
+       return error;
+}
diff --git a/drivers/input/mouse/vmmouse.h b/drivers/input/mouse/vmmouse.h
new file mode 100644 (file)
index 0000000..6f12601
--- /dev/null
@@ -0,0 +1,30 @@
+/*
+ * Driver for Virtual PS/2 Mouse on VMware and QEMU hypervisors.
+ *
+ * Copyright (C) 2014, VMware, Inc. All Rights Reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 as published by
+ * the Free Software Foundation.
+ */
+
+#ifndef _VMMOUSE_H
+#define _VMMOUSE_H
+
+#ifdef CONFIG_MOUSE_PS2_VMMOUSE
+#define VMMOUSE_PSNAME  "VirtualPS/2"
+
+int vmmouse_detect(struct psmouse *psmouse, bool set_properties);
+int vmmouse_init(struct psmouse *psmouse);
+#else
+static inline int vmmouse_detect(struct psmouse *psmouse, bool set_properties)
+{
+       return -ENOSYS;
+}
+static inline int vmmouse_init(struct psmouse *psmouse)
+{
+       return -ENOSYS;
+}
+#endif
+
+#endif
index 2875ddf37289521c2059ad64d1301e6bb40b3ba4..40b98dda8f38be802c7896e4652bb7591c57c88c 100644 (file)
@@ -14,6 +14,8 @@
  *
  */
 
+#include <linux/acpi.h>
+#include <linux/dmi.h>
 #include <linux/module.h>
 #include <linux/init.h>
 #include <linux/completion.h>
@@ -2371,7 +2373,7 @@ static void mxt_input_close(struct input_dev *dev)
 }
 
 #ifdef CONFIG_OF
-static struct mxt_platform_data *mxt_parse_dt(struct i2c_client *client)
+static const struct mxt_platform_data *mxt_parse_dt(struct i2c_client *client)
 {
        struct mxt_platform_data *pdata;
        u32 *keymap;
@@ -2379,7 +2381,7 @@ static struct mxt_platform_data *mxt_parse_dt(struct i2c_client *client)
        int proplen, i, ret;
 
        if (!client->dev.of_node)
-               return ERR_PTR(-ENODEV);
+               return ERR_PTR(-ENOENT);
 
        pdata = devm_kzalloc(&client->dev, sizeof(*pdata), GFP_KERNEL);
        if (!pdata)
@@ -2410,25 +2412,132 @@ static struct mxt_platform_data *mxt_parse_dt(struct i2c_client *client)
        return pdata;
 }
 #else
-static struct mxt_platform_data *mxt_parse_dt(struct i2c_client *client)
+static const struct mxt_platform_data *mxt_parse_dt(struct i2c_client *client)
 {
-       dev_dbg(&client->dev, "No platform data specified\n");
-       return ERR_PTR(-EINVAL);
+       return ERR_PTR(-ENOENT);
+}
+#endif
+
+#ifdef CONFIG_ACPI
+
+struct mxt_acpi_platform_data {
+       const char *hid;
+       struct mxt_platform_data pdata;
+};
+
+static unsigned int samus_touchpad_buttons[] = {
+       KEY_RESERVED,
+       KEY_RESERVED,
+       KEY_RESERVED,
+       BTN_LEFT
+};
+
+static struct mxt_acpi_platform_data samus_platform_data[] = {
+       {
+               /* Touchpad */
+               .hid    = "ATML0000",
+               .pdata  = {
+                       .t19_num_keys   = ARRAY_SIZE(samus_touchpad_buttons),
+                       .t19_keymap     = samus_touchpad_buttons,
+               },
+       },
+       {
+               /* Touchscreen */
+               .hid    = "ATML0001",
+       },
+       { }
+};
+
+static const struct dmi_system_id mxt_dmi_table[] = {
+       {
+               /* 2015 Google Pixel */
+               .ident = "Chromebook Pixel 2",
+               .matches = {
+                       DMI_MATCH(DMI_SYS_VENDOR, "GOOGLE"),
+                       DMI_MATCH(DMI_PRODUCT_NAME, "Samus"),
+               },
+               .driver_data = samus_platform_data,
+       },
+       { }
+};
+
+static const struct mxt_platform_data *mxt_parse_acpi(struct i2c_client *client)
+{
+       struct acpi_device *adev;
+       const struct dmi_system_id *system_id;
+       const struct mxt_acpi_platform_data *acpi_pdata;
+
+       /*
+        * Ignore ACPI devices representing bootloader mode.
+        *
+        * This is a bit of a hack: Google Chromebook BIOS creates ACPI
+        * devices for both application and bootloader modes, but we are
+        * interested in application mode only (if device is in bootloader
+        * mode we'll end up switching into application anyway). So far
+        * application mode addresses were all above 0x40, so we'll use it
+        * as a threshold.
+        */
+       if (client->addr < 0x40)
+               return ERR_PTR(-ENXIO);
+
+       adev = ACPI_COMPANION(&client->dev);
+       if (!adev)
+               return ERR_PTR(-ENOENT);
+
+       system_id = dmi_first_match(mxt_dmi_table);
+       if (!system_id)
+               return ERR_PTR(-ENOENT);
+
+       acpi_pdata = system_id->driver_data;
+       if (!acpi_pdata)
+               return ERR_PTR(-ENOENT);
+
+       while (acpi_pdata->hid) {
+               if (!strcmp(acpi_device_hid(adev), acpi_pdata->hid))
+                       return &acpi_pdata->pdata;
+
+               acpi_pdata++;
+       }
+
+       return ERR_PTR(-ENOENT);
+}
+#else
+static const struct mxt_platform_data *mxt_parse_acpi(struct i2c_client *client)
+{
+       return ERR_PTR(-ENOENT);
 }
 #endif
 
+static const struct mxt_platform_data *
+mxt_get_platform_data(struct i2c_client *client)
+{
+       const struct mxt_platform_data *pdata;
+
+       pdata = dev_get_platdata(&client->dev);
+       if (pdata)
+               return pdata;
+
+       pdata = mxt_parse_dt(client);
+       if (!IS_ERR(pdata) || PTR_ERR(pdata) != -ENOENT)
+               return pdata;
+
+       pdata = mxt_parse_acpi(client);
+       if (!IS_ERR(pdata) || PTR_ERR(pdata) != -ENOENT)
+               return pdata;
+
+       dev_err(&client->dev, "No platform data specified\n");
+       return ERR_PTR(-EINVAL);
+}
+
 static int mxt_probe(struct i2c_client *client, const struct i2c_device_id *id)
 {
        struct mxt_data *data;
        const struct mxt_platform_data *pdata;
        int error;
 
-       pdata = dev_get_platdata(&client->dev);
-       if (!pdata) {
-               pdata = mxt_parse_dt(client);
-               if (IS_ERR(pdata))
-                       return PTR_ERR(pdata);
-       }
+       pdata = mxt_get_platform_data(client);
+       if (IS_ERR(pdata))
+               return PTR_ERR(pdata);
 
        data = kzalloc(sizeof(struct mxt_data), GFP_KERNEL);
        if (!data) {
@@ -2536,6 +2645,15 @@ static const struct of_device_id mxt_of_match[] = {
 };
 MODULE_DEVICE_TABLE(of, mxt_of_match);
 
+#ifdef CONFIG_ACPI
+static const struct acpi_device_id mxt_acpi_id[] = {
+       { "ATML0000", 0 },      /* Touchpad */
+       { "ATML0001", 0 },      /* Touchscreen */
+       { }
+};
+MODULE_DEVICE_TABLE(acpi, mxt_acpi_id);
+#endif
+
 static const struct i2c_device_id mxt_id[] = {
        { "qt602240_ts", 0 },
        { "atmel_mxt_ts", 0 },
@@ -2550,6 +2668,7 @@ static struct i2c_driver mxt_driver = {
                .name   = "atmel_mxt_ts",
                .owner  = THIS_MODULE,
                .of_match_table = of_match_ptr(mxt_of_match),
+               .acpi_match_table = ACPI_PTR(mxt_acpi_id),
                .pm     = &mxt_pm_ops,
        },
        .probe          = mxt_probe,
index 43b3c9c2d788ed2637178e4e6b4395cec3a552af..0efd766a545bc677cce1fa2f753265dd06bfe8d9 100644 (file)
@@ -699,7 +699,7 @@ static int elants_i2c_fw_update(struct elants_data *ts)
        char *fw_name;
        int error;
 
-       fw_name = kasprintf(GFP_KERNEL, "elants_i2c_%4x.bin", ts->hw_version);
+       fw_name = kasprintf(GFP_KERNEL, "elants_i2c_%04x.bin", ts->hw_version);
        if (!fw_name)
                return -ENOMEM;