Merge branch 'for-linus' of git://git.kernel.org/pub/scm/linux/kernel/git/dtor/input
authorLinus Torvalds <torvalds@linux-foundation.org>
Fri, 7 May 2021 06:37:55 +0000 (23:37 -0700)
committerLinus Torvalds <torvalds@linux-foundation.org>
Fri, 7 May 2021 06:37:55 +0000 (23:37 -0700)
Pull input updates from Dmitry Torokhov:

 - three new touchscreen drivers: Hycon HY46XX, ILITEK Lego Series,
   and MStar MSG2638

 - a new driver for Azoteq IQS626A proximity and touch controller

 - addition of Amazon Game Controller to the list of devices handled
   by the xpad driver

 - Elan touchscreen driver will avoid binding to devices described as
   I2CHID compatible in ACPI tables

 - various driver fixes

* 'for-linus' of git://git.kernel.org/pub/scm/linux/kernel/git/dtor/input: (56 commits)
  Input: xpad - add support for Amazon Game Controller
  Input: ili210x - add missing negation for touch indication on ili210x
  MAINTAINERS: repair reference in HYCON HY46XX TOUCHSCREEN SUPPORT
  Input: add driver for the Hycon HY46XX touchpanel series
  dt-bindings: touchscreen: Add HY46XX bindings
  dt-bindings: Add Hycon Technology vendor prefix
  Input: cyttsp - flag the device properly
  Input: cyttsp - set abs params for ABS_MT_TOUCH_MAJOR
  Input: cyttsp - drop the phys path
  Input: cyttsp - reduce reset pulse timings
  Input: cyttsp - error message on boot mode exit error
  Input: apbps2 - remove useless variable
  Input: mms114 - support MMS136
  Input: mms114 - convert bindings to YAML and extend
  Input: Add support for ILITEK Lego Series
  dt-bindings: input: touchscreen: ilitek_ts_i2c: Add bindings
  Input: add MStar MSG2638 touchscreen driver
  dt-bindings: input/touchscreen: add bindings for msg2638
  Input: silead - add workaround for x86 BIOS-es which bring the chip up in a stuck state
  Input: elants_i2c - do not bind to i2c-hid compatible ACPI instantiated devices
  ...

53 files changed:
Documentation/ABI/testing/sysfs-driver-input-exc3000
Documentation/devicetree/bindings/input/atmel,maxtouch.yaml
Documentation/devicetree/bindings/input/iqs626a.yaml [new file with mode: 0644]
Documentation/devicetree/bindings/input/touchscreen/azoteq,iqs5xx.yaml [new file with mode: 0644]
Documentation/devicetree/bindings/input/touchscreen/hycon,hy46xx.yaml [new file with mode: 0644]
Documentation/devicetree/bindings/input/touchscreen/ilitek_ts_i2c.yaml [new file with mode: 0644]
Documentation/devicetree/bindings/input/touchscreen/iqs5xx.txt [deleted file]
Documentation/devicetree/bindings/input/touchscreen/melfas,mms114.yaml [new file with mode: 0644]
Documentation/devicetree/bindings/input/touchscreen/mms114.txt [deleted file]
Documentation/devicetree/bindings/input/touchscreen/mstar,msg2638.yaml [new file with mode: 0644]
Documentation/devicetree/bindings/vendor-prefixes.yaml
Documentation/input/devices/rotary-encoder.rst
MAINTAINERS
drivers/input/Makefile
drivers/input/joystick/xpad.c
drivers/input/keyboard/gpio_keys.c
drivers/input/keyboard/imx_keypad.c
drivers/input/keyboard/tca6416-keypad.c
drivers/input/keyboard/tegra-kbc.c
drivers/input/misc/Kconfig
drivers/input/misc/Makefile
drivers/input/misc/ims-pcu.c
drivers/input/misc/iqs626a.c [new file with mode: 0644]
drivers/input/misc/max8997_haptic.c
drivers/input/mouse/elan_i2c.h
drivers/input/mouse/elan_i2c_core.c
drivers/input/serio/apbps2.c
drivers/input/touchscreen.c [moved from drivers/input/touchscreen/of_touchscreen.c with 93% similarity]
drivers/input/touchscreen/Kconfig
drivers/input/touchscreen/Makefile
drivers/input/touchscreen/ar1021_i2c.c
drivers/input/touchscreen/atmel_mxt_ts.c
drivers/input/touchscreen/bu21029_ts.c
drivers/input/touchscreen/cyttsp_core.c
drivers/input/touchscreen/cyttsp_core.h
drivers/input/touchscreen/elants_i2c.c
drivers/input/touchscreen/exc3000.c
drivers/input/touchscreen/hycon-hy46xx.c [new file with mode: 0644]
drivers/input/touchscreen/ili210x.c
drivers/input/touchscreen/ilitek_ts_i2c.c [new file with mode: 0644]
drivers/input/touchscreen/iqs5xx.c
drivers/input/touchscreen/lpc32xx_ts.c
drivers/input/touchscreen/melfas_mip4.c
drivers/input/touchscreen/mms114.c
drivers/input/touchscreen/msg2638.c [new file with mode: 0644]
drivers/input/touchscreen/silead.c
drivers/input/touchscreen/stmfts.c
drivers/input/touchscreen/tsc2007.h
drivers/input/touchscreen/tsc2007_core.c
drivers/input/touchscreen/wacom_i2c.c
drivers/input/touchscreen/wm831x-ts.c
drivers/input/touchscreen/zinitix.c
include/dt-bindings/input/atmel-maxtouch.h [new file with mode: 0644]

index cd7c578..704434b 100644 (file)
@@ -15,3 +15,12 @@ Description:    Reports the model identification provided by the touchscreen, fo
                Access: Read
 
                Valid values: Represented as string
+
+What:          /sys/bus/i2c/devices/xxx/type
+Date:          Jan 2021
+Contact:       linux-input@vger.kernel.org
+Description:   Reports the type identification provided by the touchscreen, for example "PCAP82H80 Series"
+
+               Access: Read
+
+               Valid values: Represented as string
index 8c6418f..3ec579d 100644 (file)
@@ -39,6 +39,13 @@ properties:
       (active low). The line must be flagged with
       GPIO_ACTIVE_LOW.
 
+  wake-gpios:
+    maxItems: 1
+    description:
+      Optional GPIO specifier for the touchscreen's wake pin
+      (active low). The line must be flagged with
+      GPIO_ACTIVE_LOW.
+
   linux,gpio-keymap:
     $ref: /schemas/types.yaml#/definitions/uint32-array
     description: |
@@ -53,6 +60,29 @@ properties:
       or experiment to determine which bit corresponds to which input. Use
       KEY_RESERVED for unused padding values.
 
+  atmel,wakeup-method:
+    $ref: /schemas/types.yaml#/definitions/uint32
+    description: |
+      The WAKE line is an active-low input that is used to wake up the touch
+      controller from deep-sleep mode before communication with the controller
+      could be started. This optional feature used to minimize current
+      consumption when the controller is in deep sleep mode. This feature is
+      relevant only to some controller families, like mXT1386 controller for
+      example.
+
+      The WAKE pin can be connected in one of the following ways:
+       1) left permanently low
+       2) connected to the I2C-compatible SCL pin
+       3) connected to a GPIO pin on the host
+    enum:
+      - 0 # ATMEL_MXT_WAKEUP_NONE
+      - 1 # ATMEL_MXT_WAKEUP_I2C_SCL
+      - 2 # ATMEL_MXT_WAKEUP_GPIO
+    default: 0
+
+  wakeup-source:
+    type: boolean
+
 required:
   - compatible
   - reg
@@ -63,6 +93,7 @@ additionalProperties: false
 examples:
   - |
     #include <dt-bindings/interrupt-controller/irq.h>
+    #include <dt-bindings/input/atmel-maxtouch.h>
     #include <dt-bindings/gpio/gpio.h>
     i2c {
       #address-cells = <1>;
@@ -75,6 +106,7 @@ examples:
         reset-gpios = <&gpio 27 GPIO_ACTIVE_LOW>;
         vdda-supply = <&ab8500_ldo_aux2_reg>;
         vdd-supply = <&ab8500_ldo_aux5_reg>;
+        atmel,wakeup-method = <ATMEL_MXT_WAKEUP_I2C_SCL>;
       };
     };
 
diff --git a/Documentation/devicetree/bindings/input/iqs626a.yaml b/Documentation/devicetree/bindings/input/iqs626a.yaml
new file mode 100644 (file)
index 0000000..0cb736c
--- /dev/null
@@ -0,0 +1,843 @@
+# SPDX-License-Identifier: (GPL-2.0 OR BSD-2-Clause)
+%YAML 1.2
+---
+$id: http://devicetree.org/schemas/input/iqs626a.yaml#
+$schema: http://devicetree.org/meta-schemas/core.yaml#
+
+title: Azoteq IQS626A Capacitive Touch Controller
+
+maintainers:
+  - Jeff LaBundy <jeff@labundy.com>
+
+description: |
+  The Azoteq IQS626A is a 14-channel capacitive touch controller that features
+  additional Hall-effect and inductive sensing capabilities.
+
+  Link to datasheet: https://www.azoteq.com/
+
+allOf:
+  - $ref: touchscreen/touchscreen.yaml#
+
+properties:
+  compatible:
+    const: azoteq,iqs626a
+
+  reg:
+    maxItems: 1
+
+  interrupts:
+    maxItems: 1
+
+  "#address-cells":
+    const: 1
+
+  "#size-cells":
+    const: 0
+
+  azoteq,suspend-mode:
+    $ref: /schemas/types.yaml#/definitions/uint32
+    enum: [0, 1, 2, 3]
+    default: 0
+    description: |
+      Specifies the power mode during suspend as follows:
+      0: Automatic (same as normal runtime, i.e. suspend/resume disabled)
+      1: Low power (all sensing at a reduced reporting rate)
+      2: Ultra-low power (ULP channel proximity sensing)
+      3: Halt (no sensing)
+
+  azoteq,clk-div:
+    type: boolean
+    description: Divides the device's core clock by a factor of 4.
+
+  azoteq,ulp-enable:
+    type: boolean
+    description:
+      Permits the device to automatically enter ultra-low-power mode from low-
+      power mode.
+
+  azoteq,ulp-update:
+    $ref: /schemas/types.yaml#/definitions/uint32
+    enum: [0, 1, 2, 3, 4, 5, 6, 7]
+    default: 3
+    description: |
+      Specifies the rate at which the trackpad, generic and Hall channels are
+      updated during ultra-low-power mode as follows:
+      0: 8
+      1: 13
+      2: 28
+      3: 54
+      4: 89
+      5: 135
+      6: 190
+      7: 256
+
+  azoteq,ati-band-disable:
+    type: boolean
+    description: Disables the ATI band check.
+
+  azoteq,ati-lp-only:
+    type: boolean
+    description: Limits automatic ATI to low-power mode.
+
+  azoteq,gpio3-select:
+    $ref: /schemas/types.yaml#/definitions/uint32
+    enum: [0, 1, 2, 3, 4, 5, 6, 7]
+    default: 1
+    description: |
+      Selects the channel or group of channels for which the GPIO3 pin
+      represents touch state as follows:
+      0: None
+      1: ULP channel
+      2: Trackpad
+      3: Trackpad
+      4: Generic channel 0
+      5: Generic channel 1
+      6: Generic channel 2
+      7: Hall channel
+
+  azoteq,reseed-select:
+    $ref: /schemas/types.yaml#/definitions/uint32
+    enum: [0, 1, 2, 3]
+    default: 0
+    description: |
+      Specifies the event(s) that prompt the device to reseed (i.e. reset the
+      long-term average) of an associated channel as follows:
+      0: None
+      1: Proximity
+      2: Proximity or touch
+      3: Proximity, touch or deep touch
+
+  azoteq,thresh-extend:
+    type: boolean
+    description: Multiplies all touch and deep-touch thresholds by 4.
+
+  azoteq,tracking-enable:
+    type: boolean
+    description:
+      Enables all associated channels to track their respective reference
+      channels.
+
+  azoteq,reseed-offset:
+    type: boolean
+    description:
+      Applies an 8-count offset to all long-term averages upon either ATI or
+      reseed events.
+
+  azoteq,rate-np-ms:
+    minimum: 0
+    maximum: 255
+    default: 150
+    description: Specifies the report rate (in ms) during normal-power mode.
+
+  azoteq,rate-lp-ms:
+    minimum: 0
+    maximum: 255
+    default: 150
+    description: Specifies the report rate (in ms) during low-power mode.
+
+  azoteq,rate-ulp-ms:
+    multipleOf: 16
+    minimum: 0
+    maximum: 4080
+    default: 0
+    description: Specifies the report rate (in ms) during ultra-low-power mode.
+
+  azoteq,timeout-pwr-ms:
+    multipleOf: 512
+    minimum: 0
+    maximum: 130560
+    default: 2560
+    description:
+      Specifies the length of time (in ms) to wait for an event before moving
+      from normal-power mode to low-power mode, or (if 'azoteq,ulp-enable' is
+      present) from low-power mode to ultra-low-power mode.
+
+  azoteq,timeout-lta-ms:
+    multipleOf: 512
+    minimum: 0
+    maximum: 130560
+    default: 40960
+    description:
+      Specifies the length of time (in ms) to wait before resetting the long-
+      term average of all channels. Specify the maximum timeout to disable it
+      altogether.
+
+  touchscreen-inverted-x: true
+  touchscreen-inverted-y: true
+  touchscreen-swapped-x-y: true
+
+patternProperties:
+  "^ulp-0|generic-[0-2]|hall$":
+    type: object
+    description:
+      Represents a single sensing channel. A channel is active if defined and
+      inactive otherwise.
+
+    properties:
+      azoteq,ati-exclude:
+        type: boolean
+        description:
+          Prevents the channel from participating in an ATI event that is
+          manually triggered during initialization.
+
+      azoteq,reseed-disable:
+        type: boolean
+        description:
+          Prevents the channel from being reseeded if the long-term average
+          timeout (defined in 'azoteq,timeout-lta') expires.
+
+      azoteq,meas-cap-decrease:
+        type: boolean
+        description:
+          Decreases the internal measurement capacitance from 60 pF to 15 pF.
+
+      azoteq,rx-inactive:
+        $ref: /schemas/types.yaml#/definitions/uint32
+        enum: [0, 1, 2]
+        default: 0
+        description: |
+          Specifies how inactive CRX pins are to be terminated as follows:
+          0: VSS
+          1: Floating
+          2: VREG (generic channels only)
+
+      azoteq,linearize:
+        type: boolean
+        description:
+          Enables linearization of the channel's counts (generic and Hall
+          channels) or inverts the polarity of the channel's proximity or
+          touch states (ULP channel).
+
+      azoteq,dual-direction:
+        type: boolean
+        description:
+          Specifies that the channel's long-term average is to freeze in the
+          presence of either increasing or decreasing counts, thereby permit-
+          ting events to be reported in either direction.
+
+      azoteq,filt-disable:
+        type: boolean
+        description: Disables raw count filtering for the channel.
+
+      azoteq,ati-mode:
+        $ref: /schemas/types.yaml#/definitions/uint32
+        enum: [0, 1, 2, 3]
+        description: |
+          Specifies the channel's ATI mode as follows:
+          0: Disabled
+          1: Semi-partial
+          2: Partial
+          3: Full
+
+          The default value is a function of the channel and the device's reset
+          user interface (RUI); reference the datasheet for further information
+          about the available RUI options.
+
+      azoteq,ati-base:
+        $ref: /schemas/types.yaml#/definitions/uint32
+        enum: [75, 100, 150, 200]
+        description:
+          Specifies the channel's ATI base. The default value is a function
+          of the channel and the device's RUI.
+
+      azoteq,ati-target:
+        $ref: /schemas/types.yaml#/definitions/uint32
+        multipleOf: 32
+        minimum: 0
+        maximum: 2016
+        description:
+          Specifies the channel's ATI target. The default value is a function
+          of the channel and the device's RUI.
+
+      azoteq,cct-increase:
+        $ref: /schemas/types.yaml#/definitions/uint32
+        minimum: 0
+        maximum: 16
+        default: 0
+        description:
+          Specifies the degree to which the channel's charge cycle time is to
+          be increased, with 0 representing no increase. The maximum value is
+          limited to 4 in the case of the ULP channel, and the property is un-
+          available entirely in the case of the Hall channel.
+
+      azoteq,proj-bias:
+        $ref: /schemas/types.yaml#/definitions/uint32
+        enum: [0, 1, 2, 3]
+        default: 0
+        description: |
+          Specifies the bias current applied during projected-capacitance
+          sensing as follows:
+          0: 2.5 uA
+          1: 5 uA
+          2: 10 uA
+          3: 20 uA
+
+          This property is unavailable in the case of the Hall channel.
+
+      azoteq,sense-freq:
+        $ref: /schemas/types.yaml#/definitions/uint32
+        enum: [0, 1, 2, 3]
+        description: |
+          Specifies the channel's sensing frequency as follows (parenthesized
+          numbers represent the frequency if 'azoteq,clk-div' is present):
+          0: 4 MHz (1 MHz)
+          1: 2 MHz (500 kHz)
+          2: 1 MHz (250 kHz)
+          3: 500 kHz (125 kHz)
+
+          This property is unavailable in the case of the Hall channel. The
+          default value is a function of the channel and the device's RUI.
+
+      azoteq,ati-band-tighten:
+        type: boolean
+        description:
+          Tightens the ATI band from 1/8 to 1/16 of the desired target (ULP and
+          generic channels only).
+
+      azoteq,proj-enable:
+        type: boolean
+        description: Enables projected-capacitance sensing (ULP channel only).
+
+      azoteq,filt-str-np-cnt:
+        $ref: /schemas/types.yaml#/definitions/uint32
+        enum: [0, 1, 2, 3]
+        default: 0
+        description:
+          Specifies the raw count filter strength during normal-power mode (ULP
+          and generic channels only).
+
+      azoteq,filt-str-lp-cnt:
+        $ref: /schemas/types.yaml#/definitions/uint32
+        enum: [0, 1, 2, 3]
+        default: 0
+        description:
+          Specifies the raw count filter strength during low-power mode (ULP and
+          generic channels only).
+
+      azoteq,filt-str-np-lta:
+        $ref: /schemas/types.yaml#/definitions/uint32
+        enum: [0, 1, 2, 3]
+        default: 0
+        description:
+          Specifies the long-term average filter strength during normal-power
+          mode (ULP and generic channels only).
+
+      azoteq,filt-str-lp-lta:
+        $ref: /schemas/types.yaml#/definitions/uint32
+        enum: [0, 1, 2, 3]
+        default: 0
+        description:
+          Specifies the long-term average filter strength during low-power mode
+          (ULP and generic channels only).
+
+      azoteq,rx-enable:
+        $ref: /schemas/types.yaml#/definitions/uint32-array
+        minItems: 1
+        maxItems: 8
+        items:
+          minimum: 0
+          maximum: 7
+        description:
+          Specifies the CRX pin(s) associated with the channel.
+
+          This property is unavailable in the case of the Hall channel. The
+          default value is a function of the channel and the device's RUI.
+
+      azoteq,tx-enable:
+        $ref: /schemas/types.yaml#/definitions/uint32-array
+        minItems: 1
+        maxItems: 8
+        items:
+          minimum: 0
+          maximum: 7
+        description:
+          Specifies the TX pin(s) associated with the channel.
+
+          This property is unavailable in the case of the Hall channel. The
+          default value is a function of the channel and the device's RUI.
+
+      azoteq,local-cap-size:
+        $ref: /schemas/types.yaml#/definitions/uint32
+        enum: [0, 1, 2, 3, 4]
+        default: 0
+        description: |
+          Specifies the capacitance to be added to the channel as follows:
+          0: 0 pF
+          1: 0.5 pF
+          2: 1.0 pF
+          3: 1.5 pF
+          4: 2.0 pF
+
+          This property is unavailable in the case of the ULP or Hall channels.
+
+      azoteq,sense-mode:
+        $ref: /schemas/types.yaml#/definitions/uint32
+        enum: [0, 1, 8, 9, 12, 14, 15]
+        description: |
+          Specifies the channel's sensing mode as follows:
+          0:  Self capacitance
+          1:  Projected capacitance
+          8:  Self inductance
+          9:  Mutual inductance
+          12: External
+          14: Hall effect
+          15: Temperature
+
+          This property is unavailable in the case of the ULP or Hall channels.
+          The default value is a function of the channel and the device's RUI.
+
+      azoteq,tx-freq:
+        $ref: /schemas/types.yaml#/definitions/uint32
+        enum: [0, 1, 2, 3]
+        default: 0
+        description: |
+          Specifies the inductive sensing excitation frequency as follows
+          (parenthesized numbers represent the frequency if 'azoteq,clk-div'
+          is present):
+          0: 16 MHz (4 MHz)
+          1: 8 MHz (2 MHz)
+          2: 4 MHz (1 MHz)
+          3: 2 MHz (500 kHz)
+
+          This property is unavailable in the case of the ULP or Hall channels.
+
+      azoteq,invert-enable:
+        type: boolean
+        description:
+          Inverts the polarity of the states reported for proximity, touch and
+          deep-touch events relative to their respective thresholds (generic
+          channels only).
+
+      azoteq,comp-disable:
+        type: boolean
+        description:
+          Disables compensation for the channel (generic channels only).
+
+      azoteq,static-enable:
+        type: boolean
+        description:
+          Enables the static front-end for the channel (generic channels only).
+
+      azoteq,assoc-select:
+        $ref: /schemas/types.yaml#/definitions/string-array
+        minItems: 1
+        maxItems: 6
+        items:
+          enum:
+            - ulp-0
+            - trackpad-3x2
+            - trackpad-3x3
+            - generic-0
+            - generic-1
+            - generic-2
+            - hall
+        description:
+          Specifies the associated channels for which the channel serves as a
+          reference channel. By default, no channels are selected. This prop-
+          erty is only available for the generic channels.
+
+      azoteq,assoc-weight:
+        $ref: /schemas/types.yaml#/definitions/uint32
+        minimum: 0
+        maximum: 255
+        default: 0
+        description:
+          Specifies the channel's impact weight if it acts as an associated
+          channel (0 = 0% impact, 255 = 200% impact). This property is only
+          available for the generic channels.
+
+    patternProperties:
+      "^event-(prox|touch|deep)(-alt)?$":
+        type: object
+        description:
+          Represents a proximity, touch or deep-touch event reported by the
+          channel in response to a decrease in counts. Node names suffixed with
+          '-alt' instead correspond to an increase in counts.
+
+          By default, the long-term average tracks an increase in counts such
+          that only events corresponding to a decrease in counts are reported
+          (refer to the datasheet for more information).
+
+          Specify 'azoteq,dual-direction' to freeze the long-term average when
+          the counts increase or decrease such that events of either direction
+          can be reported. Alternatively, specify 'azoteq,invert-enable' to in-
+          vert the polarity of the states reported by the channel.
+
+          Complementary events (e.g. event-touch and event-touch-alt) can both
+          be present and specify different key or switch codes, but not differ-
+          ent thresholds or hysteresis (if applicable).
+
+          Proximity events are unavailable in the case of the Hall channel, and
+          deep-touch events are only available for the generic channels. Unless
+          otherwise specified, default values are a function of the channel and
+          the device's RUI.
+
+        properties:
+          azoteq,thresh:
+            $ref: /schemas/types.yaml#/definitions/uint32
+            minimum: 0
+            maximum: 255
+            description: Specifies the threshold for the event.
+
+          azoteq,hyst:
+            $ref: /schemas/types.yaml#/definitions/uint32
+            minimum: 0
+            maximum: 15
+            description:
+              Specifies the hysteresis for the event (touch and deep-touch
+              events only).
+
+          linux,code:
+            $ref: /schemas/types.yaml#/definitions/uint32
+            description: Numeric key or switch code associated with the event.
+
+          linux,input-type:
+            $ref: /schemas/types.yaml#/definitions/uint32
+            enum: [1, 5]
+            description:
+              Specifies whether the event is to be interpreted as a key (1) or
+              a switch (5). By default, Hall-channel events are interpreted as
+              switches and all others are interpreted as keys.
+
+        dependencies:
+          linux,input-type: ["linux,code"]
+
+        additionalProperties: false
+
+    dependencies:
+      azoteq,assoc-weight: ["azoteq,assoc-select"]
+
+    additionalProperties: false
+
+  "^trackpad-3x[2-3]$":
+    type: object
+    description:
+      Represents all channels associated with the trackpad. The channels are
+      collectively active if the trackpad is defined and inactive otherwise.
+
+    properties:
+      azoteq,ati-exclude:
+        type: boolean
+        description:
+          Prevents the trackpad channels from participating in an ATI event
+          that is manually triggered during initialization.
+
+      azoteq,reseed-disable:
+        type: boolean
+        description:
+          Prevents the trackpad channels from being reseeded if the long-term
+          average timeout (defined in 'azoteq,timeout-lta') expires.
+
+      azoteq,meas-cap-decrease:
+        type: boolean
+        description:
+          Decreases the internal measurement capacitance from 60 pF to 15 pF.
+
+      azoteq,rx-inactive:
+        $ref: /schemas/types.yaml#/definitions/uint32
+        enum: [0, 1]
+        default: 0
+        description: |
+          Specifies how inactive CRX pins are to be terminated as follows:
+          0: VSS
+          1: Floating
+
+      azoteq,linearize:
+        type: boolean
+        description: Inverts the polarity of the trackpad's touch state.
+
+      azoteq,dual-direction:
+        type: boolean
+        description:
+          Specifies that the trackpad's long-term averages are to freeze in
+          the presence of either increasing or decreasing counts, thereby
+          permitting events to be reported in either direction.
+
+      azoteq,filt-disable:
+        type: boolean
+        description: Disables raw count filtering for the trackpad channels.
+
+      azoteq,ati-mode:
+        $ref: /schemas/types.yaml#/definitions/uint32
+        enum: [0, 1, 2, 3]
+        default: 0
+        description: |
+          Specifies the trackpad's ATI mode as follows:
+          0: Disabled
+          1: Semi-partial
+          2: Partial
+          3: Full
+
+      azoteq,ati-base:
+        $ref: /schemas/types.yaml#/definitions/uint32-array
+        minItems: 6
+        maxItems: 9
+        items:
+          minimum: 45
+          maximum: 300
+        default: [45, 45, 45, 45, 45, 45, 45, 45, 45]
+        description: Specifies each individual trackpad channel's ATI base.
+
+      azoteq,ati-target:
+        $ref: /schemas/types.yaml#/definitions/uint32
+        multipleOf: 32
+        minimum: 0
+        maximum: 2016
+        default: 0
+        description: Specifies the trackpad's ATI target.
+
+      azoteq,cct-increase:
+        $ref: /schemas/types.yaml#/definitions/uint32
+        minimum: 0
+        maximum: 4
+        default: 0
+        description:
+          Specifies the degree to which the trackpad's charge cycle time is to
+          be increased, with 0 representing no increase.
+
+      azoteq,proj-bias:
+        $ref: /schemas/types.yaml#/definitions/uint32
+        enum: [0, 1, 2, 3]
+        default: 0
+        description: |
+          Specifies the bias current applied during projected-capacitance
+          sensing as follows:
+          0: 2.5 uA
+          1: 5 uA
+          2: 10 uA
+          3: 20 uA
+
+      azoteq,sense-freq:
+        $ref: /schemas/types.yaml#/definitions/uint32
+        enum: [0, 1, 2, 3]
+        default: 0
+        description: |
+          Specifies the trackpad's sensing frequency as follows (parenthesized
+          numbers represent the frequency if 'azoteq,clk-div' is present):
+          0: 4 MHz (1 MHz)
+          1: 2 MHz (500 kHz)
+          2: 1 MHz (250 kHz)
+          3: 500 kHz (125 kHz)
+
+      azoteq,ati-band-tighten:
+        type: boolean
+        description:
+          Tightens the ATI band from 1/8 to 1/16 of the desired target.
+
+      azoteq,thresh:
+        $ref: /schemas/types.yaml#/definitions/uint32-array
+        minItems: 6
+        maxItems: 9
+        items:
+          minimum: 0
+          maximum: 255
+        default: [0, 0, 0, 0, 0, 0, 0, 0, 0]
+        description:
+          Specifies each individual trackpad channel's touch threshold.
+
+      azoteq,hyst:
+        $ref: /schemas/types.yaml#/definitions/uint32
+        minimum: 0
+        maximum: 15
+        default: 0
+        description: Specifies the trackpad's touch hysteresis.
+
+      azoteq,lta-update:
+        $ref: /schemas/types.yaml#/definitions/uint32
+        enum: [0, 1, 2, 3, 4, 5, 6, 7]
+        default: 0
+        description: |
+          Specifies the update rate of the trackpad's long-term average during
+          ultra-low-power mode as follows:
+          0: 2
+          1: 4
+          2: 8
+          3: 16
+          4: 32
+          5: 64
+          6: 128
+          7: 255
+
+      azoteq,filt-str-trackpad:
+        $ref: /schemas/types.yaml#/definitions/uint32
+        enum: [0, 1, 2, 3]
+        default: 0
+        description: Specifies the trackpad coordinate filter strength.
+
+      azoteq,filt-str-np-cnt:
+        $ref: /schemas/types.yaml#/definitions/uint32
+        enum: [0, 1, 2, 3]
+        default: 0
+        description:
+          Specifies the raw count filter strength during normal-power mode.
+
+      azoteq,filt-str-lp-cnt:
+        $ref: /schemas/types.yaml#/definitions/uint32
+        enum: [0, 1, 2, 3]
+        default: 0
+        description:
+          Specifies the raw count filter strength during low-power mode.
+
+      linux,keycodes:
+        $ref: /schemas/types.yaml#/definitions/uint32-array
+        minItems: 1
+        maxItems: 6
+        description: |
+          Specifies the numeric keycodes associated with each available gesture
+          in the following order (enter 0 for unused gestures):
+          0: Positive flick or swipe in X direction
+          1: Negative flick or swipe in X direction
+          2: Positive flick or swipe in Y direction
+          3: Negative flick or swipe in Y direction
+          4: Tap
+          5: Hold
+
+      azoteq,gesture-swipe:
+        type: boolean
+        description:
+          Directs the device to interpret axial gestures as a swipe (finger
+          remains on trackpad) instead of a flick (finger leaves trackpad).
+
+      azoteq,timeout-tap-ms:
+        multipleOf: 16
+        minimum: 0
+        maximum: 4080
+        default: 0
+        description:
+          Specifies the length of time (in ms) within which a trackpad touch
+          must be released in order to be interpreted as a tap.
+
+      azoteq,timeout-swipe-ms:
+        multipleOf: 16
+        minimum: 0
+        maximum: 4080
+        default: 0
+        description:
+          Specifies the length of time (in ms) within which an axial gesture
+          must be completed in order to be interpreted as a flick or swipe.
+
+      azoteq,thresh-swipe:
+        $ref: /schemas/types.yaml#/definitions/uint32
+        minimum: 0
+        maximum: 255
+        default: 0
+        description:
+          Specifies the number of points across which an axial gesture must
+          travel in order to be interpreted as a flick or swipe.
+
+    dependencies:
+      azoteq,gesture-swipe: ["linux,keycodes"]
+      azoteq,timeout-tap-ms: ["linux,keycodes"]
+      azoteq,timeout-swipe-ms: ["linux,keycodes"]
+      azoteq,thresh-swipe: ["linux,keycodes"]
+
+    additionalProperties: false
+
+required:
+  - compatible
+  - reg
+  - interrupts
+  - "#address-cells"
+  - "#size-cells"
+
+additionalProperties: false
+
+examples:
+  - |
+    #include <dt-bindings/input/input.h>
+    #include <dt-bindings/interrupt-controller/irq.h>
+
+    i2c {
+            #address-cells = <1>;
+            #size-cells = <0>;
+
+            iqs626a@44 {
+                    #address-cells = <1>;
+                    #size-cells = <0>;
+
+                    compatible = "azoteq,iqs626a";
+                    reg = <0x44>;
+                    interrupt-parent = <&gpio>;
+                    interrupts = <17 IRQ_TYPE_LEVEL_LOW>;
+
+                    azoteq,rate-np-ms = <16>;
+                    azoteq,rate-lp-ms = <160>;
+
+                    azoteq,timeout-pwr-ms = <2560>;
+                    azoteq,timeout-lta-ms = <32768>;
+
+                    ulp-0 {
+                            azoteq,meas-cap-decrease;
+
+                            azoteq,ati-base = <75>;
+                            azoteq,ati-target = <1024>;
+
+                            azoteq,rx-enable = <2>, <3>, <4>,
+                                               <5>, <6>, <7>;
+
+                            event-prox {
+                                    linux,code = <KEY_POWER>;
+                            };
+                    };
+
+                    trackpad-3x3 {
+                            azoteq,filt-str-np-cnt = <1>;
+                            azoteq,filt-str-lp-cnt = <1>;
+
+                            azoteq,hyst = <4>;
+                            azoteq,thresh = <35>, <40>, <40>,
+                                            <38>, <33>, <38>,
+                                            <35>, <35>, <35>;
+
+                            azoteq,ati-mode = <3>;
+                            azoteq,ati-base = <195>, <195>, <195>,
+                                              <195>, <195>, <195>,
+                                              <195>, <195>, <195>;
+                            azoteq,ati-target = <512>;
+
+                            azoteq,proj-bias = <1>;
+                            azoteq,sense-freq = <2>;
+
+                            linux,keycodes = <KEY_VOLUMEUP>,
+                                             <KEY_VOLUMEDOWN>,
+                                             <KEY_NEXTSONG>,
+                                             <KEY_PREVIOUSSONG>,
+                                             <KEY_PLAYPAUSE>,
+                                             <KEY_STOPCD>;
+
+                            azoteq,gesture-swipe;
+                            azoteq,timeout-swipe-ms = <800>;
+                            azoteq,timeout-tap-ms = <400>;
+                            azoteq,thresh-swipe = <40>;
+                    };
+
+                    /*
+                     * Preserve the default register settings for
+                     * the temperature-tracking channel leveraged
+                     * by reset user interface (RUI) 1.
+                     *
+                     * Scalar properties (e.g. ATI mode) are left
+                     * untouched by simply omitting them; boolean
+                     * properties must be specified explicitly as
+                     * needed.
+                     */
+                    generic-2 {
+                            azoteq,reseed-disable;
+                            azoteq,meas-cap-decrease;
+                            azoteq,dual-direction;
+                            azoteq,comp-disable;
+                            azoteq,static-enable;
+                    };
+
+                    hall {
+                            azoteq,reseed-disable;
+                            azoteq,meas-cap-decrease;
+
+                            event-touch {
+                                    linux,code = <SW_LID>;
+                            };
+                    };
+            };
+    };
+
+...
diff --git a/Documentation/devicetree/bindings/input/touchscreen/azoteq,iqs5xx.yaml b/Documentation/devicetree/bindings/input/touchscreen/azoteq,iqs5xx.yaml
new file mode 100644 (file)
index 0000000..b5f3772
--- /dev/null
@@ -0,0 +1,75 @@
+# SPDX-License-Identifier: (GPL-2.0 OR BSD-2-Clause)
+%YAML 1.2
+---
+$id: http://devicetree.org/schemas/input/touchscreen/azoteq,iqs5xx.yaml#
+$schema: http://devicetree.org/meta-schemas/core.yaml#
+
+title: Azoteq IQS550/572/525 Trackpad/Touchscreen Controller
+
+maintainers:
+  - Jeff LaBundy <jeff@labundy.com>
+
+description: |
+  The Azoteq IQS550, IQS572 and IQS525 trackpad and touchscreen controllers
+  employ projected-capacitance sensing and can track up to five independent
+  contacts.
+
+  Link to datasheet: https://www.azoteq.com/
+
+allOf:
+  - $ref: touchscreen.yaml#
+
+properties:
+  compatible:
+    enum:
+      - azoteq,iqs550
+      - azoteq,iqs572
+      - azoteq,iqs525
+
+  reg:
+    maxItems: 1
+
+  interrupts:
+    maxItems: 1
+
+  reset-gpios:
+    maxItems: 1
+
+  wakeup-source: true
+
+  touchscreen-size-x: true
+  touchscreen-size-y: true
+  touchscreen-inverted-x: true
+  touchscreen-inverted-y: true
+  touchscreen-swapped-x-y: true
+
+required:
+  - compatible
+  - reg
+  - interrupts
+
+additionalProperties: false
+
+examples:
+  - |
+    #include <dt-bindings/gpio/gpio.h>
+    #include <dt-bindings/interrupt-controller/irq.h>
+
+    i2c {
+            #address-cells = <1>;
+            #size-cells = <0>;
+
+            touchscreen@74 {
+                    compatible = "azoteq,iqs550";
+                    reg = <0x74>;
+                    interrupt-parent = <&gpio>;
+                    interrupts = <27 IRQ_TYPE_LEVEL_HIGH>;
+                    reset-gpios = <&gpio 22 (GPIO_ACTIVE_LOW |
+                                             GPIO_PUSH_PULL)>;
+
+                    touchscreen-size-x = <800>;
+                    touchscreen-size-y = <480>;
+            };
+    };
+
+...
diff --git a/Documentation/devicetree/bindings/input/touchscreen/hycon,hy46xx.yaml b/Documentation/devicetree/bindings/input/touchscreen/hycon,hy46xx.yaml
new file mode 100644 (file)
index 0000000..942562f
--- /dev/null
@@ -0,0 +1,119 @@
+# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
+%YAML 1.2
+---
+$id: http://devicetree.org/schemas/input/touchscreen/hycon,hy46xx.yaml#
+$schema: http://devicetree.org/meta-schemas/core.yaml#
+
+title: Hycon HY46XX series touchscreen controller bindings
+
+description: |
+  There are 6 variants of the chip for various touch panel sizes and cover lens material
+   Glass: 0.3mm--4.0mm
+    PET/PMMA: 0.2mm--2.0mm
+    HY4613(B)-N048  < 6"
+    HY4614(B)-N068  7" .. 10.1"
+    HY4621-NS32  < 5"
+    HY4623-NS48  5.1" .. 7"
+   Glass: 0.3mm--8.0mm
+    PET/PMMA: 0.2mm--4.0mm
+    HY4633(B)-N048  < 6"
+    HY4635(B)-N048  < 7" .. 10.1"
+
+maintainers:
+  - Giulio Benetti <giulio.benetti@benettiengineering.com>
+
+allOf:
+  - $ref: touchscreen.yaml#
+
+properties:
+  compatible:
+    enum:
+      - hycon,hy4613
+      - hycon,hy4614
+      - hycon,hy4621
+      - hycon,hy4623
+      - hycon,hy4633
+      - hycon,hy4635
+
+  reg:
+    maxItems: 1
+
+  interrupts:
+    maxItems: 1
+
+  reset-gpios:
+    maxItems: 1
+
+  vcc-supply: true
+
+  hycon,threshold:
+    description: Allows setting the sensitivity in the range from 0 to 255.
+    $ref: /schemas/types.yaml#/definitions/uint32
+    minimum: 0
+    maximum: 255
+
+  hycon,glove-enable:
+    type: boolean
+    description: Allows enabling glove setting.
+
+  hycon,report-speed-hz:
+    description: Allows setting the report speed in Hertz.
+    minimum: 1
+    maximum: 255
+
+  hycon,noise-filter-enable:
+    type: boolean
+    description: Allows enabling power noise filter.
+
+  hycon,filter-data:
+    description: Allows setting how many samples throw before reporting touch
+                 in the range from 0 to 5.
+    $ref: /schemas/types.yaml#/definitions/uint32
+    minimum: 0
+    maximum: 5
+
+  hycon,gain:
+    description: Allows setting the sensitivity distance in the range from 0 to 5.
+    $ref: /schemas/types.yaml#/definitions/uint32
+    minimum: 0
+    maximum: 5
+
+  hycon,edge-offset:
+    description: Allows setting the edge compensation in the range from 0 to 16.
+    $ref: /schemas/types.yaml#/definitions/uint32
+    minimum: 0
+    maximum: 16
+
+  touchscreen-size-x: true
+  touchscreen-size-y: true
+  touchscreen-fuzz-x: true
+  touchscreen-fuzz-y: true
+  touchscreen-inverted-x: true
+  touchscreen-inverted-y: true
+  touchscreen-swapped-x-y: true
+  interrupt-controller: true
+
+additionalProperties: false
+
+required:
+  - compatible
+  - reg
+  - interrupts
+
+examples:
+  - |
+    #include <dt-bindings/gpio/gpio.h>
+    #include <dt-bindings/interrupt-controller/arm-gic.h>
+    i2c {
+      #address-cells = <1>;
+      #size-cells = <0>;
+      touchscreen@1c {
+        compatible = "hycon,hy4633";
+        reg = <0x1c>;
+        interrupt-parent = <&gpio2>;
+        interrupts = <5 IRQ_TYPE_EDGE_FALLING>;
+        reset-gpios = <&gpio2 6 GPIO_ACTIVE_LOW>;
+      };
+    };
+
+...
diff --git a/Documentation/devicetree/bindings/input/touchscreen/ilitek_ts_i2c.yaml b/Documentation/devicetree/bindings/input/touchscreen/ilitek_ts_i2c.yaml
new file mode 100644 (file)
index 0000000..a190e7b
--- /dev/null
@@ -0,0 +1,73 @@
+# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
+%YAML 1.2
+---
+$id: http://devicetree.org/schemas/input/touchscreen/ilitek_ts_i2c.yaml#
+$schema: http://devicetree.org/meta-schemas/core.yaml#
+
+title: Ilitek I2C Touchscreen Controller
+
+maintainers:
+  - Dmitry Torokhov <dmitry.torokhov@gmail.com>
+
+allOf:
+  - $ref: touchscreen.yaml#
+
+properties:
+  compatible:
+    enum:
+      - ilitek,ili2130
+      - ilitek,ili2131
+      - ilitek,ili2132
+      - ilitek,ili2316
+      - ilitek,ili2322
+      - ilitek,ili2323
+      - ilitek,ili2326
+      - ilitek,ili2520
+      - ilitek,ili2521
+
+  reg:
+    const: 0x41
+
+  interrupts:
+    maxItems: 1
+
+  reset-gpios:
+    maxItems: 1
+
+  wakeup-source:
+    type: boolean
+    description: touchscreen can be used as a wakeup source.
+
+  touchscreen-size-x: true
+  touchscreen-size-y: true
+  touchscreen-inverted-x: true
+  touchscreen-inverted-y: true
+  touchscreen-swapped-x-y: true
+
+additionalProperties: false
+
+required:
+  - compatible
+  - reg
+  - interrupts
+  - reset-gpios
+
+examples:
+  - |
+    #include <dt-bindings/interrupt-controller/irq.h>
+    #include <dt-bindings/gpio/gpio.h>
+    i2c {
+        #address-cells = <1>;
+        #size-cells = <0>;
+
+        touchscreen@41 {
+            compatible = "ilitek,ili2520";
+            reg = <0x41>;
+
+            interrupt-parent = <&gpio1>;
+            interrupts = <7 IRQ_TYPE_LEVEL_LOW>;
+            reset-gpios = <&gpio1 8 GPIO_ACTIVE_LOW>;
+            touchscreen-inverted-y;
+            wakeup-source;
+        };
+    };
diff --git a/Documentation/devicetree/bindings/input/touchscreen/iqs5xx.txt b/Documentation/devicetree/bindings/input/touchscreen/iqs5xx.txt
deleted file mode 100644 (file)
index efa0820..0000000
+++ /dev/null
@@ -1,80 +0,0 @@
-Azoteq IQS550/572/525 Trackpad/Touchscreen Controller
-
-Required properties:
-
-- compatible                   : Must be equal to one of the following:
-                                 "azoteq,iqs550"
-                                 "azoteq,iqs572"
-                                 "azoteq,iqs525"
-
-- reg                          : I2C slave address for the device.
-
-- interrupts                   : GPIO to which the device's active-high RDY
-                                 output is connected (see [0]).
-
-- reset-gpios                  : GPIO to which the device's active-low NRST
-                                 input is connected (see [1]).
-
-Optional properties:
-
-- touchscreen-min-x            : See [2].
-
-- touchscreen-min-y            : See [2].
-
-- touchscreen-size-x           : See [2]. If this property is omitted, the
-                                 maximum x-coordinate is specified by the
-                                 device's "X Resolution" register.
-
-- touchscreen-size-y           : See [2]. If this property is omitted, the
-                                 maximum y-coordinate is specified by the
-                                 device's "Y Resolution" register.
-
-- touchscreen-max-pressure     : See [2]. Pressure is expressed as the sum of
-                                 the deltas across all channels impacted by a
-                                 touch event. A channel's delta is calculated
-                                 as its count value minus a reference, where
-                                 the count value is inversely proportional to
-                                 the channel's capacitance.
-
-- touchscreen-fuzz-x           : See [2].
-
-- touchscreen-fuzz-y           : See [2].
-
-- touchscreen-fuzz-pressure    : See [2].
-
-- touchscreen-inverted-x       : See [2]. Inversion is applied relative to that
-                                 which may already be specified by the device's
-                                 FLIP_X and FLIP_Y register fields.
-
-- touchscreen-inverted-y       : See [2]. Inversion is applied relative to that
-                                 which may already be specified by the device's
-                                 FLIP_X and FLIP_Y register fields.
-
-- touchscreen-swapped-x-y      : See [2]. Swapping is applied relative to that
-                                 which may already be specified by the device's
-                                 SWITCH_XY_AXIS register field.
-
-[0]: Documentation/devicetree/bindings/interrupt-controller/interrupts.txt
-[1]: Documentation/devicetree/bindings/gpio/gpio.txt
-[2]: Documentation/devicetree/bindings/input/touchscreen/touchscreen.txt
-
-Example:
-
-       &i2c1 {
-               /* ... */
-
-               touchscreen@74 {
-                       compatible = "azoteq,iqs550";
-                       reg = <0x74>;
-                       interrupt-parent = <&gpio>;
-                       interrupts = <17 4>;
-                       reset-gpios = <&gpio 27 1>;
-
-                       touchscreen-size-x = <640>;
-                       touchscreen-size-y = <480>;
-
-                       touchscreen-max-pressure = <16000>;
-               };
-
-               /* ... */
-       };
diff --git a/Documentation/devicetree/bindings/input/touchscreen/melfas,mms114.yaml b/Documentation/devicetree/bindings/input/touchscreen/melfas,mms114.yaml
new file mode 100644 (file)
index 0000000..6236688
--- /dev/null
@@ -0,0 +1,87 @@
+# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
+%YAML 1.2
+---
+$id: http://devicetree.org/schemas/input/touchscreen/melfas,mms114.yaml#
+$schema: http://devicetree.org/meta-schemas/core.yaml#
+
+title: Melfas MMS114 family touchscreen controller bindings
+
+maintainers:
+  - Linus Walleij <linus.walleij@linaro.org>
+
+allOf:
+  - $ref: touchscreen.yaml#
+
+properties:
+  $nodename:
+    pattern: "^touchscreen(@.*)?$"
+
+  compatible:
+    items:
+      - enum:
+          - melfas,mms114
+          - melfas,mms134s
+          - melfas,mms136
+          - melfas,mms152
+          - melfas,mms345l
+
+  reg:
+    description: I2C address
+
+  clock-frequency:
+    description: I2C client clock frequency, defined for host
+    minimum: 100000
+    maximum: 400000
+
+  interrupts:
+    maxItems: 1
+
+  avdd-supply:
+    description: Analog power supply regulator on AVDD pin
+
+  vdd-supply:
+    description: Digital power supply regulator on VDD pin
+
+  touchscreen-size-x: true
+  touchscreen-size-y: true
+  touchscreen-fuzz-x: true
+  touchscreen-fuzz-y: true
+  touchscreen-fuzz-pressure: true
+  touchscreen-inverted-x: true
+  touchscreen-inverted-y: true
+  touchscreen-swapped-x-y: true
+  touchscreen-max-pressure: true
+
+additionalProperties: false
+
+required:
+  - compatible
+  - reg
+  - interrupts
+  - touchscreen-size-x
+  - touchscreen-size-y
+
+examples:
+  - |
+    #include <dt-bindings/interrupt-controller/irq.h>
+    i2c {
+      #address-cells = <1>;
+      #size-cells = <0>;
+      touchscreen@48 {
+        compatible = "melfas,mms114";
+        reg = <0x48>;
+        interrupt-parent = <&gpio>;
+        interrupts = <39 IRQ_TYPE_EDGE_FALLING>;
+        avdd-supply = <&ldo1_reg>;
+        vdd-supply = <&ldo2_reg>;
+        touchscreen-size-x = <720>;
+        touchscreen-size-y = <1280>;
+        touchscreen-fuzz-x = <10>;
+        touchscreen-fuzz-y = <10>;
+        touchscreen-fuzz-pressure = <10>;
+        touchscreen-inverted-x;
+        touchscreen-inverted-y;
+      };
+    };
+
+...
diff --git a/Documentation/devicetree/bindings/input/touchscreen/mms114.txt b/Documentation/devicetree/bindings/input/touchscreen/mms114.txt
deleted file mode 100644 (file)
index 707234c..0000000
+++ /dev/null
@@ -1,42 +0,0 @@
-* MELFAS MMS114/MMS152/MMS345L touchscreen controller
-
-Required properties:
-- compatible: should be one of:
-       - "melfas,mms114"
-       - "melfas,mms152"
-       - "melfas,mms345l"
-- reg: I2C address of the chip
-- interrupts: interrupt to which the chip is connected
-- touchscreen-size-x: See [1]
-- touchscreen-size-y: See [1]
-
-Optional properties:
-- touchscreen-fuzz-x: See [1]
-- touchscreen-fuzz-y: See [1]
-- touchscreen-fuzz-pressure: See [1]
-- touchscreen-inverted-x: See [1]
-- touchscreen-inverted-y: See [1]
-- touchscreen-swapped-x-y: See [1]
-
-[1]: Documentation/devicetree/bindings/input/touchscreen/touchscreen.txt
-
-Example:
-
-       i2c@00000000 {
-               /* ... */
-
-               touchscreen@48 {
-                       compatible = "melfas,mms114";
-                       reg = <0x48>;
-                       interrupts = <39 0>;
-                       touchscreen-size-x = <720>;
-                       touchscreen-size-y = <1280>;
-                       touchscreen-fuzz-x = <10>;
-                       touchscreen-fuzz-y = <10>;
-                       touchscreen-fuzz-pressure = <10>;
-                       touchscreen-inverted-x;
-                       touchscreen-inverted-y;
-               };
-
-               /* ... */
-       };
diff --git a/Documentation/devicetree/bindings/input/touchscreen/mstar,msg2638.yaml b/Documentation/devicetree/bindings/input/touchscreen/mstar,msg2638.yaml
new file mode 100644 (file)
index 0000000..3a42c23
--- /dev/null
@@ -0,0 +1,69 @@
+# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
+%YAML 1.2
+---
+$id: http://devicetree.org/schemas/input/touchscreen/mstar,msg2638.yaml#
+$schema: http://devicetree.org/meta-schemas/core.yaml#
+
+title: MStar msg2638 touchscreen controller Bindings
+
+maintainers:
+  - Vincent Knecht <vincent.knecht@mailoo.org>
+
+allOf:
+  - $ref: touchscreen.yaml#
+
+properties:
+  compatible:
+    const: mstar,msg2638
+
+  reg:
+    const: 0x26
+
+  interrupts:
+    maxItems: 1
+
+  reset-gpios:
+    maxItems: 1
+
+  vdd-supply:
+    description: Power supply regulator for the chip
+
+  vddio-supply:
+    description: Power supply regulator for the I2C bus
+
+  touchscreen-size-x: true
+  touchscreen-size-y: true
+
+additionalProperties: false
+
+required:
+  - compatible
+  - reg
+  - interrupts
+  - reset-gpios
+  - touchscreen-size-x
+  - touchscreen-size-y
+
+examples:
+  - |
+    #include <dt-bindings/gpio/gpio.h>
+    #include <dt-bindings/interrupt-controller/irq.h>
+    i2c {
+      #address-cells = <1>;
+      #size-cells = <0>;
+      touchscreen@26 {
+        compatible = "mstar,msg2638";
+        reg = <0x26>;
+        interrupt-parent = <&msmgpio>;
+        interrupts = <13 IRQ_TYPE_EDGE_FALLING>;
+        reset-gpios = <&msmgpio 100 GPIO_ACTIVE_LOW>;
+        pinctrl-names = "default";
+        pinctrl-0 = <&ts_int_reset_default>;
+        vdd-supply = <&pm8916_l17>;
+        vddio-supply = <&pm8916_l5>;
+        touchscreen-size-x = <2048>;
+        touchscreen-size-y = <2048>;
+      };
+    };
+
+...
index 99a72a4..b868cef 100644 (file)
@@ -495,6 +495,8 @@ patternProperties:
     description: Shenzhen Hugsun Technology Co. Ltd.
   "^hwacom,.*":
     description: HwaCom Systems Inc.
