Merge branch 'for-linus' of git://git.kernel.org/pub/scm/linux/kernel/git/dtor/input

Pull input updates from Dmitry Torokhov:

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

 - a new driver for Azoteq IQS626A proximity and touch controller

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

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

 - various driver fixes

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

View File

@ -15,3 +15,12 @@ Description: Reports the model identification provided by the touchscreen, fo
Access: Read
Valid values: Represented as string
What: /sys/bus/i2c/devices/xxx/type
Date: Jan 2021
Contact: linux-input@vger.kernel.org
Description: Reports the type identification provided by the touchscreen, for example "PCAP82H80 Series"
Access: Read
Valid values: Represented as string

View File

@ -39,6 +39,13 @@ properties:
(active low). The line must be flagged with
GPIO_ACTIVE_LOW.
wake-gpios:
maxItems: 1
description:
Optional GPIO specifier for the touchscreen's wake pin
(active low). The line must be flagged with
GPIO_ACTIVE_LOW.
linux,gpio-keymap:
$ref: /schemas/types.yaml#/definitions/uint32-array
description: |
@ -53,6 +60,29 @@ properties:
or experiment to determine which bit corresponds to which input. Use
KEY_RESERVED for unused padding values.
atmel,wakeup-method:
$ref: /schemas/types.yaml#/definitions/uint32
description: |
The WAKE line is an active-low input that is used to wake up the touch
controller from deep-sleep mode before communication with the controller
could be started. This optional feature used to minimize current
consumption when the controller is in deep sleep mode. This feature is
relevant only to some controller families, like mXT1386 controller for
example.
The WAKE pin can be connected in one of the following ways:
1) left permanently low
2) connected to the I2C-compatible SCL pin
3) connected to a GPIO pin on the host
enum:
- 0 # ATMEL_MXT_WAKEUP_NONE
- 1 # ATMEL_MXT_WAKEUP_I2C_SCL
- 2 # ATMEL_MXT_WAKEUP_GPIO
default: 0
wakeup-source:
type: boolean
required:
- compatible
- reg
@ -63,6 +93,7 @@ additionalProperties: false
examples:
- |
#include <dt-bindings/interrupt-controller/irq.h>
#include <dt-bindings/input/atmel-maxtouch.h>
#include <dt-bindings/gpio/gpio.h>
i2c {
#address-cells = <1>;
@ -75,6 +106,7 @@ examples:
reset-gpios = <&gpio 27 GPIO_ACTIVE_LOW>;
vdda-supply = <&ab8500_ldo_aux2_reg>;
vdd-supply = <&ab8500_ldo_aux5_reg>;
atmel,wakeup-method = <ATMEL_MXT_WAKEUP_I2C_SCL>;
};
};

View File

@ -0,0 +1,843 @@
# SPDX-License-Identifier: (GPL-2.0 OR BSD-2-Clause)
%YAML 1.2
---
$id: http://devicetree.org/schemas/input/iqs626a.yaml#
$schema: http://devicetree.org/meta-schemas/core.yaml#
title: Azoteq IQS626A Capacitive Touch Controller
maintainers:
- Jeff LaBundy <jeff@labundy.com>
description: |
The Azoteq IQS626A is a 14-channel capacitive touch controller that features
additional Hall-effect and inductive sensing capabilities.
Link to datasheet: https://www.azoteq.com/
allOf:
- $ref: touchscreen/touchscreen.yaml#
properties:
compatible:
const: azoteq,iqs626a
reg:
maxItems: 1
interrupts:
maxItems: 1
"#address-cells":
const: 1
"#size-cells":
const: 0
azoteq,suspend-mode:
$ref: /schemas/types.yaml#/definitions/uint32
enum: [0, 1, 2, 3]
default: 0
description: |
Specifies the power mode during suspend as follows:
0: Automatic (same as normal runtime, i.e. suspend/resume disabled)
1: Low power (all sensing at a reduced reporting rate)
2: Ultra-low power (ULP channel proximity sensing)
3: Halt (no sensing)
azoteq,clk-div:
type: boolean
description: Divides the device's core clock by a factor of 4.
azoteq,ulp-enable:
type: boolean
description:
Permits the device to automatically enter ultra-low-power mode from low-
power mode.
azoteq,ulp-update:
$ref: /schemas/types.yaml#/definitions/uint32
enum: [0, 1, 2, 3, 4, 5, 6, 7]
default: 3
description: |
Specifies the rate at which the trackpad, generic and Hall channels are
updated during ultra-low-power mode as follows:
0: 8
1: 13
2: 28
3: 54
4: 89
5: 135
6: 190
7: 256
azoteq,ati-band-disable:
type: boolean
description: Disables the ATI band check.
azoteq,ati-lp-only:
type: boolean
description: Limits automatic ATI to low-power mode.
azoteq,gpio3-select:
$ref: /schemas/types.yaml#/definitions/uint32
enum: [0, 1, 2, 3, 4, 5, 6, 7]
default: 1
description: |
Selects the channel or group of channels for which the GPIO3 pin
represents touch state as follows:
0: None
1: ULP channel
2: Trackpad
3: Trackpad
4: Generic channel 0
5: Generic channel 1
6: Generic channel 2
7: Hall channel
azoteq,reseed-select:
$ref: /schemas/types.yaml#/definitions/uint32
enum: [0, 1, 2, 3]
default: 0
description: |
Specifies the event(s) that prompt the device to reseed (i.e. reset the
long-term average) of an associated channel as follows:
0: None
1: Proximity
2: Proximity or touch
3: Proximity, touch or deep touch
azoteq,thresh-extend:
type: boolean
description: Multiplies all touch and deep-touch thresholds by 4.
azoteq,tracking-enable:
type: boolean
description:
Enables all associated channels to track their respective reference
channels.
azoteq,reseed-offset:
type: boolean
description:
Applies an 8-count offset to all long-term averages upon either ATI or
reseed events.
azoteq,rate-np-ms:
minimum: 0
maximum: 255
default: 150
description: Specifies the report rate (in ms) during normal-power mode.
azoteq,rate-lp-ms:
minimum: 0
maximum: 255
default: 150
description: Specifies the report rate (in ms) during low-power mode.
azoteq,rate-ulp-ms:
multipleOf: 16
minimum: 0
maximum: 4080
default: 0
description: Specifies the report rate (in ms) during ultra-low-power mode.
azoteq,timeout-pwr-ms:
multipleOf: 512
minimum: 0
maximum: 130560
default: 2560
description:
Specifies the length of time (in ms) to wait for an event before moving
from normal-power mode to low-power mode, or (if 'azoteq,ulp-enable' is
present) from low-power mode to ultra-low-power mode.
azoteq,timeout-lta-ms:
multipleOf: 512
minimum: 0
maximum: 130560
default: 40960
description:
Specifies the length of time (in ms) to wait before resetting the long-
term average of all channels. Specify the maximum timeout to disable it
altogether.
touchscreen-inverted-x: true
touchscreen-inverted-y: true
touchscreen-swapped-x-y: true
patternProperties:
"^ulp-0|generic-[0-2]|hall$":
type: object
description:
Represents a single sensing channel. A channel is active if defined and
inactive otherwise.
properties:
azoteq,ati-exclude:
type: boolean
description:
Prevents the channel from participating in an ATI event that is
manually triggered during initialization.
azoteq,reseed-disable:
type: boolean
description:
Prevents the channel from being reseeded if the long-term average
timeout (defined in 'azoteq,timeout-lta') expires.
azoteq,meas-cap-decrease:
type: boolean
description:
Decreases the internal measurement capacitance from 60 pF to 15 pF.
azoteq,rx-inactive:
$ref: /schemas/types.yaml#/definitions/uint32
enum: [0, 1, 2]
default: 0
description: |
Specifies how inactive CRX pins are to be terminated as follows:
0: VSS
1: Floating
2: VREG (generic channels only)
azoteq,linearize:
type: boolean
description:
Enables linearization of the channel's counts (generic and Hall
channels) or inverts the polarity of the channel's proximity or
touch states (ULP channel).
azoteq,dual-direction:
type: boolean
description:
Specifies that the channel's long-term average is to freeze in the
presence of either increasing or decreasing counts, thereby permit-
ting events to be reported in either direction.
azoteq,filt-disable:
type: boolean
description: Disables raw count filtering for the channel.
azoteq,ati-mode:
$ref: /schemas/types.yaml#/definitions/uint32
enum: [0, 1, 2, 3]
description: |
Specifies the channel's ATI mode as follows:
0: Disabled
1: Semi-partial
2: Partial
3: Full
The default value is a function of the channel and the device's reset
user interface (RUI); reference the datasheet for further information
about the available RUI options.
azoteq,ati-base:
$ref: /schemas/types.yaml#/definitions/uint32
enum: [75, 100, 150, 200]
description:
Specifies the channel's ATI base. The default value is a function
of the channel and the device's RUI.
azoteq,ati-target:
$ref: /schemas/types.yaml#/definitions/uint32
multipleOf: 32
minimum: 0
maximum: 2016
description:
Specifies the channel's ATI target. The default value is a function
of the channel and the device's RUI.
azoteq,cct-increase:
$ref: /schemas/types.yaml#/definitions/uint32
minimum: 0
maximum: 16
default: 0
description:
Specifies the degree to which the channel's charge cycle time is to
be increased, with 0 representing no increase. The maximum value is
limited to 4 in the case of the ULP channel, and the property is un-
available entirely in the case of the Hall channel.
azoteq,proj-bias:
$ref: /schemas/types.yaml#/definitions/uint32
enum: [0, 1, 2, 3]
default: 0
description: |
Specifies the bias current applied during projected-capacitance
sensing as follows:
0: 2.5 uA
1: 5 uA
2: 10 uA
3: 20 uA
This property is unavailable in the case of the Hall channel.
azoteq,sense-freq:
$ref: /schemas/types.yaml#/definitions/uint32
enum: [0, 1, 2, 3]
description: |
Specifies the channel's sensing frequency as follows (parenthesized
numbers represent the frequency if 'azoteq,clk-div' is present):
0: 4 MHz (1 MHz)
1: 2 MHz (500 kHz)
2: 1 MHz (250 kHz)
3: 500 kHz (125 kHz)
This property is unavailable in the case of the Hall channel. The
default value is a function of the channel and the device's RUI.
azoteq,ati-band-tighten:
type: boolean
description:
Tightens the ATI band from 1/8 to 1/16 of the desired target (ULP and
generic channels only).
azoteq,proj-enable:
type: boolean
description: Enables projected-capacitance sensing (ULP channel only).
azoteq,filt-str-np-cnt:
$ref: /schemas/types.yaml#/definitions/uint32
enum: [0, 1, 2, 3]
default: 0
description:
Specifies the raw count filter strength during normal-power mode (ULP
and generic channels only).
azoteq,filt-str-lp-cnt:
$ref: /schemas/types.yaml#/definitions/uint32
enum: [0, 1, 2, 3]
default: 0
description:
Specifies the raw count filter strength during low-power mode (ULP and
generic channels only).
azoteq,filt-str-np-lta:
$ref: /schemas/types.yaml#/definitions/uint32
enum: [0, 1, 2, 3]
default: 0
description:
Specifies the long-term average filter strength during normal-power
mode (ULP and generic channels only).
azoteq,filt-str-lp-lta:
$ref: /schemas/types.yaml#/definitions/uint32
enum: [0, 1, 2, 3]
default: 0
description:
Specifies the long-term average filter strength during low-power mode
(ULP and generic channels only).
azoteq,rx-enable:
$ref: /schemas/types.yaml#/definitions/uint32-array
minItems: 1
maxItems: 8
items:
minimum: 0
maximum: 7
description:
Specifies the CRX pin(s) associated with the channel.
This property is unavailable in the case of the Hall channel. The
default value is a function of the channel and the device's RUI.
azoteq,tx-enable:
$ref: /schemas/types.yaml#/definitions/uint32-array
minItems: 1
maxItems: 8
items:
minimum: 0
maximum: 7
description:
Specifies the TX pin(s) associated with the channel.
This property is unavailable in the case of the Hall channel. The
default value is a function of the channel and the device's RUI.
azoteq,local-cap-size:
$ref: /schemas/types.yaml#/definitions/uint32
enum: [0, 1, 2, 3, 4]
default: 0
description: |
Specifies the capacitance to be added to the channel as follows:
0: 0 pF
1: 0.5 pF
2: 1.0 pF
3: 1.5 pF
4: 2.0 pF
This property is unavailable in the case of the ULP or Hall channels.
azoteq,sense-mode:
$ref: /schemas/types.yaml#/definitions/uint32
enum: [0, 1, 8, 9, 12, 14, 15]
description: |
Specifies the channel's sensing mode as follows:
0: Self capacitance
1: Projected capacitance
8: Self inductance
9: Mutual inductance
12: External
14: Hall effect
15: Temperature
This property is unavailable in the case of the ULP or Hall channels.
The default value is a function of the channel and the device's RUI.
azoteq,tx-freq:
$ref: /schemas/types.yaml#/definitions/uint32
enum: [0, 1, 2, 3]
default: 0
description: |
Specifies the inductive sensing excitation frequency as follows
(parenthesized numbers represent the frequency if 'azoteq,clk-div'
is present):
0: 16 MHz (4 MHz)
1: 8 MHz (2 MHz)
2: 4 MHz (1 MHz)
3: 2 MHz (500 kHz)
This property is unavailable in the case of the ULP or Hall channels.
azoteq,invert-enable:
type: boolean
description:
Inverts the polarity of the states reported for proximity, touch and
deep-touch events relative to their respective thresholds (generic
channels only).
azoteq,comp-disable:
type: boolean
description:
Disables compensation for the channel (generic channels only).
azoteq,static-enable:
type: boolean
description:
Enables the static front-end for the channel (generic channels only).
azoteq,assoc-select:
$ref: /schemas/types.yaml#/definitions/string-array
minItems: 1
maxItems: 6
items:
enum:
- ulp-0
- trackpad-3x2
- trackpad-3x3
- generic-0
- generic-1
- generic-2
- hall
description:
Specifies the associated channels for which the channel serves as a
reference channel. By default, no channels are selected. This prop-
erty is only available for the generic channels.
azoteq,assoc-weight:
$ref: /schemas/types.yaml#/definitions/uint32
minimum: 0
maximum: 255
default: 0
description:
Specifies the channel's impact weight if it acts as an associated
channel (0 = 0% impact, 255 = 200% impact). This property is only
available for the generic channels.
patternProperties:
"^event-(prox|touch|deep)(-alt)?$":
type: object
description:
Represents a proximity, touch or deep-touch event reported by the
channel in response to a decrease in counts. Node names suffixed with
'-alt' instead correspond to an increase in counts.
By default, the long-term average tracks an increase in counts such
that only events corresponding to a decrease in counts are reported
(refer to the datasheet for more information).
Specify 'azoteq,dual-direction' to freeze the long-term average when
the counts increase or decrease such that events of either direction
can be reported. Alternatively, specify 'azoteq,invert-enable' to in-
vert the polarity of the states reported by the channel.
Complementary events (e.g. event-touch and event-touch-alt) can both
be present and specify different key or switch codes, but not differ-
ent thresholds or hysteresis (if applicable).
Proximity events are unavailable in the case of the Hall channel, and
deep-touch events are only available for the generic channels. Unless
otherwise specified, default values are a function of the channel and
the device's RUI.
properties:
azoteq,thresh:
$ref: /schemas/types.yaml#/definitions/uint32
minimum: 0
maximum: 255
description: Specifies the threshold for the event.
azoteq,hyst:
$ref: /schemas/types.yaml#/definitions/uint32
minimum: 0
maximum: 15
description:
Specifies the hysteresis for the event (touch and deep-touch
events only).
linux,code:
$ref: /schemas/types.yaml#/definitions/uint32
description: Numeric key or switch code associated with the event.
linux,input-type:
$ref: /schemas/types.yaml#/definitions/uint32
enum: [1, 5]
description:
Specifies whether the event is to be interpreted as a key (1) or
a switch (5). By default, Hall-channel events are interpreted as
switches and all others are interpreted as keys.
dependencies:
linux,input-type: ["linux,code"]
additionalProperties: false
dependencies:
azoteq,assoc-weight: ["azoteq,assoc-select"]
additionalProperties: false
"^trackpad-3x[2-3]$":
type: object
description:
Represents all channels associated with the trackpad. The channels are
collectively active if the trackpad is defined and inactive otherwise.
properties:
azoteq,ati-exclude:
type: boolean
description:
Prevents the trackpad channels from participating in an ATI event
that is manually triggered during initialization.
azoteq,reseed-disable:
type: boolean
description:
Prevents the trackpad channels from being reseeded if the long-term
average timeout (defined in 'azoteq,timeout-lta') expires.
azoteq,meas-cap-decrease:
type: boolean
description:
Decreases the internal measurement capacitance from 60 pF to 15 pF.
azoteq,rx-inactive:
$ref: /schemas/types.yaml#/definitions/uint32
enum: [0, 1]
default: 0
description: |
Specifies how inactive CRX pins are to be terminated as follows:
0: VSS
1: Floating
azoteq,linearize:
type: boolean
description: Inverts the polarity of the trackpad's touch state.
azoteq,dual-direction:
type: boolean
description:
Specifies that the trackpad's long-term averages are to freeze in
the presence of either increasing or decreasing counts, thereby
permitting events to be reported in either direction.
azoteq,filt-disable:
type: boolean
description: Disables raw count filtering for the trackpad channels.
azoteq,ati-mode:
$ref: /schemas/types.yaml#/definitions/uint32
enum: [0, 1, 2, 3]
default: 0
description: |
Specifies the trackpad's ATI mode as follows:
0: Disabled
1: Semi-partial
2: Partial
3: Full
azoteq,ati-base:
$ref: /schemas/types.yaml#/definitions/uint32-array
minItems: 6
maxItems: 9
items:
minimum: 45
maximum: 300
default: [45, 45, 45, 45, 45, 45, 45, 45, 45]
description: Specifies each individual trackpad channel's ATI base.
azoteq,ati-target:
$ref: /schemas/types.yaml#/definitions/uint32
multipleOf: 32
minimum: 0
maximum: 2016
default: 0
description: Specifies the trackpad's ATI target.
azoteq,cct-increase:
$ref: /schemas/types.yaml#/definitions/uint32
minimum: 0
maximum: 4
default: 0
description:
Specifies the degree to which the trackpad's charge cycle time is to
be increased, with 0 representing no increase.
azoteq,proj-bias:
$ref: /schemas/types.yaml#/definitions/uint32
enum: [0, 1, 2, 3]
default: 0
description: |
Specifies the bias current applied during projected-capacitance
sensing as follows:
0: 2.5 uA
1: 5 uA
2: 10 uA
3: 20 uA
azoteq,sense-freq:
$ref: /schemas/types.yaml#/definitions/uint32
enum: [0, 1, 2, 3]
default: 0
description: |
Specifies the trackpad's sensing frequency as follows (parenthesized
numbers represent the frequency if 'azoteq,clk-div' is present):
0: 4 MHz (1 MHz)
1: 2 MHz (500 kHz)
2: 1 MHz (250 kHz)
3: 500 kHz (125 kHz)
azoteq,ati-band-tighten:
type: boolean
description:
Tightens the ATI band from 1/8 to 1/16 of the desired target.
azoteq,thresh:
$ref: /schemas/types.yaml#/definitions/uint32-array
minItems: 6
maxItems: 9
items:
minimum: 0
maximum: 255
default: [0, 0, 0, 0, 0, 0, 0, 0, 0]
description:
Specifies each individual trackpad channel's touch threshold.
azoteq,hyst:
$ref: /schemas/types.yaml#/definitions/uint32
minimum: 0
maximum: 15
default: 0
description: Specifies the trackpad's touch hysteresis.
azoteq,lta-update:
$ref: /schemas/types.yaml#/definitions/uint32
enum: [0, 1, 2, 3, 4, 5, 6, 7]
default: 0
description: |
Specifies the update rate of the trackpad's long-term average during
ultra-low-power mode as follows:
0: 2
1: 4
2: 8
3: 16
4: 32
5: 64
6: 128
7: 255
azoteq,filt-str-trackpad:
$ref: /schemas/types.yaml#/definitions/uint32
enum: [0, 1, 2, 3]
default: 0
description: Specifies the trackpad coordinate filter strength.
azoteq,filt-str-np-cnt:
$ref: /schemas/types.yaml#/definitions/uint32
enum: [0, 1, 2, 3]
default: 0
description:
Specifies the raw count filter strength during normal-power mode.
azoteq,filt-str-lp-cnt:
$ref: /schemas/types.yaml#/definitions/uint32
enum: [0, 1, 2, 3]
default: 0
description:
Specifies the raw count filter strength during low-power mode.
linux,keycodes:
$ref: /schemas/types.yaml#/definitions/uint32-array
minItems: 1
maxItems: 6
description: |
Specifies the numeric keycodes associated with each available gesture
in the following order (enter 0 for unused gestures):
0: Positive flick or swipe in X direction
1: Negative flick or swipe in X direction
2: Positive flick or swipe in Y direction
3: Negative flick or swipe in Y direction
4: Tap
5: Hold
azoteq,gesture-swipe:
type: boolean
description:
Directs the device to interpret axial gestures as a swipe (finger
remains on trackpad) instead of a flick (finger leaves trackpad).
azoteq,timeout-tap-ms:
multipleOf: 16
minimum: 0
maximum: 4080
default: 0
description:
Specifies the length of time (in ms) within which a trackpad touch
must be released in order to be interpreted as a tap.
azoteq,timeout-swipe-ms:
multipleOf: 16
minimum: 0
maximum: 4080
default: 0
description:
Specifies the length of time (in ms) within which an axial gesture
must be completed in order to be interpreted as a flick or swipe.
azoteq,thresh-swipe:
$ref: /schemas/types.yaml#/definitions/uint32
minimum: 0
maximum: 255
default: 0
description:
Specifies the number of points across which an axial gesture must
travel in order to be interpreted as a flick or swipe.
dependencies:
azoteq,gesture-swipe: ["linux,keycodes"]
azoteq,timeout-tap-ms: ["linux,keycodes"]
azoteq,timeout-swipe-ms: ["linux,keycodes"]
azoteq,thresh-swipe: ["linux,keycodes"]
additionalProperties: false
required:
- compatible
- reg
- interrupts
- "#address-cells"
- "#size-cells"
additionalProperties: false
examples:
- |
#include <dt-bindings/input/input.h>
#include <dt-bindings/interrupt-controller/irq.h>
i2c {
#address-cells = <1>;
#size-cells = <0>;
iqs626a@44 {
#address-cells = <1>;
#size-cells = <0>;
compatible = "azoteq,iqs626a";
reg = <0x44>;
interrupt-parent = <&gpio>;
interrupts = <17 IRQ_TYPE_LEVEL_LOW>;
azoteq,rate-np-ms = <16>;
azoteq,rate-lp-ms = <160>;
azoteq,timeout-pwr-ms = <2560>;
azoteq,timeout-lta-ms = <32768>;
ulp-0 {
azoteq,meas-cap-decrease;
azoteq,ati-base = <75>;
azoteq,ati-target = <1024>;
azoteq,rx-enable = <2>, <3>, <4>,
<5>, <6>, <7>;
event-prox {
linux,code = <KEY_POWER>;
};
};
trackpad-3x3 {
azoteq,filt-str-np-cnt = <1>;
azoteq,filt-str-lp-cnt = <1>;
azoteq,hyst = <4>;
azoteq,thresh = <35>, <40>, <40>,
<38>, <33>, <38>,
<35>, <35>, <35>;
azoteq,ati-mode = <3>;
azoteq,ati-base = <195>, <195>, <195>,
<195>, <195>, <195>,
<195>, <195>, <195>;
azoteq,ati-target = <512>;
azoteq,proj-bias = <1>;
azoteq,sense-freq = <2>;
linux,keycodes = <KEY_VOLUMEUP>,
<KEY_VOLUMEDOWN>,
<KEY_NEXTSONG>,
<KEY_PREVIOUSSONG>,
<KEY_PLAYPAUSE>,
<KEY_STOPCD>;
azoteq,gesture-swipe;
azoteq,timeout-swipe-ms = <800>;
azoteq,timeout-tap-ms = <400>;
azoteq,thresh-swipe = <40>;
};
/*
* Preserve the default register settings for
* the temperature-tracking channel leveraged
* by reset user interface (RUI) 1.
*
* Scalar properties (e.g. ATI mode) are left
* untouched by simply omitting them; boolean
* properties must be specified explicitly as
* needed.
*/
generic-2 {
azoteq,reseed-disable;
azoteq,meas-cap-decrease;
azoteq,dual-direction;
azoteq,comp-disable;
azoteq,static-enable;
};
hall {
azoteq,reseed-disable;
azoteq,meas-cap-decrease;
event-touch {
linux,code = <SW_LID>;
};
};
};
};
...

