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:
Коммит
aef511fb91
|
@ -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
|
||||
|
|
|
@ -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>;
|
||||
};
|
||||
};
|
||||
|
||||
|
|
|
@ -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>;
|
||||
};
|
||||
};
|
||||
};
|
||||
};
|
||||
|
||||
...
|
|
@ -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>;
|
||||
};
|
||||
};
|
||||
|
||||
...
|
|
@ -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>;
|
||||
};
|
||||
};
|
||||
|
||||
...
|
|
@ -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;
|
||||
};
|
||||
};
|
|
@ -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>;
|
||||
};
|
||||
|
||||
/* ... */
|
||||
};
|
|
@ -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;
|
||||
};
|
||||
};
|
||||
|
||||
...
|
|
@ -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;
|
||||
};
|
||||
|
||||
/* ... */
|
||||
};
|
|
@ -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>;
|
||||
};
|
||||
};
|
||||
|
||||
...
|
|
@ -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,.*":
|
||||
|
|
|
@ -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);
|
||||
|
||||
...
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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 */
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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,
|
||||
};
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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");
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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)
|
||||
|
|
Разница между файлами не показана из-за своего большого размера
Загрузить разницу
|
@ -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,
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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);
|
||||
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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");
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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");
|
|
@ -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);
|
||||
|
|
|
@ -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");
|
|
@ -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;
|
||||
|
|
|
@ -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)
|
||||
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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");
|
|
@ -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;
|
||||
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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 */
|
Загрузка…
Ссылка в новой задаче