+  "^hycon,.*":
+    description: Hycon Technology Corp.
   "^hydis,.*":
     description: Hydis Technologies
   "^hyundai,.*":
index 810ae02..5865748 100644 (file)
@@ -107,13 +107,17 @@ example below:
                },
        };
 
-       static const struct property_entry rotary_encoder_properties[] __initconst = {
+       static const struct property_entry rotary_encoder_properties[] = {
                PROPERTY_ENTRY_U32("rotary-encoder,steps-per-period", 24),
                PROPERTY_ENTRY_U32("linux,axis",                      ABS_X),
                PROPERTY_ENTRY_U32("rotary-encoder,relative_axis",    0),
                { },
        };
 
+       static const struct software_node rotary_encoder_node = {
+               .properties = rotary_encoder_properties,
+       };
+
        static struct platform_device rotary_encoder_device = {
                .name           = "rotary-encoder",
                .id             = 0,
@@ -122,7 +126,7 @@ example below:
        ...
 
        gpiod_add_lookup_table(&rotary_encoder_gpios);
-       device_add_properties(&rotary_encoder_device, rotary_encoder_properties);
+       device_add_software_node(&rotary_encoder_device.dev, &rotary_encoder_node);
        platform_device_register(&rotary_encoder_device);
 
        ...
index 65d200e..483ced0 100644 (file)
@@ -8388,6 +8388,13 @@ S:       Maintained
 F:     mm/hwpoison-inject.c
 F:     mm/memory-failure.c
 
+HYCON HY46XX TOUCHSCREEN SUPPORT
+M:     Giulio Benetti <giulio.benetti@benettiengineering.com>
+L:     linux-input@vger.kernel.org
+S:     Maintained
+F:     Documentation/devicetree/bindings/input/touchscreen/hycon,hy46xx.yaml
+F:     drivers/input/touchscreen/hycon-hy46xx.c
+
 HYGON PROCESSOR SUPPORT
 M:     Pu Wen <puwen@hygon.cn>
 L:     linux-kernel@vger.kernel.org
index d8f5310..037cc59 100644 (file)
@@ -7,6 +7,7 @@
 
 obj-$(CONFIG_INPUT)            += input-core.o
 input-core-y := input.o input-compat.o input-mt.o input-poller.o ff-core.o
+input-core-y += touchscreen.o
 
 obj-$(CONFIG_INPUT_FF_MEMLESS) += ff-memless.o
 obj-$(CONFIG_INPUT_SPARSEKMAP) += sparse-keymap.o
index 9f0d07d..d69d765 100644 (file)
@@ -268,6 +268,7 @@ static const struct xpad_device {
        { 0x1689, 0xfd00, "Razer Onza Tournament Edition", 0, XTYPE_XBOX360 },
        { 0x1689, 0xfd01, "Razer Onza Classic Edition", 0, XTYPE_XBOX360 },
        { 0x1689, 0xfe00, "Razer Sabertooth", 0, XTYPE_XBOX360 },
+       { 0x1949, 0x041a, "Amazon Game Controller", 0, XTYPE_XBOX360 },
        { 0x1bad, 0x0002, "Harmonix Rock Band Guitar", 0, XTYPE_XBOX360 },
        { 0x1bad, 0x0003, "Harmonix Rock Band Drumkit", MAP_DPAD_TO_BUTTONS, XTYPE_XBOX360 },
        { 0x1bad, 0x0130, "Ion Drum Rocker", MAP_DPAD_TO_BUTTONS, XTYPE_XBOX360 },
@@ -440,6 +441,7 @@ static const struct usb_device_id xpad_table[] = {
        XPAD_XBOX360_VENDOR(0x15e4),            /* Numark X-Box 360 controllers */
        XPAD_XBOX360_VENDOR(0x162e),            /* Joytech X-Box 360 controllers */
        XPAD_XBOX360_VENDOR(0x1689),            /* Razer Onza */
+       XPAD_XBOX360_VENDOR(0x1949),            /* Amazon controllers */
        XPAD_XBOX360_VENDOR(0x1bad),            /* Harminix Rock Band Guitar and Drums */
        XPAD_XBOX360_VENDOR(0x20d6),            /* PowerA Controllers */
        XPAD_XBOXONE_VENDOR(0x20d6),            /* PowerA Controllers */
index 77bac4d..8dbf1e6 100644 (file)
@@ -8,6 +8,7 @@
 
 #include <linux/module.h>
 
+#include <linux/hrtimer.h>
 #include <linux/init.h>
 #include <linux/fs.h>
 #include <linux/interrupt.h>
@@ -36,10 +37,11 @@ struct gpio_button_data {
 
        unsigned short *code;
 
-       struct timer_list release_timer;
+       struct hrtimer release_timer;
        unsigned int release_delay;     /* in msecs, for IRQ-only buttons */
 
        struct delayed_work work;
+       struct hrtimer debounce_timer;
        unsigned int software_debounce; /* in msecs, for GPIO-driven buttons */
 
        unsigned int irq;
@@ -48,6 +50,7 @@ struct gpio_button_data {
        bool disabled;
        bool key_pressed;
        bool suspended;
+       bool debounce_use_hrtimer;
 };
 
 struct gpio_keys_drvdata {
@@ -122,6 +125,18 @@ static const unsigned long *get_bm_events_by_type(struct input_dev *dev,
        return (type == EV_KEY) ? dev->keybit : dev->swbit;
 }
 
+static void gpio_keys_quiesce_key(void *data)
+{
+       struct gpio_button_data *bdata = data;
+
+       if (!bdata->gpiod)
+               hrtimer_cancel(&bdata->release_timer);
+       if (bdata->debounce_use_hrtimer)
+               hrtimer_cancel(&bdata->debounce_timer);
+       else
+               cancel_delayed_work_sync(&bdata->work);
+}
+
 /**
  * gpio_keys_disable_button() - disables given GPIO button
  * @bdata: button data for button to be disabled
@@ -142,12 +157,7 @@ static void gpio_keys_disable_button(struct gpio_button_data *bdata)
                 * Disable IRQ and associated timer/work structure.
                 */
                disable_irq(bdata->irq);
-
-               if (bdata->gpiod)
-                       cancel_delayed_work_sync(&bdata->work);
-               else
-                       del_timer_sync(&bdata->release_timer);
-
+               gpio_keys_quiesce_key(bdata);
                bdata->disabled = true;
        }
 }
@@ -360,7 +370,9 @@ static void gpio_keys_gpio_report_event(struct gpio_button_data *bdata)
        unsigned int type = button->type ?: EV_KEY;
        int state;
 
-       state = gpiod_get_value_cansleep(bdata->gpiod);
+       state = bdata->debounce_use_hrtimer ?
+                       gpiod_get_value(bdata->gpiod) :
+                       gpiod_get_value_cansleep(bdata->gpiod);
        if (state < 0) {
                dev_err(input->dev.parent,
                        "failed to get gpio state: %d\n", state);
@@ -373,7 +385,15 @@ static void gpio_keys_gpio_report_event(struct gpio_button_data *bdata)
        } else {
                input_event(input, type, *bdata->code, state);
        }
-       input_sync(input);
+}
+
+static void gpio_keys_debounce_event(struct gpio_button_data *bdata)
+{
+       gpio_keys_gpio_report_event(bdata);
+       input_sync(bdata->input);
+
+       if (bdata->button->wakeup)
+               pm_relax(bdata->input->dev.parent);
 }
 
 static void gpio_keys_gpio_work_func(struct work_struct *work)
@@ -381,10 +401,17 @@ static void gpio_keys_gpio_work_func(struct work_struct *work)
        struct gpio_button_data *bdata =
                container_of(work, struct gpio_button_data, work.work);
 
-       gpio_keys_gpio_report_event(bdata);
+       gpio_keys_debounce_event(bdata);
+}
 
-       if (bdata->button->wakeup)
-               pm_relax(bdata->input->dev.parent);
+static enum hrtimer_restart gpio_keys_debounce_timer(struct hrtimer *t)
+{
+       struct gpio_button_data *bdata =
+               container_of(t, struct gpio_button_data, debounce_timer);
+
+       gpio_keys_debounce_event(bdata);
+
+       return HRTIMER_NORESTART;
 }
 
 static irqreturn_t gpio_keys_gpio_isr(int irq, void *dev_id)
@@ -408,26 +435,33 @@ static irqreturn_t gpio_keys_gpio_isr(int irq, void *dev_id)
                }
        }
 
-       mod_delayed_work(system_wq,
-                        &bdata->work,
-                        msecs_to_jiffies(bdata->software_debounce));
+       if (bdata->debounce_use_hrtimer) {
+               hrtimer_start(&bdata->debounce_timer,
+                             ms_to_ktime(bdata->software_debounce),
+                             HRTIMER_MODE_REL);
+       } else {
+               mod_delayed_work(system_wq,
+                                &bdata->work,
+                                msecs_to_jiffies(bdata->software_debounce));
+       }
 
        return IRQ_HANDLED;
 }
 
-static void gpio_keys_irq_timer(struct timer_list *t)
+static enum hrtimer_restart gpio_keys_irq_timer(struct hrtimer *t)
 {
-       struct gpio_button_data *bdata = from_timer(bdata, t, release_timer);
+       struct gpio_button_data *bdata = container_of(t,
+                                                     struct gpio_button_data,
+                                                     release_timer);
        struct input_dev *input = bdata->input;
-       unsigned long flags;
 
-       spin_lock_irqsave(&bdata->lock, flags);
        if (bdata->key_pressed) {
                input_event(input, EV_KEY, *bdata->code, 0);
                input_sync(input);
                bdata->key_pressed = false;
        }
-       spin_unlock_irqrestore(&bdata->lock, flags);
+
+       return HRTIMER_NORESTART;
 }
 
 static irqreturn_t gpio_keys_irq_isr(int irq, void *dev_id)
@@ -457,23 +491,14 @@ static irqreturn_t gpio_keys_irq_isr(int irq, void *dev_id)
        }
 
        if (bdata->release_delay)
-               mod_timer(&bdata->release_timer,
-                       jiffies + msecs_to_jiffies(bdata->release_delay));
+               hrtimer_start(&bdata->release_timer,
+                             ms_to_ktime(bdata->release_delay),
+                             HRTIMER_MODE_REL_HARD);
 out:
        spin_unlock_irqrestore(&bdata->lock, flags);
        return IRQ_HANDLED;
 }
 
