Merge branch 'for-linus' of git://git.kernel.org/pub/scm/linux/kernel/git/dtor/input
authorLinus Torvalds <torvalds@linux-foundation.org>
Wed, 27 Jul 2016 21:30:41 +0000 (14:30 -0700)
committerLinus Torvalds <torvalds@linux-foundation.org>
Wed, 27 Jul 2016 21:30:41 +0000 (14:30 -0700)
Pull input updates from Dmitry Torokhov:
 "Updates for the input subsystem.  This contains the following new
  drivers promised in the last merge window:

   - driver for touchscreen controller found in Surface 3
   - driver for Pegasus Notetaker tablet
   - driver for Atmel Captouch Buttons
   - driver for Raydium I2C touchscreen controllers
   - powerkey driver for HISI 65xx SoC

  plus a few fixes"

* 'for-linus' of git://git.kernel.org/pub/scm/linux/kernel/git/dtor/input: (40 commits)
  Input: tty/vt/keyboard - use memdup_user()
  Input: pegasus_notetaker - set device mode in reset_resume() if in use
  Input: pegasus_notetaker - cancel workqueue's work in suspend()
  Input: pegasus_notetaker - fix usb_autopm calls to be balanced
  Input: pegasus_notetaker - handle usb control msg errors
  Input: wacom_w8001 - handle errors from input_mt_init_slots()
  Input: wacom_w8001 - resolution wasn't set for ABS_MT_POSITION_X/Y
  Input: pixcir_ts - add support for axis inversion / swapping
  Input: icn8318 - use of_touchscreen helpers for inverting / swapping axes
  Input: edt-ft5x06 - add support for inverting / swapping axes
  Input: of_touchscreen - add support for inverted / swapped axes
  Input: synaptics-rmi4 - use the RMI_F11_REL_BYTES define in rmi_f11_rel_pos_report
  Input: synaptics-rmi4 - remove unneeded variable
  Input: synaptics-rmi4 - remove pointer to rmi_function in f12_data
  Input: synaptics-rmi4 - support regulator supplies
  Input: raydium_i2c_ts - check CRC of incoming packets
  Input: xen-kbdfront - prefer xenbus_write() over xenbus_printf() where possible
  Input: fix a double word "is is" in include/linux/input.h
  Input: add powerkey driver for HISI 65xx SoC
  Input: apanel - spelling mistake - "skiping" -> "skipping"
  ...

41 files changed:
Documentation/devicetree/bindings/input/atmel,captouch.txt [new file with mode: 0644]
Documentation/devicetree/bindings/input/raydium_i2c_ts.txt [new file with mode: 0644]
Documentation/devicetree/bindings/input/rmi4/rmi_i2c.txt
Documentation/devicetree/bindings/vendor-prefixes.txt
drivers/input/input-mt.c
drivers/input/input.c
drivers/input/keyboard/tc3589x-keypad.c
drivers/input/keyboard/tegra-kbc.c
drivers/input/misc/Kconfig
drivers/input/misc/Makefile
drivers/input/misc/apanel.c
drivers/input/misc/atmel_captouch.c [new file with mode: 0644]
drivers/input/misc/hisi_powerkey.c [new file with mode: 0644]
drivers/input/misc/regulator-haptic.c
drivers/input/misc/xen-kbdfront.c
drivers/input/mouse/elantech.c
drivers/input/mouse/lifebook.c
drivers/input/rmi4/rmi_f01.c
drivers/input/rmi4/rmi_f11.c
drivers/input/rmi4/rmi_f12.c
drivers/input/rmi4/rmi_i2c.c
drivers/input/serio/ams_delta_serio.c
drivers/input/tablet/Kconfig
drivers/input/tablet/Makefile
drivers/input/tablet/pegasus_notetaker.c [new file with mode: 0644]
drivers/input/touchscreen/Kconfig
drivers/input/touchscreen/Makefile
drivers/input/touchscreen/ad7879.c
drivers/input/touchscreen/chipone_icn8318.c
drivers/input/touchscreen/cyttsp_core.c
drivers/input/touchscreen/edt-ft5x06.c
drivers/input/touchscreen/migor_ts.c
drivers/input/touchscreen/of_touchscreen.c
drivers/input/touchscreen/pixcir_i2c_ts.c
drivers/input/touchscreen/raydium_i2c_ts.c [new file with mode: 0644]
drivers/input/touchscreen/surface3_spi.c [new file with mode: 0644]
drivers/input/touchscreen/ti_am335x_tsc.c
drivers/input/touchscreen/tsc200x-core.c
drivers/input/touchscreen/wacom_w8001.c
include/linux/input.h
include/linux/input/touchscreen.h

diff --git a/Documentation/devicetree/bindings/input/atmel,captouch.txt b/Documentation/devicetree/bindings/input/atmel,captouch.txt
new file mode 100644 (file)
index 0000000..fe9ee5c
--- /dev/null
@@ -0,0 +1,36 @@
+Device tree bindings for Atmel capacitive touch device, typically
+an Atmel touch sensor connected to AtmegaXX MCU running firmware
+based on Qtouch library.
+
+The node for this device must be a child of a I2C controller node, as the
+device communicates via I2C.
+
+Required properties:
+
+       compatible:     Must be "atmel,captouch".
+       reg:            The I2C slave address of the device.
+       interrupts:     Property describing the interrupt line the device
+                       is connected to. The device only has one interrupt
+                       source.
+       linux,keycodes: Specifies an array of numeric keycode values to
+                       be used for reporting button presses. The array can
+                       contain up to 8 entries.
+
+Optional properties:
+
+       autorepeat:     Enables the Linux input system's autorepeat
+                       feature on the input device.
+
+Example:
+
+       atmel-captouch@51 {
+               compatible = "atmel,captouch";
+               reg = <0x51>;
+               interrupt-parent = <&tlmm>;
+               interrupts = <67 IRQ_TYPE_EDGE_FALLING>;
+               linux,keycodes = <BTN_0>, <BTN_1>,
+                       <BTN_2>, <BTN_3>,
+                       <BTN_4>, <BTN_5>,
+                       <BTN_6>, <BTN_7>;
+               autorepeat;
+       };
diff --git a/Documentation/devicetree/bindings/input/raydium_i2c_ts.txt b/Documentation/devicetree/bindings/input/raydium_i2c_ts.txt
new file mode 100644 (file)
index 0000000..5b6232d
--- /dev/null
@@ -0,0 +1,20 @@
+Raydium I2C touchscreen
+
+Required properties:
+- compatible: must be "raydium,rm32380"
+- reg: The I2C address of the device
+- interrupt-parent: the phandle for the interrupt controller
+- interrupts: interrupt to which the chip is connected
+    See ../interrupt-controller/interrupts.txt
+Optional properties:
+- avdd-supply: analog power supply needed to power device
+- vccio-supply: IO Power source
+- reset-gpios: reset gpio the chip is connected to.
+
+Example:
+       touchscreen@39 {
+               compatible = "raydium,rm32380";
+               reg = <0x39>;
+               interrupt-parent = <&gpio>;
+               interrupts = <0x0 IRQ_TYPE_EDGE_FALLING>;
+       };
index 95fa715..ec908b9 100644 (file)
@@ -22,6 +22,15 @@ See Documentation/devicetree/bindings/interrupt-controller/interrupts.txt
 - syna,reset-delay-ms: The number of milliseconds to wait after resetting the
                        device.
 
+- syna,startup-delay-ms: The number of milliseconds to wait after powering on
+                        the device.
+
+- vdd-supply: VDD power supply.
+See ../regulator/regulator.txt
+
+- vio-supply: VIO power supply
+See ../regulator/regulator.txt
+
 Function Parameters:
 Parameters specific to RMI functions are contained in child nodes of the rmi device
  node. Documentation for the parameters of each function can be found in:
index 2c2500d..d2bce22 100644 (file)
@@ -214,6 +214,7 @@ raidsonic   RaidSonic Technology GmbH
 ralink Mediatek/Ralink Technology Corp.
 ramtron        Ramtron International
 raspberrypi    Raspberry Pi Foundation
+raydium        Raydium Semiconductor Corp.
 realtek Realtek Semiconductor Corp.
 renesas        Renesas Electronics Corporation
 richtek        Richtek Technology Corporation
index 54fce56..a1bbec9 100644 (file)
@@ -218,8 +218,23 @@ void input_mt_report_pointer_emulation(struct input_dev *dev, bool use_count)
        }
 
        input_event(dev, EV_KEY, BTN_TOUCH, count > 0);
