Merge branch 'for-linus' of git://git.kernel.org/pub/scm/linux/kernel/git/dtor/input
authorLinus Torvalds <torvalds@linux-foundation.org>
Mon, 11 Mar 2019 17:57:11 +0000 (10:57 -0700)
committerLinus Torvalds <torvalds@linux-foundation.org>
Mon, 11 Mar 2019 17:57:11 +0000 (10:57 -0700)
Pull input updates from Dmitry Torokhov:

 - update the ili210x touchscreen driver, refreshing the code and adding
   support for ILI251X line

 - add support for st1633 to the st1232 touchscreen driver

 - add support for sx8650 to the the sx8654 touchscreen driver

 - add support for Evervision FT5726 to the edt-ft5x06 touchscreen
   driver

 - add support for gt5688 to the Goodix touchscreen driver

 - new vibrator driver for MSM SOCs

 - miscellaneous fixes for the rest of input drivers

* 'for-linus' of git://git.kernel.org/pub/scm/linux/kernel/git/dtor/input: (53 commits)
  Input: wacom_serial4 - add support for Wacom ArtPad II tablet
  Input: elan_i2c - add id for touchpad found in Lenovo s21e-20
  Input: raspberrypi-ts - select CONFIG_INPUT_POLLDEV
  Input: msm-vibrator - use correct gpio header
  Input: ti_am335x_tsc - remove set but not used variable 'tscadc_dev'
  Input: i8042 - rework DT node name comparisons
  Input: goodix - print values in case of inconsistencies
  Input: goodix - refer to touchscreen.txt in device tree bindings
  Input: goodix - support Goodix gt5688
  Input: synaptics_i2c - remove redundant spinlock
  Input: db9 - mark expected switch fall-through
  Input: qt2160 - remove redundant spinlock
  Input: st1232 - handle common DT bindings
  Input: ims-pcu - switch to using brightness_set_blocking()
  Input: st1232 - switch to gpiod API
  Input: ili210x - fetch touchscreen geometry from DT
  Input: msm-vibrator - tweak an error message
  Input: tm2-touchkey - acknowledge that setting brightness is a blocking call
  Input: stmfts - acknowledge that setting brightness is a blocking call
  Input: ili210x - switch to using devm_device_add_group()
  ...

33 files changed:
Documentation/devicetree/bindings/input/cypress,tm2-touchkey.txt
Documentation/devicetree/bindings/input/ilitek,ili2xxx.txt [new file with mode: 0644]
Documentation/devicetree/bindings/input/msm-vibrator.txt [new file with mode: 0644]
Documentation/devicetree/bindings/input/touchscreen/edt-ft5x06.txt
Documentation/devicetree/bindings/input/touchscreen/goodix.txt
Documentation/devicetree/bindings/input/touchscreen/sitronix-st1232.txt
Documentation/devicetree/bindings/input/touchscreen/sx8654.txt
drivers/input/joystick/db9.c
drivers/input/keyboard/gpio_keys.c
drivers/input/keyboard/mcs_touchkey.c
drivers/input/keyboard/mtk-pmic-keys.c
drivers/input/keyboard/qt2160.c
drivers/input/keyboard/tca6416-keypad.c
drivers/input/keyboard/tm2-touchkey.c
drivers/input/misc/Kconfig
drivers/input/misc/Makefile
drivers/input/misc/ims-pcu.c
drivers/input/misc/msm-vibrator.c [new file with mode: 0644]
drivers/input/misc/soc_button_array.c
drivers/input/mouse/elan_i2c_core.c
drivers/input/mouse/synaptics_i2c.c
drivers/input/serio/i8042-sparcio.h
drivers/input/tablet/wacom_serial4.c
drivers/input/touchscreen/Kconfig
drivers/input/touchscreen/ad7879.c
drivers/input/touchscreen/edt-ft5x06.c
drivers/input/touchscreen/goodix.c
drivers/input/touchscreen/ili210x.c
drivers/input/touchscreen/st1232.c
drivers/input/touchscreen/stmfts.c
drivers/input/touchscreen/sx8654.c
drivers/input/touchscreen/ti_am335x_tsc.c
include/linux/input/ili210x.h [deleted file]

index 0c252d9..ef2ae72 100644 (file)
@@ -1,13 +1,19 @@
 Samsung tm2-touchkey
 
 Required properties:
-- compatible: must be "cypress,tm2-touchkey"
+- compatible:
+    * "cypress,tm2-touchkey" - for the touchkey found on the tm2 board
+    * "cypress,midas-touchkey" - for the touchkey found on midas boards
+    * "cypress,aries-touchkey" - for the touchkey found on aries boards
 - reg: I2C address of the chip.
 - interrupts: interrupt to which the chip is connected (see interrupt
        binding[0]).
 - vcc-supply : internal regulator output. 1.8V
 - vdd-supply : power supply for IC 3.3V
 
+Optional properties:
+- linux,keycodes: array of keycodes (max 4), default KEY_PHONE and KEY_BACK
+
 [0]: Documentation/devicetree/bindings/interrupt-controller/interrupts.txt
 
 Example:
@@ -21,5 +27,6 @@ Example:
                        interrupts = <2 IRQ_TYPE_EDGE_FALLING>;
                        vcc-supply=<&ldo32_reg>;
                        vdd-supply=<&ldo33_reg>;
+                       linux,keycodes = <KEY_PHONE KEY_BACK>;
                };
        };
diff --git a/Documentation/devicetree/bindings/input/ilitek,ili2xxx.txt b/Documentation/devicetree/bindings/input/ilitek,ili2xxx.txt
new file mode 100644 (file)
index 0000000..b2a7630
--- /dev/null
@@ -0,0 +1,25 @@
+Ilitek ILI210x/ILI251x touchscreen controller
+
+Required properties:
+- compatible:
+    ilitek,ili210x for ILI210x
+    ilitek,ili251x for ILI251x
+
+- reg: The I2C address of the device
+
+- interrupts: The sink for the touchscreen's IRQ output
+    See ../interrupt-controller/interrupts.txt
+
+Optional properties for main touchpad device:
+
+- reset-gpios: GPIO specifier for the touchscreen's reset pin (active low)
+
+Example:
+
+       touchscreen@41 {
+               compatible = "ilitek,ili251x";
+               reg = <0x41>;
+               interrupt-parent = <&gpio4>;
+               interrupts = <7 IRQ_TYPE_EDGE_FALLING>;
+               reset-gpios = <&gpio5 21 GPIO_ACTIVE_LOW>;
+       };
diff --git a/Documentation/devicetree/bindings/input/msm-vibrator.txt b/Documentation/devicetree/bindings/input/msm-vibrator.txt
new file mode 100644 (file)
index 0000000..8dcf014
--- /dev/null
@@ -0,0 +1,36 @@
+* Device tree bindings for the Qualcomm MSM vibrator
+
+Required properties:
+
+  - compatible: Should be one of
+               "qcom,msm8226-vibrator"
+               "qcom,msm8974-vibrator"
+  - reg: the base address and length of the IO memory for the registers.
+  - pinctrl-names: set to default.
+  - pinctrl-0: phandles pointing to pin configuration nodes. See
+               Documentation/devicetree/bindings/pinctrl/pinctrl-bindings.txt
+  - clock-names: set to pwm
+  - clocks: phandle of the clock. See
+            Documentation/devicetree/bindings/clock/clock-bindings.txt
+  - enable-gpios: GPIO that enables the vibrator.
+
+Optional properties:
+
+  - vcc-supply: phandle to the regulator that provides power to the sensor.
+
+Example from a LG Nexus 5 (hammerhead) phone:
+
+vibrator@fd8c3450 {
+       reg = <0xfd8c3450 0x400>;
+       compatible = "qcom,msm8974-vibrator";
+
+       vcc-supply = <&pm8941_l19>;
+
+       clocks = <&mmcc CAMSS_GP1_CLK>;
+       clock-names = "pwm";
+
+       enable-gpios = <&msmgpio 60 GPIO_ACTIVE_HIGH>;
+
+       pinctrl-names = "default";
+       pinctrl-0 = <&vibrator_pin>;
+};
index da2dc5d..870b8c5 100644 (file)
@@ -1,11 +1,12 @@
 FocalTech EDT-FT5x06 Polytouch driver
 =====================================
 
-There are 3 variants of the chip for various touch panel sizes
+There are 5 variants of the chip for various touch panel sizes
 FT5206GE1  2.8" .. 3.8"
 FT5306DE4  4.3" .. 7"
 FT5406EE8  7"   .. 8.9"
 FT5506EEG  7"   .. 8.9"
+FT5726NEI  5.7” .. 11.6"
 
 The software interface is identical for all those chips, so that
 currently there is no need for the driver to distinguish between the
@@ -19,6 +20,7 @@ Required properties:
            or:  "edt,edt-ft5306"
            or:  "edt,edt-ft5406"
            or:  "edt,edt-ft5506"
+           or:  "evervision,ev-ft5726"
            or:  "focaltech,ft6236"
 
  - reg:         I2C slave address of the chip (0x38)
@@ -42,6 +44,15 @@ Optional properties:
 
  - offset:      allows setting the edge compensation in the range from
                 0 to 31.
+
+ - offset-x:    Same as offset, but applies only to the horizontal position.
+                Range from 0 to 80, only supported by evervision,ev-ft5726
+                devices.
+
+ - offset-y:    Same as offset, but applies only to the vertical position.
+                Range from 0 to 80, only supported by evervision,ev-ft5726
+                devices.
+
  - touchscreen-size-x     : See touchscreen.txt
  - touchscreen-size-y     : See touchscreen.txt
  - touchscreen-fuzz-x      : See touchscreen.txt
index f7e95c5..8cf0b4d 100644 (file)
@@ -3,6 +3,7 @@ Device tree bindings for Goodix GT9xx series touchscreen controller
 Required properties:
 
  - compatible          : Should be "goodix,gt1151"
+                                or "goodix,gt5688"
                                 or "goodix,gt911"
                                 or "goodix,gt9110"
                                 or "goodix,gt912"
@@ -18,11 +19,14 @@ Optional properties:
  - irq-gpios           : GPIO pin used for IRQ. The driver uses the
                          interrupt gpio pin as output to reset the device.
  - reset-gpios         : GPIO pin used for reset
-
- - touchscreen-inverted-x  : X axis is inverted (boolean)
- - touchscreen-inverted-y  : Y axis is inverted (boolean)
- - touchscreen-swapped-x-y : X and Y axis are swapped (boolean)
-                             (swapping is done after inverting the axis)
+ - touchscreen-inverted-x
+ - touchscreen-inverted-y
+ - touchscreen-size-x
+ - touchscreen-size-y
+ - touchscreen-swapped-x-y
+
+The touchscreen-* properties are documented in touchscreen.txt in this
+directory.
 
 Example:
 
index 64ad48b..0193732 100644 (file)
@@ -1,13 +1,17 @@
-* Sitronix st1232 touchscreen controller
+* Sitronix st1232 or st1633 touchscreen controller
 
 Required properties:
-- compatible: must be "sitronix,st1232"
+- compatible: must contain one of
+  * "sitronix,st1232"
+  * "sitronix,st1633"
 - reg: I2C address of the chip
 - interrupts: interrupt to which the chip is connected
 
 Optional properties:
 - gpios: a phandle to the reset GPIO
 
+For additional optional properties see: touchscreen.txt
+
 Example:
 
        i2c@00000000 {
index 4886c4a..0ebe6dd 100644 (file)
@@ -1,10 +1,17 @@
 * Semtech SX8654 I2C Touchscreen Controller
 
 Required properties:
-- compatible: must be "semtech,sx8654"
+- compatible: must be one of the following, depending on the model:
+       "semtech,sx8650"
+       "semtech,sx8654"
+       "semtech,sx8655"
+       "semtech,sx8656"
 - reg: i2c slave address
 - interrupts: touch controller interrupt
 
+Optional properties:
+ - reset-gpios: GPIO specification for the NRST input
+
 Example:
 
        sx8654@48 {
@@ -12,4 +19,5 @@ Example:
                reg = <0x48>;
                interrupt-parent = <&gpio6>;
                interrupts = <3 IRQ_TYPE_EDGE_FALLING>;
+               reset-gpios = <&gpio4 2 GPIO_ACTIVE_LOW>;
        };
index 804b1b8..5a52b65 100644 (file)
@@ -259,7 +259,7 @@ static unsigned char db9_saturn_read_packet(struct parport *port, unsigned char
                        db9_saturn_write_sub(port, type, 3, powered, 0);
                        return data[0] = 0xe3;
                }
-               /* else: fall through */
+               /* fall through */
        default:
                return data[0];
        }
index 492a971..6cd199e 100644 (file)
@@ -1015,8 +1015,18 @@ static int __maybe_unused gpio_keys_resume(struct device *dev)
 
 static SIMPLE_DEV_PM_OPS(gpio_keys_pm_ops, gpio_keys_suspend, gpio_keys_resume);
 
