Merge branch 'rotary-encoder' into next
authorDmitry Torokhov <dmitry.torokhov@gmail.com>
Fri, 4 Mar 2016 19:32:40 +0000 (11:32 -0800)
committerDmitry Torokhov <dmitry.torokhov@gmail.com>
Fri, 4 Mar 2016 19:32:40 +0000 (11:32 -0800)
Bring in updates to roraty encoder driver switching it away from legacy
platform data and over to generic device properties and adding support
for encoders using more than 2 GPIOs.

58 files changed:
Documentation/devicetree/bindings/goldfish/events.txt [new file with mode: 0644]
Documentation/devicetree/bindings/input/gpio-keys.txt
Documentation/devicetree/bindings/input/touchscreen/cyttsp.txt [new file with mode: 0644]
Documentation/devicetree/bindings/input/touchscreen/goodix.txt
Documentation/devicetree/bindings/input/touchscreen/pixcir_i2c_ts.txt
Documentation/devicetree/bindings/input/touchscreen/ts4800-ts.txt [new file with mode: 0644]
drivers/gpio/gpiolib-acpi.c
drivers/gpio/gpiolib.c
drivers/gpio/gpiolib.h
drivers/input/joystick/xpad.c
drivers/input/keyboard/Kconfig
drivers/input/keyboard/goldfish_events.c
drivers/input/keyboard/gpio_keys.c
drivers/input/keyboard/omap-keypad.c
drivers/input/keyboard/snvs_pwrkey.c
drivers/input/keyboard/spear-keyboard.c
drivers/input/misc/bma150.c
drivers/input/misc/da9063_onkey.c
drivers/input/misc/sparcspkr.c
drivers/input/misc/uinput.c
drivers/input/mouse/Kconfig
drivers/input/mouse/Makefile
drivers/input/mouse/alps.c
drivers/input/mouse/byd.c [new file with mode: 0644]
drivers/input/mouse/byd.h [new file with mode: 0644]
drivers/input/mouse/cyapa.c
drivers/input/mouse/cyapa.h
drivers/input/mouse/cyapa_gen3.c
drivers/input/mouse/cyapa_gen5.c
drivers/input/mouse/cyapa_gen6.c
drivers/input/mouse/elantech.c
drivers/input/mouse/focaltech.c
drivers/input/mouse/focaltech.h
drivers/input/mouse/logips2pp.c
drivers/input/mouse/logips2pp.h
drivers/input/mouse/psmouse-base.c
drivers/input/mouse/psmouse.h
drivers/input/touchscreen/Kconfig
drivers/input/touchscreen/Makefile
drivers/input/touchscreen/atmel_mxt_ts.c
drivers/input/touchscreen/cyttsp_core.c
drivers/input/touchscreen/cyttsp_core.h
drivers/input/touchscreen/cyttsp_i2c.c
drivers/input/touchscreen/cyttsp_spi.c
drivers/input/touchscreen/egalax_ts_serial.c [new file with mode: 0644]
drivers/input/touchscreen/goodix.c
drivers/input/touchscreen/melfas_mip4.c [new file with mode: 0644]
drivers/input/touchscreen/pcap_ts.c
drivers/input/touchscreen/pixcir_i2c_ts.c
drivers/input/touchscreen/rohm_bu21023.c
drivers/input/touchscreen/stmpe-ts.c
drivers/input/touchscreen/ti_am335x_tsc.c
drivers/input/touchscreen/ts4800-ts.c [new file with mode: 0644]
drivers/input/touchscreen/wacom_w8001.c
include/linux/input/cyttsp.h
include/linux/uinput.h
include/uapi/linux/serio.h
include/uapi/linux/uinput.h

diff --git a/Documentation/devicetree/bindings/goldfish/events.txt b/Documentation/devicetree/bindings/goldfish/events.txt
new file mode 100644 (file)
index 0000000..5babf46
--- /dev/null
@@ -0,0 +1,17 @@
+Android Goldfish Events Keypad
+
+Android goldfish events keypad device generated by android emulator.
+
+Required properties:
+
+- compatible : should contain "google,goldfish-events-keypad" to match emulator
+- reg        : <registers mapping>
+- interrupts : <interrupt mapping>
+
+Example:
+
+       goldfish-events@9040000 {
+               compatible = "google,goldfish-events-keypad";
+               reg = <0x9040000 0x1000>;
+               interrupts = <0x5>;
+       };
index cf1333d..2164123 100644 (file)
@@ -6,6 +6,7 @@ Required properties:
 Optional properties:
        - autorepeat: Boolean, Enable auto repeat feature of Linux input
          subsystem.
+       - label: String, name of the input device.
 
 Each button (key) is represented as a sub-node of "gpio-keys":
 Subnode properties:
diff --git a/Documentation/devicetree/bindings/input/touchscreen/cyttsp.txt b/Documentation/devicetree/bindings/input/touchscreen/cyttsp.txt
new file mode 100644 (file)
index 0000000..b75d4cf
--- /dev/null
@@ -0,0 +1,95 @@
+* Cypress cyttsp touchscreen controller
+
+Required properties:
+ - compatible          : must be "cypress,cyttsp-i2c" or "cypress,cyttsp-spi"
+ - reg                 : Device I2C address or SPI chip select number
+ - spi-max-frequency   : Maximum SPI clocking speed of the device (for cyttsp-spi)
+ - interrupt-parent    : the phandle for the gpio controller
+                         (see interrupt binding[0]).
+ - interrupts          : (gpio) interrupt to which the chip is connected
+                         (see interrupt binding[0]).
+ - bootloader-key      : the 8-byte bootloader key that is required to switch
+                         the chip from bootloader mode (default mode) to
+                         application mode.
+                         This property has to be specified as an array of 8
+                         '/bits/ 8' values.
+
+Optional properties:
+ - reset-gpios         : the reset gpio the chip is connected to
+                         (see GPIO binding[1] for more details).
+ - touchscreen-size-x  : horizontal resolution of touchscreen (in pixels)
+ - touchscreen-size-y  : vertical resolution of touchscreen (in pixels)
+ - touchscreen-fuzz-x  : horizontal noise value of the absolute input device
+                         (in pixels)
+ - touchscreen-fuzz-y  : vertical noise value of the absolute input device
+                         (in pixels)
+ - active-distance     : the distance in pixels beyond which a touch must move
+                         before movement is detected and reported by the device.
+                         Valid values: 0-15.
+ - active-interval-ms  : the minimum period in ms between consecutive
+                         scanning/processing cycles when the chip is in active mode.
+                         Valid values: 0-255.
+ - lowpower-interval-ms        : the minimum period in ms between consecutive
+                         scanning/processing cycles when the chip is in low-power mode.
+                         Valid values: 0-2550
+ - touch-timeout-ms    : minimum time in ms spent in the active power state while no
+                         touches are detected before entering low-power mode.
+                         Valid values: 0-2550
+ - use-handshake       : enable register-based handshake (boolean). This should
+                         only be used if the chip is configured to use 'blocking
+                         communication with timeout' (in this case the device
+                         generates an interrupt at the end of every
+                         scanning/processing cycle).
+
+[0]: Documentation/devicetree/bindings/interrupt-controller/interrupts.txt
+[1]: Documentation/devicetree/bindings/gpio/gpio.txt
+
+Example:
+       &i2c1 {
+               /* ... */
+               cyttsp@a {
+                       compatible = "cypress,cyttsp-i2c";
+                       reg = <0xa>;
+                       interrupt-parent = <&gpio0>;
+                       interrupts = <28 0>;
+                       reset-gpios = <&gpio3 4 GPIO_ACTIVE_LOW>;
+
+                       touchscreen-size-x = <800>;
+                       touchscreen-size-y = <480>;
+                       touchscreen-fuzz-x = <4>;
+                       touchscreen-fuzz-y = <7>;
+
+                       bootloader-key = /bits/ 8 <0x01 0x02 0x03 0x04 0x05 0x06 0x07 0x08>;
+                       active-distance = <8>;
+                       active-interval-ms = <0>;
+                       lowpower-interval-ms = <200>;
+                       touch-timeout-ms = <100>;
+               };
+
+               /* ... */
+       };
+
+       &mcspi1 {
+               /* ... */
+               cyttsp@0 {
+                       compatible = "cypress,cyttsp-spi";
+                       spi-max-frequency = <6000000>;
+                       reg = <0>;
+                       interrupt-parent = <&gpio0>;
+                       interrupts = <28 0>;
+                       reset-gpios = <&gpio3 4 GPIO_ACTIVE_LOW>;
+
+                       touchscreen-size-x = <800>;
+                       touchscreen-size-y = <480>;
+                       touchscreen-fuzz-x = <4>;
+                       touchscreen-fuzz-y = <7>;
+
+                       bootloader-key = /bits/ 8 <0x01 0x02 0x03 0x04 0x05 0x06 0x07 0x08>;
+                       active-distance = <8>;
+                       active-interval-ms = <0>;
+                       lowpower-interval-ms = <200>;
+                       touch-timeout-ms = <100>;
+               };
+
+               /* ... */
+       };
index 8ba98ee..c98757a 100644 (file)
@@ -13,6 +13,17 @@ Required properties:
  - interrupt-parent    : Interrupt controller to which the chip is connected
  - interrupts          : Interrupt to which the chip is connected
 
+Optional properties:
+
+ - irq-gpios           : GPIO pin used for IRQ. The driver uses the
+                         interrupt gpio pin as output to reset the device.
+ - reset-gpios         : GPIO pin used for reset
+
+ - touchscreen-inverted-x  : X axis is inverted (boolean)
+ - touchscreen-inverted-y  : Y axis is inverted (boolean)
+ - touchscreen-swapped-x-y : X and Y axis are swapped (boolean)
+                             (swapping is done after inverting the axis)
+
 Example:
 
        i2c@00000000 {
@@ -23,6 +34,9 @@ Example:
                        reg = <0x5d>;
                        interrupt-parent = <&gpio>;
                        interrupts = <0 0>;
+
+                       irq-gpios = <&gpio1 0 0>;
+                       reset-gpios = <&gpio1 1 0>;
                };
 
                /* ... */
index 8eb240a..697a3e7 100644 (file)
@@ -9,7 +9,9 @@ Required properties:
 - touchscreen-size-y: vertical resolution of touchscreen (in pixels)
 
 Optional properties:
-- reset-gpio: GPIO connected to the RESET line of the chip
+- reset-gpios: GPIO connected to the RESET line of the chip
+- enable-gpios: GPIO connected to the ENABLE line of the chip
+- wake-gpios: GPIO connected to the WAKE line of the chip
 
 Example:
 
diff --git a/Documentation/devicetree/bindings/input/touchscreen/ts4800-ts.txt b/Documentation/devicetree/bindings/input/touchscreen/ts4800-ts.txt
new file mode 100644 (file)
index 0000000..4c1c092
--- /dev/null
@@ -0,0 +1,11 @@
+* TS-4800 Touchscreen bindings
+
+Required properties:
+- compatible: must be "technologic,ts4800-ts"
+- reg: physical base address of the controller and length of memory mapped
+  region.
+- syscon: phandle / integers array that points to the syscon node which
+          describes the FPGA's syscon registers.
+          - phandle to FPGA's syscon
+          - offset to the touchscreen register
+          - offset to the touchscreen enable bit
index 16a7b68..7ab7bc3 100644 (file)
@@ -922,3 +922,46 @@ int acpi_gpio_count(struct device *dev, const char *con_id)
        }
        return count;
 }
