Merge branch 'ib/5.17-cros-ec-keyb' into next
authorDmitry Torokhov <dmitry.torokhov@gmail.com>
Wed, 18 May 2022 22:02:27 +0000 (15:02 -0700)
committerDmitry Torokhov <dmitry.torokhov@gmail.com>
Wed, 18 May 2022 22:02:27 +0000 (15:02 -0700)
Merge changes to ChromeOS EC Keyboard driver.

54 files changed:
Documentation/devicetree/bindings/input/allwinner,sun4i-a10-lradc-keys.yaml
Documentation/devicetree/bindings/input/azoteq,iqs7222.yaml [new file with mode: 0644]
Documentation/devicetree/bindings/input/mediatek,mt6779-keypad.yaml [new file with mode: 0644]
Documentation/devicetree/bindings/input/mtk-pmic-keys.txt
Documentation/devicetree/bindings/input/touchscreen/imagis,ist3038c.yaml [new file with mode: 0644]
Documentation/devicetree/bindings/vendor-prefixes.yaml
MAINTAINERS
drivers/hid/Kconfig
drivers/hid/Makefile
drivers/hid/hid-google-hammer.c
drivers/hid/hid-vivaldi-common.c [new file with mode: 0644]
drivers/hid/hid-vivaldi-common.h [new file with mode: 0644]
drivers/hid/hid-vivaldi.c
drivers/input/Kconfig
drivers/input/Makefile
drivers/input/input.c
drivers/input/joystick/Kconfig
drivers/input/joystick/Makefile
drivers/input/joystick/adi.c
drivers/input/joystick/sensehat-joystick.c [new file with mode: 0644]
drivers/input/joystick/xpad.c
drivers/input/keyboard/Kconfig
drivers/input/keyboard/Makefile
drivers/input/keyboard/atkbd.c
drivers/input/keyboard/bcm-keypad.c
drivers/input/keyboard/clps711x-keypad.c
drivers/input/keyboard/cros_ec_keyb.c
drivers/input/keyboard/ep93xx_keypad.c
drivers/input/keyboard/mt6779-keypad.c [new file with mode: 0644]
drivers/input/keyboard/mtk-pmic-keys.c
drivers/input/keyboard/sun4i-lradc-keys.c
drivers/input/misc/Kconfig
drivers/input/misc/Makefile
drivers/input/misc/da9063_onkey.c
drivers/input/misc/iqs7222.c [new file with mode: 0644]
drivers/input/misc/pm8941-pwrkey.c
drivers/input/misc/sparcspkr.c
drivers/input/mouse/psmouse-smbus.c
drivers/input/mouse/synaptics.c
drivers/input/mouse/vmmouse.c
drivers/input/rmi4/rmi_f54.c
drivers/input/serio/ps2-gpio.c
drivers/input/tablet/aiptek.c
drivers/input/touchscreen/Kconfig
drivers/input/touchscreen/Makefile
drivers/input/touchscreen/goodix.c
drivers/input/touchscreen/goodix.h
drivers/input/touchscreen/imagis.c [new file with mode: 0644]
drivers/input/touchscreen/iqs5xx.c
drivers/input/touchscreen/stmfts.c
drivers/input/touchscreen/tsc200x-core.c
drivers/input/vivaldi-fmap.c [new file with mode: 0644]
include/linux/input.h
include/linux/input/vivaldi-fmap.h [new file with mode: 0644]

index d74f2002409e4f635edc68a660fd9c17e0271fb8..3399fc288afbad8ad9d2e57120617feac31d3076 100644 (file)
@@ -18,10 +18,20 @@ properties:
       - items:
           - const: allwinner,sun50i-a64-lradc
           - const: allwinner,sun8i-a83t-r-lradc
+      - const: allwinner,sun50i-r329-lradc
+      - items:
+          - const: allwinner,sun20i-d1-lradc
+          - const: allwinner,sun50i-r329-lradc
 
   reg:
     maxItems: 1
 
+  clocks:
+    maxItems: 1
+
+  resets:
+    maxItems: 1
+
   interrupts:
     maxItems: 1
 
@@ -68,6 +78,18 @@ required:
   - interrupts
   - vref-supply
 
+if:
+  properties:
+    compatible:
+      contains:
+        enum:
+          - allwinner,sun50i-r329-lradc
+
+then:
+  required:
+    - clocks
+    - resets
+
 additionalProperties: false
 
 examples:
diff --git a/Documentation/devicetree/bindings/input/azoteq,iqs7222.yaml b/Documentation/devicetree/bindings/input/azoteq,iqs7222.yaml
new file mode 100644 (file)
index 0000000..a3a1e5a
--- /dev/null
@@ -0,0 +1,960 @@
+# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
+%YAML 1.2
+---
+$id: http://devicetree.org/schemas/input/azoteq,iqs7222.yaml#
+$schema: http://devicetree.org/meta-schemas/core.yaml#
+
+title: Azoteq IQS7222A/B/C Capacitive Touch Controller
+
+maintainers:
+  - Jeff LaBundy <jeff@labundy.com>
+
+description: |
+  The Azoteq IQS7222A, IQS7222B and IQS7222C are multichannel capacitive touch
+  controllers that feature additional sensing capabilities.
+
+  Link to datasheets: https://www.azoteq.com/
+
+properties:
+  compatible:
+    enum:
+      - azoteq,iqs7222a
+      - azoteq,iqs7222b
+      - azoteq,iqs7222c
+
+  reg:
+    maxItems: 1
+
+  irq-gpios:
+    maxItems: 1
+    description:
+      Specifies the GPIO connected to the device's active-low RDY output.
+
+  reset-gpios:
+    maxItems: 1
+    description:
+      Specifies the GPIO connected to the device's active-low MCLR input. The
+      device is temporarily held in hardware reset prior to initialization if
+      this property is present.
+
+  azoteq,rf-filt-enable:
+    type: boolean
+    description: Enables the device's internal RF filter.
+
+  azoteq,max-counts:
+    $ref: /schemas/types.yaml#/definitions/uint32
+    enum: [0, 1, 2, 3]
+    description: |
+      Specifies the maximum number of conversion periods (counts) that can be
+      reported as follows:
+      0: 1023
+      1: 2047
+      2: 4095
+      3: 16384
+
+  azoteq,auto-mode:
+    $ref: /schemas/types.yaml#/definitions/uint32
+    enum: [0, 1, 2, 3]
+    description: |
+      Specifies the number of conversions to occur before an interrupt is
+      generated as follows:
+      0: 4
+      1: 8
+      2: 16
+      3: 32
+
+  azoteq,ati-frac-div-fine:
+    $ref: /schemas/types.yaml#/definitions/uint32
+    minimum: 0
+    maximum: 31
+    description: Specifies the preloaded ATI fine fractional divider.
+
+  azoteq,ati-frac-div-coarse:
+    $ref: /schemas/types.yaml#/definitions/uint32
+    minimum: 0
+    maximum: 31
+    description: Specifies the preloaded ATI coarse fractional divider.
+
+  azoteq,ati-comp-select:
+    $ref: /schemas/types.yaml#/definitions/uint32
+    minimum: 0
+    maximum: 1023
+    description: Specifies the preloaded ATI compensation selection.
+
+  azoteq,lta-beta-lp:
+    $ref: /schemas/types.yaml#/definitions/uint32
+    minimum: 0
+    maximum: 15
+    description:
+      Specifies the long-term average filter damping factor to be applied during
+      low-power mode.
+
+  azoteq,lta-beta-np:
+    $ref: /schemas/types.yaml#/definitions/uint32
+    minimum: 0
+    maximum: 15
+    description:
+      Specifies the long-term average filter damping factor to be applied during
+      normal-power mode.
+
+  azoteq,counts-beta-lp:
+    $ref: /schemas/types.yaml#/definitions/uint32
+    minimum: 0
+    maximum: 15
+    description:
+      Specifies the counts filter damping factor to be applied during low-power
+      mode.
+
+  azoteq,counts-beta-np:
+    $ref: /schemas/types.yaml#/definitions/uint32
+    minimum: 0
+    maximum: 15
+    description:
+      Specifies the counts filter damping factor to be applied during normal-
+      power mode.
+
+  azoteq,lta-fast-beta-lp:
+    $ref: /schemas/types.yaml#/definitions/uint32
+    minimum: 0
+    maximum: 15
+    description:
+      Specifies the long-term average filter fast damping factor to be applied
+      during low-power mode.
+
+  azoteq,lta-fast-beta-np:
+    $ref: /schemas/types.yaml#/definitions/uint32
+    minimum: 0
+    maximum: 15
+    description:
+      Specifies the long-term average filter fast damping factor to be applied
+      during normal-power mode.
+
+  azoteq,timeout-ati-ms:
+    multipleOf: 500
+    minimum: 0
+    maximum: 32767500
+    description:
+      Specifies the delay (in ms) before ATI is retried following an ATI error.
+
+  azoteq,rate-ati-ms:
+    minimum: 0
+    maximum: 65535
+    description: Specifies the rate (in ms) at which ATI status is evaluated.
+
+  azoteq,timeout-np-ms:
+    minimum: 0
+    maximum: 65535
+    description:
+      Specifies the length of time (in ms) to wait for an event before moving
+      from normal-power mode to low-power mode.
+
+  azoteq,rate-np-ms:
+    minimum: 0
+    maximum: 3000
+    description: Specifies the report rate (in ms) during normal-power mode.
+
+  azoteq,timeout-lp-ms:
+    minimum: 0
+    maximum: 65535
+    description:
+      Specifies the length of time (in ms) to wait for an event before moving
+      from low-power mode to ultra-low-power mode.
+
+  azoteq,rate-lp-ms:
+    minimum: 0
+    maximum: 3000
+    description: Specifies the report rate (in ms) during low-power mode.
+
+  azoteq,timeout-ulp-ms:
+    minimum: 0
+    maximum: 65535
+    description:
+      Specifies the rate (in ms) at which channels not regularly sampled during
+      ultra-low-power mode are updated.
+
+  azoteq,rate-ulp-ms:
+    minimum: 0
+    maximum: 3000
+    description: Specifies the report rate (in ms) during ultra-low-power mode.
+
+patternProperties:
+  "^cycle-[0-9]$":
+    type: object
+    description: Represents a conversion cycle serving two sensing channels.
+
+    properties:
+      azoteq,conv-period:
+        $ref: /schemas/types.yaml#/definitions/uint32
+        minimum: 0
+        maximum: 255
+        description: Specifies the cycle's conversion period.
+
+      azoteq,conv-frac:
+        $ref: /schemas/types.yaml#/definitions/uint32
+        minimum: 0
+        maximum: 255
+        description: Specifies the cycle's conversion frequency fraction.
+
+      azoteq,tx-enable:
+        $ref: /schemas/types.yaml#/definitions/uint32-array
+        minItems: 1
+        maxItems: 9
+        items:
+          minimum: 0
+          maximum: 8
+        description: Specifies the CTx pin(s) associated with the cycle.
+
+      azoteq,rx-float-inactive:
+        type: boolean
+        description: Floats any inactive CRx pins instead of grounding them.
+
+      azoteq,dead-time-enable:
+        type: boolean
+        description:
+          Increases the denominator of the conversion frequency formula by one.
+
+      azoteq,tx-freq-fosc:
+        type: boolean
+        description:
+          Fixes the conversion frequency to that of the device's core clock.
+
+      azoteq,vbias-enable:
+        type: boolean
+        description: Enables the bias voltage for use during inductive sensing.
+
+      azoteq,sense-mode:
+        $ref: /schemas/types.yaml#/definitions/uint32
+        enum: [0, 1, 2, 3]
+        description: |
+          Specifies the cycle's sensing mode as follows:
+          0: None
+          1: Self capacitive
+          2: Mutual capacitive
+          3: Inductive
+
+          Note that in the case of IQS7222A, cycles 5 and 6 are restricted to
+          Hall-effect sensing.
+
+      azoteq,iref-enable:
+        type: boolean
+        description:
+          Enables the current reference for use during various sensing modes.
+
+      azoteq,iref-level:
+        $ref: /schemas/types.yaml#/definitions/uint32
+        minimum: 0
+        maximum: 15
+        description: Specifies the cycle's current reference level.
+
+      azoteq,iref-trim:
+        $ref: /schemas/types.yaml#/definitions/uint32
+        minimum: 0
+        maximum: 15
+        description: Specifies the cycle's current reference trim.
+
+    dependencies:
+      azoteq,iref-level: ["azoteq,iref-enable"]
+      azoteq,iref-trim: ["azoteq,iref-enable"]
+
+    additionalProperties: false
+
+  "^channel-([0-9]|1[0-9])$":
+    type: object
+    description:
+      Represents a single sensing channel. A channel is active if defined and
+      inactive otherwise.
+
+      Note that in the case of IQS7222A, channels 10 and 11 are restricted to
+      Hall-effect sensing with events reported on channel 10 only.
+
+    properties:
+      azoteq,ulp-allow:
+        type: boolean
+        description:
+          Permits the device to enter ultra-low-power mode while the channel
+          lies in a state of touch or proximity.
+
+      azoteq,ref-select:
+        $ref: /schemas/types.yaml#/definitions/uint32
+        minimum: 0
+        maximum: 9
+        description: Specifies a separate reference channel to be followed.
+
+      azoteq,ref-weight:
+        $ref: /schemas/types.yaml#/definitions/uint32
+        minimum: 0
+        maximum: 65535
+        description: Specifies the relative weight of the reference channel.
+
+      azoteq,use-prox:
+        type: boolean
+        description:
+          Activates the reference channel in response to proximity events
+          instead of touch events.
+
+      azoteq,ati-band:
+        $ref: /schemas/types.yaml#/definitions/uint32
+        enum: [0, 1, 2, 3]
+        description: |
+          Specifies the channel's ATI band as a fraction of its ATI target as
+          follows:
+          0: 1/16
+          1: 1/8
+          2: 1/4
+          3: 1/2
+
+      azoteq,global-halt:
+        type: boolean
+        description:
+          Specifies that the channel's long-term average is to freeze if any
+          other participating channel lies in a proximity or touch state.
+
+      azoteq,invert-enable:
+        type: boolean
+        description:
+          Inverts the polarity of the states reported for proximity and touch
+          events relative to their respective thresholds.
+
+      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,rx-enable:
+        $ref: /schemas/types.yaml#/definitions/uint32-array
+        minItems: 1
+        maxItems: 4
+        items:
+          minimum: 0
+          maximum: 7
+        description: Specifies the CRx pin(s) associated with the channel.
+
+      azoteq,samp-cap-double:
+        type: boolean
+        description: Doubles the sampling capacitance from 40 pF to 80 pF.
+
+      azoteq,vref-half:
+        type: boolean
+        description: Halves the discharge threshold from 1.0 V to 0.5 V.
+
+      azoteq,proj-bias:
+        $ref: /schemas/types.yaml#/definitions/uint32
+        enum: [0, 1, 2, 3]
+        description: |
+          Specifies the bias current applied during mutual (projected)
+          capacitive sensing as follows:
+          0: 2 uA
+          1: 5 uA
+          2: 7 uA
+          3: 10 uA
+
+      azoteq,ati-target:
+        $ref: /schemas/types.yaml#/definitions/uint32
+        multipleOf: 8
+        minimum: 0
+        maximum: 2040
+        description: Specifies the channel's ATI target.
+
+      azoteq,ati-base:
+        $ref: /schemas/types.yaml#/definitions/uint32
+        multipleOf: 16
+        minimum: 0
+        maximum: 496
+        description: Specifies the channel's ATI base.
+
+      azoteq,ati-mode:
+        $ref: /schemas/types.yaml#/definitions/uint32
+        enum: [0, 1, 2, 3, 4, 5]
+        description: |
+          Specifies the channel's ATI mode as follows:
+          0: Disabled
+          1: Compensation
+          2: Compensation divider
+          3: Fine fractional divider
+          4: Coarse fractional divider
+          5: Full
+
+      azoteq,ati-frac-div-fine:
+        $ref: /schemas/types.yaml#/definitions/uint32
+        minimum: 0
+        maximum: 31
+        description: Specifies the channel's ATI fine fractional divider.
+
+      azoteq,ati-frac-mult-coarse:
+        $ref: /schemas/types.yaml#/definitions/uint32
+        minimum: 0
+        maximum: 15
+        description: Specifies the channel's ATI coarse fractional multiplier.
+
+      azoteq,ati-frac-div-coarse:
+        $ref: /schemas/types.yaml#/definitions/uint32
+        minimum: 0
+        maximum: 31
+        description: Specifies the channel's ATI coarse fractional divider.
+
+      azoteq,ati-comp-div:
+        $ref: /schemas/types.yaml#/definitions/uint32
+        minimum: 0
+        maximum: 31
+        description: Specifies the channel's ATI compensation divider.
+
+      azoteq,ati-comp-select:
+        $ref: /schemas/types.yaml#/definitions/uint32
+        minimum: 0
+        maximum: 1023
+        description: Specifies the channel's ATI compensation selection.
+
+      azoteq,debounce-enter:
+        $ref: /schemas/types.yaml#/definitions/uint32
+        minimum: 0
+        maximum: 15
+        description: Specifies the channel's debounce entrance factor.
+
+      azoteq,debounce-exit:
+        $ref: /schemas/types.yaml#/definitions/uint32
+        minimum: 0
+        maximum: 15
+        description: Specifies the channel's debounce exit factor.
+
+    patternProperties:
+      "^event-(prox|touch)$":
+        type: object
+        description:
+          Represents a proximity or touch event reported by the channel.
+
+        properties:
+          azoteq,gpio-select:
+            $ref: /schemas/types.yaml#/definitions/uint32-array
+            minItems: 1
+            maxItems: 3
+            items:
+              minimum: 0
+              maximum: 2
+            description: |
+              Specifies one or more GPIO mapped to the event as follows:
+              0: GPIO0
+              1: GPIO3 (IQS7222C only)
+              2: GPIO4 (IQS7222C only)
+
+              Note that although multiple events can be mapped to a single
+              GPIO, they must all be of the same type (proximity, touch or
+              slider gesture).
+
+          azoteq,thresh:
+            $ref: /schemas/types.yaml#/definitions/uint32
+            description:
+              Specifies the threshold for the event. Valid entries range from
+              0-127 and 0-255 for proximity and touch events, respectively.
+
+          azoteq,hyst:
+            $ref: /schemas/types.yaml#/definitions/uint32
+            minimum: 0
+            maximum: 255
+            description:
+              Specifies the hysteresis for the event (touch events only).
+
+          azoteq,timeout-press-ms:
+            multipleOf: 500
+            minimum: 0
+            maximum: 127500
+            description:
+              Specifies the length of time (in ms) to wait before automatically
+              releasing a press event. Specify zero to allow the press state to
+              persist indefinitely.
+
+              The IQS7222B does not feature channel-specific timeouts; the time-
+              out specified for any one channel applies to all channels.
+
+          linux,code:
+            $ref: /schemas/types.yaml#/definitions/uint32
+            description:
+              Numeric key or switch code associated with the event. Specify
+              KEY_RESERVED (0) to opt out of event reporting.
+
+          linux,input-type:
+            $ref: /schemas/types.yaml#/definitions/uint32
+            enum: [1, 5]
+            default: 1
+            description:
+              Specifies whether the event is to be interpreted as a key (1)
+              or a switch (5).
+
+        required:
+          - linux,code
+
+        additionalProperties: false
+
+    dependencies:
+      azoteq,ref-weight: ["azoteq,ref-select"]
+      azoteq,use-prox: ["azoteq,ref-select"]
+
+    additionalProperties: false
+
+  "^slider-[0-1]$":
+    type: object
+    description: Represents a slider comprising three or four channels.
+
+    properties:
+      azoteq,channel-select:
+        $ref: /schemas/types.yaml#/definitions/uint32-array
+        minItems: 3
+        maxItems: 4
+        items:
+          minimum: 0
+          maximum: 9
+        description:
+          Specifies the order of the channels that participate in the slider.
+
+      azoteq,slider-size:
+        $ref: /schemas/types.yaml#/definitions/uint32
+        minimum: 0
+        maximum: 65535
+        description:
+          Specifies the slider's one-dimensional resolution, equal to the
+          maximum coordinate plus one.
+
+      azoteq,lower-cal:
+        $ref: /schemas/types.yaml#/definitions/uint32
+        minimum: 0
+        maximum: 255
+        description: Specifies the slider's lower starting point.
+
+      azoteq,upper-cal:
+        $ref: /schemas/types.yaml#/definitions/uint32
+        minimum: 0
+        maximum: 255
+        description: Specifies the slider's upper starting point.
+
+      azoteq,top-speed:
+        $ref: /schemas/types.yaml#/definitions/uint32
+        minimum: 0
+        maximum: 65535
+        description:
+          Specifies the speed of movement after which coordinate filtering is
+          no longer applied.
+
+      azoteq,bottom-speed:
+        $ref: /schemas/types.yaml#/definitions/uint32
+        multipleOf: 4
+        minimum: 0
+        maximum: 1020
+        description:
+          Specifies the speed of movement after which coordinate filtering is
+          linearly reduced.
+
+      azoteq,bottom-beta:
+        $ref: /schemas/types.yaml#/definitions/uint32
+        minimum: 0
+        maximum: 7
+        description:
+          Specifies the coordinate filter damping factor to be applied
+          while the speed of movement is below that which is specified
+          by azoteq,bottom-speed.
+
+      azoteq,static-beta:
+        type: boolean
+        description:
+          Applies the coordinate filter damping factor specified by
+          azoteq,bottom-beta regardless of the speed of movement.
+
+      azoteq,use-prox:
+        type: boolean
+        description:
+          Directs the slider to respond to the proximity states of the selected
+          channels instead of their corresponding touch states. Note the slider
+          cannot report granular coordinates during a state of proximity.
+
+      linux,axis:
+        $ref: /schemas/types.yaml#/definitions/uint32
+        description:
+          Specifies the absolute axis to which coordinates are mapped. Specify
+          ABS_WHEEL to operate the slider as a wheel (IQS7222C only).
+
+    patternProperties:
+      "^event-(press|tap|(swipe|flick)-(pos|neg))$":
+        type: object
+        description:
+          Represents a press or gesture (IQS7222A only) event reported by
+          the slider.
+
+        properties:
+          linux,code:
+            $ref: /schemas/types.yaml#/definitions/uint32
+            description: Numeric key code associated with the event.
+
+          azoteq,gesture-max-ms:
+            multipleOf: 4
+            minimum: 0
+            maximum: 1020
+            description:
+              Specifies the length of time (in ms) within which a tap, swipe
+              or flick gesture must be completed in order to be acknowledged
+              by the device. The number specified for any one swipe or flick
+              gesture applies to all remaining swipe or flick gestures.
+
+          azoteq,gesture-min-ms:
+            multipleOf: 4
+            minimum: 0
+            maximum: 124
+            description:
+              Specifies the length of time (in ms) for which a tap gesture must
+              be held in order to be acknowledged by the device.
+
+          azoteq,gesture-dist:
+            $ref: /schemas/types.yaml#/definitions/uint32
+            multipleOf: 16
+            minimum: 0
+            maximum: 4080
+            description:
+              Specifies the distance across which a swipe or flick gesture must
+              travel in order to be acknowledged by the device. The number spec-
+              ified for any one swipe or flick gesture applies to all remaining
+              swipe or flick gestures.
+
+          azoteq,gpio-select:
+            $ref: /schemas/types.yaml#/definitions/uint32-array
+            minItems: 1
+            maxItems: 1
+            items:
+              minimum: 0
+              maximum: 0
+            description: |
+              Specifies an individual GPIO mapped to a tap, swipe or flick
+              gesture as follows:
+              0: GPIO0
+              1: GPIO3 (reserved)
+              2: GPIO4 (reserved)
+
+              Note that although multiple events can be mapped to a single
+              GPIO, they must all be of the same type (proximity, touch or
+              slider gesture).
+
+        required:
+          - linux,code
+
+        additionalProperties: false
+
+    required:
+      - azoteq,channel-select
+
+    additionalProperties: false
+
+  "^gpio-[0-2]$":
+    type: object
+    description: |
+      Represents a GPIO mapped to one or more events as follows:
+      gpio-0: GPIO0
+      gpio-1: GPIO3 (IQS7222C only)
+      gpio-2: GPIO4 (IQS7222C only)
+
+    allOf:
+      - $ref: ../pinctrl/pincfg-node.yaml#
+
+    properties:
+      drive-open-drain: true
+
+    additionalProperties: false
+
+allOf:
+  - if:
+      properties:
+        compatible:
+          contains:
+            const: azoteq,iqs7222b
+
+    then:
+      patternProperties:
+        "^cycle-[0-9]$":
+          properties:
+            azoteq,iref-enable: false
+
+        "^channel-([0-9]|1[0-9])$":
+          properties:
+            azoteq,ref-select: false
+
+          patternProperties:
+            "^event-(prox|touch)$":
+              properties:
+                azoteq,gpio-select: false
+
+        "^slider-[0-1]$": false
+
+        "^gpio-[0-2]$": false
+
+  - if:
+      properties:
+        compatible:
+          contains:
+            const: azoteq,iqs7222a
+
+    then:
+      patternProperties:
+        "^channel-([0-9]|1[0-9])$":
+          patternProperties:
+            "^event-(prox|touch)$":
+              properties:
+                azoteq,gpio-select:
+                  maxItems: 1
+                  items:
+                    maximum: 0
+
+        "^slider-[0-1]$":
+          properties:
+            azoteq,slider-size:
+              multipleOf: 16
+              maximum: 4080
+
+            azoteq,top-speed:
+              multipleOf: 4
+              maximum: 1020
+
+    else:
+      patternProperties:
+        "^channel-([0-9]|1[0-9])$":
+          properties:
+            azoteq,ulp-allow: false
+
+        "^slider-[0-1]$":
+          patternProperties:
+            "^event-(press|tap|(swipe|flick)-(pos|neg))$":
+              properties:
+                azoteq,gesture-max-ms: false
+
+                azoteq,gesture-min-ms: false
+
+                azoteq,gesture-dist: false
+
+                azoteq,gpio-select: false
+
+required:
+  - compatible
+  - reg
+  - irq-gpios
+
+additionalProperties: false
+
+examples:
+  - |
+    #include <dt-bindings/gpio/gpio.h>
+    #include <dt-bindings/input/input.h>
+
+    i2c {
+            #address-cells = <1>;
+            #size-cells = <0>;
+
+            iqs7222a@44 {
+                    compatible = "azoteq,iqs7222a";
+                    reg = <0x44>;
+                    irq-gpios = <&gpio 4 GPIO_ACTIVE_LOW>;
+                    azoteq,lta-beta-lp = <7>;
+                    azoteq,lta-beta-np = <8>;
+                    azoteq,counts-beta-lp = <2>;
+                    azoteq,counts-beta-np = <3>;
+                    azoteq,lta-fast-beta-lp = <3>;
+                    azoteq,lta-fast-beta-np = <4>;
+
+                    cycle-0 {
+                            azoteq,conv-period = <5>;
+                            azoteq,conv-frac = <127>;
+                            azoteq,tx-enable = <1>, <2>, <4>, <5>;
+                            azoteq,dead-time-enable;
+                            azoteq,sense-mode = <2>;
+                    };
+
+                    cycle-1 {
+                            azoteq,conv-period = <5>;
+                            azoteq,conv-frac = <127>;
+                            azoteq,tx-enable = <5>;
+                            azoteq,dead-time-enable;
+                            azoteq,sense-mode = <2>;
+                    };
+
+                    cycle-2 {
+                            azoteq,conv-period = <5>;
+                            azoteq,conv-frac = <127>;
+                            azoteq,tx-enable = <4>;
+                            azoteq,dead-time-enable;
+                            azoteq,sense-mode = <2>;
+                    };
+
+                    cycle-3 {
+                            azoteq,conv-period = <5>;
+                            azoteq,conv-frac = <127>;
+                            azoteq,tx-enable = <2>;
+                            azoteq,dead-time-enable;
+                            azoteq,sense-mode = <2>;
+                    };
+
+                    cycle-4 {
+                            azoteq,conv-period = <5>;
+                            azoteq,conv-frac = <127>;
+                            azoteq,tx-enable = <1>;
+                            azoteq,dead-time-enable;
+                            azoteq,sense-mode = <2>;
+                    };
+
+                    cycle-5 {
+                            azoteq,conv-period = <2>;
+                            azoteq,conv-frac = <0>;
+                    };
+
+                    cycle-6 {
+                            azoteq,conv-period = <2>;
+                            azoteq,conv-frac = <0>;
+                    };
+
+                    channel-0 {
+                            azoteq,ulp-allow;
+                            azoteq,global-halt;
+                            azoteq,invert-enable;
+                            azoteq,rx-enable = <3>;
+                            azoteq,ati-target = <800>;
+                            azoteq,ati-base = <208>;
+                            azoteq,ati-mode = <5>;
+                    };
+
+                    channel-1 {
+                            azoteq,global-halt;
+                            azoteq,invert-enable;
+                            azoteq,rx-enable = <3>;
+                            azoteq,ati-target = <496>;
+                            azoteq,ati-base = <208>;
+                            azoteq,ati-mode = <5>;
+                    };
+
+                    channel-2 {
+                            azoteq,global-halt;
+                            azoteq,invert-enable;
+                            azoteq,rx-enable = <3>;
+                            azoteq,ati-target = <496>;
+                            azoteq,ati-base = <208>;
+                            azoteq,ati-mode = <5>;
+                    };
+
+                    channel-3 {
+                            azoteq,global-halt;
+                            azoteq,invert-enable;
+                            azoteq,rx-enable = <3>;
+                            azoteq,ati-target = <496>;
+                            azoteq,ati-base = <208>;
+                            azoteq,ati-mode = <5>;
+                    };
+
+                    channel-4 {
+                            azoteq,global-halt;
+                            azoteq,invert-enable;
+                            azoteq,rx-enable = <3>;
+                            azoteq,ati-target = <496>;
+                            azoteq,ati-base = <208>;
+                            azoteq,ati-mode = <5>;
+                    };
+
+                    channel-5 {
+                            azoteq,ulp-allow;
+                            azoteq,global-halt;
+                            azoteq,invert-enable;
+                            azoteq,rx-enable = <6>;
+                            azoteq,ati-target = <800>;
+                            azoteq,ati-base = <144>;
+                            azoteq,ati-mode = <5>;
+                    };
+
+                    channel-6 {
+                            azoteq,global-halt;
+                            azoteq,invert-enable;
+                            azoteq,rx-enable = <6>;
+                            azoteq,ati-target = <496>;
+                            azoteq,ati-base = <160>;
+                            azoteq,ati-mode = <5>;
+
+                            event-touch {
+                                    linux,code = <KEY_MUTE>;
+                            };
+                    };
+
+                    channel-7 {
+                            azoteq,global-halt;
+                            azoteq,invert-enable;
+                            azoteq,rx-enable = <6>;
+                            azoteq,ati-target = <496>;
+                            azoteq,ati-base = <160>;
+                            azoteq,ati-mode = <5>;
+
+                            event-touch {
+                                    linux,code = <KEY_VOLUMEDOWN>;
+                            };
+                    };
+
+                    channel-8 {
+                            azoteq,global-halt;
+                            azoteq,invert-enable;
+                            azoteq,rx-enable = <6>;
+                            azoteq,ati-target = <496>;
+                            azoteq,ati-base = <160>;
+                            azoteq,ati-mode = <5>;
+
+                            event-touch {
+                                    linux,code = <KEY_VOLUMEUP>;
+                            };
+                    };
+
+                    channel-9 {
+                            azoteq,global-halt;
+                            azoteq,invert-enable;
+                            azoteq,rx-enable = <6>;
+                            azoteq,ati-target = <496>;
+                            azoteq,ati-base = <160>;
+                            azoteq,ati-mode = <5>;
+
+                            event-touch {
+                                    linux,code = <KEY_POWER>;
+                            };
+                    };
+
+                    channel-10 {
+                            azoteq,ulp-allow;
+                            azoteq,ati-target = <496>;
+                            azoteq,ati-base = <112>;
+
+                            event-touch {
+                                    linux,code = <SW_LID>;
+                                    linux,input-type = <EV_SW>;
+                            };
+                    };
+
+                    channel-11 {
+                            azoteq,ati-target = <496>;
+                            azoteq,ati-base = <112>;
+                    };
+
+                    slider-0 {
+                            azoteq,channel-select = <1>, <2>, <3>, <4>;
+                            azoteq,slider-size = <4080>;
+                            azoteq,upper-cal = <50>;
+                            azoteq,lower-cal = <30>;
+                            azoteq,top-speed = <200>;
+                            azoteq,bottom-speed = <1>;
+                            azoteq,bottom-beta = <3>;
+
+                            event-tap {
+                                    linux,code = <KEY_PLAYPAUSE>;
+                                    azoteq,gesture-max-ms = <600>;
+                                    azoteq,gesture-min-ms = <24>;
+                            };
+
+                            event-flick-pos {
+                                    linux,code = <KEY_NEXTSONG>;
+                                    azoteq,gesture-max-ms = <600>;
+                                    azoteq,gesture-dist = <816>;
+                            };
+
+                            event-flick-neg {
+                                    linux,code = <KEY_PREVIOUSSONG>;
+                            };
+                    };
+            };
+    };
+
+...
diff --git a/Documentation/devicetree/bindings/input/mediatek,mt6779-keypad.yaml b/Documentation/devicetree/bindings/input/mediatek,mt6779-keypad.yaml
new file mode 100644 (file)
index 0000000..b177064
--- /dev/null
@@ -0,0 +1,77 @@
+# SPDX-License-Identifier: (GPL-2.0 OR BSD-2-Clause)
+%YAML 1.2
+---
+$id: http://devicetree.org/schemas/input/mediatek,mt6779-keypad.yaml#
+$schema: http://devicetree.org/meta-schemas/core.yaml#
+
+title: Mediatek's Keypad Controller device tree bindings
+
+maintainers:
+  - Fengping Yu <fengping.yu@mediatek.com>
+
+allOf:
+  - $ref: "/schemas/input/matrix-keymap.yaml#"
+
+description: |
+  Mediatek's Keypad controller is used to interface a SoC with a matrix-type
+  keypad device. The keypad controller supports multiple row and column lines.
+  A key can be placed at each intersection of a unique row and a unique column.
+  The keypad controller can sense a key-press and key-release and report the
+  event using a interrupt to the cpu.
+
+properties:
+  compatible:
+    oneOf:
+      - const: mediatek,mt6779-keypad
+      - items:
+          - enum:
+              - mediatek,mt6873-keypad
+          - const: mediatek,mt6779-keypad
+
+  reg:
+    maxItems: 1
+
+  interrupts:
+    maxItems: 1
+
+  clocks:
+    maxItems: 1
+
+  clock-names:
+    items:
+      - const: kpd
+
+  wakeup-source:
+    description: use any event on keypad as wakeup event
+    type: boolean
+
+  debounce-delay-ms:
+    maximum: 256
+    default: 16
+
+required:
+  - compatible
+  - reg
+  - interrupts
+  - clocks
+  - clock-names
+
+additionalProperties: false
+
+examples:
+  - |
+    #include <dt-bindings/input/input.h>
+    #include <dt-bindings/interrupt-controller/arm-gic.h>
+
+    soc {
+        #address-cells = <2>;
+        #size-cells = <2>;
+
+        keyboard@10010000 {
+          compatible = "mediatek,mt6779-keypad";
+          reg = <0 0x10010000 0 0x1000>;
+          interrupts = <GIC_SPI 75 IRQ_TYPE_EDGE_FALLING>;
+          clocks = <&clk26m>;
+          clock-names = "kpd";
+        };
+    };
index 535d92885372b0ec8138a2ff1896953656bbb0b9..9d00f2a8e13a4ece0e8573ff6df7b1ec6fc17b39 100644 (file)
@@ -9,7 +9,10 @@ For MT6397/MT6323 MFD bindings see:
 Documentation/devicetree/bindings/mfd/mt6397.txt
 
 Required properties:
