Merge tag 'v4.17-rc6' into next
authorDmitry Torokhov <dmitry.torokhov@gmail.com>
Thu, 24 May 2018 16:30:15 +0000 (09:30 -0700)
committerDmitry Torokhov <dmitry.torokhov@gmail.com>
Thu, 24 May 2018 16:30:15 +0000 (09:30 -0700)
Sync up with mainline to bring in Atmel controller changes for Caroline.

20 files changed:
Documentation/devicetree/bindings/input/elan_i2c.txt
MAINTAINERS
drivers/input/input.c
drivers/input/joystick/as5011.c
drivers/input/joystick/gamecon.c
drivers/input/joystick/xpad.c
drivers/input/misc/ati_remote2.c
drivers/input/mouse/Kconfig
drivers/input/mouse/alps.c
drivers/input/mouse/elan_i2c_core.c
drivers/input/mouse/elantech.c
drivers/input/mouse/elantech.h
drivers/input/mouse/psmouse-base.c
drivers/input/mouse/psmouse-smbus.c
drivers/input/mouse/psmouse.h
drivers/input/mouse/synaptics.c
drivers/input/touchscreen/Kconfig
drivers/input/touchscreen/Makefile
drivers/input/touchscreen/chipone_icn8505.c [new file with mode: 0644]
drivers/input/touchscreen/usbtouchscreen.c

index ee3242c4ba67a43e67b5b3295637523ecd8bef9c..d80a83583238a16ad0fe493f560028b7df83c605 100644 (file)
@@ -14,6 +14,7 @@ Optional properties:
 - pinctrl-0: a phandle pointing to the pin settings for the device (see
   pinctrl binding [1]).
 - vcc-supply: a phandle for the regulator supplying 3.3V power.
+- elan,trackpoint: touchpad can support a trackpoint (boolean)
 
 [0]: Documentation/devicetree/bindings/interrupt-controller/interrupts.txt
 [1]: Documentation/devicetree/bindings/pinctrl/pinctrl-bindings.txt
index 078fd80f664fb652b5de36a45920f9e31d56e0a6..1078c0bc0efd5f76b5c88277d78c54383cae5f47 100644 (file)
@@ -3431,6 +3431,12 @@ S:       Maintained
 F:     Documentation/devicetree/bindings/input/touchscreen/chipone_icn8318.txt
 F:     drivers/input/touchscreen/chipone_icn8318.c
 
+CHIPONE ICN8505 I2C TOUCHSCREEN DRIVER
+M:     Hans de Goede <hdegoede@redhat.com>
+L:     linux-input@vger.kernel.org
+S:     Maintained
+F:     drivers/input/touchscreen/chipone_icn8505.c
+
 CHROME HARDWARE PLATFORM SUPPORT
 M:     Benson Leung <bleung@chromium.org>
 M:     Olof Johansson <olof@lixom.net>
index 9785546420a7237aecffb817631d4826f052c03e..6365c19582644b83baea63d5932a696f7de7c3b9 100644 (file)
@@ -1943,8 +1943,7 @@ void input_set_capability(struct input_dev *dev, unsigned int type, unsigned int
                break;
 
        default:
-               pr_err("input_set_capability: unknown type %u (code %u)\n",
-                      type, code);
+               pr_err("%s: unknown type %u (code %u)\n", __func__, type, code);
                dump_stack();
                return;
        }