+static void gpio_keys_shutdown(struct platform_device *pdev)
+{
+       int ret;
+
+       ret = gpio_keys_suspend(&pdev->dev);
+       if (ret)
+               dev_err(&pdev->dev, "failed to shutdown\n");
+}
+
 static struct platform_driver gpio_keys_device_driver = {
        .probe          = gpio_keys_probe,
+       .shutdown       = gpio_keys_shutdown,
        .driver         = {
                .name   = "gpio-keys",
                .pm     = &gpio_keys_pm_ops,
index be56d4f..b132662 100644 (file)
@@ -113,9 +113,8 @@ static int mcs_touchkey_probe(struct i2c_client *client,
                return -EINVAL;
        }
 
-       data = kzalloc(sizeof(struct mcs_touchkey_data) +
-                       sizeof(data->keycodes[0]) * (pdata->key_maxval + 1),
-                       GFP_KERNEL);
+       data = kzalloc(struct_size(data, keycodes, pdata->key_maxval + 1),
+                      GFP_KERNEL);
        input_dev = input_allocate_device();
        if (!data || !input_dev) {
                dev_err(&client->dev, "Failed to allocate memory\n");
index 02c67a1..8e6ebab 100644 (file)
  *
  */
 
-#include <linux/module.h>
-#include <linux/kernel.h>
 #include <linux/input.h>
 #include <linux/interrupt.h>
-#include <linux/platform_device.h>
 #include <linux/kernel.h>
-#include <linux/of.h>
-#include <linux/of_device.h>
-#include <linux/regmap.h>
 #include <linux/mfd/mt6323/registers.h>
-#include <linux/mfd/mt6397/registers.h>
 #include <linux/mfd/mt6397/core.h>
+#include <linux/mfd/mt6397/registers.h>
+#include <linux/module.h>
+#include <linux/of_device.h>
+#include <linux/of.h>
+#include <linux/platform_device.h>
+#include <linux/regmap.h>
 
 #define MTK_PMIC_PWRKEY_RST_EN_MASK    0x1
 #define MTK_PMIC_PWRKEY_RST_EN_SHIFT   6
index d466bc0..6a43895 100644 (file)
@@ -68,7 +68,6 @@ struct qt2160_data {
        struct i2c_client *client;
        struct input_dev *input;
        struct delayed_work dwork;
-       spinlock_t lock;        /* Protects canceling/rescheduling of dwork */
        unsigned short keycodes[ARRAY_SIZE(qt2160_key2code)];
        u16 key_matrix;
 #ifdef CONFIG_LEDS_CLASS
@@ -212,22 +211,15 @@ static int qt2160_get_key_matrix(struct qt2160_data *qt2160)
 static irqreturn_t qt2160_irq(int irq, void *_qt2160)
 {
        struct qt2160_data *qt2160 = _qt2160;
-       unsigned long flags;
-
-       spin_lock_irqsave(&qt2160->lock, flags);
 
        mod_delayed_work(system_wq, &qt2160->dwork, 0);
 
-       spin_unlock_irqrestore(&qt2160->lock, flags);
-
        return IRQ_HANDLED;
 }
 
 static void qt2160_schedule_read(struct qt2160_data *qt2160)
 {
-       spin_lock_irq(&qt2160->lock);
        schedule_delayed_work(&qt2160->dwork, QT2160_CYCLE_INTERVAL);
-       spin_unlock_irq(&qt2160->lock);
 }
 
 static void qt2160_worker(struct work_struct *work)
@@ -391,7 +383,6 @@ static int qt2160_probe(struct i2c_client *client,
        qt2160->client = client;
        qt2160->input = input;
        INIT_DELAYED_WORK(&qt2160->dwork, qt2160_worker);
-       spin_lock_init(&qt2160->lock);
 
        input->name = "AT42QT2160 Touch Sense Keyboard";
        input->id.bustype = BUS_I2C;
index dc983ab..cdeef18 100644 (file)
@@ -219,9 +219,7 @@ static int tca6416_keypad_probe(struct i2c_client *client,
                return -EINVAL;
        }
 
-       chip = kzalloc(sizeof(struct tca6416_keypad_chip) +
-                      pdata->nbuttons * sizeof(struct tca6416_button),
-                      GFP_KERNEL);
+       chip = kzalloc(struct_size(chip, buttons, pdata->nbuttons), GFP_KERNEL);
        input = input_allocate_device();
        if (!chip || !input) {
                error = -ENOMEM;
index abc266e..d4455f3 100644 (file)
 #include <linux/leds.h>
 #include <linux/module.h>
 #include <linux/of.h>
+#include <linux/of_device.h>
 #include <linux/pm.h>
 #include <linux/regulator/consumer.h>
 
 #define TM2_TOUCHKEY_DEV_NAME          "tm2-touchkey"
-#define TM2_TOUCHKEY_KEYCODE_REG       0x03
-#define TM2_TOUCHKEY_BASE_REG          0x00
+
+#define ARIES_TOUCHKEY_CMD_LED_ON      0x1
+#define ARIES_TOUCHKEY_CMD_LED_OFF     0x2
 #define TM2_TOUCHKEY_CMD_LED_ON                0x10
 #define TM2_TOUCHKEY_CMD_LED_OFF       0x20
 #define TM2_TOUCHKEY_BIT_PRESS_EV      BIT(3)
 #define TM2_TOUCHKEY_LED_VOLTAGE_MIN   2500000
 #define TM2_TOUCHKEY_LED_VOLTAGE_MAX   3300000
 
-enum {
-       TM2_TOUCHKEY_KEY_MENU = 0x1,
-       TM2_TOUCHKEY_KEY_BACK,
+struct touchkey_variant {
+       u8 keycode_reg;
+       u8 base_reg;
+       u8 cmd_led_on;
+       u8 cmd_led_off;
+       bool no_reg;
+       bool fixed_regulator;
 };
 
 struct tm2_touchkey_data {
@@ -46,9 +52,33 @@ struct tm2_touchkey_data {
        struct led_classdev led_dev;
        struct regulator *vdd;
        struct regulator_bulk_data regulators[2];
+       const struct touchkey_variant *variant;
+       u32 keycodes[4];
+       int num_keycodes;
+};
+
+static const struct touchkey_variant tm2_touchkey_variant = {
+       .keycode_reg = 0x03,
+       .base_reg = 0x00,
+       .cmd_led_on = TM2_TOUCHKEY_CMD_LED_ON,
+       .cmd_led_off = TM2_TOUCHKEY_CMD_LED_OFF,
+};
+
+static const struct touchkey_variant midas_touchkey_variant = {
+       .keycode_reg = 0x00,
+       .base_reg = 0x00,
+       .cmd_led_on = TM2_TOUCHKEY_CMD_LED_ON,
+       .cmd_led_off = TM2_TOUCHKEY_CMD_LED_OFF,
 };
 
-static void tm2_touchkey_led_brightness_set(struct led_classdev *led_dev,
+static struct touchkey_variant aries_touchkey_variant = {
+       .no_reg = true,
+       .fixed_regulator = true,
+       .cmd_led_on = ARIES_TOUCHKEY_CMD_LED_ON,
+       .cmd_led_off = ARIES_TOUCHKEY_CMD_LED_OFF,
+};
+
+static int tm2_touchkey_led_brightness_set(struct led_classdev *led_dev,
                                            enum led_brightness brightness)
 {
        struct tm2_touchkey_data *touchkey =
@@ -58,15 +88,19 @@ static void tm2_touchkey_led_brightness_set(struct led_classdev *led_dev,
 
        if (brightness == LED_OFF) {
                volt = TM2_TOUCHKEY_LED_VOLTAGE_MIN;
-               data = TM2_TOUCHKEY_CMD_LED_OFF;
+               data = touchkey->variant->cmd_led_off;
        } else {
                volt = TM2_TOUCHKEY_LED_VOLTAGE_MAX;
-               data = TM2_TOUCHKEY_CMD_LED_ON;
+               data = touchkey->variant->cmd_led_on;
        }
 
-       regulator_set_voltage(touchkey->vdd, volt, volt);
-       i2c_smbus_write_byte_data(touchkey->client,
-                                 TM2_TOUCHKEY_BASE_REG, data);
+       if (!touchkey->variant->fixed_regulator)
+               regulator_set_voltage(touchkey->vdd, volt, volt);
+
+       return touchkey->variant->no_reg ?
+               i2c_smbus_write_byte(touchkey->client, data) :
+               i2c_smbus_write_byte_data(touchkey->client,
+                                         touchkey->variant->base_reg, data);
 }
 
 static int tm2_touchkey_power_enable(struct tm2_touchkey_data *touchkey)
@@ -96,49 +130,57 @@ static irqreturn_t tm2_touchkey_irq_handler(int irq, void *devid)
 {
        struct tm2_touchkey_data *touchkey = devid;
        int data;
-       int key;
-
-       data = i2c_smbus_read_byte_data(touchkey->client,
-                                       TM2_TOUCHKEY_KEYCODE_REG);
+       int index;
+       int i;
+
+       if (touchkey->variant->no_reg)
+               data = i2c_smbus_read_byte(touchkey->client);
+       else
+               data = i2c_smbus_read_byte_data(touchkey->client,
+                                               touchkey->variant->keycode_reg);
        if (data < 0) {
                dev_err(&touchkey->client->dev,
                        "failed to read i2c data: %d\n", data);
                goto out;
        }
 
-       switch (data & TM2_TOUCHKEY_BIT_KEYCODE) {
-       case TM2_TOUCHKEY_KEY_MENU:
-               key = KEY_PHONE;
-               break;
-
-       case TM2_TOUCHKEY_KEY_BACK:
-               key = KEY_BACK;
-               break;
-
-       default:
+       index = (data & TM2_TOUCHKEY_BIT_KEYCODE) - 1;
+       if (index < 0 || index >= touchkey->num_keycodes) {
                dev_warn(&touchkey->client->dev,
-                        "unhandled keycode, data %#02x\n", data);
+                        "invalid keycode index %d\n", index);
                goto out;
        }
 
        if (data & TM2_TOUCHKEY_BIT_PRESS_EV) {
-               input_report_key(touchkey->input_dev, KEY_PHONE, 0);
-               input_report_key(touchkey->input_dev, KEY_BACK, 0);
+               for (i = 0; i < touchkey->num_keycodes; i++)
+                       input_report_key(touchkey->input_dev,
+                                        touchkey->keycodes[i], 0);
        } else {
-               input_report_key(touchkey->input_dev, key, 1);
+               input_report_key(touchkey->input_dev,
+                                touchkey->keycodes[index], 1);
        }
 
        input_sync(touchkey->input_dev);
 
 out:
+       if (touchkey->variant->fixed_regulator &&
+                               data & TM2_TOUCHKEY_BIT_PRESS_EV) {
+               /* touch turns backlight on, so make sure we're in sync */
+               if (touchkey->led_dev.brightness == LED_OFF)
+                       tm2_touchkey_led_brightness_set(&touchkey->led_dev,
+                                                       LED_OFF);
+       }
+
        return IRQ_HANDLED;
 }
 
 static int tm2_touchkey_probe(struct i2c_client *client,
                              const struct i2c_device_id *id)
 {
+       struct device_node *np = client->dev.of_node;
        struct tm2_touchkey_data *touchkey;
        int error;
+       int i;
 
        if (!i2c_check_functionality(client->adapter,
                        I2C_FUNC_SMBUS_BYTE | I2C_FUNC_SMBUS_BYTE_DATA)) {
@@ -153,6 +195,8 @@ static int tm2_touchkey_probe(struct i2c_client *client,
        touchkey->client = client;
        i2c_set_clientdata(client, touchkey);
 
+       touchkey->variant = of_device_get_match_data(&client->dev);
+
        touchkey->regulators[0].supply = "vcc";
        touchkey->regulators[1].supply = "vdd";
        error = devm_regulator_bulk_get(&client->dev,
@@ -166,6 +210,16 @@ static int tm2_touchkey_probe(struct i2c_client *client,
        /* Save VDD for easy access */
        touchkey->vdd = touchkey->regulators[1].consumer;
 
+       touchkey->num_keycodes = of_property_read_variable_u32_array(np,
+                                       "linux,keycodes", touchkey->keycodes, 0,
+                                       ARRAY_SIZE(touchkey->keycodes));
+       if (touchkey->num_keycodes <= 0) {
+               /* default keycodes */
+               touchkey->keycodes[0] = KEY_PHONE;
+               touchkey->keycodes[1] = KEY_BACK;
+               touchkey->num_keycodes = 2;
+       }
+
        error = tm2_touchkey_power_enable(touchkey);
        if (error) {
                dev_err(&client->dev, "failed to power up device: %d\n", error);
@@ -190,8 +244,9 @@ static int tm2_touchkey_probe(struct i2c_client *client,
        touchkey->input_dev->name = TM2_TOUCHKEY_DEV_NAME;
        touchkey->input_dev->id.bustype = BUS_I2C;
 
-       input_set_capability(touchkey->input_dev, EV_KEY, KEY_PHONE);
-       input_set_capability(touchkey->input_dev, EV_KEY, KEY_BACK);
+       for (i = 0; i < touchkey->num_keycodes; i++)
+               input_set_capability(touchkey->input_dev, EV_KEY,
+                                    touchkey->keycodes[i]);
 
        error = input_register_device(touchkey->input_dev);
        if (error) {
@@ -212,9 +267,10 @@ static int tm2_touchkey_probe(struct i2c_client *client,
 
        /* led device */
        touchkey->led_dev.name = TM2_TOUCHKEY_DEV_NAME;
-       touchkey->led_dev.brightness = LED_FULL;
+       touchkey->led_dev.brightness = LED_ON;
        touchkey->led_dev.max_brightness = LED_ON;
-       touchkey->led_dev.brightness_set = tm2_touchkey_led_brightness_set;
+       touchkey->led_dev.brightness_set_blocking =
+                                       tm2_touchkey_led_brightness_set;
 
        error = devm_led_classdev_register(&client->dev, &touchkey->led_dev);
        if (error) {
@@ -223,6 +279,9 @@ static int tm2_touchkey_probe(struct i2c_client *client,
                return error;
        }
 
+       if (touchkey->variant->fixed_regulator)
+               tm2_touchkey_led_brightness_set(&touchkey->led_dev, LED_ON);
+
        return 0;
 }
 
@@ -262,7 +321,16 @@ static const struct i2c_device_id tm2_touchkey_id_table[] = {
 MODULE_DEVICE_TABLE(i2c, tm2_touchkey_id_table);
 
 static const struct of_device_id tm2_touchkey_of_match[] = {
-       { .compatible = "cypress,tm2-touchkey", },
+       {
+               .compatible = "cypress,tm2-touchkey",
+               .data = &tm2_touchkey_variant,
+       }, {
+               .compatible = "cypress,midas-touchkey",
+               .data = &midas_touchkey_variant,
+       }, {
+               .compatible = "cypress,aries-touchkey",
+               .data = &aries_touchkey_variant,
+       },
        { },
 };
 MODULE_DEVICE_TABLE(of, tm2_touchkey_of_match);
index 279fb02..e15ed1b 100644 (file)
@@ -117,6 +117,16 @@ config INPUT_E3X0_BUTTON
          To compile this driver as a module, choose M here: the
          module will be called e3x0_button.
 
+config INPUT_MSM_VIBRATOR
+       tristate "Qualcomm MSM vibrator driver"
+       select INPUT_FF_MEMLESS
+       help
+         Support for the vibrator that is found on various Qualcomm MSM
+         SOCs.
+
+         To compile this driver as a module, choose M here: the module
+         will be called msm_vibrator.
+
 config INPUT_PCSPKR
        tristate "PC Speaker support"
        depends on PCSPKR_PLATFORM
index 1b44202..b936c5b 100644 (file)
@@ -48,6 +48,7 @@ obj-$(CONFIG_INPUT_MAX8925_ONKEY)     += max8925_onkey.o
 obj-$(CONFIG_INPUT_MAX8997_HAPTIC)     += max8997_haptic.o
 obj-$(CONFIG_INPUT_MC13783_PWRBUTTON)  += mc13783-pwrbutton.o
 obj-$(CONFIG_INPUT_MMA8450)            += mma8450.o
+obj-$(CONFIG_INPUT_MSM_VIBRATOR)       += msm-vibrator.o
 obj-$(CONFIG_INPUT_PALMAS_PWRBUTTON)   += palmas-pwrbutton.o
 obj-$(CONFIG_INPUT_PCAP)               += pcap_keys.o
 obj-$(CONFIG_INPUT_PCF50633_PMU)       += pcf50633-input.o
index 3d51175..74cf3b6 100644 (file)
@@ -39,8 +39,6 @@ struct ims_pcu_gamepad {
 
 struct ims_pcu_backlight {
        struct led_classdev cdev;
-       struct work_struct work;
-       enum led_brightness desired_brightness;
        char name[32];
 };
 
@@ -949,14 +947,14 @@ out:
 
 #define IMS_PCU_MAX_BRIGHTNESS         31998
 
-static void ims_pcu_backlight_work(struct work_struct *work)
+static int ims_pcu_backlight_set_brightness(struct led_classdev *cdev,
+                                           enum led_brightness value)
 {
        struct ims_pcu_backlight *backlight =
-                       container_of(work, struct ims_pcu_backlight, work);
+                       container_of(cdev, struct ims_pcu_backlight, cdev);
        struct ims_pcu *pcu =
                        container_of(backlight, struct ims_pcu, backlight);
-       int desired_brightness = backlight->desired_brightness;
-       __le16 br_val = cpu_to_le16(desired_brightness);
+       __le16 br_val = cpu_to_le16(value);
        int error;
 
        mutex_lock(&pcu->cmd_mutex);
@@ -966,19 +964,11 @@ static void ims_pcu_backlight_work(struct work_struct *work)
        if (error && error != -ENODEV)
                dev_warn(pcu->dev,
                         "Failed to set desired brightness %u, error: %d\n",
-                        desired_brightness, error);
+                        value, error);
 
        mutex_unlock(&pcu->cmd_mutex);
-}
 
-static void ims_pcu_backlight_set_brightness(struct led_classdev *cdev,
-                                            enum led_brightness value)
-{
-       struct ims_pcu_backlight *backlight =
-                       container_of(cdev, struct ims_pcu_backlight, cdev);
-
-       backlight->desired_brightness = value;
-       schedule_work(&backlight->work);
+       return error;
 }
 
 static enum led_brightness
@@ -1015,14 +1005,14 @@ static int ims_pcu_setup_backlight(struct ims_pcu *pcu)
        struct ims_pcu_backlight *backlight = &pcu->backlight;
        int error;
 
-       INIT_WORK(&backlight->work, ims_pcu_backlight_work);
        snprintf(backlight->name, sizeof(backlight->name),
                 "pcu%d::kbd_backlight", pcu->device_no);
 
        backlight->cdev.name = backlight->name;
        backlight->cdev.max_brightness = IMS_PCU_MAX_BRIGHTNESS;
        backlight->cdev.brightness_get = ims_pcu_backlight_get_brightness;
-       backlight->cdev.brightness_set = ims_pcu_backlight_set_brightness;
+       backlight->cdev.brightness_set_blocking =
+                                        ims_pcu_backlight_set_brightness;
 
        error = led_classdev_register(pcu->dev, &backlight->cdev);
        if (error) {
@@ -1040,7 +1030,6 @@ static void ims_pcu_destroy_backlight(struct ims_pcu *pcu)
        struct ims_pcu_backlight *backlight = &pcu->backlight;
 
        led_classdev_unregister(&backlight->cdev);
-       cancel_work_sync(&backlight->work);
 }
 
 
diff --git a/drivers/input/misc/msm-vibrator.c b/drivers/input/misc/msm-vibrator.c
new file mode 100644 (file)
index 0000000..b60f1aa
--- /dev/null
@@ -0,0 +1,281 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * Qualcomm MSM vibrator driver
+ *
+ * Copyright (c) 2018 Brian Masney <masneyb@onstation.org>
+ *
+ * Based on qcom,pwm-vibrator.c from:
+ * Copyright (c) 2018 Jonathan Marek <jonathan@marek.ca>
+ *
+ * Based on msm_pwm_vibrator.c from downstream Android sources:
+ * Copyright (C) 2009-2014 LGE, Inc.
+ */
+
+#include <linux/clk.h>
+#include <linux/err.h>
+#include <linux/gpio/consumer.h>
+#include <linux/input.h>
+#include <linux/io.h>
+#include <linux/module.h>
+#include <linux/of.h>
+#include <linux/platform_device.h>
+#include <linux/regulator/consumer.h>
+
+#define REG_CMD_RCGR           0x00
+#define REG_CFG_RCGR           0x04
+#define REG_M                  0x08
+#define REG_N                  0x0C
+#define REG_D                  0x10
+#define REG_CBCR               0x24
+#define MMSS_CC_M_DEFAULT      1
+
+struct msm_vibrator {
+       struct input_dev *input;
+       struct mutex mutex;
+       struct work_struct worker;
+       void __iomem *base;
+       struct regulator *vcc;
+       struct clk *clk;
+       struct gpio_desc *enable_gpio;
+       u16 magnitude;
+       bool enabled;
+};
+
+static void msm_vibrator_write(struct msm_vibrator *vibrator, int offset,
+                              u32 value)
+{
+       writel(value, vibrator->base + offset);
+}
+
+static int msm_vibrator_start(struct msm_vibrator *vibrator)
+{
+       int d_reg_val, ret = 0;
+
+       mutex_lock(&vibrator->mutex);
+
+       if (!vibrator->enabled) {
+               ret = clk_set_rate(vibrator->clk, 24000);
+               if (ret) {
+                       dev_err(&vibrator->input->dev,
+                               "Failed to set clock rate: %d\n", ret);
+                       goto unlock;
+               }
+
+               ret = clk_prepare_enable(vibrator->clk);
+               if (ret) {
+                       dev_err(&vibrator->input->dev,
+                               "Failed to enable clock: %d\n", ret);
+                       goto unlock;
+               }
+
+               ret = regulator_enable(vibrator->vcc);
+               if (ret) {
+                       dev_err(&vibrator->input->dev,
+                               "Failed to enable regulator: %d\n", ret);
+                       clk_disable(vibrator->clk);
+                       goto unlock;
+               }
+
+               gpiod_set_value_cansleep(vibrator->enable_gpio, 1);
+
+               vibrator->enabled = true;
+       }
+
+       d_reg_val = 127 - ((126 * vibrator->magnitude) / 0xffff);
+       msm_vibrator_write(vibrator, REG_CFG_RCGR,
+                          (2 << 12) | /* dual edge mode */
+                          (0 << 8) |  /* cxo */
+                          (7 << 0));
+       msm_vibrator_write(vibrator, REG_M, 1);
+       msm_vibrator_write(vibrator, REG_N, 128);
+       msm_vibrator_write(vibrator, REG_D, d_reg_val);
+       msm_vibrator_write(vibrator, REG_CMD_RCGR, 1);
+       msm_vibrator_write(vibrator, REG_CBCR, 1);
+
+unlock:
+       mutex_unlock(&vibrator->mutex);
+
+       return ret;
+}
+
+static void msm_vibrator_stop(struct msm_vibrator *vibrator)
+{
+       mutex_lock(&vibrator->mutex);
+
+       if (vibrator->enabled) {
+               gpiod_set_value_cansleep(vibrator->enable_gpio, 0);
+               regulator_disable(vibrator->vcc);
+               clk_disable(vibrator->clk);
+               vibrator->enabled = false;
+       }
+
+       mutex_unlock(&vibrator->mutex);
+}
+
+static void msm_vibrator_worker(struct work_struct *work)
+{
+       struct msm_vibrator *vibrator = container_of(work,
+                                                    struct msm_vibrator,
+                                                    worker);
+
+       if (vibrator->magnitude)
+               msm_vibrator_start(vibrator);
+       else
+               msm_vibrator_stop(vibrator);
+}
+
+static int msm_vibrator_play_effect(struct input_dev *dev, void *data,
+                                   struct ff_effect *effect)
+{
+       struct msm_vibrator *vibrator = input_get_drvdata(dev);
+
+       mutex_lock(&vibrator->mutex);
+
+       if (effect->u.rumble.strong_magnitude > 0)
+               vibrator->magnitude = effect->u.rumble.strong_magnitude;
+       else
+               vibrator->magnitude = effect->u.rumble.weak_magnitude;
+
+       mutex_unlock(&vibrator->mutex);
+
+       schedule_work(&vibrator->worker);
+
+       return 0;
+}
+
+static void msm_vibrator_close(struct input_dev *input)
+{
+       struct msm_vibrator *vibrator = input_get_drvdata(input);
+
+       cancel_work_sync(&vibrator->worker);
+       msm_vibrator_stop(vibrator);
+}
+
+static int msm_vibrator_probe(struct platform_device *pdev)
+{
+       struct msm_vibrator *vibrator;
+       struct resource *res;
+       int ret;
+
+       vibrator = devm_kzalloc(&pdev->dev, sizeof(*vibrator), GFP_KERNEL);
+       if (!vibrator)
+               return -ENOMEM;
+
+       vibrator->input = devm_input_allocate_device(&pdev->dev);
+       if (!vibrator->input)
+               return -ENOMEM;
+
+       vibrator->vcc = devm_regulator_get(&pdev->dev, "vcc");
+       if (IS_ERR(vibrator->vcc)) {
+               if (PTR_ERR(vibrator->vcc) != -EPROBE_DEFER)
+                       dev_err(&pdev->dev, "Failed to get regulator: %ld\n",
+                               PTR_ERR(vibrator->vcc));
+               return PTR_ERR(vibrator->vcc);
+       }
+
+       vibrator->enable_gpio = devm_gpiod_get(&pdev->dev, "enable",
+                                              GPIOD_OUT_LOW);
+       if (IS_ERR(vibrator->enable_gpio)) {
+               if (PTR_ERR(vibrator->enable_gpio) != -EPROBE_DEFER)
+                       dev_err(&pdev->dev, "Failed to get enable gpio: %ld\n",
+                               PTR_ERR(vibrator->enable_gpio));
+               return PTR_ERR(vibrator->enable_gpio);
+       }
+
+       vibrator->clk = devm_clk_get(&pdev->dev, "pwm");
+       if (IS_ERR(vibrator->clk)) {
+               if (PTR_ERR(vibrator->clk) != -EPROBE_DEFER)
+                       dev_err(&pdev->dev, "Failed to lookup pwm clock: %ld\n",
+                               PTR_ERR(vibrator->clk));
+               return PTR_ERR(vibrator->clk);
+       }
+
+       res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+       if (!res) {
+               dev_err(&pdev->dev, "Failed to get platform resource\n");
+               return -ENODEV;
+       }
+
+       vibrator->base = devm_ioremap(&pdev->dev, res->start,
+                                    resource_size(res));
+       if (!vibrator->base) {
+               dev_err(&pdev->dev, "Failed to iomap resource.\n");
+               return -ENOMEM;
+       }
+
+       vibrator->enabled = false;
+       mutex_init(&vibrator->mutex);
+       INIT_WORK(&vibrator->worker, msm_vibrator_worker);
+
+       vibrator->input->name = "msm-vibrator";
+       vibrator->input->id.bustype = BUS_HOST;
+       vibrator->input->close = msm_vibrator_close;
+
+       input_set_drvdata(vibrator->input, vibrator);
+       input_set_capability(vibrator->input, EV_FF, FF_RUMBLE);
+
+       ret = input_ff_create_memless(vibrator->input, NULL,
+                                     msm_vibrator_play_effect);
+       if (ret) {
+               dev_err(&pdev->dev, "Failed to create ff memless: %d", ret);
+               return ret;
+       }
+
+       ret = input_register_device(vibrator->input);
+       if (ret) {
+               dev_err(&pdev->dev, "Failed to register input device: %d", ret);
+               return ret;
+       }
+
+       platform_set_drvdata(pdev, vibrator);
+
+       return 0;
+}
+
+static int __maybe_unused msm_vibrator_suspend(struct device *dev)
+{
+       struct platform_device *pdev = to_platform_device(dev);
+       struct msm_vibrator *vibrator = platform_get_drvdata(pdev);
+
+       cancel_work_sync(&vibrator->worker);
+
+       if (vibrator->enabled)
+               msm_vibrator_stop(vibrator);
+
+       return 0;
+}
+
+static int __maybe_unused msm_vibrator_resume(struct device *dev)
+{
+       struct platform_device *pdev = to_platform_device(dev);
+       struct msm_vibrator *vibrator = platform_get_drvdata(pdev);
+
+       if (vibrator->enabled)
+               msm_vibrator_start(vibrator);
+
+       return 0;
+}
+
+static SIMPLE_DEV_PM_OPS(msm_vibrator_pm_ops, msm_vibrator_suspend,
+                        msm_vibrator_resume);
+
+static const struct of_device_id msm_vibrator_of_match[] = {
+       { .compatible = "qcom,msm8226-vibrator" },
+       { .compatible = "qcom,msm8974-vibrator" },
+       {},
+};
+MODULE_DEVICE_TABLE(of, msm_vibrator_of_match);
+
+static struct platform_driver msm_vibrator_driver = {
+       .probe  = msm_vibrator_probe,
+       .driver = {
+               .name = "msm-vibrator",
+               .pm = &msm_vibrator_pm_ops,
+               .of_match_table = of_match_ptr(msm_vibrator_of_match),
+       },
+};
+module_platform_driver(msm_vibrator_driver);
+
+MODULE_AUTHOR("Brian Masney <masneyb@onstation.org>");
+MODULE_DESCRIPTION("Qualcomm MSM vibrator driver");
+MODULE_LICENSE("GPL");
index 23520df..bb458be 100644 (file)
@@ -185,6 +185,10 @@ static int soc_button_parse_btn_desc(struct device *dev,
                info->name = "power";
                info->event_code = KEY_POWER;
                info->wakeup = true;
+       } else if (upage == 0x01 && usage == 0xca) {
+               info->name = "rotation lock switch";
+               info->event_type = EV_SW;
+               info->event_code = SW_ROTATE_LOCK;
        } else if (upage == 0x07 && usage == 0xe3) {
                info->name = "home";
                info->event_code = KEY_LEFTMETA;
@@ -373,7 +377,7 @@ static struct soc_button_info soc_button_PNP0C40[] = {
        { "home", 1, EV_KEY, KEY_LEFTMETA, false, true },
        { "volume_up", 2, EV_KEY, KEY_VOLUMEUP, true, false },
        { "volume_down", 3, EV_KEY, KEY_VOLUMEDOWN, true, false },
-       { "rotation_lock", 4, EV_SW, SW_ROTATE_LOCK, false, false },
+       { "rotation_lock", 4, EV_KEY, KEY_ROTATE_LOCK_TOGGLE, false, false },
        { }
 };
 
index 225ae69..628ef61 100644 (file)
@@ -1337,6 +1337,7 @@ static const struct acpi_device_id elan_acpi_id[] = {
        { "ELAN0000", 0 },
        { "ELAN0100", 0 },
        { "ELAN0600", 0 },
+       { "ELAN0601", 0 },
        { "ELAN0602", 0 },
        { "ELAN0605", 0 },
        { "ELAN0608", 0 },
index 8538318..fa30464 100644 (file)
@@ -219,7 +219,6 @@ struct synaptics_i2c {
        struct i2c_client       *client;
        struct input_dev        *input;
        struct delayed_work     dwork;
-       spinlock_t              lock;
        int                     no_data_count;
        int                     no_decel_param;
        int                     reduce_report_param;
@@ -369,23 +368,11 @@ static bool synaptics_i2c_get_input(struct synaptics_i2c *touch)
        return xy_delta || gesture;
 }
 
-static void synaptics_i2c_reschedule_work(struct synaptics_i2c *touch,
-                                         unsigned long delay)
-{
-       unsigned long flags;
-
-       spin_lock_irqsave(&touch->lock, flags);
-
-       mod_delayed_work(system_wq, &touch->dwork, delay);
-
-       spin_unlock_irqrestore(&touch->lock, flags);
-}
-
 static irqreturn_t synaptics_i2c_irq(int irq, void *dev_id)
 {
        struct synaptics_i2c *touch = dev_id;
 
-       synaptics_i2c_reschedule_work(touch, 0);
+       mod_delayed_work(system_wq, &touch->dwork, 0);
 
        return IRQ_HANDLED;
 }
@@ -461,7 +448,7 @@ static void synaptics_i2c_work_handler(struct work_struct *work)
         * We poll the device once in THREAD_IRQ_SLEEP_SECS and
         * if error is detected, we try to reset and reconfigure the touchpad.
         */
-       synaptics_i2c_reschedule_work(touch, delay);
+       mod_delayed_work(system_wq, &touch->dwork, delay);
 }
 
 static int synaptics_i2c_open(struct input_dev *input)
@@ -474,7 +461,7 @@ static int synaptics_i2c_open(struct input_dev *input)
                return ret;
 
        if (polling_req)
-               synaptics_i2c_reschedule_work(touch,
+               mod_delayed_work(system_wq, &touch->dwork,
                                msecs_to_jiffies(NO_DATA_SLEEP_MSECS));
 
        return 0;
@@ -530,7 +517,6 @@ static struct synaptics_i2c *synaptics_i2c_touch_create(struct i2c_client *clien
        touch->scan_rate_param = scan_rate;
        set_scan_rate(touch, scan_rate);
        INIT_DELAYED_WORK(&touch->dwork, synaptics_i2c_work_handler);
-       spin_lock_init(&touch->lock);
 
        return touch;
 }
@@ -637,7 +623,7 @@ static int __maybe_unused synaptics_i2c_resume(struct device *dev)
        if (ret)
                return ret;
 
-       synaptics_i2c_reschedule_work(touch,
+       mod_delayed_work(system_wq, &touch->dwork,
                                msecs_to_jiffies(NO_DATA_SLEEP_MSECS));
 
        return 0;
index 7962898..fce7681 100644 (file)
@@ -53,12 +53,11 @@ static struct resource *kbd_res;
 
 static int sparc_i8042_probe(struct platform_device *op)
 {
-       struct device_node *dp = op->dev.of_node;
+       struct device_node *dp;
 
-       dp = dp->child;
-       while (dp) {
-               if (!strcmp(dp->name, OBP_PS2KBD_NAME1) ||
-                   !strcmp(dp->name, OBP_PS2KBD_NAME2)) {
+       for_each_child_of_node(op->dev.of_node, dp) {
+               if (of_node_name_eq(dp, OBP_PS2KBD_NAME1) ||
+                   of_node_name_eq(dp, OBP_PS2KBD_NAME2)) {
                        struct platform_device *kbd = of_find_device_by_node(dp);
                        unsigned int irq = kbd->archdata.irqs[0];
                        if (irq == 0xffffffff)
@@ -67,16 +66,14 @@ static int sparc_i8042_probe(struct platform_device *op)
                        kbd_iobase = of_ioremap(&kbd->resource[0],
                                                0, 8, "kbd");
                        kbd_res = &kbd->resource[0];
-               } else if (!strcmp(dp->name, OBP_PS2MS_NAME1) ||
-                          !strcmp(dp->name, OBP_PS2MS_NAME2)) {
+               } else if (of_node_name_eq(dp, OBP_PS2MS_NAME1) ||
+                          of_node_name_eq(dp, OBP_PS2MS_NAME2)) {
                        struct platform_device *ms = of_find_device_by_node(dp);
                        unsigned int irq = ms->archdata.irqs[0];
                        if (irq == 0xffffffff)
                                irq = op->archdata.irqs[0];
                        i8042_aux_irq = irq;
                }
-
-               dp = dp->sibling;
        }
 
        return 0;
@@ -109,8 +106,9 @@ static struct platform_driver sparc_i8042_driver = {
 static int __init i8042_platform_init(void)
 {
        struct device_node *root = of_find_node_by_path("/");
+       const char *name = of_get_property(root, "name", NULL);
 
-       if (!strcmp(root->name, "SUNW,JavaStation-1")) {
+       if (name && !strcmp(name, "SUNW,JavaStation-1")) {
                /* Hardcoded values for MrCoffee.  */
                i8042_kbd_irq = i8042_aux_irq = 13 | 0x20;
                kbd_iobase = ioremap(0x71300060, 8);
@@ -139,8 +137,9 @@ static int __init i8042_platform_init(void)
 static inline void i8042_platform_exit(void)
 {
        struct device_node *root = of_find_node_by_path("/");
+       const char *name = of_get_property(root, "name", NULL);
 
-       if (strcmp(root->name, "SUNW,JavaStation-1"))
+       if (!name || strcmp(name, "SUNW,JavaStation-1"))
                platform_driver_unregister(&sparc_i8042_driver);
 }
 
index 38bfaca..150f9ee 100644 (file)
@@ -187,6 +187,7 @@ enum {
        MODEL_DIGITIZER_II      = 0x5544, /* UD */
        MODEL_GRAPHIRE          = 0x4554, /* ET */
        MODEL_PENPARTNER        = 0x4354, /* CT */
+       MODEL_ARTPAD_II         = 0x4B54, /* KT */
 };
 
 static void wacom_handle_model_response(struct wacom *wacom)
@@ -245,6 +246,7 @@ static void wacom_handle_model_response(struct wacom *wacom)
                wacom->flags = F_HAS_STYLUS2 | F_HAS_SCROLLWHEEL;
                break;
 
+       case MODEL_ARTPAD_II:
        case MODEL_DIGITIZER_II:
                wacom->dev->name = "Wacom Digitizer II";
                wacom->dev->id.version = MODEL_DIGITIZER_II;
index 068dbbc..7a4884a 100644 (file)
@@ -699,6 +699,7 @@ config TOUCHSCREEN_EDT_FT5X06
 config TOUCHSCREEN_RASPBERRYPI_FW
        tristate "Raspberry Pi's firmware base touch screen support"
        depends on RASPBERRYPI_FIRMWARE || (RASPBERRYPI_FIRMWARE=n && COMPILE_TEST)
+       select INPUT_POLLDEV
        help
          Say Y here if you have the official Raspberry Pi 7 inch screen on
          your system.
@@ -1168,11 +1169,11 @@ config TOUCHSCREEN_SIS_I2C
          module will be called sis_i2c.
 
 config TOUCHSCREEN_ST1232
-       tristate "Sitronix ST1232 touchscreen controllers"
+       tristate "Sitronix ST1232 or ST1633 touchscreen controllers"
        depends on I2C
        help
-         Say Y here if you want to support Sitronix ST1232
-         touchscreen controller.
+         Say Y here if you want to support the Sitronix ST1232
+         or ST1633 touchscreen controller.
 
          If unsure, say N.
 
index 6fa714c..3a016f4 100644 (file)
@@ -246,11 +246,14 @@ static void ad7879_timer(struct timer_list *t)
 static irqreturn_t ad7879_irq(int irq, void *handle)
 {
        struct ad7879 *ts = handle;
+       int error;
 
-       regmap_bulk_read(ts->regmap, AD7879_REG_XPLUS,
-                        ts->conversion_data, AD7879_NR_SENSE);
-
-       if (!ad7879_report(ts))
+       error = regmap_bulk_read(ts->regmap, AD7879_REG_XPLUS,
+                                ts->conversion_data, AD7879_NR_SENSE);
+       if (error)
+               dev_err_ratelimited(ts->dev, "failed to read %#02x: %d\n",
+                                   AD7879_REG_XPLUS, error);
+       else if (!ad7879_report(ts))
                mod_timer(&ts->timer, jiffies + TS_PEN_UP_TIMEOUT);
 
        return IRQ_HANDLED;
index 1e18ca0..702bfda 100644 (file)
@@ -31,6 +31,7 @@
 #include <linux/interrupt.h>
 #include <linux/input.h>
 #include <linux/i2c.h>
+#include <linux/kernel.h>
 #include <linux/uaccess.h>
 #include <linux/delay.h>
 #include <linux/debugfs.h>
 #define M09_REGISTER_NUM_X             0x94
 #define M09_REGISTER_NUM_Y             0x95
 
+#define EV_REGISTER_THRESHOLD          0x40
+#define EV_REGISTER_GAIN               0x41
+#define EV_REGISTER_OFFSET_Y           0x45
+#define EV_REGISTER_OFFSET_X           0x46
+
 #define NO_REGISTER                    0xff
 
 #define WORK_REGISTER_OPMODE           0x3c
@@ -73,6 +79,7 @@ enum edt_ver {
        EDT_M06,
        EDT_M09,
        EDT_M12,
+       EV_FT,
        GENERIC_FT,
 };
 
@@ -81,6 +88,8 @@ struct edt_reg_addr {
        int reg_report_rate;
        int reg_gain;
        int reg_offset;
+       int reg_offset_x;
+       int reg_offset_y;
        int reg_num_x;
        int reg_num_y;
 };
@@ -106,6 +115,8 @@ struct edt_ft5x06_ts_data {
        int threshold;
        int gain;
        int offset;
+       int offset_x;
+       int offset_y;
        int report_rate;
        int max_support_points;
 
@@ -190,6 +201,7 @@ static irqreturn_t edt_ft5x06_ts_isr(int irq, void *dev_id)
 
        case EDT_M09:
        case EDT_M12:
+       case EV_FT:
        case GENERIC_FT:
                cmd = 0x0;
                offset = 3;
@@ -242,6 +254,10 @@ static irqreturn_t edt_ft5x06_ts_isr(int irq, void *dev_id)
 
                x = ((buf[0] << 8) | buf[1]) & 0x0fff;
                y = ((buf[2] << 8) | buf[3]) & 0x0fff;
+               /* The FT5x26 send the y coordinate first */
+               if (tsdata->version == EV_FT)
+                       swap(x, y);
+
                id = (buf[2] >> 4) & 0x0f;
                down = type != TOUCH_EVENT_UP;
 
@@ -275,8 +291,10 @@ static int edt_ft5x06_register_write(struct edt_ft5x06_ts_data *tsdata,
                wrbuf[3] = wrbuf[0] ^ wrbuf[1] ^ wrbuf[2];
                return edt_ft5x06_ts_readwrite(tsdata->client, 4,
                                        wrbuf, 0, NULL);
+       /* fallthrough */
        case EDT_M09:
        case EDT_M12:
+       case EV_FT:
        case GENERIC_FT:
                wrbuf[0] = addr;
                wrbuf[1] = value;
@@ -315,8 +333,10 @@ static int edt_ft5x06_register_read(struct edt_ft5x06_ts_data *tsdata,
                }
                break;
 
+       /* fallthrough */
        case EDT_M09:
        case EDT_M12:
+       case EV_FT:
        case GENERIC_FT:
                wrbuf[0] = addr;
                error = edt_ft5x06_ts_readwrite(tsdata->client, 1,
@@ -339,9 +359,10 @@ struct edt_ft5x06_attribute {
        u8 limit_high;
        u8 addr_m06;
        u8 addr_m09;
+       u8 addr_ev;
 };
 
-#define EDT_ATTR(_field, _mode, _addr_m06, _addr_m09,                  \
+#define EDT_ATTR(_field, _mode, _addr_m06, _addr_m09, _addr_ev,                \
                _limit_low, _limit_high)                                \
        struct edt_ft5x06_attribute edt_ft5x06_attr_##_field = {        \
                .dattr = __ATTR(_field, _mode,                          \
@@ -350,6 +371,7 @@ struct edt_ft5x06_attribute {
                .field_offset = offsetof(struct edt_ft5x06_ts_data, _field), \
                .addr_m06 = _addr_m06,                                  \
                .addr_m09 = _addr_m09,                                  \
+               .addr_ev  = _addr_ev,                                   \
                .limit_low = _limit_low,                                \
                .limit_high = _limit_high,                              \
        }
@@ -386,6 +408,10 @@ static ssize_t edt_ft5x06_setting_show(struct device *dev,
                addr = attr->addr_m09;
                break;
 
+       case EV_FT:
+               addr = attr->addr_ev;
+               break;
+
        default:
                error = -ENODEV;
                goto out;
@@ -457,6 +483,10 @@ static ssize_t edt_ft5x06_setting_store(struct device *dev,
                addr = attr->addr_m09;
                break;
 
+       case EV_FT:
+               addr = attr->addr_ev;
+               break;
+
        default:
                error = -ENODEV;
                goto out;
@@ -480,20 +510,28 @@ out:
 
 /* m06, m09: range 0-31, m12: range 0-5 */
 static EDT_ATTR(gain, S_IWUSR | S_IRUGO, WORK_REGISTER_GAIN,
-               M09_REGISTER_GAIN, 0, 31);
+               M09_REGISTER_GAIN, EV_REGISTER_GAIN, 0, 31);
 /* m06, m09: range 0-31, m12: range 0-16 */
 static EDT_ATTR(offset, S_IWUSR | S_IRUGO, WORK_REGISTER_OFFSET,
-               M09_REGISTER_OFFSET, 0, 31);
+               M09_REGISTER_OFFSET, NO_REGISTER, 0, 31);
+/* m06, m09, m12: no supported, ev_ft: range 0-80 */
+static EDT_ATTR(offset_x, S_IWUSR | S_IRUGO, NO_REGISTER, NO_REGISTER,
+               EV_REGISTER_OFFSET_X, 0, 80);
+/* m06, m09, m12: no supported, ev_ft: range 0-80 */
+static EDT_ATTR(offset_y, S_IWUSR | S_IRUGO, NO_REGISTER, NO_REGISTER,
+               EV_REGISTER_OFFSET_Y, 0, 80);
 /* m06: range 20 to 80, m09: range 0 to 30, m12: range 1 to 255... */
 static EDT_ATTR(threshold, S_IWUSR | S_IRUGO, WORK_REGISTER_THRESHOLD,
-               M09_REGISTER_THRESHOLD, 0, 255);
+               M09_REGISTER_THRESHOLD, EV_REGISTER_THRESHOLD, 0, 255);
 /* m06: range 3 to 14, m12: (0x64: 100Hz) */
 static EDT_ATTR(report_rate, S_IWUSR | S_IRUGO, WORK_REGISTER_REPORT_RATE,
-               NO_REGISTER, 0, 255);
+               NO_REGISTER, NO_REGISTER, 0, 255);
 
 static struct attribute *edt_ft5x06_attrs[] = {
        &edt_ft5x06_attr_gain.dattr.attr,
        &edt_ft5x06_attr_offset.dattr.attr,
+       &edt_ft5x06_attr_offset_x.dattr.attr,
+       &edt_ft5x06_attr_offset_y.dattr.attr,
        &edt_ft5x06_attr_threshold.dattr.attr,
        &edt_ft5x06_attr_report_rate.dattr.attr,
        NULL
@@ -605,8 +643,15 @@ static int edt_ft5x06_work_mode(struct edt_ft5x06_ts_data *tsdata)
                                  tsdata->threshold);
        edt_ft5x06_register_write(tsdata, reg_addr->reg_gain,
                                  tsdata->gain);
-       edt_ft5x06_register_write(tsdata, reg_addr->reg_offset,
-                                 tsdata->offset);
+       if (reg_addr->reg_offset != NO_REGISTER)
+               edt_ft5x06_register_write(tsdata, reg_addr->reg_offset,
+                                         tsdata->offset);
+       if (reg_addr->reg_offset_x != NO_REGISTER)
+               edt_ft5x06_register_write(tsdata, reg_addr->reg_offset_x,
+                                         tsdata->offset_x);
+       if (reg_addr->reg_offset_y != NO_REGISTER)
+               edt_ft5x06_register_write(tsdata, reg_addr->reg_offset_y,
+                                         tsdata->offset_y);
        if (reg_addr->reg_report_rate != NO_REGISTER)
                edt_ft5x06_register_write(tsdata, reg_addr->reg_report_rate,
                                  tsdata->report_rate);
@@ -867,6 +912,16 @@ static int edt_ft5x06_ts_identify(struct i2c_client *client,
                case 0x5a:   /* Solomon Goldentek Display */
                        snprintf(model_name, EDT_NAME_LEN, "GKTW50SCED1R0");
                        break;
+               case 0x59:  /* Evervision Display with FT5xx6 TS */
+                       tsdata->version = EV_FT;
+                       error = edt_ft5x06_ts_readwrite(client, 1, "\x53",
+                                                       1, rdbuf);
+                       if (error)
+                               return error;
+                       strlcpy(fw_version, rdbuf, 1);
+                       snprintf(model_name, EDT_NAME_LEN,
+                                "EVERVISION-FT5726NEi");
+                       break;
                default:
                        snprintf(model_name, EDT_NAME_LEN,
                                 "generic ft5x06 (%02x)",
@@ -902,6 +957,18 @@ static void edt_ft5x06_ts_get_defaults(struct device *dev,
                edt_ft5x06_register_write(tsdata, reg_addr->reg_offset, val);
                tsdata->offset = val;
        }
+
+       error = device_property_read_u32(dev, "offset-x", &val);
+       if (!error) {
+               edt_ft5x06_register_write(tsdata, reg_addr->reg_offset_x, val);
+               tsdata->offset_x = val;
+       }
+
+       error = device_property_read_u32(dev, "offset-y", &val);
+       if (!error) {
+               edt_ft5x06_register_write(tsdata, reg_addr->reg_offset_y, val);
+               tsdata->offset_y = val;
+       }
 }
 
 static void
@@ -912,7 +979,15 @@ edt_ft5x06_ts_get_parameters(struct edt_ft5x06_ts_data *tsdata)
        tsdata->threshold = edt_ft5x06_register_read(tsdata,
                                                     reg_addr->reg_threshold);
        tsdata->gain = edt_ft5x06_register_read(tsdata, reg_addr->reg_gain);
-       tsdata->offset = edt_ft5x06_register_read(tsdata, reg_addr->reg_offset);
+       if (reg_addr->reg_offset != NO_REGISTER)
+               tsdata->offset =
+                       edt_ft5x06_register_read(tsdata, reg_addr->reg_offset);
+       if (reg_addr->reg_offset_x != NO_REGISTER)
+               tsdata->offset_x = edt_ft5x06_register_read(tsdata,
+                                               reg_addr->reg_offset_x);
+       if (reg_addr->reg_offset_y != NO_REGISTER)
+               tsdata->offset_y = edt_ft5x06_register_read(tsdata,
+                                               reg_addr->reg_offset_y);
        if (reg_addr->reg_report_rate != NO_REGISTER)
                tsdata->report_rate = edt_ft5x06_register_read(tsdata,
                                                reg_addr->reg_report_rate);
@@ -940,6 +1015,8 @@ edt_ft5x06_ts_set_regs(struct edt_ft5x06_ts_data *tsdata)
                reg_addr->reg_report_rate = WORK_REGISTER_REPORT_RATE;
                reg_addr->reg_gain = WORK_REGISTER_GAIN;
                reg_addr->reg_offset = WORK_REGISTER_OFFSET;
+               reg_addr->reg_offset_x = NO_REGISTER;
+               reg_addr->reg_offset_y = NO_REGISTER;
                reg_addr->reg_num_x = WORK_REGISTER_NUM_X;
                reg_addr->reg_num_y = WORK_REGISTER_NUM_Y;
                break;
@@ -950,15 +1027,30 @@ edt_ft5x06_ts_set_regs(struct edt_ft5x06_ts_data *tsdata)
                reg_addr->reg_report_rate = NO_REGISTER;
                reg_addr->reg_gain = M09_REGISTER_GAIN;
                reg_addr->reg_offset = M09_REGISTER_OFFSET;
+               reg_addr->reg_offset_x = NO_REGISTER;
+               reg_addr->reg_offset_y = NO_REGISTER;
                reg_addr->reg_num_x = M09_REGISTER_NUM_X;
                reg_addr->reg_num_y = M09_REGISTER_NUM_Y;
                break;
 
+       case EV_FT:
+               reg_addr->reg_threshold = EV_REGISTER_THRESHOLD;
+               reg_addr->reg_gain = EV_REGISTER_GAIN;
+               reg_addr->reg_offset = NO_REGISTER;
+               reg_addr->reg_offset_x = EV_REGISTER_OFFSET_X;
+               reg_addr->reg_offset_y = EV_REGISTER_OFFSET_Y;
+               reg_addr->reg_num_x = NO_REGISTER;
+               reg_addr->reg_num_y = NO_REGISTER;
+               reg_addr->reg_report_rate = NO_REGISTER;
+               break;
+
        case GENERIC_FT:
                /* this is a guesswork */
                reg_addr->reg_threshold = M09_REGISTER_THRESHOLD;
                reg_addr->reg_gain = M09_REGISTER_GAIN;
                reg_addr->reg_offset = M09_REGISTER_OFFSET;
+               reg_addr->reg_offset_x = NO_REGISTER;
+               reg_addr->reg_offset_y = NO_REGISTER;
                break;
        }
 }
@@ -1155,6 +1247,7 @@ static const struct edt_i2c_chip_data edt_ft6236_data = {
 static const struct i2c_device_id edt_ft5x06_ts_id[] = {
        { .name = "edt-ft5x06", .driver_data = (long)&edt_ft5x06_data },
        { .name = "edt-ft5506", .driver_data = (long)&edt_ft5506_data },
+       { .name = "ev-ft5726", .driver_data = (long)&edt_ft5506_data },
        /* Note no edt- prefix for compatibility with the ft6236.c driver */
        { .name = "ft6236", .driver_data = (long)&edt_ft6236_data },
        { /* sentinel */ }
@@ -1167,6 +1260,7 @@ static const struct of_device_id edt_ft5x06_of_match[] = {
        { .compatible = "edt,edt-ft5306", .data = &edt_ft5x06_data },
        { .compatible = "edt,edt-ft5406", .data = &edt_ft5x06_data },
        { .compatible = "edt,edt-ft5506", .data = &edt_ft5506_data },
+       { .compatible = "evervision,ev-ft5726", .data = &edt_ft5506_data },
        /* Note focaltech vendor prefix for compatibility with ft6236.c */
        { .compatible = "focaltech,ft6236", .data = &edt_ft6236_data },
        { /* sentinel */ }
index f2d9c2c..f57d822 100644 (file)
@@ -216,6 +216,7 @@ static const struct goodix_chip_data *goodix_get_chip_data(u16 id)
 {
        switch (id) {
        case 1151:
+       case 5688:
                return &gt1x_chip_data;
 
        case 911:
@@ -692,7 +693,9 @@ static int goodix_configure_dev(struct goodix_ts_data *ts)
        touchscreen_parse_properties(ts->input_dev, true, &ts->prop);
 
        if (!ts->prop.max_x || !ts->prop.max_y || !ts->max_touch_num) {
-               dev_err(&ts->client->dev, "Invalid config, using defaults\n");
+               dev_err(&ts->client->dev,
+                       "Invalid config (%d, %d, %d), using defaults\n",
+                       ts->prop.max_x, ts->prop.max_y, ts->max_touch_num);
                ts->prop.max_x = GOODIX_MAX_WIDTH - 1;
                ts->prop.max_y = GOODIX_MAX_HEIGHT - 1;
                ts->max_touch_num = GOODIX_MAX_CONTACTS;
@@ -942,6 +945,7 @@ MODULE_DEVICE_TABLE(acpi, goodix_acpi_match);
 #ifdef CONFIG_OF
 static const struct of_device_id goodix_of_match[] = {
        { .compatible = "goodix,gt1151" },
+       { .compatible = "goodix,gt5688" },
        { .compatible = "goodix,gt911" },
        { .compatible = "goodix,gt9110" },
        { .compatible = "goodix,gt912" },
index 6f76eee..9169aa0 100644 (file)
@@ -4,11 +4,15 @@
 #include <linux/slab.h>
 #include <linux/input.h>
 #include <linux/input/mt.h>
+#include <linux/input/touchscreen.h>
 #include <linux/delay.h>
 #include <linux/workqueue.h>
-#include <linux/input/ili210x.h>
+#include <linux/gpio/consumer.h>
+#include <linux/of_device.h>
+#include <asm/unaligned.h>
 
-#define MAX_TOUCHES            2
+#define ILI210X_TOUCHES                2
+#define ILI251X_TOUCHES                10
 #define DEFAULT_POLL_PERIOD    20
 
 /* Touchscreen commands */
 #define REG_FIRMWARE_VERSION   0x40
 #define REG_CALIBRATE          0xcc
 
-struct finger {
-       u8 x_low;
-       u8 x_high;
-       u8 y_low;
-       u8 y_high;
-} __packed;
-
-struct touchdata {
-       u8 status;
-       struct finger finger[MAX_TOUCHES];
-} __packed;
-
-struct panel_info {
-       struct finger finger_max;
-       u8 xchannel_num;
-       u8 ychannel_num;
-} __packed;
-
 struct firmware_version {
        u8 id;
        u8 major;
        u8 minor;
 } __packed;
 
+enum ili2xxx_model {
+       MODEL_ILI210X,
+       MODEL_ILI251X,
+};
+
 struct ili210x {
        struct i2c_client *client;
        struct input_dev *input;
-       bool (*get_pendown_state)(void);
        unsigned int poll_period;
        struct delayed_work dwork;
+       struct gpio_desc *reset_gpio;
+       struct touchscreen_properties prop;
+       enum ili2xxx_model model;
+       unsigned int max_touches;
 };
 
 static int ili210x_read_reg(struct i2c_client *client, u8 reg, void *buf,
                            size_t len)
 {
+       struct ili210x *priv = i2c_get_clientdata(client);
        struct i2c_msg msg[2] = {
                {
                        .addr   = client->addr,
@@ -67,7 +62,38 @@ static int ili210x_read_reg(struct i2c_client *client, u8 reg, void *buf,
                }
        };
 
-       if (i2c_transfer(client->adapter, msg, 2) != 2) {
+       if (priv->model == MODEL_ILI251X) {
+               if (i2c_transfer(client->adapter, msg, 1) != 1) {
+                       dev_err(&client->dev, "i2c transfer failed\n");
+                       return -EIO;
+               }
+
+               usleep_range(5000, 5500);
+
+               if (i2c_transfer(client->adapter, msg + 1, 1) != 1) {
+                       dev_err(&client->dev, "i2c transfer failed\n");
+                       return -EIO;
+               }
+       } else {
+               if (i2c_transfer(client->adapter, msg, 2) != 2) {
+                       dev_err(&client->dev, "i2c transfer failed\n");
+                       return -EIO;
+               }
+       }
+
+       return 0;
+}
+
+static int ili210x_read(struct i2c_client *client, void *buf, size_t len)
+{
+       struct i2c_msg msg = {
+               .addr   = client->addr,
+               .flags  = I2C_M_RD,
+               .len    = len,
+               .buf    = buf,
+       };
+
+       if (i2c_transfer(client->adapter, &msg, 1) != 1) {
                dev_err(&client->dev, "i2c transfer failed\n");
                return -EIO;
        }
@@ -75,42 +101,72 @@ static int ili210x_read_reg(struct i2c_client *client, u8 reg, void *buf,
        return 0;
 }
 
-static void ili210x_report_events(struct input_dev *input,
-                                 const struct touchdata *touchdata)
+static bool ili210x_touchdata_to_coords(struct ili210x *priv, u8 *touchdata,
+                                       unsigned int finger,
+                                       unsigned int *x, unsigned int *y)
 {
-       int i;
-       bool touch;
-       unsigned int x, y;
-       const struct finger *finger;
+       if (finger >= ILI210X_TOUCHES)
+               return false;
 
-       for (i = 0; i < MAX_TOUCHES; i++) {
-               input_mt_slot(input, i);
+       if (touchdata[0] & BIT(finger))
+               return false;
 
-               finger = &touchdata->finger[i];
+       *x = get_unaligned_be16(touchdata + 1 + (finger * 4) + 0);
+       *y = get_unaligned_be16(touchdata + 1 + (finger * 4) + 2);
 
-               touch = touchdata->status & (1 << i);
-               input_mt_report_slot_state(input, MT_TOOL_FINGER, touch);
-               if (touch) {
-                       x = finger->x_low | (finger->x_high << 8);
-                       y = finger->y_low | (finger->y_high << 8);
+       return true;
+}
+
+static bool ili251x_touchdata_to_coords(struct ili210x *priv, u8 *touchdata,
+                                       unsigned int finger,
+                                       unsigned int *x, unsigned int *y)
+{
+       if (finger >= ILI251X_TOUCHES)
+               return false;
+
+       *x = get_unaligned_be16(touchdata + 1 + (finger * 5) + 0);
+       if (!(*x & BIT(15)))    /* Touch indication */
+               return false;
 
-                       input_report_abs(input, ABS_MT_POSITION_X, x);
-                       input_report_abs(input, ABS_MT_POSITION_Y, y);
+       *x &= 0x3fff;
+       *y = get_unaligned_be16(touchdata + 1 + (finger * 5) + 2);
+
+       return true;
+}
+
+static bool ili210x_report_events(struct ili210x *priv, u8 *touchdata)
+{
+       struct input_dev *input = priv->input;
+       int i;
+       bool contact = false, touch = false;
+       unsigned int x = 0, y = 0;
+
+       for (i = 0; i < priv->max_touches; i++) {
+               if (priv->model == MODEL_ILI210X) {
+                       touch = ili210x_touchdata_to_coords(priv, touchdata,
+                                                           i, &x, &y);
+               } else if (priv->model == MODEL_ILI251X) {
+                       touch = ili251x_touchdata_to_coords(priv, touchdata,
+                                                           i, &x, &y);
+                       if (touch)
+                               contact = true;
                }
+
+               input_mt_slot(input, i);
+               input_mt_report_slot_state(input, MT_TOOL_FINGER, touch);
+               if (!touch)
+                       continue;
+               touchscreen_report_pos(input, &priv->prop, x, y,
+                                      true);
        }
 
        input_mt_report_pointer_emulation(input, false);
        input_sync(input);
-}
 
-static bool get_pendown_state(const struct ili210x *priv)
-{
-       bool state = false;
-
-       if (priv->get_pendown_state)
-               state = priv->get_pendown_state();
+       if (priv->model == MODEL_ILI210X)
+               contact = touchdata[0] & 0xf3;
 
-       return state;
+       return contact;
 }
 
 static void ili210x_work(struct work_struct *work)
@@ -118,20 +174,29 @@ static void ili210x_work(struct work_struct *work)
        struct ili210x *priv = container_of(work, struct ili210x,
                                            dwork.work);
        struct i2c_client *client = priv->client;
-       struct touchdata touchdata;
-       int error;
+       u8 touchdata[64] = { 0 };
+       bool touch;
+       int error = -EINVAL;
+
+       if (priv->model == MODEL_ILI210X) {
+               error = ili210x_read_reg(client, REG_TOUCHDATA,
+                                        touchdata, sizeof(touchdata));
+       } else if (priv->model == MODEL_ILI251X) {
+               error = ili210x_read_reg(client, REG_TOUCHDATA,
+                                        touchdata, 31);
+               if (!error && touchdata[0] == 2)
+                       error = ili210x_read(client, &touchdata[31], 20);
+       }
 
-       error = ili210x_read_reg(client, REG_TOUCHDATA,
-                                &touchdata, sizeof(touchdata));
        if (error) {
                dev_err(&client->dev,
                        "Unable to get touchdata, err = %d\n", error);
                return;
        }
 
-       ili210x_report_events(priv->input, &touchdata);
+       touch = ili210x_report_events(priv, touchdata);
 
-       if ((touchdata.status & 0xf3) || get_pendown_state(priv))
+       if (touch)
                schedule_delayed_work(&priv->dwork,
                                      msecs_to_jiffies(priv->poll_period));
 }
@@ -180,103 +245,119 @@ static const struct attribute_group ili210x_attr_group = {
        .attrs = ili210x_attributes,
 };
 
+static void ili210x_power_down(void *data)
+{
+       struct gpio_desc *reset_gpio = data;
+
+       gpiod_set_value_cansleep(reset_gpio, 1);
+}
+
+static void ili210x_cancel_work(void *data)
+{
+       struct ili210x *priv = data;
+
+       cancel_delayed_work_sync(&priv->dwork);
+}
+
 static int ili210x_i2c_probe(struct i2c_client *client,
                                       const struct i2c_device_id *id)
 {
        struct device *dev = &client->dev;
-       const struct ili210x_platform_data *pdata = dev_get_platdata(dev);
        struct ili210x *priv;
+       struct gpio_desc *reset_gpio;
        struct input_dev *input;
-       struct panel_info panel;
        struct firmware_version firmware;
-       int xmax, ymax;
+       enum ili2xxx_model model;
        int error;
 
-       dev_dbg(dev, "Probing for ILI210X I2C Touschreen driver");
+       model = (enum ili2xxx_model)id->driver_data;
 
-       if (!pdata) {
-               dev_err(dev, "No platform data!\n");
-               return -EINVAL;
-       }
+       dev_dbg(dev, "Probing for ILI210X I2C Touschreen driver");
 
        if (client->irq <= 0) {
                dev_err(dev, "No IRQ!\n");
                return -EINVAL;
        }
 
-       /* Get firmware version */
-       error = ili210x_read_reg(client, REG_FIRMWARE_VERSION,
-                                &firmware, sizeof(firmware));
-       if (error) {
-               dev_err(dev, "Failed to get firmware version, err: %d\n",
-                       error);
-               return error;
-       }
+       reset_gpio = devm_gpiod_get_optional(dev, "reset", GPIOD_OUT_HIGH);
+       if (IS_ERR(reset_gpio))
+               return PTR_ERR(reset_gpio);
 
-       /* get panel info */
-       error = ili210x_read_reg(client, REG_PANEL_INFO, &panel, sizeof(panel));
-       if (error) {
-               dev_err(dev, "Failed to get panel information, err: %d\n",
-                       error);
-               return error;
+       if (reset_gpio) {
+               error = devm_add_action_or_reset(dev, ili210x_power_down,
+                                                reset_gpio);
+               if (error)
+                       return error;
+
+               usleep_range(50, 100);
+               gpiod_set_value_cansleep(reset_gpio, 0);
+               msleep(100);
        }
 
-       xmax = panel.finger_max.x_low | (panel.finger_max.x_high << 8);
-       ymax = panel.finger_max.y_low | (panel.finger_max.y_high << 8);
+       priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL);
+       if (!priv)
+               return -ENOMEM;
 
-       priv = kzalloc(sizeof(*priv), GFP_KERNEL);
-       input = input_allocate_device();
-       if (!priv || !input) {
-               error = -ENOMEM;
-               goto err_free_mem;
-       }
+       input = devm_input_allocate_device(dev);
+       if (!input)
+               return -ENOMEM;
 
        priv->client = client;
        priv->input = input;
-       priv->get_pendown_state = pdata->get_pendown_state;
-       priv->poll_period = pdata->poll_period ? : DEFAULT_POLL_PERIOD;
+       priv->poll_period = DEFAULT_POLL_PERIOD;
        INIT_DELAYED_WORK(&priv->dwork, ili210x_work);
+       priv->reset_gpio = reset_gpio;
+       priv->model = model;
+       if (model == MODEL_ILI210X)
+               priv->max_touches = ILI210X_TOUCHES;
+       if (model == MODEL_ILI251X)
+               priv->max_touches = ILI251X_TOUCHES;
+
+       i2c_set_clientdata(client, priv);
+
+       /* Get firmware version */
+       error = ili210x_read_reg(client, REG_FIRMWARE_VERSION,
+                                &firmware, sizeof(firmware));
+       if (error) {
+               dev_err(dev, "Failed to get firmware version, err: %d\n",
+                       error);
+               return error;
+       }
 
        /* Setup input device */
        input->name = "ILI210x Touchscreen";
        input->id.bustype = BUS_I2C;
        input->dev.parent = dev;
 
-       __set_bit(EV_SYN, input->evbit);
-       __set_bit(EV_KEY, input->evbit);
-       __set_bit(EV_ABS, input->evbit);
-       __set_bit(BTN_TOUCH, input->keybit);
-
-       /* Single touch */
-       input_set_abs_params(input, ABS_X, 0, xmax, 0, 0);
-       input_set_abs_params(input, ABS_Y, 0, ymax, 0, 0);
-
        /* Multi touch */
-       input_mt_init_slots(input, MAX_TOUCHES, 0);
-       input_set_abs_params(input, ABS_MT_POSITION_X, 0, xmax, 0, 0);
-       input_set_abs_params(input, ABS_MT_POSITION_Y, 0, ymax, 0, 0);
+       input_set_abs_params(input, ABS_MT_POSITION_X, 0, 0xffff, 0, 0);
+       input_set_abs_params(input, ABS_MT_POSITION_Y, 0, 0xffff, 0, 0);
+       touchscreen_parse_properties(input, true, &priv->prop);
+       input_mt_init_slots(input, priv->max_touches, INPUT_MT_DIRECT);
 
-       i2c_set_clientdata(client, priv);
+       error = devm_add_action(dev, ili210x_cancel_work, priv);
+       if (error)
+               return error;
 
-       error = request_irq(client->irq, ili210x_irq, pdata->irq_flags,
-                           client->name, priv);
+       error = devm_request_irq(dev, client->irq, ili210x_irq, 0,
+                                client->name, priv);
        if (error) {
                dev_err(dev, "Unable to request touchscreen IRQ, err: %d\n",
                        error);
-               goto err_free_mem;
+               return error;
        }
 
-       error = sysfs_create_group(&dev->kobj, &ili210x_attr_group);
+       error = devm_device_add_group(dev, &ili210x_attr_group);
        if (error) {
                dev_err(dev, "Unable to create sysfs attributes, err: %d\n",
                        error);
-               goto err_free_irq;
+               return error;
        }
 
        error = input_register_device(priv->input);
        if (error) {
                dev_err(dev, "Cannot register input device, err: %d\n", error);
-               goto err_remove_sysfs;
+               return error;
        }
 
        device_init_wakeup(dev, 1);
@@ -286,28 +367,6 @@ static int ili210x_i2c_probe(struct i2c_client *client,
                client->irq, firmware.id, firmware.major, firmware.minor);
 
        return 0;
-
-err_remove_sysfs:
-       sysfs_remove_group(&dev->kobj, &ili210x_attr_group);
-err_free_irq:
-       free_irq(client->irq, priv);
-err_free_mem:
-       input_free_device(input);
-       kfree(priv);
-       return error;
-}
-
-static int ili210x_i2c_remove(struct i2c_client *client)
-{
-       struct ili210x *priv = i2c_get_clientdata(client);
-
-       sysfs_remove_group(&client->dev.kobj, &ili210x_attr_group);
-       free_irq(priv->client->irq, priv);
-       cancel_delayed_work_sync(&priv->dwork);
-       input_unregister_device(priv->input);
-       kfree(priv);
-
-       return 0;
 }
 
 static int __maybe_unused ili210x_i2c_suspend(struct device *dev)
@@ -334,19 +393,27 @@ static SIMPLE_DEV_PM_OPS(ili210x_i2c_pm,
                         ili210x_i2c_suspend, ili210x_i2c_resume);
 
 static const struct i2c_device_id ili210x_i2c_id[] = {
-       { "ili210x", 0 },
+       { "ili210x", MODEL_ILI210X },
+       { "ili251x", MODEL_ILI251X },
        { }
 };
 MODULE_DEVICE_TABLE(i2c, ili210x_i2c_id);
 
+static const struct of_device_id ili210x_dt_ids[] = {
+       { .compatible = "ilitek,ili210x", .data = (void *)MODEL_ILI210X },
+       { .compatible = "ilitek,ili251x", .data = (void *)MODEL_ILI251X },
+       { },
+};
+MODULE_DEVICE_TABLE(of, ili210x_dt_ids);
+
 static struct i2c_driver ili210x_ts_driver = {
        .driver = {
                .name = "ili210x_i2c",
                .pm = &ili210x_i2c_pm,
+               .of_match_table = ili210x_dt_ids,
        },
        .id_table = ili210x_i2c_id,
        .probe = ili210x_i2c_probe,
-       .remove = ili210x_i2c_remove,
 };
 
 module_i2c_driver(ili210x_ts_driver);
index 11ff32c..3492339 100644 (file)
  */
 
 #include <linux/delay.h>
-#include <linux/gpio.h>
+#include <linux/gpio/consumer.h>
 #include <linux/i2c.h>
 #include <linux/input.h>
 #include <linux/interrupt.h>
 #include <linux/module.h>
 #include <linux/of.h>
-#include <linux/of_gpio.h>
 #include <linux/pm_qos.h>
 #include <linux/slab.h>
 #include <linux/types.h>
+#include <linux/input/touchscreen.h>
 
 #define ST1232_TS_NAME "st1232-ts"
-
-#define MIN_X          0x00
-#define MIN_Y          0x00
-#define MAX_X          0x31f   /* (800 - 1) */
-#define MAX_Y          0x1df   /* (480 - 1) */
-#define MAX_AREA       0xff
-#define MAX_FINGERS    2
+#define ST1633_TS_NAME "st1633-ts"
 
 struct st1232_ts_finger {
        u16 x;
@@ -38,12 +32,25 @@ struct st1232_ts_finger {
        bool is_valid;
 };
 
+struct st_chip_info {
+       bool    have_z;
+       u16     max_x;
+       u16     max_y;
+       u16     max_area;
+       u16     max_fingers;
+       u8      start_reg;
+};
+
 struct st1232_ts_data {
        struct i2c_client *client;
        struct input_dev *input_dev;
-       struct st1232_ts_finger finger[MAX_FINGERS];
+       struct touchscreen_properties prop;
        struct dev_pm_qos_request low_latency_req;
-       int reset_gpio;
+       struct gpio_desc *reset_gpio;
+       const struct st_chip_info *chip_info;
+       int read_buf_len;
+       u8 *read_buf;
+       struct st1232_ts_finger *finger;
 };
 
 static int st1232_ts_read_data(struct st1232_ts_data *ts)
@@ -52,40 +59,35 @@ static int st1232_ts_read_data(struct st1232_ts_data *ts)
        struct i2c_client *client = ts->client;
        struct i2c_msg msg[2];
        int error;
-       u8 start_reg;
-       u8 buf[10];
+       int i, y;
+       u8 start_reg = ts->chip_info->start_reg;
+       u8 *buf = ts->read_buf;
 
-       /* read touchscreen data from ST1232 */
+       /* read touchscreen data */
        msg[0].addr = client->addr;
        msg[0].flags = 0;
        msg[0].len = 1;
        msg[0].buf = &start_reg;
-       start_reg = 0x10;
 
        msg[1].addr = ts->client->addr;
        msg[1].flags = I2C_M_RD;
-       msg[1].len = sizeof(buf);
+       msg[1].len = ts->read_buf_len;
        msg[1].buf = buf;
 
        error = i2c_transfer(client->adapter, msg, 2);
        if (error < 0)
                return error;
 
-       /* get "valid" bits */
-       finger[0].is_valid = buf[2] >> 7;
-       finger[1].is_valid = buf[5] >> 7;
+       for (i = 0, y = 0; i < ts->chip_info->max_fingers; i++, y += 3) {
+               finger[i].is_valid = buf[i + y] >> 7;
+               if (finger[i].is_valid) {
+                       finger[i].x = ((buf[i + y] & 0x0070) << 4) | buf[i + 1];
+                       finger[i].y = ((buf[i + y] & 0x0007) << 8) | buf[i + 2];
 
-       /* get xy coordinate */
-       if (finger[0].is_valid) {
-               finger[0].x = ((buf[2] & 0x0070) << 4) | buf[3];
-               finger[0].y = ((buf[2] & 0x0007) << 8) | buf[4];
-               finger[0].t = buf[8];
-       }
-
-       if (finger[1].is_valid) {
-               finger[1].x = ((buf[5] & 0x0070) << 4) | buf[6];
-               finger[1].y = ((buf[5] & 0x0007) << 8) | buf[7];
-               finger[1].t = buf[9];
+                       /* st1232 includes a z-axis / touch strength */
+                       if (ts->chip_info->have_z)
+                               finger[i].t = buf[i + 6];
+               }
        }
 
        return 0;
@@ -104,13 +106,16 @@ static irqreturn_t st1232_ts_irq_handler(int irq, void *dev_id)
                goto end;
 
        /* multi touch protocol */
-       for (i = 0; i < MAX_FINGERS; i++) {
+       for (i = 0; i < ts->chip_info->max_fingers; i++) {
                if (!finger[i].is_valid)
                        continue;
 
-               input_report_abs(input_dev, ABS_MT_TOUCH_MAJOR, finger[i].t);
-               input_report_abs(input_dev, ABS_MT_POSITION_X, finger[i].x);
-               input_report_abs(input_dev, ABS_MT_POSITION_Y, finger[i].y);
+               if (ts->chip_info->have_z)
+                       input_report_abs(input_dev, ABS_MT_TOUCH_MAJOR,
+                                        finger[i].t);
+
+               touchscreen_report_pos(input_dev, &ts->prop,
+                                       finger[i].x, finger[i].y, true);
                input_mt_sync(input_dev);
                count++;
        }
@@ -138,17 +143,45 @@ end:
 
 static void st1232_ts_power(struct st1232_ts_data *ts, bool poweron)
 {
-       if (gpio_is_valid(ts->reset_gpio))
-               gpio_direction_output(ts->reset_gpio, poweron);
+       if (ts->reset_gpio)
+               gpiod_set_value_cansleep(ts->reset_gpio, !poweron);
 }
 
+static const struct st_chip_info st1232_chip_info = {
+       .have_z         = true,
+       .max_x          = 0x31f, /* 800 - 1 */
+       .max_y          = 0x1df, /* 480 -1 */
+       .max_area       = 0xff,
+       .max_fingers    = 2,
+       .start_reg      = 0x12,
+};
+
+static const struct st_chip_info st1633_chip_info = {
+       .have_z         = false,
+       .max_x          = 0x13f, /* 320 - 1 */
+       .max_y          = 0x1df, /* 480 -1 */
+       .max_area       = 0x00,
+       .max_fingers    = 5,
+       .start_reg      = 0x12,
+};
+
 static int st1232_ts_probe(struct i2c_client *client,
                           const struct i2c_device_id *id)
 {
+       const struct st_chip_info *match;
        struct st1232_ts_data *ts;
+       struct st1232_ts_finger *finger;
        struct input_dev *input_dev;
        int error;
 
+       match = device_get_match_data(&client->dev);
+       if (!match && id)
+               match = (const void *)id->driver_data;
+       if (!match) {
+               dev_err(&client->dev, "unknown device model\n");
+               return -ENODEV;
+       }
+
        if (!i2c_check_functionality(client->adapter, I2C_FUNC_I2C)) {
                dev_err(&client->dev, "need I2C_FUNC_I2C\n");
                return -EIO;
@@ -163,6 +196,19 @@ static int st1232_ts_probe(struct i2c_client *client,
        if (!ts)
                return -ENOMEM;
 
+       ts->chip_info = match;
+       ts->finger = devm_kcalloc(&client->dev,
+                                 ts->chip_info->max_fingers, sizeof(*finger),
+                                 GFP_KERNEL);
+       if (!ts->finger)
+               return -ENOMEM;
+
+       /* allocate a buffer according to the number of registers to read */
+       ts->read_buf_len = ts->chip_info->max_fingers * 4;
+       ts->read_buf = devm_kzalloc(&client->dev, ts->read_buf_len, GFP_KERNEL);
+       if (!ts->read_buf)
+               return -ENOMEM;
+
        input_dev = devm_input_allocate_device(&client->dev);
        if (!input_dev)
                return -ENOMEM;
@@ -170,15 +216,13 @@ static int st1232_ts_probe(struct i2c_client *client,
        ts->client = client;
        ts->input_dev = input_dev;
 
-       ts->reset_gpio = of_get_gpio(client->dev.of_node, 0);
-       if (gpio_is_valid(ts->reset_gpio)) {
-               error = devm_gpio_request(&client->dev, ts->reset_gpio, NULL);
-               if (error) {
-                       dev_err(&client->dev,
-                               "Unable to request GPIO pin %d.\n",
-                               ts->reset_gpio);
-                               return error;
-               }
+       ts->reset_gpio = devm_gpiod_get_optional(&client->dev, NULL,
+                                                GPIOD_OUT_HIGH);
+       if (IS_ERR(ts->reset_gpio)) {
+               error = PTR_ERR(ts->reset_gpio);
+               dev_err(&client->dev, "Unable to request GPIO pin: %d.\n",
+                       error);
+               return error;
        }
 
        st1232_ts_power(ts, true);
@@ -192,9 +236,16 @@ static int st1232_ts_probe(struct i2c_client *client,
        __set_bit(EV_KEY, input_dev->evbit);
        __set_bit(EV_ABS, input_dev->evbit);
 
-       input_set_abs_params(input_dev, ABS_MT_TOUCH_MAJOR, 0, MAX_AREA, 0, 0);
-       input_set_abs_params(input_dev, ABS_MT_POSITION_X, MIN_X, MAX_X, 0, 0);
-       input_set_abs_params(input_dev, ABS_MT_POSITION_Y, MIN_Y, MAX_Y, 0, 0);
+       if (ts->chip_info->have_z)
+               input_set_abs_params(input_dev, ABS_MT_TOUCH_MAJOR, 0,
+                                    ts->chip_info->max_area, 0, 0);
+
+       input_set_abs_params(input_dev, ABS_MT_POSITION_X,
+                            0, ts->chip_info->max_x, 0, 0);
+       input_set_abs_params(input_dev, ABS_MT_POSITION_Y,
+                            0, ts->chip_info->max_y, 0, 0);
+
+       touchscreen_parse_properties(input_dev, true, &ts->prop);
 
        error = devm_request_threaded_irq(&client->dev, client->irq,
                                          NULL, st1232_ts_irq_handler,
@@ -261,13 +312,15 @@ static SIMPLE_DEV_PM_OPS(st1232_ts_pm_ops,
                         st1232_ts_suspend, st1232_ts_resume);
 
 static const struct i2c_device_id st1232_ts_id[] = {
-       { ST1232_TS_NAME, 0 },
+       { ST1232_TS_NAME, (unsigned long)&st1232_chip_info },
+       { ST1633_TS_NAME, (unsigned long)&st1633_chip_info },
        { }
 };
 MODULE_DEVICE_TABLE(i2c, st1232_ts_id);
 
 static const struct of_device_id st1232_ts_dt_ids[] = {
-       { .compatible = "sitronix,st1232", },
+       { .compatible = "sitronix,st1232", .data = &st1232_chip_info },
+       { .compatible = "sitronix,st1633", .data = &st1633_chip_info },
        { }
 };
 MODULE_DEVICE_TABLE(of, st1232_ts_dt_ids);
@@ -286,5 +339,6 @@ static struct i2c_driver st1232_ts_driver = {
 module_i2c_driver(st1232_ts_driver);
 
 MODULE_AUTHOR("Tony SIM <chinyeow.sim.xt@renesas.com>");
+MODULE_AUTHOR("Martin Kepplinger <martin.kepplinger@ginzinger.com>");
 MODULE_DESCRIPTION("SITRONIX ST1232 Touchscreen Controller Driver");
 MODULE_LICENSE("GPL v2");
index 704e990..b6f95f2 100644 (file)
@@ -106,27 +106,29 @@ struct stmfts_data {
        bool running;
 };
 
-static void stmfts_brightness_set(struct led_classdev *led_cdev,
+static int stmfts_brightness_set(struct led_classdev *led_cdev,
                                        enum led_brightness value)
 {
        struct stmfts_data *sdata = container_of(led_cdev,
                                        struct stmfts_data, led_cdev);
        int err;
 
-       if (value == sdata->led_status || !sdata->ledvdd)
-               return;
-
-       if (!value) {
-               regulator_disable(sdata->ledvdd);
-       } else {
-               err = regulator_enable(sdata->ledvdd);
-               if (err)
-                       dev_warn(&sdata->client->dev,
-                                "failed to disable ledvdd regulator: %d\n",
-                                err);
+       if (value != sdata->led_status && sdata->ledvdd) {
+               if (!value) {
+                       regulator_disable(sdata->ledvdd);
+               } else {
+                       err = regulator_enable(sdata->ledvdd);
+                       if (err) {
+                               dev_warn(&sdata->client->dev,
+                                        "failed to disable ledvdd regulator: %d\n",
+                                        err);
+                               return err;
+                       }
+               }
+               sdata->led_status = value;
        }
 
-       sdata->led_status = value;
+       return 0;
 }
 
 static enum led_brightness stmfts_brightness_get(struct led_classdev *led_cdev)
@@ -608,7 +610,7 @@ static int stmfts_enable_led(struct stmfts_data *sdata)
        sdata->led_cdev.name = STMFTS_DEV_NAME;
        sdata->led_cdev.max_brightness = LED_ON;
        sdata->led_cdev.brightness = LED_OFF;
-       sdata->led_cdev.brightness_set = stmfts_brightness_set;
+       sdata->led_cdev.brightness_set_blocking = stmfts_brightness_set;
        sdata->led_cdev.brightness_get = stmfts_brightness_get;
 
        err = devm_led_classdev_register(&sdata->client->dev, &sdata->led_cdev);
index ed29db3..dbdf489 100644 (file)
  *  published by the Free Software Foundation.
  */
 
-#include <linux/input.h>
-#include <linux/module.h>
-#include <linux/of.h>
+#include <linux/bitops.h>
+#include <linux/delay.h>
+#include <linux/gpio/consumer.h>
 #include <linux/i2c.h>
+#include <linux/input.h>
+#include <linux/input/touchscreen.h>
 #include <linux/interrupt.h>
 #include <linux/irq.h>
+#include <linux/module.h>
+#include <linux/of.h>
 
 /* register addresses */
 #define I2C_REG_TOUCH0                 0x00
 #define I2C_REG_IRQSRC                 0x23
 #define I2C_REG_SOFTRESET              0x3f
 
+#define I2C_REG_SX8650_STAT            0x05
+#define SX8650_STAT_CONVIRQ            BIT(7)
+
 /* commands */
 #define CMD_READ_REGISTER              0x40
-#define CMD_MANUAL                     0xc0
 #define CMD_PENTRG                     0xe0
 
 /* value for I2C_REG_SOFTRESET */
 #define SOFTRESET_VALUE                        0xde
 
 /* bits for I2C_REG_IRQSRC */
-#define IRQ_PENTOUCH_TOUCHCONVDONE     0x08
-#define IRQ_PENRELEASE                 0x04
+#define IRQ_PENTOUCH_TOUCHCONVDONE     BIT(3)
+#define IRQ_PENRELEASE                 BIT(2)
 
 /* bits for RegTouch1 */
 #define CONDIRQ                                0x20
+#define RPDNT_100K                     0x00
 #define FILT_7SA                       0x03
 
 /* bits for I2C_REG_CHANMASK */
-#define CONV_X                         0x80
-#define CONV_Y                         0x40
+#define CONV_X                         BIT(7)
+#define CONV_Y                         BIT(6)
 
 /* coordinates rate: higher nibble of CTRL0 register */
 #define RATE_MANUAL                    0x00
 /* power delay: lower nibble of CTRL0 register */
 #define POWDLY_1_1MS                   0x0b
 
+/* for sx8650, as we have no pen release IRQ there: timeout in ns following the
+ * last PENIRQ after which we assume the pen is lifted.
+ */
+#define SX8650_PENIRQ_TIMEOUT          msecs_to_jiffies(10)
+
 #define MAX_12BIT                      ((1 << 12) - 1)
+#define MAX_I2C_READ_LEN               10 /* see datasheet section 5.1.5 */
+
+/* channel definition */
+#define CH_X                           0x00
+#define CH_Y                           0x01
+
+struct sx865x_data {
+       u8 cmd_manual;
+       u8 chan_mask;
+       bool has_irq_penrelease;
+       bool has_reg_irqmask;
+       irq_handler_t irqh;
+};
 
 struct sx8654 {
        struct input_dev *input;
        struct i2c_client *client;
+       struct gpio_desc *gpio_reset;
+
+       spinlock_t lock;        /* for input reporting from irq/timer */
+       struct timer_list timer;
+
+       struct touchscreen_properties props;
+
+       const struct sx865x_data *data;
 };
 
+static inline void sx865x_penrelease(struct sx8654 *ts)
+{
+       struct input_dev *input_dev = ts->input;
+
+       input_report_key(input_dev, BTN_TOUCH, 0);
+       input_sync(input_dev);
+}
+
+static void sx865x_penrelease_timer_handler(struct timer_list *t)
+{
+       struct sx8654 *ts = from_timer(ts, t, timer);
+       unsigned long flags;
+
+       spin_lock_irqsave(&ts->lock, flags);
+       sx865x_penrelease(ts);
+       spin_unlock_irqrestore(&ts->lock, flags);
+       dev_dbg(&ts->client->dev, "penrelease by timer\n");
+}
+
+static irqreturn_t sx8650_irq(int irq, void *handle)
+{
+       struct sx8654 *ts = handle;
+       struct device *dev = &ts->client->dev;
+       int len, i;
+       unsigned long flags;
+       u8 stat;
+       u16 x, y;
+       u16 ch;
+       u16 chdata;
+       __be16 data[MAX_I2C_READ_LEN / sizeof(__be16)];
+       u8 nchan = hweight32(ts->data->chan_mask);
+       u8 readlen = nchan * sizeof(*data);
+
+       stat = i2c_smbus_read_byte_data(ts->client, CMD_READ_REGISTER
+                                                   | I2C_REG_SX8650_STAT);
+
+       if (!(stat & SX8650_STAT_CONVIRQ)) {
+               dev_dbg(dev, "%s ignore stat [0x%02x]", __func__, stat);
+               return IRQ_HANDLED;
+       }
+
+       len = i2c_master_recv(ts->client, (u8 *)data, readlen);
+       if (len != readlen) {
+               dev_dbg(dev, "ignore short recv (%d)\n", len);
+               return IRQ_HANDLED;
+       }
+
+       spin_lock_irqsave(&ts->lock, flags);
+
+       x = 0;
+       y = 0;
+       for (i = 0; i < nchan; i++) {
+               chdata = be16_to_cpu(data[i]);
+
+               if (unlikely(chdata == 0xFFFF)) {
+                       dev_dbg(dev, "invalid qualified data @ %d\n", i);
+                       continue;
+               } else if (unlikely(chdata & 0x8000)) {
+                       dev_warn(dev, "hibit @ %d [0x%04x]\n", i, chdata);
+                       continue;
+               }
+
+               ch = chdata >> 12;
+               if (ch == CH_X)
+                       x = chdata & MAX_12BIT;
+               else if (ch == CH_Y)
+                       y = chdata & MAX_12BIT;
+               else
+                       dev_warn(dev, "unknown channel %d [0x%04x]\n", ch,
+                                chdata);
+       }
+
+       touchscreen_report_pos(ts->input, &ts->props, x, y, false);
+       input_report_key(ts->input, BTN_TOUCH, 1);
+       input_sync(ts->input);
+       dev_dbg(dev, "point(%4d,%4d)\n", x, y);
+
+       mod_timer(&ts->timer, jiffies + SX8650_PENIRQ_TIMEOUT);
+       spin_unlock_irqrestore(&ts->lock, flags);
+
+       return IRQ_HANDLED;
+}
+
 static irqreturn_t sx8654_irq(int irq, void *handle)
 {
        struct sx8654 *sx8654 = handle;
@@ -112,8 +228,8 @@ static irqreturn_t sx8654_irq(int irq, void *handle)
                x = ((data[0] & 0xf) << 8) | (data[1]);
                y = ((data[2] & 0xf) << 8) | (data[3]);
 
-               input_report_abs(sx8654->input, ABS_X, x);
-               input_report_abs(sx8654->input, ABS_Y, y);
+               touchscreen_report_pos(sx8654->input, &sx8654->props, x, y,
+                                      false);
                input_report_key(sx8654->input, BTN_TOUCH, 1);
                input_sync(sx8654->input);
 
@@ -124,6 +240,25 @@ out:
        return IRQ_HANDLED;
 }
 
+static int sx8654_reset(struct sx8654 *ts)
+{
+       int err;
+
+       if (ts->gpio_reset) {
+               gpiod_set_value_cansleep(ts->gpio_reset, 1);
+               udelay(2); /* Tpulse > 1µs */
+               gpiod_set_value_cansleep(ts->gpio_reset, 0);
+       } else {
+               dev_dbg(&ts->client->dev, "NRST unavailable, try softreset\n");
+               err = i2c_smbus_write_byte_data(ts->client, I2C_REG_SOFTRESET,
+                                               SOFTRESET_VALUE);
+               if (err)
+                       return err;
+       }
+
+       return 0;
+}
+
 static int sx8654_open(struct input_dev *dev)
 {
        struct sx8654 *sx8654 = input_get_drvdata(dev);
@@ -157,14 +292,17 @@ static void sx8654_close(struct input_dev *dev)
 
        disable_irq(client->irq);
 
+       if (!sx8654->data->has_irq_penrelease)
+               del_timer_sync(&sx8654->timer);
+
        /* enable manual mode mode */
-       error = i2c_smbus_write_byte(client, CMD_MANUAL);
+       error = i2c_smbus_write_byte(client, sx8654->data->cmd_manual);
        if (error) {
                dev_err(&client->dev, "writing command CMD_MANUAL failed");
                return;
        }
 
-       error = i2c_smbus_write_byte_data(client, I2C_REG_TOUCH0, 0);
+       error = i2c_smbus_write_byte_data(client, I2C_REG_TOUCH0, RATE_MANUAL);
        if (error) {
                dev_err(&client->dev, "writing to I2C_REG_TOUCH0 failed");
                return;
@@ -186,6 +324,31 @@ static int sx8654_probe(struct i2c_client *client,
        if (!sx8654)
                return -ENOMEM;
 
+       sx8654->gpio_reset = devm_gpiod_get_optional(&client->dev, "reset",
+                                                    GPIOD_OUT_HIGH);
+       if (IS_ERR(sx8654->gpio_reset)) {
+               error = PTR_ERR(sx8654->gpio_reset);
+               if (error != -EPROBE_DEFER)
+                       dev_err(&client->dev, "unable to get reset-gpio: %d\n",
+                               error);
+               return error;
+       }
+       dev_dbg(&client->dev, "got GPIO reset pin\n");
+
+       sx8654->data = device_get_match_data(&client->dev);
+       if (!sx8654->data)
+               sx8654->data = (const struct sx865x_data *)id->driver_data;
+       if (!sx8654->data) {
+               dev_err(&client->dev, "invalid or missing device data\n");
+               return -EINVAL;
+       }
+
+       if (!sx8654->data->has_irq_penrelease) {
+               dev_dbg(&client->dev, "use timer for penrelease\n");
+               timer_setup(&sx8654->timer, sx865x_penrelease_timer_handler, 0);
+               spin_lock_init(&sx8654->lock);
+       }
+
        input = devm_input_allocate_device(&client->dev);
        if (!input)
                return -ENOMEM;
@@ -201,43 +364,46 @@ static int sx8654_probe(struct i2c_client *client,
        input_set_abs_params(input, ABS_X, 0, MAX_12BIT, 0, 0);
        input_set_abs_params(input, ABS_Y, 0, MAX_12BIT, 0, 0);
 
+       touchscreen_parse_properties(input, false, &sx8654->props);
+
        sx8654->client = client;
        sx8654->input = input;
 
        input_set_drvdata(sx8654->input, sx8654);
 
-       error = i2c_smbus_write_byte_data(client, I2C_REG_SOFTRESET,
-                                         SOFTRESET_VALUE);
+       error = sx8654_reset(sx8654);
        if (error) {
-               dev_err(&client->dev, "writing softreset value failed");
+               dev_err(&client->dev, "reset failed");
                return error;
        }
 
        error = i2c_smbus_write_byte_data(client, I2C_REG_CHANMASK,
-                                         CONV_X | CONV_Y);
+                                         sx8654->data->chan_mask);
        if (error) {
                dev_err(&client->dev, "writing to I2C_REG_CHANMASK failed");
                return error;
        }
 
-       error = i2c_smbus_write_byte_data(client, I2C_REG_IRQMASK,
-                                         IRQ_PENTOUCH_TOUCHCONVDONE |
-                                               IRQ_PENRELEASE);
-       if (error) {
-               dev_err(&client->dev, "writing to I2C_REG_IRQMASK failed");
-               return error;
+       if (sx8654->data->has_reg_irqmask) {
+               error = i2c_smbus_write_byte_data(client, I2C_REG_IRQMASK,
+                                                 IRQ_PENTOUCH_TOUCHCONVDONE |
+                                                       IRQ_PENRELEASE);
+               if (error) {
+                       dev_err(&client->dev, "writing I2C_REG_IRQMASK failed");
+                       return error;
+               }
        }
 
        error = i2c_smbus_write_byte_data(client, I2C_REG_TOUCH1,
-                                         CONDIRQ | FILT_7SA);
+                                         CONDIRQ | RPDNT_100K | FILT_7SA);
        if (error) {
                dev_err(&client->dev, "writing to I2C_REG_TOUCH1 failed");
                return error;
        }
 
        error = devm_request_threaded_irq(&client->dev, client->irq,
-                                         NULL, sx8654_irq,
-                                         IRQF_TRIGGER_FALLING | IRQF_ONESHOT,
+                                         NULL, sx8654->data->irqh,
+                                         IRQF_ONESHOT,
                                          client->name, sx8654);
        if (error) {
                dev_err(&client->dev,
@@ -256,17 +422,48 @@ static int sx8654_probe(struct i2c_client *client,
        return 0;
 }
 
+static const struct sx865x_data sx8650_data = {
+       .cmd_manual             = 0xb0,
+       .has_irq_penrelease     = false,
+       .has_reg_irqmask        = false,
+       .chan_mask              = (CONV_X | CONV_Y),
+       .irqh                   = sx8650_irq,
+};
+
+static const struct sx865x_data sx8654_data = {
+       .cmd_manual             = 0xc0,
+       .has_irq_penrelease     = true,
+       .has_reg_irqmask        = true,
+       .chan_mask              = (CONV_X | CONV_Y),
+       .irqh                   = sx8654_irq,
+};
+
 #ifdef CONFIG_OF
 static const struct of_device_id sx8654_of_match[] = {
-       { .compatible = "semtech,sx8654", },
-       { },
+       {
+               .compatible = "semtech,sx8650",
+               .data = &sx8650_data,
+       }, {
+               .compatible = "semtech,sx8654",
+               .data = &sx8654_data,
+       }, {
+               .compatible = "semtech,sx8655",
+               .data = &sx8654_data,
+       }, {
+               .compatible = "semtech,sx8656",
+               .data = &sx8654_data,
+       },
+       { }
 };
 MODULE_DEVICE_TABLE(of, sx8654_of_match);
 #endif
 
 static const struct i2c_device_id sx8654_id_table[] = {
-       { "semtech_sx8654", 0 },
-       { },
+       { .name = "semtech_sx8650", .driver_data = (long)&sx8650_data },
+       { .name = "semtech_sx8654", .driver_data = (long)&sx8654_data },
+       { .name = "semtech_sx8655", .driver_data = (long)&sx8654_data },
+       { .name = "semtech_sx8656", .driver_data = (long)&sx8654_data },
+       { }
 };
 MODULE_DEVICE_TABLE(i2c, sx8654_id_table);
 
index 9e8684a..83e6855 100644 (file)
@@ -507,10 +507,8 @@ static int titsc_remove(struct platform_device *pdev)
 static int __maybe_unused titsc_suspend(struct device *dev)
 {
        struct titsc *ts_dev = dev_get_drvdata(dev);
-       struct ti_tscadc_dev *tscadc_dev;
        unsigned int idle;
 
-       tscadc_dev = ti_tscadc_dev_get(to_platform_device(dev));
        if (device_may_wakeup(dev)) {
                titsc_writel(ts_dev, REG_IRQSTATUS, TSC_IRQENB_MASK);
                idle = titsc_readl(ts_dev, REG_IRQENABLE);
@@ -524,9 +522,7 @@ static int __maybe_unused titsc_suspend(struct device *dev)
 static int __maybe_unused titsc_resume(struct device *dev)
 {
        struct titsc *ts_dev = dev_get_drvdata(dev);
-       struct ti_tscadc_dev *tscadc_dev;
 
-       tscadc_dev = ti_tscadc_dev_get(to_platform_device(dev));
        if (device_may_wakeup(dev)) {
                titsc_writel(ts_dev, REG_IRQWAKEUP,
                                0x00);
diff --git a/include/linux/input/ili210x.h b/include/linux/input/ili210x.h
deleted file mode 100644 (file)
index b76e7c1..0000000
+++ /dev/null
@@ -1,11 +0,0 @@
-/* SPDX-License-Identifier: GPL-2.0 */
-#ifndef _ILI210X_H
-#define _ILI210X_H
-
-struct ili210x_platform_data {
-       unsigned long irq_flags;
-       unsigned int poll_period;
-       bool (*get_pendown_state)(void);
-};
-
-#endif