-static void gpio_keys_quiesce_key(void *data)
-{
-       struct gpio_button_data *bdata = data;
-
-       if (bdata->gpiod)
-               cancel_delayed_work_sync(&bdata->work);
-       else
-               del_timer_sync(&bdata->release_timer);
-}
-
 static int gpio_keys_setup_key(struct platform_device *pdev,
                                struct input_dev *input,
                                struct gpio_keys_drvdata *ddata,
@@ -543,6 +568,14 @@ static int gpio_keys_setup_key(struct platform_device *pdev,
                        if (error < 0)
                                bdata->software_debounce =
                                                button->debounce_interval;
+
+                       /*
+                        * If reading the GPIO won't sleep, we can use a
+                        * hrtimer instead of a standard timer for the software
+                        * debounce, to reduce the latency as much as possible.
+                        */
+                       bdata->debounce_use_hrtimer =
+                                       !gpiod_cansleep(bdata->gpiod);
                }
 
                if (button->irq) {
@@ -561,6 +594,10 @@ static int gpio_keys_setup_key(struct platform_device *pdev,
 
                INIT_DELAYED_WORK(&bdata->work, gpio_keys_gpio_work_func);
 
+               hrtimer_init(&bdata->debounce_timer,
+                            CLOCK_REALTIME, HRTIMER_MODE_REL);
+               bdata->debounce_timer.function = gpio_keys_debounce_timer;
+
                isr = gpio_keys_gpio_isr;
                irqflags = IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING;
 
@@ -595,7 +632,9 @@ static int gpio_keys_setup_key(struct platform_device *pdev,
                }
 
                bdata->release_delay = button->debounce_interval;
-               timer_setup(&bdata->release_timer, gpio_keys_irq_timer, 0);
+               hrtimer_init(&bdata->release_timer,
+                            CLOCK_REALTIME, HRTIMER_MODE_REL_HARD);
+               bdata->release_timer.function = gpio_keys_irq_timer;
 
                isr = gpio_keys_irq_isr;
                irqflags = 0;
index 1f5c9ea..ae93038 100644 (file)
@@ -408,27 +408,18 @@ open_err:
        return -EIO;
 }
 
-#ifdef CONFIG_OF
 static const struct of_device_id imx_keypad_of_match[] = {
        { .compatible = "fsl,imx21-kpp", },
        { /* sentinel */ }
 };
 MODULE_DEVICE_TABLE(of, imx_keypad_of_match);
-#endif
 
 static int imx_keypad_probe(struct platform_device *pdev)
 {
-       const struct matrix_keymap_data *keymap_data =
-                       dev_get_platdata(&pdev->dev);
        struct imx_keypad *keypad;
        struct input_dev *input_dev;
        int irq, error, i, row, col;
 
-       if (!keymap_data && !pdev->dev.of_node) {
-               dev_err(&pdev->dev, "no keymap defined\n");
-               return -EINVAL;
-       }
-
        irq = platform_get_irq(pdev, 0);
        if (irq < 0)
                return irq;
@@ -469,7 +460,7 @@ static int imx_keypad_probe(struct platform_device *pdev)
        input_dev->open = imx_keypad_open;
        input_dev->close = imx_keypad_close;
 
-       error = matrix_keypad_build_keymap(keymap_data, NULL,
+       error = matrix_keypad_build_keymap(NULL, NULL,
                                           MAX_MATRIX_KEY_ROWS,
                                           MAX_MATRIX_KEY_COLS,
                                           keypad->keycodes, input_dev);
@@ -582,7 +573,7 @@ static struct platform_driver imx_keypad_driver = {
        .driver         = {
                .name   = "imx-keypad",
                .pm     = &imx_kbd_pm_ops,
-               .of_match_table = of_match_ptr(imx_keypad_of_match),
+               .of_match_table = imx_keypad_of_match,
        },
        .probe          = imx_keypad_probe,
 };
index 9b0f966..2a97559 100644 (file)
@@ -274,7 +274,7 @@ static int tca6416_keypad_probe(struct i2c_client *client,
                error = request_threaded_irq(chip->irqnum, NULL,
                                             tca6416_keys_isr,
                                             IRQF_TRIGGER_FALLING |
-                                               IRQF_ONESHOT,
+                                            IRQF_ONESHOT | IRQF_NO_AUTOEN,
                                             "tca6416-keypad", chip);
                if (error) {
                        dev_dbg(&client->dev,
@@ -282,7 +282,6 @@ static int tca6416_keypad_probe(struct i2c_client *client,
                                chip->irqnum, error);
                        goto fail1;
                }
-               disable_irq(chip->irqnum);
        }
 
        error = input_register_device(input);
index 9671842..570fe18 100644 (file)
@@ -694,14 +694,13 @@ static int tegra_kbc_probe(struct platform_device *pdev)
        input_set_drvdata(kbc->idev, kbc);
 
        err = devm_request_irq(&pdev->dev, kbc->irq, tegra_kbc_isr,
-                              IRQF_TRIGGER_HIGH, pdev->name, kbc);
+                              IRQF_TRIGGER_HIGH | IRQF_NO_AUTOEN,
+                              pdev->name, kbc);
        if (err) {
                dev_err(&pdev->dev, "failed to request keyboard IRQ\n");
                return err;
        }
 
-       disable_irq(kbc->irq);
-
        err = input_register_device(kbc->idev);
        if (err) {
                dev_err(&pdev->dev, "failed to register input device\n");
index 7237dc4..498cde3 100644 (file)
@@ -763,6 +763,17 @@ config INPUT_IQS269A
          To compile this driver as a module, choose M here: the
          module will be called iqs269a.
 
+config INPUT_IQS626A
+       tristate "Azoteq IQS626A capacitive touch controller"
+       depends on I2C
+       select REGMAP_I2C
+       help
+         Say Y to enable support for the Azoteq IQS626A capacitive
+         touch controller.
+
+         To compile this driver as a module, choose M here: the
+         module will be called iqs626a.
+
 config INPUT_CMA3000
        tristate "VTI CMA3000 Tri-axis accelerometer"
        help
index 46db664..f593bee 100644 (file)
@@ -43,6 +43,7 @@ obj-$(CONFIG_INPUT_HISI_POWERKEY)     += hisi_powerkey.o
 obj-$(CONFIG_HP_SDC_RTC)               += hp_sdc_rtc.o
 obj-$(CONFIG_INPUT_IMS_PCU)            += ims-pcu.o
 obj-$(CONFIG_INPUT_IQS269A)            += iqs269a.o
+obj-$(CONFIG_INPUT_IQS626A)            += iqs626a.o
 obj-$(CONFIG_INPUT_IXP4XX_BEEPER)      += ixp4xx-beeper.o
 obj-$(CONFIG_INPUT_KEYSPAN_REMOTE)     += keyspan_remote.o
 obj-$(CONFIG_INPUT_KXTJ9)              += kxtj9.o
index 08b9b5c..81de8c4 100644 (file)
@@ -2018,7 +2018,6 @@ static int ims_pcu_probe(struct usb_interface *intf,
        }
 
        usb_set_intfdata(pcu->ctrl_intf, pcu);
-       usb_set_intfdata(pcu->data_intf, pcu);
 
        error = ims_pcu_buffers_alloc(pcu);
        if (error)
diff --git a/drivers/input/misc/iqs626a.c b/drivers/input/misc/iqs626a.c
new file mode 100644 (file)
index 0000000..d57e996
--- /dev/null
@@ -0,0 +1,1838 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * Azoteq IQS626A Capacitive Touch Controller
+ *
+ * Copyright (C) 2020 Jeff LaBundy <jeff@labundy.com>
+ *
+ * This driver registers up to 2 input devices: one representing capacitive or
+ * inductive keys as well as Hall-effect switches, and one for a trackpad that
+ * can express various gestures.
+ */
+
+#include <linux/bits.h>
+#include <linux/completion.h>
+#include <linux/delay.h>
+#include <linux/device.h>
+#include <linux/err.h>
+#include <linux/i2c.h>
+#include <linux/input.h>
+#include <linux/input/touchscreen.h>
+#include <linux/interrupt.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/of_device.h>
+#include <linux/property.h>
+#include <linux/regmap.h>
+#include <linux/slab.h>
+
+#define IQS626_VER_INFO                                0x00
+#define IQS626_VER_INFO_PROD_NUM               0x51
+
+#define IQS626_SYS_FLAGS                       0x02
+#define IQS626_SYS_FLAGS_SHOW_RESET            BIT(15)
+#define IQS626_SYS_FLAGS_IN_ATI                        BIT(12)
+#define IQS626_SYS_FLAGS_PWR_MODE_MASK         GENMASK(9, 8)
+#define IQS626_SYS_FLAGS_PWR_MODE_SHIFT                8
+
+#define IQS626_HALL_OUTPUT                     0x23
+
+#define IQS626_SYS_SETTINGS                    0x80
+#define IQS626_SYS_SETTINGS_CLK_DIV            BIT(15)
+#define IQS626_SYS_SETTINGS_ULP_AUTO           BIT(14)
+#define IQS626_SYS_SETTINGS_DIS_AUTO           BIT(13)
+#define IQS626_SYS_SETTINGS_PWR_MODE_MASK      GENMASK(12, 11)
+#define IQS626_SYS_SETTINGS_PWR_MODE_SHIFT     11
+#define IQS626_SYS_SETTINGS_PWR_MODE_MAX       3
+#define IQS626_SYS_SETTINGS_ULP_UPDATE_MASK    GENMASK(10, 8)
+#define IQS626_SYS_SETTINGS_ULP_UPDATE_SHIFT   8
+#define IQS626_SYS_SETTINGS_ULP_UPDATE_MAX     7
+#define IQS626_SYS_SETTINGS_EVENT_MODE         BIT(5)
+#define IQS626_SYS_SETTINGS_EVENT_MODE_LP      BIT(4)
+#define IQS626_SYS_SETTINGS_REDO_ATI           BIT(2)
+#define IQS626_SYS_SETTINGS_ACK_RESET          BIT(0)
+
+#define IQS626_MISC_A_ATI_BAND_DISABLE         BIT(7)
+#define IQS626_MISC_A_TPx_LTA_UPDATE_MASK      GENMASK(6, 4)
+#define IQS626_MISC_A_TPx_LTA_UPDATE_SHIFT     4
+#define IQS626_MISC_A_TPx_LTA_UPDATE_MAX       7
+#define IQS626_MISC_A_ATI_LP_ONLY              BIT(3)
+#define IQS626_MISC_A_GPIO3_SELECT_MASK                GENMASK(2, 0)
+#define IQS626_MISC_A_GPIO3_SELECT_MAX         7
+
+#define IQS626_EVENT_MASK_SYS                  BIT(6)
+#define IQS626_EVENT_MASK_GESTURE              BIT(3)
+#define IQS626_EVENT_MASK_DEEP                 BIT(2)
+#define IQS626_EVENT_MASK_TOUCH                        BIT(1)
+#define IQS626_EVENT_MASK_PROX                 BIT(0)
+
+#define IQS626_RATE_NP_MS_MAX                  255
+#define IQS626_RATE_LP_MS_MAX                  255
+#define IQS626_RATE_ULP_MS_MAX                 4080
+#define IQS626_TIMEOUT_PWR_MS_MAX              130560
+#define IQS626_TIMEOUT_LTA_MS_MAX              130560
+
+#define IQS626_MISC_B_RESEED_UI_SEL_MASK       GENMASK(7, 6)
+#define IQS626_MISC_B_RESEED_UI_SEL_SHIFT      6
+#define IQS626_MISC_B_RESEED_UI_SEL_MAX                3
+#define IQS626_MISC_B_THRESH_EXTEND            BIT(5)
+#define IQS626_MISC_B_TRACKING_UI_ENABLE       BIT(4)
+#define IQS626_MISC_B_TPx_SWIPE                        BIT(3)
+#define IQS626_MISC_B_RESEED_OFFSET            BIT(2)
+#define IQS626_MISC_B_FILT_STR_TPx             GENMASK(1, 0)
+
+#define IQS626_THRESH_SWIPE_MAX                        255
+#define IQS626_TIMEOUT_TAP_MS_MAX              4080
+#define IQS626_TIMEOUT_SWIPE_MS_MAX            4080
+
+#define IQS626_CHx_ENG_0_MEAS_CAP_SIZE         BIT(7)
+#define IQS626_CHx_ENG_0_RX_TERM_VSS           BIT(5)
+#define IQS626_CHx_ENG_0_LINEARIZE             BIT(4)
+#define IQS626_CHx_ENG_0_DUAL_DIR              BIT(3)
+#define IQS626_CHx_ENG_0_FILT_DISABLE          BIT(2)
+#define IQS626_CHx_ENG_0_ATI_MODE_MASK         GENMASK(1, 0)
+#define IQS626_CHx_ENG_0_ATI_MODE_MAX          3
+
+#define IQS626_CHx_ENG_1_CCT_HIGH_1            BIT(7)
+#define IQS626_CHx_ENG_1_CCT_HIGH_0            BIT(6)
+#define IQS626_CHx_ENG_1_PROJ_BIAS_MASK                GENMASK(5, 4)
+#define IQS626_CHx_ENG_1_PROJ_BIAS_SHIFT       4
+#define IQS626_CHx_ENG_1_PROJ_BIAS_MAX         3
+#define IQS626_CHx_ENG_1_CCT_ENABLE            BIT(3)
+#define IQS626_CHx_ENG_1_SENSE_FREQ_MASK       GENMASK(2, 1)
+#define IQS626_CHx_ENG_1_SENSE_FREQ_SHIFT      1
+#define IQS626_CHx_ENG_1_SENSE_FREQ_MAX                3
+#define IQS626_CHx_ENG_1_ATI_BAND_TIGHTEN      BIT(0)
+
+#define IQS626_CHx_ENG_2_LOCAL_CAP_MASK                GENMASK(7, 6)
+#define IQS626_CHx_ENG_2_LOCAL_CAP_SHIFT       6
+#define IQS626_CHx_ENG_2_LOCAL_CAP_MAX         3
+#define IQS626_CHx_ENG_2_LOCAL_CAP_ENABLE      BIT(5)
+#define IQS626_CHx_ENG_2_SENSE_MODE_MASK       GENMASK(3, 0)
+#define IQS626_CHx_ENG_2_SENSE_MODE_MAX                15
+
+#define IQS626_CHx_ENG_3_TX_FREQ_MASK          GENMASK(5, 4)
+#define IQS626_CHx_ENG_3_TX_FREQ_SHIFT         4
+#define IQS626_CHx_ENG_3_TX_FREQ_MAX           3
+#define IQS626_CHx_ENG_3_INV_LOGIC             BIT(0)
+
+#define IQS626_CHx_ENG_4_RX_TERM_VREG          BIT(6)
+#define IQS626_CHx_ENG_4_CCT_LOW_1             BIT(5)
+#define IQS626_CHx_ENG_4_CCT_LOW_0             BIT(4)
+#define IQS626_CHx_ENG_4_COMP_DISABLE          BIT(1)
+#define IQS626_CHx_ENG_4_STATIC_ENABLE         BIT(0)
+
+#define IQS626_TPx_ATI_BASE_MIN                        45
+#define IQS626_TPx_ATI_BASE_MAX                        300
+#define IQS626_CHx_ATI_BASE_MASK               GENMASK(7, 6)
+#define IQS626_CHx_ATI_BASE_75                 0x00
+#define IQS626_CHx_ATI_BASE_100                        0x40
+#define IQS626_CHx_ATI_BASE_150                        0x80
+#define IQS626_CHx_ATI_BASE_200                        0xC0
+#define IQS626_CHx_ATI_TARGET_MASK             GENMASK(5, 0)
+#define IQS626_CHx_ATI_TARGET_MAX              2016
+
+#define IQS626_CHx_THRESH_MAX                  255
+#define IQS626_CHx_HYST_DEEP_MASK              GENMASK(7, 4)
+#define IQS626_CHx_HYST_DEEP_SHIFT             4
+#define IQS626_CHx_HYST_TOUCH_MASK             GENMASK(3, 0)
+#define IQS626_CHx_HYST_MAX                    15
+
+#define IQS626_FILT_STR_NP_TPx_MASK            GENMASK(7, 6)
+#define IQS626_FILT_STR_NP_TPx_SHIFT           6
+#define IQS626_FILT_STR_LP_TPx_MASK            GENMASK(5, 4)
+#define IQS626_FILT_STR_LP_TPx_SHIFT           4
+
+#define IQS626_FILT_STR_NP_CNT_MASK            GENMASK(7, 6)
+#define IQS626_FILT_STR_NP_CNT_SHIFT           6
+#define IQS626_FILT_STR_LP_CNT_MASK            GENMASK(5, 4)
+#define IQS626_FILT_STR_LP_CNT_SHIFT           4
+#define IQS626_FILT_STR_NP_LTA_MASK            GENMASK(3, 2)
+#define IQS626_FILT_STR_NP_LTA_SHIFT           2
+#define IQS626_FILT_STR_LP_LTA_MASK            GENMASK(1, 0)
+#define IQS626_FILT_STR_MAX                    3
+
+#define IQS626_ULP_PROJ_ENABLE                 BIT(4)
+#define IQS626_GEN_WEIGHT_MAX                  255
+
+#define IQS626_MAX_REG                         0xFF
+
+#define IQS626_NUM_CH_TP_3                     9
+#define IQS626_NUM_CH_TP_2                     6
+#define IQS626_NUM_CH_GEN                      3
+#define IQS626_NUM_CRx_TX                      8
+
+#define IQS626_PWR_MODE_POLL_SLEEP_US          50000
+#define IQS626_PWR_MODE_POLL_TIMEOUT_US                500000
+
+#define iqs626_irq_wait()                      usleep_range(350, 400)
+
+enum iqs626_ch_id {
+       IQS626_CH_ULP_0,
+       IQS626_CH_TP_2,
+       IQS626_CH_TP_3,
+       IQS626_CH_GEN_0,
+       IQS626_CH_GEN_1,
+       IQS626_CH_GEN_2,
+       IQS626_CH_HALL,
+};
+
+enum iqs626_rx_inactive {
+       IQS626_RX_INACTIVE_VSS,
+       IQS626_RX_INACTIVE_FLOAT,
+       IQS626_RX_INACTIVE_VREG,
+};
+
+enum iqs626_st_offs {
+       IQS626_ST_OFFS_PROX,
+       IQS626_ST_OFFS_DIR,
+       IQS626_ST_OFFS_TOUCH,
+       IQS626_ST_OFFS_DEEP,
+};
+
+enum iqs626_th_offs {
+       IQS626_TH_OFFS_PROX,
+       IQS626_TH_OFFS_TOUCH,
+       IQS626_TH_OFFS_DEEP,
+};
+
+enum iqs626_event_id {
+       IQS626_EVENT_PROX_DN,
+       IQS626_EVENT_PROX_UP,
+       IQS626_EVENT_TOUCH_DN,
+       IQS626_EVENT_TOUCH_UP,
+       IQS626_EVENT_DEEP_DN,
+       IQS626_EVENT_DEEP_UP,
+};
+
+enum iqs626_gesture_id {
+       IQS626_GESTURE_FLICK_X_POS,
+       IQS626_GESTURE_FLICK_X_NEG,
+       IQS626_GESTURE_FLICK_Y_POS,
+       IQS626_GESTURE_FLICK_Y_NEG,
+       IQS626_GESTURE_TAP,
+       IQS626_GESTURE_HOLD,
+       IQS626_NUM_GESTURES,
+};
+
+struct iqs626_event_desc {
+       const char *name;
+       enum iqs626_st_offs st_offs;
+       enum iqs626_th_offs th_offs;
+       bool dir_up;
+       u8 mask;
+};
+
+static const struct iqs626_event_desc iqs626_events[] = {
+       [IQS626_EVENT_PROX_DN] = {
+               .name = "event-prox",
+               .st_offs = IQS626_ST_OFFS_PROX,
+               .th_offs = IQS626_TH_OFFS_PROX,
+               .mask = IQS626_EVENT_MASK_PROX,
+       },
+       [IQS626_EVENT_PROX_UP] = {
+               .name = "event-prox-alt",
+               .st_offs = IQS626_ST_OFFS_PROX,
+               .th_offs = IQS626_TH_OFFS_PROX,
+               .dir_up = true,
+               .mask = IQS626_EVENT_MASK_PROX,
+       },
+       [IQS626_EVENT_TOUCH_DN] = {
+               .name = "event-touch",
+               .st_offs = IQS626_ST_OFFS_TOUCH,
+               .th_offs = IQS626_TH_OFFS_TOUCH,
+               .mask = IQS626_EVENT_MASK_TOUCH,
+       },
+       [IQS626_EVENT_TOUCH_UP] = {
+               .name = "event-touch-alt",
+               .st_offs = IQS626_ST_OFFS_TOUCH,
+               .th_offs = IQS626_TH_OFFS_TOUCH,
+               .dir_up = true,
+               .mask = IQS626_EVENT_MASK_TOUCH,
+       },
+       [IQS626_EVENT_DEEP_DN] = {
+               .name = "event-deep",
+               .st_offs = IQS626_ST_OFFS_DEEP,
+               .th_offs = IQS626_TH_OFFS_DEEP,
+               .mask = IQS626_EVENT_MASK_DEEP,
+       },
+       [IQS626_EVENT_DEEP_UP] = {
+               .name = "event-deep-alt",
+               .st_offs = IQS626_ST_OFFS_DEEP,
+               .th_offs = IQS626_TH_OFFS_DEEP,
+               .dir_up = true,
+               .mask = IQS626_EVENT_MASK_DEEP,
+       },
+};
+
+struct iqs626_ver_info {
+       u8 prod_num;
+       u8 sw_num;
+       u8 hw_num;
+       u8 padding;
+} __packed;
+
+struct iqs626_flags {
+       __be16 system;
+       u8 gesture;
+       u8 padding_a;
+       u8 states[4];
+       u8 ref_active;
+       u8 padding_b;
+       u8 comp_min;
+       u8 comp_max;
+       u8 trackpad_x;
+       u8 trackpad_y;
+} __packed;
+
+struct iqs626_ch_reg_ulp {
+       u8 thresh[2];
+       u8 hyst;
+       u8 filter;
+       u8 engine[2];
+       u8 ati_target;
+       u8 padding;
+       __be16 ati_comp;
+       u8 rx_enable;
+       u8 tx_enable;
+} __packed;
+
+struct iqs626_ch_reg_tp {
+       u8 thresh;
+       u8 ati_base;
+       __be16 ati_comp;
+} __packed;
+
+struct iqs626_tp_grp_reg {
+       u8 hyst;
+       u8 ati_target;
+       u8 engine[2];
+       struct iqs626_ch_reg_tp ch_reg_tp[IQS626_NUM_CH_TP_3];
+} __packed;
+
+struct iqs626_ch_reg_gen {
+       u8 thresh[3];
+       u8 padding;
+       u8 hyst;
+       u8 ati_target;
+       __be16 ati_comp;
+       u8 engine[5];
+       u8 filter;
+       u8 rx_enable;
+       u8 tx_enable;
+       u8 assoc_select;
+       u8 assoc_weight;
+} __packed;
+
+struct iqs626_ch_reg_hall {
+       u8 engine;
+       u8 thresh;
+       u8 hyst;
+       u8 ati_target;
+       __be16 ati_comp;
+} __packed;
+
+struct iqs626_sys_reg {
+       __be16 general;
+       u8 misc_a;
+       u8 event_mask;
+       u8 active;
+       u8 reseed;
+       u8 rate_np;
+       u8 rate_lp;
+       u8 rate_ulp;
+       u8 timeout_pwr;
+       u8 timeout_rdy;
+       u8 timeout_lta;
+       u8 misc_b;
+       u8 thresh_swipe;
+       u8 timeout_tap;
+       u8 timeout_swipe;
+       u8 redo_ati;
+       u8 padding;
+       struct iqs626_ch_reg_ulp ch_reg_ulp;
+       struct iqs626_tp_grp_reg tp_grp_reg;
+       struct iqs626_ch_reg_gen ch_reg_gen[IQS626_NUM_CH_GEN];
+       struct iqs626_ch_reg_hall ch_reg_hall;
+} __packed;
+
+struct iqs626_channel_desc {
+       const char *name;
+       int num_ch;
+       u8 active;
+       bool events[ARRAY_SIZE(iqs626_events)];
+};
+
+static const struct iqs626_channel_desc iqs626_channels[] = {
+       [IQS626_CH_ULP_0] = {
+               .name = "ulp-0",
+               .num_ch = 1,
+               .active = BIT(0),
+               .events = {
+                       [IQS626_EVENT_PROX_DN] = true,
+                       [IQS626_EVENT_PROX_UP] = true,
+                       [IQS626_EVENT_TOUCH_DN] = true,
+                       [IQS626_EVENT_TOUCH_UP] = true,
+               },
+       },
+       [IQS626_CH_TP_2] = {
+               .name = "trackpad-3x2",
+               .num_ch = IQS626_NUM_CH_TP_2,
+               .active = BIT(1),
+               .events = {
+                       [IQS626_EVENT_TOUCH_DN] = true,
+               },
+       },
+       [IQS626_CH_TP_3] = {
+               .name = "trackpad-3x3",
+               .num_ch = IQS626_NUM_CH_TP_3,
+               .active = BIT(2) | BIT(1),
+               .events = {
+                       [IQS626_EVENT_TOUCH_DN] = true,
+               },
+       },
+       [IQS626_CH_GEN_0] = {
+               .name = "generic-0",
+               .num_ch = 1,
+               .active = BIT(4),
+               .events = {
+                       [IQS626_EVENT_PROX_DN] = true,
+                       [IQS626_EVENT_PROX_UP] = true,
+                       [IQS626_EVENT_TOUCH_DN] = true,
+                       [IQS626_EVENT_TOUCH_UP] = true,
+                       [IQS626_EVENT_DEEP_DN] = true,
+                       [IQS626_EVENT_DEEP_UP] = true,
+               },
+       },
+       [IQS626_CH_GEN_1] = {
+               .name = "generic-1",
+               .num_ch = 1,
+               .active = BIT(5),
+               .events = {
+                       [IQS626_EVENT_PROX_DN] = true,
+                       [IQS626_EVENT_PROX_UP] = true,
+                       [IQS626_EVENT_TOUCH_DN] = true,
+                       [IQS626_EVENT_TOUCH_UP] = true,
+                       [IQS626_EVENT_DEEP_DN] = true,
+                       [IQS626_EVENT_DEEP_UP] = true,
+               },
+       },
+       [IQS626_CH_GEN_2] = {
+               .name = "generic-2",
+               .num_ch = 1,
+               .active = BIT(6),
+               .events = {
+                       [IQS626_EVENT_PROX_DN] = true,
+                       [IQS626_EVENT_PROX_UP] = true,
+                       [IQS626_EVENT_TOUCH_DN] = true,
+                       [IQS626_EVENT_TOUCH_UP] = true,
+                       [IQS626_EVENT_DEEP_DN] = true,
+                       [IQS626_EVENT_DEEP_UP] = true,
+               },
+       },
+       [IQS626_CH_HALL] = {
+               .name = "hall",
+               .num_ch = 1,
+               .active = BIT(7),
+               .events = {
+                       [IQS626_EVENT_TOUCH_DN] = true,
+                       [IQS626_EVENT_TOUCH_UP] = true,
+               },
+       },
+};
+
+struct iqs626_private {
+       struct i2c_client *client;
+       struct regmap *regmap;
+       struct iqs626_sys_reg sys_reg;
+       struct completion ati_done;
+       struct input_dev *keypad;
+       struct input_dev *trackpad;
+       struct touchscreen_properties prop;
+       unsigned int kp_type[ARRAY_SIZE(iqs626_channels)]
+                           [ARRAY_SIZE(iqs626_events)];
+       unsigned int kp_code[ARRAY_SIZE(iqs626_channels)]
+                           [ARRAY_SIZE(iqs626_events)];
+       unsigned int tp_code[IQS626_NUM_GESTURES];
+       unsigned int suspend_mode;
+};
+
+static int iqs626_parse_events(struct iqs626_private *iqs626,
+                              const struct fwnode_handle *ch_node,
+                              enum iqs626_ch_id ch_id)
+{
+       struct iqs626_sys_reg *sys_reg = &iqs626->sys_reg;
+       struct i2c_client *client = iqs626->client;
+       const struct fwnode_handle *ev_node;
+       const char *ev_name;
+       u8 *thresh, *hyst;
+       unsigned int thresh_tp[IQS626_NUM_CH_TP_3];
+       unsigned int val;
+       int num_ch = iqs626_channels[ch_id].num_ch;
+       int error, i, j;
+
+       switch (ch_id) {
+       case IQS626_CH_ULP_0:
+               thresh = sys_reg->ch_reg_ulp.thresh;
+               hyst = &sys_reg->ch_reg_ulp.hyst;
+               break;
+
+       case IQS626_CH_TP_2:
+       case IQS626_CH_TP_3:
+               thresh = &sys_reg->tp_grp_reg.ch_reg_tp[0].thresh;
+               hyst = &sys_reg->tp_grp_reg.hyst;
+               break;
+
+       case IQS626_CH_GEN_0:
+       case IQS626_CH_GEN_1:
+       case IQS626_CH_GEN_2:
+               i = ch_id - IQS626_CH_GEN_0;
+               thresh = sys_reg->ch_reg_gen[i].thresh;
+               hyst = &sys_reg->ch_reg_gen[i].hyst;
+               break;
+
+       case IQS626_CH_HALL:
+               thresh = &sys_reg->ch_reg_hall.thresh;
+               hyst = &sys_reg->ch_reg_hall.hyst;
+               break;
+
+       default:
+               return -EINVAL;
+       }
+
+       for (i = 0; i < ARRAY_SIZE(iqs626_events); i++) {
+               if (!iqs626_channels[ch_id].events[i])
+                       continue;
+
+               if (ch_id == IQS626_CH_TP_2 || ch_id == IQS626_CH_TP_3) {
+                       /*
+                        * Trackpad touch events are simply described under the
+                        * trackpad child node.
+                        */
+                       ev_node = ch_node;
+               } else {
+                       ev_name = iqs626_events[i].name;
+                       ev_node = fwnode_get_named_child_node(ch_node, ev_name);
+                       if (!ev_node)
+                               continue;
+
+                       if (!fwnode_property_read_u32(ev_node, "linux,code",
+                                                     &val)) {
+                               iqs626->kp_code[ch_id][i] = val;
+
+                               if (fwnode_property_read_u32(ev_node,
+                                                            "linux,input-type",
+                                                            &val)) {
+                                       if (ch_id == IQS626_CH_HALL)
+                                               val = EV_SW;
+                                       else
+                                               val = EV_KEY;
+                               }
+
+                               if (val != EV_KEY && val != EV_SW) {
+                                       dev_err(&client->dev,
+                                               "Invalid input type: %u\n",
+                                               val);
+                                       return -EINVAL;
+                               }
+
+                               iqs626->kp_type[ch_id][i] = val;
+
+                               sys_reg->event_mask &= ~iqs626_events[i].mask;
+                       }
+               }
+
+               if (!fwnode_property_read_u32(ev_node, "azoteq,hyst", &val)) {
+                       if (val > IQS626_CHx_HYST_MAX) {
+                               dev_err(&client->dev,
+                                       "Invalid %s channel hysteresis: %u\n",
+                                       fwnode_get_name(ch_node), val);
+                               return -EINVAL;
+                       }
+
+                       if (i == IQS626_EVENT_DEEP_DN ||
+                           i == IQS626_EVENT_DEEP_UP) {
+                               *hyst &= ~IQS626_CHx_HYST_DEEP_MASK;
+                               *hyst |= (val << IQS626_CHx_HYST_DEEP_SHIFT);
+                       } else if (i == IQS626_EVENT_TOUCH_DN ||
+                                  i == IQS626_EVENT_TOUCH_UP) {
+                               *hyst &= ~IQS626_CHx_HYST_TOUCH_MASK;
+                               *hyst |= val;
+                       }
+               }
+
+               if (ch_id != IQS626_CH_TP_2 && ch_id != IQS626_CH_TP_3 &&
+                   !fwnode_property_read_u32(ev_node, "azoteq,thresh", &val)) {
+                       if (val > IQS626_CHx_THRESH_MAX) {
+                               dev_err(&client->dev,
+                                       "Invalid %s channel threshold: %u\n",
+                                       fwnode_get_name(ch_node), val);
+                               return -EINVAL;
+                       }
+
+                       if (ch_id == IQS626_CH_HALL)
+                               *thresh = val;
+                       else
+                               *(thresh + iqs626_events[i].th_offs) = val;
+
+                       continue;
+               }
+
+               if (!fwnode_property_present(ev_node, "azoteq,thresh"))
+                       continue;
+
+               error = fwnode_property_read_u32_array(ev_node, "azoteq,thresh",
+                                                      thresh_tp, num_ch);
+               if (error) {
+                       dev_err(&client->dev,
+                               "Failed to read %s channel thresholds: %d\n",
+                               fwnode_get_name(ch_node), error);
+                       return error;
+               }
+
+               for (j = 0; j < num_ch; j++) {
+                       if (thresh_tp[j] > IQS626_CHx_THRESH_MAX) {
+                               dev_err(&client->dev,
+                                       "Invalid %s channel threshold: %u\n",
+                                       fwnode_get_name(ch_node), thresh_tp[j]);
+                               return -EINVAL;
+                       }
+
+                       sys_reg->tp_grp_reg.ch_reg_tp[j].thresh = thresh_tp[j];
+               }
+       }
+
+       return 0;
+}
+
+static int iqs626_parse_ati_target(struct iqs626_private *iqs626,
+                                  const struct fwnode_handle *ch_node,
+                                  enum iqs626_ch_id ch_id)
+{
+       struct iqs626_sys_reg *sys_reg = &iqs626->sys_reg;
+       struct i2c_client *client = iqs626->client;
+       unsigned int ati_base[IQS626_NUM_CH_TP_3];
+       unsigned int val;
+       u8 *ati_target;
+       int num_ch = iqs626_channels[ch_id].num_ch;
+       int error, i;
+
+       switch (ch_id) {
+       case IQS626_CH_ULP_0:
+               ati_target = &sys_reg->ch_reg_ulp.ati_target;
+               break;
+
+       case IQS626_CH_TP_2:
+       case IQS626_CH_TP_3:
+               ati_target = &sys_reg->tp_grp_reg.ati_target;
+               break;
+
+       case IQS626_CH_GEN_0:
+       case IQS626_CH_GEN_1:
+       case IQS626_CH_GEN_2:
+               i = ch_id - IQS626_CH_GEN_0;
+               ati_target = &sys_reg->ch_reg_gen[i].ati_target;
+               break;
+
+       case IQS626_CH_HALL:
+               ati_target = &sys_reg->ch_reg_hall.ati_target;
+               break;
+
+       default:
+               return -EINVAL;
+       }
+
+       if (!fwnode_property_read_u32(ch_node, "azoteq,ati-target", &val)) {
+               if (val > IQS626_CHx_ATI_TARGET_MAX) {
+                       dev_err(&client->dev,
+                               "Invalid %s channel ATI target: %u\n",
+                               fwnode_get_name(ch_node), val);
+                       return -EINVAL;
+               }
+
+               *ati_target &= ~IQS626_CHx_ATI_TARGET_MASK;
+               *ati_target |= (val / 32);
+       }
+
+       if (ch_id != IQS626_CH_TP_2 && ch_id != IQS626_CH_TP_3 &&
+           !fwnode_property_read_u32(ch_node, "azoteq,ati-base", &val)) {
+               switch (val) {
+               case 75:
+                       val = IQS626_CHx_ATI_BASE_75;
+                       break;
+
+               case 100:
+                       val = IQS626_CHx_ATI_BASE_100;
+                       break;
+
+               case 150:
+                       val = IQS626_CHx_ATI_BASE_150;
+                       break;
+
+               case 200:
+                       val = IQS626_CHx_ATI_BASE_200;
+                       break;
+
+               default:
+                       dev_err(&client->dev,
+                               "Invalid %s channel ATI base: %u\n",
+                               fwnode_get_name(ch_node), val);
+                       return -EINVAL;
+               }
+
+               *ati_target &= ~IQS626_CHx_ATI_BASE_MASK;
+               *ati_target |= val;
+
+               return 0;
+       }
+
+       if (!fwnode_property_present(ch_node, "azoteq,ati-base"))
+               return 0;
+
+       error = fwnode_property_read_u32_array(ch_node, "azoteq,ati-base",
+                                              ati_base, num_ch);
+       if (error) {
+               dev_err(&client->dev,
+                       "Failed to read %s channel ATI bases: %d\n",
+                       fwnode_get_name(ch_node), error);
+               return error;
+       }
+
+       for (i = 0; i < num_ch; i++) {
+               if (ati_base[i] < IQS626_TPx_ATI_BASE_MIN ||
+                   ati_base[i] > IQS626_TPx_ATI_BASE_MAX) {
+                       dev_err(&client->dev,
+                               "Invalid %s channel ATI base: %u\n",
+                               fwnode_get_name(ch_node), ati_base[i]);
+                       return -EINVAL;
+               }
+
+               ati_base[i] -= IQS626_TPx_ATI_BASE_MIN;
+               sys_reg->tp_grp_reg.ch_reg_tp[i].ati_base = ati_base[i];
+       }
+
+       return 0;
+}
+
+static int iqs626_parse_pins(struct iqs626_private *iqs626,
+                            const struct fwnode_handle *ch_node,
+                            const char *propname, u8 *enable)
+{
+       struct i2c_client *client = iqs626->client;
+       unsigned int val[IQS626_NUM_CRx_TX];
+       int error, count, i;
+
+       if (!fwnode_property_present(ch_node, propname))
+               return 0;
+
+       count = fwnode_property_count_u32(ch_node, propname);
+       if (count > IQS626_NUM_CRx_TX) {
+               dev_err(&client->dev,
+                       "Too many %s channel CRX/TX pins present\n",
+                       fwnode_get_name(ch_node));
+               return -EINVAL;
+       } else if (count < 0) {
+               dev_err(&client->dev,
+                       "Failed to count %s channel CRX/TX pins: %d\n",
+                       fwnode_get_name(ch_node), count);
+               return count;
+       }
+
+       error = fwnode_property_read_u32_array(ch_node, propname, val, count);
+       if (error) {
+               dev_err(&client->dev,
+                       "Failed to read %s channel CRX/TX pins: %d\n",
+                       fwnode_get_name(ch_node), error);
+               return error;
+       }
+
+       *enable = 0;
+
+       for (i = 0; i < count; i++) {
+               if (val[i] >= IQS626_NUM_CRx_TX) {
+                       dev_err(&client->dev,
+                               "Invalid %s channel CRX/TX pin: %u\n",
+                               fwnode_get_name(ch_node), val[i]);
+                       return -EINVAL;
+               }
+
+               *enable |= BIT(val[i]);
+       }
+
+       return 0;
+}
+
+static int iqs626_parse_trackpad(struct iqs626_private *iqs626,
+                                const struct fwnode_handle *ch_node)
+{
+       struct iqs626_sys_reg *sys_reg = &iqs626->sys_reg;
+       struct i2c_client *client = iqs626->client;
+       u8 *hyst = &sys_reg->tp_grp_reg.hyst;
+       unsigned int val;
+       int error, count;
+
+       if (!fwnode_property_read_u32(ch_node, "azoteq,lta-update", &val)) {
+               if (val > IQS626_MISC_A_TPx_LTA_UPDATE_MAX) {
+                       dev_err(&client->dev,
+                               "Invalid %s channel update rate: %u\n",
+                               fwnode_get_name(ch_node), val);
+                       return -EINVAL;
+               }
+
+               sys_reg->misc_a &= ~IQS626_MISC_A_TPx_LTA_UPDATE_MASK;
+               sys_reg->misc_a |= (val << IQS626_MISC_A_TPx_LTA_UPDATE_SHIFT);
+       }
+
+       if (!fwnode_property_read_u32(ch_node, "azoteq,filt-str-trackpad",
+                                     &val)) {
+               if (val > IQS626_FILT_STR_MAX) {
+                       dev_err(&client->dev,
+                               "Invalid %s channel filter strength: %u\n",
+                               fwnode_get_name(ch_node), val);
+                       return -EINVAL;
+               }
+
+               sys_reg->misc_b &= ~IQS626_MISC_B_FILT_STR_TPx;
+               sys_reg->misc_b |= val;
+       }
+
+       if (!fwnode_property_read_u32(ch_node, "azoteq,filt-str-np-cnt",
+                                     &val)) {
+               if (val > IQS626_FILT_STR_MAX) {
+                       dev_err(&client->dev,
+                               "Invalid %s channel filter strength: %u\n",
+                               fwnode_get_name(ch_node), val);
+                       return -EINVAL;
+               }
+
+               *hyst &= ~IQS626_FILT_STR_NP_TPx_MASK;
+               *hyst |= (val << IQS626_FILT_STR_NP_TPx_SHIFT);
+       }
+
+       if (!fwnode_property_read_u32(ch_node, "azoteq,filt-str-lp-cnt",
+                                     &val)) {
+               if (val > IQS626_FILT_STR_MAX) {
+                       dev_err(&client->dev,
+                               "Invalid %s channel filter strength: %u\n",
+                               fwnode_get_name(ch_node), val);
+                       return -EINVAL;
+               }
+
+               *hyst &= ~IQS626_FILT_STR_LP_TPx_MASK;
+               *hyst |= (val << IQS626_FILT_STR_LP_TPx_SHIFT);
+       }
+
+       if (!fwnode_property_present(ch_node, "linux,keycodes"))
+               return 0;
+
+       count = fwnode_property_count_u32(ch_node, "linux,keycodes");
+       if (count > IQS626_NUM_GESTURES) {
+               dev_err(&client->dev, "Too many keycodes present\n");
+               return -EINVAL;
+       } else if (count < 0) {
+               dev_err(&client->dev, "Failed to count keycodes: %d\n", count);
+               return count;
+       }
+
+       error = fwnode_property_read_u32_array(ch_node, "linux,keycodes",
+                                              iqs626->tp_code, count);
+       if (error) {
+               dev_err(&client->dev, "Failed to read keycodes: %d\n", error);
+               return error;
+       }
+
+       sys_reg->misc_b &= ~IQS626_MISC_B_TPx_SWIPE;
+       if (fwnode_property_present(ch_node, "azoteq,gesture-swipe"))
+               sys_reg->misc_b |= IQS626_MISC_B_TPx_SWIPE;
+
+       if (!fwnode_property_read_u32(ch_node, "azoteq,timeout-tap-ms",
+                                     &val)) {
+               if (val > IQS626_TIMEOUT_TAP_MS_MAX) {
+                       dev_err(&client->dev,
+                               "Invalid %s channel timeout: %u\n",
+                               fwnode_get_name(ch_node), val);
+                       return -EINVAL;
+               }
+
+               sys_reg->timeout_tap = val / 16;
+       }
+
+       if (!fwnode_property_read_u32(ch_node, "azoteq,timeout-swipe-ms",
+                                     &val)) {
+               if (val > IQS626_TIMEOUT_SWIPE_MS_MAX) {
+                       dev_err(&client->dev,
+                               "Invalid %s channel timeout: %u\n",
+                               fwnode_get_name(ch_node), val);
+                       return -EINVAL;
+               }
+
+               sys_reg->timeout_swipe = val / 16;
+       }
+
+       if (!fwnode_property_read_u32(ch_node, "azoteq,thresh-swipe",
+                                     &val)) {
+               if (val > IQS626_THRESH_SWIPE_MAX) {
+                       dev_err(&client->dev,
+                               "Invalid %s channel threshold: %u\n",
+                               fwnode_get_name(ch_node), val);
+                       return -EINVAL;
+               }
+
+               sys_reg->thresh_swipe = val;
+       }
+
+       sys_reg->event_mask &= ~IQS626_EVENT_MASK_GESTURE;
+
+       return 0;
+}
+
+static int iqs626_parse_channel(struct iqs626_private *iqs626,
+                               const struct fwnode_handle *ch_node,
+                               enum iqs626_ch_id ch_id)
+{
+       struct iqs626_sys_reg *sys_reg = &iqs626->sys_reg;
+       struct i2c_client *client = iqs626->client;
+       u8 *engine, *filter, *rx_enable, *tx_enable;
+       u8 *assoc_select, *assoc_weight;
+       unsigned int val;
+       int error, i;
+
+       switch (ch_id) {
+       case IQS626_CH_ULP_0:
+               engine = sys_reg->ch_reg_ulp.engine;
+               break;
+
+       case IQS626_CH_TP_2:
+       case IQS626_CH_TP_3:
+               engine = sys_reg->tp_grp_reg.engine;
+               break;
+
+       case IQS626_CH_GEN_0:
+       case IQS626_CH_GEN_1:
+       case IQS626_CH_GEN_2:
+               i = ch_id - IQS626_CH_GEN_0;
+               engine = sys_reg->ch_reg_gen[i].engine;
+               break;
+
+       case IQS626_CH_HALL:
+               engine = &sys_reg->ch_reg_hall.engine;
+               break;
+
+       default:
+               return -EINVAL;
+       }
+
+       *engine |= IQS626_CHx_ENG_0_MEAS_CAP_SIZE;
+       if (fwnode_property_present(ch_node, "azoteq,meas-cap-decrease"))
+               *engine &= ~IQS626_CHx_ENG_0_MEAS_CAP_SIZE;
+
+       *engine |= IQS626_CHx_ENG_0_RX_TERM_VSS;
+       if (!fwnode_property_read_u32(ch_node, "azoteq,rx-inactive", &val)) {
+               switch (val) {
+               case IQS626_RX_INACTIVE_VSS:
+                       break;
+
+               case IQS626_RX_INACTIVE_FLOAT:
+                       *engine &= ~IQS626_CHx_ENG_0_RX_TERM_VSS;
+                       if (ch_id == IQS626_CH_GEN_0 ||
+                           ch_id == IQS626_CH_GEN_1 ||
+                           ch_id == IQS626_CH_GEN_2)
+                               *(engine + 4) &= ~IQS626_CHx_ENG_4_RX_TERM_VREG;
+                       break;
+
+               case IQS626_RX_INACTIVE_VREG:
+                       if (ch_id == IQS626_CH_GEN_0 ||
+                           ch_id == IQS626_CH_GEN_1 ||
+                           ch_id == IQS626_CH_GEN_2) {
+                               *engine &= ~IQS626_CHx_ENG_0_RX_TERM_VSS;
+                               *(engine + 4) |= IQS626_CHx_ENG_4_RX_TERM_VREG;
+                               break;
+                       }
+                       fallthrough;
+
+               default:
+                       dev_err(&client->dev,
+                               "Invalid %s channel CRX pin termination: %u\n",
+                               fwnode_get_name(ch_node), val);
+                       return -EINVAL;
+               }
+       }
+
+       *engine &= ~IQS626_CHx_ENG_0_LINEARIZE;
+       if (fwnode_property_present(ch_node, "azoteq,linearize"))
+               *engine |= IQS626_CHx_ENG_0_LINEARIZE;
+
+       *engine &= ~IQS626_CHx_ENG_0_DUAL_DIR;
+       if (fwnode_property_present(ch_node, "azoteq,dual-direction"))
+               *engine |= IQS626_CHx_ENG_0_DUAL_DIR;
+
+       *engine &= ~IQS626_CHx_ENG_0_FILT_DISABLE;
+       if (fwnode_property_present(ch_node, "azoteq,filt-disable"))
+               *engine |= IQS626_CHx_ENG_0_FILT_DISABLE;
+
+       if (!fwnode_property_read_u32(ch_node, "azoteq,ati-mode", &val)) {
+               if (val > IQS626_CHx_ENG_0_ATI_MODE_MAX) {
+                       dev_err(&client->dev,
+                               "Invalid %s channel ATI mode: %u\n",
+                               fwnode_get_name(ch_node), val);
+                       return -EINVAL;
+               }
+
+               *engine &= ~IQS626_CHx_ENG_0_ATI_MODE_MASK;
+               *engine |= val;
+       }
+
+       if (ch_id == IQS626_CH_HALL)
+               return 0;
+
+       *(engine + 1) &= ~IQS626_CHx_ENG_1_CCT_ENABLE;
+       if (!fwnode_property_read_u32(ch_node, "azoteq,cct-increase",
+                                     &val) && val) {
+               unsigned int orig_val = val--;
+
+               /*
+                * In the case of the generic channels, the charge cycle time
+                * field doubles in size and straddles two separate registers.
+                */
+               if (ch_id == IQS626_CH_GEN_0 ||
+                   ch_id == IQS626_CH_GEN_1 ||
+                   ch_id == IQS626_CH_GEN_2) {
+                       *(engine + 4) &= ~IQS626_CHx_ENG_4_CCT_LOW_1;
+                       if (val & BIT(1))
+                               *(engine + 4) |= IQS626_CHx_ENG_4_CCT_LOW_1;
+
+                       *(engine + 4) &= ~IQS626_CHx_ENG_4_CCT_LOW_0;
+                       if (val & BIT(0))
+                               *(engine + 4) |= IQS626_CHx_ENG_4_CCT_LOW_0;
+
+                       val >>= 2;
+               }
+
+               if (val & ~GENMASK(1, 0)) {
+                       dev_err(&client->dev,
+                               "Invalid %s channel charge cycle time: %u\n",
+                               fwnode_get_name(ch_node), orig_val);
+                       return -EINVAL;
+               }
+
+               *(engine + 1) &= ~IQS626_CHx_ENG_1_CCT_HIGH_1;
+               if (val & BIT(1))
+                       *(engine + 1) |= IQS626_CHx_ENG_1_CCT_HIGH_1;
+
+               *(engine + 1) &= ~IQS626_CHx_ENG_1_CCT_HIGH_0;
+               if (val & BIT(0))
+                       *(engine + 1) |= IQS626_CHx_ENG_1_CCT_HIGH_0;
+
+               *(engine + 1) |= IQS626_CHx_ENG_1_CCT_ENABLE;
+       }
+
+       if (!fwnode_property_read_u32(ch_node, "azoteq,proj-bias", &val)) {
+               if (val > IQS626_CHx_ENG_1_PROJ_BIAS_MAX) {
+                       dev_err(&client->dev,
+                               "Invalid %s channel bias current: %u\n",
+                               fwnode_get_name(ch_node), val);
+                       return -EINVAL;
+               }
+
+               *(engine + 1) &= ~IQS626_CHx_ENG_1_PROJ_BIAS_MASK;
+               *(engine + 1) |= (val << IQS626_CHx_ENG_1_PROJ_BIAS_SHIFT);
+       }
+
+       if (!fwnode_property_read_u32(ch_node, "azoteq,sense-freq", &val)) {
+               if (val > IQS626_CHx_ENG_1_SENSE_FREQ_MAX) {
+                       dev_err(&client->dev,
+                               "Invalid %s channel sensing frequency: %u\n",
+                               fwnode_get_name(ch_node), val);
+                       return -EINVAL;
+               }
+
+               *(engine + 1) &= ~IQS626_CHx_ENG_1_SENSE_FREQ_MASK;
+               *(engine + 1) |= (val << IQS626_CHx_ENG_1_SENSE_FREQ_SHIFT);
+       }
+
+       *(engine + 1) &= ~IQS626_CHx_ENG_1_ATI_BAND_TIGHTEN;
+       if (fwnode_property_present(ch_node, "azoteq,ati-band-tighten"))
+               *(engine + 1) |= IQS626_CHx_ENG_1_ATI_BAND_TIGHTEN;
+
+       if (ch_id == IQS626_CH_TP_2 || ch_id == IQS626_CH_TP_3)
+               return iqs626_parse_trackpad(iqs626, ch_node);
+
+       if (ch_id == IQS626_CH_ULP_0) {
+               sys_reg->ch_reg_ulp.hyst &= ~IQS626_ULP_PROJ_ENABLE;
+               if (fwnode_property_present(ch_node, "azoteq,proj-enable"))
+                       sys_reg->ch_reg_ulp.hyst |= IQS626_ULP_PROJ_ENABLE;
+
+               filter = &sys_reg->ch_reg_ulp.filter;
+
+               rx_enable = &sys_reg->ch_reg_ulp.rx_enable;
+               tx_enable = &sys_reg->ch_reg_ulp.tx_enable;
+       } else {
+               i = ch_id - IQS626_CH_GEN_0;
+               filter = &sys_reg->ch_reg_gen[i].filter;
+
+               rx_enable = &sys_reg->ch_reg_gen[i].rx_enable;
+               tx_enable = &sys_reg->ch_reg_gen[i].tx_enable;
+       }
+
+       if (!fwnode_property_read_u32(ch_node, "azoteq,filt-str-np-cnt",
+                                     &val)) {
+               if (val > IQS626_FILT_STR_MAX) {
+                       dev_err(&client->dev,
+                               "Invalid %s channel filter strength: %u\n",
+                               fwnode_get_name(ch_node), val);
+                       return -EINVAL;
+               }
+
+               *filter &= ~IQS626_FILT_STR_NP_CNT_MASK;
+               *filter |= (val << IQS626_FILT_STR_NP_CNT_SHIFT);
+       }
+
+       if (!fwnode_property_read_u32(ch_node, "azoteq,filt-str-lp-cnt",
+                                     &val)) {
+               if (val > IQS626_FILT_STR_MAX) {
+                       dev_err(&client->dev,
+                               "Invalid %s channel filter strength: %u\n",
+                               fwnode_get_name(ch_node), val);
+                       return -EINVAL;
+               }
+
+               *filter &= ~IQS626_FILT_STR_LP_CNT_MASK;
+               *filter |= (val << IQS626_FILT_STR_LP_CNT_SHIFT);
+       }
+
+       if (!fwnode_property_read_u32(ch_node, "azoteq,filt-str-np-lta",
+                                     &val)) {
+               if (val > IQS626_FILT_STR_MAX) {
+                       dev_err(&client->dev,
+                               "Invalid %s channel filter strength: %u\n",
+                               fwnode_get_name(ch_node), val);
+                       return -EINVAL;
+               }
+
+               *filter &= ~IQS626_FILT_STR_NP_LTA_MASK;
+               *filter |= (val << IQS626_FILT_STR_NP_LTA_SHIFT);
+       }
+
+       if (!fwnode_property_read_u32(ch_node, "azoteq,filt-str-lp-lta",
+                                     &val)) {
+               if (val > IQS626_FILT_STR_MAX) {
+                       dev_err(&client->dev,
+                               "Invalid %s channel filter strength: %u\n",
+                               fwnode_get_name(ch_node), val);
+                       return -EINVAL;
+               }
+
+               *filter &= ~IQS626_FILT_STR_LP_LTA_MASK;
+               *filter |= val;
+       }
+
+       error = iqs626_parse_pins(iqs626, ch_node, "azoteq,rx-enable",
+                                 rx_enable);
+       if (error)
+               return error;
+
+       error = iqs626_parse_pins(iqs626, ch_node, "azoteq,tx-enable",
+                                 tx_enable);
+       if (error)
+               return error;
+
+       if (ch_id == IQS626_CH_ULP_0)
+               return 0;
+
+       *(engine + 2) &= ~IQS626_CHx_ENG_2_LOCAL_CAP_ENABLE;
+       if (!fwnode_property_read_u32(ch_node, "azoteq,local-cap-size",
+                                     &val) && val) {
+               unsigned int orig_val = val--;
+
+               if (val > IQS626_CHx_ENG_2_LOCAL_CAP_MAX) {
+                       dev_err(&client->dev,
+                               "Invalid %s channel local cap. size: %u\n",
+                               fwnode_get_name(ch_node), orig_val);
+                       return -EINVAL;
+               }
+
+               *(engine + 2) &= ~IQS626_CHx_ENG_2_LOCAL_CAP_MASK;
+               *(engine + 2) |= (val << IQS626_CHx_ENG_2_LOCAL_CAP_SHIFT);
+
+               *(engine + 2) |= IQS626_CHx_ENG_2_LOCAL_CAP_ENABLE;
+       }
+
+       if (!fwnode_property_read_u32(ch_node, "azoteq,sense-mode", &val)) {
+               if (val > IQS626_CHx_ENG_2_SENSE_MODE_MAX) {
+                       dev_err(&client->dev,
+                               "Invalid %s channel sensing mode: %u\n",
+                               fwnode_get_name(ch_node), val);
+                       return -EINVAL;
+               }
+
+               *(engine + 2) &= ~IQS626_CHx_ENG_2_SENSE_MODE_MASK;
+               *(engine + 2) |= val;
+       }
+
+       if (!fwnode_property_read_u32(ch_node, "azoteq,tx-freq", &val)) {
+               if (val > IQS626_CHx_ENG_3_TX_FREQ_MAX) {
+                       dev_err(&client->dev,
+                               "Invalid %s channel excitation frequency: %u\n",
+                               fwnode_get_name(ch_node), val);
+                       return -EINVAL;
+               }
+
+               *(engine + 3) &= ~IQS626_CHx_ENG_3_TX_FREQ_MASK;
+               *(engine + 3) |= (val << IQS626_CHx_ENG_3_TX_FREQ_SHIFT);
+       }
+
+       *(engine + 3) &= ~IQS626_CHx_ENG_3_INV_LOGIC;
+       if (fwnode_property_present(ch_node, "azoteq,invert-enable"))
+               *(engine + 3) |= IQS626_CHx_ENG_3_INV_LOGIC;
+
+       *(engine + 4) &= ~IQS626_CHx_ENG_4_COMP_DISABLE;
+       if (fwnode_property_present(ch_node, "azoteq,comp-disable"))
+               *(engine + 4) |= IQS626_CHx_ENG_4_COMP_DISABLE;
+
+       *(engine + 4) &= ~IQS626_CHx_ENG_4_STATIC_ENABLE;
+       if (fwnode_property_present(ch_node, "azoteq,static-enable"))
+               *(engine + 4) |= IQS626_CHx_ENG_4_STATIC_ENABLE;
+
+       i = ch_id - IQS626_CH_GEN_0;
+       assoc_select = &sys_reg->ch_reg_gen[i].assoc_select;
+       assoc_weight = &sys_reg->ch_reg_gen[i].assoc_weight;
+
+       *assoc_select = 0;
+       if (!fwnode_property_present(ch_node, "azoteq,assoc-select"))
+               return 0;
+
+       for (i = 0; i < ARRAY_SIZE(iqs626_channels); i++) {
+               if (fwnode_property_match_string(ch_node, "azoteq,assoc-select",
+                                                iqs626_channels[i].name) < 0)
+                       continue;
+
+               *assoc_select |= iqs626_channels[i].active;
+       }
+
+       if (fwnode_property_read_u32(ch_node, "azoteq,assoc-weight", &val))
+               return 0;
+
+       if (val > IQS626_GEN_WEIGHT_MAX) {
+               dev_err(&client->dev,
+                       "Invalid %s channel associated weight: %u\n",
+                       fwnode_get_name(ch_node), val);
+               return -EINVAL;
+       }
+
+       *assoc_weight = val;
+
+       return 0;
+}
+
+static int iqs626_parse_prop(struct iqs626_private *iqs626)
+{
+       struct iqs626_sys_reg *sys_reg = &iqs626->sys_reg;
+       struct i2c_client *client = iqs626->client;
+       struct fwnode_handle *ch_node;
+       unsigned int val;
+       int error, i;
+       u16 general;
+
+       if (!device_property_read_u32(&client->dev, "azoteq,suspend-mode",
+                                     &val)) {
+               if (val > IQS626_SYS_SETTINGS_PWR_MODE_MAX) {
+                       dev_err(&client->dev, "Invalid suspend mode: %u\n",
+                               val);
+                       return -EINVAL;
+               }
+
+               iqs626->suspend_mode = val;
+       }
+
+       error = regmap_raw_read(iqs626->regmap, IQS626_SYS_SETTINGS, sys_reg,
+                               sizeof(*sys_reg));
+       if (error)
+               return error;
+
+       general = be16_to_cpu(sys_reg->general);
+       general &= IQS626_SYS_SETTINGS_ULP_UPDATE_MASK;
+
+       if (device_property_present(&client->dev, "azoteq,clk-div"))
+               general |= IQS626_SYS_SETTINGS_CLK_DIV;
+
+       if (device_property_present(&client->dev, "azoteq,ulp-enable"))
+               general |= IQS626_SYS_SETTINGS_ULP_AUTO;
+
+       if (!device_property_read_u32(&client->dev, "azoteq,ulp-update",
+                                     &val)) {
+               if (val > IQS626_SYS_SETTINGS_ULP_UPDATE_MAX) {
+                       dev_err(&client->dev, "Invalid update rate: %u\n", val);
+                       return -EINVAL;
+               }
+
+               general &= ~IQS626_SYS_SETTINGS_ULP_UPDATE_MASK;
+               general |= (val << IQS626_SYS_SETTINGS_ULP_UPDATE_SHIFT);
+       }
+
+       sys_reg->misc_a &= ~IQS626_MISC_A_ATI_BAND_DISABLE;
+       if (device_property_present(&client->dev, "azoteq,ati-band-disable"))
+               sys_reg->misc_a |= IQS626_MISC_A_ATI_BAND_DISABLE;
+
+       sys_reg->misc_a &= ~IQS626_MISC_A_ATI_LP_ONLY;
+       if (device_property_present(&client->dev, "azoteq,ati-lp-only"))
+               sys_reg->misc_a |= IQS626_MISC_A_ATI_LP_ONLY;
+
+       if (!device_property_read_u32(&client->dev, "azoteq,gpio3-select",
+                                     &val)) {
+               if (val > IQS626_MISC_A_GPIO3_SELECT_MAX) {
+                       dev_err(&client->dev, "Invalid GPIO3 selection: %u\n",
+                               val);
+                       return -EINVAL;
+               }
+
+               sys_reg->misc_a &= ~IQS626_MISC_A_GPIO3_SELECT_MASK;
+               sys_reg->misc_a |= val;
+       }
+
+       if (!device_property_read_u32(&client->dev, "azoteq,reseed-select",
+                                     &val)) {
+               if (val > IQS626_MISC_B_RESEED_UI_SEL_MAX) {
+                       dev_err(&client->dev, "Invalid reseed selection: %u\n",
+                               val);
+                       return -EINVAL;
+               }
+
+               sys_reg->misc_b &= ~IQS626_MISC_B_RESEED_UI_SEL_MASK;
+               sys_reg->misc_b |= (val << IQS626_MISC_B_RESEED_UI_SEL_SHIFT);
+       }
+
+       sys_reg->misc_b &= ~IQS626_MISC_B_THRESH_EXTEND;
+       if (device_property_present(&client->dev, "azoteq,thresh-extend"))
+               sys_reg->misc_b |= IQS626_MISC_B_THRESH_EXTEND;
+
+       sys_reg->misc_b &= ~IQS626_MISC_B_TRACKING_UI_ENABLE;
+       if (device_property_present(&client->dev, "azoteq,tracking-enable"))
+               sys_reg->misc_b |= IQS626_MISC_B_TRACKING_UI_ENABLE;
+
+       sys_reg->misc_b &= ~IQS626_MISC_B_RESEED_OFFSET;
+       if (device_property_present(&client->dev, "azoteq,reseed-offset"))
+               sys_reg->misc_b |= IQS626_MISC_B_RESEED_OFFSET;
+
+       if (!device_property_read_u32(&client->dev, "azoteq,rate-np-ms",
+                                     &val)) {
+               if (val > IQS626_RATE_NP_MS_MAX) {
+                       dev_err(&client->dev, "Invalid report rate: %u\n", val);
+                       return -EINVAL;
+               }
+
+               sys_reg->rate_np = val;
+       }
+
+       if (!device_property_read_u32(&client->dev, "azoteq,rate-lp-ms",
+                                     &val)) {
+               if (val > IQS626_RATE_LP_MS_MAX) {
+                       dev_err(&client->dev, "Invalid report rate: %u\n", val);
+                       return -EINVAL;
+               }
+
+               sys_reg->rate_lp = val;
+       }
+
+       if (!device_property_read_u32(&client->dev, "azoteq,rate-ulp-ms",
+                                     &val)) {
+               if (val > IQS626_RATE_ULP_MS_MAX) {
+                       dev_err(&client->dev, "Invalid report rate: %u\n", val);
+                       return -EINVAL;
+               }
+
+               sys_reg->rate_ulp = val / 16;
+       }
+
+       if (!device_property_read_u32(&client->dev, "azoteq,timeout-pwr-ms",
+                                     &val)) {
+               if (val > IQS626_TIMEOUT_PWR_MS_MAX) {
+                       dev_err(&client->dev, "Invalid timeout: %u\n", val);
+                       return -EINVAL;
+               }
+
+               sys_reg->timeout_pwr = val / 512;
+       }
+
+       if (!device_property_read_u32(&client->dev, "azoteq,timeout-lta-ms",
+                                     &val)) {
+               if (val > IQS626_TIMEOUT_LTA_MS_MAX) {
+                       dev_err(&client->dev, "Invalid timeout: %u\n", val);
+                       return -EINVAL;
+               }
+
+               sys_reg->timeout_lta = val / 512;
+       }
+
+       sys_reg->event_mask = ~((u8)IQS626_EVENT_MASK_SYS);
+       sys_reg->redo_ati = 0;
+
+       sys_reg->reseed = 0;
+       sys_reg->active = 0;
+
+       for (i = 0; i < ARRAY_SIZE(iqs626_channels); i++) {
+               ch_node = device_get_named_child_node(&client->dev,
+                                                     iqs626_channels[i].name);
+               if (!ch_node)
+                       continue;
+
+               error = iqs626_parse_channel(iqs626, ch_node, i);
+               if (error)
+                       return error;
+
+               error = iqs626_parse_ati_target(iqs626, ch_node, i);
+               if (error)
+                       return error;
+
+               error = iqs626_parse_events(iqs626, ch_node, i);
+               if (error)
+                       return error;
+
+               if (!fwnode_property_present(ch_node, "azoteq,ati-exclude"))
+                       sys_reg->redo_ati |= iqs626_channels[i].active;
+
+               if (!fwnode_property_present(ch_node, "azoteq,reseed-disable"))
+                       sys_reg->reseed |= iqs626_channels[i].active;
+
+               sys_reg->active |= iqs626_channels[i].active;
+       }
+
+       general |= IQS626_SYS_SETTINGS_EVENT_MODE;
+
+       /*
+        * Enable streaming during normal-power mode if the trackpad is used to
+        * report raw coordinates instead of gestures. In that case, the device
+        * returns to event mode during low-power mode.
+        */
+       if (sys_reg->active & iqs626_channels[IQS626_CH_TP_2].active &&
+           sys_reg->event_mask & IQS626_EVENT_MASK_GESTURE)
+               general |= IQS626_SYS_SETTINGS_EVENT_MODE_LP;
+
+       general |= IQS626_SYS_SETTINGS_REDO_ATI;
+       general |= IQS626_SYS_SETTINGS_ACK_RESET;
+
+       sys_reg->general = cpu_to_be16(general);
+
+       error = regmap_raw_write(iqs626->regmap, IQS626_SYS_SETTINGS,
+                                &iqs626->sys_reg, sizeof(iqs626->sys_reg));
+       if (error)
+               return error;
+
+       iqs626_irq_wait();
+
+       return 0;
+}
+
+static int iqs626_input_init(struct iqs626_private *iqs626)
+{
+       struct iqs626_sys_reg *sys_reg = &iqs626->sys_reg;
+       struct i2c_client *client = iqs626->client;
+       int error, i, j;
+
+       iqs626->keypad = devm_input_allocate_device(&client->dev);
+       if (!iqs626->keypad)
+               return -ENOMEM;
+
+       iqs626->keypad->keycodemax = ARRAY_SIZE(iqs626->kp_code);
+       iqs626->keypad->keycode = iqs626->kp_code;
+       iqs626->keypad->keycodesize = sizeof(**iqs626->kp_code);
+
+       iqs626->keypad->name = "iqs626a_keypad";
+       iqs626->keypad->id.bustype = BUS_I2C;
+
+       for (i = 0; i < ARRAY_SIZE(iqs626_channels); i++) {
+               if (!(sys_reg->active & iqs626_channels[i].active))
+                       continue;
+
+               for (j = 0; j < ARRAY_SIZE(iqs626_events); j++) {
+                       if (!iqs626->kp_type[i][j])
+                               continue;
+
+                       input_set_capability(iqs626->keypad,
+                                            iqs626->kp_type[i][j],
+                                            iqs626->kp_code[i][j]);
+               }
+       }
+
+       if (!(sys_reg->active & iqs626_channels[IQS626_CH_TP_2].active))
+               return 0;
+
+       iqs626->trackpad = devm_input_allocate_device(&client->dev);
+       if (!iqs626->trackpad)
+               return -ENOMEM;
+
+       iqs626->trackpad->keycodemax = ARRAY_SIZE(iqs626->tp_code);
+       iqs626->trackpad->keycode = iqs626->tp_code;
+       iqs626->trackpad->keycodesize = sizeof(*iqs626->tp_code);
+
+       iqs626->trackpad->name = "iqs626a_trackpad";
+       iqs626->trackpad->id.bustype = BUS_I2C;
+
+       /*
+        * Present the trackpad as a traditional pointing device if no gestures
+        * have been mapped to a keycode.
+        */
+       if (sys_reg->event_mask & IQS626_EVENT_MASK_GESTURE) {
+               u8 tp_mask = iqs626_channels[IQS626_CH_TP_3].active;
+
+               input_set_capability(iqs626->trackpad, EV_KEY, BTN_TOUCH);
+               input_set_abs_params(iqs626->trackpad, ABS_Y, 0, 255, 0, 0);
+
+               if ((sys_reg->active & tp_mask) == tp_mask)
+                       input_set_abs_params(iqs626->trackpad,
+                                            ABS_X, 0, 255, 0, 0);
+               else
+                       input_set_abs_params(iqs626->trackpad,
+                                            ABS_X, 0, 128, 0, 0);
+
+               touchscreen_parse_properties(iqs626->trackpad, false,
+                                            &iqs626->prop);
+       } else {
+               for (i = 0; i < IQS626_NUM_GESTURES; i++)
+                       if (iqs626->tp_code[i] != KEY_RESERVED)
+                               input_set_capability(iqs626->trackpad, EV_KEY,
+                                                    iqs626->tp_code[i]);
+       }
+
+       error = input_register_device(iqs626->trackpad);
+       if (error)
+               dev_err(&client->dev, "Failed to register trackpad: %d\n",
+                       error);
+
+       return error;
+}
+
+static int iqs626_report(struct iqs626_private *iqs626)
+{
+       struct iqs626_sys_reg *sys_reg = &iqs626->sys_reg;
+       struct i2c_client *client = iqs626->client;
+       struct iqs626_flags flags;
+       __le16 hall_output;
+       int error, i, j;
+       u8 state;
+       u8 *dir_mask = &flags.states[IQS626_ST_OFFS_DIR];
+
+       error = regmap_raw_read(iqs626->regmap, IQS626_SYS_FLAGS, &flags,
+                               sizeof(flags));
+       if (error) {
+               dev_err(&client->dev, "Failed to read device status: %d\n",
+                       error);
+               return error;
+       }
+
+       /*
+        * The device resets itself if its own watchdog bites, which can happen
+        * in the event of an I2C communication error. In this case, the device
+        * asserts a SHOW_RESET interrupt and all registers must be restored.
+        */
+       if (be16_to_cpu(flags.system) & IQS626_SYS_FLAGS_SHOW_RESET) {
+               dev_err(&client->dev, "Unexpected device reset\n");
+
+               error = regmap_raw_write(iqs626->regmap, IQS626_SYS_SETTINGS,
+                                        sys_reg, sizeof(*sys_reg));
+               if (error)
+                       dev_err(&client->dev,
+                               "Failed to re-initialize device: %d\n", error);
+
+               return error;
+       }
+
+       if (be16_to_cpu(flags.system) & IQS626_SYS_FLAGS_IN_ATI)
+               return 0;
+
+       /*
+        * Unlike the ULP or generic channels, the Hall channel does not have a
+        * direction flag. Instead, the direction (i.e. magnet polarity) can be
+        * derived based on the sign of the 2's complement differential output.
+        */
+       if (sys_reg->active & iqs626_channels[IQS626_CH_HALL].active) {
+               error = regmap_raw_read(iqs626->regmap, IQS626_HALL_OUTPUT,
+                                       &hall_output, sizeof(hall_output));
+               if (error) {
+                       dev_err(&client->dev,
+                               "Failed to read Hall output: %d\n", error);
+                       return error;
+               }
+
+               *dir_mask &= ~iqs626_channels[IQS626_CH_HALL].active;
+               if (le16_to_cpu(hall_output) < 0x8000)
+                       *dir_mask |= iqs626_channels[IQS626_CH_HALL].active;
+       }
+
+       for (i = 0; i < ARRAY_SIZE(iqs626_channels); i++) {
+               if (!(sys_reg->active & iqs626_channels[i].active))
+                       continue;
+
+               for (j = 0; j < ARRAY_SIZE(iqs626_events); j++) {
+                       if (!iqs626->kp_type[i][j])
+                               continue;
+
+                       state = flags.states[iqs626_events[j].st_offs];
+                       state &= iqs626_events[j].dir_up ? *dir_mask
+                                                        : ~(*dir_mask);
+                       state &= iqs626_channels[i].active;
+
+                       input_event(iqs626->keypad, iqs626->kp_type[i][j],
+                                   iqs626->kp_code[i][j], !!state);
+               }
+       }
+
+       input_sync(iqs626->keypad);
+
+       /*
+        * The following completion signals that ATI has finished, any initial
+        * switch states have been reported and the keypad can be registered.
+        */
+       complete_all(&iqs626->ati_done);
+
+       if (!(sys_reg->active & iqs626_channels[IQS626_CH_TP_2].active))
+               return 0;
+
+       if (sys_reg->event_mask & IQS626_EVENT_MASK_GESTURE) {
+               state = flags.states[IQS626_ST_OFFS_TOUCH];
+               state &= iqs626_channels[IQS626_CH_TP_2].active;
+
+               input_report_key(iqs626->trackpad, BTN_TOUCH, state);
+
+               if (state)
+                       touchscreen_report_pos(iqs626->trackpad, &iqs626->prop,
+                                              flags.trackpad_x,
+                                              flags.trackpad_y, false);
+       } else {
+               for (i = 0; i < IQS626_NUM_GESTURES; i++)
+                       input_report_key(iqs626->trackpad, iqs626->tp_code[i],
+                                        flags.gesture & BIT(i));
+
+               if (flags.gesture & GENMASK(IQS626_GESTURE_TAP, 0)) {
+                       input_sync(iqs626->trackpad);
+
+                       /*
+                        * Momentary gestures are followed by a complementary
+                        * release cycle so as to emulate a full keystroke.
+                        */
+                       for (i = 0; i < IQS626_GESTURE_HOLD; i++)
+                               input_report_key(iqs626->trackpad,
+                                                iqs626->tp_code[i], 0);
+               }
+       }
+
+       input_sync(iqs626->trackpad);
+
+       return 0;
+}
+
+static irqreturn_t iqs626_irq(int irq, void *context)
+{
+       struct iqs626_private *iqs626 = context;
+
+       if (iqs626_report(iqs626))
+               return IRQ_NONE;
+
+       /*
+        * The device does not deassert its interrupt (RDY) pin until shortly
+        * after receiving an I2C stop condition; the following delay ensures
+        * the interrupt handler does not return before this time.
+        */
+       iqs626_irq_wait();
+
+       return IRQ_HANDLED;
+}
+
+static const struct regmap_config iqs626_regmap_config = {
+       .reg_bits = 8,
+       .val_bits = 16,
+       .max_register = IQS626_MAX_REG,
+};
+
+static int iqs626_probe(struct i2c_client *client)
+{
+       struct iqs626_ver_info ver_info;
+       struct iqs626_private *iqs626;
+       int error;
+
+       iqs626 = devm_kzalloc(&client->dev, sizeof(*iqs626), GFP_KERNEL);
+       if (!iqs626)
+               return -ENOMEM;
+
+       i2c_set_clientdata(client, iqs626);
+       iqs626->client = client;
+
+       iqs626->regmap = devm_regmap_init_i2c(client, &iqs626_regmap_config);
+       if (IS_ERR(iqs626->regmap)) {
+               error = PTR_ERR(iqs626->regmap);
+               dev_err(&client->dev, "Failed to initialize register map: %d\n",
+                       error);
+               return error;
+       }
+
+       init_completion(&iqs626->ati_done);
+
+       error = regmap_raw_read(iqs626->regmap, IQS626_VER_INFO, &ver_info,
+                               sizeof(ver_info));
+       if (error)
+               return error;
+
+       if (ver_info.prod_num != IQS626_VER_INFO_PROD_NUM) {
+               dev_err(&client->dev, "Unrecognized product number: 0x%02X\n",
+                       ver_info.prod_num);
+               return -EINVAL;
+       }
+
+       error = iqs626_parse_prop(iqs626);
+       if (error)
+               return error;
+
+       error = iqs626_input_init(iqs626);
+       if (error)
+               return error;
+
+       error = devm_request_threaded_irq(&client->dev, client->irq,
+                                         NULL, iqs626_irq, IRQF_ONESHOT,
+                                         client->name, iqs626);
+       if (error) {
+               dev_err(&client->dev, "Failed to request IRQ: %d\n", error);
+               return error;
+       }
+
+       if (!wait_for_completion_timeout(&iqs626->ati_done,
+                                        msecs_to_jiffies(2000))) {
+               dev_err(&client->dev, "Failed to complete ATI\n");
+               return -ETIMEDOUT;
+       }
+
+       /*
+        * The keypad may include one or more switches and is not registered
+        * until ATI is complete and the initial switch states are read.
+        */
+       error = input_register_device(iqs626->keypad);
+       if (error)
+               dev_err(&client->dev, "Failed to register keypad: %d\n", error);
+
+       return error;
+}
+
+static int __maybe_unused iqs626_suspend(struct device *dev)
+{
+       struct iqs626_private *iqs626 = dev_get_drvdata(dev);
+       struct i2c_client *client = iqs626->client;
+       unsigned int val;
+       int error;
+
+       if (!iqs626->suspend_mode)
+               return 0;
+
+       disable_irq(client->irq);
+
+       /*
+        * Automatic power mode switching must be disabled before the device is
+        * forced into any particular power mode. In this case, the device will
+        * transition into normal-power mode.
+        */
+       error = regmap_update_bits(iqs626->regmap, IQS626_SYS_SETTINGS,
+                                  IQS626_SYS_SETTINGS_DIS_AUTO, ~0);
+       if (error)
+               goto err_irq;
+
+       /*
+        * The following check ensures the device has completed its transition
+        * into normal-power mode before a manual mode switch is performed.
+        */
+       error = regmap_read_poll_timeout(iqs626->regmap, IQS626_SYS_FLAGS, val,
+                                       !(val & IQS626_SYS_FLAGS_PWR_MODE_MASK),
+                                        IQS626_PWR_MODE_POLL_SLEEP_US,
+                                        IQS626_PWR_MODE_POLL_TIMEOUT_US);
+       if (error)
+               goto err_irq;
+
+       error = regmap_update_bits(iqs626->regmap, IQS626_SYS_SETTINGS,
+                                  IQS626_SYS_SETTINGS_PWR_MODE_MASK,
+                                  iqs626->suspend_mode <<
+                                  IQS626_SYS_SETTINGS_PWR_MODE_SHIFT);
+       if (error)
+               goto err_irq;
+
+       /*
+        * This last check ensures the device has completed its transition into
+        * the desired power mode to prevent any spurious interrupts from being
+        * triggered after iqs626_suspend has already returned.
+        */
+       error = regmap_read_poll_timeout(iqs626->regmap, IQS626_SYS_FLAGS, val,
+                                        (val & IQS626_SYS_FLAGS_PWR_MODE_MASK)
+                                        == (iqs626->suspend_mode <<
+                                            IQS626_SYS_FLAGS_PWR_MODE_SHIFT),
+                                        IQS626_PWR_MODE_POLL_SLEEP_US,
+                                        IQS626_PWR_MODE_POLL_TIMEOUT_US);
+
+err_irq:
+       iqs626_irq_wait();
+       enable_irq(client->irq);
+
+       return error;
+}
+
+static int __maybe_unused iqs626_resume(struct device *dev)
+{
+       struct iqs626_private *iqs626 = dev_get_drvdata(dev);
+       struct i2c_client *client = iqs626->client;
+       unsigned int val;
+       int error;
+
+       if (!iqs626->suspend_mode)
+               return 0;
+
+       disable_irq(client->irq);
+
+       error = regmap_update_bits(iqs626->regmap, IQS626_SYS_SETTINGS,
+                                  IQS626_SYS_SETTINGS_PWR_MODE_MASK, 0);
+       if (error)
+               goto err_irq;
+
+       /*
+        * This check ensures the device has returned to normal-power mode
+        * before automatic power mode switching is re-enabled.
+        */
+       error = regmap_read_poll_timeout(iqs626->regmap, IQS626_SYS_FLAGS, val,
+                                       !(val & IQS626_SYS_FLAGS_PWR_MODE_MASK),
+                                        IQS626_PWR_MODE_POLL_SLEEP_US,
+                                        IQS626_PWR_MODE_POLL_TIMEOUT_US);
+       if (error)
+               goto err_irq;
+
+       error = regmap_update_bits(iqs626->regmap, IQS626_SYS_SETTINGS,
+                                  IQS626_SYS_SETTINGS_DIS_AUTO, 0);
+       if (error)
+               goto err_irq;
+
+       /*
+        * This step reports any events that may have been "swallowed" as a
+        * result of polling PWR_MODE (which automatically acknowledges any
+        * pending interrupts).
+        */
+       error = iqs626_report(iqs626);
+
+err_irq:
+       iqs626_irq_wait();
+       enable_irq(client->irq);
+
+       return error;
+}
+
+static SIMPLE_DEV_PM_OPS(iqs626_pm, iqs626_suspend, iqs626_resume);
+
+static const struct of_device_id iqs626_of_match[] = {
+       { .compatible = "azoteq,iqs626a" },
+       { }
+};
+MODULE_DEVICE_TABLE(of, iqs626_of_match);
+
+static struct i2c_driver iqs626_i2c_driver = {
+       .driver = {
+               .name = "iqs626a",
+               .of_match_table = iqs626_of_match,
+               .pm = &iqs626_pm,
+       },
+       .probe_new = iqs626_probe,
+};
+module_i2c_driver(iqs626_i2c_driver);
+
+MODULE_AUTHOR("Jeff LaBundy <jeff@labundy.com>");
+MODULE_DESCRIPTION("Azoteq IQS626A Capacitive Touch Controller");
+MODULE_LICENSE("GPL");
index 20ff087..cd5e99e 100644 (file)
@@ -61,15 +61,10 @@ static int max8997_haptic_set_duty_cycle(struct max8997_haptic *chip)
                unsigned int duty = chip->pwm_period * chip->level / 100;
                ret = pwm_config(chip->pwm, duty, chip->pwm_period);
        } else {
-               int i;
                u8 duty_index = 0;
 
-               for (i = 0; i <= 64; i++) {
-                       if (chip->level <= i * 100 / 64) {
-                               duty_index = i;
-                               break;
-                       }
-               }
+               duty_index = DIV_ROUND_UP(chip->level * 64, 100);
+
                switch (chip->internal_mode_pattern) {
                case 0:
                        max8997_write_reg(chip->client,
index e12da5b..dc4a240 100644 (file)
 #define ETP_FW_PAGE_SIZE_512   512
 #define ETP_FW_SIGNATURE_SIZE  6
 
+#define ETP_PRODUCT_ID_DELBIN  0x00C2
+#define ETP_PRODUCT_ID_VOXEL   0x00BF
+#define ETP_PRODUCT_ID_MAGPIE  0x0120
+#define ETP_PRODUCT_ID_BOBBA   0x0121
+
 struct i2c_client;
 struct completion;
 
@@ -73,7 +78,7 @@ struct elan_transport_ops {
        int (*calibrate_result)(struct i2c_client *client, u8 *val);
 
        int (*get_baseline_data)(struct i2c_client *client,
-                                bool max_baseliune, u8 *value);
+                                bool max_baseline, u8 *value);
 
        int (*get_version)(struct i2c_client *client, u8 pattern, bool iap,
                           u8 *version);
index bef7382..dad22c1 100644 (file)
@@ -46,6 +46,9 @@
 #define ETP_FINGER_WIDTH       15
 #define ETP_RETRY_COUNT                3
 
+/* quirks to control the device */
+#define ETP_QUIRK_QUICK_WAKEUP BIT(0)
+
 /* The main device structure */
 struct elan_tp_data {
        struct i2c_client       *client;
@@ -90,8 +93,38 @@ struct elan_tp_data {
        bool                    baseline_ready;
        u8                      clickpad;
        bool                    middle_button;
+
+       u32                     quirks;         /* Various quirks */
 };
 
+static u32 elan_i2c_lookup_quirks(u16 ic_type, u16 product_id)
+{
+       static const struct {
+               u16 ic_type;
+               u16 product_id;
+               u32 quirks;
+       } elan_i2c_quirks[] = {
+               { 0x0D, ETP_PRODUCT_ID_DELBIN, ETP_QUIRK_QUICK_WAKEUP },
+               { 0x10, ETP_PRODUCT_ID_VOXEL, ETP_QUIRK_QUICK_WAKEUP },
+               { 0x14, ETP_PRODUCT_ID_MAGPIE, ETP_QUIRK_QUICK_WAKEUP },
+               { 0x14, ETP_PRODUCT_ID_BOBBA, ETP_QUIRK_QUICK_WAKEUP },
+       };
+       u32 quirks = 0;
+       int i;
+
+       for (i = 0; i < ARRAY_SIZE(elan_i2c_quirks); i++) {
+               if (elan_i2c_quirks[i].ic_type == ic_type &&
+                   elan_i2c_quirks[i].product_id == product_id) {
+                       quirks = elan_i2c_quirks[i].quirks;
+               }
+       }
+
+       if (ic_type >= 0x0D && product_id >= 0x123)
+               quirks |= ETP_QUIRK_QUICK_WAKEUP;
+
+       return quirks;
+}
+
 static int elan_get_fwinfo(u16 ic_type, u8 iap_version, u16 *validpage_count,
                           u32 *signature_address, u16 *page_size)
 {
@@ -258,16 +291,18 @@ static int elan_check_ASUS_special_fw(struct elan_tp_data *data)
        return false;
 }
 
-static int __elan_initialize(struct elan_tp_data *data)
+static int __elan_initialize(struct elan_tp_data *data, bool skip_reset)
 {
        struct i2c_client *client = data->client;
        bool woken_up = false;
        int error;
 
-       error = data->ops->initialize(client);
-       if (error) {
-               dev_err(&client->dev, "device initialize failed: %d\n", error);
-               return error;
+       if (!skip_reset) {
+               error = data->ops->initialize(client);
+               if (error) {
+                       dev_err(&client->dev, "device initialize failed: %d\n", error);
+                       return error;
+               }
        }
 
        error = elan_query_product(data);
@@ -311,16 +346,17 @@ static int __elan_initialize(struct elan_tp_data *data)
        return 0;
 }
 
-static int elan_initialize(struct elan_tp_data *data)
+static int elan_initialize(struct elan_tp_data *data, bool skip_reset)
 {
        int repeat = ETP_RETRY_COUNT;
        int error;
 
        do {
-               error = __elan_initialize(data);
+               error = __elan_initialize(data, skip_reset);
                if (!error)
                        return 0;
 
+               skip_reset = false;
                msleep(30);
        } while (--repeat > 0);
 
@@ -357,6 +393,8 @@ static int elan_query_device_info(struct elan_tp_data *data)
        if (error)
                return error;
 
+       data->quirks = elan_i2c_lookup_quirks(data->ic_type, data->product_id);
+
        error = elan_get_fwinfo(data->ic_type, data->iap_version,
                                &data->fw_validpage_count,
                                &data->fw_signature_address,
@@ -546,7 +584,7 @@ static int elan_update_firmware(struct elan_tp_data *data,
                data->ops->iap_reset(client);
        } else {
                /* Reinitialize TP after fw is updated */
-               elan_initialize(data);
+               elan_initialize(data, false);
                elan_query_device_info(data);
        }
 
@@ -1247,7 +1285,7 @@ static int elan_probe(struct i2c_client *client,
        }
 
        /* Initialize the touchpad. */
-       error = elan_initialize(data);
+       error = elan_initialize(data, false);
        if (error)
                return error;
 
@@ -1384,7 +1422,7 @@ static int __maybe_unused elan_resume(struct device *dev)
                goto err;
        }
 
-       error = elan_initialize(data);
+       error = elan_initialize(data, data->quirks & ETP_QUIRK_QUICK_WAKEUP);
        if (error)
                dev_err(dev, "initialize when resuming failed: %d\n", error);
 
index 594ac4e..974d7bf 100644 (file)
@@ -103,7 +103,6 @@ static int apbps2_open(struct serio *io)
 {
        struct apbps2_priv *priv = io->port_data;
        int limit;
-       unsigned long tmp;
 
        /* clear error flags */
        iowrite32be(0, &priv->regs->status);
@@ -111,7 +110,7 @@ static int apbps2_open(struct serio *io)
        /* Clear old data if available (unlikely) */
        limit = 1024;
        while ((ioread32be(&priv->regs->status) & APBPS2_STATUS_DR) && --limit)
-               tmp = ioread32be(&priv->regs->data);
+               ioread32be(&priv->regs->data);
 
        /* Enable reciever and it's interrupt */
        iowrite32be(APBPS2_CTRL_RE | APBPS2_CTRL_RI, &priv->regs->ctrl);
similarity index 93%
rename from drivers/input/touchscreen/of_touchscreen.c
rename to drivers/input/touchscreen.c
index 97342e1..dd18cb9 100644 (file)
@@ -1,6 +1,7 @@
 // SPDX-License-Identifier: GPL-2.0-only
 /*
- *  Generic DT helper functions for touchscreen devices
+ *  Generic helper functions for touchscreens and other two-dimensional
+ *  pointing devices
  *
  *  Copyright (c) 2014 Sebastian Reichel <sre@kernel.org>
  */
@@ -37,7 +38,7 @@ static void touchscreen_set_params(struct input_dev *dev,
 
        if (!test_bit(axis, dev->absbit)) {
                dev_warn(&dev->dev,
-                        "DT specifies parameters but the axis %lu is not set up\n",
+                        "Parameters are specified but the axis %lu is not set up\n",
                         axis);
                return;
        }
@@ -49,7 +50,7 @@ static void touchscreen_set_params(struct input_dev *dev,
 }
 
 /**
- * touchscreen_parse_properties - parse common touchscreen DT properties
+ * touchscreen_parse_properties - parse common touchscreen properties
  * @input: input device that should be parsed
  * @multitouch: specifies whether parsed properties should be applied to
  *     single-touch or multi-touch axes
@@ -57,9 +58,9 @@ static void touchscreen_set_params(struct input_dev *dev,
  *     axis swap and invert info for use with touchscreen_report_x_y();
  *     or %NULL
  *
- * This function parses common DT properties for touchscreens and setups the
+ * This function parses common properties for touchscreens and sets up the
  * input device accordingly. The function keeps previously set up default
- * values if no value is specified via DT.
+ * values if no value is specified.
  */
 void touchscreen_parse_properties(struct input_dev *input, bool multitouch,
                                  struct touchscreen_properties *prop)
@@ -203,4 +204,4 @@ void touchscreen_report_pos(struct input_dev *input,
 EXPORT_SYMBOL(touchscreen_report_pos);
 
 MODULE_LICENSE("GPL v2");
-MODULE_DESCRIPTION("Device-tree helpers functions for touchscreen devices");
+MODULE_DESCRIPTION("Helper functions for touchscreens and other devices");
index 529614d..ad454cd 100644 (file)
@@ -12,10 +12,6 @@ menuconfig INPUT_TOUCHSCREEN
 
 if INPUT_TOUCHSCREEN
 
-config TOUCHSCREEN_PROPERTIES
-       def_tristate INPUT
-       depends on INPUT
-
 config TOUCHSCREEN_88PM860X
        tristate "Marvell 88PM860x touchscreen"
        depends on MFD_88PM860X
@@ -415,6 +411,17 @@ config TOUCHSCREEN_HIDEEP
          To compile this driver as a module, choose M here : the
          module will be called hideep_ts.
 
+config TOUCHSCREEN_HYCON_HY46XX
+       tristate "Hycon hy46xx touchscreen support"
+       depends on I2C
+       help
+         Say Y here if you have a touchscreen using Hycon hy46xx
+
+         If unsure, say N.
+
+         To compile this driver as a module, choose M here: the
+         module will be called hycon-hy46xx.
+
 config TOUCHSCREEN_ILI210X
        tristate "Ilitek ILI210X based touchscreen"
        depends on I2C
@@ -430,6 +437,18 @@ config TOUCHSCREEN_ILI210X
          To compile this driver as a module, choose M here: the
          module will be called ili210x.
 
+config TOUCHSCREEN_ILITEK
+       tristate "Ilitek I2C 213X/23XX/25XX/Lego Series Touch ICs"
+       depends on I2C
+       help
+         Say Y here if you have touchscreen with ILITEK touch IC,
+         it supports 213X/23XX/25XX and other Lego series.
+
+         If unsure, say N.
+
+         To compile this driver as a module, choose M here: the
+         module will be called ilitek_ts_i2c.
+
 config TOUCHSCREEN_IPROC
        tristate "IPROC touch panel driver support"
        depends on ARCH_BCM_IPROC || COMPILE_TEST
@@ -594,6 +613,18 @@ config TOUCHSCREEN_MELFAS_MIP4
          To compile this driver as a module, choose M here:
          the module will be called melfas_mip4.
 
+config TOUCHSCREEN_MSG2638
+       tristate "MStar msg2638 touchscreen support"
+       depends on I2C
+       depends on GPIOLIB || COMPILE_TEST
+       help
+         Say Y here if you have an I2C touchscreen using MStar msg2638.
+
+         If unsure, say N.
+
+         To compile this driver as a module, choose M here: the
+         module will be called msg2638.
+
 config TOUCHSCREEN_MTOUCH
        tristate "MicroTouch serial touchscreens"
        select SERIO
index 6233541..7d34100 100644 (file)
@@ -7,7 +7,6 @@
 
 wm97xx-ts-y := wm97xx-core.o
 
-obj-$(CONFIG_TOUCHSCREEN_PROPERTIES)   += of_touchscreen.o
 obj-$(CONFIG_TOUCHSCREEN_88PM860X)     += 88pm860x-ts.o
 obj-$(CONFIG_TOUCHSCREEN_AD7877)       += ad7877.o
 obj-$(CONFIG_TOUCHSCREEN_AD7879)       += ad7879.o
@@ -35,6 +34,7 @@ obj-$(CONFIG_TOUCHSCREEN_DA9052)      += da9052_tsi.o
 obj-$(CONFIG_TOUCHSCREEN_DYNAPRO)      += dynapro.o
 obj-$(CONFIG_TOUCHSCREEN_EDT_FT5X06)   += edt-ft5x06.o
 obj-$(CONFIG_TOUCHSCREEN_HAMPSHIRE)    += hampshire.o
+obj-$(CONFIG_TOUCHSCREEN_HYCON_HY46XX) += hycon-hy46xx.o
 obj-$(CONFIG_TOUCHSCREEN_GUNZE)                += gunze.o
 obj-$(CONFIG_TOUCHSCREEN_EETI)         += eeti_ts.o
 obj-$(CONFIG_TOUCHSCREEN_EKTF2127)     += ektf2127.o
@@ -47,6 +47,7 @@ obj-$(CONFIG_TOUCHSCREEN_FUJITSU)     += fujitsu_ts.o
 obj-$(CONFIG_TOUCHSCREEN_GOODIX)       += goodix.o
 obj-$(CONFIG_TOUCHSCREEN_HIDEEP)       += hideep.o
 obj-$(CONFIG_TOUCHSCREEN_ILI210X)      += ili210x.o
+obj-$(CONFIG_TOUCHSCREEN_ILITEK)       += ilitek_ts_i2c.o
 obj-$(CONFIG_TOUCHSCREEN_IMX6UL_TSC)   += imx6ul_tsc.o
 obj-$(CONFIG_TOUCHSCREEN_INEXIO)       += inexio.o
 obj-$(CONFIG_TOUCHSCREEN_IPROC)                += bcm_iproc_tsc.o
@@ -59,6 +60,7 @@ obj-$(CONFIG_TOUCHSCREEN_MCS5000)     += mcs5000_ts.o
 obj-$(CONFIG_TOUCHSCREEN_MELFAS_MIP4)  += melfas_mip4.o
 obj-$(CONFIG_TOUCHSCREEN_MIGOR)                += migor_ts.o
 obj-$(CONFIG_TOUCHSCREEN_MMS114)       += mms114.o
+obj-$(CONFIG_TOUCHSCREEN_MSG2638)      += msg2638.o
 obj-$(CONFIG_TOUCHSCREEN_MTOUCH)       += mtouch.o
 obj-$(CONFIG_TOUCHSCREEN_MK712)                += mk712.o
 obj-$(CONFIG_TOUCHSCREEN_HP600)                += hp680_ts_input.o
index c0d5c24..dc6a853 100644 (file)
@@ -125,7 +125,7 @@ static int ar1021_i2c_probe(struct i2c_client *client,
 
        error = devm_request_threaded_irq(&client->dev, client->irq,
                                          NULL, ar1021_i2c_irq,
-                                         IRQF_ONESHOT,
+                                         IRQF_ONESHOT | IRQF_NO_AUTOEN,
                                          "ar1021_i2c", ar1021);
        if (error) {
                dev_err(&client->dev,
@@ -133,9 +133,6 @@ static int ar1021_i2c_probe(struct i2c_client *client,
                return error;
        }
 
-       /* Disable the IRQ, we'll enable it in ar1021_i2c_open() */
-       disable_irq(client->irq);
-
        error = input_register_device(ar1021->input);
        if (error) {
                dev_err(&client->dev,
index 383a848..05de92c 100644 (file)
@@ -31,6 +31,7 @@
 #include <media/v4l2-ioctl.h>
 #include <media/videobuf2-v4l2.h>
 #include <media/videobuf2-vmalloc.h>
+#include <dt-bindings/input/atmel-maxtouch.h>
 
 /* Firmware files */
 #define MXT_FW_NAME            "maxtouch.fw"
@@ -199,6 +200,7 @@ enum t100_type {
 #define MXT_CRC_TIMEOUT                1000    /* msec */
 #define MXT_FW_RESET_TIME      3000    /* msec */
 #define MXT_FW_CHG_TIMEOUT     300     /* msec */
+#define MXT_WAKEUP_TIME                25      /* msec */
 
 /* Command to unlock bootloader */
 #define MXT_UNLOCK_CMD_MSB     0xaa
@@ -312,6 +314,7 @@ struct mxt_data {
        struct mxt_dbg dbg;
        struct regulator_bulk_data regulators[2];
        struct gpio_desc *reset_gpio;
+       struct gpio_desc *wake_gpio;
        bool use_retrigen_workaround;
 
        /* Cached parameters from object table */
@@ -342,6 +345,8 @@ struct mxt_data {
        unsigned int t19_num_keys;
 
        enum mxt_suspend_mode suspend_mode;
+
+       u32 wakeup_method;
 };
 
 struct mxt_vb2_buffer {
@@ -621,10 +626,42 @@ static int mxt_send_bootloader_cmd(struct mxt_data *data, bool unlock)
        return mxt_bootloader_write(data, buf, sizeof(buf));
 }
 
+static bool mxt_wakeup_toggle(struct i2c_client *client,
+                             bool wake_up, bool in_i2c)
+{
+       struct mxt_data *data = i2c_get_clientdata(client);
+
+       switch (data->wakeup_method) {
+       case ATMEL_MXT_WAKEUP_I2C_SCL:
+               if (!in_i2c)
+                       return false;
+               break;
+
+       case ATMEL_MXT_WAKEUP_GPIO:
+               if (in_i2c)
+                       return false;
+
+               gpiod_set_value(data->wake_gpio, wake_up);
+               break;
+
+       default:
+               return false;
+       }
+
+       if (wake_up) {
+               dev_dbg(&client->dev, "waking up controller\n");
+
+               msleep(MXT_WAKEUP_TIME);
+       }
+
+       return true;
+}
+
 static int __mxt_read_reg(struct i2c_client *client,
                               u16 reg, u16 len, void *val)
 {
        struct i2c_msg xfer[2];
+       bool retried = false;
        u8 buf[2];
        int ret;
 
@@ -643,9 +680,13 @@ static int __mxt_read_reg(struct i2c_client *client,
        xfer[1].len = len;
        xfer[1].buf = val;
 
+retry:
        ret = i2c_transfer(client->adapter, xfer, 2);
        if (ret == 2) {
                ret = 0;
+       } else if (!retried && mxt_wakeup_toggle(client, true, true)) {
+               retried = true;
+               goto retry;
        } else {
                if (ret >= 0)
                        ret = -EIO;
@@ -659,6 +700,7 @@ static int __mxt_read_reg(struct i2c_client *client,
 static int __mxt_write_reg(struct i2c_client *client, u16 reg, u16 len,
                           const void *val)
 {
+       bool retried = false;
        u8 *buf;
        size_t count;
        int ret;
@@ -672,9 +714,13 @@ static int __mxt_write_reg(struct i2c_client *client, u16 reg, u16 len,
        buf[1] = (reg >> 8) & 0xff;
        memcpy(&buf[2], val, len);
 
+retry:
        ret = i2c_master_send(client, buf, count);
        if (ret == count) {
                ret = 0;
+       } else if (!retried && mxt_wakeup_toggle(client, true, true)) {
+               retried = true;
+               goto retry;
        } else {
                if (ret >= 0)
                        ret = -EIO;
@@ -2975,6 +3021,8 @@ static const struct attribute_group mxt_attr_group = {
 
 static void mxt_start(struct mxt_data *data)
 {
+       mxt_wakeup_toggle(data->client, true, false);
+
        switch (data->suspend_mode) {
        case MXT_SUSPEND_T9_CTRL:
                mxt_soft_reset(data);
@@ -3009,6 +3057,8 @@ static void mxt_stop(struct mxt_data *data)
                mxt_set_t7_power_cfg(data, MXT_POWER_CFG_DEEPSLEEP);
                break;
        }
+
+       mxt_wakeup_toggle(data->client, false, false);
 }
 
 static int mxt_input_open(struct input_dev *dev)
@@ -3155,16 +3205,24 @@ static int mxt_probe(struct i2c_client *client, const struct i2c_device_id *id)
                return error;
        }
 
+       /* Request the WAKE line as asserted so we go out of sleep */
+       data->wake_gpio = devm_gpiod_get_optional(&client->dev,
+                                                 "wake", GPIOD_OUT_HIGH);
+       if (IS_ERR(data->wake_gpio)) {
+               error = PTR_ERR(data->wake_gpio);
+               dev_err(&client->dev, "Failed to get wake gpio: %d\n", error);
+               return error;
+       }
+
        error = devm_request_threaded_irq(&client->dev, client->irq,
-                                         NULL, mxt_interrupt, IRQF_ONESHOT,
+                                         NULL, mxt_interrupt,
+                                         IRQF_ONESHOT | IRQF_NO_AUTOEN,
                                          client->name, data);
        if (error) {
                dev_err(&client->dev, "Failed to register interrupt\n");
                return error;
        }
 
-       disable_irq(client->irq);
-
        error = regulator_bulk_enable(ARRAY_SIZE(data->regulators),
                                      data->regulators);
        if (error) {
@@ -3185,6 +3243,25 @@ static int mxt_probe(struct i2c_client *client, const struct i2c_device_id *id)
                msleep(MXT_RESET_INVALID_CHG);
        }
 
+       /*
+        * Controllers like mXT1386 have a dedicated WAKE line that could be
+        * connected to a GPIO or to I2C SCL pin, or permanently asserted low.
+        *
+        * This WAKE line is used for waking controller from a deep-sleep and
+        * it needs to be asserted low for 25 milliseconds before I2C transfers
+        * could be accepted by controller if it was in a deep-sleep mode.
+        * Controller will go into sleep automatically after 2 seconds of
+        * inactivity if WAKE line is deasserted and deep sleep is activated.
+        *
+        * If WAKE line is connected to I2C SCL pin, then the first I2C transfer
+        * will get an instant NAK and transfer needs to be retried after 25ms.
+        *
+        * If WAKE line is connected to a GPIO line, the line must be asserted
+        * 25ms before the host attempts to communicate with the controller.
+        */
+       device_property_read_u32(&client->dev, "atmel,wakeup-method",
+                                &data->wakeup_method);
+
        error = mxt_initialize(data);
        if (error)
                goto err_disable_regulators;
index 341925e..392950a 100644 (file)
@@ -401,10 +401,10 @@ static int bu21029_probe(struct i2c_client *client,
 
        input_set_drvdata(in_dev, bu21029);
 
-       irq_set_status_flags(client->irq, IRQ_NOAUTOEN);
        error = devm_request_threaded_irq(&client->dev, client->irq,
                                          NULL, bu21029_touch_soft_irq,
-                                         IRQF_ONESHOT, DRIVER_NAME, bu21029);
+                                         IRQF_ONESHOT | IRQF_NO_AUTOEN,
+                                         DRIVER_NAME, bu21029);
        if (error) {
                dev_err(&client->dev,
                        "unable to request touch irq: %d\n", error);
index 73c854f..106dd49 100644 (file)
@@ -229,16 +229,21 @@ static int cyttsp_set_sysinfo_regs(struct cyttsp *ts)
 static void cyttsp_hard_reset(struct cyttsp *ts)
 {
        if (ts->reset_gpio) {
+               /*
+                * According to the CY8CTMA340 datasheet page 21, the external
+                * reset pulse width should be >= 1 ms. The datasheet does not
+                * specify how long we have to wait after reset but a vendor
+                * tree specifies 5 ms here.
+                */
                gpiod_set_value_cansleep(ts->reset_gpio, 1);
-               msleep(CY_DELAY_DFLT);
+               usleep_range(1000, 2000);
                gpiod_set_value_cansleep(ts->reset_gpio, 0);
-               msleep(CY_DELAY_DFLT);
+               usleep_range(5000, 6000);
        }
 }
 
 static int cyttsp_soft_reset(struct cyttsp *ts)
 {
-       unsigned long timeout;
        int retval;
 
        /* wait for interrupt to set ready completion */
@@ -248,12 +253,16 @@ static int cyttsp_soft_reset(struct cyttsp *ts)
        enable_irq(ts->irq);
 
        retval = ttsp_send_command(ts, CY_SOFT_RESET_MODE);
-       if (retval)
+       if (retval) {
+               dev_err(ts->dev, "failed to send soft reset\n");
                goto out;
+       }
 
-       timeout = wait_for_completion_timeout(&ts->bl_ready,
-                       msecs_to_jiffies(CY_DELAY_DFLT * CY_DELAY_MAX));
-       retval = timeout ? 0 : -EIO;
+       if (!wait_for_completion_timeout(&ts->bl_ready,
+                       msecs_to_jiffies(CY_DELAY_DFLT * CY_DELAY_MAX))) {
+               dev_err(ts->dev, "timeout waiting for soft reset\n");
+               retval = -EIO;
+       }
 
 out:
        ts->state = CY_IDLE_STATE;
@@ -405,8 +414,10 @@ static int cyttsp_power_on(struct cyttsp *ts)
        if (GET_BOOTLOADERMODE(ts->bl_data.bl_status) &&
            IS_VALID_APP(ts->bl_data.bl_status)) {
                error = cyttsp_exit_bl_mode(ts);
-               if (error)
+               if (error) {
+                       dev_err(ts->dev, "failed to exit bootloader mode\n");
                        return error;
+               }
        }
 
        if (GET_HSTMODE(ts->bl_data.bl_file) != CY_OPERATE_MODE ||
@@ -629,10 +640,8 @@ struct cyttsp *cyttsp_probe(const struct cyttsp_bus_ops *bus_ops,
                return ERR_PTR(error);
 
        init_completion(&ts->bl_ready);
-       snprintf(ts->phys, sizeof(ts->phys), "%s/input0", dev_name(dev));
 
        input_dev->name = "Cypress TTSP TouchScreen";
-       input_dev->phys = ts->phys;
        input_dev->id.bustype = bus_ops->bustype;
        input_dev->dev.parent = ts->dev;
 
@@ -643,16 +652,20 @@ struct cyttsp *cyttsp_probe(const struct cyttsp_bus_ops *bus_ops,
 
        input_set_capability(input_dev, EV_ABS, ABS_MT_POSITION_X);
        input_set_capability(input_dev, EV_ABS, ABS_MT_POSITION_Y);
+       /* One byte for width 0..255 so this is the limit */
+       input_set_abs_params(input_dev, ABS_MT_TOUCH_MAJOR, 0, 255, 0, 0);
+
        touchscreen_parse_properties(input_dev, true, NULL);
 
-       error = input_mt_init_slots(input_dev, CY_MAX_ID, 0);
+       error = input_mt_init_slots(input_dev, CY_MAX_ID, INPUT_MT_DIRECT);
        if (error) {
                dev_err(dev, "Unable to init MT slots.\n");
                return ERR_PTR(error);
        }
 
        error = devm_request_threaded_irq(dev, ts->irq, NULL, cyttsp_irq,
-                                         IRQF_TRIGGER_FALLING | IRQF_ONESHOT,
+                                         IRQF_TRIGGER_FALLING | IRQF_ONESHOT |
+                                         IRQF_NO_AUTOEN,
                                          "cyttsp", ts);
        if (error) {
                dev_err(ts->dev, "failed to request IRQ %d, err: %d\n",
@@ -660,8 +673,6 @@ struct cyttsp *cyttsp_probe(const struct cyttsp_bus_ops *bus_ops,
                return ERR_PTR(error);
        }
 
-       disable_irq(ts->irq);
-
        cyttsp_hard_reset(ts);
 
        error = cyttsp_power_on(ts);
index 8c65133..9bc4fe7 100644 (file)
@@ -114,7 +114,6 @@ struct cyttsp {
        struct device *dev;
        int irq;
        struct input_dev *input;
-       char phys[32];
        const struct cyttsp_bus_ops *bus_ops;
        struct cyttsp_bootloader_data bl_data;
        struct cyttsp_sysinfo_data sysinfo_data;
index 5f7706f..17540bd 100644 (file)
@@ -38,6 +38,7 @@
 #include <linux/of.h>
 #include <linux/gpio/consumer.h>
 #include <linux/regulator/consumer.h>
+#include <linux/uuid.h>
 #include <asm/unaligned.h>
 
 /* Device, Driver information */
@@ -1334,6 +1335,40 @@ static void elants_i2c_power_off(void *_data)
        }
 }
 
+#ifdef CONFIG_ACPI
+static const struct acpi_device_id i2c_hid_ids[] = {
+       {"ACPI0C50", 0 },
+       {"PNP0C50", 0 },
+       { },
+};
+
+static const guid_t i2c_hid_guid =
+       GUID_INIT(0x3CDFF6F7, 0x4267, 0x4555,
+                 0xAD, 0x05, 0xB3, 0x0A, 0x3D, 0x89, 0x38, 0xDE);
+
+static bool elants_acpi_is_hid_device(struct device *dev)
+{
+       acpi_handle handle = ACPI_HANDLE(dev);
+       union acpi_object *obj;
+
+       if (acpi_match_device_ids(ACPI_COMPANION(dev), i2c_hid_ids))
+               return false;
+
+       obj = acpi_evaluate_dsm_typed(handle, &i2c_hid_guid, 1, 1, NULL, ACPI_TYPE_INTEGER);
+       if (obj) {
+               ACPI_FREE(obj);
+               return true;
+       }
+
+       return false;
+}
+#else
+static bool elants_acpi_is_hid_device(struct device *dev)
+{
+       return false;
+}
+#endif
+
 static int elants_i2c_probe(struct i2c_client *client,
                            const struct i2c_device_id *id)
 {
@@ -1342,9 +1377,14 @@ static int elants_i2c_probe(struct i2c_client *client,
        unsigned long irqflags;
        int error;
 
+       /* Don't bind to i2c-hid compatible devices, these are handled by the i2c-hid drv. */
+       if (elants_acpi_is_hid_device(&client->dev)) {
+               dev_warn(&client->dev, "This device appears to be an I2C-HID device, not binding\n");
+               return -ENODEV;
+       }
+
        if (!i2c_check_functionality(client->adapter, I2C_FUNC_I2C)) {
-               dev_err(&client->dev,
-                       "%s: i2c check functionality error\n", DEVICE_NAME);
+               dev_err(&client->dev, "I2C check functionality error\n");
                return -ENXIO;
        }
 
index a6597f0..cbe0dd4 100644 (file)
 #define EXC3000_NUM_SLOTS              10
 #define EXC3000_SLOTS_PER_FRAME                5
 #define EXC3000_LEN_FRAME              66
+#define EXC3000_LEN_VENDOR_REQUEST     68
 #define EXC3000_LEN_POINT              10
 
 #define EXC3000_LEN_MODEL_NAME         16
 #define EXC3000_LEN_FW_VERSION         16
 
+#define EXC3000_VENDOR_EVENT           0x03
 #define EXC3000_MT1_EVENT              0x06
 #define EXC3000_MT2_EVENT              0x18
 
@@ -76,9 +78,6 @@ struct exc3000_data {
        u8 buf[2 * EXC3000_LEN_FRAME];
        struct completion wait_event;
        struct mutex query_lock;
-       int query_result;
-       char model[EXC3000_LEN_MODEL_NAME];
-       char fw_version[EXC3000_LEN_FW_VERSION];
 };
 
 static void exc3000_report_slots(struct input_dev *input,
@@ -105,15 +104,16 @@ static void exc3000_timer(struct timer_list *t)
        input_sync(data->input);
 }
 
+static inline void exc3000_schedule_timer(struct exc3000_data *data)
+{
+       mod_timer(&data->timer, jiffies + msecs_to_jiffies(EXC3000_TIMEOUT_MS));
+}
+
 static int exc3000_read_frame(struct exc3000_data *data, u8 *buf)
 {
        struct i2c_client *client = data->client;
-       u8 expected_event = EXC3000_MT1_EVENT;
        int ret;
 
-       if (data->info->max_xy == SZ_16K - 1)
-               expected_event = EXC3000_MT2_EVENT;
-
        ret = i2c_master_send(client, "'", 2);
        if (ret < 0)
                return ret;
@@ -131,175 +131,196 @@ static int exc3000_read_frame(struct exc3000_data *data, u8 *buf)
        if (get_unaligned_le16(buf) != EXC3000_LEN_FRAME)
                return -EINVAL;
 
-       if (buf[2] != expected_event)
-               return -EINVAL;
-
        return 0;
 }
 
-static int exc3000_read_data(struct exc3000_data *data,
-                            u8 *buf, int *n_slots)
+static int exc3000_handle_mt_event(struct exc3000_data *data)
 {
-       int error;
-
-       error = exc3000_read_frame(data, buf);
-       if (error)
-               return error;
+       struct input_dev *input = data->input;
+       int ret, total_slots;
+       u8 *buf = data->buf;
 
-       *n_slots = buf[3];
-       if (!*n_slots || *n_slots > EXC3000_NUM_SLOTS)
-               return -EINVAL;
+       total_slots = buf[3];
+       if (!total_slots || total_slots > EXC3000_NUM_SLOTS) {
+               ret = -EINVAL;
+               goto out_fail;
+       }
 
-       if (*n_slots > EXC3000_SLOTS_PER_FRAME) {
+       if (total_slots > EXC3000_SLOTS_PER_FRAME) {
                /* Read 2nd frame to get the rest of the contacts. */
-               error = exc3000_read_frame(data, buf + EXC3000_LEN_FRAME);
-               if (error)
-                       return error;
+               ret = exc3000_read_frame(data, buf + EXC3000_LEN_FRAME);
+               if (ret)
+                       goto out_fail;
 
                /* 2nd chunk must have number of contacts set to 0. */
-               if (buf[EXC3000_LEN_FRAME + 3] != 0)
-                       return -EINVAL;
+               if (buf[EXC3000_LEN_FRAME + 3] != 0) {
+                       ret = -EINVAL;
+                       goto out_fail;
+               }
        }
 
-       return 0;
-}
-
-static int exc3000_query_interrupt(struct exc3000_data *data)
-{
-       u8 *buf = data->buf;
-       int error;
+       /*
+        * We read full state successfully, no contacts will be "stuck".
+        */
+       del_timer_sync(&data->timer);
 
-       error = i2c_master_recv(data->client, buf, EXC3000_LEN_FRAME);
-       if (error < 0)
-               return error;
+       while (total_slots > 0) {
+               int slots = min(total_slots, EXC3000_SLOTS_PER_FRAME);
 
-       if (buf[0] != 'B')
-               return -EPROTO;
+               exc3000_report_slots(input, &data->prop, buf + 4, slots);
+               total_slots -= slots;
+               buf += EXC3000_LEN_FRAME;
+       }
 
-       if (buf[4] == 'E')
-               strlcpy(data->model, buf + 5, sizeof(data->model));
-       else if (buf[4] == 'D')
-               strlcpy(data->fw_version, buf + 5, sizeof(data->fw_version));
-       else
-               return -EPROTO;
+       input_mt_sync_frame(input);
+       input_sync(input);
 
        return 0;
+
+out_fail:
+       /* Schedule a timer to release "stuck" contacts */
+       exc3000_schedule_timer(data);
+
+       return ret;
 }
 
 static irqreturn_t exc3000_interrupt(int irq, void *dev_id)
 {
        struct exc3000_data *data = dev_id;
-       struct input_dev *input = data->input;
        u8 *buf = data->buf;
-       int slots, total_slots;
-       int error;
-
-       if (mutex_is_locked(&data->query_lock)) {
-               data->query_result = exc3000_query_interrupt(data);
-               complete(&data->wait_event);
-               goto out;
-       }
+       int ret;
 
-       error = exc3000_read_data(data, buf, &total_slots);
-       if (error) {
+       ret = exc3000_read_frame(data, buf);
+       if (ret) {
                /* Schedule a timer to release "stuck" contacts */
-               mod_timer(&data->timer,
-                         jiffies + msecs_to_jiffies(EXC3000_TIMEOUT_MS));
+               exc3000_schedule_timer(data);
                goto out;
        }
 
-       /*
-        * We read full state successfully, no contacts will be "stuck".
-        */
-       del_timer_sync(&data->timer);
+       switch (buf[2]) {
+       case EXC3000_VENDOR_EVENT:
+               complete(&data->wait_event);
+               break;
 
-       while (total_slots > 0) {
-               slots = min(total_slots, EXC3000_SLOTS_PER_FRAME);
-               exc3000_report_slots(input, &data->prop, buf + 4, slots);
-               total_slots -= slots;
-               buf += EXC3000_LEN_FRAME;
-       }
+       case EXC3000_MT1_EVENT:
+       case EXC3000_MT2_EVENT:
+               exc3000_handle_mt_event(data);
+               break;
 
-       input_mt_sync_frame(input);
-       input_sync(input);
+       default:
+               break;
+       }
 
 out:
        return IRQ_HANDLED;
 }
 
-static ssize_t fw_version_show(struct device *dev,
-                              struct device_attribute *attr, char *buf)
+static int exc3000_vendor_data_request(struct exc3000_data *data, u8 *request,
+                                      u8 request_len, u8 *response, int timeout)
 {
-       struct i2c_client *client = to_i2c_client(dev);
-       struct exc3000_data *data = i2c_get_clientdata(client);
-       static const u8 request[68] = {
-               0x67, 0x00, 0x42, 0x00, 0x03, 0x01, 'D', 0x00
-       };
-       int error;
+       u8 buf[EXC3000_LEN_VENDOR_REQUEST] = { 0x67, 0x00, 0x42, 0x00, 0x03 };
+       int ret;
 
        mutex_lock(&data->query_lock);
 
-       data->query_result = -ETIMEDOUT;
        reinit_completion(&data->wait_event);
 
-       error = i2c_master_send(client, request, sizeof(request));
-       if (error < 0) {
-               mutex_unlock(&data->query_lock);
-               return error;
+       buf[5] = request_len;
+       memcpy(&buf[6], request, request_len);
+
+       ret = i2c_master_send(data->client, buf, EXC3000_LEN_VENDOR_REQUEST);
+       if (ret < 0)
+               goto out_unlock;
+
+       if (response) {
+               ret = wait_for_completion_timeout(&data->wait_event,
+                                                 timeout * HZ);
+               if (ret <= 0) {
+                       ret = -ETIMEDOUT;
+                       goto out_unlock;
+               }
+
+               if (data->buf[3] >= EXC3000_LEN_FRAME) {
+                       ret = -ENOSPC;
+                       goto out_unlock;
+               }
+
+               memcpy(response, &data->buf[4], data->buf[3]);
+               ret = data->buf[3];
        }
 
-       wait_for_completion_interruptible_timeout(&data->wait_event, 1 * HZ);
+out_unlock:
        mutex_unlock(&data->query_lock);
 
-       if (data->query_result < 0)
-               return data->query_result;
-
-       return sprintf(buf, "%s\n", data->fw_version);
+       return ret;
 }
-static DEVICE_ATTR_RO(fw_version);
 
-static ssize_t exc3000_get_model(struct exc3000_data *data)
+static ssize_t fw_version_show(struct device *dev,
+                              struct device_attribute *attr, char *buf)
 {
-       static const u8 request[68] = {
-               0x67, 0x00, 0x42, 0x00, 0x03, 0x01, 'E', 0x00
-       };
-       struct i2c_client *client = data->client;
-       int error;
+       struct i2c_client *client = to_i2c_client(dev);
+       struct exc3000_data *data = i2c_get_clientdata(client);
+       u8 response[EXC3000_LEN_FRAME];
+       int ret;
 
-       mutex_lock(&data->query_lock);
-       data->query_result = -ETIMEDOUT;
-       reinit_completion(&data->wait_event);
+       /* query bootloader info */
+       ret = exc3000_vendor_data_request(data,
+                                         (u8[]){0x39, 0x02}, 2, response, 1);
+       if (ret < 0)
+               return ret;
 
-       error = i2c_master_send(client, request, sizeof(request));
-       if (error < 0) {
-               mutex_unlock(&data->query_lock);
-               return error;
-       }
+       /*
+        * If the bootloader version is non-zero then the device is in
+        * bootloader mode and won't answer a query for the application FW
+        * version, so we just use the bootloader version info.
+        */
+       if (response[2] || response[3])
+               return sprintf(buf, "%d.%d\n", response[2], response[3]);
 
-       wait_for_completion_interruptible_timeout(&data->wait_event, 1 * HZ);
-       mutex_unlock(&data->query_lock);
+       ret = exc3000_vendor_data_request(data, (u8[]){'D'}, 1, response, 1);
+       if (ret < 0)
+               return ret;
 
-       return data->query_result;
+       return sprintf(buf, "%s\n", &response[1]);
 }
+static DEVICE_ATTR_RO(fw_version);
 
 static ssize_t model_show(struct device *dev,
                          struct device_attribute *attr, char *buf)
 {
        struct i2c_client *client = to_i2c_client(dev);
        struct exc3000_data *data = i2c_get_clientdata(client);
-       int error;
+       u8 response[EXC3000_LEN_FRAME];
+       int ret;
 
-       error = exc3000_get_model(data);
-       if (error < 0)
-               return error;
+       ret = exc3000_vendor_data_request(data, (u8[]){'E'}, 1, response, 1);
+       if (ret < 0)
+               return ret;
 
-       return sprintf(buf, "%s\n", data->model);
+       return sprintf(buf, "%s\n", &response[1]);
 }
 static DEVICE_ATTR_RO(model);
 
+static ssize_t type_show(struct device *dev,
+                         struct device_attribute *attr, char *buf)
+{
+       struct i2c_client *client = to_i2c_client(dev);
+       struct exc3000_data *data = i2c_get_clientdata(client);
+       u8 response[EXC3000_LEN_FRAME];
+       int ret;
+
+       ret = exc3000_vendor_data_request(data, (u8[]){'F'}, 1, response, 1);
+       if (ret < 0)
+               return ret;
+
+       return sprintf(buf, "%s\n", &response[1]);
+}
+static DEVICE_ATTR_RO(type);
+
 static struct attribute *sysfs_attrs[] = {
        &dev_attr_fw_version.attr,
        &dev_attr_model.attr,
+       &dev_attr_type.attr,
        NULL
 };
 
@@ -379,9 +400,15 @@ static int exc3000_probe(struct i2c_client *client)
         * or two touch events anyways).
         */
        for (retry = 0; retry < 3; retry++) {
-               error = exc3000_get_model(data);
-               if (!error)
+               u8 response[EXC3000_LEN_FRAME];
+
+               error = exc3000_vendor_data_request(data, (u8[]){'E'}, 1,
+                                                   response, 1);
+               if (error > 0) {
+                       dev_dbg(&client->dev, "TS Model: %s", &response[1]);
+                       error = 0;
                        break;
+               }
                dev_warn(&client->dev, "Retry %d get EETI EXC3000 model: %d\n",
                         retry + 1, error);
        }
@@ -389,8 +416,6 @@ static int exc3000_probe(struct i2c_client *client)
        if (error)
                return error;
 
-       dev_dbg(&client->dev, "TS Model: %s", data->model);
-
        i2c_set_clientdata(client, data);
 
        error = devm_device_add_group(&client->dev, &exc3000_attribute_group);
diff --git a/drivers/input/touchscreen/hycon-hy46xx.c b/drivers/input/touchscreen/hycon-hy46xx.c
new file mode 100644 (file)
index 0000000..891d043
--- /dev/null
@@ -0,0 +1,591 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Copyright (C) 2021
+ * Author(s): Giulio Benetti <giulio.benetti@benettiengineering.com>
+ */
+
+#include <linux/delay.h>
+#include <linux/gpio/consumer.h>
+#include <linux/i2c.h>
+#include <linux/interrupt.h>
+#include <linux/input.h>
+#include <linux/input/mt.h>
+#include <linux/input/touchscreen.h>
+#include <linux/irq.h>
+#include <linux/regulator/consumer.h>
+#include <linux/regmap.h>
+
+#include <asm/unaligned.h>
+
+#define HY46XX_CHKSUM_CODE             0x1
+#define HY46XX_FINGER_NUM              0x2
+#define HY46XX_CHKSUM_LEN              0x7
+#define HY46XX_THRESHOLD               0x80
+#define HY46XX_GLOVE_EN                        0x84
+#define HY46XX_REPORT_SPEED            0x88
+#define HY46XX_PWR_NOISE_EN            0x89
+#define HY46XX_FILTER_DATA             0x8A
+#define HY46XX_GAIN                    0x92
+#define HY46XX_EDGE_OFFSET             0x93
+#define HY46XX_RX_NR_USED              0x94
+#define HY46XX_TX_NR_USED              0x95
+#define HY46XX_PWR_MODE                        0xA5
+#define HY46XX_FW_VERSION              0xA6
+#define HY46XX_LIB_VERSION             0xA7
+#define HY46XX_TP_INFO                 0xA8
+#define HY46XX_TP_CHIP_ID              0xA9
+#define HY46XX_BOOT_VER                        0xB0
+
+#define HY46XX_TPLEN                   0x6
+#define HY46XX_REPORT_PKT_LEN          0x44
+
+#define HY46XX_MAX_SUPPORTED_POINTS    11
+
+#define TOUCH_EVENT_DOWN               0x00
+#define TOUCH_EVENT_UP                 0x01
+#define TOUCH_EVENT_CONTACT            0x02
+#define TOUCH_EVENT_RESERVED           0x03
+
+struct hycon_hy46xx_data {
+       struct i2c_client *client;
+       struct input_dev *input;
+       struct touchscreen_properties prop;
+       struct regulator *vcc;
+
+       struct gpio_desc *reset_gpio;
+
+       struct mutex mutex;
+       struct regmap *regmap;
+
+       int threshold;
+       bool glove_enable;
+       int report_speed;
+       bool noise_filter_enable;
+       int filter_data;
+       int gain;
+       int edge_offset;
+       int rx_number_used;
+       int tx_number_used;
+       int power_mode;
+       int fw_version;
+       int lib_version;
+       int tp_information;
+       int tp_chip_id;
+       int bootloader_version;
+};
+
+static const struct regmap_config hycon_hy46xx_i2c_regmap_config = {
+       .reg_bits = 8,
+       .val_bits = 8,
+};
+
+static bool hycon_hy46xx_check_checksum(struct hycon_hy46xx_data *tsdata, u8 *buf)
+{
+       u8 chksum = 0;
+       int i;
+
+       for (i = 2; i < buf[HY46XX_CHKSUM_LEN]; i++)
+               chksum += buf[i];
+
+       if (chksum == buf[HY46XX_CHKSUM_CODE])
+               return true;
+
+       dev_err_ratelimited(&tsdata->client->dev,
+                           "checksum error: 0x%02x expected, got 0x%02x\n",
+                           chksum, buf[HY46XX_CHKSUM_CODE]);
+
+       return false;
+}
+
+static irqreturn_t hycon_hy46xx_isr(int irq, void *dev_id)
+{
+       struct hycon_hy46xx_data *tsdata = dev_id;
+       struct device *dev = &tsdata->client->dev;
+       u8 rdbuf[HY46XX_REPORT_PKT_LEN];
+       int i, x, y, id;
+       int error;
+
+       memset(rdbuf, 0, sizeof(rdbuf));
+
+       error = regmap_bulk_read(tsdata->regmap, 0, rdbuf, sizeof(rdbuf));
+       if (error) {
+               dev_err_ratelimited(dev, "Unable to fetch data, error: %d\n",
+                                   error);
+               goto out;
+       }
+
+       if (!hycon_hy46xx_check_checksum(tsdata, rdbuf))
+               goto out;
+
+       for (i = 0; i < HY46XX_MAX_SUPPORTED_POINTS; i++) {
+               u8 *buf = &rdbuf[3 + (HY46XX_TPLEN * i)];
+               int type = buf[0] >> 6;
+
+               if (type == TOUCH_EVENT_RESERVED)
+                       continue;
+
+               x = get_unaligned_be16(buf) & 0x0fff;
+               y = get_unaligned_be16(buf + 2) & 0x0fff;
+
+               id = buf[2] >> 4;
+
+               input_mt_slot(tsdata->input, id);
+               if (input_mt_report_slot_state(tsdata->input, MT_TOOL_FINGER,
+                                              type != TOUCH_EVENT_UP))
+                       touchscreen_report_pos(tsdata->input, &tsdata->prop,
+                                              x, y, true);
+       }
+
+       input_mt_report_pointer_emulation(tsdata->input, false);
+       input_sync(tsdata->input);
+
+out:
+       return IRQ_HANDLED;
+}
+
+struct hycon_hy46xx_attribute {
+       struct device_attribute dattr;
+       size_t field_offset;
+       u8 address;
+       u8 limit_low;
+       u8 limit_high;
+};
+
+#define HYCON_ATTR_U8(_field, _mode, _address, _limit_low, _limit_high)        \
+       struct hycon_hy46xx_attribute hycon_hy46xx_attr_##_field = {            \
+               .dattr = __ATTR(_field, _mode,                          \
+                               hycon_hy46xx_setting_show,                      \
+                               hycon_hy46xx_setting_store),                    \
+               .field_offset = offsetof(struct hycon_hy46xx_data, _field),     \
+               .address = _address,                                    \
+               .limit_low = _limit_low,                                \
+               .limit_high = _limit_high,                              \
+       }
+
+#define HYCON_ATTR_BOOL(_field, _mode, _address)                       \
+       struct hycon_hy46xx_attribute hycon_hy46xx_attr_##_field = {            \
+               .dattr = __ATTR(_field, _mode,                          \
+                               hycon_hy46xx_setting_show,                      \
+                               hycon_hy46xx_setting_store),                    \
+               .field_offset = offsetof(struct hycon_hy46xx_data, _field),     \
+               .address = _address,                                    \
+               .limit_low = false,                                     \
+               .limit_high = true,                                     \
+       }
+
+static ssize_t hycon_hy46xx_setting_show(struct device *dev,
+                                  struct device_attribute *dattr, char *buf)
+{
+       struct i2c_client *client = to_i2c_client(dev);
+       struct hycon_hy46xx_data *tsdata = i2c_get_clientdata(client);
+       struct hycon_hy46xx_attribute *attr =
+                       container_of(dattr, struct hycon_hy46xx_attribute, dattr);
+       u8 *field = (u8 *)tsdata + attr->field_offset;
+       size_t count = 0;
+       int error = 0;
+       int val;
+
+       mutex_lock(&tsdata->mutex);
+
+       error = regmap_read(tsdata->regmap, attr->address, &val);
+       if (error < 0) {
+               dev_err(&tsdata->client->dev,
+                       "Failed to fetch attribute %s, error %d\n",
+                       dattr->attr.name, error);
+               goto out;
+       }
+
+       if (val != *field) {
+               dev_warn(&tsdata->client->dev,
+                        "%s: read (%d) and stored value (%d) differ\n",
+                        dattr->attr.name, val, *field);
+               *field = val;
+       }
+
+       count = scnprintf(buf, PAGE_SIZE, "%d\n", val);
+
+out:
+       mutex_unlock(&tsdata->mutex);
+       return error ?: count;
+}
+
+static ssize_t hycon_hy46xx_setting_store(struct device *dev,
+                                       struct device_attribute *dattr,
+                                       const char *buf, size_t count)
+{
+       struct i2c_client *client = to_i2c_client(dev);
+       struct hycon_hy46xx_data *tsdata = i2c_get_clientdata(client);
+       struct hycon_hy46xx_attribute *attr =
+                       container_of(dattr, struct hycon_hy46xx_attribute, dattr);
+       u8 *field = (u8 *)tsdata + attr->field_offset;
+       unsigned int val;
+       int error;
+
+       mutex_lock(&tsdata->mutex);
+
+       error = kstrtouint(buf, 0, &val);
+       if (error)
+               goto out;
+
+       if (val < attr->limit_low || val > attr->limit_high) {
+               error = -ERANGE;
+               goto out;
+       }
+
+       error = regmap_write(tsdata->regmap, attr->address, val);
+       if (error < 0) {
+               dev_err(&tsdata->client->dev,
+                       "Failed to update attribute %s, error: %d\n",
+                       dattr->attr.name, error);
+               goto out;
+       }
+       *field = val;
+
+out:
+       mutex_unlock(&tsdata->mutex);
+       return error ?: count;
+}
+
+static HYCON_ATTR_U8(threshold, 0644, HY46XX_THRESHOLD, 0, 255);
+static HYCON_ATTR_BOOL(glove_enable, 0644, HY46XX_GLOVE_EN);
+static HYCON_ATTR_U8(report_speed, 0644, HY46XX_REPORT_SPEED, 0, 255);
+static HYCON_ATTR_BOOL(noise_filter_enable, 0644, HY46XX_PWR_NOISE_EN);
+static HYCON_ATTR_U8(filter_data, 0644, HY46XX_FILTER_DATA, 0, 5);
+static HYCON_ATTR_U8(gain, 0644, HY46XX_GAIN, 0, 5);
+static HYCON_ATTR_U8(edge_offset, 0644, HY46XX_EDGE_OFFSET, 0, 5);
+static HYCON_ATTR_U8(fw_version, 0444, HY46XX_FW_VERSION, 0, 255);
+static HYCON_ATTR_U8(lib_version, 0444, HY46XX_LIB_VERSION, 0, 255);
+static HYCON_ATTR_U8(tp_information, 0444, HY46XX_TP_INFO, 0, 255);
+static HYCON_ATTR_U8(tp_chip_id, 0444, HY46XX_TP_CHIP_ID, 0, 255);
+static HYCON_ATTR_U8(bootloader_version, 0444, HY46XX_BOOT_VER, 0, 255);
+
+static struct attribute *hycon_hy46xx_attrs[] = {
+       &hycon_hy46xx_attr_threshold.dattr.attr,
+       &hycon_hy46xx_attr_glove_enable.dattr.attr,
+       &hycon_hy46xx_attr_report_speed.dattr.attr,
+       &hycon_hy46xx_attr_noise_filter_enable.dattr.attr,
+       &hycon_hy46xx_attr_filter_data.dattr.attr,
+       &hycon_hy46xx_attr_gain.dattr.attr,
+       &hycon_hy46xx_attr_edge_offset.dattr.attr,
+       &hycon_hy46xx_attr_fw_version.dattr.attr,
+       &hycon_hy46xx_attr_lib_version.dattr.attr,
+       &hycon_hy46xx_attr_tp_information.dattr.attr,
+       &hycon_hy46xx_attr_tp_chip_id.dattr.attr,
+       &hycon_hy46xx_attr_bootloader_version.dattr.attr,
+       NULL
+};
+
+static const struct attribute_group hycon_hy46xx_attr_group = {
+       .attrs = hycon_hy46xx_attrs,
+};
+
+static void hycon_hy46xx_get_defaults(struct device *dev, struct hycon_hy46xx_data *tsdata)
+{
+       bool val_bool;
+       int error;
+       u32 val;
+
+       error = device_property_read_u32(dev, "hycon,threshold", &val);
+       if (!error) {
+               error = regmap_write(tsdata->regmap, HY46XX_THRESHOLD, val);
+               if (error < 0)
+                       goto out;
+
+               tsdata->threshold = val;
+       }
+
+       val_bool = device_property_read_bool(dev, "hycon,glove-enable");
+       error = regmap_write(tsdata->regmap, HY46XX_GLOVE_EN, val_bool);
+       if (error < 0)
+               goto out;
+       tsdata->glove_enable = val_bool;
+
+       error = device_property_read_u32(dev, "hycon,report-speed-hz", &val);
+       if (!error) {
+               error = regmap_write(tsdata->regmap, HY46XX_REPORT_SPEED, val);
+               if (error < 0)
+                       goto out;
+
+               tsdata->report_speed = val;
+       }
+
+       val_bool = device_property_read_bool(dev, "hycon,noise-filter-enable");
+       error = regmap_write(tsdata->regmap, HY46XX_PWR_NOISE_EN, val_bool);
+       if (error < 0)
+               goto out;
+       tsdata->noise_filter_enable = val_bool;
+
+       error = device_property_read_u32(dev, "hycon,filter-data", &val);
+       if (!error) {
+               error = regmap_write(tsdata->regmap, HY46XX_FILTER_DATA, val);
+               if (error < 0)
+                       goto out;
+
+               tsdata->filter_data = val;
+       }
+
+       error = device_property_read_u32(dev, "hycon,gain", &val);
+       if (!error) {
+               error = regmap_write(tsdata->regmap, HY46XX_GAIN, val);
+               if (error < 0)
+                       goto out;
+
+               tsdata->gain = val;
+       }
+
+       error = device_property_read_u32(dev, "hycon,edge-offset", &val);
+       if (!error) {
+               error = regmap_write(tsdata->regmap, HY46XX_EDGE_OFFSET, val);
+               if (error < 0)
+                       goto out;
+
+               tsdata->edge_offset = val;
+       }
+
+       return;
+out:
+       dev_err(&tsdata->client->dev, "Failed to set default settings");
+}
+
+static void hycon_hy46xx_get_parameters(struct hycon_hy46xx_data *tsdata)
+{
+       int error;
+       u32 val;
+
+       error = regmap_read(tsdata->regmap, HY46XX_THRESHOLD, &val);
+       if (error < 0)
+               goto out;
+       tsdata->threshold = val;
+
+       error = regmap_read(tsdata->regmap, HY46XX_GLOVE_EN, &val);
+       if (error < 0)
+               goto out;
+       tsdata->glove_enable = val;
+
+       error = regmap_read(tsdata->regmap, HY46XX_REPORT_SPEED, &val);
+       if (error < 0)
+               goto out;
+       tsdata->report_speed = val;
+
+       error = regmap_read(tsdata->regmap, HY46XX_PWR_NOISE_EN, &val);
+       if (error < 0)
+               goto out;
+       tsdata->noise_filter_enable = val;
+
+       error = regmap_read(tsdata->regmap, HY46XX_FILTER_DATA, &val);
+       if (error < 0)
+               goto out;
+       tsdata->filter_data = val;
+
+       error = regmap_read(tsdata->regmap, HY46XX_GAIN, &val);
+       if (error < 0)
+               goto out;
+       tsdata->gain = val;
+
+       error = regmap_read(tsdata->regmap, HY46XX_EDGE_OFFSET, &val);
+       if (error < 0)
+               goto out;
+       tsdata->edge_offset = val;
+
+       error = regmap_read(tsdata->regmap, HY46XX_RX_NR_USED, &val);
+       if (error < 0)
+               goto out;
+       tsdata->rx_number_used = val;
+
+       error = regmap_read(tsdata->regmap, HY46XX_TX_NR_USED, &val);
+       if (error < 0)
+               goto out;
+       tsdata->tx_number_used = val;
+
+       error = regmap_read(tsdata->regmap, HY46XX_PWR_MODE, &val);
+       if (error < 0)
+               goto out;
+       tsdata->power_mode = val;
+
+       error = regmap_read(tsdata->regmap, HY46XX_FW_VERSION, &val);
+       if (error < 0)
+               goto out;
+       tsdata->fw_version = val;
+
+       error = regmap_read(tsdata->regmap, HY46XX_LIB_VERSION, &val);
+       if (error < 0)
+               goto out;
+       tsdata->lib_version = val;
+
+       error = regmap_read(tsdata->regmap, HY46XX_TP_INFO, &val);
+       if (error < 0)
+               goto out;
+       tsdata->tp_information = val;
+
+       error = regmap_read(tsdata->regmap, HY46XX_TP_CHIP_ID, &val);
+       if (error < 0)
+               goto out;
+       tsdata->tp_chip_id = val;
+
+       error = regmap_read(tsdata->regmap, HY46XX_BOOT_VER, &val);
+       if (error < 0)
+               goto out;
+       tsdata->bootloader_version = val;
+
+       return;
+out:
+       dev_err(&tsdata->client->dev, "Failed to read default settings");
+}
+
+static void hycon_hy46xx_disable_regulator(void *arg)
+{
+       struct hycon_hy46xx_data *data = arg;
+
+       regulator_disable(data->vcc);
+}
+
+static int hycon_hy46xx_probe(struct i2c_client *client,
+                                        const struct i2c_device_id *id)
+{
+       struct hycon_hy46xx_data *tsdata;
+       struct input_dev *input;
+       int error;
+
+       dev_dbg(&client->dev, "probing for HYCON HY46XX I2C\n");
+
+       tsdata = devm_kzalloc(&client->dev, sizeof(*tsdata), GFP_KERNEL);
+       if (!tsdata)
+               return -ENOMEM;
+
+       tsdata->vcc = devm_regulator_get(&client->dev, "vcc");
+       if (IS_ERR(tsdata->vcc)) {
+               error = PTR_ERR(tsdata->vcc);
+               if (error != -EPROBE_DEFER)
+                       dev_err(&client->dev,
+                               "failed to request regulator: %d\n", error);
+               return error;
+       }
+
+       error = regulator_enable(tsdata->vcc);
+       if (error < 0) {
+               dev_err(&client->dev, "failed to enable vcc: %d\n", error);
+               return error;
+       }
+
+       error = devm_add_action_or_reset(&client->dev,
+                                        hycon_hy46xx_disable_regulator,
+                                        tsdata);
+       if (error)
+               return error;
+
+       tsdata->reset_gpio = devm_gpiod_get_optional(&client->dev,
+                                                    "reset", GPIOD_OUT_LOW);
+       if (IS_ERR(tsdata->reset_gpio)) {
+               error = PTR_ERR(tsdata->reset_gpio);
+               dev_err(&client->dev,
+                       "Failed to request GPIO reset pin, error %d\n", error);
+               return error;
+       }
+
+       if (tsdata->reset_gpio) {
+               usleep_range(5000, 6000);
+               gpiod_set_value_cansleep(tsdata->reset_gpio, 1);
+               usleep_range(5000, 6000);
+               gpiod_set_value_cansleep(tsdata->reset_gpio, 0);
+               msleep(1000);
+       }
+
+       input = devm_input_allocate_device(&client->dev);
+       if (!input) {
+               dev_err(&client->dev, "failed to allocate input device.\n");
+               return -ENOMEM;
+       }
+
+       mutex_init(&tsdata->mutex);
+       tsdata->client = client;
+       tsdata->input = input;
+
+       tsdata->regmap = devm_regmap_init_i2c(client,
+                                             &hycon_hy46xx_i2c_regmap_config);
+       if (IS_ERR(tsdata->regmap)) {
+               dev_err(&client->dev, "regmap allocation failed\n");
+               return PTR_ERR(tsdata->regmap);
+       }
+
+       hycon_hy46xx_get_defaults(&client->dev, tsdata);
+       hycon_hy46xx_get_parameters(tsdata);
+
+       input->name = "Hycon Capacitive Touch";
+       input->id.bustype = BUS_I2C;
+       input->dev.parent = &client->dev;
+
+       input_set_abs_params(input, ABS_MT_POSITION_X, 0, -1, 0, 0);
+       input_set_abs_params(input, ABS_MT_POSITION_Y, 0, -1, 0, 0);
+
+       touchscreen_parse_properties(input, true, &tsdata->prop);
+
+       error = input_mt_init_slots(input, HY46XX_MAX_SUPPORTED_POINTS,
+                                   INPUT_MT_DIRECT);
+       if (error) {
+               dev_err(&client->dev, "Unable to init MT slots.\n");
+               return error;
+       }
+
+       i2c_set_clientdata(client, tsdata);
+
+       error = devm_request_threaded_irq(&client->dev, client->irq,
+                                         NULL, hycon_hy46xx_isr, IRQF_ONESHOT,
+                                         client->name, tsdata);
+       if (error) {
+               dev_err(&client->dev, "Unable to request touchscreen IRQ.\n");
+               return error;
+       }
+
+       error = devm_device_add_group(&client->dev, &hycon_hy46xx_attr_group);
+       if (error)
+               return error;
+
+       error = input_register_device(input);
+       if (error)
+               return error;
+
+       dev_dbg(&client->dev,
+               "HYCON HY46XX initialized: IRQ %d, Reset pin %d.\n",
+               client->irq,
+               tsdata->reset_gpio ? desc_to_gpio(tsdata->reset_gpio) : -1);
+
+       return 0;
+}
+
+static const struct i2c_device_id hycon_hy46xx_id[] = {
+       { .name = "hy4613" },
+       { .name = "hy4614" },
+       { .name = "hy4621" },
+       { .name = "hy4623" },
+       { .name = "hy4633" },
+       { .name = "hy4635" },
+       { /* sentinel */ }
+};
+MODULE_DEVICE_TABLE(i2c, hycon_hy46xx_id);
+
+static const struct of_device_id hycon_hy46xx_of_match[] = {
+       { .compatible = "hycon,hy4613" },
+       { .compatible = "hycon,hy4614" },
+       { .compatible = "hycon,hy4621" },
+       { .compatible = "hycon,hy4623" },
+       { .compatible = "hycon,hy4633" },
+       { .compatible = "hycon,hy4635" },
+       { /* sentinel */ }
+};
+MODULE_DEVICE_TABLE(of, hycon_hy46xx_of_match);
+
+static struct i2c_driver hycon_hy46xx_driver = {
+       .driver = {
+               .name = "hycon_hy46xx",
+               .of_match_table = hycon_hy46xx_of_match,
+               .probe_type = PROBE_PREFER_ASYNCHRONOUS,
+       },
+       .id_table = hycon_hy46xx_id,
+       .probe    = hycon_hy46xx_probe,
+};
+
+module_i2c_driver(hycon_hy46xx_driver);
+
+MODULE_AUTHOR("Giulio Benetti <giulio.benetti@benettiengineering.com>");
+MODULE_DESCRIPTION("HYCON HY46XX I2C Touchscreen Driver");
+MODULE_LICENSE("GPL v2");
index d8fccf0..30576a5 100644 (file)
@@ -87,7 +87,7 @@ static bool ili210x_touchdata_to_coords(const u8 *touchdata,
                                        unsigned int *x, unsigned int *y,
                                        unsigned int *z)
 {
-       if (touchdata[0] & BIT(finger))
+       if (!(touchdata[0] & BIT(finger)))
                return false;
 
        *x = get_unaligned_be16(touchdata + 1 + (finger * 4) + 0);
diff --git a/drivers/input/touchscreen/ilitek_ts_i2c.c b/drivers/input/touchscreen/ilitek_ts_i2c.c
new file mode 100644 (file)
index 0000000..c5d259c
--- /dev/null
@@ -0,0 +1,690 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * ILITEK Touch IC driver for 23XX, 25XX and Lego series
+ *
+ * Copyright (C) 2011 ILI Technology Corporation.
+ * Copyright (C) 2020 Luca Hsu <luca_hsu@ilitek.com>
+ * Copyright (C) 2021 Joe Hung <joe_hung@ilitek.com>
+ */
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/input.h>
+#include <linux/input/mt.h>
+#include <linux/i2c.h>
+#include <linux/slab.h>
+#include <linux/delay.h>
+#include <linux/interrupt.h>
+#include <linux/gpio.h>
+#include <linux/gpio/consumer.h>
+#include <linux/errno.h>
+#include <linux/acpi.h>
+#include <linux/input/touchscreen.h>
+#include <asm/unaligned.h>
+
+
+#define ILITEK_TS_NAME                                 "ilitek_ts"
+#define BL_V1_8                                                0x108
+#define BL_V1_7                                                0x107
+#define BL_V1_6                                                0x106
+
+#define ILITEK_TP_CMD_GET_TP_RES                       0x20
+#define ILITEK_TP_CMD_GET_SCRN_RES                     0x21
+#define ILITEK_TP_CMD_SET_IC_SLEEP                     0x30
+#define ILITEK_TP_CMD_SET_IC_WAKE                      0x31
+#define ILITEK_TP_CMD_GET_FW_VER                       0x40
+#define ILITEK_TP_CMD_GET_PRL_VER                      0x42
+#define ILITEK_TP_CMD_GET_MCU_VER                      0x61
+#define ILITEK_TP_CMD_GET_IC_MODE                      0xC0
+
+#define REPORT_COUNT_ADDRESS                           61
+#define ILITEK_SUPPORT_MAX_POINT                       40
+
+struct ilitek_protocol_info {
+       u16 ver;
+       u8 ver_major;
+};
+
+struct ilitek_ts_data {
+       struct i2c_client               *client;
+       struct gpio_desc                *reset_gpio;
+       struct input_dev                *input_dev;
+       struct touchscreen_properties   prop;
+
+       const struct ilitek_protocol_map *ptl_cb_func;
+       struct ilitek_protocol_info     ptl;
+
+       char                            product_id[30];
+       u16                             mcu_ver;
+       u8                              ic_mode;
+       u8                              firmware_ver[8];
+
+       s32                             reset_time;
+       s32                             screen_max_x;
+       s32                             screen_max_y;
+       s32                             screen_min_x;
+       s32                             screen_min_y;
+       s32                             max_tp;
+};
+
+struct ilitek_protocol_map {
+       u16 cmd;
+       const char *name;
+       int (*func)(struct ilitek_ts_data *ts, u16 cmd, u8 *inbuf, u8 *outbuf);
+};
+
+enum ilitek_cmds {
+       /* common cmds */
+       GET_PTL_VER = 0,
+       GET_FW_VER,
+       GET_SCRN_RES,
+       GET_TP_RES,
+       GET_IC_MODE,
+       GET_MCU_VER,
+       SET_IC_SLEEP,
+       SET_IC_WAKE,
+
+       /* ALWAYS keep at the end */
+       MAX_CMD_CNT
+};
+
+/* ILITEK I2C R/W APIs */
+static int ilitek_i2c_write_and_read(struct ilitek_ts_data *ts,
+                                    u8 *cmd, int write_len, int delay,
+                                    u8 *data, int read_len)
+{
+       int error;
+       struct i2c_client *client = ts->client;
+       struct i2c_msg msgs[] = {
+               {
+                       .addr = client->addr,
+                       .flags = 0,
+                       .len = write_len,
+                       .buf = cmd,
+               },
+               {
+                       .addr = client->addr,
+                       .flags = I2C_M_RD,
+                       .len = read_len,
+                       .buf = data,
+               },
+       };
+
+       if (delay == 0 && write_len > 0 && read_len > 0) {
+               error = i2c_transfer(client->adapter, msgs, ARRAY_SIZE(msgs));
+               if (error < 0)
+                       return error;
+       } else {
+               if (write_len > 0) {
+                       error = i2c_transfer(client->adapter, msgs, 1);
+                       if (error < 0)
+                               return error;
+               }
+               if (delay > 0)
+                       mdelay(delay);
+
+               if (read_len > 0) {
+                       error = i2c_transfer(client->adapter, msgs + 1, 1);
+                       if (error < 0)
+                               return error;
+               }
+       }
+
+       return 0;
+}
+
+/* ILITEK ISR APIs */
+static void ilitek_touch_down(struct ilitek_ts_data *ts, unsigned int id,
+                             unsigned int x, unsigned int y)
+{
+       struct input_dev *input = ts->input_dev;
+
+       input_mt_slot(input, id);
+       input_mt_report_slot_state(input, MT_TOOL_FINGER, true);
+
+       touchscreen_report_pos(input, &ts->prop, x, y, true);
+}
+
+static int ilitek_process_and_report_v6(struct ilitek_ts_data *ts)
+{
+       int error = 0;
+       u8 buf[512];
+       int packet_len = 5;
+       int packet_max_point = 10;
+       int report_max_point;
+       int i, count;
+       struct input_dev *input = ts->input_dev;
+       struct device *dev = &ts->client->dev;
+       unsigned int x, y, status, id;
+
+       error = ilitek_i2c_write_and_read(ts, NULL, 0, 0, buf, 64);
+       if (error) {
+               dev_err(dev, "get touch info failed, err:%d\n", error);
+               goto err_sync_frame;
+       }
+
+       report_max_point = buf[REPORT_COUNT_ADDRESS];
+       if (report_max_point > ts->max_tp) {
+               dev_err(dev, "FW report max point:%d > panel info. max:%d\n",
+                       report_max_point, ts->max_tp);
+               error = -EINVAL;
+               goto err_sync_frame;
+       }
+
+       count = DIV_ROUND_UP(report_max_point, packet_max_point);
+       for (i = 1; i < count; i++) {
+               error = ilitek_i2c_write_and_read(ts, NULL, 0, 0,
+                                                 buf + i * 64, 64);
+               if (error) {
+                       dev_err(dev, "get touch info. failed, cnt:%d, err:%d\n",
+                               count, error);
+                       goto err_sync_frame;
+               }
+       }
+
+       for (i = 0; i < report_max_point; i++) {
+               status = buf[i * packet_len + 1] & 0x40;
+               if (!status)
+                       continue;
+
+               id = buf[i * packet_len + 1] & 0x3F;
+
+               x = get_unaligned_le16(buf + i * packet_len + 2);
+               y = get_unaligned_le16(buf + i * packet_len + 4);
+
+               if (x > ts->screen_max_x || x < ts->screen_min_x ||
+                   y > ts->screen_max_y || y < ts->screen_min_y) {
+                       dev_warn(dev, "invalid position, X[%d,%u,%d], Y[%d,%u,%d]\n",
+                                ts->screen_min_x, x, ts->screen_max_x,
+                                ts->screen_min_y, y, ts->screen_max_y);
+                       continue;
+               }
+
+               ilitek_touch_down(ts, id, x, y);
+       }
+
+err_sync_frame:
+       input_mt_sync_frame(input);
+       input_sync(input);
+       return error;
+}
+
+/* APIs of cmds for ILITEK Touch IC */
+static int api_protocol_set_cmd(struct ilitek_ts_data *ts,
+                               u16 idx, u8 *inbuf, u8 *outbuf)
+{
+       u16 cmd;
+       int error;
+
+       if (idx >= MAX_CMD_CNT)
+               return -EINVAL;
+
+       cmd = ts->ptl_cb_func[idx].cmd;
+       error = ts->ptl_cb_func[idx].func(ts, cmd, inbuf, outbuf);
+       if (error)
+               return error;
+
+       return 0;
+}
+
+static int api_protocol_get_ptl_ver(struct ilitek_ts_data *ts,
+                                   u16 cmd, u8 *inbuf, u8 *outbuf)
+{
+       int error;
+       u8 buf[64];
+
+       buf[0] = cmd;
+       error = ilitek_i2c_write_and_read(ts, buf, 1, 5, outbuf, 3);
+       if (error)
+               return error;
+
+       ts->ptl.ver = get_unaligned_be16(outbuf);
+       ts->ptl.ver_major = outbuf[0];
+
+       return 0;
+}
+
+static int api_protocol_get_mcu_ver(struct ilitek_ts_data *ts,
+                                   u16 cmd, u8 *inbuf, u8 *outbuf)
+{
+       int error;
+       u8 buf[64];
+
+       buf[0] = cmd;
+       error = ilitek_i2c_write_and_read(ts, buf, 1, 5, outbuf, 32);
+       if (error)
+               return error;
+
+       ts->mcu_ver = get_unaligned_le16(outbuf);
+       memset(ts->product_id, 0, sizeof(ts->product_id));
+       memcpy(ts->product_id, outbuf + 6, 26);
+
+       return 0;
+}
+
+static int api_protocol_get_fw_ver(struct ilitek_ts_data *ts,
+                                  u16 cmd, u8 *inbuf, u8 *outbuf)
+{
+       int error;
+       u8 buf[64];
+
+       buf[0] = cmd;
+       error = ilitek_i2c_write_and_read(ts, buf, 1, 5, outbuf, 8);
+       if (error)
+               return error;
+
+       memcpy(ts->firmware_ver, outbuf, 8);
+
+       return 0;
+}
+
+static int api_protocol_get_scrn_res(struct ilitek_ts_data *ts,
+                                    u16 cmd, u8 *inbuf, u8 *outbuf)
+{
+       int error;
+       u8 buf[64];
+
+       buf[0] = cmd;
+       error = ilitek_i2c_write_and_read(ts, buf, 1, 5, outbuf, 8);
+       if (error)
+               return error;
+
+       ts->screen_min_x = get_unaligned_le16(outbuf);
+       ts->screen_min_y = get_unaligned_le16(outbuf + 2);
+       ts->screen_max_x = get_unaligned_le16(outbuf + 4);
+       ts->screen_max_y = get_unaligned_le16(outbuf + 6);
+
+       return 0;
+}
+
+static int api_protocol_get_tp_res(struct ilitek_ts_data *ts,
+                                  u16 cmd, u8 *inbuf, u8 *outbuf)
+{
+       int error;
+       u8 buf[64];
+
+       buf[0] = cmd;
+       error = ilitek_i2c_write_and_read(ts, buf, 1, 5, outbuf, 15);
+       if (error)
+               return error;
+
+       ts->max_tp = outbuf[8];
+       if (ts->max_tp > ILITEK_SUPPORT_MAX_POINT) {
+               dev_err(&ts->client->dev, "Invalid MAX_TP:%d from FW\n",
+                       ts->max_tp);
+               return -EINVAL;
+       }
+
+       return 0;
+}
+
+static int api_protocol_get_ic_mode(struct ilitek_ts_data *ts,
+                                   u16 cmd, u8 *inbuf, u8 *outbuf)
+{
+       int error;
+       u8 buf[64];
+
+       buf[0] = cmd;
+       error = ilitek_i2c_write_and_read(ts, buf, 1, 5, outbuf, 2);
+       if (error)
+               return error;
+
+       ts->ic_mode = outbuf[0];
+       return 0;
+}
+
+static int api_protocol_set_ic_sleep(struct ilitek_ts_data *ts,
+                                    u16 cmd, u8 *inbuf, u8 *outbuf)
+{
+       u8 buf[64];
+
+       buf[0] = cmd;
+       return ilitek_i2c_write_and_read(ts, buf, 1, 0, NULL, 0);
+}
+
+static int api_protocol_set_ic_wake(struct ilitek_ts_data *ts,
+                                   u16 cmd, u8 *inbuf, u8 *outbuf)
+{
+       u8 buf[64];
+
+       buf[0] = cmd;
+       return ilitek_i2c_write_and_read(ts, buf, 1, 0, NULL, 0);
+}
+
+static const struct ilitek_protocol_map ptl_func_map[] = {
+       /* common cmds */
+       [GET_PTL_VER] = {
+               ILITEK_TP_CMD_GET_PRL_VER, "GET_PTL_VER",
+               api_protocol_get_ptl_ver
+       },
+       [GET_FW_VER] = {
+               ILITEK_TP_CMD_GET_FW_VER, "GET_FW_VER",
+               api_protocol_get_fw_ver
+       },
+       [GET_SCRN_RES] = {
+               ILITEK_TP_CMD_GET_SCRN_RES, "GET_SCRN_RES",
+               api_protocol_get_scrn_res
+       },
+       [GET_TP_RES] = {
+               ILITEK_TP_CMD_GET_TP_RES, "GET_TP_RES",
+               api_protocol_get_tp_res
+       },
+       [GET_IC_MODE] = {
+               ILITEK_TP_CMD_GET_IC_MODE, "GET_IC_MODE",
+                          api_protocol_get_ic_mode
+       },
+       [GET_MCU_VER] = {
+               ILITEK_TP_CMD_GET_MCU_VER, "GET_MOD_VER",
+                          api_protocol_get_mcu_ver
+       },
+       [SET_IC_SLEEP] = {
+               ILITEK_TP_CMD_SET_IC_SLEEP, "SET_IC_SLEEP",
+               api_protocol_set_ic_sleep
+       },
+       [SET_IC_WAKE] = {
+               ILITEK_TP_CMD_SET_IC_WAKE, "SET_IC_WAKE",
+               api_protocol_set_ic_wake
+       },
+};
+
+/* Probe APIs */
+static void ilitek_reset(struct ilitek_ts_data *ts, int delay)
+{
+       if (ts->reset_gpio) {
+               gpiod_set_value(ts->reset_gpio, 1);
+               mdelay(10);
+               gpiod_set_value(ts->reset_gpio, 0);
+               mdelay(delay);
+       }
+}
+
+static int ilitek_protocol_init(struct ilitek_ts_data *ts)
+{
+       int error;
+       u8 outbuf[64];
+
+       ts->ptl_cb_func = ptl_func_map;
+       ts->reset_time = 600;
+
+       error = api_protocol_set_cmd(ts, GET_PTL_VER, NULL, outbuf);
+       if (error)
+               return error;
+
+       /* Protocol v3 is not support currently */
+       if (ts->ptl.ver_major == 0x3 ||
+           ts->ptl.ver == BL_V1_6 ||
+           ts->ptl.ver == BL_V1_7)
+               return -EINVAL;
+
+       return 0;
+}
+
+static int ilitek_read_tp_info(struct ilitek_ts_data *ts, bool boot)
+{
+       u8 outbuf[256];
+       int error;
+
+       error = api_protocol_set_cmd(ts, GET_PTL_VER, NULL, outbuf);
+       if (error)
+               return error;
+
+       error = api_protocol_set_cmd(ts, GET_MCU_VER, NULL, outbuf);
+       if (error)
+               return error;
+
+       error = api_protocol_set_cmd(ts, GET_FW_VER, NULL, outbuf);
+       if (error)
+               return error;
+
+       if (boot) {
+               error = api_protocol_set_cmd(ts, GET_SCRN_RES, NULL,
+                                            outbuf);
+               if (error)
+                       return error;
+       }
+
+       error = api_protocol_set_cmd(ts, GET_TP_RES, NULL, outbuf);
+       if (error)
+               return error;
+
+       error = api_protocol_set_cmd(ts, GET_IC_MODE, NULL, outbuf);
+       if (error)
+               return error;
+
+       return 0;
+}
+
+static int ilitek_input_dev_init(struct device *dev, struct ilitek_ts_data *ts)
+{
+       int error;
+       struct input_dev *input;
+
+       input = devm_input_allocate_device(dev);
+       if (!input)
+               return -ENOMEM;
+
+       ts->input_dev = input;
+       input->name = ILITEK_TS_NAME;
+       input->id.bustype = BUS_I2C;
+
+       __set_bit(INPUT_PROP_DIRECT, input->propbit);
+
+       input_set_abs_params(input, ABS_MT_POSITION_X,
+                            ts->screen_min_x, ts->screen_max_x, 0, 0);
+       input_set_abs_params(input, ABS_MT_POSITION_Y,
+                            ts->screen_min_y, ts->screen_max_y, 0, 0);
+
+       touchscreen_parse_properties(input, true, &ts->prop);
+
+       error = input_mt_init_slots(input, ts->max_tp,
+                                   INPUT_MT_DIRECT | INPUT_MT_DROP_UNUSED);
+       if (error) {
+               dev_err(dev, "initialize MT slots failed, err:%d\n", error);
+               return error;
+       }
+
+       error = input_register_device(input);
+       if (error) {
+               dev_err(dev, "register input device failed, err:%d\n", error);
+               return error;
+       }
+
+       return 0;
+}
+
+static irqreturn_t ilitek_i2c_isr(int irq, void *dev_id)
+{
+       struct ilitek_ts_data *ts = dev_id;
+       int error;
+
+       error = ilitek_process_and_report_v6(ts);
+       if (error < 0) {
+               dev_err(&ts->client->dev, "[%s] err:%d\n", __func__, error);
+               return IRQ_NONE;
+       }
+
+       return IRQ_HANDLED;
+}
+
+static ssize_t firmware_version_show(struct device *dev,
+                                    struct device_attribute *attr, char *buf)
+{
+       struct i2c_client *client = to_i2c_client(dev);
+       struct ilitek_ts_data *ts = i2c_get_clientdata(client);
+
+       return scnprintf(buf, PAGE_SIZE,
+                        "fw version: [%02X%02X.%02X%02X.%02X%02X.%02X%02X]\n",
+                        ts->firmware_ver[0], ts->firmware_ver[1],
+                        ts->firmware_ver[2], ts->firmware_ver[3],
+                        ts->firmware_ver[4], ts->firmware_ver[5],
+                        ts->firmware_ver[6], ts->firmware_ver[7]);
+}
+static DEVICE_ATTR_RO(firmware_version);
+
+static ssize_t product_id_show(struct device *dev,
+                              struct device_attribute *attr, char *buf)
+{
+       struct i2c_client *client = to_i2c_client(dev);
+       struct ilitek_ts_data *ts = i2c_get_clientdata(client);
+
+       return scnprintf(buf, PAGE_SIZE, "product id: [%04X], module: [%s]\n",
+                        ts->mcu_ver, ts->product_id);
+}
+static DEVICE_ATTR_RO(product_id);
+
+static struct attribute *ilitek_sysfs_attrs[] = {
+       &dev_attr_firmware_version.attr,
+       &dev_attr_product_id.attr,
+       NULL
+};
+
+static struct attribute_group ilitek_attrs_group = {
+       .attrs = ilitek_sysfs_attrs,
+};
+
+static int ilitek_ts_i2c_probe(struct i2c_client *client,
+                              const struct i2c_device_id *id)
+{
+       struct ilitek_ts_data *ts;
+       struct device *dev = &client->dev;
+       int error;
+
+       if (!i2c_check_functionality(client->adapter, I2C_FUNC_I2C)) {
+               dev_err(dev, "i2c check functionality failed\n");
+               return -ENXIO;
+       }
+
+       ts = devm_kzalloc(dev, sizeof(*ts), GFP_KERNEL);
+       if (!ts)
+               return -ENOMEM;
+
+       ts->client = client;
+       i2c_set_clientdata(client, ts);
+
+       ts->reset_gpio = devm_gpiod_get_optional(dev, "reset", GPIOD_OUT_LOW);
+       if (IS_ERR(ts->reset_gpio)) {
+               error = PTR_ERR(ts->reset_gpio);
+               dev_err(dev, "request gpiod failed: %d", error);
+               return error;
+       }
+
+       ilitek_reset(ts, 1000);
+
+       error = ilitek_protocol_init(ts);
+       if (error) {
+               dev_err(dev, "protocol init failed: %d", error);
+               return error;
+       }
+
+       error = ilitek_read_tp_info(ts, true);
+       if (error) {
+               dev_err(dev, "read tp info failed: %d", error);
+               return error;
+       }
+
+       error = ilitek_input_dev_init(dev, ts);
+       if (error) {
+               dev_err(dev, "input dev init failed: %d", error);
+               return error;
+       }
+
+       error = devm_request_threaded_irq(dev, ts->client->irq,
+                                         NULL, ilitek_i2c_isr, IRQF_ONESHOT,
+                                         "ilitek_touch_irq", ts);
+       if (error) {
+               dev_err(dev, "request threaded irq failed: %d\n", error);
+               return error;
+       }
+
+       error = devm_device_add_group(dev, &ilitek_attrs_group);
+       if (error) {
+               dev_err(dev, "sysfs create group failed: %d\n", error);
+               return error;
+       }
+
+       return 0;
+}
+
+static int __maybe_unused ilitek_suspend(struct device *dev)
+{
+       struct i2c_client *client = to_i2c_client(dev);
+       struct ilitek_ts_data *ts = i2c_get_clientdata(client);
+       int error;
+
+       disable_irq(client->irq);
+
+       if (!device_may_wakeup(dev)) {
+               error = api_protocol_set_cmd(ts, SET_IC_SLEEP, NULL, NULL);
+               if (error)
+                       return error;
+       }
+
+       return 0;
+}
+
+static int __maybe_unused ilitek_resume(struct device *dev)
+{
+       struct i2c_client *client = to_i2c_client(dev);
+       struct ilitek_ts_data *ts = i2c_get_clientdata(client);
+       int error;
+
+       if (!device_may_wakeup(dev)) {
+               error = api_protocol_set_cmd(ts, SET_IC_WAKE, NULL, NULL);
+               if (error)
+                       return error;
+
+               ilitek_reset(ts, ts->reset_time);
+       }
+
+       enable_irq(client->irq);
+
+       return 0;
+}
+
+static SIMPLE_DEV_PM_OPS(ilitek_pm_ops, ilitek_suspend, ilitek_resume);
+
+static const struct i2c_device_id ilitek_ts_i2c_id[] = {
+       { ILITEK_TS_NAME, 0 },
+       { },
+};
+MODULE_DEVICE_TABLE(i2c, ilitek_ts_i2c_id);
+
+#ifdef CONFIG_ACPI
+static const struct acpi_device_id ilitekts_acpi_id[] = {
+       { "ILTK0001", 0 },
+       { },
+};
+MODULE_DEVICE_TABLE(acpi, ilitekts_acpi_id);
+#endif
+
+#ifdef CONFIG_OF
+static const struct of_device_id ilitek_ts_i2c_match[] = {
+       {.compatible = "ilitek,ili2130",},
+       {.compatible = "ilitek,ili2131",},
+       {.compatible = "ilitek,ili2132",},
+       {.compatible = "ilitek,ili2316",},
+       {.compatible = "ilitek,ili2322",},
+       {.compatible = "ilitek,ili2323",},
+       {.compatible = "ilitek,ili2326",},
+       {.compatible = "ilitek,ili2520",},
+       {.compatible = "ilitek,ili2521",},
+       { },
+};
+MODULE_DEVICE_TABLE(of, ilitek_ts_i2c_match);
+#endif
+
+static struct i2c_driver ilitek_ts_i2c_driver = {
+       .driver = {
+               .name = ILITEK_TS_NAME,
+               .pm = &ilitek_pm_ops,
+               .of_match_table = of_match_ptr(ilitek_ts_i2c_match),
+               .acpi_match_table = ACPI_PTR(ilitekts_acpi_id),
+       },
+       .probe = ilitek_ts_i2c_probe,
+       .id_table = ilitek_ts_i2c_id,
+};
+module_i2c_driver(ilitek_ts_i2c_driver);
+
+MODULE_AUTHOR("ILITEK");
+MODULE_DESCRIPTION("ILITEK I2C Touchscreen Driver");
+MODULE_LICENSE("GPL");
index 54f3003..b3fa712 100644 (file)
@@ -8,7 +8,7 @@
  * made available by the vendor. Firmware files may be pushed to the device's
  * nonvolatile memory by writing the filename to the 'fw_file' sysfs control.
  *
- * Link to PC-based configuration tool and data sheet: http://www.azoteq.com/
+ * Link to PC-based configuration tool and datasheet: https://www.azoteq.com/
  */
 
 #include <linux/bits.h>
 #define IQS5XX_NUM_RETRIES     10
 #define IQS5XX_NUM_CONTACTS    5
 #define IQS5XX_WR_BYTES_MAX    2
-#define IQS5XX_XY_RES_MAX      0xFFFE
 
 #define IQS5XX_PROD_NUM_IQS550 40
 #define IQS5XX_PROD_NUM_IQS572 58
 #define IQS5XX_PROD_NUM_IQS525 52
-#define IQS5XX_PROJ_NUM_A000   0
-#define IQS5XX_PROJ_NUM_B000   15
-#define IQS5XX_MAJOR_VER_MIN   2
 
 #define IQS5XX_SHOW_RESET      BIT(7)
 #define IQS5XX_ACK_RESET       BIT(7)
@@ -64,6 +60,7 @@
 #define IQS5XX_SYS_CFG1                0x058F
 #define IQS5XX_X_RES           0x066E
 #define IQS5XX_Y_RES           0x0670
+#define IQS5XX_EXP_FILE                0x0677
 #define IQS5XX_CHKSM           0x83C0
 #define IQS5XX_APP             0x8400
 #define IQS5XX_CSTM            0xBE00
 #define IQS5XX_BL_CMD_CRC      0x03
 #define IQS5XX_BL_BLK_LEN_MAX  64
 #define IQS5XX_BL_ID           0x0200
-#define IQS5XX_BL_STATUS_RESET 0x00
-#define IQS5XX_BL_STATUS_AVAIL 0xA5
 #define IQS5XX_BL_STATUS_NONE  0xEE
 #define IQS5XX_BL_CRC_PASS     0x00
 #define IQS5XX_BL_CRC_FAIL     0x01
 #define IQS5XX_BL_ATTEMPTS     3
 
-struct iqs5xx_private {
-       struct i2c_client *client;
-       struct input_dev *input;
-       struct gpio_desc *reset_gpio;
-       struct touchscreen_properties prop;
-       struct mutex lock;
-       u8 bl_status;
-};
-
 struct iqs5xx_dev_id_info {
        __be16 prod_num;
        __be16 proj_num;
@@ -134,6 +120,16 @@ struct iqs5xx_status {
        struct iqs5xx_touch_data touch_data[IQS5XX_NUM_CONTACTS];
 } __packed;
 
+struct iqs5xx_private {
+       struct i2c_client *client;
+       struct input_dev *input;
+       struct gpio_desc *reset_gpio;
+       struct touchscreen_properties prop;
+       struct mutex lock;
+       struct iqs5xx_dev_id_info dev_id_info;
+       u8 exp_file[2];
+};
+
 static int iqs5xx_read_burst(struct i2c_client *client,
                             u16 reg, void *val, u16 len)
 {
@@ -446,7 +442,7 @@ static int iqs5xx_set_state(struct i2c_client *client, u8 state)
        struct iqs5xx_private *iqs5xx = i2c_get_clientdata(client);
        int error1, error2;
 
-       if (iqs5xx->bl_status == IQS5XX_BL_STATUS_RESET)
+       if (!iqs5xx->dev_id_info.bl_status)
                return 0;
 
        mutex_lock(&iqs5xx->lock);
@@ -504,10 +500,6 @@ static int iqs5xx_axis_init(struct i2c_client *client)
                input->open = iqs5xx_open;
                input->close = iqs5xx_close;
 
-               input_set_capability(input, EV_ABS, ABS_MT_POSITION_X);
-               input_set_capability(input, EV_ABS, ABS_MT_POSITION_Y);
-               input_set_capability(input, EV_ABS, ABS_MT_PRESSURE);
-
                input_set_drvdata(input, iqs5xx);
                iqs5xx->input = input;
        }
@@ -520,26 +512,29 @@ static int iqs5xx_axis_init(struct i2c_client *client)
        if (error)
                return error;
 
-       input_abs_set_max(iqs5xx->input, ABS_MT_POSITION_X, max_x);
-       input_abs_set_max(iqs5xx->input, ABS_MT_POSITION_Y, max_y);
+       input_set_abs_params(iqs5xx->input, ABS_MT_POSITION_X, 0, max_x, 0, 0);
+       input_set_abs_params(iqs5xx->input, ABS_MT_POSITION_Y, 0, max_y, 0, 0);
+       input_set_abs_params(iqs5xx->input, ABS_MT_PRESSURE, 0, U16_MAX, 0, 0);
 
        touchscreen_parse_properties(iqs5xx->input, true, prop);
 
-       if (prop->max_x > IQS5XX_XY_RES_MAX) {
-               dev_err(&client->dev, "Invalid maximum x-coordinate: %u > %u\n",
-                       prop->max_x, IQS5XX_XY_RES_MAX);
+       /*
+        * The device reserves 0xFFFF for coordinates that correspond to slots
+        * which are not in a state of touch.
+        */
+       if (prop->max_x >= U16_MAX || prop->max_y >= U16_MAX) {
+               dev_err(&client->dev, "Invalid touchscreen size: %u*%u\n",
+                       prop->max_x, prop->max_y);
                return -EINVAL;
-       } else if (prop->max_x != max_x) {
+       }
+
+       if (prop->max_x != max_x) {
                error = iqs5xx_write_word(client, IQS5XX_X_RES, prop->max_x);
                if (error)
                        return error;
        }
 
-       if (prop->max_y > IQS5XX_XY_RES_MAX) {
-               dev_err(&client->dev, "Invalid maximum y-coordinate: %u > %u\n",
-                       prop->max_y, IQS5XX_XY_RES_MAX);
-               return -EINVAL;
-       } else if (prop->max_y != max_y) {
+       if (prop->max_y != max_y) {
                error = iqs5xx_write_word(client, IQS5XX_Y_RES, prop->max_y);
                if (error)
                        return error;
@@ -574,7 +569,7 @@ static int iqs5xx_dev_init(struct i2c_client *client)
         * the missing zero is prepended).
         */
        buf[0] = 0;
-       dev_id_info = (struct iqs5xx_dev_id_info *)&buf[(buf[1] > 0) ? 0 : 1];
+       dev_id_info = (struct iqs5xx_dev_id_info *)&buf[buf[1] ? 0 : 1];
 
        switch (be16_to_cpu(dev_id_info->prod_num)) {
        case IQS5XX_PROD_NUM_IQS550:
@@ -587,35 +582,20 @@ static int iqs5xx_dev_init(struct i2c_client *client)
                return -EINVAL;
        }
 
-       switch (be16_to_cpu(dev_id_info->proj_num)) {
-       case IQS5XX_PROJ_NUM_A000:
-               dev_err(&client->dev, "Unsupported project number: %u\n",
-                       be16_to_cpu(dev_id_info->proj_num));
-               return iqs5xx_bl_open(client);
-       case IQS5XX_PROJ_NUM_B000:
-               break;
-       default:
-               dev_err(&client->dev, "Unrecognized project number: %u\n",
-                       be16_to_cpu(dev_id_info->proj_num));
-               return -EINVAL;
-       }
-
-       if (dev_id_info->major_ver < IQS5XX_MAJOR_VER_MIN) {
-               dev_err(&client->dev, "Unsupported major version: %u\n",
-                       dev_id_info->major_ver);
+       /*
+        * With the product number recognized yet shifted by one byte, open the
+        * bootloader and wait for user space to convert the A000 device into a
+        * B000 device via new firmware.
+        */
+       if (buf[1]) {
+               dev_err(&client->dev, "Opening bootloader for A000 device\n");
                return iqs5xx_bl_open(client);
        }
 
-       switch (dev_id_info->bl_status) {
-       case IQS5XX_BL_STATUS_AVAIL:
-       case IQS5XX_BL_STATUS_NONE:
-               break;
-       default:
-               dev_err(&client->dev,
-                       "Unrecognized bootloader status: 0x%02X\n",
-                       dev_id_info->bl_status);
-               return -EINVAL;
-       }
+       error = iqs5xx_read_burst(client, IQS5XX_EXP_FILE,
+                                 iqs5xx->exp_file, sizeof(iqs5xx->exp_file));
+       if (error)
+               return error;
 
        error = iqs5xx_axis_init(client);
        if (error)
@@ -640,7 +620,7 @@ static int iqs5xx_dev_init(struct i2c_client *client)
        if (error)
                return error;
 
-       iqs5xx->bl_status = dev_id_info->bl_status;
+       iqs5xx->dev_id_info = *dev_id_info;
 
        /*
         * The following delay allows ATI to complete before the open and close
@@ -666,7 +646,7 @@ static irqreturn_t iqs5xx_irq(int irq, void *data)
         * RDY output during bootloader mode. If the device operates outside of
         * bootloader mode, the input device is guaranteed to be allocated.
         */
-       if (iqs5xx->bl_status == IQS5XX_BL_STATUS_RESET)
+       if (!iqs5xx->dev_id_info.bl_status)
                return IRQ_NONE;
 
        error = iqs5xx_read_burst(client, IQS5XX_SYS_INFO0,
@@ -852,12 +832,9 @@ static int iqs5xx_fw_file_parse(struct i2c_client *client,
 static int iqs5xx_fw_file_write(struct i2c_client *client, const char *fw_file)
 {
        struct iqs5xx_private *iqs5xx = i2c_get_clientdata(client);
-       int error, error_bl = 0;
+       int error, error_init = 0;
        u8 *pmap;
 
-       if (iqs5xx->bl_status == IQS5XX_BL_STATUS_NONE)
-               return -EPERM;
-
        pmap = kzalloc(IQS5XX_PMAP_LEN, GFP_KERNEL);
        if (!pmap)
                return -ENOMEM;
@@ -875,7 +852,7 @@ static int iqs5xx_fw_file_write(struct i2c_client *client, const char *fw_file)
         */
        disable_irq(client->irq);
 
-       iqs5xx->bl_status = IQS5XX_BL_STATUS_RESET;
+       iqs5xx->dev_id_info.bl_status = 0;
 
        error = iqs5xx_bl_cmd(client, IQS5XX_BL_CMD_VER, 0);
        if (error) {
@@ -895,21 +872,14 @@ static int iqs5xx_fw_file_write(struct i2c_client *client, const char *fw_file)
        error = iqs5xx_bl_verify(client, IQS5XX_CSTM,
                                 pmap + IQS5XX_CHKSM_LEN + IQS5XX_APP_LEN,
                                 IQS5XX_CSTM_LEN);
-       if (error)
-               goto err_reset;
-
-       error = iqs5xx_bl_cmd(client, IQS5XX_BL_CMD_EXEC, 0);
 
 err_reset:
-       if (error) {
-               iqs5xx_reset(client);
-               usleep_range(10000, 10100);
-       }
+       iqs5xx_reset(client);
+       usleep_range(15000, 15100);
 
-       error_bl = error;
-       error = iqs5xx_dev_init(client);
-       if (!error && iqs5xx->bl_status == IQS5XX_BL_STATUS_RESET)
-               error = -EINVAL;
+       error_init = iqs5xx_dev_init(client);
+       if (!iqs5xx->dev_id_info.bl_status)
+               error_init = error_init ? : -EINVAL;
 
        enable_irq(client->irq);
 
@@ -918,10 +888,7 @@ err_reset:
 err_kfree:
        kfree(pmap);
 
-       if (error_bl)
-               return error_bl;
-
-       return error;
+       return error ? : error_init;
 }
 
 static ssize_t fw_file_store(struct device *dev,
@@ -968,14 +935,47 @@ static ssize_t fw_file_store(struct device *dev,
        return count;
 }
 
+static ssize_t fw_info_show(struct device *dev,
+                           struct device_attribute *attr, char *buf)
+{
+       struct iqs5xx_private *iqs5xx = dev_get_drvdata(dev);
+
+       if (!iqs5xx->dev_id_info.bl_status)
+               return -ENODATA;
+
+       return scnprintf(buf, PAGE_SIZE, "%u.%u.%u.%u:%u.%u\n",
+                        be16_to_cpu(iqs5xx->dev_id_info.prod_num),
+                        be16_to_cpu(iqs5xx->dev_id_info.proj_num),
+                        iqs5xx->dev_id_info.major_ver,
+                        iqs5xx->dev_id_info.minor_ver,
+                        iqs5xx->exp_file[0], iqs5xx->exp_file[1]);
+}
+
 static DEVICE_ATTR_WO(fw_file);
+static DEVICE_ATTR_RO(fw_info);
 
 static struct attribute *iqs5xx_attrs[] = {
        &dev_attr_fw_file.attr,
+       &dev_attr_fw_info.attr,
        NULL,
 };
 
+static umode_t iqs5xx_attr_is_visible(struct kobject *kobj,
+                                     struct attribute *attr, int i)
+{
+       struct device *dev = kobj_to_dev(kobj);
+       struct iqs5xx_private *iqs5xx = dev_get_drvdata(dev);
+
+       if (attr == &dev_attr_fw_file.attr &&
+           (iqs5xx->dev_id_info.bl_status == IQS5XX_BL_STATUS_NONE ||
+           !iqs5xx->reset_gpio))
+               return 0;
+
+       return attr->mode;
+}
+
 static const struct attribute_group iqs5xx_attr_group = {
+       .is_visible = iqs5xx_attr_is_visible,
        .attrs = iqs5xx_attrs,
 };
 
@@ -1032,8 +1032,8 @@ static int iqs5xx_probe(struct i2c_client *client,
        i2c_set_clientdata(client, iqs5xx);
        iqs5xx->client = client;
 
-       iqs5xx->reset_gpio = devm_gpiod_get(&client->dev,
-                                           "reset", GPIOD_OUT_LOW);
+       iqs5xx->reset_gpio = devm_gpiod_get_optional(&client->dev,
+                                                    "reset", GPIOD_OUT_LOW);
        if (IS_ERR(iqs5xx->reset_gpio)) {
                error = PTR_ERR(iqs5xx->reset_gpio);
                dev_err(&client->dev, "Failed to request GPIO: %d\n", error);
@@ -1042,9 +1042,6 @@ static int iqs5xx_probe(struct i2c_client *client,
 
        mutex_init(&iqs5xx->lock);
 
-       iqs5xx_reset(client);
-       usleep_range(10000, 10100);
-
        error = iqs5xx_dev_init(client);
        if (error)
                return error;
index b51450b..15b5cb7 100644 (file)
 #define LPC32XX_TSC_AUX_MIN                    0x38
 #define LPC32XX_TSC_AUX_MAX                    0x3C
 
-#define LPC32XX_TSC_STAT_FIFO_OVRRN            (1 << 8)
-#define LPC32XX_TSC_STAT_FIFO_EMPTY            (1 << 7)
+#define LPC32XX_TSC_STAT_FIFO_OVRRN            BIT(8)
+#define LPC32XX_TSC_STAT_FIFO_EMPTY            BIT(7)
 
 #define LPC32XX_TSC_SEL_DEFVAL                 0x0284
 
 #define LPC32XX_TSC_ADCCON_IRQ_TO_FIFO_4       (0x1 << 11)
 #define LPC32XX_TSC_ADCCON_X_SAMPLE_SIZE(s)    ((10 - (s)) << 7)
 #define LPC32XX_TSC_ADCCON_Y_SAMPLE_SIZE(s)    ((10 - (s)) << 4)
-#define LPC32XX_TSC_ADCCON_POWER_UP            (1 << 2)
-#define LPC32XX_TSC_ADCCON_AUTO_EN             (1 << 0)
+#define LPC32XX_TSC_ADCCON_POWER_UP            BIT(2)
+#define LPC32XX_TSC_ADCCON_AUTO_EN             BIT(0)
 
-#define LPC32XX_TSC_FIFO_TS_P_LEVEL            (1 << 31)
+#define LPC32XX_TSC_FIFO_TS_P_LEVEL            BIT(31)
 #define LPC32XX_TSC_FIFO_NORMALIZE_X_VAL(x)    (((x) & 0x03FF0000) >> 16)
 #define LPC32XX_TSC_FIFO_NORMALIZE_Y_VAL(y)    ((y) & 0x000003FF)
 
index 225796a..2745bf1 100644 (file)
@@ -1502,7 +1502,8 @@ static int mip4_probe(struct i2c_client *client, const struct i2c_device_id *id)
 
        error = devm_request_threaded_irq(&client->dev, client->irq,
                                          NULL, mip4_interrupt,
-                                         IRQF_ONESHOT, MIP4_DEVICE_NAME, ts);
+                                         IRQF_ONESHOT | IRQF_NO_AUTOEN,
+                                         MIP4_DEVICE_NAME, ts);
        if (error) {
                dev_err(&client->dev,
                        "Failed to request interrupt %d: %d\n",
@@ -1510,8 +1511,6 @@ static int mip4_probe(struct i2c_client *client, const struct i2c_device_id *id)
                return error;
        }
 
-       disable_irq(client->irq);
-
        error = input_register_device(input);
        if (error) {
                dev_err(&client->dev,
index 16557f5..0efd1a1 100644 (file)
@@ -1,5 +1,5 @@
 // SPDX-License-Identifier: GPL-2.0
-// Melfas MMS114/MMS152 touchscreen device driver
+// Melfas MMS114/MMS136/MMS152 touchscreen device driver
 //
 // Copyright (c) 2012 Samsung Electronics Co., Ltd.
 // Author: Joonyoung Shim <jy0922.shim@samsung.com>
@@ -44,7 +44,8 @@
 #define MMS114_MAX_AREA                        0xff
 
 #define MMS114_MAX_TOUCH               10
-#define MMS114_PACKET_NUM              8
+#define MMS114_EVENT_SIZE              8
+#define MMS136_EVENT_SIZE              6
 
 /* Touch type */
 #define MMS114_TYPE_NONE               0
@@ -53,6 +54,7 @@
 
 enum mms_type {
        TYPE_MMS114     = 114,
+       TYPE_MMS136     = 136,
        TYPE_MMS152     = 152,
        TYPE_MMS345L    = 345,
 };
@@ -209,7 +211,11 @@ static irqreturn_t mms114_interrupt(int irq, void *dev_id)
        if (packet_size <= 0)
                goto out;
 
-       touch_size = packet_size / MMS114_PACKET_NUM;
+       /* MMS136 has slightly different event size */
+       if (data->type == TYPE_MMS136)
+               touch_size = packet_size / MMS136_EVENT_SIZE;
+       else
+               touch_size = packet_size / MMS114_EVENT_SIZE;
 
        error = __mms114_read_reg(data, MMS114_INFORMATION, packet_size,
                        (u8 *)touch);
@@ -275,6 +281,7 @@ static int mms114_get_version(struct mms114_data *data)
                break;
 
        case TYPE_MMS114:
+       case TYPE_MMS136:
                error = __mms114_read_reg(data, MMS114_TSP_REV, 6, buf);
                if (error)
                        return error;
@@ -297,8 +304,8 @@ static int mms114_setup_regs(struct mms114_data *data)
        if (error < 0)
                return error;
 
-       /* Only MMS114 has configuration and power on registers */
-       if (data->type != TYPE_MMS114)
+       /* Only MMS114 and MMS136 have configuration and power on registers */
+       if (data->type != TYPE_MMS114 && data->type != TYPE_MMS136)
                return 0;
 
        error = mms114_set_active(data, true);
@@ -480,7 +487,7 @@ static int mms114_probe(struct i2c_client *client,
                                     0, data->props.max_y, 0, 0);
        }
 
-       if (data->type == TYPE_MMS114) {
+       if (data->type == TYPE_MMS114 || data->type == TYPE_MMS136) {
                /*
                 * The firmware handles movement and pressure fuzz, so
                 * don't duplicate that in software.
@@ -530,13 +537,13 @@ static int mms114_probe(struct i2c_client *client,
        }
 
        error = devm_request_threaded_irq(&client->dev, client->irq,
-                                         NULL, mms114_interrupt, IRQF_ONESHOT,
+                                         NULL, mms114_interrupt,
+                                         IRQF_ONESHOT | IRQF_NO_AUTOEN,
                                          dev_name(&client->dev), data);
        if (error) {
                dev_err(&client->dev, "Failed to register interrupt\n");
                return error;
        }
-       disable_irq(client->irq);
 
        error = input_register_device(data->input_dev);
        if (error) {
@@ -605,6 +612,9 @@ static const struct of_device_id mms114_dt_match[] = {
                .compatible = "melfas,mms114",
                .data = (void *)TYPE_MMS114,
        }, {
+               .compatible = "melfas,mms136",
+               .data = (void *)TYPE_MMS136,
+       }, {
                .compatible = "melfas,mms152",
                .data = (void *)TYPE_MMS152,
        }, {
diff --git a/drivers/input/touchscreen/msg2638.c b/drivers/input/touchscreen/msg2638.c
new file mode 100644 (file)
index 0000000..75536bc
--- /dev/null
@@ -0,0 +1,337 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * Driver for MStar msg2638 touchscreens
+ *
+ * Copyright (c) 2021 Vincent Knecht <vincent.knecht@mailoo.org>
+ *
+ * Checksum and IRQ handler based on mstar_drv_common.c and
+ * mstar_drv_mutual_fw_control.c
+ * Copyright (c) 2006-2012 MStar Semiconductor, Inc.
+ *
+ * Driver structure based on zinitix.c by Michael Srba <Michael.Srba@seznam.cz>
+ */
+
+#include <linux/delay.h>
+#include <linux/gpio/consumer.h>
+#include <linux/i2c.h>
+#include <linux/input.h>
+#include <linux/input/mt.h>
+#include <linux/input/touchscreen.h>
+#include <linux/interrupt.h>
+#include <linux/kernel.h>
+#include <linux/mod_devicetable.h>
+#include <linux/module.h>
+#include <linux/regulator/consumer.h>
+#include <linux/slab.h>
+
+#define MODE_DATA_RAW                  0x5A
+
+#define MAX_SUPPORTED_FINGER_NUM       5
+
+#define CHIP_ON_DELAY_MS               15
+#define FIRMWARE_ON_DELAY_MS           50
+#define RESET_DELAY_MIN_US             10000
+#define RESET_DELAY_MAX_US             11000
+
+struct packet {
+       u8      xy_hi; /* higher bits of x and y coordinates */
+       u8      x_low;
+       u8      y_low;
+       u8      pressure;
+};
+
+struct touch_event {
+       u8      mode;
+       struct  packet pkt[MAX_SUPPORTED_FINGER_NUM];
+       u8      proximity;
+       u8      checksum;
+};
+
+struct msg2638_ts_data {
+       struct i2c_client *client;
+       struct input_dev *input_dev;
+       struct touchscreen_properties prop;
+       struct regulator_bulk_data supplies[2];
+       struct gpio_desc *reset_gpiod;
+};
+
+static u8 msg2638_checksum(u8 *data, u32 length)
+{
+       s32 sum = 0;
+       u32 i;
+
+       for (i = 0; i < length; i++)
+               sum += data[i];
+
+       return (u8)((-sum) & 0xFF);
+}
+
+static irqreturn_t msg2638_ts_irq_handler(int irq, void *msg2638_handler)
+{
+       struct msg2638_ts_data *msg2638 = msg2638_handler;
+       struct i2c_client *client = msg2638->client;
+       struct input_dev *input = msg2638->input_dev;
+       struct touch_event touch_event;
+       u32 len = sizeof(touch_event);
+       struct i2c_msg msg[] = {
+               {
+                       .addr   = client->addr,
+                       .flags  = I2C_M_RD,
+                       .len    = sizeof(touch_event),
+                       .buf    = (u8 *)&touch_event,
+               },
+       };
+       struct packet *p;
+       u16 x, y;
+       int ret;
+       int i;
+
+       ret = i2c_transfer(client->adapter, msg, ARRAY_SIZE(msg));
+       if (ret != ARRAY_SIZE(msg)) {
+               dev_err(&client->dev,
+                       "Failed I2C transfer in irq handler: %d\n",
+                       ret < 0 ? ret : -EIO);
+               goto out;
+       }
+
+       if (touch_event.mode != MODE_DATA_RAW)
+               goto out;
+
+       if (msg2638_checksum((u8 *)&touch_event, len - 1) !=
+                                               touch_event.checksum) {
+               dev_err(&client->dev, "Failed checksum!\n");
+               goto out;
+       }
+
+       for (i = 0; i < MAX_SUPPORTED_FINGER_NUM; i++) {
+               p = &touch_event.pkt[i];
+
+               /* Ignore non-pressed finger data */
+               if (p->xy_hi == 0xFF && p->x_low == 0xFF && p->y_low == 0xFF)
+                       continue;
+
+               x = (((p->xy_hi & 0xF0) << 4) | p->x_low);
+               y = (((p->xy_hi & 0x0F) << 8) | p->y_low);
+
+               input_mt_slot(input, i);
+               input_mt_report_slot_state(input, MT_TOOL_FINGER, true);
+               touchscreen_report_pos(input, &msg2638->prop, x, y, true);
+       }
+
+       input_mt_sync_frame(msg2638->input_dev);
+       input_sync(msg2638->input_dev);
+
+out:
+       return IRQ_HANDLED;
+}
+
+static void msg2638_reset(struct msg2638_ts_data *msg2638)
+{
+       gpiod_set_value_cansleep(msg2638->reset_gpiod, 1);
+       usleep_range(RESET_DELAY_MIN_US, RESET_DELAY_MAX_US);
+       gpiod_set_value_cansleep(msg2638->reset_gpiod, 0);
+       msleep(FIRMWARE_ON_DELAY_MS);
+}
+
+static int msg2638_start(struct msg2638_ts_data *msg2638)
+{
+       int error;
+
+       error = regulator_bulk_enable(ARRAY_SIZE(msg2638->supplies),
+                                     msg2638->supplies);
+       if (error) {
+               dev_err(&msg2638->client->dev,
+                       "Failed to enable regulators: %d\n", error);
+               return error;
+       }
+
+       msleep(CHIP_ON_DELAY_MS);
+
+       msg2638_reset(msg2638);
+
+       enable_irq(msg2638->client->irq);
+
+       return 0;
+}
+
+static int msg2638_stop(struct msg2638_ts_data *msg2638)
+{
+       int error;
+
+       disable_irq(msg2638->client->irq);
+
+       error = regulator_bulk_disable(ARRAY_SIZE(msg2638->supplies),
+                                      msg2638->supplies);
+       if (error) {
+               dev_err(&msg2638->client->dev,
+                       "Failed to disable regulators: %d\n", error);
+               return error;
+       }
+
+       return 0;
+}
+
+static int msg2638_input_open(struct input_dev *dev)
+{
+       struct msg2638_ts_data *msg2638 = input_get_drvdata(dev);
+
+       return msg2638_start(msg2638);
+}
+
+static void msg2638_input_close(struct input_dev *dev)
+{
+       struct msg2638_ts_data *msg2638 = input_get_drvdata(dev);
+
+       msg2638_stop(msg2638);
+}
+
+static int msg2638_init_input_dev(struct msg2638_ts_data *msg2638)
+{
+       struct device *dev = &msg2638->client->dev;
+       struct input_dev *input_dev;
+       int error;
+
+       input_dev = devm_input_allocate_device(dev);
+       if (!input_dev) {
+               dev_err(dev, "Failed to allocate input device.\n");
+               return -ENOMEM;
+       }
+
+       input_set_drvdata(input_dev, msg2638);
+       msg2638->input_dev = input_dev;
+
+       input_dev->name = "MStar TouchScreen";
+       input_dev->phys = "input/ts";
+       input_dev->id.bustype = BUS_I2C;
+       input_dev->open = msg2638_input_open;
+       input_dev->close = msg2638_input_close;
+
+       input_set_capability(input_dev, EV_ABS, ABS_MT_POSITION_X);
+       input_set_capability(input_dev, EV_ABS, ABS_MT_POSITION_Y);
+
+       touchscreen_parse_properties(input_dev, true, &msg2638->prop);
+       if (!msg2638->prop.max_x || !msg2638->prop.max_y) {
+               dev_err(dev, "touchscreen-size-x and/or touchscreen-size-y not set in properties\n");
+               return -EINVAL;
+       }
+
+       error = input_mt_init_slots(input_dev, MAX_SUPPORTED_FINGER_NUM,
+                                   INPUT_MT_DIRECT | INPUT_MT_DROP_UNUSED);
+       if (error) {
+               dev_err(dev, "Failed to initialize MT slots: %d\n", error);
+               return error;
+       }
+
+       error = input_register_device(input_dev);
+       if (error) {
+               dev_err(dev, "Failed to register input device: %d\n", error);
+               return error;
+       }
+
+       return 0;
+}
+
+static int msg2638_ts_probe(struct i2c_client *client)
+{
+       struct device *dev = &client->dev;
+       struct msg2638_ts_data *msg2638;
+       int error;
+
+       if (!i2c_check_functionality(client->adapter, I2C_FUNC_I2C)) {
+               dev_err(dev, "Failed to assert adapter's support for plain I2C.\n");
+               return -ENXIO;
+       }
+
+       msg2638 = devm_kzalloc(dev, sizeof(*msg2638), GFP_KERNEL);
+       if (!msg2638)
+               return -ENOMEM;
+
+       msg2638->client = client;
+       i2c_set_clientdata(client, msg2638);
+
+       msg2638->supplies[0].supply = "vdd";
+       msg2638->supplies[1].supply = "vddio";
+       error = devm_regulator_bulk_get(dev, ARRAY_SIZE(msg2638->supplies),
+                                       msg2638->supplies);
+       if (error) {
+               dev_err(dev, "Failed to get regulators: %d\n", error);
+               return error;
+       }
+
+       msg2638->reset_gpiod = devm_gpiod_get(dev, "reset", GPIOD_OUT_LOW);
+       if (IS_ERR(msg2638->reset_gpiod)) {
+               error = PTR_ERR(msg2638->reset_gpiod);
+               dev_err(dev, "Failed to request reset GPIO: %d\n", error);
+               return error;
+       }
+
+       error = msg2638_init_input_dev(msg2638);
+       if (error) {
+               dev_err(dev, "Failed to initialize input device: %d\n", error);
+               return error;
+       }
+
+       error = devm_request_threaded_irq(dev, client->irq,
+                                         NULL, msg2638_ts_irq_handler,
+                                         IRQF_ONESHOT | IRQF_NO_AUTOEN,
+                                         client->name, msg2638);
+       if (error) {
+               dev_err(dev, "Failed to request IRQ: %d\n", error);
+               return error;
+       }
+
+       return 0;
+}
+
+static int __maybe_unused msg2638_suspend(struct device *dev)
+{
+       struct i2c_client *client = to_i2c_client(dev);
+       struct msg2638_ts_data *msg2638 = i2c_get_clientdata(client);
+
+       mutex_lock(&msg2638->input_dev->mutex);
+
+       if (input_device_enabled(msg2638->input_dev))
+               msg2638_stop(msg2638);
+
+       mutex_unlock(&msg2638->input_dev->mutex);
+
+       return 0;
+}
+
+static int __maybe_unused msg2638_resume(struct device *dev)
+{
+       struct i2c_client *client = to_i2c_client(dev);
+       struct msg2638_ts_data *msg2638 = i2c_get_clientdata(client);
+       int ret = 0;
+
+       mutex_lock(&msg2638->input_dev->mutex);
+
+       if (input_device_enabled(msg2638->input_dev))
+               ret = msg2638_start(msg2638);
+
+       mutex_unlock(&msg2638->input_dev->mutex);
+
+       return ret;
+}
+
+static SIMPLE_DEV_PM_OPS(msg2638_pm_ops, msg2638_suspend, msg2638_resume);
+
+static const struct of_device_id msg2638_of_match[] = {
+       { .compatible = "mstar,msg2638" },
+       { }
+};
+MODULE_DEVICE_TABLE(of, msg2638_of_match);
+
+static struct i2c_driver msg2638_ts_driver = {
+       .probe_new = msg2638_ts_probe,
+       .driver = {
+               .name = "MStar-TS",
+               .pm = &msg2638_pm_ops,
+               .of_match_table = msg2638_of_match,
+       },
+};
+module_i2c_driver(msg2638_ts_driver);
+
+MODULE_AUTHOR("Vincent Knecht <vincent.knecht@mailoo.org>");
+MODULE_DESCRIPTION("MStar MSG2638 touchscreen driver");
+MODULE_LICENSE("GPL v2");
index 8fa2f3b..1ee760b 100644 (file)
@@ -20,6 +20,7 @@
 #include <linux/input/mt.h>
 #include <linux/input/touchscreen.h>
 #include <linux/pm.h>
+#include <linux/pm_runtime.h>
 #include <linux/irq.h>
 #include <linux/regulator/consumer.h>
 
@@ -335,10 +336,8 @@ static int silead_ts_get_id(struct i2c_client *client)
 
        error = i2c_smbus_read_i2c_block_data(client, SILEAD_REG_ID,
                                              sizeof(chip_id), (u8 *)&chip_id);
-       if (error < 0) {
-               dev_err(&client->dev, "Chip ID read error %d\n", error);
+       if (error < 0)
                return error;
-       }
 
        data->chip_id = le32_to_cpu(chip_id);
        dev_info(&client->dev, "Silead chip ID: 0x%8X", data->chip_id);
@@ -351,12 +350,49 @@ static int silead_ts_setup(struct i2c_client *client)
        int error;
        u32 status;
 
+       /*
+        * Some buggy BIOS-es bring up the chip in a stuck state where it
+        * blocks the I2C bus. The following steps are necessary to
+        * unstuck the chip / bus:
+        * 1. Turn off the Silead chip.
+        * 2. Try to do an I2C transfer with the chip, this will fail in
+        *    response to which the I2C-bus-driver will call:
+        *    i2c_recover_bus() which will unstuck the I2C-bus. Note the
+        *    unstuck-ing of the I2C bus only works if we first drop the
+        *    chip off the bus by turning it off.
+        * 3. Turn the chip back on.
+        *
+        * On the x86/ACPI systems were this problem is seen, step 1. and
+        * 3. require making ACPI calls and dealing with ACPI Power
+        * Resources. The workaround below runtime-suspends the chip to
+        * turn it off, leaving it up to the ACPI subsystem to deal with
+        * this.
+        */
+
+       if (device_property_read_bool(&client->dev,
+                                     "silead,stuck-controller-bug")) {
+               pm_runtime_set_active(&client->dev);
+               pm_runtime_enable(&client->dev);
+               pm_runtime_allow(&client->dev);
+
+               pm_runtime_suspend(&client->dev);
+
+               dev_warn(&client->dev, FW_BUG "Stuck I2C bus: please ignore the next 'controller timed out' error\n");
+               silead_ts_get_id(client);
+
+               /* The forbid will also resume the device */
+               pm_runtime_forbid(&client->dev);
+               pm_runtime_disable(&client->dev);
+       }
+
        silead_ts_set_power(client, SILEAD_POWER_OFF);
        silead_ts_set_power(client, SILEAD_POWER_ON);
 
        error = silead_ts_get_id(client);
-       if (error)
+       if (error) {
+               dev_err(&client->dev, "Chip ID read error %d\n", error);
                return error;
+       }
 
        error = silead_ts_init(client);
        if (error)
@@ -486,7 +522,7 @@ static int silead_ts_probe(struct i2c_client *client,
 
        silead_ts_read_props(client);
 
-       /* We must have the IRQ provided by DT or ACPI subsytem */
+       /* We must have the IRQ provided by DT or ACPI subsystem */
        if (client->irq <= 0)
                return -ENODEV;
 
index 9a64e1d..bc11203 100644 (file)
@@ -691,10 +691,9 @@ static int stmfts_probe(struct i2c_client *client,
         * interrupts. To be on the safe side it's better to not enable
         * the interrupts during their request.
         */
-       irq_set_status_flags(client->irq, IRQ_NOAUTOEN);
        err = devm_request_threaded_irq(&client->dev, client->irq,
                                        NULL, stmfts_irq_handler,
-                                       IRQF_ONESHOT,
+                                       IRQF_ONESHOT | IRQF_NO_AUTOEN,
                                        "stmfts_irq", sdata);
        if (err)
                return err;
index 91c60bf..69b08dd 100644 (file)
@@ -19,6 +19,8 @@
 #ifndef _TSC2007_H
 #define _TSC2007_H
 
+struct gpio_desc;
+
 #define TSC2007_MEASURE_TEMP0          (0x0 << 4)
 #define TSC2007_MEASURE_AUX            (0x2 << 4)
 #define TSC2007_MEASURE_TEMP1          (0x4 << 4)
@@ -69,7 +71,7 @@ struct tsc2007 {
        int                     fuzzy;
        int                     fuzzz;
 
-       unsigned int            gpio;
+       struct gpio_desc        *gpiod;
        int                     irq;
 
        wait_queue_head_t       wait;
index 3b80abf..3e871d1 100644 (file)
 
 #include <linux/module.h>
 #include <linux/slab.h>
+#include <linux/gpio/consumer.h>
 #include <linux/input.h>
 #include <linux/interrupt.h>
 #include <linux/i2c.h>
-#include <linux/of_device.h>
-#include <linux/of_gpio.h>
+#include <linux/mod_devicetable.h>
+#include <linux/property.h>
 #include <linux/platform_data/tsc2007.h>
 #include "tsc2007.h"
 
@@ -220,71 +221,58 @@ static void tsc2007_close(struct input_dev *input_dev)
        tsc2007_stop(ts);
 }
 
-#ifdef CONFIG_OF
 static int tsc2007_get_pendown_state_gpio(struct device *dev)
 {
        struct i2c_client *client = to_i2c_client(dev);
        struct tsc2007 *ts = i2c_get_clientdata(client);
 
-       return !gpio_get_value(ts->gpio);
+       return gpiod_get_value(ts->gpiod);
 }
 
-static int tsc2007_probe_dt(struct i2c_client *client, struct tsc2007 *ts)
+static int tsc2007_probe_properties(struct device *dev, struct tsc2007 *ts)
 {
-       struct device_node *np = client->dev.of_node;
        u32 val32;
        u64 val64;
 
-       if (!np) {
-               dev_err(&client->dev, "missing device tree data\n");
-               return -EINVAL;
-       }
-
-       if (!of_property_read_u32(np, "ti,max-rt", &val32))
+       if (!device_property_read_u32(dev, "ti,max-rt", &val32))
                ts->max_rt = val32;
        else
                ts->max_rt = MAX_12BIT;
 
-       if (!of_property_read_u32(np, "ti,fuzzx", &val32))
+       if (!device_property_read_u32(dev, "ti,fuzzx", &val32))
                ts->fuzzx = val32;
 
-       if (!of_property_read_u32(np, "ti,fuzzy", &val32))
+       if (!device_property_read_u32(dev, "ti,fuzzy", &val32))
                ts->fuzzy = val32;
 
-       if (!of_property_read_u32(np, "ti,fuzzz", &val32))
+       if (!device_property_read_u32(dev, "ti,fuzzz", &val32))
                ts->fuzzz = val32;
 
-       if (!of_property_read_u64(np, "ti,poll-period", &val64))
+       if (!device_property_read_u64(dev, "ti,poll-period", &val64))
                ts->poll_period = msecs_to_jiffies(val64);
        else
                ts->poll_period = msecs_to_jiffies(1);
 
-       if (!of_property_read_u32(np, "ti,x-plate-ohms", &val32)) {
+       if (!device_property_read_u32(dev, "ti,x-plate-ohms", &val32)) {
                ts->x_plate_ohms = val32;
        } else {
-               dev_err(&client->dev, "missing ti,x-plate-ohms devicetree property.");
+               dev_err(dev, "Missing ti,x-plate-ohms device property\n");
                return -EINVAL;
        }
 
-       ts->gpio = of_get_gpio(np, 0);
-       if (gpio_is_valid(ts->gpio))
+       ts->gpiod = devm_gpiod_get_optional(dev, NULL, GPIOD_IN);
+       if (IS_ERR(ts->gpiod))
+               return PTR_ERR(ts->gpiod);
+
+       if (ts->gpiod)
                ts->get_pendown_state = tsc2007_get_pendown_state_gpio;
        else
-               dev_warn(&client->dev,
-                        "GPIO not specified in DT (of_get_gpio returned %d)\n",
-                        ts->gpio);
+               dev_warn(dev, "Pen down GPIO is not specified in properties\n");
 
        return 0;
 }
-#else
-static int tsc2007_probe_dt(struct i2c_client *client, struct tsc2007 *ts)
-{
-       dev_err(&client->dev, "platform data is required!\n");
-       return -EINVAL;
-}
-#endif
 
-static int tsc2007_probe_pdev(struct i2c_client *client, struct tsc2007 *ts,
+static int tsc2007_probe_pdev(struct device *dev, struct tsc2007 *ts,
                              const struct tsc2007_platform_data *pdata,
                              const struct i2c_device_id *id)
 {
@@ -299,7 +287,7 @@ static int tsc2007_probe_pdev(struct i2c_client *client, struct tsc2007 *ts,
        ts->fuzzz             = pdata->fuzzz;
 
        if (pdata->x_plate_ohms == 0) {
-               dev_err(&client->dev, "x_plate_ohms is not set up in platform data");
+               dev_err(dev, "x_plate_ohms is not set up in platform data\n");
                return -EINVAL;
        }
 
@@ -332,9 +320,9 @@ static int tsc2007_probe(struct i2c_client *client,
                return -ENOMEM;
 
        if (pdata)
-               err = tsc2007_probe_pdev(client, ts, pdata, id);
+               err = tsc2007_probe_pdev(&client->dev, ts, pdata, id);
        else
-               err = tsc2007_probe_dt(client, ts);
+               err = tsc2007_probe_properties(&client->dev, ts);
        if (err)
                return err;
 
@@ -431,18 +419,16 @@ static const struct i2c_device_id tsc2007_idtable[] = {
 
 MODULE_DEVICE_TABLE(i2c, tsc2007_idtable);
 
-#ifdef CONFIG_OF
 static const struct of_device_id tsc2007_of_match[] = {
        { .compatible = "ti,tsc2007" },
        { /* sentinel */ }
 };
 MODULE_DEVICE_TABLE(of, tsc2007_of_match);
-#endif
 
 static struct i2c_driver tsc2007_driver = {
        .driver = {
                .name   = "tsc2007",
-               .of_match_table = of_match_ptr(tsc2007_of_match),
+               .of_match_table = tsc2007_of_match,
        },
        .id_table       = tsc2007_idtable,
        .probe          = tsc2007_probe,
index 1afc6bd..22826c3 100644 (file)
@@ -145,15 +145,16 @@ static void wacom_i2c_close(struct input_dev *dev)
 }
 
 static int wacom_i2c_probe(struct i2c_client *client,
-                                    const struct i2c_device_id *id)
+                          const struct i2c_device_id *id)
 {
+       struct device *dev = &client->dev;
        struct wacom_i2c *wac_i2c;
        struct input_dev *input;
        struct wacom_features features = { 0 };
        int error;
 
        if (!i2c_check_functionality(client->adapter, I2C_FUNC_I2C)) {
-               dev_err(&client->dev, "i2c_check_functionality error\n");
+               dev_err(dev, "i2c_check_functionality error\n");
                return -EIO;
        }
 
@@ -161,21 +162,22 @@ static int wacom_i2c_probe(struct i2c_client *client,
        if (error)
                return error;
 
-       wac_i2c = kzalloc(sizeof(*wac_i2c), GFP_KERNEL);
-       input = input_allocate_device();
-       if (!wac_i2c || !input) {
-               error = -ENOMEM;
-               goto err_free_mem;
-       }
+       wac_i2c = devm_kzalloc(dev, sizeof(*wac_i2c), GFP_KERNEL);
+       if (!wac_i2c)
+               return -ENOMEM;
 
        wac_i2c->client = client;
+
+       input = devm_input_allocate_device(dev);
+       if (!input)
+               return -ENOMEM;
+
        wac_i2c->input = input;
 
        input->name = "Wacom I2C Digitizer";
        input->id.bustype = BUS_I2C;
        input->id.vendor = 0x56a;
        input->id.version = features.fw_version;
-       input->dev.parent = &client->dev;
        input->open = wacom_i2c_open;
        input->close = wacom_i2c_close;
 
@@ -194,13 +196,11 @@ static int wacom_i2c_probe(struct i2c_client *client,
 
        input_set_drvdata(input, wac_i2c);
 
-       error = request_threaded_irq(client->irq, NULL, wacom_i2c_irq,
-                                    IRQF_TRIGGER_LOW | IRQF_ONESHOT,
-                                    "wacom_i2c", wac_i2c);
+       error = devm_request_threaded_irq(dev, client->irq, NULL, wacom_i2c_irq,
+                                         IRQF_ONESHOT, "wacom_i2c", wac_i2c);
        if (error) {
-               dev_err(&client->dev,
-                       "Failed to enable IRQ, error: %d\n", error);
-               goto err_free_mem;
+               dev_err(dev, "Failed to request IRQ: %d\n", error);
+               return error;
        }
 
        /* Disable the IRQ, we'll enable it in wac_i2c_open() */
@@ -208,31 +208,10 @@ static int wacom_i2c_probe(struct i2c_client *client,
 
        error = input_register_device(wac_i2c->input);
        if (error) {
-               dev_err(&client->dev,
-                       "Failed to register input device, error: %d\n", error);
-               goto err_free_irq;
+               dev_err(dev, "Failed to register input device: %d\n", error);
+               return error;
        }
 
-       i2c_set_clientdata(client, wac_i2c);
-       return 0;
-
-err_free_irq:
-       free_irq(client->irq, wac_i2c);
-err_free_mem:
-       input_free_device(input);
-       kfree(wac_i2c);
-
-       return error;
-}
-
-static int wacom_i2c_remove(struct i2c_client *client)
-{
-       struct wacom_i2c *wac_i2c = i2c_get_clientdata(client);
-
-       free_irq(client->irq, wac_i2c);
-       input_unregister_device(wac_i2c->input);
-       kfree(wac_i2c);
-
        return 0;
 }
 
@@ -269,7 +248,6 @@ static struct i2c_driver wacom_i2c_driver = {
        },
 
        .probe          = wacom_i2c_probe,
-       .remove         = wacom_i2c_remove,
        .id_table       = wacom_i2c_id,
 };
 module_i2c_driver(wacom_i2c_driver);
index bb1699e..319f57f 100644 (file)
@@ -317,14 +317,13 @@ static int wm831x_ts_probe(struct platform_device *pdev)
 
        error = request_threaded_irq(wm831x_ts->data_irq,
                                     NULL, wm831x_ts_data_irq,
-                                    irqf | IRQF_ONESHOT,
+                                    irqf | IRQF_ONESHOT | IRQF_NO_AUTOEN,
                                     "Touchscreen data", wm831x_ts);
        if (error) {
                dev_err(&pdev->dev, "Failed to request data IRQ %d: %d\n",
                        wm831x_ts->data_irq, error);
                goto err_alloc;
        }
-       disable_irq(wm831x_ts->data_irq);
 
        if (pdata && pdata->pd_irqf)
                irqf = pdata->pd_irqf;
index 3b636be..b8d9010 100644 (file)
@@ -513,10 +513,10 @@ static int zinitix_ts_probe(struct i2c_client *client)
                return -EINVAL;
        }
 
-       irq_set_status_flags(client->irq, IRQ_NOAUTOEN);
        error = devm_request_threaded_irq(&client->dev, client->irq,
                                          NULL, zinitix_ts_irq_handler,
-                                         IRQF_ONESHOT, client->name, bt541);
+                                         IRQF_ONESHOT | IRQF_NO_AUTOEN,
+                                         client->name, bt541);
        if (error) {
                dev_err(&client->dev, "Failed to request IRQ: %d\n", error);
                return error;
diff --git a/include/dt-bindings/input/atmel-maxtouch.h b/include/dt-bindings/input/atmel-maxtouch.h
new file mode 100644 (file)
index 0000000..7345ab3
--- /dev/null
@@ -0,0 +1,10 @@
+/* SPDX-License-Identifier: GPL-2.0+ */
+
+#ifndef _DT_BINDINGS_ATMEL_MAXTOUCH_H
+#define _DT_BINDINGS_ATMEL_MAXTOUCH_H
+
+#define ATMEL_MXT_WAKEUP_NONE          0
+#define ATMEL_MXT_WAKEUP_I2C_SCL       1
+#define ATMEL_MXT_WAKEUP_GPIO          2
+
+#endif /* _DT_BINDINGS_ATMEL_MAXTOUCH_H */