+
+struct acpi_crs_lookup {
+       struct list_head node;
+       struct acpi_device *adev;
+       const char *con_id;
+};
+
+static DEFINE_MUTEX(acpi_crs_lookup_lock);
+static LIST_HEAD(acpi_crs_lookup_list);
+
+bool acpi_can_fallback_to_crs(struct acpi_device *adev, const char *con_id)
+{
+       struct acpi_crs_lookup *l, *lookup = NULL;
+
+       /* Never allow fallback if the device has properties */
+       if (adev->data.properties || adev->driver_gpios)
+               return false;
+
+       mutex_lock(&acpi_crs_lookup_lock);
+
+       list_for_each_entry(l, &acpi_crs_lookup_list, node) {
+               if (l->adev == adev) {
+                       lookup = l;
+                       break;
+               }
+       }
+
+       if (!lookup) {
+               lookup = kmalloc(sizeof(*lookup), GFP_KERNEL);
+               if (lookup) {
+                       lookup->adev = adev;
+                       lookup->con_id = con_id;
+                       list_add_tail(&lookup->node, &acpi_crs_lookup_list);
+               }
+       }
+
+       mutex_unlock(&acpi_crs_lookup_lock);
+
+       return lookup &&
+               ((!lookup->con_id && !con_id) ||
+                (lookup->con_id && con_id &&
+                 strcmp(lookup->con_id, con_id) == 0));
+}
index 4e4c308..a3ad3ba 100644 (file)
@@ -1874,6 +1874,9 @@ static struct gpio_desc *acpi_find_gpio(struct device *dev, const char *con_id,
 
        /* Then from plain _CRS GPIOs */
        if (IS_ERR(desc)) {
+               if (!acpi_can_fallback_to_crs(adev, con_id))
+                       return ERR_PTR(-ENOENT);
+
                desc = acpi_get_gpiod_by_index(adev, NULL, idx, &info);
                if (IS_ERR(desc))
                        return desc;
index 98ab08c..2b87cbd 100644 (file)
@@ -47,6 +47,8 @@ struct gpio_desc *acpi_node_get_gpiod(struct fwnode_handle *fwnode,
                                      struct acpi_gpio_info *info);
 
 int acpi_gpio_count(struct device *dev, const char *con_id);
+
+bool acpi_can_fallback_to_crs(struct acpi_device *adev, const char *con_id);
 #else
 static inline void acpi_gpiochip_add(struct gpio_chip *chip) { }
 static inline void acpi_gpiochip_remove(struct gpio_chip *chip) { }
@@ -73,6 +75,12 @@ static inline int acpi_gpio_count(struct device *dev, const char *con_id)
 {
        return -ENODEV;
 }
+
+static inline bool acpi_can_fallback_to_crs(struct acpi_device *adev,
+                                           const char *con_id)
+{
+       return false;
+}
 #endif
 
 struct gpio_desc *of_get_named_gpiod_flags(struct device_node *np,
index fd4100d..6727954 100644 (file)
  */
 
 #include <linux/kernel.h>
+#include <linux/input.h>
+#include <linux/rcupdate.h>
 #include <linux/slab.h>
 #include <linux/stat.h>
 #include <linux/module.h>
 #include <linux/usb/input.h>
+#include <linux/usb/quirks.h>
 
 #define DRIVER_AUTHOR "Marko Friedemann <mfr@bmx-chemnitz.de>"
 #define DRIVER_DESC "X-Box pad driver"
@@ -125,7 +128,7 @@ static const struct xpad_device {
        { 0x045e, 0x0289, "Microsoft X-Box pad v2 (US)", 0, XTYPE_XBOX },
        { 0x045e, 0x028e, "Microsoft X-Box 360 pad", 0, XTYPE_XBOX360 },
        { 0x045e, 0x02d1, "Microsoft X-Box One pad", 0, XTYPE_XBOXONE },
-       { 0x045e, 0x02dd, "Microsoft X-Box One pad (Covert Forces)", 0, XTYPE_XBOXONE },
+       { 0x045e, 0x02dd, "Microsoft X-Box One pad (Firmware 2015)", 0, XTYPE_XBOXONE },
        { 0x045e, 0x0291, "Xbox 360 Wireless Receiver (XBOX)", MAP_DPAD_TO_BUTTONS, XTYPE_XBOX360W },
        { 0x045e, 0x0719, "Xbox 360 Wireless Receiver", MAP_DPAD_TO_BUTTONS, XTYPE_XBOX360W },
        { 0x044f, 0x0f07, "Thrustmaster, Inc. Controller", 0, XTYPE_XBOX },
@@ -317,21 +320,42 @@ static struct usb_device_id xpad_table[] = {
 
 MODULE_DEVICE_TABLE(usb, xpad_table);
 
+struct xpad_output_packet {
+       u8 data[XPAD_PKT_LEN];
+       u8 len;
+       bool pending;
+};
+
+#define XPAD_OUT_CMD_IDX       0
+#define XPAD_OUT_FF_IDX                1
+#define XPAD_OUT_LED_IDX       (1 + IS_ENABLED(CONFIG_JOYSTICK_XPAD_FF))
+#define XPAD_NUM_OUT_PACKETS   (1 + \
+                                IS_ENABLED(CONFIG_JOYSTICK_XPAD_FF) + \
+                                IS_ENABLED(CONFIG_JOYSTICK_XPAD_LEDS))
+
 struct usb_xpad {
        struct input_dev *dev;          /* input device interface */
+       struct input_dev __rcu *x360w_dev;
        struct usb_device *udev;        /* usb device */
        struct usb_interface *intf;     /* usb interface */
 
-       int pad_present;
+       bool pad_present;
+       bool input_created;
 
        struct urb *irq_in;             /* urb for interrupt in report */
        unsigned char *idata;           /* input data */
        dma_addr_t idata_dma;
 
        struct urb *irq_out;            /* urb for interrupt out report */
+       struct usb_anchor irq_out_anchor;
+       bool irq_out_active;            /* we must not use an active URB */
+       u8 odata_serial;                /* serial number for xbox one protocol */
        unsigned char *odata;           /* output data */
        dma_addr_t odata_dma;
-       struct mutex odata_mutex;
+       spinlock_t odata_lock;
+
+       struct xpad_output_packet out_packets[XPAD_NUM_OUT_PACKETS];
+       int last_out_packet;
 
 #if defined(CONFIG_JOYSTICK_XPAD_LEDS)
        struct xpad_led *led;
@@ -343,8 +367,12 @@ struct usb_xpad {
        int xtype;                      /* type of xbox device */
        int pad_nr;                     /* the order x360 pads were attached */
        const char *name;               /* name of the device */
+       struct work_struct work;        /* init/remove device from callback */
 };
 
+static int xpad_init_input(struct usb_xpad *xpad);
+static void xpad_deinit_input(struct usb_xpad *xpad);
+
 /*
  *     xpad_process_packet
  *
@@ -424,11 +452,9 @@ static void xpad_process_packet(struct usb_xpad *xpad, u16 cmd, unsigned char *d
  *             http://www.free60.org/wiki/Gamepad
  */
 
-static void xpad360_process_packet(struct usb_xpad *xpad,
+static void xpad360_process_packet(struct usb_xpad *xpad, struct input_dev *dev,
                                   u16 cmd, unsigned char *data)
 {
-       struct input_dev *dev = xpad->dev;
-
        /* digital pad */
        if (xpad->mapping & MAP_DPAD_TO_BUTTONS) {
                /* dpad as buttons (left, right, up, down) */
@@ -495,7 +521,30 @@ static void xpad360_process_packet(struct usb_xpad *xpad,
        input_sync(dev);
 }
 
-static void xpad_identify_controller(struct usb_xpad *xpad);
+static void xpad_presence_work(struct work_struct *work)
+{
+       struct usb_xpad *xpad = container_of(work, struct usb_xpad, work);
+       int error;
+
+       if (xpad->pad_present) {
+               error = xpad_init_input(xpad);
+               if (error) {
+                       /* complain only, not much else we can do here */
+                       dev_err(&xpad->dev->dev,
+                               "unable to init device: %d\n", error);
+               } else {
+                       rcu_assign_pointer(xpad->x360w_dev, xpad->dev);
+               }
+       } else {
+               RCU_INIT_POINTER(xpad->x360w_dev, NULL);
+               synchronize_rcu();
+               /*
+                * Now that we are sure xpad360w_process_packet is not
+                * using input device we can get rid of it.
+                */
+               xpad_deinit_input(xpad);
+       }
+}
 
 /*
  * xpad360w_process_packet
@@ -513,24 +562,28 @@ static void xpad_identify_controller(struct usb_xpad *xpad);
  */
 static void xpad360w_process_packet(struct usb_xpad *xpad, u16 cmd, unsigned char *data)
 {
+       struct input_dev *dev;
+       bool present;
+
        /* Presence change */
        if (data[0] & 0x08) {
-               if (data[1] & 0x80) {
-                       xpad->pad_present = 1;
-                       /*
-                        * Light up the segment corresponding to
-                        * controller number.
-                        */
-                       xpad_identify_controller(xpad);
-               } else
-                       xpad->pad_present = 0;
+               present = (data[1] & 0x80) != 0;
+
+               if (xpad->pad_present != present) {
+                       xpad->pad_present = present;
+                       schedule_work(&xpad->work);
+               }
        }
 
        /* Valid pad data */
-       if (!(data[1] & 0x1))
+       if (data[1] != 0x1)
                return;
 
-       xpad360_process_packet(xpad, cmd, &data[4]);
+       rcu_read_lock();
+       dev = rcu_dereference(xpad->x360w_dev);
+       if (dev)
+               xpad360_process_packet(xpad, dev, cmd, &data[4]);
+       rcu_read_unlock();
 }
 
 /*
@@ -659,7 +712,7 @@ static void xpad_irq_in(struct urb *urb)
 
        switch (xpad->xtype) {
        case XTYPE_XBOX360:
-               xpad360_process_packet(xpad, 0, xpad->idata);
+               xpad360_process_packet(xpad, xpad->dev, 0, xpad->idata);
                break;
        case XTYPE_XBOX360W:
                xpad360w_process_packet(xpad, 0, xpad->idata);
@@ -678,18 +731,73 @@ exit:
                        __func__, retval);
 }
 
+/* Callers must hold xpad->odata_lock spinlock */
+static bool xpad_prepare_next_out_packet(struct usb_xpad *xpad)
+{
+       struct xpad_output_packet *pkt, *packet = NULL;
+       int i;
+
+       for (i = 0; i < XPAD_NUM_OUT_PACKETS; i++) {
+               if (++xpad->last_out_packet >= XPAD_NUM_OUT_PACKETS)
+                       xpad->last_out_packet = 0;
+
+               pkt = &xpad->out_packets[xpad->last_out_packet];
+               if (pkt->pending) {
+                       dev_dbg(&xpad->intf->dev,
+                               "%s - found pending output packet %d\n",
+                               __func__, xpad->last_out_packet);
+                       packet = pkt;
+                       break;
+               }
+       }
+
+       if (packet) {
+               memcpy(xpad->odata, packet->data, packet->len);
+               xpad->irq_out->transfer_buffer_length = packet->len;
+               return true;
+       }
+
+       return false;
+}
+
+/* Callers must hold xpad->odata_lock spinlock */
+static int xpad_try_sending_next_out_packet(struct usb_xpad *xpad)
+{
+       int error;
+
+       if (!xpad->irq_out_active && xpad_prepare_next_out_packet(xpad)) {
+               usb_anchor_urb(xpad->irq_out, &xpad->irq_out_anchor);
+               error = usb_submit_urb(xpad->irq_out, GFP_ATOMIC);
+               if (error) {
+                       dev_err(&xpad->intf->dev,
+                               "%s - usb_submit_urb failed with result %d\n",
+                               __func__, error);
+                       usb_unanchor_urb(xpad->irq_out);
+                       return -EIO;
+               }
+
+               xpad->irq_out_active = true;
+       }
+
+       return 0;
+}
+
 static void xpad_irq_out(struct urb *urb)
 {
        struct usb_xpad *xpad = urb->context;
        struct device *dev = &xpad->intf->dev;
-       int retval, status;
+       int status = urb->status;
+       int error;
+       unsigned long flags;
 
-       status = urb->status;
+       spin_lock_irqsave(&xpad->odata_lock, flags);
 
        switch (status) {
        case 0:
                /* success */
-               return;
+               xpad->out_packets[xpad->last_out_packet].pending = false;
+               xpad->irq_out_active = xpad_prepare_next_out_packet(xpad);
+               break;
 
        case -ECONNRESET:
        case -ENOENT:
@@ -697,19 +805,28 @@ static void xpad_irq_out(struct urb *urb)
                /* this urb is terminated, clean up */
                dev_dbg(dev, "%s - urb shutting down with status: %d\n",
                        __func__, status);
-               return;
+               xpad->irq_out_active = false;
+               break;
 
        default:
                dev_dbg(dev, "%s - nonzero urb status received: %d\n",
                        __func__, status);
-               goto exit;
+               break;
        }
 
-exit:
-       retval = usb_submit_urb(urb, GFP_ATOMIC);
-       if (retval)
-               dev_err(dev, "%s - usb_submit_urb failed with result %d\n",
-                       __func__, retval);
+       if (xpad->irq_out_active) {
+               usb_anchor_urb(urb, &xpad->irq_out_anchor);
+               error = usb_submit_urb(urb, GFP_ATOMIC);
+               if (error) {
+                       dev_err(dev,
+                               "%s - usb_submit_urb failed with result %d\n",
+                               __func__, error);
+                       usb_unanchor_urb(urb);
+                       xpad->irq_out_active = false;
+               }
+       }
+
+       spin_unlock_irqrestore(&xpad->odata_lock, flags);
 }
 
 static int xpad_init_output(struct usb_interface *intf, struct usb_xpad *xpad)
@@ -721,6 +838,8 @@ static int xpad_init_output(struct usb_interface *intf, struct usb_xpad *xpad)
        if (xpad->xtype == XTYPE_UNKNOWN)
                return 0;
 
+       init_usb_anchor(&xpad->irq_out_anchor);
+
        xpad->odata = usb_alloc_coherent(xpad->udev, XPAD_PKT_LEN,
                                         GFP_KERNEL, &xpad->odata_dma);
        if (!xpad->odata) {
@@ -728,7 +847,7 @@ static int xpad_init_output(struct usb_interface *intf, struct usb_xpad *xpad)
                goto fail1;
        }
 
-       mutex_init(&xpad->odata_mutex);
+       spin_lock_init(&xpad->odata_lock);
 
        xpad->irq_out = usb_alloc_urb(0, GFP_KERNEL);
        if (!xpad->irq_out) {
@@ -755,8 +874,14 @@ static int xpad_init_output(struct usb_interface *intf, struct usb_xpad *xpad)
 
 static void xpad_stop_output(struct usb_xpad *xpad)
 {
-       if (xpad->xtype != XTYPE_UNKNOWN)
-               usb_kill_urb(xpad->irq_out);
+       if (xpad->xtype != XTYPE_UNKNOWN) {
+               if (!usb_wait_anchor_empty_timeout(&xpad->irq_out_anchor,
+                                                  5000)) {
+                       dev_warn(&xpad->intf->dev,
+                                "timed out waiting for output URB to complete, killing\n");
+                       usb_kill_anchored_urbs(&xpad->irq_out_anchor);
+               }
+       }
 }
 
 static void xpad_deinit_output(struct usb_xpad *xpad)
@@ -770,27 +895,60 @@ static void xpad_deinit_output(struct usb_xpad *xpad)
 
 static int xpad_inquiry_pad_presence(struct usb_xpad *xpad)
 {
+       struct xpad_output_packet *packet =
+                       &xpad->out_packets[XPAD_OUT_CMD_IDX];
+       unsigned long flags;
        int retval;
 
-       mutex_lock(&xpad->odata_mutex);
+       spin_lock_irqsave(&xpad->odata_lock, flags);
+
+       packet->data[0] = 0x08;
+       packet->data[1] = 0x00;
+       packet->data[2] = 0x0F;
+       packet->data[3] = 0xC0;
+       packet->data[4] = 0x00;
+       packet->data[5] = 0x00;
+       packet->data[6] = 0x00;
+       packet->data[7] = 0x00;
+       packet->data[8] = 0x00;
+       packet->data[9] = 0x00;
+       packet->data[10] = 0x00;
+       packet->data[11] = 0x00;
+       packet->len = 12;
+       packet->pending = true;
+
+       /* Reset the sequence so we send out presence first */
+       xpad->last_out_packet = -1;
+       retval = xpad_try_sending_next_out_packet(xpad);
+
+       spin_unlock_irqrestore(&xpad->odata_lock, flags);
 
-       xpad->odata[0] = 0x08;
-       xpad->odata[1] = 0x00;
-       xpad->odata[2] = 0x0F;
-       xpad->odata[3] = 0xC0;
-       xpad->odata[4] = 0x00;
-       xpad->odata[5] = 0x00;
-       xpad->odata[6] = 0x00;
-       xpad->odata[7] = 0x00;
-       xpad->odata[8] = 0x00;
-       xpad->odata[9] = 0x00;
-       xpad->odata[10] = 0x00;
-       xpad->odata[11] = 0x00;
-       xpad->irq_out->transfer_buffer_length = 12;
+       return retval;
+}
+
+static int xpad_start_xbox_one(struct usb_xpad *xpad)
+{
+       struct xpad_output_packet *packet =
+                       &xpad->out_packets[XPAD_OUT_CMD_IDX];
+       unsigned long flags;
+       int retval;
 
-       retval = usb_submit_urb(xpad->irq_out, GFP_KERNEL);
+       spin_lock_irqsave(&xpad->odata_lock, flags);
 
-       mutex_unlock(&xpad->odata_mutex);
+       /* Xbox one controller needs to be initialized. */
+       packet->data[0] = 0x05;
+       packet->data[1] = 0x20;
+       packet->data[2] = xpad->odata_serial++; /* packet serial */
+       packet->data[3] = 0x01; /* rumble bit enable?  */
+       packet->data[4] = 0x00;
+       packet->len = 5;
+       packet->pending = true;
+
+       /* Reset the sequence so we send out start packet first */
+       xpad->last_out_packet = -1;
+       retval = xpad_try_sending_next_out_packet(xpad);
+
+       spin_unlock_irqrestore(&xpad->odata_lock, flags);
 
        return retval;
 }
@@ -799,8 +957,11 @@ static int xpad_inquiry_pad_presence(struct usb_xpad *xpad)
 static int xpad_play_effect(struct input_dev *dev, void *data, struct ff_effect *effect)
 {
        struct usb_xpad *xpad = input_get_drvdata(dev);
+       struct xpad_output_packet *packet = &xpad->out_packets[XPAD_OUT_FF_IDX];
        __u16 strong;
        __u16 weak;
+       int retval;
+       unsigned long flags;
 
        if (effect->type != FF_RUMBLE)
                return 0;
@@ -808,69 +969,81 @@ static int xpad_play_effect(struct input_dev *dev, void *data, struct ff_effect
        strong = effect->u.rumble.strong_magnitude;
        weak = effect->u.rumble.weak_magnitude;
 
+       spin_lock_irqsave(&xpad->odata_lock, flags);
+
        switch (xpad->xtype) {
        case XTYPE_XBOX:
-               xpad->odata[0] = 0x00;
-               xpad->odata[1] = 0x06;
-               xpad->odata[2] = 0x00;
-               xpad->odata[3] = strong / 256;  /* left actuator */
-               xpad->odata[4] = 0x00;
-               xpad->odata[5] = weak / 256;    /* right actuator */
-               xpad->irq_out->transfer_buffer_length = 6;
+               packet->data[0] = 0x00;
+               packet->data[1] = 0x06;
+               packet->data[2] = 0x00;
+               packet->data[3] = strong / 256; /* left actuator */
+               packet->data[4] = 0x00;
+               packet->data[5] = weak / 256;   /* right actuator */
+               packet->len = 6;
+               packet->pending = true;
                break;
 
        case XTYPE_XBOX360:
-               xpad->odata[0] = 0x00;
-               xpad->odata[1] = 0x08;
-               xpad->odata[2] = 0x00;
-               xpad->odata[3] = strong / 256;  /* left actuator? */
-               xpad->odata[4] = weak / 256;    /* right actuator? */
-               xpad->odata[5] = 0x00;
-               xpad->odata[6] = 0x00;
-               xpad->odata[7] = 0x00;
-               xpad->irq_out->transfer_buffer_length = 8;
+               packet->data[0] = 0x00;
+               packet->data[1] = 0x08;
+               packet->data[2] = 0x00;
+               packet->data[3] = strong / 256;  /* left actuator? */
+               packet->data[4] = weak / 256;   /* right actuator? */
+               packet->data[5] = 0x00;
+               packet->data[6] = 0x00;
+               packet->data[7] = 0x00;
+               packet->len = 8;
+               packet->pending = true;
                break;
 
        case XTYPE_XBOX360W:
-               xpad->odata[0] = 0x00;
-               xpad->odata[1] = 0x01;
-               xpad->odata[2] = 0x0F;
-               xpad->odata[3] = 0xC0;
-               xpad->odata[4] = 0x00;
-               xpad->odata[5] = strong / 256;
-               xpad->odata[6] = weak / 256;
-               xpad->odata[7] = 0x00;
-               xpad->odata[8] = 0x00;
-               xpad->odata[9] = 0x00;
-               xpad->odata[10] = 0x00;
-               xpad->odata[11] = 0x00;
-               xpad->irq_out->transfer_buffer_length = 12;
+               packet->data[0] = 0x00;
+               packet->data[1] = 0x01;
+               packet->data[2] = 0x0F;
+               packet->data[3] = 0xC0;
+               packet->data[4] = 0x00;
+               packet->data[5] = strong / 256;
+               packet->data[6] = weak / 256;
+               packet->data[7] = 0x00;
+               packet->data[8] = 0x00;
+               packet->data[9] = 0x00;
+               packet->data[10] = 0x00;
+               packet->data[11] = 0x00;
+               packet->len = 12;
+               packet->pending = true;
                break;
 
        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;
+               packet->data[0] = 0x09; /* activate rumble */
+               packet->data[1] = 0x08;
+               packet->data[2] = xpad->odata_serial++;
+               packet->data[3] = 0x08; /* continuous effect */
+               packet->data[4] = 0x00; /* simple rumble mode */
+               packet->data[5] = 0x03; /* L and R actuator only */
+               packet->data[6] = 0x00; /* TODO: LT actuator */
+               packet->data[7] = 0x00; /* TODO: RT actuator */
+               packet->data[8] = strong / 512; /* left actuator */
+               packet->data[9] = weak / 512;   /* right actuator */
+               packet->data[10] = 0x80;        /* length of pulse */
+               packet->data[11] = 0x00;        /* stop period of pulse */
+               packet->data[12] = 0x00;
+               packet->len = 13;
+               packet->pending = true;
                break;
 
        default:
                dev_dbg(&xpad->dev->dev,
                        "%s - rumble command sent to unsupported xpad type: %d\n",
                        __func__, xpad->xtype);
-               return -EINVAL;
+               retval = -EINVAL;
+               goto out;
        }
 
-       return usb_submit_urb(xpad->irq_out, GFP_ATOMIC);
+       retval = xpad_try_sending_next_out_packet(xpad);
+
+out:
+       spin_unlock_irqrestore(&xpad->odata_lock, flags);
+       return retval;
 }
 
 static int xpad_init_ff(struct usb_xpad *xpad)
@@ -921,36 +1094,44 @@ struct xpad_led {
  */
 static void xpad_send_led_command(struct usb_xpad *xpad, int command)
 {
+       struct xpad_output_packet *packet =
+                       &xpad->out_packets[XPAD_OUT_LED_IDX];
+       unsigned long flags;
+
        command %= 16;
 
-       mutex_lock(&xpad->odata_mutex);
+       spin_lock_irqsave(&xpad->odata_lock, flags);
 
        switch (xpad->xtype) {
        case XTYPE_XBOX360:
-               xpad->odata[0] = 0x01;
-               xpad->odata[1] = 0x03;
-               xpad->odata[2] = command;
-               xpad->irq_out->transfer_buffer_length = 3;
+               packet->data[0] = 0x01;
+               packet->data[1] = 0x03;
+               packet->data[2] = command;
+               packet->len = 3;
+               packet->pending = true;
                break;
+
        case XTYPE_XBOX360W:
-               xpad->odata[0] = 0x00;
-               xpad->odata[1] = 0x00;
-               xpad->odata[2] = 0x08;
-               xpad->odata[3] = 0x40 + command;
-               xpad->odata[4] = 0x00;
-               xpad->odata[5] = 0x00;
-               xpad->odata[6] = 0x00;
-               xpad->odata[7] = 0x00;
-               xpad->odata[8] = 0x00;
-               xpad->odata[9] = 0x00;
-               xpad->odata[10] = 0x00;
-               xpad->odata[11] = 0x00;
-               xpad->irq_out->transfer_buffer_length = 12;
+               packet->data[0] = 0x00;
+               packet->data[1] = 0x00;
+               packet->data[2] = 0x08;
+               packet->data[3] = 0x40 + command;
+               packet->data[4] = 0x00;
+               packet->data[5] = 0x00;
+               packet->data[6] = 0x00;
+               packet->data[7] = 0x00;
+               packet->data[8] = 0x00;
+               packet->data[9] = 0x00;
+               packet->data[10] = 0x00;
+               packet->data[11] = 0x00;
+               packet->len = 12;
+               packet->pending = true;
                break;
        }
 
-       usb_submit_urb(xpad->irq_out, GFP_KERNEL);
-       mutex_unlock(&xpad->odata_mutex);
+       xpad_try_sending_next_out_packet(xpad);
+
+       spin_unlock_irqrestore(&xpad->odata_lock, flags);
 }
 
 /*
@@ -959,7 +1140,7 @@ static void xpad_send_led_command(struct usb_xpad *xpad, int command)
  */
 static void xpad_identify_controller(struct usb_xpad *xpad)
 {
-       xpad_send_led_command(xpad, (xpad->pad_nr % 4) + 2);
+       led_set_brightness(&xpad->led->led_cdev, (xpad->pad_nr % 4) + 2);
 }
 
 static void xpad_led_set(struct led_classdev *led_cdev,
@@ -1001,14 +1182,7 @@ static int xpad_led_probe(struct usb_xpad *xpad)
        if (error)
                goto err_free_id;
 
-       if (xpad->xtype == XTYPE_XBOX360) {
-               /*
-                * Light up the segment corresponding to controller
-                * number on wired devices. On wireless we'll do that
-                * when they respond to "presence" packet.
-                */
-               xpad_identify_controller(xpad);
-       }
+       xpad_identify_controller(xpad);
 
        return 0;
 
@@ -1036,37 +1210,73 @@ static void xpad_led_disconnect(struct usb_xpad *xpad) { }
 static void xpad_identify_controller(struct usb_xpad *xpad) { }
 #endif
 
-static int xpad_open(struct input_dev *dev)
+static int xpad_start_input(struct usb_xpad *xpad)
 {
-       struct usb_xpad *xpad = input_get_drvdata(dev);
-
-       /* URB was submitted in probe */
-       if (xpad->xtype == XTYPE_XBOX360W)
-               return 0;
+       int error;
 
-       xpad->irq_in->dev = xpad->udev;
        if (usb_submit_urb(xpad->irq_in, GFP_KERNEL))
                return -EIO;
 
        if (xpad->xtype == XTYPE_XBOXONE) {
-               /* Xbox one controller needs to be initialized. */
-               xpad->odata[0] = 0x05;
-               xpad->odata[1] = 0x20;
-               xpad->irq_out->transfer_buffer_length = 2;
-               return usb_submit_urb(xpad->irq_out, GFP_KERNEL);
+               error = xpad_start_xbox_one(xpad);
+               if (error) {
+                       usb_kill_urb(xpad->irq_in);
+                       return error;
+               }
        }
 
        return 0;
 }
 
-static void xpad_close(struct input_dev *dev)
+static void xpad_stop_input(struct usb_xpad *xpad)
 {
-       struct usb_xpad *xpad = input_get_drvdata(dev);
+       usb_kill_urb(xpad->irq_in);
+}
+
+static int xpad360w_start_input(struct usb_xpad *xpad)
+{
+       int error;
 
-       if (xpad->xtype != XTYPE_XBOX360W)
+       error = usb_submit_urb(xpad->irq_in, GFP_KERNEL);
+       if (error)
+               return -EIO;
+
+       /*
+        * Send presence packet.
+        * This will force the controller to resend connection packets.
+        * This is useful in the case we activate the module after the
+        * adapter has been plugged in, as it won't automatically
+        * send us info about the controllers.
+        */
+       error = xpad_inquiry_pad_presence(xpad);
+       if (error) {
                usb_kill_urb(xpad->irq_in);
+               return error;
+       }
 
-       xpad_stop_output(xpad);
+       return 0;
+}
+
+static void xpad360w_stop_input(struct usb_xpad *xpad)
+{
+       usb_kill_urb(xpad->irq_in);
+
+       /* Make sure we are done with presence work if it was scheduled */
+       flush_work(&xpad->work);
+}
+
+static int xpad_open(struct input_dev *dev)
+{
+       struct usb_xpad *xpad = input_get_drvdata(dev);
+
+       return xpad_start_input(xpad);
+}
+
+static void xpad_close(struct input_dev *dev)
+{
+       struct usb_xpad *xpad = input_get_drvdata(dev);
+
+       xpad_stop_input(xpad);
 }
 
 static void xpad_set_up_abs(struct input_dev *input_dev, signed short abs)
@@ -1097,8 +1307,11 @@ static void xpad_set_up_abs(struct input_dev *input_dev, signed short abs)
 
 static void xpad_deinit_input(struct usb_xpad *xpad)
 {
-       xpad_led_disconnect(xpad);
-       input_unregister_device(xpad->dev);
+       if (xpad->input_created) {
+               xpad->input_created = false;
+               xpad_led_disconnect(xpad);
+               input_unregister_device(xpad->dev);
+       }
 }
 
 static int xpad_init_input(struct usb_xpad *xpad)
@@ -1118,8 +1331,10 @@ static int xpad_init_input(struct usb_xpad *xpad)
 
        input_set_drvdata(input_dev, xpad);
 
-       input_dev->open = xpad_open;
-       input_dev->close = xpad_close;
+       if (xpad->xtype != XTYPE_XBOX360W) {
+               input_dev->open = xpad_open;
+               input_dev->close = xpad_close;
+       }
 
        __set_bit(EV_KEY, input_dev->evbit);
 
@@ -1181,6 +1396,7 @@ static int xpad_init_input(struct usb_xpad *xpad)
        if (error)
                goto err_disconnect_led;
 
+       xpad->input_created = true;
        return 0;
 
 err_disconnect_led:
@@ -1241,6 +1457,7 @@ static int xpad_probe(struct usb_interface *intf, const struct usb_device_id *id
        xpad->mapping = xpad_device[i].mapping;
        xpad->xtype = xpad_device[i].xtype;
        xpad->name = xpad_device[i].name;
+       INIT_WORK(&xpad->work, xpad_presence_work);
 
        if (xpad->xtype == XTYPE_UNKNOWN) {
                if (intf->cur_altsetting->desc.bInterfaceClass == USB_CLASS_VENDOR_SPEC) {
@@ -1277,10 +1494,6 @@ static int xpad_probe(struct usb_interface *intf, const struct usb_device_id *id
 
        usb_set_intfdata(intf, xpad);
 
-       error = xpad_init_input(xpad);
-       if (error)
-               goto err_deinit_output;
-
        if (xpad->xtype == XTYPE_XBOX360W) {
                /*
                 * Submit the int URB immediately rather than waiting for open
@@ -1289,28 +1502,24 @@ static int xpad_probe(struct usb_interface *intf, const struct usb_device_id *id
                 * exactly the message that a controller has arrived that
                 * we're waiting for.
                 */
-               xpad->irq_in->dev = xpad->udev;
-               error = usb_submit_urb(xpad->irq_in, GFP_KERNEL);
+               error = xpad360w_start_input(xpad);
                if (error)
-                       goto err_deinit_input;
-
+                       goto err_deinit_output;
                /*
-                * Send presence packet.
-                * This will force the controller to resend connection packets.
-                * This is useful in the case we activate the module after the
-                * adapter has been plugged in, as it won't automatically
-                * send us info about the controllers.
+                * Wireless controllers require RESET_RESUME to work properly
+                * after suspend. Ideally this quirk should be in usb core
+                * quirk list, but we have too many vendors producing these
+                * controllers and we'd need to maintain 2 identical lists
+                * here in this driver and in usb core.
                 */
-               error = xpad_inquiry_pad_presence(xpad);
+               udev->quirks |= USB_QUIRK_RESET_RESUME;
+       } else {
+               error = xpad_init_input(xpad);
                if (error)
-                       goto err_kill_in_urb;
+                       goto err_deinit_output;
        }
        return 0;
 
-err_kill_in_urb:
-       usb_kill_urb(xpad->irq_in);
-err_deinit_input:
-       xpad_deinit_input(xpad);
 err_deinit_output:
        xpad_deinit_output(xpad);
 err_free_in_urb:
@@ -1320,19 +1529,24 @@ err_free_idata:
 err_free_mem:
        kfree(xpad);
        return error;
-
 }
 
 static void xpad_disconnect(struct usb_interface *intf)
 {
-       struct usb_xpad *xpad = usb_get_intfdata (intf);
+       struct usb_xpad *xpad = usb_get_intfdata(intf);
+
+       if (xpad->xtype == XTYPE_XBOX360W)
+               xpad360w_stop_input(xpad);
 
        xpad_deinit_input(xpad);
-       xpad_deinit_output(xpad);
 
-       if (xpad->xtype == XTYPE_XBOX360W) {
-               usb_kill_urb(xpad->irq_in);
-       }
+       /*
+        * Now that both input device and LED device are gone we can
+        * stop output URB.
+        */
+       xpad_stop_output(xpad);
+
+       xpad_deinit_output(xpad);
 
        usb_free_urb(xpad->irq_in);
        usb_free_coherent(xpad->udev, XPAD_PKT_LEN,
@@ -1343,10 +1557,55 @@ static void xpad_disconnect(struct usb_interface *intf)
        usb_set_intfdata(intf, NULL);
 }
 
+static int xpad_suspend(struct usb_interface *intf, pm_message_t message)
+{
+       struct usb_xpad *xpad = usb_get_intfdata(intf);
+       struct input_dev *input = xpad->dev;
+
+       if (xpad->xtype == XTYPE_XBOX360W) {
+               /*
+                * Wireless controllers always listen to input so
+                * they are notified when controller shows up
+                * or goes away.
+                */
+               xpad360w_stop_input(xpad);
+       } else {
+               mutex_lock(&input->mutex);
+               if (input->users)
+                       xpad_stop_input(xpad);
+               mutex_unlock(&input->mutex);
+       }
+
+       xpad_stop_output(xpad);
+
+       return 0;
+}
+
+static int xpad_resume(struct usb_interface *intf)
+{
+       struct usb_xpad *xpad = usb_get_intfdata(intf);
+       struct input_dev *input = xpad->dev;
+       int retval = 0;
+
+       if (xpad->xtype == XTYPE_XBOX360W) {
+               retval = xpad360w_start_input(xpad);
+       } else {
+               mutex_lock(&input->mutex);
+               if (input->users)
+                       retval = xpad_start_input(xpad);
+               mutex_unlock(&input->mutex);
+       }
+
+       return retval;
+}
+
 static struct usb_driver xpad_driver = {
        .name           = "xpad",
        .probe          = xpad_probe,
        .disconnect     = xpad_disconnect,
+       .suspend        = xpad_suspend,
+       .resume         = xpad_resume,
+       .reset_resume   = xpad_resume,
        .id_table       = xpad_table,
 };
 
index ddd8148..509608c 100644 (file)
@@ -560,7 +560,7 @@ config KEYBOARD_SUNKBD
 
 config KEYBOARD_SH_KEYSC
        tristate "SuperH KEYSC keypad support"
-       depends on SUPERH || ARCH_SHMOBILE || COMPILE_TEST
+       depends on ARCH_SHMOBILE || COMPILE_TEST
        help
          Say Y here if you want to use a keypad attached to the KEYSC block
          on SuperH processors such as sh7722 and sh7343.
index 907e4e2..f6e643b 100644 (file)
@@ -22,6 +22,7 @@
 #include <linux/slab.h>
 #include <linux/irq.h>
 #include <linux/io.h>
+#include <linux/acpi.h>
 
 enum {
        REG_READ        = 0x00,
@@ -178,10 +179,26 @@ static int events_probe(struct platform_device *pdev)
        return 0;
 }
 
+static const struct of_device_id goldfish_events_of_match[] = {
+       { .compatible = "google,goldfish-events-keypad", },
+       {},
+};
+MODULE_DEVICE_TABLE(of, goldfish_events_of_match);
+
+#ifdef CONFIG_ACPI
+static const struct acpi_device_id goldfish_events_acpi_match[] = {
+       { "GFSH0002", 0 },
+       { },
+};
+MODULE_DEVICE_TABLE(acpi, goldfish_events_acpi_match);
+#endif
+
 static struct platform_driver events_driver = {
        .probe  = events_probe,
        .driver = {
                .name   = "goldfish_events",
+               .of_match_table = goldfish_events_of_match,
+               .acpi_match_table = ACPI_PTR(goldfish_events_acpi_match),
        },
 };
 
index bef317f..2909365 100644 (file)
@@ -96,13 +96,29 @@ struct gpio_keys_drvdata {
  * Return value of this function can be used to allocate bitmap
  * large enough to hold all bits for given type.
  */
-static inline int get_n_events_by_type(int type)
+static int get_n_events_by_type(int type)
 {
        BUG_ON(type != EV_SW && type != EV_KEY);
 
        return (type == EV_KEY) ? KEY_CNT : SW_CNT;
 }
 
+/**
+ * get_bm_events_by_type() - returns bitmap of supported events per @type
+ * @input: input device from which bitmap is retrieved
+ * @type: type of button (%EV_KEY, %EV_SW)
+ *
+ * Return value of this function can be used to allocate bitmap
+ * large enough to hold all bits for given type.
+ */
+static const unsigned long *get_bm_events_by_type(struct input_dev *dev,
+                                                 int type)
+{
+       BUG_ON(type != EV_SW && type != EV_KEY);
+
+       return (type == EV_KEY) ? dev->keybit : dev->swbit;
+}
+
 /**
  * gpio_keys_disable_button() - disables given GPIO button
  * @bdata: button data for button to be disabled
@@ -213,6 +229,7 @@ static ssize_t gpio_keys_attr_store_helper(struct gpio_keys_drvdata *ddata,
                                           const char *buf, unsigned int type)
 {
        int n_events = get_n_events_by_type(type);
+       const unsigned long *bitmap = get_bm_events_by_type(ddata->input, type);
        unsigned long *bits;
        ssize_t error;
        int i;
@@ -226,6 +243,11 @@ static ssize_t gpio_keys_attr_store_helper(struct gpio_keys_drvdata *ddata,
                goto out;
 
        /* First validate */
+       if (!bitmap_subset(bits, bitmap, n_events)) {
+               error = -EINVAL;
+               goto out;
+       }
+
        for (i = 0; i < ddata->pdata->nbuttons; i++) {
                struct gpio_button_data *bdata = &ddata->data[i];
 
@@ -239,11 +261,6 @@ static ssize_t gpio_keys_attr_store_helper(struct gpio_keys_drvdata *ddata,
                }
        }
 
-       if (i == ddata->pdata->nbuttons) {
-               error = -EINVAL;
-               goto out;
-       }
-
        mutex_lock(&ddata->disable_lock);
 
        for (i = 0; i < ddata->pdata->nbuttons; i++) {
@@ -613,7 +630,7 @@ gpio_keys_get_devtree_pdata(struct device *dev)
        if (!node)
                return ERR_PTR(-ENODEV);
 
-       nbuttons = of_get_child_count(node);
+       nbuttons = of_get_available_child_count(node);
        if (nbuttons == 0)
                return ERR_PTR(-ENODEV);
 
@@ -628,8 +645,10 @@ gpio_keys_get_devtree_pdata(struct device *dev)
 
        pdata->rep = !!of_get_property(node, "autorepeat", NULL);
 
+       of_property_read_string(node, "label", &pdata->name);
+
        i = 0;
-       for_each_child_of_node(node, pp) {
+       for_each_available_child_of_node(node, pp) {
                enum of_gpio_flags flags;
 
                button = &pdata->buttons[i++];
index 7502e46..e0d72c8 100644 (file)
@@ -155,14 +155,6 @@ static void omap_kp_tasklet(unsigned long data)
                               "pressed" : "released");
 #else
                        key = keycodes[MATRIX_SCAN_CODE(row, col, row_shift)];
-                       if (key < 0) {
-                               printk(KERN_WARNING
-                                     "omap-keypad: Spurious key event %d-%d\n",
-                                      col, row);
-                               /* We scan again after a couple of seconds */
-                               spurious = 1;
-                               continue;
-                       }
 
                        if (!(kp_cur_group == (key & GROUP_MASK) ||
                              kp_cur_group == -1))
@@ -292,8 +284,8 @@ static int omap_kp_probe(struct platform_device *pdev)
        setup_timer(&omap_kp->timer, omap_kp_timer, (unsigned long)omap_kp);
 
        /* get the irq and init timer*/
-       tasklet_enable(&kp_tasklet);
        kp_tasklet.data = (unsigned long) omap_kp;
+       tasklet_enable(&kp_tasklet);
 
        ret = device_create_file(&pdev->dev, &dev_attr_enable);
        if (ret < 0)
index 9adf13a..b0ffade 100644 (file)
@@ -180,7 +180,7 @@ static int imx_snvs_pwrkey_probe(struct platform_device *pdev)
        return 0;
 }
 
-static int imx_snvs_pwrkey_suspend(struct device *dev)
+static int __maybe_unused imx_snvs_pwrkey_suspend(struct device *dev)
 {
        struct platform_device *pdev = to_platform_device(dev);
        struct pwrkey_drv_data *pdata = platform_get_drvdata(pdev);
@@ -191,7 +191,7 @@ static int imx_snvs_pwrkey_suspend(struct device *dev)
        return 0;
 }
 
-static int imx_snvs_pwrkey_resume(struct device *dev)
+static int __maybe_unused imx_snvs_pwrkey_resume(struct device *dev)
 {
        struct platform_device *pdev = to_platform_device(dev);
        struct pwrkey_drv_data *pdata = platform_get_drvdata(pdev);
index 623d451..8083eaa 100644 (file)
@@ -288,8 +288,7 @@ static int spear_kbd_remove(struct platform_device *pdev)
        return 0;
 }
 
-#ifdef CONFIG_PM
-static int spear_kbd_suspend(struct device *dev)
+static int __maybe_unused spear_kbd_suspend(struct device *dev)
 {
        struct platform_device *pdev = to_platform_device(dev);
        struct spear_kbd *kbd = platform_get_drvdata(pdev);
@@ -342,7 +341,7 @@ static int spear_kbd_suspend(struct device *dev)
        return 0;
 }
 
-static int spear_kbd_resume(struct device *dev)
+static int __maybe_unused spear_kbd_resume(struct device *dev)
 {
        struct platform_device *pdev = to_platform_device(dev);
        struct spear_kbd *kbd = platform_get_drvdata(pdev);
@@ -368,7 +367,6 @@ static int spear_kbd_resume(struct device *dev)
 
        return 0;
 }
-#endif
 
 static SIMPLE_DEV_PM_OPS(spear_kbd_pm_ops, spear_kbd_suspend, spear_kbd_resume);
 
index 1d0e61d..b0d4453 100644 (file)
@@ -147,7 +147,7 @@ struct bma150_data {
  * are stated and verified by Bosch Sensortec where they are configured
  * to provide a generic sensitivity performance.
  */
-static struct bma150_cfg default_cfg = {
+static const struct bma150_cfg default_cfg = {
        .any_motion_int = 1,
        .hg_int = 1,
        .lg_int = 1,
index 8eb697d..bb863e0 100644 (file)
@@ -179,13 +179,13 @@ static irqreturn_t da9063_onkey_irq_handler(int irq, void *data)
                input_report_key(onkey->input, KEY_POWER, 1);
                input_sync(onkey->input);
                schedule_delayed_work(&onkey->work, 0);
-               dev_dbg(onkey->dev, "KEY_POWER pressed.\n");
+               dev_dbg(onkey->dev, "KEY_POWER long press.\n");
        } else {
-               input_report_key(onkey->input, KEY_SLEEP, 1);
+               input_report_key(onkey->input, KEY_POWER, 1);
                input_sync(onkey->input);
-               input_report_key(onkey->input, KEY_SLEEP, 0);
+               input_report_key(onkey->input, KEY_POWER, 0);
                input_sync(onkey->input);
-               dev_dbg(onkey->dev, "KEY_SLEEP pressed.\n");
+               dev_dbg(onkey->dev, "KEY_POWER short press.\n");
        }
 
        return IRQ_HANDLED;
index 6f997aa..4a5afc7 100644 (file)
@@ -345,23 +345,19 @@ static struct platform_driver grover_beep_driver = {
        .shutdown       = sparcspkr_shutdown,
 };
 
+static struct platform_driver * const drivers[] = {
+       &bbc_beep_driver,
+       &grover_beep_driver,
+};
+
 static int __init sparcspkr_init(void)
 {
-       int err = platform_driver_register(&bbc_beep_driver);
-
-       if (!err) {
-               err = platform_driver_register(&grover_beep_driver);
-               if (err)
-                       platform_driver_unregister(&bbc_beep_driver);
-       }
-
-       return err;
+       return platform_register_drivers(drivers, ARRAY_SIZE(drivers));
 }
 
 static void __exit sparcspkr_exit(void)
 {
-       platform_driver_unregister(&bbc_beep_driver);
-       platform_driver_unregister(&grover_beep_driver);
+       platform_unregister_drivers(drivers, ARRAY_SIZE(drivers));
 }
 
 module_init(sparcspkr_init);
index 5adbced..4eb9e4d 100644 (file)
@@ -256,13 +256,29 @@ static void uinput_destroy_device(struct uinput_device *udev)
 static int uinput_create_device(struct uinput_device *udev)
 {
        struct input_dev *dev = udev->dev;
-       int error;
+       int error, nslot;
 
        if (udev->state != UIST_SETUP_COMPLETE) {
                printk(KERN_DEBUG "%s: write device info first\n", UINPUT_NAME);
                return -EINVAL;
        }
 
+       if (test_bit(ABS_MT_SLOT, dev->absbit)) {
+               nslot = input_abs_get_max(dev, ABS_MT_SLOT) + 1;
+               error = input_mt_init_slots(dev, nslot, 0);
+               if (error)
+                       goto fail1;
+       } else if (test_bit(ABS_MT_POSITION_X, dev->absbit)) {
+               input_set_events_per_packet(dev, 60);
+       }
+
+       if (test_bit(EV_FF, dev->evbit) && !udev->ff_effects_max) {
+               printk(KERN_DEBUG "%s: ff_effects_max should be non-zero when FF_BIT is set\n",
+                       UINPUT_NAME);
+               error = -EINVAL;
+               goto fail1;
+       }
+
        if (udev->ff_effects_max) {
                error = input_ff_create(dev, udev->ff_effects_max);
                if (error)
@@ -308,10 +324,35 @@ static int uinput_open(struct inode *inode, struct file *file)
        return 0;
 }
 
+static int uinput_validate_absinfo(struct input_dev *dev, unsigned int code,
+                                  const struct input_absinfo *abs)
+{
+       int min, max;
+
+       min = abs->minimum;
+       max = abs->maximum;
+
+       if ((min != 0 || max != 0) && max <= min) {
+               printk(KERN_DEBUG
+                      "%s: invalid abs[%02x] min:%d max:%d\n",
+                      UINPUT_NAME, code, min, max);
+               return -EINVAL;
+       }
+
+       if (abs->flat > max - min) {
+               printk(KERN_DEBUG
+                      "%s: abs_flat #%02x out of range: %d (min:%d/max:%d)\n",
+                      UINPUT_NAME, code, abs->flat, min, max);
+               return -EINVAL;
+       }
+
+       return 0;
+}
+
 static int uinput_validate_absbits(struct input_dev *dev)
 {
        unsigned int cnt;
-       int nslot;
+       int error;
 
        if (!test_bit(EV_ABS, dev->evbit))
                return 0;
@@ -321,38 +362,12 @@ static int uinput_validate_absbits(struct input_dev *dev)
         */
 
        for_each_set_bit(cnt, dev->absbit, ABS_CNT) {
-               int min, max;
-
-               min = input_abs_get_min(dev, cnt);
-               max = input_abs_get_max(dev, cnt);
-
-               if ((min != 0 || max != 0) && max <= min) {
-                       printk(KERN_DEBUG
-                               "%s: invalid abs[%02x] min:%d max:%d\n",
-                               UINPUT_NAME, cnt,
-                               input_abs_get_min(dev, cnt),
-                               input_abs_get_max(dev, cnt));
+               if (!dev->absinfo)
                        return -EINVAL;
-               }
 
-               if (input_abs_get_flat(dev, cnt) >
-                   input_abs_get_max(dev, cnt) - input_abs_get_min(dev, cnt)) {
-                       printk(KERN_DEBUG
-                               "%s: abs_flat #%02x out of range: %d "
-                               "(min:%d/max:%d)\n",
-                               UINPUT_NAME, cnt,
-                               input_abs_get_flat(dev, cnt),
-                               input_abs_get_min(dev, cnt),
-                               input_abs_get_max(dev, cnt));
-                       return -EINVAL;
-               }
-       }
-
-       if (test_bit(ABS_MT_SLOT, dev->absbit)) {
-               nslot = input_abs_get_max(dev, ABS_MT_SLOT) + 1;
-               input_mt_init_slots(dev, nslot, 0);
-       } else if (test_bit(ABS_MT_POSITION_X, dev->absbit)) {
-               input_set_events_per_packet(dev, 60);
+               error = uinput_validate_absinfo(dev, cnt, &dev->absinfo[cnt]);
+               if (error)
+                       return error;
        }
 
        return 0;
@@ -370,8 +385,71 @@ static int uinput_allocate_device(struct uinput_device *udev)
        return 0;
 }
 
-static int uinput_setup_device(struct uinput_device *udev,
-                              const char __user *buffer, size_t count)
+static int uinput_dev_setup(struct uinput_device *udev,
+                           struct uinput_setup __user *arg)
+{
+       struct uinput_setup setup;
+       struct input_dev *dev;
+
+       if (udev->state == UIST_CREATED)
+               return -EINVAL;
+
+       if (copy_from_user(&setup, arg, sizeof(setup)))
+               return -EFAULT;
+
+       if (!setup.name[0])
+               return -EINVAL;
+
+       dev = udev->dev;
+       dev->id = setup.id;
+       udev->ff_effects_max = setup.ff_effects_max;
+
+       kfree(dev->name);
+       dev->name = kstrndup(setup.name, UINPUT_MAX_NAME_SIZE, GFP_KERNEL);
+       if (!dev->name)
+               return -ENOMEM;
+
+       udev->state = UIST_SETUP_COMPLETE;
+       return 0;
+}
+
+static int uinput_abs_setup(struct uinput_device *udev,
+                           struct uinput_setup __user *arg, size_t size)
+{
+       struct uinput_abs_setup setup = {};
+       struct input_dev *dev;
+       int error;
+
+       if (size > sizeof(setup))
+               return -E2BIG;
+
+       if (udev->state == UIST_CREATED)
+               return -EINVAL;
+
+       if (copy_from_user(&setup, arg, size))
+               return -EFAULT;
+
+       if (setup.code > ABS_MAX)
+               return -ERANGE;
+
+       dev = udev->dev;
+
+       error = uinput_validate_absinfo(dev, setup.code, &setup.absinfo);
+       if (error)
+               return error;
+
+       input_alloc_absinfo(dev);
+       if (!dev->absinfo)
+               return -ENOMEM;
+
+       set_bit(setup.code, dev->absbit);
+       dev->absinfo[setup.code] = setup.absinfo;
+       return 0;
+}
+
+/* legacy setup via write() */
+static int uinput_setup_device_legacy(struct uinput_device *udev,
+                                     const char __user *buffer, size_t count)
 {
        struct uinput_user_dev  *user_dev;
        struct input_dev        *dev;
@@ -474,7 +552,7 @@ static ssize_t uinput_write(struct file *file, const char __user *buffer,
 
        retval = udev->state == UIST_CREATED ?
                        uinput_inject_events(udev, buffer, count) :
-                       uinput_setup_device(udev, buffer, count);
+                       uinput_setup_device_legacy(udev, buffer, count);
 
        mutex_unlock(&udev->mutex);
 
@@ -735,6 +813,12 @@ static long uinput_ioctl_handler(struct file *file, unsigned int cmd,
                        uinput_destroy_device(udev);
                        goto out;
 
+               case UI_DEV_SETUP:
+                       retval = uinput_dev_setup(udev, p);
+                       goto out;
+
+               /* UI_ABS_SETUP is handled in the variable size ioctls */
+
                case UI_SET_EVBIT:
                        retval = uinput_set_bit(arg, evbit, EV_MAX);
                        goto out;
@@ -879,6 +963,10 @@ static long uinput_ioctl_handler(struct file *file, unsigned int cmd,
                name = dev_name(&udev->dev->dev);
                retval = uinput_str_to_user(p, name, size);
                goto out;
+
+       case UI_ABS_SETUP & ~IOCSIZE_MASK:
+               retval = uinput_abs_setup(udev, p, size);
+               goto out;
        }
 
        retval = -EINVAL;
index 17f97e5..096abb4 100644 (file)
@@ -48,6 +48,16 @@ config MOUSE_PS2_ALPS
 
          If unsure, say Y.
 
+config MOUSE_PS2_BYD
+       bool "BYD PS/2 mouse protocol extension" if EXPERT
+       default y
+       depends on MOUSE_PS2
+       help
+         Say Y here if you have a BYD PS/2 touchpad connected to
+         your system.
+
+         If unsure, say Y.
+
 config MOUSE_PS2_LOGIPS2PP
        bool "Logitech PS/2++ mouse protocol extension" if EXPERT
        default y
index ee6a6e9..6168b13 100644 (file)
@@ -28,6 +28,7 @@ cyapatp-objs := cyapa.o cyapa_gen3.o cyapa_gen5.o cyapa_gen6.o
 psmouse-objs := psmouse-base.o synaptics.o focaltech.o
 
 psmouse-$(CONFIG_MOUSE_PS2_ALPS)       += alps.o
+psmouse-$(CONFIG_MOUSE_PS2_BYD)                += byd.o
 psmouse-$(CONFIG_MOUSE_PS2_ELANTECH)   += elantech.o
 psmouse-$(CONFIG_MOUSE_PS2_OLPC)       += hgpk.o
 psmouse-$(CONFIG_MOUSE_PS2_LOGIPS2PP)  += logips2pp.o
index 41e6cb5..936f07a 100644 (file)
@@ -31,6 +31,7 @@
 #define ALPS_CMD_NIBBLE_10     0x01f2
 
 #define ALPS_REG_BASE_RUSHMORE 0xc2c0
+#define ALPS_REG_BASE_V7       0xc2c0
 #define ALPS_REG_BASE_PINNACLE 0x0000
 
 static const struct alps_nibble_commands alps_v3_nibble_commands[] = {
@@ -2047,7 +2048,7 @@ static int alps_absolute_mode_v3(struct psmouse *psmouse)
        return 0;
 }
 
-static int alps_probe_trackstick_v3(struct psmouse *psmouse, int reg_base)
+static int alps_probe_trackstick_v3_v7(struct psmouse *psmouse, int reg_base)
 {
        int ret = -EIO, reg_val;
 
@@ -2128,15 +2129,12 @@ error:
 
 static int alps_hw_init_v3(struct psmouse *psmouse)
 {
+       struct alps_data *priv = psmouse->private;
        struct ps2dev *ps2dev = &psmouse->ps2dev;
        int reg_val;
        unsigned char param[4];
 
-       reg_val = alps_probe_trackstick_v3(psmouse, ALPS_REG_BASE_PINNACLE);
-       if (reg_val == -EIO)
-               goto error;
-
-       if (reg_val == 0 &&
+       if ((priv->flags & ALPS_DUALPOINT) &&
            alps_setup_trackstick_v3(psmouse, ALPS_REG_BASE_PINNACLE) == -EIO)
                goto error;
 
@@ -2613,6 +2611,11 @@ static int alps_set_protocol(struct psmouse *psmouse,
                priv->decode_fields = alps_decode_pinnacle;
                priv->nibble_commands = alps_v3_nibble_commands;
                priv->addr_command = PSMOUSE_CMD_RESET_WRAP;
+
+               if (alps_probe_trackstick_v3_v7(psmouse,
+                                               ALPS_REG_BASE_PINNACLE) < 0)
+                       priv->flags &= ~ALPS_DUALPOINT;
+
                break;
 
        case ALPS_PROTO_V3_RUSHMORE:
@@ -2625,8 +2628,8 @@ static int alps_set_protocol(struct psmouse *psmouse,
                priv->x_bits = 16;
                priv->y_bits = 12;
 
-               if (alps_probe_trackstick_v3(psmouse,
-                                            ALPS_REG_BASE_RUSHMORE) < 0)
+               if (alps_probe_trackstick_v3_v7(psmouse,
+                                               ALPS_REG_BASE_RUSHMORE) < 0)
                        priv->flags &= ~ALPS_DUALPOINT;
 
                break;
@@ -2676,6 +2679,9 @@ static int alps_set_protocol(struct psmouse *psmouse,
                if (priv->fw_ver[1] != 0xba)
                        priv->flags |= ALPS_BUTTONPAD;
 
+               if (alps_probe_trackstick_v3_v7(psmouse, ALPS_REG_BASE_V7) < 0)
+                       priv->flags &= ~ALPS_DUALPOINT;
+
                break;
 
        case ALPS_PROTO_V8:
diff --git a/drivers/input/mouse/byd.c b/drivers/input/mouse/byd.c
new file mode 100644 (file)
index 0000000..9425e0f
--- /dev/null
@@ -0,0 +1,337 @@
+/*
+ * BYD TouchPad PS/2 mouse driver
+ *
+ * Copyright (C) 2015 Chris Diamand <chris@diamand.org>
+ *
+ * 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.
+ */
+
+#include <linux/delay.h>
+#include <linux/input.h>
+#include <linux/libps2.h>
+#include <linux/serio.h>
+
+#include "psmouse.h"
+#include "byd.h"
+
+#define PS2_Y_OVERFLOW BIT_MASK(7)
+#define PS2_X_OVERFLOW BIT_MASK(6)
+#define PS2_Y_SIGN     BIT_MASK(5)
+#define PS2_X_SIGN     BIT_MASK(4)
+#define PS2_ALWAYS_1   BIT_MASK(3)
+#define PS2_MIDDLE     BIT_MASK(2)
+#define PS2_RIGHT      BIT_MASK(1)
+#define PS2_LEFT       BIT_MASK(0)
+
+/*
+ * The touchpad reports gestures in the last byte of each packet. It can take
+ * any of the following values:
+ */
+
+/* One-finger scrolling in one of the edge scroll zones. */
+#define BYD_SCROLLUP           0xCA
+#define BYD_SCROLLDOWN         0x36
+#define BYD_SCROLLLEFT         0xCB
+#define BYD_SCROLLRIGHT                0x35
+/* Two-finger scrolling. */
+#define BYD_2DOWN              0x2B
+#define BYD_2UP                        0xD5
+#define BYD_2LEFT              0xD6
+#define BYD_2RIGHT             0x2A
+/* Pinching in or out. */
+#define BYD_ZOOMOUT            0xD8
+#define BYD_ZOOMIN             0x28
+/* Three-finger swipe. */
+#define BYD_3UP                        0xD3
+#define BYD_3DOWN              0x2D
+#define BYD_3LEFT              0xD4
+#define BYD_3RIGHT             0x2C
+/* Four-finger swipe. */
+#define BYD_4UP                        0xCD
+#define BYD_4DOWN              0x33
+
+int byd_detect(struct psmouse *psmouse, bool set_properties)
+{
+       struct ps2dev *ps2dev = &psmouse->ps2dev;
+       unsigned char param[4];
+
+       param[0] = 0x03;
+       param[1] = 0x00;
+       param[2] = 0x00;
+       param[3] = 0x00;
+
+       if (ps2_command(ps2dev, param, PSMOUSE_CMD_SETRES))
+               return -1;
+       if (ps2_command(ps2dev, param, PSMOUSE_CMD_SETRES))
+               return -1;
+       if (ps2_command(ps2dev, param, PSMOUSE_CMD_SETRES))
+               return -1;
+       if (ps2_command(ps2dev, param, PSMOUSE_CMD_SETRES))
+               return -1;
+       if (ps2_command(ps2dev, param, PSMOUSE_CMD_GETINFO))
+               return -1;
+
+       if (param[1] != 0x03 || param[2] != 0x64)
+               return -ENODEV;
+
+       psmouse_dbg(psmouse, "BYD touchpad detected\n");
+
+       if (set_properties) {
+               psmouse->vendor = "BYD";
+               psmouse->name = "TouchPad";
+       }
+
+       return 0;
+}
+
+static psmouse_ret_t byd_process_byte(struct psmouse *psmouse)
+{
+       struct input_dev *dev = psmouse->dev;
+       u8 *pkt = psmouse->packet;
+
+       if (psmouse->pktcnt > 0 && !(pkt[0] & PS2_ALWAYS_1)) {
+               psmouse_warn(psmouse, "Always_1 bit not 1. pkt[0] = %02x\n",
+                            pkt[0]);
+               return PSMOUSE_BAD_DATA;
+       }
+
+       if (psmouse->pktcnt < psmouse->pktsize)
+               return PSMOUSE_GOOD_DATA;
+
+       /* Otherwise, a full packet has been received */
+       switch (pkt[3]) {
+       case 0: {
+               /* Standard packet */
+               /* Sign-extend if a sign bit is set. */
+               unsigned int signx = pkt[0] & PS2_X_SIGN ? ~0xFF : 0;
+               unsigned int signy = pkt[0] & PS2_Y_SIGN ? ~0xFF : 0;
+               int dx = signx | (int) pkt[1];
+               int dy = signy | (int) pkt[2];
+
+               input_report_rel(psmouse->dev, REL_X, dx);
+               input_report_rel(psmouse->dev, REL_Y, -dy);
+
+               input_report_key(psmouse->dev, BTN_LEFT, pkt[0] & PS2_LEFT);
+               input_report_key(psmouse->dev, BTN_RIGHT, pkt[0] & PS2_RIGHT);
+               input_report_key(psmouse->dev, BTN_MIDDLE, pkt[0] & PS2_MIDDLE);
+               break;
+       }
+
+       case BYD_SCROLLDOWN:
+       case BYD_2DOWN:
+               input_report_rel(dev, REL_WHEEL, -1);
+               break;
+
+       case BYD_SCROLLUP:
+       case BYD_2UP:
+               input_report_rel(dev, REL_WHEEL, 1);
+               break;
+
+       case BYD_SCROLLLEFT:
+       case BYD_2LEFT:
+               input_report_rel(dev, REL_HWHEEL, -1);
+               break;
+
+       case BYD_SCROLLRIGHT:
+       case BYD_2RIGHT:
+               input_report_rel(dev, REL_HWHEEL, 1);
+               break;
+
+       case BYD_ZOOMOUT:
+       case BYD_ZOOMIN:
+       case BYD_3UP:
+       case BYD_3DOWN:
+       case BYD_3LEFT:
+       case BYD_3RIGHT:
+       case BYD_4UP:
+       case BYD_4DOWN:
+               break;
+
+       default:
+               psmouse_warn(psmouse,
+                            "Unrecognized Z: pkt = %02x %02x %02x %02x\n",
+                            psmouse->packet[0], psmouse->packet[1],
+                            psmouse->packet[2], psmouse->packet[3]);
+               return PSMOUSE_BAD_DATA;
+       }
+
+       input_sync(dev);
+
+       return PSMOUSE_FULL_PACKET;
+}
+
+/* Send a sequence of bytes, where each is ACKed before the next is sent. */
+static int byd_send_sequence(struct psmouse *psmouse, const u8 *seq, size_t len)
+{
+       unsigned int i;
+
+       for (i = 0; i < len; ++i) {
+               if (ps2_command(&psmouse->ps2dev, NULL, seq[i]))
+                       return -1;
+       }
+       return 0;
+}
+
+/* Keep scrolling after fingers are removed. */
+#define SCROLL_INERTIAL                0x01
+#define SCROLL_NO_INERTIAL     0x02
+
+/* Clicking can be done by tapping or pressing. */
+#define CLICK_BOTH             0x01
+/* Clicking can only be done by pressing. */
+#define CLICK_PRESS_ONLY       0x02
+
+static int byd_enable(struct psmouse *psmouse)
+{
+       const u8 seq1[] = { 0xE2, 0x00, 0xE0, 0x02, 0xE0 };
+       const u8 seq2[] = {
+               0xD3, 0x01,
+               0xD0, 0x00,
+               0xD0, 0x04,
+               /* Whether clicking is done by tapping or pressing. */
+               0xD4, CLICK_PRESS_ONLY,
+               0xD5, 0x01,
+               0xD7, 0x03,
+               /* Vertical and horizontal one-finger scroll zone inertia. */
+               0xD8, SCROLL_INERTIAL,
+               0xDA, 0x05,
+               0xDB, 0x02,
+               0xE4, 0x05,
+               0xD6, 0x01,
+               0xDE, 0x04,
+               0xE3, 0x01,
+               0xCF, 0x00,
+               0xD2, 0x03,
+               /* Vertical and horizontal two-finger scrolling inertia. */
+               0xE5, SCROLL_INERTIAL,
+               0xD9, 0x02,
+               0xD9, 0x07,
+               0xDC, 0x03,
+               0xDD, 0x03,
+               0xDF, 0x03,
+               0xE1, 0x03,
+               0xD1, 0x00,
+               0xCE, 0x00,
+               0xCC, 0x00,
+               0xE0, 0x00,
+               0xE2, 0x01
+       };
+       u8 param[4];
+
+       if (byd_send_sequence(psmouse, seq1, ARRAY_SIZE(seq1)))
+               return -1;
+
+       /* Send a 0x01 command, which should return 4 bytes. */
+       if (ps2_command(&psmouse->ps2dev, param, 0x0401))
+               return -1;
+
+       if (byd_send_sequence(psmouse, seq2, ARRAY_SIZE(seq2)))
+               return -1;
+
+       return 0;
+}
+
+/*
+ * Send the set of PS/2 commands required to make it identify as an
+ * intellimouse with 4-byte instead of 3-byte packets.
+ */
+static int byd_send_intellimouse_sequence(struct psmouse *psmouse)
+{
+       struct ps2dev *ps2dev = &psmouse->ps2dev;
+       u8 param[4];
+       int i;
+       const struct {
+               u16 command;
+               u8 arg;
+       } seq[] = {
+               { PSMOUSE_CMD_RESET_BAT, 0 },
+               { PSMOUSE_CMD_RESET_BAT, 0 },
+               { PSMOUSE_CMD_GETID, 0 },
+               { PSMOUSE_CMD_SETSCALE11, 0 },
+               { PSMOUSE_CMD_SETSCALE11, 0 },
+               { PSMOUSE_CMD_SETSCALE11, 0 },
+               { PSMOUSE_CMD_GETINFO, 0 },
+               { PSMOUSE_CMD_SETRES, 0x03 },
+               { PSMOUSE_CMD_SETRATE, 0xC8 },
+               { PSMOUSE_CMD_SETRATE, 0x64 },
+               { PSMOUSE_CMD_SETRATE, 0x50 },
+               { PSMOUSE_CMD_GETID, 0 },
+               { PSMOUSE_CMD_SETRATE, 0xC8 },
+               { PSMOUSE_CMD_SETRATE, 0xC8 },
+               { PSMOUSE_CMD_SETRATE, 0x50 },
+               { PSMOUSE_CMD_GETID, 0 },
+               { PSMOUSE_CMD_SETRATE, 0x64 },
+               { PSMOUSE_CMD_SETRES, 0x03 },
+               { PSMOUSE_CMD_ENABLE, 0 }
+       };
+
+       memset(param, 0, sizeof(param));
+       for (i = 0; i < ARRAY_SIZE(seq); ++i) {
+               param[0] = seq[i].arg;
+               if (ps2_command(ps2dev, param, seq[i].command))
+                       return -1;
+       }
+
+       return 0;
+}
+
+static int byd_reset_touchpad(struct psmouse *psmouse)
+{
+       if (byd_send_intellimouse_sequence(psmouse))
+               return -EIO;
+
+       if (byd_enable(psmouse))
+               return -EIO;
+
+       return 0;
+}
+
+static int byd_reconnect(struct psmouse *psmouse)
+{
+       int retry = 0, error = 0;
+
+       psmouse_dbg(psmouse, "Reconnect\n");
+       do {
+               psmouse_reset(psmouse);
+               if (retry)
+                       ssleep(1);
+               error = byd_detect(psmouse, 0);
+       } while (error && ++retry < 3);
+
+       if (error)
+               return error;
+
+       psmouse_dbg(psmouse, "Reconnected after %d attempts\n", retry);
+
+       error = byd_reset_touchpad(psmouse);
+       if (error) {
+               psmouse_err(psmouse, "Unable to initialize device\n");
+               return error;
+       }
+
+       return 0;
+}
+
+int byd_init(struct psmouse *psmouse)
+{
+       struct input_dev *dev = psmouse->dev;
+
+       if (psmouse_reset(psmouse))
+               return -EIO;
+
+       if (byd_reset_touchpad(psmouse))
+               return -EIO;
+
+       psmouse->reconnect = byd_reconnect;
+       psmouse->protocol_handler = byd_process_byte;
+       psmouse->pktsize = 4;
+       psmouse->resync_time = 0;
+
+       __set_bit(BTN_MIDDLE, dev->keybit);
+       __set_bit(REL_WHEEL, dev->relbit);
+       __set_bit(REL_HWHEEL, dev->relbit);
+
+       return 0;
+}
diff --git a/drivers/input/mouse/byd.h b/drivers/input/mouse/byd.h
new file mode 100644 (file)
index 0000000..d6c120c
--- /dev/null
@@ -0,0 +1,18 @@
+#ifndef _BYD_H
+#define _BYD_H
+
+#ifdef CONFIG_MOUSE_PS2_BYD
+int byd_detect(struct psmouse *psmouse, bool set_properties);
+int byd_init(struct psmouse *psmouse);
+#else
+static inline int byd_detect(struct psmouse *psmouse, bool set_properties)
+{
+       return -ENOSYS;
+}
+static inline int byd_init(struct psmouse *psmouse)
+{
+       return -ENOSYS;
+}
+#endif /* CONFIG_MOUSE_PS2_BYD */
+
+#endif /* _BYD_H */
index eb76b61..dc23942 100644 (file)
@@ -383,7 +383,7 @@ static int cyapa_open(struct input_dev *input)
                 * when in operational mode.
                 */
                error = cyapa->ops->set_power_mode(cyapa,
-                               PWR_MODE_FULL_ACTIVE, 0, false);
+                               PWR_MODE_FULL_ACTIVE, 0, CYAPA_PM_ACTIVE);
                if (error) {
                        dev_warn(dev, "set active power failed: %d\n", error);
                        goto out;
@@ -424,7 +424,8 @@ static void cyapa_close(struct input_dev *input)
        pm_runtime_set_suspended(dev);
 
        if (cyapa->operational)
-               cyapa->ops->set_power_mode(cyapa, PWR_MODE_OFF, 0, false);
+               cyapa->ops->set_power_mode(cyapa,
+                               PWR_MODE_OFF, 0, CYAPA_PM_DEACTIVE);
 
        mutex_unlock(&cyapa->state_sync_lock);
 }
@@ -534,7 +535,7 @@ static void cyapa_enable_irq_for_cmd(struct cyapa *cyapa)
                 */
                if (!input || cyapa->operational)
                        cyapa->ops->set_power_mode(cyapa,
-                               PWR_MODE_FULL_ACTIVE, 0, false);
+                               PWR_MODE_FULL_ACTIVE, 0, CYAPA_PM_ACTIVE);
                /* Gen3 always using polling mode for command. */
                if (cyapa->gen >= CYAPA_GEN5)
                        enable_irq(cyapa->client->irq);
@@ -550,7 +551,7 @@ static void cyapa_disable_irq_for_cmd(struct cyapa *cyapa)
                        disable_irq(cyapa->client->irq);
                if (!input || cyapa->operational)
                        cyapa->ops->set_power_mode(cyapa,
-                                                  PWR_MODE_OFF, 0, false);
+                                       PWR_MODE_OFF, 0, CYAPA_PM_ACTIVE);
        }
 }
 
@@ -617,7 +618,8 @@ static int cyapa_initialize(struct cyapa *cyapa)
 
        /* Power down the device until we need it. */
        if (cyapa->operational)
-               cyapa->ops->set_power_mode(cyapa, PWR_MODE_OFF, 0, false);
+               cyapa->ops->set_power_mode(cyapa,
+                               PWR_MODE_OFF, 0, CYAPA_PM_ACTIVE);
 
        return 0;
 }
@@ -634,7 +636,7 @@ static int cyapa_reinitialize(struct cyapa *cyapa)
        /* Avoid command failures when TP was in OFF state. */
        if (cyapa->operational)
                cyapa->ops->set_power_mode(cyapa,
-                                          PWR_MODE_FULL_ACTIVE, 0, false);
+                               PWR_MODE_FULL_ACTIVE, 0, CYAPA_PM_ACTIVE);
 
        error = cyapa_detect(cyapa);
        if (error)
@@ -654,7 +656,7 @@ out:
                /* Reset to power OFF state to save power when no user open. */
                if (cyapa->operational)
                        cyapa->ops->set_power_mode(cyapa,
-                                                  PWR_MODE_OFF, 0, false);
+                                       PWR_MODE_OFF, 0, CYAPA_PM_DEACTIVE);
        } else if (!error && cyapa->operational) {
                /*
                 * Make sure only enable runtime PM when device is
@@ -1392,7 +1394,7 @@ static int __maybe_unused cyapa_suspend(struct device *dev)
                power_mode = device_may_wakeup(dev) ? cyapa->suspend_power_mode
                                                    : PWR_MODE_OFF;
                error = cyapa->ops->set_power_mode(cyapa, power_mode,
-                               cyapa->suspend_sleep_time, true);
+                               cyapa->suspend_sleep_time, CYAPA_PM_SUSPEND);
                if (error)
                        dev_err(dev, "suspend set power mode failed: %d\n",
                                        error);
@@ -1447,7 +1449,7 @@ static int __maybe_unused cyapa_runtime_suspend(struct device *dev)
        error = cyapa->ops->set_power_mode(cyapa,
                        cyapa->runtime_suspend_power_mode,
                        cyapa->runtime_suspend_sleep_time,
-                       false);
+                       CYAPA_PM_RUNTIME_SUSPEND);
        if (error)
                dev_warn(dev, "runtime suspend failed: %d\n", error);
 
@@ -1460,7 +1462,7 @@ static int __maybe_unused cyapa_runtime_resume(struct device *dev)
        int error;
 
        error = cyapa->ops->set_power_mode(cyapa,
-                                          PWR_MODE_FULL_ACTIVE, 0, false);
+                       PWR_MODE_FULL_ACTIVE, 0, CYAPA_PM_RUNTIME_RESUME);
        if (error)
                dev_warn(dev, "runtime resume failed: %d\n", error);
 
index b812bba..ce951fe 100644 (file)
@@ -250,6 +250,15 @@ struct cyapa;
 
 typedef bool (*cb_sort)(struct cyapa *, u8 *, int);
 
+enum cyapa_pm_stage {
+       CYAPA_PM_DEACTIVE,
+       CYAPA_PM_ACTIVE,
+       CYAPA_PM_SUSPEND,
+       CYAPA_PM_RESUME,
+       CYAPA_PM_RUNTIME_SUSPEND,
+       CYAPA_PM_RUNTIME_RESUME,
+};
+
 struct cyapa_dev_ops {
        int (*check_fw)(struct cyapa *, const struct firmware *);
        int (*bl_enter)(struct cyapa *);
@@ -273,7 +282,7 @@ struct cyapa_dev_ops {
        int (*sort_empty_output_data)(struct cyapa *,
                        u8 *, int *, cb_sort);
 
-       int (*set_power_mode)(struct cyapa *, u8, u16, bool);
+       int (*set_power_mode)(struct cyapa *, u8, u16, enum cyapa_pm_stage);
 
        int (*set_proximity)(struct cyapa *, bool);
 };
@@ -289,6 +298,9 @@ struct cyapa_pip_cmd_states {
        u8 *resp_data;
        int *resp_len;
 
+       enum cyapa_pm_stage pm_stage;
+       struct mutex pm_stage_lock;
+
        u8 irq_cmd_buf[CYAPA_REG_MAP_SIZE];
        u8 empty_buf[CYAPA_REG_MAP_SIZE];
 };
index 1a9d12a..f960075 100644 (file)
@@ -269,6 +269,7 @@ static const struct cyapa_cmd_len cyapa_smbus_cmds[] = {
        { CYAPA_SMBUS_MIN_BASELINE, 1 },        /* CYAPA_CMD_MIN_BASELINE */
 };
 
+static int cyapa_gen3_try_poll_handler(struct cyapa *cyapa);
 
 /*
  * cyapa_smbus_read_block - perform smbus block read command
@@ -950,12 +951,14 @@ static u16 cyapa_get_wait_time_for_pwr_cmd(u8 pwr_mode)
  * Device power mode can only be set when device is in operational mode.
  */
 static int cyapa_gen3_set_power_mode(struct cyapa *cyapa, u8 power_mode,
-               u16 always_unused, bool is_suspend_unused)
+               u16 always_unused, enum cyapa_pm_stage pm_stage)
 {
-       int ret;
+       struct input_dev *input = cyapa->input;
        u8 power;
        int tries;
-       u16 sleep_time;
+       int sleep_time;
+       int interval;
+       int ret;
 
        if (cyapa->state != CYAPA_STATE_OP)
                return 0;
@@ -977,7 +980,7 @@ static int cyapa_gen3_set_power_mode(struct cyapa *cyapa, u8 power_mode,
        if ((ret & PWR_MODE_MASK) == power_mode)
                return 0;
 
-       sleep_time = cyapa_get_wait_time_for_pwr_cmd(ret & PWR_MODE_MASK);
+       sleep_time = (int)cyapa_get_wait_time_for_pwr_cmd(ret & PWR_MODE_MASK);
        power = ret;
        power &= ~PWR_MODE_MASK;
        power |= power_mode & PWR_MODE_MASK;
@@ -995,7 +998,23 @@ static int cyapa_gen3_set_power_mode(struct cyapa *cyapa, u8 power_mode,
         * doing so before issuing the next command may result in errors
         * depending on the command's content.
         */
-       msleep(sleep_time);
+       if (cyapa->operational && input && input->users &&
+           (pm_stage == CYAPA_PM_RUNTIME_SUSPEND ||
+            pm_stage == CYAPA_PM_RUNTIME_RESUME)) {
+               /* Try to polling in 120Hz, read may fail, just ignore it. */
+               interval = 1000 / 120;
+               while (sleep_time > 0) {
+                       if (sleep_time > interval)
+                               msleep(interval);
+                       else
+                               msleep(sleep_time);
+                       sleep_time -= interval;
+                       cyapa_gen3_try_poll_handler(cyapa);
+               }
+       } else {
+               msleep(sleep_time);
+       }
+
        return ret;
 }
 
@@ -1112,7 +1131,7 @@ static int cyapa_gen3_do_operational_check(struct cyapa *cyapa)
                 * may cause problems, so we set the power mode first here.
                 */
                error = cyapa_gen3_set_power_mode(cyapa,
-                               PWR_MODE_FULL_ACTIVE, 0, false);
+                               PWR_MODE_FULL_ACTIVE, 0, CYAPA_PM_ACTIVE);
                if (error)
                        dev_err(dev, "%s: set full power mode failed: %d\n",
                                __func__, error);
@@ -1168,32 +1187,16 @@ static bool cyapa_gen3_irq_cmd_handler(struct cyapa *cyapa)
        return false;
 }
 
-static int cyapa_gen3_irq_handler(struct cyapa *cyapa)
+static int cyapa_gen3_event_process(struct cyapa *cyapa,
+                                   struct cyapa_reg_data *data)
 {
        struct input_dev *input = cyapa->input;
-       struct device *dev = &cyapa->client->dev;
-       struct cyapa_reg_data data;
        int num_fingers;
-       int ret;
        int i;
 
-       ret = cyapa_read_block(cyapa, CYAPA_CMD_GROUP_DATA, (u8 *)&data);
-       if (ret != sizeof(data)) {
-               dev_err(dev, "failed to read report data, (%d)\n", ret);
-               return -EINVAL;
-       }
-
-       if ((data.device_status & OP_STATUS_SRC) != OP_STATUS_SRC ||
-           (data.device_status & OP_STATUS_DEV) != CYAPA_DEV_NORMAL ||
-           (data.finger_btn & OP_DATA_VALID) != OP_DATA_VALID) {
-               dev_err(dev, "invalid device state bytes, %02x %02x\n",
-                       data.device_status, data.finger_btn);
-               return -EINVAL;
-       }
-
-       num_fingers = (data.finger_btn >> 4) & 0x0f;
+       num_fingers = (data->finger_btn >> 4) & 0x0f;
        for (i = 0; i < num_fingers; i++) {
-               const struct cyapa_touch *touch = &data.touches[i];
+               const struct cyapa_touch *touch = &data->touches[i];
                /* Note: touch->id range is 1 to 15; slots are 0 to 14. */
                int slot = touch->id - 1;
 
@@ -1210,18 +1213,65 @@ static int cyapa_gen3_irq_handler(struct cyapa *cyapa)
 
        if (cyapa->btn_capability & CAPABILITY_LEFT_BTN_MASK)
                input_report_key(input, BTN_LEFT,
-                                !!(data.finger_btn & OP_DATA_LEFT_BTN));
+                                !!(data->finger_btn & OP_DATA_LEFT_BTN));
        if (cyapa->btn_capability & CAPABILITY_MIDDLE_BTN_MASK)
                input_report_key(input, BTN_MIDDLE,
-                                !!(data.finger_btn & OP_DATA_MIDDLE_BTN));
+                                !!(data->finger_btn & OP_DATA_MIDDLE_BTN));
        if (cyapa->btn_capability & CAPABILITY_RIGHT_BTN_MASK)
                input_report_key(input, BTN_RIGHT,
-                                !!(data.finger_btn & OP_DATA_RIGHT_BTN));
+                                !!(data->finger_btn & OP_DATA_RIGHT_BTN));
        input_sync(input);
 
        return 0;
 }
 
+static int cyapa_gen3_irq_handler(struct cyapa *cyapa)
+{
+       struct device *dev = &cyapa->client->dev;
+       struct cyapa_reg_data data;
+       int ret;
+
+       ret = cyapa_read_block(cyapa, CYAPA_CMD_GROUP_DATA, (u8 *)&data);
+       if (ret != sizeof(data)) {
+               dev_err(dev, "failed to read report data, (%d)\n", ret);
+               return -EINVAL;
+       }
+
+       if ((data.device_status & OP_STATUS_SRC) != OP_STATUS_SRC ||
+           (data.device_status & OP_STATUS_DEV) != CYAPA_DEV_NORMAL ||
+           (data.finger_btn & OP_DATA_VALID) != OP_DATA_VALID) {
+               dev_err(dev, "invalid device state bytes: %02x %02x\n",
+                       data.device_status, data.finger_btn);
+               return -EINVAL;
+       }
+
+       return cyapa_gen3_event_process(cyapa, &data);
+}
+
+/*
+ * This function will be called in the cyapa_gen3_set_power_mode function,
+ * and it's known that it may failed in some situation after the set power
+ * mode command was sent. So this function is aimed to avoid the knwon
+ * and unwanted output I2C and data parse error messages.
+ */
+static int cyapa_gen3_try_poll_handler(struct cyapa *cyapa)
+{
+       struct cyapa_reg_data data;
+       int ret;
+
+       ret = cyapa_read_block(cyapa, CYAPA_CMD_GROUP_DATA, (u8 *)&data);
+       if (ret != sizeof(data))
+               return -EINVAL;
+
+       if ((data.device_status & OP_STATUS_SRC) != OP_STATUS_SRC ||
+           (data.device_status & OP_STATUS_DEV) != CYAPA_DEV_NORMAL ||
+           (data.finger_btn & OP_DATA_VALID) != OP_DATA_VALID)
+               return -EINVAL;
+
+       return cyapa_gen3_event_process(cyapa, &data);
+
+}
+
 static int cyapa_gen3_initialize(struct cyapa *cyapa) { return 0; }
 static int cyapa_gen3_bl_initiate(struct cyapa *cyapa,
                const struct firmware *fw) { return 0; }
index 118ba97..5775d40 100644 (file)
@@ -342,6 +342,9 @@ u8 pip_bl_read_app_info[] = { 0x04, 0x00, 0x0b, 0x00, 0x40, 0x00,
 static u8 cyapa_pip_bl_cmd_key[] = { 0xa5, 0x01, 0x02, 0x03,
        0xff, 0xfe, 0xfd, 0x5a };
 
+static int cyapa_pip_event_process(struct cyapa *cyapa,
+                                  struct cyapa_pip_report_data *report_data);
+
 int cyapa_pip_cmd_state_initialize(struct cyapa *cyapa)
 {
        struct cyapa_pip_cmd_states *pip = &cyapa->cmd_states.pip;
@@ -350,6 +353,9 @@ int cyapa_pip_cmd_state_initialize(struct cyapa *cyapa)
        atomic_set(&pip->cmd_issued, 0);
        mutex_init(&pip->cmd_lock);
 
+       mutex_init(&pip->pm_stage_lock);
+       pip->pm_stage = CYAPA_PM_DEACTIVE;
+
        pip->resp_sort_func = NULL;
        pip->in_progress_cmd = PIP_INVALID_CMD;
        pip->resp_data = NULL;
@@ -397,6 +403,38 @@ ssize_t cyapa_i2c_pip_write(struct cyapa *cyapa, u8 *buf, size_t size)
        return 0;
 }
 
+static void cyapa_set_pip_pm_state(struct cyapa *cyapa,
+                                  enum cyapa_pm_stage pm_stage)
+{
+       struct cyapa_pip_cmd_states *pip = &cyapa->cmd_states.pip;
+
+       mutex_lock(&pip->pm_stage_lock);
+       pip->pm_stage = pm_stage;
+       mutex_unlock(&pip->pm_stage_lock);
+}
+
+static void cyapa_reset_pip_pm_state(struct cyapa *cyapa)
+{
+       struct cyapa_pip_cmd_states *pip = &cyapa->cmd_states.pip;
+
+       /* Indicates the pip->pm_stage is not valid. */
+       mutex_lock(&pip->pm_stage_lock);
+       pip->pm_stage = CYAPA_PM_DEACTIVE;
+       mutex_unlock(&pip->pm_stage_lock);
+}
+
+static enum cyapa_pm_stage cyapa_get_pip_pm_state(struct cyapa *cyapa)
+{
+       struct cyapa_pip_cmd_states *pip = &cyapa->cmd_states.pip;
+       enum cyapa_pm_stage pm_stage;
+
+       mutex_lock(&pip->pm_stage_lock);
+       pm_stage = pip->pm_stage;
+       mutex_unlock(&pip->pm_stage_lock);
+
+       return pm_stage;
+}
+
 /**
  * This function is aimed to dump all not read data in Gen5 trackpad
  * before send any command, otherwise, the interrupt line will be blocked.
@@ -404,7 +442,9 @@ ssize_t cyapa_i2c_pip_write(struct cyapa *cyapa, u8 *buf, size_t size)
 int cyapa_empty_pip_output_data(struct cyapa *cyapa,
                u8 *buf, int *len, cb_sort func)
 {
+       struct input_dev *input = cyapa->input;
        struct cyapa_pip_cmd_states *pip = &cyapa->cmd_states.pip;
+       enum cyapa_pm_stage pm_stage = cyapa_get_pip_pm_state(cyapa);
        int length;
        int report_count;
        int empty_count;
@@ -478,6 +518,12 @@ int cyapa_empty_pip_output_data(struct cyapa *cyapa,
                        *len = length;
                        /* Response found, success. */
                        return 0;
+               } else if (cyapa->operational && input && input->users &&
+                          (pm_stage == CYAPA_PM_RUNTIME_RESUME ||
+                           pm_stage == CYAPA_PM_RUNTIME_SUSPEND)) {
+                       /* Parse the data and report it if it's valid. */
+                       cyapa_pip_event_process(cyapa,
+                              (struct cyapa_pip_report_data *)pip->empty_buf);
                }
 
                error = -EINVAL;
@@ -1566,15 +1612,17 @@ int cyapa_pip_deep_sleep(struct cyapa *cyapa, u8 state)
 }
 
 static int cyapa_gen5_set_power_mode(struct cyapa *cyapa,
-               u8 power_mode, u16 sleep_time, bool is_suspend)
+               u8 power_mode, u16 sleep_time, enum cyapa_pm_stage pm_stage)
 {
        struct device *dev = &cyapa->client->dev;
        u8 power_state;
-       int error;
+       int error = 0;
 
        if (cyapa->state != CYAPA_STATE_GEN5_APP)
                return 0;
 
+       cyapa_set_pip_pm_state(cyapa, pm_stage);
+
        if (PIP_DEV_GET_PWR_STATE(cyapa) == UNINIT_PWR_MODE) {
                /*
                 * Assume TP in deep sleep mode when driver is loaded,
@@ -1597,7 +1645,7 @@ static int cyapa_gen5_set_power_mode(struct cyapa *cyapa,
                        power_mode == PWR_MODE_BTN_ONLY ||
                        PIP_DEV_GET_SLEEP_TIME(cyapa) == sleep_time) {
                        /* Has in correct power mode state, early return. */
-                       return 0;
+                       goto out;
                }
        }
 
@@ -1605,11 +1653,11 @@ static int cyapa_gen5_set_power_mode(struct cyapa *cyapa,
                error = cyapa_pip_deep_sleep(cyapa, PIP_DEEP_SLEEP_STATE_OFF);
                if (error) {
                        dev_err(dev, "enter deep sleep fail: %d\n", error);
-                       return error;
+                       goto out;
                }
 
                PIP_DEV_SET_PWR_STATE(cyapa, PWR_MODE_OFF);
-               return 0;
+               goto out;
        }
 
        /*
@@ -1621,7 +1669,7 @@ static int cyapa_gen5_set_power_mode(struct cyapa *cyapa,
                error = cyapa_pip_deep_sleep(cyapa, PIP_DEEP_SLEEP_STATE_ON);
                if (error) {
                        dev_err(dev, "deep sleep wake fail: %d\n", error);
-                       return error;
+                       goto out;
                }
        }
 
@@ -1630,7 +1678,7 @@ static int cyapa_gen5_set_power_mode(struct cyapa *cyapa,
                                GEN5_POWER_STATE_ACTIVE);
                if (error) {
                        dev_err(dev, "change to active fail: %d\n", error);
-                       return error;
+                       goto out;
                }
 
                PIP_DEV_SET_PWR_STATE(cyapa, PWR_MODE_FULL_ACTIVE);
@@ -1639,7 +1687,7 @@ static int cyapa_gen5_set_power_mode(struct cyapa *cyapa,
                                GEN5_POWER_STATE_BTN_ONLY);
                if (error) {
                        dev_err(dev, "fail to button only mode: %d\n", error);
-                       return error;
+                       goto out;
                }
 
                PIP_DEV_SET_PWR_STATE(cyapa, PWR_MODE_BTN_ONLY);
@@ -1664,7 +1712,7 @@ static int cyapa_gen5_set_power_mode(struct cyapa *cyapa,
                if (error) {
                        dev_err(dev, "set power state to 0x%02x failed: %d\n",
                                power_state, error);
-                       return error;
+                       goto out;
                }
 
                /*
@@ -1677,14 +1725,16 @@ static int cyapa_gen5_set_power_mode(struct cyapa *cyapa,
                 * is suspending which may cause interrupt line unable to be
                 * asserted again.
                 */
-               if (is_suspend)
+               if (pm_stage == CYAPA_PM_SUSPEND)
                        cyapa_gen5_disable_pip_report(cyapa);
 
                PIP_DEV_SET_PWR_STATE(cyapa,
                        cyapa_sleep_time_to_pwr_cmd(sleep_time));
        }
 
-       return 0;
+out:
+       cyapa_reset_pip_pm_state(cyapa);
+       return error;
 }
 
 int cyapa_pip_resume_scanning(struct cyapa *cyapa)
@@ -2513,7 +2563,7 @@ static int cyapa_gen5_do_operational_check(struct cyapa *cyapa)
                 * the device state is required.
                 */
                error = cyapa_gen5_set_power_mode(cyapa,
-                               PWR_MODE_FULL_ACTIVE, 0, false);
+                               PWR_MODE_FULL_ACTIVE, 0, CYAPA_PM_ACTIVE);
                if (error)
                        dev_warn(dev, "%s: failed to set power active mode.\n",
                                __func__);
@@ -2715,7 +2765,6 @@ int cyapa_pip_irq_handler(struct cyapa *cyapa)
        struct device *dev = &cyapa->client->dev;
        struct cyapa_pip_report_data report_data;
        unsigned int report_len;
-       u8 report_id;
        int ret;
 
        if (!cyapa_is_pip_app_mode(cyapa)) {
@@ -2752,7 +2801,23 @@ int cyapa_pip_irq_handler(struct cyapa *cyapa)
                return -EINVAL;
        }
 
-       report_id = report_data.report_head[PIP_RESP_REPORT_ID_OFFSET];
+       return cyapa_pip_event_process(cyapa, &report_data);
+}
+
+static int cyapa_pip_event_process(struct cyapa *cyapa,
+                                  struct cyapa_pip_report_data *report_data)
+{
+       struct device *dev = &cyapa->client->dev;
+       unsigned int report_len;
+       u8 report_id;
+
+       report_len = get_unaligned_le16(
+                       &report_data->report_head[PIP_RESP_LENGTH_OFFSET]);
+       /* Idle, no data for report. */
+       if (report_len == PIP_RESP_LENGTH_SIZE)
+               return 0;
+
+       report_id = report_data->report_head[PIP_RESP_REPORT_ID_OFFSET];
        if (report_id == PIP_WAKEUP_EVENT_REPORT_ID &&
                        report_len == PIP_WAKEUP_EVENT_SIZE) {
                /*
@@ -2805,11 +2870,11 @@ int cyapa_pip_irq_handler(struct cyapa *cyapa)
        }
 
        if (report_id == PIP_TOUCH_REPORT_ID)
-               cyapa_pip_report_touches(cyapa, &report_data);
+               cyapa_pip_report_touches(cyapa, report_data);
        else if (report_id == PIP_PROXIMITY_REPORT_ID)
-               cyapa_pip_report_proximity(cyapa, &report_data);
+               cyapa_pip_report_proximity(cyapa, report_data);
        else
-               cyapa_pip_report_buttons(cyapa, &report_data);
+               cyapa_pip_report_buttons(cyapa, report_data);
 
        return 0;
 }
index e4eb048..0163978 100644 (file)
@@ -425,7 +425,7 @@ static int cyapa_gen6_deep_sleep(struct cyapa *cyapa, u8 state)
 }
 
 static int cyapa_gen6_set_power_mode(struct cyapa *cyapa,
-               u8 power_mode, u16 sleep_time, bool is_suspend)
+               u8 power_mode, u16 sleep_time, enum cyapa_pm_stage pm_stage)
 {
        struct device *dev = &cyapa->client->dev;
        struct gen6_interval_setting *interval_setting =
@@ -689,7 +689,7 @@ static int cyapa_gen6_operational_check(struct cyapa *cyapa)
                 * the device state is required.
                 */
                error = cyapa_gen6_set_power_mode(cyapa,
-                               PWR_MODE_FULL_ACTIVE, 0, false);
+                               PWR_MODE_FULL_ACTIVE, 0, CYAPA_PM_ACTIVE);
                if (error)
                        dev_warn(dev, "%s: failed to set power active mode.\n",
                                __func__);
index 537ebb0..78f93cf 100644 (file)
@@ -1222,7 +1222,7 @@ static int elantech_set_input_params(struct psmouse *psmouse)
                        input_set_abs_params(dev, ABS_TOOL_WIDTH, ETP_WMIN_V2,
                                             ETP_WMAX_V2, 0, 0);
                }
-               input_mt_init_slots(dev, 2, 0);
+               input_mt_init_slots(dev, 2, INPUT_MT_SEMI_MT);
                input_set_abs_params(dev, ABS_MT_POSITION_X, x_min, x_max, 0, 0);
                input_set_abs_params(dev, ABS_MT_POSITION_Y, y_min, y_max, 0, 0);
                break;
index 4d5576d..c8c6a8c 100644 (file)
@@ -49,12 +49,6 @@ int focaltech_detect(struct psmouse *psmouse, bool set_properties)
        return 0;
 }
 
-static void focaltech_reset(struct psmouse *psmouse)
-{
-       ps2_command(&psmouse->ps2dev, NULL, PSMOUSE_CMD_RESET_DIS);
-       psmouse_reset(psmouse);
-}
-
 #ifdef CONFIG_MOUSE_PS2_FOCALTECH
 
 /*
@@ -300,6 +294,12 @@ static int focaltech_switch_protocol(struct psmouse *psmouse)
        return 0;
 }
 
+static void focaltech_reset(struct psmouse *psmouse)
+{
+       ps2_command(&psmouse->ps2dev, NULL, PSMOUSE_CMD_RESET_DIS);
+       psmouse_reset(psmouse);
+}
+
 static void focaltech_disconnect(struct psmouse *psmouse)
 {
        focaltech_reset(psmouse);
@@ -456,14 +456,4 @@ fail:
        kfree(priv);
        return error;
 }
-
-#else /* CONFIG_MOUSE_PS2_FOCALTECH */
-
-int focaltech_init(struct psmouse *psmouse)
-{
-       focaltech_reset(psmouse);
-
-       return 0;
-}
-
 #endif /* CONFIG_MOUSE_PS2_FOCALTECH */
index ca61ebf..783b28e 100644 (file)
 #define _FOCALTECH_H
 
 int focaltech_detect(struct psmouse *psmouse, bool set_properties);
+
+#ifdef CONFIG_MOUSE_PS2_FOCALTECH
 int focaltech_init(struct psmouse *psmouse);
+#else
+static inline int focaltech_init(struct psmouse *psmouse)
+{
+       return -ENOSYS;
+}
+#endif
 
 #endif
index 136e222..422da1c 100644 (file)
@@ -325,7 +325,7 @@ static void ps2pp_set_model_properties(struct psmouse *psmouse,
  * that support it.
  */
 
-int ps2pp_init(struct psmouse *psmouse, bool set_properties)
+int ps2pp_detect(struct psmouse *psmouse, bool set_properties)
 {
        struct ps2dev *ps2dev = &psmouse->ps2dev;
        unsigned char param[4];
index 0c186f0..bf62945 100644 (file)
@@ -12,9 +12,9 @@
 #define _LOGIPS2PP_H
 
 #ifdef CONFIG_MOUSE_PS2_LOGIPS2PP
-int ps2pp_init(struct psmouse *psmouse, bool set_properties);
+int ps2pp_detect(struct psmouse *psmouse, bool set_properties);
 #else
-inline int ps2pp_init(struct psmouse *psmouse, bool set_properties)
+static inline int ps2pp_detect(struct psmouse *psmouse, bool set_properties)
 {
        return -ENOSYS;
 }
index ad18dab..39d1bec 100644 (file)
@@ -37,6 +37,7 @@
 #include "cypress_ps2.h"
 #include "focaltech.h"
 #include "vmmouse.h"
+#include "byd.h"
 
 #define DRIVER_DESC    "PS/2 mouse driver"
 
@@ -119,6 +120,7 @@ struct psmouse_protocol {
        enum psmouse_type type;
        bool maxproto;
        bool ignore_parity; /* Protocol should ignore parity errors from KBC */
+       bool try_passthru; /* Try protocol also on passthrough ports */
        const char *name;
        const char *alias;
        int (*detect)(struct psmouse *, bool);
@@ -129,7 +131,6 @@ struct psmouse_protocol {
  * psmouse_process_byte() analyzes the PS/2 data stream and reports
  * relevant events to the input module once full packet has arrived.
  */
-
 psmouse_ret_t psmouse_process_byte(struct psmouse *psmouse)
 {
        struct input_dev *dev = psmouse->dev;
@@ -138,22 +139,16 @@ psmouse_ret_t psmouse_process_byte(struct psmouse *psmouse)
        if (psmouse->pktcnt < psmouse->pktsize)
                return PSMOUSE_GOOD_DATA;
 
-/*
- * Full packet accumulated, process it
- */
+       /* Full packet accumulated, process it */
 
-/*
- * Scroll wheel on IntelliMice, scroll buttons on NetMice
- */
-
-       if (psmouse->type == PSMOUSE_IMPS || psmouse->type == PSMOUSE_GENPS)
+       switch (psmouse->type) {
+       case PSMOUSE_IMPS:
+               /* IntelliMouse has scroll wheel */
                input_report_rel(dev, REL_WHEEL, -(signed char) packet[3]);
+               break;
 
-/*
- * Scroll wheel and buttons on IntelliMouse Explorer
- */
-
-       if (psmouse->type == PSMOUSE_IMEX) {
+       case PSMOUSE_IMEX:
+               /* Scroll wheel and buttons on IntelliMouse Explorer */
                switch (packet[3] & 0xC0) {
                case 0x80: /* vertical scroll on IntelliMouse Explorer 4.0 */
                        input_report_rel(dev, REL_WHEEL, (int) (packet[3] & 32) - (int) (packet[3] & 31));
@@ -168,39 +163,42 @@ psmouse_ret_t psmouse_process_byte(struct psmouse *psmouse)
                        input_report_key(dev, BTN_EXTRA, (packet[3] >> 5) & 1);
                        break;
                }
-       }
+               break;
 
-/*
- * Extra buttons on Genius NewNet 3D
- */
+       case PSMOUSE_GENPS:
+               /* Report scroll buttons on NetMice */
+               input_report_rel(dev, REL_WHEEL, -(signed char) packet[3]);
 
-       if (psmouse->type == PSMOUSE_GENPS) {
+               /* Extra buttons on Genius NewNet 3D */
                input_report_key(dev, BTN_SIDE, (packet[0] >> 6) & 1);
                input_report_key(dev, BTN_EXTRA, (packet[0] >> 7) & 1);
-       }
+               break;
 
-/*
- * Extra button on ThinkingMouse
- */
-       if (psmouse->type == PSMOUSE_THINKPS) {
+       case PSMOUSE_THINKPS:
+               /* Extra button on ThinkingMouse */
                input_report_key(dev, BTN_EXTRA, (packet[0] >> 3) & 1);
-               /* Without this bit of weirdness moving up gives wildly high Y changes. */
+
+               /*
+                * Without this bit of weirdness moving up gives wildly
+                * high Y changes.
+                */
                packet[1] |= (packet[0] & 0x40) << 1;
-       }
+               break;
 
-/*
- * Cortron PS2 Trackball reports SIDE button on the 4th bit of the first
- * byte.
- */
-       if (psmouse->type == PSMOUSE_CORTRON) {
+       case PSMOUSE_CORTRON:
+               /*
+                * Cortron PS2 Trackball reports SIDE button in the
+                * 4th bit of the first byte.
+                */
                input_report_key(dev, BTN_SIDE, (packet[0] >> 3) & 1);
                packet[0] |= 0x08;
-       }
+               break;
 
-/*
- * Generic PS/2 Mouse
- */
+       default:
+               break;
+       }
 
+       /* Generic PS/2 Mouse */
        input_report_key(dev, BTN_LEFT,    packet[0]       & 1);
        input_report_key(dev, BTN_MIDDLE, (packet[0] >> 2) & 1);
        input_report_key(dev, BTN_RIGHT,  (packet[0] >> 1) & 1);
@@ -222,7 +220,6 @@ void psmouse_queue_work(struct psmouse *psmouse, struct delayed_work *work,
 /*
  * __psmouse_set_state() sets new psmouse state and resets all flags.
  */
-
 static inline void __psmouse_set_state(struct psmouse *psmouse, enum psmouse_state new_state)
 {
        psmouse->state = new_state;
@@ -231,13 +228,11 @@ static inline void __psmouse_set_state(struct psmouse *psmouse, enum psmouse_sta
        psmouse->last = jiffies;
 }
 
-
 /*
  * psmouse_set_state() sets new psmouse state and resets all flags and
  * counters while holding serio lock so fighting with interrupt handler
  * is not a concern.
  */
-
 void psmouse_set_state(struct psmouse *psmouse, enum psmouse_state new_state)
 {
        serio_pause_rx(psmouse->ps2dev.serio);
@@ -249,7 +244,6 @@ void psmouse_set_state(struct psmouse *psmouse, enum psmouse_state new_state)
  * psmouse_handle_byte() processes one byte of the input data stream
  * by calling corresponding protocol handler.
  */
-
 static int psmouse_handle_byte(struct psmouse *psmouse)
 {
        psmouse_ret_t rc = psmouse->protocol_handler(psmouse);
@@ -292,7 +286,6 @@ static int psmouse_handle_byte(struct psmouse *psmouse)
  * psmouse_interrupt() handles incoming characters, either passing them
  * for normal processing or gathering them as command response.
  */
-
 static irqreturn_t psmouse_interrupt(struct serio *serio,
                unsigned char data, unsigned int flags)
 {
@@ -335,9 +328,8 @@ static irqreturn_t psmouse_interrupt(struct serio *serio,
        }
 
        psmouse->packet[psmouse->pktcnt++] = data;
-/*
- * Check if this is a new device announcement (0xAA 0x00)
- */
+
+       /* Check if this is a new device announcement (0xAA 0x00) */
        if (unlikely(psmouse->packet[0] == PSMOUSE_RET_BAT && psmouse->pktcnt <= 2)) {
                if (psmouse->pktcnt == 1) {
                        psmouse->last = jiffies;
@@ -351,9 +343,8 @@ static irqreturn_t psmouse_interrupt(struct serio *serio,
                        serio_reconnect(serio);
                        goto out;
                }
-/*
- * Not a new device, try processing first byte normally
- */
+
+               /* Not a new device, try processing first byte normally */
                psmouse->pktcnt = 1;
                if (psmouse_handle_byte(psmouse))
                        goto out;
@@ -361,9 +352,10 @@ static irqreturn_t psmouse_interrupt(struct serio *serio,
                psmouse->packet[psmouse->pktcnt++] = data;
        }
 
-/*
- * See if we need to force resync because mouse was idle for too long
- */
+       /*
+        * See if we need to force resync because mouse was idle for
+        * too long.
+        */
        if (psmouse->state == PSMOUSE_ACTIVATED &&
            psmouse->pktcnt == 1 && psmouse->resync_time &&
            time_after(jiffies, psmouse->last + psmouse->resync_time * HZ)) {
@@ -380,7 +372,6 @@ static irqreturn_t psmouse_interrupt(struct serio *serio,
        return IRQ_HANDLED;
 }
 
-
 /*
  * psmouse_sliced_command() sends an extended PS/2 command to the mouse
  * using sliced syntax, understood by advanced devices, such as Logitech
@@ -404,7 +395,6 @@ int psmouse_sliced_command(struct psmouse *psmouse, unsigned char command)
        return 0;
 }
 
-
 /*
  * psmouse_reset() resets the mouse into power-on state.
  */
@@ -424,7 +414,6 @@ int psmouse_reset(struct psmouse *psmouse)
 /*
  * Here we set the mouse resolution.
  */
-
 void psmouse_set_resolution(struct psmouse *psmouse, unsigned int resolution)
 {
        static const unsigned char params[] = { 0, 1, 2, 2, 3 };
@@ -441,7 +430,6 @@ void psmouse_set_resolution(struct psmouse *psmouse, unsigned int resolution)
 /*
  * Here we set the mouse report rate.
  */
-
 static void psmouse_set_rate(struct psmouse *psmouse, unsigned int rate)
 {
        static const unsigned char rates[] = { 200, 100, 80, 60, 40, 20, 10, 0 };
@@ -457,7 +445,6 @@ static void psmouse_set_rate(struct psmouse *psmouse, unsigned int rate)
 /*
  * Here we set the mouse scaling.
  */
-
 static void psmouse_set_scale(struct psmouse *psmouse, enum psmouse_scale scale)
 {
        ps2_command(&psmouse->ps2dev, NULL,
@@ -468,7 +455,6 @@ static void psmouse_set_scale(struct psmouse *psmouse, enum psmouse_scale scale)
 /*
  * psmouse_poll() - default poll handler. Everyone except for ALPS uses it.
  */
-
 static int psmouse_poll(struct psmouse *psmouse)
 {
        return ps2_command(&psmouse->ps2dev, psmouse->packet,
@@ -602,7 +588,7 @@ static int im_explorer_detect(struct psmouse *psmouse, bool set_properties)
        if (param[0] != 4)
                return -1;
 
-/* Magic to enable horizontal scrolling on IntelliMouse 4.0 */
+       /* Magic to enable horizontal scrolling on IntelliMouse 4.0 */
        param[0] = 200;
        ps2_command(ps2dev, param, PSMOUSE_CMD_SETRATE);
        param[0] =  80;
@@ -672,10 +658,10 @@ static int ps2bare_detect(struct psmouse *psmouse, bool set_properties)
                if (!psmouse->name)
                        psmouse->name = "Mouse";
 
-/*
- * We have no way of figuring true number of buttons so let's
- * assume that the device has 3.
- */
+               /*
               * We have no way of figuring true number of buttons so let's
               * assume that the device has 3.
               */
                __set_bit(BTN_MIDDLE, psmouse->dev->keybit);
        }
 
@@ -699,284 +685,6 @@ static int cortron_detect(struct psmouse *psmouse, bool set_properties)
        return 0;
 }
 
-/*
- * Apply default settings to the psmouse structure. Most of them will
- * be overridden by individual protocol initialization routines.
- */
-
-static void psmouse_apply_defaults(struct psmouse *psmouse)
-{
-       struct input_dev *input_dev = psmouse->dev;
-
-       memset(input_dev->evbit, 0, sizeof(input_dev->evbit));
-       memset(input_dev->keybit, 0, sizeof(input_dev->keybit));
-       memset(input_dev->relbit, 0, sizeof(input_dev->relbit));
-       memset(input_dev->absbit, 0, sizeof(input_dev->absbit));
-       memset(input_dev->mscbit, 0, sizeof(input_dev->mscbit));
-
-       __set_bit(EV_KEY, input_dev->evbit);
-       __set_bit(EV_REL, input_dev->evbit);
-
-       __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);
-
-       __set_bit(INPUT_PROP_POINTER, input_dev->propbit);
-
-       psmouse->set_rate = psmouse_set_rate;
-       psmouse->set_resolution = psmouse_set_resolution;
-       psmouse->set_scale = psmouse_set_scale;
-       psmouse->poll = psmouse_poll;
-       psmouse->protocol_handler = psmouse_process_byte;
-       psmouse->pktsize = 3;
-       psmouse->reconnect = NULL;
-       psmouse->disconnect = NULL;
-       psmouse->cleanup = NULL;
-       psmouse->pt_activate = NULL;
-       psmouse->pt_deactivate = NULL;
-}
-
-/*
- * Apply default settings to the psmouse structure and call specified
- * protocol detection or initialization routine.
- */
-static int psmouse_do_detect(int (*detect)(struct psmouse *psmouse,
-                                          bool set_properties),
-                            struct psmouse *psmouse, bool set_properties)
-{
-       if (set_properties)
-               psmouse_apply_defaults(psmouse);
-
-       return detect(psmouse, set_properties);
-}
-
-/*
- * psmouse_extensions() probes for any extensions to the basic PS/2 protocol
- * the mouse may have.
- */
-
-static int psmouse_extensions(struct psmouse *psmouse,
-                             unsigned int max_proto, bool set_properties)
-{
-       bool synaptics_hardware = false;
-
-/* Always check for focaltech, this is safe as it uses pnp-id matching */
-       if (psmouse_do_detect(focaltech_detect, psmouse, set_properties) == 0) {
-               if (max_proto > PSMOUSE_IMEX) {
-                       if (!set_properties || focaltech_init(psmouse) == 0) {
-                               if (IS_ENABLED(CONFIG_MOUSE_PS2_FOCALTECH))
-                                       return PSMOUSE_FOCALTECH;
-                               /*
-                                * Note that we need to also restrict
-                                * psmouse_max_proto so that psmouse_initialize()
-                                * does not try to reset rate and resolution,
-                                * because even that upsets the device.
-                                */
-                               psmouse_max_proto = PSMOUSE_PS2;
-                               return PSMOUSE_PS2;
-                       }
-               }
-       }
-
-/*
- * We always check for lifebook because it does not disturb mouse
- * (it only checks DMI information).
- */
-       if (psmouse_do_detect(lifebook_detect, psmouse, set_properties) == 0) {
-               if (max_proto > PSMOUSE_IMEX) {
-                       if (!set_properties || lifebook_init(psmouse) == 0)
-                               return PSMOUSE_LIFEBOOK;
-               }
-       }
-
-       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).
- */
-
-       if (max_proto > PSMOUSE_IMEX &&
-           psmouse_do_detect(thinking_detect, psmouse, set_properties) == 0) {
-               return PSMOUSE_THINKPS;
-       }
-
-/*
- * Try Synaptics TouchPad. Note that probing is done even if Synaptics protocol
- * support is disabled in config - we need to know if it is synaptics so we
- * can reset it properly after probing for intellimouse.
- */
-       if (max_proto > PSMOUSE_PS2 &&
-           psmouse_do_detect(synaptics_detect, psmouse, set_properties) == 0) {
-               synaptics_hardware = true;
-
-               if (max_proto > PSMOUSE_IMEX) {
-/*
- * Try activating protocol, but check if support is enabled first, since
- * we try detecting Synaptics even when protocol is disabled.
- */
-                       if (IS_ENABLED(CONFIG_MOUSE_PS2_SYNAPTICS) &&
-                           (!set_properties || synaptics_init(psmouse) == 0)) {
-                               return PSMOUSE_SYNAPTICS;
-                       }
-
-/*
- * Some Synaptics touchpads can emulate extended protocols (like IMPS/2).
- * Unfortunately Logitech/Genius probes confuse some firmware versions so
- * we'll have to skip them.
- */
-                       max_proto = PSMOUSE_IMEX;
-               }
-/*
- * Make sure that touchpad is in relative mode, gestures (taps) are enabled
- */
-               synaptics_reset(psmouse);
-       }
-
-/*
- * Try Cypress Trackpad.
- * Must try it before Finger Sensing Pad because Finger Sensing Pad probe
- * upsets some modules of Cypress Trackpads.
- */
-       if (max_proto > PSMOUSE_IMEX &&
-                       cypress_detect(psmouse, set_properties) == 0) {
-               if (IS_ENABLED(CONFIG_MOUSE_PS2_CYPRESS)) {
-                       if (cypress_init(psmouse) == 0)
-                               return PSMOUSE_CYPRESS;
-
-                       /*
-                        * Finger Sensing Pad probe upsets some modules of
-                        * Cypress Trackpad, must avoid Finger Sensing Pad
-                        * probe if Cypress Trackpad device detected.
-                        */
-                       return PSMOUSE_PS2;
-               }
-
-               max_proto = PSMOUSE_IMEX;
-       }
-
-/*
- * Try ALPS TouchPad
- */
-       if (max_proto > PSMOUSE_IMEX) {
-               ps2_command(&psmouse->ps2dev, NULL, PSMOUSE_CMD_RESET_DIS);
-               if (psmouse_do_detect(alps_detect,
-                                     psmouse, set_properties) == 0) {
-                       if (!set_properties || alps_init(psmouse) == 0)
-                               return PSMOUSE_ALPS;
-/*
- * Init failed, try basic relative protocols
- */
-                       max_proto = PSMOUSE_IMEX;
-               }
-       }
-
-/*
- * Try OLPC HGPK touchpad.
- */
-       if (max_proto > PSMOUSE_IMEX &&
-           psmouse_do_detect(hgpk_detect, psmouse, set_properties) == 0) {
-               if (!set_properties || hgpk_init(psmouse) == 0)
-                       return PSMOUSE_HGPK;
-/*
- * Init failed, try basic relative protocols
- */
-               max_proto = PSMOUSE_IMEX;
-       }
-
-/*
- * Try Elantech touchpad.
- */
-       if (max_proto > PSMOUSE_IMEX &&
-           psmouse_do_detect(elantech_detect, psmouse, set_properties) == 0) {
-               if (!set_properties || elantech_init(psmouse) == 0)
-                       return PSMOUSE_ELANTECH;
-/*
- * Init failed, try basic relative protocols
- */
-               max_proto = PSMOUSE_IMEX;
-       }
-
-       if (max_proto > PSMOUSE_IMEX) {
-               if (psmouse_do_detect(genius_detect,
-                                     psmouse, set_properties) == 0)
-                       return PSMOUSE_GENPS;
-
-               if (psmouse_do_detect(ps2pp_init,
-                                     psmouse, set_properties) == 0)
-                       return PSMOUSE_PS2PP;
-
-               if (psmouse_do_detect(trackpoint_detect,
-                                     psmouse, set_properties) == 0)
-                       return PSMOUSE_TRACKPOINT;
-
-               if (psmouse_do_detect(touchkit_ps2_detect,
-                                     psmouse, set_properties) == 0)
-                       return PSMOUSE_TOUCHKIT_PS2;
-       }
-
-/*
- * Try Finger Sensing Pad. We do it here because its probe upsets
- * Trackpoint devices (causing TP_READ_ID command to time out).
- */
-       if (max_proto > PSMOUSE_IMEX) {
-               if (psmouse_do_detect(fsp_detect,
-                                     psmouse, set_properties) == 0) {
-                       if (!set_properties || fsp_init(psmouse) == 0)
-                               return PSMOUSE_FSP;
-/*
- * Init failed, try basic relative protocols
- */
-                       max_proto = PSMOUSE_IMEX;
-               }
-       }
-
-/*
- * Reset to defaults in case the device got confused by extended
- * protocol probes. Note that we follow up with full reset because
- * some mice put themselves to sleep when they see PSMOUSE_RESET_DIS.
- */
-       ps2_command(&psmouse->ps2dev, NULL, PSMOUSE_CMD_RESET_DIS);
-       psmouse_reset(psmouse);
-
-       if (max_proto >= PSMOUSE_IMEX &&
-           psmouse_do_detect(im_explorer_detect,
-                             psmouse, set_properties) == 0) {
-               return PSMOUSE_IMEX;
-       }
-
-       if (max_proto >= PSMOUSE_IMPS &&
-           psmouse_do_detect(intellimouse_detect,
-                             psmouse, set_properties) == 0) {
-               return PSMOUSE_IMPS;
-       }
-
-/*
- * Okay, all failed, we have a standard mouse here. The number of the buttons
- * is still a question, though. We assume 3.
- */
-       psmouse_do_detect(ps2bare_detect, psmouse, set_properties);
-
-       if (synaptics_hardware) {
-/*
- * We detected Synaptics hardware but it did not respond to IMPS/2 probes.
- * We need to reset the touchpad because if there is a track point on the
- * pass through port it could get disabled while probing for protocol
- * extensions.
- */
-               psmouse_reset(psmouse);
-       }
-
-       return PSMOUSE_PS2;
-}
-
 static const struct psmouse_protocol psmouse_protocols[] = {
        {
                .type           = PSMOUSE_PS2,
@@ -985,13 +693,14 @@ static const struct psmouse_protocol psmouse_protocols[] = {
                .maxproto       = true,
                .ignore_parity  = true,
                .detect         = ps2bare_detect,
+               .try_passthru   = true,
        },
 #ifdef CONFIG_MOUSE_PS2_LOGIPS2PP
        {
                .type           = PSMOUSE_PS2PP,
                .name           = "PS2++",
                .alias          = "logitech",
-               .detect         = ps2pp_init,
+               .detect         = ps2pp_detect,
        },
 #endif
        {
@@ -1022,6 +731,7 @@ static const struct psmouse_protocol psmouse_protocols[] = {
                .maxproto       = true,
                .ignore_parity  = true,
                .detect         = intellimouse_detect,
+               .try_passthru   = true,
        },
        {
                .type           = PSMOUSE_IMEX,
@@ -1030,6 +740,7 @@ static const struct psmouse_protocol psmouse_protocols[] = {
                .maxproto       = true,
                .ignore_parity  = true,
                .detect         = im_explorer_detect,
+               .try_passthru   = true,
        },
 #ifdef CONFIG_MOUSE_PS2_SYNAPTICS
        {
@@ -1061,6 +772,7 @@ static const struct psmouse_protocol psmouse_protocols[] = {
                .type           = PSMOUSE_LIFEBOOK,
                .name           = "LBPS/2",
                .alias          = "lifebook",
+               .detect         = lifebook_detect,
                .init           = lifebook_init,
        },
 #endif
@@ -1070,6 +782,7 @@ static const struct psmouse_protocol psmouse_protocols[] = {
                .name           = "TPPS/2",
                .alias          = "trackpoint",
                .detect         = trackpoint_detect,
+               .try_passthru   = true,
        },
 #endif
 #ifdef CONFIG_MOUSE_PS2_TOUCHKIT
@@ -1129,6 +842,15 @@ static const struct psmouse_protocol psmouse_protocols[] = {
                .detect         = vmmouse_detect,
                .init           = vmmouse_init,
        },
+#endif
+#ifdef CONFIG_MOUSE_PS2_BYD
+       {
+               .type           = PSMOUSE_BYD,
+               .name           = "BydPS/2",
+               .alias          = "byd",
+               .detect         = byd_detect,
+               .init           = byd_init,
+       },
 #endif
        {
                .type           = PSMOUSE_AUTO,
@@ -1138,7 +860,7 @@ static const struct psmouse_protocol psmouse_protocols[] = {
        },
 };
 
-static const struct psmouse_protocol *psmouse_protocol_by_type(enum psmouse_type type)
+static const struct psmouse_protocol *__psmouse_protocol_by_type(enum psmouse_type type)
 {
        int i;
 
@@ -1146,6 +868,17 @@ static const struct psmouse_protocol *psmouse_protocol_by_type(enum psmouse_type
                if (psmouse_protocols[i].type == type)
                        return &psmouse_protocols[i];
 
+       return NULL;
+}
+
+static const struct psmouse_protocol *psmouse_protocol_by_type(enum psmouse_type type)
+{
+       const struct psmouse_protocol *proto;
+
+       proto = __psmouse_protocol_by_type(type);
+       if (proto)
+               return proto;
+
        WARN_ON(1);
        return &psmouse_protocols[0];
 }
@@ -1166,23 +899,292 @@ static const struct psmouse_protocol *psmouse_protocol_by_name(const char *name,
        return NULL;
 }
 
+/*
+ * Apply default settings to the psmouse structure. Most of them will
+ * be overridden by individual protocol initialization routines.
+ */
+static void psmouse_apply_defaults(struct psmouse *psmouse)
+{
+       struct input_dev *input_dev = psmouse->dev;
+
+       memset(input_dev->evbit, 0, sizeof(input_dev->evbit));
+       memset(input_dev->keybit, 0, sizeof(input_dev->keybit));
+       memset(input_dev->relbit, 0, sizeof(input_dev->relbit));
+       memset(input_dev->absbit, 0, sizeof(input_dev->absbit));
+       memset(input_dev->mscbit, 0, sizeof(input_dev->mscbit));
+
+       __set_bit(EV_KEY, input_dev->evbit);
+       __set_bit(EV_REL, input_dev->evbit);
+
+       __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);
+
+       __set_bit(INPUT_PROP_POINTER, input_dev->propbit);
+
+       psmouse->set_rate = psmouse_set_rate;
+       psmouse->set_resolution = psmouse_set_resolution;
+       psmouse->set_scale = psmouse_set_scale;
+       psmouse->poll = psmouse_poll;
+       psmouse->protocol_handler = psmouse_process_byte;
+       psmouse->pktsize = 3;
+       psmouse->reconnect = NULL;
+       psmouse->disconnect = NULL;
+       psmouse->cleanup = NULL;
+       psmouse->pt_activate = NULL;
+       psmouse->pt_deactivate = NULL;
+}
+
+static bool psmouse_try_protocol(struct psmouse *psmouse,
+                                enum psmouse_type type,
+                                unsigned int *max_proto,
+                                bool set_properties, bool init_allowed)
+{
+       const struct psmouse_protocol *proto;
+
+       proto = __psmouse_protocol_by_type(type);
+       if (!proto)
+               return false;
+
+       if (psmouse->ps2dev.serio->id.type == SERIO_PS_PSTHRU &&
+           !proto->try_passthru) {
+               return false;
+       }
+
+       if (set_properties)
+               psmouse_apply_defaults(psmouse);
+
+       if (proto->detect(psmouse, set_properties) != 0)
+               return false;
+
+       if (set_properties && proto->init && init_allowed) {
+               if (proto->init(psmouse) != 0) {
+                       /*
+                        * We detected device, but init failed. Adjust
+                        * max_proto so we only try standard protocols.
+                        */
+                       if (*max_proto > PSMOUSE_IMEX)
+                               *max_proto = PSMOUSE_IMEX;
+
+                       return false;
+               }
+       }
+
+       return true;
+}
 
 /*
- * psmouse_probe() probes for a PS/2 mouse.
+ * psmouse_extensions() probes for any extensions to the basic PS/2 protocol
+ * the mouse may have.
  */
+static int psmouse_extensions(struct psmouse *psmouse,
+                             unsigned int max_proto, bool set_properties)
+{
+       bool synaptics_hardware = false;
+
+       /*
+        * Always check for focaltech, this is safe as it uses pnp-id
+        * matching.
+        */
+       if (psmouse_try_protocol(psmouse, PSMOUSE_FOCALTECH,
+                                &max_proto, set_properties, false)) {
+               if (max_proto > PSMOUSE_IMEX &&
+                   IS_ENABLED(CONFIG_MOUSE_PS2_FOCALTECH) &&
+                   (!set_properties || focaltech_init(psmouse) == 0)) {
+                       return PSMOUSE_FOCALTECH;
+               }
+               /*
+                * Restrict psmouse_max_proto so that psmouse_initialize()
+                * does not try to reset rate and resolution, because even
+                * that upsets the device.
+                * This also causes us to basically fall through to basic
+                * protocol detection, where we fully reset the mouse,
+                * and set it up as bare PS/2 protocol device.
+                */
+               psmouse_max_proto = max_proto = PSMOUSE_PS2;
+       }
+
+       /*
+        * We always check for LifeBook because it does not disturb mouse
+        * (it only checks DMI information).
+        */
+       if (psmouse_try_protocol(psmouse, PSMOUSE_LIFEBOOK, &max_proto,
+                                set_properties, max_proto > PSMOUSE_IMEX))
+               return PSMOUSE_LIFEBOOK;
+
+       if (psmouse_try_protocol(psmouse, PSMOUSE_VMMOUSE, &max_proto,
+                                set_properties, max_proto > PSMOUSE_IMEX))
+               return PSMOUSE_VMMOUSE;
+
+       /*
+        * Try Kensington ThinkingMouse (we try first, because Synaptics
+        * probe upsets the ThinkingMouse).
+        */
+       if (max_proto > PSMOUSE_IMEX &&
+           psmouse_try_protocol(psmouse, PSMOUSE_THINKPS, &max_proto,
+                                set_properties, true)) {
+               return PSMOUSE_THINKPS;
+       }
+
+       /*
+        * Try Synaptics TouchPad. Note that probing is done even if
+        * Synaptics protocol support is disabled in config - we need to
+        * know if it is Synaptics so we can reset it properly after
+        * probing for IntelliMouse.
+        */
+       if (max_proto > PSMOUSE_PS2 &&
+           psmouse_try_protocol(psmouse, PSMOUSE_SYNAPTICS, &max_proto,
+                                set_properties, false)) {
+               synaptics_hardware = true;
 
+               if (max_proto > PSMOUSE_IMEX) {
+                       /*
+                        * Try activating protocol, but check if support is
+                        * enabled first, since we try detecting Synaptics
+                        * even when protocol is disabled.
+                        */
+                       if (IS_ENABLED(CONFIG_MOUSE_PS2_SYNAPTICS) &&
+                           (!set_properties || synaptics_init(psmouse) == 0)) {
+                               return PSMOUSE_SYNAPTICS;
+                       }
+
+                       /*
+                        * Some Synaptics touchpads can emulate extended
+                        * protocols (like IMPS/2).  Unfortunately
+                        * Logitech/Genius probes confuse some firmware
+                        * versions so we'll have to skip them.
+                        */
+                       max_proto = PSMOUSE_IMEX;
+               }
+
+               /*
+                * Make sure that touchpad is in relative mode, gestures
+                * (taps) are enabled.
+                */
+               synaptics_reset(psmouse);
+       }
+
+       /*
+        * Try Cypress Trackpad. We must try it before Finger Sensing Pad
+        * because Finger Sensing Pad probe upsets some modules of Cypress
+        * Trackpads.
+        */
+       if (max_proto > PSMOUSE_IMEX &&
+           psmouse_try_protocol(psmouse, PSMOUSE_CYPRESS, &max_proto,
+                                set_properties, true)) {
+               return PSMOUSE_CYPRESS;
+       }
+
+       /* Try ALPS TouchPad */
+       if (max_proto > PSMOUSE_IMEX) {
+               ps2_command(&psmouse->ps2dev, NULL, PSMOUSE_CMD_RESET_DIS);
+               if (psmouse_try_protocol(psmouse, PSMOUSE_ALPS,
+                                        &max_proto, set_properties, true))
+                       return PSMOUSE_ALPS;
+       }
+
+       /* Try OLPC HGPK touchpad */
+       if (max_proto > PSMOUSE_IMEX &&
+           psmouse_try_protocol(psmouse, PSMOUSE_HGPK, &max_proto,
+                                set_properties, true)) {
+               return PSMOUSE_HGPK;
+       }
+
+       /* Try Elantech touchpad */
+       if (max_proto > PSMOUSE_IMEX &&
+           psmouse_try_protocol(psmouse, PSMOUSE_ELANTECH,
+                                &max_proto, set_properties, true)) {
+               return PSMOUSE_ELANTECH;
+       }
+
+       if (max_proto > PSMOUSE_IMEX) {
+               if (psmouse_try_protocol(psmouse, PSMOUSE_GENPS,
+                                        &max_proto, set_properties, true))
+                       return PSMOUSE_GENPS;
+
+               if (psmouse_try_protocol(psmouse, PSMOUSE_PS2PP,
+                                        &max_proto, set_properties, true))
+                       return PSMOUSE_PS2PP;
+
+               if (psmouse_try_protocol(psmouse, PSMOUSE_TRACKPOINT,
+                                        &max_proto, set_properties, true))
+                       return PSMOUSE_TRACKPOINT;
+
+               if (psmouse_try_protocol(psmouse, PSMOUSE_TOUCHKIT_PS2,
+                                        &max_proto, set_properties, true))
+                       return PSMOUSE_TOUCHKIT_PS2;
+
+               if (psmouse_try_protocol(psmouse, PSMOUSE_BYD,
+                                        &max_proto, set_properties, true))
+                       return PSMOUSE_BYD;
+       }
+
+       /*
+        * Try Finger Sensing Pad. We do it here because its probe upsets
+        * Trackpoint devices (causing TP_READ_ID command to time out).
+        */
+       if (max_proto > PSMOUSE_IMEX &&
+           psmouse_try_protocol(psmouse, PSMOUSE_FSP,
+                                &max_proto, set_properties, true)) {
+               return PSMOUSE_FSP;
+       }
+
+       /*
+        * Reset to defaults in case the device got confused by extended
+        * protocol probes. Note that we follow up with full reset because
+        * some mice put themselves to sleep when they see PSMOUSE_RESET_DIS.
+        */
+       ps2_command(&psmouse->ps2dev, NULL, PSMOUSE_CMD_RESET_DIS);
+       psmouse_reset(psmouse);
+
+       if (max_proto >= PSMOUSE_IMEX &&
+           psmouse_try_protocol(psmouse, PSMOUSE_IMEX,
+                                &max_proto, set_properties, true)) {
+               return PSMOUSE_IMEX;
+       }
+
+       if (max_proto >= PSMOUSE_IMPS &&
+           psmouse_try_protocol(psmouse, PSMOUSE_IMPS,
+                                &max_proto, set_properties, true)) {
+               return PSMOUSE_IMPS;
+       }
+
+       /*
+        * Okay, all failed, we have a standard mouse here. The number of
+        * the buttons is still a question, though. We assume 3.
+        */
+       psmouse_try_protocol(psmouse, PSMOUSE_PS2,
+                            &max_proto, set_properties, true);
+
+       if (synaptics_hardware) {
+               /*
+                * We detected Synaptics hardware but it did not respond to
+                * IMPS/2 probes.  We need to reset the touchpad because if
+                * there is a track point on the pass through port it could
+                * get disabled while probing for protocol extensions.
+                */
+               psmouse_reset(psmouse);
+       }
+
+       return PSMOUSE_PS2;
+}
+
+/*
+ * psmouse_probe() probes for a PS/2 mouse.
+ */
 static int psmouse_probe(struct psmouse *psmouse)
 {
        struct ps2dev *ps2dev = &psmouse->ps2dev;
        unsigned char param[2];
 
-/*
- * First, we check if it's a mouse. It should send 0x00 or 0x03
- * in case of an IntelliMouse in 4-byte mode or 0x04 for IM Explorer.
- * Sunrex K8561 IR Keyboard/Mouse reports 0xff on second and subsequent
- * ID queries, probably due to a firmware bug.
- */
-
+       /*
+        * First, we check if it's a mouse. It should send 0x00 or 0x03 in
+        * case of an IntelliMouse in 4-byte mode or 0x04 for IM Explorer.
+        * Sunrex K8561 IR Keyboard/Mouse reports 0xff on second and
+        * subsequent ID queries, probably due to a firmware bug.
+        */
        param[0] = 0xa5;
        if (ps2_command(ps2dev, param, PSMOUSE_CMD_GETID))
                return -1;
@@ -1191,10 +1193,10 @@ static int psmouse_probe(struct psmouse *psmouse)
            param[0] != 0x04 && param[0] != 0xff)
                return -1;
 
-/*
- * Then we reset and disable the mouse so that it doesn't generate events.
- */
-
+       /*
+        * Then we reset and disable the mouse so that it doesn't generate
+        * events.
+        */
        if (ps2_command(ps2dev, NULL, PSMOUSE_CMD_RESET_DIS))
                psmouse_warn(psmouse, "Failed to reset mouse on %s\n",
                             ps2dev->serio->phys);
@@ -1205,13 +1207,11 @@ static int psmouse_probe(struct psmouse *psmouse)
 /*
  * psmouse_initialize() initializes the mouse to a sane state.
  */
-
 static void psmouse_initialize(struct psmouse *psmouse)
 {
-/*
- * We set the mouse report rate, resolution and scaling.
- */
-
+       /*
+        * We set the mouse report rate, resolution and scaling.
+        */
        if (psmouse_max_proto != PSMOUSE_PS2) {
                psmouse->set_rate(psmouse, psmouse->rate);
                psmouse->set_resolution(psmouse, psmouse->resolution);
@@ -1222,7 +1222,6 @@ static void psmouse_initialize(struct psmouse *psmouse)
 /*
  * psmouse_activate() enables the mouse so that we get motion reports from it.
  */
-
 int psmouse_activate(struct psmouse *psmouse)
 {
        if (ps2_command(&psmouse->ps2dev, NULL, PSMOUSE_CMD_ENABLE)) {
@@ -1236,10 +1235,9 @@ int psmouse_activate(struct psmouse *psmouse)
 }
 
 /*
- * psmouse_deactivate() puts the mouse into poll mode so that we don't get motion
- * reports from it unless we explicitly request it.
+ * psmouse_deactivate() puts the mouse into poll mode so that we don't get
+ * motion reports from it unless we explicitly request it.
  */
-
 int psmouse_deactivate(struct psmouse *psmouse)
 {
        if (ps2_command(&psmouse->ps2dev, NULL, PSMOUSE_CMD_DISABLE)) {
@@ -1252,11 +1250,9 @@ int psmouse_deactivate(struct psmouse *psmouse)
        return 0;
 }
 
-
 /*
  * psmouse_resync() attempts to re-validate current protocol.
  */
-
 static void psmouse_resync(struct work_struct *work)
 {
        struct psmouse *parent = NULL, *psmouse =
@@ -1276,16 +1272,16 @@ static void psmouse_resync(struct work_struct *work)
                psmouse_deactivate(parent);
        }
 
-/*
- * Some mice don't ACK commands sent while they are in the middle of
- * transmitting motion packet. To avoid delay we use ps2_sendbyte()
- * instead of ps2_command() which would wait for 200ms for an ACK
- * that may never come.
- * As an additional quirk ALPS touchpads may not only forget to ACK
- * disable command but will stop reporting taps, so if we see that
- * mouse at least once ACKs disable we will do full reconnect if ACK
- * is missing.
- */
+       /*
       * Some mice don't ACK commands sent while they are in the middle of
       * transmitting motion packet. To avoid delay we use ps2_sendbyte()
       * instead of ps2_command() which would wait for 200ms for an ACK
       * that may never come.
       * As an additional quirk ALPS touchpads may not only forget to ACK
       * disable command but will stop reporting taps, so if we see that
       * mouse at least once ACKs disable we will do full reconnect if ACK
       * is missing.
       */
        psmouse->num_resyncs++;
 
        if (ps2_sendbyte(&psmouse->ps2dev, PSMOUSE_CMD_DISABLE, 20)) {
@@ -1294,13 +1290,13 @@ static void psmouse_resync(struct work_struct *work)
        } else
                psmouse->acks_disable_command = true;
 
-/*
- * Poll the mouse. If it was reset the packet will be shorter than
- * psmouse->pktsize and ps2_command will fail. We do not expect and
- * do not handle scenario when mouse "upgrades" its protocol while
- * disconnected since it would require additional delay. If we ever
- * see a mouse that does it we'll adjust the code.
- */
+       /*
       * Poll the mouse. If it was reset the packet will be shorter than
       * psmouse->pktsize and ps2_command will fail. We do not expect and
       * do not handle scenario when mouse "upgrades" its protocol while
       * disconnected since it would require additional delay. If we ever
       * see a mouse that does it we'll adjust the code.
       */
        if (!failed) {
                if (psmouse->poll(psmouse))
                        failed = true;
@@ -1317,11 +1313,12 @@ static void psmouse_resync(struct work_struct *work)
                        psmouse_set_state(psmouse, PSMOUSE_RESYNCING);
                }
        }
-/*
- * Now try to enable mouse. We try to do that even if poll failed and also
- * repeat our attempts 5 times, otherwise we may be left out with disabled
- * mouse.
- */
+
+       /*
+        * Now try to enable mouse. We try to do that even if poll failed
+        * and also repeat our attempts 5 times, otherwise we may be left
+        * out with disabled mouse.
+        */
        for (i = 0; i < 5; i++) {
                if (!ps2_command(&psmouse->ps2dev, NULL, PSMOUSE_CMD_ENABLE)) {
                        enabled = true;
@@ -1353,7 +1350,6 @@ static void psmouse_resync(struct work_struct *work)
 /*
  * psmouse_cleanup() resets the mouse into power-on state.
  */
-
 static void psmouse_cleanup(struct serio *serio)
 {
        struct psmouse *psmouse = serio_get_drvdata(serio);
@@ -1378,15 +1374,15 @@ static void psmouse_cleanup(struct serio *serio)
        if (psmouse->cleanup)
                psmouse->cleanup(psmouse);
 
-/*
- * Reset the mouse to defaults (bare PS/2 protocol).
- */
+       /*
       * Reset the mouse to defaults (bare PS/2 protocol).
       */
        ps2_command(&psmouse->ps2dev, NULL, PSMOUSE_CMD_RESET_DIS);
 
-/*
- * Some boxes, such as HP nx7400, get terribly confused if mouse
- * is not fully enabled before suspending/shutting down.
- */
+       /*
       * Some boxes, such as HP nx7400, get terribly confused if mouse
       * is not fully enabled before suspending/shutting down.
       */
        ps2_command(&psmouse->ps2dev, NULL, PSMOUSE_CMD_ENABLE);
 
        if (parent) {
@@ -1402,7 +1398,6 @@ static void psmouse_cleanup(struct serio *serio)
 /*
  * psmouse_disconnect() closes and frees.
  */
-
 static void psmouse_disconnect(struct serio *serio)
 {
        struct psmouse *psmouse, *parent = NULL;
@@ -1602,7 +1597,6 @@ static int psmouse_connect(struct serio *serio, struct serio_driver *drv)
        goto out;
 }
 
-
 static int psmouse_reconnect(struct serio *serio)
 {
        struct psmouse *psmouse = serio_get_drvdata(serio);
index ad5a5a1..e0ca6cd 100644 (file)
@@ -104,6 +104,7 @@ enum psmouse_type {
        PSMOUSE_CYPRESS,
        PSMOUSE_FOCALTECH,
        PSMOUSE_VMMOUSE,
+       PSMOUSE_BYD,
        PSMOUSE_AUTO            /* This one should always be last */
 };
 
index ae33da7..309951d 100644 (file)
@@ -295,6 +295,16 @@ config TOUCHSCREEN_EGALAX
          To compile this driver as a module, choose M here: the
          module will be called egalax_ts.
 
+config TOUCHSCREEN_EGALAX_SERIAL
+       tristate "EETI eGalax serial touchscreen"
+       select SERIO
+       help
+         Say Y here to enable support for serial connected EETI
+         eGalax touch panels.
+
+         To compile this driver as a module, choose M here: the
+         module will be called egalax_ts_serial.
+
 config TOUCHSCREEN_FT6236
        tristate "FT6236 I2C touchscreen"
        depends on I2C
@@ -324,6 +334,7 @@ config TOUCHSCREEN_FUJITSU
 config TOUCHSCREEN_GOODIX
        tristate "Goodix I2C touchscreen"
        depends on I2C
+       depends on GPIOLIB || COMPILE_TEST
        help
          Say Y here if you have the Goodix touchscreen (such as one
          installed in Onda v975w tablets) connected to your
@@ -480,6 +491,17 @@ config TOUCHSCREEN_MMS114
          To compile this driver as a module, choose M here: the
          module will be called mms114.
 
+config TOUCHSCREEN_MELFAS_MIP4
+       tristate "MELFAS MIP4 Touchscreen"
+       depends on I2C
+       help
+         Say Y here if you have a MELFAS MIP4 Touchscreen device.
+
+         If unsure, say N.
+
+         To compile this driver as a module, choose M here:
+         the module will be called melfas_mip4.
+
 config TOUCHSCREEN_MTOUCH
        tristate "MicroTouch serial touchscreens"
        select SERIO
@@ -927,6 +949,22 @@ config TOUCHSCREEN_TOUCHIT213
          To compile this driver as a module, choose M here: the
          module will be called touchit213.
 
+config TOUCHSCREEN_TS4800
+       tristate "TS-4800 touchscreen"
+       depends on HAS_IOMEM && OF
+       select MFD_SYSCON
+       select INPUT_POLLDEV
+       help
+         Say Y here if you have a touchscreen on a TS-4800 board.
+
+         On TS-4800, the touchscreen is not handled directly by Linux but by
+         a companion FPGA.
+
+         If unsure, say N.
+
+         To compile this driver as a module, choose M here: the
+         module will be called ts4800_ts.
+
 config TOUCHSCREEN_TSC_SERIO
        tristate "TSC-10/25/40 serial touchscreen support"
        select SERIO
@@ -1085,7 +1123,8 @@ config TOUCHSCREEN_ZFORCE
 
 config TOUCHSCREEN_COLIBRI_VF50
        tristate "Toradex Colibri on board touchscreen driver"
-       depends on GPIOLIB && IIO && VF610_ADC
+       depends on IIO && VF610_ADC
+       depends on GPIOLIB || COMPILE_TEST
        help
          Say Y here if you have a Colibri VF50 and plan to use
          the on-board provided 4-wire touchscreen driver.
index cbaa6ab..4b518c7 100644 (file)
@@ -35,6 +35,7 @@ obj-$(CONFIG_TOUCHSCREEN_EETI)                += eeti_ts.o
 obj-$(CONFIG_TOUCHSCREEN_ELAN)         += elants_i2c.o
 obj-$(CONFIG_TOUCHSCREEN_ELO)          += elo.o
 obj-$(CONFIG_TOUCHSCREEN_EGALAX)       += egalax_ts.o
+obj-$(CONFIG_TOUCHSCREEN_EGALAX_SERIAL)        += egalax_ts_serial.o
 obj-$(CONFIG_TOUCHSCREEN_FT6236)       += ft6236.o
 obj-$(CONFIG_TOUCHSCREEN_FUJITSU)      += fujitsu_ts.o
 obj-$(CONFIG_TOUCHSCREEN_GOODIX)       += goodix.o
@@ -47,6 +48,7 @@ obj-$(CONFIG_TOUCHSCREEN_LPC32XX)     += lpc32xx_ts.o
 obj-$(CONFIG_TOUCHSCREEN_MAX11801)     += max11801_ts.o
 obj-$(CONFIG_TOUCHSCREEN_MC13783)      += mc13783_ts.o
 obj-$(CONFIG_TOUCHSCREEN_MCS5000)      += mcs5000_ts.o
+obj-$(CONFIG_TOUCHSCREEN_MELFAS_MIP4)  += melfas_mip4.o
 obj-$(CONFIG_TOUCHSCREEN_MIGOR)                += migor_ts.o
 obj-$(CONFIG_TOUCHSCREEN_MMS114)       += mms114.o
 obj-$(CONFIG_TOUCHSCREEN_MTOUCH)       += mtouch.o
@@ -68,6 +70,7 @@ obj-$(CONFIG_TOUCHSCREEN_TI_AM335X_TSC)       += ti_am335x_tsc.o
 obj-$(CONFIG_TOUCHSCREEN_TOUCHIT213)   += touchit213.o
 obj-$(CONFIG_TOUCHSCREEN_TOUCHRIGHT)   += touchright.o
 obj-$(CONFIG_TOUCHSCREEN_TOUCHWIN)     += touchwin.o
+obj-$(CONFIG_TOUCHSCREEN_TS4800)       += ts4800-ts.o
 obj-$(CONFIG_TOUCHSCREEN_TSC_SERIO)    += tsc40.o
 obj-$(CONFIG_TOUCHSCREEN_TSC200X_CORE) += tsc200x-core.o
 obj-$(CONFIG_TOUCHSCREEN_TSC2004)      += tsc2004.o
index 2d5794e..2160512 100644 (file)
@@ -113,8 +113,8 @@ struct t7_config {
 #define MXT_T9_DETECT          (1 << 7)
 
 struct t9_range {
-       u16 x;
-       u16 y;
+       __le16 x;
+       __le16 y;
 } __packed;
 
 /* MXT_TOUCH_MULTI_T9 orient */
@@ -216,6 +216,7 @@ struct mxt_data {
        unsigned int irq;
        unsigned int max_x;
        unsigned int max_y;
+       bool xy_switch;
        bool in_bootloader;
        u16 mem_size;
        u8 t100_aux_ampl;
@@ -1665,8 +1666,8 @@ static int mxt_read_t9_resolution(struct mxt_data *data)
        if (error)
                return error;
 
-       le16_to_cpus(&range.x);
-       le16_to_cpus(&range.y);
+       data->max_x = get_unaligned_le16(&range.x);
+       data->max_y = get_unaligned_le16(&range.y);
 
        error =  __mxt_read_reg(client,
                                object->start_address + MXT_T9_ORIENT,
@@ -1674,23 +1675,7 @@ static int mxt_read_t9_resolution(struct mxt_data *data)
        if (error)
                return error;
 
-       /* Handle default values */
-       if (range.x == 0)
-               range.x = 1023;
-
-       if (range.y == 0)
-               range.y = 1023;
-
-       if (orient & MXT_T9_ORIENT_SWITCH) {
-               data->max_x = range.y;
-               data->max_y = range.x;
-       } else {
-               data->max_x = range.x;
-               data->max_y = range.y;
-       }
-
-       dev_dbg(&client->dev,
-               "Touchscreen size X%uY%u\n", data->max_x, data->max_y);
+       data->xy_switch = orient & MXT_T9_ORIENT_SWITCH;
 
        return 0;
 }
@@ -1708,13 +1693,14 @@ static int mxt_read_t100_config(struct mxt_data *data)
        if (!object)
                return -EINVAL;
 
+       /* read touchscreen dimensions */
        error = __mxt_read_reg(client,
                               object->start_address + MXT_T100_XRANGE,
                               sizeof(range_x), &range_x);
        if (error)
                return error;
 
-       le16_to_cpus(&range_x);
+       data->max_x = get_unaligned_le16(&range_x);
 
        error = __mxt_read_reg(client,
                               object->start_address + MXT_T100_YRANGE,
@@ -1722,36 +1708,24 @@ static int mxt_read_t100_config(struct mxt_data *data)
        if (error)
                return error;
 
-       le16_to_cpus(&range_y);
+       data->max_y = get_unaligned_le16(&range_y);
 
+       /* read orientation config */
        error =  __mxt_read_reg(client,
                                object->start_address + MXT_T100_CFG1,
                                1, &cfg);
        if (error)
                return error;
 
+       data->xy_switch = cfg & MXT_T100_CFG_SWITCHXY;
+
+       /* allocate aux bytes */
        error =  __mxt_read_reg(client,
                                object->start_address + MXT_T100_TCHAUX,
                                1, &tchaux);
        if (error)
                return error;
 
-       /* Handle default values */
-       if (range_x == 0)
-               range_x = 1023;
-
-       if (range_y == 0)
-               range_y = 1023;
-
-       if (cfg & MXT_T100_CFG_SWITCHXY) {
-               data->max_x = range_y;
-               data->max_y = range_x;
-       } else {
-               data->max_x = range_x;
-               data->max_y = range_y;
-       }
-
-       /* allocate aux bytes */
        aux = 6;
 
        if (tchaux & MXT_T100_TCHAUX_VECT)
@@ -1767,9 +1741,6 @@ static int mxt_read_t100_config(struct mxt_data *data)
                "T100 aux mappings vect:%u ampl:%u area:%u\n",
                data->t100_aux_vect, data->t100_aux_ampl, data->t100_aux_area);
 
-       dev_info(&client->dev,
-                "T100 Touchscreen size X%uY%u\n", data->max_x, data->max_y);
-
        return 0;
 }
 
@@ -1828,6 +1799,19 @@ static int mxt_initialize_input_device(struct mxt_data *data)
                return -EINVAL;
        }
 
+       /* Handle default values and orientation switch */
+       if (data->max_x == 0)
+               data->max_x = 1023;
+
+       if (data->max_y == 0)
+               data->max_y = 1023;
+
+       if (data->xy_switch)
+               swap(data->max_x, data->max_y);
+
+       dev_info(dev, "Touchscreen size X%uY%u\n", data->max_x, data->max_y);
+
+       /* Register input device */
        input_dev = input_allocate_device();
        if (!input_dev) {
                dev_err(dev, "Failed to allocate memory\n");
index 5b74e8b..91cda8f 100644 (file)
 #include <linux/delay.h>
 #include <linux/input.h>
 #include <linux/input/mt.h>
+#include <linux/input/touchscreen.h>
 #include <linux/gpio.h>
 #include <linux/interrupt.h>
 #include <linux/slab.h>
+#include <linux/property.h>
+#include <linux/gpio/consumer.h>
 
 #include "cyttsp_core.h"
 
@@ -57,6 +60,7 @@
 #define CY_DELAY_DFLT                  20 /* ms */
 #define CY_DELAY_MAX                   500
 #define CY_ACT_DIST_DFLT               0xF8
+#define CY_ACT_DIST_MASK               0x0F
 #define CY_HNDSHK_BIT                  0x80
 /* device mode bits */
 #define CY_OPERATE_MODE                        0x00
@@ -120,7 +124,7 @@ static int ttsp_send_command(struct cyttsp *ts, u8 cmd)
 
 static int cyttsp_handshake(struct cyttsp *ts)
 {
-       if (ts->pdata->use_hndshk)
+       if (ts->use_hndshk)
                return ttsp_send_command(ts,
                                ts->xy_data.hst_mode ^ CY_HNDSHK_BIT);
 
@@ -142,9 +146,9 @@ static int cyttsp_exit_bl_mode(struct cyttsp *ts)
        u8 bl_cmd[sizeof(bl_command)];
 
        memcpy(bl_cmd, bl_command, sizeof(bl_command));
-       if (ts->pdata->bl_keys)
+       if (ts->bl_keys)
                memcpy(&bl_cmd[sizeof(bl_command) - CY_NUM_BL_KEYS],
-                       ts->pdata->bl_keys, CY_NUM_BL_KEYS);
+                       ts->bl_keys, CY_NUM_BL_KEYS);
 
        error = ttsp_write_block_data(ts, CY_REG_BASE,
                                      sizeof(bl_cmd), bl_cmd);
@@ -217,14 +221,14 @@ static int cyttsp_set_sysinfo_regs(struct cyttsp *ts)
 {
        int retval = 0;
 
-       if (ts->pdata->act_intrvl != CY_ACT_INTRVL_DFLT ||
-           ts->pdata->tch_tmout != CY_TCH_TMOUT_DFLT ||
-           ts->pdata->lp_intrvl != CY_LP_INTRVL_DFLT) {
+       if (ts->act_intrvl != CY_ACT_INTRVL_DFLT ||
+           ts->tch_tmout != CY_TCH_TMOUT_DFLT ||
+           ts->lp_intrvl != CY_LP_INTRVL_DFLT) {
 
                u8 intrvl_ray[] = {
-                       ts->pdata->act_intrvl,
-                       ts->pdata->tch_tmout,
-                       ts->pdata->lp_intrvl
+                       ts->act_intrvl,
+                       ts->tch_tmout,
+                       ts->lp_intrvl
                };
 
                /* set intrvl registers */
@@ -236,6 +240,16 @@ static int cyttsp_set_sysinfo_regs(struct cyttsp *ts)
        return retval;
 }
 
+static void cyttsp_hard_reset(struct cyttsp *ts)
+{
+       if (ts->reset_gpio) {
+               gpiod_set_value_cansleep(ts->reset_gpio, 1);
+               msleep(CY_DELAY_DFLT);
+               gpiod_set_value_cansleep(ts->reset_gpio, 0);
+               msleep(CY_DELAY_DFLT);
+       }
+}
+
 static int cyttsp_soft_reset(struct cyttsp *ts)
 {
        unsigned long timeout;
@@ -263,7 +277,7 @@ out:
 
 static int cyttsp_act_dist_setup(struct cyttsp *ts)
 {
-       u8 act_dist_setup = ts->pdata->act_dist;
+       u8 act_dist_setup = ts->act_dist;
 
        /* Init gesture; active distance setup */
        return ttsp_write_block_data(ts, CY_REG_ACT_DIST,
@@ -528,45 +542,110 @@ static void cyttsp_close(struct input_dev *dev)
                cyttsp_disable(ts);
 }
 
+static int cyttsp_parse_properties(struct cyttsp *ts)
+{
+       struct device *dev = ts->dev;
+       u32 dt_value;
+       int ret;
+
+       ts->bl_keys = devm_kzalloc(dev, CY_NUM_BL_KEYS, GFP_KERNEL);
+       if (!ts->bl_keys)
+               return -ENOMEM;
+
+       /* Set some default values */
+       ts->use_hndshk = false;
+       ts->act_dist = CY_ACT_DIST_DFLT;
+       ts->act_intrvl = CY_ACT_INTRVL_DFLT;
+       ts->tch_tmout = CY_TCH_TMOUT_DFLT;
+       ts->lp_intrvl = CY_LP_INTRVL_DFLT;
+
+       ret = device_property_read_u8_array(dev, "bootloader-key",
+                                           ts->bl_keys, CY_NUM_BL_KEYS);
+       if (ret) {
+               dev_err(dev,
+                       "bootloader-key property could not be retrieved\n");
+               return ret;
+       }
+
+       ts->use_hndshk = device_property_present(dev, "use-handshake");
+
+       if (!device_property_read_u32(dev, "active-distance", &dt_value)) {
+               if (dt_value > 15) {
+                       dev_err(dev, "active-distance (%u) must be [0-15]\n",
+                               dt_value);
+                       return -EINVAL;
+               }
+               ts->act_dist &= ~CY_ACT_DIST_MASK;
+               ts->act_dist |= dt_value;
+       }
+
+       if (!device_property_read_u32(dev, "active-interval-ms", &dt_value)) {
+               if (dt_value > 255) {
+                       dev_err(dev, "active-interval-ms (%u) must be [0-255]\n",
+                               dt_value);
+                       return -EINVAL;
+               }
+               ts->act_intrvl = dt_value;
+       }
+
+       if (!device_property_read_u32(dev, "lowpower-interval-ms", &dt_value)) {
+               if (dt_value > 2550) {
+                       dev_err(dev, "lowpower-interval-ms (%u) must be [0-2550]\n",
+                               dt_value);
+                       return -EINVAL;
+               }
+               /* Register value is expressed in 0.01s / bit */
+               ts->lp_intrvl = dt_value / 10;
+       }
+
+       if (!device_property_read_u32(dev, "touch-timeout-ms", &dt_value)) {
+               if (dt_value > 2550) {
+                       dev_err(dev, "touch-timeout-ms (%u) must be [0-2550]\n",
+                               dt_value);
+                       return -EINVAL;
+               }
+               /* Register value is expressed in 0.01s / bit */
+               ts->tch_tmout = dt_value / 10;
+       }
+
+       return 0;
+}
+
 struct cyttsp *cyttsp_probe(const struct cyttsp_bus_ops *bus_ops,
                            struct device *dev, int irq, size_t xfer_buf_size)
 {
-       const struct cyttsp_platform_data *pdata = dev_get_platdata(dev);
        struct cyttsp *ts;
        struct input_dev *input_dev;
        int error;
 
-       if (!pdata || !pdata->name || irq <= 0) {
-               error = -EINVAL;
-               goto err_out;
-       }
+       ts = devm_kzalloc(dev, sizeof(*ts) + xfer_buf_size, GFP_KERNEL);
+       if (!ts)
+               return ERR_PTR(-ENOMEM);
 
-       ts = kzalloc(sizeof(*ts) + xfer_buf_size, GFP_KERNEL);
-       input_dev = input_allocate_device();
-       if (!ts || !input_dev) {
-               error = -ENOMEM;
-               goto err_free_mem;
-       }
+       input_dev = devm_input_allocate_device(dev);
+       if (!input_dev)
+               return ERR_PTR(-ENOMEM);
 
        ts->dev = dev;
        ts->input = input_dev;
-       ts->pdata = dev_get_platdata(dev);
        ts->bus_ops = bus_ops;
        ts->irq = irq;
 
+       ts->reset_gpio = devm_gpiod_get_optional(dev, "reset", GPIOD_OUT_LOW);
+       if (IS_ERR(ts->reset_gpio)) {
+               error = PTR_ERR(ts->reset_gpio);
+               dev_err(dev, "Failed to request reset gpio, error %d\n", error);
+               return ERR_PTR(error);
+       }
+
+       error = cyttsp_parse_properties(ts);
+       if (error)
+               return ERR_PTR(error);
+
        init_completion(&ts->bl_ready);
        snprintf(ts->phys, sizeof(ts->phys), "%s/input0", dev_name(dev));
 
-       if (pdata->init) {
-               error = pdata->init();
-               if (error) {
-                       dev_err(ts->dev, "platform init failed, err: %d\n",
-                               error);
-                       goto err_free_mem;
-               }
-       }
-
-       input_dev->name = pdata->name;
+       input_dev->name = "Cypress TTSP TouchScreen";
        input_dev->phys = ts->phys;
        input_dev->id.bustype = bus_ops->bustype;
        input_dev->dev.parent = ts->dev;
@@ -576,63 +655,44 @@ struct cyttsp *cyttsp_probe(const struct cyttsp_bus_ops *bus_ops,
 
        input_set_drvdata(input_dev, ts);
 
-       __set_bit(EV_ABS, input_dev->evbit);
-       input_set_abs_params(input_dev, ABS_MT_POSITION_X,
-                            0, pdata->maxx, 0, 0);
-       input_set_abs_params(input_dev, ABS_MT_POSITION_Y,
-                            0, pdata->maxy, 0, 0);
-       input_set_abs_params(input_dev, ABS_MT_TOUCH_MAJOR,
-                            0, CY_MAXZ, 0, 0);
+       input_set_capability(input_dev, EV_ABS, ABS_MT_POSITION_X);
+       input_set_capability(input_dev, EV_ABS, ABS_MT_POSITION_Y);
+       touchscreen_parse_properties(input_dev, true);
 
-       input_mt_init_slots(input_dev, CY_MAX_ID, 0);
+       error = input_mt_init_slots(input_dev, CY_MAX_ID, 0);
+       if (error) {
+               dev_err(dev, "Unable to init MT slots.\n");
+               return ERR_PTR(error);
+       }
 
-       error = request_threaded_irq(ts->irq, NULL, cyttsp_irq,
-                                    IRQF_TRIGGER_FALLING | IRQF_ONESHOT,
-                                    pdata->name, ts);
+       error = devm_request_threaded_irq(dev, ts->irq, NULL, cyttsp_irq,
+                                         IRQF_TRIGGER_FALLING | IRQF_ONESHOT,
+                                         "cyttsp", ts);
        if (error) {
                dev_err(ts->dev, "failed to request IRQ %d, err: %d\n",
                        ts->irq, error);
-               goto err_platform_exit;
+               return ERR_PTR(error);
        }
 
        disable_irq(ts->irq);
 
+       cyttsp_hard_reset(ts);
+
        error = cyttsp_power_on(ts);
        if (error)
-               goto err_free_irq;
+               return ERR_PTR(error);
 
        error = input_register_device(input_dev);
        if (error) {
                dev_err(ts->dev, "failed to register input device: %d\n",
                        error);
-               goto err_free_irq;
+               return ERR_PTR(error);
        }
 
        return ts;
-
-err_free_irq:
-       free_irq(ts->irq, ts);
-err_platform_exit:
-       if (pdata->exit)
-               pdata->exit();
-err_free_mem:
-       input_free_device(input_dev);
-       kfree(ts);
-err_out:
-       return ERR_PTR(error);
 }
 EXPORT_SYMBOL_GPL(cyttsp_probe);
 
-void cyttsp_remove(struct cyttsp *ts)
-{
-       free_irq(ts->irq, ts);
-       input_unregister_device(ts->input);
-       if (ts->pdata->exit)
-               ts->pdata->exit();
-       kfree(ts);
-}
-EXPORT_SYMBOL_GPL(cyttsp_remove);
-
 MODULE_LICENSE("GPL");
 MODULE_DESCRIPTION("Cypress TrueTouch(R) Standard touchscreen driver core");
 MODULE_AUTHOR("Cypress");
index 0707411..7835e2b 100644 (file)
@@ -129,7 +129,6 @@ struct cyttsp {
        int irq;
        struct input_dev *input;
        char phys[32];
-       const struct cyttsp_platform_data *pdata;
        const struct cyttsp_bus_ops *bus_ops;
        struct cyttsp_bootloader_data bl_data;
        struct cyttsp_sysinfo_data sysinfo_data;
@@ -138,12 +137,19 @@ struct cyttsp {
        enum cyttsp_state state;
        bool suspended;
 
+       struct gpio_desc *reset_gpio;
+       bool use_hndshk;
+       u8 act_dist;
+       u8 act_intrvl;
+       u8 tch_tmout;
+       u8 lp_intrvl;
+       u8 *bl_keys;
+
        u8 xfer_buf[] ____cacheline_aligned;
 };
 
 struct cyttsp *cyttsp_probe(const struct cyttsp_bus_ops *bus_ops,
                            struct device *dev, int irq, size_t xfer_buf_size);
-void cyttsp_remove(struct cyttsp *ts);
 
 int cyttsp_i2c_write_block_data(struct device *dev, u8 *xfer_buf, u16 addr,
                u8 length, const void *values);
index eee51b3..1edfdba 100644 (file)
@@ -56,15 +56,6 @@ static int cyttsp_i2c_probe(struct i2c_client *client,
        return 0;
 }
 
-static int cyttsp_i2c_remove(struct i2c_client *client)
-{
-       struct cyttsp *ts = i2c_get_clientdata(client);
-
-       cyttsp_remove(ts);
-
-       return 0;
-}
-
 static const struct i2c_device_id cyttsp_i2c_id[] = {
        { CY_I2C_NAME, 0 },
        { }
@@ -77,7 +68,6 @@ static struct i2c_driver cyttsp_i2c_driver = {
                .pm     = &cyttsp_pm_ops,
        },
        .probe          = cyttsp_i2c_probe,
-       .remove         = cyttsp_i2c_remove,
        .id_table       = cyttsp_i2c_id,
 };
 
index bbeeb24..3c9d18b 100644 (file)
@@ -170,22 +170,12 @@ static int cyttsp_spi_probe(struct spi_device *spi)
        return 0;
 }
 
-static int cyttsp_spi_remove(struct spi_device *spi)
-{
-       struct cyttsp *ts = spi_get_drvdata(spi);
-
-       cyttsp_remove(ts);
-
-       return 0;
-}
-
 static struct spi_driver cyttsp_spi_driver = {
        .driver = {
                .name   = CY_SPI_NAME,
                .pm     = &cyttsp_pm_ops,
        },
        .probe  = cyttsp_spi_probe,
-       .remove = cyttsp_spi_remove,
 };
 
 module_spi_driver(cyttsp_spi_driver);
diff --git a/drivers/input/touchscreen/egalax_ts_serial.c b/drivers/input/touchscreen/egalax_ts_serial.c
new file mode 100644 (file)
index 0000000..657bbae
--- /dev/null
@@ -0,0 +1,194 @@
+/*
+ * EETI Egalax serial touchscreen driver
+ *
+ * Copyright (c) 2015 Zoltán Böszörményi <zboszor@pr.hu>
+ *
+ * based on the
+ *
+ * Hampshire serial touchscreen driver (Copyright (c) 2010 Adam Bennett)
+ */
+
+/*
+ * 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.
+ */
+
+#include <linux/errno.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/slab.h>
+#include <linux/input.h>
+#include <linux/serio.h>
+
+#define DRIVER_DESC    "EETI Egalax serial touchscreen driver"
+
+/*
+ * Definitions & global arrays.
+ */
+
+#define EGALAX_FORMAT_MAX_LENGTH       6
+#define EGALAX_FORMAT_START_BIT                BIT(7)
+#define EGALAX_FORMAT_PRESSURE_BIT     BIT(6)
+#define EGALAX_FORMAT_TOUCH_BIT                BIT(0)
+#define EGALAX_FORMAT_RESOLUTION_MASK  0x06
+
+#define EGALAX_MIN_XC                  0
+#define EGALAX_MAX_XC                  0x4000
+#define EGALAX_MIN_YC                  0
+#define EGALAX_MAX_YC                  0x4000
+
+/*
+ * Per-touchscreen data.
+ */
+struct egalax {
+       struct input_dev *input;
+       struct serio *serio;
+       int idx;
+       u8 data[EGALAX_FORMAT_MAX_LENGTH];
+       char phys[32];
+};
+
+static void egalax_process_data(struct egalax *egalax)
+{
+       struct input_dev *dev = egalax->input;
+       u8 *data = egalax->data;
+       u16 x, y;
+       u8 shift;
+       u8 mask;
+
+       shift = 3 - ((data[0] & EGALAX_FORMAT_RESOLUTION_MASK) >> 1);
+       mask = 0xff >> (shift + 1);
+
+       x = (((u16)(data[1] & mask) << 7) | (data[2] & 0x7f)) << shift;
+       y = (((u16)(data[3] & mask) << 7) | (data[4] & 0x7f)) << shift;
+
+       input_report_key(dev, BTN_TOUCH, data[0] & EGALAX_FORMAT_TOUCH_BIT);
+       input_report_abs(dev, ABS_X, x);
+       input_report_abs(dev, ABS_Y, y);
+       input_sync(dev);
+}
+
+static irqreturn_t egalax_interrupt(struct serio *serio,
+                                   unsigned char data, unsigned int flags)
+{
+       struct egalax *egalax = serio_get_drvdata(serio);
+       int pkt_len;
+
+       egalax->data[egalax->idx++] = data;
+
+       if (likely(egalax->data[0] & EGALAX_FORMAT_START_BIT)) {
+               pkt_len = egalax->data[0] & EGALAX_FORMAT_PRESSURE_BIT ? 6 : 5;
+               if (pkt_len == egalax->idx) {
+                       egalax_process_data(egalax);
+                       egalax->idx = 0;
+               }
+       } else {
+               dev_dbg(&serio->dev, "unknown/unsynchronized data: %x\n",
+                       egalax->data[0]);
+               egalax->idx = 0;
+       }
+
+       return IRQ_HANDLED;
+}
+
+/*
+ * egalax_connect() is the routine that is called when someone adds a
+ * new serio device that supports egalax protocol and registers it as
+ * an input device. This is usually accomplished using inputattach.
+ */
+static int egalax_connect(struct serio *serio, struct serio_driver *drv)
+{
+       struct egalax *egalax;
+       struct input_dev *input_dev;
+       int error;
+
+       egalax = kzalloc(sizeof(struct egalax), GFP_KERNEL);
+       input_dev = input_allocate_device();
+       if (!egalax || !input_dev) {
+               error = -ENOMEM;
+               goto err_free_mem;
+       }
+
+       egalax->serio = serio;
+       egalax->input = input_dev;
+       snprintf(egalax->phys, sizeof(egalax->phys),
+                "%s/input0", serio->phys);
+
+       input_dev->name = "EETI eGalaxTouch Serial TouchScreen";
+       input_dev->phys = egalax->phys;
+       input_dev->id.bustype = BUS_RS232;
+       input_dev->id.vendor = SERIO_EGALAX;
+       input_dev->id.product = 0;
+       input_dev->id.version = 0x0001;
+       input_dev->dev.parent = &serio->dev;
+
+       input_set_capability(input_dev, EV_KEY, BTN_TOUCH);
+       input_set_abs_params(input_dev, ABS_X,
+                            EGALAX_MIN_XC, EGALAX_MAX_XC, 0, 0);
+       input_set_abs_params(input_dev, ABS_Y,
+                            EGALAX_MIN_YC, EGALAX_MAX_YC, 0, 0);
+
+       serio_set_drvdata(serio, egalax);
+
+       error = serio_open(serio, drv);
+       if (error)
+               goto err_reset_drvdata;
+
+       error = input_register_device(input_dev);
+       if (error)
+               goto err_close_serio;
+
+       return 0;
+
+err_close_serio:
+       serio_close(serio);
+err_reset_drvdata:
+       serio_set_drvdata(serio, NULL);
+err_free_mem:
+       input_free_device(input_dev);
+       kfree(egalax);
+       return error;
+}
+
+static void egalax_disconnect(struct serio *serio)
+{
+       struct egalax *egalax = serio_get_drvdata(serio);
+
+       serio_close(serio);
+       serio_set_drvdata(serio, NULL);
+       input_unregister_device(egalax->input);
+       kfree(egalax);
+}
+
+/*
+ * The serio driver structure.
+ */
+
+static const struct serio_device_id egalax_serio_ids[] = {
+       {
+               .type   = SERIO_RS232,
+               .proto  = SERIO_EGALAX,
+               .id     = SERIO_ANY,
+               .extra  = SERIO_ANY,
+       },
+       { 0 }
+};
+
+MODULE_DEVICE_TABLE(serio, egalax_serio_ids);
+
+static struct serio_driver egalax_drv = {
+       .driver         = {
+               .name   = "egalax",
+       },
+       .description    = DRIVER_DESC,
+       .id_table       = egalax_serio_ids,
+       .interrupt      = egalax_interrupt,
+       .connect        = egalax_connect,
+       .disconnect     = egalax_disconnect,
+};
+module_serio_driver(egalax_drv);
+
+MODULE_AUTHOR("Zoltán Böszörményi <zboszor@pr.hu>");
+MODULE_DESCRIPTION(DRIVER_DESC);
+MODULE_LICENSE("GPL v2");
index 4d113c9..240b16f 100644 (file)
@@ -2,6 +2,7 @@
  *  Driver for Goodix Touchscreens
  *
  *  Copyright (c) 2014 Red Hat Inc.
+ *  Copyright (c) 2015 K. Merker <merker@debian.org>
  *
  *  This code is based on gt9xx.c authored by andrew@goodix.com:
  *
@@ -16,6 +17,8 @@
 
 #include <linux/kernel.h>
 #include <linux/dmi.h>
+#include <linux/firmware.h>
+#include <linux/gpio/consumer.h>
 #include <linux/i2c.h>
 #include <linux/input.h>
 #include <linux/input/mt.h>
@@ -33,11 +36,24 @@ struct goodix_ts_data {
        struct input_dev *input_dev;
        int abs_x_max;
        int abs_y_max;
+       bool swapped_x_y;
+       bool inverted_x;
+       bool inverted_y;
        unsigned int max_touch_num;
        unsigned int int_trigger_type;
-       bool rotated_screen;
+       int cfg_len;
+       struct gpio_desc *gpiod_int;
+       struct gpio_desc *gpiod_rst;
+       u16 id;
+       u16 version;
+       const char *cfg_name;
+       struct completion firmware_loading_complete;
+       unsigned long irq_flags;
 };
 
+#define GOODIX_GPIO_INT_NAME           "irq"
+#define GOODIX_GPIO_RST_NAME           "reset"
+
 #define GOODIX_MAX_HEIGHT              4096
 #define GOODIX_MAX_WIDTH               4096
 #define GOODIX_INT_TRIGGER             1
@@ -45,8 +61,13 @@ struct goodix_ts_data {
 #define GOODIX_MAX_CONTACTS            10
 
 #define GOODIX_CONFIG_MAX_LENGTH       240
+#define GOODIX_CONFIG_911_LENGTH       186
+#define GOODIX_CONFIG_967_LENGTH       228
 
 /* Register defines */
+#define GOODIX_REG_COMMAND             0x8040
+#define GOODIX_CMD_SCREEN_OFF          0x05
+
 #define GOODIX_READ_COOR_ADDR          0x814E
 #define GOODIX_REG_CONFIG_DATA         0x8047
 #define GOODIX_REG_ID                  0x8140
@@ -115,6 +136,63 @@ static int goodix_i2c_read(struct i2c_client *client,
        return ret < 0 ? ret : (ret != ARRAY_SIZE(msgs) ? -EIO : 0);
 }
 
+/**
+ * goodix_i2c_write - write data to a register of the i2c slave device.
+ *
+ * @client: i2c device.
+ * @reg: the register to write to.
+ * @buf: raw data buffer to write.
+ * @len: length of the buffer to write
+ */
+static int goodix_i2c_write(struct i2c_client *client, u16 reg, const u8 *buf,
+                           unsigned len)
+{
+       u8 *addr_buf;
+       struct i2c_msg msg;
+       int ret;
+
+       addr_buf = kmalloc(len + 2, GFP_KERNEL);
+       if (!addr_buf)
+               return -ENOMEM;
+
+       addr_buf[0] = reg >> 8;
+       addr_buf[1] = reg & 0xFF;
+       memcpy(&addr_buf[2], buf, len);
+
+       msg.flags = 0;
+       msg.addr = client->addr;
+       msg.buf = addr_buf;
+       msg.len = len + 2;
+
+       ret = i2c_transfer(client->adapter, &msg, 1);
+       kfree(addr_buf);
+       return ret < 0 ? ret : (ret != 1 ? -EIO : 0);
+}
+
+static int goodix_i2c_write_u8(struct i2c_client *client, u16 reg, u8 value)
+{
+       return goodix_i2c_write(client, reg, &value, sizeof(value));
+}
+
+static int goodix_get_cfg_len(u16 id)
+{
+       switch (id) {
+       case 911:
+       case 9271:
+       case 9110:
+       case 927:
+       case 928:
+               return GOODIX_CONFIG_911_LENGTH;
+
+       case 912:
+       case 967:
+               return GOODIX_CONFIG_967_LENGTH;
+
+       default:
+               return GOODIX_CONFIG_MAX_LENGTH;
+       }
+}
+
 static int goodix_ts_read_input_report(struct goodix_ts_data *ts, u8 *data)
 {
        int touch_num;
@@ -155,10 +233,13 @@ static void goodix_ts_report_touch(struct goodix_ts_data *ts, u8 *coor_data)
        int input_y = get_unaligned_le16(&coor_data[3]);
        int input_w = get_unaligned_le16(&coor_data[5]);
 
-       if (ts->rotated_screen) {
+       /* Inversions have to happen before axis swapping */
+       if (ts->inverted_x)
                input_x = ts->abs_x_max - input_x;
+       if (ts->inverted_y)
                input_y = ts->abs_y_max - input_y;
-       }
+       if (ts->swapped_x_y)
+               swap(input_x, input_y);
 
        input_mt_slot(ts->input_dev, id);
        input_mt_report_slot_state(ts->input_dev, MT_TOOL_FINGER, true);
@@ -202,21 +283,195 @@ static void goodix_process_events(struct goodix_ts_data *ts)
  */
 static irqreturn_t goodix_ts_irq_handler(int irq, void *dev_id)
 {
-       static const u8 end_cmd[] = {
-               GOODIX_READ_COOR_ADDR >> 8,
-               GOODIX_READ_COOR_ADDR & 0xff,
-               0
-       };
        struct goodix_ts_data *ts = dev_id;
 
        goodix_process_events(ts);
 
-       if (i2c_master_send(ts->client, end_cmd, sizeof(end_cmd)) < 0)
+       if (goodix_i2c_write_u8(ts->client, GOODIX_READ_COOR_ADDR, 0) < 0)
                dev_err(&ts->client->dev, "I2C write end_cmd error\n");
 
        return IRQ_HANDLED;
 }
 
+static void goodix_free_irq(struct goodix_ts_data *ts)
+{
+       devm_free_irq(&ts->client->dev, ts->client->irq, ts);
+}
+
+static int goodix_request_irq(struct goodix_ts_data *ts)
+{
+       return devm_request_threaded_irq(&ts->client->dev, ts->client->irq,
+                                        NULL, goodix_ts_irq_handler,
+                                        ts->irq_flags, ts->client->name, ts);
+}
+
+/**
+ * goodix_check_cfg - Checks if config fw is valid
+ *
+ * @ts: goodix_ts_data pointer
+ * @cfg: firmware config data
+ */
+static int goodix_check_cfg(struct goodix_ts_data *ts,
+                           const struct firmware *cfg)
+{
+       int i, raw_cfg_len;
+       u8 check_sum = 0;
+
+       if (cfg->size > GOODIX_CONFIG_MAX_LENGTH) {
+               dev_err(&ts->client->dev,
+                       "The length of the config fw is not correct");
+               return -EINVAL;
+       }
+
+       raw_cfg_len = cfg->size - 2;
+       for (i = 0; i < raw_cfg_len; i++)
+               check_sum += cfg->data[i];
+       check_sum = (~check_sum) + 1;
+       if (check_sum != cfg->data[raw_cfg_len]) {
+               dev_err(&ts->client->dev,
+                       "The checksum of the config fw is not correct");
+               return -EINVAL;
+       }
+
+       if (cfg->data[raw_cfg_len + 1] != 1) {
+               dev_err(&ts->client->dev,
+                       "Config fw must have Config_Fresh register set");
+               return -EINVAL;
+       }
+
+       return 0;
+}
+
+/**
+ * goodix_send_cfg - Write fw config to device
+ *
+ * @ts: goodix_ts_data pointer
+ * @cfg: config firmware to write to device
+ */
+static int goodix_send_cfg(struct goodix_ts_data *ts,
+                          const struct firmware *cfg)
+{
+       int error;
+
+       error = goodix_check_cfg(ts, cfg);
+       if (error)
+               return error;
+
+       error = goodix_i2c_write(ts->client, GOODIX_REG_CONFIG_DATA, cfg->data,
+                                cfg->size);
+       if (error) {
+               dev_err(&ts->client->dev, "Failed to write config data: %d",
+                       error);
+               return error;
+       }
+       dev_dbg(&ts->client->dev, "Config sent successfully.");
+
+       /* Let the firmware reconfigure itself, so sleep for 10ms */
+       usleep_range(10000, 11000);
+
+       return 0;
+}
+
+static int goodix_int_sync(struct goodix_ts_data *ts)
+{
+       int error;
+
+       error = gpiod_direction_output(ts->gpiod_int, 0);
+       if (error)
+               return error;
+
+       msleep(50);                             /* T5: 50ms */
+
+       error = gpiod_direction_input(ts->gpiod_int);
+       if (error)
+               return error;
+
+       return 0;
+}
+
+/**
+ * goodix_reset - Reset device during power on
+ *
+ * @ts: goodix_ts_data pointer
+ */
+static int goodix_reset(struct goodix_ts_data *ts)
+{
+       int error;
+
+       /* begin select I2C slave addr */
+       error = gpiod_direction_output(ts->gpiod_rst, 0);
+       if (error)
+               return error;
+
+       msleep(20);                             /* T2: > 10ms */
+
+       /* HIGH: 0x28/0x29, LOW: 0xBA/0xBB */
+       error = gpiod_direction_output(ts->gpiod_int, ts->client->addr == 0x14);
+       if (error)
+               return error;
+
+       usleep_range(100, 2000);                /* T3: > 100us */
+
+       error = gpiod_direction_output(ts->gpiod_rst, 1);
+       if (error)
+               return error;
+
+       usleep_range(6000, 10000);              /* T4: > 5ms */
+
+       /* end select I2C slave addr */
+       error = gpiod_direction_input(ts->gpiod_rst);
+       if (error)
+               return error;
+
+       error = goodix_int_sync(ts);
+       if (error)
+               return error;
+
+       return 0;
+}
+
+/**
+ * goodix_get_gpio_config - Get GPIO config from ACPI/DT
+ *
+ * @ts: goodix_ts_data pointer
+ */
+static int goodix_get_gpio_config(struct goodix_ts_data *ts)
+{
+       int error;
+       struct device *dev;
+       struct gpio_desc *gpiod;
+
+       if (!ts->client)
+               return -EINVAL;
+       dev = &ts->client->dev;
+
+       /* Get the interrupt GPIO pin number */
+       gpiod = devm_gpiod_get_optional(dev, GOODIX_GPIO_INT_NAME, GPIOD_IN);
+       if (IS_ERR(gpiod)) {
+               error = PTR_ERR(gpiod);
+               if (error != -EPROBE_DEFER)
+                       dev_dbg(dev, "Failed to get %s GPIO: %d\n",
+                               GOODIX_GPIO_INT_NAME, error);
+               return error;
+       }
+
+       ts->gpiod_int = gpiod;
+
+       /* Get the reset line GPIO pin number */
+       gpiod = devm_gpiod_get_optional(dev, GOODIX_GPIO_RST_NAME, GPIOD_IN);
+       if (IS_ERR(gpiod)) {
+               error = PTR_ERR(gpiod);
+               if (error != -EPROBE_DEFER)
+                       dev_dbg(dev, "Failed to get %s GPIO: %d\n",
+                               GOODIX_GPIO_RST_NAME, error);
+               return error;
+       }
+
+       ts->gpiod_rst = gpiod;
+
+       return 0;
+}
+
 /**
  * goodix_read_config - Read the embedded configuration of the panel
  *
@@ -230,14 +485,15 @@ static void goodix_read_config(struct goodix_ts_data *ts)
        int error;
 
        error = goodix_i2c_read(ts->client, GOODIX_REG_CONFIG_DATA,
-                               config,
-                               GOODIX_CONFIG_MAX_LENGTH);
+                               config, ts->cfg_len);
        if (error) {
                dev_warn(&ts->client->dev,
                         "Error reading config (%d), using defaults\n",
                         error);
                ts->abs_x_max = GOODIX_MAX_WIDTH;
                ts->abs_y_max = GOODIX_MAX_HEIGHT;
+               if (ts->swapped_x_y)
+                       swap(ts->abs_x_max, ts->abs_y_max);
                ts->int_trigger_type = GOODIX_INT_TRIGGER;
                ts->max_touch_num = GOODIX_MAX_CONTACTS;
                return;
@@ -245,6 +501,8 @@ static void goodix_read_config(struct goodix_ts_data *ts)
 
        ts->abs_x_max = get_unaligned_le16(&config[RESOLUTION_LOC]);
        ts->abs_y_max = get_unaligned_le16(&config[RESOLUTION_LOC + 2]);
+       if (ts->swapped_x_y)
+               swap(ts->abs_x_max, ts->abs_y_max);
        ts->int_trigger_type = config[TRIGGER_LOC] & 0x03;
        ts->max_touch_num = config[MAX_CONTACTS_LOC] & 0x0f;
        if (!ts->abs_x_max || !ts->abs_y_max || !ts->max_touch_num) {
@@ -252,42 +510,45 @@ static void goodix_read_config(struct goodix_ts_data *ts)
                        "Invalid config, using defaults\n");
                ts->abs_x_max = GOODIX_MAX_WIDTH;
                ts->abs_y_max = GOODIX_MAX_HEIGHT;
+               if (ts->swapped_x_y)
+                       swap(ts->abs_x_max, ts->abs_y_max);
                ts->max_touch_num = GOODIX_MAX_CONTACTS;
        }
 
-       ts->rotated_screen = dmi_check_system(rotated_screen);
-       if (ts->rotated_screen)
+       if (dmi_check_system(rotated_screen)) {
+               ts->inverted_x = true;
+               ts->inverted_y = true;
                dev_dbg(&ts->client->dev,
                         "Applying '180 degrees rotated screen' quirk\n");
+       }
 }
 
 /**
  * goodix_read_version - Read goodix touchscreen version
  *
- * @client: the i2c client
- * @version: output buffer containing the version on success
- * @id: output buffer containing the id on success
+ * @ts: our goodix_ts_data pointer
  */
-static int goodix_read_version(struct i2c_client *client, u16 *version, u16 *id)
+static int goodix_read_version(struct goodix_ts_data *ts)
 {
        int error;
        u8 buf[6];
        char id_str[5];
 
-       error = goodix_i2c_read(client, GOODIX_REG_ID, buf, sizeof(buf));
+       error = goodix_i2c_read(ts->client, GOODIX_REG_ID, buf, sizeof(buf));
        if (error) {
-               dev_err(&client->dev, "read version failed: %d\n", error);
+               dev_err(&ts->client->dev, "read version failed: %d\n", error);
                return error;
        }
 
        memcpy(id_str, buf, 4);
        id_str[4] = 0;
-       if (kstrtou16(id_str, 10, id))
-               *id = 0x1001;
+       if (kstrtou16(id_str, 10, &ts->id))
+               ts->id = 0x1001;
 
-       *version = get_unaligned_le16(&buf[4]);
+       ts->version = get_unaligned_le16(&buf[4]);
 
-       dev_info(&client->dev, "ID %d, version: %04x\n", *id, *version);
+       dev_info(&ts->client->dev, "ID %d, version: %04x\n", ts->id,
+                ts->version);
 
        return 0;
 }
@@ -321,13 +582,10 @@ static int goodix_i2c_test(struct i2c_client *client)
  * goodix_request_input_dev - Allocate, populate and register the input device
  *
  * @ts: our goodix_ts_data pointer
- * @version: device firmware version
- * @id: device ID
  *
  * Must be called during probe
  */
-static int goodix_request_input_dev(struct goodix_ts_data *ts, u16 version,
-                                   u16 id)
+static int goodix_request_input_dev(struct goodix_ts_data *ts)
 {
        int error;
 
@@ -351,8 +609,8 @@ static int goodix_request_input_dev(struct goodix_ts_data *ts, u16 version,
        ts->input_dev->phys = "input/ts";
        ts->input_dev->id.bustype = BUS_I2C;
        ts->input_dev->id.vendor = 0x0416;
-       ts->input_dev->id.product = id;
-       ts->input_dev->id.version = version;
+       ts->input_dev->id.product = ts->id;
+       ts->input_dev->id.version = ts->version;
 
        error = input_register_device(ts->input_dev);
        if (error) {
@@ -364,13 +622,75 @@ static int goodix_request_input_dev(struct goodix_ts_data *ts, u16 version,
        return 0;
 }
 
+/**
+ * goodix_configure_dev - Finish device initialization
+ *
+ * @ts: our goodix_ts_data pointer
+ *
+ * Must be called from probe to finish initialization of the device.
+ * Contains the common initialization code for both devices that
+ * declare gpio pins and devices that do not. It is either called
+ * directly from probe or from request_firmware_wait callback.
+ */
+static int goodix_configure_dev(struct goodix_ts_data *ts)
+{
+       int error;
+
+       ts->swapped_x_y = device_property_read_bool(&ts->client->dev,
+                                                   "touchscreen-swapped-x-y");
+       ts->inverted_x = device_property_read_bool(&ts->client->dev,
+                                                  "touchscreen-inverted-x");
+       ts->inverted_y = device_property_read_bool(&ts->client->dev,
+                                                  "touchscreen-inverted-y");
+
+       goodix_read_config(ts);
+
+       error = goodix_request_input_dev(ts);
+       if (error)
+               return error;
+
+       ts->irq_flags = goodix_irq_flags[ts->int_trigger_type] | IRQF_ONESHOT;
+       error = goodix_request_irq(ts);
+       if (error) {
+               dev_err(&ts->client->dev, "request IRQ failed: %d\n", error);
+               return error;
+       }
+
+       return 0;
+}
+
+/**
+ * goodix_config_cb - Callback to finish device init
+ *
+ * @ts: our goodix_ts_data pointer
+ *
+ * request_firmware_wait callback that finishes
+ * initialization of the device.
+ */
+static void goodix_config_cb(const struct firmware *cfg, void *ctx)
+{
+       struct goodix_ts_data *ts = ctx;
+       int error;
+
+       if (cfg) {
+               /* send device configuration to the firmware */
+               error = goodix_send_cfg(ts, cfg);
+               if (error)
+                       goto err_release_cfg;
+       }
+
+       goodix_configure_dev(ts);
+
+err_release_cfg:
+       release_firmware(cfg);
+       complete_all(&ts->firmware_loading_complete);
+}
+
 static int goodix_ts_probe(struct i2c_client *client,
                           const struct i2c_device_id *id)
 {
        struct goodix_ts_data *ts;
-       unsigned long irq_flags;
        int error;
-       u16 version_info, id_info;
 
        dev_dbg(&client->dev, "I2C Address: 0x%02x\n", client->addr);
 
@@ -385,6 +705,20 @@ static int goodix_ts_probe(struct i2c_client *client,
 
        ts->client = client;
        i2c_set_clientdata(client, ts);
+       init_completion(&ts->firmware_loading_complete);
+
+       error = goodix_get_gpio_config(ts);
+       if (error)
+               return error;
+
+       if (ts->gpiod_int && ts->gpiod_rst) {
+               /* reset the controller */
+               error = goodix_reset(ts);
+               if (error) {
+                       dev_err(&client->dev, "Controller reset failed.\n");
+                       return error;
+               }
+       }
 
        error = goodix_i2c_test(client);
        if (error) {
@@ -392,30 +726,125 @@ static int goodix_ts_probe(struct i2c_client *client,
                return error;
        }
 
-       error = goodix_read_version(client, &version_info, &id_info);
+       error = goodix_read_version(ts);
        if (error) {
                dev_err(&client->dev, "Read version failed.\n");
                return error;
        }
 
-       goodix_read_config(ts);
+       ts->cfg_len = goodix_get_cfg_len(ts->id);
+
+       if (ts->gpiod_int && ts->gpiod_rst) {
+               /* update device config */
+               ts->cfg_name = devm_kasprintf(&client->dev, GFP_KERNEL,
+                                             "goodix_%d_cfg.bin", ts->id);
+               if (!ts->cfg_name)
+                       return -ENOMEM;
+
+               error = request_firmware_nowait(THIS_MODULE, true, ts->cfg_name,
+                                               &client->dev, GFP_KERNEL, ts,
+                                               goodix_config_cb);
+               if (error) {
+                       dev_err(&client->dev,
+                               "Failed to invoke firmware loader: %d\n",
+                               error);
+                       return error;
+               }
 
-       error = goodix_request_input_dev(ts, version_info, id_info);
-       if (error)
-               return error;
+               return 0;
+       } else {
+               error = goodix_configure_dev(ts);
+               if (error)
+                       return error;
+       }
+
+       return 0;
+}
+
+static int goodix_ts_remove(struct i2c_client *client)
+{
+       struct goodix_ts_data *ts = i2c_get_clientdata(client);
+
+       if (ts->gpiod_int && ts->gpiod_rst)
+               wait_for_completion(&ts->firmware_loading_complete);
+
+       return 0;
+}
+
+static int __maybe_unused goodix_suspend(struct device *dev)
+{
+       struct i2c_client *client = to_i2c_client(dev);
+       struct goodix_ts_data *ts = i2c_get_clientdata(client);
+       int error;
 
-       irq_flags = goodix_irq_flags[ts->int_trigger_type] | IRQF_ONESHOT;
-       error = devm_request_threaded_irq(&ts->client->dev, client->irq,
-                                         NULL, goodix_ts_irq_handler,
-                                         irq_flags, client->name, ts);
+       /* We need gpio pins to suspend/resume */
+       if (!ts->gpiod_int || !ts->gpiod_rst)
+               return 0;
+
+       wait_for_completion(&ts->firmware_loading_complete);
+
+       /* Free IRQ as IRQ pin is used as output in the suspend sequence */
+       goodix_free_irq(ts);
+
+       /* Output LOW on the INT pin for 5 ms */
+       error = gpiod_direction_output(ts->gpiod_int, 0);
        if (error) {
-               dev_err(&client->dev, "request IRQ failed: %d\n", error);
+               goodix_request_irq(ts);
                return error;
        }
 
+       usleep_range(5000, 6000);
+
+       error = goodix_i2c_write_u8(ts->client, GOODIX_REG_COMMAND,
+                                   GOODIX_CMD_SCREEN_OFF);
+       if (error) {
+               dev_err(&ts->client->dev, "Screen off command failed\n");
+               gpiod_direction_input(ts->gpiod_int);
+               goodix_request_irq(ts);
+               return -EAGAIN;
+       }
+
+       /*
+        * The datasheet specifies that the interval between sending screen-off
+        * command and wake-up should be longer than 58 ms. To avoid waking up
+        * sooner, delay 58ms here.
+        */
+       msleep(58);
+       return 0;
+}
+
+static int __maybe_unused goodix_resume(struct device *dev)
+{
+       struct i2c_client *client = to_i2c_client(dev);
+       struct goodix_ts_data *ts = i2c_get_clientdata(client);
+       int error;
+
+       if (!ts->gpiod_int || !ts->gpiod_rst)
+               return 0;
+
+       /*
+        * Exit sleep mode by outputting HIGH level to INT pin
+        * for 2ms~5ms.
+        */
+       error = gpiod_direction_output(ts->gpiod_int, 1);
+       if (error)
+               return error;
+
+       usleep_range(2000, 5000);
+
+       error = goodix_int_sync(ts);
+       if (error)
+               return error;
+
+       error = goodix_request_irq(ts);
+       if (error)
+               return error;
+
        return 0;
 }
 
+static SIMPLE_DEV_PM_OPS(goodix_pm_ops, goodix_suspend, goodix_resume);
+
 static const struct i2c_device_id goodix_ts_id[] = {
        { "GDIX1001:00", 0 },
        { }
@@ -446,11 +875,13 @@ MODULE_DEVICE_TABLE(of, goodix_of_match);
 
 static struct i2c_driver goodix_ts_driver = {
        .probe = goodix_ts_probe,
+       .remove = goodix_ts_remove,
        .id_table = goodix_ts_id,
        .driver = {
                .name = "Goodix-TS",
                .acpi_match_table = ACPI_PTR(goodix_acpi_match),
                .of_match_table = of_match_ptr(goodix_of_match),
+               .pm = &goodix_pm_ops,
        },
 };
 module_i2c_driver(goodix_ts_driver);
diff --git a/drivers/input/touchscreen/melfas_mip4.c b/drivers/input/touchscreen/melfas_mip4.c
new file mode 100644 (file)
index 0000000..8927297
--- /dev/null
@@ -0,0 +1,1517 @@
+/*
+ * MELFAS MIP4 Touchscreen
+ *
+ * Copyright (C) 2016 MELFAS Inc.
+ *
+ * Author : Sangwon Jee <jeesw@melfas.com>
+ *
+ * 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 the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+#include <linux/acpi.h>
+#include <linux/delay.h>
+#include <linux/firmware.h>
+#include <linux/gpio/consumer.h>
+#include <linux/i2c.h>
+#include <linux/input.h>
+#include <linux/input/mt.h>
+#include <linux/interrupt.h>
+#include <linux/module.h>
+#include <linux/of.h>
+#include <linux/slab.h>
+#include <asm/unaligned.h>
+
+#define MIP4_DEVICE_NAME       "mip4_ts"
+
+/*****************************************************************
+ * Protocol
+ * Version : MIP 4.0 Rev 4.6
+ *****************************************************************/
+
+/* Address */
+#define MIP4_R0_BOOT                           0x00
+#define MIP4_R1_BOOT_MODE                      0x01
+#define MIP4_R1_BOOT_BUF_ADDR                  0x10
+#define MIP4_R1_BOOT_STATUS                    0x20
+#define MIP4_R1_BOOT_CMD                       0x30
+#define MIP4_R1_BOOT_TARGET_ADDR               0x40
+#define MIP4_R1_BOOT_SIZE                      0x44
+
+#define MIP4_R0_INFO                           0x01
+#define MIP4_R1_INFO_PRODUCT_NAME              0x00
+#define MIP4_R1_INFO_RESOLUTION_X              0x10
+#define MIP4_R1_INFO_RESOLUTION_Y              0x12
+#define MIP4_R1_INFO_NODE_NUM_X                        0x14
+#define MIP4_R1_INFO_NODE_NUM_Y                        0x15
+#define MIP4_R1_INFO_KEY_NUM                   0x16
+#define MIP4_R1_INFO_PRESSURE_NUM              0x17
+#define MIP4_R1_INFO_LENGTH_X                  0x18
+#define MIP4_R1_INFO_LENGTH_Y                  0x1A
+#define MIP4_R1_INFO_PPM_X                     0x1C
+#define MIP4_R1_INFO_PPM_Y                     0x1D
+#define MIP4_R1_INFO_VERSION_BOOT              0x20
+#define MIP4_R1_INFO_VERSION_CORE              0x22
+#define MIP4_R1_INFO_VERSION_APP               0x24
+#define MIP4_R1_INFO_VERSION_PARAM             0x26
+#define MIP4_R1_INFO_SECT_BOOT_START           0x30
+#define MIP4_R1_INFO_SECT_BOOT_END             0x31
+#define MIP4_R1_INFO_SECT_CORE_START           0x32
+#define MIP4_R1_INFO_SECT_CORE_END             0x33
+#define MIP4_R1_INFO_SECT_APP_START            0x34
+#define MIP4_R1_INFO_SECT_APP_END              0x35
+#define MIP4_R1_INFO_SECT_PARAM_START          0x36
+#define MIP4_R1_INFO_SECT_PARAM_END            0x37
+#define MIP4_R1_INFO_BUILD_DATE                        0x40
+#define MIP4_R1_INFO_BUILD_TIME                        0x44
+#define MIP4_R1_INFO_CHECKSUM_PRECALC          0x48
+#define MIP4_R1_INFO_CHECKSUM_REALTIME         0x4A
+#define MIP4_R1_INFO_PROTOCOL_NAME             0x50
+#define MIP4_R1_INFO_PROTOCOL_VERSION          0x58
+#define MIP4_R1_INFO_IC_ID                     0x70
+#define MIP4_R1_INFO_IC_NAME                   0x71
+#define MIP4_R1_INFO_IC_VENDOR_ID              0x75
+#define MIP4_R1_INFO_IC_HW_CATEGORY            0x77
+#define MIP4_R1_INFO_CONTACT_THD_SCR           0x78
+#define MIP4_R1_INFO_CONTACT_THD_KEY           0x7A
+
+#define MIP4_R0_EVENT                          0x02
+#define MIP4_R1_EVENT_SUPPORTED_FUNC           0x00
+#define MIP4_R1_EVENT_FORMAT                   0x04
+#define MIP4_R1_EVENT_SIZE                     0x06
+#define MIP4_R1_EVENT_PACKET_INFO              0x10
+#define MIP4_R1_EVENT_PACKET_DATA              0x11
+
+#define MIP4_R0_CTRL                           0x06
+#define MIP4_R1_CTRL_READY_STATUS              0x00
+#define MIP4_R1_CTRL_EVENT_READY               0x01
+#define MIP4_R1_CTRL_MODE                      0x10
+#define MIP4_R1_CTRL_EVENT_TRIGGER_TYPE                0x11
+#define MIP4_R1_CTRL_RECALIBRATE               0x12
+#define MIP4_R1_CTRL_POWER_STATE               0x13
+#define MIP4_R1_CTRL_GESTURE_TYPE              0x14
+#define MIP4_R1_CTRL_DISABLE_ESD_ALERT         0x18
+#define MIP4_R1_CTRL_CHARGER_MODE              0x19
+#define MIP4_R1_CTRL_HIGH_SENS_MODE            0x1A
+#define MIP4_R1_CTRL_WINDOW_MODE               0x1B
+#define MIP4_R1_CTRL_PALM_REJECTION            0x1C
+#define MIP4_R1_CTRL_EDGE_CORRECTION           0x1D
+#define MIP4_R1_CTRL_ENTER_GLOVE_MODE          0x1E
+#define MIP4_R1_CTRL_I2C_ON_LPM                        0x1F
+#define MIP4_R1_CTRL_GESTURE_DEBUG             0x20
+#define MIP4_R1_CTRL_PALM_EVENT                        0x22
+#define MIP4_R1_CTRL_PROXIMITY_SENSING         0x23
+
+/* Value */
+#define MIP4_BOOT_MODE_BOOT                    0x01
+#define MIP4_BOOT_MODE_APP                     0x02
+
+#define MIP4_BOOT_STATUS_BUSY                  0x05
+#define MIP4_BOOT_STATUS_ERROR                 0x0E
+#define MIP4_BOOT_STATUS_DONE                  0xA0
+
+#define MIP4_BOOT_CMD_MASS_ERASE               0x15
+#define MIP4_BOOT_CMD_PROGRAM                  0x54
+#define MIP4_BOOT_CMD_ERASE                    0x8F
+#define MIP4_BOOT_CMD_WRITE                    0xA5
+#define MIP4_BOOT_CMD_READ                     0xC2
+
+#define MIP4_EVENT_INPUT_TYPE_KEY              0
+#define MIP4_EVENT_INPUT_TYPE_SCREEN           1
+#define MIP4_EVENT_INPUT_TYPE_PROXIMITY                2
+
+#define I2C_RETRY_COUNT                                3       /* 2~ */
+
+#define MIP4_BUF_SIZE                          128
+#define MIP4_MAX_FINGERS                       10
+#define MIP4_MAX_KEYS                          4
+
+#define MIP4_TOUCH_MAJOR_MIN                   0
+#define MIP4_TOUCH_MAJOR_MAX                   255
+#define MIP4_TOUCH_MINOR_MIN                   0
+#define MIP4_TOUCH_MINOR_MAX                   255
+#define MIP4_PRESSURE_MIN                      0
+#define MIP4_PRESSURE_MAX                      255
+
+#define MIP4_FW_NAME                   "melfas_mip4.fw"
+#define MIP4_FW_UPDATE_DEBUG           0       /* 0 (default) or 1 */
+
+struct mip4_fw_version {
+       u16 boot;
+       u16 core;
+       u16 app;
+       u16 param;
+};
+
+struct mip4_ts {
+       struct i2c_client *client;
+       struct input_dev *input;
+       struct gpio_desc *gpio_ce;
+
+       char phys[32];
+       char product_name[16];
+
+       unsigned int max_x;
+       unsigned int max_y;
+       u8 node_x;
+       u8 node_y;
+       u8 node_key;
+       unsigned int ppm_x;
+       unsigned int ppm_y;
+
+       struct mip4_fw_version fw_version;
+
+       unsigned int event_size;
+       unsigned int event_format;
+
+       unsigned int key_num;
+       unsigned short key_code[MIP4_MAX_KEYS];
+
+       bool wake_irq_enabled;
+
+       u8 buf[MIP4_BUF_SIZE];
+};
+
+static int mip4_i2c_xfer(struct mip4_ts *ts,
+                        char *write_buf, unsigned int write_len,
+                        char *read_buf, unsigned int read_len)
+{
+       struct i2c_msg msg[] = {
+               {
+                       .addr = ts->client->addr,
+                       .flags = 0,
+                       .buf = write_buf,
+                       .len = write_len,
+               }, {
+                       .addr = ts->client->addr,
+                       .flags = I2C_M_RD,
+                       .buf = read_buf,
+                       .len = read_len,
+               },
+       };
+       int retry = I2C_RETRY_COUNT;
+       int res;
+       int error;
+
+       do {
+               res = i2c_transfer(ts->client->adapter, msg, ARRAY_SIZE(msg));
+               if (res == ARRAY_SIZE(msg))
+                       return 0;
+
+               error = res < 0 ? res : -EIO;
+               dev_err(&ts->client->dev,
+                       "%s - i2c_transfer failed: %d (%d)\n",
+                       __func__, error, res);
+       } while (--retry);
+
+       return error;
+}
+
+static void mip4_parse_fw_version(const u8 *buf, struct mip4_fw_version *v)
+{
+       v->boot  = get_unaligned_le16(buf + 0);
+       v->core  = get_unaligned_le16(buf + 2);
+       v->app   = get_unaligned_le16(buf + 4);
+       v->param = get_unaligned_le16(buf + 6);
+}
+
+/*
+ * Read chip firmware version
+ */
+static int mip4_get_fw_version(struct mip4_ts *ts)
+{
+       u8 cmd[] = { MIP4_R0_INFO, MIP4_R1_INFO_VERSION_BOOT };
+       u8 buf[sizeof(ts->fw_version)];
+       int error;
+
+       error = mip4_i2c_xfer(ts, cmd, sizeof(cmd), buf, sizeof(buf));
+       if (error) {
+               memset(&ts->fw_version, 0xff, sizeof(ts->fw_version));
+               return error;
+       }
+
+       mip4_parse_fw_version(buf, &ts->fw_version);
+
+       return 0;
+}
+
+/*
+ * Fetch device characteristics
+ */
+static int mip4_query_device(struct mip4_ts *ts)
+{
+       int error;
+       u8 cmd[2];
+       u8 buf[14];
+
+       /* Product name */
+       cmd[0] = MIP4_R0_INFO;
+       cmd[1] = MIP4_R1_INFO_PRODUCT_NAME;
+       error = mip4_i2c_xfer(ts, cmd, sizeof(cmd),
+                             ts->product_name, sizeof(ts->product_name));
+       if (error)
+               dev_warn(&ts->client->dev,
+                        "Failed to retrieve product name: %d\n", error);
+       else
+               dev_dbg(&ts->client->dev, "product name: %.*s\n",
+                       (int)sizeof(ts->product_name), ts->product_name);
+
+       /* Firmware version */
+       error = mip4_get_fw_version(ts);
+       if (error)
+               dev_warn(&ts->client->dev,
+                       "Failed to retrieve FW version: %d\n", error);
+       else
+               dev_dbg(&ts->client->dev, "F/W Version: %04X %04X %04X %04X\n",
+                        ts->fw_version.boot, ts->fw_version.core,
+                        ts->fw_version.app, ts->fw_version.param);
+
+       /* Resolution */
+       cmd[0] = MIP4_R0_INFO;
+       cmd[1] = MIP4_R1_INFO_RESOLUTION_X;
+       error = mip4_i2c_xfer(ts, cmd, sizeof(cmd), buf, 14);
+       if (error) {
+               dev_warn(&ts->client->dev,
+                        "Failed to retrieve touchscreen parameters: %d\n",
+                        error);
+       } else {
+               ts->max_x = get_unaligned_le16(&buf[0]);
+               ts->max_y = get_unaligned_le16(&buf[2]);
+               dev_dbg(&ts->client->dev, "max_x: %d, max_y: %d\n",
+                       ts->max_x, ts->max_y);
+
+               ts->node_x = buf[4];
+               ts->node_y = buf[5];
+               ts->node_key = buf[6];
+               dev_dbg(&ts->client->dev,
+                       "node_x: %d, node_y: %d, node_key: %d\n",
+                       ts->node_x, ts->node_y, ts->node_key);
+
+               ts->ppm_x = buf[12];
+               ts->ppm_y = buf[13];
+               dev_dbg(&ts->client->dev, "ppm_x: %d, ppm_y: %d\n",
+                       ts->ppm_x, ts->ppm_y);
+
+               /* Key ts */
+               if (ts->node_key > 0)
+                       ts->key_num = ts->node_key;
+       }
+
+       /* Protocol */
+       cmd[0] = MIP4_R0_EVENT;
+       cmd[1] = MIP4_R1_EVENT_SUPPORTED_FUNC;
+       error = mip4_i2c_xfer(ts, cmd, sizeof(cmd), buf, 7);
+       if (error) {
+               dev_warn(&ts->client->dev,
+                       "Failed to retrieve device type: %d\n", error);
+               ts->event_format = 0xff;
+       } else {
+               ts->event_format = get_unaligned_le16(&buf[4]);
+               ts->event_size = buf[6];
+               dev_dbg(&ts->client->dev, "event_format: %d, event_size: %d\n",
+                       ts->event_format, ts->event_size);
+
+               if (ts->event_format == 2 || ts->event_format > 3)
+                       dev_warn(&ts->client->dev,
+                                "Unknown event format %d\n", ts->event_format);
+       }
+
+       return 0;
+}
+
+static int mip4_power_on(struct mip4_ts *ts)
+{
+       if (ts->gpio_ce) {
+               gpiod_set_value_cansleep(ts->gpio_ce, 1);
+
+               /* Booting delay : 200~300ms */
+               usleep_range(200 * 1000, 300 * 1000);
+       }
+
+       return 0;
+}
+
+static void mip4_power_off(struct mip4_ts *ts)
+{
+       if (ts->gpio_ce)
+               gpiod_set_value_cansleep(ts->gpio_ce, 0);
+}
+
+/*
+ * Clear touch input event status
+ */
+static void mip4_clear_input(struct mip4_ts *ts)
+{
+       int i;
+
+       /* Screen */
+       for (i = 0; i < MIP4_MAX_FINGERS; i++) {
+               input_mt_slot(ts->input, i);
+               input_mt_report_slot_state(ts->input, MT_TOOL_FINGER, 0);
+       }
+
+       /* Keys */
+       for (i = 0; i < ts->key_num; i++)
+               input_report_key(ts->input, ts->key_code[i], 0);
+
+       input_sync(ts->input);
+}
+
+static int mip4_enable(struct mip4_ts *ts)
+{
+       int error;
+
+       error = mip4_power_on(ts);
+       if (error)
+               return error;
+
+       enable_irq(ts->client->irq);
+
+       return 0;
+}
+
+static void mip4_disable(struct mip4_ts *ts)
+{
+       disable_irq(ts->client->irq);
+
+       mip4_power_off(ts);
+
+       mip4_clear_input(ts);
+}
+
+/*****************************************************************
+ * Input handling
+ *****************************************************************/
+
+static void mip4_report_keys(struct mip4_ts *ts, u8 *packet)
+{
+       u8 key;
+       bool down;
+
+       switch (ts->event_format) {
+       case 0:
+       case 1:
+               key = packet[0] & 0x0F;
+               down = packet[0] & 0x80;
+               break;
+
+       case 3:
+       default:
+               key = packet[0] & 0x0F;
+               down = packet[1] & 0x01;
+               break;
+       }
+
+       /* Report key event */
+       if (key >= 1 && key <= ts->key_num) {
+               unsigned short keycode = ts->key_code[key - 1];
+
+               dev_dbg(&ts->client->dev,
+                       "Key - ID: %d, keycode: %d, state: %d\n",
+                       key, keycode, down);
+
+               input_event(ts->input, EV_MSC, MSC_SCAN, keycode);
+               input_report_key(ts->input, keycode, down);
+
+       } else {
+               dev_err(&ts->client->dev, "Unknown key: %d\n", key);
+       }
+}
+
+static void mip4_report_touch(struct mip4_ts *ts, u8 *packet)
+{
+       int id;
+       bool hover;
+       bool palm;
+       bool state;
+       u16 x, y;
+       u8 pressure_stage = 0;
+       u8 pressure;
+       u8 size;
+       u8 touch_major;
+       u8 touch_minor;
+
+       switch (ts->event_format) {
+       case 0:
+       case 1:
+               /* Touch only */
+               state = packet[0] & BIT(7);
+               hover = packet[0] & BIT(5);
+               palm = packet[0] & BIT(4);
+               id = (packet[0] & 0x0F) - 1;
+               x = ((packet[1] & 0x0F) << 8) | packet[2];
+               y = (((packet[1] >> 4) & 0x0F) << 8) |
+                       packet[3];
+               pressure = packet[4];
+               size = packet[5];
+               if (ts->event_format == 0) {
+                       touch_major = packet[5];
+                       touch_minor = packet[5];
+               } else {
+                       touch_major = packet[6];
+                       touch_minor = packet[7];
+               }
+               break;
+
+       case 3:
+       default:
+               /* Touch + Force(Pressure) */
+               id = (packet[0] & 0x0F) - 1;
+               hover = packet[1] & BIT(2);
+               palm = packet[1] & BIT(1);
+               state = packet[1] & BIT(0);
+               x = ((packet[2] & 0x0F) << 8) | packet[3];
+               y = (((packet[2] >> 4) & 0x0F) << 8) |
+                       packet[4];
+               size = packet[6];
+               pressure_stage = (packet[7] & 0xF0) >> 4;
+               pressure = ((packet[7] & 0x0F) << 8) |
+                       packet[8];
+               touch_major = packet[9];
+               touch_minor = packet[10];
+               break;
+       }
+
+       dev_dbg(&ts->client->dev,
+               "Screen - Slot: %d State: %d X: %04d Y: %04d Z: %d\n",
+               id, state, x, y, pressure);
+
+       if (unlikely(id < 0 || id >= MIP4_MAX_FINGERS)) {
+               dev_err(&ts->client->dev, "Screen - invalid slot ID: %d\n", id);
+       } else if (state) {
+               /* Press or Move event */
+               input_mt_slot(ts->input, id);
+               input_mt_report_slot_state(ts->input, MT_TOOL_FINGER, true);
+               input_report_abs(ts->input, ABS_MT_POSITION_X, x);
+               input_report_abs(ts->input, ABS_MT_POSITION_Y, y);
+               input_report_abs(ts->input, ABS_MT_PRESSURE, pressure);
+               input_report_abs(ts->input, ABS_MT_TOUCH_MAJOR, touch_major);
+               input_report_abs(ts->input, ABS_MT_TOUCH_MINOR, touch_minor);
+       } else {
+               /* Release event */
+               input_mt_slot(ts->input, id);
+               input_mt_report_slot_state(ts->input, MT_TOOL_FINGER, 0);
+       }
+
+       input_mt_sync_frame(ts->input);
+}
+
+static int mip4_handle_packet(struct mip4_ts *ts, u8 *packet)
+{
+       u8 type;
+
+       switch (ts->event_format) {
+       case 0:
+       case 1:
+               type = (packet[0] & 0x40) >> 6;
+               break;
+
+       case 3:
+               type = (packet[0] & 0xF0) >> 4;
+               break;
+
+       default:
+               /* Should not happen unless we have corrupted firmware */
+               return -EINVAL;
+       }
+
+       dev_dbg(&ts->client->dev, "Type: %d\n", type);
+
+       /* Report input event */
+       switch (type) {
+       case MIP4_EVENT_INPUT_TYPE_KEY:
+               mip4_report_keys(ts, packet);
+               break;
+
+       case MIP4_EVENT_INPUT_TYPE_SCREEN:
+               mip4_report_touch(ts, packet);
+               break;
+
+       default:
+               dev_err(&ts->client->dev, "Unknown event type: %d\n", type);
+               break;
+       }
+
+       return 0;
+}
+
+static irqreturn_t mip4_interrupt(int irq, void *dev_id)
+{
+       struct mip4_ts *ts = dev_id;
+       struct i2c_client *client = ts->client;
+       unsigned int i;
+       int error;
+       u8 cmd[2];
+       u8 size;
+       bool alert;
+
+       /* Read packet info */
+       cmd[0] = MIP4_R0_EVENT;
+       cmd[1] = MIP4_R1_EVENT_PACKET_INFO;
+       error = mip4_i2c_xfer(ts, cmd, sizeof(cmd), ts->buf, 1);
+       if (error) {
+               dev_err(&client->dev,
+                       "Failed to read packet info: %d\n", error);
+               goto out;
+       }
+
+       size = ts->buf[0] & 0x7F;
+       alert = ts->buf[0] & BIT(7);
+       dev_dbg(&client->dev, "packet size: %d, alert: %d\n", size, alert);
+
+       /* Check size */
+       if (!size) {
+               dev_err(&client->dev, "Empty packet\n");
+               goto out;
+       }
+
+       /* Read packet data */
+       cmd[0] = MIP4_R0_EVENT;
+       cmd[1] = MIP4_R1_EVENT_PACKET_DATA;
+       error = mip4_i2c_xfer(ts, cmd, sizeof(cmd), ts->buf, size);
+       if (error) {
+               dev_err(&client->dev,
+                       "Failed to read packet data: %d\n", error);
+               goto out;
+       }
+
+       if (alert) {
+               dev_dbg(&client->dev, "Alert: %d\n", ts->buf[0]);
+       } else {
+               for (i = 0; i < size; i += ts->event_size) {
+                       error = mip4_handle_packet(ts, &ts->buf[i]);
+                       if (error)
+                               break;
+               }
+
+               input_sync(ts->input);
+       }
+
+out:
+       return IRQ_HANDLED;
+}
+
+static int mip4_input_open(struct input_dev *dev)
+{
+       struct mip4_ts *ts = input_get_drvdata(dev);
+
+       return mip4_enable(ts);
+}
+
+static void mip4_input_close(struct input_dev *dev)
+{
+       struct mip4_ts *ts = input_get_drvdata(dev);
+
+       mip4_disable(ts);
+}
+
+/*****************************************************************
+ * Firmware update
+ *****************************************************************/
+
+/* Firmware Info */
+#define MIP4_BL_PAGE_SIZE              512     /* 512 */
+#define MIP4_BL_PACKET_SIZE            512     /* 512, 256, 128, 64, ... */
+
+/*
+ * Firmware binary tail info
+ */
+
+struct mip4_bin_tail {
+       u8 tail_mark[4];
+       u8 chip_name[4];
+
+       __le32 bin_start_addr;
+       __le32 bin_length;
+
+       __le16 ver_boot;
+       __le16 ver_core;
+       __le16 ver_app;
+       __le16 ver_param;
+
+       u8 boot_start;
+       u8 boot_end;
+       u8 core_start;
+       u8 core_end;
+       u8 app_start;
+       u8 app_end;
+       u8 param_start;
+       u8 param_end;
+
+       u8 checksum_type;
+       u8 hw_category;
+
+       __le16 param_id;
+       __le32 param_length;
+       __le32 build_date;
+       __le32 build_time;
+
+       __le32 reserved1;
+       __le32 reserved2;
+       __le16 reserved3;
+       __le16 tail_size;
+       __le32 crc;
+} __packed;
+
+#define MIP4_BIN_TAIL_MARK     "MBT\001"
+#define MIP4_BIN_TAIL_SIZE     (sizeof(struct mip4_bin_tail))
+
+/*
+* Bootloader - Read status
+*/
+static int mip4_bl_read_status(struct mip4_ts *ts)
+{
+       u8 cmd[] = { MIP4_R0_BOOT, MIP4_R1_BOOT_STATUS };
+       u8 result;
+       struct i2c_msg msg[] = {
+               {
+                       .addr = ts->client->addr,
+                       .flags = 0,
+                       .buf = cmd,
+                       .len = sizeof(cmd),
+               }, {
+                       .addr = ts->client->addr,
+                       .flags = I2C_M_RD,
+                       .buf = &result,
+                       .len = sizeof(result),
+               },
+       };
+       int ret;
+       int error;
+       int retry = 1000;
+
+       do {
+               ret = i2c_transfer(ts->client->adapter, msg, ARRAY_SIZE(msg));
+               if (ret != ARRAY_SIZE(msg)) {
+                       error = ret < 0 ? ret : -EIO;
+                       dev_err(&ts->client->dev,
+                               "Failed to read bootloader status: %d\n",
+                               error);
+                       return error;
+               }
+
+               switch (result) {
+               case MIP4_BOOT_STATUS_DONE:
+                       dev_dbg(&ts->client->dev, "%s - done\n", __func__);
+                       return 0;
+
+               case MIP4_BOOT_STATUS_ERROR:
+                       dev_err(&ts->client->dev, "Bootloader failure\n");
+                       return -EIO;
+
+               case MIP4_BOOT_STATUS_BUSY:
+                       dev_dbg(&ts->client->dev, "%s - Busy\n", __func__);
+                       error = -EBUSY;
+                       break;
+
+               default:
+                       dev_err(&ts->client->dev,
+                               "Unexpected bootloader status: %#02x\n",
+                               result);
+                       error = -EINVAL;
+                       break;
+               }
+
+               usleep_range(1000, 2000);
+       } while (--retry);
+
+       return error;
+}
+
+/*
+* Bootloader - Change mode
+*/
+static int mip4_bl_change_mode(struct mip4_ts *ts, u8 mode)
+{
+       u8 mode_chg_cmd[] = { MIP4_R0_BOOT, MIP4_R1_BOOT_MODE, mode };
+       u8 mode_read_cmd[] = { MIP4_R0_BOOT, MIP4_R1_BOOT_MODE };
+       u8 result;
+       struct i2c_msg msg[] = {
+               {
+                       .addr = ts->client->addr,
+                       .flags = 0,
+                       .buf = mode_read_cmd,
+                       .len = sizeof(mode_read_cmd),
+               }, {
+                       .addr = ts->client->addr,
+                       .flags = I2C_M_RD,
+                       .buf = &result,
+                       .len = sizeof(result),
+               },
+       };
+       int retry = 10;
+       int ret;
+       int error;
+
+       do {
+               /* Send mode change command */
+               ret = i2c_master_send(ts->client,
+                                     mode_chg_cmd, sizeof(mode_chg_cmd));
+               if (ret != sizeof(mode_chg_cmd)) {
+                       error = ret < 0 ? ret : -EIO;
+                       dev_err(&ts->client->dev,
+                               "Failed to send %d mode change: %d (%d)\n",
+                               mode, error, ret);
+                       return error;
+               }
+
+               dev_dbg(&ts->client->dev,
+                       "Sent mode change request (mode: %d)\n", mode);
+
+               /* Wait */
+               msleep(1000);
+
+               /* Verify target mode */
+               ret = i2c_transfer(ts->client->adapter, msg, ARRAY_SIZE(msg));
+               if (ret != ARRAY_SIZE(msg)) {
+                       error = ret < 0 ? ret : -EIO;
+                       dev_err(&ts->client->dev,
+                               "Failed to read device mode: %d\n", error);
+                       return error;
+               }
+
+               dev_dbg(&ts->client->dev,
+                       "Current device mode: %d, want: %d\n", result, mode);
+
+               if (result == mode)
+                       return 0;
+
+       } while (--retry);
+
+       return -EIO;
+}
+
+/*
+ * Bootloader - Start bootloader mode
+ */
+static int mip4_bl_enter(struct mip4_ts *ts)
+{
+       return mip4_bl_change_mode(ts, MIP4_BOOT_MODE_BOOT);
+}
+
+/*
+ * Bootloader - Exit bootloader mode
+ */
+static int mip4_bl_exit(struct mip4_ts *ts)
+{
+       return mip4_bl_change_mode(ts, MIP4_BOOT_MODE_APP);
+}
+
+static int mip4_bl_get_address(struct mip4_ts *ts, u16 *buf_addr)
+{
+       u8 cmd[] = { MIP4_R0_BOOT, MIP4_R1_BOOT_BUF_ADDR };
+       u8 result[sizeof(u16)];
+       struct i2c_msg msg[] = {
+               {
+                       .addr = ts->client->addr,
+                       .flags = 0,
+                       .buf = cmd,
+                       .len = sizeof(cmd),
+               }, {
+                       .addr = ts->client->addr,
+                       .flags = I2C_M_RD,
+                       .buf = result,
+                       .len = sizeof(result),
+               },
+       };
+       int ret;
+       int error;
+
+       ret = i2c_transfer(ts->client->adapter, msg, ARRAY_SIZE(msg));
+       if (ret != ARRAY_SIZE(msg)) {
+               error = ret < 0 ? ret : -EIO;
+               dev_err(&ts->client->dev,
+                       "Failed to retrieve bootloader buffer address: %d\n",
+                       error);
+               return error;
+       }
+
+       *buf_addr = get_unaligned_le16(result);
+       dev_dbg(&ts->client->dev,
+               "Bootloader buffer address %#04x\n", *buf_addr);
+
+       return 0;
+}
+
+static int mip4_bl_program_page(struct mip4_ts *ts, int offset,
+                               const u8 *data, int length, u16 buf_addr)
+{
+       u8 cmd[6];
+       u8 *data_buf;
+       u16 buf_offset;
+       int ret;
+       int error;
+
+       dev_dbg(&ts->client->dev, "Writing page @%#06x (%d)\n",
+               offset, length);
+
+       if (length > MIP4_BL_PAGE_SIZE || length % MIP4_BL_PACKET_SIZE) {
+               dev_err(&ts->client->dev,
+                       "Invalid page length: %d\n", length);
+               return -EINVAL;
+       }
+
+       data_buf = kmalloc(2 + MIP4_BL_PACKET_SIZE, GFP_KERNEL);
+       if (!data_buf)
+               return -ENOMEM;
+
+       /* Addr */
+       cmd[0] = MIP4_R0_BOOT;
+       cmd[1] = MIP4_R1_BOOT_TARGET_ADDR;
+       put_unaligned_le32(offset, &cmd[2]);
+       ret = i2c_master_send(ts->client, cmd, 6);
+       if (ret != 6) {
+               error = ret < 0 ? ret : -EIO;
+               dev_err(&ts->client->dev,
+                       "Failed to send write page address: %d\n", error);
+               goto out;
+       }
+
+       /* Size */
+       cmd[0] = MIP4_R0_BOOT;
+       cmd[1] = MIP4_R1_BOOT_SIZE;
+       put_unaligned_le32(length, &cmd[2]);
+       ret = i2c_master_send(ts->client, cmd, 6);
+       if (ret != 6) {
+               error = ret < 0 ? ret : -EIO;
+               dev_err(&ts->client->dev,
+                       "Failed to send write page size: %d\n", error);
+               goto out;
+       }
+
+       /* Data */
+       for (buf_offset = 0;
+            buf_offset < length;
+            buf_offset += MIP4_BL_PACKET_SIZE) {
+               dev_dbg(&ts->client->dev,
+                       "writing chunk at %#04x (size %d)\n",
+                       buf_offset, MIP4_BL_PACKET_SIZE);
+               put_unaligned_be16(buf_addr + buf_offset, data_buf);
+               memcpy(&data_buf[2], &data[buf_offset], MIP4_BL_PACKET_SIZE);
+               ret = i2c_master_send(ts->client,
+                                     data_buf, 2 + MIP4_BL_PACKET_SIZE);
+               if (ret != 2 + MIP4_BL_PACKET_SIZE) {
+                       error = ret < 0 ? ret : -EIO;
+                       dev_err(&ts->client->dev,
+                               "Failed to read chunk at %#04x (size %d): %d\n",
+                               buf_offset, MIP4_BL_PACKET_SIZE, error);
+                       goto out;
+               }
+       }
+
+       /* Command */
+       cmd[0] = MIP4_R0_BOOT;
+       cmd[1] = MIP4_R1_BOOT_CMD;
+       cmd[2] = MIP4_BOOT_CMD_PROGRAM;
+       ret = i2c_master_send(ts->client, cmd, 3);
+       if (ret != 3) {
+               error = ret < 0 ? ret : -EIO;
+               dev_err(&ts->client->dev,
+                       "Failed to send 'write' command: %d\n", error);
+               goto out;
+       }
+
+       /* Status */
+       error = mip4_bl_read_status(ts);
+
+out:
+       kfree(data_buf);
+       return error ? error : 0;
+}
+
+static int mip4_bl_verify_page(struct mip4_ts *ts, int offset,
+                              const u8 *data, int length, int buf_addr)
+{
+       u8 cmd[8];
+       u8 *read_buf;
+       int buf_offset;
+       struct i2c_msg msg[] = {
+               {
+                       .addr = ts->client->addr,
+                       .flags = 0,
+                       .buf = cmd,
+                       .len = 2,
+               }, {
+                       .addr = ts->client->addr,
+                       .flags = I2C_M_RD,
+                       .len = MIP4_BL_PACKET_SIZE,
+               },
+       };
+       int ret;
+       int error;
+
+       dev_dbg(&ts->client->dev, "Validating page @%#06x (%d)\n",
+               offset, length);
+
+       /* Addr */
+       cmd[0] = MIP4_R0_BOOT;
+       cmd[1] = MIP4_R1_BOOT_TARGET_ADDR;
+       put_unaligned_le32(offset, &cmd[2]);
+       ret = i2c_master_send(ts->client, cmd, 6);
+       if (ret != 6) {
+               error = ret < 0 ? ret : -EIO;
+               dev_err(&ts->client->dev,
+                       "Failed to send read page address: %d\n", error);
+               return error;
+       }
+
+       /* Size */
+       cmd[0] = MIP4_R0_BOOT;
+       cmd[1] = MIP4_R1_BOOT_SIZE;
+       put_unaligned_le32(length, &cmd[2]);
+       ret = i2c_master_send(ts->client, cmd, 6);
+       if (ret != 6) {
+               error = ret < 0 ? ret : -EIO;
+               dev_err(&ts->client->dev,
+                       "Failed to send read page size: %d\n", error);
+               return error;
+       }
+
+       /* Command */
+       cmd[0] = MIP4_R0_BOOT;
+       cmd[1] = MIP4_R1_BOOT_CMD;
+       cmd[2] = MIP4_BOOT_CMD_READ;
+       ret = i2c_master_send(ts->client, cmd, 3);
+       if (ret != 3) {
+               error = ret < 0 ? ret : -EIO;
+               dev_err(&ts->client->dev,
+                       "Failed to send 'read' command: %d\n", error);
+               return error;
+       }
+
+       /* Status */
+       error = mip4_bl_read_status(ts);
+       if (error)
+               return error;
+
+       /* Read */
+       msg[1].buf = read_buf = kmalloc(MIP4_BL_PACKET_SIZE, GFP_KERNEL);
+       if (!read_buf)
+               return -ENOMEM;
+
+       for (buf_offset = 0;
+            buf_offset < length;
+            buf_offset += MIP4_BL_PACKET_SIZE) {
+               dev_dbg(&ts->client->dev,
+                       "reading chunk at %#04x (size %d)\n",
+                       buf_offset, MIP4_BL_PACKET_SIZE);
+               put_unaligned_be16(buf_addr + buf_offset, cmd);
+               ret = i2c_transfer(ts->client->adapter, msg, ARRAY_SIZE(msg));
+               if (ret != ARRAY_SIZE(msg)) {
+                       error = ret < 0 ? ret : -EIO;
+                       dev_err(&ts->client->dev,
+                               "Failed to read chunk at %#04x (size %d): %d\n",
+                               buf_offset, MIP4_BL_PACKET_SIZE, error);
+                       break;
+               }
+
+               if (memcmp(&data[buf_offset], read_buf, MIP4_BL_PACKET_SIZE)) {
+                       dev_err(&ts->client->dev,
+                               "Failed to validate chunk at %#04x (size %d)\n",
+                               buf_offset, MIP4_BL_PACKET_SIZE);
+#if MIP4_FW_UPDATE_DEBUG
+                       print_hex_dump(KERN_DEBUG,
+                                      MIP4_DEVICE_NAME " F/W File: ",
+                                      DUMP_PREFIX_OFFSET, 16, 1,
+                                      data + offset, MIP4_BL_PACKET_SIZE,
+                                      false);
+                       print_hex_dump(KERN_DEBUG,
+                                      MIP4_DEVICE_NAME " F/W Chip: ",
+                                      DUMP_PREFIX_OFFSET, 16, 1,
+                                      read_buf, MIP4_BL_PAGE_SIZE, false);
+#endif
+                       error = -EINVAL;
+                       break;
+               }
+       }
+
+       kfree(read_buf);
+       return error ? error : 0;
+}
+
+/*
+ * Flash chip firmware
+ */
+static int mip4_flash_fw(struct mip4_ts *ts,
+                        const u8 *fw_data, u32 fw_size, u32 fw_offset)
+{
+       struct i2c_client *client = ts->client;
+       int offset;
+       u16 buf_addr;
+       int error, error2;
+
+       /* Enter bootloader mode */
+       dev_dbg(&client->dev, "Entering bootloader mode\n");
+
+       error = mip4_bl_enter(ts);
+       if (error) {
+               dev_err(&client->dev,
+                       "Failed to enter bootloader mode: %d\n",
+                       error);
+               return error;
+       }
+
+       /* Read info */
+       error = mip4_bl_get_address(ts, &buf_addr);
+       if (error)
+               goto exit_bl;
+
+       /* Program & Verify */
+       dev_dbg(&client->dev,
+               "Program & Verify, page size: %d, packet size: %d\n",
+               MIP4_BL_PAGE_SIZE, MIP4_BL_PACKET_SIZE);
+
+       for (offset = fw_offset;
+            offset < fw_offset + fw_size;
+            offset += MIP4_BL_PAGE_SIZE) {
+               /* Program */
+               error = mip4_bl_program_page(ts, offset, fw_data + offset,
+                                            MIP4_BL_PAGE_SIZE, buf_addr);
+               if (error)
+                       break;
+
+               /* Verify */
+               error = mip4_bl_verify_page(ts, offset, fw_data + offset,
+                                           MIP4_BL_PAGE_SIZE, buf_addr);
+               if (error)
+                       break;
+       }
+
+exit_bl:
+       /* Exit bootloader mode */
+       dev_dbg(&client->dev, "Exiting bootloader mode\n");
+
+       error2 = mip4_bl_exit(ts);
+       if (error2) {
+               dev_err(&client->dev,
+                       "Failed to exit bootloader mode: %d\n", error2);
+               if (!error)
+                       error = error2;
+       }
+
+       /* Reset chip */
+       mip4_power_off(ts);
+       mip4_power_on(ts);
+
+       mip4_query_device(ts);
+
+       /* Refresh device parameters */
+       input_set_abs_params(ts->input, ABS_MT_POSITION_X, 0, ts->max_x, 0, 0);
+       input_set_abs_params(ts->input, ABS_MT_POSITION_Y, 0, ts->max_y, 0, 0);
+       input_set_abs_params(ts->input, ABS_X, 0, ts->max_x, 0, 0);
+       input_set_abs_params(ts->input, ABS_Y, 0, ts->max_y, 0, 0);
+       input_abs_set_res(ts->input, ABS_MT_POSITION_X, ts->ppm_x);
+       input_abs_set_res(ts->input, ABS_MT_POSITION_Y, ts->ppm_y);
+       input_abs_set_res(ts->input, ABS_X, ts->ppm_x);
+       input_abs_set_res(ts->input, ABS_Y, ts->ppm_y);
+
+       return error ? error : 0;
+}
+
+static int mip4_parse_firmware(struct mip4_ts *ts, const struct firmware *fw,
+                              u32 *fw_offset_start, u32 *fw_size,
+                              const struct mip4_bin_tail **pfw_info)
+{
+       const struct mip4_bin_tail *fw_info;
+       struct mip4_fw_version fw_version;
+       u16 tail_size;
+
+       if (fw->size < MIP4_BIN_TAIL_SIZE) {
+               dev_err(&ts->client->dev,
+                       "Invalid firmware, size mismatch (tail %zd vs %zd)\n",
+                       MIP4_BIN_TAIL_SIZE, fw->size);
+               return -EINVAL;
+       }
+
+       fw_info = (const void *)&fw->data[fw->size - MIP4_BIN_TAIL_SIZE];
+
+#if MIP4_FW_UPDATE_DEBUG
+       print_hex_dump(KERN_ERR, MIP4_DEVICE_NAME " Bin Info: ",
+                      DUMP_PREFIX_OFFSET, 16, 1, *fw_info, tail_size, false);
+#endif
+
+       tail_size = get_unaligned_le16(&fw_info->tail_size);
+       if (tail_size != MIP4_BIN_TAIL_SIZE) {
+               dev_err(&ts->client->dev,
+                       "wrong tail size: %d (expected %zd)\n",
+                       tail_size, MIP4_BIN_TAIL_SIZE);
+               return -EINVAL;
+       }
+
+       /* Check bin format */
+       if (memcmp(fw_info->tail_mark, MIP4_BIN_TAIL_MARK,
+                  sizeof(fw_info->tail_mark))) {
+               dev_err(&ts->client->dev,
+                       "unable to locate tail marker (%*ph vs %*ph)\n",
+                       (int)sizeof(fw_info->tail_mark), fw_info->tail_mark,
+                       (int)sizeof(fw_info->tail_mark), MIP4_BIN_TAIL_MARK);
+               return -EINVAL;
+       }
+
+       *fw_offset_start = get_unaligned_le32(&fw_info->bin_start_addr);
+       *fw_size = get_unaligned_le32(&fw_info->bin_length);
+
+       dev_dbg(&ts->client->dev,
+               "F/W Data offset: %#08x, size: %d\n",
+               *fw_offset_start, *fw_size);
+
+       if (*fw_size % MIP4_BL_PAGE_SIZE) {
+               dev_err(&ts->client->dev,
+                       "encoded fw length %d is not multiple of pages (%d)\n",
+                       *fw_size, MIP4_BL_PAGE_SIZE);
+               return -EINVAL;
+       }
+
+       if (fw->size != *fw_offset_start + *fw_size) {
+               dev_err(&ts->client->dev,
+                       "Wrong firmware size, expected %d bytes, got %zd\n",
+                       *fw_offset_start + *fw_size, fw->size);
+               return -EINVAL;
+       }
+
+       mip4_parse_fw_version((const u8 *)&fw_info->ver_boot, &fw_version);
+
+       dev_dbg(&ts->client->dev,
+               "F/W file version %04X %04X %04X %04X\n",
+               fw_version.boot, fw_version.core,
+               fw_version.app, fw_version.param);
+
+       dev_dbg(&ts->client->dev, "F/W chip version: %04X %04X %04X %04X\n",
+                ts->fw_version.boot, ts->fw_version.core,
+                ts->fw_version.app, ts->fw_version.param);
+
+       /* Check F/W type */
+       if (fw_version.boot != 0xEEEE && fw_version.boot != 0xFFFF &&
+           fw_version.core == 0xEEEE &&
+           fw_version.app == 0xEEEE &&
+           fw_version.param == 0xEEEE) {
+               dev_dbg(&ts->client->dev, "F/W type: Bootloader\n");
+       } else if (fw_version.boot == 0xEEEE &&
+                  fw_version.core != 0xEEEE && fw_version.core != 0xFFFF &&
+                  fw_version.app != 0xEEEE && fw_version.app != 0xFFFF &&
+                  fw_version.param != 0xEEEE && fw_version.param != 0xFFFF) {
+               dev_dbg(&ts->client->dev, "F/W type: Main\n");
+       } else {
+               dev_err(&ts->client->dev, "Wrong firmware type\n");
+               return -EINVAL;
+       }
+
+       return 0;
+}
+
+static int mip4_execute_fw_update(struct mip4_ts *ts, const struct firmware *fw)
+{
+       const struct mip4_bin_tail *fw_info;
+       u32 fw_start_offset;
+       u32 fw_size;
+       int retires = 3;
+       int error;
+
+       error = mip4_parse_firmware(ts, fw,
+                                   &fw_start_offset, &fw_size, &fw_info);
+       if (error)
+               return error;
+
+       if (ts->input->users) {
+               disable_irq(ts->client->irq);
+       } else {
+               error = mip4_power_on(ts);
+               if (error)
+                       return error;
+       }
+
+       /* Update firmware */
+       do {
+               error = mip4_flash_fw(ts, fw->data, fw_size, fw_start_offset);
+               if (!error)
+                       break;
+       } while (--retires);
+
+       if (error)
+               dev_err(&ts->client->dev,
+                       "Failed to flash firmware: %d\n", error);
+
+       /* Enable IRQ */
+       if (ts->input->users)
+               enable_irq(ts->client->irq);
+       else
+               mip4_power_off(ts);
+
+       return error ? error : 0;
+}
+
+static ssize_t mip4_sysfs_fw_update(struct device *dev,
+                                   struct device_attribute *attr,
+                                   const char *buf, size_t count)
+{
+       struct i2c_client *client = to_i2c_client(dev);
+       struct mip4_ts *ts = i2c_get_clientdata(client);
+       const struct firmware *fw;
+       int error;
+
+       error = request_firmware(&fw, MIP4_FW_NAME, dev);
+       if (error) {
+               dev_err(&ts->client->dev,
+                       "Failed to retrieve firmware %s: %d\n",
+                       MIP4_FW_NAME, error);
+               return error;
+       }
+
+       /*
+        * Take input mutex to prevent racing with itself and also with
+        * userspace opening and closing the device and also suspend/resume
+        * transitions.
+        */
+       mutex_lock(&ts->input->mutex);
+
+       error = mip4_execute_fw_update(ts, fw);
+
+       mutex_unlock(&ts->input->mutex);
+
+       release_firmware(fw);
+
+       if (error) {
+               dev_err(&ts->client->dev,
+                       "Firmware update failed: %d\n", error);
+               return error;
+       }
+
+       return count;
+}
+
+static DEVICE_ATTR(update_fw, S_IWUSR, NULL, mip4_sysfs_fw_update);
+
+static ssize_t mip4_sysfs_read_fw_version(struct device *dev,
+                                         struct device_attribute *attr,
+                                         char *buf)
+{
+       struct i2c_client *client = to_i2c_client(dev);
+       struct mip4_ts *ts = i2c_get_clientdata(client);
+       size_t count;
+
+       /* Take lock to prevent racing with firmware update */
+       mutex_lock(&ts->input->mutex);
+
+       count = snprintf(buf, PAGE_SIZE, "%04X %04X %04X %04X\n",
+                        ts->fw_version.boot, ts->fw_version.core,
+                        ts->fw_version.app, ts->fw_version.param);
+
+       mutex_unlock(&ts->input->mutex);
+
+       return count;
+}
+
+static DEVICE_ATTR(fw_version, S_IRUGO, mip4_sysfs_read_fw_version, NULL);
+
+static struct attribute *mip4_attrs[] = {
+       &dev_attr_fw_version.attr,
+       &dev_attr_update_fw.attr,
+       NULL,
+};
+
+static const struct attribute_group mip4_attr_group = {
+       .attrs = mip4_attrs,
+};
+
+static void mip4_sysfs_remove(void *_data)
+{
+       struct mip4_ts *ts = _data;
+
+       sysfs_remove_group(&ts->client->dev.kobj, &mip4_attr_group);
+}
+
+static int mip4_probe(struct i2c_client *client, const struct i2c_device_id *id)
+{
+       struct mip4_ts *ts;
+       struct input_dev *input;
+       int error;
+
+       if (!i2c_check_functionality(client->adapter, I2C_FUNC_I2C)) {
+               dev_err(&client->dev, "Not supported I2C adapter\n");
+               return -ENXIO;
+       }
+
+       ts = devm_kzalloc(&client->dev, sizeof(*ts), GFP_KERNEL);
+       if (!ts)
+               return -ENOMEM;
+
+       input = devm_input_allocate_device(&client->dev);
+       if (!input)
+               return -ENOMEM;
+
+       ts->client = client;
+       ts->input = input;
+
+       snprintf(ts->phys, sizeof(ts->phys),
+                "%s/input0", dev_name(&client->dev));
+
+       ts->gpio_ce = devm_gpiod_get_optional(&client->dev,
+                                             "ce", GPIOD_OUT_LOW);
+       if (IS_ERR(ts->gpio_ce)) {
+               error = PTR_ERR(ts->gpio_ce);
+               if (error != EPROBE_DEFER)
+                       dev_err(&client->dev,
+                               "Failed to get gpio: %d\n", error);
+               return error;
+       }
+
+       error = mip4_power_on(ts);
+       if (error)
+               return error;
+       error = mip4_query_device(ts);
+       mip4_power_off(ts);
+       if (error)
+               return error;
+
+       input->name = "MELFAS MIP4 Touchscreen";
+       input->phys = ts->phys;
+
+       input->id.bustype = BUS_I2C;
+       input->id.vendor = 0x13c5;
+
+       input->open = mip4_input_open;
+       input->close = mip4_input_close;
+
+       input_set_drvdata(input, ts);
+
+       input->keycode = ts->key_code;
+       input->keycodesize = sizeof(*ts->key_code);
+       input->keycodemax = ts->key_num;
+
+       input_set_abs_params(input, ABS_MT_POSITION_X, 0, ts->max_x, 0, 0);
+       input_set_abs_params(input, ABS_MT_POSITION_Y, 0, ts->max_y, 0, 0);
+       input_set_abs_params(input, ABS_MT_PRESSURE,
+                            MIP4_PRESSURE_MIN, MIP4_PRESSURE_MAX, 0, 0);
+       input_set_abs_params(input, ABS_MT_TOUCH_MAJOR,
+                            MIP4_TOUCH_MAJOR_MIN, MIP4_TOUCH_MAJOR_MAX, 0, 0);
+       input_set_abs_params(input, ABS_MT_TOUCH_MINOR,
+                            MIP4_TOUCH_MINOR_MIN, MIP4_TOUCH_MINOR_MAX, 0, 0);
+       input_abs_set_res(ts->input, ABS_MT_POSITION_X, ts->ppm_x);
+       input_abs_set_res(ts->input, ABS_MT_POSITION_Y, ts->ppm_y);
+
+       error = input_mt_init_slots(input, MIP4_MAX_FINGERS, INPUT_MT_DIRECT);
+       if (error)
+               return error;
+
+       i2c_set_clientdata(client, ts);
+
+       error = devm_request_threaded_irq(&client->dev, client->irq,
+                                         NULL, mip4_interrupt,
+                                         IRQF_ONESHOT, MIP4_DEVICE_NAME, ts);
+       if (error) {
+               dev_err(&client->dev,
+                       "Failed to request interrupt %d: %d\n",
+                       client->irq, error);
+               return error;
+       }
+
+       disable_irq(client->irq);
+
+       error = input_register_device(input);
+       if (error) {
+               dev_err(&client->dev,
+                       "Failed to register input device: %d\n", error);
+               return error;
+       }
+
+       error = sysfs_create_group(&client->dev.kobj, &mip4_attr_group);
+       if (error) {
+               dev_err(&client->dev,
+                       "Failed to create sysfs attribute group: %d\n", error);
+               return error;
+       }
+
+       error = devm_add_action(&client->dev, mip4_sysfs_remove, ts);
+       if (error) {
+               mip4_sysfs_remove(ts);
+               dev_err(&client->dev,
+                       "Failed to install sysfs remoce action: %d\n", error);
+               return error;
+       }
+
+       return 0;
+}
+
+static int __maybe_unused mip4_suspend(struct device *dev)
+{
+       struct i2c_client *client = to_i2c_client(dev);
+       struct mip4_ts *ts = i2c_get_clientdata(client);
+       struct input_dev *input = ts->input;
+
+       mutex_lock(&input->mutex);
+
+       if (device_may_wakeup(dev))
+               ts->wake_irq_enabled = enable_irq_wake(client->irq) == 0;
+       else if (input->users)
+               mip4_disable(ts);
+
+       mutex_unlock(&input->mutex);
+
+       return 0;
+}
+
+static int __maybe_unused mip4_resume(struct device *dev)
+{
+       struct i2c_client *client = to_i2c_client(dev);
+       struct mip4_ts *ts = i2c_get_clientdata(client);
+       struct input_dev *input = ts->input;
+
+       mutex_lock(&input->mutex);
+
+       if (ts->wake_irq_enabled)
+               disable_irq_wake(client->irq);
+       else if (input->users)
+               mip4_enable(ts);
+
+       mutex_unlock(&input->mutex);
+
+       return 0;
+}
+
+static SIMPLE_DEV_PM_OPS(mip4_pm_ops, mip4_suspend, mip4_resume);
+
+#ifdef CONFIG_OF
+static const struct of_device_id mip4_of_match[] = {
+       { .compatible = "melfas,"MIP4_DEVICE_NAME, },
+       { },
+};
+MODULE_DEVICE_TABLE(of, mip4_of_match);
+#endif
+
+#ifdef CONFIG_ACPI
+static const struct acpi_device_id mip4_acpi_match[] = {
+       { "MLFS0000", 0},
+       { },
+};
+MODULE_DEVICE_TABLE(acpi, mip4_acpi_match);
+#endif
+
+static const struct i2c_device_id mip4_i2c_ids[] = {
+       { MIP4_DEVICE_NAME, 0 },
+       { },
+};
+MODULE_DEVICE_TABLE(i2c, mip4_i2c_ids);
+
+static struct i2c_driver mip4_driver = {
+       .id_table = mip4_i2c_ids,
+       .probe = mip4_probe,
+       .driver = {
+               .name = MIP4_DEVICE_NAME,
+               .of_match_table = of_match_ptr(mip4_of_match),
+               .acpi_match_table = ACPI_PTR(mip4_acpi_match),
+               .pm = &mip4_pm_ops,
+       },
+};
+module_i2c_driver(mip4_driver);
+
+MODULE_DESCRIPTION("MELFAS MIP4 Touchscreen");
+MODULE_VERSION("2016.03.03");
+MODULE_AUTHOR("Sangwon Jee <jeesw@melfas.com>");
+MODULE_LICENSE("GPL");
index 23a354a..0e3fc41 100644 (file)
@@ -87,7 +87,7 @@ static void pcap_ts_read_xy(void *data, u16 res[2])
 
 static void pcap_ts_work(struct work_struct *work)
 {
-       struct delayed_work *dw = container_of(work, struct delayed_work, work);
+       struct delayed_work *dw = to_delayed_work(work);
        struct pcap_ts *pcap_ts = container_of(dw, struct pcap_ts, work);
        u8 ch[2];
 
index 4b961ad..09523a3 100644 (file)
@@ -38,6 +38,8 @@ struct pixcir_i2c_ts_data {
        struct input_dev *input;
        struct gpio_desc *gpio_attb;
        struct gpio_desc *gpio_reset;
+       struct gpio_desc *gpio_enable;
+       struct gpio_desc *gpio_wake;
        const struct pixcir_i2c_chip_data *chip;
        int max_fingers;        /* Max fingers supported in this instance */
        bool running;
@@ -208,6 +210,11 @@ static int pixcir_set_power_mode(struct pixcir_i2c_ts_data *ts,
        struct device *dev = &ts->client->dev;
        int ret;
 
+       if (mode == PIXCIR_POWER_ACTIVE || mode == PIXCIR_POWER_IDLE) {
+               if (ts->gpio_wake)
+                       gpiod_set_value_cansleep(ts->gpio_wake, 1);
+       }
+
        ret = i2c_smbus_read_byte_data(ts->client, PIXCIR_REG_POWER_MODE);
        if (ret < 0) {
                dev_err(dev, "%s: can't read reg 0x%x : %d\n",
@@ -228,6 +235,11 @@ static int pixcir_set_power_mode(struct pixcir_i2c_ts_data *ts,
                return ret;
        }
 
+       if (mode == PIXCIR_POWER_HALT) {
+               if (ts->gpio_wake)
+                       gpiod_set_value_cansleep(ts->gpio_wake, 0);
+       }
+
        return 0;
 }
 
@@ -302,6 +314,11 @@ static int pixcir_start(struct pixcir_i2c_ts_data *ts)
        struct device *dev = &ts->client->dev;
        int error;
 
+       if (ts->gpio_enable) {
+               gpiod_set_value_cansleep(ts->gpio_enable, 1);
+               msleep(100);
+       }
+
        /* LEVEL_TOUCH interrupt with active low polarity */
        error = pixcir_set_int_mode(ts, PIXCIR_INT_LEVEL_TOUCH, 0);
        if (error) {
@@ -343,6 +360,9 @@ static int pixcir_stop(struct pixcir_i2c_ts_data *ts)
        /* Wait till running ISR is complete */
        synchronize_irq(ts->client->irq);
 
+       if (ts->gpio_enable)
+               gpiod_set_value_cansleep(ts->gpio_enable, 0);
+
        return 0;
 }
 
@@ -534,6 +554,27 @@ static int pixcir_i2c_ts_probe(struct i2c_client *client,
                return error;
        }
 
+       tsdata->gpio_wake = devm_gpiod_get_optional(dev, "wake",
+                                                   GPIOD_OUT_HIGH);
+       if (IS_ERR(tsdata->gpio_wake)) {
+               error = PTR_ERR(tsdata->gpio_wake);
+               if (error != -EPROBE_DEFER)
+                       dev_err(dev, "Failed to get wake gpio: %d\n", error);
+               return error;
+       }
+
+       tsdata->gpio_enable = devm_gpiod_get_optional(dev, "enable",
+                                                     GPIOD_OUT_HIGH);
+       if (IS_ERR(tsdata->gpio_enable)) {
+               error = PTR_ERR(tsdata->gpio_enable);
+               if (error != -EPROBE_DEFER)
+                       dev_err(dev, "Failed to get enable gpio: %d\n", error);
+               return error;
+       }
+
+       if (tsdata->gpio_enable)
+               msleep(100);
+
        error = devm_request_threaded_irq(dev, client->irq, NULL, pixcir_ts_isr,
                                          IRQF_TRIGGER_FALLING | IRQF_ONESHOT,
                                          client->name, tsdata);
index ba6024f..611156a 100644 (file)
@@ -725,7 +725,7 @@ static int rohm_ts_load_firmware(struct i2c_client *client,
                        break;
 
                error = -EIO;
-       } while (++retry >= FIRMWARE_RETRY_MAX);
+       } while (++retry <= FIRMWARE_RETRY_MAX);
 
 out:
        error2 = i2c_smbus_write_byte_data(client, INT_MASK, INT_ALL);
index e414d43..2a78e27 100644 (file)
 #define STMPE_TS_NAME                  "stmpe-ts"
 #define XY_MASK                                0xfff
 
+/**
+ * struct stmpe_touch - stmpe811 touch screen controller state
+ * @stmpe: pointer back to STMPE MFD container
+ * @idev: registered input device
+ * @work: a work item used to scan the device
+ * @dev: a pointer back to the MFD cell struct device*
+ * @sample_time: ADC converstion time in number of clock.
+ * (0 -> 36 clocks, 1 -> 44 clocks, 2 -> 56 clocks, 3 -> 64 clocks,
+ * 4 -> 80 clocks, 5 -> 96 clocks, 6 -> 144 clocks),
+ * recommended is 4.
+ * @mod_12b: ADC Bit mode (0 -> 10bit ADC, 1 -> 12bit ADC)
+ * @ref_sel: ADC reference source
+ * (0 -> internal reference, 1 -> external reference)
+ * @adc_freq: ADC Clock speed
+ * (0 -> 1.625 MHz, 1 -> 3.25 MHz, 2 || 3 -> 6.5 MHz)
+ * @ave_ctrl: Sample average control
+ * (0 -> 1 sample, 1 -> 2 samples, 2 -> 4 samples, 3 -> 8 samples)
+ * @touch_det_delay: Touch detect interrupt delay
+ * (0 -> 10 us, 1 -> 50 us, 2 -> 100 us, 3 -> 500 us,
+ * 4-> 1 ms, 5 -> 5 ms, 6 -> 10 ms, 7 -> 50 ms)
+ * recommended is 3
+ * @settling: Panel driver settling time
+ * (0 -> 10 us, 1 -> 100 us, 2 -> 500 us, 3 -> 1 ms,
+ * 4 -> 5 ms, 5 -> 10 ms, 6 for 50 ms, 7 -> 100 ms)
+ * recommended is 2
+ * @fraction_z: Length of the fractional part in z
+ * (fraction_z ([0..7]) = Count of the fractional part)
+ * recommended is 7
+ * @i_drive: current limit value of the touchscreen drivers
+ * (0 -> 20 mA typical 35 mA max, 1 -> 50 mA typical 80 mA max)
+ */
 struct stmpe_touch {
        struct stmpe *stmpe;
        struct input_dev *idev;
index 191a1b8..a21a07c 100644 (file)
@@ -273,8 +273,6 @@ static irqreturn_t titsc_irq(int irq, void *dev)
        status = titsc_readl(ts_dev, REG_RAWIRQSTATUS);
        if (status & IRQENB_HW_PEN) {
                ts_dev->pen_down = true;
-               titsc_writel(ts_dev, REG_IRQWAKEUP, 0x00);
-               titsc_writel(ts_dev, REG_IRQCLR, IRQENB_HW_PEN);
                irqclr |= IRQENB_HW_PEN;
        }
 
diff --git a/drivers/input/touchscreen/ts4800-ts.c b/drivers/input/touchscreen/ts4800-ts.c
new file mode 100644 (file)
index 0000000..3c3dd78
--- /dev/null
@@ -0,0 +1,216 @@
+/*
+ * Touchscreen driver for the TS-4800 board
+ *
+ * Copyright (c) 2015 - Savoir-faire Linux
+ *
+ * This file is licensed under the terms of the GNU General Public
+ * License version 2. This program is licensed "as is" without any
+ * warranty of any kind, whether express or implied.
+ */
+
+#include <linux/bitops.h>
+#include <linux/input.h>
+#include <linux/input-polldev.h>
+#include <linux/io.h>
+#include <linux/kernel.h>
+#include <linux/mfd/syscon.h>
+#include <linux/module.h>
+#include <linux/of.h>
+#include <linux/platform_device.h>
+#include <linux/regmap.h>
+
+/* polling interval in ms */
+#define POLL_INTERVAL          3
+
+#define DEBOUNCE_COUNT         1
+
+/* sensor values are 12-bit wide */
+#define MAX_12BIT              ((1 << 12) - 1)
+
+#define PENDOWN_MASK           0x1
+
+#define X_OFFSET               0x0
+#define Y_OFFSET               0x2
+
+struct ts4800_ts {
+       struct input_polled_dev *poll_dev;
+       struct device           *dev;
+       char                    phys[32];
+
+       void __iomem            *base;
+       struct regmap           *regmap;
+       unsigned int            reg;
+       unsigned int            bit;
+
+       bool                    pendown;
+       int                     debounce;
+};
+
+static void ts4800_ts_open(struct input_polled_dev *dev)
+{
+       struct ts4800_ts *ts = dev->private;
+       int ret;
+
+       ts->pendown = false;
+       ts->debounce = DEBOUNCE_COUNT;
+
+       ret = regmap_update_bits(ts->regmap, ts->reg, ts->bit, ts->bit);
+       if (ret)
+               dev_warn(ts->dev, "Failed to enable touchscreen\n");
+}
+
+static void ts4800_ts_close(struct input_polled_dev *dev)
+{
+       struct ts4800_ts *ts = dev->private;
+       int ret;
+
+       ret = regmap_update_bits(ts->regmap, ts->reg, ts->bit, 0);
+       if (ret)
+               dev_warn(ts->dev, "Failed to disable touchscreen\n");
+
+}
+
+static void ts4800_ts_poll(struct input_polled_dev *dev)
+{
+       struct input_dev *input_dev = dev->input;
+       struct ts4800_ts *ts = dev->private;
+       u16 last_x = readw(ts->base + X_OFFSET);
+       u16 last_y = readw(ts->base + Y_OFFSET);
+       bool pendown = last_x & PENDOWN_MASK;
+
+       if (pendown) {
+               if (ts->debounce) {
+                       ts->debounce--;
+                       return;
+               }
+
+               if (!ts->pendown) {
+                       input_report_key(input_dev, BTN_TOUCH, 1);
+                       ts->pendown = true;
+               }
+
+               last_x = ((~last_x) >> 4) & MAX_12BIT;
+               last_y = ((~last_y) >> 4) & MAX_12BIT;
+
+               input_report_abs(input_dev, ABS_X, last_x);
+               input_report_abs(input_dev, ABS_Y, last_y);
+               input_sync(input_dev);
+       } else if (ts->pendown) {
+               ts->pendown = false;
+               ts->debounce = DEBOUNCE_COUNT;
+               input_report_key(input_dev, BTN_TOUCH, 0);
+               input_sync(input_dev);
+       }
+}
+
+static int ts4800_parse_dt(struct platform_device *pdev,
+                          struct ts4800_ts *ts)
+{
+       struct device *dev = &pdev->dev;
+       struct device_node *np = dev->of_node;
+       struct device_node *syscon_np;
+       u32 reg, bit;
+       int error;
+
+       syscon_np = of_parse_phandle(np, "syscon", 0);
+       if (!syscon_np) {
+               dev_err(dev, "no syscon property\n");
+               return -ENODEV;
+       }
+
+       error = of_property_read_u32_index(np, "syscon", 1, &reg);
+       if (error < 0) {
+               dev_err(dev, "no offset in syscon\n");
+               return error;
+       }
+
+       ts->reg = reg;
+
+       error = of_property_read_u32_index(np, "syscon", 2, &bit);
+       if (error < 0) {
+               dev_err(dev, "no bit in syscon\n");
+               return error;
+       }
+
+       ts->bit = BIT(bit);
+
+       ts->regmap = syscon_node_to_regmap(syscon_np);
+       if (IS_ERR(ts->regmap)) {
+               dev_err(dev, "cannot get parent's regmap\n");
+               return PTR_ERR(ts->regmap);
+       }
+
+       return 0;
+}
+
+static int ts4800_ts_probe(struct platform_device *pdev)
+{
+       struct input_polled_dev *poll_dev;
+       struct ts4800_ts *ts;
+       struct resource *res;
+       int error;
+
+       ts = devm_kzalloc(&pdev->dev, sizeof(*ts), GFP_KERNEL);
+       if (!ts)
+               return -ENOMEM;
+
+       error = ts4800_parse_dt(pdev, ts);
+       if (error)
+               return error;
+
+       res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+       ts->base = devm_ioremap_resource(&pdev->dev, res);
+       if (IS_ERR(ts->base))
+               return PTR_ERR(ts->base);
+
+       poll_dev = devm_input_allocate_polled_device(&pdev->dev);
+       if (!poll_dev)
+               return -ENOMEM;
+
+       snprintf(ts->phys, sizeof(ts->phys), "%s/input0", dev_name(&pdev->dev));
+       ts->poll_dev = poll_dev;
+       ts->dev = &pdev->dev;
+
+       poll_dev->private = ts;
+       poll_dev->poll_interval = POLL_INTERVAL;
+       poll_dev->open = ts4800_ts_open;
+       poll_dev->close = ts4800_ts_close;
+       poll_dev->poll = ts4800_ts_poll;
+
+       poll_dev->input->name = "TS-4800 Touchscreen";
+       poll_dev->input->phys = ts->phys;
+
+       input_set_capability(poll_dev->input, EV_KEY, BTN_TOUCH);
+       input_set_abs_params(poll_dev->input, ABS_X, 0, MAX_12BIT, 0, 0);
+       input_set_abs_params(poll_dev->input, ABS_Y, 0, MAX_12BIT, 0, 0);
+
+       error = input_register_polled_device(poll_dev);
+       if (error) {
+               dev_err(&pdev->dev,
+                       "Unabled to register polled input device (%d)\n",
+                       error);
+               return error;
+       }
+
+       return 0;
+}
+
+static const struct of_device_id ts4800_ts_of_match[] = {
+       { .compatible = "technologic,ts4800-ts", },
+       { },
+};
+MODULE_DEVICE_TABLE(of, ts4800_ts_of_match);
+
+static struct platform_driver ts4800_ts_driver = {
+       .driver = {
+               .name = "ts4800-ts",
+               .of_match_table = ts4800_ts_of_match,
+       },
+       .probe = ts4800_ts_probe,
+};
+module_platform_driver(ts4800_ts_driver);
+
+MODULE_AUTHOR("Damien Riegel <damien.riegel@savoirfairelinux.com>");
+MODULE_DESCRIPTION("TS-4800 Touchscreen Driver");
+MODULE_LICENSE("GPL v2");
+MODULE_ALIAS("platform:ts4800_ts");
index 2792ca3..bab3c6a 100644 (file)
@@ -80,7 +80,8 @@ struct w8001_touch_query {
  */
 
 struct w8001 {
-       struct input_dev *dev;
+       struct input_dev *pen_dev;
+       struct input_dev *touch_dev;
        struct serio *serio;
        struct completion cmd_done;
        int id;
@@ -95,7 +96,10 @@ struct w8001 {
        u16 max_touch_y;
        u16 max_pen_x;
        u16 max_pen_y;
-       char name[64];
+       char pen_name[64];
+       char touch_name[64];
+       int open_count;
+       struct mutex mutex;
 };
 
 static void parse_pen_data(u8 *data, struct w8001_coord *coord)
@@ -141,7 +145,7 @@ static void scale_touch_coordinates(struct w8001 *w8001,
 
 static void parse_multi_touch(struct w8001 *w8001)
 {
-       struct input_dev *dev = w8001->dev;
+       struct input_dev *dev = w8001->touch_dev;
        unsigned char *data = w8001->data;
        unsigned int x, y;
        int i;
@@ -151,7 +155,6 @@ static void parse_multi_touch(struct w8001 *w8001)
                bool touch = data[0] & (1 << i);
 
                input_mt_slot(dev, i);
-               input_mt_report_slot_state(dev, MT_TOOL_FINGER, touch);
                if (touch) {
                        x = (data[6 * i + 1] << 7) | data[6 * i + 2];
                        y = (data[6 * i + 3] << 7) | data[6 * i + 4];
@@ -207,7 +210,7 @@ static void parse_touchquery(u8 *data, struct w8001_touch_query *query)
 
 static void report_pen_events(struct w8001 *w8001, struct w8001_coord *coord)
 {
-       struct input_dev *dev = w8001->dev;
+       struct input_dev *dev = w8001->pen_dev;
 
        /*
         * We have 1 bit for proximity (rdy) and 3 bits for tip, side,
@@ -233,11 +236,6 @@ static void report_pen_events(struct w8001 *w8001, struct w8001_coord *coord)
                break;
 
        case BTN_TOOL_FINGER:
-               input_report_key(dev, BTN_TOUCH, 0);
-               input_report_key(dev, BTN_TOOL_FINGER, 0);
-               input_sync(dev);
-               /* fall through */
-
        case KEY_RESERVED:
                w8001->type = coord->f2 ? BTN_TOOL_RUBBER : BTN_TOOL_PEN;
                break;
@@ -261,7 +259,7 @@ static void report_pen_events(struct w8001 *w8001, struct w8001_coord *coord)
 
 static void report_single_touch(struct w8001 *w8001, struct w8001_coord *coord)
 {
-       struct input_dev *dev = w8001->dev;
+       struct input_dev *dev = w8001->touch_dev;
        unsigned int x = coord->x;
        unsigned int y = coord->y;
 
@@ -271,7 +269,6 @@ static void report_single_touch(struct w8001 *w8001, struct w8001_coord *coord)
        input_report_abs(dev, ABS_X, x);
        input_report_abs(dev, ABS_Y, y);
        input_report_key(dev, BTN_TOUCH, coord->tsw);
-       input_report_key(dev, BTN_TOOL_FINGER, coord->tsw);
 
        input_sync(dev);
 
@@ -369,22 +366,36 @@ static int w8001_command(struct w8001 *w8001, unsigned char command,
 static int w8001_open(struct input_dev *dev)
 {
        struct w8001 *w8001 = input_get_drvdata(dev);
+       int err;
 
-       return w8001_command(w8001, W8001_CMD_START, false);
+       err = mutex_lock_interruptible(&w8001->mutex);
+       if (err)
+               return err;
+
+       if (w8001->open_count++ == 0) {
+               err = w8001_command(w8001, W8001_CMD_START, false);
+               if (err)
+                       w8001->open_count--;
+       }
+
+       mutex_unlock(&w8001->mutex);
+       return err;
 }
 
 static void w8001_close(struct input_dev *dev)
 {
        struct w8001 *w8001 = input_get_drvdata(dev);
 
-       w8001_command(w8001, W8001_CMD_STOP, false);
+       mutex_lock(&w8001->mutex);
+
+       if (--w8001->open_count == 0)
+               w8001_command(w8001, W8001_CMD_STOP, false);
+
+       mutex_unlock(&w8001->mutex);
 }
 
-static int w8001_setup(struct w8001 *w8001)
+static int w8001_detect(struct w8001 *w8001)
 {
-       struct input_dev *dev = w8001->dev;
-       struct w8001_coord coord;
-       struct w8001_touch_query touch;
        int error;
 
        error = w8001_command(w8001, W8001_CMD_STOP, false);
@@ -393,105 +404,145 @@ static int w8001_setup(struct w8001 *w8001)
 
        msleep(250);    /* wait 250ms before querying the device */
 
-       dev->evbit[0] = BIT_MASK(EV_KEY) | BIT_MASK(EV_ABS);
-       strlcat(w8001->name, "Wacom Serial", sizeof(w8001->name));
+       return 0;
+}
 
-       __set_bit(INPUT_PROP_DIRECT, dev->propbit);
+static int w8001_setup_pen(struct w8001 *w8001, char *basename,
+                          size_t basename_sz)
+{
+       struct input_dev *dev = w8001->pen_dev;
+       struct w8001_coord coord;
+       int error;
 
        /* penabled? */
        error = w8001_command(w8001, W8001_CMD_QUERY, true);
-       if (!error) {
-               __set_bit(BTN_TOUCH, dev->keybit);
-               __set_bit(BTN_TOOL_PEN, dev->keybit);
-               __set_bit(BTN_TOOL_RUBBER, dev->keybit);
-               __set_bit(BTN_STYLUS, dev->keybit);
-               __set_bit(BTN_STYLUS2, dev->keybit);
-
-               parse_pen_data(w8001->response, &coord);
-               w8001->max_pen_x = coord.x;
-               w8001->max_pen_y = coord.y;
-
-               input_set_abs_params(dev, ABS_X, 0, coord.x, 0, 0);
-               input_set_abs_params(dev, ABS_Y, 0, coord.y, 0, 0);
-               input_abs_set_res(dev, ABS_X, W8001_PEN_RESOLUTION);
-               input_abs_set_res(dev, ABS_Y, W8001_PEN_RESOLUTION);
-               input_set_abs_params(dev, ABS_PRESSURE, 0, coord.pen_pressure, 0, 0);
-               if (coord.tilt_x && coord.tilt_y) {
-                       input_set_abs_params(dev, ABS_TILT_X, 0, coord.tilt_x, 0, 0);
-                       input_set_abs_params(dev, ABS_TILT_Y, 0, coord.tilt_y, 0, 0);
-               }
-               w8001->id = 0x90;
-               strlcat(w8001->name, " Penabled", sizeof(w8001->name));
+       if (error)
+               return error;
+
+       __set_bit(EV_KEY, dev->evbit);
+       __set_bit(EV_ABS, dev->evbit);
+       __set_bit(BTN_TOUCH, dev->keybit);
+       __set_bit(BTN_TOOL_PEN, dev->keybit);
+       __set_bit(BTN_TOOL_RUBBER, dev->keybit);
+       __set_bit(BTN_STYLUS, dev->keybit);
+       __set_bit(BTN_STYLUS2, dev->keybit);
+       __set_bit(INPUT_PROP_DIRECT, dev->propbit);
+
+       parse_pen_data(w8001->response, &coord);
+       w8001->max_pen_x = coord.x;
+       w8001->max_pen_y = coord.y;
+
+       input_set_abs_params(dev, ABS_X, 0, coord.x, 0, 0);
+       input_set_abs_params(dev, ABS_Y, 0, coord.y, 0, 0);
+       input_abs_set_res(dev, ABS_X, W8001_PEN_RESOLUTION);
+       input_abs_set_res(dev, ABS_Y, W8001_PEN_RESOLUTION);
+       input_set_abs_params(dev, ABS_PRESSURE, 0, coord.pen_pressure, 0, 0);
+       if (coord.tilt_x && coord.tilt_y) {
+               input_set_abs_params(dev, ABS_TILT_X, 0, coord.tilt_x, 0, 0);
+               input_set_abs_params(dev, ABS_TILT_Y, 0, coord.tilt_y, 0, 0);
        }
 
+       w8001->id = 0x90;
+       strlcat(basename, " Penabled", basename_sz);
+
+       return 0;
+}
+
+static int w8001_setup_touch(struct w8001 *w8001, char *basename,
+                            size_t basename_sz)
+{
+       struct input_dev *dev = w8001->touch_dev;
+       struct w8001_touch_query touch;
+       int error;
+
+
        /* Touch enabled? */
        error = w8001_command(w8001, W8001_CMD_TOUCHQUERY, true);
-
+       if (error)
+               return error;
        /*
         * Some non-touch devices may reply to the touch query. But their
         * second byte is empty, which indicates touch is not supported.
         */
-       if (!error && w8001->response[1]) {
-               __set_bit(BTN_TOUCH, dev->keybit);
-               __set_bit(BTN_TOOL_FINGER, dev->keybit);
-
-               parse_touchquery(w8001->response, &touch);
-               w8001->max_touch_x = touch.x;
-               w8001->max_touch_y = touch.y;
-
-               if (w8001->max_pen_x && w8001->max_pen_y) {
-                       /* if pen is supported scale to pen maximum */
-                       touch.x = w8001->max_pen_x;
-                       touch.y = w8001->max_pen_y;
-                       touch.panel_res = W8001_PEN_RESOLUTION;
-               }
+       if (!w8001->response[1])
+               return -ENXIO;
 
-               input_set_abs_params(dev, ABS_X, 0, touch.x, 0, 0);
-               input_set_abs_params(dev, ABS_Y, 0, touch.y, 0, 0);
-               input_abs_set_res(dev, ABS_X, touch.panel_res);
-               input_abs_set_res(dev, ABS_Y, touch.panel_res);
-
-               switch (touch.sensor_id) {
-               case 0:
-               case 2:
-                       w8001->pktlen = W8001_PKTLEN_TOUCH93;
-                       w8001->id = 0x93;
-                       strlcat(w8001->name, " 1FG", sizeof(w8001->name));
-                       break;
+       __set_bit(EV_KEY, dev->evbit);
+       __set_bit(EV_ABS, dev->evbit);
+       __set_bit(BTN_TOUCH, dev->keybit);
+       __set_bit(INPUT_PROP_DIRECT, dev->propbit);
 
-               case 1:
-               case 3:
-               case 4:
-                       w8001->pktlen = W8001_PKTLEN_TOUCH9A;
-                       strlcat(w8001->name, " 1FG", sizeof(w8001->name));
-                       w8001->id = 0x9a;
-                       break;
+       parse_touchquery(w8001->response, &touch);
+       w8001->max_touch_x = touch.x;
+       w8001->max_touch_y = touch.y;
 
-               case 5:
-                       w8001->pktlen = W8001_PKTLEN_TOUCH2FG;
-
-                       input_mt_init_slots(dev, 2, 0);
-                       input_set_abs_params(dev, ABS_MT_POSITION_X,
-                                               0, touch.x, 0, 0);
-                       input_set_abs_params(dev, ABS_MT_POSITION_Y,
-                                               0, touch.y, 0, 0);
-                       input_set_abs_params(dev, ABS_MT_TOOL_TYPE,
-                                               0, MT_TOOL_MAX, 0, 0);
-
-                       strlcat(w8001->name, " 2FG", sizeof(w8001->name));
-                       if (w8001->max_pen_x && w8001->max_pen_y)
-                               w8001->id = 0xE3;
-                       else
-                               w8001->id = 0xE2;
-                       break;
-               }
+       if (w8001->max_pen_x && w8001->max_pen_y) {
+               /* if pen is supported scale to pen maximum */
+               touch.x = w8001->max_pen_x;
+               touch.y = w8001->max_pen_y;
+               touch.panel_res = W8001_PEN_RESOLUTION;
+       }
+
+       input_set_abs_params(dev, ABS_X, 0, touch.x, 0, 0);
+       input_set_abs_params(dev, ABS_Y, 0, touch.y, 0, 0);
+       input_abs_set_res(dev, ABS_X, touch.panel_res);
+       input_abs_set_res(dev, ABS_Y, touch.panel_res);
+
+       switch (touch.sensor_id) {
+       case 0:
+       case 2:
+               w8001->pktlen = W8001_PKTLEN_TOUCH93;
+               w8001->id = 0x93;
+               strlcat(basename, " 1FG", basename_sz);
+               break;
+
+       case 1:
+       case 3:
+       case 4:
+               w8001->pktlen = W8001_PKTLEN_TOUCH9A;
+               strlcat(basename, " 1FG", basename_sz);
+               w8001->id = 0x9a;
+               break;
+
+       case 5:
+               w8001->pktlen = W8001_PKTLEN_TOUCH2FG;
+
+               __set_bit(BTN_TOOL_DOUBLETAP, dev->keybit);
+               input_mt_init_slots(dev, 2, 0);
+               input_set_abs_params(dev, ABS_MT_POSITION_X,
+                                       0, touch.x, 0, 0);
+               input_set_abs_params(dev, ABS_MT_POSITION_Y,
+                                       0, touch.y, 0, 0);
+
+               strlcat(basename, " 2FG", basename_sz);
+               if (w8001->max_pen_x && w8001->max_pen_y)
+                       w8001->id = 0xE3;
+               else
+                       w8001->id = 0xE2;
+               break;
        }
 
-       strlcat(w8001->name, " Touchscreen", sizeof(w8001->name));
+       strlcat(basename, " Touchscreen", basename_sz);
 
        return 0;
 }
 
+static void w8001_set_devdata(struct input_dev *dev, struct w8001 *w8001,
+                             struct serio *serio)
+{
+       dev->phys = w8001->phys;
+       dev->id.bustype = BUS_RS232;
+       dev->id.product = w8001->id;
+       dev->id.vendor = 0x056a;
+       dev->id.version = 0x0100;
+       dev->open = w8001_open;
+       dev->close = w8001_close;
+
+       dev->dev.parent = &serio->dev;
+
+       input_set_drvdata(dev, w8001);
+}
+
 /*
  * w8001_disconnect() is the opposite of w8001_connect()
  */
@@ -502,7 +553,10 @@ static void w8001_disconnect(struct serio *serio)
 
        serio_close(serio);
 
-       input_unregister_device(w8001->dev);
+       if (w8001->pen_dev)
+               input_unregister_device(w8001->pen_dev);
+       if (w8001->touch_dev)
+               input_unregister_device(w8001->touch_dev);
        kfree(w8001);
 
        serio_set_drvdata(serio, NULL);
@@ -517,18 +571,23 @@ static void w8001_disconnect(struct serio *serio)
 static int w8001_connect(struct serio *serio, struct serio_driver *drv)
 {
        struct w8001 *w8001;
-       struct input_dev *input_dev;
-       int err;
+       struct input_dev *input_dev_pen;
+       struct input_dev *input_dev_touch;
+       char basename[64];
+       int err, err_pen, err_touch;
 
        w8001 = kzalloc(sizeof(struct w8001), GFP_KERNEL);
-       input_dev = input_allocate_device();
-       if (!w8001 || !input_dev) {
+       input_dev_pen = input_allocate_device();
+       input_dev_touch = input_allocate_device();
+       if (!w8001 || !input_dev_pen || !input_dev_touch) {
                err = -ENOMEM;
                goto fail1;
        }
 
        w8001->serio = serio;
-       w8001->dev = input_dev;
+       w8001->pen_dev = input_dev_pen;
+       w8001->touch_dev = input_dev_touch;
+       mutex_init(&w8001->mutex);
        init_completion(&w8001->cmd_done);
        snprintf(w8001->phys, sizeof(w8001->phys), "%s/input0", serio->phys);
 
@@ -537,35 +596,67 @@ static int w8001_connect(struct serio *serio, struct serio_driver *drv)
        if (err)
                goto fail2;
 
-       err = w8001_setup(w8001);
+       err = w8001_detect(w8001);
        if (err)
                goto fail3;
 
-       input_dev->name = w8001->name;
-       input_dev->phys = w8001->phys;
-       input_dev->id.product = w8001->id;
-       input_dev->id.bustype = BUS_RS232;
-       input_dev->id.vendor = 0x056a;
-       input_dev->id.version = 0x0100;
-       input_dev->dev.parent = &serio->dev;
+       /* For backwards-compatibility we compose the basename based on
+        * capabilities and then just append the tool type
+        */
+       strlcpy(basename, "Wacom Serial", sizeof(basename));
+
+       err_pen = w8001_setup_pen(w8001, basename, sizeof(basename));
+       err_touch = w8001_setup_touch(w8001, basename, sizeof(basename));
+       if (err_pen && err_touch) {
+               err = -ENXIO;
+               goto fail3;
+       }
+
+       if (!err_pen) {
+               strlcpy(w8001->pen_name, basename, sizeof(w8001->pen_name));
+               strlcat(w8001->pen_name, " Pen", sizeof(w8001->pen_name));
+               input_dev_pen->name = w8001->pen_name;
 
-       input_dev->open = w8001_open;
-       input_dev->close = w8001_close;
+               w8001_set_devdata(input_dev_pen, w8001, serio);
 
-       input_set_drvdata(input_dev, w8001);
+               err = input_register_device(w8001->pen_dev);
+               if (err)
+                       goto fail3;
+       } else {
+               input_free_device(input_dev_pen);
+               input_dev_pen = NULL;
+               w8001->pen_dev = NULL;
+       }
 
-       err = input_register_device(w8001->dev);
-       if (err)
-               goto fail3;
+       if (!err_touch) {
+               strlcpy(w8001->touch_name, basename, sizeof(w8001->touch_name));
+               strlcat(w8001->touch_name, " Finger",
+                       sizeof(w8001->touch_name));
+               input_dev_touch->name = w8001->touch_name;
+
+               w8001_set_devdata(input_dev_touch, w8001, serio);
+
+               err = input_register_device(w8001->touch_dev);
+               if (err)
+                       goto fail4;
+       } else {
+               input_free_device(input_dev_touch);
+               input_dev_touch = NULL;
+               w8001->touch_dev = NULL;
+       }
 
        return 0;
 
+fail4:
+       if (w8001->pen_dev)
+               input_unregister_device(w8001->pen_dev);
 fail3:
        serio_close(serio);
 fail2:
        serio_set_drvdata(serio, NULL);
 fail1:
-       input_free_device(input_dev);
+       input_free_device(input_dev_pen);
+       input_free_device(input_dev_touch);
        kfree(w8001);
        return err;
 }
index 5af7c66..586c8c9 100644 (file)
 /* Active distance in pixels for a gesture to be reported */
 #define CY_ACT_DIST_DFLT 0xF8 /* pixels */
 
-struct cyttsp_platform_data {
-       u32 maxx;
-       u32 maxy;
-       bool use_hndshk;
-       u8 act_dist;    /* Active distance */
-       u8 act_intrvl;  /* Active refresh interval; ms */
-       u8 tch_tmout;   /* Active touch timeout; ms */
-       u8 lp_intrvl;   /* Low power refresh interval; ms */
-       int (*init)(void);
-       void (*exit)(void);
-       char *name;
-       s16 irq_gpio;
-       u8 *bl_keys;
-};
-
 #endif /* _CYTTSP_H_ */
index 0994c0d..75de43d 100644 (file)
  * Author: Aristeu Sergio Rozanski Filho <aris@cathedrallabs.org>
  *
  * Changes/Revisions:
+ *     0.5     08/13/2015 (David Herrmann <dh.herrmann@gmail.com> &
+ *                         Benjamin Tissoires <benjamin.tissoires@redhat.com>)
+ *             - add UI_DEV_SETUP ioctl
+ *             - add UI_ABS_SETUP ioctl
+ *             - add UI_GET_VERSION ioctl
  *     0.4     01/09/2014 (Benjamin Tissoires <benjamin.tissoires@redhat.com>)
  *             - add UI_GET_SYSNAME ioctl
  *     0.3     24/05/2006 (Anssi Hannula <anssi.hannulagmail.com>)
index becdd78..c2ea169 100644 (file)
@@ -77,5 +77,6 @@
 #define SERIO_PS2MULT  0x3c
 #define SERIO_TSC40    0x3d
 #define SERIO_WACOM_IV 0x3e
+#define SERIO_EGALAX   0x3f
 
 #endif /* _UAPI_SERIO_H */
index 013c9d8..dc652e2 100644 (file)
  * Author: Aristeu Sergio Rozanski Filho <aris@cathedrallabs.org>
  *
  * Changes/Revisions:
+ *     0.5     08/13/2015 (David Herrmann <dh.herrmann@gmail.com> &
+ *                         Benjamin Tissoires <benjamin.tissoires@redhat.com>)
+ *             - add UI_DEV_SETUP ioctl
+ *             - add UI_ABS_SETUP ioctl
+ *             - add UI_GET_VERSION ioctl
  *     0.4     01/09/2014 (Benjamin Tissoires <benjamin.tissoires@redhat.com>)
  *             - add UI_GET_SYSNAME ioctl
  *     0.3     24/05/2006 (Anssi Hannula <anssi.hannulagmail.com>)
@@ -37,8 +42,8 @@
 #include <linux/types.h>
 #include <linux/input.h>
 
-#define UINPUT_VERSION         4
-
+#define UINPUT_VERSION         5
+#define UINPUT_MAX_NAME_SIZE   80
 
 struct uinput_ff_upload {
        __u32                   request_id;
@@ -58,6 +63,76 @@ struct uinput_ff_erase {
 #define UI_DEV_CREATE          _IO(UINPUT_IOCTL_BASE, 1)
 #define UI_DEV_DESTROY         _IO(UINPUT_IOCTL_BASE, 2)
 
+struct uinput_setup {
+       struct input_id id;
+       char name[UINPUT_MAX_NAME_SIZE];
+       __u32 ff_effects_max;
+};
+
+/**
+ * UI_DEV_SETUP - Set device parameters for setup
+ *
+ * This ioctl sets parameters for the input device to be created.  It
+ * supersedes the old "struct uinput_user_dev" method, which wrote this data
+ * via write(). To actually set the absolute axes UI_ABS_SETUP should be
+ * used.
+ *
+ * The ioctl takes a "struct uinput_setup" object as argument. The fields of
+ * this object are as follows:
+ *              id: See the description of "struct input_id". This field is
+ *                  copied unchanged into the new device.
+ *            name: This is used unchanged as name for the new device.
+ *  ff_effects_max: This limits the maximum numbers of force-feedback effects.
+ *                  See below for a description of FF with uinput.
+ *
+ * This ioctl can be called multiple times and will overwrite previous values.
+ * If this ioctl fails with -EINVAL, it is recommended to use the old
+ * "uinput_user_dev" method via write() as a fallback, in case you run on an
+ * old kernel that does not support this ioctl.
+ *
+ * This ioctl may fail with -EINVAL if it is not supported or if you passed
+ * incorrect values, -ENOMEM if the kernel runs out of memory or -EFAULT if the
+ * passed uinput_setup object cannot be read/written.
+ * If this call fails, partial data may have already been applied to the
+ * internal device.
+ */
+#define UI_DEV_SETUP _IOW(UINPUT_IOCTL_BASE, 3, struct uinput_setup)
+
+struct uinput_abs_setup {
+       __u16  code; /* axis code */
+       /* __u16 filler; */
+       struct input_absinfo absinfo;
+};
+
+/**
+ * UI_ABS_SETUP - Set absolute axis information for the device to setup
+ *
+ * This ioctl sets one absolute axis information for the input device to be
+ * created. It supersedes the old "struct uinput_user_dev" method, which wrote
+ * part of this data and the content of UI_DEV_SETUP via write().
+ *
+ * The ioctl takes a "struct uinput_abs_setup" object as argument. The fields
+ * of this object are as follows:
+ *            code: The corresponding input code associated with this axis
+ *                  (ABS_X, ABS_Y, etc...)
+ *         absinfo: See "struct input_absinfo" for a description of this field.
+ *                  This field is copied unchanged into the kernel for the
+ *                  specified axis. If the axis is not enabled via
+ *                  UI_SET_ABSBIT, this ioctl will enable it.
+ *
+ * This ioctl can be called multiple times and will overwrite previous values.
+ * If this ioctl fails with -EINVAL, it is recommended to use the old
+ * "uinput_user_dev" method via write() as a fallback, in case you run on an
+ * old kernel that does not support this ioctl.
+ *
+ * This ioctl may fail with -EINVAL if it is not supported or if you passed
+ * incorrect values, -ENOMEM if the kernel runs out of memory or -EFAULT if the
+ * passed uinput_setup object cannot be read/written.
+ * If this call fails, partial data may have already been applied to the
+ * internal device.
+ */
+#define UI_ABS_SETUP _IOW(UINPUT_IOCTL_BASE, 4, struct uinput_abs_setup)
+
 #define UI_SET_EVBIT           _IOW(UINPUT_IOCTL_BASE, 100, int)
 #define UI_SET_KEYBIT          _IOW(UINPUT_IOCTL_BASE, 101, int)
 #define UI_SET_RELBIT          _IOW(UINPUT_IOCTL_BASE, 102, int)
@@ -144,7 +219,6 @@ struct uinput_ff_erase {
 #define UI_FF_UPLOAD           1
 #define UI_FF_ERASE            2
 
-#define UINPUT_MAX_NAME_SIZE   80
 struct uinput_user_dev {
        char name[UINPUT_MAX_NAME_SIZE];
        struct input_id id;