-- compatible: "mediatek,mt6397-keys" or "mediatek,mt6323-keys"
+- compatible: Should be one of:
+       - "mediatek,mt6397-keys"
+       - "mediatek,mt6323-keys"
+       - "mediatek,mt6358-keys"
 - linux,keycodes: See Documentation/devicetree/bindings/input/input.yaml
 
 Optional Properties:
diff --git a/Documentation/devicetree/bindings/input/touchscreen/imagis,ist3038c.yaml b/Documentation/devicetree/bindings/input/touchscreen/imagis,ist3038c.yaml
new file mode 100644 (file)
index 0000000..e3a2b87
--- /dev/null
@@ -0,0 +1,74 @@
+# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
+%YAML 1.2
+---
+$id: http://devicetree.org/schemas/input/touchscreen/imagis,ist3038c.yaml#
+$schema: http://devicetree.org/meta-schemas/core.yaml#
+
+title: Imagis IST30XXC family touchscreen controller bindings
+
+maintainers:
+  - Markuss Broks <markuss.broks@gmail.com>
+
+allOf:
+  - $ref: touchscreen.yaml#
+
+properties:
+  $nodename:
+    pattern: "^touchscreen@[0-9a-f]+$"
+
+  compatible:
+    enum:
+      - imagis,ist3038c
+
+  reg:
+    maxItems: 1
+
+  interrupts:
+    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
+  touchscreen-fuzz-x: true
+  touchscreen-fuzz-y: true
+  touchscreen-inverted-x: true
+  touchscreen-inverted-y: true
+  touchscreen-swapped-x-y: 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@50 {
+        compatible = "imagis,ist3038c";
+        reg = <0x50>;
+        interrupt-parent = <&gpio>;
+        interrupts = <13 IRQ_TYPE_EDGE_FALLING>;
+        vdd-supply = <&ldo1_reg>;
+        vddio-supply = <&ldo2_reg>;
+        touchscreen-size-x = <720>;
+        touchscreen-size-y = <1280>;
+        touchscreen-fuzz-x = <10>;
+        touchscreen-fuzz-y = <10>;
+        touchscreen-inverted-x;
+        touchscreen-inverted-y;
+      };
+    };
+
+...
index 294093d45a2308fb51b6453e9ca791210ed125df..f5600020dbec2735c865db738a6c4e32323f73d4 100644 (file)
@@ -545,6 +545,8 @@ patternProperties:
     description: Ingenieurburo Fur Ic-Technologie (I/F/I)
   "^ilitek,.*":
     description: ILI Technology Corporation (ILITEK)
+  "^imagis,.*":
+    description: Imagis Technologies Co., Ltd.
   "^img,.*":
     description: Imagination Technologies Ltd.
   "^imi,.*":
index cd0f68d4a34a6069e66d882daa8f664b6b8dcaad..91439d6e2a7c54c11530825db100e1ac64710b1f 100644 (file)
@@ -9370,6 +9370,12 @@ M:       Stanislaw Gruszka <stf_xl@wp.pl>
 S:     Maintained
 F:     drivers/usb/atm/ueagle-atm.c
 
+IMAGIS TOUCHSCREEN DRIVER
+M:     Markuss Broks <markuss.broks@gmail.com>
+S:     Maintained
+F:     Documentation/devicetree/bindings/input/touchscreen/imagis,ist3038c.yaml
+F:     drivers/input/touchscreen/imagis.c
+
 IMGTEC ASCII LCD DRIVER
 M:     Paul Burton <paulburton@kernel.org>
 S:     Maintained
index f5544157576c97fdec7fd72d4dc8de0b8480ddd4..4bea966e617b68be9923f4d1247d20b105b3df37 100644 (file)
@@ -403,14 +403,25 @@ config HOLTEK_FF
          Say Y here if you have a Holtek On Line Grip based game controller
          and want to have force feedback support for it.
 
+config HID_VIVALDI_COMMON
+       tristate
+       help
+         ChromeOS Vivaldi HID parsing support library. This is a hidden
+         option so that drivers can use common code to parse the HID
+         descriptors for vivaldi function row keymap.
+
 config HID_GOOGLE_HAMMER
        tristate "Google Hammer Keyboard"
+       select HID_VIVALDI_COMMON
+       select INPUT_VIVALDIFMAP
        depends on USB_HID && LEDS_CLASS && CROS_EC
        help
        Say Y here if you have a Google Hammer device.
 
 config HID_VIVALDI
        tristate "Vivaldi Keyboard"
+       select HID_VIVALDI_COMMON
+       select INPUT_VIVALDIFMAP
        depends on HID
        help
          Say Y here if you want to enable support for Vivaldi keyboards.
index 6d3e630e81af51af5c1c853fce48a5484e4c08d3..469a6159ebaef5a6a149c2edef4120161d82c08c 100644 (file)
@@ -50,6 +50,7 @@ obj-$(CONFIG_HID_FT260)               += hid-ft260.o
 obj-$(CONFIG_HID_GEMBIRD)      += hid-gembird.o
 obj-$(CONFIG_HID_GFRM)         += hid-gfrm.o
 obj-$(CONFIG_HID_GLORIOUS)  += hid-glorious.o
+obj-$(CONFIG_HID_VIVALDI_COMMON) += hid-vivaldi-common.o
 obj-$(CONFIG_HID_GOOGLE_HAMMER)        += hid-google-hammer.o
 obj-$(CONFIG_HID_VIVALDI)      += hid-vivaldi.o
 obj-$(CONFIG_HID_GT683R)       += hid-gt683r.o
index 0403beb3104b9e47af2c7cb79acf734879e05b24..7fd342081183a152e9fce17470c082422b4f3e23 100644 (file)
@@ -15,6 +15,7 @@
 
 #include <linux/acpi.h>
 #include <linux/hid.h>
+#include <linux/input/vivaldi-fmap.h>
 #include <linux/leds.h>
 #include <linux/module.h>
 #include <linux/of.h>
@@ -25,6 +26,7 @@
 #include <asm/unaligned.h>
 
 #include "hid-ids.h"
+#include "hid-vivaldi-common.h"
 
 /*
  * C(hrome)B(ase)A(ttached)S(witch) - switch exported by Chrome EC and reporting
@@ -340,9 +342,9 @@ static int hammer_kbd_brightness_set_blocking(struct led_classdev *cdev,
 static int hammer_register_leds(struct hid_device *hdev)
 {
        struct hammer_kbd_leds *kbd_backlight;
-       int error;
 
-       kbd_backlight = kzalloc(sizeof(*kbd_backlight), GFP_KERNEL);
+       kbd_backlight = devm_kzalloc(&hdev->dev, sizeof(*kbd_backlight),
+                                    GFP_KERNEL);
        if (!kbd_backlight)
                return -ENOMEM;
 
@@ -356,26 +358,7 @@ static int hammer_register_leds(struct hid_device *hdev)
        /* Set backlight to 0% initially. */
        hammer_kbd_brightness_set_blocking(&kbd_backlight->cdev, 0);
 
-       error = led_classdev_register(&hdev->dev, &kbd_backlight->cdev);
-       if (error)
-               goto err_free_mem;
-
-       hid_set_drvdata(hdev, kbd_backlight);
-       return 0;
-
-err_free_mem:
-       kfree(kbd_backlight);
-       return error;
-}
-
-static void hammer_unregister_leds(struct hid_device *hdev)
-{
-       struct hammer_kbd_leds *kbd_backlight = hid_get_drvdata(hdev);
-
-       if (kbd_backlight) {
-               led_classdev_unregister(&kbd_backlight->cdev);
-               kfree(kbd_backlight);
-       }
+       return devm_led_classdev_register(&hdev->dev, &kbd_backlight->cdev);
 }
 
 #define HID_UP_GOOGLEVENDOR    0xffd10000
@@ -512,11 +495,23 @@ out:
        kfree(buf);
 }
 