View File

@ -0,0 +1,75 @@
# SPDX-License-Identifier: (GPL-2.0 OR BSD-2-Clause)
%YAML 1.2
---
$id: http://devicetree.org/schemas/input/touchscreen/azoteq,iqs5xx.yaml#
$schema: http://devicetree.org/meta-schemas/core.yaml#
title: Azoteq IQS550/572/525 Trackpad/Touchscreen Controller
maintainers:
- Jeff LaBundy <jeff@labundy.com>
description: |
The Azoteq IQS550, IQS572 and IQS525 trackpad and touchscreen controllers
employ projected-capacitance sensing and can track up to five independent
contacts.
Link to datasheet: https://www.azoteq.com/
allOf:
- $ref: touchscreen.yaml#
properties:
compatible:
enum:
- azoteq,iqs550
- azoteq,iqs572
- azoteq,iqs525
reg:
maxItems: 1
interrupts:
maxItems: 1
reset-gpios:
maxItems: 1
wakeup-source: true
touchscreen-size-x: true
touchscreen-size-y: true
touchscreen-inverted-x: true
touchscreen-inverted-y: true
touchscreen-swapped-x-y: true
required:
- compatible
- reg
- interrupts
additionalProperties: false
examples:
- |
#include <dt-bindings/gpio/gpio.h>
#include <dt-bindings/interrupt-controller/irq.h>
i2c {
#address-cells = <1>;
#size-cells = <0>;
touchscreen@74 {
compatible = "azoteq,iqs550";
reg = <0x74>;
interrupt-parent = <&gpio>;
interrupts = <27 IRQ_TYPE_LEVEL_HIGH>;
reset-gpios = <&gpio 22 (GPIO_ACTIVE_LOW |
GPIO_PUSH_PULL)>;
touchscreen-size-x = <800>;
touchscreen-size-y = <480>;
};
};
...

View File

@ -0,0 +1,119 @@
# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
%YAML 1.2
---
$id: http://devicetree.org/schemas/input/touchscreen/hycon,hy46xx.yaml#
$schema: http://devicetree.org/meta-schemas/core.yaml#
title: Hycon HY46XX series touchscreen controller bindings
description: |
There are 6 variants of the chip for various touch panel sizes and cover lens material
Glass: 0.3mm--4.0mm
PET/PMMA: 0.2mm--2.0mm
HY4613(B)-N048 < 6"
HY4614(B)-N068 7" .. 10.1"
HY4621-NS32 < 5"
HY4623-NS48 5.1" .. 7"
Glass: 0.3mm--8.0mm
PET/PMMA: 0.2mm--4.0mm
HY4633(B)-N048 < 6"
HY4635(B)-N048 < 7" .. 10.1"
maintainers:
- Giulio Benetti <giulio.benetti@benettiengineering.com>
allOf:
- $ref: touchscreen.yaml#
properties:
compatible:
enum:
- hycon,hy4613
- hycon,hy4614
- hycon,hy4621
- hycon,hy4623
- hycon,hy4633
- hycon,hy4635
reg:
maxItems: 1
interrupts:
maxItems: 1
reset-gpios:
maxItems: 1
vcc-supply: true
hycon,threshold:
description: Allows setting the sensitivity in the range from 0 to 255.
$ref: /schemas/types.yaml#/definitions/uint32
minimum: 0
maximum: 255
hycon,glove-enable:
type: boolean
description: Allows enabling glove setting.
hycon,report-speed-hz:
description: Allows setting the report speed in Hertz.
minimum: 1
maximum: 255
hycon,noise-filter-enable:
type: boolean
description: Allows enabling power noise filter.
hycon,filter-data:
description: Allows setting how many samples throw before reporting touch
in the range from 0 to 5.
$ref: /schemas/types.yaml#/definitions/uint32
minimum: 0
maximum: 5
hycon,gain:
description: Allows setting the sensitivity distance in the range from 0 to 5.
$ref: /schemas/types.yaml#/definitions/uint32
minimum: 0
maximum: 5
hycon,edge-offset:
description: Allows setting the edge compensation in the range from 0 to 16.
$ref: /schemas/types.yaml#/definitions/uint32
minimum: 0
maximum: 16
touchscreen-size-x: true
touchscreen-size-y: true
touchscreen-fuzz-x: true
touchscreen-fuzz-y: true
touchscreen-inverted-x: true
touchscreen-inverted-y: true
touchscreen-swapped-x-y: true
interrupt-controller: true
additionalProperties: false
required:
- compatible
- reg
- interrupts
examples:
- |
#include <dt-bindings/gpio/gpio.h>
#include <dt-bindings/interrupt-controller/arm-gic.h>
i2c {
#address-cells = <1>;
#size-cells = <0>;
touchscreen@1c {
compatible = "hycon,hy4633";
reg = <0x1c>;
interrupt-parent = <&gpio2>;
interrupts = <5 IRQ_TYPE_EDGE_FALLING>;
reset-gpios = <&gpio2 6 GPIO_ACTIVE_LOW>;
};
};
...

View File

@ -0,0 +1,73 @@
# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
%YAML 1.2
---
$id: http://devicetree.org/schemas/input/touchscreen/ilitek_ts_i2c.yaml#
$schema: http://devicetree.org/meta-schemas/core.yaml#
title: Ilitek I2C Touchscreen Controller
maintainers:
- Dmitry Torokhov <dmitry.torokhov@gmail.com>
allOf:
- $ref: touchscreen.yaml#
properties:
compatible:
enum:
- ilitek,ili2130
- ilitek,ili2131
- ilitek,ili2132
- ilitek,ili2316
- ilitek,ili2322
- ilitek,ili2323
- ilitek,ili2326
- ilitek,ili2520
- ilitek,ili2521
reg:
const: 0x41
interrupts:
maxItems: 1
reset-gpios:
maxItems: 1
wakeup-source:
type: boolean
description: touchscreen can be used as a wakeup source.
touchscreen-size-x: true
touchscreen-size-y: true
touchscreen-inverted-x: true
touchscreen-inverted-y: true
touchscreen-swapped-x-y: true
additionalProperties: false
required:
- compatible
- reg
- interrupts
- reset-gpios
examples:
- |
#include <dt-bindings/interrupt-controller/irq.h>
#include <dt-bindings/gpio/gpio.h>
i2c {
#address-cells = <1>;
#size-cells = <0>;
touchscreen@41 {
compatible = "ilitek,ili2520";
reg = <0x41>;
interrupt-parent = <&gpio1>;
interrupts = <7 IRQ_TYPE_LEVEL_LOW>;
reset-gpios = <&gpio1 8 GPIO_ACTIVE_LOW>;
touchscreen-inverted-y;
wakeup-source;
};
};

View File

@ -1,80 +0,0 @@
Azoteq IQS550/572/525 Trackpad/Touchscreen Controller
Required properties:
- compatible : Must be equal to one of the following:
"azoteq,iqs550"
"azoteq,iqs572"
"azoteq,iqs525"
- reg : I2C slave address for the device.
- interrupts : GPIO to which the device's active-high RDY
output is connected (see [0]).
- reset-gpios : GPIO to which the device's active-low NRST
input is connected (see [1]).
Optional properties:
- touchscreen-min-x : See [2].
- touchscreen-min-y : See [2].
- touchscreen-size-x : See [2]. If this property is omitted, the
maximum x-coordinate is specified by the
device's "X Resolution" register.
- touchscreen-size-y : See [2]. If this property is omitted, the
maximum y-coordinate is specified by the
device's "Y Resolution" register.
- touchscreen-max-pressure : See [2]. Pressure is expressed as the sum of
the deltas across all channels impacted by a
touch event. A channel's delta is calculated
as its count value minus a reference, where
the count value is inversely proportional to
the channel's capacitance.
- touchscreen-fuzz-x : See [2].
- touchscreen-fuzz-y : See [2].
- touchscreen-fuzz-pressure : See [2].
- touchscreen-inverted-x : See [2]. Inversion is applied relative to that
which may already be specified by the device's
FLIP_X and FLIP_Y register fields.
- touchscreen-inverted-y : See [2]. Inversion is applied relative to that
which may already be specified by the device's
FLIP_X and FLIP_Y register fields.
- touchscreen-swapped-x-y : See [2]. Swapping is applied relative to that
which may already be specified by the device's
SWITCH_XY_AXIS register field.
[0]: Documentation/devicetree/bindings/interrupt-controller/interrupts.txt
[1]: Documentation/devicetree/bindings/gpio/gpio.txt
[2]: Documentation/devicetree/bindings/input/touchscreen/touchscreen.txt
Example:
&i2c1 {
/* ... */
touchscreen@74 {
compatible = "azoteq,iqs550";
reg = <0x74>;
interrupt-parent = <&gpio>;
interrupts = <17 4>;
reset-gpios = <&gpio 27 1>;
touchscreen-size-x = <640>;
touchscreen-size-y = <480>;
touchscreen-max-pressure = <16000>;
};
/* ... */
};

View File

@ -0,0 +1,87 @@
# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
%YAML 1.2
---
$id: http://devicetree.org/schemas/input/touchscreen/melfas,mms114.yaml#
$schema: http://devicetree.org/meta-schemas/core.yaml#
title: Melfas MMS114 family touchscreen controller bindings
maintainers:
- Linus Walleij <linus.walleij@linaro.org>
allOf:
- $ref: touchscreen.yaml#
properties:
$nodename:
pattern: "^touchscreen(@.*)?$"
compatible:
items:
- enum:
- melfas,mms114
- melfas,mms134s
- melfas,mms136
- melfas,mms152
- melfas,mms345l
reg:
description: I2C address
clock-frequency:
description: I2C client clock frequency, defined for host
minimum: 100000
maximum: 400000
interrupts:
maxItems: 1
avdd-supply:
description: Analog power supply regulator on AVDD pin
vdd-supply:
description: Digital power supply regulator on VDD pin
touchscreen-size-x: true
touchscreen-size-y: true
touchscreen-fuzz-x: true
touchscreen-fuzz-y: true
touchscreen-fuzz-pressure: true
touchscreen-inverted-x: true
touchscreen-inverted-y: true
touchscreen-swapped-x-y: true
touchscreen-max-pressure: true
additionalProperties: false
required:
- compatible
- reg
- interrupts
- touchscreen-size-x
- touchscreen-size-y
examples:
- |
#include <dt-bindings/interrupt-controller/irq.h>
i2c {
#address-cells = <1>;
#size-cells = <0>;
touchscreen@48 {
compatible = "melfas,mms114";
reg = <0x48>;
interrupt-parent = <&gpio>;
interrupts = <39 IRQ_TYPE_EDGE_FALLING>;
avdd-supply = <&ldo1_reg>;
vdd-supply = <&ldo2_reg>;
touchscreen-size-x = <720>;
touchscreen-size-y = <1280>;
touchscreen-fuzz-x = <10>;
touchscreen-fuzz-y = <10>;
touchscreen-fuzz-pressure = <10>;
touchscreen-inverted-x;
touchscreen-inverted-y;
};
};
...

View File

@ -1,42 +0,0 @@
* MELFAS MMS114/MMS152/MMS345L touchscreen controller
Required properties:
- compatible: should be one of:
- "melfas,mms114"
- "melfas,mms152"
- "melfas,mms345l"
- reg: I2C address of the chip
- interrupts: interrupt to which the chip is connected
- touchscreen-size-x: See [1]
- touchscreen-size-y: See [1]
Optional properties:
- touchscreen-fuzz-x: See [1]
- touchscreen-fuzz-y: See [1]
- touchscreen-fuzz-pressure: See [1]
- touchscreen-inverted-x: See [1]
- touchscreen-inverted-y: See [1]
- touchscreen-swapped-x-y: See [1]
[1]: Documentation/devicetree/bindings/input/touchscreen/touchscreen.txt
Example:
i2c@00000000 {
/* ... */
touchscreen@48 {
compatible = "melfas,mms114";
reg = <0x48>;
interrupts = <39 0>;
touchscreen-size-x = <720>;
touchscreen-size-y = <1280>;
touchscreen-fuzz-x = <10>;
touchscreen-fuzz-y = <10>;
touchscreen-fuzz-pressure = <10>;
touchscreen-inverted-x;
touchscreen-inverted-y;
};
/* ... */
};

View File

@ -0,0 +1,69 @@
# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
%YAML 1.2
---
$id: http://devicetree.org/schemas/input/touchscreen/mstar,msg2638.yaml#
$schema: http://devicetree.org/meta-schemas/core.yaml#
title: MStar msg2638 touchscreen controller Bindings
maintainers:
- Vincent Knecht <vincent.knecht@mailoo.org>
allOf:
- $ref: touchscreen.yaml#
properties:
compatible:
const: mstar,msg2638
reg:
const: 0x26
interrupts:
maxItems: 1
reset-gpios:
maxItems: 1
vdd-supply:
description: Power supply regulator for the chip
vddio-supply:
description: Power supply regulator for the I2C bus
touchscreen-size-x: true
touchscreen-size-y: true
additionalProperties: false
required:
- compatible
- reg
- interrupts
- reset-gpios
- touchscreen-size-x
- touchscreen-size-y
examples:
- |
#include <dt-bindings/gpio/gpio.h>
#include <dt-bindings/interrupt-controller/irq.h>
i2c {
#address-cells = <1>;
#size-cells = <0>;
touchscreen@26 {
compatible = "mstar,msg2638";
reg = <0x26>;
interrupt-parent = <&msmgpio>;
interrupts = <13 IRQ_TYPE_EDGE_FALLING>;
reset-gpios = <&msmgpio 100 GPIO_ACTIVE_LOW>;
pinctrl-names = "default";
pinctrl-0 = <&ts_int_reset_default>;
vdd-supply = <&pm8916_l17>;
vddio-supply = <&pm8916_l5>;
touchscreen-size-x = <2048>;
touchscreen-size-y = <2048>;
};
};
...

View File

@ -495,6 +495,8 @@ patternProperties:
description: Shenzhen Hugsun Technology Co. Ltd.
"^hwacom,.*":
description: HwaCom Systems Inc.
"^hycon,.*":
description: Hycon Technology Corp.
"^hydis,.*":
description: Hydis Technologies
"^hyundai,.*":

View File

