summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--Documentation/devicetree/bindings/goldfish/events.txt17
-rw-r--r--Documentation/devicetree/bindings/input/gpio-keys.txt1
-rw-r--r--Documentation/devicetree/bindings/input/touchscreen/cyttsp.txt95
-rw-r--r--Documentation/devicetree/bindings/input/touchscreen/goodix.txt14
-rw-r--r--Documentation/devicetree/bindings/input/touchscreen/pixcir_i2c_ts.txt4
-rw-r--r--Documentation/devicetree/bindings/input/touchscreen/ts4800-ts.txt11
-rw-r--r--drivers/gpio/gpiolib-acpi.c43
-rw-r--r--drivers/gpio/gpiolib.c3
-rw-r--r--drivers/gpio/gpiolib.h8
-rw-r--r--drivers/input/joystick/xpad.c591
-rw-r--r--drivers/input/keyboard/Kconfig2
-rw-r--r--drivers/input/keyboard/goldfish_events.c17
-rw-r--r--drivers/input/keyboard/gpio_keys.c35
-rw-r--r--drivers/input/keyboard/omap-keypad.c10
-rw-r--r--drivers/input/keyboard/snvs_pwrkey.c4
-rw-r--r--drivers/input/keyboard/spear-keyboard.c6
-rw-r--r--drivers/input/misc/bma150.c2
-rw-r--r--drivers/input/misc/da9063_onkey.c8
-rw-r--r--drivers/input/misc/sparcspkr.c18
-rw-r--r--drivers/input/misc/uinput.c158
-rw-r--r--drivers/input/mouse/Kconfig10
-rw-r--r--drivers/input/mouse/Makefile1
-rw-r--r--drivers/input/mouse/alps.c22
-rw-r--r--drivers/input/mouse/byd.c337
-rw-r--r--drivers/input/mouse/byd.h18
-rw-r--r--drivers/input/mouse/cyapa.c22
-rw-r--r--drivers/input/mouse/cyapa.h14
-rw-r--r--drivers/input/mouse/cyapa_gen3.c108
-rw-r--r--drivers/input/mouse/cyapa_gen5.c99
-rw-r--r--drivers/input/mouse/cyapa_gen6.c4
-rw-r--r--drivers/input/mouse/elantech.c2
-rw-r--r--drivers/input/mouse/focaltech.c22
-rw-r--r--drivers/input/mouse/focaltech.h8
-rw-r--r--drivers/input/mouse/logips2pp.c2
-rw-r--r--drivers/input/mouse/logips2pp.h4
-rw-r--r--drivers/input/mouse/psmouse-base.c784
-rw-r--r--drivers/input/mouse/psmouse.h1
-rw-r--r--drivers/input/touchscreen/Kconfig41
-rw-r--r--drivers/input/touchscreen/Makefile3
-rw-r--r--drivers/input/touchscreen/atmel_mxt_ts.c68
-rw-r--r--drivers/input/touchscreen/cyttsp_core.c194
-rw-r--r--drivers/input/touchscreen/cyttsp_core.h10
-rw-r--r--drivers/input/touchscreen/cyttsp_i2c.c10
-rw-r--r--drivers/input/touchscreen/cyttsp_spi.c10
-rw-r--r--drivers/input/touchscreen/egalax_ts_serial.c194
-rw-r--r--drivers/input/touchscreen/goodix.c513
-rw-r--r--drivers/input/touchscreen/melfas_mip4.c1517
-rw-r--r--drivers/input/touchscreen/pcap_ts.c2
-rw-r--r--drivers/input/touchscreen/pixcir_i2c_ts.c41
-rw-r--r--drivers/input/touchscreen/rohm_bu21023.c2
-rw-r--r--drivers/input/touchscreen/stmpe-ts.c31
-rw-r--r--drivers/input/touchscreen/ti_am335x_tsc.c2
-rw-r--r--drivers/input/touchscreen/ts4800-ts.c216
-rw-r--r--drivers/input/touchscreen/wacom_w8001.c325
-rw-r--r--include/linux/input/cyttsp.h15
-rw-r--r--include/linux/uinput.h5
-rw-r--r--include/uapi/linux/serio.h1
-rw-r--r--include/uapi/linux/uinput.h80
58 files changed, 4749 insertions, 1036 deletions
diff --git a/Documentation/devicetree/bindings/goldfish/events.txt b/Documentation/devicetree/bindings/goldfish/events.txt
new file mode 100644
index 0000000..5babf46
--- /dev/null
+++ b/Documentation/devicetree/bindings/goldfish/events.txt
@@ -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>;
+ };
diff --git a/Documentation/devicetree/bindings/input/gpio-keys.txt b/Documentation/devicetree/bindings/input/gpio-keys.txt
index cf1333d..2164123 100644
--- a/Documentation/devicetree/bindings/input/gpio-keys.txt
+++ b/Documentation/devicetree/bindings/input/gpio-keys.txt
@@ -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
index 0000000..b75d4cf
--- /dev/null
+++ b/Documentation/devicetree/bindings/input/touchscreen/cyttsp.txt
@@ -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>;
+ };
+
+ /* ... */
+ };
diff --git a/Documentation/devicetree/bindings/input/touchscreen/goodix.txt b/Documentation/devicetree/bindings/input/touchscreen/goodix.txt
index 8ba98ee..c98757a 100644
--- a/Documentation/devicetree/bindings/input/touchscreen/goodix.txt
+++ b/Documentation/devicetree/bindings/input/touchscreen/goodix.txt
@@ -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>;
};
/* ... */
diff --git a/Documentation/devicetree/bindings/input/touchscreen/pixcir_i2c_ts.txt b/Documentation/devicetree/bindings/input/touchscreen/pixcir_i2c_ts.txt
index 8eb240a..697a3e7 100644
--- a/Documentation/devicetree/bindings/input/touchscreen/pixcir_i2c_ts.txt
+++ b/Documentation/devicetree/bindings/input/touchscreen/pixcir_i2c_ts.txt
@@ -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
index 0000000..4c1c092
--- /dev/null
+++ b/Documentation/devicetree/bindings/input/touchscreen/ts4800-ts.txt
@@ -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
diff --git a/drivers/gpio/gpiolib-acpi.c b/drivers/gpio/gpiolib-acpi.c
index 16a7b68..7ab7bc3 100644
--- a/drivers/gpio/gpiolib-acpi.c
+++ b/drivers/gpio/gpiolib-acpi.c
@@ -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));
+}
diff --git a/drivers/gpio/gpiolib.c b/drivers/gpio/gpiolib.c
index 4e4c308..a3ad3ba 100644
--- a/drivers/gpio/gpiolib.c
+++ b/drivers/gpio/gpiolib.c
@@ -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;
diff --git a/drivers/gpio/gpiolib.h b/drivers/gpio/gpiolib.h
index 98ab08c..2b87cbd 100644
--- a/drivers/gpio/gpiolib.h
+++ b/drivers/gpio/gpiolib.h
@@ -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,
diff --git a/drivers/input/joystick/xpad.c b/drivers/input/joystick/xpad.c
index fd4100d..6727954 100644
--- a/drivers/input/joystick/xpad.c
+++ b/drivers/input/joystick/xpad.c
@@ -76,10 +76,13 @@
*/
#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,
};
diff --git a/drivers/input/keyboard/Kconfig b/drivers/input/keyboard/Kconfig
index ddd8148..509608c 100644
--- a/drivers/input/keyboard/Kconfig
+++ b/drivers/input/keyboard/Kconfig
@@ -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.
diff --git a/drivers/input/keyboard/goldfish_events.c b/drivers/input/keyboard/goldfish_events.c
index 907e4e2..f6e643b 100644
--- a/drivers/input/keyboard/goldfish_events.c
+++ b/drivers/input/keyboard/goldfish_events.c
@@ -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),
},
};
diff --git a/drivers/input/keyboard/gpio_keys.c b/drivers/input/keyboard/gpio_keys.c
index bef317f..2909365 100644
--- a/drivers/input/keyboard/gpio_keys.c
+++ b/drivers/input/keyboard/gpio_keys.c
@@ -96,7 +96,7 @@ 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);
@@ -104,6 +104,22 @@ static inline int get_n_events_by_type(int type)
}
/**
+ * 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++];
diff --git a/drivers/input/keyboard/omap-keypad.c b/drivers/input/keyboard/omap-keypad.c
index 7502e46..e0d72c8 100644
--- a/drivers/input/keyboard/omap-keypad.c
+++ b/drivers/input/keyboard/omap-keypad.c
@@ -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)
diff --git a/drivers/input/keyboard/snvs_pwrkey.c b/drivers/input/keyboard/snvs_pwrkey.c
index 9adf13a..b0ffade 100644
--- a/drivers/input/keyboard/snvs_pwrkey.c
+++ b/drivers/input/keyboard/snvs_pwrkey.c
@@ -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);
diff --git a/drivers/input/keyboard/spear-keyboard.c b/drivers/input/keyboard/spear-keyboard.c
index 623d451..8083eaa 100644
--- a/drivers/input/keyboard/spear-keyboard.c
+++ b/drivers/input/keyboard/spear-keyboard.c
@@ -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);
diff --git a/drivers/input/misc/bma150.c b/drivers/input/misc/bma150.c
index 1d0e61d..b0d4453 100644
--- a/drivers/input/misc/bma150.c
+++ b/drivers/input/misc/bma150.c
@@ -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,
diff --git a/drivers/input/misc/da9063_onkey.c b/drivers/input/misc/da9063_onkey.c
index 8eb697d..bb863e0 100644
--- a/drivers/input/misc/da9063_onkey.c
+++ b/drivers/input/misc/da9063_onkey.c
@@ -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;
diff --git a/drivers/input/misc/sparcspkr.c b/drivers/input/misc/sparcspkr.c
index 6f997aa..4a5afc7 100644
--- a/drivers/input/misc/sparcspkr.c
+++ b/drivers/input/misc/sparcspkr.c
@@ -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);
diff --git a/drivers/input/misc/uinput.c b/drivers/input/misc/uinput.c
index 5adbced..4eb9e4d 100644
--- a/drivers/input/misc/uinput.c
+++ b/drivers/input/misc/uinput.c
@@ -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;
diff --git a/drivers/input/mouse/Kconfig b/drivers/input/mouse/Kconfig
index 17f97e5..096abb4 100644
--- a/drivers/input/mouse/Kconfig
+++ b/drivers/input/mouse/Kconfig
@@ -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
diff --git a/drivers/input/mouse/Makefile b/drivers/input/mouse/Makefile
index ee6a6e9..6168b13 100644
--- a/drivers/input/mouse/Makefile
+++ b/drivers/input/mouse/Makefile
@@ -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
diff --git a/drivers/input/mouse/alps.c b/drivers/input/mouse/alps.c
index 41e6cb5..936f07a 100644
--- a/drivers/input/mouse/alps.c
+++ b/drivers/input/mouse/alps.c
@@ -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
index 0000000..9425e0f
--- /dev/null
+++ b/drivers/input/mouse/byd.c
@@ -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
index 0000000..d6c120c
--- /dev/null
+++ b/drivers/input/mouse/byd.h
@@ -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 */
diff --git a/drivers/input/mouse/cyapa.c b/drivers/input/mouse/cyapa.c
index eb76b61..dc23942 100644
--- a/drivers/input/mouse/cyapa.c
+++ b/drivers/input/mouse/cyapa.c
@@ -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);
diff --git a/drivers/input/mouse/cyapa.h b/drivers/input/mouse/cyapa.h
index b812bba..ce951fe 100644
--- a/drivers/input/mouse/cyapa.h
+++ b/drivers/input/mouse/cyapa.h
@@ -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];
};
diff --git a/drivers/input/mouse/cyapa_gen3.c b/drivers/input/mouse/cyapa_gen3.c
index 1a9d12a..f9600753 100644
--- a/drivers/input/mouse/cyapa_gen3.c
+++ b/drivers/input/mouse/cyapa_gen3.c
@@ -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; }
diff --git a/drivers/input/mouse/cyapa_gen5.c b/drivers/input/mouse/cyapa_gen5.c
index 118ba97..5775d40 100644
--- a/drivers/input/mouse/cyapa_gen5.c
+++ b/drivers/input/mouse/cyapa_gen5.c
@@ -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;
}
diff --git a/drivers/input/mouse/cyapa_gen6.c b/drivers/input/mouse/cyapa_gen6.c
index e4eb048..0163978 100644
--- a/drivers/input/mouse/cyapa_gen6.c
+++ b/drivers/input/mouse/cyapa_gen6.c
@@ -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__);
diff --git a/drivers/input/mouse/elantech.c b/drivers/input/mouse/elantech.c
index 537ebb0..78f93cf 100644
--- a/drivers/input/mouse/elantech.c
+++ b/drivers/input/mouse/elantech.c
@@ -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;
diff --git a/drivers/input/mouse/focaltech.c b/drivers/input/mouse/focaltech.c
index 4d5576d..c8c6a8c 100644
--- a/drivers/input/mouse/focaltech.c
+++ b/drivers/input/mouse/focaltech.c
@@ -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 */
diff --git a/drivers/input/mouse/focaltech.h b/drivers/input/mouse/focaltech.h
index ca61ebf..783b28e 100644
--- a/drivers/input/mouse/focaltech.h
+++ b/drivers/input/mouse/focaltech.h
@@ -18,6 +18,14 @@
#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
diff --git a/drivers/input/mouse/logips2pp.c b/drivers/input/mouse/logips2pp.c
index 136e222..422da1c 100644
--- a/drivers/input/mouse/logips2pp.c
+++ b/drivers/input/mouse/logips2pp.c
@@ -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];
diff --git a/drivers/input/mouse/logips2pp.h b/drivers/input/mouse/logips2pp.h
index 0c186f0..bf62945 100644
--- a/drivers/input/mouse/logips2pp.h
+++ b/drivers/input/mouse/logips2pp.h
@@ -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;
}
diff --git a/drivers/input/mouse/psmouse-base.c b/drivers/input/mouse/psmouse-base.c
index ad18dab..39d1bec 100644
--- a/drivers/input/mouse/psmouse-base.c
+++ b/drivers/input/mouse/psmouse-base.c
@@ -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
@@ -1130,6 +843,15 @@ static const struct psmouse_protocol psmouse_protocols[] = {
.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,
.name = "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);
diff --git a/drivers/input/mouse/psmouse.h b/drivers/input/mouse/psmouse.h
index ad5a5a1..e0ca6cd 100644
--- a/drivers/input/mouse/psmouse.h
+++ b/drivers/input/mouse/psmouse.h
@@ -104,6 +104,7 @@ enum psmouse_type {
PSMOUSE_CYPRESS,
PSMOUSE_FOCALTECH,
PSMOUSE_VMMOUSE,
+ PSMOUSE_BYD,
PSMOUSE_AUTO /* This one should always be last */
};
diff --git a/drivers/input/touchscreen/Kconfig b/drivers/input/touchscreen/Kconfig
index ae33da7..309951d 100644
--- a/drivers/input/touchscreen/Kconfig
+++ b/drivers/input/touchscreen/Kconfig
@@ -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.
diff --git a/drivers/input/touchscreen/Makefile b/drivers/input/touchscreen/Makefile
index cbaa6ab..4b518c7 100644
--- a/drivers/input/touchscreen/Makefile
+++ b/drivers/input/touchscreen/Makefile
@@ -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
diff --git a/drivers/input/touchscreen/atmel_mxt_ts.c b/drivers/input/touchscreen/atmel_mxt_ts.c
index 2d5794e..2160512 100644
--- a/drivers/input/touchscreen/atmel_mxt_ts.c
+++ b/drivers/input/touchscreen/atmel_mxt_ts.c
@@ -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");
diff --git a/drivers/input/touchscreen/cyttsp_core.c b/drivers/input/touchscreen/cyttsp_core.c
index 5b74e8b..91cda8f 100644
--- a/drivers/input/touchscreen/cyttsp_core.c
+++ b/drivers/input/touchscreen/cyttsp_core.c
@@ -30,9 +30,12 @@
#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");
diff --git a/drivers/input/touchscreen/cyttsp_core.h b/drivers/input/touchscreen/cyttsp_core.h
index 0707411..7835e2b 100644
--- a/drivers/input/touchscreen/cyttsp_core.h
+++ b/drivers/input/touchscreen/cyttsp_core.h
@@ -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);
diff --git a/drivers/input/touchscreen/cyttsp_i2c.c b/drivers/input/touchscreen/cyttsp_i2c.c
index eee51b3..1edfdba 100644
--- a/drivers/input/touchscreen/cyttsp_i2c.c
+++ b/drivers/input/touchscreen/cyttsp_i2c.c
@@ -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,
};
diff --git a/drivers/input/touchscreen/cyttsp_spi.c b/drivers/input/touchscreen/cyttsp_spi.c
index bbeeb24..3c9d18b 100644
--- a/drivers/input/touchscreen/cyttsp_spi.c
+++ b/drivers/input/touchscreen/cyttsp_spi.c
@@ -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
index 0000000..657bbae
--- /dev/null
+++ b/drivers/input/touchscreen/egalax_ts_serial.c
@@ -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");
diff --git a/drivers/input/touchscreen/goodix.c b/drivers/input/touchscreen/goodix.c
index 4d113c9..240b16f 100644
--- a/drivers/input/touchscreen/goodix.c
+++ b/drivers/input/touchscreen/goodix.c
@@ -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
index 0000000..89272973
--- /dev/null
+++ b/drivers/input/touchscreen/melfas_mip4.c
@@ -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");
diff --git a/drivers/input/touchscreen/pcap_ts.c b/drivers/input/touchscreen/pcap_ts.c
index 23a354a..0e3fc41 100644
--- a/drivers/input/touchscreen/pcap_ts.c
+++ b/drivers/input/touchscreen/pcap_ts.c
@@ -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];
diff --git a/drivers/input/touchscreen/pixcir_i2c_ts.c b/drivers/input/touchscreen/pixcir_i2c_ts.c
index 4b961ad..09523a3 100644
--- a/drivers/input/touchscreen/pixcir_i2c_ts.c
+++ b/drivers/input/touchscreen/pixcir_i2c_ts.c
@@ -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);
diff --git a/drivers/input/touchscreen/rohm_bu21023.c b/drivers/input/touchscreen/rohm_bu21023.c
index ba6024f..611156a 100644
--- a/drivers/input/touchscreen/rohm_bu21023.c
+++ b/drivers/input/touchscreen/rohm_bu21023.c
@@ -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);
diff --git a/drivers/input/touchscreen/stmpe-ts.c b/drivers/input/touchscreen/stmpe-ts.c
index e414d43..2a78e27 100644
--- a/drivers/input/touchscreen/stmpe-ts.c
+++ b/drivers/input/touchscreen/stmpe-ts.c
@@ -63,6 +63,37 @@
#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;
diff --git a/drivers/input/touchscreen/ti_am335x_tsc.c b/drivers/input/touchscreen/ti_am335x_tsc.c
index 191a1b8..a21a07c 100644
--- a/drivers/input/touchscreen/ti_am335x_tsc.c
+++ b/drivers/input/touchscreen/ti_am335x_tsc.c
@@ -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
index 0000000..3c3dd78
--- /dev/null
+++ b/drivers/input/touchscreen/ts4800-ts.c
@@ -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");
diff --git a/drivers/input/touchscreen/wacom_w8001.c b/drivers/input/touchscreen/wacom_w8001.c
index 2792ca3..bab3c6a 100644
--- a/drivers/input/touchscreen/wacom_w8001.c
+++ b/drivers/input/touchscreen/wacom_w8001.c
@@ -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;
}
diff --git a/include/linux/input/cyttsp.h b/include/linux/input/cyttsp.h
index 5af7c66..586c8c9 100644
--- a/include/linux/input/cyttsp.h
+++ b/include/linux/input/cyttsp.h
@@ -40,19 +40,4 @@
/* 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_ */
diff --git a/include/linux/uinput.h b/include/linux/uinput.h
index 0994c0d..75de43d 100644
--- a/include/linux/uinput.h
+++ b/include/linux/uinput.h
@@ -20,6 +20,11 @@
* 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>)
diff --git a/include/uapi/linux/serio.h b/include/uapi/linux/serio.h
index becdd78..c2ea169 100644
--- a/include/uapi/linux/serio.h
+++ b/include/uapi/linux/serio.h
@@ -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 */
diff --git a/include/uapi/linux/uinput.h b/include/uapi/linux/uinput.h
index 013c9d8..dc652e2 100644
--- a/include/uapi/linux/uinput.h
+++ b/include/uapi/linux/uinput.h
@@ -20,6 +20,11 @@
* 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;