+static void hammer_stop(void *hdev)
+{
+       hid_hw_stop(hdev);
+}
+
 static int hammer_probe(struct hid_device *hdev,
                        const struct hid_device_id *id)
 {
+       struct vivaldi_data *vdata;
        int error;
 
+       vdata = devm_kzalloc(&hdev->dev, sizeof(*vdata), GFP_KERNEL);
+       if (!vdata)
+               return -ENOMEM;
+
+       hid_set_drvdata(hdev, vdata);
+
        error = hid_parse(hdev);
        if (error)
                return error;
@@ -525,6 +520,10 @@ static int hammer_probe(struct hid_device *hdev,
        if (error)
                return error;
 
+       error = devm_add_action(&hdev->dev, hammer_stop, hdev);
+       if (error)
+               return error;
+
        /*
         * We always want to poll for, and handle tablet mode events from
         * devices that have folded usage, even when nobody has opened the input
@@ -577,15 +576,13 @@ static void hammer_remove(struct hid_device *hdev)
                spin_unlock_irqrestore(&cbas_ec_lock, flags);
        }
 
-       hammer_unregister_leds(hdev);
-
-       hid_hw_stop(hdev);
+       /* Unregistering LEDs and stopping the hardware is done via devm */
 }
 
 static const struct hid_device_id hammer_devices[] = {
        { HID_DEVICE(BUS_USB, HID_GROUP_GENERIC,
                     USB_VENDOR_ID_GOOGLE, USB_DEVICE_ID_GOOGLE_DON) },
-       { HID_DEVICE(BUS_USB, HID_GROUP_GENERIC,
+       { HID_DEVICE(BUS_USB, HID_GROUP_VIVALDI,
                     USB_VENDOR_ID_GOOGLE, USB_DEVICE_ID_GOOGLE_EEL) },
        { HID_DEVICE(BUS_USB, HID_GROUP_GENERIC,
                     USB_VENDOR_ID_GOOGLE, USB_DEVICE_ID_GOOGLE_HAMMER) },
@@ -610,6 +607,8 @@ static struct hid_driver hammer_driver = {
        .id_table = hammer_devices,
        .probe = hammer_probe,
        .remove = hammer_remove,
+       .feature_mapping = vivaldi_feature_mapping,
+       .input_configured = vivaldi_input_configured,
        .input_mapping = hammer_input_mapping,
        .event = hammer_event,
 };
diff --git a/drivers/hid/hid-vivaldi-common.c b/drivers/hid/hid-vivaldi-common.c
new file mode 100644 (file)
index 0000000..8b3e515
--- /dev/null
@@ -0,0 +1,140 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Helpers for ChromeOS HID Vivaldi keyboards
+ *
+ * Copyright (C) 2022 Google, Inc
+ */
+
+#include <linux/export.h>
+#include <linux/hid.h>
+#include <linux/input/vivaldi-fmap.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/types.h>
+
+#include "hid-vivaldi-common.h"
+
+#define MIN_FN_ROW_KEY 1
+#define MAX_FN_ROW_KEY VIVALDI_MAX_FUNCTION_ROW_KEYS
+#define HID_VD_FN_ROW_PHYSMAP 0x00000001
+#define HID_USAGE_FN_ROW_PHYSMAP (HID_UP_GOOGLEVENDOR | HID_VD_FN_ROW_PHYSMAP)
+
+/**
+ * vivaldi_feature_mapping - Fill out vivaldi keymap data exposed via HID
+ * @hdev: HID device to parse
+ * @field: HID field to parse
+ * @usage: HID usage to parse
+ *
+ * Note: this function assumes that driver data attached to @hdev contains an
+ * instance of &struct vivaldi_data at the very beginning.
+ */
+void vivaldi_feature_mapping(struct hid_device *hdev,
+                            struct hid_field *field, struct hid_usage *usage)
+{
+       struct vivaldi_data *data = hid_get_drvdata(hdev);
+       struct hid_report *report = field->report;
+       u8 *report_data, *buf;
+       u32 report_len;
+       unsigned int fn_key;
+       int ret;
+
+       if (field->logical != HID_USAGE_FN_ROW_PHYSMAP ||
+           (usage->hid & HID_USAGE_PAGE) != HID_UP_ORDINAL)
+               return;
+
+       fn_key = usage->hid & HID_USAGE;
+       if (fn_key < MIN_FN_ROW_KEY || fn_key > MAX_FN_ROW_KEY)
+               return;
+
+       if (fn_key > data->num_function_row_keys)
+               data->num_function_row_keys = fn_key;
+
+       report_data = buf = hid_alloc_report_buf(report, GFP_KERNEL);
+       if (!report_data)
+               return;
+
+       report_len = hid_report_len(report);
+       if (!report->id) {
+               /*
+                * hid_hw_raw_request() will stuff report ID (which will be 0)
+                * into the first byte of the buffer even for unnumbered
+                * reports, so we need to account for this to avoid getting
+                * -EOVERFLOW in return.
+                * Note that hid_alloc_report_buf() adds 7 bytes to the size
+                * so we can safely say that we have space for an extra byte.
+                */
+               report_len++;
+       }
+
+       ret = hid_hw_raw_request(hdev, report->id, report_data,
+                                report_len, HID_FEATURE_REPORT,
+                                HID_REQ_GET_REPORT);
+       if (ret < 0) {
+               dev_warn(&hdev->dev, "failed to fetch feature %d\n",
+                        field->report->id);
+               goto out;
+       }
+
+       if (!report->id) {
+               /*
+                * Undo the damage from hid_hw_raw_request() for unnumbered
+                * reports.
+                */
+               report_data++;
+               report_len--;
+       }
+
+       ret = hid_report_raw_event(hdev, HID_FEATURE_REPORT, report_data,
+                                  report_len, 0);
+       if (ret) {
+               dev_warn(&hdev->dev, "failed to report feature %d\n",
+                        field->report->id);
+               goto out;
+       }
+
+       data->function_row_physmap[fn_key - MIN_FN_ROW_KEY] =
+               field->value[usage->usage_index];
+
+out:
+       kfree(buf);
+}
+EXPORT_SYMBOL_GPL(vivaldi_feature_mapping);
+
+static ssize_t function_row_physmap_show(struct device *dev,
+                                        struct device_attribute *attr,
+                                        char *buf)
+{
+       struct hid_device *hdev = to_hid_device(dev);
+       struct vivaldi_data *data = hid_get_drvdata(hdev);
+
+       return vivaldi_function_row_physmap_show(data, buf);
+}
+
+static DEVICE_ATTR_RO(function_row_physmap);
+static struct attribute *vivaldi_sysfs_attrs[] = {
+       &dev_attr_function_row_physmap.attr,
+       NULL
+};
+
+static const struct attribute_group vivaldi_attribute_group = {
+       .attrs = vivaldi_sysfs_attrs,
+};
+
+/**
+ * vivaldi_input_configured - Complete initialization of device using vivaldi map
+ * @hdev: HID device to which vivaldi attributes should be attached
+ * @hidinput: HID input device (unused)
+ */
+int vivaldi_input_configured(struct hid_device *hdev,
+                            struct hid_input *hidinput)
+{
+       struct vivaldi_data *data = hid_get_drvdata(hdev);
+
+       if (!data->num_function_row_keys)
+               return 0;
+
+       return devm_device_add_group(&hdev->dev, &vivaldi_attribute_group);
+}
+EXPORT_SYMBOL_GPL(vivaldi_input_configured);
+
+MODULE_LICENSE("GPL");
diff --git a/drivers/hid/hid-vivaldi-common.h b/drivers/hid/hid-vivaldi-common.h
new file mode 100644 (file)
index 0000000..d42e82d
--- /dev/null
@@ -0,0 +1,16 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+#ifndef _HID_VIVALDI_COMMON_H
+#define _HID_VIVALDI_COMMON_H
+
+struct hid_device;
+struct hid_field;
+struct hid_input;
+struct hid_usage;
+
+void vivaldi_feature_mapping(struct hid_device *hdev,
+                            struct hid_field *field, struct hid_usage *usage);
+
+int vivaldi_input_configured(struct hid_device *hdev,
+                            struct hid_input *hidinput);
+
+#endif /* _HID_VIVALDI_COMMON_H */
index 42ceb2058a094e00582f17edde9a73e91657b75a..3a979123e7d33f072be079425bbcfc9a619d92a2 100644 (file)
@@ -8,48 +8,11 @@
 
 #include <linux/device.h>
 #include <linux/hid.h>
+#include <linux/input/vivaldi-fmap.h>
 #include <linux/kernel.h>
 #include <linux/module.h>
-#include <linux/sysfs.h>
 
-#define MIN_FN_ROW_KEY 1
-#define MAX_FN_ROW_KEY 24
-#define HID_VD_FN_ROW_PHYSMAP 0x00000001
-#define HID_USAGE_FN_ROW_PHYSMAP (HID_UP_GOOGLEVENDOR | HID_VD_FN_ROW_PHYSMAP)
-
-struct vivaldi_data {
-       u32 function_row_physmap[MAX_FN_ROW_KEY - MIN_FN_ROW_KEY + 1];
-       int max_function_row_key;
-};
-
-static ssize_t function_row_physmap_show(struct device *dev,
-                                        struct device_attribute *attr,
-                                        char *buf)
-{
-       struct hid_device *hdev = to_hid_device(dev);
-       struct vivaldi_data *drvdata = hid_get_drvdata(hdev);
-       ssize_t size = 0;
-       int i;
-
-       if (!drvdata->max_function_row_key)
-               return 0;
-
-       for (i = 0; i < drvdata->max_function_row_key; i++)
-               size += sprintf(buf + size, "%02X ",
-                               drvdata->function_row_physmap[i]);
-       size += sprintf(buf + size, "\n");
-       return size;
-}
-
-static DEVICE_ATTR_RO(function_row_physmap);
-static struct attribute *sysfs_attrs[] = {
-       &dev_attr_function_row_physmap.attr,
-       NULL
-};
-
-static const struct attribute_group input_attribute_group = {
-       .attrs = sysfs_attrs
-};
+#include "hid-vivaldi-common.h"
 
 static int vivaldi_probe(struct hid_device *hdev,
                         const struct hid_device_id *id)
@@ -70,86 +33,8 @@ static int vivaldi_probe(struct hid_device *hdev,
        return hid_hw_start(hdev, HID_CONNECT_DEFAULT);
 }
 
-static void vivaldi_feature_mapping(struct hid_device *hdev,
-                                   struct hid_field *field,
-                                   struct hid_usage *usage)
-{
-       struct vivaldi_data *drvdata = hid_get_drvdata(hdev);
-       struct hid_report *report = field->report;
-       int fn_key;
-       int ret;
-       u32 report_len;
-       u8 *report_data, *buf;
-
-       if (field->logical != HID_USAGE_FN_ROW_PHYSMAP ||
-           (usage->hid & HID_USAGE_PAGE) != HID_UP_ORDINAL)
-               return;
-
-       fn_key = (usage->hid & HID_USAGE);
-       if (fn_key < MIN_FN_ROW_KEY || fn_key > MAX_FN_ROW_KEY)
-               return;
-       if (fn_key > drvdata->max_function_row_key)
-               drvdata->max_function_row_key = fn_key;
-
-       report_data = buf = hid_alloc_report_buf(report, GFP_KERNEL);
-       if (!report_data)
-               return;
-
-       report_len = hid_report_len(report);
-       if (!report->id) {
-               /*
-                * hid_hw_raw_request() will stuff report ID (which will be 0)
-                * into the first byte of the buffer even for unnumbered
-                * reports, so we need to account for this to avoid getting
-                * -EOVERFLOW in return.
-                * Note that hid_alloc_report_buf() adds 7 bytes to the size
-                * so we can safely say that we have space for an extra byte.
-                */
-               report_len++;
-       }
-
-       ret = hid_hw_raw_request(hdev, report->id, report_data,
-                                report_len, HID_FEATURE_REPORT,
-                                HID_REQ_GET_REPORT);
-       if (ret < 0) {
-               dev_warn(&hdev->dev, "failed to fetch feature %d\n",
-                        field->report->id);
-               goto out;
-       }
-
-       if (!report->id) {
-               /*
-                * Undo the damage from hid_hw_raw_request() for unnumbered
-                * reports.
-                */
-               report_data++;
-               report_len--;
-       }
-
-       ret = hid_report_raw_event(hdev, HID_FEATURE_REPORT, report_data,
-                                  report_len, 0);
-       if (ret) {
-               dev_warn(&hdev->dev, "failed to report feature %d\n",
-                        field->report->id);
-               goto out;
-       }
-
-       drvdata->function_row_physmap[fn_key - MIN_FN_ROW_KEY] =
-           field->value[usage->usage_index];
-
-out:
-       kfree(buf);
-}
-
-static int vivaldi_input_configured(struct hid_device *hdev,
-                                   struct hid_input *hidinput)
-{
-       return devm_device_add_group(&hdev->dev, &input_attribute_group);
-}
-
 static const struct hid_device_id vivaldi_table[] = {
-       { HID_DEVICE(HID_BUS_ANY, HID_GROUP_VIVALDI, HID_ANY_ID,
-                    HID_ANY_ID) },
+       { HID_DEVICE(HID_BUS_ANY, HID_GROUP_VIVALDI, HID_ANY_ID, HID_ANY_ID) },
        { }
 };
 
index 5baebf62df3305ae869c1ab57cfdca96c2f8d7d4..e2752f7364bcfe8e687710926846fe175d86e4da 100644 (file)
@@ -77,6 +77,13 @@ config INPUT_MATRIXKMAP
          To compile this driver as a module, choose M here: the
          module will be called matrix-keymap.
 
+config INPUT_VIVALDIFMAP
+       tristate
+       help
+         ChromeOS Vivaldi keymap support library. This is a hidden
+         option so that drivers can use common code to parse and
+         expose the vivaldi function row keymap.
+
 comment "Userland interfaces"
 
 config INPUT_MOUSEDEV
index 037cc595106c0f6b2d88557b0106660910e642e4..2266c7d010efca94d348881dcb40233422508e51 100644 (file)
@@ -12,6 +12,7 @@ input-core-y += touchscreen.o
 obj-$(CONFIG_INPUT_FF_MEMLESS) += ff-memless.o
 obj-$(CONFIG_INPUT_SPARSEKMAP) += sparse-keymap.o
 obj-$(CONFIG_INPUT_MATRIXKMAP) += matrix-keymap.o
+obj-$(CONFIG_INPUT_VIVALDIFMAP)        += vivaldi-fmap.o
 
 obj-$(CONFIG_INPUT_LEDS)       += input-leds.o
 obj-$(CONFIG_INPUT_MOUSEDEV)   += mousedev.o
index c3139bc2aa0db106f8a63cced11051a98c0ecbc5..0e7f3d065b098463ab349a068060e9e014ac78a6 100644 (file)
@@ -47,6 +47,17 @@ static DEFINE_MUTEX(input_mutex);
 
 static const struct input_value input_value_sync = { EV_SYN, SYN_REPORT, 1 };
 
+static const unsigned int input_max_code[EV_CNT] = {
+       [EV_KEY] = KEY_MAX,
+       [EV_REL] = REL_MAX,
+       [EV_ABS] = ABS_MAX,
+       [EV_MSC] = MSC_MAX,
+       [EV_SW] = SW_MAX,
+       [EV_LED] = LED_MAX,
+       [EV_SND] = SND_MAX,
+       [EV_FF] = FF_MAX,
+};
+
 static inline int is_event_supported(unsigned int code,
                                     unsigned long *bm, unsigned int max)
 {
@@ -511,6 +522,9 @@ void input_set_abs_params(struct input_dev *dev, unsigned int axis,
 {
        struct input_absinfo *absinfo;
 
+       __set_bit(EV_ABS, dev->evbit);
+       __set_bit(axis, dev->absbit);
+
        input_alloc_absinfo(dev);
        if (!dev->absinfo)
                return;
@@ -520,12 +534,45 @@ void input_set_abs_params(struct input_dev *dev, unsigned int axis,
        absinfo->maximum = max;
        absinfo->fuzz = fuzz;
        absinfo->flat = flat;
-
-       __set_bit(EV_ABS, dev->evbit);
-       __set_bit(axis, dev->absbit);
 }
 EXPORT_SYMBOL(input_set_abs_params);
 
+/**
+ * input_copy_abs - Copy absinfo from one input_dev to another
+ * @dst: Destination input device to copy the abs settings to
+ * @dst_axis: ABS_* value selecting the destination axis
+ * @src: Source input device to copy the abs settings from
+ * @src_axis: ABS_* value selecting the source axis
+ *
+ * Set absinfo for the selected destination axis by copying it from
+ * the specified source input device's source axis.
+ * This is useful to e.g. setup a pen/stylus input-device for combined
+ * touchscreen/pen hardware where the pen uses the same coordinates as
+ * the touchscreen.
+ */
+void input_copy_abs(struct input_dev *dst, unsigned int dst_axis,
+                   const struct input_dev *src, unsigned int src_axis)
+{
+       /* src must have EV_ABS and src_axis set */
+       if (WARN_ON(!(test_bit(EV_ABS, src->evbit) &&
+                     test_bit(src_axis, src->absbit))))
+               return;
+
+       /*
+        * input_alloc_absinfo() may have failed for the source. Our caller is
+        * expected to catch this when registering the input devices, which may
+        * happen after the input_copy_abs() call.
+        */
+       if (!src->absinfo)
+               return;
+
+       input_set_capability(dst, EV_ABS, dst_axis);
+       if (!dst->absinfo)
+               return;
+
+       dst->absinfo[dst_axis] = src->absinfo[src_axis];
+}
+EXPORT_SYMBOL(input_copy_abs);
 
 /**
  * input_grab_device - grabs device for exclusive use
@@ -1746,8 +1793,6 @@ EXPORT_SYMBOL(input_reset_device);
 
 static int input_inhibit_device(struct input_dev *dev)
 {
-       int ret = 0;
-
        mutex_lock(&dev->mutex);
 
        if (dev->inhibited)
@@ -1769,7 +1814,7 @@ static int input_inhibit_device(struct input_dev *dev)
 
 out:
        mutex_unlock(&dev->mutex);
-       return ret;
+       return 0;
 }
 
 static int input_uninhibit_device(struct input_dev *dev)
@@ -2074,6 +2119,14 @@ EXPORT_SYMBOL(input_get_timestamp);
  */
 void input_set_capability(struct input_dev *dev, unsigned int type, unsigned int code)
 {
+       if (type < EV_CNT && input_max_code[type] &&
+           code > input_max_code[type]) {
+               pr_err("%s: invalid code %u for type %u\n", __func__, code,
+                      type);
+               dump_stack();
+               return;
+       }
+
        switch (type) {
        case EV_KEY:
                __set_bit(code, dev->keybit);
@@ -2085,9 +2138,6 @@ void input_set_capability(struct input_dev *dev, unsigned int type, unsigned int
 
        case EV_ABS:
                input_alloc_absinfo(dev);
-               if (!dev->absinfo)
-                       return;
-
                __set_bit(code, dev->absbit);
                break;
 
index 3b23078bc7b5b9a8eee41fe9a601e292355d4cfd..505a032e2786d2121e8019c35c849e77f4a25ee3 100644 (file)
@@ -399,4 +399,15 @@ config JOYSTICK_N64
          Say Y here if you want enable support for the four
          built-in controller ports on the Nintendo 64 console.
 
+config JOYSTICK_SENSEHAT
+       tristate "Raspberry Pi Sense HAT joystick"
+       depends on INPUT && I2C
+       select MFD_SIMPLE_MFD_I2C
+       help
+         Say Y here if you want to enable the driver for the
+         the Raspberry Pi Sense HAT.
+
+         To compile this driver as a module, choose M here: the
+         module will be called sensehat_joystick.
+
 endif
index 5174b8aba2dd01ba0a6cf4e601ae50f250c55901..3937535f00981e52052bea22e449326c3c88716e 100644 (file)
@@ -28,6 +28,7 @@ obj-$(CONFIG_JOYSTICK_N64)            += n64joy.o
 obj-$(CONFIG_JOYSTICK_PSXPAD_SPI)      += psxpad-spi.o
 obj-$(CONFIG_JOYSTICK_PXRC)            += pxrc.o
 obj-$(CONFIG_JOYSTICK_QWIIC)           += qwiic-joystick.o
+obj-$(CONFIG_JOYSTICK_SENSEHAT)        += sensehat-joystick.o
 obj-$(CONFIG_JOYSTICK_SIDEWINDER)      += sidewinder.o
 obj-$(CONFIG_JOYSTICK_SPACEBALL)       += spaceball.o
 obj-$(CONFIG_JOYSTICK_SPACEORB)                += spaceorb.o
index 592c95b87f54786e5726ec9d7350dc2dae283312..e10d57bf1180c7c2eb06824ac31d71239a14ae88 100644 (file)
@@ -123,7 +123,7 @@ static void adi_read_packet(struct adi_port *port)
 {
        struct adi *adi = port->adi;
        struct gameport *gameport = port->gameport;
-       unsigned char u, v, w, x, z;
+       unsigned char u, v, w, x;
        int t[2], s[2], i;
        unsigned long flags;
 
@@ -136,7 +136,7 @@ static void adi_read_packet(struct adi_port *port)
        local_irq_save(flags);
 
        gameport_trigger(gameport);
-       v = z = gameport_read(gameport);
+       v = gameport_read(gameport);
 
        do {
                u = v;
diff --git a/drivers/input/joystick/sensehat-joystick.c b/drivers/input/joystick/sensehat-joystick.c
new file mode 100644 (file)
index 0000000..5ad1fe4
--- /dev/null
@@ -0,0 +1,137 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*
+ * Raspberry Pi Sense HAT joystick driver
+ * http://raspberrypi.org
+ *
+ * Copyright (C) 2015 Raspberry Pi
+ * Copyright (C) 2021 Charles Mirabile, Mwesigwa Guma, Joel Savitz
+ *
+ * Original Author: Serge Schneider
+ * Revised for upstream Linux by: Charles Mirabile, Mwesigwa Guma, Joel Savitz
+ */
+
+#include <linux/module.h>
+#include <linux/input.h>
+#include <linux/i2c.h>
+#include <linux/interrupt.h>
+#include <linux/platform_device.h>
+#include <linux/regmap.h>
+#include <linux/property.h>
+
+#define JOYSTICK_SMB_REG 0xf2
+
+struct sensehat_joystick {
+       struct platform_device *pdev;
+       struct input_dev *keys_dev;
+       unsigned long prev_states;
+       struct regmap *regmap;
+};
+
+static const unsigned int keymap[] = {
+       BTN_DPAD_DOWN, BTN_DPAD_RIGHT, BTN_DPAD_UP, BTN_SELECT, BTN_DPAD_LEFT,
+};
+
+static irqreturn_t sensehat_joystick_report(int irq, void *cookie)
+{
+       struct sensehat_joystick *sensehat_joystick = cookie;
+       unsigned long curr_states, changes;
+       unsigned int keys;
+       int error;
+       int i;
+
+       error = regmap_read(sensehat_joystick->regmap, JOYSTICK_SMB_REG, &keys);
+       if (error < 0) {
+               dev_err(&sensehat_joystick->pdev->dev,
+                       "Failed to read joystick state: %d", error);
+               return IRQ_NONE;
+       }
+       curr_states = keys;
+       bitmap_xor(&changes, &curr_states, &sensehat_joystick->prev_states,
+                  ARRAY_SIZE(keymap));
+
+       for_each_set_bit(i, &changes, ARRAY_SIZE(keymap))
+               input_report_key(sensehat_joystick->keys_dev, keymap[i],
+                                curr_states & BIT(i));
+
+       input_sync(sensehat_joystick->keys_dev);
+       sensehat_joystick->prev_states = keys;
+       return IRQ_HANDLED;
+}
+
+static int sensehat_joystick_probe(struct platform_device *pdev)
+{
+       struct sensehat_joystick *sensehat_joystick;
+       int error, i, irq;
+
+       sensehat_joystick = devm_kzalloc(&pdev->dev, sizeof(*sensehat_joystick),
+                                        GFP_KERNEL);
+       if (!sensehat_joystick)
+               return -ENOMEM;
+
+       sensehat_joystick->pdev = pdev;
+
+       sensehat_joystick->regmap = dev_get_regmap(pdev->dev.parent, NULL);
+       if (!sensehat_joystick->regmap) {
+               dev_err(&pdev->dev, "unable to get sensehat regmap");
+               return -ENODEV;
+       }
+
+       sensehat_joystick->keys_dev = devm_input_allocate_device(&pdev->dev);
+       if (!sensehat_joystick->keys_dev) {
+               dev_err(&pdev->dev, "Could not allocate input device");
+               return -ENOMEM;
+       }
+
+       sensehat_joystick->keys_dev->name = "Raspberry Pi Sense HAT Joystick";
+       sensehat_joystick->keys_dev->phys = "sensehat-joystick/input0";
+       sensehat_joystick->keys_dev->id.bustype = BUS_I2C;
+
+       __set_bit(EV_KEY, sensehat_joystick->keys_dev->evbit);
+       __set_bit(EV_REP, sensehat_joystick->keys_dev->evbit);
+       for (i = 0; i < ARRAY_SIZE(keymap); i++)
+               __set_bit(keymap[i], sensehat_joystick->keys_dev->keybit);
+
+       error = input_register_device(sensehat_joystick->keys_dev);
+       if (error) {
+               dev_err(&pdev->dev, "Could not register input device");
+               return error;
+       }
+
+       irq = platform_get_irq(pdev, 0);
+       if (irq < 0) {
+               dev_err(&pdev->dev, "Could not retrieve interrupt request");
+               return irq;
+       }
+
+       error = devm_request_threaded_irq(&pdev->dev, irq,
+                                         NULL, sensehat_joystick_report,
+                                         IRQF_ONESHOT, "keys",
+                                         sensehat_joystick);
+       if (error) {
+               dev_err(&pdev->dev, "IRQ request failed");
+               return error;
+       }
+
+       return 0;
+}
+
+static const struct of_device_id sensehat_joystick_device_id[] = {
+       { .compatible = "raspberrypi,sensehat-joystick" },
+       {},
+};
+MODULE_DEVICE_TABLE(of, sensehat_joystick_device_id);
+
+static struct platform_driver sensehat_joystick_driver = {
+       .probe = sensehat_joystick_probe,
+       .driver = {
+               .name = "sensehat-joystick",
+               .of_match_table = sensehat_joystick_device_id,
+       },
+};
+
+module_platform_driver(sensehat_joystick_driver);
+
+MODULE_DESCRIPTION("Raspberry Pi Sense HAT joystick driver");
+MODULE_AUTHOR("Charles Mirabile <cmirabil@redhat.com>");
+MODULE_AUTHOR("Serge Schneider <serge@raspberrypi.org>");
+MODULE_LICENSE("GPL");
index 4c914f75a9027db503db69d9a04443059c8b5712..18190b529bca3fdf81b6b9e8aee9e11d0ec7da16 100644 (file)
@@ -131,7 +131,7 @@ static const struct xpad_device {
        { 0x045e, 0x02e3, "Microsoft X-Box One Elite pad", 0, XTYPE_XBOXONE },
        { 0x045e, 0x02ea, "Microsoft X-Box One S pad", 0, XTYPE_XBOXONE },
        { 0x045e, 0x0719, "Xbox 360 Wireless Receiver", MAP_DPAD_TO_BUTTONS, XTYPE_XBOX360W },
-       { 0x045e, 0x0b12, "Microsoft Xbox One X pad", MAP_SELECT_BUTTON, XTYPE_XBOXONE },
+       { 0x045e, 0x0b12, "Microsoft Xbox Series S|X Controller", MAP_SELECT_BUTTON, XTYPE_XBOXONE },
        { 0x046d, 0xc21d, "Logitech Gamepad F310", 0, XTYPE_XBOX360 },
        { 0x046d, 0xc21e, "Logitech Gamepad F510", 0, XTYPE_XBOX360 },
        { 0x046d, 0xc21f, "Logitech Gamepad F710", 0, XTYPE_XBOX360 },
index 9417ee0b1eff8fd6c45883c89806eb2e9297c337..4ea79db8f134becd2a0e6938a2e27ea951e59927 100644 (file)
@@ -103,6 +103,7 @@ config KEYBOARD_ATKBD
        select SERIO_LIBPS2
        select SERIO_I8042 if ARCH_MIGHT_HAVE_PC_SERIO
        select SERIO_GSCPS2 if GSC
+       select INPUT_VIVALDIFMAP
        help
          Say Y here if you want to use a standard AT or PS/2 keyboard. Usually
          you'll need this, unless you have a different type keyboard (USB, ADB
@@ -749,6 +750,7 @@ config KEYBOARD_XTKBD
 config KEYBOARD_CROS_EC
        tristate "ChromeOS EC keyboard"
        select INPUT_MATRIXKMAP
+       select INPUT_VIVALDIFMAP
        depends on CROS_EC
        help
          Say Y here to enable the matrix keyboard used by ChromeOS devices
@@ -779,6 +781,18 @@ config KEYBOARD_BCM
          To compile this driver as a module, choose M here: the
          module will be called bcm-keypad.
 
+config KEYBOARD_MT6779
+       tristate "MediaTek Keypad Support"
+       depends on ARCH_MEDIATEK || COMPILE_TEST
+       select REGMAP_MMIO
+       select INPUT_MATRIXKMAP
+       help
+         Say Y here if you want to use the keypad on MediaTek SoCs.
+         If unsure, say N.
+
+         To compile this driver as a module, choose M here: the
+         module will be called mt6779-keypad.
+
 config KEYBOARD_MTK_PMIC
        tristate "MediaTek PMIC keys support"
        depends on MFD_MT6397
index e3c8648f834ef480335b918a22413f17fa6aa160..721936e9029002c5fe341b55c6647bd1f4e4efad 100644 (file)
@@ -44,6 +44,7 @@ obj-$(CONFIG_KEYBOARD_MATRIX)         += matrix_keypad.o
 obj-$(CONFIG_KEYBOARD_MAX7359)         += max7359_keypad.o
 obj-$(CONFIG_KEYBOARD_MCS)             += mcs_touchkey.o
 obj-$(CONFIG_KEYBOARD_MPR121)          += mpr121_touchkey.o
+obj-$(CONFIG_KEYBOARD_MT6779)          += mt6779-keypad.o
 obj-$(CONFIG_KEYBOARD_MTK_PMIC)        += mtk-pmic-keys.o
 obj-$(CONFIG_KEYBOARD_NEWTON)          += newtonkbd.o
 obj-$(CONFIG_KEYBOARD_NOMADIK)         += nomadik-ske-keypad.o
index fbdef95291e90d70e9127b83701994d8a0850190..d4131236d18c5442d000e8bb568e8c9161b49a7c 100644 (file)
@@ -19,6 +19,7 @@
 #include <linux/interrupt.h>
 #include <linux/init.h>
 #include <linux/input.h>
+#include <linux/input/vivaldi-fmap.h>
 #include <linux/serio.h>
 #include <linux/workqueue.h>
 #include <linux/libps2.h>
@@ -64,8 +65,6 @@ static bool atkbd_terminal;
 module_param_named(terminal, atkbd_terminal, bool, 0);
 MODULE_PARM_DESC(terminal, "Enable break codes on an IBM Terminal keyboard connected via AT/PS2");
 
-#define MAX_FUNCTION_ROW_KEYS  24
-
 #define SCANCODE(keymap)       ((keymap >> 16) & 0xFFFF)
 #define KEYCODE(keymap)                (keymap & 0xFFFF)
 
@@ -237,8 +236,7 @@ struct atkbd {
        /* Serializes reconnect(), attr->set() and event work */
        struct mutex mutex;
 
-       u32 function_row_physmap[MAX_FUNCTION_ROW_KEYS];
-       int num_function_row_keys;
+       struct vivaldi_data vdata;
 };
 
 /*
@@ -308,17 +306,7 @@ static struct attribute *atkbd_attributes[] = {
 
 static ssize_t atkbd_show_function_row_physmap(struct atkbd *atkbd, char *buf)
 {
-       ssize_t size = 0;
-       int i;
-
-       if (!atkbd->num_function_row_keys)
-               return 0;
-
-       for (i = 0; i < atkbd->num_function_row_keys; i++)
-               size += scnprintf(buf + size, PAGE_SIZE - size, "%02X ",
-                                 atkbd->function_row_physmap[i]);
-       size += scnprintf(buf + size, PAGE_SIZE - size, "\n");
-       return size;
+       return vivaldi_function_row_physmap_show(&atkbd->vdata, buf);
 }
 
 static umode_t atkbd_attr_is_visible(struct kobject *kobj,
@@ -329,7 +317,7 @@ static umode_t atkbd_attr_is_visible(struct kobject *kobj,
        struct atkbd *atkbd = serio_get_drvdata(serio);
 
        if (attr == &atkbd_attr_function_row_physmap.attr &&
-           !atkbd->num_function_row_keys)
+           !atkbd->vdata.num_function_row_keys)
                return 0;
 
        return attr->mode;
@@ -1206,10 +1194,11 @@ static void atkbd_parse_fwnode_data(struct serio *serio)
 
        /* Parse "function-row-physmap" property */
        n = device_property_count_u32(dev, "function-row-physmap");
-       if (n > 0 && n <= MAX_FUNCTION_ROW_KEYS &&
+       if (n > 0 && n <= VIVALDI_MAX_FUNCTION_ROW_KEYS &&
            !device_property_read_u32_array(dev, "function-row-physmap",
-                                           atkbd->function_row_physmap, n)) {
-               atkbd->num_function_row_keys = n;
+                                           atkbd->vdata.function_row_physmap,
+                                           n)) {
+               atkbd->vdata.num_function_row_keys = n;
                dev_dbg(dev, "FW reported %d function-row key locations\n", n);
        }
 }
index 2b771c3a557846631b708b4435b4b605d51804cb..166d6023a53884eac7ebcaf9df2763379c05f8f9 100644 (file)
@@ -183,8 +183,7 @@ static void bcm_kp_stop(const struct bcm_kp *kp)
        writel(0xFFFFFFFF, kp->base + KPICR0_OFFSET);
        writel(0xFFFFFFFF, kp->base + KPICR1_OFFSET);
 
-       if (kp->clk)
-               clk_disable_unprepare(kp->clk);
+       clk_disable_unprepare(kp->clk);
 }
 
 static int bcm_kp_open(struct input_dev *dev)
index 019dd6ed2c295e1928e9563ae0941f53f59d06bb..939c88655fc02a20adf3a07305a066f73d04084d 100644 (file)
@@ -95,8 +95,7 @@ static int clps711x_keypad_probe(struct platform_device *pdev)
        if (!priv)
                return -ENOMEM;
 
-       priv->syscon =
-               syscon_regmap_lookup_by_compatible("cirrus,ep7209-syscon1");
+       priv->syscon = syscon_regmap_lookup_by_phandle(np, "syscon");
        if (IS_ERR(priv->syscon))
                return PTR_ERR(priv->syscon);
 
index 06a90fe82a8380acc82f9792ff817dff9e760352..cc73a149da28f9fd8abdae368b944e7418e0a3f7 100644 (file)
@@ -15,6 +15,7 @@
 #include <linux/bitops.h>
 #include <linux/i2c.h>
 #include <linux/input.h>
+#include <linux/input/vivaldi-fmap.h>
 #include <linux/interrupt.h>
 #include <linux/kernel.h>
 #include <linux/notifier.h>
@@ -27,8 +28,6 @@
 
 #include <asm/unaligned.h>
 
-#define MAX_NUM_TOP_ROW_KEYS   15
-
 /**
  * struct cros_ec_keyb - Structure representing EC keyboard device
  *
@@ -44,9 +43,7 @@
  * @idev: The input device for the matrix keys.
  * @bs_idev: The input device for non-matrix buttons and switches (or NULL).
  * @notifier: interrupt event notifier for transport devices
- * @function_row_physmap: An array of the encoded rows/columns for the top
- *                        row function keys, in an order from left to right
- * @num_function_row_keys: The number of top row keys in a custom keyboard
+ * @vdata: vivaldi function row data
  */
 struct cros_ec_keyb {
        unsigned int rows;
@@ -64,8 +61,7 @@ struct cros_ec_keyb {
        struct input_dev *bs_idev;
        struct notifier_block notifier;
 
-       u16 function_row_physmap[MAX_NUM_TOP_ROW_KEYS];
-       size_t num_function_row_keys;
+       struct vivaldi_data vdata;
 };
 
 /**
@@ -540,9 +536,9 @@ static int cros_ec_keyb_register_matrix(struct cros_ec_keyb *ckdev)
        int err;
        struct property *prop;
        const __be32 *p;
-       u16 *physmap;
+       u32 *physmap;
        u32 key_pos;
-       int row, col;
+       unsigned int row, col, scancode, n_physmap;
 
        err = matrix_keypad_parse_properties(dev, &ckdev->rows, &ckdev->cols);
        if (err)
@@ -594,20 +590,21 @@ static int cros_ec_keyb_register_matrix(struct cros_ec_keyb *ckdev)
        ckdev->idev = idev;
        cros_ec_keyb_compute_valid_keys(ckdev);
 
-       physmap = ckdev->function_row_physmap;
+       physmap = ckdev->vdata.function_row_physmap;
+       n_physmap = 0;
        of_property_for_each_u32(dev->of_node, "function-row-physmap",
                                 prop, p, key_pos) {
-               if (ckdev->num_function_row_keys == MAX_NUM_TOP_ROW_KEYS) {
+               if (n_physmap == VIVALDI_MAX_FUNCTION_ROW_KEYS) {
                        dev_warn(dev, "Only support up to %d top row keys\n",
-                                MAX_NUM_TOP_ROW_KEYS);
+                                VIVALDI_MAX_FUNCTION_ROW_KEYS);
                        break;
                }
                row = KEY_ROW(key_pos);
                col = KEY_COL(key_pos);
-               *physmap = MATRIX_SCAN_CODE(row, col, ckdev->row_shift);
-               physmap++;
-               ckdev->num_function_row_keys++;
+               scancode = MATRIX_SCAN_CODE(row, col, ckdev->row_shift);
+               physmap[n_physmap++] = scancode;
        }
+       ckdev->vdata.num_function_row_keys = n_physmap;
 
        err = input_register_device(ckdev->idev);
        if (err) {
@@ -622,18 +619,10 @@ static ssize_t function_row_physmap_show(struct device *dev,
                                         struct device_attribute *attr,
                                         char *buf)
 {
-       ssize_t size = 0;
-       int i;
-       struct cros_ec_keyb *ckdev = dev_get_drvdata(dev);
-       u16 *physmap = ckdev->function_row_physmap;
-
-       for (i = 0; i < ckdev->num_function_row_keys; i++)
-               size += scnprintf(buf + size, PAGE_SIZE - size,
-                                 "%s%02X", size ? " " : "", physmap[i]);
-       if (size)
-               size += scnprintf(buf + size, PAGE_SIZE - size, "\n");
+       const struct cros_ec_keyb *ckdev = dev_get_drvdata(dev);
+       const struct vivaldi_data *data = &ckdev->vdata;
 
-       return size;
+       return vivaldi_function_row_physmap_show(data, buf);
 }
 
 static DEVICE_ATTR_RO(function_row_physmap);
@@ -651,7 +640,7 @@ static umode_t cros_ec_keyb_attr_is_visible(struct kobject *kobj,
        struct cros_ec_keyb *ckdev = dev_get_drvdata(dev);
 
        if (attr == &dev_attr_function_row_physmap.attr &&
-           !ckdev->num_function_row_keys)
+           !ckdev->vdata.num_function_row_keys)
                return 0;
 
        return attr->mode;
index 272a4f1c6e8191f44aa159ecd793c4b05043e567..7a3b0664ab4f47eab4acf4849ca1b94ba33faa74 100644 (file)
@@ -231,7 +231,6 @@ static int ep93xx_keypad_probe(struct platform_device *pdev)
        struct ep93xx_keypad *keypad;
        const struct matrix_keymap_data *keymap_data;
        struct input_dev *input_dev;
-       struct resource *res;
        int err;
 
        keypad = devm_kzalloc(&pdev->dev, sizeof(*keypad), GFP_KERNEL);
@@ -250,11 +249,7 @@ static int ep93xx_keypad_probe(struct platform_device *pdev)
        if (keypad->irq < 0)
                return keypad->irq;
 
-       res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
-       if (!res)
-               return -ENXIO;
-
-       keypad->mmio_base = devm_ioremap_resource(&pdev->dev, res);
+       keypad->mmio_base = devm_platform_ioremap_resource(pdev, 0);
        if (IS_ERR(keypad->mmio_base))
                return PTR_ERR(keypad->mmio_base);
 
diff --git a/drivers/input/keyboard/mt6779-keypad.c b/drivers/input/keyboard/mt6779-keypad.c
new file mode 100644 (file)
index 0000000..2e7c918
--- /dev/null
@@ -0,0 +1,221 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Copyright (C) 2022 MediaTek Inc.
+ * Author Fengping Yu <fengping.yu@mediatek.com>
+ */
+#include <linux/bitops.h>
+#include <linux/clk.h>
+#include <linux/input/matrix_keypad.h>
+#include <linux/interrupt.h>
+#include <linux/module.h>
+#include <linux/property.h>
+#include <linux/platform_device.h>
+#include <linux/regmap.h>
+
+#define MTK_KPD_NAME           "mt6779-keypad"
+#define MTK_KPD_MEM            0x0004
+#define MTK_KPD_DEBOUNCE       0x0018
+#define MTK_KPD_DEBOUNCE_MASK  GENMASK(13, 0)
+#define MTK_KPD_DEBOUNCE_MAX_MS        256
+#define MTK_KPD_NUM_MEMS       5
+#define MTK_KPD_NUM_BITS       136     /* 4*32+8 MEM5 only use 8 BITS */
+
+struct mt6779_keypad {
+       struct regmap *regmap;
+       struct input_dev *input_dev;
+       struct clk *clk;
+       u32 n_rows;
+       u32 n_cols;
+       DECLARE_BITMAP(keymap_state, MTK_KPD_NUM_BITS);
+};
+
+static const struct regmap_config mt6779_keypad_regmap_cfg = {
+       .reg_bits = 32,
+       .val_bits = 32,
+       .reg_stride = sizeof(u32),
+       .max_register = 36,
+};
+
+static irqreturn_t mt6779_keypad_irq_handler(int irq, void *dev_id)
+{
+       struct mt6779_keypad *keypad = dev_id;
+       const unsigned short *keycode = keypad->input_dev->keycode;
+       DECLARE_BITMAP(new_state, MTK_KPD_NUM_BITS);
+       DECLARE_BITMAP(change, MTK_KPD_NUM_BITS);
+       unsigned int bit_nr;
+       unsigned int row, col;
+       unsigned int scancode;
+       unsigned int row_shift = get_count_order(keypad->n_cols);
+       bool pressed;
+
+       regmap_bulk_read(keypad->regmap, MTK_KPD_MEM,
+                        new_state, MTK_KPD_NUM_MEMS);
+
+       bitmap_xor(change, new_state, keypad->keymap_state, MTK_KPD_NUM_BITS);
+
+       for_each_set_bit(bit_nr, change, MTK_KPD_NUM_BITS) {
+               /*
+                * Registers are 32bits, but only bits [15:0] are used to
+                * indicate key status.
+                */
+               if (bit_nr % 32 >= 16)
+                       continue;
+
+               row = bit_nr / 32;
+               col = bit_nr % 32;
+               scancode = MATRIX_SCAN_CODE(row, col, row_shift);
+               /* 1: not pressed, 0: pressed */
+               pressed = !test_bit(bit_nr, new_state);
+               dev_dbg(&keypad->input_dev->dev, "%s",
+                       pressed ? "pressed" : "released");
+
+               input_event(keypad->input_dev, EV_MSC, MSC_SCAN, scancode);
+               input_report_key(keypad->input_dev, keycode[scancode], pressed);
+               input_sync(keypad->input_dev);
+
+               dev_dbg(&keypad->input_dev->dev,
+                       "report Linux keycode = %d\n", keycode[scancode]);
+       }
+
+       bitmap_copy(keypad->keymap_state, new_state, MTK_KPD_NUM_BITS);
+
+       return IRQ_HANDLED;
+}
+
+static void mt6779_keypad_clk_disable(void *data)
+{
+       clk_disable_unprepare(data);
+}
+
+static int mt6779_keypad_pdrv_probe(struct platform_device *pdev)
+{
+       struct mt6779_keypad *keypad;
+       void __iomem *base;
+       int irq;
+       u32 debounce;
+       bool wakeup;
+       int error;
+
+       keypad = devm_kzalloc(&pdev->dev, sizeof(*keypad), GFP_KERNEL);
+       if (!keypad)
+               return -ENOMEM;
+
+       base = devm_platform_ioremap_resource(pdev, 0);
+       if (IS_ERR(base))
+               return PTR_ERR(base);
+
+       keypad->regmap = devm_regmap_init_mmio(&pdev->dev, base,
+                                              &mt6779_keypad_regmap_cfg);
+       if (IS_ERR(keypad->regmap)) {
+               dev_err(&pdev->dev,
+                       "regmap init failed:%pe\n", keypad->regmap);
+               return PTR_ERR(keypad->regmap);
+       }
+
+       bitmap_fill(keypad->keymap_state, MTK_KPD_NUM_BITS);
+
+       keypad->input_dev = devm_input_allocate_device(&pdev->dev);
+       if (!keypad->input_dev) {
+               dev_err(&pdev->dev, "Failed to allocate input dev\n");
+               return -ENOMEM;
+       }
+
+       keypad->input_dev->name = MTK_KPD_NAME;
+       keypad->input_dev->id.bustype = BUS_HOST;
+
+       error = matrix_keypad_parse_properties(&pdev->dev, &keypad->n_rows,
+                                              &keypad->n_cols);
+       if (error) {
+               dev_err(&pdev->dev, "Failed to parse keypad params\n");
+               return error;
+       }
+
+       if (device_property_read_u32(&pdev->dev, "debounce-delay-ms",
+                                    &debounce))
+               debounce = 16;
+
+       if (debounce > MTK_KPD_DEBOUNCE_MAX_MS) {
+               dev_err(&pdev->dev,
+                       "Debounce time exceeds the maximum allowed time %dms\n",
+                       MTK_KPD_DEBOUNCE_MAX_MS);
+               return -EINVAL;
+       }
+
+       wakeup = device_property_read_bool(&pdev->dev, "wakeup-source");
+
+       dev_dbg(&pdev->dev, "n_row=%d n_col=%d debounce=%d\n",
+               keypad->n_rows, keypad->n_cols, debounce);
+
+       error = matrix_keypad_build_keymap(NULL, NULL,
+                                          keypad->n_rows, keypad->n_cols,
+                                          NULL, keypad->input_dev);
+       if (error) {
+               dev_err(&pdev->dev, "Failed to build keymap\n");
+               return error;
+       }
+
+       input_set_capability(keypad->input_dev, EV_MSC, MSC_SCAN);
+
+       regmap_write(keypad->regmap, MTK_KPD_DEBOUNCE,
+                    (debounce * (1 << 5)) & MTK_KPD_DEBOUNCE_MASK);
+
+       keypad->clk = devm_clk_get(&pdev->dev, "kpd");
+       if (IS_ERR(keypad->clk))
+               return PTR_ERR(keypad->clk);
+
+       error = clk_prepare_enable(keypad->clk);
+       if (error) {
+               dev_err(&pdev->dev, "cannot prepare/enable keypad clock\n");
+               return error;
+       }
+
+       error = devm_add_action_or_reset(&pdev->dev, mt6779_keypad_clk_disable,
+                                        keypad->clk);
+       if (error)
+               return error;
+
+       irq = platform_get_irq(pdev, 0);
+       if (irq < 0)
+               return irq;
+
+       error = devm_request_threaded_irq(&pdev->dev, irq,
+                                         NULL, mt6779_keypad_irq_handler,
+                                         IRQF_ONESHOT, MTK_KPD_NAME, keypad);
+       if (error) {
+               dev_err(&pdev->dev, "Failed to request IRQ#%d: %d\n",
+                       irq, error);
+               return error;
+       }
+
+       error = input_register_device(keypad->input_dev);
+       if (error) {
+               dev_err(&pdev->dev, "Failed to register device\n");
+               return error;
+       }
+
+       error = device_init_wakeup(&pdev->dev, wakeup);
+       if (error)
+               dev_warn(&pdev->dev, "device_init_wakeup() failed: %d\n",
+                        error);
+
+       return 0;
+}
+
+static const struct of_device_id mt6779_keypad_of_match[] = {
+       { .compatible = "mediatek,mt6779-keypad" },
+       { .compatible = "mediatek,mt6873-keypad" },
+       { /* sentinel */ }
+};
+
+static struct platform_driver mt6779_keypad_pdrv = {
+       .probe = mt6779_keypad_pdrv_probe,
+       .driver = {
+                  .name = MTK_KPD_NAME,
+                  .of_match_table = mt6779_keypad_of_match,
+       },
+};
+module_platform_driver(mt6779_keypad_pdrv);
+
+MODULE_AUTHOR("Mediatek Corporation");
+MODULE_DESCRIPTION("MTK Keypad (KPD) Driver");
+MODULE_LICENSE("GPL");
index 62391d6c7da6f9b4e0f6bc9198d9b5547a68a5df..c31ab4368388fb5309067b832b2bc838eaf78316 100644 (file)
@@ -9,6 +9,7 @@
 #include <linux/interrupt.h>
 #include <linux/kernel.h>
 #include <linux/mfd/mt6323/registers.h>
+#include <linux/mfd/mt6358/registers.h>
 #include <linux/mfd/mt6397/core.h>
 #include <linux/mfd/mt6397/registers.h>
 #include <linux/module.h>
@@ -74,11 +75,22 @@ static const struct mtk_pmic_regs mt6323_regs = {
        .pmic_rst_reg = MT6323_TOP_RST_MISC,
 };
 
+static const struct mtk_pmic_regs mt6358_regs = {
+       .keys_regs[MTK_PMIC_PWRKEY_INDEX] =
+               MTK_PMIC_KEYS_REGS(MT6358_TOPSTATUS,
+                                  0x2, MT6358_PSC_TOP_INT_CON0, 0x5),
+       .keys_regs[MTK_PMIC_HOMEKEY_INDEX] =
+               MTK_PMIC_KEYS_REGS(MT6358_TOPSTATUS,
+                                  0x8, MT6358_PSC_TOP_INT_CON0, 0xa),
+       .pmic_rst_reg = MT6358_TOP_RST_MISC,
+};
+
 struct mtk_pmic_keys_info {
        struct mtk_pmic_keys *keys;
        const struct mtk_pmic_keys_regs *regs;
        unsigned int keycode;
        int irq;
+       int irq_r; /* optional: release irq if different */
        bool wakeup:1;
 };
 
@@ -188,6 +200,18 @@ static int mtk_pmic_key_setup(struct mtk_pmic_keys *keys,
                return ret;
        }
 
+       if (info->irq_r > 0) {
+               ret = devm_request_threaded_irq(keys->dev, info->irq_r, NULL,
+                                               mtk_pmic_keys_irq_handler_thread,
+                                               IRQF_ONESHOT | IRQF_TRIGGER_HIGH,
+                                               "mtk-pmic-keys", info);
+               if (ret) {
+                       dev_err(keys->dev, "Failed to request IRQ_r: %d: %d\n",
+                               info->irq, ret);
+                       return ret;
+               }
+       }
+
        input_set_capability(keys->input_dev, EV_KEY, info->keycode);
 
        return 0;
@@ -199,8 +223,11 @@ static int __maybe_unused mtk_pmic_keys_suspend(struct device *dev)
        int index;
 
        for (index = 0; index < MTK_PMIC_MAX_KEY_COUNT; index++) {
-               if (keys->keys[index].wakeup)
+               if (keys->keys[index].wakeup) {
                        enable_irq_wake(keys->keys[index].irq);
+                       if (keys->keys[index].irq_r > 0)
+                               enable_irq_wake(keys->keys[index].irq_r);
+               }
        }
 
        return 0;
@@ -212,8 +239,11 @@ static int __maybe_unused mtk_pmic_keys_resume(struct device *dev)
        int index;
 
        for (index = 0; index < MTK_PMIC_MAX_KEY_COUNT; index++) {
-               if (keys->keys[index].wakeup)
+               if (keys->keys[index].wakeup) {
                        disable_irq_wake(keys->keys[index].irq);
+                       if (keys->keys[index].irq_r > 0)
+                               disable_irq_wake(keys->keys[index].irq_r);
+               }
        }
 
        return 0;
@@ -229,6 +259,9 @@ static const struct of_device_id of_mtk_pmic_keys_match_tbl[] = {
        }, {
                .compatible = "mediatek,mt6323-keys",
                .data = &mt6323_regs,
+       }, {
+               .compatible = "mediatek,mt6358-keys",
+               .data = &mt6358_regs,
        }, {
                /* sentinel */
        }
@@ -241,6 +274,8 @@ static int mtk_pmic_keys_probe(struct platform_device *pdev)
        unsigned int keycount;
        struct mt6397_chip *pmic_chip = dev_get_drvdata(pdev->dev.parent);
        struct device_node *node = pdev->dev.of_node, *child;
+       static const char *const irqnames[] = { "powerkey", "homekey" };
+       static const char *const irqnames_r[] = { "powerkey_r", "homekey_r" };
        struct mtk_pmic_keys *keys;
        const struct mtk_pmic_regs *mtk_pmic_regs;
        struct input_dev *input_dev;
@@ -268,7 +303,8 @@ static int mtk_pmic_keys_probe(struct platform_device *pdev)
        input_dev->id.version = 0x0001;
 
        keycount = of_get_available_child_count(node);
-       if (keycount > MTK_PMIC_MAX_KEY_COUNT) {
+       if (keycount > MTK_PMIC_MAX_KEY_COUNT ||
+           keycount > ARRAY_SIZE(irqnames)) {
                dev_err(keys->dev, "too many keys defined (%d)\n", keycount);
                return -EINVAL;
        }
@@ -276,12 +312,23 @@ static int mtk_pmic_keys_probe(struct platform_device *pdev)
        for_each_child_of_node(node, child) {
                keys->keys[index].regs = &mtk_pmic_regs->keys_regs[index];
 
-               keys->keys[index].irq = platform_get_irq(pdev, index);
+               keys->keys[index].irq =
+                       platform_get_irq_byname(pdev, irqnames[index]);
                if (keys->keys[index].irq < 0) {
                        of_node_put(child);
                        return keys->keys[index].irq;
                }
 
+               if (of_device_is_compatible(node, "mediatek,mt6358-keys")) {
+                       keys->keys[index].irq_r = platform_get_irq_byname(pdev,
+                                                                         irqnames_r[index]);
+
+                       if (keys->keys[index].irq_r < 0) {
+                               of_node_put(child);
+                               return keys->keys[index].irq_r;
+                       }
+               }
+
                error = of_property_read_u32(child,
                        "linux,keycodes", &keys->keys[index].keycode);
                if (error) {
index 4a796bed48acc10b13cc235d4871ce82e6ed7368..15c15c0958b00be87bce9ce0ea2f9fffe58f247e 100644 (file)
@@ -14,6 +14,7 @@
  * there are no boards known to use channel 1.
  */
 
+#include <linux/clk.h>
 #include <linux/err.h>
 #include <linux/init.h>
 #include <linux/input.h>
 #include <linux/module.h>
 #include <linux/of_platform.h>
 #include <linux/platform_device.h>
+#include <linux/pm_wakeirq.h>
+#include <linux/pm_wakeup.h>
 #include <linux/regulator/consumer.h>
+#include <linux/reset.h>
 #include <linux/slab.h>
 
 #define LRADC_CTRL             0x00
 /* struct lradc_variant - Describe sun4i-a10-lradc-keys hardware variant
  * @divisor_numerator:         The numerator of lradc Vref internally divisor
  * @divisor_denominator:       The denominator of lradc Vref internally divisor
+ * @has_clock_reset:           If the binding requires a clock and reset
  */
 struct lradc_variant {
        u8 divisor_numerator;
        u8 divisor_denominator;
+       bool has_clock_reset;
 };
 
 static const struct lradc_variant lradc_variant_a10 = {
@@ -74,6 +80,12 @@ static const struct lradc_variant r_lradc_variant_a83t = {
        .divisor_denominator = 4
 };
 
+static const struct lradc_variant lradc_variant_r329 = {
+       .divisor_numerator = 3,
+       .divisor_denominator = 4,
+       .has_clock_reset = true,
+};
+
 struct sun4i_lradc_keymap {
        u32 voltage;
        u32 keycode;
@@ -83,6 +95,8 @@ struct sun4i_lradc_data {
        struct device *dev;
        struct input_dev *input;
        void __iomem *base;
+       struct clk *clk;
+       struct reset_control *reset;
        struct regulator *vref_supply;
        struct sun4i_lradc_keymap *chan0_map;
        const struct lradc_variant *variant;
@@ -140,6 +154,14 @@ static int sun4i_lradc_open(struct input_dev *dev)
        if (error)
                return error;
 
+       error = reset_control_deassert(lradc->reset);
+       if (error)
+               goto err_disable_reg;
+
+       error = clk_prepare_enable(lradc->clk);
+       if (error)
+               goto err_assert_reset;
+
        lradc->vref = regulator_get_voltage(lradc->vref_supply) *
                      lradc->variant->divisor_numerator /
                      lradc->variant->divisor_denominator;
@@ -153,6 +175,13 @@ static int sun4i_lradc_open(struct input_dev *dev)
        writel(CHAN0_KEYUP_IRQ | CHAN0_KEYDOWN_IRQ, lradc->base + LRADC_INTC);
 
        return 0;
+
+err_assert_reset:
+       reset_control_assert(lradc->reset);
+err_disable_reg:
+       regulator_disable(lradc->vref_supply);
+
+       return error;
 }
 
 static void sun4i_lradc_close(struct input_dev *dev)
@@ -164,6 +193,8 @@ static void sun4i_lradc_close(struct input_dev *dev)
                SAMPLE_RATE(2), lradc->base + LRADC_CTRL);
        writel(0, lradc->base + LRADC_INTC);
 
+       clk_disable_unprepare(lradc->clk);
+       reset_control_assert(lradc->reset);
        regulator_disable(lradc->vref_supply);
 }
 
@@ -226,8 +257,7 @@ static int sun4i_lradc_probe(struct platform_device *pdev)
 {
        struct sun4i_lradc_data *lradc;
        struct device *dev = &pdev->dev;
-       int i;
-       int error;
+       int error, i, irq;
 
        lradc = devm_kzalloc(dev, sizeof(struct sun4i_lradc_data), GFP_KERNEL);
        if (!lradc)
@@ -243,6 +273,16 @@ static int sun4i_lradc_probe(struct platform_device *pdev)
                return -EINVAL;
        }
 
+       if (lradc->variant->has_clock_reset) {
+               lradc->clk = devm_clk_get(dev, NULL);
+               if (IS_ERR(lradc->clk))
+                       return PTR_ERR(lradc->clk);
+
+               lradc->reset = devm_reset_control_get_exclusive(dev, NULL);
+               if (IS_ERR(lradc->reset))
+                       return PTR_ERR(lradc->reset);
+       }
+
        lradc->vref_supply = devm_regulator_get(dev, "vref");
        if (IS_ERR(lradc->vref_supply))
                return PTR_ERR(lradc->vref_supply);
@@ -272,8 +312,11 @@ static int sun4i_lradc_probe(struct platform_device *pdev)
        if (IS_ERR(lradc->base))
                return PTR_ERR(lradc->base);
 
-       error = devm_request_irq(dev, platform_get_irq(pdev, 0),
-                                sun4i_lradc_irq, 0,
+       irq = platform_get_irq(pdev, 0);
+       if (irq < 0)
+               return irq;
+
+       error = devm_request_irq(dev, irq, sun4i_lradc_irq, 0,
                                 "sun4i-a10-lradc-keys", lradc);
        if (error)
                return error;
@@ -282,6 +325,16 @@ static int sun4i_lradc_probe(struct platform_device *pdev)
        if (error)
                return error;
 
+       if (device_property_read_bool(dev, "wakeup-source")) {
+               error = dev_pm_set_wake_irq(dev, irq);
+               if (error)
+                       dev_warn(dev,
+                                "Failed to set IRQ %d as a wake IRQ: %d\n",
+                                irq, error);
+               else
+                       device_set_wakeup_capable(dev, true);
+       }
+
        return 0;
 }
 
@@ -290,6 +343,8 @@ static const struct of_device_id sun4i_lradc_of_match[] = {
                .data = &lradc_variant_a10 },
        { .compatible = "allwinner,sun8i-a83t-r-lradc",
                .data = &r_lradc_variant_a83t },
+       { .compatible = "allwinner,sun50i-r329-lradc",
+               .data = &lradc_variant_r329 },
        { /* sentinel */ }
 };
 MODULE_DEVICE_TABLE(of, sun4i_lradc_of_match);
index dd5227cf86964b75445709662884af2c4fdc4a41..a18ab7358d8f39c0fea58157ea86dfa467706606 100644 (file)
@@ -762,6 +762,16 @@ config INPUT_IQS626A
          To compile this driver as a module, choose M here: the
          module will be called iqs626a.
 
+config INPUT_IQS7222
+       tristate "Azoteq IQS7222A/B/C capacitive touch controller"
+       depends on I2C
+       help
+         Say Y to enable support for the Azoteq IQS7222A/B/C family
+         of capacitive touch controllers.
+
+         To compile this driver as a module, choose M here: the
+         module will be called iqs7222.
+
 config INPUT_CMA3000
        tristate "VTI CMA3000 Tri-axis accelerometer"
        help
index b92c53a6b5ae8116f98c97b1d1a6b754f4718c5b..28dfc444f0a96b0d9b1b00ed43b4a1dc990fcce4 100644 (file)
@@ -44,6 +44,7 @@ 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_IQS7222)            += iqs7222.o
 obj-$(CONFIG_INPUT_KEYSPAN_REMOTE)     += keyspan_remote.o
 obj-$(CONFIG_INPUT_KXTJ9)              += kxtj9.o
 obj-$(CONFIG_INPUT_M68K_BEEP)          += m68kspkr.o
index 79851923ee576ab59d28d2f764b30d6b6a4b2f67..b14a389600c9eab7c356d6a85bea78aaf1e46289 100644 (file)
@@ -4,6 +4,7 @@
  * Copyright (C) 2015  Dialog Semiconductor Ltd.
  */
 
+#include <linux/devm-helpers.h>
 #include <linux/module.h>
 #include <linux/errno.h>
 #include <linux/input.h>
@@ -182,13 +183,6 @@ static irqreturn_t da9063_onkey_irq_handler(int irq, void *data)
        return IRQ_HANDLED;
 }
 
-static void da9063_cancel_poll(void *data)
-{
-       struct da9063_onkey *onkey = data;
-
-       cancel_delayed_work_sync(&onkey->work);
-}
-
 static int da9063_onkey_probe(struct platform_device *pdev)
 {
        struct da9063_onkey *onkey;
@@ -234,9 +228,8 @@ static int da9063_onkey_probe(struct platform_device *pdev)
 
        input_set_capability(onkey->input, EV_KEY, KEY_POWER);
 
-       INIT_DELAYED_WORK(&onkey->work, da9063_poll_on);
-
-       error = devm_add_action(&pdev->dev, da9063_cancel_poll, onkey);
+       error = devm_delayed_work_autocancel(&pdev->dev, &onkey->work,
+                                            da9063_poll_on);
        if (error) {
                dev_err(&pdev->dev,
                        "Failed to add cancel poll action: %d\n",
diff --git a/drivers/input/misc/iqs7222.c b/drivers/input/misc/iqs7222.c
new file mode 100644 (file)
index 0000000..6b41387
--- /dev/null
@@ -0,0 +1,2446 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*
+ * Azoteq IQS7222A/B/C Capacitive Touch Controller
+ *
+ * Copyright (C) 2022 Jeff LaBundy <jeff@labundy.com>
+ */
+
+#include <linux/bits.h>
+#include <linux/delay.h>
+#include <linux/device.h>
+#include <linux/err.h>
+#include <linux/gpio/consumer.h>
+#include <linux/i2c.h>
+#include <linux/input.h>
+#include <linux/interrupt.h>
+#include <linux/kernel.h>
+#include <linux/ktime.h>
+#include <linux/module.h>
+#include <linux/of_device.h>
+#include <linux/property.h>
+#include <linux/slab.h>
+#include <asm/unaligned.h>
+
+#define IQS7222_PROD_NUM                       0x00
+#define IQS7222_PROD_NUM_A                     840
+#define IQS7222_PROD_NUM_B                     698
+#define IQS7222_PROD_NUM_C                     863
+
+#define IQS7222_SYS_STATUS                     0x10
+#define IQS7222_SYS_STATUS_RESET               BIT(3)
+#define IQS7222_SYS_STATUS_ATI_ERROR           BIT(1)
+#define IQS7222_SYS_STATUS_ATI_ACTIVE          BIT(0)
+
+#define IQS7222_CHAN_SETUP_0_REF_MODE_MASK     GENMASK(15, 14)
+#define IQS7222_CHAN_SETUP_0_REF_MODE_FOLLOW   BIT(15)
+#define IQS7222_CHAN_SETUP_0_REF_MODE_REF      BIT(14)
+#define IQS7222_CHAN_SETUP_0_CHAN_EN           BIT(8)
+
+#define IQS7222_SLDR_SETUP_0_CHAN_CNT_MASK     GENMASK(2, 0)
+#define IQS7222_SLDR_SETUP_2_RES_MASK          GENMASK(15, 8)
+#define IQS7222_SLDR_SETUP_2_RES_SHIFT         8
+#define IQS7222_SLDR_SETUP_2_TOP_SPEED_MASK    GENMASK(7, 0)
+#define IQS7222_SLDR_SETUP_3_CHAN_SEL_MASK     GENMASK(9, 0)
+
+#define IQS7222_GPIO_SETUP_0_GPIO_EN           BIT(0)
+
+#define IQS7222_SYS_SETUP                      0xD0
+#define IQS7222_SYS_SETUP_INTF_MODE_MASK       GENMASK(7, 6)
+#define IQS7222_SYS_SETUP_INTF_MODE_TOUCH      BIT(7)
+#define IQS7222_SYS_SETUP_INTF_MODE_EVENT      BIT(6)
+#define IQS7222_SYS_SETUP_PWR_MODE_MASK                GENMASK(5, 4)
+#define IQS7222_SYS_SETUP_PWR_MODE_AUTO                IQS7222_SYS_SETUP_PWR_MODE_MASK
+#define IQS7222_SYS_SETUP_REDO_ATI             BIT(2)
+#define IQS7222_SYS_SETUP_ACK_RESET            BIT(0)
+
+#define IQS7222_EVENT_MASK_ATI                 BIT(12)
+
+#define IQS7222_COMMS_HOLD                     BIT(0)
+#define IQS7222_COMMS_ERROR                    0xEEEE
+#define IQS7222_COMMS_RETRY_MS                 50
+#define IQS7222_COMMS_TIMEOUT_MS               100
+#define IQS7222_RESET_TIMEOUT_MS               250
+#define IQS7222_ATI_TIMEOUT_MS                 2000
+
+#define IQS7222_MAX_COLS_STAT                  8
+#define IQS7222_MAX_COLS_CYCLE                 3
+#define IQS7222_MAX_COLS_GLBL                  3
+#define IQS7222_MAX_COLS_BTN                   3
+#define IQS7222_MAX_COLS_CHAN                  6
+#define IQS7222_MAX_COLS_FILT                  2
+#define IQS7222_MAX_COLS_SLDR                  11
+#define IQS7222_MAX_COLS_GPIO                  3
+#define IQS7222_MAX_COLS_SYS                   13
+
+#define IQS7222_MAX_CHAN                       20
+#define IQS7222_MAX_SLDR                       2
+
+#define IQS7222_NUM_RETRIES                    5
+#define IQS7222_REG_OFFSET                     0x100
+
+enum iqs7222_reg_key_id {
+       IQS7222_REG_KEY_NONE,
+       IQS7222_REG_KEY_PROX,
+       IQS7222_REG_KEY_TOUCH,
+       IQS7222_REG_KEY_DEBOUNCE,
+       IQS7222_REG_KEY_TAP,
+       IQS7222_REG_KEY_AXIAL,
+       IQS7222_REG_KEY_WHEEL,
+       IQS7222_REG_KEY_NO_WHEEL,
+       IQS7222_REG_KEY_RESERVED
+};
+
+enum iqs7222_reg_grp_id {
+       IQS7222_REG_GRP_STAT,
+       IQS7222_REG_GRP_CYCLE,
+       IQS7222_REG_GRP_GLBL,
+       IQS7222_REG_GRP_BTN,
+       IQS7222_REG_GRP_CHAN,
+       IQS7222_REG_GRP_FILT,
+       IQS7222_REG_GRP_SLDR,
+       IQS7222_REG_GRP_GPIO,
+       IQS7222_REG_GRP_SYS,
+       IQS7222_NUM_REG_GRPS
+};
+
+static const char * const iqs7222_reg_grp_names[] = {
+       [IQS7222_REG_GRP_CYCLE] = "cycle",
+       [IQS7222_REG_GRP_CHAN] = "channel",
+       [IQS7222_REG_GRP_SLDR] = "slider",
+       [IQS7222_REG_GRP_GPIO] = "gpio",
+};
+
+static const unsigned int iqs7222_max_cols[] = {
+       [IQS7222_REG_GRP_STAT] = IQS7222_MAX_COLS_STAT,
+       [IQS7222_REG_GRP_CYCLE] = IQS7222_MAX_COLS_CYCLE,
+       [IQS7222_REG_GRP_GLBL] = IQS7222_MAX_COLS_GLBL,
+       [IQS7222_REG_GRP_BTN] = IQS7222_MAX_COLS_BTN,
+       [IQS7222_REG_GRP_CHAN] = IQS7222_MAX_COLS_CHAN,
+       [IQS7222_REG_GRP_FILT] = IQS7222_MAX_COLS_FILT,
+       [IQS7222_REG_GRP_SLDR] = IQS7222_MAX_COLS_SLDR,
+       [IQS7222_REG_GRP_GPIO] = IQS7222_MAX_COLS_GPIO,
+       [IQS7222_REG_GRP_SYS] = IQS7222_MAX_COLS_SYS,
+};
+
+static const unsigned int iqs7222_gpio_links[] = { 2, 5, 6, };
+
+struct iqs7222_event_desc {
+       const char *name;
+       u16 mask;
+       u16 val;
+       u16 enable;
+       enum iqs7222_reg_key_id reg_key;
+};
+
+static const struct iqs7222_event_desc iqs7222_kp_events[] = {
+       {
+               .name = "event-prox",
+               .enable = BIT(0),
+               .reg_key = IQS7222_REG_KEY_PROX,
+       },
+       {
+               .name = "event-touch",
+               .enable = BIT(1),
+               .reg_key = IQS7222_REG_KEY_TOUCH,
+       },
+};
+
+static const struct iqs7222_event_desc iqs7222_sl_events[] = {
+       { .name = "event-press", },
+       {
+               .name = "event-tap",
+               .mask = BIT(0),
+               .val = BIT(0),
+               .enable = BIT(0),
+               .reg_key = IQS7222_REG_KEY_TAP,
+       },
+       {
+               .name = "event-swipe-pos",
+               .mask = BIT(5) | BIT(1),
+               .val = BIT(1),
+               .enable = BIT(1),
+               .reg_key = IQS7222_REG_KEY_AXIAL,
+       },
+       {
+               .name = "event-swipe-neg",
+               .mask = BIT(5) | BIT(1),
+               .val = BIT(5) | BIT(1),
+               .enable = BIT(1),
+               .reg_key = IQS7222_REG_KEY_AXIAL,
+       },
+       {
+               .name = "event-flick-pos",
+               .mask = BIT(5) | BIT(2),
+               .val = BIT(2),
+               .enable = BIT(2),
+               .reg_key = IQS7222_REG_KEY_AXIAL,
+       },
+       {
+               .name = "event-flick-neg",
+               .mask = BIT(5) | BIT(2),
+               .val = BIT(5) | BIT(2),
+               .enable = BIT(2),
+               .reg_key = IQS7222_REG_KEY_AXIAL,
+       },
+};
+
+struct iqs7222_reg_grp_desc {
+       u16 base;
+       int num_row;
+       int num_col;
+};
+
+struct iqs7222_dev_desc {
+       u16 prod_num;
+       u16 fw_major;
+       u16 fw_minor;
+       u16 sldr_res;
+       u16 touch_link;
+       u16 wheel_enable;
+       int allow_offset;
+       int event_offset;
+       int comms_offset;
+       struct iqs7222_reg_grp_desc reg_grps[IQS7222_NUM_REG_GRPS];
+};
+
+static const struct iqs7222_dev_desc iqs7222_devs[] = {
+       {
+               .prod_num = IQS7222_PROD_NUM_A,
+               .fw_major = 1,
+               .fw_minor = 12,
+               .sldr_res = U8_MAX * 16,
+               .touch_link = 1768,
+               .allow_offset = 9,
+               .event_offset = 10,
+               .comms_offset = 12,
+               .reg_grps = {
+                       [IQS7222_REG_GRP_STAT] = {
+                               .base = IQS7222_SYS_STATUS,
+                               .num_row = 1,
+                               .num_col = 8,
+                       },
+                       [IQS7222_REG_GRP_CYCLE] = {
+                               .base = 0x8000,
+                               .num_row = 7,
+                               .num_col = 3,
+                       },
+                       [IQS7222_REG_GRP_GLBL] = {
+                               .base = 0x8700,
+                               .num_row = 1,
+                               .num_col = 3,
+                       },
+                       [IQS7222_REG_GRP_BTN] = {
+                               .base = 0x9000,
+                               .num_row = 12,
+                               .num_col = 3,
+                       },
+                       [IQS7222_REG_GRP_CHAN] = {
+                               .base = 0xA000,
+                               .num_row = 12,
+                               .num_col = 6,
+                       },
+                       [IQS7222_REG_GRP_FILT] = {
+                               .base = 0xAC00,
+                               .num_row = 1,
+                               .num_col = 2,
+                       },
+                       [IQS7222_REG_GRP_SLDR] = {
+                               .base = 0xB000,
+                               .num_row = 2,
+                               .num_col = 11,
+                       },
+                       [IQS7222_REG_GRP_GPIO] = {
+                               .base = 0xC000,
+                               .num_row = 1,
+                               .num_col = 3,
+                       },
+                       [IQS7222_REG_GRP_SYS] = {
+                               .base = IQS7222_SYS_SETUP,
+                               .num_row = 1,
+                               .num_col = 13,
+                       },
+               },
+       },
+       {
+               .prod_num = IQS7222_PROD_NUM_B,
+               .fw_major = 1,
+               .fw_minor = 43,
+               .event_offset = 10,
+               .comms_offset = 11,
+               .reg_grps = {
+                       [IQS7222_REG_GRP_STAT] = {
+                               .base = IQS7222_SYS_STATUS,
+                               .num_row = 1,
+                               .num_col = 6,
+                       },
+                       [IQS7222_REG_GRP_CYCLE] = {
+                               .base = 0x8000,
+                               .num_row = 10,
+                               .num_col = 2,
+                       },
+                       [IQS7222_REG_GRP_GLBL] = {
+                               .base = 0x8A00,
+                               .num_row = 1,
+                               .num_col = 3,
+                       },
+                       [IQS7222_REG_GRP_BTN] = {
+                               .base = 0x9000,
+                               .num_row = 20,
+                               .num_col = 2,
+                       },
+                       [IQS7222_REG_GRP_CHAN] = {
+                               .base = 0xB000,
+                               .num_row = 20,
+                               .num_col = 4,
+                       },
+                       [IQS7222_REG_GRP_FILT] = {
+                               .base = 0xC400,
+                               .num_row = 1,
+                               .num_col = 2,
+                       },
+                       [IQS7222_REG_GRP_SYS] = {
+                               .base = IQS7222_SYS_SETUP,
+                               .num_row = 1,
+                               .num_col = 13,
+                       },
+               },
+       },
+       {
+               .prod_num = IQS7222_PROD_NUM_B,
+               .fw_major = 1,
+               .fw_minor = 27,
+               .reg_grps = {
+                       [IQS7222_REG_GRP_STAT] = {
+                               .base = IQS7222_SYS_STATUS,
+                               .num_row = 1,
+                               .num_col = 6,
+                       },
+                       [IQS7222_REG_GRP_CYCLE] = {
+                               .base = 0x8000,
+                               .num_row = 10,
+                               .num_col = 2,
+                       },
+                       [IQS7222_REG_GRP_GLBL] = {
+                               .base = 0x8A00,
+                               .num_row = 1,
+                               .num_col = 3,
+                       },
+                       [IQS7222_REG_GRP_BTN] = {
+                               .base = 0x9000,
+                               .num_row = 20,
+                               .num_col = 2,
+                       },
+                       [IQS7222_REG_GRP_CHAN] = {
+                               .base = 0xB000,
+                               .num_row = 20,
+                               .num_col = 4,
+                       },
+                       [IQS7222_REG_GRP_FILT] = {
+                               .base = 0xC400,
+                               .num_row = 1,
+                               .num_col = 2,
+                       },
+                       [IQS7222_REG_GRP_SYS] = {
+                               .base = IQS7222_SYS_SETUP,
+                               .num_row = 1,
+                               .num_col = 10,
+                       },
+               },
+       },
+       {
+               .prod_num = IQS7222_PROD_NUM_C,
+               .fw_major = 2,
+               .fw_minor = 6,
+               .sldr_res = U16_MAX,
+               .touch_link = 1686,
+               .wheel_enable = BIT(3),
+               .event_offset = 9,
+               .comms_offset = 10,
+               .reg_grps = {
+                       [IQS7222_REG_GRP_STAT] = {
+                               .base = IQS7222_SYS_STATUS,
+                               .num_row = 1,
+                               .num_col = 6,
+                       },
+                       [IQS7222_REG_GRP_CYCLE] = {
+                               .base = 0x8000,
+                               .num_row = 5,
+                               .num_col = 3,
+                       },
+                       [IQS7222_REG_GRP_GLBL] = {
+                               .base = 0x8500,
+                               .num_row = 1,
+                               .num_col = 3,
+                       },
+                       [IQS7222_REG_GRP_BTN] = {
+                               .base = 0x9000,
+                               .num_row = 10,
+                               .num_col = 3,
+                       },
+                       [IQS7222_REG_GRP_CHAN] = {
+                               .base = 0xA000,
+                               .num_row = 10,
+                               .num_col = 6,
+                       },
+                       [IQS7222_REG_GRP_FILT] = {
+                               .base = 0xAA00,
+                               .num_row = 1,
+                               .num_col = 2,
+                       },
+                       [IQS7222_REG_GRP_SLDR] = {
+                               .base = 0xB000,
+                               .num_row = 2,
+                               .num_col = 10,
+                       },
+                       [IQS7222_REG_GRP_GPIO] = {
+                               .base = 0xC000,
+                               .num_row = 3,
+                               .num_col = 3,
+                       },
+                       [IQS7222_REG_GRP_SYS] = {
+                               .base = IQS7222_SYS_SETUP,
+                               .num_row = 1,
+                               .num_col = 12,
+                       },
+               },
+       },
+       {
+               .prod_num = IQS7222_PROD_NUM_C,
+               .fw_major = 1,
+               .fw_minor = 13,
+               .sldr_res = U16_MAX,
+               .touch_link = 1674,
+               .wheel_enable = BIT(3),
+               .event_offset = 9,
+               .comms_offset = 10,
+               .reg_grps = {
+                       [IQS7222_REG_GRP_STAT] = {
+                               .base = IQS7222_SYS_STATUS,
+                               .num_row = 1,
+                               .num_col = 6,
+                       },
+                       [IQS7222_REG_GRP_CYCLE] = {
+                               .base = 0x8000,
+                               .num_row = 5,
+                               .num_col = 3,
+                       },
+                       [IQS7222_REG_GRP_GLBL] = {
+                               .base = 0x8500,
+                               .num_row = 1,
+                               .num_col = 3,
+                       },
+                       [IQS7222_REG_GRP_BTN] = {
+                               .base = 0x9000,
+                               .num_row = 10,
+                               .num_col = 3,
+                       },
+                       [IQS7222_REG_GRP_CHAN] = {
+                               .base = 0xA000,
+                               .num_row = 10,
+                               .num_col = 6,
+                       },
+                       [IQS7222_REG_GRP_FILT] = {
+                               .base = 0xAA00,
+                               .num_row = 1,
+                               .num_col = 2,
+                       },
+                       [IQS7222_REG_GRP_SLDR] = {
+                               .base = 0xB000,
+                               .num_row = 2,
+                               .num_col = 10,
+                       },
+                       [IQS7222_REG_GRP_GPIO] = {
+                               .base = 0xC000,
+                               .num_row = 1,
+                               .num_col = 3,
+                       },
+                       [IQS7222_REG_GRP_SYS] = {
+                               .base = IQS7222_SYS_SETUP,
+                               .num_row = 1,
+                               .num_col = 11,
+                       },
+               },
+       },
+};
+
+struct iqs7222_prop_desc {
+       const char *name;
+       enum iqs7222_reg_grp_id reg_grp;
+       enum iqs7222_reg_key_id reg_key;
+       int reg_offset;
+       int reg_shift;
+       int reg_width;
+       int val_pitch;
+       int val_min;
+       int val_max;
+       bool invert;
+       const char *label;
+};
+
+static const struct iqs7222_prop_desc iqs7222_props[] = {
+       {
+               .name = "azoteq,conv-period",
+               .reg_grp = IQS7222_REG_GRP_CYCLE,
+               .reg_offset = 0,
+               .reg_shift = 8,
+               .reg_width = 8,
+               .label = "conversion period",
+       },
+       {
+               .name = "azoteq,conv-frac",
+               .reg_grp = IQS7222_REG_GRP_CYCLE,
+               .reg_offset = 0,
+               .reg_shift = 0,
+               .reg_width = 8,
+               .label = "conversion frequency fractional divider",
+       },
+       {
+               .name = "azoteq,rx-float-inactive",
+               .reg_grp = IQS7222_REG_GRP_CYCLE,
+               .reg_offset = 1,
+               .reg_shift = 6,
+               .reg_width = 1,
+               .invert = true,
+       },
+       {
+               .name = "azoteq,dead-time-enable",
+               .reg_grp = IQS7222_REG_GRP_CYCLE,
+               .reg_offset = 1,
+               .reg_shift = 5,
+               .reg_width = 1,
+       },
+       {
+               .name = "azoteq,tx-freq-fosc",
+               .reg_grp = IQS7222_REG_GRP_CYCLE,
+               .reg_offset = 1,
+               .reg_shift = 4,
+               .reg_width = 1,
+       },
+       {
+               .name = "azoteq,vbias-enable",
+               .reg_grp = IQS7222_REG_GRP_CYCLE,
+               .reg_offset = 1,
+               .reg_shift = 3,
+               .reg_width = 1,
+       },
+       {
+               .name = "azoteq,sense-mode",
+               .reg_grp = IQS7222_REG_GRP_CYCLE,
+               .reg_offset = 1,
+               .reg_shift = 0,
+               .reg_width = 3,
+               .val_max = 3,
+               .label = "sensing mode",
+       },
+       {
+               .name = "azoteq,iref-enable",
+               .reg_grp = IQS7222_REG_GRP_CYCLE,
+               .reg_offset = 2,
+               .reg_shift = 10,
+               .reg_width = 1,
+       },
+       {
+               .name = "azoteq,iref-level",
+               .reg_grp = IQS7222_REG_GRP_CYCLE,
+               .reg_offset = 2,
+               .reg_shift = 4,
+               .reg_width = 4,
+               .label = "current reference level",
+       },
+       {
+               .name = "azoteq,iref-trim",
+               .reg_grp = IQS7222_REG_GRP_CYCLE,
+               .reg_offset = 2,
+               .reg_shift = 0,
+               .reg_width = 4,
+               .label = "current reference trim",
+       },
+       {
+               .name = "azoteq,rf-filt-enable",
+               .reg_grp = IQS7222_REG_GRP_GLBL,
+               .reg_offset = 0,
+               .reg_shift = 15,
+               .reg_width = 1,
+       },
+       {
+               .name = "azoteq,max-counts",
+               .reg_grp = IQS7222_REG_GRP_GLBL,
+               .reg_offset = 0,
+               .reg_shift = 13,
+               .reg_width = 2,
+               .label = "maximum counts",
+       },
+       {
+               .name = "azoteq,auto-mode",
+               .reg_grp = IQS7222_REG_GRP_GLBL,
+               .reg_offset = 0,
+               .reg_shift = 2,
+               .reg_width = 2,
+               .label = "number of conversions",
+       },
+       {
+               .name = "azoteq,ati-frac-div-fine",
+               .reg_grp = IQS7222_REG_GRP_GLBL,
+               .reg_offset = 1,
+               .reg_shift = 9,
+               .reg_width = 5,
+               .label = "ATI fine fractional divider",
+       },
+       {
+               .name = "azoteq,ati-frac-div-coarse",
+               .reg_grp = IQS7222_REG_GRP_GLBL,
+               .reg_offset = 1,
+               .reg_shift = 0,
+               .reg_width = 5,
+               .label = "ATI coarse fractional divider",
+       },
+       {
+               .name = "azoteq,ati-comp-select",
+               .reg_grp = IQS7222_REG_GRP_GLBL,
+               .reg_offset = 2,
+               .reg_shift = 0,
+               .reg_width = 10,
+               .label = "ATI compensation selection",
+       },
+       {
+               .name = "azoteq,ati-band",
+               .reg_grp = IQS7222_REG_GRP_CHAN,
+               .reg_offset = 0,
+               .reg_shift = 12,
+               .reg_width = 2,
+               .label = "ATI band",
+       },
+       {
+               .name = "azoteq,global-halt",
+               .reg_grp = IQS7222_REG_GRP_CHAN,
+               .reg_offset = 0,
+               .reg_shift = 11,
+               .reg_width = 1,
+       },
+       {
+               .name = "azoteq,invert-enable",
+               .reg_grp = IQS7222_REG_GRP_CHAN,
+               .reg_offset = 0,
+               .reg_shift = 10,
+               .reg_width = 1,
+       },
+       {
+               .name = "azoteq,dual-direction",
+               .reg_grp = IQS7222_REG_GRP_CHAN,
+               .reg_offset = 0,
+               .reg_shift = 9,
+               .reg_width = 1,
+       },
+       {
+               .name = "azoteq,samp-cap-double",
+               .reg_grp = IQS7222_REG_GRP_CHAN,
+               .reg_offset = 0,
+               .reg_shift = 3,
+               .reg_width = 1,
+       },
+       {
+               .name = "azoteq,vref-half",
+               .reg_grp = IQS7222_REG_GRP_CHAN,
+               .reg_offset = 0,
+               .reg_shift = 2,
+               .reg_width = 1,
+       },
+       {
+               .name = "azoteq,proj-bias",
+               .reg_grp = IQS7222_REG_GRP_CHAN,
+               .reg_offset = 0,
+               .reg_shift = 0,
+               .reg_width = 2,
+               .label = "projected bias current",
+       },
+       {
+               .name = "azoteq,ati-target",
+               .reg_grp = IQS7222_REG_GRP_CHAN,
+               .reg_offset = 1,
+               .reg_shift = 8,
+               .reg_width = 8,
+               .val_pitch = 8,
+               .label = "ATI target",
+       },
+       {
+               .name = "azoteq,ati-base",
+               .reg_grp = IQS7222_REG_GRP_CHAN,
+               .reg_offset = 1,
+               .reg_shift = 3,
+               .reg_width = 5,
+               .val_pitch = 16,
+               .label = "ATI base",
+       },
+       {
+               .name = "azoteq,ati-mode",
+               .reg_grp = IQS7222_REG_GRP_CHAN,
+               .reg_offset = 1,
+               .reg_shift = 0,
+               .reg_width = 3,
+               .val_max = 5,
+               .label = "ATI mode",
+       },
+       {
+               .name = "azoteq,ati-frac-div-fine",
+               .reg_grp = IQS7222_REG_GRP_CHAN,
+               .reg_offset = 2,
+               .reg_shift = 9,
+               .reg_width = 5,
+               .label = "ATI fine fractional divider",
+       },
+       {
+               .name = "azoteq,ati-frac-mult-coarse",
+               .reg_grp = IQS7222_REG_GRP_CHAN,
+               .reg_offset = 2,
+               .reg_shift = 5,
+               .reg_width = 4,
+               .label = "ATI coarse fractional multiplier",
+       },
+       {
+               .name = "azoteq,ati-frac-div-coarse",
+               .reg_grp = IQS7222_REG_GRP_CHAN,
+               .reg_offset = 2,
+               .reg_shift = 0,
+               .reg_width = 5,
+               .label = "ATI coarse fractional divider",
+       },
+       {
+               .name = "azoteq,ati-comp-div",
+               .reg_grp = IQS7222_REG_GRP_CHAN,
+               .reg_offset = 3,
+               .reg_shift = 11,
+               .reg_width = 5,
+               .label = "ATI compensation divider",
+       },
+       {
+               .name = "azoteq,ati-comp-select",
+               .reg_grp = IQS7222_REG_GRP_CHAN,
+               .reg_offset = 3,
+               .reg_shift = 0,
+               .reg_width = 10,
+               .label = "ATI compensation selection",
+       },
+       {
+               .name = "azoteq,debounce-exit",
+               .reg_grp = IQS7222_REG_GRP_BTN,
+               .reg_key = IQS7222_REG_KEY_DEBOUNCE,
+               .reg_offset = 0,
+               .reg_shift = 12,
+               .reg_width = 4,
+               .label = "debounce exit factor",
+       },
+       {
+               .name = "azoteq,debounce-enter",
+               .reg_grp = IQS7222_REG_GRP_BTN,
+               .reg_key = IQS7222_REG_KEY_DEBOUNCE,
+               .reg_offset = 0,
+               .reg_shift = 8,
+               .reg_width = 4,
+               .label = "debounce entrance factor",
+       },
+       {
+               .name = "azoteq,thresh",
+               .reg_grp = IQS7222_REG_GRP_BTN,
+               .reg_key = IQS7222_REG_KEY_PROX,
+               .reg_offset = 0,
+               .reg_shift = 0,
+               .reg_width = 8,
+               .val_max = 127,
+               .label = "threshold",
+       },
+       {
+               .name = "azoteq,thresh",
+               .reg_grp = IQS7222_REG_GRP_BTN,
+               .reg_key = IQS7222_REG_KEY_TOUCH,
+               .reg_offset = 1,
+               .reg_shift = 0,
+               .reg_width = 8,
+               .label = "threshold",
+       },
+       {
+               .name = "azoteq,hyst",
+               .reg_grp = IQS7222_REG_GRP_BTN,
+               .reg_key = IQS7222_REG_KEY_TOUCH,
+               .reg_offset = 1,
+               .reg_shift = 8,
+               .reg_width = 8,
+               .label = "hysteresis",
+       },
+       {
+               .name = "azoteq,lta-beta-lp",
+               .reg_grp = IQS7222_REG_GRP_FILT,
+               .reg_offset = 0,
+               .reg_shift = 12,
+               .reg_width = 4,
+               .label = "low-power mode long-term average beta",
+       },
+       {
+               .name = "azoteq,lta-beta-np",
+               .reg_grp = IQS7222_REG_GRP_FILT,
+               .reg_offset = 0,
+               .reg_shift = 8,
+               .reg_width = 4,
+               .label = "normal-power mode long-term average beta",
+       },
+       {
+               .name = "azoteq,counts-beta-lp",
+               .reg_grp = IQS7222_REG_GRP_FILT,
+               .reg_offset = 0,
+               .reg_shift = 4,
+               .reg_width = 4,
+               .label = "low-power mode counts beta",
+       },
+       {
+               .name = "azoteq,counts-beta-np",
+               .reg_grp = IQS7222_REG_GRP_FILT,
+               .reg_offset = 0,
+               .reg_shift = 0,
+               .reg_width = 4,
+               .label = "normal-power mode counts beta",
+       },
+       {
+               .name = "azoteq,lta-fast-beta-lp",
+               .reg_grp = IQS7222_REG_GRP_FILT,
+               .reg_offset = 1,
+               .reg_shift = 4,
+               .reg_width = 4,
+               .label = "low-power mode long-term average fast beta",
+       },
+       {
+               .name = "azoteq,lta-fast-beta-np",
+               .reg_grp = IQS7222_REG_GRP_FILT,
+               .reg_offset = 1,
+               .reg_shift = 0,
+               .reg_width = 4,
+               .label = "normal-power mode long-term average fast beta",
+       },
+       {
+               .name = "azoteq,lower-cal",
+               .reg_grp = IQS7222_REG_GRP_SLDR,
+               .reg_offset = 0,
+               .reg_shift = 8,
+               .reg_width = 8,
+               .label = "lower calibration",
+       },
+       {
+               .name = "azoteq,static-beta",
+               .reg_grp = IQS7222_REG_GRP_SLDR,
+               .reg_key = IQS7222_REG_KEY_NO_WHEEL,
+               .reg_offset = 0,
+               .reg_shift = 6,
+               .reg_width = 1,
+       },
+       {
+               .name = "azoteq,bottom-beta",
+               .reg_grp = IQS7222_REG_GRP_SLDR,
+               .reg_key = IQS7222_REG_KEY_NO_WHEEL,
+               .reg_offset = 0,
+               .reg_shift = 3,
+               .reg_width = 3,
+               .label = "bottom beta",
+       },
+       {
+               .name = "azoteq,static-beta",
+               .reg_grp = IQS7222_REG_GRP_SLDR,
+               .reg_key = IQS7222_REG_KEY_WHEEL,
+               .reg_offset = 0,
+               .reg_shift = 7,
+               .reg_width = 1,
+       },
+       {
+               .name = "azoteq,bottom-beta",
+               .reg_grp = IQS7222_REG_GRP_SLDR,
+               .reg_key = IQS7222_REG_KEY_WHEEL,
+               .reg_offset = 0,
+               .reg_shift = 4,
+               .reg_width = 3,
+               .label = "bottom beta",
+       },
+       {
+               .name = "azoteq,bottom-speed",
+               .reg_grp = IQS7222_REG_GRP_SLDR,
+               .reg_offset = 1,
+               .reg_shift = 8,
+               .reg_width = 8,
+               .label = "bottom speed",
+       },
+       {
+               .name = "azoteq,upper-cal",
+               .reg_grp = IQS7222_REG_GRP_SLDR,
+               .reg_offset = 1,
+               .reg_shift = 0,
+               .reg_width = 8,
+               .label = "upper calibration",
+       },
+       {
+               .name = "azoteq,gesture-max-ms",
+               .reg_grp = IQS7222_REG_GRP_SLDR,
+               .reg_key = IQS7222_REG_KEY_TAP,
+               .reg_offset = 9,
+               .reg_shift = 8,
+               .reg_width = 8,
+               .val_pitch = 4,
+               .label = "maximum gesture time",
+       },
+       {
+               .name = "azoteq,gesture-min-ms",
+               .reg_grp = IQS7222_REG_GRP_SLDR,
+               .reg_key = IQS7222_REG_KEY_TAP,
+               .reg_offset = 9,
+               .reg_shift = 3,
+               .reg_width = 5,
+               .val_pitch = 4,
+               .label = "minimum gesture time",
+       },
+       {
+               .name = "azoteq,gesture-dist",
+               .reg_grp = IQS7222_REG_GRP_SLDR,
+               .reg_key = IQS7222_REG_KEY_AXIAL,
+               .reg_offset = 10,
+               .reg_shift = 8,
+               .reg_width = 8,
+               .val_pitch = 16,
+               .label = "gesture distance",
+       },
+       {
+               .name = "azoteq,gesture-max-ms",
+               .reg_grp = IQS7222_REG_GRP_SLDR,
+               .reg_key = IQS7222_REG_KEY_AXIAL,
+               .reg_offset = 10,
+               .reg_shift = 0,
+               .reg_width = 8,
+               .val_pitch = 4,
+               .label = "maximum gesture time",
+       },
+       {
+               .name = "drive-open-drain",
+               .reg_grp = IQS7222_REG_GRP_GPIO,
+               .reg_offset = 0,
+               .reg_shift = 1,
+               .reg_width = 1,
+       },
+       {
+               .name = "azoteq,timeout-ati-ms",
+               .reg_grp = IQS7222_REG_GRP_SYS,
+               .reg_offset = 1,
+               .reg_shift = 0,
+               .reg_width = 16,
+               .val_pitch = 500,
+               .label = "ATI error timeout",
+       },
+       {
+               .name = "azoteq,rate-ati-ms",
+               .reg_grp = IQS7222_REG_GRP_SYS,
+               .reg_offset = 2,
+               .reg_shift = 0,
+               .reg_width = 16,
+               .label = "ATI report rate",
+       },
+       {
+               .name = "azoteq,timeout-np-ms",
+               .reg_grp = IQS7222_REG_GRP_SYS,
+               .reg_offset = 3,
+               .reg_shift = 0,
+               .reg_width = 16,
+               .label = "normal-power mode timeout",
+       },
+       {
+               .name = "azoteq,rate-np-ms",
+               .reg_grp = IQS7222_REG_GRP_SYS,
+               .reg_offset = 4,
+               .reg_shift = 0,
+               .reg_width = 16,
+               .val_max = 3000,
+               .label = "normal-power mode report rate",
+       },
+       {
+               .name = "azoteq,timeout-lp-ms",
+               .reg_grp = IQS7222_REG_GRP_SYS,
+               .reg_offset = 5,
+               .reg_shift = 0,
+               .reg_width = 16,
+               .label = "low-power mode timeout",
+       },
+       {
+               .name = "azoteq,rate-lp-ms",
+               .reg_grp = IQS7222_REG_GRP_SYS,
+               .reg_offset = 6,
+               .reg_shift = 0,
+               .reg_width = 16,
+               .val_max = 3000,
+               .label = "low-power mode report rate",
+       },
+       {
+               .name = "azoteq,timeout-ulp-ms",
+               .reg_grp = IQS7222_REG_GRP_SYS,
+               .reg_offset = 7,
+               .reg_shift = 0,
+               .reg_width = 16,
+               .label = "ultra-low-power mode timeout",
+       },
+       {
+               .name = "azoteq,rate-ulp-ms",
+               .reg_grp = IQS7222_REG_GRP_SYS,
+               .reg_offset = 8,
+               .reg_shift = 0,
+               .reg_width = 16,
+               .val_max = 3000,
+               .label = "ultra-low-power mode report rate",
+       },
+};
+
+struct iqs7222_private {
+       const struct iqs7222_dev_desc *dev_desc;
+       struct gpio_desc *reset_gpio;
+       struct gpio_desc *irq_gpio;
+       struct i2c_client *client;
+       struct input_dev *keypad;
+       unsigned int kp_type[IQS7222_MAX_CHAN][ARRAY_SIZE(iqs7222_kp_events)];
+       unsigned int kp_code[IQS7222_MAX_CHAN][ARRAY_SIZE(iqs7222_kp_events)];
+       unsigned int sl_code[IQS7222_MAX_SLDR][ARRAY_SIZE(iqs7222_sl_events)];
+       unsigned int sl_axis[IQS7222_MAX_SLDR];
+       u16 cycle_setup[IQS7222_MAX_CHAN / 2][IQS7222_MAX_COLS_CYCLE];
+       u16 glbl_setup[IQS7222_MAX_COLS_GLBL];
+       u16 btn_setup[IQS7222_MAX_CHAN][IQS7222_MAX_COLS_BTN];
+       u16 chan_setup[IQS7222_MAX_CHAN][IQS7222_MAX_COLS_CHAN];
+       u16 filt_setup[IQS7222_MAX_COLS_FILT];
+       u16 sldr_setup[IQS7222_MAX_SLDR][IQS7222_MAX_COLS_SLDR];
+       u16 gpio_setup[ARRAY_SIZE(iqs7222_gpio_links)][IQS7222_MAX_COLS_GPIO];
+       u16 sys_setup[IQS7222_MAX_COLS_SYS];
+};
+
+static u16 *iqs7222_setup(struct iqs7222_private *iqs7222,
+                         enum iqs7222_reg_grp_id reg_grp, int row)
+{
+       switch (reg_grp) {
+       case IQS7222_REG_GRP_CYCLE:
+               return iqs7222->cycle_setup[row];
+
+       case IQS7222_REG_GRP_GLBL:
+               return iqs7222->glbl_setup;
+
+       case IQS7222_REG_GRP_BTN:
+               return iqs7222->btn_setup[row];
+
+       case IQS7222_REG_GRP_CHAN:
+               return iqs7222->chan_setup[row];
+
+       case IQS7222_REG_GRP_FILT:
+               return iqs7222->filt_setup;
+
+       case IQS7222_REG_GRP_SLDR:
+               return iqs7222->sldr_setup[row];
+
+       case IQS7222_REG_GRP_GPIO:
+               return iqs7222->gpio_setup[row];
+
+       case IQS7222_REG_GRP_SYS:
+               return iqs7222->sys_setup;
+
+       default:
+               return NULL;
+       }
+}
+
+static int iqs7222_irq_poll(struct iqs7222_private *iqs7222, u16 timeout_ms)
+{
+       ktime_t irq_timeout = ktime_add_ms(ktime_get(), timeout_ms);
+       int ret;
+
+       do {
+               usleep_range(1000, 1100);
+
+               ret = gpiod_get_value_cansleep(iqs7222->irq_gpio);
+               if (ret < 0)
+                       return ret;
+               else if (ret > 0)
+                       return 0;
+       } while (ktime_compare(ktime_get(), irq_timeout) < 0);
+
+       return -EBUSY;
+}
+
+static int iqs7222_hard_reset(struct iqs7222_private *iqs7222)
+{
+       struct i2c_client *client = iqs7222->client;
+       int error;
+
+       if (!iqs7222->reset_gpio)
+               return 0;
+
+       gpiod_set_value_cansleep(iqs7222->reset_gpio, 1);
+       usleep_range(1000, 1100);
+
+       gpiod_set_value_cansleep(iqs7222->reset_gpio, 0);
+
+       error = iqs7222_irq_poll(iqs7222, IQS7222_RESET_TIMEOUT_MS);
+       if (error)
+               dev_err(&client->dev, "Failed to reset device: %d\n", error);
+
+       return error;
+}
+
+static int iqs7222_force_comms(struct iqs7222_private *iqs7222)
+{
+       u8 msg_buf[] = { 0xFF, 0x00, };
+       int ret;
+
+       /*
+        * The device cannot communicate until it asserts its interrupt (RDY)
+        * pin. Attempts to do so while RDY is deasserted return an ACK; how-
+        * ever all write data is ignored, and all read data returns 0xEE.
+        *
+        * Unsolicited communication must be preceded by a special force com-
+        * munication command, after which the device eventually asserts its
+        * RDY pin and agrees to communicate.
+        *
+        * Regardless of whether communication is forced or the result of an
+        * interrupt, the device automatically deasserts its RDY pin once it
+        * detects an I2C stop condition, or a timeout expires.
+        */
+       ret = gpiod_get_value_cansleep(iqs7222->irq_gpio);
+       if (ret < 0)
+               return ret;
+       else if (ret > 0)
+               return 0;
+
+       ret = i2c_master_send(iqs7222->client, msg_buf, sizeof(msg_buf));
+       if (ret < (int)sizeof(msg_buf)) {
+               if (ret >= 0)
+                       ret = -EIO;
+
+               /*
+                * The datasheet states that the host must wait to retry any
+                * failed attempt to communicate over I2C.
+                */
+               msleep(IQS7222_COMMS_RETRY_MS);
+               return ret;
+       }
+
+       return iqs7222_irq_poll(iqs7222, IQS7222_COMMS_TIMEOUT_MS);
+}
+
+static int iqs7222_read_burst(struct iqs7222_private *iqs7222,
+                             u16 reg, void *val, u16 num_val)
+{
+       u8 reg_buf[sizeof(__be16)];
+       int ret, i;
+       struct i2c_client *client = iqs7222->client;
+       struct i2c_msg msg[] = {
+               {
+                       .addr = client->addr,
+                       .flags = 0,
+                       .len = reg > U8_MAX ? sizeof(reg) : sizeof(u8),
+                       .buf = reg_buf,
+               },
+               {
+                       .addr = client->addr,
+                       .flags = I2C_M_RD,
+                       .len = num_val * sizeof(__le16),
+                       .buf = (u8 *)val,
+               },
+       };
+
+       if (reg > U8_MAX)
+               put_unaligned_be16(reg, reg_buf);
+       else
+               *reg_buf = (u8)reg;
+
+       /*
+        * The following loop protects against an edge case in which the RDY
+        * pin is automatically deasserted just as the read is initiated. In
+        * that case, the read must be retried using forced communication.
+        */
+       for (i = 0; i < IQS7222_NUM_RETRIES; i++) {
+               ret = iqs7222_force_comms(iqs7222);
+               if (ret < 0)
+                       continue;
+
+               ret = i2c_transfer(client->adapter, msg, ARRAY_SIZE(msg));
+               if (ret < (int)ARRAY_SIZE(msg)) {
+                       if (ret >= 0)
+                               ret = -EIO;
+
+                       msleep(IQS7222_COMMS_RETRY_MS);
+                       continue;
+               }
+
+               if (get_unaligned_le16(msg[1].buf) == IQS7222_COMMS_ERROR) {
+                       ret = -ENODATA;
+                       continue;
+               }
+
+               ret = 0;
+               break;
+       }
+
+       /*
+        * The following delay ensures the device has deasserted the RDY pin
+        * following the I2C stop condition.
+        */
+       usleep_range(50, 100);
+
+       if (ret < 0)
+               dev_err(&client->dev,
+                       "Failed to read from address 0x%04X: %d\n", reg, ret);
+
+       return ret;
+}
+
+static int iqs7222_read_word(struct iqs7222_private *iqs7222, u16 reg, u16 *val)
+{
+       __le16 val_buf;
+       int error;
+
+       error = iqs7222_read_burst(iqs7222, reg, &val_buf, 1);
+       if (error)
+               return error;
+
+       *val = le16_to_cpu(val_buf);
+
+       return 0;
+}
+
+static int iqs7222_write_burst(struct iqs7222_private *iqs7222,
+                              u16 reg, const void *val, u16 num_val)
+{
+       int reg_len = reg > U8_MAX ? sizeof(reg) : sizeof(u8);
+       int val_len = num_val * sizeof(__le16);
+       int msg_len = reg_len + val_len;
+       int ret, i;
+       struct i2c_client *client = iqs7222->client;
+       u8 *msg_buf;
+
+       msg_buf = kzalloc(msg_len, GFP_KERNEL);
+       if (!msg_buf)
+               return -ENOMEM;
+
+       if (reg > U8_MAX)
+               put_unaligned_be16(reg, msg_buf);
+       else
+               *msg_buf = (u8)reg;
+
+       memcpy(msg_buf + reg_len, val, val_len);
+
+       /*
+        * The following loop protects against an edge case in which the RDY
+        * pin is automatically asserted just before the force communication
+        * command is sent.
+        *
+        * In that case, the subsequent I2C stop condition tricks the device
+        * into preemptively deasserting the RDY pin and the command must be
+        * sent again.
+        */
+       for (i = 0; i < IQS7222_NUM_RETRIES; i++) {
+               ret = iqs7222_force_comms(iqs7222);
+               if (ret < 0)
+                       continue;
+
+               ret = i2c_master_send(client, msg_buf, msg_len);
+               if (ret < msg_len) {
+                       if (ret >= 0)
+                               ret = -EIO;
+
+                       msleep(IQS7222_COMMS_RETRY_MS);
+                       continue;
+               }
+
+               ret = 0;
+               break;
+       }
+
+       kfree(msg_buf);
+
+       usleep_range(50, 100);
+
+       if (ret < 0)
+               dev_err(&client->dev,
+                       "Failed to write to address 0x%04X: %d\n", reg, ret);
+
+       return ret;
+}
+
+static int iqs7222_write_word(struct iqs7222_private *iqs7222, u16 reg, u16 val)
+{
+       __le16 val_buf = cpu_to_le16(val);
+
+       return iqs7222_write_burst(iqs7222, reg, &val_buf, 1);
+}
+
+static int iqs7222_ati_trigger(struct iqs7222_private *iqs7222)
+{
+       struct i2c_client *client = iqs7222->client;
+       ktime_t ati_timeout;
+       u16 sys_status = 0;
+       u16 sys_setup = iqs7222->sys_setup[0] & ~IQS7222_SYS_SETUP_ACK_RESET;
+       int error, i;
+
+       for (i = 0; i < IQS7222_NUM_RETRIES; i++) {
+               /*
+                * Trigger ATI from streaming and normal-power modes so that
+                * the RDY pin continues to be asserted during ATI.
+                */
+               error = iqs7222_write_word(iqs7222, IQS7222_SYS_SETUP,
+                                          sys_setup |
+                                          IQS7222_SYS_SETUP_REDO_ATI);
+               if (error)
+                       return error;
+
+               ati_timeout = ktime_add_ms(ktime_get(), IQS7222_ATI_TIMEOUT_MS);
+
+               do {
+                       error = iqs7222_irq_poll(iqs7222,
+                                                IQS7222_COMMS_TIMEOUT_MS);
+                       if (error)
+                               continue;
+
+                       error = iqs7222_read_word(iqs7222, IQS7222_SYS_STATUS,
+                                                 &sys_status);
+                       if (error)
+                               return error;
+
+                       if (sys_status & IQS7222_SYS_STATUS_ATI_ACTIVE)
+                               continue;
+
+                       if (sys_status & IQS7222_SYS_STATUS_ATI_ERROR)
+                               break;
+
+                       /*
+                        * Use stream-in-touch mode if either slider reports
+                        * absolute position.
+                        */
+                       sys_setup |= test_bit(EV_ABS, iqs7222->keypad->evbit)
+                                  ? IQS7222_SYS_SETUP_INTF_MODE_TOUCH
+                                  : IQS7222_SYS_SETUP_INTF_MODE_EVENT;
+                       sys_setup |= IQS7222_SYS_SETUP_PWR_MODE_AUTO;
+
+                       return iqs7222_write_word(iqs7222, IQS7222_SYS_SETUP,
+                                                 sys_setup);
+               } while (ktime_compare(ktime_get(), ati_timeout) < 0);
+
+               dev_err(&client->dev,
+                       "ATI attempt %d of %d failed with status 0x%02X, %s\n",
+                       i + 1, IQS7222_NUM_RETRIES, (u8)sys_status,
+                       i < IQS7222_NUM_RETRIES ? "retrying..." : "stopping");
+       }
+
+       return -ETIMEDOUT;
+}
+
+static int iqs7222_dev_init(struct iqs7222_private *iqs7222, int dir)
+{
+       const struct iqs7222_dev_desc *dev_desc = iqs7222->dev_desc;
+       int comms_offset = dev_desc->comms_offset;
+       int error, i, j, k;
+
+       /*
+        * Take advantage of the stop-bit disable function, if available, to
+        * save the trouble of having to reopen a communication window after
+        * each burst read or write.
+        */
+       if (comms_offset) {
+               u16 comms_setup;
+
+               error = iqs7222_read_word(iqs7222,
+                                         IQS7222_SYS_SETUP + comms_offset,
+                                         &comms_setup);
+               if (error)
+                       return error;
+
+               error = iqs7222_write_word(iqs7222,
+                                          IQS7222_SYS_SETUP + comms_offset,
+                                          comms_setup | IQS7222_COMMS_HOLD);
+               if (error)
+                       return error;
+       }
+
+       for (i = 0; i < IQS7222_NUM_REG_GRPS; i++) {
+               int num_row = dev_desc->reg_grps[i].num_row;
+               int num_col = dev_desc->reg_grps[i].num_col;
+               u16 reg = dev_desc->reg_grps[i].base;
+               __le16 *val_buf;
+               u16 *val;
+
+               if (!num_col)
+                       continue;
+
+               val = iqs7222_setup(iqs7222, i, 0);
+               if (!val)
+                       continue;
+
+               val_buf = kcalloc(num_col, sizeof(__le16), GFP_KERNEL);
+               if (!val_buf)
+                       return -ENOMEM;
+
+               for (j = 0; j < num_row; j++) {
+                       switch (dir) {
+                       case READ:
+                               error = iqs7222_read_burst(iqs7222, reg,
+                                                          val_buf, num_col);
+                               for (k = 0; k < num_col; k++)
+                                       val[k] = le16_to_cpu(val_buf[k]);
+                               break;
+
+                       case WRITE:
+                               for (k = 0; k < num_col; k++)
+                                       val_buf[k] = cpu_to_le16(val[k]);
+                               error = iqs7222_write_burst(iqs7222, reg,
+                                                           val_buf, num_col);
+                               break;
+
+                       default:
+                               error = -EINVAL;
+                       }
+
+                       if (error)
+                               break;
+
+                       reg += IQS7222_REG_OFFSET;
+                       val += iqs7222_max_cols[i];
+               }
+
+               kfree(val_buf);
+
+               if (error)
+                       return error;
+       }
+
+       if (comms_offset) {
+               u16 comms_setup;
+
+               error = iqs7222_read_word(iqs7222,
+                                         IQS7222_SYS_SETUP + comms_offset,
+                                         &comms_setup);
+               if (error)
+                       return error;
+
+               error = iqs7222_write_word(iqs7222,
+                                          IQS7222_SYS_SETUP + comms_offset,
+                                          comms_setup & ~IQS7222_COMMS_HOLD);
+               if (error)
+                       return error;
+       }
+
+       if (dir == READ)
+               return 0;
+
+       return iqs7222_ati_trigger(iqs7222);
+}
+
+static int iqs7222_dev_info(struct iqs7222_private *iqs7222)
+{
+       struct i2c_client *client = iqs7222->client;
+       bool prod_num_valid = false;
+       __le16 dev_id[3];
+       int error, i;
+
+       error = iqs7222_read_burst(iqs7222, IQS7222_PROD_NUM, dev_id,
+                                  ARRAY_SIZE(dev_id));
+       if (error)
+               return error;
+
+       for (i = 0; i < ARRAY_SIZE(iqs7222_devs); i++) {
+               if (le16_to_cpu(dev_id[0]) != iqs7222_devs[i].prod_num)
+                       continue;
+
+               prod_num_valid = true;
+
+               if (le16_to_cpu(dev_id[1]) < iqs7222_devs[i].fw_major)
+                       continue;
+
+               if (le16_to_cpu(dev_id[2]) < iqs7222_devs[i].fw_minor)
+                       continue;
+
+               iqs7222->dev_desc = &iqs7222_devs[i];
+               return 0;
+       }
+
+       if (prod_num_valid)
+               dev_err(&client->dev, "Unsupported firmware revision: %u.%u\n",
+                       le16_to_cpu(dev_id[1]), le16_to_cpu(dev_id[2]));
+       else
+               dev_err(&client->dev, "Unrecognized product number: %u\n",
+                       le16_to_cpu(dev_id[0]));
+
+       return -EINVAL;
+}
+
+static int iqs7222_gpio_select(struct iqs7222_private *iqs7222,
+                              struct fwnode_handle *child_node,
+                              int child_enable, u16 child_link)
+{
+       const struct iqs7222_dev_desc *dev_desc = iqs7222->dev_desc;
+       struct i2c_client *client = iqs7222->client;
+       int num_gpio = dev_desc->reg_grps[IQS7222_REG_GRP_GPIO].num_row;
+       int error, count, i;
+       unsigned int gpio_sel[ARRAY_SIZE(iqs7222_gpio_links)];
+
+       if (!num_gpio)
+               return 0;
+
+       if (!fwnode_property_present(child_node, "azoteq,gpio-select"))
+               return 0;
+
+       count = fwnode_property_count_u32(child_node, "azoteq,gpio-select");
+       if (count > num_gpio) {
+               dev_err(&client->dev, "Invalid number of %s GPIOs\n",
+                       fwnode_get_name(child_node));
+               return -EINVAL;
+       } else if (count < 0) {
+               dev_err(&client->dev, "Failed to count %s GPIOs: %d\n",
+                       fwnode_get_name(child_node), count);
+               return count;
+       }
+
+       error = fwnode_property_read_u32_array(child_node,
+                                              "azoteq,gpio-select",
+                                              gpio_sel, count);
+       if (error) {
+               dev_err(&client->dev, "Failed to read %s GPIOs: %d\n",
+                       fwnode_get_name(child_node), error);
+               return error;
+       }
+
+       for (i = 0; i < count; i++) {
+               u16 *gpio_setup;
+
+               if (gpio_sel[i] >= num_gpio) {
+                       dev_err(&client->dev, "Invalid %s GPIO: %u\n",
+                               fwnode_get_name(child_node), gpio_sel[i]);
+                       return -EINVAL;
+               }
+
+               gpio_setup = iqs7222->gpio_setup[gpio_sel[i]];
+
+               if (gpio_setup[2] && child_link != gpio_setup[2]) {
+                       dev_err(&client->dev,
+                               "Conflicting GPIO %u event types\n",
+                               gpio_sel[i]);
+                       return -EINVAL;
+               }
+
+               gpio_setup[0] |= IQS7222_GPIO_SETUP_0_GPIO_EN;
+               gpio_setup[1] |= child_enable;
+               gpio_setup[2] = child_link;
+       }
+
+       return 0;
+}
+
+static int iqs7222_parse_props(struct iqs7222_private *iqs7222,
+                              struct fwnode_handle **child_node,
+                              int child_index,
+                              enum iqs7222_reg_grp_id reg_grp,
+                              enum iqs7222_reg_key_id reg_key)
+{
+       u16 *setup = iqs7222_setup(iqs7222, reg_grp, child_index);
+       struct i2c_client *client = iqs7222->client;
+       struct fwnode_handle *reg_grp_node;
+       char reg_grp_name[16];
+       int i;
+
+       switch (reg_grp) {
+       case IQS7222_REG_GRP_CYCLE:
+       case IQS7222_REG_GRP_CHAN:
+       case IQS7222_REG_GRP_SLDR:
+       case IQS7222_REG_GRP_GPIO:
+       case IQS7222_REG_GRP_BTN:
+               /*
+                * These groups derive a child node and return it to the caller
+                * for additional group-specific processing. In some cases, the
+                * child node may have already been derived.
+                */
+               reg_grp_node = *child_node;
+               if (reg_grp_node)
+                       break;
+
+               snprintf(reg_grp_name, sizeof(reg_grp_name), "%s-%d",
+                        iqs7222_reg_grp_names[reg_grp], child_index);
+
+               reg_grp_node = device_get_named_child_node(&client->dev,
+                                                          reg_grp_name);
+               if (!reg_grp_node)
+                       return 0;
+
+               *child_node = reg_grp_node;
+               break;
+
+       case IQS7222_REG_GRP_GLBL:
+       case IQS7222_REG_GRP_FILT:
+       case IQS7222_REG_GRP_SYS:
+               /*
+                * These groups are not organized beneath a child node, nor are
+                * they subject to any additional processing by the caller.
+                */
+               reg_grp_node = dev_fwnode(&client->dev);
+               break;
+
+       default:
+               return -EINVAL;
+       }
+
+       for (i = 0; i < ARRAY_SIZE(iqs7222_props); i++) {
+               const char *name = iqs7222_props[i].name;
+               int reg_offset = iqs7222_props[i].reg_offset;
+               int reg_shift = iqs7222_props[i].reg_shift;
+               int reg_width = iqs7222_props[i].reg_width;
+               int val_pitch = iqs7222_props[i].val_pitch ? : 1;
+               int val_min = iqs7222_props[i].val_min;
+               int val_max = iqs7222_props[i].val_max;
+               bool invert = iqs7222_props[i].invert;
+               const char *label = iqs7222_props[i].label ? : name;
+               unsigned int val;
+               int error;
+
+               if (iqs7222_props[i].reg_grp != reg_grp ||
+                   iqs7222_props[i].reg_key != reg_key)
+                       continue;
+
+               /*
+                * Boolean register fields are one bit wide; they are forcibly
+                * reset to provide a means to undo changes by a bootloader if
+                * necessary.
+                *
+                * Scalar fields, on the other hand, are left untouched unless
+                * their corresponding properties are present.
+                */
+               if (reg_width == 1) {
+                       if (invert)
+                               setup[reg_offset] |= BIT(reg_shift);
+                       else
+                               setup[reg_offset] &= ~BIT(reg_shift);
+               }
+
+               if (!fwnode_property_present(reg_grp_node, name))
+                       continue;
+
+               if (reg_width == 1) {
+                       if (invert)
+                               setup[reg_offset] &= ~BIT(reg_shift);
+                       else
+                               setup[reg_offset] |= BIT(reg_shift);
+
+                       continue;
+               }
+
+               error = fwnode_property_read_u32(reg_grp_node, name, &val);
+               if (error) {
+                       dev_err(&client->dev, "Failed to read %s %s: %d\n",
+                               fwnode_get_name(reg_grp_node), label, error);
+                       return error;
+               }
+
+               if (!val_max)
+                       val_max = GENMASK(reg_width - 1, 0) * val_pitch;
+
+               if (val < val_min || val > val_max) {
+                       dev_err(&client->dev, "Invalid %s %s: %u\n",
+                               fwnode_get_name(reg_grp_node), label, val);
+                       return -EINVAL;
+               }
+
+               setup[reg_offset] &= ~GENMASK(reg_shift + reg_width - 1,
+                                             reg_shift);
+               setup[reg_offset] |= (val / val_pitch << reg_shift);
+       }
+
+       return 0;
+}
+
+static int iqs7222_parse_cycle(struct iqs7222_private *iqs7222, int cycle_index)
+{
+       u16 *cycle_setup = iqs7222->cycle_setup[cycle_index];
+       struct i2c_client *client = iqs7222->client;
+       struct fwnode_handle *cycle_node = NULL;
+       unsigned int pins[9];
+       int error, count, i;
+
+       /*
+        * Each channel shares a cycle with one other channel; the mapping of
+        * channels to cycles is fixed. Properties defined for a cycle impact
+        * both channels tied to the cycle.
+        */
+       error = iqs7222_parse_props(iqs7222, &cycle_node, cycle_index,
+                                   IQS7222_REG_GRP_CYCLE,
+                                   IQS7222_REG_KEY_NONE);
+       if (error)
+               return error;
+
+       if (!cycle_node)
+               return 0;
+
+       /*
+        * Unlike channels which are restricted to a select range of CRx pins
+        * based on channel number, any cycle can claim any of the device's 9
+        * CTx pins (CTx0-8).
+        */
+       if (!fwnode_property_present(cycle_node, "azoteq,tx-enable"))
+               return 0;
+
+       count = fwnode_property_count_u32(cycle_node, "azoteq,tx-enable");
+       if (count < 0) {
+               dev_err(&client->dev, "Failed to count %s CTx pins: %d\n",
+                       fwnode_get_name(cycle_node), count);
+               return count;
+       } else if (count > ARRAY_SIZE(pins)) {
+               dev_err(&client->dev, "Invalid number of %s CTx pins\n",
+                       fwnode_get_name(cycle_node));
+               return -EINVAL;
+       }
+
+       error = fwnode_property_read_u32_array(cycle_node, "azoteq,tx-enable",
+                                              pins, count);
+       if (error) {
+               dev_err(&client->dev, "Failed to read %s CTx pins: %d\n",
+                       fwnode_get_name(cycle_node), error);
+               return error;
+       }
+
+       cycle_setup[1] &= ~GENMASK(7 + ARRAY_SIZE(pins) - 1, 7);
+
+       for (i = 0; i < count; i++) {
+               if (pins[i] > 8) {
+                       dev_err(&client->dev, "Invalid %s CTx pin: %u\n",
+                               fwnode_get_name(cycle_node), pins[i]);
+                       return -EINVAL;
+               }
+
+               cycle_setup[1] |= BIT(pins[i] + 7);
+       }
+
+       return 0;
+}
+
+static int iqs7222_parse_chan(struct iqs7222_private *iqs7222, int chan_index)
+{
+       const struct iqs7222_dev_desc *dev_desc = iqs7222->dev_desc;
+       struct i2c_client *client = iqs7222->client;
+       struct fwnode_handle *chan_node = NULL;
+       int num_chan = dev_desc->reg_grps[IQS7222_REG_GRP_CHAN].num_row;
+       int ext_chan = rounddown(num_chan, 10);
+       int error, i;
+       u16 *chan_setup = iqs7222->chan_setup[chan_index];
+       u16 *sys_setup = iqs7222->sys_setup;
+       unsigned int val;
+
+       error = iqs7222_parse_props(iqs7222, &chan_node, chan_index,
+                                   IQS7222_REG_GRP_CHAN,
+                                   IQS7222_REG_KEY_NONE);
+       if (error)
+               return error;
+
+       if (!chan_node)
+               return 0;
+
+       if (dev_desc->allow_offset) {
+               sys_setup[dev_desc->allow_offset] |= BIT(chan_index);
+               if (fwnode_property_present(chan_node, "azoteq,ulp-allow"))
+                       sys_setup[dev_desc->allow_offset] &= ~BIT(chan_index);
+       }
+
+       chan_setup[0] |= IQS7222_CHAN_SETUP_0_CHAN_EN;
+
+       /*
+        * The reference channel function allows for differential measurements
+        * and is only available in the case of IQS7222A or IQS7222C.
+        */
+       if (dev_desc->reg_grps[IQS7222_REG_GRP_CHAN].num_col > 4 &&
+           fwnode_property_present(chan_node, "azoteq,ref-select")) {
+               u16 *ref_setup;
+
+               error = fwnode_property_read_u32(chan_node, "azoteq,ref-select",
+                                                &val);
+               if (error) {
+                       dev_err(&client->dev,
+                               "Failed to read %s reference channel: %d\n",
+                               fwnode_get_name(chan_node), error);
+                       return error;
+               }
+
+               if (val >= ext_chan) {
+                       dev_err(&client->dev,
+                               "Invalid %s reference channel: %u\n",
+                               fwnode_get_name(chan_node), val);
+                       return -EINVAL;
+               }
+
+               ref_setup = iqs7222->chan_setup[val];
+
+               /*
+                * Configure the current channel as a follower of the selected
+                * reference channel.
+                */
+               chan_setup[0] |= IQS7222_CHAN_SETUP_0_REF_MODE_FOLLOW;
+               chan_setup[4] = val * 42 + 1048;
+
+               if (!fwnode_property_read_u32(chan_node, "azoteq,ref-weight",
+                                             &val)) {
+                       if (val > U16_MAX) {
+                               dev_err(&client->dev,
+                                       "Invalid %s reference weight: %u\n",
+                                       fwnode_get_name(chan_node), val);
+                               return -EINVAL;
+                       }
+
+                       chan_setup[5] = val;
+               }
+
+               /*
+                * Configure the selected channel as a reference channel which
+                * serves the current channel.
+                */
+               ref_setup[0] |= IQS7222_CHAN_SETUP_0_REF_MODE_REF;
+               ref_setup[5] |= BIT(chan_index);
+
+               ref_setup[4] = dev_desc->touch_link;
+               if (fwnode_property_present(chan_node, "azoteq,use-prox"))
+                       ref_setup[4] -= 2;
+       }
+
+       if (fwnode_property_present(chan_node, "azoteq,rx-enable")) {
+               /*
+                * Each channel can claim up to 4 CRx pins. The first half of
+                * the channels can use CRx0-3, while the second half can use
+                * CRx4-7.
+                */
+               unsigned int pins[4];
+               int count;
+
+               count = fwnode_property_count_u32(chan_node,
+                                                 "azoteq,rx-enable");
+               if (count < 0) {
+                       dev_err(&client->dev,
+                               "Failed to count %s CRx pins: %d\n",
+                               fwnode_get_name(chan_node), count);
+                       return count;
+               } else if (count > ARRAY_SIZE(pins)) {
+                       dev_err(&client->dev,
+                               "Invalid number of %s CRx pins\n",
+                               fwnode_get_name(chan_node));
+                       return -EINVAL;
+               }
+
+               error = fwnode_property_read_u32_array(chan_node,
+                                                      "azoteq,rx-enable",
+                                                      pins, count);
+               if (error) {
+                       dev_err(&client->dev,
+                               "Failed to read %s CRx pins: %d\n",
+                               fwnode_get_name(chan_node), error);
+                       return error;
+               }
+
+               chan_setup[0] &= ~GENMASK(4 + ARRAY_SIZE(pins) - 1, 4);
+
+               for (i = 0; i < count; i++) {
+                       int min_crx = chan_index < ext_chan / 2 ? 0 : 4;
+
+                       if (pins[i] < min_crx || pins[i] > min_crx + 3) {
+                               dev_err(&client->dev,
+                                       "Invalid %s CRx pin: %u\n",
+                                       fwnode_get_name(chan_node), pins[i]);
+                               return -EINVAL;
+                       }
+
+                       chan_setup[0] |= BIT(pins[i] + 4 - min_crx);
+               }
+       }
+
+       for (i = 0; i < ARRAY_SIZE(iqs7222_kp_events); i++) {
+               const char *event_name = iqs7222_kp_events[i].name;
+               u16 event_enable = iqs7222_kp_events[i].enable;
+               struct fwnode_handle *event_node;
+
+               event_node = fwnode_get_named_child_node(chan_node, event_name);
+               if (!event_node)
+                       continue;
+
+               error = iqs7222_parse_props(iqs7222, &event_node, chan_index,
+                                           IQS7222_REG_GRP_BTN,
+                                           iqs7222_kp_events[i].reg_key);
+               if (error)
+                       return error;
+
+               error = iqs7222_gpio_select(iqs7222, event_node,
+                                           BIT(chan_index),
+                                           dev_desc->touch_link - (i ? 0 : 2));
+               if (error)
+                       return error;
+
+               if (!fwnode_property_read_u32(event_node,
+                                             "azoteq,timeout-press-ms",
+                                             &val)) {
+                       /*
+                        * The IQS7222B employs a global pair of press timeout
+                        * registers as opposed to channel-specific registers.
+                        */
+                       u16 *setup = dev_desc->reg_grps
+                                    [IQS7222_REG_GRP_BTN].num_col > 2 ?
+                                    &iqs7222->btn_setup[chan_index][2] :
+                                    &sys_setup[9];
+
+                       if (val > U8_MAX * 500) {
+                               dev_err(&client->dev,
+                                       "Invalid %s press timeout: %u\n",
+                                       fwnode_get_name(chan_node), val);
+                               return -EINVAL;
+                       }
+
+                       *setup &= ~(U8_MAX << i * 8);
+                       *setup |= (val / 500 << i * 8);
+               }
+
+               error = fwnode_property_read_u32(event_node, "linux,code",
+                                                &val);
+               if (error) {
+                       dev_err(&client->dev, "Failed to read %s code: %d\n",
+                               fwnode_get_name(chan_node), error);
+                       return error;
+               }
+
+               iqs7222->kp_code[chan_index][i] = val;
+               iqs7222->kp_type[chan_index][i] = EV_KEY;
+
+               if (fwnode_property_present(event_node, "linux,input-type")) {
+                       error = fwnode_property_read_u32(event_node,
+                                                        "linux,input-type",
+                                                        &val);
+                       if (error) {
+                               dev_err(&client->dev,
+                                       "Failed to read %s input type: %d\n",
+                                       fwnode_get_name(chan_node), error);
+                               return error;
+                       }
+
+                       if (val != EV_KEY && val != EV_SW) {
+                               dev_err(&client->dev,
+                                       "Invalid %s input type: %u\n",
+                                       fwnode_get_name(chan_node), val);
+                               return -EINVAL;
+                       }
+
+                       iqs7222->kp_type[chan_index][i] = val;
+               }
+
+               /*
+                * Reference channels can opt out of event reporting by using
+                * KEY_RESERVED in place of a true key or switch code.
+                */
+               if (iqs7222->kp_type[chan_index][i] == EV_KEY &&
+                   iqs7222->kp_code[chan_index][i] == KEY_RESERVED)
+                       continue;
+
+               input_set_capability(iqs7222->keypad,
+                                    iqs7222->kp_type[chan_index][i],
+                                    iqs7222->kp_code[chan_index][i]);
+
+               if (!dev_desc->event_offset)
+                       continue;
+
+               sys_setup[dev_desc->event_offset] |= event_enable;
+       }
+
+       /*
+        * The following call handles a special pair of properties that apply
+        * to a channel node, but reside within the button (event) group.
+        */
+       return iqs7222_parse_props(iqs7222, &chan_node, chan_index,
+                                  IQS7222_REG_GRP_BTN,
+                                  IQS7222_REG_KEY_DEBOUNCE);
+}
+
+static int iqs7222_parse_sldr(struct iqs7222_private *iqs7222, int sldr_index)
+{
+       const struct iqs7222_dev_desc *dev_desc = iqs7222->dev_desc;
+       struct i2c_client *client = iqs7222->client;
+       struct fwnode_handle *sldr_node = NULL;
+       int num_chan = dev_desc->reg_grps[IQS7222_REG_GRP_CHAN].num_row;
+       int ext_chan = rounddown(num_chan, 10);
+       int count, error, reg_offset, i;
+       u16 *sldr_setup = iqs7222->sldr_setup[sldr_index];
+       u16 *sys_setup = iqs7222->sys_setup;
+       unsigned int chan_sel[4], val;
+
+       error = iqs7222_parse_props(iqs7222, &sldr_node, sldr_index,
+                                   IQS7222_REG_GRP_SLDR,
+                                   IQS7222_REG_KEY_NONE);
+       if (error)
+               return error;
+
+       if (!sldr_node)
+               return 0;
+
+       /*
+        * Each slider can be spread across 3 to 4 channels. It is possible to
+        * select only 2 channels, but doing so prevents the slider from using
+        * the specified resolution.
+        */
+       count = fwnode_property_count_u32(sldr_node, "azoteq,channel-select");
+       if (count < 0) {
+               dev_err(&client->dev, "Failed to count %s channels: %d\n",
+                       fwnode_get_name(sldr_node), count);
+               return count;
+       } else if (count < 3 || count > ARRAY_SIZE(chan_sel)) {
+               dev_err(&client->dev, "Invalid number of %s channels\n",
+                       fwnode_get_name(sldr_node));
+               return -EINVAL;
+       }
+
+       error = fwnode_property_read_u32_array(sldr_node,
+                                              "azoteq,channel-select",
+                                              chan_sel, count);
+       if (error) {
+               dev_err(&client->dev, "Failed to read %s channels: %d\n",
+                       fwnode_get_name(sldr_node), error);
+               return error;
+       }
+
+       /*
+        * Resolution and top speed, if small enough, are packed into a single
+        * register. Otherwise, each occupies its own register and the rest of
+        * the slider-related register addresses are offset by one.
+        */
+       reg_offset = dev_desc->sldr_res < U16_MAX ? 0 : 1;
+
+       sldr_setup[0] |= count;
+       sldr_setup[3 + reg_offset] &= ~IQS7222_SLDR_SETUP_3_CHAN_SEL_MASK;
+
+       for (i = 0; i < ARRAY_SIZE(chan_sel); i++) {
+               sldr_setup[5 + reg_offset + i] = 0;
+               if (i >= count)
+                       continue;
+
+               if (chan_sel[i] >= ext_chan) {
+                       dev_err(&client->dev, "Invalid %s channel: %u\n",
+                               fwnode_get_name(sldr_node), chan_sel[i]);
+                       return -EINVAL;
+               }
+
+               /*
+                * The following fields indicate which channels participate in
+                * the slider, as well as each channel's relative placement.
+                */
+               sldr_setup[3 + reg_offset] |= BIT(chan_sel[i]);
+               sldr_setup[5 + reg_offset + i] = chan_sel[i] * 42 + 1080;
+       }
+
+       sldr_setup[4 + reg_offset] = dev_desc->touch_link;
+       if (fwnode_property_present(sldr_node, "azoteq,use-prox"))
+               sldr_setup[4 + reg_offset] -= 2;
+
+       if (!fwnode_property_read_u32(sldr_node, "azoteq,slider-size", &val)) {
+               if (!val || val > dev_desc->sldr_res) {
+                       dev_err(&client->dev, "Invalid %s size: %u\n",
+                               fwnode_get_name(sldr_node), val);
+                       return -EINVAL;
+               }
+
+               if (reg_offset) {
+                       sldr_setup[3] = val;
+               } else {
+                       sldr_setup[2] &= ~IQS7222_SLDR_SETUP_2_RES_MASK;
+                       sldr_setup[2] |= (val / 16 <<
+                                         IQS7222_SLDR_SETUP_2_RES_SHIFT);
+               }
+       }
+
+       if (!fwnode_property_read_u32(sldr_node, "azoteq,top-speed", &val)) {
+               if (val > (reg_offset ? U16_MAX : U8_MAX * 4)) {
+                       dev_err(&client->dev, "Invalid %s top speed: %u\n",
+                               fwnode_get_name(sldr_node), val);
+                       return -EINVAL;
+               }
+
+               if (reg_offset) {
+                       sldr_setup[2] = val;
+               } else {
+                       sldr_setup[2] &= ~IQS7222_SLDR_SETUP_2_TOP_SPEED_MASK;
+                       sldr_setup[2] |= (val / 4);
+               }
+       }
+
+       if (!fwnode_property_read_u32(sldr_node, "linux,axis", &val)) {
+               u16 sldr_max = sldr_setup[3] - 1;
+
+               if (!reg_offset) {
+                       sldr_max = sldr_setup[2];
+
+                       sldr_max &= IQS7222_SLDR_SETUP_2_RES_MASK;
+                       sldr_max >>= IQS7222_SLDR_SETUP_2_RES_SHIFT;
+
+                       sldr_max = sldr_max * 16 - 1;
+               }
+
+               input_set_abs_params(iqs7222->keypad, val, 0, sldr_max, 0, 0);
+               iqs7222->sl_axis[sldr_index] = val;
+       }
+
+       if (dev_desc->wheel_enable) {
+               sldr_setup[0] &= ~dev_desc->wheel_enable;
+               if (iqs7222->sl_axis[sldr_index] == ABS_WHEEL)
+                       sldr_setup[0] |= dev_desc->wheel_enable;
+       }
+
+       for (i = 0; i < ARRAY_SIZE(iqs7222_sl_events); i++) {
+               const char *event_name = iqs7222_sl_events[i].name;
+               struct fwnode_handle *event_node;
+
+               /*
+                * The absence of a register offset means the remaining fields
+                * in the group represent gesture settings.
+                */
+               if (iqs7222_sl_events[i].enable && !reg_offset)
+                       sldr_setup[9] &= ~iqs7222_sl_events[i].enable;
+
+               event_node = fwnode_get_named_child_node(sldr_node, event_name);
+               if (!event_node)
+                       continue;
+
+               error = iqs7222_parse_props(iqs7222, &event_node, sldr_index,
+                                           IQS7222_REG_GRP_SLDR,
+                                           reg_offset ?
+                                           IQS7222_REG_KEY_RESERVED :
+                                           iqs7222_sl_events[i].reg_key);
+               if (error)
+                       return error;
+
+               error = fwnode_property_read_u32(event_node, "linux,code",
+                                                &val);
+               if (error) {
+                       dev_err(&client->dev, "Failed to read %s code: %d\n",
+                               fwnode_get_name(sldr_node), error);
+                       return error;
+               }
+
+               iqs7222->sl_code[sldr_index][i] = val;
+               input_set_capability(iqs7222->keypad, EV_KEY, val);
+
+               /*
+                * The press/release event is determined based on whether the
+                * coordinate field reports 0xFFFF and has no explicit enable
+                * control.
+                */
+               if (!iqs7222_sl_events[i].enable || reg_offset)
+                       continue;
+
+               sldr_setup[9] |= iqs7222_sl_events[i].enable;
+
+               error = iqs7222_gpio_select(iqs7222, event_node,
+                                           iqs7222_sl_events[i].enable,
+                                           1568 + sldr_index * 30);
+               if (error)
+                       return error;
+
+               if (!dev_desc->event_offset)
+                       continue;
+
+               sys_setup[dev_desc->event_offset] |= BIT(10 + sldr_index);
+       }
+
+       /*
+        * The following call handles a special pair of properties that shift
+        * to make room for a wheel enable control in the case of IQS7222C.
+        */
+       return iqs7222_parse_props(iqs7222, &sldr_node, sldr_index,
+                                  IQS7222_REG_GRP_SLDR,
+                                  dev_desc->wheel_enable ?
+                                  IQS7222_REG_KEY_WHEEL :
+                                  IQS7222_REG_KEY_NO_WHEEL);
+}
+
+static int iqs7222_parse_all(struct iqs7222_private *iqs7222)
+{
+       const struct iqs7222_dev_desc *dev_desc = iqs7222->dev_desc;
+       const struct iqs7222_reg_grp_desc *reg_grps = dev_desc->reg_grps;
+       u16 *sys_setup = iqs7222->sys_setup;
+       int error, i;
+
+       if (dev_desc->event_offset)
+               sys_setup[dev_desc->event_offset] = IQS7222_EVENT_MASK_ATI;
+
+       for (i = 0; i < reg_grps[IQS7222_REG_GRP_CYCLE].num_row; i++) {
+               error = iqs7222_parse_cycle(iqs7222, i);
+               if (error)
+                       return error;
+       }
+
+       error = iqs7222_parse_props(iqs7222, NULL, 0, IQS7222_REG_GRP_GLBL,
+                                   IQS7222_REG_KEY_NONE);
+       if (error)
+               return error;
+
+       for (i = 0; i < reg_grps[IQS7222_REG_GRP_GPIO].num_row; i++) {
+               struct fwnode_handle *gpio_node = NULL;
+               u16 *gpio_setup = iqs7222->gpio_setup[i];
+               int j;
+
+               gpio_setup[0] &= ~IQS7222_GPIO_SETUP_0_GPIO_EN;
+               gpio_setup[1] = 0;
+               gpio_setup[2] = 0;
+
+               error = iqs7222_parse_props(iqs7222, &gpio_node, i,
+                                           IQS7222_REG_GRP_GPIO,
+                                           IQS7222_REG_KEY_NONE);
+               if (error)
+                       return error;
+
+               if (reg_grps[IQS7222_REG_GRP_GPIO].num_row == 1)
+                       continue;
+
+               /*
+                * The IQS7222C exposes multiple GPIO and must be informed
+                * as to which GPIO this group represents.
+                */
+               for (j = 0; j < ARRAY_SIZE(iqs7222_gpio_links); j++)
+                       gpio_setup[0] &= ~BIT(iqs7222_gpio_links[j]);
+
+               gpio_setup[0] |= BIT(iqs7222_gpio_links[i]);
+       }
+
+       for (i = 0; i < reg_grps[IQS7222_REG_GRP_CHAN].num_row; i++) {
+               u16 *chan_setup = iqs7222->chan_setup[i];
+
+               chan_setup[0] &= ~IQS7222_CHAN_SETUP_0_REF_MODE_MASK;
+               chan_setup[0] &= ~IQS7222_CHAN_SETUP_0_CHAN_EN;
+
+               chan_setup[5] = 0;
+       }
+
+       for (i = 0; i < reg_grps[IQS7222_REG_GRP_CHAN].num_row; i++) {
+               error = iqs7222_parse_chan(iqs7222, i);
+               if (error)
+                       return error;
+       }
+
+       error = iqs7222_parse_props(iqs7222, NULL, 0, IQS7222_REG_GRP_FILT,
+                                   IQS7222_REG_KEY_NONE);
+       if (error)
+               return error;
+
+       for (i = 0; i < reg_grps[IQS7222_REG_GRP_SLDR].num_row; i++) {
+               u16 *sldr_setup = iqs7222->sldr_setup[i];
+
+               sldr_setup[0] &= ~IQS7222_SLDR_SETUP_0_CHAN_CNT_MASK;
+
+               error = iqs7222_parse_sldr(iqs7222, i);
+               if (error)
+                       return error;
+       }
+
+       sys_setup[0] &= ~IQS7222_SYS_SETUP_INTF_MODE_MASK;
+       sys_setup[0] &= ~IQS7222_SYS_SETUP_PWR_MODE_MASK;
+
+       sys_setup[0] |= IQS7222_SYS_SETUP_ACK_RESET;
+
+       return iqs7222_parse_props(iqs7222, NULL, 0, IQS7222_REG_GRP_SYS,
+                                  IQS7222_REG_KEY_NONE);
+}
+
+static int iqs7222_report(struct iqs7222_private *iqs7222)
+{
+       const struct iqs7222_dev_desc *dev_desc = iqs7222->dev_desc;
+       struct i2c_client *client = iqs7222->client;
+       int num_chan = dev_desc->reg_grps[IQS7222_REG_GRP_CHAN].num_row;
+       int num_stat = dev_desc->reg_grps[IQS7222_REG_GRP_STAT].num_col;
+       int error, i, j;
+       __le16 status[IQS7222_MAX_COLS_STAT];
+
+       error = iqs7222_read_burst(iqs7222, IQS7222_SYS_STATUS, status,
+                                  num_stat);
+       if (error)
+               return error;
+
+       if (le16_to_cpu(status[0]) & IQS7222_SYS_STATUS_RESET) {
+               dev_err(&client->dev, "Unexpected device reset\n");
+               return iqs7222_dev_init(iqs7222, WRITE);
+       }
+
+       if (le16_to_cpu(status[0]) & IQS7222_SYS_STATUS_ATI_ERROR) {
+               dev_err(&client->dev, "Unexpected ATI error\n");
+               return iqs7222_ati_trigger(iqs7222);
+       }
+
+       if (le16_to_cpu(status[0]) & IQS7222_SYS_STATUS_ATI_ACTIVE)
+               return 0;
+
+       for (i = 0; i < num_chan; i++) {
+               u16 *chan_setup = iqs7222->chan_setup[i];
+
+               if (!(chan_setup[0] & IQS7222_CHAN_SETUP_0_CHAN_EN))
+                       continue;
+
+               for (j = 0; j < ARRAY_SIZE(iqs7222_kp_events); j++) {
+                       /*
+                        * Proximity state begins at offset 2 and spills into
+                        * offset 3 for devices with more than 16 channels.
+                        *
+                        * Touch state begins at the first offset immediately
+                        * following proximity state.
+                        */
+                       int k = 2 + j * (num_chan > 16 ? 2 : 1);
+                       u16 state = le16_to_cpu(status[k + i / 16]);
+
+                       input_event(iqs7222->keypad,
+                                   iqs7222->kp_type[i][j],
+                                   iqs7222->kp_code[i][j],
+                                   !!(state & BIT(i % 16)));
+               }
+       }
+
+       for (i = 0; i < dev_desc->reg_grps[IQS7222_REG_GRP_SLDR].num_row; i++) {
+               u16 *sldr_setup = iqs7222->sldr_setup[i];
+               u16 sldr_pos = le16_to_cpu(status[4 + i]);
+               u16 state = le16_to_cpu(status[6 + i]);
+
+               if (!(sldr_setup[0] & IQS7222_SLDR_SETUP_0_CHAN_CNT_MASK))
+                       continue;
+
+               if (sldr_pos < dev_desc->sldr_res)
+                       input_report_abs(iqs7222->keypad, iqs7222->sl_axis[i],
+                                        sldr_pos);
+
+               for (j = 0; j < ARRAY_SIZE(iqs7222_sl_events); j++) {
+                       u16 mask = iqs7222_sl_events[j].mask;
+                       u16 val = iqs7222_sl_events[j].val;
+
+                       if (!iqs7222_sl_events[j].enable) {
+                               input_report_key(iqs7222->keypad,
+                                                iqs7222->sl_code[i][j],
+                                                sldr_pos < dev_desc->sldr_res);
+                               continue;
+                       }
+
+                       /*
+                        * The remaining offsets represent gesture state, and
+                        * are discarded in the case of IQS7222C because only
+                        * absolute position is reported.
+                        */
+                       if (num_stat < IQS7222_MAX_COLS_STAT)
+                               continue;
+
+                       input_report_key(iqs7222->keypad,
+                                        iqs7222->sl_code[i][j],
+                                        (state & mask) == val);
+               }
+       }
+
+       input_sync(iqs7222->keypad);
+
+       return 0;
+}
+
+static irqreturn_t iqs7222_irq(int irq, void *context)
+{
+       struct iqs7222_private *iqs7222 = context;
+
+       return iqs7222_report(iqs7222) ? IRQ_NONE : IRQ_HANDLED;
+}
+
+static int iqs7222_probe(struct i2c_client *client)
+{
+       struct iqs7222_private *iqs7222;
+       unsigned long irq_flags;
+       int error, irq;
+
+       iqs7222 = devm_kzalloc(&client->dev, sizeof(*iqs7222), GFP_KERNEL);
+       if (!iqs7222)
+               return -ENOMEM;
+
+       i2c_set_clientdata(client, iqs7222);
+       iqs7222->client = client;
+
+       iqs7222->keypad = devm_input_allocate_device(&client->dev);
+       if (!iqs7222->keypad)
+               return -ENOMEM;
+
+       iqs7222->keypad->name = client->name;
+       iqs7222->keypad->id.bustype = BUS_I2C;
+
+       /*
+        * The RDY pin behaves as an interrupt, but must also be polled ahead
+        * of unsolicited I2C communication. As such, it is first opened as a
+        * GPIO and then passed to gpiod_to_irq() to register the interrupt.
+        */
+       iqs7222->irq_gpio = devm_gpiod_get(&client->dev, "irq", GPIOD_IN);
+       if (IS_ERR(iqs7222->irq_gpio)) {
+               error = PTR_ERR(iqs7222->irq_gpio);
+               dev_err(&client->dev, "Failed to request IRQ GPIO: %d\n",
+                       error);
+               return error;
+       }
+
+       iqs7222->reset_gpio = devm_gpiod_get_optional(&client->dev, "reset",
+                                                     GPIOD_OUT_HIGH);
+       if (IS_ERR(iqs7222->reset_gpio)) {
+               error = PTR_ERR(iqs7222->reset_gpio);
+               dev_err(&client->dev, "Failed to request reset GPIO: %d\n",
+                       error);
+               return error;
+       }
+
+       error = iqs7222_hard_reset(iqs7222);
+       if (error)
+               return error;
+
+       error = iqs7222_dev_info(iqs7222);
+       if (error)
+               return error;
+
+       error = iqs7222_dev_init(iqs7222, READ);
+       if (error)
+               return error;
+
+       error = iqs7222_parse_all(iqs7222);
+       if (error)
+               return error;
+
+       error = iqs7222_dev_init(iqs7222, WRITE);
+       if (error)
+               return error;
+
+       error = iqs7222_report(iqs7222);
+       if (error)
+               return error;
+
+       error = input_register_device(iqs7222->keypad);
+       if (error) {
+               dev_err(&client->dev, "Failed to register device: %d\n", error);
+               return error;
+       }
+
+       irq = gpiod_to_irq(iqs7222->irq_gpio);
+       if (irq < 0)
+               return irq;
+
+       irq_flags = gpiod_is_active_low(iqs7222->irq_gpio) ? IRQF_TRIGGER_LOW
+                                                          : IRQF_TRIGGER_HIGH;
+       irq_flags |= IRQF_ONESHOT;
+
+       error = devm_request_threaded_irq(&client->dev, irq, NULL, iqs7222_irq,
+                                         irq_flags, client->name, iqs7222);
+       if (error)
+               dev_err(&client->dev, "Failed to request IRQ: %d\n", error);
+
+       return error;
+}
+
+static const struct of_device_id iqs7222_of_match[] = {
+       { .compatible = "azoteq,iqs7222a" },
+       { .compatible = "azoteq,iqs7222b" },
+       { .compatible = "azoteq,iqs7222c" },
+       { }
+};
+MODULE_DEVICE_TABLE(of, iqs7222_of_match);
+
+static struct i2c_driver iqs7222_i2c_driver = {
+       .driver = {
+               .name = "iqs7222",
+               .of_match_table = iqs7222_of_match,
+       },
+       .probe_new = iqs7222_probe,
+};
+module_i2c_driver(iqs7222_i2c_driver);
+
+MODULE_AUTHOR("Jeff LaBundy <jeff@labundy.com>");
+MODULE_DESCRIPTION("Azoteq IQS7222A/B/C Capacitive Touch Controller");
+MODULE_LICENSE("GPL");
index 89af52498c96960453138f4a11bc070095a8657a..549df01b6ee33eb29b3ecf9fd22c351948745ce0 100644 (file)
@@ -9,9 +9,11 @@
 #include <linux/input.h>
 #include <linux/interrupt.h>
 #include <linux/kernel.h>
+#include <linux/ktime.h>
 #include <linux/log2.h>
 #include <linux/module.h>
 #include <linux/of.h>
+#include <linux/of_address.h>
 #include <linux/of_device.h>
 #include <linux/platform_device.h>
 #include <linux/reboot.h>
 
 #define PON_REV2                       0x01
 
+#define PON_SUBTYPE                    0x05
+
+#define PON_SUBTYPE_PRIMARY            0x01
+#define PON_SUBTYPE_SECONDARY          0x02
+#define PON_SUBTYPE_1REG               0x03
+#define PON_SUBTYPE_GEN2_PRIMARY       0x04
+#define PON_SUBTYPE_GEN2_SECONDARY     0x05
+#define PON_SUBTYPE_GEN3_PBS           0x08
+#define PON_SUBTYPE_GEN3_HLOS          0x09
+
 #define PON_RT_STS                     0x10
 #define  PON_KPDPWR_N_SET              BIT(0)
 #define  PON_RESIN_N_SET               BIT(1)
@@ -45,6 +57,7 @@ struct pm8941_data {
        unsigned int    status_bit;
        bool            supports_ps_hold_poff_config;
        bool            supports_debounce_config;
+       bool            has_pon_pbs;
        const char      *name;
        const char      *phys;
 };
@@ -53,13 +66,18 @@ struct pm8941_pwrkey {
        struct device *dev;
        int irq;
        u32 baseaddr;
+       u32 pon_pbs_baseaddr;
        struct regmap *regmap;
        struct input_dev *input;
 
        unsigned int revision;
+       unsigned int subtype;
        struct notifier_block reboot_notifier;
 
        u32 code;
+       u32 sw_debounce_time_us;
+       ktime_t sw_debounce_end_time;
+       bool last_status;
        const struct pm8941_data *data;
 };
 
@@ -129,20 +147,76 @@ static irqreturn_t pm8941_pwrkey_irq(int irq, void *_data)
 {
        struct pm8941_pwrkey *pwrkey = _data;
        unsigned int sts;
-       int error;
+       int err;
+
+       if (pwrkey->sw_debounce_time_us) {
+               if (ktime_before(ktime_get(), pwrkey->sw_debounce_end_time)) {
+                       dev_dbg(pwrkey->dev,
+                               "ignoring key event received before debounce end %llu us\n",
+                               pwrkey->sw_debounce_end_time);
+                       return IRQ_HANDLED;
+               }
+       }
 
-       error = regmap_read(pwrkey->regmap,
-                           pwrkey->baseaddr + PON_RT_STS, &sts);
-       if (error)
+       err = regmap_read(pwrkey->regmap, pwrkey->baseaddr + PON_RT_STS, &sts);
+       if (err)
                return IRQ_HANDLED;
 
-       input_report_key(pwrkey->input, pwrkey->code,
-                        sts & pwrkey->data->status_bit);
+       sts &= pwrkey->data->status_bit;
+
+       if (pwrkey->sw_debounce_time_us && !sts)
+               pwrkey->sw_debounce_end_time = ktime_add_us(ktime_get(),
+                                               pwrkey->sw_debounce_time_us);
+
+       /*
+        * Simulate a press event in case a release event occurred without a
+        * corresponding press event.
+        */
+       if (!pwrkey->last_status && !sts) {
+               input_report_key(pwrkey->input, pwrkey->code, 1);
+               input_sync(pwrkey->input);
+       }
+       pwrkey->last_status = sts;
+
+       input_report_key(pwrkey->input, pwrkey->code, sts);
        input_sync(pwrkey->input);
 
        return IRQ_HANDLED;
 }
 
+static int pm8941_pwrkey_sw_debounce_init(struct pm8941_pwrkey *pwrkey)
+{
+       unsigned int val, addr, mask;
+       int error;
+
+       if (pwrkey->data->has_pon_pbs && !pwrkey->pon_pbs_baseaddr) {
+               dev_err(pwrkey->dev,
+                       "PON_PBS address missing, can't read HW debounce time\n");
+               return 0;
+       }
+
+       if (pwrkey->pon_pbs_baseaddr)
+               addr = pwrkey->pon_pbs_baseaddr + PON_DBC_CTL;
+       else
+               addr = pwrkey->baseaddr + PON_DBC_CTL;
+       error = regmap_read(pwrkey->regmap, addr, &val);
+       if (error)
+               return error;
+
+       if (pwrkey->subtype >= PON_SUBTYPE_GEN2_PRIMARY)
+               mask = 0xf;
+       else
+               mask = 0x7;
+
+       pwrkey->sw_debounce_time_us =
+               2 * USEC_PER_SEC / (1 << (mask - (val & mask)));
+
+       dev_dbg(pwrkey->dev, "SW debounce time = %u us\n",
+               pwrkey->sw_debounce_time_us);
+
+       return 0;
+}
+
 static int __maybe_unused pm8941_pwrkey_suspend(struct device *dev)
 {
        struct pm8941_pwrkey *pwrkey = dev_get_drvdata(dev);
@@ -171,6 +245,8 @@ static int pm8941_pwrkey_probe(struct platform_device *pdev)
        struct pm8941_pwrkey *pwrkey;
        bool pull_up;
        struct device *parent;
+       struct device_node *regmap_node;
+       const __be32 *addr;
        u32 req_delay;
        int error;
 
@@ -192,8 +268,10 @@ static int pm8941_pwrkey_probe(struct platform_device *pdev)
        pwrkey->data = of_device_get_match_data(&pdev->dev);
 
        parent = pdev->dev.parent;
+       regmap_node = pdev->dev.of_node;
        pwrkey->regmap = dev_get_regmap(parent, NULL);
        if (!pwrkey->regmap) {
+               regmap_node = parent->of_node;
                /*
                 * We failed to get regmap for parent. Let's see if we are
                 * a child of pon node and read regmap and reg from its
@@ -204,15 +282,21 @@ static int pm8941_pwrkey_probe(struct platform_device *pdev)
                        dev_err(&pdev->dev, "failed to locate regmap\n");
                        return -ENODEV;
                }
+       }
 
-               error = of_property_read_u32(parent->of_node,
-                                            "reg", &pwrkey->baseaddr);
-       } else {
-               error = of_property_read_u32(pdev->dev.of_node, "reg",
-                                            &pwrkey->baseaddr);
+       addr = of_get_address(regmap_node, 0, NULL, NULL);
+       if (!addr) {
+               dev_err(&pdev->dev, "reg property missing\n");
+               return -EINVAL;
+       }
+       pwrkey->baseaddr = be32_to_cpup(addr);
+
+       if (pwrkey->data->has_pon_pbs) {
+               /* PON_PBS base address is optional */
+               addr = of_get_address(regmap_node, 1, NULL, NULL);
+               if (addr)
+                       pwrkey->pon_pbs_baseaddr = be32_to_cpup(addr);
        }
-       if (error)
-               return error;
 
        pwrkey->irq = platform_get_irq(pdev, 0);
        if (pwrkey->irq < 0)
@@ -221,7 +305,14 @@ static int pm8941_pwrkey_probe(struct platform_device *pdev)
        error = regmap_read(pwrkey->regmap, pwrkey->baseaddr + PON_REV2,
                            &pwrkey->revision);
        if (error) {
-               dev_err(&pdev->dev, "failed to set debounce: %d\n", error);
+               dev_err(&pdev->dev, "failed to read revision: %d\n", error);
+               return error;
+       }
+
+       error = regmap_read(pwrkey->regmap, pwrkey->baseaddr + PON_SUBTYPE,
+                           &pwrkey->subtype);
+       if (error) {
+               dev_err(&pdev->dev, "failed to read subtype: %d\n", error);
                return error;
        }
 
@@ -259,6 +350,10 @@ static int pm8941_pwrkey_probe(struct platform_device *pdev)
                }
        }
 
+       error = pm8941_pwrkey_sw_debounce_init(pwrkey);
+       if (error)
+               return error;
+
        if (pwrkey->data->pull_up_bit) {
                error = regmap_update_bits(pwrkey->regmap,
                                           pwrkey->baseaddr + PON_PULL_CTL,
@@ -320,6 +415,7 @@ static const struct pm8941_data pwrkey_data = {
        .phys = "pm8941_pwrkey/input0",
        .supports_ps_hold_poff_config = true,
        .supports_debounce_config = true,
+       .has_pon_pbs = false,
 };
 
 static const struct pm8941_data resin_data = {
@@ -329,6 +425,7 @@ static const struct pm8941_data resin_data = {
        .phys = "pm8941_resin/input0",
        .supports_ps_hold_poff_config = true,
        .supports_debounce_config = true,
+       .has_pon_pbs = false,
 };
 
 static const struct pm8941_data pon_gen3_pwrkey_data = {
@@ -337,6 +434,7 @@ static const struct pm8941_data pon_gen3_pwrkey_data = {
        .phys = "pmic_pwrkey/input0",
        .supports_ps_hold_poff_config = false,
        .supports_debounce_config = false,
+       .has_pon_pbs = true,
 };
 
 static const struct pm8941_data pon_gen3_resin_data = {
@@ -345,6 +443,7 @@ static const struct pm8941_data pon_gen3_resin_data = {
        .phys = "pmic_resin/input0",
        .supports_ps_hold_poff_config = false,
        .supports_debounce_config = false,
+       .has_pon_pbs = true,
 };
 
 static const struct of_device_id pm8941_pwr_key_id_table[] = {
index fe43e5557ed72b1b8b942b7a3b78d8807a700980..cdcb7737c46aa50a91d5205d8728f13f9d8215a1 100644 (file)
@@ -205,6 +205,7 @@ static int bbc_beep_probe(struct platform_device *op)
 
        info = &state->u.bbc;
        info->clock_freq = of_getintprop_default(dp, "clock-frequency", 0);
+       of_node_put(dp);
        if (!info->clock_freq)
                goto out_free;
 
index 164f6c757f6bed637b9e04c0ce8677888b93c33a..2a2459b1b4f2cb91b0465f77637f22a0b71bcf62 100644 (file)
@@ -26,6 +26,8 @@ struct psmouse_smbus_dev {
 static LIST_HEAD(psmouse_smbus_list);
 static DEFINE_MUTEX(psmouse_smbus_mutex);
 
+static struct workqueue_struct *psmouse_smbus_wq;
+
 static void psmouse_smbus_check_adapter(struct i2c_adapter *adapter)
 {
        struct psmouse_smbus_dev *smbdev;
@@ -161,7 +163,7 @@ static void psmouse_smbus_schedule_remove(struct i2c_client *client)
                INIT_WORK(&rwork->work, psmouse_smbus_remove_i2c_device);
                rwork->client = client;
 
-               schedule_work(&rwork->work);
+               queue_work(psmouse_smbus_wq, &rwork->work);
        }
 }
 
@@ -305,9 +307,14 @@ int __init psmouse_smbus_module_init(void)
 {
        int error;
 
+       psmouse_smbus_wq = alloc_workqueue("psmouse-smbus", 0, 0);
+       if (!psmouse_smbus_wq)
+               return -ENOMEM;
+
        error = bus_register_notifier(&i2c_bus_type, &psmouse_smbus_notifier);
        if (error) {
                pr_err("failed to register i2c bus notifier: %d\n", error);
+               destroy_workqueue(psmouse_smbus_wq);
                return error;
        }
 
@@ -317,5 +324,5 @@ int __init psmouse_smbus_module_init(void)
 void psmouse_smbus_module_exit(void)
 {
        bus_unregister_notifier(&i2c_bus_type, &psmouse_smbus_notifier);
-       flush_scheduled_work();
+       destroy_workqueue(psmouse_smbus_wq);
 }
index ffad142801b395fda9a850685d9fdb96b5629cd4..434d48ae4b12e8a0fc27658d26ff55e18812497f 100644 (file)
@@ -186,6 +186,7 @@ static const char * const smbus_pnp_ids[] = {
        "LEN2044", /* L470  */
        "LEN2054", /* E480 */
        "LEN2055", /* E580 */
+       "LEN2064", /* T14 Gen 1 AMD / P14s Gen 1 AMD */
        "LEN2068", /* T14 Gen 1 */
        "SYN3052", /* HP EliteBook 840 G4 */
        "SYN3221", /* HP 15-ay000 */
index 42443ffba7c4d8a6b5c4ded3d167b02cd4ab9cbe..ea9eff7c8099b8e4f8a88987066cb47cc4f0d726 100644 (file)
@@ -365,6 +365,19 @@ int vmmouse_detect(struct psmouse *psmouse, bool set_properties)
        return 0;
 }
 
+/**
+ * vmmouse_reset - Disable vmmouse and reset
+ *
+ * @psmouse: Pointer to the psmouse struct
+ *
+ * Tries to disable vmmouse mode before enter suspend.
+ */
+static void vmmouse_reset(struct psmouse *psmouse)
+{
+       vmmouse_disable(psmouse);
+       psmouse_reset(psmouse);
+}
+
 /**
  * vmmouse_disconnect - Take down vmmouse driver
  *
@@ -472,6 +485,7 @@ int vmmouse_init(struct psmouse *psmouse)
        psmouse->protocol_handler = vmmouse_process_byte;
        psmouse->disconnect = vmmouse_disconnect;
        psmouse->reconnect = vmmouse_reconnect;
+       psmouse->cleanup = vmmouse_reset;
 
        return 0;
 
index 93b328c796c643006b3589673dd79813b3d6067d..c5ce907535ef998ba5616e5beee0c11fdf8a87de 100644 (file)
@@ -733,7 +733,6 @@ remove_v4l2:
        v4l2_device_unregister(&f54->v4l2);
 remove_wq:
        cancel_delayed_work_sync(&f54->work);
-       flush_workqueue(f54->workqueue);
        destroy_workqueue(f54->workqueue);
        return ret;
 }
index 8970b49ea09a25053d79b90b79014f3619badc37..9b02dd5dd2b99895c95db0739f56493aa1cfb397 100644 (file)
@@ -19,6 +19,7 @@
 #include <linux/of.h>
 #include <linux/jiffies.h>
 #include <linux/delay.h>
+#include <linux/timekeeping.h>
 
 #define DRIVER_NAME            "ps2-gpio"
 
 #define PS2_DATA_BIT7          8
 #define PS2_PARITY_BIT         9
 #define PS2_STOP_BIT           10
-#define PS2_TX_TIMEOUT         11
-#define PS2_ACK_BIT            12
+#define PS2_ACK_BIT            11
 
 #define PS2_DEV_RET_ACK                0xfa
 #define PS2_DEV_RET_NACK       0xfe
 
 #define PS2_CMD_RESEND         0xfe
 
+/*
+ * The PS2 protocol specifies a clock frequency between 10kHz and 16.7kHz,
+ * therefore the maximal interrupt interval should be 100us and the minimum
+ * interrupt interval should be ~60us. Let's allow +/- 20us for frequency
+ * deviations and interrupt latency.
+ *
+ * The data line must be samples after ~30us to 50us after the falling edge,
+ * since the device updates the data line at the rising edge.
+ *
+ * ___            ______            ______            ______            ___
+ *    \          /      \          /      \          /      \          /
+ *     \        /        \        /        \        /        \        /
+ *      \______/          \______/          \______/          \______/
+ *
+ *     |-----------------|                 |--------|
+ *          60us/100us                      30us/50us
+ */
+#define PS2_CLK_FREQ_MIN_HZ            10000
+#define PS2_CLK_FREQ_MAX_HZ            16700
+#define PS2_CLK_MIN_INTERVAL_US                ((1000 * 1000) / PS2_CLK_FREQ_MAX_HZ)
+#define PS2_CLK_MAX_INTERVAL_US                ((1000 * 1000) / PS2_CLK_FREQ_MIN_HZ)
+#define PS2_IRQ_MIN_INTERVAL_US                (PS2_CLK_MIN_INTERVAL_US - 20)
+#define PS2_IRQ_MAX_INTERVAL_US                (PS2_CLK_MAX_INTERVAL_US + 20)
+
 struct ps2_gpio_data {
        struct device *dev;
        struct serio *serio;
@@ -52,19 +76,30 @@ struct ps2_gpio_data {
        struct gpio_desc *gpio_data;
        bool write_enable;
        int irq;
-       unsigned char rx_cnt;
-       unsigned char rx_byte;
-       unsigned char tx_cnt;
-       unsigned char tx_byte;
-       struct completion tx_done;
-       struct mutex tx_mutex;
-       struct delayed_work tx_work;
+       ktime_t t_irq_now;
+       ktime_t t_irq_last;
+       struct {
+               unsigned char cnt;
+               unsigned char byte;
+       } rx;
+       struct {
+               unsigned char cnt;
+               unsigned char byte;
+               ktime_t t_xfer_start;
+               ktime_t t_xfer_end;
+               struct completion complete;
+               struct mutex mutex;
+               struct delayed_work work;
+       } tx;
 };
 
 static int ps2_gpio_open(struct serio *serio)
 {
        struct ps2_gpio_data *drvdata = serio->port_data;
 
+       drvdata->t_irq_last = 0;
+       drvdata->tx.t_xfer_end = 0;
+
        enable_irq(drvdata->irq);
        return 0;
 }
@@ -73,7 +108,7 @@ static void ps2_gpio_close(struct serio *serio)
 {
        struct ps2_gpio_data *drvdata = serio->port_data;
 
-       flush_delayed_work(&drvdata->tx_work);
+       flush_delayed_work(&drvdata->tx.work);
        disable_irq(drvdata->irq);
 }
 
@@ -85,9 +120,9 @@ static int __ps2_gpio_write(struct serio *serio, unsigned char val)
        gpiod_direction_output(drvdata->gpio_clk, 0);
 
        drvdata->mode = PS2_MODE_TX;
-       drvdata->tx_byte = val;
+       drvdata->tx.byte = val;
 
-       schedule_delayed_work(&drvdata->tx_work, usecs_to_jiffies(200));
+       schedule_delayed_work(&drvdata->tx.work, usecs_to_jiffies(200));
 
        return 0;
 }
@@ -98,12 +133,12 @@ static int ps2_gpio_write(struct serio *serio, unsigned char val)
        int ret = 0;
 
        if (in_task()) {
-               mutex_lock(&drvdata->tx_mutex);
+               mutex_lock(&drvdata->tx.mutex);
                __ps2_gpio_write(serio, val);
-               if (!wait_for_completion_timeout(&drvdata->tx_done,
+               if (!wait_for_completion_timeout(&drvdata->tx.complete,
                                                 msecs_to_jiffies(10000)))
                        ret = SERIO_TIMEOUT;
-               mutex_unlock(&drvdata->tx_mutex);
+               mutex_unlock(&drvdata->tx.mutex);
        } else {
                __ps2_gpio_write(serio, val);
        }
@@ -115,9 +150,10 @@ static void ps2_gpio_tx_work_fn(struct work_struct *work)
 {
        struct delayed_work *dwork = to_delayed_work(work);
        struct ps2_gpio_data *drvdata = container_of(dwork,
-                                                   struct ps2_gpio_data,
-                                                   tx_work);
+                                                    struct ps2_gpio_data,
+                                                    tx.work);
 
+       drvdata->tx.t_xfer_start = ktime_get();
        enable_irq(drvdata->irq);
        gpiod_direction_output(drvdata->gpio_data, 0);
        gpiod_direction_input(drvdata->gpio_clk);
@@ -128,20 +164,31 @@ static irqreturn_t ps2_gpio_irq_rx(struct ps2_gpio_data *drvdata)
        unsigned char byte, cnt;
        int data;
        int rxflags = 0;
-       static unsigned long old_jiffies;
+       s64 us_delta;
 
-       byte = drvdata->rx_byte;
-       cnt = drvdata->rx_cnt;
+       byte = drvdata->rx.byte;
+       cnt = drvdata->rx.cnt;
 
-       if (old_jiffies == 0)
-               old_jiffies = jiffies;
+       drvdata->t_irq_now = ktime_get();
+
+       /*
+        * We need to consider spurious interrupts happening right after
+        * a TX xfer finished.
+        */
+       us_delta = ktime_us_delta(drvdata->t_irq_now, drvdata->tx.t_xfer_end);
+       if (unlikely(us_delta < PS2_IRQ_MIN_INTERVAL_US))
+               goto end;
 
-       if ((jiffies - old_jiffies) > usecs_to_jiffies(100)) {
+       us_delta = ktime_us_delta(drvdata->t_irq_now, drvdata->t_irq_last);
+       if (us_delta > PS2_IRQ_MAX_INTERVAL_US && cnt) {
                dev_err(drvdata->dev,
                        "RX: timeout, probably we missed an interrupt\n");
                goto err;
+       } else if (unlikely(us_delta < PS2_IRQ_MIN_INTERVAL_US)) {
+               /* Ignore spurious IRQs. */
+               goto end;
        }
-       old_jiffies = jiffies;
+       drvdata->t_irq_last = drvdata->t_irq_now;
 
        data = gpiod_get_value(drvdata->gpio_data);
        if (unlikely(data < 0)) {
@@ -178,8 +225,16 @@ static irqreturn_t ps2_gpio_irq_rx(struct ps2_gpio_data *drvdata)
                        if (!drvdata->write_enable)
                                goto err;
                }
+               break;
+       case PS2_STOP_BIT:
+               /* stop bit should be high */
+               if (unlikely(!data)) {
+                       dev_err(drvdata->dev, "RX: stop bit should be high\n");
+                       goto err;
+               }
 
-               /* Do not send spurious ACK's and NACK's when write fn is
+               /*
+                * Do not send spurious ACK's and NACK's when write fn is
                 * not provided.
                 */
                if (!drvdata->write_enable) {
@@ -189,23 +244,11 @@ static irqreturn_t ps2_gpio_irq_rx(struct ps2_gpio_data *drvdata)
                                break;
                }
 
-               /* Let's send the data without waiting for the stop bit to be
-                * sent. It may happen that we miss the stop bit. When this
-                * happens we have no way to recover from this, certainly
-                * missing the parity bit would be recognized when processing
-                * the stop bit. When missing both, data is lost.
-                */
                serio_interrupt(drvdata->serio, byte, rxflags);
                dev_dbg(drvdata->dev, "RX: sending byte 0x%x\n", byte);
-               break;
-       case PS2_STOP_BIT:
-               /* stop bit should be high */
-               if (unlikely(!data)) {
-                       dev_err(drvdata->dev, "RX: stop bit should be high\n");
-                       goto err;
-               }
+
                cnt = byte = 0;
-               old_jiffies = 0;
+
                goto end; /* success */
        default:
                dev_err(drvdata->dev, "RX: got out of sync with the device\n");
@@ -217,11 +260,10 @@ static irqreturn_t ps2_gpio_irq_rx(struct ps2_gpio_data *drvdata)
 
 err:
        cnt = byte = 0;
-       old_jiffies = 0;
        __ps2_gpio_write(drvdata->serio, PS2_CMD_RESEND);
 end:
-       drvdata->rx_cnt = cnt;
-       drvdata->rx_byte = byte;
+       drvdata->rx.cnt = cnt;
+       drvdata->rx.byte = byte;
        return IRQ_HANDLED;
 }
 
@@ -229,20 +271,34 @@ static irqreturn_t ps2_gpio_irq_tx(struct ps2_gpio_data *drvdata)
 {
        unsigned char byte, cnt;
        int data;
-       static unsigned long old_jiffies;
+       s64 us_delta;
+
+       cnt = drvdata->tx.cnt;
+       byte = drvdata->tx.byte;
 
-       cnt = drvdata->tx_cnt;
-       byte = drvdata->tx_byte;
+       drvdata->t_irq_now = ktime_get();
 
-       if (old_jiffies == 0)
-               old_jiffies = jiffies;
+       /*
+        * There might be pending IRQs since we disabled IRQs in
+        * __ps2_gpio_write().  We can expect at least one clock period until
+        * the device generates the first falling edge after releasing the
+        * clock line.
+        */
+       us_delta = ktime_us_delta(drvdata->t_irq_now,
+                                 drvdata->tx.t_xfer_start);
+       if (unlikely(us_delta < PS2_CLK_MIN_INTERVAL_US))
+               goto end;
 
-       if ((jiffies - old_jiffies) > usecs_to_jiffies(100)) {
+       us_delta = ktime_us_delta(drvdata->t_irq_now, drvdata->t_irq_last);
+       if (us_delta > PS2_IRQ_MAX_INTERVAL_US && cnt > 1) {
                dev_err(drvdata->dev,
                        "TX: timeout, probably we missed an interrupt\n");
                goto err;
+       } else if (unlikely(us_delta < PS2_IRQ_MIN_INTERVAL_US)) {
+               /* Ignore spurious IRQs. */
+               goto end;
        }
-       old_jiffies = jiffies;
+       drvdata->t_irq_last = drvdata->t_irq_now;
 
        switch (cnt) {
        case PS2_START_BIT:
@@ -270,27 +326,22 @@ static irqreturn_t ps2_gpio_irq_tx(struct ps2_gpio_data *drvdata)
                /* release data line to generate stop bit */
                gpiod_direction_input(drvdata->gpio_data);
                break;
-       case PS2_TX_TIMEOUT:
-               /* Devices generate one extra clock pulse before sending the
-                * acknowledgment.
-                */
-               break;
        case PS2_ACK_BIT:
-               gpiod_direction_input(drvdata->gpio_data);
                data = gpiod_get_value(drvdata->gpio_data);
                if (data) {
                        dev_warn(drvdata->dev, "TX: received NACK, retry\n");
                        goto err;
                }
 
+               drvdata->tx.t_xfer_end = ktime_get();
                drvdata->mode = PS2_MODE_RX;
-               complete(&drvdata->tx_done);
+               complete(&drvdata->tx.complete);
 
                cnt = 1;
-               old_jiffies = 0;
                goto end; /* success */
        default:
-               /* Probably we missed the stop bit. Therefore we release data
+               /*
+                * Probably we missed the stop bit. Therefore we release data
                 * line and try again.
                 */
                gpiod_direction_input(drvdata->gpio_data);
@@ -303,11 +354,10 @@ static irqreturn_t ps2_gpio_irq_tx(struct ps2_gpio_data *drvdata)
 
 err:
        cnt = 1;
-       old_jiffies = 0;
        gpiod_direction_input(drvdata->gpio_data);
-       __ps2_gpio_write(drvdata->serio, drvdata->tx_byte);
+       __ps2_gpio_write(drvdata->serio, drvdata->tx.byte);
 end:
-       drvdata->tx_cnt = cnt;
+       drvdata->tx.cnt = cnt;
        return IRQ_HANDLED;
 }
 
@@ -322,14 +372,19 @@ static irqreturn_t ps2_gpio_irq(int irq, void *dev_id)
 static int ps2_gpio_get_props(struct device *dev,
                                 struct ps2_gpio_data *drvdata)
 {
-       drvdata->gpio_data = devm_gpiod_get(dev, "data", GPIOD_IN);
+       enum gpiod_flags gflags;
+
+       /* Enforce open drain, since this is required by the PS/2 bus. */
+       gflags = GPIOD_IN | GPIOD_FLAGS_BIT_OPEN_DRAIN;
+
+       drvdata->gpio_data = devm_gpiod_get(dev, "data", gflags);
        if (IS_ERR(drvdata->gpio_data)) {
                dev_err(dev, "failed to request data gpio: %ld",
                        PTR_ERR(drvdata->gpio_data));
                return PTR_ERR(drvdata->gpio_data);
        }
 
-       drvdata->gpio_clk = devm_gpiod_get(dev, "clk", GPIOD_IN);
+       drvdata->gpio_clk = devm_gpiod_get(dev, "clk", gflags);
        if (IS_ERR(drvdata->gpio_clk)) {
                dev_err(dev, "failed to request clock gpio: %ld",
                        PTR_ERR(drvdata->gpio_clk));
@@ -387,7 +442,8 @@ static int ps2_gpio_probe(struct platform_device *pdev)
        serio->id.type = SERIO_8042;
        serio->open = ps2_gpio_open;
        serio->close = ps2_gpio_close;
-       /* Write can be enabled in platform/dt data, but possibly it will not
+       /*
+        * Write can be enabled in platform/dt data, but possibly it will not
         * work because of the tough timings.
         */
        serio->write = drvdata->write_enable ? ps2_gpio_write : NULL;
@@ -400,14 +456,15 @@ static int ps2_gpio_probe(struct platform_device *pdev)
        drvdata->dev = dev;
        drvdata->mode = PS2_MODE_RX;
 
-       /* Tx count always starts at 1, as the start bit is sent implicitly by
+       /*
+        * Tx count always starts at 1, as the start bit is sent implicitly by
         * host-to-device communication initialization.
         */
-       drvdata->tx_cnt = 1;
+       drvdata->tx.cnt = 1;
 
-       INIT_DELAYED_WORK(&drvdata->tx_work, ps2_gpio_tx_work_fn);
-       init_completion(&drvdata->tx_done);
-       mutex_init(&drvdata->tx_mutex);
+       INIT_DELAYED_WORK(&drvdata->tx.work, ps2_gpio_tx_work_fn);
+       init_completion(&drvdata->tx.complete);
+       mutex_init(&drvdata->tx.mutex);
 
        serio_register_port(serio);
        platform_set_drvdata(pdev, drvdata);
index 1581f6ef0927933118affc39a47a11870fc8aa1a..24ec4844a5c3e67fa078211879143b44bb4340c0 100644 (file)
@@ -931,8 +931,7 @@ aiptek_query(struct aiptek *aiptek, unsigned char command, unsigned char data)
        }
        msleep(aiptek->curSetting.programmableDelay);
 
-       if ((ret =
-            aiptek_get_report(aiptek, 3, 2, buf, sizeof_buf)) != sizeof_buf) {
+       if (aiptek_get_report(aiptek, 3, 2, buf, sizeof_buf) != sizeof_buf) {
                dev_dbg(&aiptek->intf->dev,
                        "aiptek_query failed: returned 0x%02x 0x%02x 0x%02x\n",
                        buf[0], buf[1], buf[2]);
index 2f6adfb7b938f83b0cc5240c0ee4f060346fb007..f1414f0ad7af24d88699464b9ac375bdf70cc7c7 100644 (file)
@@ -638,6 +638,16 @@ config TOUCHSCREEN_MTOUCH
          To compile this driver as a module, choose M here: the
          module will be called mtouch.
 
+config TOUCHSCREEN_IMAGIS
+       tristate "Imagis touchscreen support"
+       depends on I2C
+       help
+         Say Y here if you have an Imagis IST30xxC touchscreen.
+         If unsure, say N.
+
+         To compile this driver as a module, choose M here: the
+         module will be called imagis.
+
 config TOUCHSCREEN_IMX6UL_TSC
        tristate "Freescale i.MX6UL touchscreen controller"
        depends on ((OF && GPIOLIB) || COMPILE_TEST) && HAS_IOMEM
index 39a8127cf6a55fcf27e145db77007620ceb47072..557f84fd20755f6c7ef213b847c4fb2d36d37c60 100644 (file)
@@ -49,6 +49,7 @@ obj-$(CONFIG_TOUCHSCREEN_GOODIX)      += goodix_ts.o
 obj-$(CONFIG_TOUCHSCREEN_HIDEEP)       += hideep.o
 obj-$(CONFIG_TOUCHSCREEN_ILI210X)      += ili210x.o
 obj-$(CONFIG_TOUCHSCREEN_ILITEK)       += ilitek_ts_i2c.o
+obj-$(CONFIG_TOUCHSCREEN_IMAGIS)       += imagis.o
 obj-$(CONFIG_TOUCHSCREEN_IMX6UL_TSC)   += imx6ul_tsc.o
 obj-$(CONFIG_TOUCHSCREEN_INEXIO)       += inexio.o
 obj-$(CONFIG_TOUCHSCREEN_IPROC)                += bcm_iproc_tsc.o
index 752e8ba4fecb1c7c1c4dc3983d941fb47c606bd2..3ad9870db1081f825e1a5076c60121b46410c70c 100644 (file)
@@ -298,32 +298,17 @@ static int goodix_ts_read_input_report(struct goodix_ts_data *ts, u8 *data)
        return -ENOMSG;
 }
 
-static struct input_dev *goodix_create_pen_input(struct goodix_ts_data *ts)
+static int goodix_create_pen_input(struct goodix_ts_data *ts)
 {
        struct device *dev = &ts->client->dev;
        struct input_dev *input;
 
        input = devm_input_allocate_device(dev);
        if (!input)
-               return NULL;
-
-       input_alloc_absinfo(input);
-       if (!input->absinfo) {
-               input_free_device(input);
-               return NULL;
-       }
-
-       input->absinfo[ABS_X] = ts->input_dev->absinfo[ABS_MT_POSITION_X];
-       input->absinfo[ABS_Y] = ts->input_dev->absinfo[ABS_MT_POSITION_Y];
-       __set_bit(ABS_X, input->absbit);
-       __set_bit(ABS_Y, input->absbit);
-       input_set_abs_params(input, ABS_PRESSURE, 0, 255, 0, 0);
+               return -ENOMEM;
 
-       input_set_capability(input, EV_KEY, BTN_TOUCH);
-       input_set_capability(input, EV_KEY, BTN_TOOL_PEN);
-       input_set_capability(input, EV_KEY, BTN_STYLUS);
-       input_set_capability(input, EV_KEY, BTN_STYLUS2);
-       __set_bit(INPUT_PROP_DIRECT, input->propbit);
+       input_copy_abs(input, ABS_X, ts->input_dev, ABS_MT_POSITION_X);
+       input_copy_abs(input, ABS_Y, ts->input_dev, ABS_MT_POSITION_Y);
        /*
         * The resolution of these touchscreens is about 10 units/mm, the actual
         * resolution does not matter much since we set INPUT_PROP_DIRECT.
@@ -331,6 +316,13 @@ static struct input_dev *goodix_create_pen_input(struct goodix_ts_data *ts)
         */
        input_abs_set_res(input, ABS_X, 10);
        input_abs_set_res(input, ABS_Y, 10);
+       input_set_abs_params(input, ABS_PRESSURE, 0, 255, 0, 0);
+
+       input_set_capability(input, EV_KEY, BTN_TOUCH);
+       input_set_capability(input, EV_KEY, BTN_TOOL_PEN);
+       input_set_capability(input, EV_KEY, BTN_STYLUS);
+       input_set_capability(input, EV_KEY, BTN_STYLUS2);
+       __set_bit(INPUT_PROP_DIRECT, input->propbit);
 
        input->name = "Goodix Active Pen";
        input->phys = "input/pen";
@@ -340,25 +332,23 @@ static struct input_dev *goodix_create_pen_input(struct goodix_ts_data *ts)
                input->id.product = 0x1001;
        input->id.version = ts->version;
 
-       if (input_register_device(input) != 0) {
-               input_free_device(input);
-               return NULL;
-       }
-
-       return input;
+       ts->input_pen = input;
+       return 0;
 }
 
 static void goodix_ts_report_pen_down(struct goodix_ts_data *ts, u8 *data)
 {
-       int input_x, input_y, input_w;
+       int input_x, input_y, input_w, error;
        u8 key_value;
 
-       if (!ts->input_pen) {
-               ts->input_pen = goodix_create_pen_input(ts);
-               if (!ts->input_pen)
-                       return;
+       if (!ts->pen_input_registered) {
+               error = input_register_device(ts->input_pen);
+               ts->pen_input_registered = (error == 0) ? 1 : error;
        }
 
+       if (ts->pen_input_registered < 0)
+               return;
+
        if (ts->contact_size == 9) {
                input_x = get_unaligned_le16(&data[4]);
                input_y = get_unaligned_le16(&data[6]);
@@ -1215,6 +1205,17 @@ static int goodix_configure_dev(struct goodix_ts_data *ts)
                return error;
        }
 
+       /*
+        * Create the input_pen device before goodix_request_irq() calls
+        * devm_request_threaded_irq() so that the devm framework frees
+        * it after disabling the irq.
+        * Unfortunately there is no way to detect if the touchscreen has pen
+        * support, so registering the dev is delayed till the first pen event.
+        */
+       error = goodix_create_pen_input(ts);
+       if (error)
+               return error;
+
        ts->irq_flags = goodix_irq_flags[ts->int_trigger_type] | IRQF_ONESHOT;
        error = goodix_request_irq(ts);
        if (error) {
index fa8602e78a6480c066103214cb06e475b36de6eb..87797cc88b3243cb677d283502b32e0dee885639 100644 (file)
@@ -94,6 +94,7 @@ struct goodix_ts_data {
        u16 version;
        bool reset_controller_at_probe;
        bool load_cfg_from_disk;
+       int pen_input_registered;
        struct completion firmware_loading_complete;
        unsigned long irq_flags;
        enum goodix_irq_pin_access_method irq_pin_access_method;
diff --git a/drivers/input/touchscreen/imagis.c b/drivers/input/touchscreen/imagis.c
new file mode 100644 (file)
index 0000000..e2697e6
--- /dev/null
@@ -0,0 +1,367 @@
+// SPDX-License-Identifier: GPL-2.0-only
+
+#include <linux/bits.h>
+#include <linux/delay.h>
+#include <linux/i2c.h>
+#include <linux/input.h>
+#include <linux/input/mt.h>
+#include <linux/input/touchscreen.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/property.h>
+#include <linux/regulator/consumer.h>
+
+#define IST3038C_HIB_ACCESS            (0x800B << 16)
+#define IST3038C_DIRECT_ACCESS         BIT(31)
+#define IST3038C_REG_CHIPID            0x40001000
+#define IST3038C_REG_HIB_BASE          0x30000100
+#define IST3038C_REG_TOUCH_STATUS      (IST3038C_REG_HIB_BASE | IST3038C_HIB_ACCESS)
+#define IST3038C_REG_TOUCH_COORD       (IST3038C_REG_HIB_BASE | IST3038C_HIB_ACCESS | 0x8)
+#define IST3038C_REG_INTR_MESSAGE      (IST3038C_REG_HIB_BASE | IST3038C_HIB_ACCESS | 0x4)
+#define IST3038C_WHOAMI                        0x38c
+#define IST3038C_CHIP_ON_DELAY_MS      60
+#define IST3038C_I2C_RETRY_COUNT       3
+#define IST3038C_MAX_FINGER_NUM                10
+#define IST3038C_X_MASK                        GENMASK(23, 12)
+#define IST3038C_X_SHIFT               12
+#define IST3038C_Y_MASK                        GENMASK(11, 0)
+#define IST3038C_AREA_MASK             GENMASK(27, 24)
+#define IST3038C_AREA_SHIFT            24
+#define IST3038C_FINGER_COUNT_MASK     GENMASK(15, 12)
+#define IST3038C_FINGER_COUNT_SHIFT    12
+#define IST3038C_FINGER_STATUS_MASK    GENMASK(9, 0)
+
+struct imagis_ts {
+       struct i2c_client *client;
+       struct input_dev *input_dev;
+       struct touchscreen_properties prop;
+       struct regulator_bulk_data supplies[2];
+};
+
+static int imagis_i2c_read_reg(struct imagis_ts *ts,
+                              unsigned int reg, u32 *data)
+{
+       __be32 ret_be;
+       __be32 reg_be = cpu_to_be32(reg);
+       struct i2c_msg msg[] = {
+               {
+                       .addr = ts->client->addr,
+                       .flags = 0,
+                       .buf = (unsigned char *)&reg_be,
+                       .len = sizeof(reg_be),
+               }, {
+                       .addr = ts->client->addr,
+                       .flags = I2C_M_RD,
+                       .buf = (unsigned char *)&ret_be,
+                       .len = sizeof(ret_be),
+               },
+       };
+       int ret, error;
+       int retry = IST3038C_I2C_RETRY_COUNT;
+
+       /* Retry in case the controller fails to respond */
+       do {
+               ret = i2c_transfer(ts->client->adapter, msg, ARRAY_SIZE(msg));
+               if (ret == ARRAY_SIZE(msg)) {
+                       *data = be32_to_cpu(ret_be);
+                       return 0;
+               }
+
+               error = ret < 0 ? ret : -EIO;
+               dev_err(&ts->client->dev,
+                       "%s - i2c_transfer failed: %d (%d)\n",
+                       __func__, error, ret);
+       } while (--retry);
+
+       return error;
+}
+
+static irqreturn_t imagis_interrupt(int irq, void *dev_id)
+{
+       struct imagis_ts *ts = dev_id;
+       u32 intr_message, finger_status;
+       unsigned int finger_count, finger_pressed;
+       int i;
+       int error;
+
+       error = imagis_i2c_read_reg(ts, IST3038C_REG_INTR_MESSAGE,
+                                   &intr_message);
+       if (error) {
+               dev_err(&ts->client->dev,
+                       "failed to read the interrupt message: %d\n", error);
+               goto out;
+       }
+
+       finger_count = (intr_message & IST3038C_FINGER_COUNT_MASK) >>
+                               IST3038C_FINGER_COUNT_SHIFT;
+       if (finger_count > IST3038C_MAX_FINGER_NUM) {
+               dev_err(&ts->client->dev,
+                       "finger count %d is more than maximum supported\n",
+                       finger_count);
+               goto out;
+       }
+
+       finger_pressed = intr_message & IST3038C_FINGER_STATUS_MASK;
+
+       for (i = 0; i < finger_count; i++) {
+               error = imagis_i2c_read_reg(ts,
+                                           IST3038C_REG_TOUCH_COORD + (i * 4),
+                                           &finger_status);
+               if (error) {
+                       dev_err(&ts->client->dev,
+                               "failed to read coordinates for finger %d: %d\n",
+                               i, error);
+                       goto out;
+               }
+
+               input_mt_slot(ts->input_dev, i);
+               input_mt_report_slot_state(ts->input_dev, MT_TOOL_FINGER,
+                                          finger_pressed & BIT(i));
+               touchscreen_report_pos(ts->input_dev, &ts->prop,
+                                      (finger_status & IST3038C_X_MASK) >>
+                                               IST3038C_X_SHIFT,
+                                      finger_status & IST3038C_Y_MASK, 1);
+               input_report_abs(ts->input_dev, ABS_MT_TOUCH_MAJOR,
+                                (finger_status & IST3038C_AREA_MASK) >>
+                                       IST3038C_AREA_SHIFT);
+       }
+
+       input_mt_sync_frame(ts->input_dev);
+       input_sync(ts->input_dev);
+
+out:
+       return IRQ_HANDLED;
+}
+
+static void imagis_power_off(void *_ts)
+{
+       struct imagis_ts *ts = _ts;
+
+       regulator_bulk_disable(ARRAY_SIZE(ts->supplies), ts->supplies);
+}
+
+static int imagis_power_on(struct imagis_ts *ts)
+{
+       int error;
+
+       error = regulator_bulk_enable(ARRAY_SIZE(ts->supplies), ts->supplies);
+       if (error)
+               return error;
+
+       msleep(IST3038C_CHIP_ON_DELAY_MS);
+
+       return 0;
+}
+
+static int imagis_start(struct imagis_ts *ts)
+{
+       int error;
+
+       error = imagis_power_on(ts);
+       if (error)
+               return error;
+
+       enable_irq(ts->client->irq);
+
+       return 0;
+}
+
+static int imagis_stop(struct imagis_ts *ts)
+{
+       disable_irq(ts->client->irq);
+
+       imagis_power_off(ts);
+
+       return 0;
+}
+
+static int imagis_input_open(struct input_dev *dev)
+{
+       struct imagis_ts *ts = input_get_drvdata(dev);
+
+       return imagis_start(ts);
+}
+
+static void imagis_input_close(struct input_dev *dev)
+{
+       struct imagis_ts *ts = input_get_drvdata(dev);
+
+       imagis_stop(ts);
+}
+
+static int imagis_init_input_dev(struct imagis_ts *ts)
+{
+       struct input_dev *input_dev;
+       int error;
+
+       input_dev = devm_input_allocate_device(&ts->client->dev);
+       if (!input_dev)
+               return -ENOMEM;
+
+       ts->input_dev = input_dev;
+
+       input_dev->name = "Imagis capacitive touchscreen";
+       input_dev->phys = "input/ts";
+       input_dev->id.bustype = BUS_I2C;
+       input_dev->open = imagis_input_open;
+       input_dev->close = imagis_input_close;
+
+       input_set_drvdata(input_dev, ts);
+
+       input_set_capability(input_dev, EV_ABS, ABS_MT_POSITION_X);
+       input_set_capability(input_dev, EV_ABS, ABS_MT_POSITION_Y);
+       input_set_abs_params(input_dev, ABS_MT_TOUCH_MAJOR, 0, 255, 0, 0);
+
+       touchscreen_parse_properties(input_dev, true, &ts->prop);
+       if (!ts->prop.max_x || !ts->prop.max_y) {
+               dev_err(&ts->client->dev,
+                       "Touchscreen-size-x and/or touchscreen-size-y not set in dts\n");
+               return -EINVAL;
+       }
+
+       error = input_mt_init_slots(input_dev,
+                                   IST3038C_MAX_FINGER_NUM,
+                                   INPUT_MT_DIRECT | INPUT_MT_DROP_UNUSED);
+       if (error) {
+               dev_err(&ts->client->dev,
+                       "Failed to initialize MT slots: %d", error);
+               return error;
+       }
+
+       error = input_register_device(input_dev);
+       if (error) {
+               dev_err(&ts->client->dev,
+                       "Failed to register input device: %d", error);
+               return error;
+       }
+
+       return 0;
+}
+
+static int imagis_init_regulators(struct imagis_ts *ts)
+{
+       struct i2c_client *client = ts->client;
+
+       ts->supplies[0].supply = "vdd";
+       ts->supplies[1].supply = "vddio";
+       return devm_regulator_bulk_get(&client->dev,
+                                      ARRAY_SIZE(ts->supplies),
+                                      ts->supplies);
+}
+
+static int imagis_probe(struct i2c_client *i2c)
+{
+       struct device *dev = &i2c->dev;
+       struct imagis_ts *ts;
+       int chip_id, error;
+
+       ts = devm_kzalloc(dev, sizeof(*ts), GFP_KERNEL);
+       if (!ts)
+               return -ENOMEM;
+
+       ts->client = i2c;
+
+       error = imagis_init_regulators(ts);
+       if (error) {
+               dev_err(dev, "regulator init error: %d\n", error);
+               return error;
+       }
+
+       error = imagis_power_on(ts);
+       if (error) {
+               dev_err(dev, "failed to enable regulators: %d\n", error);
+               return error;
+       }
+
+       error = devm_add_action_or_reset(dev, imagis_power_off, ts);
+       if (error) {
+               dev_err(dev, "failed to install poweroff action: %d\n", error);
+               return error;
+       }
+
+       error = imagis_i2c_read_reg(ts,
+                       IST3038C_REG_CHIPID | IST3038C_DIRECT_ACCESS,
+                       &chip_id);
+       if (error) {
+               dev_err(dev, "chip ID read failure: %d\n", error);
+               return error;
+       }
+
+       if (chip_id != IST3038C_WHOAMI) {
+               dev_err(dev, "unknown chip ID: 0x%x\n", chip_id);
+               return -EINVAL;
+       }
+
+       error = devm_request_threaded_irq(dev, i2c->irq,
+                                         NULL, imagis_interrupt,
+                                         IRQF_ONESHOT | IRQF_NO_AUTOEN,
+                                         "imagis-touchscreen", ts);
+       if (error) {
+               dev_err(dev, "IRQ %d allocation failure: %d\n",
+                       i2c->irq, error);
+               return error;
+       }
+
+       error = imagis_init_input_dev(ts);
+       if (error)
+               return error;
+
+       return 0;
+}
+
+static int __maybe_unused imagis_suspend(struct device *dev)
+{
+       struct i2c_client *client = to_i2c_client(dev);
+       struct imagis_ts *ts = i2c_get_clientdata(client);
+       int retval = 0;
+
+       mutex_lock(&ts->input_dev->mutex);
+
+       if (input_device_enabled(ts->input_dev))
+               retval = imagis_stop(ts);
+
+       mutex_unlock(&ts->input_dev->mutex);
+
+       return retval;
+}
+
+static int __maybe_unused imagis_resume(struct device *dev)
+{
+       struct i2c_client *client = to_i2c_client(dev);
+       struct imagis_ts *ts = i2c_get_clientdata(client);
+       int retval = 0;
+
+       mutex_lock(&ts->input_dev->mutex);
+
+       if (input_device_enabled(ts->input_dev))
+               retval = imagis_start(ts);
+
+       mutex_unlock(&ts->input_dev->mutex);
+
+       return retval;
+}
+
+static SIMPLE_DEV_PM_OPS(imagis_pm_ops, imagis_suspend, imagis_resume);
+
+#ifdef CONFIG_OF
+static const struct of_device_id imagis_of_match[] = {
+       { .compatible = "imagis,ist3038c", },
+       { },
+};
+MODULE_DEVICE_TABLE(of, imagis_of_match);
+#endif
+
+static struct i2c_driver imagis_ts_driver = {
+       .driver = {
+               .name = "imagis-touchscreen",
+               .pm = &imagis_pm_ops,
+               .of_match_table = of_match_ptr(imagis_of_match),
+       },
+       .probe_new = imagis_probe,
+};
+
+module_i2c_driver(imagis_ts_driver);
+
+MODULE_DESCRIPTION("Imagis IST3038C Touchscreen Driver");
+MODULE_AUTHOR("Markuss Broks <markuss.broks@gmail.com>");
+MODULE_LICENSE("GPL");
index b3fa71213d603421d040b2a1503482d05db4f8cd..34c4cca57d133b717325b9ab68f7e8045fe882a7 100644 (file)
@@ -486,11 +486,11 @@ static int iqs5xx_axis_init(struct i2c_client *client)
 {
        struct iqs5xx_private *iqs5xx = i2c_get_clientdata(client);
        struct touchscreen_properties *prop = &iqs5xx->prop;
-       struct input_dev *input;
+       struct input_dev *input = iqs5xx->input;
        u16 max_x, max_y;
        int error;
 
-       if (!iqs5xx->input) {
+       if (!input) {
                input = devm_input_allocate_device(&client->dev);
                if (!input)
                        return -ENOMEM;
@@ -512,11 +512,11 @@ static int iqs5xx_axis_init(struct i2c_client *client)
        if (error)
                return error;
 
-       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);
+       input_set_abs_params(input, ABS_MT_POSITION_X, 0, max_x, 0, 0);
+       input_set_abs_params(input, ABS_MT_POSITION_Y, 0, max_y, 0, 0);
+       input_set_abs_params(input, ABS_MT_PRESSURE, 0, U16_MAX, 0, 0);
 
-       touchscreen_parse_properties(iqs5xx->input, true, prop);
+       touchscreen_parse_properties(input, true, prop);
 
        /*
         * The device reserves 0xFFFF for coordinates that correspond to slots
@@ -540,7 +540,7 @@ static int iqs5xx_axis_init(struct i2c_client *client)
                        return error;
        }
 
-       error = input_mt_init_slots(iqs5xx->input, IQS5XX_NUM_CONTACTS,
+       error = input_mt_init_slots(input, IQS5XX_NUM_CONTACTS,
                                    INPUT_MT_DIRECT);
        if (error)
                dev_err(&client->dev, "Failed to initialize slots: %d\n",
@@ -674,7 +674,7 @@ static irqreturn_t iqs5xx_irq(int irq, void *data)
                input_mt_slot(input, i);
                if (input_mt_report_slot_state(input, MT_TOOL_FINGER,
                                               pressure != 0)) {
-                       touchscreen_report_pos(iqs5xx->input, &iqs5xx->prop,
+                       touchscreen_report_pos(input, &iqs5xx->prop,
                                               be16_to_cpu(touch_data->abs_x),
                                               be16_to_cpu(touch_data->abs_y),
                                               true);
index bc11203c9cf785f5afcdf070ed5314185573a1cd..72e0b767e1ba4c87c31b5346c45746f82ff9773b 100644 (file)
@@ -339,11 +339,11 @@ static int stmfts_input_open(struct input_dev *dev)
 
        err = pm_runtime_get_sync(&sdata->client->dev);
        if (err < 0)
-               return err;
+               goto out;
 
        err = i2c_smbus_write_byte(sdata->client, STMFTS_MS_MT_SENSE_ON);
        if (err)
-               return err;
+               goto out;
 
        mutex_lock(&sdata->mutex);
        sdata->running = true;
@@ -366,7 +366,9 @@ static int stmfts_input_open(struct input_dev *dev)
                                 "failed to enable touchkey\n");
        }
 
-       return 0;
+out:
+       pm_runtime_put_noidle(&sdata->client->dev);
+       return err;
 }
 
 static void stmfts_input_close(struct input_dev *dev)
index 27810f6c69f67900d7419083e6f2e30cca716c67..72c7258b93a54612b5dbc94ca4d4fcd200fae571 100644 (file)
@@ -88,6 +88,8 @@ struct tsc200x {
        int                     in_z1;
        int                     in_z2;
 
+       struct touchscreen_properties prop;
+
        spinlock_t              lock;
        struct timer_list       penup_timer;
 
@@ -113,8 +115,7 @@ static void tsc200x_update_pen_state(struct tsc200x *ts,
                                     int x, int y, int pressure)
 {
        if (pressure) {
-               input_report_abs(ts->idev, ABS_X, x);
-               input_report_abs(ts->idev, ABS_Y, y);
+               touchscreen_report_pos(ts->idev, &ts->prop, x, y, false);
                input_report_abs(ts->idev, ABS_PRESSURE, pressure);
                if (!ts->pen_down) {
                        input_report_key(ts->idev, BTN_TOUCH, !!pressure);
@@ -533,7 +534,7 @@ int tsc200x_probe(struct device *dev, int irq, const struct input_id *tsc_id,
        input_set_abs_params(input_dev, ABS_PRESSURE,
                             0, MAX_12BIT, TSC200X_DEF_P_FUZZ, 0);
 
-       touchscreen_parse_properties(input_dev, false, NULL);
+       touchscreen_parse_properties(input_dev, false, &ts->prop);
 
        /* Ensure the touchscreen is off */
        tsc200x_stop_scan(ts);
diff --git a/drivers/input/vivaldi-fmap.c b/drivers/input/vivaldi-fmap.c
new file mode 100644 (file)
index 0000000..6dae83d
--- /dev/null
@@ -0,0 +1,39 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Helpers for ChromeOS Vivaldi keyboard function row mapping
+ *
+ * Copyright (C) 2022 Google, Inc
+ */
+
+#include <linux/export.h>
+#include <linux/input/vivaldi-fmap.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/types.h>
+
+/**
+ * vivaldi_function_row_physmap_show - Print vivaldi function row physmap attribute
+ * @data: The vivaldi function row map
+ * @buf: Buffer to print the function row phsymap to
+ */
+ssize_t vivaldi_function_row_physmap_show(const struct vivaldi_data *data,
+                                         char *buf)
+{
+       ssize_t size = 0;
+       int i;
+       const u32 *physmap = data->function_row_physmap;
+
+       if (!data->num_function_row_keys)
+               return 0;
+
+       for (i = 0; i < data->num_function_row_keys; i++)
+               size += scnprintf(buf + size, PAGE_SIZE - size,
+                                 "%s%02X", size ? " " : "", physmap[i]);
+       if (size)
+               size += scnprintf(buf + size, PAGE_SIZE - size, "\n");
+
+       return size;
+}
+EXPORT_SYMBOL_GPL(vivaldi_function_row_physmap_show);
+
+MODULE_LICENSE("GPL");
index 0354b298d874908319d9d3b00b3fe9780a32e9c8..49790c1bd2c43e21615fda95cbf3febb22ba6da0 100644 (file)
@@ -475,6 +475,8 @@ static inline void input_set_events_per_packet(struct input_dev *dev, int n_even
 void input_alloc_absinfo(struct input_dev *dev);
 void input_set_abs_params(struct input_dev *dev, unsigned int axis,
                          int min, int max, int fuzz, int flat);
+void input_copy_abs(struct input_dev *dst, unsigned int dst_axis,
+                   const struct input_dev *src, unsigned int src_axis);
 
 #define INPUT_GENERATE_ABS_ACCESSORS(_suffix, _item)                   \
 static inline int input_abs_get_##_suffix(struct input_dev *dev,       \
diff --git a/include/linux/input/vivaldi-fmap.h b/include/linux/input/vivaldi-fmap.h
new file mode 100644 (file)
index 0000000..7e4b702
--- /dev/null
@@ -0,0 +1,27 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+#ifndef _VIVALDI_FMAP_H
+#define _VIVALDI_FMAP_H
+
+#include <linux/types.h>
+
+#define VIVALDI_MAX_FUNCTION_ROW_KEYS  24
+
+/**
+ * struct vivaldi_data - Function row map data for ChromeOS Vivaldi keyboards
+ * @function_row_physmap: An array of scancodes or their equivalent (HID usage
+ *                        codes, encoded rows/columns, etc) for the top
+ *                        row function keys, in an order from left to right
+ * @num_function_row_keys: The number of top row keys in a custom keyboard
+ *
+ * This structure is supposed to be used by ChromeOS keyboards using
+ * the Vivaldi keyboard function row design.
+ */
+struct vivaldi_data {
+       u32 function_row_physmap[VIVALDI_MAX_FUNCTION_ROW_KEYS];
+       unsigned int num_function_row_keys;
+};
+
+ssize_t vivaldi_function_row_physmap_show(const struct vivaldi_data *data,
+                                         char *buf);
+
+#endif /* _VIVALDI_FMAP_H */