@ -107,13 +107,17 @@ example below:
},
};
static const struct property_entry rotary_encoder_properties[] __initconst = {
static const struct property_entry rotary_encoder_properties[] = {
PROPERTY_ENTRY_U32("rotary-encoder,steps-per-period", 24),
PROPERTY_ENTRY_U32("linux,axis", ABS_X),
PROPERTY_ENTRY_U32("rotary-encoder,relative_axis", 0),
{ },
};
static const struct software_node rotary_encoder_node = {
.properties = rotary_encoder_properties,
};
static struct platform_device rotary_encoder_device = {
.name = "rotary-encoder",
.id = 0,
@ -122,7 +126,7 @@ example below:
...
gpiod_add_lookup_table(&rotary_encoder_gpios);
device_add_properties(&rotary_encoder_device, rotary_encoder_properties);
device_add_software_node(&rotary_encoder_device.dev, &rotary_encoder_node);
platform_device_register(&rotary_encoder_device);
...

View File

@ -8388,6 +8388,13 @@ S: Maintained
F: mm/hwpoison-inject.c
F: mm/memory-failure.c
HYCON HY46XX TOUCHSCREEN SUPPORT
M: Giulio Benetti <giulio.benetti@benettiengineering.com>
L: linux-input@vger.kernel.org
S: Maintained
F: Documentation/devicetree/bindings/input/touchscreen/hycon,hy46xx.yaml
F: drivers/input/touchscreen/hycon-hy46xx.c
HYGON PROCESSOR SUPPORT
M: Pu Wen <puwen@hygon.cn>
L: linux-kernel@vger.kernel.org

View File

@ -7,6 +7,7 @@
obj-$(CONFIG_INPUT) += input-core.o
input-core-y := input.o input-compat.o input-mt.o input-poller.o ff-core.o
input-core-y += touchscreen.o
obj-$(CONFIG_INPUT_FF_MEMLESS) += ff-memless.o
obj-$(CONFIG_INPUT_SPARSEKMAP) += sparse-keymap.o

View File

@ -268,6 +268,7 @@ static const struct xpad_device {
{ 0x1689, 0xfd00, "Razer Onza Tournament Edition", 0, XTYPE_XBOX360 },
{ 0x1689, 0xfd01, "Razer Onza Classic Edition", 0, XTYPE_XBOX360 },
{ 0x1689, 0xfe00, "Razer Sabertooth", 0, XTYPE_XBOX360 },
{ 0x1949, 0x041a, "Amazon Game Controller", 0, XTYPE_XBOX360 },
{ 0x1bad, 0x0002, "Harmonix Rock Band Guitar", 0, XTYPE_XBOX360 },
{ 0x1bad, 0x0003, "Harmonix Rock Band Drumkit", MAP_DPAD_TO_BUTTONS, XTYPE_XBOX360 },
{ 0x1bad, 0x0130, "Ion Drum Rocker", MAP_DPAD_TO_BUTTONS, XTYPE_XBOX360 },
@ -440,6 +441,7 @@ static const struct usb_device_id xpad_table[] = {
XPAD_XBOX360_VENDOR(0x15e4), /* Numark X-Box 360 controllers */
XPAD_XBOX360_VENDOR(0x162e), /* Joytech X-Box 360 controllers */
XPAD_XBOX360_VENDOR(0x1689), /* Razer Onza */
XPAD_XBOX360_VENDOR(0x1949), /* Amazon controllers */
XPAD_XBOX360_VENDOR(0x1bad), /* Harminix Rock Band Guitar and Drums */
XPAD_XBOX360_VENDOR(0x20d6), /* PowerA Controllers */
XPAD_XBOXONE_VENDOR(0x20d6), /* PowerA Controllers */

View File

@ -8,6 +8,7 @@
#include <linux/module.h>
#include <linux/hrtimer.h>
#include <linux/init.h>
#include <linux/fs.h>
#include <linux/interrupt.h>
@ -36,10 +37,11 @@ struct gpio_button_data {
unsigned short *code;
struct timer_list release_timer;
struct hrtimer release_timer;
unsigned int release_delay; /* in msecs, for IRQ-only buttons */
struct delayed_work work;
struct hrtimer debounce_timer;
unsigned int software_debounce; /* in msecs, for GPIO-driven buttons */
unsigned int irq;
@ -48,6 +50,7 @@ struct gpio_button_data {
bool disabled;
bool key_pressed;
bool suspended;
bool debounce_use_hrtimer;
};
struct gpio_keys_drvdata {
@ -122,6 +125,18 @@ static const unsigned long *get_bm_events_by_type(struct input_dev *dev,
return (type == EV_KEY) ? dev->keybit : dev->swbit;
}
static void gpio_keys_quiesce_key(void *data)
{
struct gpio_button_data *bdata = data;
if (!bdata->gpiod)
hrtimer_cancel(&bdata->release_timer);
if (bdata->debounce_use_hrtimer)
hrtimer_cancel(&bdata->debounce_timer);
else
cancel_delayed_work_sync(&bdata->work);
}
/**
* gpio_keys_disable_button() - disables given GPIO button
* @bdata: button data for button to be disabled
@ -142,12 +157,7 @@ static void gpio_keys_disable_button(struct gpio_button_data *bdata)
* Disable IRQ and associated timer/work structure.
*/
disable_irq(bdata->irq);
if (bdata->gpiod)
cancel_delayed_work_sync(&bdata->work);
else
del_timer_sync(&bdata->release_timer);
gpio_keys_quiesce_key(bdata);
bdata->disabled = true;
}
}
@ -360,7 +370,9 @@ static void gpio_keys_gpio_report_event(struct gpio_button_data *bdata)
unsigned int type = button->type ?: EV_KEY;
int state;
state = gpiod_get_value_cansleep(bdata->gpiod);
state = bdata->debounce_use_hrtimer ?
gpiod_get_value(bdata->gpiod) :
gpiod_get_value_cansleep(bdata->gpiod);
if (state < 0) {
dev_err(input->dev.parent,
"failed to get gpio state: %d\n", state);
@ -373,7 +385,15 @@ static void gpio_keys_gpio_report_event(struct gpio_button_data *bdata)
} else {
input_event(input, type, *bdata->code, state);
}
input_sync(input);
}
static void gpio_keys_debounce_event(struct gpio_button_data *bdata)
{
gpio_keys_gpio_report_event(bdata);
input_sync(bdata->input);
if (bdata->button->wakeup)
pm_relax(bdata->input->dev.parent);
}
static void gpio_keys_gpio_work_func(struct work_struct *work)
@ -381,10 +401,17 @@ static void gpio_keys_gpio_work_func(struct work_struct *work)
struct gpio_button_data *bdata =
container_of(work, struct gpio_button_data, work.work);
gpio_keys_gpio_report_event(bdata);
gpio_keys_debounce_event(bdata);
}
if (bdata->button->wakeup)
pm_relax(bdata->input->dev.parent);
static enum hrtimer_restart gpio_keys_debounce_timer(struct hrtimer *t)
{
struct gpio_button_data *bdata =
container_of(t, struct gpio_button_data, debounce_timer);
gpio_keys_debounce_event(bdata);
return HRTIMER_NORESTART;
}
static irqreturn_t gpio_keys_gpio_isr(int irq, void *dev_id)
@ -408,26 +435,33 @@ static irqreturn_t gpio_keys_gpio_isr(int irq, void *dev_id)
}
}
mod_delayed_work(system_wq,
&bdata->work,
msecs_to_jiffies(bdata->software_debounce));
if (bdata->debounce_use_hrtimer) {
hrtimer_start(&bdata->debounce_timer,
ms_to_ktime(bdata->software_debounce),
HRTIMER_MODE_REL);
} else {
mod_delayed_work(system_wq,
&bdata->work,
msecs_to_jiffies(bdata->software_debounce));
}
return IRQ_HANDLED;
}
static void gpio_keys_irq_timer(struct timer_list *t)
static enum hrtimer_restart gpio_keys_irq_timer(struct hrtimer *t)
{
struct gpio_button_data *bdata = from_timer(bdata, t, release_timer);
struct gpio_button_data *bdata = container_of(t,
struct gpio_button_data,
release_timer);
struct input_dev *input = bdata->input;
unsigned long flags;
spin_lock_irqsave(&bdata->lock, flags);
if (bdata->key_pressed) {
input_event(input, EV_KEY, *bdata->code, 0);
input_sync(input);
bdata->key_pressed = false;
}
spin_unlock_irqrestore(&bdata->lock, flags);
return HRTIMER_NORESTART;
}
static irqreturn_t gpio_keys_irq_isr(int irq, void *dev_id)
@ -457,23 +491,14 @@ static irqreturn_t gpio_keys_irq_isr(int irq, void *dev_id)
}
if (bdata->release_delay)
mod_timer(&bdata->release_timer,
jiffies + msecs_to_jiffies(bdata->release_delay));
hrtimer_start(&bdata->release_timer,
ms_to_ktime(bdata->release_delay),
HRTIMER_MODE_REL_HARD);
out:
spin_unlock_irqrestore(&bdata->lock, flags);
return IRQ_HANDLED;
}
static void gpio_keys_quiesce_key(void *data)
{
struct gpio_button_data *bdata = data;
if (bdata->gpiod)
cancel_delayed_work_sync(&bdata->work);
else
del_timer_sync(&bdata->release_timer);
}
static int gpio_keys_setup_key(struct platform_device *pdev,
struct input_dev *input,
struct gpio_keys_drvdata *ddata,
@ -543,6 +568,14 @@ static int gpio_keys_setup_key(struct platform_device *pdev,
if (error < 0)
bdata->software_debounce =
button->debounce_interval;
/*
* If reading the GPIO won't sleep, we can use a
* hrtimer instead of a standard timer for the software
* debounce, to reduce the latency as much as possible.
*/
bdata->debounce_use_hrtimer =
!gpiod_cansleep(bdata->gpiod);
}
if (button->irq) {
@ -561,6 +594,10 @@ static int gpio_keys_setup_key(struct platform_device *pdev,
INIT_DELAYED_WORK(&bdata->work, gpio_keys_gpio_work_func);
hrtimer_init(&bdata->debounce_timer,
CLOCK_REALTIME, HRTIMER_MODE_REL);
bdata->debounce_timer.function = gpio_keys_debounce_timer;
isr = gpio_keys_gpio_isr;
irqflags = IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING;
@ -595,7 +632,9 @@ static int gpio_keys_setup_key(struct platform_device *pdev,
}
bdata->release_delay = button->debounce_interval;
timer_setup(&bdata->release_timer, gpio_keys_irq_timer, 0);
hrtimer_init(&bdata->release_timer,
CLOCK_REALTIME, HRTIMER_MODE_REL_HARD);
bdata->release_timer.function = gpio_keys_irq_timer;
isr = gpio_keys_irq_isr;
irqflags = 0;

View File

@ -408,27 +408,18 @@ open_err:
return -EIO;
}
#ifdef CONFIG_OF
static const struct of_device_id imx_keypad_of_match[] = {
{ .compatible = "fsl,imx21-kpp", },
{ /* sentinel */ }
};
MODULE_DEVICE_TABLE(of, imx_keypad_of_match);
#endif
static int imx_keypad_probe(struct platform_device *pdev)
{
const struct matrix_keymap_data *keymap_data =
dev_get_platdata(&pdev->dev);
struct imx_keypad *keypad;
struct input_dev *input_dev;
int irq, error, i, row, col;
if (!keymap_data && !pdev->dev.of_node) {
dev_err(&pdev->dev, "no keymap defined\n");
return -EINVAL;
}
irq = platform_get_irq(pdev, 0);
if (irq < 0)
return irq;
@ -469,7 +460,7 @@ static int imx_keypad_probe(struct platform_device *pdev)
input_dev->open = imx_keypad_open;
input_dev->close = imx_keypad_close;
error = matrix_keypad_build_keymap(keymap_data, NULL,
error = matrix_keypad_build_keymap(NULL, NULL,
MAX_MATRIX_KEY_ROWS,
MAX_MATRIX_KEY_COLS,
keypad->keycodes, input_dev);
@ -582,7 +573,7 @@ static struct platform_driver imx_keypad_driver = {
.driver = {
.name = "imx-keypad",
.pm = &imx_kbd_pm_ops,
.of_match_table = of_match_ptr(imx_keypad_of_match),
.of_match_table = imx_keypad_of_match,
},
.probe = imx_keypad_probe,
};

View File

@ -274,7 +274,7 @@ static int tca6416_keypad_probe(struct i2c_client *client,
error = request_threaded_irq(chip->irqnum, NULL,
tca6416_keys_isr,
IRQF_TRIGGER_FALLING |
IRQF_ONESHOT,
IRQF_ONESHOT | IRQF_NO_AUTOEN,
"tca6416-keypad", chip);
if (error) {
dev_dbg(&client->dev,
@ -282,7 +282,6 @@ static int tca6416_keypad_probe(struct i2c_client *client,
chip->irqnum, error);
goto fail1;
}
disable_irq(chip->irqnum);
}
error = input_register_device(input);

View File

@ -694,14 +694,13 @@ static int tegra_kbc_probe(struct platform_device *pdev)
input_set_drvdata(kbc->idev, kbc);
err = devm_request_irq(&pdev->dev, kbc->irq, tegra_kbc_isr,
IRQF_TRIGGER_HIGH, pdev->name, kbc);
IRQF_TRIGGER_HIGH | IRQF_NO_AUTOEN,
pdev->name, kbc);
if (err) {
dev_err(&pdev->dev, "failed to request keyboard IRQ\n");
return err;
}
disable_irq(kbc->irq);
err = input_register_device(kbc->idev);
if (err) {
dev_err(&pdev->dev, "failed to register input device\n");

View File

@ -763,6 +763,17 @@ config INPUT_IQS269A
To compile this driver as a module, choose M here: the
module will be called iqs269a.
config INPUT_IQS626A
tristate "Azoteq IQS626A capacitive touch controller"
depends on I2C
select REGMAP_I2C
help
Say Y to enable support for the Azoteq IQS626A capacitive
touch controller.
To compile this driver as a module, choose M here: the
module will be called iqs626a.
config INPUT_CMA3000
tristate "VTI CMA3000 Tri-axis accelerometer"
help

View File

@ -43,6 +43,7 @@ obj-$(CONFIG_INPUT_HISI_POWERKEY) += hisi_powerkey.o
obj-$(CONFIG_HP_SDC_RTC) += hp_sdc_rtc.o
obj-$(CONFIG_INPUT_IMS_PCU) += ims-pcu.o
obj-$(CONFIG_INPUT_IQS269A) += iqs269a.o
obj-$(CONFIG_INPUT_IQS626A) += iqs626a.o
obj-$(CONFIG_INPUT_IXP4XX_BEEPER) += ixp4xx-beeper.o
obj-$(CONFIG_INPUT_KEYSPAN_REMOTE) += keyspan_remote.o
obj-$(CONFIG_INPUT_KXTJ9) += kxtj9.o

View File

@ -2018,7 +2018,6 @@ static int ims_pcu_probe(struct usb_interface *intf,
}
usb_set_intfdata(pcu->ctrl_intf, pcu);
usb_set_intfdata(pcu->data_intf, pcu);
error = ims_pcu_buffers_alloc(pcu);
if (error)

1838
drivers/input/misc/iqs626a.c Normal file

File diff suppressed because it is too large Load Diff

View File

@ -61,15 +61,10 @@ static int max8997_haptic_set_duty_cycle(struct max8997_haptic *chip)
unsigned int duty = chip->pwm_period * chip->level / 100;
ret = pwm_config(chip->pwm, duty, chip->pwm_period);
} else {
int i;
u8 duty_index = 0;
for (i = 0; i <= 64; i++) {
if (chip->level <= i * 100 / 64) {
duty_index = i;
break;
}
}
duty_index = DIV_ROUND_UP(chip->level * 64, 100);
switch (chip->internal_mode_pattern) {
case 0:
max8997_write_reg(chip->client,

View File

@ -55,6 +55,11 @@
#define ETP_FW_PAGE_SIZE_512 512
#define ETP_FW_SIGNATURE_SIZE 6
#define ETP_PRODUCT_ID_DELBIN 0x00C2
#define ETP_PRODUCT_ID_VOXEL 0x00BF
#define ETP_PRODUCT_ID_MAGPIE 0x0120
#define ETP_PRODUCT_ID_BOBBA 0x0121
struct i2c_client;
struct completion;
@ -73,7 +78,7 @@ struct elan_transport_ops {
int (*calibrate_result)(struct i2c_client *client, u8 *val);
int (*get_baseline_data)(struct i2c_client *client,
bool max_baseliune, u8 *value);
bool max_baseline, u8 *value);
int (*get_version)(struct i2c_client *client, u8 pattern, bool iap,
u8 *version);

View File

@ -46,6 +46,9 @@
#define ETP_FINGER_WIDTH 15
#define ETP_RETRY_COUNT 3
/* quirks to control the device */
#define ETP_QUIRK_QUICK_WAKEUP BIT(0)
/* The main device structure */
struct elan_tp_data {
struct i2c_client *client;
@ -90,8 +93,38 @@ struct elan_tp_data {
bool baseline_ready;
u8 clickpad;
bool middle_button;
u32 quirks; /* Various quirks */
};
static u32 elan_i2c_lookup_quirks(u16 ic_type, u16 product_id)
{
static const struct {
u16 ic_type;
u16 product_id;
u32 quirks;
} elan_i2c_quirks[] = {
{ 0x0D, ETP_PRODUCT_ID_DELBIN, ETP_QUIRK_QUICK_WAKEUP },
{ 0x10, ETP_PRODUCT_ID_VOXEL, ETP_QUIRK_QUICK_WAKEUP },
{ 0x14, ETP_PRODUCT_ID_MAGPIE, ETP_QUIRK_QUICK_WAKEUP },
{ 0x14, ETP_PRODUCT_ID_BOBBA, ETP_QUIRK_QUICK_WAKEUP },
};
u32 quirks = 0;
int i;
for (i = 0; i < ARRAY_SIZE(elan_i2c_quirks); i++) {
if (elan_i2c_quirks[i].ic_type == ic_type &&
elan_i2c_quirks[i].product_id == product_id) {
quirks = elan_i2c_quirks[i].quirks;
}
}
if (ic_type >= 0x0D && product_id >= 0x123)
quirks |= ETP_QUIRK_QUICK_WAKEUP;
return quirks;
}
static int elan_get_fwinfo(u16 ic_type, u8 iap_version, u16 *validpage_count,
u32 *signature_address, u16 *page_size)
{
@ -258,16 +291,18 @@ static int elan_check_ASUS_special_fw(struct elan_tp_data *data)
return false;
}
static int __elan_initialize(struct elan_tp_data *data)
static int __elan_initialize(struct elan_tp_data *data, bool skip_reset)
{
struct i2c_client *client = data->client;
bool woken_up = false;
int error;
error = data->ops->initialize(client);
if (error) {
dev_err(&client->dev, "device initialize failed: %d\n", error);
return error;
if (!skip_reset) {
error = data->ops->initialize(client);
if (error) {
dev_err(&client->dev, "device initialize failed: %d\n", error);
return error;
}
}
error = elan_query_product(data);
@ -311,16 +346,17 @@ static int __elan_initialize(struct elan_tp_data *data)
return 0;
}
static int elan_initialize(struct elan_tp_data *data)
static int elan_initialize(struct elan_tp_data *data, bool skip_reset)
{
int repeat = ETP_RETRY_COUNT;
int error;
do {
error = __elan_initialize(data);
error = __elan_initialize(data, skip_reset);
if (!error)
return 0;
skip_reset = false;
msleep(30);
} while (--repeat > 0);
@ -357,6 +393,8 @@ static int elan_query_device_info(struct elan_tp_data *data)
if (error)
return error;
data->quirks = elan_i2c_lookup_quirks(data->ic_type, data->product_id);
error = elan_get_fwinfo(data->ic_type, data->iap_version,
&data->fw_validpage_count,
&data->fw_signature_address,
@ -546,7 +584,7 @@ static int elan_update_firmware(struct elan_tp_data *data,
data->ops->iap_reset(client);
} else {
/* Reinitialize TP after fw is updated */
elan_initialize(data);
elan_initialize(data, false);
elan_query_device_info(data);
}
@ -1247,7 +1285,7 @@ static int elan_probe(struct i2c_client *client,
}
/* Initialize the touchpad. */
error = elan_initialize(data);
error = elan_initialize(data, false);
if (error)
return error;
@ -1384,7 +1422,7 @@ static int __maybe_unused elan_resume(struct device *dev)
goto err;
}
error = elan_initialize(data);
error = elan_initialize(data, data->quirks & ETP_QUIRK_QUICK_WAKEUP);
if (error)
dev_err(dev, "initialize when resuming failed: %d\n", error);

View File

@ -103,7 +103,6 @@ static int apbps2_open(struct serio *io)
{
struct apbps2_priv *priv = io->port_data;
int limit;
unsigned long tmp;
/* clear error flags */
iowrite32be(0, &priv->regs->status);
@ -111,7 +110,7 @@ static int apbps2_open(struct serio *io)
/* Clear old data if available (unlikely) */
limit = 1024;
while ((ioread32be(&priv->regs->status) & APBPS2_STATUS_DR) && --limit)
tmp = ioread32be(&priv->regs->data);
ioread32be(&priv->regs->data);
/* Enable reciever and it's interrupt */
iowrite32be(APBPS2_CTRL_RE | APBPS2_CTRL_RI, &priv->regs->ctrl);

View File

@ -1,6 +1,7 @@
// SPDX-License-Identifier: GPL-2.0-only
/*
* Generic DT helper functions for touchscreen devices
* Generic helper functions for touchscreens and other two-dimensional
* pointing devices
*
* Copyright (c) 2014 Sebastian Reichel <sre@kernel.org>
*/
@ -37,7 +38,7 @@ static void touchscreen_set_params(struct input_dev *dev,
if (!test_bit(axis, dev->absbit)) {
dev_warn(&dev->dev,
"DT specifies parameters but the axis %lu is not set up\n",
"Parameters are specified but the axis %lu is not set up\n",
axis);
return;
}
@ -49,7 +50,7 @@ static void touchscreen_set_params(struct input_dev *dev,
}
/**
* touchscreen_parse_properties - parse common touchscreen DT properties
* touchscreen_parse_properties - parse common touchscreen properties
* @input: input device that should be parsed
* @multitouch: specifies whether parsed properties should be applied to
* single-touch or multi-touch axes
@ -57,9 +58,9 @@ static void touchscreen_set_params(struct input_dev *dev,
* axis swap and invert info for use with touchscreen_report_x_y();
* or %NULL
*
* This function parses common DT properties for touchscreens and setups the
* This function parses common properties for touchscreens and sets up the
* input device accordingly. The function keeps previously set up default
* values if no value is specified via DT.
* values if no value is specified.
*/
void touchscreen_parse_properties(struct input_dev *input, bool multitouch,
struct touchscreen_properties *prop)
@ -203,4 +204,4 @@ void touchscreen_report_pos(struct input_dev *input,
EXPORT_SYMBOL(touchscreen_report_pos);
MODULE_LICENSE("GPL v2");
MODULE_DESCRIPTION("Device-tree helpers functions for touchscreen devices");
MODULE_DESCRIPTION("Helper functions for touchscreens and other devices");

View File

@ -12,10 +12,6 @@ menuconfig INPUT_TOUCHSCREEN
if INPUT_TOUCHSCREEN
config TOUCHSCREEN_PROPERTIES
def_tristate INPUT
depends on INPUT
config TOUCHSCREEN_88PM860X
tristate "Marvell 88PM860x touchscreen"
depends on MFD_88PM860X
@ -415,6 +411,17 @@ config TOUCHSCREEN_HIDEEP
To compile this driver as a module, choose M here : the
module will be called hideep_ts.
config TOUCHSCREEN_HYCON_HY46XX
tristate "Hycon hy46xx touchscreen support"
depends on I2C
help
Say Y here if you have a touchscreen using Hycon hy46xx
If unsure, say N.
To compile this driver as a module, choose M here: the
module will be called hycon-hy46xx.
config TOUCHSCREEN_ILI210X
tristate "Ilitek ILI210X based touchscreen"
depends on I2C
@ -430,6 +437,18 @@ config TOUCHSCREEN_ILI210X
To compile this driver as a module, choose M here: the
module will be called ili210x.
config TOUCHSCREEN_ILITEK
tristate "Ilitek I2C 213X/23XX/25XX/Lego Series Touch ICs"
depends on I2C
help
Say Y here if you have touchscreen with ILITEK touch IC,
it supports 213X/23XX/25XX and other Lego series.
If unsure, say N.
To compile this driver as a module, choose M here: the
module will be called ilitek_ts_i2c.
config TOUCHSCREEN_IPROC
tristate "IPROC touch panel driver support"
depends on ARCH_BCM_IPROC || COMPILE_TEST
@ -594,6 +613,18 @@ config TOUCHSCREEN_MELFAS_MIP4
To compile this driver as a module, choose M here:
the module will be called melfas_mip4.
config TOUCHSCREEN_MSG2638
tristate "MStar msg2638 touchscreen support"
depends on I2C
depends on GPIOLIB || COMPILE_TEST
help
Say Y here if you have an I2C touchscreen using MStar msg2638.
If unsure, say N.
To compile this driver as a module, choose M here: the
module will be called msg2638.
config TOUCHSCREEN_MTOUCH
tristate "MicroTouch serial touchscreens"
select SERIO

View File

@ -7,7 +7,6 @@
wm97xx-ts-y := wm97xx-core.o
obj-$(CONFIG_TOUCHSCREEN_PROPERTIES) += of_touchscreen.o
obj-$(CONFIG_TOUCHSCREEN_88PM860X) += 88pm860x-ts.o
obj-$(CONFIG_TOUCHSCREEN_AD7877) += ad7877.o
obj-$(CONFIG_TOUCHSCREEN_AD7879) += ad7879.o
@ -35,6 +34,7 @@ obj-$(CONFIG_TOUCHSCREEN_DA9052) += da9052_tsi.o
obj-$(CONFIG_TOUCHSCREEN_DYNAPRO) += dynapro.o
obj-$(CONFIG_TOUCHSCREEN_EDT_FT5X06) += edt-ft5x06.o
obj-$(CONFIG_TOUCHSCREEN_HAMPSHIRE) += hampshire.o
obj-$(CONFIG_TOUCHSCREEN_HYCON_HY46XX) += hycon-hy46xx.o
obj-$(CONFIG_TOUCHSCREEN_GUNZE) += gunze.o
obj-$(CONFIG_TOUCHSCREEN_EETI) += eeti_ts.o
obj-$(CONFIG_TOUCHSCREEN_EKTF2127) += ektf2127.o
@ -47,6 +47,7 @@ obj-$(CONFIG_TOUCHSCREEN_FUJITSU) += fujitsu_ts.o
obj-$(CONFIG_TOUCHSCREEN_GOODIX) += goodix.o
obj-$(CONFIG_TOUCHSCREEN_HIDEEP) += hideep.o
obj-$(CONFIG_TOUCHSCREEN_ILI210X) += ili210x.o
obj-$(CONFIG_TOUCHSCREEN_ILITEK) += ilitek_ts_i2c.o
obj-$(CONFIG_TOUCHSCREEN_IMX6UL_TSC) += imx6ul_tsc.o
obj-$(CONFIG_TOUCHSCREEN_INEXIO) += inexio.o
obj-$(CONFIG_TOUCHSCREEN_IPROC) += bcm_iproc_tsc.o
@ -59,6 +60,7 @@ obj-$(CONFIG_TOUCHSCREEN_MCS5000) += mcs5000_ts.o
obj-$(CONFIG_TOUCHSCREEN_MELFAS_MIP4) += melfas_mip4.o
obj-$(CONFIG_TOUCHSCREEN_MIGOR) += migor_ts.o
obj-$(CONFIG_TOUCHSCREEN_MMS114) += mms114.o
obj-$(CONFIG_TOUCHSCREEN_MSG2638) += msg2638.o
obj-$(CONFIG_TOUCHSCREEN_MTOUCH) += mtouch.o
obj-$(CONFIG_TOUCHSCREEN_MK712) += mk712.o
obj-$(CONFIG_TOUCHSCREEN_HP600) += hp680_ts_input.o

View File

@ -125,7 +125,7 @@ static int ar1021_i2c_probe(struct i2c_client *client,
error = devm_request_threaded_irq(&client->dev, client->irq,
NULL, ar1021_i2c_irq,
IRQF_ONESHOT,
IRQF_ONESHOT | IRQF_NO_AUTOEN,
"ar1021_i2c", ar1021);
if (error) {
dev_err(&client->dev,
@ -133,9 +133,6 @@ static int ar1021_i2c_probe(struct i2c_client *client,
return error;
}
/* Disable the IRQ, we'll enable it in ar1021_i2c_open() */
disable_irq(client->irq);
error = input_register_device(ar1021->input);
if (error) {
dev_err(&client->dev,

View File

@ -31,6 +31,7 @@
#include <media/v4l2-ioctl.h>
#include <media/videobuf2-v4l2.h>
#include <media/videobuf2-vmalloc.h>
#include <dt-bindings/input/atmel-maxtouch.h>
/* Firmware files */
#define MXT_FW_NAME "maxtouch.fw"
@ -199,6 +200,7 @@ enum t100_type {
#define MXT_CRC_TIMEOUT 1000 /* msec */
#define MXT_FW_RESET_TIME 3000 /* msec */
#define MXT_FW_CHG_TIMEOUT 300 /* msec */
#define MXT_WAKEUP_TIME 25 /* msec */
/* Command to unlock bootloader */
#define MXT_UNLOCK_CMD_MSB 0xaa
@ -312,6 +314,7 @@ struct mxt_data {
struct mxt_dbg dbg;
struct regulator_bulk_data regulators[2];
struct gpio_desc *reset_gpio;
struct gpio_desc *wake_gpio;
bool use_retrigen_workaround;
/* Cached parameters from object table */
@ -342,6 +345,8 @@ struct mxt_data {
unsigned int t19_num_keys;
enum mxt_suspend_mode suspend_mode;
u32 wakeup_method;
};
struct mxt_vb2_buffer {
@ -621,10 +626,42 @@ static int mxt_send_bootloader_cmd(struct mxt_data *data, bool unlock)
return mxt_bootloader_write(data, buf, sizeof(buf));
}
static bool mxt_wakeup_toggle(struct i2c_client *client,
bool wake_up, bool in_i2c)
{
struct mxt_data *data = i2c_get_clientdata(client);
switch (data->wakeup_method) {
case ATMEL_MXT_WAKEUP_I2C_SCL:
if (!in_i2c)
return false;
break;
case ATMEL_MXT_WAKEUP_GPIO:
if (in_i2c)
return false;
gpiod_set_value(data->wake_gpio, wake_up);
break;
default:
return false;
}
if (wake_up) {
dev_dbg(&client->dev, "waking up controller\n");
msleep(MXT_WAKEUP_TIME);
}
return true;
}
static int __mxt_read_reg(struct i2c_client *client,
u16 reg, u16 len, void *val)
{
struct i2c_msg xfer[2];
bool retried = false;
u8 buf[2];
int ret;
@ -643,9 +680,13 @@ static int __mxt_read_reg(struct i2c_client *client,
xfer[1].len = len;
xfer[1].buf = val;
retry:
ret = i2c_transfer(client->adapter, xfer, 2);
if (ret == 2) {
ret = 0;
} else if (!retried && mxt_wakeup_toggle(client, true, true)) {
retried = true;
goto retry;
} else {
if (ret >= 0)
ret = -EIO;
@ -659,6 +700,7 @@ static int __mxt_read_reg(struct i2c_client *client,
static int __mxt_write_reg(struct i2c_client *client, u16 reg, u16 len,
const void *val)
{
bool retried = false;
u8 *buf;
size_t count;
int ret;
@ -672,9 +714,13 @@ static int __mxt_write_reg(struct i2c_client *client, u16 reg, u16 len,
buf[1] = (reg >> 8) & 0xff;
memcpy(&buf[2], val, len);
retry:
ret = i2c_master_send(client, buf, count);
if (ret == count) {
ret = 0;
} else if (!retried && mxt_wakeup_toggle(client, true, true)) {
retried = true;
goto retry;
} else {
if (ret >= 0)
ret = -EIO;
@ -2975,6 +3021,8 @@ static const struct attribute_group mxt_attr_group = {
static void mxt_start(struct mxt_data *data)
{
mxt_wakeup_toggle(data->client, true, false);
switch (data->suspend_mode) {
case MXT_SUSPEND_T9_CTRL:
mxt_soft_reset(data);
@ -3009,6 +3057,8 @@ static void mxt_stop(struct mxt_data *data)
mxt_set_t7_power_cfg(data, MXT_POWER_CFG_DEEPSLEEP);
break;
}
mxt_wakeup_toggle(data->client, false, false);
}
static int mxt_input_open(struct input_dev *dev)
@ -3155,16 +3205,24 @@ static int mxt_probe(struct i2c_client *client, const struct i2c_device_id *id)
return error;
}
/* Request the WAKE line as asserted so we go out of sleep */
data->wake_gpio = devm_gpiod_get_optional(&client->dev,
"wake", GPIOD_OUT_HIGH);
if (IS_ERR(data->wake_gpio)) {
error = PTR_ERR(data->wake_gpio);
dev_err(&client->dev, "Failed to get wake gpio: %d\n", error);
return error;
}
error = devm_request_threaded_irq(&client->dev, client->irq,
NULL, mxt_interrupt, IRQF_ONESHOT,
NULL, mxt_interrupt,
IRQF_ONESHOT | IRQF_NO_AUTOEN,
client->name, data);
if (error) {
dev_err(&client->dev, "Failed to register interrupt\n");
return error;
}
disable_irq(client->irq);
error = regulator_bulk_enable(ARRAY_SIZE(data->regulators),
data->regulators);
if (error) {
@ -3185,6 +3243,25 @@ static int mxt_probe(struct i2c_client *client, const struct i2c_device_id *id)
msleep(MXT_RESET_INVALID_CHG);
}
/*
* Controllers like mXT1386 have a dedicated WAKE line that could be
* connected to a GPIO or to I2C SCL pin, or permanently asserted low.
*
* This WAKE line is used for waking controller from a deep-sleep and
* it needs to be asserted low for 25 milliseconds before I2C transfers
* could be accepted by controller if it was in a deep-sleep mode.
* Controller will go into sleep automatically after 2 seconds of
* inactivity if WAKE line is deasserted and deep sleep is activated.
*
* If WAKE line is connected to I2C SCL pin, then the first I2C transfer
* will get an instant NAK and transfer needs to be retried after 25ms.
*
* If WAKE line is connected to a GPIO line, the line must be asserted
* 25ms before the host attempts to communicate with the controller.
*/
device_property_read_u32(&client->dev, "atmel,wakeup-method",
&data->wakeup_method);
error = mxt_initialize(data);
if (error)
goto err_disable_regulators;

View File

@ -401,10 +401,10 @@ static int bu21029_probe(struct i2c_client *client,
input_set_drvdata(in_dev, bu21029);
irq_set_status_flags(client->irq, IRQ_NOAUTOEN);
error = devm_request_threaded_irq(&client->dev, client->irq,
NULL, bu21029_touch_soft_irq,
IRQF_ONESHOT, DRIVER_NAME, bu21029);
IRQF_ONESHOT | IRQF_NO_AUTOEN,
DRIVER_NAME, bu21029);
if (error) {
dev_err(&client->dev,
"unable to request touch irq: %d\n", error);

View File

@ -229,16 +229,21 @@ static int cyttsp_set_sysinfo_regs(struct cyttsp *ts)
static void cyttsp_hard_reset(struct cyttsp *ts)
{
if (ts->reset_gpio) {
/*
* According to the CY8CTMA340 datasheet page 21, the external
* reset pulse width should be >= 1 ms. The datasheet does not
* specify how long we have to wait after reset but a vendor
* tree specifies 5 ms here.
*/
gpiod_set_value_cansleep(ts->reset_gpio, 1);
msleep(CY_DELAY_DFLT);
usleep_range(1000, 2000);
gpiod_set_value_cansleep(ts->reset_gpio, 0);
msleep(CY_DELAY_DFLT);
usleep_range(5000, 6000);
}
}
static int cyttsp_soft_reset(struct cyttsp *ts)
{
unsigned long timeout;
int retval;
/* wait for interrupt to set ready completion */
@ -248,12 +253,16 @@ static int cyttsp_soft_reset(struct cyttsp *ts)
enable_irq(ts->irq);
retval = ttsp_send_command(ts, CY_SOFT_RESET_MODE);
if (retval)
if (retval) {
dev_err(ts->dev, "failed to send soft reset\n");
goto out;
}
timeout = wait_for_completion_timeout(&ts->bl_ready,
msecs_to_jiffies(CY_DELAY_DFLT * CY_DELAY_MAX));
retval = timeout ? 0 : -EIO;
if (!wait_for_completion_timeout(&ts->bl_ready,
msecs_to_jiffies(CY_DELAY_DFLT * CY_DELAY_MAX))) {
dev_err(ts->dev, "timeout waiting for soft reset\n");
retval = -EIO;
}
out:
ts->state = CY_IDLE_STATE;
@ -405,8 +414,10 @@ static int cyttsp_power_on(struct cyttsp *ts)
if (GET_BOOTLOADERMODE(ts->bl_data.bl_status) &&
IS_VALID_APP(ts->bl_data.bl_status)) {
error = cyttsp_exit_bl_mode(ts);
if (error)
if (error) {
dev_err(ts->dev, "failed to exit bootloader mode\n");
return error;
}
}
if (GET_HSTMODE(ts->bl_data.bl_file) != CY_OPERATE_MODE ||
@ -629,10 +640,8 @@ struct cyttsp *cyttsp_probe(const struct cyttsp_bus_ops *bus_ops,
return ERR_PTR(error);
init_completion(&ts->bl_ready);
snprintf(ts->phys, sizeof(ts->phys), "%s/input0", dev_name(dev));
input_dev->name = "Cypress TTSP TouchScreen";
input_dev->phys = ts->phys;
input_dev->id.bustype = bus_ops->bustype;
input_dev->dev.parent = ts->dev;
@ -643,16 +652,20 @@ struct cyttsp *cyttsp_probe(const struct cyttsp_bus_ops *bus_ops,
input_set_capability(input_dev, EV_ABS, ABS_MT_POSITION_X);
input_set_capability(input_dev, EV_ABS, ABS_MT_POSITION_Y);
/* One byte for width 0..255 so this is the limit */
input_set_abs_params(input_dev, ABS_MT_TOUCH_MAJOR, 0, 255, 0, 0);
touchscreen_parse_properties(input_dev, true, NULL);
error = input_mt_init_slots(input_dev, CY_MAX_ID, 0);
error = input_mt_init_slots(input_dev, CY_MAX_ID, INPUT_MT_DIRECT);
if (error) {
dev_err(dev, "Unable to init MT slots.\n");
return ERR_PTR(error);
}
error = devm_request_threaded_irq(dev, ts->irq, NULL, cyttsp_irq,
IRQF_TRIGGER_FALLING | IRQF_ONESHOT,
IRQF_TRIGGER_FALLING | IRQF_ONESHOT |
IRQF_NO_AUTOEN,
"cyttsp", ts);
if (error) {
dev_err(ts->dev, "failed to request IRQ %d, err: %d\n",
@ -660,8 +673,6 @@ struct cyttsp *cyttsp_probe(const struct cyttsp_bus_ops *bus_ops,
return ERR_PTR(error);
}
disable_irq(ts->irq);
cyttsp_hard_reset(ts);
error = cyttsp_power_on(ts);

View File

@ -114,7 +114,6 @@ struct cyttsp {
struct device *dev;
int irq;
struct input_dev *input;
char phys[32];
const struct cyttsp_bus_ops *bus_ops;
struct cyttsp_bootloader_data bl_data;
struct cyttsp_sysinfo_data sysinfo_data;

View File

@ -38,6 +38,7 @@
#include <linux/of.h>
#include <linux/gpio/consumer.h>
#include <linux/regulator/consumer.h>
#include <linux/uuid.h>
#include <asm/unaligned.h>
/* Device, Driver information */
@ -1334,6 +1335,40 @@ static void elants_i2c_power_off(void *_data)
}
}
#ifdef CONFIG_ACPI
static const struct acpi_device_id i2c_hid_ids[] = {
{"ACPI0C50", 0 },
{"PNP0C50", 0 },
{ },
};
static const guid_t i2c_hid_guid =
GUID_INIT(0x3CDFF6F7, 0x4267, 0x4555,
0xAD, 0x05, 0xB3, 0x0A, 0x3D, 0x89, 0x38, 0xDE);
static bool elants_acpi_is_hid_device(struct device *dev)
{
acpi_handle handle = ACPI_HANDLE(dev);
union acpi_object *obj;
if (acpi_match_device_ids(ACPI_COMPANION(dev), i2c_hid_ids))
return false;
obj = acpi_evaluate_dsm_typed(handle, &i2c_hid_guid, 1, 1, NULL, ACPI_TYPE_INTEGER);
if (obj) {
ACPI_FREE(obj);
return true;
}
return false;
}
#else
static bool elants_acpi_is_hid_device(struct device *dev)
{
return false;
}
#endif
static int elants_i2c_probe(struct i2c_client *client,
const struct i2c_device_id *id)
{
@ -1342,9 +1377,14 @@ static int elants_i2c_probe(struct i2c_client *client,
unsigned long irqflags;
int error;
/* Don't bind to i2c-hid compatible devices, these are handled by the i2c-hid drv. */
if (elants_acpi_is_hid_device(&client->dev)) {
dev_warn(&client->dev, "This device appears to be an I2C-HID device, not binding\n");
return -ENODEV;
}
if (!i2c_check_functionality(client->adapter, I2C_FUNC_I2C)) {
dev_err(&client->dev,
"%s: i2c check functionality error\n", DEVICE_NAME);
dev_err(&client->dev, "I2C check functionality error\n");
return -ENXIO;
}

View File

@ -25,11 +25,13 @@
#define EXC3000_NUM_SLOTS 10
#define EXC3000_SLOTS_PER_FRAME 5
#define EXC3000_LEN_FRAME 66
#define EXC3000_LEN_VENDOR_REQUEST 68
#define EXC3000_LEN_POINT 10
#define EXC3000_LEN_MODEL_NAME 16
#define EXC3000_LEN_FW_VERSION 16
#define EXC3000_VENDOR_EVENT 0x03
#define EXC3000_MT1_EVENT 0x06
#define EXC3000_MT2_EVENT 0x18
@ -76,9 +78,6 @@ struct exc3000_data {
u8 buf[2 * EXC3000_LEN_FRAME];
struct completion wait_event;
struct mutex query_lock;
int query_result;
char model[EXC3000_LEN_MODEL_NAME];
char fw_version[EXC3000_LEN_FW_VERSION];
};
static void exc3000_report_slots(struct input_dev *input,
@ -105,15 +104,16 @@ static void exc3000_timer(struct timer_list *t)
input_sync(data->input);
}
static inline void exc3000_schedule_timer(struct exc3000_data *data)
{
mod_timer(&data->timer, jiffies + msecs_to_jiffies(EXC3000_TIMEOUT_MS));
}
static int exc3000_read_frame(struct exc3000_data *data, u8 *buf)
{
struct i2c_client *client = data->client;
u8 expected_event = EXC3000_MT1_EVENT;
int ret;
if (data->info->max_xy == SZ_16K - 1)
expected_event = EXC3000_MT2_EVENT;
ret = i2c_master_send(client, "'", 2);
if (ret < 0)
return ret;
@ -131,81 +131,32 @@ static int exc3000_read_frame(struct exc3000_data *data, u8 *buf)
if (get_unaligned_le16(buf) != EXC3000_LEN_FRAME)
return -EINVAL;
if (buf[2] != expected_event)
return -EINVAL;
return 0;
}
static int exc3000_read_data(struct exc3000_data *data,
u8 *buf, int *n_slots)
static int exc3000_handle_mt_event(struct exc3000_data *data)
{
int error;
struct input_dev *input = data->input;
int ret, total_slots;
u8 *buf = data->buf;
error = exc3000_read_frame(data, buf);
if (error)
return error;
total_slots = buf[3];
if (!total_slots || total_slots > EXC3000_NUM_SLOTS) {
ret = -EINVAL;
goto out_fail;
}
*n_slots = buf[3];
if (!*n_slots || *n_slots > EXC3000_NUM_SLOTS)
return -EINVAL;
if (*n_slots > EXC3000_SLOTS_PER_FRAME) {
if (total_slots > EXC3000_SLOTS_PER_FRAME) {
/* Read 2nd frame to get the rest of the contacts. */
error = exc3000_read_frame(data, buf + EXC3000_LEN_FRAME);
if (error)
return error;
ret = exc3000_read_frame(data, buf + EXC3000_LEN_FRAME);
if (ret)
goto out_fail;
/* 2nd chunk must have number of contacts set to 0. */
if (buf[EXC3000_LEN_FRAME + 3] != 0)
return -EINVAL;
}
return 0;
}
static int exc3000_query_interrupt(struct exc3000_data *data)
{
u8 *buf = data->buf;
int error;
error = i2c_master_recv(data->client, buf, EXC3000_LEN_FRAME);
if (error < 0)
return error;
if (buf[0] != 'B')
return -EPROTO;
if (buf[4] == 'E')
strlcpy(data->model, buf + 5, sizeof(data->model));
else if (buf[4] == 'D')
strlcpy(data->fw_version, buf + 5, sizeof(data->fw_version));
else
return -EPROTO;
return 0;
}
static irqreturn_t exc3000_interrupt(int irq, void *dev_id)
{
struct exc3000_data *data = dev_id;
struct input_dev *input = data->input;
u8 *buf = data->buf;
int slots, total_slots;
int error;
if (mutex_is_locked(&data->query_lock)) {
data->query_result = exc3000_query_interrupt(data);
complete(&data->wait_event);
goto out;
}
error = exc3000_read_data(data, buf, &total_slots);
if (error) {
/* Schedule a timer to release "stuck" contacts */
mod_timer(&data->timer,
jiffies + msecs_to_jiffies(EXC3000_TIMEOUT_MS));
goto out;
if (buf[EXC3000_LEN_FRAME + 3] != 0) {
ret = -EINVAL;
goto out_fail;
}
}
/*
@ -214,7 +165,8 @@ static irqreturn_t exc3000_interrupt(int irq, void *dev_id)
del_timer_sync(&data->timer);
while (total_slots > 0) {
slots = min(total_slots, EXC3000_SLOTS_PER_FRAME);
int slots = min(total_slots, EXC3000_SLOTS_PER_FRAME);
exc3000_report_slots(input, &data->prop, buf + 4, slots);
total_slots -= slots;
buf += EXC3000_LEN_FRAME;
@ -223,83 +175,152 @@ static irqreturn_t exc3000_interrupt(int irq, void *dev_id)
input_mt_sync_frame(input);
input_sync(input);
return 0;
out_fail:
/* Schedule a timer to release "stuck" contacts */
exc3000_schedule_timer(data);
return ret;
}
static irqreturn_t exc3000_interrupt(int irq, void *dev_id)
{
struct exc3000_data *data = dev_id;
u8 *buf = data->buf;
int ret;
ret = exc3000_read_frame(data, buf);
if (ret) {
/* Schedule a timer to release "stuck" contacts */
exc3000_schedule_timer(data);
goto out;
}
switch (buf[2]) {
case EXC3000_VENDOR_EVENT:
complete(&data->wait_event);
break;
case EXC3000_MT1_EVENT:
case EXC3000_MT2_EVENT:
exc3000_handle_mt_event(data);
break;
default:
break;
}
out:
return IRQ_HANDLED;
}
static int exc3000_vendor_data_request(struct exc3000_data *data, u8 *request,
u8 request_len, u8 *response, int timeout)
{
u8 buf[EXC3000_LEN_VENDOR_REQUEST] = { 0x67, 0x00, 0x42, 0x00, 0x03 };
int ret;
mutex_lock(&data->query_lock);
reinit_completion(&data->wait_event);
buf[5] = request_len;
memcpy(&buf[6], request, request_len);
ret = i2c_master_send(data->client, buf, EXC3000_LEN_VENDOR_REQUEST);
if (ret < 0)
goto out_unlock;
if (response) {
ret = wait_for_completion_timeout(&data->wait_event,
timeout * HZ);
if (ret <= 0) {
ret = -ETIMEDOUT;
goto out_unlock;
}
if (data->buf[3] >= EXC3000_LEN_FRAME) {
ret = -ENOSPC;
goto out_unlock;
}
memcpy(response, &data->buf[4], data->buf[3]);
ret = data->buf[3];
}
out_unlock:
mutex_unlock(&data->query_lock);
return ret;
}
static ssize_t fw_version_show(struct device *dev,
struct device_attribute *attr, char *buf)
{
struct i2c_client *client = to_i2c_client(dev);
struct exc3000_data *data = i2c_get_clientdata(client);
static const u8 request[68] = {
0x67, 0x00, 0x42, 0x00, 0x03, 0x01, 'D', 0x00
};
int error;
u8 response[EXC3000_LEN_FRAME];
int ret;
mutex_lock(&data->query_lock);
/* query bootloader info */
ret = exc3000_vendor_data_request(data,
(u8[]){0x39, 0x02}, 2, response, 1);
if (ret < 0)
return ret;
data->query_result = -ETIMEDOUT;
reinit_completion(&data->wait_event);
/*
* If the bootloader version is non-zero then the device is in
* bootloader mode and won't answer a query for the application FW
* version, so we just use the bootloader version info.
*/
if (response[2] || response[3])
return sprintf(buf, "%d.%d\n", response[2], response[3]);
error = i2c_master_send(client, request, sizeof(request));
if (error < 0) {
mutex_unlock(&data->query_lock);
return error;
}
ret = exc3000_vendor_data_request(data, (u8[]){'D'}, 1, response, 1);
if (ret < 0)
return ret;
wait_for_completion_interruptible_timeout(&data->wait_event, 1 * HZ);
mutex_unlock(&data->query_lock);
if (data->query_result < 0)
return data->query_result;
return sprintf(buf, "%s\n", data->fw_version);
return sprintf(buf, "%s\n", &response[1]);
}
static DEVICE_ATTR_RO(fw_version);
static ssize_t exc3000_get_model(struct exc3000_data *data)
{
static const u8 request[68] = {
0x67, 0x00, 0x42, 0x00, 0x03, 0x01, 'E', 0x00
};
struct i2c_client *client = data->client;
int error;
mutex_lock(&data->query_lock);
data->query_result = -ETIMEDOUT;
reinit_completion(&data->wait_event);
error = i2c_master_send(client, request, sizeof(request));
if (error < 0) {
mutex_unlock(&data->query_lock);
return error;
}
wait_for_completion_interruptible_timeout(&data->wait_event, 1 * HZ);
mutex_unlock(&data->query_lock);
return data->query_result;
}
static ssize_t model_show(struct device *dev,
struct device_attribute *attr, char *buf)
{
struct i2c_client *client = to_i2c_client(dev);
struct exc3000_data *data = i2c_get_clientdata(client);
int error;
u8 response[EXC3000_LEN_FRAME];
int ret;
error = exc3000_get_model(data);
if (error < 0)
return error;
ret = exc3000_vendor_data_request(data, (u8[]){'E'}, 1, response, 1);
if (ret < 0)
return ret;
return sprintf(buf, "%s\n", data->model);
return sprintf(buf, "%s\n", &response[1]);
}
static DEVICE_ATTR_RO(model);
static ssize_t type_show(struct device *dev,
struct device_attribute *attr, char *buf)
{
struct i2c_client *client = to_i2c_client(dev);
struct exc3000_data *data = i2c_get_clientdata(client);
u8 response[EXC3000_LEN_FRAME];
int ret;
ret = exc3000_vendor_data_request(data, (u8[]){'F'}, 1, response, 1);
if (ret < 0)
return ret;
return sprintf(buf, "%s\n", &response[1]);
}
static DEVICE_ATTR_RO(type);
static struct attribute *sysfs_attrs[] = {
&dev_attr_fw_version.attr,
&dev_attr_model.attr,
&dev_attr_type.attr,
NULL
};
@ -379,9 +400,15 @@ static int exc3000_probe(struct i2c_client *client)
* or two touch events anyways).
*/
for (retry = 0; retry < 3; retry++) {
error = exc3000_get_model(data);
if (!error)
u8 response[EXC3000_LEN_FRAME];
error = exc3000_vendor_data_request(data, (u8[]){'E'}, 1,
response, 1);
if (error > 0) {
dev_dbg(&client->dev, "TS Model: %s", &response[1]);
error = 0;
break;
}
dev_warn(&client->dev, "Retry %d get EETI EXC3000 model: %d\n",
retry + 1, error);
}
@ -389,8 +416,6 @@ static int exc3000_probe(struct i2c_client *client)
if (error)
return error;
dev_dbg(&client->dev, "TS Model: %s", data->model);
i2c_set_clientdata(client, data);
error = devm_device_add_group(&client->dev, &exc3000_attribute_group);

View File

@ -0,0 +1,591 @@
// SPDX-License-Identifier: GPL-2.0
/*
* Copyright (C) 2021
* Author(s): Giulio Benetti <giulio.benetti@benettiengineering.com>
*/
#include <linux/delay.h>
#include <linux/gpio/consumer.h>
#include <linux/i2c.h>
#include <linux/interrupt.h>
#include <linux/input.h>
#include <linux/input/mt.h>
#include <linux/input/touchscreen.h>
#include <linux/irq.h>
#include <linux/regulator/consumer.h>
#include <linux/regmap.h>
#include <asm/unaligned.h>
#define HY46XX_CHKSUM_CODE 0x1
#define HY46XX_FINGER_NUM 0x2
#define HY46XX_CHKSUM_LEN 0x7
#define HY46XX_THRESHOLD 0x80
#define HY46XX_GLOVE_EN 0x84
#define HY46XX_REPORT_SPEED 0x88
#define HY46XX_PWR_NOISE_EN 0x89
#define HY46XX_FILTER_DATA 0x8A
#define HY46XX_GAIN 0x92
#define HY46XX_EDGE_OFFSET 0x93
#define HY46XX_RX_NR_USED 0x94
#define HY46XX_TX_NR_USED 0x95
#define HY46XX_PWR_MODE 0xA5
#define HY46XX_FW_VERSION 0xA6
#define HY46XX_LIB_VERSION 0xA7
#define HY46XX_TP_INFO 0xA8
#define HY46XX_TP_CHIP_ID 0xA9
#define HY46XX_BOOT_VER 0xB0
#define HY46XX_TPLEN 0x6
#define HY46XX_REPORT_PKT_LEN 0x44
#define HY46XX_MAX_SUPPORTED_POINTS 11
#define TOUCH_EVENT_DOWN 0x00
#define TOUCH_EVENT_UP 0x01
#define TOUCH_EVENT_CONTACT 0x02
#define TOUCH_EVENT_RESERVED 0x03
struct hycon_hy46xx_data {
struct i2c_client *client;
struct input_dev *input;
struct touchscreen_properties prop;
struct regulator *vcc;
struct gpio_desc *reset_gpio;
struct mutex mutex;
struct regmap *regmap;
int threshold;
bool glove_enable;
int report_speed;
bool noise_filter_enable;
int filter_data;
int gain;
int edge_offset;
int rx_number_used;
int tx_number_used;
int power_mode;
int fw_version;
int lib_version;
int tp_information;
int tp_chip_id;
int bootloader_version;
};
static const struct regmap_config hycon_hy46xx_i2c_regmap_config = {
.reg_bits = 8,
.val_bits = 8,
};
static bool hycon_hy46xx_check_checksum(struct hycon_hy46xx_data *tsdata, u8 *buf)
{
u8 chksum = 0;
int i;
for (i = 2; i < buf[HY46XX_CHKSUM_LEN]; i++)
chksum += buf[i];
if (chksum == buf[HY46XX_CHKSUM_CODE])
return true;
dev_err_ratelimited(&tsdata->client->dev,
"checksum error: 0x%02x expected, got 0x%02x\n",
chksum, buf[HY46XX_CHKSUM_CODE]);
return false;
}
static irqreturn_t hycon_hy46xx_isr(int irq, void *dev_id)
{
struct hycon_hy46xx_data *tsdata = dev_id;
struct device *dev = &tsdata->client->dev;
u8 rdbuf[HY46XX_REPORT_PKT_LEN];
int i, x, y, id;
int error;
memset(rdbuf, 0, sizeof(rdbuf));
error = regmap_bulk_read(tsdata->regmap, 0, rdbuf, sizeof(rdbuf));
if (error) {
dev_err_ratelimited(dev, "Unable to fetch data, error: %d\n",
error);
goto out;
}
if (!hycon_hy46xx_check_checksum(tsdata, rdbuf))
goto out;
for (i = 0; i < HY46XX_MAX_SUPPORTED_POINTS; i++) {
u8 *buf = &rdbuf[3 + (HY46XX_TPLEN * i)];
int type = buf[0] >> 6;
if (type == TOUCH_EVENT_RESERVED)
continue;
x = get_unaligned_be16(buf) & 0x0fff;
y = get_unaligned_be16(buf + 2) & 0x0fff;
id = buf[2] >> 4;
input_mt_slot(tsdata->input, id);
if (input_mt_report_slot_state(tsdata->input, MT_TOOL_FINGER,
type != TOUCH_EVENT_UP))
touchscreen_report_pos(tsdata->input, &tsdata->prop,
x, y, true);
}
input_mt_report_pointer_emulation(tsdata->input, false);
input_sync(tsdata->input);
out:
return IRQ_HANDLED;
}
struct hycon_hy46xx_attribute {
struct device_attribute dattr;
size_t field_offset;
u8 address;
u8 limit_low;
u8 limit_high;
};
#define HYCON_ATTR_U8(_field, _mode, _address, _limit_low, _limit_high) \
struct hycon_hy46xx_attribute hycon_hy46xx_attr_##_field = { \
.dattr = __ATTR(_field, _mode, \
hycon_hy46xx_setting_show, \
hycon_hy46xx_setting_store), \
.field_offset = offsetof(struct hycon_hy46xx_data, _field), \
.address = _address, \
.limit_low = _limit_low, \
.limit_high = _limit_high, \
}
#define HYCON_ATTR_BOOL(_field, _mode, _address) \
struct hycon_hy46xx_attribute hycon_hy46xx_attr_##_field = { \
.dattr = __ATTR(_field, _mode, \
hycon_hy46xx_setting_show, \
hycon_hy46xx_setting_store), \
.field_offset = offsetof(struct hycon_hy46xx_data, _field), \
.address = _address, \
.limit_low = false, \
.limit_high = true, \
}
static ssize_t hycon_hy46xx_setting_show(struct device *dev,
struct device_attribute *dattr, char *buf)
{
struct i2c_client *client = to_i2c_client(dev);
struct hycon_hy46xx_data *tsdata = i2c_get_clientdata(client);
struct hycon_hy46xx_attribute *attr =
container_of(dattr, struct hycon_hy46xx_attribute, dattr);
u8 *field = (u8 *)tsdata + attr->field_offset;
size_t count = 0;
int error = 0;
int val;
mutex_lock(&tsdata->mutex);
error = regmap_read(tsdata->regmap, attr->address, &val);
if (error < 0) {
dev_err(&tsdata->client->dev,
"Failed to fetch attribute %s, error %d\n",
dattr->attr.name, error);
goto out;
}
if (val != *field) {
dev_warn(&tsdata->client->dev,
"%s: read (%d) and stored value (%d) differ\n",
dattr->attr.name, val, *field);
*field = val;
}
count = scnprintf(buf, PAGE_SIZE, "%d\n", val);
out:
mutex_unlock(&tsdata->mutex);
return error ?: count;
}
static ssize_t hycon_hy46xx_setting_store(struct device *dev,
struct device_attribute *dattr,
const char *buf, size_t count)
{
struct i2c_client *client = to_i2c_client(dev);
struct hycon_hy46xx_data *tsdata = i2c_get_clientdata(client);
struct hycon_hy46xx_attribute *attr =
container_of(dattr, struct hycon_hy46xx_attribute, dattr);
u8 *field = (u8 *)tsdata + attr->field_offset;
unsigned int val;
int error;
mutex_lock(&tsdata->mutex);
error = kstrtouint(buf, 0, &val);
if (error)
goto out;
if (val < attr->limit_low || val > attr->limit_high) {
error = -ERANGE;
goto out;
}
error = regmap_write(tsdata->regmap, attr->address, val);
if (error < 0) {
dev_err(&tsdata->client->dev,
"Failed to update attribute %s, error: %d\n",
dattr->attr.name, error);
goto out;
}
*field = val;
out:
mutex_unlock(&tsdata->mutex);
return error ?: count;
}
static HYCON_ATTR_U8(threshold, 0644, HY46XX_THRESHOLD, 0, 255);
static HYCON_ATTR_BOOL(glove_enable, 0644, HY46XX_GLOVE_EN);
static HYCON_ATTR_U8(report_speed, 0644, HY46XX_REPORT_SPEED, 0, 255);
static HYCON_ATTR_BOOL(noise_filter_enable, 0644, HY46XX_PWR_NOISE_EN);
static HYCON_ATTR_U8(filter_data, 0644, HY46XX_FILTER_DATA, 0, 5);
static HYCON_ATTR_U8(gain, 0644, HY46XX_GAIN, 0, 5);
static HYCON_ATTR_U8(edge_offset, 0644, HY46XX_EDGE_OFFSET, 0, 5);
static HYCON_ATTR_U8(fw_version, 0444, HY46XX_FW_VERSION, 0, 255);
static HYCON_ATTR_U8(lib_version, 0444, HY46XX_LIB_VERSION, 0, 255);
static HYCON_ATTR_U8(tp_information, 0444, HY46XX_TP_INFO, 0, 255);
static HYCON_ATTR_U8(tp_chip_id, 0444, HY46XX_TP_CHIP_ID, 0, 255);
static HYCON_ATTR_U8(bootloader_version, 0444, HY46XX_BOOT_VER, 0, 255);
static struct attribute *hycon_hy46xx_attrs[] = {
&hycon_hy46xx_attr_threshold.dattr.attr,
&hycon_hy46xx_attr_glove_enable.dattr.attr,
&hycon_hy46xx_attr_report_speed.dattr.attr,
&hycon_hy46xx_attr_noise_filter_enable.dattr.attr,
&hycon_hy46xx_attr_filter_data.dattr.attr,
&hycon_hy46xx_attr_gain.dattr.attr,
&hycon_hy46xx_attr_edge_offset.dattr.attr,
&hycon_hy46xx_attr_fw_version.dattr.attr,
&hycon_hy46xx_attr_lib_version.dattr.attr,
&hycon_hy46xx_attr_tp_information.dattr.attr,
&hycon_hy46xx_attr_tp_chip_id.dattr.attr,
&hycon_hy46xx_attr_bootloader_version.dattr.attr,
NULL
};
static const struct attribute_group hycon_hy46xx_attr_group = {
.attrs = hycon_hy46xx_attrs,
};
static void hycon_hy46xx_get_defaults(struct device *dev, struct hycon_hy46xx_data *tsdata)
{
bool val_bool;
int error;
u32 val;
error = device_property_read_u32(dev, "hycon,threshold", &val);
if (!error) {
error = regmap_write(tsdata->regmap, HY46XX_THRESHOLD, val);
if (error < 0)
goto out;
tsdata->threshold = val;
}
val_bool = device_property_read_bool(dev, "hycon,glove-enable");
error = regmap_write(tsdata->regmap, HY46XX_GLOVE_EN, val_bool);
if (error < 0)
goto out;
tsdata->glove_enable = val_bool;
error = device_property_read_u32(dev, "hycon,report-speed-hz", &val);
if (!error) {
error = regmap_write(tsdata->regmap, HY46XX_REPORT_SPEED, val);
if (error < 0)
goto out;
tsdata->report_speed = val;
}
val_bool = device_property_read_bool(dev, "hycon,noise-filter-enable");
error = regmap_write(tsdata->regmap, HY46XX_PWR_NOISE_EN, val_bool);
if (error < 0)
goto out;
tsdata->noise_filter_enable = val_bool;
error = device_property_read_u32(dev, "hycon,filter-data", &val);
if (!error) {
error = regmap_write(tsdata->regmap, HY46XX_FILTER_DATA, val);
if (error < 0)
goto out;
tsdata->filter_data = val;
}
error = device_property_read_u32(dev, "hycon,gain", &val);
if (!error) {
error = regmap_write(tsdata->regmap, HY46XX_GAIN, val);
if (error < 0)
goto out;
tsdata->gain = val;
}
error = device_property_read_u32(dev, "hycon,edge-offset", &val);
if (!error) {
error = regmap_write(tsdata->regmap, HY46XX_EDGE_OFFSET, val);
if (error < 0)
goto out;
tsdata->edge_offset = val;
}
return;
out:
dev_err(&tsdata->client->dev, "Failed to set default settings");
}
static void hycon_hy46xx_get_parameters(struct hycon_hy46xx_data *tsdata)
{
int error;
u32 val;
error = regmap_read(tsdata->regmap, HY46XX_THRESHOLD, &val);
if (error < 0)
goto out;
tsdata->threshold = val;
error = regmap_read(tsdata->regmap, HY46XX_GLOVE_EN, &val);
if (error < 0)
goto out;
tsdata->glove_enable = val;
error = regmap_read(tsdata->regmap, HY46XX_REPORT_SPEED, &val);
if (error < 0)
goto out;
tsdata->report_speed = val;
error = regmap_read(tsdata->regmap, HY46XX_PWR_NOISE_EN, &val);
if (error < 0)
goto out;
tsdata->noise_filter_enable = val;
error = regmap_read(tsdata->regmap, HY46XX_FILTER_DATA, &val);
if (error < 0)
goto out;
tsdata->filter_data = val;
error = regmap_read(tsdata->regmap, HY46XX_GAIN, &val);
if (error < 0)
goto out;
tsdata->gain = val;
error = regmap_read(tsdata->regmap, HY46XX_EDGE_OFFSET, &val);
if (error < 0)
goto out;
tsdata->edge_offset = val;
error = regmap_read(tsdata->regmap, HY46XX_RX_NR_USED, &val);
if (error < 0)
goto out;
tsdata->rx_number_used = val;
error = regmap_read(tsdata->regmap, HY46XX_TX_NR_USED, &val);
if (error < 0)
goto out;
tsdata->tx_number_used = val;
error = regmap_read(tsdata->regmap, HY46XX_PWR_MODE, &val);
if (error < 0)
goto out;
tsdata->power_mode = val;
error = regmap_read(tsdata->regmap, HY46XX_FW_VERSION, &val);
if (error < 0)
goto out;
tsdata->fw_version = val;
error = regmap_read(tsdata->regmap, HY46XX_LIB_VERSION, &val);
if (error < 0)
goto out;
tsdata->lib_version = val;
error = regmap_read(tsdata->regmap, HY46XX_TP_INFO, &val);
if (error < 0)
goto out;
tsdata->tp_information = val;
error = regmap_read(tsdata->regmap, HY46XX_TP_CHIP_ID, &val);
if (error < 0)
goto out;
tsdata->tp_chip_id = val;
error = regmap_read(tsdata->regmap, HY46XX_BOOT_VER, &val);
if (error < 0)
goto out;
tsdata->bootloader_version = val;
return;
out:
dev_err(&tsdata->client->dev, "Failed to read default settings");
}
static void hycon_hy46xx_disable_regulator(void *arg)
{
struct hycon_hy46xx_data *data = arg;
regulator_disable(data->vcc);
}
static int hycon_hy46xx_probe(struct i2c_client *client,
const struct i2c_device_id *id)
{
struct hycon_hy46xx_data *tsdata;
struct input_dev *input;
int error;
dev_dbg(&client->dev, "probing for HYCON HY46XX I2C\n");
tsdata = devm_kzalloc(&client->dev, sizeof(*tsdata), GFP_KERNEL);
if (!tsdata)
return -ENOMEM;
tsdata->vcc = devm_regulator_get(&client->dev, "vcc");
if (IS_ERR(tsdata->vcc)) {
error = PTR_ERR(tsdata->vcc);
if (error != -EPROBE_DEFER)
dev_err(&client->dev,
"failed to request regulator: %d\n", error);
return error;
}
error = regulator_enable(tsdata->vcc);
if (error < 0) {
dev_err(&client->dev, "failed to enable vcc: %d\n", error);
return error;
}
error = devm_add_action_or_reset(&client->dev,
hycon_hy46xx_disable_regulator,
tsdata);
if (error)
return error;
tsdata->reset_gpio = devm_gpiod_get_optional(&client->dev,
"reset", GPIOD_OUT_LOW);
if (IS_ERR(tsdata->reset_gpio)) {
error = PTR_ERR(tsdata->reset_gpio);
dev_err(&client->dev,
"Failed to request GPIO reset pin, error %d\n", error);
return error;
}
if (tsdata->reset_gpio) {
usleep_range(5000, 6000);
gpiod_set_value_cansleep(tsdata->reset_gpio, 1);
usleep_range(5000, 6000);
gpiod_set_value_cansleep(tsdata->reset_gpio, 0);
msleep(1000);
}
input = devm_input_allocate_device(&client->dev);
if (!input) {
dev_err(&client->dev, "failed to allocate input device.\n");
return -ENOMEM;
}
mutex_init(&tsdata->mutex);
tsdata->client = client;
tsdata->input = input;
tsdata->regmap = devm_regmap_init_i2c(client,
&hycon_hy46xx_i2c_regmap_config);
if (IS_ERR(tsdata->regmap)) {
dev_err(&client->dev, "regmap allocation failed\n");
return PTR_ERR(tsdata->regmap);
}
hycon_hy46xx_get_defaults(&client->dev, tsdata);
hycon_hy46xx_get_parameters(tsdata);
input->name = "Hycon Capacitive Touch";
input->id.bustype = BUS_I2C;
input->dev.parent = &client->dev;
input_set_abs_params(input, ABS_MT_POSITION_X, 0, -1, 0, 0);
input_set_abs_params(input, ABS_MT_POSITION_Y, 0, -1, 0, 0);
touchscreen_parse_properties(input, true, &tsdata->prop);
error = input_mt_init_slots(input, HY46XX_MAX_SUPPORTED_POINTS,
INPUT_MT_DIRECT);
if (error) {
dev_err(&client->dev, "Unable to init MT slots.\n");
return error;
}
i2c_set_clientdata(client, tsdata);
error = devm_request_threaded_irq(&client->dev, client->irq,
NULL, hycon_hy46xx_isr, IRQF_ONESHOT,
client->name, tsdata);
if (error) {
dev_err(&client->dev, "Unable to request touchscreen IRQ.\n");
return error;
}
error = devm_device_add_group(&client->dev, &hycon_hy46xx_attr_group);
if (error)
return error;
error = input_register_device(input);
if (error)
return error;
dev_dbg(&client->dev,
"HYCON HY46XX initialized: IRQ %d, Reset pin %d.\n",
client->irq,
tsdata->reset_gpio ? desc_to_gpio(tsdata->reset_gpio) : -1);
return 0;
}
static const struct i2c_device_id hycon_hy46xx_id[] = {
{ .name = "hy4613" },
{ .name = "hy4614" },
{ .name = "hy4621" },
{ .name = "hy4623" },
{ .name = "hy4633" },
{ .name = "hy4635" },
{ /* sentinel */ }
};
MODULE_DEVICE_TABLE(i2c, hycon_hy46xx_id);
static const struct of_device_id hycon_hy46xx_of_match[] = {
{ .compatible = "hycon,hy4613" },
{ .compatible = "hycon,hy4614" },
{ .compatible = "hycon,hy4621" },
{ .compatible = "hycon,hy4623" },
{ .compatible = "hycon,hy4633" },
{ .compatible = "hycon,hy4635" },
{ /* sentinel */ }
};
MODULE_DEVICE_TABLE(of, hycon_hy46xx_of_match);
static struct i2c_driver hycon_hy46xx_driver = {
.driver = {
.name = "hycon_hy46xx",
.of_match_table = hycon_hy46xx_of_match,
.probe_type = PROBE_PREFER_ASYNCHRONOUS,
},
.id_table = hycon_hy46xx_id,
.probe = hycon_hy46xx_probe,
};
module_i2c_driver(hycon_hy46xx_driver);
MODULE_AUTHOR("Giulio Benetti <giulio.benetti@benettiengineering.com>");
MODULE_DESCRIPTION("HYCON HY46XX I2C Touchscreen Driver");
MODULE_LICENSE("GPL v2");

View File

@ -87,7 +87,7 @@ static bool ili210x_touchdata_to_coords(const u8 *touchdata,
unsigned int *x, unsigned int *y,
unsigned int *z)
{
if (touchdata[0] & BIT(finger))
if (!(touchdata[0] & BIT(finger)))
return false;
*x = get_unaligned_be16(touchdata + 1 + (finger * 4) + 0);

View File

@ -0,0 +1,690 @@
// SPDX-License-Identifier: GPL-2.0
/*
* ILITEK Touch IC driver for 23XX, 25XX and Lego series
*
* Copyright (C) 2011 ILI Technology Corporation.
* Copyright (C) 2020 Luca Hsu <luca_hsu@ilitek.com>
* Copyright (C) 2021 Joe Hung <joe_hung@ilitek.com>
*/
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/input.h>
#include <linux/input/mt.h>
#include <linux/i2c.h>
#include <linux/slab.h>
#include <linux/delay.h>
#include <linux/interrupt.h>
#include <linux/gpio.h>
#include <linux/gpio/consumer.h>
#include <linux/errno.h>
#include <linux/acpi.h>
#include <linux/input/touchscreen.h>
#include <asm/unaligned.h>
#define ILITEK_TS_NAME "ilitek_ts"
#define BL_V1_8 0x108
#define BL_V1_7 0x107
#define BL_V1_6 0x106
#define ILITEK_TP_CMD_GET_TP_RES 0x20
#define ILITEK_TP_CMD_GET_SCRN_RES 0x21
#define ILITEK_TP_CMD_SET_IC_SLEEP 0x30
#define ILITEK_TP_CMD_SET_IC_WAKE 0x31
#define ILITEK_TP_CMD_GET_FW_VER 0x40
#define ILITEK_TP_CMD_GET_PRL_VER 0x42
#define ILITEK_TP_CMD_GET_MCU_VER 0x61
#define ILITEK_TP_CMD_GET_IC_MODE 0xC0
#define REPORT_COUNT_ADDRESS 61
#define ILITEK_SUPPORT_MAX_POINT 40
struct ilitek_protocol_info {
u16 ver;
u8 ver_major;
};
struct ilitek_ts_data {
struct i2c_client *client;
struct gpio_desc *reset_gpio;
struct input_dev *input_dev;
struct touchscreen_properties prop;
const struct ilitek_protocol_map *ptl_cb_func;
struct ilitek_protocol_info ptl;
char product_id[30];
u16 mcu_ver;
u8 ic_mode;
u8 firmware_ver[8];
s32 reset_time;
s32 screen_max_x;
s32 screen_max_y;
s32 screen_min_x;
s32 screen_min_y;
s32 max_tp;
};
struct ilitek_protocol_map {
u16 cmd;
const char *name;
int (*func)(struct ilitek_ts_data *ts, u16 cmd, u8 *inbuf, u8 *outbuf);
};
enum ilitek_cmds {
/* common cmds */
GET_PTL_VER = 0,
GET_FW_VER,
GET_SCRN_RES,
GET_TP_RES,
GET_IC_MODE,
GET_MCU_VER,
SET_IC_SLEEP,
SET_IC_WAKE,
/* ALWAYS keep at the end */
MAX_CMD_CNT
};
/* ILITEK I2C R/W APIs */
static int ilitek_i2c_write_and_read(struct ilitek_ts_data *ts,
u8 *cmd, int write_len, int delay,
u8 *data, int read_len)
{
int error;
struct i2c_client *client = ts->client;
struct i2c_msg msgs[] = {
{
.addr = client->addr,
.flags = 0,
.len = write_len,
.buf = cmd,
},
{
.addr = client->addr,
.flags = I2C_M_RD,
.len = read_len,
.buf = data,
},
};
if (delay == 0 && write_len > 0 && read_len > 0) {
error = i2c_transfer(client->adapter, msgs, ARRAY_SIZE(msgs));
if (error < 0)
return error;
} else {
if (write_len > 0) {
error = i2c_transfer(client->adapter, msgs, 1);
if (error < 0)
return error;
}
if (delay > 0)
mdelay(delay);
if (read_len > 0) {
error = i2c_transfer(client->adapter, msgs + 1, 1);
if (error < 0)
return error;
}
}
return 0;
}
/* ILITEK ISR APIs */
static void ilitek_touch_down(struct ilitek_ts_data *ts, unsigned int id,
unsigned int x, unsigned int y)
{
struct input_dev *input = ts->input_dev;
input_mt_slot(input, id);
input_mt_report_slot_state(input, MT_TOOL_FINGER, true);
touchscreen_report_pos(input, &ts->prop, x, y, true);
}
static int ilitek_process_and_report_v6(struct ilitek_ts_data *ts)
{
int error = 0;
u8 buf[512];
int packet_len = 5;
int packet_max_point = 10;
int report_max_point;
int i, count;
struct input_dev *input = ts->input_dev;
struct device *dev = &ts->client->dev;
unsigned int x, y, status, id;
error = ilitek_i2c_write_and_read(ts, NULL, 0, 0, buf, 64);
if (error) {
dev_err(dev, "get touch info failed, err:%d\n", error);
goto err_sync_frame;
}
report_max_point = buf[REPORT_COUNT_ADDRESS];
if (report_max_point > ts->max_tp) {
dev_err(dev, "FW report max point:%d > panel info. max:%d\n",
report_max_point, ts->max_tp);
error = -EINVAL;
goto err_sync_frame;
}
count = DIV_ROUND_UP(report_max_point, packet_max_point);
for (i = 1; i < count; i++) {
error = ilitek_i2c_write_and_read(ts, NULL, 0, 0,
buf + i * 64, 64);
if (error) {
dev_err(dev, "get touch info. failed, cnt:%d, err:%d\n",
count, error);
goto err_sync_frame;
}
}
for (i = 0; i < report_max_point; i++) {
status = buf[i * packet_len + 1] & 0x40;
if (!status)
continue;
id = buf[i * packet_len + 1] & 0x3F;
x = get_unaligned_le16(buf + i * packet_len + 2);
y = get_unaligned_le16(buf + i * packet_len + 4);
if (x > ts->screen_max_x || x < ts->screen_min_x ||
y > ts->screen_max_y || y < ts->screen_min_y) {
dev_warn(dev, "invalid position, X[%d,%u,%d], Y[%d,%u,%d]\n",
ts->screen_min_x, x, ts->screen_max_x,
ts->screen_min_y, y, ts->screen_max_y);
continue;
}
ilitek_touch_down(ts, id, x, y);
}
err_sync_frame:
input_mt_sync_frame(input);
input_sync(input);
return error;
}
/* APIs of cmds for ILITEK Touch IC */
static int api_protocol_set_cmd(struct ilitek_ts_data *ts,
u16 idx, u8 *inbuf, u8 *outbuf)
{
u16 cmd;
int error;
if (idx >= MAX_CMD_CNT)
return -EINVAL;
cmd = ts->ptl_cb_func[idx].cmd;
error = ts->ptl_cb_func[idx].func(ts, cmd, inbuf, outbuf);
if (error)
return error;
return 0;
}
static int api_protocol_get_ptl_ver(struct ilitek_ts_data *ts,
u16 cmd, u8 *inbuf, u8 *outbuf)
{
int error;
u8 buf[64];
buf[0] = cmd;
error = ilitek_i2c_write_and_read(ts, buf, 1, 5, outbuf, 3);
if (error)
return error;
ts->ptl.ver = get_unaligned_be16(outbuf);
ts->ptl.ver_major = outbuf[0];
return 0;
}
static int api_protocol_get_mcu_ver(struct ilitek_ts_data *ts,
u16 cmd, u8 *inbuf, u8 *outbuf)
{
int error;
u8 buf[64];
buf[0] = cmd;
error = ilitek_i2c_write_and_read(ts, buf, 1, 5, outbuf, 32);
if (error)
return error;
ts->mcu_ver = get_unaligned_le16(outbuf);
memset(ts->product_id, 0, sizeof(ts->product_id));
memcpy(ts->product_id, outbuf + 6, 26);
return 0;
}
static int api_protocol_get_fw_ver(struct ilitek_ts_data *ts,
u16 cmd, u8 *inbuf, u8 *outbuf)
{
int error;
u8 buf[64];
buf[0] = cmd;
error = ilitek_i2c_write_and_read(ts, buf, 1, 5, outbuf, 8);
if (error)
return error;
memcpy(ts->firmware_ver, outbuf, 8);
return 0;
}
static int api_protocol_get_scrn_res(struct ilitek_ts_data *ts,
u16 cmd, u8 *inbuf, u8 *outbuf)
{
int error;
u8 buf[64];
buf[0] = cmd;
error = ilitek_i2c_write_and_read(ts, buf, 1, 5, outbuf, 8);
if (error)
return error;
ts->screen_min_x = get_unaligned_le16(outbuf);
ts->screen_min_y = get_unaligned_le16(outbuf + 2);
ts->screen_max_x = get_unaligned_le16(outbuf + 4);
ts->screen_max_y = get_unaligned_le16(outbuf + 6);
return 0;
}
static int api_protocol_get_tp_res(struct ilitek_ts_data *ts,
u16 cmd, u8 *inbuf, u8 *outbuf)
{
int error;
u8 buf[64];
buf[0] = cmd;
error = ilitek_i2c_write_and_read(ts, buf, 1, 5, outbuf, 15);
if (error)
return error;
ts->max_tp = outbuf[8];
if (ts->max_tp > ILITEK_SUPPORT_MAX_POINT) {
dev_err(&ts->client->dev, "Invalid MAX_TP:%d from FW\n",
ts->max_tp);
return -EINVAL;
}
return 0;
}
static int api_protocol_get_ic_mode(struct ilitek_ts_data *ts,
u16 cmd, u8 *inbuf, u8 *outbuf)
{
int error;
u8 buf[64];
buf[0] = cmd;
error = ilitek_i2c_write_and_read(ts, buf, 1, 5, outbuf, 2);
if (error)
return error;
ts->ic_mode = outbuf[0];
return 0;
}
static int api_protocol_set_ic_sleep(struct ilitek_ts_data *ts,
u16 cmd, u8 *inbuf, u8 *outbuf)
{
u8 buf[64];
buf[0] = cmd;
return ilitek_i2c_write_and_read(ts, buf, 1, 0, NULL, 0);
}
static int api_protocol_set_ic_wake(struct ilitek_ts_data *ts,
u16 cmd, u8 *inbuf, u8 *outbuf)
{
u8 buf[64];
buf[0] = cmd;
return ilitek_i2c_write_and_read(ts, buf, 1, 0, NULL, 0);
}
static const struct ilitek_protocol_map ptl_func_map[] = {
/* common cmds */
[GET_PTL_VER] = {
ILITEK_TP_CMD_GET_PRL_VER, "GET_PTL_VER",
api_protocol_get_ptl_ver
},
[GET_FW_VER] = {
ILITEK_TP_CMD_GET_FW_VER, "GET_FW_VER",
api_protocol_get_fw_ver
},
[GET_SCRN_RES] = {
ILITEK_TP_CMD_GET_SCRN_RES, "GET_SCRN_RES",
api_protocol_get_scrn_res
},
[GET_TP_RES] = {
ILITEK_TP_CMD_GET_TP_RES, "GET_TP_RES",
api_protocol_get_tp_res
},
[GET_IC_MODE] = {
ILITEK_TP_CMD_GET_IC_MODE, "GET_IC_MODE",
api_protocol_get_ic_mode
},
[GET_MCU_VER] = {
ILITEK_TP_CMD_GET_MCU_VER, "GET_MOD_VER",
api_protocol_get_mcu_ver
},
[SET_IC_SLEEP] = {
ILITEK_TP_CMD_SET_IC_SLEEP, "SET_IC_SLEEP",
api_protocol_set_ic_sleep
},
[SET_IC_WAKE] = {
ILITEK_TP_CMD_SET_IC_WAKE, "SET_IC_WAKE",
api_protocol_set_ic_wake
},
};
/* Probe APIs */
static void ilitek_reset(struct ilitek_ts_data *ts, int delay)
{
if (ts->reset_gpio) {
gpiod_set_value(ts->reset_gpio, 1);
mdelay(10);
gpiod_set_value(ts->reset_gpio, 0);
mdelay(delay);
}
}
static int ilitek_protocol_init(struct ilitek_ts_data *ts)
{
int error;
u8 outbuf[64];
ts->ptl_cb_func = ptl_func_map;
ts->reset_time = 600;
error = api_protocol_set_cmd(ts, GET_PTL_VER, NULL, outbuf);
if (error)
return error;
/* Protocol v3 is not support currently */
if (ts->ptl.ver_major == 0x3 ||
ts->ptl.ver == BL_V1_6 ||
ts->ptl.ver == BL_V1_7)
return -EINVAL;
return 0;
}
static int ilitek_read_tp_info(struct ilitek_ts_data *ts, bool boot)
{
u8 outbuf[256];
int error;
error = api_protocol_set_cmd(ts, GET_PTL_VER, NULL, outbuf);
if (error)
return error;
error = api_protocol_set_cmd(ts, GET_MCU_VER, NULL, outbuf);
if (error)
return error;
error = api_protocol_set_cmd(ts, GET_FW_VER, NULL, outbuf);
if (error)
return error;
if (boot) {
error = api_protocol_set_cmd(ts, GET_SCRN_RES, NULL,
outbuf);
if (error)
return error;
}
error = api_protocol_set_cmd(ts, GET_TP_RES, NULL, outbuf);
if (error)
return error;
error = api_protocol_set_cmd(ts, GET_IC_MODE, NULL, outbuf);
if (error)
return error;
return 0;
}
static int ilitek_input_dev_init(struct device *dev, struct ilitek_ts_data *ts)
{
int error;
struct input_dev *input;
input = devm_input_allocate_device(dev);
if (!input)
return -ENOMEM;
ts->input_dev = input;
input->name = ILITEK_TS_NAME;
input->id.bustype = BUS_I2C;
__set_bit(INPUT_PROP_DIRECT, input->propbit);
input_set_abs_params(input, ABS_MT_POSITION_X,
ts->screen_min_x, ts->screen_max_x, 0, 0);
input_set_abs_params(input, ABS_MT_POSITION_Y,
ts->screen_min_y, ts->screen_max_y, 0, 0);
touchscreen_parse_properties(input, true, &ts->prop);
error = input_mt_init_slots(input, ts->max_tp,
INPUT_MT_DIRECT | INPUT_MT_DROP_UNUSED);
if (error) {
dev_err(dev, "initialize MT slots failed, err:%d\n", error);
return error;
}
error = input_register_device(input);
if (error) {
dev_err(dev, "register input device failed, err:%d\n", error);
return error;
}
return 0;
}
static irqreturn_t ilitek_i2c_isr(int irq, void *dev_id)
{
struct ilitek_ts_data *ts = dev_id;
int error;
error = ilitek_process_and_report_v6(ts);
if (error < 0) {
dev_err(&ts->client->dev, "[%s] err:%d\n", __func__, error);
return IRQ_NONE;
}
return IRQ_HANDLED;
}
static ssize_t firmware_version_show(struct device *dev,
struct device_attribute *attr, char *buf)
{
struct i2c_client *client = to_i2c_client(dev);
struct ilitek_ts_data *ts = i2c_get_clientdata(client);
return scnprintf(buf, PAGE_SIZE,
"fw version: [%02X%02X.%02X%02X.%02X%02X.%02X%02X]\n",
ts->firmware_ver[0], ts->firmware_ver[1],
ts->firmware_ver[2], ts->firmware_ver[3],
ts->firmware_ver[4], ts->firmware_ver[5],
ts->firmware_ver[6], ts->firmware_ver[7]);
}
static DEVICE_ATTR_RO(firmware_version);
static ssize_t product_id_show(struct device *dev,
struct device_attribute *attr, char *buf)
{
struct i2c_client *client = to_i2c_client(dev);
struct ilitek_ts_data *ts = i2c_get_clientdata(client);
return scnprintf(buf, PAGE_SIZE, "product id: [%04X], module: [%s]\n",
ts->mcu_ver, ts->product_id);
}
static DEVICE_ATTR_RO(product_id);
static struct attribute *ilitek_sysfs_attrs[] = {
&dev_attr_firmware_version.attr,
&dev_attr_product_id.attr,
NULL
};
static struct attribute_group ilitek_attrs_group = {
.attrs = ilitek_sysfs_attrs,
};
static int ilitek_ts_i2c_probe(struct i2c_client *client,
const struct i2c_device_id *id)
{
struct ilitek_ts_data *ts;
struct device *dev = &client->dev;
int error;
if (!i2c_check_functionality(client->adapter, I2C_FUNC_I2C)) {
dev_err(dev, "i2c check functionality failed\n");
return -ENXIO;
}
ts = devm_kzalloc(dev, sizeof(*ts), GFP_KERNEL);
if (!ts)
return -ENOMEM;
ts->client = client;
i2c_set_clientdata(client, ts);
ts->reset_gpio = devm_gpiod_get_optional(dev, "reset", GPIOD_OUT_LOW);
if (IS_ERR(ts->reset_gpio)) {
error = PTR_ERR(ts->reset_gpio);
dev_err(dev, "request gpiod failed: %d", error);
return error;
}
ilitek_reset(ts, 1000);
error = ilitek_protocol_init(ts);
if (error) {
dev_err(dev, "protocol init failed: %d", error);
return error;
}
error = ilitek_read_tp_info(ts, true);
if (error) {
dev_err(dev, "read tp info failed: %d", error);
return error;
}
error = ilitek_input_dev_init(dev, ts);
if (error) {
dev_err(dev, "input dev init failed: %d", error);
return error;
}
error = devm_request_threaded_irq(dev, ts->client->irq,
NULL, ilitek_i2c_isr, IRQF_ONESHOT,
"ilitek_touch_irq", ts);
if (error) {
dev_err(dev, "request threaded irq failed: %d\n", error);
return error;
}
error = devm_device_add_group(dev, &ilitek_attrs_group);
if (error) {
dev_err(dev, "sysfs create group failed: %d\n", error);
return error;
}
return 0;
}
static int __maybe_unused ilitek_suspend(struct device *dev)
{
struct i2c_client *client = to_i2c_client(dev);
struct ilitek_ts_data *ts = i2c_get_clientdata(client);
int error;
disable_irq(client->irq);
if (!device_may_wakeup(dev)) {
error = api_protocol_set_cmd(ts, SET_IC_SLEEP, NULL, NULL);
if (error)
return error;
}
return 0;
}
static int __maybe_unused ilitek_resume(struct device *dev)
{
struct i2c_client *client = to_i2c_client(dev);
struct ilitek_ts_data *ts = i2c_get_clientdata(client);
int error;
if (!device_may_wakeup(dev)) {
error = api_protocol_set_cmd(ts, SET_IC_WAKE, NULL, NULL);
if (error)
return error;
ilitek_reset(ts, ts->reset_time);
}
enable_irq(client->irq);
return 0;
}
static SIMPLE_DEV_PM_OPS(ilitek_pm_ops, ilitek_suspend, ilitek_resume);
static const struct i2c_device_id ilitek_ts_i2c_id[] = {
{ ILITEK_TS_NAME, 0 },
{ },
};
MODULE_DEVICE_TABLE(i2c, ilitek_ts_i2c_id);
#ifdef CONFIG_ACPI
static const struct acpi_device_id ilitekts_acpi_id[] = {
{ "ILTK0001", 0 },
{ },
};
MODULE_DEVICE_TABLE(acpi, ilitekts_acpi_id);
#endif
#ifdef CONFIG_OF
static const struct of_device_id ilitek_ts_i2c_match[] = {
{.compatible = "ilitek,ili2130",},
{.compatible = "ilitek,ili2131",},
{.compatible = "ilitek,ili2132",},
{.compatible = "ilitek,ili2316",},
{.compatible = "ilitek,ili2322",},
{.compatible = "ilitek,ili2323",},
{.compatible = "ilitek,ili2326",},
{.compatible = "ilitek,ili2520",},
{.compatible = "ilitek,ili2521",},
{ },
};
MODULE_DEVICE_TABLE(of, ilitek_ts_i2c_match);
#endif
static struct i2c_driver ilitek_ts_i2c_driver = {
.driver = {
.name = ILITEK_TS_NAME,
.pm = &ilitek_pm_ops,
.of_match_table = of_match_ptr(ilitek_ts_i2c_match),
.acpi_match_table = ACPI_PTR(ilitekts_acpi_id),
},
.probe = ilitek_ts_i2c_probe,
.id_table = ilitek_ts_i2c_id,
};
module_i2c_driver(ilitek_ts_i2c_driver);
MODULE_AUTHOR("ILITEK");
MODULE_DESCRIPTION("ILITEK I2C Touchscreen Driver");
MODULE_LICENSE("GPL");

View File

@ -8,7 +8,7 @@
* made available by the vendor. Firmware files may be pushed to the device's
* nonvolatile memory by writing the filename to the 'fw_file' sysfs control.
*
* Link to PC-based configuration tool and data sheet: http://www.azoteq.com/
* Link to PC-based configuration tool and datasheet: https://www.azoteq.com/
*/
#include <linux/bits.h>
@ -32,14 +32,10 @@
#define IQS5XX_NUM_RETRIES 10
#define IQS5XX_NUM_CONTACTS 5
#define IQS5XX_WR_BYTES_MAX 2
#define IQS5XX_XY_RES_MAX 0xFFFE
#define IQS5XX_PROD_NUM_IQS550 40
#define IQS5XX_PROD_NUM_IQS572 58
#define IQS5XX_PROD_NUM_IQS525 52
#define IQS5XX_PROJ_NUM_A000 0
#define IQS5XX_PROJ_NUM_B000 15
#define IQS5XX_MAJOR_VER_MIN 2
#define IQS5XX_SHOW_RESET BIT(7)
#define IQS5XX_ACK_RESET BIT(7)
@ -64,6 +60,7 @@
#define IQS5XX_SYS_CFG1 0x058F
#define IQS5XX_X_RES 0x066E
#define IQS5XX_Y_RES 0x0670
#define IQS5XX_EXP_FILE 0x0677
#define IQS5XX_CHKSM 0x83C0
#define IQS5XX_APP 0x8400
#define IQS5XX_CSTM 0xBE00
@ -87,22 +84,11 @@
#define IQS5XX_BL_CMD_CRC 0x03
#define IQS5XX_BL_BLK_LEN_MAX 64
#define IQS5XX_BL_ID 0x0200
#define IQS5XX_BL_STATUS_RESET 0x00
#define IQS5XX_BL_STATUS_AVAIL 0xA5
#define IQS5XX_BL_STATUS_NONE 0xEE
#define IQS5XX_BL_CRC_PASS 0x00
#define IQS5XX_BL_CRC_FAIL 0x01
#define IQS5XX_BL_ATTEMPTS 3
struct iqs5xx_private {
struct i2c_client *client;
struct input_dev *input;
struct gpio_desc *reset_gpio;
struct touchscreen_properties prop;
struct mutex lock;
u8 bl_status;
};
struct iqs5xx_dev_id_info {
__be16 prod_num;
__be16 proj_num;
@ -134,6 +120,16 @@ struct iqs5xx_status {
struct iqs5xx_touch_data touch_data[IQS5XX_NUM_CONTACTS];
} __packed;
struct iqs5xx_private {
struct i2c_client *client;
struct input_dev *input;
struct gpio_desc *reset_gpio;
struct touchscreen_properties prop;
struct mutex lock;
struct iqs5xx_dev_id_info dev_id_info;
u8 exp_file[2];
};
static int iqs5xx_read_burst(struct i2c_client *client,
u16 reg, void *val, u16 len)
{
@ -446,7 +442,7 @@ static int iqs5xx_set_state(struct i2c_client *client, u8 state)
struct iqs5xx_private *iqs5xx = i2c_get_clientdata(client);
int error1, error2;
if (iqs5xx->bl_status == IQS5XX_BL_STATUS_RESET)
if (!iqs5xx->dev_id_info.bl_status)
return 0;
mutex_lock(&iqs5xx->lock);
@ -504,10 +500,6 @@ static int iqs5xx_axis_init(struct i2c_client *client)
input->open = iqs5xx_open;
input->close = iqs5xx_close;
input_set_capability(input, EV_ABS, ABS_MT_POSITION_X);
input_set_capability(input, EV_ABS, ABS_MT_POSITION_Y);
input_set_capability(input, EV_ABS, ABS_MT_PRESSURE);
input_set_drvdata(input, iqs5xx);
iqs5xx->input = input;
}
@ -520,26 +512,29 @@ static int iqs5xx_axis_init(struct i2c_client *client)
if (error)
return error;
input_abs_set_max(iqs5xx->input, ABS_MT_POSITION_X, max_x);
input_abs_set_max(iqs5xx->input, ABS_MT_POSITION_Y, max_y);
input_set_abs_params(iqs5xx->input, ABS_MT_POSITION_X, 0, max_x, 0, 0);
input_set_abs_params(iqs5xx->input, ABS_MT_POSITION_Y, 0, max_y, 0, 0);
input_set_abs_params(iqs5xx->input, ABS_MT_PRESSURE, 0, U16_MAX, 0, 0);
touchscreen_parse_properties(iqs5xx->input, true, prop);
if (prop->max_x > IQS5XX_XY_RES_MAX) {
dev_err(&client->dev, "Invalid maximum x-coordinate: %u > %u\n",
prop->max_x, IQS5XX_XY_RES_MAX);
/*
* The device reserves 0xFFFF for coordinates that correspond to slots
* which are not in a state of touch.
*/
if (prop->max_x >= U16_MAX || prop->max_y >= U16_MAX) {
dev_err(&client->dev, "Invalid touchscreen size: %u*%u\n",
prop->max_x, prop->max_y);
return -EINVAL;
} else if (prop->max_x != max_x) {
}
if (prop->max_x != max_x) {
error = iqs5xx_write_word(client, IQS5XX_X_RES, prop->max_x);
if (error)
return error;
}
if (prop->max_y > IQS5XX_XY_RES_MAX) {
dev_err(&client->dev, "Invalid maximum y-coordinate: %u > %u\n",
prop->max_y, IQS5XX_XY_RES_MAX);
return -EINVAL;
} else if (prop->max_y != max_y) {
if (prop->max_y != max_y) {
error = iqs5xx_write_word(client, IQS5XX_Y_RES, prop->max_y);
if (error)
return error;
@ -574,7 +569,7 @@ static int iqs5xx_dev_init(struct i2c_client *client)
* the missing zero is prepended).
*/
buf[0] = 0;
dev_id_info = (struct iqs5xx_dev_id_info *)&buf[(buf[1] > 0) ? 0 : 1];
dev_id_info = (struct iqs5xx_dev_id_info *)&buf[buf[1] ? 0 : 1];
switch (be16_to_cpu(dev_id_info->prod_num)) {
case IQS5XX_PROD_NUM_IQS550:
@ -587,35 +582,20 @@ static int iqs5xx_dev_init(struct i2c_client *client)
return -EINVAL;
}
switch (be16_to_cpu(dev_id_info->proj_num)) {
case IQS5XX_PROJ_NUM_A000:
dev_err(&client->dev, "Unsupported project number: %u\n",
be16_to_cpu(dev_id_info->proj_num));
return iqs5xx_bl_open(client);
case IQS5XX_PROJ_NUM_B000:
break;
default:
dev_err(&client->dev, "Unrecognized project number: %u\n",
be16_to_cpu(dev_id_info->proj_num));
return -EINVAL;
}
if (dev_id_info->major_ver < IQS5XX_MAJOR_VER_MIN) {
dev_err(&client->dev, "Unsupported major version: %u\n",
dev_id_info->major_ver);
/*
* With the product number recognized yet shifted by one byte, open the
* bootloader and wait for user space to convert the A000 device into a
* B000 device via new firmware.
*/
if (buf[1]) {
dev_err(&client->dev, "Opening bootloader for A000 device\n");
return iqs5xx_bl_open(client);
}
switch (dev_id_info->bl_status) {
case IQS5XX_BL_STATUS_AVAIL:
case IQS5XX_BL_STATUS_NONE:
break;
default:
dev_err(&client->dev,
"Unrecognized bootloader status: 0x%02X\n",
dev_id_info->bl_status);
return -EINVAL;
}
error = iqs5xx_read_burst(client, IQS5XX_EXP_FILE,
iqs5xx->exp_file, sizeof(iqs5xx->exp_file));
if (error)
return error;
error = iqs5xx_axis_init(client);
if (error)
@ -640,7 +620,7 @@ static int iqs5xx_dev_init(struct i2c_client *client)
if (error)
return error;
iqs5xx->bl_status = dev_id_info->bl_status;
iqs5xx->dev_id_info = *dev_id_info;
/*
* The following delay allows ATI to complete before the open and close
@ -666,7 +646,7 @@ static irqreturn_t iqs5xx_irq(int irq, void *data)
* RDY output during bootloader mode. If the device operates outside of
* bootloader mode, the input device is guaranteed to be allocated.
*/
if (iqs5xx->bl_status == IQS5XX_BL_STATUS_RESET)
if (!iqs5xx->dev_id_info.bl_status)
return IRQ_NONE;
error = iqs5xx_read_burst(client, IQS5XX_SYS_INFO0,
@ -852,12 +832,9 @@ static int iqs5xx_fw_file_parse(struct i2c_client *client,
static int iqs5xx_fw_file_write(struct i2c_client *client, const char *fw_file)
{
struct iqs5xx_private *iqs5xx = i2c_get_clientdata(client);
int error, error_bl = 0;
int error, error_init = 0;
u8 *pmap;
if (iqs5xx->bl_status == IQS5XX_BL_STATUS_NONE)
return -EPERM;
pmap = kzalloc(IQS5XX_PMAP_LEN, GFP_KERNEL);
if (!pmap)
return -ENOMEM;
@ -875,7 +852,7 @@ static int iqs5xx_fw_file_write(struct i2c_client *client, const char *fw_file)
*/
disable_irq(client->irq);
iqs5xx->bl_status = IQS5XX_BL_STATUS_RESET;
iqs5xx->dev_id_info.bl_status = 0;
error = iqs5xx_bl_cmd(client, IQS5XX_BL_CMD_VER, 0);
if (error) {
@ -895,21 +872,14 @@ static int iqs5xx_fw_file_write(struct i2c_client *client, const char *fw_file)
error = iqs5xx_bl_verify(client, IQS5XX_CSTM,
pmap + IQS5XX_CHKSM_LEN + IQS5XX_APP_LEN,
IQS5XX_CSTM_LEN);
if (error)
goto err_reset;
error = iqs5xx_bl_cmd(client, IQS5XX_BL_CMD_EXEC, 0);
err_reset:
if (error) {
iqs5xx_reset(client);
usleep_range(10000, 10100);
}
iqs5xx_reset(client);
usleep_range(15000, 15100);
error_bl = error;
error = iqs5xx_dev_init(client);
if (!error && iqs5xx->bl_status == IQS5XX_BL_STATUS_RESET)
error = -EINVAL;
error_init = iqs5xx_dev_init(client);
if (!iqs5xx->dev_id_info.bl_status)
error_init = error_init ? : -EINVAL;
enable_irq(client->irq);
@ -918,10 +888,7 @@ err_reset:
err_kfree:
kfree(pmap);
if (error_bl)
return error_bl;
return error;
return error ? : error_init;
}
static ssize_t fw_file_store(struct device *dev,
@ -968,14 +935,47 @@ static ssize_t fw_file_store(struct device *dev,
return count;
}
static ssize_t fw_info_show(struct device *dev,
struct device_attribute *attr, char *buf)
{
struct iqs5xx_private *iqs5xx = dev_get_drvdata(dev);
if (!iqs5xx->dev_id_info.bl_status)
return -ENODATA;
return scnprintf(buf, PAGE_SIZE, "%u.%u.%u.%u:%u.%u\n",
be16_to_cpu(iqs5xx->dev_id_info.prod_num),
be16_to_cpu(iqs5xx->dev_id_info.proj_num),
iqs5xx->dev_id_info.major_ver,
iqs5xx->dev_id_info.minor_ver,
iqs5xx->exp_file[0], iqs5xx->exp_file[1]);
}
static DEVICE_ATTR_WO(fw_file);
static DEVICE_ATTR_RO(fw_info);
static struct attribute *iqs5xx_attrs[] = {
&dev_attr_fw_file.attr,
&dev_attr_fw_info.attr,
NULL,
};
static umode_t iqs5xx_attr_is_visible(struct kobject *kobj,
struct attribute *attr, int i)
{
struct device *dev = kobj_to_dev(kobj);
struct iqs5xx_private *iqs5xx = dev_get_drvdata(dev);
if (attr == &dev_attr_fw_file.attr &&
(iqs5xx->dev_id_info.bl_status == IQS5XX_BL_STATUS_NONE ||
!iqs5xx->reset_gpio))
return 0;
return attr->mode;
}
static const struct attribute_group iqs5xx_attr_group = {
.is_visible = iqs5xx_attr_is_visible,
.attrs = iqs5xx_attrs,
};
@ -1032,8 +1032,8 @@ static int iqs5xx_probe(struct i2c_client *client,
i2c_set_clientdata(client, iqs5xx);
iqs5xx->client = client;
iqs5xx->reset_gpio = devm_gpiod_get(&client->dev,
"reset", GPIOD_OUT_LOW);
iqs5xx->reset_gpio = devm_gpiod_get_optional(&client->dev,
"reset", GPIOD_OUT_LOW);
if (IS_ERR(iqs5xx->reset_gpio)) {
error = PTR_ERR(iqs5xx->reset_gpio);
dev_err(&client->dev, "Failed to request GPIO: %d\n", error);
@ -1042,9 +1042,6 @@ static int iqs5xx_probe(struct i2c_client *client,
mutex_init(&iqs5xx->lock);
iqs5xx_reset(client);
usleep_range(10000, 10100);
error = iqs5xx_dev_init(client);
if (error)
return error;

View File

@ -34,18 +34,18 @@
#define LPC32XX_TSC_AUX_MIN 0x38
#define LPC32XX_TSC_AUX_MAX 0x3C
#define LPC32XX_TSC_STAT_FIFO_OVRRN (1 << 8)
#define LPC32XX_TSC_STAT_FIFO_EMPTY (1 << 7)
#define LPC32XX_TSC_STAT_FIFO_OVRRN BIT(8)
#define LPC32XX_TSC_STAT_FIFO_EMPTY BIT(7)
#define LPC32XX_TSC_SEL_DEFVAL 0x0284
#define LPC32XX_TSC_ADCCON_IRQ_TO_FIFO_4 (0x1 << 11)
#define LPC32XX_TSC_ADCCON_X_SAMPLE_SIZE(s) ((10 - (s)) << 7)
#define LPC32XX_TSC_ADCCON_Y_SAMPLE_SIZE(s) ((10 - (s)) << 4)
#define LPC32XX_TSC_ADCCON_POWER_UP (1 << 2)
#define LPC32XX_TSC_ADCCON_AUTO_EN (1 << 0)
#define LPC32XX_TSC_ADCCON_POWER_UP BIT(2)
#define LPC32XX_TSC_ADCCON_AUTO_EN BIT(0)
#define LPC32XX_TSC_FIFO_TS_P_LEVEL (1 << 31)
#define LPC32XX_TSC_FIFO_TS_P_LEVEL BIT(31)
#define LPC32XX_TSC_FIFO_NORMALIZE_X_VAL(x) (((x) & 0x03FF0000) >> 16)
#define LPC32XX_TSC_FIFO_NORMALIZE_Y_VAL(y) ((y) & 0x000003FF)

View File

@ -1502,7 +1502,8 @@ static int mip4_probe(struct i2c_client *client, const struct i2c_device_id *id)
error = devm_request_threaded_irq(&client->dev, client->irq,
NULL, mip4_interrupt,
IRQF_ONESHOT, MIP4_DEVICE_NAME, ts);
IRQF_ONESHOT | IRQF_NO_AUTOEN,
MIP4_DEVICE_NAME, ts);
if (error) {
dev_err(&client->dev,
"Failed to request interrupt %d: %d\n",
@ -1510,8 +1511,6 @@ static int mip4_probe(struct i2c_client *client, const struct i2c_device_id *id)
return error;
}
disable_irq(client->irq);
error = input_register_device(input);
if (error) {
dev_err(&client->dev,

View File

@ -1,5 +1,5 @@
// SPDX-License-Identifier: GPL-2.0
// Melfas MMS114/MMS152 touchscreen device driver
// Melfas MMS114/MMS136/MMS152 touchscreen device driver
//
// Copyright (c) 2012 Samsung Electronics Co., Ltd.
// Author: Joonyoung Shim <jy0922.shim@samsung.com>
@ -44,7 +44,8 @@
#define MMS114_MAX_AREA 0xff
#define MMS114_MAX_TOUCH 10
#define MMS114_PACKET_NUM 8
#define MMS114_EVENT_SIZE 8
#define MMS136_EVENT_SIZE 6
/* Touch type */
#define MMS114_TYPE_NONE 0
@ -53,6 +54,7 @@
enum mms_type {
TYPE_MMS114 = 114,
TYPE_MMS136 = 136,
TYPE_MMS152 = 152,
TYPE_MMS345L = 345,
};
@ -209,7 +211,11 @@ static irqreturn_t mms114_interrupt(int irq, void *dev_id)
if (packet_size <= 0)
goto out;
touch_size = packet_size / MMS114_PACKET_NUM;
/* MMS136 has slightly different event size */
if (data->type == TYPE_MMS136)
touch_size = packet_size / MMS136_EVENT_SIZE;
else
touch_size = packet_size / MMS114_EVENT_SIZE;
error = __mms114_read_reg(data, MMS114_INFORMATION, packet_size,
(u8 *)touch);
@ -275,6 +281,7 @@ static int mms114_get_version(struct mms114_data *data)
break;
case TYPE_MMS114:
case TYPE_MMS136:
error = __mms114_read_reg(data, MMS114_TSP_REV, 6, buf);
if (error)
return error;
@ -297,8 +304,8 @@ static int mms114_setup_regs(struct mms114_data *data)
if (error < 0)
return error;
/* Only MMS114 has configuration and power on registers */
if (data->type != TYPE_MMS114)
/* Only MMS114 and MMS136 have configuration and power on registers */
if (data->type != TYPE_MMS114 && data->type != TYPE_MMS136)
return 0;
error = mms114_set_active(data, true);
@ -480,7 +487,7 @@ static int mms114_probe(struct i2c_client *client,
0, data->props.max_y, 0, 0);
}
if (data->type == TYPE_MMS114) {
if (data->type == TYPE_MMS114 || data->type == TYPE_MMS136) {
/*
* The firmware handles movement and pressure fuzz, so
* don't duplicate that in software.
@ -530,13 +537,13 @@ static int mms114_probe(struct i2c_client *client,
}
error = devm_request_threaded_irq(&client->dev, client->irq,
NULL, mms114_interrupt, IRQF_ONESHOT,
NULL, mms114_interrupt,
IRQF_ONESHOT | IRQF_NO_AUTOEN,
dev_name(&client->dev), data);
if (error) {
dev_err(&client->dev, "Failed to register interrupt\n");
return error;
}
disable_irq(client->irq);
error = input_register_device(data->input_dev);
if (error) {
@ -604,6 +611,9 @@ static const struct of_device_id mms114_dt_match[] = {
{
.compatible = "melfas,mms114",
.data = (void *)TYPE_MMS114,
}, {
.compatible = "melfas,mms136",
.data = (void *)TYPE_MMS136,
}, {
.compatible = "melfas,mms152",
.data = (void *)TYPE_MMS152,

View File

@ -0,0 +1,337 @@
// SPDX-License-Identifier: GPL-2.0-only
/*
* Driver for MStar msg2638 touchscreens
*
* Copyright (c) 2021 Vincent Knecht <vincent.knecht@mailoo.org>
*
* Checksum and IRQ handler based on mstar_drv_common.c and
* mstar_drv_mutual_fw_control.c
* Copyright (c) 2006-2012 MStar Semiconductor, Inc.
*
* Driver structure based on zinitix.c by Michael Srba <Michael.Srba@seznam.cz>
*/
#include <linux/delay.h>
#include <linux/gpio/consumer.h>
#include <linux/i2c.h>
#include <linux/input.h>
#include <linux/input/mt.h>
#include <linux/input/touchscreen.h>
#include <linux/interrupt.h>
#include <linux/kernel.h>
#include <linux/mod_devicetable.h>
#include <linux/module.h>
#include <linux/regulator/consumer.h>
#include <linux/slab.h>
#define MODE_DATA_RAW 0x5A
#define MAX_SUPPORTED_FINGER_NUM 5
#define CHIP_ON_DELAY_MS 15
#define FIRMWARE_ON_DELAY_MS 50
#define RESET_DELAY_MIN_US 10000
#define RESET_DELAY_MAX_US 11000
struct packet {
u8 xy_hi; /* higher bits of x and y coordinates */
u8 x_low;
u8 y_low;
u8 pressure;
};
struct touch_event {
u8 mode;
struct packet pkt[MAX_SUPPORTED_FINGER_NUM];
u8 proximity;
u8 checksum;
};
struct msg2638_ts_data {
struct i2c_client *client;
struct input_dev *input_dev;
struct touchscreen_properties prop;
struct regulator_bulk_data supplies[2];
struct gpio_desc *reset_gpiod;
};
static u8 msg2638_checksum(u8 *data, u32 length)
{
s32 sum = 0;
u32 i;
for (i = 0; i < length; i++)
sum += data[i];
return (u8)((-sum) & 0xFF);
}
static irqreturn_t msg2638_ts_irq_handler(int irq, void *msg2638_handler)
{
struct msg2638_ts_data *msg2638 = msg2638_handler;
struct i2c_client *client = msg2638->client;
struct input_dev *input = msg2638->input_dev;
struct touch_event touch_event;
u32 len = sizeof(touch_event);
struct i2c_msg msg[] = {
{
.addr = client->addr,
.flags = I2C_M_RD,
.len = sizeof(touch_event),
.buf = (u8 *)&touch_event,
},
};
struct packet *p;
u16 x, y;
int ret;
int i;
ret = i2c_transfer(client->adapter, msg, ARRAY_SIZE(msg));
if (ret != ARRAY_SIZE(msg)) {
dev_err(&client->dev,
"Failed I2C transfer in irq handler: %d\n",
ret < 0 ? ret : -EIO);
goto out;
}
if (touch_event.mode != MODE_DATA_RAW)
goto out;
if (msg2638_checksum((u8 *)&touch_event, len - 1) !=
touch_event.checksum) {
dev_err(&client->dev, "Failed checksum!\n");
goto out;
}
for (i = 0; i < MAX_SUPPORTED_FINGER_NUM; i++) {
p = &touch_event.pkt[i];
/* Ignore non-pressed finger data */
if (p->xy_hi == 0xFF && p->x_low == 0xFF && p->y_low == 0xFF)
continue;
x = (((p->xy_hi & 0xF0) << 4) | p->x_low);
y = (((p->xy_hi & 0x0F) << 8) | p->y_low);
input_mt_slot(input, i);
input_mt_report_slot_state(input, MT_TOOL_FINGER, true);
touchscreen_report_pos(input, &msg2638->prop, x, y, true);
}
input_mt_sync_frame(msg2638->input_dev);
input_sync(msg2638->input_dev);
out:
return IRQ_HANDLED;
}
static void msg2638_reset(struct msg2638_ts_data *msg2638)
{
gpiod_set_value_cansleep(msg2638->reset_gpiod, 1);
usleep_range(RESET_DELAY_MIN_US, RESET_DELAY_MAX_US);
gpiod_set_value_cansleep(msg2638->reset_gpiod, 0);
msleep(FIRMWARE_ON_DELAY_MS);
}
static int msg2638_start(struct msg2638_ts_data *msg2638)
{
int error;
error = regulator_bulk_enable(ARRAY_SIZE(msg2638->supplies),
msg2638->supplies);
if (error) {
dev_err(&msg2638->client->dev,
"Failed to enable regulators: %d\n", error);
return error;
}
msleep(CHIP_ON_DELAY_MS);
msg2638_reset(msg2638);
enable_irq(msg2638->client->irq);
return 0;
}
static int msg2638_stop(struct msg2638_ts_data *msg2638)
{
int error;
disable_irq(msg2638->client->irq);
error = regulator_bulk_disable(ARRAY_SIZE(msg2638->supplies),
msg2638->supplies);
if (error) {
dev_err(&msg2638->client->dev,
"Failed to disable regulators: %d\n", error);
return error;
}
return 0;
}
static int msg2638_input_open(struct input_dev *dev)
{
struct msg2638_ts_data *msg2638 = input_get_drvdata(dev);
return msg2638_start(msg2638);
}
static void msg2638_input_close(struct input_dev *dev)
{
struct msg2638_ts_data *msg2638 = input_get_drvdata(dev);
msg2638_stop(msg2638);
}
static int msg2638_init_input_dev(struct msg2638_ts_data *msg2638)
{
struct device *dev = &msg2638->client->dev;
struct input_dev *input_dev;
int error;
input_dev = devm_input_allocate_device(dev);
if (!input_dev) {
dev_err(dev, "Failed to allocate input device.\n");
return -ENOMEM;
}
input_set_drvdata(input_dev, msg2638);
msg2638->input_dev = input_dev;
input_dev->name = "MStar TouchScreen";
input_dev->phys = "input/ts";
input_dev->id.bustype = BUS_I2C;
input_dev->open = msg2638_input_open;
input_dev->close = msg2638_input_close;
input_set_capability(input_dev, EV_ABS, ABS_MT_POSITION_X);
input_set_capability(input_dev, EV_ABS, ABS_MT_POSITION_Y);
touchscreen_parse_properties(input_dev, true, &msg2638->prop);
if (!msg2638->prop.max_x || !msg2638->prop.max_y) {
dev_err(dev, "touchscreen-size-x and/or touchscreen-size-y not set in properties\n");
return -EINVAL;
}
error = input_mt_init_slots(input_dev, MAX_SUPPORTED_FINGER_NUM,
INPUT_MT_DIRECT | INPUT_MT_DROP_UNUSED);
if (error) {
dev_err(dev, "Failed to initialize MT slots: %d\n", error);
return error;
}
error = input_register_device(input_dev);
if (error) {
dev_err(dev, "Failed to register input device: %d\n", error);
return error;
}
return 0;
}
static int msg2638_ts_probe(struct i2c_client *client)
{
struct device *dev = &client->dev;
struct msg2638_ts_data *msg2638;
int error;
if (!i2c_check_functionality(client->adapter, I2C_FUNC_I2C)) {
dev_err(dev, "Failed to assert adapter's support for plain I2C.\n");
return -ENXIO;
}
msg2638 = devm_kzalloc(dev, sizeof(*msg2638), GFP_KERNEL);
if (!msg2638)
return -ENOMEM;
msg2638->client = client;
i2c_set_clientdata(client, msg2638);
msg2638->supplies[0].supply = "vdd";
msg2638->supplies[1].supply = "vddio";
error = devm_regulator_bulk_get(dev, ARRAY_SIZE(msg2638->supplies),
msg2638->supplies);
if (error) {
dev_err(dev, "Failed to get regulators: %d\n", error);
return error;
}
msg2638->reset_gpiod = devm_gpiod_get(dev, "reset", GPIOD_OUT_LOW);
if (IS_ERR(msg2638->reset_gpiod)) {
error = PTR_ERR(msg2638->reset_gpiod);
dev_err(dev, "Failed to request reset GPIO: %d\n", error);
return error;
}
error = msg2638_init_input_dev(msg2638);
if (error) {
dev_err(dev, "Failed to initialize input device: %d\n", error);
return error;
}
error = devm_request_threaded_irq(dev, client->irq,
NULL, msg2638_ts_irq_handler,
IRQF_ONESHOT | IRQF_NO_AUTOEN,
client->name, msg2638);
if (error) {
dev_err(dev, "Failed to request IRQ: %d\n", error);
return error;
}
return 0;
}
static int __maybe_unused msg2638_suspend(struct device *dev)
{
struct i2c_client *client = to_i2c_client(dev);
struct msg2638_ts_data *msg2638 = i2c_get_clientdata(client);
mutex_lock(&msg2638->input_dev->mutex);
if (input_device_enabled(msg2638->input_dev))
msg2638_stop(msg2638);
mutex_unlock(&msg2638->input_dev->mutex);
return 0;
}
static int __maybe_unused msg2638_resume(struct device *dev)
{
struct i2c_client *client = to_i2c_client(dev);
struct msg2638_ts_data *msg2638 = i2c_get_clientdata(client);
int ret = 0;
mutex_lock(&msg2638->input_dev->mutex);
if (input_device_enabled(msg2638->input_dev))
ret = msg2638_start(msg2638);
mutex_unlock(&msg2638->input_dev->mutex);
return ret;
}
static SIMPLE_DEV_PM_OPS(msg2638_pm_ops, msg2638_suspend, msg2638_resume);
static const struct of_device_id msg2638_of_match[] = {
{ .compatible = "mstar,msg2638" },
{ }
};
MODULE_DEVICE_TABLE(of, msg2638_of_match);
static struct i2c_driver msg2638_ts_driver = {
.probe_new = msg2638_ts_probe,
.driver = {
.name = "MStar-TS",
.pm = &msg2638_pm_ops,
.of_match_table = msg2638_of_match,
},
};
module_i2c_driver(msg2638_ts_driver);
MODULE_AUTHOR("Vincent Knecht <vincent.knecht@mailoo.org>");
MODULE_DESCRIPTION("MStar MSG2638 touchscreen driver");
MODULE_LICENSE("GPL v2");

View File

@ -20,6 +20,7 @@
#include <linux/input/mt.h>
#include <linux/input/touchscreen.h>
#include <linux/pm.h>
#include <linux/pm_runtime.h>
#include <linux/irq.h>
#include <linux/regulator/consumer.h>
@ -335,10 +336,8 @@ static int silead_ts_get_id(struct i2c_client *client)
error = i2c_smbus_read_i2c_block_data(client, SILEAD_REG_ID,
sizeof(chip_id), (u8 *)&chip_id);
if (error < 0) {
dev_err(&client->dev, "Chip ID read error %d\n", error);
if (error < 0)
return error;
}
data->chip_id = le32_to_cpu(chip_id);
dev_info(&client->dev, "Silead chip ID: 0x%8X", data->chip_id);
@ -351,12 +350,49 @@ static int silead_ts_setup(struct i2c_client *client)
int error;
u32 status;
/*
* Some buggy BIOS-es bring up the chip in a stuck state where it
* blocks the I2C bus. The following steps are necessary to
* unstuck the chip / bus:
* 1. Turn off the Silead chip.
* 2. Try to do an I2C transfer with the chip, this will fail in
* response to which the I2C-bus-driver will call:
* i2c_recover_bus() which will unstuck the I2C-bus. Note the
* unstuck-ing of the I2C bus only works if we first drop the
* chip off the bus by turning it off.
* 3. Turn the chip back on.
*
* On the x86/ACPI systems were this problem is seen, step 1. and
* 3. require making ACPI calls and dealing with ACPI Power
* Resources. The workaround below runtime-suspends the chip to
* turn it off, leaving it up to the ACPI subsystem to deal with
* this.
*/
if (device_property_read_bool(&client->dev,
"silead,stuck-controller-bug")) {
pm_runtime_set_active(&client->dev);
pm_runtime_enable(&client->dev);
pm_runtime_allow(&client->dev);
pm_runtime_suspend(&client->dev);
dev_warn(&client->dev, FW_BUG "Stuck I2C bus: please ignore the next 'controller timed out' error\n");
silead_ts_get_id(client);
/* The forbid will also resume the device */
pm_runtime_forbid(&client->dev);
pm_runtime_disable(&client->dev);
}
silead_ts_set_power(client, SILEAD_POWER_OFF);
silead_ts_set_power(client, SILEAD_POWER_ON);
error = silead_ts_get_id(client);
if (error)
if (error) {
dev_err(&client->dev, "Chip ID read error %d\n", error);
return error;
}
error = silead_ts_init(client);
if (error)
@ -486,7 +522,7 @@ static int silead_ts_probe(struct i2c_client *client,
silead_ts_read_props(client);
/* We must have the IRQ provided by DT or ACPI subsytem */
/* We must have the IRQ provided by DT or ACPI subsystem */
if (client->irq <= 0)
return -ENODEV;

View File

@ -691,10 +691,9 @@ static int stmfts_probe(struct i2c_client *client,
* interrupts. To be on the safe side it's better to not enable
* the interrupts during their request.
*/
irq_set_status_flags(client->irq, IRQ_NOAUTOEN);
err = devm_request_threaded_irq(&client->dev, client->irq,
NULL, stmfts_irq_handler,
IRQF_ONESHOT,
IRQF_ONESHOT | IRQF_NO_AUTOEN,
"stmfts_irq", sdata);
if (err)
return err;

View File

@ -19,6 +19,8 @@
#ifndef _TSC2007_H
#define _TSC2007_H
struct gpio_desc;
#define TSC2007_MEASURE_TEMP0 (0x0 << 4)
#define TSC2007_MEASURE_AUX (0x2 << 4)
#define TSC2007_MEASURE_TEMP1 (0x4 << 4)
@ -69,7 +71,7 @@ struct tsc2007 {
int fuzzy;
int fuzzz;
unsigned int gpio;
struct gpio_desc *gpiod;
int irq;
wait_queue_head_t wait;

View File

@ -19,11 +19,12 @@
#include <linux/module.h>
#include <linux/slab.h>
#include <linux/gpio/consumer.h>
#include <linux/input.h>
#include <linux/interrupt.h>
#include <linux/i2c.h>
#include <linux/of_device.h>
#include <linux/of_gpio.h>
#include <linux/mod_devicetable.h>
#include <linux/property.h>
#include <linux/platform_data/tsc2007.h>
#include "tsc2007.h"
@ -220,71 +221,58 @@ static void tsc2007_close(struct input_dev *input_dev)
tsc2007_stop(ts);
}
#ifdef CONFIG_OF
static int tsc2007_get_pendown_state_gpio(struct device *dev)
{
struct i2c_client *client = to_i2c_client(dev);
struct tsc2007 *ts = i2c_get_clientdata(client);
return !gpio_get_value(ts->gpio);
return gpiod_get_value(ts->gpiod);
}
static int tsc2007_probe_dt(struct i2c_client *client, struct tsc2007 *ts)
static int tsc2007_probe_properties(struct device *dev, struct tsc2007 *ts)
{
struct device_node *np = client->dev.of_node;
u32 val32;
u64 val64;
if (!np) {
dev_err(&client->dev, "missing device tree data\n");
return -EINVAL;
}
if (!of_property_read_u32(np, "ti,max-rt", &val32))
if (!device_property_read_u32(dev, "ti,max-rt", &val32))
ts->max_rt = val32;
else
ts->max_rt = MAX_12BIT;
if (!of_property_read_u32(np, "ti,fuzzx", &val32))
if (!device_property_read_u32(dev, "ti,fuzzx", &val32))
ts->fuzzx = val32;
if (!of_property_read_u32(np, "ti,fuzzy", &val32))
if (!device_property_read_u32(dev, "ti,fuzzy", &val32))
ts->fuzzy = val32;
if (!of_property_read_u32(np, "ti,fuzzz", &val32))
if (!device_property_read_u32(dev, "ti,fuzzz", &val32))
ts->fuzzz = val32;
if (!of_property_read_u64(np, "ti,poll-period", &val64))
if (!device_property_read_u64(dev, "ti,poll-period", &val64))
ts->poll_period = msecs_to_jiffies(val64);
else
ts->poll_period = msecs_to_jiffies(1);
if (!of_property_read_u32(np, "ti,x-plate-ohms", &val32)) {
if (!device_property_read_u32(dev, "ti,x-plate-ohms", &val32)) {
ts->x_plate_ohms = val32;
} else {
dev_err(&client->dev, "missing ti,x-plate-ohms devicetree property.");
dev_err(dev, "Missing ti,x-plate-ohms device property\n");
return -EINVAL;
}
ts->gpio = of_get_gpio(np, 0);
if (gpio_is_valid(ts->gpio))
ts->gpiod = devm_gpiod_get_optional(dev, NULL, GPIOD_IN);
if (IS_ERR(ts->gpiod))
return PTR_ERR(ts->gpiod);
if (ts->gpiod)
ts->get_pendown_state = tsc2007_get_pendown_state_gpio;
else
dev_warn(&client->dev,
"GPIO not specified in DT (of_get_gpio returned %d)\n",
ts->gpio);
dev_warn(dev, "Pen down GPIO is not specified in properties\n");
return 0;
}
#else
static int tsc2007_probe_dt(struct i2c_client *client, struct tsc2007 *ts)
{
dev_err(&client->dev, "platform data is required!\n");
return -EINVAL;
}
#endif
static int tsc2007_probe_pdev(struct i2c_client *client, struct tsc2007 *ts,
static int tsc2007_probe_pdev(struct device *dev, struct tsc2007 *ts,
const struct tsc2007_platform_data *pdata,
const struct i2c_device_id *id)
{
@ -299,7 +287,7 @@ static int tsc2007_probe_pdev(struct i2c_client *client, struct tsc2007 *ts,
ts->fuzzz = pdata->fuzzz;
if (pdata->x_plate_ohms == 0) {
dev_err(&client->dev, "x_plate_ohms is not set up in platform data");
dev_err(dev, "x_plate_ohms is not set up in platform data\n");
return -EINVAL;
}
@ -332,9 +320,9 @@ static int tsc2007_probe(struct i2c_client *client,
return -ENOMEM;
if (pdata)
err = tsc2007_probe_pdev(client, ts, pdata, id);
err = tsc2007_probe_pdev(&client->dev, ts, pdata, id);
else
err = tsc2007_probe_dt(client, ts);
err = tsc2007_probe_properties(&client->dev, ts);
if (err)
return err;
@ -431,18 +419,16 @@ static const struct i2c_device_id tsc2007_idtable[] = {
MODULE_DEVICE_TABLE(i2c, tsc2007_idtable);
#ifdef CONFIG_OF
static const struct of_device_id tsc2007_of_match[] = {
{ .compatible = "ti,tsc2007" },
{ /* sentinel */ }
};
MODULE_DEVICE_TABLE(of, tsc2007_of_match);
#endif
static struct i2c_driver tsc2007_driver = {
.driver = {
.name = "tsc2007",
.of_match_table = of_match_ptr(tsc2007_of_match),
.of_match_table = tsc2007_of_match,
},
.id_table = tsc2007_idtable,
.probe = tsc2007_probe,

View File

@ -145,15 +145,16 @@ static void wacom_i2c_close(struct input_dev *dev)
}
static int wacom_i2c_probe(struct i2c_client *client,
const struct i2c_device_id *id)
const struct i2c_device_id *id)
{
struct device *dev = &client->dev;
struct wacom_i2c *wac_i2c;
struct input_dev *input;
struct wacom_features features = { 0 };
int error;
if (!i2c_check_functionality(client->adapter, I2C_FUNC_I2C)) {
dev_err(&client->dev, "i2c_check_functionality error\n");
dev_err(dev, "i2c_check_functionality error\n");
return -EIO;
}
@ -161,21 +162,22 @@ static int wacom_i2c_probe(struct i2c_client *client,
if (error)
return error;
wac_i2c = kzalloc(sizeof(*wac_i2c), GFP_KERNEL);
input = input_allocate_device();
if (!wac_i2c || !input) {
error = -ENOMEM;
goto err_free_mem;
}
wac_i2c = devm_kzalloc(dev, sizeof(*wac_i2c), GFP_KERNEL);
if (!wac_i2c)
return -ENOMEM;
wac_i2c->client = client;
input = devm_input_allocate_device(dev);
if (!input)
return -ENOMEM;
wac_i2c->input = input;
input->name = "Wacom I2C Digitizer";
input->id.bustype = BUS_I2C;
input->id.vendor = 0x56a;
input->id.version = features.fw_version;
input->dev.parent = &client->dev;
input->open = wacom_i2c_open;
input->close = wacom_i2c_close;
@ -194,13 +196,11 @@ static int wacom_i2c_probe(struct i2c_client *client,
input_set_drvdata(input, wac_i2c);
error = request_threaded_irq(client->irq, NULL, wacom_i2c_irq,
IRQF_TRIGGER_LOW | IRQF_ONESHOT,
"wacom_i2c", wac_i2c);
error = devm_request_threaded_irq(dev, client->irq, NULL, wacom_i2c_irq,
IRQF_ONESHOT, "wacom_i2c", wac_i2c);
if (error) {
dev_err(&client->dev,
"Failed to enable IRQ, error: %d\n", error);
goto err_free_mem;
dev_err(dev, "Failed to request IRQ: %d\n", error);
return error;
}
/* Disable the IRQ, we'll enable it in wac_i2c_open() */
@ -208,31 +208,10 @@ static int wacom_i2c_probe(struct i2c_client *client,
error = input_register_device(wac_i2c->input);
if (error) {
dev_err(&client->dev,
"Failed to register input device, error: %d\n", error);
goto err_free_irq;
dev_err(dev, "Failed to register input device: %d\n", error);
return error;
}
i2c_set_clientdata(client, wac_i2c);
return 0;
err_free_irq:
free_irq(client->irq, wac_i2c);
err_free_mem:
input_free_device(input);
kfree(wac_i2c);
return error;
}
static int wacom_i2c_remove(struct i2c_client *client)
{
struct wacom_i2c *wac_i2c = i2c_get_clientdata(client);
free_irq(client->irq, wac_i2c);
input_unregister_device(wac_i2c->input);
kfree(wac_i2c);
return 0;
}
@ -269,7 +248,6 @@ static struct i2c_driver wacom_i2c_driver = {
},
.probe = wacom_i2c_probe,
.remove = wacom_i2c_remove,
.id_table = wacom_i2c_id,
};
module_i2c_driver(wacom_i2c_driver);

View File

@ -317,14 +317,13 @@ static int wm831x_ts_probe(struct platform_device *pdev)
error = request_threaded_irq(wm831x_ts->data_irq,
NULL, wm831x_ts_data_irq,
irqf | IRQF_ONESHOT,
irqf | IRQF_ONESHOT | IRQF_NO_AUTOEN,
"Touchscreen data", wm831x_ts);
if (error) {
dev_err(&pdev->dev, "Failed to request data IRQ %d: %d\n",
wm831x_ts->data_irq, error);
goto err_alloc;
}
disable_irq(wm831x_ts->data_irq);
if (pdata && pdata->pd_irqf)
irqf = pdata->pd_irqf;

View File

@ -513,10 +513,10 @@ static int zinitix_ts_probe(struct i2c_client *client)
return -EINVAL;
}
irq_set_status_flags(client->irq, IRQ_NOAUTOEN);
error = devm_request_threaded_irq(&client->dev, client->irq,
NULL, zinitix_ts_irq_handler,
IRQF_ONESHOT, client->name, bt541);
IRQF_ONESHOT | IRQF_NO_AUTOEN,
client->name, bt541);
if (error) {
dev_err(&client->dev, "Failed to request IRQ: %d\n", error);
return error;

View File

@ -0,0 +1,10 @@
/* SPDX-License-Identifier: GPL-2.0+ */
#ifndef _DT_BINDINGS_ATMEL_MAXTOUCH_H
#define _DT_BINDINGS_ATMEL_MAXTOUCH_H
#define ATMEL_MXT_WAKEUP_NONE 0
#define ATMEL_MXT_WAKEUP_I2C_SCL 1
#define ATMEL_MXT_WAKEUP_GPIO 2
#endif /* _DT_BINDINGS_ATMEL_MAXTOUCH_H */