index 005d852a06e99d6ebf2299cd6a23eb9023712ef8..f051993c568e98447819aefd0c68de2a5cdf94c1 100644 (file)
@@ -269,9 +269,7 @@ static int as5011_probe(struct i2c_client *client,
        input_dev->id.bustype = BUS_I2C;
        input_dev->dev.parent = &client->dev;
 
-       __set_bit(EV_KEY, input_dev->evbit);
-       __set_bit(EV_ABS, input_dev->evbit);
-       __set_bit(BTN_JOYSTICK, input_dev->keybit);
+       input_set_capability(input_dev, EV_KEY, BTN_JOYSTICK);
 
        input_set_abs_params(input_dev, ABS_X,
                AS5011_MIN_AXIS, AS5011_MAX_AXIS, AS5011_FUZZ, AS5011_FLAT);
index 2ffb2e8bdc3bf456692754a7e6a625e2b572d671..4e10ffdf8a36aedbfa8e4240499f235ab8c9819d 100644 (file)
@@ -862,7 +862,7 @@ static int gc_setup_pad(struct gc *gc, int idx, int pad_type)
 
        case GC_N64:
                for (i = 0; i < 10; i++)
-                       __set_bit(gc_n64_btn[i], input_dev->keybit);
+                       input_set_capability(input_dev, EV_KEY, gc_n64_btn[i]);
 
                for (i = 0; i < 2; i++) {
                        input_set_abs_params(input_dev, ABS_X + i, -127, 126, 0, 2);
@@ -879,26 +879,27 @@ static int gc_setup_pad(struct gc *gc, int idx, int pad_type)
                break;
 
        case GC_SNESMOUSE:
-               __set_bit(BTN_LEFT, input_dev->keybit);
-               __set_bit(BTN_RIGHT, input_dev->keybit);
-               __set_bit(REL_X, input_dev->relbit);
-               __set_bit(REL_Y, input_dev->relbit);
+               input_set_capability(input_dev, EV_KEY, BTN_LEFT);
+               input_set_capability(input_dev, EV_KEY, BTN_RIGHT);
+               input_set_capability(input_dev, EV_REL, REL_X);
+               input_set_capability(input_dev, EV_REL, REL_Y);
                break;
 
        case GC_SNES:
                for (i = 4; i < 8; i++)
-                       __set_bit(gc_snes_btn[i], input_dev->keybit);
+                       input_set_capability(input_dev, EV_KEY, gc_snes_btn[i]);
                /* fall through */
        case GC_NES:
                for (i = 0; i < 4; i++)
-                       __set_bit(gc_snes_btn[i], input_dev->keybit);
+                       input_set_capability(input_dev, EV_KEY, gc_snes_btn[i]);
                break;
 
        case GC_MULTI2:
-               __set_bit(BTN_THUMB, input_dev->keybit);
+               input_set_capability(input_dev, EV_KEY, BTN_THUMB);
                /* fall through */
        case GC_MULTI:
-               __set_bit(BTN_TRIGGER, input_dev->keybit);
+               input_set_capability(input_dev, EV_KEY, BTN_TRIGGER);
+               /* fall through */
                break;
 
        case GC_PSX:
@@ -906,15 +907,17 @@ static int gc_setup_pad(struct gc *gc, int idx, int pad_type)
                        input_set_abs_params(input_dev,
                                             gc_psx_abs[i], 4, 252, 0, 2);
                for (i = 0; i < 12; i++)
-                       __set_bit(gc_psx_btn[i], input_dev->keybit);
+                       input_set_capability(input_dev, EV_KEY, gc_psx_btn[i]);
+               break;
 
                break;
 
        case GC_DDR:
                for (i = 0; i < 4; i++)
-                       __set_bit(gc_psx_ddr_btn[i], input_dev->keybit);
+                       input_set_capability(input_dev, EV_KEY,
+                                            gc_psx_ddr_btn[i]);
                for (i = 0; i < 12; i++)
-                       __set_bit(gc_psx_btn[i], input_dev->keybit);
+                       input_set_capability(input_dev, EV_KEY, gc_psx_btn[i]);
 
                break;
        }
index 06e9650b3b302f12394b9d92572afe872bae6da3..f5d2b5147dd44dbe348438c5cec599a2038993e8 100644 (file)
 
 #define XPAD_PKT_LEN 64
 
-/* xbox d-pads should map to buttons, as is required for DDR pads
-   but we map them to axes when possible to simplify things */
+/*
+ * xbox d-pads should map to buttons, as is required for DDR pads
+ * but we map them to axes when possible to simplify things
+ */
 #define MAP_DPAD_TO_BUTTONS            (1 << 0)
 #define MAP_TRIGGERS_TO_BUTTONS                (1 << 1)
 #define MAP_STICKS_TO_NULL             (1 << 2)
@@ -387,15 +389,15 @@ static const signed short xpad_abs_triggers[] = {
  * match against vendor id as well. Wired Xbox 360 devices have protocol 1,
  * wireless controllers have protocol 129.
  */
-#define XPAD_XBOX360_VENDOR_PROTOCOL(vend,pr) \
+#define XPAD_XBOX360_VENDOR_PROTOCOL(vend, pr) \
        .match_flags = USB_DEVICE_ID_MATCH_VENDOR | USB_DEVICE_ID_MATCH_INT_INFO, \
        .idVendor = (vend), \
        .bInterfaceClass = USB_CLASS_VENDOR_SPEC, \
        .bInterfaceSubClass = 93, \
        .bInterfaceProtocol = (pr)
 #define XPAD_XBOX360_VENDOR(vend) \
-       { XPAD_XBOX360_VENDOR_PROTOCOL(vend,1) }, \
-       { XPAD_XBOX360_VENDOR_PROTOCOL(vend,129) }
+       { XPAD_XBOX360_VENDOR_PROTOCOL((vend), 1) }, \
+       { XPAD_XBOX360_VENDOR_PROTOCOL((vend), 129) }
 
 /* The Xbox One controller uses subclass 71 and protocol 208. */
 #define XPAD_XBOXONE_VENDOR_PROTOCOL(vend, pr) \
@@ -405,7 +407,7 @@ static const signed short xpad_abs_triggers[] = {
        .bInterfaceSubClass = 71, \
        .bInterfaceProtocol = (pr)
 #define XPAD_XBOXONE_VENDOR(vend) \
-       { XPAD_XBOXONE_VENDOR_PROTOCOL(vend, 208) }
+       { XPAD_XBOXONE_VENDOR_PROTOCOL((vend), 208) }
 
 static const struct usb_device_id xpad_table[] = {
        { USB_INTERFACE_INFO('X', 'B', 0) },    /* X-Box USB-IF not approved class */
@@ -1573,7 +1575,6 @@ static void xpad_close(struct input_dev *dev)
 static void xpad_set_up_abs(struct input_dev *input_dev, signed short abs)
 {
        struct usb_xpad *xpad = input_get_drvdata(input_dev);
-       set_bit(abs, input_dev->absbit);
 
        switch (abs) {
        case ABS_X:
@@ -1593,6 +1594,9 @@ static void xpad_set_up_abs(struct input_dev *input_dev, signed short abs)
        case ABS_HAT0Y: /* the d-pad (only if dpad is mapped to axes */
                input_set_abs_params(input_dev, abs, -1, 1, 0, 0);
                break;
+       default:
+               input_set_abs_params(input_dev, abs, 0, 0, 0, 0);
+               break;
        }
 }
 
@@ -1633,10 +1637,7 @@ static int xpad_init_input(struct usb_xpad *xpad)
                input_dev->close = xpad_close;
        }
 
-       __set_bit(EV_KEY, input_dev->evbit);
-
        if (!(xpad->mapping & MAP_STICKS_TO_NULL)) {
-               __set_bit(EV_ABS, input_dev->evbit);
                /* set up axes */
                for (i = 0; xpad_abs[i] >= 0; i++)
                        xpad_set_up_abs(input_dev, xpad_abs[i]);
@@ -1644,21 +1645,22 @@ static int xpad_init_input(struct usb_xpad *xpad)
 
        /* set up standard buttons */
        for (i = 0; xpad_common_btn[i] >= 0; i++)
-               __set_bit(xpad_common_btn[i], input_dev->keybit);
+               input_set_capability(input_dev, EV_KEY, xpad_common_btn[i]);
 
        /* set up model-specific ones */
        if (xpad->xtype == XTYPE_XBOX360 || xpad->xtype == XTYPE_XBOX360W ||
            xpad->xtype == XTYPE_XBOXONE) {
                for (i = 0; xpad360_btn[i] >= 0; i++)
-                       __set_bit(xpad360_btn[i], input_dev->keybit);
+                       input_set_capability(input_dev, EV_KEY, xpad360_btn[i]);
        } else {
                for (i = 0; xpad_btn[i] >= 0; i++)
-                       __set_bit(xpad_btn[i], input_dev->keybit);
+                       input_set_capability(input_dev, EV_KEY, xpad_btn[i]);
        }
 
        if (xpad->mapping & MAP_DPAD_TO_BUTTONS) {
                for (i = 0; xpad_btn_pad[i] >= 0; i++)
-                       __set_bit(xpad_btn_pad[i], input_dev->keybit);
+                       input_set_capability(input_dev, EV_KEY,
+                                            xpad_btn_pad[i]);
        }
 
        /*
@@ -1675,7 +1677,8 @@ static int xpad_init_input(struct usb_xpad *xpad)
 
        if (xpad->mapping & MAP_TRIGGERS_TO_BUTTONS) {
                for (i = 0; xpad_btn_triggers[i] >= 0; i++)
-                       __set_bit(xpad_btn_triggers[i], input_dev->keybit);
+                       input_set_capability(input_dev, EV_KEY,
+                                            xpad_btn_triggers[i]);
        } else {
                for (i = 0; xpad_abs_triggers[i] >= 0; i++)
                        xpad_set_up_abs(input_dev, xpad_abs_triggers[i]);
index ded5b84e336dae67d885af602a49eef547bff744..d8fd58fdf05086efaef0d58b0d6676e0e69916cf 100644 (file)
@@ -22,7 +22,7 @@ MODULE_LICENSE("GPL");
 /*
  * ATI Remote Wonder II Channel Configuration
  *
- * The remote control can by assigned one of sixteen "channels" in order to facilitate
+ * The remote control can be assigned one of sixteen "channels" in order to facilitate
  * the use of multiple remote controls within range of each other.
  * A remote's "channel" may be altered by pressing and holding the "PC" button for
  * approximately 3 seconds, after which the button will slowly flash the count of the
index 89ebb8f39fee2f4f5ef16392c1cb1c98974dbbea..f27f23f2d99a4bdd42d5ab3f6405a0d14ce52292 100644 (file)
@@ -133,6 +133,18 @@ config MOUSE_PS2_ELANTECH
 
          If unsure, say N.
 
+config MOUSE_PS2_ELANTECH_SMBUS
+       bool "Elantech PS/2 SMbus companion" if EXPERT
+       default y
+       depends on MOUSE_PS2 && MOUSE_PS2_ELANTECH
+       depends on I2C=y || I2C=MOUSE_PS2
+       select MOUSE_PS2_SMBUS
+       help
+         Say Y here if you have a Elantech touchpad connected to
+         to an SMBus, but enumerated through PS/2.
+
+         If unsure, say Y.
+
 config MOUSE_PS2_SENTELIC
        bool "Sentelic Finger Sensing Pad PS/2 protocol extension"
        depends on MOUSE_PS2
index 38f9501acdf04c6127a45667e831c807e52be09b..cb5579716dba69e0c85d9556505eff97c7d62fd4 100644 (file)
@@ -2049,14 +2049,11 @@ static int alps_hw_init_v1_v2(struct psmouse *psmouse)
        return 0;
 }
 
-static int alps_hw_init_v6(struct psmouse *psmouse)
+/* Must be in passthrough mode when calling this function */
+static int alps_trackstick_enter_extended_mode_v3_v6(struct psmouse *psmouse)
 {
        unsigned char param[2] = {0xC8, 0x14};
 
-       /* Enter passthrough mode to let trackpoint enter 6byte raw mode */
-       if (alps_passthrough_mode_v2(psmouse, true))
-               return -1;
-
        if (ps2_command(&psmouse->ps2dev, NULL, PSMOUSE_CMD_SETSCALE11) ||
            ps2_command(&psmouse->ps2dev, NULL, PSMOUSE_CMD_SETSCALE11) ||
            ps2_command(&psmouse->ps2dev, NULL, PSMOUSE_CMD_SETSCALE11) ||
@@ -2064,9 +2061,25 @@ static int alps_hw_init_v6(struct psmouse *psmouse)
            ps2_command(&psmouse->ps2dev, &param[1], PSMOUSE_CMD_SETRATE))
                return -1;
 
+       return 0;
+}
+
+static int alps_hw_init_v6(struct psmouse *psmouse)
+{
+       int ret;
+
+       /* Enter passthrough mode to let trackpoint enter 6byte raw mode */
+       if (alps_passthrough_mode_v2(psmouse, true))
+               return -1;
+
+       ret = alps_trackstick_enter_extended_mode_v3_v6(psmouse);
+
        if (alps_passthrough_mode_v2(psmouse, false))
                return -1;
 
+       if (ret)
+               return ret;
+
        if (alps_absolute_mode_v6(psmouse)) {
                psmouse_err(psmouse, "Failed to enable absolute mode\n");
                return -1;
@@ -2140,10 +2153,18 @@ error:
 
 static int alps_setup_trackstick_v3(struct psmouse *psmouse, int reg_base)
 {
-       struct ps2dev *ps2dev = &psmouse->ps2dev;
        int ret = 0;
+       int reg_val;
        unsigned char param[4];
 
+       /*
+        * We need to configure trackstick to report data for touchpad in
+        * extended format. And also we need to tell touchpad to expect data
+        * from trackstick in extended format. Without this configuration
+        * trackstick packets sent from touchpad are in basic format which is
+        * different from what we expect.
+        */
+
        if (alps_passthrough_mode_v3(psmouse, reg_base, true))
                return -EIO;
 
@@ -2161,39 +2182,36 @@ static int alps_setup_trackstick_v3(struct psmouse *psmouse, int reg_base)
                ret = -ENODEV;
        } else {
                psmouse_dbg(psmouse, "trackstick E7 report: %3ph\n", param);
-
-               /*
-                * Not sure what this does, but it is absolutely
-                * essential. Without it, the touchpad does not
-                * work at all and the trackstick just emits normal
-                * PS/2 packets.
-                */
-               if (ps2_command(ps2dev, NULL, PSMOUSE_CMD_SETSCALE11) ||
-                   ps2_command(ps2dev, NULL, PSMOUSE_CMD_SETSCALE11) ||
-                   ps2_command(ps2dev, NULL, PSMOUSE_CMD_SETSCALE11) ||
-                   alps_command_mode_send_nibble(psmouse, 0x9) ||
-                   alps_command_mode_send_nibble(psmouse, 0x4)) {
-                       psmouse_err(psmouse,
-                                   "Error sending magic E6 sequence\n");
+               if (alps_trackstick_enter_extended_mode_v3_v6(psmouse)) {
+                       psmouse_err(psmouse, "Failed to enter into trackstick extended mode\n");
                        ret = -EIO;
-                       goto error;
                }
+       }
+
+       if (alps_passthrough_mode_v3(psmouse, reg_base, false))
+               return -EIO;
+
+       if (ret)
+               return ret;
 
+       if (alps_enter_command_mode(psmouse))
+               return -EIO;
+
+       reg_val = alps_command_mode_read_reg(psmouse, reg_base + 0x08);
+       if (reg_val == -1) {
+               ret = -EIO;
+       } else {
                /*
-                * This ensures the trackstick packets are in the format
-                * supported by this driver. If bit 1 isn't set the packet
-                * format is different.
+                * Tell touchpad that trackstick is now in extended mode.
+                * If bit 1 isn't set the packet format is different.
                 */
-               if (alps_enter_command_mode(psmouse) ||
-                   alps_command_mode_write_reg(psmouse,
-                                               reg_base + 0x08, 0x82) ||
-                   alps_exit_command_mode(psmouse))
+               reg_val |= BIT(1);
+               if (__alps_command_mode_write_reg(psmouse, reg_val))
                        ret = -EIO;
        }
 
-error:
-       if (alps_passthrough_mode_v3(psmouse, reg_base, false))
-               ret = -EIO;
+       if (alps_exit_command_mode(psmouse))
+               return -EIO;
 
        return ret;
 }
index 75e757520ef09f4cecbe0d9ab59a5d5a0bec75e8..2690a4b7ed53ad4a2f2d6a10fe8d4f1b4c322951 100644 (file)
@@ -36,6 +36,7 @@
 #include <linux/jiffies.h>
 #include <linux/completion.h>
 #include <linux/of.h>
+#include <linux/property.h>
 #include <linux/regulator/consumer.h>
 #include <asm/unaligned.h>
 
@@ -51,6 +52,7 @@
 #define ETP_MAX_FINGERS                5
 #define ETP_FINGER_DATA_LEN    5
 #define ETP_REPORT_ID          0x5D
+#define ETP_TP_REPORT_ID       0x5E
 #define ETP_REPORT_ID_OFFSET   2
 #define ETP_TOUCH_INFO_OFFSET  3
 #define ETP_FINGER_DATA_OFFSET 4
@@ -61,6 +63,7 @@
 struct elan_tp_data {
        struct i2c_client       *client;
        struct input_dev        *input;
+       struct input_dev        *tp_input; /* trackpoint input node */
        struct regulator        *vcc;
 
        const struct elan_transport_ops *ops;
@@ -930,6 +933,33 @@ static void elan_report_absolute(struct elan_tp_data *data, u8 *packet)
        input_sync(input);
 }
 
+static void elan_report_trackpoint(struct elan_tp_data *data, u8 *report)
+{
+       struct input_dev *input = data->tp_input;
+       u8 *packet = &report[ETP_REPORT_ID_OFFSET + 1];
+       int x, y;
+
+       if (!data->tp_input) {
+               dev_warn_once(&data->client->dev,
+                             "received a trackpoint report while no trackpoint device has been created. Please report upstream.\n");
+               return;
+       }
+
+       input_report_key(input, BTN_LEFT, packet[0] & 0x01);
+       input_report_key(input, BTN_RIGHT, packet[0] & 0x02);
+       input_report_key(input, BTN_MIDDLE, packet[0] & 0x04);
+
+       if ((packet[3] & 0x0F) == 0x06) {
+               x = packet[4] - (int)((packet[1] ^ 0x80) << 1);
+               y = (int)((packet[2] ^ 0x80) << 1) - packet[5];
+
+               input_report_rel(input, REL_X, x);
+               input_report_rel(input, REL_Y, y);
+       }
+
+       input_sync(input);
+}
+
 static irqreturn_t elan_isr(int irq, void *dev_id)
 {
        struct elan_tp_data *data = dev_id;
@@ -951,11 +981,17 @@ static irqreturn_t elan_isr(int irq, void *dev_id)
        if (error)
                goto out;
 
-       if (report[ETP_REPORT_ID_OFFSET] != ETP_REPORT_ID)
+       switch (report[ETP_REPORT_ID_OFFSET]) {
+       case ETP_REPORT_ID:
+               elan_report_absolute(data, report);
+               break;
+       case ETP_TP_REPORT_ID:
+               elan_report_trackpoint(data, report);
+               break;
+       default:
                dev_err(dev, "invalid report id data (%x)\n",
                        report[ETP_REPORT_ID_OFFSET]);
-       else
-               elan_report_absolute(data, report);
+       }
 
 out:
        return IRQ_HANDLED;
@@ -966,6 +1002,36 @@ out:
  * Elan initialization functions
  ******************************************************************
  */
+
+static int elan_setup_trackpoint_input_device(struct elan_tp_data *data)
+{
+       struct device *dev = &data->client->dev;
+       struct input_dev *input;
+
+       input = devm_input_allocate_device(dev);
+       if (!input)
+               return -ENOMEM;
+
+       input->name = "Elan TrackPoint";
+       input->id.bustype = BUS_I2C;
+       input->id.vendor = ELAN_VENDOR_ID;
+       input->id.product = data->product_id;
+       input_set_drvdata(input, data);
+
+       input_set_capability(input, EV_REL, REL_X);
+       input_set_capability(input, EV_REL, REL_Y);
+       input_set_capability(input, EV_KEY, BTN_LEFT);
+       input_set_capability(input, EV_KEY, BTN_RIGHT);
+       input_set_capability(input, EV_KEY, BTN_MIDDLE);
+
+       __set_bit(INPUT_PROP_POINTER, input->propbit);
+       __set_bit(INPUT_PROP_POINTING_STICK, input->propbit);
+
+       data->tp_input = input;
+
+       return 0;
+}
+
 static int elan_setup_input_device(struct elan_tp_data *data)
 {
        struct device *dev = &data->client->dev;
@@ -1140,6 +1206,12 @@ static int elan_probe(struct i2c_client *client,
        if (error)
                return error;
 
+       if (device_property_read_bool(&client->dev, "elan,trackpoint")) {
+               error = elan_setup_trackpoint_input_device(data);
+               if (error)
+                       return error;
+       }
+
        /*
         * Platform code (ACPI, DTS) should normally set up interrupt
         * for us, but in case it did not let's fall back to using falling
@@ -1177,6 +1249,16 @@ static int elan_probe(struct i2c_client *client,
                return error;
        }
 
+       if (data->tp_input) {
+               error = input_register_device(data->tp_input);
+               if (error) {
+                       dev_err(&client->dev,
+                               "failed to register TrackPoint input device: %d\n",
+                               error);
+                       return error;
+               }
+       }
+
        /*
         * Systems using device tree should set up wakeup via DTS,
         * the rest will configure device as wakeup source by default.
index db47a5e1d114dcaee65f63df4ee7a51fb0b6aa07..fb4d902c440345d3cbc02329ed742d48b931dc85 100644 (file)
 #include <linux/dmi.h>
 #include <linux/slab.h>
 #include <linux/module.h>
+#include <linux/i2c.h>
 #include <linux/input.h>
 #include <linux/input/mt.h>
+#include <linux/platform_device.h>
 #include <linux/serio.h>
 #include <linux/libps2.h>
 #include <asm/unaligned.h>
 #include "psmouse.h"
 #include "elantech.h"
+#include "elan_i2c.h"
 
 #define elantech_debug(fmt, ...)                                       \
        do {                                                            \
-               if (etd->debug)                                         \
+               if (etd->info.debug)                                    \
                        psmouse_printk(KERN_DEBUG, psmouse,             \
                                        fmt, ##__VA_ARGS__);            \
        } while (0)
@@ -105,7 +108,7 @@ static int elantech_read_reg(struct psmouse *psmouse, unsigned char reg,
        if (reg > 0x11 && reg < 0x20)
                return -1;
 
-       switch (etd->hw_version) {
+       switch (etd->info.hw_version) {
        case 1:
                if (ps2_sliced_command(&psmouse->ps2dev, ETP_REGISTER_READ) ||
                    ps2_sliced_command(&psmouse->ps2dev, reg) ||
@@ -137,7 +140,7 @@ static int elantech_read_reg(struct psmouse *psmouse, unsigned char reg,
 
        if (rc)
                psmouse_err(psmouse, "failed to read register 0x%02x.\n", reg);
-       else if (etd->hw_version != 4)
+       else if (etd->info.hw_version != 4)
                *val = param[0];
        else
                *val = param[1];
@@ -160,7 +163,7 @@ static int elantech_write_reg(struct psmouse *psmouse, unsigned char reg,
        if (reg > 0x11 && reg < 0x20)
                return -1;
 
-       switch (etd->hw_version) {
+       switch (etd->info.hw_version) {
        case 1:
                if (ps2_sliced_command(&psmouse->ps2dev, ETP_REGISTER_WRITE) ||
                    ps2_sliced_command(&psmouse->ps2dev, reg) ||
@@ -237,7 +240,7 @@ static void elantech_report_absolute_v1(struct psmouse *psmouse)
        unsigned char *packet = psmouse->packet;
        int fingers;
 
-       if (etd->fw_version < 0x020000) {
+       if (etd->info.fw_version < 0x020000) {
                /*
                 * byte 0:  D   U  p1  p2   1  p3   R   L
                 * byte 1:  f   0  th  tw  x9  x8  y9  y8
@@ -252,7 +255,7 @@ static void elantech_report_absolute_v1(struct psmouse *psmouse)
                fingers = (packet[0] & 0xc0) >> 6;
        }
 
-       if (etd->jumpy_cursor) {
+       if (etd->info.jumpy_cursor) {
                if (fingers != 1) {
                        etd->single_finger_reports = 0;
                } else if (etd->single_finger_reports < 2) {
@@ -282,8 +285,8 @@ static void elantech_report_absolute_v1(struct psmouse *psmouse)
 
        psmouse_report_standard_buttons(dev, packet[0]);
 
-       if (etd->fw_version < 0x020000 &&
-           (etd->capabilities[0] & ETP_CAP_HAS_ROCKER)) {
+       if (etd->info.fw_version < 0x020000 &&
+           (etd->info.capabilities[0] & ETP_CAP_HAS_ROCKER)) {
                /* rocker up */
                input_report_key(dev, BTN_FORWARD, packet[0] & 0x40);
                /* rocker down */
@@ -391,7 +394,7 @@ static void elantech_report_absolute_v2(struct psmouse *psmouse)
        input_report_key(dev, BTN_TOOL_TRIPLETAP, fingers == 3);
        input_report_key(dev, BTN_TOOL_QUADTAP, fingers == 4);
        psmouse_report_standard_buttons(dev, packet[0]);
-       if (etd->reports_pressure) {
+       if (etd->info.reports_pressure) {
                input_report_abs(dev, ABS_PRESSURE, pres);
                input_report_abs(dev, ABS_TOOL_WIDTH, width);
        }
@@ -444,7 +447,7 @@ static void elantech_report_trackpoint(struct psmouse *psmouse,
 
        default:
                /* Dump unexpected packet sequences if debug=1 (default) */
-               if (etd->debug == 1)
+               if (etd->info.debug == 1)
                        elantech_packet_dump(psmouse);
 
                break;
@@ -523,7 +526,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->fw_version & 0x001000)
+       if (etd->info.fw_version & 0x001000)
                input_report_key(dev, BTN_LEFT, packet[0] & 0x03);
        else
                psmouse_report_standard_buttons(dev, packet[0]);
@@ -541,7 +544,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->fw_version & 0x001000)
+       if (etd->info.fw_version & 0x001000)
                input_report_key(dev, BTN_LEFT, packet[0] & 0x03);
        else
                psmouse_report_standard_buttons(dev, packet[0]);
@@ -669,7 +672,7 @@ static int elantech_packet_check_v1(struct psmouse *psmouse)
        unsigned char p1, p2, p3;
 
        /* Parity bits are placed differently */
-       if (etd->fw_version < 0x020000) {
+       if (etd->info.fw_version < 0x020000) {
                /* byte 0:  D   U  p1  p2   1  p3   R   L */
                p1 = (packet[0] & 0x20) >> 5;
                p2 = (packet[0] & 0x10) >> 4;
@@ -714,7 +717,7 @@ static int elantech_packet_check_v2(struct psmouse *psmouse)
         * With all three cases, if the constant bits are not exactly what I
         * expected, I consider them invalid.
         */
-       if (etd->reports_pressure)
+       if (etd->info.reports_pressure)
                return (packet[0] & 0x0c) == 0x04 &&
                       (packet[3] & 0x0f) == 0x02;
 
@@ -751,7 +754,7 @@ static int elantech_packet_check_v3(struct psmouse *psmouse)
         * If the hardware flag 'crc_enabled' is set the packets have
         * different signatures.
         */
-       if (etd->crc_enabled) {
+       if (etd->info.crc_enabled) {
                if ((packet[3] & 0x09) == 0x08)
                        return PACKET_V3_HEAD;
 
@@ -782,7 +785,7 @@ static int elantech_packet_check_v4(struct psmouse *psmouse)
                return PACKET_TRACKPOINT;
 
        /* This represents the version of IC body. */
-       ic_version = (etd->fw_version & 0x0f0000) >> 16;
+       ic_version = (etd->info.fw_version & 0x0f0000) >> 16;
 
        /*
         * Sanity check based on the constant bits of a packet.
@@ -791,9 +794,9 @@ static int elantech_packet_check_v4(struct psmouse *psmouse)
         * the IC body, but are the same for every packet,
         * regardless of the type.
         */
-       if (etd->crc_enabled)
+       if (etd->info.crc_enabled)
                sanity_check = ((packet[3] & 0x08) == 0x00);
-       else if (ic_version == 7 && etd->samples[1] == 0x2A)
+       else if (ic_version == 7 && etd->info.samples[1] == 0x2A)
                sanity_check = ((packet[3] & 0x1c) == 0x10);
        else
                sanity_check = ((packet[0] & 0x0c) == 0x04 &&
@@ -827,12 +830,12 @@ static psmouse_ret_t elantech_process_byte(struct psmouse *psmouse)
        if (psmouse->pktcnt < psmouse->pktsize)
                return PSMOUSE_GOOD_DATA;
 
-       if (etd->debug > 1)
+       if (etd->info.debug > 1)
                elantech_packet_dump(psmouse);
 
-       switch (etd->hw_version) {
+       switch (etd->info.hw_version) {
        case 1:
-               if (etd->paritycheck && !elantech_packet_check_v1(psmouse))
+               if (etd->info.paritycheck && !elantech_packet_check_v1(psmouse))
                        return PSMOUSE_BAD_DATA;
 
                elantech_report_absolute_v1(psmouse);
@@ -843,7 +846,7 @@ static psmouse_ret_t elantech_process_byte(struct psmouse *psmouse)
                if (elantech_debounce_check_v2(psmouse))
                        return PSMOUSE_FULL_PACKET;
 
-               if (etd->paritycheck && !elantech_packet_check_v2(psmouse))
+               if (etd->info.paritycheck && !elantech_packet_check_v2(psmouse))
                        return PSMOUSE_BAD_DATA;
 
                elantech_report_absolute_v2(psmouse);
@@ -916,7 +919,7 @@ static int elantech_set_absolute_mode(struct psmouse *psmouse)
        int tries = ETP_READ_BACK_TRIES;
        int rc = 0;
 
-       switch (etd->hw_version) {
+       switch (etd->info.hw_version) {
        case 1:
                etd->reg_10 = 0x16;
                etd->reg_11 = 0x8f;
@@ -939,7 +942,7 @@ static int elantech_set_absolute_mode(struct psmouse *psmouse)
                break;
 
        case 3:
-               if (etd->set_hw_resolution)
+               if (etd->info.set_hw_resolution)
                        etd->reg_10 = 0x0b;
                else
                        etd->reg_10 = 0x01;
@@ -976,7 +979,7 @@ static int elantech_set_absolute_mode(struct psmouse *psmouse)
                if (rc) {
                        psmouse_err(psmouse,
                                    "failed to read back register 0x10.\n");
-               } else if (etd->hw_version == 1 &&
+               } else if (etd->info.hw_version == 1 &&
                           !(val & ETP_R10_ABSOLUTE_MODE)) {
                        psmouse_err(psmouse,
                                    "touchpad refuses to switch to absolute mode.\n");
@@ -997,10 +1000,11 @@ static int elantech_set_range(struct psmouse *psmouse,
                              unsigned int *width)
 {
        struct elantech_data *etd = psmouse->private;
+       struct elantech_device_info *info = &etd->info;
        unsigned char param[3];
        unsigned char traces;
 
-       switch (etd->hw_version) {
+       switch (info->hw_version) {
        case 1:
                *x_min = ETP_XMIN_V1;
                *y_min = ETP_YMIN_V1;
@@ -1009,9 +1013,9 @@ static int elantech_set_range(struct psmouse *psmouse,
                break;
 
        case 2:
-               if (etd->fw_version == 0x020800 ||
-                   etd->fw_version == 0x020b00 ||
-                   etd->fw_version == 0x020030) {
+               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;
@@ -1020,35 +1024,35 @@ static int elantech_set_range(struct psmouse *psmouse,
                        int i;
                        int fixed_dpi;
 
-                       i = (etd->fw_version > 0x020800 &&
-                            etd->fw_version < 0x020900) ? 1 : 2;
+                       i = (info->fw_version > 0x020800 &&
+                            info->fw_version < 0x020900) ? 1 : 2;
 
-                       if (etd->send_cmd(psmouse, ETP_FW_ID_QUERY, param))
+                       if (info->send_cmd(psmouse, ETP_FW_ID_QUERY, param))
                                return -1;
 
                        fixed_dpi = param[1] & 0x10;
 
-                       if (((etd->fw_version >> 16) == 0x14) && fixed_dpi) {
-                               if (etd->send_cmd(psmouse, ETP_SAMPLE_QUERY, param))
+                       if (((info->fw_version >> 16) == 0x14) && fixed_dpi) {
+                               if (info->send_cmd(psmouse, ETP_SAMPLE_QUERY, param))
                                        return -1;
 
-                               *x_max = (etd->capabilities[1] - i) * param[1] / 2;
-                               *y_max = (etd->capabilities[2] - i) * param[2] / 2;
-                       } else if (etd->fw_version == 0x040216) {
+                               *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 (etd->fw_version == 0x040219 || etd->fw_version == 0x040215) {
+                       } else if (info->fw_version == 0x040219 || info->fw_version == 0x040215) {
                                *x_max = 900;
                                *y_max = 500;
                        } else {
-                               *x_max = (etd->capabilities[1] - i) * 64;
-                               *y_max = (etd->capabilities[2] - i) * 64;
+                               *x_max = (info->capabilities[1] - i) * 64;
+                               *y_max = (info->capabilities[2] - i) * 64;
                        }
                }
                break;
 
        case 3:
-               if (etd->send_cmd(psmouse, ETP_FW_ID_QUERY, param))
+               if (info->send_cmd(psmouse, ETP_FW_ID_QUERY, param))
                        return -1;
 
                *x_max = (0x0f & param[0]) << 8 | param[1];
@@ -1056,12 +1060,12 @@ static int elantech_set_range(struct psmouse *psmouse,
                break;
 
        case 4:
-               if (etd->send_cmd(psmouse, ETP_FW_ID_QUERY, param))
+               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 = etd->capabilities[1];
+               traces = info->capabilities[1];
                if ((traces < 2) || (traces > *x_max))
                        return -1;
 
@@ -1083,7 +1087,8 @@ static unsigned int elantech_convert_res(unsigned int val)
 
 static int elantech_get_resolution_v4(struct psmouse *psmouse,
                                      unsigned int *x_res,
-                                     unsigned int *y_res)
+                                     unsigned int *y_res,
+                                     unsigned int *bus)
 {
        unsigned char param[3];
 
@@ -1092,6 +1097,7 @@ static int elantech_get_resolution_v4(struct psmouse *psmouse,
 
        *x_res = elantech_convert_res(param[1] & 0x0f);
        *y_res = elantech_convert_res((param[1] & 0xf0) >> 4);
+       *bus = param[2];
 
        return 0;
 }
@@ -1140,7 +1146,7 @@ static void elantech_set_buttonpad_prop(struct psmouse *psmouse)
        struct input_dev *dev = psmouse->dev;
        struct elantech_data *etd = psmouse->private;
 
-       if (etd->fw_version & 0x001000) {
+       if (etd->info.fw_version & 0x001000) {
                __set_bit(INPUT_PROP_BUTTONPAD, dev->propbit);
                __clear_bit(BTN_RIGHT, dev->keybit);
        }
@@ -1176,8 +1182,8 @@ 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;
-       unsigned int x_res = 31, y_res = 31;
 
        if (elantech_set_range(psmouse, &x_min, &y_min, &x_max, &y_max, &width))
                return -1;
@@ -1197,11 +1203,11 @@ static int elantech_set_input_params(struct psmouse *psmouse)
        __set_bit(BTN_TOOL_DOUBLETAP, dev->keybit);
        __set_bit(BTN_TOOL_TRIPLETAP, dev->keybit);
 
-       switch (etd->hw_version) {
+       switch (info->hw_version) {
        case 1:
                /* Rocker button */
-               if (etd->fw_version < 0x020000 &&
-                   (etd->capabilities[0] & ETP_CAP_HAS_ROCKER)) {
+               if (info->fw_version < 0x020000 &&
+                   (info->capabilities[0] & ETP_CAP_HAS_ROCKER)) {
                        __set_bit(BTN_FORWARD, dev->keybit);
                        __set_bit(BTN_BACK, dev->keybit);
                }
@@ -1214,11 +1220,11 @@ static int elantech_set_input_params(struct psmouse *psmouse)
                __set_bit(INPUT_PROP_SEMI_MT, dev->propbit);
                /* fall through */
        case 3:
-               if (etd->hw_version == 3)
+               if (info->hw_version == 3)
                        elantech_set_buttonpad_prop(psmouse);
                input_set_abs_params(dev, ABS_X, x_min, x_max, 0, 0);
                input_set_abs_params(dev, ABS_Y, y_min, y_max, 0, 0);
-               if (etd->reports_pressure) {
+               if (info->reports_pressure) {
                        input_set_abs_params(dev, ABS_PRESSURE, ETP_PMIN_V2,
                                             ETP_PMAX_V2, 0, 0);
                        input_set_abs_params(dev, ABS_TOOL_WIDTH, ETP_WMIN_V2,
@@ -1230,13 +1236,6 @@ static int elantech_set_input_params(struct psmouse *psmouse)
                break;
 
        case 4:
-               if (elantech_get_resolution_v4(psmouse, &x_res, &y_res)) {
-                       /*
-                        * if query failed, print a warning and leave the values
-                        * zero to resemble synaptics.c behavior.
-                        */
-                       psmouse_warn(psmouse, "couldn't query resolution data.\n");
-               }
                elantech_set_buttonpad_prop(psmouse);
                __set_bit(BTN_TOOL_QUADTAP, dev->keybit);
                /* For X to recognize me as touchpad. */
@@ -1265,11 +1264,11 @@ static int elantech_set_input_params(struct psmouse *psmouse)
                break;
        }
 
-       input_abs_set_res(dev, ABS_X, x_res);
-       input_abs_set_res(dev, ABS_Y, y_res);
-       if (etd->hw_version > 1) {
-               input_abs_set_res(dev, ABS_MT_POSITION_X, x_res);
-               input_abs_set_res(dev, ABS_MT_POSITION_Y, y_res);
+       input_abs_set_res(dev, ABS_X, info->x_res);
+       input_abs_set_res(dev, ABS_Y, info->y_res);
+       if (info->hw_version > 1) {
+               input_abs_set_res(dev, ABS_MT_POSITION_X, info->x_res);
+               input_abs_set_res(dev, ABS_MT_POSITION_Y, info->y_res);
        }
 
        etd->y_max = y_max;
@@ -1317,7 +1316,7 @@ static ssize_t elantech_set_int_attr(struct psmouse *psmouse,
                return err;
 
        /* Do we need to preserve some bits for version 2 hardware too? */
-       if (etd->hw_version == 1) {
+       if (etd->info.hw_version == 1) {
                if (attr->reg == 0x10)
                        /* Force absolute mode always on */
                        value |= ETP_R10_ABSOLUTE_MODE;
@@ -1337,11 +1336,22 @@ static ssize_t elantech_set_int_attr(struct psmouse *psmouse,
                .field_offset = offsetof(struct elantech_data, _name),  \
                .reg = _register,                                       \
        };                                                              \
-       PSMOUSE_DEFINE_ATTR(_name, S_IWUSR | S_IRUGO,                   \
+       PSMOUSE_DEFINE_ATTR(_name, 0644,                                \
                            &elantech_attr_##_name,                     \
                            elantech_show_int_attr,                     \
                            elantech_set_int_attr)
 
+#define ELANTECH_INFO_ATTR(_name)                                             \
+       static struct elantech_attr_data elantech_attr_##_name = {             \
+               .field_offset = offsetof(struct elantech_data, info) +         \
+                               offsetof(struct elantech_device_info, _name),  \
+               .reg = 0,                                                      \
+       };                                                                     \
+       PSMOUSE_DEFINE_ATTR(_name, 0644,                                       \
+                           &elantech_attr_##_name,                            \
+                           elantech_show_int_attr,                            \
+                           elantech_set_int_attr)
+
 ELANTECH_INT_ATTR(reg_07, 0x07);
 ELANTECH_INT_ATTR(reg_10, 0x10);
 ELANTECH_INT_ATTR(reg_11, 0x11);
@@ -1352,9 +1362,9 @@ ELANTECH_INT_ATTR(reg_23, 0x23);
 ELANTECH_INT_ATTR(reg_24, 0x24);
 ELANTECH_INT_ATTR(reg_25, 0x25);
 ELANTECH_INT_ATTR(reg_26, 0x26);
-ELANTECH_INT_ATTR(debug, 0);
-ELANTECH_INT_ATTR(paritycheck, 0);
-ELANTECH_INT_ATTR(crc_enabled, 0);
+ELANTECH_INFO_ATTR(debug);
+ELANTECH_INFO_ATTR(paritycheck);
+ELANTECH_INFO_ATTR(crc_enabled);
 
 static struct attribute *elantech_attrs[] = {
        &psmouse_attr_reg_07.dattr.attr,
@@ -1469,6 +1479,12 @@ static void elantech_disconnect(struct psmouse *psmouse)
 {
        struct elantech_data *etd = psmouse->private;
 
+       /*
+        * We might have left a breadcrumb when trying to
+        * set up SMbus companion.
+        */
+       psmouse_smbus_cleanup(psmouse);
+
        if (etd->tp_dev)
                input_unregister_device(etd->tp_dev);
        sysfs_remove_group(&psmouse->ps2dev.serio->dev.kobj,
@@ -1588,25 +1604,25 @@ static const struct dmi_system_id no_hw_res_dmi_table[] = {
 /*
  * determine hardware version and set some properties according to it.
  */
-static int elantech_set_properties(struct elantech_data *etd)
+static int elantech_set_properties(struct elantech_device_info *info)
 {
        /* This represents the version of IC body. */
-       int ver = (etd->fw_version & 0x0f0000) >> 16;
+       int ver = (info->fw_version & 0x0f0000) >> 16;
 
        /* Early version of Elan touchpads doesn't obey the rule. */
-       if (etd->fw_version < 0x020030 || etd->fw_version == 0x020600)
-               etd->hw_version = 1;
+       if (info->fw_version < 0x020030 || info->fw_version == 0x020600)
+               info->hw_version = 1;
        else {
                switch (ver) {
                case 2:
                case 4:
-                       etd->hw_version = 2;
+                       info->hw_version = 2;
                        break;
                case 5:
-                       etd->hw_version = 3;
+                       info->hw_version = 3;
                        break;
                case 6 ... 15:
-                       etd->hw_version = 4;
+                       info->hw_version = 4;
                        break;
                default:
                        return -1;
@@ -1614,100 +1630,88 @@ static int elantech_set_properties(struct elantech_data *etd)
        }
 
        /* decide which send_cmd we're gonna use early */
-       etd->send_cmd = etd->hw_version >= 3 ? elantech_send_cmd :
-                                              synaptics_send_cmd;
+       info->send_cmd = info->hw_version >= 3 ? elantech_send_cmd :
+                                                synaptics_send_cmd;
 
        /* Turn on packet checking by default */
-       etd->paritycheck = 1;
+       info->paritycheck = 1;
 
        /*
         * This firmware suffers from misreporting coordinates when
         * a touch action starts causing the mouse cursor or scrolled page
         * to jump. Enable a workaround.
         */
-       etd->jumpy_cursor =
-               (etd->fw_version == 0x020022 || etd->fw_version == 0x020600);
+       info->jumpy_cursor =
+               (info->fw_version == 0x020022 || info->fw_version == 0x020600);
 
-       if (etd->hw_version > 1) {
+       if (info->hw_version > 1) {
                /* For now show extra debug information */
-               etd->debug = 1;
+               info->debug = 1;
 
-               if (etd->fw_version >= 0x020800)
-                       etd->reports_pressure = true;
+               if (info->fw_version >= 0x020800)
+                       info->reports_pressure = true;
        }
 
        /*
         * The signatures of v3 and v4 packets change depending on the
         * value of this hardware flag.
         */
-       etd->crc_enabled = (etd->fw_version & 0x4000) == 0x4000 ||
-                          dmi_check_system(elantech_dmi_force_crc_enabled);
+       info->crc_enabled = (info->fw_version & 0x4000) == 0x4000 ||
+                            dmi_check_system(elantech_dmi_force_crc_enabled);
 
        /* Enable real hardware resolution on hw_version 3 ? */
-       etd->set_hw_resolution = !dmi_check_system(no_hw_res_dmi_table);
+       info->set_hw_resolution = !dmi_check_system(no_hw_res_dmi_table);
 
        return 0;
 }
 
-/*
- * Initialize the touchpad and create sysfs entries
- */
-int elantech_init(struct psmouse *psmouse)
+static int elantech_query_info(struct psmouse *psmouse,
+                              struct elantech_device_info *info)
 {
-       struct elantech_data *etd;
-       int i;
-       int error = -EINVAL;
        unsigned char param[3];
-       struct input_dev *tp_dev;
 
-       psmouse->private = etd = kzalloc(sizeof(struct elantech_data), GFP_KERNEL);
-       if (!etd)
-               return -ENOMEM;
-
-       psmouse_reset(psmouse);
-
-       etd->parity[0] = 1;
-       for (i = 1; i < 256; i++)
-               etd->parity[i] = etd->parity[i & (i - 1)] ^ 1;
+       memset(info, 0, sizeof(*info));
 
        /*
         * Do the version query again so we can store the result
         */
        if (synaptics_send_cmd(psmouse, ETP_FW_VERSION_QUERY, param)) {
                psmouse_err(psmouse, "failed to query firmware version.\n");
-               goto init_fail;
+               return -EINVAL;
        }
-       etd->fw_version = (param[0] << 16) | (param[1] << 8) | param[2];
+       info->fw_version = (param[0] << 16) | (param[1] << 8) | param[2];
 
-       if (elantech_set_properties(etd)) {
+       if (elantech_set_properties(info)) {
                psmouse_err(psmouse, "unknown hardware version, aborting...\n");
-               goto init_fail;
+               return -EINVAL;
        }
        psmouse_info(psmouse,
                     "assuming hardware version %d (with firmware version 0x%02x%02x%02x)\n",
-                    etd->hw_version, param[0], param[1], param[2]);
+                    info->hw_version, param[0], param[1], param[2]);
 
-       if (etd->send_cmd(psmouse, ETP_CAPABILITIES_QUERY,
-           etd->capabilities)) {
+       if (info->send_cmd(psmouse, ETP_CAPABILITIES_QUERY,
+           info->capabilities)) {
                psmouse_err(psmouse, "failed to query capabilities.\n");
-               goto init_fail;
+               return -EINVAL;
        }
        psmouse_info(psmouse,
                     "Synaptics capabilities query result 0x%02x, 0x%02x, 0x%02x.\n",
-                    etd->capabilities[0], etd->capabilities[1],
-                    etd->capabilities[2]);
+                    info->capabilities[0], info->capabilities[1],
+                    info->capabilities[2]);
 
-       if (etd->hw_version != 1) {
-               if (etd->send_cmd(psmouse, ETP_SAMPLE_QUERY, etd->samples)) {
+       if (info->hw_version != 1) {
+               if (info->send_cmd(psmouse, ETP_SAMPLE_QUERY, info->samples)) {
                        psmouse_err(psmouse, "failed to query sample data\n");
-                       goto init_fail;
+                       return -EINVAL;
                }
                psmouse_info(psmouse,
                             "Elan sample query result %02x, %02x, %02x\n",
-                            etd->samples[0], etd->samples[1], etd->samples[2]);
+                            info->samples[0],
+                            info->samples[1],
+                            info->samples[2]);
        }
 
-       if (etd->samples[1] == 0x74 && etd->hw_version == 0x03) {
+       if (info->samples[1] == 0x74 && info->hw_version == 0x03) {
                /*
                 * This module has a bug which makes absolute mode
                 * unusable, so let's abort so we'll be using standard
@@ -1715,16 +1719,181 @@ int elantech_init(struct psmouse *psmouse)
                 */
                psmouse_info(psmouse,
                             "absolute mode broken, forcing standard PS/2 protocol\n");
+               return -ENODEV;
+       }
+
+       /* The MSB indicates the presence of the trackpoint */
+       info->has_trackpoint = (info->capabilities[0] & 0x80) == 0x80;
+
+       info->x_res = 31;
+       info->y_res = 31;
+       if (info->hw_version == 4) {
+               if (elantech_get_resolution_v4(psmouse,
+                                              &info->x_res,
+                                              &info->y_res,
+                                              &info->bus)) {
+                       psmouse_warn(psmouse,
+                                    "failed to query resolution data.\n");
+               }
+       }
+
+       return 0;
+}
+
+#if defined(CONFIG_MOUSE_PS2_ELANTECH_SMBUS)
+
+/*
+ * The newest Elantech device can use a secondary bus (over SMBus) which
+ * provides a better bandwidth and allow a better control of the touchpads.
+ * This is used to decide if we need to use this bus or not.
+ */
+enum {
+       ELANTECH_SMBUS_NOT_SET = -1,
+       ELANTECH_SMBUS_OFF,
+       ELANTECH_SMBUS_ON,
+};
+
+static int elantech_smbus = IS_ENABLED(CONFIG_MOUSE_ELAN_I2C_SMBUS) ?
+               ELANTECH_SMBUS_NOT_SET : ELANTECH_SMBUS_OFF;
+module_param_named(elantech_smbus, elantech_smbus, int, 0644);
+MODULE_PARM_DESC(elantech_smbus, "Use a secondary bus for the Elantech device.");
+
+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 i2c_board_info smbus_board = {
+               I2C_BOARD_INFO("elan_i2c", 0x15),
+               .flags = I2C_CLIENT_HOST_NOTIFY,
+       };
+
+       if (info->has_trackpoint)
+               smbus_board.properties = i2c_properties;
+
+       return psmouse_smbus_init(psmouse, &smbus_board, NULL, 0, false,
+                                 leave_breadcrumbs);
+}
+
+/**
+ * elantech_setup_smbus - called once the PS/2 devices are enumerated
+ * and decides to instantiate a SMBus InterTouch device.
+ */
+static int elantech_setup_smbus(struct psmouse *psmouse,
+                               struct elantech_device_info *info,
+                               bool leave_breadcrumbs)
+{
+       int error;
+
+       if (elantech_smbus == ELANTECH_SMBUS_OFF)
+               return -ENXIO;
+
+       if (elantech_smbus == ELANTECH_SMBUS_NOT_SET) {
+               /*
+                * New ICs are enabled by default.
+                * Old ICs are up to the user to decide.
+                */
+               if (!ETP_NEW_IC_SMBUS_HOST_NOTIFY(info->fw_version))
+                       return -ENXIO;
+       }
+
+       psmouse_info(psmouse, "Trying to set up SMBus access\n");
+
+       error = elantech_create_smbus(psmouse, info, leave_breadcrumbs);
+       if (error) {
+               if (error == -EAGAIN)
+                       psmouse_info(psmouse, "SMbus companion is not ready yet\n");
+               else
+                       psmouse_err(psmouse, "unable to create intertouch device\n");
+
+               return error;
+       }
+
+       return 0;
+}
+
+static bool elantech_use_host_notify(struct psmouse *psmouse,
+                                    struct elantech_device_info *info)
+{
+       if (ETP_NEW_IC_SMBUS_HOST_NOTIFY(info->fw_version))
+               return true;
+
+       switch (info->bus) {
+       case ETP_BUS_PS2_ONLY:
+               /* expected case */
+               break;
+       case ETP_BUS_SMB_ALERT_ONLY:
+               /* fall-through  */
+       case ETP_BUS_PS2_SMB_ALERT:
+               psmouse_dbg(psmouse, "Ignoring SMBus provider through alert protocol.\n");
+               break;
+       case ETP_BUS_SMB_HST_NTFY_ONLY:
+               /* fall-through  */
+       case ETP_BUS_PS2_SMB_HST_NTFY:
+               return true;
+       default:
+               psmouse_dbg(psmouse,
+                           "Ignoring SMBus bus provider %d.\n",
+                           info->bus);
+       }
+
+       return false;
+}
+
+int elantech_init_smbus(struct psmouse *psmouse)
+{
+       struct elantech_device_info info;
+       int error = -EINVAL;
+
+       psmouse_reset(psmouse);
+
+       error = elantech_query_info(psmouse, &info);
+       if (error)
+               goto init_fail;
+
+       if (info.hw_version < 4) {
+               error = -ENXIO;
                goto init_fail;
        }
 
+       return elantech_create_smbus(psmouse, &info, false);
+ init_fail:
+       psmouse_reset(psmouse);
+       return error;
+}
+#endif /* CONFIG_MOUSE_PS2_ELANTECH_SMBUS */
+
+/*
+ * Initialize the touchpad and create sysfs entries
+ */
+static int elantech_setup_ps2(struct psmouse *psmouse,
+                             struct elantech_device_info *info)
+{
+       struct elantech_data *etd;
+       int i;
+       int error = -EINVAL;
+       struct input_dev *tp_dev;
+
+       psmouse->private = etd = kzalloc(sizeof(*etd), GFP_KERNEL);
+       if (!etd)
+               return -ENOMEM;
+
+       etd->info = *info;
+
+       etd->parity[0] = 1;
+       for (i = 1; i < 256; i++)
+               etd->parity[i] = etd->parity[i & (i - 1)] ^ 1;
+
        if (elantech_set_absolute_mode(psmouse)) {
                psmouse_err(psmouse,
                            "failed to put touchpad into absolute mode.\n");
                goto init_fail;
        }
 
-       if (etd->fw_version == 0x381f17) {
+       if (info->fw_version == 0x381f17) {
                etd->original_set_rate = psmouse->set_rate;
                psmouse->set_rate = elantech_set_rate_restore_reg_07;
        }
@@ -1743,8 +1912,7 @@ int elantech_init(struct psmouse *psmouse)
                goto init_fail;
        }
 
-       /* The MSB indicates the presence of the trackpoint */
-       if ((etd->capabilities[0] & 0x80) == 0x80) {
+       if (info->has_trackpoint) {
                tp_dev = input_allocate_device();
 
                if (!tp_dev) {
@@ -1780,7 +1948,7 @@ int elantech_init(struct psmouse *psmouse)
        psmouse->protocol_handler = elantech_process_byte;
        psmouse->disconnect = elantech_disconnect;
        psmouse->reconnect = elantech_reconnect;
-       psmouse->pktsize = etd->hw_version > 1 ? 6 : 4;
+       psmouse->pktsize = info->hw_version > 1 ? 6 : 4;
 
        return 0;
  init_fail_tp_reg:
@@ -1789,7 +1957,70 @@ int elantech_init(struct psmouse *psmouse)
        sysfs_remove_group(&psmouse->ps2dev.serio->dev.kobj,
                           &elantech_attr_group);
  init_fail:
-       psmouse_reset(psmouse);
        kfree(etd);
        return error;
 }
+
+int elantech_init_ps2(struct psmouse *psmouse)
+{
+       struct elantech_device_info info;
+       int error = -EINVAL;
+
+       psmouse_reset(psmouse);
+
+       error = elantech_query_info(psmouse, &info);
+       if (error)
+               goto init_fail;
+
+       error = elantech_setup_ps2(psmouse, &info);
+       if (error)
+               goto init_fail;
+
+       return 0;
+ init_fail:
+       psmouse_reset(psmouse);
+       return error;
+}
+
+int elantech_init(struct psmouse *psmouse)
+{
+       struct elantech_device_info info;
+       int error = -EINVAL;
+
+       psmouse_reset(psmouse);
+
+       error = elantech_query_info(psmouse, &info);
+       if (error)
+               goto init_fail;
+
+#if defined(CONFIG_MOUSE_PS2_ELANTECH_SMBUS)
+
+       if (elantech_use_host_notify(psmouse, &info)) {
+               if (!IS_ENABLED(CONFIG_MOUSE_ELAN_I2C_SMBUS) ||
+                   !IS_ENABLED(CONFIG_MOUSE_PS2_ELANTECH_SMBUS)) {
+                       psmouse_warn(psmouse,
+                                    "The touchpad can support a better bus than the too old PS/2 protocol. "
+                                    "Make sure MOUSE_PS2_ELANTECH_SMBUS and MOUSE_ELAN_I2C_SMBUS are enabled to get a better touchpad experience.\n");
+               }
+               error = elantech_setup_smbus(psmouse, &info, true);
+               if (!error)
+                       return PSMOUSE_ELANTECH_SMBUS;
+       }
+
+#endif /* CONFIG_MOUSE_PS2_ELANTECH_SMBUS */
+
+       error = elantech_setup_ps2(psmouse, &info);
+       if (error < 0) {
+               /*
+                * Not using any flavor of Elantech support, so clean up
+                * SMbus breadcrumbs, if any.
+                */
+               psmouse_smbus_cleanup(psmouse);
+               goto init_fail;
+       }
+
+       return PSMOUSE_ELANTECH;
+ init_fail:
+       psmouse_reset(psmouse);
+       return error;
+}
index e1cbf409d9c8d0d4e7d21e13d57851ae6565b535..119727085a6055e9cc289da522cec6de03e6888d 100644 (file)
  */
 #define ETP_WEIGHT_VALUE               5
 
+/*
+ * Bus information on 3rd byte of query ETP_RESOLUTION_QUERY(0x04)
+ */
+#define ETP_BUS_PS2_ONLY               0
+#define ETP_BUS_SMB_ALERT_ONLY         1
+#define ETP_BUS_SMB_HST_NTFY_ONLY      2
+#define ETP_BUS_PS2_SMB_ALERT          3
+#define ETP_BUS_PS2_SMB_HST_NTFY       4
+
+/*
+ * New ICs are either using SMBus Host Notify or just plain PS2.
+ *
+ * ETP_FW_VERSION_QUERY is:
+ * Byte 1:
+ *  - bit 0..3: IC BODY
+ * Byte 2:
+ *  - bit 4: HiddenButton
+ *  - bit 5: PS2_SMBUS_NOTIFY
+ *  - bit 6: PS2CRCCheck
+ */
+#define ETP_NEW_IC_SMBUS_HOST_NOTIFY(fw_version)       \
+               ((((fw_version) & 0x0f2000) == 0x0f2000) && \
+                ((fw_version) & 0x0000ff) > 0)
+
 /*
  * The base position for one finger, v4 hardware
  */
@@ -114,6 +138,25 @@ struct finger_pos {
        unsigned int y;
 };
 
+struct elantech_device_info {
+       unsigned char capabilities[3];
+       unsigned char samples[3];
+       unsigned char debug;
+       unsigned char hw_version;
+       unsigned int fw_version;
+       unsigned int x_res;
+       unsigned int y_res;
+       unsigned int bus;
+       bool paritycheck;
+       bool jumpy_cursor;
+       bool reports_pressure;
+       bool crc_enabled;
+       bool set_hw_resolution;
+       bool has_trackpoint;
+       int (*send_cmd)(struct psmouse *psmouse, unsigned char c,
+                       unsigned char *param);
+};
+
 struct elantech_data {
        struct input_dev *tp_dev;       /* Relative device for trackpoint */
        char tp_phys[32];
@@ -127,27 +170,18 @@ struct elantech_data {
        unsigned char reg_24;
        unsigned char reg_25;
        unsigned char reg_26;
-       unsigned char debug;
-       unsigned char capabilities[3];
-       unsigned char samples[3];
-       bool paritycheck;
-       bool jumpy_cursor;
-       bool reports_pressure;
-       bool crc_enabled;
-       bool set_hw_resolution;
-       unsigned char hw_version;
-       unsigned int fw_version;
        unsigned int single_finger_reports;
        unsigned int y_max;
        unsigned int width;
        struct finger_pos mt[ETP_MAX_FINGERS];
        unsigned char parity[256];
-       int (*send_cmd)(struct psmouse *psmouse, unsigned char c, unsigned char *param);
+       struct elantech_device_info info;
        void (*original_set_rate)(struct psmouse *psmouse, unsigned int rate);
 };
 
 #ifdef CONFIG_MOUSE_PS2_ELANTECH
 int elantech_detect(struct psmouse *psmouse, bool set_properties);
+int elantech_init_ps2(struct psmouse *psmouse);
 int elantech_init(struct psmouse *psmouse);
 #else
 static inline int elantech_detect(struct psmouse *psmouse, bool set_properties)
@@ -158,6 +192,19 @@ static inline int elantech_init(struct psmouse *psmouse)
 {
        return -ENOSYS;
 }
+static inline int elantech_init_ps2(struct psmouse *psmouse)
+{
+       return -ENOSYS;
+}
 #endif /* CONFIG_MOUSE_PS2_ELANTECH */
 
+#if defined(CONFIG_MOUSE_PS2_ELANTECH_SMBUS)
+int elantech_init_smbus(struct psmouse *psmouse);
+#else
+static inline int elantech_init_smbus(struct psmouse *psmouse)
+{
+       return -ENOSYS;
+}
+#endif /* CONFIG_MOUSE_PS2_ELANTECH_SMBUS */
+
 #endif
index 8900c3166ebfce0f0f547f0a2b4400b86091c2e8..5ff5b1952be0c7afe810cef7f6f086f71928e150 100644 (file)
@@ -856,7 +856,17 @@ static const struct psmouse_protocol psmouse_protocols[] = {
                .name           = "ETPS/2",
                .alias          = "elantech",
                .detect         = elantech_detect,
-               .init           = elantech_init,
+               .init           = elantech_init_ps2,
+       },
+#endif
+#ifdef CONFIG_MOUSE_PS2_ELANTECH_SMBUS
+       {
+               .type           = PSMOUSE_ELANTECH_SMBUS,
+               .name           = "ETSMBus",
+               .alias          = "elantech-smbus",
+               .detect         = elantech_detect,
+               .init           = elantech_init_smbus,
+               .smbus_companion = true,
        },
 #endif
 #ifdef CONFIG_MOUSE_PS2_SENTELIC
@@ -1158,8 +1168,13 @@ static int psmouse_extensions(struct psmouse *psmouse,
        /* Try Elantech touchpad */
        if (max_proto > PSMOUSE_IMEX &&
            psmouse_try_protocol(psmouse, PSMOUSE_ELANTECH,
-                                &max_proto, set_properties, true)) {
-               return PSMOUSE_ELANTECH;
+                                &max_proto, set_properties, false)) {
+               if (!set_properties)
+                       return PSMOUSE_ELANTECH;
+
+               ret = elantech_init(psmouse);
+               if (ret >= 0)
+                       return ret;
        }
 
        if (max_proto > PSMOUSE_IMEX) {
index c7ac24d119c1551174857634960d0ec0be04cfd6..852d4b486ddb4650696b7ef72339c383e90c05c4 100644 (file)
@@ -23,6 +23,7 @@ struct psmouse_smbus_dev {
        struct i2c_client *client;
        struct list_head node;
        bool dead;
+       bool need_deactivate;
 };
 
 static LIST_HEAD(psmouse_smbus_list);
@@ -118,7 +119,10 @@ static psmouse_ret_t psmouse_smbus_process_byte(struct psmouse *psmouse)
 
 static int psmouse_smbus_reconnect(struct psmouse *psmouse)
 {
-       psmouse_deactivate(psmouse);
+       struct psmouse_smbus_dev *smbdev = psmouse->private;
+
+       if (smbdev->need_deactivate)
+               psmouse_deactivate(psmouse);
 
        return 0;
 }
@@ -225,6 +229,7 @@ void psmouse_smbus_cleanup(struct psmouse *psmouse)
 int psmouse_smbus_init(struct psmouse *psmouse,
                       const struct i2c_board_info *board,
                       const void *pdata, size_t pdata_size,
+                      bool need_deactivate,
                       bool leave_breadcrumbs)
 {
        struct psmouse_smbus_dev *smbdev;
@@ -236,13 +241,20 @@ int psmouse_smbus_init(struct psmouse *psmouse,
 
        smbdev->psmouse = psmouse;
        smbdev->board = *board;
+       smbdev->need_deactivate = need_deactivate;
 
-       smbdev->board.platform_data = kmemdup(pdata, pdata_size, GFP_KERNEL);
-       if (!smbdev->board.platform_data) {
-               kfree(smbdev);
-               return -ENOMEM;
+       if (pdata) {
+               smbdev->board.platform_data = kmemdup(pdata, pdata_size,
+                                                     GFP_KERNEL);
+               if (!smbdev->board.platform_data) {
+                       kfree(smbdev);
+                       return -ENOMEM;
+               }
        }
 
+       if (need_deactivate)
+               psmouse_deactivate(psmouse);
+
        psmouse->private = smbdev;
        psmouse->protocol_handler = psmouse_smbus_process_byte;
        psmouse->reconnect = psmouse_smbus_reconnect;
@@ -250,8 +262,6 @@ int psmouse_smbus_init(struct psmouse *psmouse,
        psmouse->disconnect = psmouse_smbus_disconnect;
        psmouse->resync_time = 0;
 
-       psmouse_deactivate(psmouse);
-
        mutex_lock(&psmouse_smbus_mutex);
        list_add_tail(&smbdev->node, &psmouse_smbus_list);
        mutex_unlock(&psmouse_smbus_mutex);
index 71ac50082c8b41a1ceb032779402ef79324828b7..64c3a5d3fb3e6b92cd8c9b58102f7a29d7acb55d 100644 (file)
@@ -68,6 +68,7 @@ enum psmouse_type {
        PSMOUSE_VMMOUSE,
        PSMOUSE_BYD,
        PSMOUSE_SYNAPTICS_SMBUS,
+       PSMOUSE_ELANTECH_SMBUS,
        PSMOUSE_AUTO            /* This one should always be last */
 };
 
@@ -224,6 +225,7 @@ struct i2c_board_info;
 int psmouse_smbus_init(struct psmouse *psmouse,
                       const struct i2c_board_info *board,
                       const void *pdata, size_t pdata_size,
+                      bool need_deactivate,
                       bool leave_breadcrumbs);
 void psmouse_smbus_cleanup(struct psmouse *psmouse);
 
index 60f2c463d1cc7d2805d9a98923ad3934d5ff2294..e3e0ef585eb677797aed429f1dc49a546a2eff31 100644 (file)
@@ -1748,7 +1748,7 @@ static int synaptics_create_intertouch(struct psmouse *psmouse,
        };
 
        return psmouse_smbus_init(psmouse, &intertouch_board,
-                                 &pdata, sizeof(pdata),
+                                 &pdata, sizeof(pdata), true,
                                  leave_breadcrumbs);
 }
 
index 3e613afa10b4ba967a7def75f631997c0e0c6ae4..32267c1afebc7015eaa7588e0c9ea1f332a4b5b1 100644 (file)
@@ -164,6 +164,17 @@ config TOUCHSCREEN_CHIPONE_ICN8318
          To compile this driver as a module, choose M here: the
          module will be called chipone_icn8318.
 
+config TOUCHSCREEN_CHIPONE_ICN8505
+       tristate "chipone icn8505 touchscreen controller"
+       depends on I2C && ACPI
+       help
+         Say Y here if you have a ChipOne icn8505 based I2C touchscreen.
+
+         If unsure, say N.
+
+         To compile this driver as a module, choose M here: the
+         module will be called chipone_icn8505.
+
 config TOUCHSCREEN_CY8CTMG110
        tristate "cy8ctmg110 touchscreen"
        depends on I2C
index dddae7973436cc01d611ec368ee4a5b058c6bab6..fd4fd32fb73f37a7a17f1bd4c26ca915c1998d0b 100644 (file)
@@ -19,6 +19,7 @@ obj-$(CONFIG_TOUCHSCREEN_ATMEL_MXT)   += atmel_mxt_ts.o
 obj-$(CONFIG_TOUCHSCREEN_AUO_PIXCIR)   += auo-pixcir-ts.o
 obj-$(CONFIG_TOUCHSCREEN_BU21013)      += bu21013_ts.o
 obj-$(CONFIG_TOUCHSCREEN_CHIPONE_ICN8318)      += chipone_icn8318.o
+obj-$(CONFIG_TOUCHSCREEN_CHIPONE_ICN8505)      += chipone_icn8505.o
 obj-$(CONFIG_TOUCHSCREEN_CY8CTMG110)   += cy8ctmg110_ts.o
 obj-$(CONFIG_TOUCHSCREEN_CYTTSP_CORE)  += cyttsp_core.o
 obj-$(CONFIG_TOUCHSCREEN_CYTTSP_I2C)   += cyttsp_i2c.o cyttsp_i2c_common.o
diff --git a/drivers/input/touchscreen/chipone_icn8505.c b/drivers/input/touchscreen/chipone_icn8505.c
new file mode 100644 (file)
index 0000000..c768186
--- /dev/null
@@ -0,0 +1,520 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * Driver for ChipOne icn8505 i2c touchscreen controller
+ *
+ * Copyright (c) 2015-2018 Red Hat Inc.
+ *
+ * Red Hat authors:
+ * Hans de Goede <hdegoede@redhat.com>
+ */
+
+#include <asm/unaligned.h>
+#include <linux/acpi.h>
+#include <linux/crc32.h>
+#include <linux/delay.h>
+#include <linux/firmware.h>
+#include <linux/interrupt.h>
+#include <linux/i2c.h>
+#include <linux/input.h>
+#include <linux/input/mt.h>
+#include <linux/input/touchscreen.h>
+#include <linux/module.h>
+
+/* Normal operation mode defines */
+#define ICN8505_REG_ADDR_WIDTH         16
+
+#define ICN8505_REG_POWER              0x0004
+#define ICN8505_REG_TOUCHDATA          0x1000
+#define ICN8505_REG_CONFIGDATA         0x8000
+
+/* ICN8505_REG_POWER commands */
+#define ICN8505_POWER_ACTIVE           0x00
+#define ICN8505_POWER_MONITOR          0x01
+#define ICN8505_POWER_HIBERNATE                0x02
+/*
+ * The Android driver uses these to turn on/off the charger filter, but the
+ * filter is way too aggressive making e.g. onscreen keyboards unusable.
+ */
+#define ICN8505_POWER_ENA_CHARGER_MODE 0x55
+#define ICN8505_POWER_DIS_CHARGER_MODE 0x66
+
+#define ICN8505_MAX_TOUCHES            10
+
+/* Programming mode defines */
+#define ICN8505_PROG_I2C_ADDR          0x30
+#define ICN8505_PROG_REG_ADDR_WIDTH    24
+
+#define MAX_FW_UPLOAD_TRIES            3
+
+struct icn8505_touch {
+       u8 slot;
+       u8 x[2];
+       u8 y[2];
+       u8 pressure;    /* Seems more like finger width then pressure really */
+       u8 event;
+/* The difference between 2 and 3 is unclear */
+#define ICN8505_EVENT_NO_DATA  1 /* No finger seen yet since wakeup */
+#define ICN8505_EVENT_UPDATE1  2 /* New or updated coordinates */
+#define ICN8505_EVENT_UPDATE2  3 /* New or updated coordinates */
+#define ICN8505_EVENT_END      4 /* Finger lifted */
+} __packed;
+
+struct icn8505_touch_data {
+       u8 softbutton;
+       u8 touch_count;
+       struct icn8505_touch touches[ICN8505_MAX_TOUCHES];
+} __packed;
+
+struct icn8505_data {
+       struct i2c_client *client;
+       struct input_dev *input;
+       struct gpio_desc *wake_gpio;
+       struct touchscreen_properties prop;
+       char firmware_name[32];
+};
+
+static int icn8505_read_xfer(struct i2c_client *client, u16 i2c_addr,
+                            int reg_addr, int reg_addr_width,
+                            void *data, int len, bool silent)
+{
+       u8 buf[3];
+       int i, ret;
+       struct i2c_msg msg[2] = {
+               {
+                       .addr = i2c_addr,
+                       .buf = buf,
+                       .len = reg_addr_width / 8,
+               },
+               {
+                       .addr = i2c_addr,
+                       .flags = I2C_M_RD,
+                       .buf = data,
+                       .len = len,
+               }
+       };
+
+       for (i = 0; i < (reg_addr_width / 8); i++)
+               buf[i] = (reg_addr >> (reg_addr_width - (i + 1) * 8)) & 0xff;
+
+       ret = i2c_transfer(client->adapter, msg, 2);
+       if (ret != ARRAY_SIZE(msg)) {
+               if (ret >= 0)
+                       ret = -EIO;
+               if (!silent)
+                       dev_err(&client->dev,
+                               "Error reading addr %#x reg %#x: %d\n",
+                               i2c_addr, reg_addr, ret);
+               return ret;
+       }
+
+       return 0;
+}
+
+static int icn8505_write_xfer(struct i2c_client *client, u16 i2c_addr,
+                             int reg_addr, int reg_addr_width,
+                             const void *data, int len, bool silent)
+{
+       u8 buf[3 + 32]; /* 3 bytes for 24 bit reg-addr + 32 bytes max len */
+       int i, ret;
+       struct i2c_msg msg = {
+               .addr = i2c_addr,
+               .buf = buf,
+               .len = reg_addr_width / 8 + len,
+       };
+
+       if (WARN_ON(len > 32))
+               return -EINVAL;
+
+       for (i = 0; i < (reg_addr_width / 8); i++)
+               buf[i] = (reg_addr >> (reg_addr_width - (i + 1) * 8)) & 0xff;
+
+       memcpy(buf + reg_addr_width / 8, data, len);
+
+       ret = i2c_transfer(client->adapter, &msg, 1);
+       if (ret != 1) {
+               if (ret >= 0)
+                       ret = -EIO;
+               if (!silent)
+                       dev_err(&client->dev,
+                               "Error writing addr %#x reg %#x: %d\n",
+                               i2c_addr, reg_addr, ret);
+               return ret;
+       }
+
+       return 0;
+}
+
+static int icn8505_read_data(struct icn8505_data *icn8505, int reg,
+                            void *buf, int len)
+{
+       return icn8505_read_xfer(icn8505->client, icn8505->client->addr, reg,
+                                ICN8505_REG_ADDR_WIDTH, buf, len, false);
+}
+
+static int icn8505_read_reg_silent(struct icn8505_data *icn8505, int reg)
+{
+       u8 buf;
+       int error;
+
+       error = icn8505_read_xfer(icn8505->client, icn8505->client->addr, reg,
+                                 ICN8505_REG_ADDR_WIDTH, &buf, 1, true);
+       if (error)
+               return error;
+
+       return buf;
+}
+
+static int icn8505_write_reg(struct icn8505_data *icn8505, int reg, u8 val)
+{
+       return icn8505_write_xfer(icn8505->client, icn8505->client->addr, reg,
+                                 ICN8505_REG_ADDR_WIDTH, &val, 1, false);
+}
+
+static int icn8505_read_prog_data(struct icn8505_data *icn8505, int reg,
+                                 void *buf, int len)
+{
+       return icn8505_read_xfer(icn8505->client, ICN8505_PROG_I2C_ADDR, reg,
+                                ICN8505_PROG_REG_ADDR_WIDTH, buf, len, false);
+}
+
+static int icn8505_write_prog_data(struct icn8505_data *icn8505, int reg,
+                                  const void *buf, int len)
+{
+       return icn8505_write_xfer(icn8505->client, ICN8505_PROG_I2C_ADDR, reg,
+                                 ICN8505_PROG_REG_ADDR_WIDTH, buf, len, false);
+}
+
+static int icn8505_write_prog_reg(struct icn8505_data *icn8505, int reg, u8 val)
+{
+       return icn8505_write_xfer(icn8505->client, ICN8505_PROG_I2C_ADDR, reg,
+                                 ICN8505_PROG_REG_ADDR_WIDTH, &val, 1, false);
+}
+
+/*
+ * Note this function uses a number of magic register addresses and values,
+ * there are deliberately no defines for these because the algorithm is taken
+ * from the icn85xx Android driver and I do not want to make up possibly wrong
+ * names for the addresses and/or values.
+ */
+static int icn8505_try_fw_upload(struct icn8505_data *icn8505,
+                                const struct firmware *fw)
+{
+       struct device *dev = &icn8505->client->dev;
+       size_t offset, count;
+       int error;
+       u8 buf[4];
+       u32 crc;
+
+       /* Put the controller in programming mode */
+       error = icn8505_write_prog_reg(icn8505, 0xcc3355, 0x5a);
+       if (error)
+               return error;
+
+       usleep_range(2000, 5000);
+
+       error = icn8505_write_prog_reg(icn8505, 0x040400, 0x01);
+       if (error)
+               return error;
+
+       usleep_range(2000, 5000);
+
+       error = icn8505_read_prog_data(icn8505, 0x040002, buf, 1);
+       if (error)
+               return error;
+
+       if (buf[0] != 0x85) {
+               dev_err(dev, "Failed to enter programming mode\n");
+               return -ENODEV;
+       }
+
+       usleep_range(1000, 5000);
+
+       /* Enable CRC mode */
+       error = icn8505_write_prog_reg(icn8505, 0x40028, 1);
+       if (error)
+               return error;
+
+       /* Send the firmware to SRAM */
+       for (offset = 0; offset < fw->size; offset += count) {
+               count = min_t(size_t, fw->size - offset, 32);
+               error = icn8505_write_prog_data(icn8505, offset,
+                                             fw->data + offset, count);
+               if (error)
+                       return error;
+       }
+
+       /* Disable CRC mode */
+       error = icn8505_write_prog_reg(icn8505, 0x40028, 0);
+       if (error)
+               return error;
+
+       /* Get and check length and CRC */
+       error = icn8505_read_prog_data(icn8505, 0x40034, buf, 2);
+       if (error)
+               return error;
+
+       if (get_unaligned_le16(buf) != fw->size) {
+               dev_warn(dev, "Length mismatch after uploading fw\n");
+               return -EIO;
+       }
+
+       error = icn8505_read_prog_data(icn8505, 0x4002c, buf, 4);
+       if (error)
+               return error;
+
+       crc = crc32_be(0, fw->data, fw->size);
+       if (get_unaligned_le32(buf) != crc) {
+               dev_warn(dev, "CRC mismatch after uploading fw\n");
+               return -EIO;
+       }
+
+       /* Boot controller from SRAM */
+       error = icn8505_write_prog_reg(icn8505, 0x40400, 0x03);
+       if (error)
+               return error;
+
+       usleep_range(2000, 5000);
+       return 0;
+}
+
+static int icn8505_upload_fw(struct icn8505_data *icn8505)
+{
+       struct device *dev = &icn8505->client->dev;
+       const struct firmware *fw;
+       int i, error;
+
+       /*
+        * Always load the firmware, even if we don't need it at boot, we
+        * we may need it at resume. Having loaded it once will make the
+        * firmware class code cache it at suspend/resume.
+        */
+       error = request_firmware(&fw, icn8505->firmware_name, dev);
+       if (error) {
+               dev_err(dev, "Firmware request error %d\n", error);
+               return error;
+       }
+
+       /* Check if the controller is not already up and running */
+       if (icn8505_read_reg_silent(icn8505, 0x000a) == 0x85)
+               goto success;
+
+       for (i = 1; i <= MAX_FW_UPLOAD_TRIES; i++) {
+               error = icn8505_try_fw_upload(icn8505, fw);
+               if (!error)
+                       goto success;
+
+               dev_err(dev, "Failed to upload firmware: %d (attempt %d/%d)\n",
+                       error, i, MAX_FW_UPLOAD_TRIES);
+               usleep_range(2000, 5000);
+       }
+
+success:
+       release_firmware(fw);
+       return error;
+}
+
+static bool icn8505_touch_active(u8 event)
+{
+       return event == ICN8505_EVENT_UPDATE1 ||
+              event == ICN8505_EVENT_UPDATE2;
+}
+
+static irqreturn_t icn8505_irq(int irq, void *dev_id)
+{
+       struct icn8505_data *icn8505 = dev_id;
+       struct device *dev = &icn8505->client->dev;
+       struct icn8505_touch_data touch_data;
+       int i, error;
+
+       error = icn8505_read_data(icn8505, ICN8505_REG_TOUCHDATA,
+                                 &touch_data, sizeof(touch_data));
+       if (error) {
+               dev_err(dev, "Error reading touch data: %d\n", error);
+               return IRQ_HANDLED;
+       }
+
+       if (touch_data.touch_count > ICN8505_MAX_TOUCHES) {
+               dev_warn(dev, "Too many touches %d > %d\n",
+                        touch_data.touch_count, ICN8505_MAX_TOUCHES);
+               touch_data.touch_count = ICN8505_MAX_TOUCHES;
+       }
+
+       for (i = 0; i < touch_data.touch_count; i++) {
+               struct icn8505_touch *touch = &touch_data.touches[i];
+               bool act = icn8505_touch_active(touch->event);
+
+               input_mt_slot(icn8505->input, touch->slot);
+               input_mt_report_slot_state(icn8505->input, MT_TOOL_FINGER, act);
+               if (!act)
+                       continue;
+
+               touchscreen_report_pos(icn8505->input, &icn8505->prop,
+                                      get_unaligned_le16(touch->x),
+                                      get_unaligned_le16(touch->y),
+                                      true);
+       }
+
+       input_mt_sync_frame(icn8505->input);
+       input_report_key(icn8505->input, KEY_LEFTMETA,
+                        touch_data.softbutton == 1);
+       input_sync(icn8505->input);
+
+       return IRQ_HANDLED;
+}
+
+static int icn8505_probe_acpi(struct icn8505_data *icn8505, struct device *dev)
+{
+       struct acpi_buffer buffer = { ACPI_ALLOCATE_BUFFER, NULL };
+       const char *subsys = "unknown";
+       struct acpi_device *adev;
+       union acpi_object *obj;
+       acpi_status status;
+
+       adev = ACPI_COMPANION(dev);
+       if (!adev)
+               return -ENODEV;
+
+       status = acpi_evaluate_object(adev->handle, "_SUB", NULL, &buffer);
+       if (ACPI_SUCCESS(status)) {
+               obj = buffer.pointer;
+               if (obj->type == ACPI_TYPE_STRING)
+                       subsys = obj->string.pointer;
+               else
+                       dev_warn(dev, "Warning ACPI _SUB did not return a string\n");
+       } else {
+               dev_warn(dev, "Warning ACPI _SUB failed: %#x\n", status);
+               buffer.pointer = NULL;
+       }
+
+       snprintf(icn8505->firmware_name, sizeof(icn8505->firmware_name),
+                "chipone/icn8505-%s.fw", subsys);
+
+       kfree(buffer.pointer);
+       return 0;
+}
+
+static int icn8505_probe(struct i2c_client *client)
+{
+       struct device *dev = &client->dev;
+       struct icn8505_data *icn8505;
+       struct input_dev *input;
+       __le16 resolution[2];
+       int error;
+
+       if (!client->irq) {
+               dev_err(dev, "No irq specified\n");
+               return -EINVAL;
+       }
+
+       icn8505 = devm_kzalloc(dev, sizeof(*icn8505), GFP_KERNEL);
+       if (!icn8505)
+               return -ENOMEM;
+
+       input = devm_input_allocate_device(dev);
+       if (!input)
+               return -ENOMEM;
+
+       input->name = client->name;
+       input->id.bustype = BUS_I2C;
+
+       input_set_capability(input, EV_ABS, ABS_MT_POSITION_X);
+       input_set_capability(input, EV_ABS, ABS_MT_POSITION_Y);
+       input_set_capability(input, EV_KEY, KEY_LEFTMETA);
+
+       icn8505->client = client;
+       icn8505->input = input;
+       input_set_drvdata(input, icn8505);
+
+       error = icn8505_probe_acpi(icn8505, dev);
+       if (error)
+               return error;
+
+       error = icn8505_upload_fw(icn8505);
+       if (error)
+               return error;
+
+       error = icn8505_read_data(icn8505, ICN8505_REG_CONFIGDATA,
+                               resolution, sizeof(resolution));
+       if (error) {
+               dev_err(dev, "Error reading resolution: %d\n", error);
+               return error;
+       }
+
+       input_set_abs_params(input, ABS_MT_POSITION_X, 0,
+                            le16_to_cpu(resolution[0]) - 1, 0, 0);
+       input_set_abs_params(input, ABS_MT_POSITION_Y, 0,
+                            le16_to_cpu(resolution[1]) - 1, 0, 0);
+
+       touchscreen_parse_properties(input, true, &icn8505->prop);
+       if (!input_abs_get_max(input, ABS_MT_POSITION_X) ||
+           !input_abs_get_max(input, ABS_MT_POSITION_Y)) {
+               dev_err(dev, "Error touchscreen-size-x and/or -y missing\n");
+               return -EINVAL;
+       }
+
+       error = input_mt_init_slots(input, ICN8505_MAX_TOUCHES,
+                                 INPUT_MT_DIRECT | INPUT_MT_DROP_UNUSED);
+       if (error)
+               return error;
+
+       error = devm_request_threaded_irq(dev, client->irq, NULL, icn8505_irq,
+                                       IRQF_ONESHOT, client->name, icn8505);
+       if (error) {
+               dev_err(dev, "Error requesting irq: %d\n", error);
+               return error;
+       }
+
+       error = input_register_device(input);
+       if (error)
+               return error;
+
+       i2c_set_clientdata(client, icn8505);
+       return 0;
+}
+
+static int __maybe_unused icn8505_suspend(struct device *dev)
+{
+       struct icn8505_data *icn8505 = i2c_get_clientdata(to_i2c_client(dev));
+
+       disable_irq(icn8505->client->irq);
+
+       icn8505_write_reg(icn8505, ICN8505_REG_POWER, ICN8505_POWER_HIBERNATE);
+
+       return 0;
+}
+
+static int __maybe_unused icn8505_resume(struct device *dev)
+{
+       struct icn8505_data *icn8505 = i2c_get_clientdata(to_i2c_client(dev));
+       int error;
+
+       error = icn8505_upload_fw(icn8505);
+       if (error)
+               return error;
+
+       enable_irq(icn8505->client->irq);
+       return 0;
+}
+
+static SIMPLE_DEV_PM_OPS(icn8505_pm_ops, icn8505_suspend, icn8505_resume);
+
+static const struct acpi_device_id icn8505_acpi_match[] = {
+       { "CHPN0001" },
+       { }
+};
+MODULE_DEVICE_TABLE(acpi, icn8505_acpi_match);
+
+static struct i2c_driver icn8505_driver = {
+       .driver = {
+               .name   = "chipone_icn8505",
+               .pm     = &icn8505_pm_ops,
+               .acpi_match_table = icn8505_acpi_match,
+       },
+       .probe_new = icn8505_probe,
+};
+
+module_i2c_driver(icn8505_driver);
+
+MODULE_DESCRIPTION("ChipOne icn8505 I2C Touchscreen Driver");
+MODULE_AUTHOR("Hans de Goede <hdegoede@redhat.com>");
+MODULE_LICENSE("GPL");
index c6cf90868503e1c9697bd961ecbbe08495f221bd..d61570d64ee76bd8ac161daa6a9dc55b23a5ee40 100644 (file)
@@ -440,6 +440,8 @@ static int panjit_read_data(struct usbtouch_usb *dev, unsigned char *pkt)
 #define MTOUCHUSB_RESET                 7
 #define MTOUCHUSB_REQ_CTRLLR_ID         10
 
+#define MTOUCHUSB_REQ_CTRLLR_ID_LEN    16
+
 static int mtouch_read_data(struct usbtouch_usb *dev, unsigned char *pkt)
 {
        if (hwcalib_xy) {
@@ -454,11 +456,93 @@ static int mtouch_read_data(struct usbtouch_usb *dev, unsigned char *pkt)
        return 1;
 }
 
+struct mtouch_priv {
+       u8 fw_rev_major;
+       u8 fw_rev_minor;
+};
+
+static ssize_t mtouch_firmware_rev_show(struct device *dev,
+                               struct device_attribute *attr, char *output)
+{
+       struct usb_interface *intf = to_usb_interface(dev);
+       struct usbtouch_usb *usbtouch = usb_get_intfdata(intf);
+       struct mtouch_priv *priv = usbtouch->priv;
+
+       return scnprintf(output, PAGE_SIZE, "%1x.%1x\n",
+                        priv->fw_rev_major, priv->fw_rev_minor);
+}
+static DEVICE_ATTR(firmware_rev, 0444, mtouch_firmware_rev_show, NULL);
+
+static struct attribute *mtouch_attrs[] = {
+       &dev_attr_firmware_rev.attr,
+       NULL
+};
+
+static const struct attribute_group mtouch_attr_group = {
+       .attrs = mtouch_attrs,
+};
+
+static int mtouch_get_fw_revision(struct usbtouch_usb *usbtouch)
+{
+       struct usb_device *udev = interface_to_usbdev(usbtouch->interface);
+       struct mtouch_priv *priv = usbtouch->priv;
+       u8 *buf;
+       int ret;
+
+       buf = kzalloc(MTOUCHUSB_REQ_CTRLLR_ID_LEN, GFP_NOIO);
+       if (!buf)
+               return -ENOMEM;
+
+       ret = usb_control_msg(udev, usb_rcvctrlpipe(udev, 0),
+                             MTOUCHUSB_REQ_CTRLLR_ID,
+                             USB_DIR_IN | USB_TYPE_VENDOR | USB_RECIP_DEVICE,
+                             0, 0, buf, MTOUCHUSB_REQ_CTRLLR_ID_LEN,
+                             USB_CTRL_SET_TIMEOUT);
+       if (ret != MTOUCHUSB_REQ_CTRLLR_ID_LEN) {
+               dev_warn(&usbtouch->interface->dev,
+                        "Failed to read FW rev: %d\n", ret);
+               ret = ret < 0 ? ret : -EIO;
+               goto free;
+       }
+
+       priv->fw_rev_major = buf[3];
+       priv->fw_rev_minor = buf[4];
+
+       ret = 0;
+
+free:
+       kfree(buf);
+       return ret;
+}
+
+static int mtouch_alloc(struct usbtouch_usb *usbtouch)
+{
+       int ret;
+
+       usbtouch->priv = kmalloc(sizeof(struct mtouch_priv), GFP_KERNEL);
+       if (!usbtouch->priv)
+               return -ENOMEM;
+
+       ret = sysfs_create_group(&usbtouch->interface->dev.kobj,
+                                &mtouch_attr_group);
+       if (ret) {
+               kfree(usbtouch->priv);
+               usbtouch->priv = NULL;
+               return ret;
+       }
+
+       return 0;
+}
+
 static int mtouch_init(struct usbtouch_usb *usbtouch)
 {
        int ret, i;
        struct usb_device *udev = interface_to_usbdev(usbtouch->interface);
 
+       ret = mtouch_get_fw_revision(usbtouch);
+       if (ret)
+               return ret;
+
        ret = usb_control_msg(udev, usb_rcvctrlpipe(udev, 0),
                              MTOUCHUSB_RESET,
                              USB_DIR_OUT | USB_TYPE_VENDOR | USB_RECIP_DEVICE,
@@ -492,6 +576,14 @@ static int mtouch_init(struct usbtouch_usb *usbtouch)
 
        return 0;
 }
+
+static void mtouch_exit(struct usbtouch_usb *usbtouch)
+{
+       struct mtouch_priv *priv = usbtouch->priv;
+
+       sysfs_remove_group(&usbtouch->interface->dev.kobj, &mtouch_attr_group);
+       kfree(priv);
+}
 #endif
 
 
@@ -1119,7 +1211,9 @@ static struct usbtouch_device_info usbtouch_dev_info[] = {
                .max_yc         = 0x4000,
                .rept_size      = 11,
                .read_data      = mtouch_read_data,
+               .alloc          = mtouch_alloc,
                .init           = mtouch_init,
+               .exit           = mtouch_exit,
        },
 #endif