-       if (use_count)
+
+       if (use_count) {
+               if (count == 0 &&
+                   !test_bit(ABS_MT_DISTANCE, dev->absbit) &&
+                   test_bit(ABS_DISTANCE, dev->absbit) &&
+                   input_abs_get_val(dev, ABS_DISTANCE) != 0) {
+                       /*
+                        * Force reporting BTN_TOOL_FINGER for devices that
+                        * only report general hover (and not per-contact
+                        * distance) when contact is in proximity but not
+                        * on the surface.
+                        */
+                       count = 1;
+               }
+
                input_mt_report_finger_count(dev, count);
+       }
 
        if (oldest) {
                int x = input_mt_get_value(oldest, ABS_MT_POSITION_X);
index b87ffbd..d95c34e 100644 (file)
@@ -153,8 +153,6 @@ static void input_pass_values(struct input_dev *dev,
 
        rcu_read_unlock();
 
-       add_input_randomness(vals->type, vals->code, vals->value);
-
        /* trigger auto repeat for key events */
        if (test_bit(EV_REP, dev->evbit) && test_bit(EV_KEY, dev->evbit)) {
                for (v = vals; v != vals + count; v++) {
@@ -371,9 +369,10 @@ static int input_get_disposition(struct input_dev *dev,
 static void input_handle_event(struct input_dev *dev,
                               unsigned int type, unsigned int code, int value)
 {
-       int disposition;
+       int disposition = input_get_disposition(dev, type, code, &value);
 
-       disposition = input_get_disposition(dev, type, code, &value);
+       if (disposition != INPUT_IGNORE_EVENT && type != EV_SYN)
+               add_input_randomness(type, code, value);
 
        if ((disposition & INPUT_PASS_TO_DEVICE) && dev->event)
                dev->event(dev, type, code, value);
index e92dfd8..ec0070e 100644 (file)
@@ -32,7 +32,7 @@
 #define TC3589x_PULL_DOWN_MASK         0x1
 #define TC3589x_PULL_UP_MASK           0x2
 #define TC3589x_PULLUP_ALL_MASK                0xAA
-#define TC3589x_IO_PULL_VAL(index, mask)       ((mask)<<((index)%4)*2))
+#define TC3589x_IO_PULL_VAL(index, mask)       ((mask)<<((index)%4)*2)
 
 /* Bit masks for IOCFG register */
 #define IOCFG_BALLCFG          0x01
index acc5394..7d61439 100644 (file)
@@ -552,7 +552,7 @@ static int tegra_kbc_parse_dt(struct tegra_kbc *kbc)
 
        if (!num_rows || !num_cols || ((num_rows + num_cols) > KBC_MAX_GPIO)) {
                dev_err(kbc->dev,
-                       "keypad rows/columns not porperly specified\n");
+                       "keypad rows/columns not properly specified\n");
                return -EINVAL;
        }
 
index 1f2337a..efb0ca8 100644 (file)
@@ -82,6 +82,20 @@ config INPUT_ARIZONA_HAPTICS
          To compile this driver as a module, choose M here: the
          module will be called arizona-haptics.
 
+config INPUT_ATMEL_CAPTOUCH
+       tristate "Atmel Capacitive Touch Button Driver"
+       depends on OF || COMPILE_TEST
+       depends on I2C
+       help
+         Say Y here if an Atmel Capacitive Touch Button device which
+         implements "captouch" protocol is connected to I2C bus. Typically
+         this device consists of Atmel Touch sensor controlled by AtMegaXX
+         MCU running firmware based on Qtouch library.
+         One should find "atmel,captouch" node in the board specific DTS.
+
+         To compile this driver as a module, choose M here: the
+         module will be called atmel_captouch.
+
 config INPUT_BMA150
        tristate "BMA150/SMB380 acceleration sensor support"
        depends on I2C
@@ -796,4 +810,13 @@ config INPUT_DRV2667_HAPTICS
          To compile this driver as a module, choose M here: the
          module will be called drv2667-haptics.
 
+config INPUT_HISI_POWERKEY
+       tristate "Hisilicon PMIC ONKEY support"
+       depends on ARCH_HISI || COMPILE_TEST
+       help
+         Say Y to enable support for PMIC ONKEY.
+
+         To compile this driver as a module, choose M here: the
+         module will be called hisi_powerkey.
+
 endif
index 0357a08..6a1e5e2 100644 (file)
@@ -17,6 +17,7 @@ obj-$(CONFIG_INPUT_APANEL)            += apanel.o
 obj-$(CONFIG_INPUT_ARIZONA_HAPTICS)    += arizona-haptics.o
 obj-$(CONFIG_INPUT_ATI_REMOTE2)                += ati_remote2.o
 obj-$(CONFIG_INPUT_ATLAS_BTNS)         += atlas_btns.o
+obj-$(CONFIG_INPUT_ATMEL_CAPTOUCH)     += atmel_captouch.o
 obj-$(CONFIG_INPUT_BFIN_ROTARY)                += bfin_rotary.o
 obj-$(CONFIG_INPUT_BMA150)             += bma150.o
 obj-$(CONFIG_INPUT_CM109)              += cm109.o
@@ -34,6 +35,7 @@ obj-$(CONFIG_INPUT_DRV2667_HAPTICS)   += drv2667.o
 obj-$(CONFIG_INPUT_GP2A)               += gp2ap002a00f.o
 obj-$(CONFIG_INPUT_GPIO_BEEPER)                += gpio-beeper.o
 obj-$(CONFIG_INPUT_GPIO_TILT_POLLED)   += gpio_tilt_polled.o
+obj-$(CONFIG_INPUT_HISI_POWERKEY)      += hisi_powerkey.o
 obj-$(CONFIG_HP_SDC_RTC)               += hp_sdc_rtc.o
 obj-$(CONFIG_INPUT_IMS_PCU)            += ims-pcu.o
 obj-$(CONFIG_INPUT_IXP4XX_BEEPER)      += ixp4xx-beeper.o
index a8d2b8d..53630af 100644 (file)
@@ -297,7 +297,7 @@ static int __init apanel_init(void)
 
                if (slave != i2c_addr) {
                        pr_notice(APANEL ": only one SMBus slave "
-                                 "address supported, skiping device...\n");
+                                 "address supported, skipping device...\n");
                        continue;
                }
 
diff --git a/drivers/input/misc/atmel_captouch.c b/drivers/input/misc/atmel_captouch.c
new file mode 100644 (file)
index 0000000..9412654
--- /dev/null
@@ -0,0 +1,290 @@
+/*
+ * Atmel Atmegaxx Capacitive Touch Button Driver
+ *
+ * Copyright (C) 2016 Google, inc.
+ *
+ * This software is licensed under the terms of the GNU General Public
+ * License version 2, as published by the Free Software Foundation, and
+ * may be copied, distributed, and modified under those terms.
+ *
+ * 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.
+ */
+
+/*
+ * It's irrelevant that the HW used to develop captouch driver is based
+ * on Atmega88PA part and uses QtouchADC parts for sensing touch.
+ * Calling this driver "captouch" is an arbitrary way to distinguish
+ * the protocol this driver supported by other atmel/qtouch drivers.
+ *
+ * Captouch driver supports a newer/different version of the I2C
+ * registers/commands than the qt1070.c driver.
+ * Don't let the similarity of the general driver structure fool you.
+ *
+ * For raw i2c access from userspace, use i2cset/i2cget
+ * to poke at /dev/i2c-N devices.
+ */
+
+#include <linux/device.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/i2c.h>
+#include <linux/input.h>
+#include <linux/interrupt.h>
+#include <linux/slab.h>
+
+/* Maximum number of buttons supported */
+#define MAX_NUM_OF_BUTTONS             8
+
+/* Registers */
+#define REG_KEY1_THRESHOLD             0x02
+#define REG_KEY2_THRESHOLD             0x03
+#define REG_KEY3_THRESHOLD             0x04
+#define REG_KEY4_THRESHOLD             0x05
+
+#define REG_KEY1_REF_H                 0x20
+#define REG_KEY1_REF_L                 0x21
+#define REG_KEY2_REF_H                 0x22
+#define REG_KEY2_REF_L                 0x23
+#define REG_KEY3_REF_H                 0x24
+#define REG_KEY3_REF_L                 0x25
+#define REG_KEY4_REF_H                 0x26
+#define REG_KEY4_REF_L                 0x27
+
+#define REG_KEY1_DLT_H                 0x30
+#define REG_KEY1_DLT_L                 0x31
+#define REG_KEY2_DLT_H                 0x32
+#define REG_KEY2_DLT_L                 0x33
+#define REG_KEY3_DLT_H                 0x34
+#define REG_KEY3_DLT_L                 0x35
+#define REG_KEY4_DLT_H                 0x36
+#define REG_KEY4_DLT_L                 0x37
+
+#define REG_KEY_STATE                  0x3C
+
+/*
+ * @i2c_client: I2C slave device client pointer
+ * @input: Input device pointer
+ * @num_btn: Number of buttons
+ * @keycodes: map of button# to KeyCode
+ * @prev_btn: Previous key state to detect button "press" or "release"
+ * @xfer_buf: I2C transfer buffer
+ */
+struct atmel_captouch_device {
+       struct i2c_client *client;
+       struct input_dev *input;
+       u32 num_btn;
+       u32 keycodes[MAX_NUM_OF_BUTTONS];
+       u8 prev_btn;
+       u8 xfer_buf[8] ____cacheline_aligned;
+};
+
+/*
+ * Read from I2C slave device
+ * The protocol is that the client has to provide both the register address
+ * and the length, and while reading back the device would prepend the data
+ * with address and length for verification.
+ */
+static int atmel_read(struct atmel_captouch_device *capdev,
+                        u8 reg, u8 *data, size_t len)
+{
+       struct i2c_client *client = capdev->client;
+       struct device *dev = &client->dev;
+       struct i2c_msg msg[2];
+       int err;
+
+       if (len > sizeof(capdev->xfer_buf) - 2)
+               return -EINVAL;
+
+       capdev->xfer_buf[0] = reg;
+       capdev->xfer_buf[1] = len;
+
+       msg[0].addr = client->addr;
+       msg[0].flags = 0;
+       msg[0].buf = capdev->xfer_buf;
+       msg[0].len = 2;
+
+       msg[1].addr = client->addr;
+       msg[1].flags = I2C_M_RD;
+       msg[1].buf = capdev->xfer_buf;
+       msg[1].len = len + 2;
+
+       err = i2c_transfer(client->adapter, msg, ARRAY_SIZE(msg));
+       if (err != ARRAY_SIZE(msg))
+               return err < 0 ? err : -EIO;
+
+       if (capdev->xfer_buf[0] != reg) {
+               dev_err(dev,
+                       "I2C read error: register address does not match (%#02x vs %02x)\n",
+                       capdev->xfer_buf[0], reg);
+               return -ECOMM;
+       }
+
+       memcpy(data, &capdev->xfer_buf[2], len);
+
+       return 0;
+}
+
+/*
+ * Handle interrupt and report the key changes to the input system.
+ * Multi-touch can be supported; however, it really depends on whether
+ * the device can multi-touch.
+ */
+static irqreturn_t atmel_captouch_isr(int irq, void *data)
+{
+       struct atmel_captouch_device *capdev = data;
+       struct device *dev = &capdev->client->dev;
+       int error;
+       int i;
+       u8 new_btn;
+       u8 changed_btn;
+
+       error = atmel_read(capdev, REG_KEY_STATE, &new_btn, 1);
+       if (error) {
+               dev_err(dev, "failed to read button state: %d\n", error);
+               goto out;
+       }
+
+       dev_dbg(dev, "%s: button state %#02x\n", __func__, new_btn);
+
+       changed_btn = new_btn ^ capdev->prev_btn;
+       capdev->prev_btn = new_btn;
+
+       for (i = 0; i < capdev->num_btn; i++) {
+               if (changed_btn & BIT(i))
+                       input_report_key(capdev->input,
+                                        capdev->keycodes[i],
+                                        new_btn & BIT(i));
+       }
+
+       input_sync(capdev->input);
+
+out:
+       return IRQ_HANDLED;
+}
+
+/*
+ * Probe function to setup the device, input system and interrupt
+ */
+static int atmel_captouch_probe(struct i2c_client *client,
+               const struct i2c_device_id *id)
+{
+       struct atmel_captouch_device *capdev;
+       struct device *dev = &client->dev;
+       struct device_node *node;
+       int i;
+       int err;
+
+       if (!i2c_check_functionality(client->adapter,
+                                    I2C_FUNC_SMBUS_BYTE_DATA |
+                                       I2C_FUNC_SMBUS_WORD_DATA |
+                                       I2C_FUNC_SMBUS_I2C_BLOCK)) {
+               dev_err(dev, "needed i2c functionality is not supported\n");
+               return -EINVAL;
+       }
+
+       capdev = devm_kzalloc(dev, sizeof(*capdev), GFP_KERNEL);
+       if (!capdev)
+               return -ENOMEM;
+
+       capdev->client = client;
+       i2c_set_clientdata(client, capdev);
+
+       err = atmel_read(capdev, REG_KEY_STATE,
+                           &capdev->prev_btn, sizeof(capdev->prev_btn));
+       if (err) {
+               dev_err(dev, "failed to read initial button state: %d\n", err);
+               return err;
+       }
+
+       capdev->input = devm_input_allocate_device(dev);
+       if (!capdev->input) {
+               dev_err(dev, "failed to allocate input device\n");
+               return -ENOMEM;
+       }
+
+       capdev->input->id.bustype = BUS_I2C;
+       capdev->input->id.product = 0x880A;
+       capdev->input->id.version = 0;
+       capdev->input->name = "ATMegaXX Capacitive Button Controller";
+       __set_bit(EV_KEY, capdev->input->evbit);
+
+       node = dev->of_node;
+       if (!node) {
+               dev_err(dev, "failed to find matching node in device tree\n");
+               return -EINVAL;
+       }
+
+       if (of_property_read_bool(node, "autorepeat"))
+               __set_bit(EV_REP, capdev->input->evbit);
+
+       capdev->num_btn = of_property_count_u32_elems(node, "linux,keymap");
+       if (capdev->num_btn > MAX_NUM_OF_BUTTONS)
+               capdev->num_btn = MAX_NUM_OF_BUTTONS;
+
+       err = of_property_read_u32_array(node, "linux,keycodes",
+                                        capdev->keycodes,
+                                        capdev->num_btn);
+       if (err) {
+               dev_err(dev,
+                       "failed to read linux,keycode property: %d\n", err);
+               return err;
+       }
+
+       for (i = 0; i < capdev->num_btn; i++)
+               __set_bit(capdev->keycodes[i], capdev->input->keybit);
+
+       capdev->input->keycode = capdev->keycodes;
+       capdev->input->keycodesize = sizeof(capdev->keycodes[0]);
+       capdev->input->keycodemax = capdev->num_btn;
+
+       err = input_register_device(capdev->input);
+       if (err)
+               return err;
+
+       err = devm_request_threaded_irq(dev, client->irq,
+                                       NULL, atmel_captouch_isr,
+                                       IRQF_ONESHOT,
+                                       "atmel_captouch", capdev);
+       if (err) {
+               dev_err(dev, "failed to request irq %d: %d\n",
+                       client->irq, err);
+               return err;
+       }
+
+       return 0;
+}
+
+#ifdef CONFIG_OF
+static const struct of_device_id atmel_captouch_of_id[] = {
+       {
+               .compatible = "atmel,captouch",
+       },
+       { /* sentinel */ }
+};
+MODULE_DEVICE_TABLE(of, atmel_captouch_of_id);
+#endif
+
+static const struct i2c_device_id atmel_captouch_id[] = {
+       { "atmel_captouch", 0 },
+       { }
+};
+MODULE_DEVICE_TABLE(i2c, atmel_captouch_id);
+
+static struct i2c_driver atmel_captouch_driver = {
+       .probe          = atmel_captouch_probe,
+       .id_table       = atmel_captouch_id,
+       .driver         = {
+               .name   = "atmel_captouch",
+               .of_match_table = of_match_ptr(atmel_captouch_of_id),
+       },
+};
+module_i2c_driver(atmel_captouch_driver);
+
+/* Module information */
+MODULE_AUTHOR("Hung-yu Wu <hywu@google.com>");
+MODULE_DESCRIPTION("Atmel ATmegaXX Capacitance Touch Sensor I2C Driver");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/input/misc/hisi_powerkey.c b/drivers/input/misc/hisi_powerkey.c
new file mode 100644 (file)
index 0000000..675539c
--- /dev/null
@@ -0,0 +1,142 @@
+/*
+ * Hisilicon PMIC powerkey driver
+ *
+ * Copyright (C) 2013 Hisilicon Ltd.
+ * Copyright (C) 2015, 2016 Linaro Ltd.
+ *
+ * This file is subject to the terms and conditions of the GNU General
+ * Public License. See the file "COPYING" in the main directory of this
+ * archive for more details.
+ *
+ * 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/platform_device.h>
+#include <linux/interrupt.h>
+#include <linux/reboot.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/of_irq.h>
+#include <linux/input.h>
+#include <linux/slab.h>
+
+/* the held interrupt will trigger after 4 seconds */
+#define MAX_HELD_TIME  (4 * MSEC_PER_SEC)
+
+static irqreturn_t hi65xx_power_press_isr(int irq, void *q)
+{
+       struct input_dev *input = q;
+
+       pm_wakeup_event(input->dev.parent, MAX_HELD_TIME);
+       input_report_key(input, KEY_POWER, 1);
+       input_sync(input);
+
+       return IRQ_HANDLED;
+}
+
+static irqreturn_t hi65xx_power_release_isr(int irq, void *q)
+{
+       struct input_dev *input = q;
+
+       pm_wakeup_event(input->dev.parent, MAX_HELD_TIME);
+       input_report_key(input, KEY_POWER, 0);
+       input_sync(input);
+
+       return IRQ_HANDLED;
+}
+
+static irqreturn_t hi65xx_restart_toggle_isr(int irq, void *q)
+{
+       struct input_dev *input = q;
+       int value = test_bit(KEY_RESTART, input->key);
+
+       pm_wakeup_event(input->dev.parent, MAX_HELD_TIME);
+       input_report_key(input, KEY_RESTART, !value);
+       input_sync(input);
+
+       return IRQ_HANDLED;
+}
+
+static const struct {
+       const char *name;
+       irqreturn_t (*handler)(int irq, void *q);
+} hi65xx_irq_info[] = {
+       { "down", hi65xx_power_press_isr },
+       { "up", hi65xx_power_release_isr },
+       { "hold 4s", hi65xx_restart_toggle_isr },
+};
+
+static int hi65xx_powerkey_probe(struct platform_device *pdev)
+{
+       struct device *dev = &pdev->dev;
+       struct input_dev *input;
+       int irq, i, error;
+
+       input = devm_input_allocate_device(&pdev->dev);
+       if (!input) {
+               dev_err(&pdev->dev, "failed to allocate input device\n");
+               return -ENOMEM;
+       }
+
+       input->phys = "hisi_on/input0";
+       input->name = "HISI 65xx PowerOn Key";
+
+       input_set_capability(input, EV_KEY, KEY_POWER);
+       input_set_capability(input, EV_KEY, KEY_RESTART);
+
+       for (i = 0; i < ARRAY_SIZE(hi65xx_irq_info); i++) {
+
+               irq = platform_get_irq_byname(pdev, hi65xx_irq_info[i].name);
+               if (irq < 0) {
+                       error = irq;
+                       dev_err(dev, "couldn't get irq %s: %d\n",
+                               hi65xx_irq_info[i].name, error);
+                       return error;
+               }
+
+               error = devm_request_any_context_irq(dev, irq,
+                                                    hi65xx_irq_info[i].handler,
+                                                    IRQF_ONESHOT,
+                                                    hi65xx_irq_info[i].name,
+                                                    input);
+               if (error < 0) {
+                       dev_err(dev, "couldn't request irq %s: %d\n",
+                               hi65xx_irq_info[i].name, error);
+                       return error;
+               }
+       }
+
+       error = input_register_device(input);
+       if (error) {
+               dev_err(&pdev->dev, "failed to register input device: %d\n",
+                       error);
+               return error;
+       }
+
+       device_init_wakeup(&pdev->dev, 1);
+
+       return 0;
+}
+
+static int hi65xx_powerkey_remove(struct platform_device *pdev)
+{
+       device_init_wakeup(&pdev->dev, 0);
+
+       return 0;
+}
+
+static struct platform_driver hi65xx_powerkey_driver = {
+       .driver = {
+               .name = "hi65xx-powerkey",
+       },
+       .probe = hi65xx_powerkey_probe,
+       .remove  = hi65xx_powerkey_remove,
+};
+module_platform_driver(hi65xx_powerkey_driver);
+
+MODULE_AUTHOR("Zhiliang Xue <xuezhiliang@huawei.com");
+MODULE_DESCRIPTION("Hisi PMIC Power key driver");
+MODULE_LICENSE("GPL v2");
index a804705..2e8f801 100644 (file)
@@ -124,7 +124,7 @@ regulator_haptic_parse_dt(struct device *dev, struct regulator_haptic *haptic)
 
        node = dev->of_node;
        if(!node) {
-               dev_err(dev, "Missing dveice tree data\n");
+               dev_err(dev, "Missing device tree data\n");
                return -EINVAL;
        }
 
index 0a9ad2c..227fbd2 100644 (file)
@@ -130,8 +130,8 @@ static int xenkbd_probe(struct xenbus_device *dev,
        if (xenbus_scanf(XBT_NIL, dev->otherend, "feature-abs-pointer", "%d", &abs) < 0)
                abs = 0;
        if (abs) {
-               ret = xenbus_printf(XBT_NIL, dev->nodename,
-                                   "request-abs-pointer", "1");
+               ret = xenbus_write(XBT_NIL, dev->nodename,
+                                  "request-abs-pointer", "1");
                if (ret) {
                        pr_warning("xenkbd: can't request abs-pointer");
                        abs = 0;
@@ -327,8 +327,8 @@ InitWait:
                if (ret < 0)
                        val = 0;
                if (val) {
-                       ret = xenbus_printf(XBT_NIL, info->xbdev->nodename,
-                                           "request-abs-pointer", "1");
+                       ret = xenbus_write(XBT_NIL, info->xbdev->nodename,
+                                          "request-abs-pointer", "1");
                        if (ret)
                                pr_warning("xenkbd: can't request abs-pointer");
                }
index be5b399..615d23e 100644 (file)
@@ -1708,7 +1708,7 @@ int elantech_init(struct psmouse *psmouse)
                snprintf(etd->tp_phys, sizeof(etd->tp_phys), "%s/input1",
                        psmouse->ps2dev.serio->phys);
                tp_dev->phys = etd->tp_phys;
-               tp_dev->name = "Elantech PS/2 TrackPoint";
+               tp_dev->name = "ETPS/2 Elantech TrackPoint";
                tp_dev->id.bustype = BUS_I8042;
                tp_dev->id.vendor  = 0x0002;
                tp_dev->id.product = PSMOUSE_ELANTECH;
index e5ed216..13d324c 100644 (file)
@@ -287,7 +287,7 @@ static int lifebook_create_relative_device(struct psmouse *psmouse)
                 "%s/input1", psmouse->ps2dev.serio->phys);
 
        dev2->phys = priv->phys;
-       dev2->name = "PS/2 Touchpad";
+       dev2->name = "LBPS/2 Fujitsu Lifebook Touchpad";
        dev2->id.bustype = BUS_I8042;
        dev2->id.vendor  = 0x0002;
        dev2->id.product = PSMOUSE_LIFEBOOK;
index eb362bc..fac81fc 100644 (file)
@@ -81,26 +81,26 @@ struct f01_basic_properties {
  * This bit disables whatever sleep mode may be selected by the sleep_mode
  * field and forces the device to run at full power without sleeping.
  */
-#define RMI_F01_CRTL0_NOSLEEP_BIT      BIT(2)
+#define RMI_F01_CTRL0_NOSLEEP_BIT      BIT(2)
 
 /*
  * When this bit is set, the touch controller employs a noise-filtering
  * algorithm designed for use with a connected battery charger.
  */
-#define RMI_F01_CRTL0_CHARGER_BIT      BIT(5)
+#define RMI_F01_CTRL0_CHARGER_BIT      BIT(5)
 
 /*
  * Sets the report rate for the device. The effect of this setting is
  * highly product dependent. Check the spec sheet for your particular
  * touch sensor.
  */
-#define RMI_F01_CRTL0_REPORTRATE_BIT   BIT(6)
+#define RMI_F01_CTRL0_REPORTRATE_BIT   BIT(6)
 
 /*
  * Written by the host as an indicator that the device has been
  * successfully configured.
  */
-#define RMI_F01_CRTL0_CONFIGURED_BIT   BIT(7)
+#define RMI_F01_CTRL0_CONFIGURED_BIT   BIT(7)
 
 /**
  * @ctrl0 - see the bit definitions above.
@@ -330,10 +330,10 @@ static int rmi_f01_probe(struct rmi_function *fn)
        case RMI_F01_NOSLEEP_DEFAULT:
                break;
        case RMI_F01_NOSLEEP_OFF:
-               f01->device_control.ctrl0 &= ~RMI_F01_CRTL0_NOSLEEP_BIT;
+               f01->device_control.ctrl0 &= ~RMI_F01_CTRL0_NOSLEEP_BIT;
                break;
        case RMI_F01_NOSLEEP_ON:
-               f01->device_control.ctrl0 |= RMI_F01_CRTL0_NOSLEEP_BIT;
+               f01->device_control.ctrl0 |= RMI_F01_CTRL0_NOSLEEP_BIT;
                break;
        }
 
@@ -349,7 +349,7 @@ static int rmi_f01_probe(struct rmi_function *fn)
                f01->device_control.ctrl0 &= ~RMI_F01_CTRL0_SLEEP_MODE_MASK;
        }
 
-       f01->device_control.ctrl0 |= RMI_F01_CRTL0_CONFIGURED_BIT;
+       f01->device_control.ctrl0 |= RMI_F01_CTRL0_CONFIGURED_BIT;
 
        error = rmi_write(rmi_dev, fn->fd.control_base_addr,
                          f01->device_control.ctrl0);
@@ -535,8 +535,8 @@ static int rmi_f01_suspend(struct rmi_function *fn)
        int error;
 
        f01->old_nosleep =
-               f01->device_control.ctrl0 & RMI_F01_CRTL0_NOSLEEP_BIT;
-       f01->device_control.ctrl0 &= ~RMI_F01_CRTL0_NOSLEEP_BIT;
+               f01->device_control.ctrl0 & RMI_F01_CTRL0_NOSLEEP_BIT;
+       f01->device_control.ctrl0 &= ~RMI_F01_CTRL0_NOSLEEP_BIT;
 
        f01->device_control.ctrl0 &= ~RMI_F01_CTRL0_SLEEP_MODE_MASK;
        if (device_may_wakeup(fn->rmi_dev->xport->dev))
@@ -549,7 +549,7 @@ static int rmi_f01_suspend(struct rmi_function *fn)
        if (error) {
                dev_err(&fn->dev, "Failed to write sleep mode: %d.\n", error);
                if (f01->old_nosleep)
-                       f01->device_control.ctrl0 |= RMI_F01_CRTL0_NOSLEEP_BIT;
+                       f01->device_control.ctrl0 |= RMI_F01_CTRL0_NOSLEEP_BIT;
                f01->device_control.ctrl0 &= ~RMI_F01_CTRL0_SLEEP_MODE_MASK;
                f01->device_control.ctrl0 |= RMI_SLEEP_MODE_NORMAL;
                return error;
@@ -564,7 +564,7 @@ static int rmi_f01_resume(struct rmi_function *fn)
        int error;
 
        if (f01->old_nosleep)
-               f01->device_control.ctrl0 |= RMI_F01_CRTL0_NOSLEEP_BIT;
+               f01->device_control.ctrl0 |= RMI_F01_CTRL0_NOSLEEP_BIT;
 
        f01->device_control.ctrl0 &= ~RMI_F01_CTRL0_SLEEP_MODE_MASK;
        f01->device_control.ctrl0 |= RMI_SLEEP_MODE_NORMAL;
index ec8a10d..20c7134 100644 (file)
@@ -530,8 +530,8 @@ static void rmi_f11_rel_pos_report(struct f11_data *f11, u8 n_finger)
        struct f11_2d_data *data = &f11->data;
        s8 x, y;
 
-       x = data->rel_pos[n_finger * 2];
-       y = data->rel_pos[n_finger * 2 + 1];
+       x = data->rel_pos[n_finger * RMI_F11_REL_BYTES];
+       y = data->rel_pos[n_finger * RMI_F11_REL_BYTES + 1];
 
        rmi_2d_sensor_rel_report(sensor, x, y);
 }
@@ -1241,7 +1241,6 @@ static int rmi_f11_attention(struct rmi_function *fn, unsigned long *irq_bits)
        struct rmi_driver_data *drvdata = dev_get_drvdata(&rmi_dev->dev);
        struct f11_data *f11 = dev_get_drvdata(&fn->dev);
        u16 data_base_addr = fn->fd.data_base_addr;
-       u16 data_base_addr_offset = 0;
        int error;
 
        if (rmi_dev->xport->attn_data) {
@@ -1251,8 +1250,7 @@ static int rmi_f11_attention(struct rmi_function *fn, unsigned long *irq_bits)
                rmi_dev->xport->attn_size -= f11->sensor.attn_size;
        } else {
                error = rmi_read_block(rmi_dev,
-                               data_base_addr + data_base_addr_offset,
-                               f11->sensor.data_pkt,
+                               data_base_addr, f11->sensor.data_pkt,
                                f11->sensor.pkt_size);
                if (error < 0)
                        return error;
@@ -1260,7 +1258,6 @@ static int rmi_f11_attention(struct rmi_function *fn, unsigned long *irq_bits)
 
        rmi_f11_finger_handler(f11, &f11->sensor, irq_bits,
                                drvdata->num_of_irq_regs);
-       data_base_addr_offset += f11->sensor.pkt_size;
 
        return 0;
 }
index 88e9155..332c02f 100644 (file)
@@ -27,7 +27,6 @@ enum rmi_f12_object_type {
 };
 
 struct f12_data {
-       struct rmi_function *fn;
        struct rmi_2d_sensor sensor;
        struct rmi_2d_sensor_platform_data sensor_pdata;
 
index a96a326..6f2e0e4 100644 (file)
@@ -11,6 +11,8 @@
 #include <linux/rmi.h>
 #include <linux/irq.h>
 #include <linux/of.h>
+#include <linux/delay.h>
+#include <linux/regulator/consumer.h>
 #include "rmi_driver.h"
 
 #define BUFFER_SIZE_INCREMENT 32
@@ -37,6 +39,9 @@ struct rmi_i2c_xport {
 
        u8 *tx_buf;
        size_t tx_buf_size;
+
+       struct regulator_bulk_data supplies[2];
+       u32 startup_delay;
 };
 
 #define RMI_PAGE_SELECT_REGISTER 0xff
@@ -246,6 +251,24 @@ static int rmi_i2c_probe(struct i2c_client *client,
                return -ENODEV;
        }
 
+       rmi_i2c->supplies[0].supply = "vdd";
+       rmi_i2c->supplies[1].supply = "vio";
+       retval = devm_regulator_bulk_get(&client->dev,
+                                        ARRAY_SIZE(rmi_i2c->supplies),
+                                        rmi_i2c->supplies);
+       if (retval < 0)
+               return retval;
+
+       retval = regulator_bulk_enable(ARRAY_SIZE(rmi_i2c->supplies),
+                                      rmi_i2c->supplies);
+       if (retval < 0)
+               return retval;
+
+       of_property_read_u32(client->dev.of_node, "syna,startup-delay-ms",
+                            &rmi_i2c->startup_delay);
+
+       msleep(rmi_i2c->startup_delay);
+
        rmi_i2c->client = client;
        mutex_init(&rmi_i2c->page_mutex);
 
@@ -286,6 +309,8 @@ static int rmi_i2c_remove(struct i2c_client *client)
        struct rmi_i2c_xport *rmi_i2c = i2c_get_clientdata(client);
 
        rmi_unregister_transport_device(&rmi_i2c->xport);
+       regulator_bulk_disable(ARRAY_SIZE(rmi_i2c->supplies),
+                              rmi_i2c->supplies);
 
        return 0;
 }
@@ -308,6 +333,10 @@ static int rmi_i2c_suspend(struct device *dev)
                        dev_warn(dev, "Failed to enable irq for wake: %d\n",
                                ret);
        }
+
+       regulator_bulk_disable(ARRAY_SIZE(rmi_i2c->supplies),
+                              rmi_i2c->supplies);
+
        return ret;
 }
 
@@ -317,6 +346,13 @@ static int rmi_i2c_resume(struct device *dev)
        struct rmi_i2c_xport *rmi_i2c = i2c_get_clientdata(client);
        int ret;
 
+       ret = regulator_bulk_enable(ARRAY_SIZE(rmi_i2c->supplies),
+                                   rmi_i2c->supplies);
+       if (ret)
+               return ret;
+
+       msleep(rmi_i2c->startup_delay);
+
        enable_irq(rmi_i2c->irq);
        if (device_may_wakeup(&client->dev)) {
                ret = disable_irq_wake(rmi_i2c->irq);
@@ -346,6 +382,9 @@ static int rmi_i2c_runtime_suspend(struct device *dev)
 
        disable_irq(rmi_i2c->irq);
 
+       regulator_bulk_disable(ARRAY_SIZE(rmi_i2c->supplies),
+                              rmi_i2c->supplies);
+
        return 0;
 }
 
@@ -355,6 +394,13 @@ static int rmi_i2c_runtime_resume(struct device *dev)
        struct rmi_i2c_xport *rmi_i2c = i2c_get_clientdata(client);
        int ret;
 
+       ret = regulator_bulk_enable(ARRAY_SIZE(rmi_i2c->supplies),
+                                   rmi_i2c->supplies);
+       if (ret)
+               return ret;
+
+       msleep(rmi_i2c->startup_delay);
+
        enable_irq(rmi_i2c->irq);
 
        ret = rmi_driver_resume(rmi_i2c->xport.rmi_dev);
index 45887e3..3df501c 100644 (file)
@@ -56,7 +56,7 @@ static int check_data(int data)
        /* it should be odd */
        if (!(parity & 0x01)) {
                dev_warn(&ams_delta_serio->dev,
-                               "paritiy check failed, data=0x%X parity=0x%X\n",
+                               "parity check failed, data=0x%X parity=0x%X\n",
                                data, parity);
                return SERIO_PARITY;
        }
index 623bb9e..a2b9f97 100644 (file)
@@ -73,6 +73,21 @@ config TABLET_USB_KBTAB
          To compile this driver as a module, choose M here: the
          module will be called kbtab.
 
+config TABLET_USB_PEGASUS
+       tristate "Pegasus Mobile Notetaker Pen input tablet support"
+       depends on USB_ARCH_HAS_HCD
+       select USB
+       help
+         Say Y here if you want to use the Pegasus Mobile Notetaker,
+         also known as:
+         Genie e-note The Notetaker,
+         Staedtler Digital ballpoint pen 990 01,
+         IRISnotes Express or
+         NEWLink Digital Note Taker.
+
+         To compile this driver as a module, choose M here: the
+         module will be called pegasus_notetaker.
+
 config TABLET_SERIAL_WACOM4
        tristate "Wacom protocol 4 serial tablet support"
        select SERIO
index 2e13010..200fc4e 100644 (file)
@@ -8,4 +8,5 @@ obj-$(CONFIG_TABLET_USB_AIPTEK) += aiptek.o
 obj-$(CONFIG_TABLET_USB_GTCO)  += gtco.o
 obj-$(CONFIG_TABLET_USB_HANWANG) += hanwang.o
 obj-$(CONFIG_TABLET_USB_KBTAB) += kbtab.o
+obj-$(CONFIG_TABLET_USB_PEGASUS) += pegasus_notetaker.o
 obj-$(CONFIG_TABLET_SERIAL_WACOM4) += wacom_serial4.o
diff --git a/drivers/input/tablet/pegasus_notetaker.c b/drivers/input/tablet/pegasus_notetaker.c
new file mode 100644 (file)
index 0000000..949dacc
--- /dev/null
@@ -0,0 +1,450 @@
+/*
+ * Pegasus Mobile Notetaker Pen input tablet driver
+ *
+ * Copyright (c) 2016 Martin Kepplinger <martink@posteo.de>
+ */
+
+/*
+ * request packet (control endpoint):
+ * |-------------------------------------|
+ * | Report ID | Nr of bytes | command   |
+ * | (1 byte)  | (1 byte)    | (n bytes) |
+ * |-------------------------------------|
+ * | 0x02      | n           |           |
+ * |-------------------------------------|
+ *
+ * data packet after set xy mode command, 0x80 0xb5 0x02 0x01
+ * and pen is in range:
+ *
+ * byte        byte name               value (bits)
+ * --------------------------------------------
+ * 0   status                  0 1 0 0 0 0 X X
+ * 1   color                   0 0 0 0 H 0 S T
+ * 2   X low
+ * 3   X high
+ * 4   Y low
+ * 5   Y high
+ *
+ * X X battery state:
+ *     no state reported       0x00
+ *     battery low             0x01
+ *     battery good            0x02
+ *
+ * H   Hovering
+ * S   Switch 1 (pen button)
+ * T   Tip
+ */
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/input.h>
+#include <linux/usb/input.h>
+#include <linux/slab.h>
+
+/* USB HID defines */
+#define USB_REQ_GET_REPORT             0x01
+#define USB_REQ_SET_REPORT             0x09
+
+#define USB_VENDOR_ID_PEGASUSTECH      0x0e20
+#define USB_DEVICE_ID_PEGASUS_NOTETAKER_EN100  0x0101
+
+/* device specific defines */
+#define NOTETAKER_REPORT_ID            0x02
+#define NOTETAKER_SET_CMD              0x80
+#define NOTETAKER_SET_MODE             0xb5
+
+#define NOTETAKER_LED_MOUSE            0x02
+#define PEN_MODE_XY                    0x01
+
+#define SPECIAL_COMMAND                        0x80
+#define BUTTON_PRESSED                 0xb5
+#define COMMAND_VERSION                        0xa9
+
+/* in xy data packet */
+#define BATTERY_NO_REPORT              0x40
+#define BATTERY_LOW                    0x41
+#define BATTERY_GOOD                   0x42
+#define PEN_BUTTON_PRESSED             BIT(1)
+#define PEN_TIP                                BIT(0)
+
+struct pegasus {
+       unsigned char *data;
+       u8 data_len;
+       dma_addr_t data_dma;
+       struct input_dev *dev;
+       struct usb_device *usbdev;
+       struct usb_interface *intf;
+       struct urb *irq;
+       char name[128];
+       char phys[64];
+       struct work_struct init;
+};
+
+static int pegasus_control_msg(struct pegasus *pegasus, u8 *data, int len)
+{
+       const int sizeof_buf = len + 2;
+       int result;
+       int error;
+       u8 *cmd_buf;
+
+       cmd_buf = kmalloc(sizeof_buf, GFP_KERNEL);
+       if (!cmd_buf)
+               return -ENOMEM;
+
+       cmd_buf[0] = NOTETAKER_REPORT_ID;
+       cmd_buf[1] = len;
+       memcpy(cmd_buf + 2, data, len);
+
+       result = usb_control_msg(pegasus->usbdev,
+                                usb_sndctrlpipe(pegasus->usbdev, 0),
+                                USB_REQ_SET_REPORT,
+                                USB_TYPE_VENDOR | USB_DIR_OUT,
+                                0, 0, cmd_buf, sizeof_buf,
+                                USB_CTRL_SET_TIMEOUT);
+
+       kfree(cmd_buf);
+
+       if (unlikely(result != sizeof_buf)) {
+               error = result < 0 ? result : -EIO;
+               dev_err(&pegasus->usbdev->dev, "control msg error: %d\n",
+                       error);
+               return error;
+       }
+
+       return 0;
+}
+
+static int pegasus_set_mode(struct pegasus *pegasus, u8 mode, u8 led)
+{
+       u8 cmd[] = { NOTETAKER_SET_CMD, NOTETAKER_SET_MODE, led, mode };
+
+       return pegasus_control_msg(pegasus, cmd, sizeof(cmd));
+}
+
+static void pegasus_parse_packet(struct pegasus *pegasus)
+{
+       unsigned char *data = pegasus->data;
+       struct input_dev *dev = pegasus->dev;
+       u16 x, y;
+
+       switch (data[0]) {
+       case SPECIAL_COMMAND:
+               /* device button pressed */
+               if (data[1] == BUTTON_PRESSED)
+                       schedule_work(&pegasus->init);
+
+               break;
+
+       /* xy data */
+       case BATTERY_LOW:
+               dev_warn_once(&dev->dev, "Pen battery low\n");
+               /* fall through */
+
+       case BATTERY_NO_REPORT:
+       case BATTERY_GOOD:
+               x = le16_to_cpup((__le16 *)&data[2]);
+               y = le16_to_cpup((__le16 *)&data[4]);
+
+               /* pen-up event */
+               if (x == 0 && y == 0)
+                       break;
+
+               input_report_key(dev, BTN_TOUCH, data[1] & PEN_TIP);
+               input_report_key(dev, BTN_RIGHT, data[1] & PEN_BUTTON_PRESSED);
+               input_report_key(dev, BTN_TOOL_PEN, 1);
+               input_report_abs(dev, ABS_X, (s16)x);
+               input_report_abs(dev, ABS_Y, y);
+
+               input_sync(dev);
+               break;
+
+       default:
+               dev_warn_once(&pegasus->usbdev->dev,
+                             "unknown answer from device\n");
+       }
+}
+
+static void pegasus_irq(struct urb *urb)
+{
+       struct pegasus *pegasus = urb->context;
+       struct usb_device *dev = pegasus->usbdev;
+       int retval;
+
+       switch (urb->status) {
+       case 0:
+               pegasus_parse_packet(pegasus);
+               usb_mark_last_busy(pegasus->usbdev);
+               break;
+
+       case -ECONNRESET:
+       case -ENOENT:
+       case -ESHUTDOWN:
+               dev_err(&dev->dev, "%s - urb shutting down with status: %d",
+                       __func__, urb->status);
+               return;
+
+       default:
+               dev_err(&dev->dev, "%s - nonzero urb status received: %d",
+                       __func__, urb->status);
+               break;
+       }
+
+       retval = usb_submit_urb(urb, GFP_ATOMIC);
+       if (retval)
+               dev_err(&dev->dev, "%s - usb_submit_urb failed with result %d",
+                       __func__, retval);
+}
+
+static void pegasus_init(struct work_struct *work)
+{
+       struct pegasus *pegasus = container_of(work, struct pegasus, init);
+       int error;
+
+       error = pegasus_set_mode(pegasus, PEN_MODE_XY, NOTETAKER_LED_MOUSE);
+       if (error)
+               dev_err(&pegasus->usbdev->dev, "pegasus_set_mode error: %d\n",
+                       error);
+}
+
+static int pegasus_open(struct input_dev *dev)
+{
+       struct pegasus *pegasus = input_get_drvdata(dev);
+       int error;
+
+       error = usb_autopm_get_interface(pegasus->intf);
+       if (error)
+               return error;
+
+       pegasus->irq->dev = pegasus->usbdev;
+       if (usb_submit_urb(pegasus->irq, GFP_KERNEL)) {
+               error = -EIO;
+               goto err_autopm_put;
+       }
+
+       error = pegasus_set_mode(pegasus, PEN_MODE_XY, NOTETAKER_LED_MOUSE);
+       if (error)
+               goto err_kill_urb;
+
+       return 0;
+
+err_kill_urb:
+       usb_kill_urb(pegasus->irq);
+       cancel_work_sync(&pegasus->init);
+err_autopm_put:
+       usb_autopm_put_interface(pegasus->intf);
+       return error;
+}
+
+static void pegasus_close(struct input_dev *dev)
+{
+       struct pegasus *pegasus = input_get_drvdata(dev);
+
+       usb_kill_urb(pegasus->irq);
+       cancel_work_sync(&pegasus->init);
+       usb_autopm_put_interface(pegasus->intf);
+}
+
+static int pegasus_probe(struct usb_interface *intf,
+                        const struct usb_device_id *id)
+{
+       struct usb_device *dev = interface_to_usbdev(intf);
+       struct usb_endpoint_descriptor *endpoint;
+       struct pegasus *pegasus;
+       struct input_dev *input_dev;
+       int error;
+       int pipe;
+
+       /* We control interface 0 */
+       if (intf->cur_altsetting->desc.bInterfaceNumber >= 1)
+               return -ENODEV;
+
+       /* Sanity check that the device has an endpoint */
+       if (intf->altsetting[0].desc.bNumEndpoints < 1) {
+               dev_err(&intf->dev, "Invalid number of endpoints\n");
+               return -EINVAL;
+       }
+
+       endpoint = &intf->cur_altsetting->endpoint[0].desc;
+
+       pegasus = kzalloc(sizeof(*pegasus), GFP_KERNEL);
+       input_dev = input_allocate_device();
+       if (!pegasus || !input_dev) {
+               error = -ENOMEM;
+               goto err_free_mem;
+       }
+
+       pegasus->usbdev = dev;
+       pegasus->dev = input_dev;
+       pegasus->intf = intf;
+
+       pipe = usb_rcvintpipe(dev, endpoint->bEndpointAddress);
+       pegasus->data_len = usb_maxpacket(dev, pipe, usb_pipeout(pipe));
+
+       pegasus->data = usb_alloc_coherent(dev, pegasus->data_len, GFP_KERNEL,
+                                          &pegasus->data_dma);
+       if (!pegasus->data) {
+               error = -ENOMEM;
+               goto err_free_mem;
+       }
+
+       pegasus->irq = usb_alloc_urb(0, GFP_KERNEL);
+       if (!pegasus->irq) {
+               error = -ENOMEM;
+               goto err_free_dma;
+       }
+
+       usb_fill_int_urb(pegasus->irq, dev, pipe,
+                        pegasus->data, pegasus->data_len,
+                        pegasus_irq, pegasus, endpoint->bInterval);
+
+       pegasus->irq->transfer_dma = pegasus->data_dma;
+       pegasus->irq->transfer_flags |= URB_NO_TRANSFER_DMA_MAP;
+
+       if (dev->manufacturer)
+               strlcpy(pegasus->name, dev->manufacturer,
+                       sizeof(pegasus->name));
+
+       if (dev->product) {
+               if (dev->manufacturer)
+                       strlcat(pegasus->name, " ", sizeof(pegasus->name));
+               strlcat(pegasus->name, dev->product, sizeof(pegasus->name));
+       }
+
+       if (!strlen(pegasus->name))
+               snprintf(pegasus->name, sizeof(pegasus->name),
+                        "USB Pegasus Device %04x:%04x",
+                        le16_to_cpu(dev->descriptor.idVendor),
+                        le16_to_cpu(dev->descriptor.idProduct));
+
+       usb_make_path(dev, pegasus->phys, sizeof(pegasus->phys));
+       strlcat(pegasus->phys, "/input0", sizeof(pegasus->phys));
+
+       INIT_WORK(&pegasus->init, pegasus_init);
+
+       usb_set_intfdata(intf, pegasus);
+
+       input_dev->name = pegasus->name;
+       input_dev->phys = pegasus->phys;
+       usb_to_input_id(dev, &input_dev->id);
+       input_dev->dev.parent = &intf->dev;
+
+       input_set_drvdata(input_dev, pegasus);
+
+       input_dev->open = pegasus_open;
+       input_dev->close = pegasus_close;
+
+       __set_bit(EV_ABS, input_dev->evbit);
+       __set_bit(EV_KEY, input_dev->evbit);
+
+       __set_bit(ABS_X, input_dev->absbit);
+       __set_bit(ABS_Y, input_dev->absbit);
+
+       __set_bit(BTN_TOUCH, input_dev->keybit);
+       __set_bit(BTN_RIGHT, input_dev->keybit);
+       __set_bit(BTN_TOOL_PEN, input_dev->keybit);
+
+       __set_bit(INPUT_PROP_DIRECT, input_dev->propbit);
+       __set_bit(INPUT_PROP_POINTER, input_dev->propbit);
+
+       input_set_abs_params(input_dev, ABS_X, -1500, 1500, 8, 0);
+       input_set_abs_params(input_dev, ABS_Y, 1600, 3000, 8, 0);
+
+       error = input_register_device(pegasus->dev);
+       if (error)
+               goto err_free_urb;
+
+       return 0;
+
+err_free_urb:
+       usb_free_urb(pegasus->irq);
+err_free_dma:
+       usb_free_coherent(dev, pegasus->data_len,
+                         pegasus->data, pegasus->data_dma);
+err_free_mem:
+       input_free_device(input_dev);
+       kfree(pegasus);
+       usb_set_intfdata(intf, NULL);
+
+       return error;
+}
+
+static void pegasus_disconnect(struct usb_interface *intf)
+{
+       struct pegasus *pegasus = usb_get_intfdata(intf);
+
+       input_unregister_device(pegasus->dev);
+
+       usb_free_urb(pegasus->irq);
+       usb_free_coherent(interface_to_usbdev(intf),
+                         pegasus->data_len, pegasus->data,
+                         pegasus->data_dma);
+
+       kfree(pegasus);
+       usb_set_intfdata(intf, NULL);
+}
+
+static int pegasus_suspend(struct usb_interface *intf, pm_message_t message)
+{
+       struct pegasus *pegasus = usb_get_intfdata(intf);
+
+       mutex_lock(&pegasus->dev->mutex);
+       usb_kill_urb(pegasus->irq);
+       cancel_work_sync(&pegasus->init);
+       mutex_unlock(&pegasus->dev->mutex);
+
+       return 0;
+}
+
+static int pegasus_resume(struct usb_interface *intf)
+{
+       struct pegasus *pegasus = usb_get_intfdata(intf);
+       int retval = 0;
+
+       mutex_lock(&pegasus->dev->mutex);
+       if (pegasus->dev->users && usb_submit_urb(pegasus->irq, GFP_NOIO) < 0)
+               retval = -EIO;
+       mutex_unlock(&pegasus->dev->mutex);
+
+       return retval;
+}
+
+static int pegasus_reset_resume(struct usb_interface *intf)
+{
+       struct pegasus *pegasus = usb_get_intfdata(intf);
+       int retval = 0;
+
+       mutex_lock(&pegasus->dev->mutex);
+       if (pegasus->dev->users) {
+               retval = pegasus_set_mode(pegasus, PEN_MODE_XY,
+                                         NOTETAKER_LED_MOUSE);
+               if (!retval && usb_submit_urb(pegasus->irq, GFP_NOIO) < 0)
+                       retval = -EIO;
+       }
+       mutex_unlock(&pegasus->dev->mutex);
+
+       return retval;
+}
+
+static const struct usb_device_id pegasus_ids[] = {
+       { USB_DEVICE(USB_VENDOR_ID_PEGASUSTECH,
+                    USB_DEVICE_ID_PEGASUS_NOTETAKER_EN100) },
+       { }
+};
+MODULE_DEVICE_TABLE(usb, pegasus_ids);
+
+static struct usb_driver pegasus_driver = {
+       .name           = "pegasus_notetaker",
+       .probe          = pegasus_probe,
+       .disconnect     = pegasus_disconnect,
+       .suspend        = pegasus_suspend,
+       .resume         = pegasus_resume,
+       .reset_resume   = pegasus_reset_resume,
+       .id_table       = pegasus_ids,
+       .supports_autosuspend = 1,
+};
+
+module_usb_driver(pegasus_driver);
+
+MODULE_AUTHOR("Martin Kepplinger <martink@posteo.de>");
+MODULE_DESCRIPTION("Pegasus Mobile Notetaker Pen tablet driver");
+MODULE_LICENSE("GPL");
index 8ecdc38..ee02dc7 100644 (file)
@@ -632,7 +632,7 @@ config TOUCHSCREEN_EDT_FT5X06
 
 config TOUCHSCREEN_MIGOR
        tristate "Renesas MIGO-R touchscreen"
-       depends on SH_MIGOR && I2C
+       depends on (SH_MIGOR || COMPILE_TEST) && I2C
        help
          Say Y here to enable MIGO-R touchscreen support.
 
@@ -1046,6 +1046,19 @@ config TOUCHSCREEN_PCAP
          To compile this driver as a module, choose M here: the
          module will be called pcap_ts.
 
+config TOUCHSCREEN_RM_TS
+       tristate "Raydium I2C Touchscreen"
+       depends on I2C
+       depends on GPIOLIB || COMPILE_TEST
+       help
+         Say Y here if you have Raydium series I2C touchscreen,
+         such as RM32380, connected to your system.
+
+         If unsure, say N.
+
+         To compile this driver as a module, choose M here: the
+         module will be called raydium_i2c_ts.
+
 config TOUCHSCREEN_ST1232
        tristate "Sitronix ST1232 touchscreen controllers"
        depends on I2C
@@ -1094,6 +1107,19 @@ config TOUCHSCREEN_SUR40
          To compile this driver as a module, choose M here: the
          module will be called sur40.
 
+config TOUCHSCREEN_SURFACE3_SPI
+       tristate "Ntrig/Microsoft Surface 3 SPI touchscreen"
+       depends on SPI
+       depends on GPIOLIB || COMPILE_TEST
+       help
+         Say Y here if you have the Ntrig/Microsoft SPI touchscreen
+         controller chip as found on the Surface 3 in your system.
+
+         If unsure, say N.
+
+         To compile this driver as a module, choose M here: the
+         module will be called surface3_spi.
+
 config TOUCHSCREEN_SX8654
        tristate "Semtech SX8654 touchscreen"
        depends on I2C
index f42975e..3315882 100644 (file)
@@ -62,11 +62,13 @@ obj-$(CONFIG_TOUCHSCREEN_USB_COMPOSITE)     += usbtouchscreen.o
 obj-$(CONFIG_TOUCHSCREEN_PCAP)         += pcap_ts.o
 obj-$(CONFIG_TOUCHSCREEN_PENMOUNT)     += penmount.o
 obj-$(CONFIG_TOUCHSCREEN_PIXCIR)       += pixcir_i2c_ts.o
+obj-$(CONFIG_TOUCHSCREEN_RM_TS)                += raydium_i2c_ts.o
 obj-$(CONFIG_TOUCHSCREEN_S3C2410)      += s3c2410_ts.o
 obj-$(CONFIG_TOUCHSCREEN_ST1232)       += st1232.o
 obj-$(CONFIG_TOUCHSCREEN_STMPE)                += stmpe-ts.o
 obj-$(CONFIG_TOUCHSCREEN_SUN4I)                += sun4i-ts.o
 obj-$(CONFIG_TOUCHSCREEN_SUR40)                += sur40.o
+obj-$(CONFIG_TOUCHSCREEN_SURFACE3_SPI) += surface3_spi.o
 obj-$(CONFIG_TOUCHSCREEN_TI_AM335X_TSC)        += ti_am335x_tsc.o
 obj-$(CONFIG_TOUCHSCREEN_TOUCHIT213)   += touchit213.o
 obj-$(CONFIG_TOUCHSCREEN_TOUCHRIGHT)   += touchright.o
index e4bf110..e16a446 100644 (file)
@@ -595,7 +595,7 @@ struct ad7879 *ad7879_probe(struct device *dev, u8 devid, unsigned int irq,
        } else {
                input_set_abs_params(input_dev, ABS_X, 0, MAX_12BIT, 0, 0);
                input_set_abs_params(input_dev, ABS_Y, 0, MAX_12BIT, 0, 0);
-               touchscreen_parse_properties(input_dev, false);
+               touchscreen_parse_properties(input_dev, false, NULL);
                if (!input_abs_get_max(input_dev, ABS_PRESSURE)) {
                        dev_err(dev, "Touchscreen pressure is not specified\n");
                        return ERR_PTR(-EINVAL);
index 22a6fea..0bf1406 100644 (file)
@@ -17,6 +17,7 @@
 #include <linux/i2c.h>
 #include <linux/input.h>
 #include <linux/input/mt.h>
+#include <linux/input/touchscreen.h>
 #include <linux/module.h>
 #include <linux/of.h>
 
@@ -52,11 +53,7 @@ struct icn8318_data {
        struct i2c_client *client;
        struct input_dev *input;
        struct gpio_desc *wake_gpio;
-       u32 max_x;
-       u32 max_y;
-       bool invert_x;
-       bool invert_y;
-       bool swap_x_y;
+       struct touchscreen_properties prop;
 };
 
 static int icn8318_read_touch_data(struct i2c_client *client,
@@ -91,7 +88,7 @@ static irqreturn_t icn8318_irq(int irq, void *dev_id)
        struct icn8318_data *data = dev_id;
        struct device *dev = &data->client->dev;
        struct icn8318_touch_data touch_data;
-       int i, ret, x, y;
+       int i, ret;
 
        ret = icn8318_read_touch_data(data->client, &touch_data);
        if (ret < 0) {
@@ -124,22 +121,9 @@ static irqreturn_t icn8318_irq(int irq, void *dev_id)
                if (!act)
                        continue;
 
-               x = be16_to_cpu(touch->x);
-               y = be16_to_cpu(touch->y);
-
-               if (data->invert_x)
-                       x = data->max_x - x;
-
-               if (data->invert_y)
-                       y = data->max_y - y;
-
-               if (!data->swap_x_y) {
-                       input_event(data->input, EV_ABS, ABS_MT_POSITION_X, x);
-                       input_event(data->input, EV_ABS, ABS_MT_POSITION_Y, y);
-               } else {
-                       input_event(data->input, EV_ABS, ABS_MT_POSITION_X, y);
-                       input_event(data->input, EV_ABS, ABS_MT_POSITION_Y, x);
-               }
+               touchscreen_report_pos(data->input, &data->prop,
+                                      be16_to_cpu(touch->x),
+                                      be16_to_cpu(touch->y), true);
        }
 
        input_mt_sync_frame(data->input);
@@ -200,10 +184,8 @@ static int icn8318_probe(struct i2c_client *client,
                         const struct i2c_device_id *id)
 {
        struct device *dev = &client->dev;
-       struct device_node *np = dev->of_node;
        struct icn8318_data *data;
        struct input_dev *input;
-       u32 fuzz_x = 0, fuzz_y = 0;
        int error;
 
        if (!client->irq) {
@@ -223,19 +205,6 @@ static int icn8318_probe(struct i2c_client *client,
                return error;
        }
 
-       if (of_property_read_u32(np, "touchscreen-size-x", &data->max_x) ||
-           of_property_read_u32(np, "touchscreen-size-y", &data->max_y)) {
-               dev_err(dev, "Error touchscreen-size-x and/or -y missing\n");
-               return -EINVAL;
-       }
-
-       /* Optional */
-       of_property_read_u32(np, "touchscreen-fuzz-x", &fuzz_x);
-       of_property_read_u32(np, "touchscreen-fuzz-y", &fuzz_y);
-       data->invert_x = of_property_read_bool(np, "touchscreen-inverted-x");
-       data->invert_y = of_property_read_bool(np, "touchscreen-inverted-y");
-       data->swap_x_y = of_property_read_bool(np, "touchscreen-swapped-x-y");
-
        input = devm_input_allocate_device(dev);
        if (!input)
                return -ENOMEM;
@@ -246,16 +215,14 @@ static int icn8318_probe(struct i2c_client *client,
        input->close = icn8318_stop;
        input->dev.parent = dev;
 
-       if (!data->swap_x_y) {
-               input_set_abs_params(input, ABS_MT_POSITION_X, 0,
-                                    data->max_x, fuzz_x, 0);
-               input_set_abs_params(input, ABS_MT_POSITION_Y, 0,
-                                    data->max_y, fuzz_y, 0);
-       } else {
-               input_set_abs_params(input, ABS_MT_POSITION_X, 0,
-                                    data->max_y, fuzz_y, 0);
-               input_set_abs_params(input, ABS_MT_POSITION_Y, 0,
-                                    data->max_x, fuzz_x, 0);
+       input_set_capability(input, EV_ABS, ABS_MT_POSITION_X);
+       input_set_capability(input, EV_ABS, ABS_MT_POSITION_Y);
+
+       touchscreen_parse_properties(input, true, &data->prop);
+       if (!input_abs_get_max(input, ABS_MT_POSITION_X) ||
+           !input_abs_get_max(input, ABS_MT_POSITION_Y)) {
+               dev_err(dev, "Error touchscreen-size-x and/or -y missing\n");
+               return -EINVAL;
        }
 
        error = input_mt_init_slots(input, ICN8318_MAX_TOUCHES,
index 91cda8f..79381cc 100644 (file)
@@ -657,7 +657,7 @@ struct cyttsp *cyttsp_probe(const struct cyttsp_bus_ops *bus_ops,
 
        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);
+       touchscreen_parse_properties(input_dev, true, NULL);
 
        error = input_mt_init_slots(input_dev, CY_MAX_ID, 0);
        if (error) {
index 23fbe38..703e295 100644 (file)
@@ -86,6 +86,7 @@ struct edt_reg_addr {
 struct edt_ft5x06_ts_data {
        struct i2c_client *client;
        struct input_dev *input;
+       struct touchscreen_properties prop;
        u16 num_x;
        u16 num_y;
 
@@ -246,8 +247,8 @@ static irqreturn_t edt_ft5x06_ts_isr(int irq, void *dev_id)
                if (!down)
                        continue;
 
-               input_report_abs(tsdata->input, ABS_MT_POSITION_X, x);
-               input_report_abs(tsdata->input, ABS_MT_POSITION_Y, y);
+               touchscreen_report_pos(tsdata->input, &tsdata->prop, x, y,
+                                      true);
        }
 
        input_mt_report_pointer_emulation(tsdata->input, true);
@@ -972,7 +973,7 @@ static int edt_ft5x06_ts_probe(struct i2c_client *client,
        input_set_abs_params(input, ABS_MT_POSITION_Y,
                             0, tsdata->num_y * 64 - 1, 0, 0);
 
-       touchscreen_parse_properties(input, true);
+       touchscreen_parse_properties(input, true, &tsdata->prop);
 
        error = input_mt_init_slots(input, tsdata->max_support_points,
                                INPUT_MT_DIRECT);
index c038db9..02fb119 100644 (file)
@@ -202,7 +202,7 @@ static int migor_ts_remove(struct i2c_client *client)
        return 0;
 }
 
-static int migor_ts_suspend(struct device *dev)
+static int __maybe_unused migor_ts_suspend(struct device *dev)
 {
        struct i2c_client *client = to_i2c_client(dev);
        struct migor_ts_priv *priv = i2c_get_clientdata(client);
@@ -213,7 +213,7 @@ static int migor_ts_suspend(struct device *dev)
        return 0;
 }
 
-static int migor_ts_resume(struct device *dev)
+static int __maybe_unused migor_ts_resume(struct device *dev)
 {
        struct i2c_client *client = to_i2c_client(dev);
        struct migor_ts_priv *priv = i2c_get_clientdata(client);
@@ -230,7 +230,7 @@ static const struct i2c_device_id migor_ts_id[] = {
        { "migor_ts", 0 },
        { }
 };
-MODULE_DEVICE_TABLE(i2c, migor_ts);
+MODULE_DEVICE_TABLE(i2c, migor_ts_id);
 
 static struct i2c_driver migor_ts_driver = {
        .driver = {
index bb6f2fe..8d7f9c8 100644 (file)
@@ -55,12 +55,16 @@ static void touchscreen_set_params(struct input_dev *dev,
  * @input: input device that should be parsed
  * @multitouch: specifies whether parsed properties should be applied to
  *     single-touch or multi-touch axes
+ * @prop: pointer to a struct touchscreen_properties into which to store
+ *     axis swap and invert info for use with touchscreen_report_x_y();
+ *     or %NULL
  *
  * This function parses common DT properties for touchscreens and setups the
  * input device accordingly. The function keeps previously set up default
  * values if no value is specified via DT.
  */
-void touchscreen_parse_properties(struct input_dev *input, bool multitouch)
+void touchscreen_parse_properties(struct input_dev *input, bool multitouch,
+                                 struct touchscreen_properties *prop)
 {
        struct device *dev = input->dev.parent;
        unsigned int axis;
@@ -104,5 +108,80 @@ void touchscreen_parse_properties(struct input_dev *input, bool multitouch)
                                                &fuzz);
        if (data_present)
                touchscreen_set_params(input, axis, maximum, fuzz);
+
+       if (!prop)
+               return;
+
+       axis = multitouch ? ABS_MT_POSITION_X : ABS_X;
+
+       prop->max_x = input_abs_get_max(input, axis);
+       prop->max_y = input_abs_get_max(input, axis + 1);
+       prop->invert_x =
+               device_property_read_bool(dev, "touchscreen-inverted-x");
+       prop->invert_y =
+               device_property_read_bool(dev, "touchscreen-inverted-y");
+       prop->swap_x_y =
+               device_property_read_bool(dev, "touchscreen-swapped-x-y");
+
+       if (prop->swap_x_y)
+               swap(input->absinfo[axis], input->absinfo[axis + 1]);
 }
 EXPORT_SYMBOL(touchscreen_parse_properties);
+
+static void
+touchscreen_apply_prop_to_x_y(const struct touchscreen_properties *prop,
+                             unsigned int *x, unsigned int *y)
+{
+       if (prop->invert_x)
+               *x = prop->max_x - *x;
+
+       if (prop->invert_y)
+               *y = prop->max_y - *y;
+
+       if (prop->swap_x_y)
+               swap(*x, *y);
+}
+
+/**
+ * touchscreen_set_mt_pos - Set input_mt_pos coordinates
+ * @pos: input_mt_pos to set coordinates of
+ * @prop: pointer to a struct touchscreen_properties
+ * @x: X coordinate to store in pos
+ * @y: Y coordinate to store in pos
+ *
+ * Adjust the passed in x and y values applying any axis inversion and
+ * swapping requested in the passed in touchscreen_properties and store
+ * the result in a struct input_mt_pos.
+ */
+void touchscreen_set_mt_pos(struct input_mt_pos *pos,
+                           const struct touchscreen_properties *prop,
+                           unsigned int x, unsigned int y)
+{
+       touchscreen_apply_prop_to_x_y(prop, &x, &y);
+       pos->x = x;
+       pos->y = y;
+}
+EXPORT_SYMBOL(touchscreen_set_mt_pos);
+
+/**
+ * touchscreen_report_pos - Report touchscreen coordinates
+ * @input: input_device to report coordinates for
+ * @prop: pointer to a struct touchscreen_properties
+ * @x: X coordinate to report
+ * @y: Y coordinate to report
+ * @multitouch: Report coordinates on single-touch or multi-touch axes
+ *
+ * Adjust the passed in x and y values applying any axis inversion and
+ * swapping requested in the passed in touchscreen_properties and then
+ * report the resulting coordinates on the input_dev's x and y axis.
+ */
+void touchscreen_report_pos(struct input_dev *input,
+                           const struct touchscreen_properties *prop,
+                           unsigned int x, unsigned int y,
+                           bool multitouch)
+{
+       touchscreen_apply_prop_to_x_y(prop, &x, &y);
+       input_report_abs(input, multitouch ? ABS_MT_POSITION_X : ABS_X, x);
+       input_report_abs(input, multitouch ? ABS_MT_POSITION_Y : ABS_Y, y);
+}
+EXPORT_SYMBOL(touchscreen_report_pos);
index 09523a3..d159e14 100644 (file)
@@ -27,9 +27,9 @@
 #include <linux/input/touchscreen.h>
 #include <linux/gpio.h>
 #include <linux/gpio/consumer.h>
-/*#include <linux/of.h>*/
 #include <linux/of_device.h>
 #include <linux/platform_data/pixcir_i2c_ts.h>
+#include <asm/unaligned.h>
 
 #define PIXCIR_MAX_SLOTS       5 /* Max fingers supported by driver */
 
@@ -41,19 +41,15 @@ struct pixcir_i2c_ts_data {
        struct gpio_desc *gpio_enable;
        struct gpio_desc *gpio_wake;
        const struct pixcir_i2c_chip_data *chip;
+       struct touchscreen_properties prop;
        int max_fingers;        /* Max fingers supported in this instance */
        bool running;
 };
 
-struct pixcir_touch {
-       int x;
-       int y;
-       int id;
-};
-
 struct pixcir_report_data {
        int num_touches;
-       struct pixcir_touch touches[PIXCIR_MAX_SLOTS];
+       struct input_mt_pos pos[PIXCIR_MAX_SLOTS];
+       int ids[PIXCIR_MAX_SLOTS];
 };
 
 static void pixcir_ts_parse(struct pixcir_i2c_ts_data *tsdata,
@@ -98,11 +94,11 @@ static void pixcir_ts_parse(struct pixcir_i2c_ts_data *tsdata,
        bufptr = &rdbuf[2];
 
        for (i = 0; i < touch; i++) {
-               report->touches[i].x = (bufptr[1] << 8) | bufptr[0];
-               report->touches[i].y = (bufptr[3] << 8) | bufptr[2];
-
+               touchscreen_set_mt_pos(&report->pos[i], &tsdata->prop,
+                                      get_unaligned_le16(bufptr),
+                                      get_unaligned_le16(bufptr + 2));
                if (chip->has_hw_ids) {
-                       report->touches[i].id = bufptr[4];
+                       report->ids[i] = bufptr[4];
                        bufptr = bufptr + 5;
                } else {
                        bufptr = bufptr + 4;
@@ -113,9 +109,7 @@ static void pixcir_ts_parse(struct pixcir_i2c_ts_data *tsdata,
 static void pixcir_ts_report(struct pixcir_i2c_ts_data *ts,
                             struct pixcir_report_data *report)
 {
-       struct input_mt_pos pos[PIXCIR_MAX_SLOTS];
        int slots[PIXCIR_MAX_SLOTS];
-       struct pixcir_touch *touch;
        int n, i, slot;
        struct device *dev = &ts->client->dev;
        const struct pixcir_i2c_chip_data *chip = ts->chip;
@@ -124,24 +118,16 @@ static void pixcir_ts_report(struct pixcir_i2c_ts_data *ts,
        if (n > PIXCIR_MAX_SLOTS)
                n = PIXCIR_MAX_SLOTS;
 
-       if (!ts->chip->has_hw_ids) {
-               for (i = 0; i < n; i++) {
-                       touch = &report->touches[i];
-                       pos[i].x = touch->x;
-                       pos[i].y = touch->y;
-               }
-
-               input_mt_assign_slots(ts->input, slots, pos, n, 0);
-       }
+       if (!ts->chip->has_hw_ids)
+               input_mt_assign_slots(ts->input, slots, report->pos, n, 0);
 
        for (i = 0; i < n; i++) {
-               touch = &report->touches[i];
-
                if (chip->has_hw_ids) {
-                       slot = input_mt_get_slot_by_key(ts->input, touch->id);
+                       slot = input_mt_get_slot_by_key(ts->input,
+                                                       report->ids[i]);
                        if (slot < 0) {
                                dev_dbg(dev, "no free slot for id 0x%x\n",
-                                       touch->id);
+                                       report->ids[i]);
                                continue;
                        }
                } else {
@@ -149,14 +135,15 @@ static void pixcir_ts_report(struct pixcir_i2c_ts_data *ts,
                }
 
                input_mt_slot(ts->input, slot);
-               input_mt_report_slot_state(ts->input,
-                                          MT_TOOL_FINGER, true);
+               input_mt_report_slot_state(ts->input, MT_TOOL_FINGER, true);
 
-               input_event(ts->input, EV_ABS, ABS_MT_POSITION_X, touch->x);
-               input_event(ts->input, EV_ABS, ABS_MT_POSITION_Y, touch->y);
+               input_report_abs(ts->input, ABS_MT_POSITION_X,
+                                report->pos[i].x);
+               input_report_abs(ts->input, ABS_MT_POSITION_Y,
+                                report->pos[i].y);
 
                dev_dbg(dev, "%d: slot %d, x %d, y %d\n",
-                       i, slot, touch->x, touch->y);
+                       i, slot, report->pos[i].x, report->pos[i].y);
        }
 
        input_mt_sync_frame(ts->input);
@@ -515,7 +502,7 @@ static int pixcir_i2c_ts_probe(struct i2c_client *client,
        } else {
                input_set_capability(input, EV_ABS, ABS_MT_POSITION_X);
                input_set_capability(input, EV_ABS, ABS_MT_POSITION_Y);
-               touchscreen_parse_properties(input, true);
+               touchscreen_parse_properties(input, true, &tsdata->prop);
                if (!input_abs_get_max(input, ABS_MT_POSITION_X) ||
                    !input_abs_get_max(input, ABS_MT_POSITION_Y)) {
                        dev_err(dev, "Touchscreen size is not specified\n");
diff --git a/drivers/input/touchscreen/raydium_i2c_ts.c b/drivers/input/touchscreen/raydium_i2c_ts.c
new file mode 100644 (file)
index 0000000..a99fb5c
--- /dev/null
@@ -0,0 +1,1238 @@
+/*
+ * Raydium touchscreen I2C driver.
+ *
+ * Copyright (C) 2012-2014, Raydium Semiconductor Corporation.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * version 2, and only version 2, as published by the
+ * Free Software Foundation.
+ *
+ * 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.
+ *
+ * Raydium reserves the right to make changes without further notice
+ * to the materials described herein. Raydium does not assume any
+ * liability arising out of the application described herein.
+ *
+ * Contact Raydium Semiconductor Corporation at www.rad-ic.com
+ */
+
+#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/regulator/consumer.h>
+#include <linux/slab.h>
+#include <asm/unaligned.h>
+
+/* Slave I2C mode */
+#define RM_BOOT_BLDR           0x02
+#define RM_BOOT_MAIN           0x03
+
+/* I2C bootoloader commands */
+#define RM_CMD_BOOT_PAGE_WRT   0x0B            /* send bl page write */
+#define RM_CMD_BOOT_WRT                0x11            /* send bl write */
+#define RM_CMD_BOOT_ACK                0x22            /* send ack*/
+#define RM_CMD_BOOT_CHK                0x33            /* send data check */
+#define RM_CMD_BOOT_READ       0x44            /* send wait bl data ready*/
+
+#define RM_BOOT_RDY            0xFF            /* bl data ready */
+
+/* I2C main commands */
+#define RM_CMD_QUERY_BANK      0x2B
+#define RM_CMD_DATA_BANK       0x4D
+#define RM_CMD_ENTER_SLEEP     0x4E
+#define RM_CMD_BANK_SWITCH     0xAA
+
+#define RM_RESET_MSG_ADDR      0x40000004
+
+#define RM_MAX_READ_SIZE       56
+#define RM_PACKET_CRC_SIZE     2
+
+/* Touch relative info */
+#define RM_MAX_RETRIES         3
+#define RM_MAX_TOUCH_NUM       10
+#define RM_BOOT_DELAY_MS       100
+
+/* Offsets in contact data */
+#define RM_CONTACT_STATE_POS   0
+#define RM_CONTACT_X_POS       1
+#define RM_CONTACT_Y_POS       3
+#define RM_CONTACT_PRESSURE_POS        5
+#define RM_CONTACT_WIDTH_X_POS 6
+#define RM_CONTACT_WIDTH_Y_POS 7
+
+/* Bootloader relative info */
+#define RM_BL_WRT_CMD_SIZE     3       /* bl flash wrt cmd size */
+#define RM_BL_WRT_PKG_SIZE     32      /* bl wrt pkg size */
+#define RM_BL_WRT_LEN          (RM_BL_WRT_PKG_SIZE + RM_BL_WRT_CMD_SIZE)
+#define RM_FW_PAGE_SIZE                128
+#define RM_MAX_FW_RETRIES      30
+#define RM_MAX_FW_SIZE         0xD000
+
+#define RM_POWERON_DELAY_USEC  500
+#define RM_RESET_DELAY_MSEC    50
+
+enum raydium_bl_cmd {
+       BL_HEADER = 0,
+       BL_PAGE_STR,
+       BL_PKG_IDX,
+       BL_DATA_STR,
+};
+
+enum raydium_bl_ack {
+       RAYDIUM_ACK_NULL = 0,
+       RAYDIUM_WAIT_READY,
+       RAYDIUM_PATH_READY,
+};
+
+enum raydium_boot_mode {
+       RAYDIUM_TS_MAIN = 0,
+       RAYDIUM_TS_BLDR,
+};
+
+/* Response to RM_CMD_DATA_BANK request */
+struct raydium_data_info {
+       __le32 data_bank_addr;
+       u8 pkg_size;
+       u8 tp_info_size;
+};
+
+struct raydium_info {
+       __le32 hw_ver;          /*device version */
+       u8 main_ver;
+       u8 sub_ver;
+       __le16 ft_ver;          /* test version */
+       u8 x_num;
+       u8 y_num;
+       __le16 x_max;
+       __le16 y_max;
+       u8 x_res;               /* units/mm */
+       u8 y_res;               /* units/mm */
+};
+
+/* struct raydium_data - represents state of Raydium touchscreen device */
+struct raydium_data {
+       struct i2c_client *client;
+       struct input_dev *input;
+
+       struct regulator *avdd;
+       struct regulator *vccio;
+       struct gpio_desc *reset_gpio;
+
+       struct raydium_info info;
+
+       struct mutex sysfs_mutex;
+
+       u8 *report_data;
+
+       u32 data_bank_addr;
+       u8 report_size;
+       u8 contact_size;
+       u8 pkg_size;
+
+       enum raydium_boot_mode boot_mode;
+
+       bool wake_irq_enabled;
+};
+
+static int raydium_i2c_send(struct i2c_client *client,
+                           u8 addr, const void *data, size_t len)
+{
+       u8 *buf;
+       int tries = 0;
+       int ret;
+
+       buf = kmalloc(len + 1, GFP_KERNEL);
+       if (!buf)
+               return -ENOMEM;
+
+       buf[0] = addr;
+       memcpy(buf + 1, data, len);
+
+       do {
+               ret = i2c_master_send(client, buf, len + 1);
+               if (likely(ret == len + 1))
+                       break;
+
+               msleep(20);
+       } while (++tries < RM_MAX_RETRIES);
+
+       kfree(buf);
+
+       if (unlikely(ret != len + 1)) {
+               if (ret >= 0)
+                       ret = -EIO;
+               dev_err(&client->dev, "%s failed: %d\n", __func__, ret);
+               return ret;
+       }
+
+       return 0;
+}
+
+static int raydium_i2c_read(struct i2c_client *client,
+                           u8 addr, void *data, size_t len)
+{
+       struct i2c_msg xfer[] = {
+               {
+                       .addr = client->addr,
+                       .len = 1,
+                       .buf = &addr,
+               },
+               {
+                       .addr = client->addr,
+                       .flags = I2C_M_RD,
+                       .len = len,
+                       .buf = data,
+               }
+       };
+       int ret;
+
+       ret = i2c_transfer(client->adapter, xfer, ARRAY_SIZE(xfer));
+       if (unlikely(ret != ARRAY_SIZE(xfer)))
+               return ret < 0 ? ret : -EIO;
+
+       return 0;
+}
+
+static int raydium_i2c_read_message(struct i2c_client *client,
+                                   u32 addr, void *data, size_t len)
+{
+       __be32 be_addr;
+       size_t xfer_len;
+       int error;
+
+       while (len) {
+               xfer_len = min_t(size_t, len, RM_MAX_READ_SIZE);
+
+               be_addr = cpu_to_be32(addr);
+
+               error = raydium_i2c_send(client, RM_CMD_BANK_SWITCH,
+                                        &be_addr, sizeof(be_addr));
+               if (!error)
+                       error = raydium_i2c_read(client, addr & 0xff,
+                                                data, xfer_len);
+               if (error)
+                       return error;
+
+               len -= xfer_len;
+               data += xfer_len;
+               addr += xfer_len;
+       }
+
+       return 0;
+}
+
+static int raydium_i2c_send_message(struct i2c_client *client,
+                                   u32 addr, const void *data, size_t len)
+{
+       __be32 be_addr = cpu_to_be32(addr);
+       int error;
+
+       error = raydium_i2c_send(client, RM_CMD_BANK_SWITCH,
+                                &be_addr, sizeof(be_addr));
+       if (!error)
+               error = raydium_i2c_send(client, addr & 0xff, data, len);
+
+       return error;
+}
+
+static int raydium_i2c_sw_reset(struct i2c_client *client)
+{
+       const u8 soft_rst_cmd = 0x01;
+       int error;
+
+       error = raydium_i2c_send_message(client, RM_RESET_MSG_ADDR,
+                                        &soft_rst_cmd, sizeof(soft_rst_cmd));
+       if (error) {
+               dev_err(&client->dev, "software reset failed: %d\n", error);
+               return error;
+       }
+
+       msleep(RM_RESET_DELAY_MSEC);
+
+       return 0;
+}
+
+static int raydium_i2c_query_ts_info(struct raydium_data *ts)
+{
+       struct i2c_client *client = ts->client;
+       struct raydium_data_info data_info;
+       __le32 query_bank_addr;
+
+       int error, retry_cnt;
+
+       for (retry_cnt = 0; retry_cnt < RM_MAX_RETRIES; retry_cnt++) {
+               error = raydium_i2c_read(client, RM_CMD_DATA_BANK,
+                                        &data_info, sizeof(data_info));
+               if (error)
+                       continue;
+
+               /*
+                * Warn user if we already allocated memory for reports and
+                * then the size changed (due to firmware update?) and keep
+                * old size instead.
+                */
+               if (ts->report_data && ts->pkg_size != data_info.pkg_size) {
+                       dev_warn(&client->dev,
+                                "report size changes, was: %d, new: %d\n",
+                                ts->pkg_size, data_info.pkg_size);
+               } else {
+                       ts->pkg_size = data_info.pkg_size;
+                       ts->report_size = ts->pkg_size - RM_PACKET_CRC_SIZE;
+               }
+
+               ts->contact_size = data_info.tp_info_size;
+               ts->data_bank_addr = le32_to_cpu(data_info.data_bank_addr);
+
+               dev_dbg(&client->dev,
+                       "data_bank_addr: %#08x, report_size: %d, contact_size: %d\n",
+                       ts->data_bank_addr, ts->report_size, ts->contact_size);
+
+               error = raydium_i2c_read(client, RM_CMD_QUERY_BANK,
+                                        &query_bank_addr,
+                                        sizeof(query_bank_addr));
+               if (error)
+                       continue;
+
+               error = raydium_i2c_read_message(client,
+                                                le32_to_cpu(query_bank_addr),
+                                                &ts->info, sizeof(ts->info));
+               if (error)
+                       continue;
+
+               return 0;
+       }
+
+       dev_err(&client->dev, "failed to query device parameters: %d\n", error);
+       return error;
+}
+
+static int raydium_i2c_check_fw_status(struct raydium_data *ts)
+{
+       struct i2c_client *client = ts->client;
+       static const u8 bl_ack = 0x62;
+       static const u8 main_ack = 0x66;
+       u8 buf[4];
+       int error;
+
+       error = raydium_i2c_read(client, RM_CMD_BOOT_READ, buf, sizeof(buf));
+       if (!error) {
+               if (buf[0] == bl_ack)
+                       ts->boot_mode = RAYDIUM_TS_BLDR;
+               else if (buf[0] == main_ack)
+                       ts->boot_mode = RAYDIUM_TS_MAIN;
+               return 0;
+       }
+
+       return error;
+}
+
+static int raydium_i2c_initialize(struct raydium_data *ts)
+{
+       struct i2c_client *client = ts->client;
+       int error, retry_cnt;
+
+       for (retry_cnt = 0; retry_cnt < RM_MAX_RETRIES; retry_cnt++) {
+               /* Wait for Hello packet */
+               msleep(RM_BOOT_DELAY_MS);
+
+               error = raydium_i2c_check_fw_status(ts);
+               if (error) {
+                       dev_err(&client->dev,
+                               "failed to read 'hello' packet: %d\n", error);
+                       continue;
+               }
+
+               if (ts->boot_mode == RAYDIUM_TS_BLDR ||
+                   ts->boot_mode == RAYDIUM_TS_MAIN) {
+                       break;
+               }
+       }
+
+       if (error)
+               ts->boot_mode = RAYDIUM_TS_BLDR;
+
+       if (ts->boot_mode == RAYDIUM_TS_BLDR) {
+               ts->info.hw_ver = cpu_to_le32(0xffffffffUL);
+               ts->info.main_ver = 0xff;
+               ts->info.sub_ver = 0xff;
+       } else {
+               raydium_i2c_query_ts_info(ts);
+       }
+
+       return error;
+}
+
+static int raydium_i2c_bl_chk_state(struct i2c_client *client,
+                                   enum raydium_bl_ack state)
+{
+       static const u8 ack_ok[] = { 0xFF, 0x39, 0x30, 0x30, 0x54 };
+       u8 rbuf[sizeof(ack_ok)];
+       u8 retry;
+       int error;
+
+       for (retry = 0; retry < RM_MAX_FW_RETRIES; retry++) {
+               switch (state) {
+               case RAYDIUM_ACK_NULL:
+                       return 0;
+
+               case RAYDIUM_WAIT_READY:
+                       error = raydium_i2c_read(client, RM_CMD_BOOT_CHK,
+                                                &rbuf[0], 1);
+                       if (!error && rbuf[0] == RM_BOOT_RDY)
+                               return 0;
+
+                       break;
+
+               case RAYDIUM_PATH_READY:
+                       error = raydium_i2c_read(client, RM_CMD_BOOT_CHK,
+                                                rbuf, sizeof(rbuf));
+                       if (!error && !memcmp(rbuf, ack_ok, sizeof(ack_ok)))
+                               return 0;
+
+                       break;
+
+               default:
+                       dev_err(&client->dev, "%s: invalid target state %d\n",
+                               __func__, state);
+                       return -EINVAL;
+               }
+
+               msleep(20);
+       }
+
+       return -ETIMEDOUT;
+}
+
+static int raydium_i2c_write_object(struct i2c_client *client,
+                                   const void *data, size_t len,
+                                   enum raydium_bl_ack state)
+{
+       int error;
+
+       error = raydium_i2c_send(client, RM_CMD_BOOT_WRT, data, len);
+       if (error) {
+               dev_err(&client->dev, "WRT obj command failed: %d\n",
+                       error);
+               return error;
+       }
+
+       error = raydium_i2c_send(client, RM_CMD_BOOT_ACK, NULL, 0);
+       if (error) {
+               dev_err(&client->dev, "Ack obj command failed: %d\n", error);
+               return error;
+       }
+
+       error = raydium_i2c_bl_chk_state(client, state);
+       if (error) {
+               dev_err(&client->dev, "BL check state failed: %d\n", error);
+               return error;
+       }
+       return 0;
+}
+
+static bool raydium_i2c_boot_trigger(struct i2c_client *client)
+{
+       static const u8 cmd[7][6] = {
+               { 0x08, 0x0C, 0x09, 0x00, 0x50, 0xD7 },
+               { 0x08, 0x04, 0x09, 0x00, 0x50, 0xA5 },
+               { 0x08, 0x04, 0x09, 0x00, 0x50, 0x00 },
+               { 0x08, 0x04, 0x09, 0x00, 0x50, 0xA5 },
+               { 0x08, 0x0C, 0x09, 0x00, 0x50, 0x00 },
+               { 0x06, 0x01, 0x00, 0x00, 0x00, 0x00 },
+               { 0x02, 0xA2, 0x00, 0x00, 0x00, 0x00 },
+       };
+       int i;
+       int error;
+
+       for (i = 0; i < 7; i++) {
+               error = raydium_i2c_write_object(client, cmd[i], sizeof(cmd[i]),
+                                                RAYDIUM_WAIT_READY);
+               if (error) {
+                       dev_err(&client->dev,
+                               "boot trigger failed at step %d: %d\n",
+                               i, error);
+                       return error;
+               }
+       }
+
+       return 0;
+}
+
+static bool raydium_i2c_fw_trigger(struct i2c_client *client)
+{
+       static const u8 cmd[5][11] = {
+               { 0, 0x09, 0x71, 0x0C, 0x09, 0x00, 0x50, 0xD7, 0, 0, 0 },
+               { 0, 0x09, 0x71, 0x04, 0x09, 0x00, 0x50, 0xA5, 0, 0, 0 },
+               { 0, 0x09, 0x71, 0x04, 0x09, 0x00, 0x50, 0x00, 0, 0, 0 },
+               { 0, 0x09, 0x71, 0x04, 0x09, 0x00, 0x50, 0xA5, 0, 0, 0 },
+               { 0, 0x09, 0x71, 0x0C, 0x09, 0x00, 0x50, 0x00, 0, 0, 0 },
+       };
+       int i;
+       int error;
+
+       for (i = 0; i < 5; i++) {
+               error = raydium_i2c_write_object(client, cmd[i], sizeof(cmd[i]),
+                                                RAYDIUM_ACK_NULL);
+               if (error) {
+                       dev_err(&client->dev,
+                               "fw trigger failed at step %d: %d\n",
+                               i, error);
+                       return error;
+               }
+       }
+
+       return 0;
+}
+
+static int raydium_i2c_check_path(struct i2c_client *client)
+{
+       static const u8 cmd[] = { 0x09, 0x00, 0x09, 0x00, 0x50, 0x10, 0x00 };
+       int error;
+
+       error = raydium_i2c_write_object(client, cmd, sizeof(cmd),
+                                        RAYDIUM_PATH_READY);
+       if (error) {
+               dev_err(&client->dev, "check path command failed: %d\n", error);
+               return error;
+       }
+
+       return 0;
+}
+
+static int raydium_i2c_enter_bl(struct i2c_client *client)
+{
+       static const u8 cal_cmd[] = { 0x00, 0x01, 0x52 };
+       int error;
+
+       error = raydium_i2c_write_object(client, cal_cmd, sizeof(cal_cmd),
+                                        RAYDIUM_ACK_NULL);
+       if (error) {
+               dev_err(&client->dev, "enter bl command failed: %d\n", error);
+               return error;
+       }
+
+       msleep(RM_BOOT_DELAY_MS);
+       return 0;
+}
+
+static int raydium_i2c_leave_bl(struct i2c_client *client)
+{
+       static const u8 leave_cmd[] = { 0x05, 0x00 };
+       int error;
+
+       error = raydium_i2c_write_object(client, leave_cmd, sizeof(leave_cmd),
+                                        RAYDIUM_ACK_NULL);
+       if (error) {
+               dev_err(&client->dev, "leave bl command failed: %d\n", error);
+               return error;
+       }
+
+       msleep(RM_BOOT_DELAY_MS);
+       return 0;
+}
+
+static int raydium_i2c_write_checksum(struct i2c_client *client,
+                                     size_t length, u16 checksum)
+{
+       u8 checksum_cmd[] = { 0x00, 0x05, 0x6D, 0x00, 0x00, 0x00, 0x00 };
+       int error;
+
+       put_unaligned_le16(length, &checksum_cmd[3]);
+       put_unaligned_le16(checksum, &checksum_cmd[5]);
+
+       error = raydium_i2c_write_object(client,
+                                        checksum_cmd, sizeof(checksum_cmd),
+                                        RAYDIUM_ACK_NULL);
+       if (error) {
+               dev_err(&client->dev, "failed to write checksum: %d\n",
+                       error);
+               return error;
+       }
+
+       return 0;
+}
+
+static int raydium_i2c_disable_watch_dog(struct i2c_client *client)
+{
+       static const u8 cmd[] = { 0x0A, 0xAA };
+       int error;
+
+       error = raydium_i2c_write_object(client, cmd, sizeof(cmd),
+                                        RAYDIUM_WAIT_READY);
+       if (error) {
+               dev_err(&client->dev, "disable watchdog command failed: %d\n",
+                       error);
+               return error;
+       }
+
+       return 0;
+}
+
+static int raydium_i2c_fw_write_page(struct i2c_client *client,
+                                    u16 page_idx, const void *data, size_t len)
+{
+       u8 buf[RM_BL_WRT_LEN];
+       size_t xfer_len;
+       int error;
+       int i;
+
+       BUILD_BUG_ON((RM_FW_PAGE_SIZE % RM_BL_WRT_PKG_SIZE) != 0);
+
+       for (i = 0; i < RM_FW_PAGE_SIZE / RM_BL_WRT_PKG_SIZE; i++) {
+               buf[BL_HEADER] = RM_CMD_BOOT_PAGE_WRT;
+               buf[BL_PAGE_STR] = page_idx ? 0xff : 0;
+               buf[BL_PKG_IDX] = i + 1;
+
+               xfer_len = min_t(size_t, len, RM_BL_WRT_PKG_SIZE);
+               memcpy(&buf[BL_DATA_STR], data, xfer_len);
+               if (len < RM_BL_WRT_PKG_SIZE)
+                       memset(&buf[BL_DATA_STR + xfer_len], 0xff,
+                               RM_BL_WRT_PKG_SIZE - xfer_len);
+
+               error = raydium_i2c_write_object(client, buf, RM_BL_WRT_LEN,
+                                                RAYDIUM_WAIT_READY);
+               if (error) {
+                       dev_err(&client->dev,
+                               "page write command failed for page %d, chunk %d: %d\n",
+                               page_idx, i, error);
+                       return error;
+               }
+
+               data += xfer_len;
+               len -= xfer_len;
+       }
+
+       return error;
+}
+
+static u16 raydium_calc_chksum(const u8 *buf, u16 len)
+{
+       u16 checksum = 0;
+       u16 i;
+
+       for (i = 0; i < len; i++)
+               checksum += buf[i];
+
+       return checksum;
+}
+
+static int raydium_i2c_do_update_firmware(struct raydium_data *ts,
+                                        const struct firmware *fw)
+{
+       struct i2c_client *client = ts->client;
+       const void *data;
+       size_t data_len;
+       size_t len;
+       int page_nr;
+       int i;
+       int error;
+       u16 fw_checksum;
+
+       if (fw->size == 0 || fw->size > RM_MAX_FW_SIZE) {
+               dev_err(&client->dev, "Invalid firmware length\n");
+               return -EINVAL;
+       }
+
+       error = raydium_i2c_check_fw_status(ts);
+       if (error) {
+               dev_err(&client->dev, "Unable to access IC %d\n", error);
+               return error;
+       }
+
+       if (ts->boot_mode == RAYDIUM_TS_MAIN) {
+               for (i = 0; i < RM_MAX_RETRIES; i++) {
+                       error = raydium_i2c_enter_bl(client);
+                       if (!error) {
+                               error = raydium_i2c_check_fw_status(ts);
+                               if (error) {
+                                       dev_err(&client->dev,
+                                               "unable to access IC: %d\n",
+                                               error);
+                                       return error;
+                               }
+
+                               if (ts->boot_mode == RAYDIUM_TS_BLDR)
+                                       break;
+                       }
+               }
+
+               if (ts->boot_mode == RAYDIUM_TS_MAIN) {
+                       dev_err(&client->dev,
+                               "failied to jump to boot loader: %d\n",
+                               error);
+                       return -EIO;
+               }
+       }
+
+       error = raydium_i2c_disable_watch_dog(client);
+       if (error)
+               return error;
+
+       error = raydium_i2c_check_path(client);
+       if (error)
+               return error;
+
+       error = raydium_i2c_boot_trigger(client);
+       if (error) {
+               dev_err(&client->dev, "send boot trigger fail: %d\n", error);
+               return error;
+       }
+
+       msleep(RM_BOOT_DELAY_MS);
+
+       data = fw->data;
+       data_len = fw->size;
+       page_nr = 0;
+
+       while (data_len) {
+               len = min_t(size_t, data_len, RM_FW_PAGE_SIZE);
+
+               error = raydium_i2c_fw_write_page(client, page_nr++, data, len);
+               if (error)
+                       return error;
+
+               msleep(20);
+
+               data += len;
+               data_len -= len;
+       }
+
+       error = raydium_i2c_leave_bl(client);
+       if (error) {
+               dev_err(&client->dev,
+                       "failed to leave boot loader: %d\n", error);
+               return error;
+       }
+
+       dev_dbg(&client->dev, "left boot loader mode\n");
+       msleep(RM_BOOT_DELAY_MS);
+
+       error = raydium_i2c_check_fw_status(ts);
+       if (error) {
+               dev_err(&client->dev,
+                       "failed to check fw status after write: %d\n",
+                       error);
+               return error;
+       }
+
+       if (ts->boot_mode != RAYDIUM_TS_MAIN) {
+               dev_err(&client->dev,
+                       "failed to switch to main fw after writing firmware: %d\n",
+                       error);
+               return -EINVAL;
+       }
+
+       error = raydium_i2c_fw_trigger(client);
+       if (error) {
+               dev_err(&client->dev, "failed to trigger fw: %d\n", error);
+               return error;
+       }
+
+       fw_checksum = raydium_calc_chksum(fw->data, fw->size);
+
+       error = raydium_i2c_write_checksum(client, fw->size, fw_checksum);
+       if (error)
+               return error;
+
+       return 0;
+}
+
+static int raydium_i2c_fw_update(struct raydium_data *ts)
+{
+       struct i2c_client *client = ts->client;
+       const struct firmware *fw = NULL;
+       const char *fw_file = "raydium.fw";
+       int error;
+
+       error = request_firmware(&fw, fw_file, &client->dev);
+       if (error) {
+               dev_err(&client->dev, "Unable to open firmware %s\n", fw_file);
+               return error;
+       }
+
+       disable_irq(client->irq);
+
+       error = raydium_i2c_do_update_firmware(ts, fw);
+       if (error) {
+               dev_err(&client->dev, "firmware update failed: %d\n", error);
+               ts->boot_mode = RAYDIUM_TS_BLDR;
+               goto out_enable_irq;
+       }
+
+       error = raydium_i2c_initialize(ts);
+       if (error) {
+               dev_err(&client->dev,
+                       "failed to initialize device after firmware update: %d\n",
+                       error);
+               ts->boot_mode = RAYDIUM_TS_BLDR;
+               goto out_enable_irq;
+       }
+
+       ts->boot_mode = RAYDIUM_TS_MAIN;
+
+out_enable_irq:
+       enable_irq(client->irq);
+       msleep(100);
+
+       release_firmware(fw);
+
+       return error;
+}
+
+static void raydium_mt_event(struct raydium_data *ts)
+{
+       int i;
+
+       for (i = 0; i < ts->report_size / ts->contact_size; i++) {
+               u8 *contact = &ts->report_data[ts->contact_size * i];
+               bool state = contact[RM_CONTACT_STATE_POS];
+               u8 wx, wy;
+
+               input_mt_slot(ts->input, i);
+               input_mt_report_slot_state(ts->input, MT_TOOL_FINGER, state);
+
+               if (!state)
+                       continue;
+
+               input_report_abs(ts->input, ABS_MT_POSITION_X,
+                               get_unaligned_le16(&contact[RM_CONTACT_X_POS]));
+               input_report_abs(ts->input, ABS_MT_POSITION_Y,
+                               get_unaligned_le16(&contact[RM_CONTACT_Y_POS]));
+               input_report_abs(ts->input, ABS_MT_PRESSURE,
+                               contact[RM_CONTACT_PRESSURE_POS]);
+
+               wx = contact[RM_CONTACT_WIDTH_X_POS];
+               wy = contact[RM_CONTACT_WIDTH_Y_POS];
+
+               input_report_abs(ts->input, ABS_MT_TOUCH_MAJOR, max(wx, wy));
+               input_report_abs(ts->input, ABS_MT_TOUCH_MINOR, min(wx, wy));
+       }
+
+       input_mt_sync_frame(ts->input);
+       input_sync(ts->input);
+}
+
+static irqreturn_t raydium_i2c_irq(int irq, void *_dev)
+{
+       struct raydium_data *ts = _dev;
+       int error;
+       u16 fw_crc;
+       u16 calc_crc;
+
+       if (ts->boot_mode != RAYDIUM_TS_MAIN)
+               goto out;
+
+       error = raydium_i2c_read_message(ts->client, ts->data_bank_addr,
+                                        ts->report_data, ts->pkg_size);
+       if (error)
+               goto out;
+
+       fw_crc = get_unaligned_le16(&ts->report_data[ts->report_size]);
+       calc_crc = raydium_calc_chksum(ts->report_data, ts->report_size);
+       if (unlikely(fw_crc != calc_crc)) {
+               dev_warn(&ts->client->dev,
+                        "%s: invalid packet crc %#04x vs %#04x\n",
+                        __func__, calc_crc, fw_crc);
+               goto out;
+       }
+
+       raydium_mt_event(ts);
+
+out:
+       return IRQ_HANDLED;
+}
+
+static ssize_t raydium_i2c_fw_ver_show(struct device *dev,
+                                      struct device_attribute *attr, char *buf)
+{
+       struct i2c_client *client = to_i2c_client(dev);
+       struct raydium_data *ts = i2c_get_clientdata(client);
+
+       return sprintf(buf, "%d.%d\n", ts->info.main_ver, ts->info.sub_ver);
+}
+
+static ssize_t raydium_i2c_hw_ver_show(struct device *dev,
+                                      struct device_attribute *attr, char *buf)
+{
+       struct i2c_client *client = to_i2c_client(dev);
+       struct raydium_data *ts = i2c_get_clientdata(client);
+
+       return sprintf(buf, "%#04x\n", le32_to_cpu(ts->info.hw_ver));
+}
+
+static ssize_t raydium_i2c_boot_mode_show(struct device *dev,
+                                         struct device_attribute *attr,
+                                         char *buf)
+{
+       struct i2c_client *client = to_i2c_client(dev);
+       struct raydium_data *ts = i2c_get_clientdata(client);
+
+       return sprintf(buf, "%s\n",
+                      ts->boot_mode == RAYDIUM_TS_MAIN ?
+                               "Normal" : "Recovery");
+}
+
+static ssize_t raydium_i2c_update_fw_store(struct device *dev,
+                                          struct device_attribute *attr,
+                                          const char *buf, size_t count)
+{
+       struct i2c_client *client = to_i2c_client(dev);
+       struct raydium_data *ts = i2c_get_clientdata(client);
+       int error;
+
+       error = mutex_lock_interruptible(&ts->sysfs_mutex);
+       if (error)
+               return error;
+
+       error = raydium_i2c_fw_update(ts);
+
+       mutex_unlock(&ts->sysfs_mutex);
+
+       return error ?: count;
+}
+
+static ssize_t raydium_i2c_calibrate_store(struct device *dev,
+                                          struct device_attribute *attr,
+                                          const char *buf, size_t count)
+{
+       struct i2c_client *client = to_i2c_client(dev);
+       struct raydium_data *ts = i2c_get_clientdata(client);
+       static const u8 cal_cmd[] = { 0x00, 0x01, 0x9E };
+       int error;
+
+       error = mutex_lock_interruptible(&ts->sysfs_mutex);
+       if (error)
+               return error;
+
+       error = raydium_i2c_write_object(client, cal_cmd, sizeof(cal_cmd),
+                                        RAYDIUM_WAIT_READY);
+       if (error)
+               dev_err(&client->dev, "calibrate command failed: %d\n", error);
+
+       mutex_unlock(&ts->sysfs_mutex);
+       return error ?: count;
+}
+
+static DEVICE_ATTR(fw_version, S_IRUGO, raydium_i2c_fw_ver_show, NULL);
+static DEVICE_ATTR(hw_version, S_IRUGO, raydium_i2c_hw_ver_show, NULL);
+static DEVICE_ATTR(boot_mode, S_IRUGO, raydium_i2c_boot_mode_show, NULL);
+static DEVICE_ATTR(update_fw, S_IWUSR, NULL, raydium_i2c_update_fw_store);
+static DEVICE_ATTR(calibrate, S_IWUSR, NULL, raydium_i2c_calibrate_store);
+
+static struct attribute *raydium_i2c_attributes[] = {
+       &dev_attr_update_fw.attr,
+       &dev_attr_boot_mode.attr,
+       &dev_attr_fw_version.attr,
+       &dev_attr_hw_version.attr,
+       &dev_attr_calibrate.attr,
+       NULL
+};
+
+static struct attribute_group raydium_i2c_attribute_group = {
+       .attrs = raydium_i2c_attributes,
+};
+
+static void raydium_i2c_remove_sysfs_group(void *_data)
+{
+       struct raydium_data *ts = _data;
+
+       sysfs_remove_group(&ts->client->dev.kobj, &raydium_i2c_attribute_group);
+}
+
+static int raydium_i2c_power_on(struct raydium_data *ts)
+{
+       int error;
+
+       if (!ts->reset_gpio)
+               return 0;
+
+       gpiod_set_value_cansleep(ts->reset_gpio, 1);
+
+       error = regulator_enable(ts->avdd);
+       if (error) {
+               dev_err(&ts->client->dev,
+                       "failed to enable avdd regulator: %d\n", error);
+               goto release_reset_gpio;
+       }
+
+       error = regulator_enable(ts->vccio);
+       if (error) {
+               regulator_disable(ts->avdd);
+               dev_err(&ts->client->dev,
+                       "failed to enable vccio regulator: %d\n", error);
+               goto release_reset_gpio;
+       }
+
+       udelay(RM_POWERON_DELAY_USEC);
+
+release_reset_gpio:
+       gpiod_set_value_cansleep(ts->reset_gpio, 0);
+
+       if (error)
+               return error;
+
+       msleep(RM_RESET_DELAY_MSEC);
+
+       return 0;
+}
+
+static void raydium_i2c_power_off(void *_data)
+{
+       struct raydium_data *ts = _data;
+
+       if (ts->reset_gpio) {
+               gpiod_set_value_cansleep(ts->reset_gpio, 1);
+               regulator_disable(ts->vccio);
+               regulator_disable(ts->avdd);
+       }
+}
+
+static int raydium_i2c_probe(struct i2c_client *client,
+                            const struct i2c_device_id *id)
+{
+       union i2c_smbus_data dummy;
+       struct raydium_data *ts;
+       int error;
+
+       if (!i2c_check_functionality(client->adapter, I2C_FUNC_I2C)) {
+               dev_err(&client->dev,
+                       "i2c check functionality error (need I2C_FUNC_I2C)\n");
+               return -ENXIO;
+       }
+
+       ts = devm_kzalloc(&client->dev, sizeof(*ts), GFP_KERNEL);
+       if (!ts)
+               return -ENOMEM;
+
+       mutex_init(&ts->sysfs_mutex);
+
+       ts->client = client;
+       i2c_set_clientdata(client, ts);
+
+       ts->avdd = devm_regulator_get(&client->dev, "avdd");
+       if (IS_ERR(ts->avdd)) {
+               error = PTR_ERR(ts->avdd);
+               if (error != -EPROBE_DEFER)
+                       dev_err(&client->dev,
+                               "Failed to get 'avdd' regulator: %d\n", error);
+               return error;
+       }
+
+       ts->vccio = devm_regulator_get(&client->dev, "vccio");
+       if (IS_ERR(ts->vccio)) {
+               error = PTR_ERR(ts->vccio);
+               if (error != -EPROBE_DEFER)
+                       dev_err(&client->dev,
+                               "Failed to get 'vccio' regulator: %d\n", error);
+               return error;
+       }
+
+       ts->reset_gpio = devm_gpiod_get_optional(&client->dev, "reset",
+                                                GPIOD_OUT_LOW);
+       if (IS_ERR(ts->reset_gpio)) {
+               error = PTR_ERR(ts->reset_gpio);
+               if (error != -EPROBE_DEFER)
+                       dev_err(&client->dev,
+                               "failed to get reset gpio: %d\n", error);
+               return error;
+       }
+
+       error = raydium_i2c_power_on(ts);
+       if (error)
+               return error;
+
+       error = devm_add_action(&client->dev, raydium_i2c_power_off, ts);
+       if (error) {
+               dev_err(&client->dev,
+                       "failed to install power off action: %d\n", error);
+               raydium_i2c_power_off(ts);
+               return error;
+       }
+
+       /* Make sure there is something at this address */
+       if (i2c_smbus_xfer(client->adapter, client->addr, 0,
+                          I2C_SMBUS_READ, 0, I2C_SMBUS_BYTE, &dummy) < 0) {
+               dev_err(&client->dev, "nothing at this address\n");
+               return -ENXIO;
+       }
+
+       error = raydium_i2c_initialize(ts);
+       if (error) {
+               dev_err(&client->dev, "failed to initialize: %d\n", error);
+               return error;
+       }
+
+       ts->report_data = devm_kmalloc(&client->dev,
+                                      ts->pkg_size, GFP_KERNEL);
+       if (!ts->report_data)
+               return -ENOMEM;
+
+       ts->input = devm_input_allocate_device(&client->dev);
+       if (!ts->input) {
+               dev_err(&client->dev, "Failed to allocate input device\n");
+               return -ENOMEM;
+       }
+
+       ts->input->name = "Raydium Touchscreen";
+       ts->input->id.bustype = BUS_I2C;
+
+       input_set_drvdata(ts->input, ts);
+
+       input_set_abs_params(ts->input, ABS_MT_POSITION_X,
+                            0, le16_to_cpu(ts->info.x_max), 0, 0);
+       input_set_abs_params(ts->input, ABS_MT_POSITION_Y,
+                            0, le16_to_cpu(ts->info.y_max), 0, 0);
+       input_abs_set_res(ts->input, ABS_MT_POSITION_X, ts->info.x_res);
+       input_abs_set_res(ts->input, ABS_MT_POSITION_Y, ts->info.y_res);
+
+       input_set_abs_params(ts->input, ABS_MT_TOUCH_MAJOR, 0, 255, 0, 0);
+       input_set_abs_params(ts->input, ABS_MT_PRESSURE, 0, 255, 0, 0);
+
+       error = input_mt_init_slots(ts->input, RM_MAX_TOUCH_NUM,
+                                   INPUT_MT_DIRECT | INPUT_MT_DROP_UNUSED);
+       if (error) {
+               dev_err(&client->dev,
+                       "failed to initialize MT slots: %d\n", error);
+               return error;
+       }
+
+       error = input_register_device(ts->input);
+       if (error) {
+               dev_err(&client->dev,
+                       "unable to register input device: %d\n", error);
+               return error;
+       }
+
+       error = devm_request_threaded_irq(&client->dev, client->irq,
+                                         NULL, raydium_i2c_irq,
+                                         IRQF_ONESHOT, client->name, ts);
+       if (error) {
+               dev_err(&client->dev, "Failed to register interrupt\n");
+               return error;
+       }
+
+       error = sysfs_create_group(&client->dev.kobj,
+                                  &raydium_i2c_attribute_group);
+       if (error) {
+               dev_err(&client->dev, "failed to create sysfs attributes: %d\n",
+                       error);
+               return error;
+       }
+
+       error = devm_add_action(&client->dev,
+                               raydium_i2c_remove_sysfs_group, ts);
+       if (error) {
+               raydium_i2c_remove_sysfs_group(ts);
+               dev_err(&client->dev,
+                       "Failed to add sysfs cleanup action: %d\n", error);
+               return error;
+       }
+
+       return 0;
+}
+
+static void __maybe_unused raydium_enter_sleep(struct i2c_client *client)
+{
+       static const u8 sleep_cmd[] = { 0x5A, 0xff, 0x00, 0x0f };
+       int error;
+
+       error = raydium_i2c_send(client, RM_CMD_ENTER_SLEEP,
+                                sleep_cmd, sizeof(sleep_cmd));
+       if (error)
+               dev_err(&client->dev,
+                       "sleep command failed: %d\n", error);
+}
+
+static int __maybe_unused raydium_i2c_suspend(struct device *dev)
+{
+       struct i2c_client *client = to_i2c_client(dev);
+       struct raydium_data *ts = i2c_get_clientdata(client);
+
+       /* Sleep is not available in BLDR recovery mode */
+       if (ts->boot_mode != RAYDIUM_TS_MAIN)
+               return -EBUSY;
+
+       disable_irq(client->irq);
+
+       if (device_may_wakeup(dev)) {
+               raydium_enter_sleep(client);
+
+               ts->wake_irq_enabled = (enable_irq_wake(client->irq) == 0);
+       } else {
+               raydium_i2c_power_off(ts);
+       }
+
+       return 0;
+}
+
+static int __maybe_unused raydium_i2c_resume(struct device *dev)
+{
+       struct i2c_client *client = to_i2c_client(dev);
+       struct raydium_data *ts = i2c_get_clientdata(client);
+
+       if (device_may_wakeup(dev)) {
+               if (ts->wake_irq_enabled)
+                       disable_irq_wake(client->irq);
+               raydium_i2c_sw_reset(client);
+       } else {
+               raydium_i2c_power_on(ts);
+               raydium_i2c_initialize(ts);
+       }
+
+       enable_irq(client->irq);
+
+       return 0;
+}
+
+static SIMPLE_DEV_PM_OPS(raydium_i2c_pm_ops,
+                        raydium_i2c_suspend, raydium_i2c_resume);
+
+static const struct i2c_device_id raydium_i2c_id[] = {
+       { "raydium_i2c" , 0 },
+       { "rm32380", 0 },
+       { /* sentinel */ }
+};
+MODULE_DEVICE_TABLE(i2c, raydium_i2c_id);
+
+#ifdef CONFIG_ACPI
+static const struct acpi_device_id raydium_acpi_id[] = {
+       { "RAYD0001", 0 },
+       { /* sentinel */ }
+};
+MODULE_DEVICE_TABLE(acpi, raydium_acpi_id);
+#endif
+
+#ifdef CONFIG_OF
+static const struct of_device_id raydium_of_match[] = {
+       { .compatible = "raydium,rm32380", },
+       { /* sentinel */ }
+};
+MODULE_DEVICE_TABLE(of, raydium_of_match);
+#endif
+
+static struct i2c_driver raydium_i2c_driver = {
+       .probe = raydium_i2c_probe,
+       .id_table = raydium_i2c_id,
+       .driver = {
+               .name = "raydium_ts",
+               .pm = &raydium_i2c_pm_ops,
+               .acpi_match_table = ACPI_PTR(raydium_acpi_id),
+               .of_match_table = of_match_ptr(raydium_of_match),
+       },
+};
+module_i2c_driver(raydium_i2c_driver);
+
+MODULE_AUTHOR("Raydium");
+MODULE_DESCRIPTION("Raydium I2c Touchscreen driver");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/input/touchscreen/surface3_spi.c b/drivers/input/touchscreen/surface3_spi.c
new file mode 100644 (file)
index 0000000..e12fb9b
--- /dev/null
@@ -0,0 +1,427 @@
+/*
+ *  Driver for Ntrig/Microsoft Touchscreens over SPI
+ *
+ *  Copyright (c) 2016 Red Hat Inc.
+ */
+
+/*
+ * 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; version 2 of the License.
+ */
+
+#include <linux/kernel.h>
+
+#include <linux/delay.h>
+#include <linux/gpio/consumer.h>
+#include <linux/input.h>
+#include <linux/input/mt.h>
+#include <linux/interrupt.h>
+#include <linux/module.h>
+#include <linux/slab.h>
+#include <linux/spi/spi.h>
+#include <linux/acpi.h>
+
+#include <asm/unaligned.h>
+
+#define SURFACE3_PACKET_SIZE   264
+
+#define SURFACE3_REPORT_TOUCH  0xd2
+#define SURFACE3_REPORT_PEN    0x16
+
+struct surface3_ts_data {
+       struct spi_device *spi;
+       struct gpio_desc *gpiod_rst[2];
+       struct input_dev *input_dev;
+       struct input_dev *pen_input_dev;
+       int pen_tool;
+
+       u8 rd_buf[SURFACE3_PACKET_SIZE]         ____cacheline_aligned;
+};
+
+struct surface3_ts_data_finger {
+       u8 status;
+       __le16 tracking_id;
+       __le16 x;
+       __le16 cx;
+       __le16 y;
+       __le16 cy;
+       __le16 width;
+       __le16 height;
+       u32 padding;
+} __packed;
+
+struct surface3_ts_data_pen {
+       u8 status;
+       __le16 x;
+       __le16 y;
+       __le16 pressure;
+       u8 padding;
+} __packed;
+
+static int surface3_spi_read(struct surface3_ts_data *ts_data)
+{
+       struct spi_device *spi = ts_data->spi;
+
+       memset(ts_data->rd_buf, 0, sizeof(ts_data->rd_buf));
+       return spi_read(spi, ts_data->rd_buf, sizeof(ts_data->rd_buf));
+}
+
+static void surface3_spi_report_touch(struct surface3_ts_data *ts_data,
+                                  struct surface3_ts_data_finger *finger)
+{
+       int st = finger->status & 0x01;
+       int slot;
+
+       slot = input_mt_get_slot_by_key(ts_data->input_dev,
+                               get_unaligned_le16(&finger->tracking_id));
+       if (slot < 0)
+               return;
+
+       input_mt_slot(ts_data->input_dev, slot);
+       input_mt_report_slot_state(ts_data->input_dev, MT_TOOL_FINGER, st);
+       if (st) {
+               input_report_abs(ts_data->input_dev,
+                                ABS_MT_POSITION_X,
+                                get_unaligned_le16(&finger->x));
+               input_report_abs(ts_data->input_dev,
+                                ABS_MT_POSITION_Y,
+                                get_unaligned_le16(&finger->y));
+               input_report_abs(ts_data->input_dev,
+                                ABS_MT_WIDTH_MAJOR,
+                                get_unaligned_le16(&finger->width));
+               input_report_abs(ts_data->input_dev,
+                                ABS_MT_WIDTH_MINOR,
+                                get_unaligned_le16(&finger->height));
+       }
+}
+
+static void surface3_spi_process_touch(struct surface3_ts_data *ts_data, u8 *data)
+{
+       u16 timestamp;
+       unsigned int i;
+       timestamp = get_unaligned_le16(&data[15]);
+
+       for (i = 0; i < 13; i++) {
+               struct surface3_ts_data_finger *finger;
+
+               finger = (struct surface3_ts_data_finger *)&data[17 +
+                               i * sizeof(struct surface3_ts_data_finger)];
+
+               /*
+                * When bit 5 of status is 1, it marks the end of the report:
+                * - touch present: 0xe7
+                * - touch released: 0xe4
+                * - nothing valuable: 0xff
+                */
+               if (finger->status & 0x10)
+                       break;
+
+               surface3_spi_report_touch(ts_data, finger);
+       }
+
+       input_mt_sync_frame(ts_data->input_dev);
+       input_sync(ts_data->input_dev);
+}
+
+static void surface3_spi_report_pen(struct surface3_ts_data *ts_data,
+                                   struct surface3_ts_data_pen *pen)
+{
+       struct input_dev *dev = ts_data->pen_input_dev;
+       int st = pen->status;
+       int prox = st & 0x01;
+       int rubber = st & 0x18;
+       int tool = (prox && rubber) ? BTN_TOOL_RUBBER : BTN_TOOL_PEN;
+
+       /* fake proximity out to switch tools */
+       if (ts_data->pen_tool != tool) {
+               input_report_key(dev, ts_data->pen_tool, 0);
+               input_sync(dev);
+               ts_data->pen_tool = tool;
+       }
+
+       input_report_key(dev, BTN_TOUCH, st & 0x12);
+
+       input_report_key(dev, ts_data->pen_tool, prox);
+
+       if (st) {
+               input_report_key(dev,
+                                BTN_STYLUS,
+                                st & 0x04);
+
+               input_report_abs(dev,
+                                ABS_X,
+                                get_unaligned_le16(&pen->x));
+               input_report_abs(dev,
+                                ABS_Y,
+                                get_unaligned_le16(&pen->y));
+               input_report_abs(dev,
+                                ABS_PRESSURE,
+                                get_unaligned_le16(&pen->pressure));
+       }
+}
+
+static void surface3_spi_process_pen(struct surface3_ts_data *ts_data, u8 *data)
+{
+       struct surface3_ts_data_pen *pen;
+
+       pen = (struct surface3_ts_data_pen *)&data[15];
+
+       surface3_spi_report_pen(ts_data, pen);
+       input_sync(ts_data->pen_input_dev);
+}
+
+static void surface3_spi_process(struct surface3_ts_data *ts_data)
+{
+       const char header[] = {
+               0xff, 0xff, 0xff, 0xff, 0xa5, 0x5a, 0xe7, 0x7e, 0x01
+       };
+       u8 *data = ts_data->rd_buf;
+
+       if (memcmp(header, data, sizeof(header)))
+               dev_err(&ts_data->spi->dev,
+                       "%s header error: %*ph, ignoring...\n",
+                       __func__, (int)sizeof(header), data);
+
+       switch (data[9]) {
+       case SURFACE3_REPORT_TOUCH:
+               surface3_spi_process_touch(ts_data, data);
+               break;
+       case SURFACE3_REPORT_PEN:
+               surface3_spi_process_pen(ts_data, data);
+               break;
+       default:
+               dev_err(&ts_data->spi->dev,
+                       "%s unknown packet type: %x, ignoring...\n",
+                       __func__, data[9]);
+               break;
+       }
+}
+
+static irqreturn_t surface3_spi_irq_handler(int irq, void *dev_id)
+{
+       struct surface3_ts_data *data = dev_id;
+
+       if (surface3_spi_read(data))
+               return IRQ_HANDLED;
+
+       dev_dbg(&data->spi->dev, "%s received -> %*ph\n",
+               __func__, SURFACE3_PACKET_SIZE, data->rd_buf);
+       surface3_spi_process(data);
+
+       return IRQ_HANDLED;
+}
+
+static void surface3_spi_power(struct surface3_ts_data *data, bool on)
+{
+       gpiod_set_value(data->gpiod_rst[0], on);
+       gpiod_set_value(data->gpiod_rst[1], on);
+       /* let the device settle a little */
+       msleep(20);
+}
+
+/**
+ * surface3_spi_get_gpio_config - Get GPIO config from ACPI/DT
+ *
+ * @ts: surface3_spi_ts_data pointer
+ */
+static int surface3_spi_get_gpio_config(struct surface3_ts_data *data)
+{
+       int error;
+       struct device *dev;
+       struct gpio_desc *gpiod;
+       int i;
+
+       dev = &data->spi->dev;
+
+       /* Get the reset lines GPIO pin number */
+       for (i = 0; i < 2; i++) {
+               gpiod = devm_gpiod_get_index(dev, NULL, i, GPIOD_OUT_LOW);
+               if (IS_ERR(gpiod)) {
+                       error = PTR_ERR(gpiod);
+                       if (error != -EPROBE_DEFER)
+                               dev_err(dev,
+                                       "Failed to get power GPIO %d: %d\n",
+                                       i,
+                                       error);
+                       return error;
+               }
+
+               data->gpiod_rst[i] = gpiod;
+       }
+
+       return 0;
+}
+
+static int surface3_spi_create_touch_input(struct surface3_ts_data *data)
+{
+       struct input_dev *input;
+       int error;
+
+       input = devm_input_allocate_device(&data->spi->dev);
+       if (!input)
+               return -ENOMEM;
+
+       data->input_dev = input;
+
+       input_set_abs_params(input, ABS_MT_POSITION_X, 0, 9600, 0, 0);
+       input_abs_set_res(input, ABS_MT_POSITION_X, 40);
+       input_set_abs_params(input, ABS_MT_POSITION_Y, 0, 7200, 0, 0);
+       input_abs_set_res(input, ABS_MT_POSITION_Y, 48);
+       input_set_abs_params(input, ABS_MT_WIDTH_MAJOR, 0, 1024, 0, 0);
+       input_set_abs_params(input, ABS_MT_WIDTH_MINOR, 0, 1024, 0, 0);
+       input_mt_init_slots(input, 10, INPUT_MT_DIRECT);
+
+       input->name = "Surface3 SPI Capacitive TouchScreen";
+       input->phys = "input/ts";
+       input->id.bustype = BUS_SPI;
+       input->id.vendor = 0x045e;      /* Microsoft */
+       input->id.product = 0x0001;
+       input->id.version = 0x0000;
+
+       error = input_register_device(input);
+       if (error) {
+               dev_err(&data->spi->dev,
+                       "Failed to register input device: %d", error);
+               return error;
+       }
+
+       return 0;
+}
+
+static int surface3_spi_create_pen_input(struct surface3_ts_data *data)
+{
+       struct input_dev *input;
+       int error;
+
+       input = devm_input_allocate_device(&data->spi->dev);
+       if (!input)
+               return -ENOMEM;
+
+       data->pen_input_dev = input;
+       data->pen_tool = BTN_TOOL_PEN;
+
+       __set_bit(INPUT_PROP_DIRECT, input->propbit);
+       __set_bit(INPUT_PROP_POINTER, input->propbit);
+       input_set_abs_params(input, ABS_X, 0, 9600, 0, 0);
+       input_abs_set_res(input, ABS_X, 40);
+       input_set_abs_params(input, ABS_Y, 0, 7200, 0, 0);
+       input_abs_set_res(input, ABS_Y, 48);
+       input_set_abs_params(input, ABS_PRESSURE, 0, 1024, 0, 0);
+       input_set_capability(input, EV_KEY, BTN_TOUCH);
+       input_set_capability(input, EV_KEY, BTN_STYLUS);
+       input_set_capability(input, EV_KEY, BTN_TOOL_PEN);
+       input_set_capability(input, EV_KEY, BTN_TOOL_RUBBER);
+
+       input->name = "Surface3 SPI Pen Input";
+       input->phys = "input/ts";
+       input->id.bustype = BUS_SPI;
+       input->id.vendor = 0x045e;     /* Microsoft */
+       input->id.product = 0x0002;
+       input->id.version = 0x0000;
+
+       error = input_register_device(input);
+       if (error) {
+               dev_err(&data->spi->dev,
+                       "Failed to register input device: %d", error);
+               return error;
+       }
+
+       return 0;
+}
+
+static int surface3_spi_probe(struct spi_device *spi)
+{
+       struct surface3_ts_data *data;
+       int error;
+
+       /* Set up SPI*/
+       spi->bits_per_word = 8;
+       spi->mode = SPI_MODE_0;
+       error = spi_setup(spi);
+       if (error)
+               return error;
+
+       data = devm_kzalloc(&spi->dev, sizeof(*data), GFP_KERNEL);
+       if (!data)
+               return -ENOMEM;
+
+       data->spi = spi;
+       spi_set_drvdata(spi, data);
+
+       error = surface3_spi_get_gpio_config(data);
+       if (error)
+               return error;
+
+       surface3_spi_power(data, true);
+       surface3_spi_power(data, false);
+       surface3_spi_power(data, true);
+
+       error = surface3_spi_create_touch_input(data);
+       if (error)
+               return error;
+
+       error = surface3_spi_create_pen_input(data);
+       if (error)
+               return error;
+
+       error = devm_request_threaded_irq(&spi->dev, spi->irq,
+                                         NULL, surface3_spi_irq_handler,
+                                         IRQF_ONESHOT,
+                                         "Surface3-irq", data);
+       if (error)
+               return error;
+
+       return 0;
+}
+
+static int __maybe_unused surface3_spi_suspend(struct device *dev)
+{
+       struct spi_device *spi = to_spi_device(dev);
+       struct surface3_ts_data *data = spi_get_drvdata(spi);
+
+       disable_irq(data->spi->irq);
+
+       surface3_spi_power(data, false);
+
+       return 0;
+}
+
+static int __maybe_unused surface3_spi_resume(struct device *dev)
+{
+       struct spi_device *spi = to_spi_device(dev);
+       struct surface3_ts_data *data = spi_get_drvdata(spi);
+
+       surface3_spi_power(data, true);
+
+       enable_irq(data->spi->irq);
+
+       return 0;
+}
+
+static SIMPLE_DEV_PM_OPS(surface3_spi_pm_ops,
+                        surface3_spi_suspend,
+                        surface3_spi_resume);
+
+#ifdef CONFIG_ACPI
+static const struct acpi_device_id surface3_spi_acpi_match[] = {
+       { "MSHW0037", 0 },
+       { }
+};
+MODULE_DEVICE_TABLE(acpi, surface3_spi_acpi_match);
+#endif
+
+static struct spi_driver surface3_spi_driver = {
+       .driver = {
+               .name   = "Surface3-spi",
+               .acpi_match_table = ACPI_PTR(surface3_spi_acpi_match),
+               .pm = &surface3_spi_pm_ops,
+       },
+       .probe = surface3_spi_probe,
+};
+
+module_spi_driver(surface3_spi_driver);
+
+MODULE_AUTHOR("Benjamin Tissoires <benjamin.tissoires@gmail.com>");
+MODULE_DESCRIPTION("Surface 3 SPI touchscreen driver");
+MODULE_LICENSE("GPL v2");
index 8b3f15c..7953381 100644 (file)
@@ -406,7 +406,7 @@ static int titsc_probe(struct platform_device *pdev)
        int err;
 
        /* Allocate memory for device */
-       ts_dev = kzalloc(sizeof(struct titsc), GFP_KERNEL);
+       ts_dev = kzalloc(sizeof(*ts_dev), GFP_KERNEL);
        input_dev = input_allocate_device();
        if (!ts_dev || !input_dev) {
                dev_err(&pdev->dev, "failed to allocate memory.\n");
index dfa7f1c..b7059ed 100644 (file)
@@ -568,7 +568,7 @@ int tsc200x_probe(struct device *dev, int irq, const struct input_id *tsc_id,
        input_set_abs_params(input_dev, ABS_PRESSURE, 0, max_p, fudge_p, 0);
 
        if (np)
-               touchscreen_parse_properties(input_dev, false);
+               touchscreen_parse_properties(input_dev, false, NULL);
 
        input_dev->open = tsc200x_open;
        input_dev->close = tsc200x_close;
index b6fc4bd..85e9572 100644 (file)
@@ -518,13 +518,21 @@ static int w8001_setup_touch(struct w8001 *w8001, char *basename,
                w8001->pktlen = W8001_PKTLEN_TOUCH2FG;
 
                __set_bit(BTN_TOOL_DOUBLETAP, dev->keybit);
-               input_mt_init_slots(dev, 2, 0);
+               error = input_mt_init_slots(dev, 2, 0);
+               if (error) {
+                       dev_err(&w8001->serio->dev,
+                               "failed to initialize MT slots: %d\n", error);
+                       return error;
+               }
+
                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);
+               input_abs_set_res(dev, ABS_MT_POSITION_X, touch.panel_res);
+               input_abs_set_res(dev, ABS_MT_POSITION_Y, touch.panel_res);
 
                strlcat(basename, " 2FG", basename_sz);
                if (w8001->max_pen_x && w8001->max_pen_y)
index 1e96769..a65e3b2 100644 (file)
@@ -95,7 +95,7 @@ struct input_value {
  * @grab: input handle that currently has the device grabbed (via
  *     EVIOCGRAB ioctl). When a handle grabs a device it becomes sole
  *     recipient for all input events coming from the device
- * @event_lock: this spinlock is is taken when input core receives
+ * @event_lock: this spinlock is taken when input core receives
  *     and processes a new event for the device (in input_event()).
  *     Code that accesses and/or modifies parameters of a device
  *     (such as keymap or absmin, absmax, absfuzz, etc.) after device
index c91e137..09d22cc 100644 (file)
 #define _TOUCHSCREEN_H
 
 struct input_dev;
+struct input_mt_pos;
 
-void touchscreen_parse_properties(struct input_dev *dev, bool multitouch);
+struct touchscreen_properties {
+       unsigned int max_x;
+       unsigned int max_y;
+       bool invert_x;
+       bool invert_y;
+       bool swap_x_y;
+};
+
+void touchscreen_parse_properties(struct input_dev *input, bool multitouch,
+                                 struct touchscreen_properties *prop);
+
+void touchscreen_set_mt_pos(struct input_mt_pos *pos,
+                           const struct touchscreen_properties *prop,
+                           unsigned int x, unsigned int y);
+
+void touchscreen_report_pos(struct input_dev *input,
+                           const struct touchscreen_properties *prop,
+                           unsigned int x, unsigned int y,
+                           bool multitouch);
 
 #endif