mirror of
https://github.com/torvalds/linux.git
synced 2024-12-23 03:11:46 +00:00
Input updates for v6.12-rc0
- support for PixArt PS/2 touchpad - updates to tsc2004/5, usbtouchscreen, and zforce_ts drivers - support for GPIO-only mode for ADP55888 controller - support for touch keys in Zinitix driver - support for querying density of Synaptics sensors - sysfs interface for Goodex "Berlin" devices to read and write touch IC registers - more quirks to i8042 to handle various Tuxedo laptops - a number of drivers have been converted to using "guard" notation when acquiring various locks, as well as using other cleanup functions to simplify releasing of resources (with more drivers to follow) - evdev will limit amount of data that can be written into an evdev instance at a given time to 4096 bytes (170 input events) to avoid holding evdev->mutex for too long and starving other users - Spitz has been converted to use software nodes/properties to describe its matrix keypad and GPIO-connected LEDs - msc5000_ts, msc_touchkey and keypad-nomadik-ske drivers have been removed since noone in mainline have been using them - other assorted cleanups and fixes. -----BEGIN PGP SIGNATURE----- iHUEABYIAB0WIQST2eWILY88ieB2DOtAj56VGEWXnAUCZvGnWgAKCRBAj56VGEWX nDjGAQCHcRk1icJlyjx3KA85gdILriDB9zMNJnCnbYXrSfGbOQD+LZNjO3po26zB wloLEYTKRu3E/oWEI8VcfIN2m89vxw4= =bDsb -----END PGP SIGNATURE----- Merge tag 'input-for-v6.12-rc0' of git://git.kernel.org/pub/scm/linux/kernel/git/dtor/input Pull input updates from Dmitry Torokhov: - support for PixArt PS/2 touchpad - updates to tsc2004/5, usbtouchscreen, and zforce_ts drivers - support for GPIO-only mode for ADP55888 controller - support for touch keys in Zinitix driver - support for querying density of Synaptics sensors - sysfs interface for Goodex "Berlin" devices to read and write touch IC registers - more quirks to i8042 to handle various Tuxedo laptops - a number of drivers have been converted to using "guard" notation when acquiring various locks, as well as using other cleanup functions to simplify releasing of resources (with more drivers to follow) - evdev will limit amount of data that can be written into an evdev instance at a given time to 4096 bytes (170 input events) to avoid holding evdev->mutex for too long and starving other users - Spitz has been converted to use software nodes/properties to describe its matrix keypad and GPIO-connected LEDs - msc5000_ts, msc_touchkey and keypad-nomadik-ske drivers have been removed since noone in mainline have been using them - other assorted cleanups and fixes * tag 'input-for-v6.12-rc0' of git://git.kernel.org/pub/scm/linux/kernel/git/dtor/input: (98 commits) ARM: spitz: fix compile error when matrix keypad driver is enabled Input: hynitron_cstxxx - drop explicit initialization of struct i2c_device_id::driver_data to 0 Input: adp5588-keys - fix check on return code Input: Convert comma to semicolon Input: i8042 - add TUXEDO Stellaris 15 Slim Gen6 AMD to i8042 quirk table Input: i8042 - add another board name for TUXEDO Stellaris Gen5 AMD line Input: tegra-kbc - use of_property_read_variable_u32_array() and of_property_present() Input: ps2-gpio - use IRQF_NO_AUTOEN flag in request_irq() Input: ims-pcu - fix calling interruptible mutex Input: zforce_ts - switch to using asynchronous probing Input: zforce_ts - remove assert/deassert wrappers Input: zforce_ts - do not hardcode interrupt level Input: zforce_ts - switch to using devm_regulator_get_enable() Input: zforce_ts - stop treating VDD regulator as optional Input: zforce_ts - make zforce_idtable constant Input: zforce_ts - use dev_err_probe() where appropriate Input: zforce_ts - do not ignore errors when acquiring regulator Input: zforce_ts - make parsing of contacts less confusing Input: zforce_ts - switch to using get_unaligned_le16 Input: zforce_ts - use guard notation when acquiring mutexes ...
This commit is contained in:
commit
9ae2940cbc
@ -49,7 +49,10 @@ properties:
|
||||
interrupt-controller:
|
||||
description:
|
||||
This property applies if either keypad,num-rows lower than 8 or
|
||||
keypad,num-columns lower than 10.
|
||||
keypad,num-columns lower than 10. This property is optional if
|
||||
keypad,num-rows or keypad,num-columns are not specified as the
|
||||
device is then configured to be used purely for gpio during which
|
||||
interrupts may or may not be utilized.
|
||||
|
||||
'#interrupt-cells':
|
||||
const: 2
|
||||
@ -65,13 +68,23 @@ properties:
|
||||
minItems: 1
|
||||
maxItems: 2
|
||||
|
||||
dependencies:
|
||||
keypad,num-rows:
|
||||
- linux,keymap
|
||||
- keypad,num-columns
|
||||
keypad,num-columns:
|
||||
- linux,keymap
|
||||
- keypad,num-rows
|
||||
linux,keymap:
|
||||
- keypad,num-rows
|
||||
- keypad,num-columns
|
||||
- interrupts
|
||||
interrupt-controller:
|
||||
- interrupts
|
||||
|
||||
required:
|
||||
- compatible
|
||||
- reg
|
||||
- interrupts
|
||||
- keypad,num-rows
|
||||
- keypad,num-columns
|
||||
- linux,keymap
|
||||
|
||||
unevaluatedProperties: false
|
||||
|
||||
@ -108,4 +121,19 @@ examples:
|
||||
>;
|
||||
};
|
||||
};
|
||||
|
||||
- |
|
||||
#include <dt-bindings/gpio/gpio.h>
|
||||
i2c {
|
||||
#address-cells = <1>;
|
||||
#size-cells = <0>;
|
||||
gpio@34 {
|
||||
compatible = "adi,adp5588";
|
||||
reg = <0x34>;
|
||||
|
||||
#gpio-cells = <2>;
|
||||
gpio-controller;
|
||||
};
|
||||
};
|
||||
|
||||
...
|
||||
|
@ -1,50 +0,0 @@
|
||||
Rotary encoder DT bindings
|
||||
|
||||
Required properties:
|
||||
- gpios: a spec for at least two GPIOs to be used, most significant first
|
||||
|
||||
Optional properties:
|
||||
- linux,axis: the input subsystem axis to map to this rotary encoder.
|
||||
Defaults to 0 (ABS_X / REL_X)
|
||||
- rotary-encoder,steps: Number of steps in a full turnaround of the
|
||||
encoder. Only relevant for absolute axis. Defaults to 24 which is a
|
||||
typical value for such devices.
|
||||
- rotary-encoder,relative-axis: register a relative axis rather than an
|
||||
absolute one. Relative axis will only generate +1/-1 events on the input
|
||||
device, hence no steps need to be passed.
|
||||
- rotary-encoder,rollover: Automatic rollover when the rotary value becomes
|
||||
greater than the specified steps or smaller than 0. For absolute axis only.
|
||||
- rotary-encoder,steps-per-period: Number of steps (stable states) per period.
|
||||
The values have the following meaning:
|
||||
1: Full-period mode (default)
|
||||
2: Half-period mode
|
||||
4: Quarter-period mode
|
||||
- wakeup-source: Boolean, rotary encoder can wake up the system.
|
||||
- rotary-encoder,encoding: String, the method used to encode steps.
|
||||
Supported are "gray" (the default and more common) and "binary".
|
||||
|
||||
Deprecated properties:
|
||||
- rotary-encoder,half-period: Makes the driver work on half-period mode.
|
||||
This property is deprecated. Instead, a 'steps-per-period ' value should
|
||||
be used, such as "rotary-encoder,steps-per-period = <2>".
|
||||
|
||||
See Documentation/input/devices/rotary-encoder.rst for more information.
|
||||
|
||||
Example:
|
||||
|
||||
rotary@0 {
|
||||
compatible = "rotary-encoder";
|
||||
gpios = <&gpio 19 1>, <&gpio 20 0>; /* GPIO19 is inverted */
|
||||
linux,axis = <0>; /* REL_X */
|
||||
rotary-encoder,encoding = "gray";
|
||||
rotary-encoder,relative-axis;
|
||||
};
|
||||
|
||||
rotary@1 {
|
||||
compatible = "rotary-encoder";
|
||||
gpios = <&gpio 21 0>, <&gpio 22 0>;
|
||||
linux,axis = <1>; /* ABS_Y */
|
||||
rotary-encoder,steps = <24>;
|
||||
rotary-encoder,encoding = "binary";
|
||||
rotary-encoder,rollover;
|
||||
};
|
90
Documentation/devicetree/bindings/input/rotary-encoder.yaml
Normal file
90
Documentation/devicetree/bindings/input/rotary-encoder.yaml
Normal file
@ -0,0 +1,90 @@
|
||||
# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
|
||||
%YAML 1.2
|
||||
---
|
||||
$id: http://devicetree.org/schemas/input/rotary-encoder.yaml#
|
||||
$schema: http://devicetree.org/meta-schemas/core.yaml#
|
||||
|
||||
title: Rotary encoder
|
||||
|
||||
maintainers:
|
||||
- Frank Li <Frank.Li@nxp.com>
|
||||
|
||||
description:
|
||||
See Documentation/input/devices/rotary-encoder.rst for more information.
|
||||
|
||||
properties:
|
||||
compatible:
|
||||
const: rotary-encoder
|
||||
|
||||
gpios:
|
||||
minItems: 2
|
||||
|
||||
linux,axis:
|
||||
default: 0
|
||||
description:
|
||||
the input subsystem axis to map to this rotary encoder.
|
||||
Defaults to 0 (ABS_X / REL_X)
|
||||
|
||||
rotary-encoder,steps:
|
||||
$ref: /schemas/types.yaml#/definitions/uint32
|
||||
default: 24
|
||||
description:
|
||||
Number of steps in a full turnaround of the
|
||||
encoder. Only relevant for absolute axis. Defaults to 24 which is a
|
||||
typical value for such devices.
|
||||
|
||||
rotary-encoder,relative-axis:
|
||||
$ref: /schemas/types.yaml#/definitions/flag
|
||||
description:
|
||||
register a relative axis rather than an
|
||||
absolute one. Relative axis will only generate +1/-1 events on the input
|
||||
device, hence no steps need to be passed.
|
||||
|
||||
rotary-encoder,rollover:
|
||||
$ref: /schemas/types.yaml#/definitions/int32
|
||||
description:
|
||||
Automatic rollover when the rotary value becomes
|
||||
greater than the specified steps or smaller than 0. For absolute axis only.
|
||||
|
||||
rotary-encoder,steps-per-period:
|
||||
$ref: /schemas/types.yaml#/definitions/uint32
|
||||
default: 1
|
||||
enum: [1, 2, 4]
|
||||
description: |
|
||||
Number of steps (stable states) per period.
|
||||
The values have the following meaning:
|
||||
1: Full-period mode (default)
|
||||
2: Half-period mode
|
||||
4: Quarter-period mode
|
||||
|
||||
wakeup-source: true
|
||||
|
||||
rotary-encoder,encoding:
|
||||
$ref: /schemas/types.yaml#/definitions/string
|
||||
description: the method used to encode steps.
|
||||
enum: [gray, binary]
|
||||
|
||||
rotary-encoder,half-period:
|
||||
$ref: /schemas/types.yaml#/definitions/flag
|
||||
deprecated: true
|
||||
description:
|
||||
Makes the driver work on half-period mode.
|
||||
This property is deprecated. Instead, a 'steps-per-period ' value should
|
||||
be used, such as "rotary-encoder,steps-per-period = <2>".
|
||||
|
||||
required:
|
||||
- compatible
|
||||
- gpios
|
||||
|
||||
additionalProperties: false
|
||||
|
||||
examples:
|
||||
- |
|
||||
rotary {
|
||||
compatible = "rotary-encoder";
|
||||
gpios = <&gpio 19 1>, <&gpio 20 0>; /* GPIO19 is inverted */
|
||||
linux,axis = <0>; /* REL_X */
|
||||
rotary-encoder,encoding = "gray";
|
||||
rotary-encoder,relative-axis;
|
||||
};
|
||||
|
@ -1,71 +0,0 @@
|
||||
* Analog Devices AD7879(-1)/AD7889(-1) touchscreen interface (SPI/I2C)
|
||||
|
||||
Required properties:
|
||||
- compatible : for SPI slave, use "adi,ad7879"
|
||||
for I2C slave, use "adi,ad7879-1"
|
||||
- reg : SPI chipselect/I2C slave address
|
||||
See spi-bus.txt for more SPI slave properties
|
||||
- interrupts : touch controller interrupt
|
||||
- touchscreen-max-pressure : maximum reported pressure
|
||||
- adi,resistance-plate-x : total resistance of X-plate (for pressure
|
||||
calculation)
|
||||
Optional properties:
|
||||
- touchscreen-swapped-x-y : X and Y axis are swapped (boolean)
|
||||
- adi,first-conversion-delay : 0-12: In 128us steps (starting with 128us)
|
||||
13 : 2.560ms
|
||||
14 : 3.584ms
|
||||
15 : 4.096ms
|
||||
This property has to be a '/bits/ 8' value
|
||||
- adi,acquisition-time : 0: 2us
|
||||
1: 4us
|
||||
2: 8us
|
||||
3: 16us
|
||||
This property has to be a '/bits/ 8' value
|
||||
- adi,median-filter-size : 0: disabled
|
||||
1: 4 measurements
|
||||
2: 8 measurements
|
||||
3: 16 measurements
|
||||
This property has to be a '/bits/ 8' value
|
||||
- adi,averaging : 0: 2 middle values (1 if median disabled)
|
||||
1: 4 middle values
|
||||
2: 8 middle values
|
||||
3: 16 values
|
||||
This property has to be a '/bits/ 8' value
|
||||
- adi,conversion-interval: : 0 : convert one time only
|
||||
1-255: 515us + val * 35us (up to 9.440ms)
|
||||
This property has to be a '/bits/ 8' value
|
||||
- gpio-controller : Switch AUX/VBAT/GPIO pin to GPIO mode
|
||||
|
||||
Example:
|
||||
|
||||
touchscreen0@2c {
|
||||
compatible = "adi,ad7879-1";
|
||||
reg = <0x2c>;
|
||||
interrupt-parent = <&gpio1>;
|
||||
interrupts = <13 IRQ_TYPE_EDGE_FALLING>;
|
||||
touchscreen-max-pressure = <4096>;
|
||||
adi,resistance-plate-x = <120>;
|
||||
adi,first-conversion-delay = /bits/ 8 <3>;
|
||||
adi,acquisition-time = /bits/ 8 <1>;
|
||||
adi,median-filter-size = /bits/ 8 <2>;
|
||||
adi,averaging = /bits/ 8 <1>;
|
||||
adi,conversion-interval = /bits/ 8 <255>;
|
||||
};
|
||||
|
||||
touchscreen1@1 {
|
||||
compatible = "adi,ad7879";
|
||||
spi-max-frequency = <5000000>;
|
||||
reg = <1>;
|
||||
spi-cpol;
|
||||
spi-cpha;
|
||||
gpio-controller;
|
||||
interrupt-parent = <&gpio1>;
|
||||
interrupts = <13 IRQ_TYPE_EDGE_FALLING>;
|
||||
touchscreen-max-pressure = <4096>;
|
||||
adi,resistance-plate-x = <120>;
|
||||
adi,first-conversion-delay = /bits/ 8 <3>;
|
||||
adi,acquisition-time = /bits/ 8 <1>;
|
||||
adi,median-filter-size = /bits/ 8 <2>;
|
||||
adi,averaging = /bits/ 8 <1>;
|
||||
adi,conversion-interval = /bits/ 8 <255>;
|
||||
};
|
@ -0,0 +1,150 @@
|
||||
# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
|
||||
%YAML 1.2
|
||||
---
|
||||
$id: http://devicetree.org/schemas/input/touchscreen/adi,ad7879.yaml#
|
||||
$schema: http://devicetree.org/meta-schemas/core.yaml#
|
||||
|
||||
title: Analog Devices AD7879(-1)/AD7889(-1) touchscreen interface (SPI/I2C)
|
||||
|
||||
maintainers:
|
||||
- Frank Li <Frank.Li@nxp.com>
|
||||
|
||||
properties:
|
||||
compatible:
|
||||
description: |
|
||||
for SPI slave, use "adi,ad7879"
|
||||
for I2C slave, use "adi,ad7879-1"
|
||||
enum:
|
||||
- adi,ad7879
|
||||
- adi,ad7879-1
|
||||
|
||||
reg:
|
||||
maxItems: 1
|
||||
|
||||
interrupts:
|
||||
maxItems: 1
|
||||
|
||||
touchscreen-max-pressure:
|
||||
$ref: /schemas/types.yaml#/definitions/uint32
|
||||
description: maximum reported pressure
|
||||
|
||||
adi,resistance-plate-x:
|
||||
$ref: /schemas/types.yaml#/definitions/uint32
|
||||
description: total resistance of X-plate (for pressure calculation)
|
||||
|
||||
touchscreen-swapped-x-y:
|
||||
$ref: /schemas/types.yaml#/definitions/flag
|
||||
description: X and Y axis are swapped (boolean)
|
||||
|
||||
adi,first-conversion-delay:
|
||||
$ref: /schemas/types.yaml#/definitions/uint8
|
||||
default: 0
|
||||
minimum: 0
|
||||
maximum: 15
|
||||
description: |
|
||||
0-12: In 128us steps (starting with 128us)
|
||||
13 : 2.560ms
|
||||
14 : 3.584ms
|
||||
15 : 4.096ms
|
||||
This property has to be a '/bits/ 8' value
|
||||
|
||||
adi,acquisition-time:
|
||||
$ref: /schemas/types.yaml#/definitions/uint8
|
||||
default: 0
|
||||
enum: [0, 1, 2, 3]
|
||||
description: |
|
||||
0: 2us
|
||||
1: 4us
|
||||
2: 8us
|
||||
3: 16us
|
||||
This property has to be a '/bits/ 8' value
|
||||
|
||||
adi,median-filter-size:
|
||||
$ref: /schemas/types.yaml#/definitions/uint8
|
||||
default: 0
|
||||
enum: [0, 1, 2, 3]
|
||||
description: |
|
||||
0: disabled
|
||||
1: 4 measurements
|
||||
2: 8 measurements
|
||||
3: 16 measurements
|
||||
This property has to be a '/bits/ 8' value
|
||||
|
||||
adi,averaging:
|
||||
$ref: /schemas/types.yaml#/definitions/uint8
|
||||
default: 0
|
||||
enum: [0, 1, 2, 3]
|
||||
description: |
|
||||
0: 2 middle values (1 if median disabled)
|
||||
1: 4 middle values
|
||||
2: 8 middle values
|
||||
3: 16 values
|
||||
This property has to be a '/bits/ 8' value
|
||||
|
||||
adi,conversion-interval:
|
||||
$ref: /schemas/types.yaml#/definitions/uint8
|
||||
default: 0
|
||||
description: |
|
||||
0 : convert one time only
|
||||
1-255: 515us + val * 35us (up to 9.440ms)
|
||||
This property has to be a '/bits/ 8' value
|
||||
|
||||
gpio-controller: true
|
||||
|
||||
"#gpio-cells":
|
||||
const: 1
|
||||
|
||||
required:
|
||||
- compatible
|
||||
- reg
|
||||
|
||||
allOf:
|
||||
- $ref: /schemas/spi/spi-peripheral-props.yaml
|
||||
|
||||
unevaluatedProperties: false
|
||||
|
||||
examples:
|
||||
- |
|
||||
#include <dt-bindings/interrupt-controller/irq.h>
|
||||
i2c {
|
||||
#address-cells = <1>;
|
||||
#size-cells = <0>;
|
||||
|
||||
touchscreen0@2c {
|
||||
compatible = "adi,ad7879-1";
|
||||
reg = <0x2c>;
|
||||
interrupt-parent = <&gpio1>;
|
||||
interrupts = <13 IRQ_TYPE_EDGE_FALLING>;
|
||||
touchscreen-max-pressure = <4096>;
|
||||
adi,resistance-plate-x = <120>;
|
||||
adi,first-conversion-delay = /bits/ 8 <3>;
|
||||
adi,acquisition-time = /bits/ 8 <1>;
|
||||
adi,median-filter-size = /bits/ 8 <2>;
|
||||
adi,averaging = /bits/ 8 <1>;
|
||||
adi,conversion-interval = /bits/ 8 <255>;
|
||||
};
|
||||
};
|
||||
|
||||
- |
|
||||
#include <dt-bindings/interrupt-controller/irq.h>
|
||||
spi {
|
||||
#address-cells = <1>;
|
||||
#size-cells = <0>;
|
||||
|
||||
touchscreen1@1 {
|
||||
compatible = "adi,ad7879";
|
||||
reg = <1>;
|
||||
spi-max-frequency = <5000000>;
|
||||
gpio-controller;
|
||||
#gpio-cells = <1>;
|
||||
interrupt-parent = <&gpio1>;
|
||||
interrupts = <13 IRQ_TYPE_EDGE_FALLING>;
|
||||
touchscreen-max-pressure = <4096>;
|
||||
adi,resistance-plate-x = <120>;
|
||||
adi,first-conversion-delay = /bits/ 8 <3>;
|
||||
adi,acquisition-time = /bits/ 8 <1>;
|
||||
adi,median-filter-size = /bits/ 8 <2>;
|
||||
adi,averaging = /bits/ 8 <1>;
|
||||
adi,conversion-interval = /bits/ 8 <255>;
|
||||
};
|
||||
};
|
@ -1,107 +0,0 @@
|
||||
Device tree bindings for TI's ADS7843, ADS7845, ADS7846, ADS7873, TSC2046
|
||||
SPI driven touch screen controllers.
|
||||
|
||||
The node for this driver must be a child node of a SPI controller, hence
|
||||
all mandatory properties described in
|
||||
|
||||
Documentation/devicetree/bindings/spi/spi-bus.txt
|
||||
|
||||
must be specified.
|
||||
|
||||
Additional required properties:
|
||||
|
||||
compatible Must be one of the following, depending on the
|
||||
model:
|
||||
"ti,tsc2046"
|
||||
"ti,ads7843"
|
||||
"ti,ads7845"
|
||||
"ti,ads7846"
|
||||
"ti,ads7873"
|
||||
|
||||
interrupts An interrupt node describing the IRQ line the chip's
|
||||
!PENIRQ pin is connected to.
|
||||
vcc-supply A regulator node for the supply voltage.
|
||||
|
||||
|
||||
Optional properties:
|
||||
|
||||
ti,vref-delay-usecs vref supply delay in usecs, 0 for
|
||||
external vref (u16).
|
||||
ti,vref-mv The VREF voltage, in millivolts (u16).
|
||||
Set to 0 to use internal references
|
||||
(ADS7846).
|
||||
ti,keep-vref-on set to keep vref on for differential
|
||||
measurements as well
|
||||
ti,settle-delay-usec Settling time of the analog signals;
|
||||
a function of Vcc and the capacitance
|
||||
on the X/Y drivers. If set to non-zero,
|
||||
two samples are taken with settle_delay
|
||||
us apart, and the second one is used.
|
||||
~150 uSec with 0.01uF caps (u16).
|
||||
ti,penirq-recheck-delay-usecs If set to non-zero, after samples are
|
||||
taken this delay is applied and penirq
|
||||
is rechecked, to help avoid false
|
||||
events. This value is affected by the
|
||||
material used to build the touch layer
|
||||
(u16).
|
||||
ti,x-plate-ohms Resistance of the X-plate,
|
||||
in Ohms (u16).
|
||||
ti,y-plate-ohms Resistance of the Y-plate,
|
||||
in Ohms (u16).
|
||||
ti,x-min Minimum value on the X axis (u16).
|
||||
ti,y-min Minimum value on the Y axis (u16).
|
||||
ti,debounce-tol Tolerance used for filtering (u16).
|
||||
ti,debounce-rep Additional consecutive good readings
|
||||
required after the first two (u16).
|
||||
ti,pendown-gpio-debounce Platform specific debounce time for the
|
||||
pendown-gpio (u32).
|
||||
pendown-gpio GPIO handle describing the pin the !PENIRQ
|
||||
line is connected to.
|
||||
ti,hsync-gpios GPIO line to poll for hsync
|
||||
wakeup-source use any event on touchscreen as wakeup event.
|
||||
(Legacy property support: "linux,wakeup")
|
||||
touchscreen-size-x General touchscreen binding, see [1].
|
||||
touchscreen-size-y General touchscreen binding, see [1].
|
||||
touchscreen-max-pressure General touchscreen binding, see [1].
|
||||
touchscreen-min-pressure General touchscreen binding, see [1].
|
||||
touchscreen-average-samples General touchscreen binding, see [1].
|
||||
touchscreen-inverted-x General touchscreen binding, see [1].
|
||||
touchscreen-inverted-y General touchscreen binding, see [1].
|
||||
touchscreen-swapped-x-y General touchscreen binding, see [1].
|
||||
|
||||
[1] All general touchscreen properties are described in
|
||||
Documentation/devicetree/bindings/input/touchscreen/touchscreen.txt.
|
||||
|
||||
Deprecated properties:
|
||||
|
||||
ti,swap-xy swap x and y axis
|
||||
ti,x-max Maximum value on the X axis (u16).
|
||||
ti,y-max Maximum value on the Y axis (u16).
|
||||
ti,pressure-min Minimum reported pressure value
|
||||
(threshold) - u16.
|
||||
ti,pressure-max Maximum reported pressure value (u16).
|
||||
ti,debounce-max Max number of additional readings per
|
||||
sample (u16).
|
||||
|
||||
Example for a TSC2046 chip connected to an McSPI controller of an OMAP SoC::
|
||||
|
||||
spi_controller {
|
||||
tsc2046@0 {
|
||||
reg = <0>; /* CS0 */
|
||||
compatible = "ti,tsc2046";
|
||||
interrupt-parent = <&gpio1>;
|
||||
interrupts = <8 0>; /* BOOT6 / GPIO 8 */
|
||||
spi-max-frequency = <1000000>;
|
||||
pendown-gpio = <&gpio1 8 0>;
|
||||
vcc-supply = <®_vcc3>;
|
||||
|
||||
ti,x-min = /bits/ 16 <0>;
|
||||
ti,x-max = /bits/ 16 <8000>;
|
||||
ti,y-min = /bits/ 16 <0>;
|
||||
ti,y-max = /bits/ 16 <4800>;
|
||||
ti,x-plate-ohms = /bits/ 16 <40>;
|
||||
ti,pressure-max = /bits/ 16 <255>;
|
||||
|
||||
wakeup-source;
|
||||
};
|
||||
};
|
@ -666,7 +666,7 @@ examples:
|
||||
#address-cells = <1>;
|
||||
#size-cells = <0>;
|
||||
|
||||
touch@56 {
|
||||
touchscreen@56 {
|
||||
compatible = "azoteq,iqs7210a";
|
||||
reg = <0x56>;
|
||||
irq-gpios = <&gpio 4 GPIO_ACTIVE_LOW>;
|
||||
@ -704,7 +704,7 @@ examples:
|
||||
#address-cells = <1>;
|
||||
#size-cells = <0>;
|
||||
|
||||
touch@56 {
|
||||
touchscreen@56 {
|
||||
compatible = "azoteq,iqs7211e";
|
||||
reg = <0x56>;
|
||||
irq-gpios = <&gpio 4 (GPIO_ACTIVE_LOW |
|
||||
|
@ -1,34 +0,0 @@
|
||||
* Toradex Colibri VF50 Touchscreen driver
|
||||
|
||||
Required Properties:
|
||||
- compatible must be toradex,vf50-touchscreen
|
||||
- io-channels: adc channels being used by the Colibri VF50 module
|
||||
IIO ADC for Y-, X-, Y+, X+ connections
|
||||
- xp-gpios: FET gate driver for input of X+
|
||||
- xm-gpios: FET gate driver for input of X-
|
||||
- yp-gpios: FET gate driver for input of Y+
|
||||
- ym-gpios: FET gate driver for input of Y-
|
||||
- interrupts: pen irq interrupt for touch detection, signal from X plate
|
||||
- pinctrl-names: "idle", "default"
|
||||
- pinctrl-0: pinctrl node for pen/touch detection, pinctrl must provide
|
||||
pull-up resistor on X+, X-.
|
||||
- pinctrl-1: pinctrl node for X/Y and pressure measurement (ADC) state pinmux
|
||||
- vf50-ts-min-pressure: pressure level at which to stop measuring X/Y values
|
||||
|
||||
Example:
|
||||
|
||||
touchctrl: vf50_touchctrl {
|
||||
compatible = "toradex,vf50-touchscreen";
|
||||
io-channels = <&adc1 0>,<&adc0 0>,
|
||||
<&adc0 1>,<&adc1 2>;
|
||||
xp-gpios = <&gpio0 13 GPIO_ACTIVE_LOW>;
|
||||
xm-gpios = <&gpio2 29 GPIO_ACTIVE_HIGH>;
|
||||
yp-gpios = <&gpio0 12 GPIO_ACTIVE_LOW>;
|
||||
ym-gpios = <&gpio0 4 GPIO_ACTIVE_HIGH>;
|
||||
interrupt-parent = <&gpio0>;
|
||||
interrupts = <8 IRQ_TYPE_LEVEL_LOW>;
|
||||
pinctrl-names = "idle","default";
|
||||
pinctrl-0 = <&pinctrl_touchctrl_idle>, <&pinctrl_touchctrl_gpios>;
|
||||
pinctrl-1 = <&pinctrl_touchctrl_default>, <&pinctrl_touchctrl_gpios>;
|
||||
vf50-ts-min-pressure = <200>;
|
||||
};
|
@ -126,7 +126,7 @@ examples:
|
||||
i2c {
|
||||
#address-cells = <1>;
|
||||
#size-cells = <0>;
|
||||
edt-ft5x06@38 {
|
||||
touchscreen@38 {
|
||||
compatible = "edt,edt-ft5406";
|
||||
reg = <0x38>;
|
||||
interrupt-parent = <&gpio2>;
|
||||
|
@ -69,7 +69,7 @@ examples:
|
||||
i2c {
|
||||
#address-cells = <1>;
|
||||
#size-cells = <0>;
|
||||
gt928@5d {
|
||||
touchscreen@5d {
|
||||
compatible = "goodix,gt928";
|
||||
reg = <0x5d>;
|
||||
interrupt-parent = <&gpio>;
|
||||
|
@ -0,0 +1,183 @@
|
||||
# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
|
||||
%YAML 1.2
|
||||
---
|
||||
$id: http://devicetree.org/schemas/input/touchscreen/ti,ads7843.yaml#
|
||||
$schema: http://devicetree.org/meta-schemas/core.yaml#
|
||||
|
||||
title: TI's SPI driven touch screen controllers
|
||||
|
||||
maintainers:
|
||||
- Alexander Stein <alexander.stein@ew.tq-group.com>
|
||||
- Dmitry Torokhov <dmitry.torokhov@gmail.com>
|
||||
- Marek Vasut <marex@denx.de>
|
||||
|
||||
description:
|
||||
TI's ADS7843, ADS7845, ADS7846, ADS7873, TSC2046 SPI driven touch screen
|
||||
controllers.
|
||||
|
||||
properties:
|
||||
compatible:
|
||||
enum:
|
||||
- ti,ads7843
|
||||
- ti,ads7845
|
||||
- ti,ads7846
|
||||
- ti,ads7873
|
||||
- ti,tsc2046
|
||||
|
||||
interrupts:
|
||||
maxItems: 1
|
||||
|
||||
pendown-gpio:
|
||||
maxItems: 1
|
||||
description:
|
||||
GPIO handle describing the pin the !PENIRQ line is connected to.
|
||||
|
||||
vcc-supply:
|
||||
description:
|
||||
A regulator node for the supply voltage.
|
||||
|
||||
wakeup-source: true
|
||||
|
||||
ti,debounce-max:
|
||||
deprecated: true
|
||||
$ref: /schemas/types.yaml#/definitions/uint16
|
||||
description:
|
||||
Max number of additional readings per sample.
|
||||
|
||||
ti,debounce-rep:
|
||||
$ref: /schemas/types.yaml#/definitions/uint16
|
||||
description:
|
||||
Additional consecutive good readings required after the first two.
|
||||
|
||||
ti,debounce-tol:
|
||||
$ref: /schemas/types.yaml#/definitions/uint16
|
||||
description:
|
||||
Tolerance used for filtering.
|
||||
|
||||
ti,hsync-gpios:
|
||||
maxItems: 1
|
||||
description:
|
||||
GPIO line to poll for hsync.
|
||||
|
||||
ti,keep-vref-on:
|
||||
$ref: /schemas/types.yaml#/definitions/flag
|
||||
description:
|
||||
Set to keep Vref on for differential measurements as well.
|
||||
|
||||
ti,pendown-gpio-debounce:
|
||||
$ref: /schemas/types.yaml#/definitions/uint32
|
||||
description:
|
||||
Platform specific debounce time for the pendown-gpio.
|
||||
|
||||
ti,penirq-recheck-delay-usecs:
|
||||
$ref: /schemas/types.yaml#/definitions/uint16
|
||||
description:
|
||||
If set to non-zero, after samples are taken this delay is applied and
|
||||
penirq is rechecked, to help avoid false events. This value is
|
||||
affected by the material used to build the touch layer.
|
||||
|
||||
ti,pressure-max:
|
||||
deprecated: true
|
||||
$ref: /schemas/types.yaml#/definitions/uint16
|
||||
description:
|
||||
Maximum reported pressure value.
|
||||
|
||||
ti,pressure-min:
|
||||
deprecated: true
|
||||
$ref: /schemas/types.yaml#/definitions/uint16
|
||||
description:
|
||||
Minimum reported pressure value (threshold).
|
||||
|
||||
ti,settle-delay-usec:
|
||||
$ref: /schemas/types.yaml#/definitions/uint16
|
||||
description:
|
||||
Settling time of the analog signals; a function of Vcc and the
|
||||
capacitance on the X/Y drivers. If set to non-zero, two samples are
|
||||
taken with settle_delay us apart, and the second one is used. ~150
|
||||
uSec with 0.01uF caps.
|
||||
|
||||
ti,swap-xy:
|
||||
deprecated: true
|
||||
$ref: /schemas/types.yaml#/definitions/flag
|
||||
description:
|
||||
Swap x and y axis.
|
||||
|
||||
ti,vref-delay-usecs:
|
||||
$ref: /schemas/types.yaml#/definitions/uint16
|
||||
description:
|
||||
Vref supply delay in usecs, 0 for external Vref.
|
||||
|
||||
ti,vref-mv:
|
||||
$ref: /schemas/types.yaml#/definitions/uint16
|
||||
description:
|
||||
The VREF voltage, in millivolts.
|
||||
Set to 0 to use internal references (ADS7846).
|
||||
|
||||
ti,x-plate-ohms:
|
||||
$ref: /schemas/types.yaml#/definitions/uint16
|
||||
description:
|
||||
Resistance of the X-plate, in Ohms.
|
||||
|
||||
ti,x-max:
|
||||
deprecated: true
|
||||
$ref: /schemas/types.yaml#/definitions/uint16
|
||||
description:
|
||||
Maximum value on the X axis.
|
||||
|
||||
ti,x-min:
|
||||
deprecated: true
|
||||
$ref: /schemas/types.yaml#/definitions/uint16
|
||||
description:
|
||||
Minimum value on the X axis.
|
||||
|
||||
ti,y-plate-ohms:
|
||||
$ref: /schemas/types.yaml#/definitions/uint16
|
||||
description:
|
||||
Resistance of the Y-plate, in Ohms.
|
||||
|
||||
ti,y-max:
|
||||
deprecated: true
|
||||
$ref: /schemas/types.yaml#/definitions/uint16
|
||||
description:
|
||||
Maximum value on the Y axis.
|
||||
|
||||
ti,y-min:
|
||||
deprecated: true
|
||||
$ref: /schemas/types.yaml#/definitions/uint16
|
||||
description:
|
||||
Minimum value on the Y axis.
|
||||
|
||||
required:
|
||||
- compatible
|
||||
- reg
|
||||
|
||||
allOf:
|
||||
- $ref: touchscreen.yaml#
|
||||
- $ref: /schemas/spi/spi-peripheral-props.yaml#
|
||||
|
||||
unevaluatedProperties: false
|
||||
|
||||
examples:
|
||||
- |
|
||||
spi{
|
||||
#address-cells = <1>;
|
||||
#size-cells = <0>;
|
||||
|
||||
touchscreen@0 {
|
||||
compatible = "ti,tsc2046";
|
||||
reg = <0>; /* CS0 */
|
||||
interrupt-parent = <&gpio1>;
|
||||
interrupts = <8 0>; /* BOOT6 / GPIO 8 */
|
||||
pendown-gpio = <&gpio1 8 0>;
|
||||
spi-max-frequency = <1000000>;
|
||||
vcc-supply = <®_vcc3>;
|
||||
wakeup-source;
|
||||
|
||||
ti,pressure-max = /bits/ 16 <255>;
|
||||
ti,x-max = /bits/ 16 <8000>;
|
||||
ti,x-min = /bits/ 16 <0>;
|
||||
ti,x-plate-ohms = /bits/ 16 <40>;
|
||||
ti,y-max = /bits/ 16 <4800>;
|
||||
ti,y-min = /bits/ 16 <0>;
|
||||
};
|
||||
};
|
@ -0,0 +1,77 @@
|
||||
# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
|
||||
%YAML 1.2
|
||||
---
|
||||
$id: http://devicetree.org/schemas/input/touchscreen/toradex,vf50-touchscreen.yaml#
|
||||
$schema: http://devicetree.org/meta-schemas/core.yaml#
|
||||
|
||||
title: Toradex Colibri VF50 Touchscreen
|
||||
|
||||
maintainers:
|
||||
- Dmitry Torokhov <dmitry.torokhov@gmail.com>
|
||||
- Sanchayan Maity <maitysanchayan@gmail.com>
|
||||
|
||||
properties:
|
||||
compatible:
|
||||
const: toradex,vf50-touchscreen
|
||||
|
||||
interrupts:
|
||||
maxItems: 1
|
||||
|
||||
io-channels:
|
||||
maxItems: 4
|
||||
description:
|
||||
adc channels being used by the Colibri VF50 module
|
||||
IIO ADC for Y-, X-, Y+, X+ connections
|
||||
|
||||
xp-gpios:
|
||||
description: FET gate driver for input of X+
|
||||
|
||||
xm-gpios:
|
||||
description: FET gate driver for input of X-
|
||||
|
||||
yp-gpios:
|
||||
description: FET gate driver for input of Y+
|
||||
|
||||
ym-gpios:
|
||||
description: FET gate driver for input of Y-
|
||||
|
||||
vf50-ts-min-pressure:
|
||||
$ref: /schemas/types.yaml#/definitions/uint32
|
||||
minimum: 50
|
||||
maximum: 2000
|
||||
description: pressure level at which to stop measuring X/Y values
|
||||
|
||||
required:
|
||||
- compatible
|
||||
- io-channels
|
||||
- xp-gpios
|
||||
- xm-gpios
|
||||
- yp-gpios
|
||||
- ym-gpios
|
||||
- interrupts
|
||||
- vf50-ts-min-pressure
|
||||
|
||||
allOf:
|
||||
- $ref: touchscreen.yaml#
|
||||
|
||||
unevaluatedProperties: false
|
||||
|
||||
examples:
|
||||
- |
|
||||
#include <dt-bindings/interrupt-controller/irq.h>
|
||||
#include <dt-bindings/gpio/gpio.h>
|
||||
|
||||
touchscreen {
|
||||
compatible = "toradex,vf50-touchscreen";
|
||||
interrupt-parent = <&gpio0>;
|
||||
interrupts = <8 IRQ_TYPE_LEVEL_LOW>;
|
||||
io-channels = <&adc1 0>, <&adc0 0>, <&adc0 1>, <&adc1 2>;
|
||||
xp-gpios = <&gpio0 13 GPIO_ACTIVE_LOW>;
|
||||
xm-gpios = <&gpio2 29 GPIO_ACTIVE_HIGH>;
|
||||
yp-gpios = <&gpio0 12 GPIO_ACTIVE_LOW>;
|
||||
ym-gpios = <&gpio0 4 GPIO_ACTIVE_HIGH>;
|
||||
pinctrl-names = "idle", "default";
|
||||
pinctrl-0 = <&pinctrl_touchctrl_idle>, <&pinctrl_touchctrl_gpios>;
|
||||
pinctrl-1 = <&pinctrl_touchctrl_default>, <&pinctrl_touchctrl_gpios>;
|
||||
vf50-ts-min-pressure = <200>;
|
||||
};
|
@ -16,6 +16,7 @@ maintainers:
|
||||
|
||||
allOf:
|
||||
- $ref: touchscreen.yaml#
|
||||
- $ref: ../input.yaml#
|
||||
|
||||
properties:
|
||||
$nodename:
|
||||
@ -79,6 +80,15 @@ properties:
|
||||
$ref: /schemas/types.yaml#/definitions/uint32
|
||||
enum: [1, 2]
|
||||
|
||||
linux,keycodes:
|
||||
description:
|
||||
This property specifies an array of keycodes assigned to the
|
||||
touch-keys that can be present in some touchscreen configurations.
|
||||
If the touch-keys are enabled, controller firmware will assign some
|
||||
touch sense lines to those keys.
|
||||
minItems: 1
|
||||
maxItems: 8
|
||||
|
||||
touchscreen-size-x: true
|
||||
touchscreen-size-y: true
|
||||
touchscreen-fuzz-x: true
|
||||
|
@ -25,7 +25,7 @@ List of legacy properties and respective binding document
|
||||
2. "has-tpo" Documentation/devicetree/bindings/rtc/rtc-opal.txt
|
||||
3. "linux,wakeup" Documentation/devicetree/bindings/input/gpio-matrix-keypad.txt
|
||||
Documentation/devicetree/bindings/mfd/tc3589x.txt
|
||||
Documentation/devicetree/bindings/input/touchscreen/ads7846.txt
|
||||
Documentation/devicetree/bindings/input/touchscreen/ti,ads7843.yaml
|
||||
4. "linux,keypad-wakeup" Documentation/devicetree/bindings/input/qcom,pm8921-keypad.yaml
|
||||
5. "linux,input-wakeup" Documentation/devicetree/bindings/input/samsung,s3c6410-keypad.yaml
|
||||
6. "nvidia,wakeup-source" Documentation/devicetree/bindings/input/nvidia,tegra20-kbc.txt
|
||||
|
@ -11198,10 +11198,17 @@ F: Documentation/devicetree/bindings/serio/
|
||||
F: Documentation/input/
|
||||
F: drivers/input/
|
||||
F: include/dt-bindings/input/
|
||||
F: include/linux/gameport.h
|
||||
F: include/linux/i8042.h
|
||||
F: include/linux/input.h
|
||||
F: include/linux/input/
|
||||
F: include/linux/libps2.h
|
||||
F: include/linux/serio.h
|
||||
F: include/uapi/linux/gameport.h
|
||||
F: include/uapi/linux/input-event-codes.h
|
||||
F: include/uapi/linux/input.h
|
||||
F: include/uapi/linux/serio.h
|
||||
F: include/uapi/linux/uinput.h
|
||||
|
||||
INPUT MULTITOUCH (MT) PROTOCOL
|
||||
M: Henrik Rydberg <rydberg@bitmath.org>
|
||||
@ -25532,7 +25539,6 @@ F: tools/net/ynl/
|
||||
|
||||
YEALINK PHONE DRIVER
|
||||
M: Henk Vergonet <Henk.Vergonet@gmail.com>
|
||||
L: usbb2k-api-dev@nongnu.org
|
||||
S: Maintained
|
||||
F: Documentation/input/devices/yealink.rst
|
||||
F: drivers/input/misc/yealink.*
|
||||
|
@ -378,38 +378,56 @@ static const uint32_t spitz_keymap[] = {
|
||||
KEY(6, 8, KEY_RIGHT),
|
||||
};
|
||||
|
||||
static const struct matrix_keymap_data spitz_keymap_data = {
|
||||
.keymap = spitz_keymap,
|
||||
.keymap_size = ARRAY_SIZE(spitz_keymap),
|
||||
static const struct software_node_ref_args spitz_mkp_row_gpios[] = {
|
||||
SOFTWARE_NODE_REFERENCE(&pxa2xx_gpiochip_node, 12, GPIO_ACTIVE_HIGH),
|
||||
SOFTWARE_NODE_REFERENCE(&pxa2xx_gpiochip_node, 17, GPIO_ACTIVE_HIGH),
|
||||
SOFTWARE_NODE_REFERENCE(&pxa2xx_gpiochip_node, 91, GPIO_ACTIVE_HIGH),
|
||||
SOFTWARE_NODE_REFERENCE(&pxa2xx_gpiochip_node, 34, GPIO_ACTIVE_HIGH),
|
||||
SOFTWARE_NODE_REFERENCE(&pxa2xx_gpiochip_node, 36, GPIO_ACTIVE_HIGH),
|
||||
SOFTWARE_NODE_REFERENCE(&pxa2xx_gpiochip_node, 38, GPIO_ACTIVE_HIGH),
|
||||
SOFTWARE_NODE_REFERENCE(&pxa2xx_gpiochip_node, 39, GPIO_ACTIVE_HIGH),
|
||||
};
|
||||
|
||||
static const uint32_t spitz_row_gpios[] =
|
||||
{ 12, 17, 91, 34, 36, 38, 39 };
|
||||
static const uint32_t spitz_col_gpios[] =
|
||||
{ 88, 23, 24, 25, 26, 27, 52, 103, 107, 108, 114 };
|
||||
|
||||
static struct matrix_keypad_platform_data spitz_mkp_pdata = {
|
||||
.keymap_data = &spitz_keymap_data,
|
||||
.row_gpios = spitz_row_gpios,
|
||||
.col_gpios = spitz_col_gpios,
|
||||
.num_row_gpios = ARRAY_SIZE(spitz_row_gpios),
|
||||
.num_col_gpios = ARRAY_SIZE(spitz_col_gpios),
|
||||
.col_scan_delay_us = 10,
|
||||
.debounce_ms = 10,
|
||||
.wakeup = 1,
|
||||
static const struct software_node_ref_args spitz_mkp_col_gpios[] = {
|
||||
SOFTWARE_NODE_REFERENCE(&pxa2xx_gpiochip_node, 88, GPIO_ACTIVE_HIGH),
|
||||
SOFTWARE_NODE_REFERENCE(&pxa2xx_gpiochip_node, 23, GPIO_ACTIVE_HIGH),
|
||||
SOFTWARE_NODE_REFERENCE(&pxa2xx_gpiochip_node, 24, GPIO_ACTIVE_HIGH),
|
||||
SOFTWARE_NODE_REFERENCE(&pxa2xx_gpiochip_node, 25, GPIO_ACTIVE_HIGH),
|
||||
SOFTWARE_NODE_REFERENCE(&pxa2xx_gpiochip_node, 26, GPIO_ACTIVE_HIGH),
|
||||
SOFTWARE_NODE_REFERENCE(&pxa2xx_gpiochip_node, 27, GPIO_ACTIVE_HIGH),
|
||||
SOFTWARE_NODE_REFERENCE(&pxa2xx_gpiochip_node, 52, GPIO_ACTIVE_HIGH),
|
||||
SOFTWARE_NODE_REFERENCE(&pxa2xx_gpiochip_node, 103, GPIO_ACTIVE_HIGH),
|
||||
SOFTWARE_NODE_REFERENCE(&pxa2xx_gpiochip_node, 107, GPIO_ACTIVE_HIGH),
|
||||
SOFTWARE_NODE_REFERENCE(&pxa2xx_gpiochip_node, 108, GPIO_ACTIVE_HIGH),
|
||||
SOFTWARE_NODE_REFERENCE(&pxa2xx_gpiochip_node, 114, GPIO_ACTIVE_HIGH),
|
||||
};
|
||||
|
||||
static struct platform_device spitz_mkp_device = {
|
||||
static const struct property_entry spitz_mkp_properties[] = {
|
||||
PROPERTY_ENTRY_U32_ARRAY("linux,keymap", spitz_keymap),
|
||||
PROPERTY_ENTRY_REF_ARRAY("row-gpios", spitz_mkp_row_gpios),
|
||||
PROPERTY_ENTRY_REF_ARRAY("col-gpios", spitz_mkp_col_gpios),
|
||||
PROPERTY_ENTRY_U32("col-scan-delay-us", 10),
|
||||
PROPERTY_ENTRY_U32("debounce-delay-ms", 10),
|
||||
PROPERTY_ENTRY_BOOL("wakeup-source"),
|
||||
{ }
|
||||
};
|
||||
|
||||
static const struct platform_device_info spitz_mkp_info __initconst = {
|
||||
.name = "matrix-keypad",
|
||||
.id = -1,
|
||||
.dev = {
|
||||
.platform_data = &spitz_mkp_pdata,
|
||||
},
|
||||
.id = PLATFORM_DEVID_NONE,
|
||||
.properties = spitz_mkp_properties,
|
||||
};
|
||||
|
||||
|
||||
static void __init spitz_mkp_init(void)
|
||||
{
|
||||
platform_device_register(&spitz_mkp_device);
|
||||
struct platform_device *pd;
|
||||
int err;
|
||||
|
||||
pd = platform_device_register_full(&spitz_mkp_info);
|
||||
err = PTR_ERR_OR_ZERO(pd);
|
||||
if (err)
|
||||
pr_err("failed to create keypad device: %d\n", err);
|
||||
}
|
||||
#else
|
||||
static inline void spitz_mkp_init(void) {}
|
||||
@ -419,45 +437,82 @@ static inline void spitz_mkp_init(void) {}
|
||||
* GPIO keys
|
||||
******************************************************************************/
|
||||
#if defined(CONFIG_KEYBOARD_GPIO) || defined(CONFIG_KEYBOARD_GPIO_MODULE)
|
||||
static struct gpio_keys_button spitz_gpio_keys[] = {
|
||||
{
|
||||
.type = EV_PWR,
|
||||
.code = KEY_SUSPEND,
|
||||
.gpio = SPITZ_GPIO_ON_KEY,
|
||||
.desc = "On Off",
|
||||
.wakeup = 1,
|
||||
},
|
||||
/* Two buttons detecting the lid state */
|
||||
{
|
||||
.type = EV_SW,
|
||||
.code = 0,
|
||||
.gpio = SPITZ_GPIO_SWA,
|
||||
.desc = "Display Down",
|
||||
},
|
||||
{
|
||||
.type = EV_SW,
|
||||
.code = 1,
|
||||
.gpio = SPITZ_GPIO_SWB,
|
||||
.desc = "Lid Closed",
|
||||
},
|
||||
static const struct software_node spitz_gpio_keys_node = {
|
||||
.name = "spitz-gpio-keys",
|
||||
};
|
||||
|
||||
static struct gpio_keys_platform_data spitz_gpio_keys_platform_data = {
|
||||
.buttons = spitz_gpio_keys,
|
||||
.nbuttons = ARRAY_SIZE(spitz_gpio_keys),
|
||||
static const struct property_entry spitz_suspend_key_props[] = {
|
||||
PROPERTY_ENTRY_U32("linux,input-type", EV_PWR),
|
||||
PROPERTY_ENTRY_U32("linux,code", KEY_SUSPEND),
|
||||
PROPERTY_ENTRY_GPIO("gpios", &pxa2xx_gpiochip_node,
|
||||
SPITZ_GPIO_ON_KEY, GPIO_ACTIVE_HIGH),
|
||||
PROPERTY_ENTRY_STRING("label", "On Off"),
|
||||
PROPERTY_ENTRY_BOOL("wakeup-source"),
|
||||
{ }
|
||||
};
|
||||
|
||||
static struct platform_device spitz_gpio_keys_device = {
|
||||
.name = "gpio-keys",
|
||||
.id = -1,
|
||||
.dev = {
|
||||
.platform_data = &spitz_gpio_keys_platform_data,
|
||||
},
|
||||
static const struct software_node spitz_suspend_key_node = {
|
||||
.parent = &spitz_gpio_keys_node,
|
||||
.properties = spitz_suspend_key_props,
|
||||
};
|
||||
|
||||
static const struct property_entry spitz_sw1_props[] = {
|
||||
PROPERTY_ENTRY_U32("linux,input-type", EV_SW),
|
||||
PROPERTY_ENTRY_U32("linux,code", 0),
|
||||
PROPERTY_ENTRY_GPIO("gpios", &pxa2xx_gpiochip_node,
|
||||
SPITZ_GPIO_SWA, GPIO_ACTIVE_HIGH),
|
||||
PROPERTY_ENTRY_STRING("label", "Display Down"),
|
||||
{ }
|
||||
};
|
||||
|
||||
static const struct software_node spitz_sw1_node = {
|
||||
.parent = &spitz_gpio_keys_node,
|
||||
.properties = spitz_sw1_props,
|
||||
};
|
||||
|
||||
static const struct property_entry spitz_sw2_props[] = {
|
||||
PROPERTY_ENTRY_U32("linux,input-type", EV_SW),
|
||||
PROPERTY_ENTRY_U32("linux,code", 1),
|
||||
PROPERTY_ENTRY_GPIO("gpios", &pxa2xx_gpiochip_node,
|
||||
SPITZ_GPIO_SWB, GPIO_ACTIVE_HIGH),
|
||||
PROPERTY_ENTRY_STRING("label", "Lid Closed"),
|
||||
{ }
|
||||
};
|
||||
|
||||
static const struct software_node spitz_sw2_node = {
|
||||
.parent = &spitz_gpio_keys_node,
|
||||
.properties = spitz_sw2_props,
|
||||
};
|
||||
|
||||
static const struct software_node *spitz_gpio_keys_swnodes[] = {
|
||||
&spitz_gpio_keys_node,
|
||||
&spitz_suspend_key_node,
|
||||
&spitz_sw1_node,
|
||||
&spitz_sw2_node,
|
||||
NULL
|
||||
};
|
||||
|
||||
static void __init spitz_keys_init(void)
|
||||
{
|
||||
platform_device_register(&spitz_gpio_keys_device);
|
||||
struct platform_device_info keys_info = {
|
||||
.name = "gpio-keys",
|
||||
.id = PLATFORM_DEVID_NONE,
|
||||
};
|
||||
struct platform_device *pd;
|
||||
int err;
|
||||
|
||||
err = software_node_register_node_group(spitz_gpio_keys_swnodes);
|
||||
if (err) {
|
||||
pr_err("failed to register gpio-keys software nodes: %d\n", err);
|
||||
return;
|
||||
}
|
||||
|
||||
keys_info.fwnode = software_node_fwnode(&spitz_gpio_keys_node);
|
||||
|
||||
pd = platform_device_register_full(&keys_info);
|
||||
err = PTR_ERR_OR_ZERO(pd);
|
||||
if (err)
|
||||
pr_err("failed to create gpio-keys device: %d\n", err);
|
||||
}
|
||||
#else
|
||||
static inline void spitz_keys_init(void) {}
|
||||
|
@ -498,6 +498,13 @@ static ssize_t evdev_write(struct file *file, const char __user *buffer,
|
||||
struct input_event event;
|
||||
int retval = 0;
|
||||
|
||||
/*
|
||||
* Limit amount of data we inject into the input subsystem so that
|
||||
* we do not hold evdev->mutex for too long. 4096 bytes corresponds
|
||||
* to 170 input events.
|
||||
*/
|
||||
count = min(count, 4096);
|
||||
|
||||
if (count != 0 && count < input_event_size())
|
||||
return -EINVAL;
|
||||
|
||||
|
@ -2221,7 +2221,7 @@ static unsigned int input_estimate_events_per_packet(struct input_dev *dev)
|
||||
mt_slots = dev->mt->num_slots;
|
||||
} else if (test_bit(ABS_MT_TRACKING_ID, dev->absbit)) {
|
||||
mt_slots = dev->absinfo[ABS_MT_TRACKING_ID].maximum -
|
||||
dev->absinfo[ABS_MT_TRACKING_ID].minimum + 1,
|
||||
dev->absinfo[ABS_MT_TRACKING_ID].minimum + 1;
|
||||
mt_slots = clamp(mt_slots, 2, 32);
|
||||
} else if (test_bit(ABS_MT_POSITION_X, dev->absbit)) {
|
||||
mt_slots = 2;
|
||||
|
@ -132,7 +132,6 @@ static void adc_joystick_cleanup(void *data)
|
||||
static int adc_joystick_set_axes(struct device *dev, struct adc_joystick *joy)
|
||||
{
|
||||
struct adc_joystick_axis *axes = joy->axes;
|
||||
struct fwnode_handle *child;
|
||||
s32 range[2], fuzz, flat;
|
||||
unsigned int num_axes;
|
||||
int error, i;
|
||||
@ -149,31 +148,30 @@ static int adc_joystick_set_axes(struct device *dev, struct adc_joystick *joy)
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
device_for_each_child_node(dev, child) {
|
||||
device_for_each_child_node_scoped(dev, child) {
|
||||
error = fwnode_property_read_u32(child, "reg", &i);
|
||||
if (error) {
|
||||
dev_err(dev, "reg invalid or missing\n");
|
||||
goto err_fwnode_put;
|
||||
return error;
|
||||
}
|
||||
|
||||
if (i >= num_axes) {
|
||||
error = -EINVAL;
|
||||
dev_err(dev, "No matching axis for reg %d\n", i);
|
||||
goto err_fwnode_put;
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
error = fwnode_property_read_u32(child, "linux,code",
|
||||
&axes[i].code);
|
||||
if (error) {
|
||||
dev_err(dev, "linux,code invalid or missing\n");
|
||||
goto err_fwnode_put;
|
||||
return error;
|
||||
}
|
||||
|
||||
error = fwnode_property_read_u32_array(child, "abs-range",
|
||||
range, 2);
|
||||
if (error) {
|
||||
dev_err(dev, "abs-range invalid or missing\n");
|
||||
goto err_fwnode_put;
|
||||
return error;
|
||||
}
|
||||
|
||||
if (range[0] > range[1]) {
|
||||
@ -193,10 +191,6 @@ static int adc_joystick_set_axes(struct device *dev, struct adc_joystick *joy)
|
||||
}
|
||||
|
||||
return 0;
|
||||
|
||||
err_fwnode_put:
|
||||
fwnode_handle_put(child);
|
||||
return error;
|
||||
}
|
||||
|
||||
|
||||
|
@ -421,18 +421,6 @@ config KEYBOARD_MAX7359
|
||||
To compile this driver as a module, choose M here: the
|
||||
module will be called max7359_keypad.
|
||||
|
||||
config KEYBOARD_MCS
|
||||
tristate "MELFAS MCS Touchkey"
|
||||
depends on I2C
|
||||
help
|
||||
Say Y here if you have the MELFAS MCS5000/5080 touchkey controller
|
||||
chip in your system.
|
||||
|
||||
If unsure, say N.
|
||||
|
||||
To compile this driver as a module, choose M here: the
|
||||
module will be called mcs_touchkey.
|
||||
|
||||
config KEYBOARD_MPR121
|
||||
tristate "Freescale MPR121 Touchkey"
|
||||
depends on I2C
|
||||
@ -496,17 +484,6 @@ config KEYBOARD_NEWTON
|
||||
To compile this driver as a module, choose M here: the
|
||||
module will be called newtonkbd.
|
||||
|
||||
config KEYBOARD_NOMADIK
|
||||
tristate "ST-Ericsson Nomadik SKE keyboard"
|
||||
depends on (ARCH_NOMADIK || ARCH_U8500 || COMPILE_TEST)
|
||||
select INPUT_MATRIXKMAP
|
||||
help
|
||||
Say Y here if you want to use a keypad provided on the SKE controller
|
||||
used on the Ux500 and Nomadik platforms
|
||||
|
||||
To compile this driver as a module, choose M here: the
|
||||
module will be called nmk-ske-keypad.
|
||||
|
||||
config KEYBOARD_NSPIRE
|
||||
tristate "TI-NSPIRE built-in keyboard"
|
||||
depends on ARCH_NSPIRE && OF
|
||||
|
@ -42,12 +42,10 @@ obj-$(CONFIG_KEYBOARD_LPC32XX) += lpc32xx-keys.o
|
||||
obj-$(CONFIG_KEYBOARD_MAPLE) += maple_keyb.o
|
||||
obj-$(CONFIG_KEYBOARD_MATRIX) += matrix_keypad.o
|
||||
obj-$(CONFIG_KEYBOARD_MAX7359) += max7359_keypad.o
|
||||
obj-$(CONFIG_KEYBOARD_MCS) += mcs_touchkey.o
|
||||
obj-$(CONFIG_KEYBOARD_MPR121) += mpr121_touchkey.o
|
||||
obj-$(CONFIG_KEYBOARD_MT6779) += mt6779-keypad.o
|
||||
obj-$(CONFIG_KEYBOARD_MTK_PMIC) += mtk-pmic-keys.o
|
||||
obj-$(CONFIG_KEYBOARD_NEWTON) += newtonkbd.o
|
||||
obj-$(CONFIG_KEYBOARD_NOMADIK) += nomadik-ske-keypad.o
|
||||
obj-$(CONFIG_KEYBOARD_NSPIRE) += nspire-keypad.o
|
||||
obj-$(CONFIG_KEYBOARD_OMAP) += omap-keypad.o
|
||||
obj-$(CONFIG_KEYBOARD_OMAP4) += omap4-keypad.o
|
||||
|
@ -66,7 +66,6 @@ static void adc_keys_poll(struct input_dev *input)
|
||||
static int adc_keys_load_keymap(struct device *dev, struct adc_keys_state *st)
|
||||
{
|
||||
struct adc_keys_button *map;
|
||||
struct fwnode_handle *child;
|
||||
int i;
|
||||
|
||||
st->num_keys = device_get_child_node_count(dev);
|
||||
@ -80,11 +79,10 @@ static int adc_keys_load_keymap(struct device *dev, struct adc_keys_state *st)
|
||||
return -ENOMEM;
|
||||
|
||||
i = 0;
|
||||
device_for_each_child_node(dev, child) {
|
||||
device_for_each_child_node_scoped(dev, child) {
|
||||
if (fwnode_property_read_u32(child, "press-threshold-microvolt",
|
||||
&map[i].voltage)) {
|
||||
dev_err(dev, "Key with invalid or missing voltage\n");
|
||||
fwnode_handle_put(child);
|
||||
return -EINVAL;
|
||||
}
|
||||
map[i].voltage /= 1000;
|
||||
@ -92,7 +90,6 @@ static int adc_keys_load_keymap(struct device *dev, struct adc_keys_state *st)
|
||||
if (fwnode_property_read_u32(child, "linux,code",
|
||||
&map[i].keycode)) {
|
||||
dev_err(dev, "Key with invalid or missing linux,code\n");
|
||||
fwnode_handle_put(child);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
|
@ -188,6 +188,7 @@ struct adp5588_kpad {
|
||||
u32 cols;
|
||||
u32 unlock_keys[2];
|
||||
int nkeys_unlock;
|
||||
bool gpio_only;
|
||||
unsigned short keycode[ADP5588_KEYMAPSIZE];
|
||||
unsigned char gpiomap[ADP5588_MAXGPIO];
|
||||
struct gpio_chip gc;
|
||||
@ -221,15 +222,13 @@ static int adp5588_gpio_get_value(struct gpio_chip *chip, unsigned int off)
|
||||
unsigned int bit = ADP5588_BIT(kpad->gpiomap[off]);
|
||||
int val;
|
||||
|
||||
mutex_lock(&kpad->gpio_lock);
|
||||
guard(mutex)(&kpad->gpio_lock);
|
||||
|
||||
if (kpad->dir[bank] & bit)
|
||||
val = kpad->dat_out[bank];
|
||||
else
|
||||
val = adp5588_read(kpad->client, GPIO_DAT_STAT1 + bank);
|
||||
|
||||
mutex_unlock(&kpad->gpio_lock);
|
||||
|
||||
return !!(val & bit);
|
||||
}
|
||||
|
||||
@ -240,7 +239,7 @@ static void adp5588_gpio_set_value(struct gpio_chip *chip,
|
||||
unsigned int bank = ADP5588_BANK(kpad->gpiomap[off]);
|
||||
unsigned int bit = ADP5588_BIT(kpad->gpiomap[off]);
|
||||
|
||||
mutex_lock(&kpad->gpio_lock);
|
||||
guard(mutex)(&kpad->gpio_lock);
|
||||
|
||||
if (val)
|
||||
kpad->dat_out[bank] |= bit;
|
||||
@ -248,8 +247,6 @@ static void adp5588_gpio_set_value(struct gpio_chip *chip,
|
||||
kpad->dat_out[bank] &= ~bit;
|
||||
|
||||
adp5588_write(kpad->client, GPIO_DAT_OUT1 + bank, kpad->dat_out[bank]);
|
||||
|
||||
mutex_unlock(&kpad->gpio_lock);
|
||||
}
|
||||
|
||||
static int adp5588_gpio_set_config(struct gpio_chip *chip, unsigned int off,
|
||||
@ -259,7 +256,6 @@ static int adp5588_gpio_set_config(struct gpio_chip *chip, unsigned int off,
|
||||
unsigned int bank = ADP5588_BANK(kpad->gpiomap[off]);
|
||||
unsigned int bit = ADP5588_BIT(kpad->gpiomap[off]);
|
||||
bool pull_disable;
|
||||
int ret;
|
||||
|
||||
switch (pinconf_to_config_param(config)) {
|
||||
case PIN_CONFIG_BIAS_PULL_UP:
|
||||
@ -272,19 +268,15 @@ static int adp5588_gpio_set_config(struct gpio_chip *chip, unsigned int off,
|
||||
return -ENOTSUPP;
|
||||
}
|
||||
|
||||
mutex_lock(&kpad->gpio_lock);
|
||||
guard(mutex)(&kpad->gpio_lock);
|
||||
|
||||
if (pull_disable)
|
||||
kpad->pull_dis[bank] |= bit;
|
||||
else
|
||||
kpad->pull_dis[bank] &= bit;
|
||||
|
||||
ret = adp5588_write(kpad->client, GPIO_PULL1 + bank,
|
||||
kpad->pull_dis[bank]);
|
||||
|
||||
mutex_unlock(&kpad->gpio_lock);
|
||||
|
||||
return ret;
|
||||
return adp5588_write(kpad->client, GPIO_PULL1 + bank,
|
||||
kpad->pull_dis[bank]);
|
||||
}
|
||||
|
||||
static int adp5588_gpio_direction_input(struct gpio_chip *chip, unsigned int off)
|
||||
@ -292,16 +284,11 @@ static int adp5588_gpio_direction_input(struct gpio_chip *chip, unsigned int off
|
||||
struct adp5588_kpad *kpad = gpiochip_get_data(chip);
|
||||
unsigned int bank = ADP5588_BANK(kpad->gpiomap[off]);
|
||||
unsigned int bit = ADP5588_BIT(kpad->gpiomap[off]);
|
||||
int ret;
|
||||
|
||||
mutex_lock(&kpad->gpio_lock);
|
||||
guard(mutex)(&kpad->gpio_lock);
|
||||
|
||||
kpad->dir[bank] &= ~bit;
|
||||
ret = adp5588_write(kpad->client, GPIO_DIR1 + bank, kpad->dir[bank]);
|
||||
|
||||
mutex_unlock(&kpad->gpio_lock);
|
||||
|
||||
return ret;
|
||||
return adp5588_write(kpad->client, GPIO_DIR1 + bank, kpad->dir[bank]);
|
||||
}
|
||||
|
||||
static int adp5588_gpio_direction_output(struct gpio_chip *chip,
|
||||
@ -310,9 +297,9 @@ static int adp5588_gpio_direction_output(struct gpio_chip *chip,
|
||||
struct adp5588_kpad *kpad = gpiochip_get_data(chip);
|
||||
unsigned int bank = ADP5588_BANK(kpad->gpiomap[off]);
|
||||
unsigned int bit = ADP5588_BIT(kpad->gpiomap[off]);
|
||||
int ret;
|
||||
int error;
|
||||
|
||||
mutex_lock(&kpad->gpio_lock);
|
||||
guard(mutex)(&kpad->gpio_lock);
|
||||
|
||||
kpad->dir[bank] |= bit;
|
||||
|
||||
@ -321,17 +308,16 @@ static int adp5588_gpio_direction_output(struct gpio_chip *chip,
|
||||
else
|
||||
kpad->dat_out[bank] &= ~bit;
|
||||
|
||||
ret = adp5588_write(kpad->client, GPIO_DAT_OUT1 + bank,
|
||||
kpad->dat_out[bank]);
|
||||
if (ret)
|
||||
goto out_unlock;
|
||||
error = adp5588_write(kpad->client, GPIO_DAT_OUT1 + bank,
|
||||
kpad->dat_out[bank]);
|
||||
if (error)
|
||||
return error;
|
||||
|
||||
ret = adp5588_write(kpad->client, GPIO_DIR1 + bank, kpad->dir[bank]);
|
||||
error = adp5588_write(kpad->client, GPIO_DIR1 + bank, kpad->dir[bank]);
|
||||
if (error)
|
||||
return error;
|
||||
|
||||
out_unlock:
|
||||
mutex_unlock(&kpad->gpio_lock);
|
||||
|
||||
return ret;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int adp5588_build_gpiomap(struct adp5588_kpad *kpad)
|
||||
@ -446,10 +432,17 @@ static int adp5588_gpio_add(struct adp5588_kpad *kpad)
|
||||
kpad->gc.label = kpad->client->name;
|
||||
kpad->gc.owner = THIS_MODULE;
|
||||
|
||||
girq = &kpad->gc.irq;
|
||||
gpio_irq_chip_set_chip(girq, &adp5588_irq_chip);
|
||||
girq->handler = handle_bad_irq;
|
||||
girq->threaded = true;
|
||||
if (device_property_present(dev, "interrupt-controller")) {
|
||||
if (!kpad->client->irq) {
|
||||
dev_err(dev, "Unable to serve as interrupt controller without interrupt");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
girq = &kpad->gc.irq;
|
||||
gpio_irq_chip_set_chip(girq, &adp5588_irq_chip);
|
||||
girq->handler = handle_bad_irq;
|
||||
girq->threaded = true;
|
||||
}
|
||||
|
||||
mutex_init(&kpad->gpio_lock);
|
||||
|
||||
@ -627,7 +620,7 @@ static int adp5588_setup(struct adp5588_kpad *kpad)
|
||||
|
||||
for (i = 0; i < KEYP_MAX_EVENT; i++) {
|
||||
ret = adp5588_read(client, KEY_EVENTA);
|
||||
if (ret)
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
}
|
||||
|
||||
@ -647,6 +640,18 @@ static int adp5588_fw_parse(struct adp5588_kpad *kpad)
|
||||
struct i2c_client *client = kpad->client;
|
||||
int ret, i;
|
||||
|
||||
/*
|
||||
* Check if the device is to be operated purely in GPIO mode. To do
|
||||
* so, check that no keypad rows or columns have been specified,
|
||||
* since all GPINS should be configured as GPIO.
|
||||
*/
|
||||
if (!device_property_present(&client->dev, "keypad,num-rows") &&
|
||||
!device_property_present(&client->dev, "keypad,num-columns")) {
|
||||
/* If purely GPIO, skip keypad setup */
|
||||
kpad->gpio_only = true;
|
||||
return 0;
|
||||
}
|
||||
|
||||
ret = matrix_keypad_parse_properties(&client->dev, &kpad->rows,
|
||||
&kpad->cols);
|
||||
if (ret)
|
||||
@ -790,17 +795,19 @@ static int adp5588_probe(struct i2c_client *client)
|
||||
if (error)
|
||||
return error;
|
||||
|
||||
error = devm_request_threaded_irq(&client->dev, client->irq,
|
||||
adp5588_hard_irq, adp5588_thread_irq,
|
||||
IRQF_TRIGGER_FALLING | IRQF_ONESHOT,
|
||||
client->dev.driver->name, kpad);
|
||||
if (error) {
|
||||
dev_err(&client->dev, "failed to request irq %d: %d\n",
|
||||
client->irq, error);
|
||||
return error;
|
||||
if (client->irq) {
|
||||
error = devm_request_threaded_irq(&client->dev, client->irq,
|
||||
adp5588_hard_irq, adp5588_thread_irq,
|
||||
IRQF_TRIGGER_FALLING | IRQF_ONESHOT,
|
||||
client->dev.driver->name, kpad);
|
||||
if (error) {
|
||||
dev_err(&client->dev, "failed to request irq %d: %d\n",
|
||||
client->irq, error);
|
||||
return error;
|
||||
}
|
||||
}
|
||||
|
||||
dev_info(&client->dev, "Rev.%d keypad, irq %d\n", revid, client->irq);
|
||||
dev_info(&client->dev, "Rev.%d controller\n", revid);
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
@ -639,7 +639,7 @@ static void atkbd_event_work(struct work_struct *work)
|
||||
{
|
||||
struct atkbd *atkbd = container_of(work, struct atkbd, event_work.work);
|
||||
|
||||
mutex_lock(&atkbd->mutex);
|
||||
guard(mutex)(&atkbd->mutex);
|
||||
|
||||
if (!atkbd->enabled) {
|
||||
/*
|
||||
@ -657,8 +657,6 @@ static void atkbd_event_work(struct work_struct *work)
|
||||
if (test_and_clear_bit(ATKBD_REP_EVENT_BIT, &atkbd->event_mask))
|
||||
atkbd_set_repeat_rate(atkbd);
|
||||
}
|
||||
|
||||
mutex_unlock(&atkbd->mutex);
|
||||
}
|
||||
|
||||
/*
|
||||
@ -1361,7 +1359,7 @@ static int atkbd_reconnect(struct serio *serio)
|
||||
{
|
||||
struct atkbd *atkbd = atkbd_from_serio(serio);
|
||||
struct serio_driver *drv = serio->drv;
|
||||
int retval = -1;
|
||||
int error;
|
||||
|
||||
if (!atkbd || !drv) {
|
||||
dev_dbg(&serio->dev,
|
||||
@ -1369,16 +1367,17 @@ static int atkbd_reconnect(struct serio *serio)
|
||||
return -1;
|
||||
}
|
||||
|
||||
mutex_lock(&atkbd->mutex);
|
||||
guard(mutex)(&atkbd->mutex);
|
||||
|
||||
atkbd_disable(atkbd);
|
||||
|
||||
if (atkbd->write) {
|
||||
if (atkbd_probe(atkbd))
|
||||
goto out;
|
||||
error = atkbd_probe(atkbd);
|
||||
if (error)
|
||||
return error;
|
||||
|
||||
if (atkbd->set != atkbd_select_set(atkbd, atkbd->set, atkbd->extra))
|
||||
goto out;
|
||||
return -EIO;
|
||||
|
||||
/*
|
||||
* Restore LED state and repeat rate. While input core
|
||||
@ -1404,11 +1403,7 @@ static int atkbd_reconnect(struct serio *serio)
|
||||
if (atkbd->write)
|
||||
atkbd_activate(atkbd);
|
||||
|
||||
retval = 0;
|
||||
|
||||
out:
|
||||
mutex_unlock(&atkbd->mutex);
|
||||
return retval;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct serio_device_id atkbd_serio_ids[] = {
|
||||
@ -1465,17 +1460,15 @@ static ssize_t atkbd_attr_set_helper(struct device *dev, const char *buf, size_t
|
||||
struct atkbd *atkbd = atkbd_from_serio(serio);
|
||||
int retval;
|
||||
|
||||
retval = mutex_lock_interruptible(&atkbd->mutex);
|
||||
if (retval)
|
||||
scoped_guard(mutex_intr, &atkbd->mutex) {
|
||||
atkbd_disable(atkbd);
|
||||
retval = handler(atkbd, buf, count);
|
||||
atkbd_enable(atkbd);
|
||||
|
||||
return retval;
|
||||
}
|
||||
|
||||
atkbd_disable(atkbd);
|
||||
retval = handler(atkbd, buf, count);
|
||||
atkbd_enable(atkbd);
|
||||
|
||||
mutex_unlock(&atkbd->mutex);
|
||||
|
||||
return retval;
|
||||
return -EINTR;
|
||||
}
|
||||
|
||||
static ssize_t atkbd_show_extra(struct atkbd *atkbd, char *buf)
|
||||
|
@ -245,23 +245,20 @@ static ssize_t gpio_keys_attr_store_helper(struct gpio_keys_drvdata *ddata,
|
||||
{
|
||||
int n_events = get_n_events_by_type(type);
|
||||
const unsigned long *bitmap = get_bm_events_by_type(ddata->input, type);
|
||||
unsigned long *bits;
|
||||
ssize_t error;
|
||||
int i;
|
||||
|
||||
bits = bitmap_alloc(n_events, GFP_KERNEL);
|
||||
unsigned long *bits __free(bitmap) = bitmap_alloc(n_events, GFP_KERNEL);
|
||||
if (!bits)
|
||||
return -ENOMEM;
|
||||
|
||||
error = bitmap_parselist(buf, bits, n_events);
|
||||
if (error)
|
||||
goto out;
|
||||
return error;
|
||||
|
||||
/* First validate */
|
||||
if (!bitmap_subset(bits, bitmap, n_events)) {
|
||||
error = -EINVAL;
|
||||
goto out;
|
||||
}
|
||||
if (!bitmap_subset(bits, bitmap, n_events))
|
||||
return -EINVAL;
|
||||
|
||||
for (i = 0; i < ddata->pdata->nbuttons; i++) {
|
||||
struct gpio_button_data *bdata = &ddata->data[i];
|
||||
@ -271,12 +268,11 @@ static ssize_t gpio_keys_attr_store_helper(struct gpio_keys_drvdata *ddata,
|
||||
|
||||
if (test_bit(*bdata->code, bits) &&
|
||||
!bdata->button->can_disable) {
|
||||
error = -EINVAL;
|
||||
goto out;
|
||||
return -EINVAL;
|
||||
}
|
||||
}
|
||||
|
||||
mutex_lock(&ddata->disable_lock);
|
||||
guard(mutex)(&ddata->disable_lock);
|
||||
|
||||
for (i = 0; i < ddata->pdata->nbuttons; i++) {
|
||||
struct gpio_button_data *bdata = &ddata->data[i];
|
||||
@ -290,11 +286,7 @@ static ssize_t gpio_keys_attr_store_helper(struct gpio_keys_drvdata *ddata,
|
||||
gpio_keys_enable_button(bdata);
|
||||
}
|
||||
|
||||
mutex_unlock(&ddata->disable_lock);
|
||||
|
||||
out:
|
||||
bitmap_free(bits);
|
||||
return error;
|
||||
return 0;
|
||||
}
|
||||
|
||||
#define ATTR_SHOW_FN(name, type, only_disabled) \
|
||||
@ -470,11 +462,10 @@ static irqreturn_t gpio_keys_irq_isr(int irq, void *dev_id)
|
||||
{
|
||||
struct gpio_button_data *bdata = dev_id;
|
||||
struct input_dev *input = bdata->input;
|
||||
unsigned long flags;
|
||||
|
||||
BUG_ON(irq != bdata->irq);
|
||||
|
||||
spin_lock_irqsave(&bdata->lock, flags);
|
||||
guard(spinlock_irqsave)(&bdata->lock);
|
||||
|
||||
if (!bdata->key_pressed) {
|
||||
if (bdata->button->wakeup)
|
||||
@ -497,7 +488,6 @@ static irqreturn_t gpio_keys_irq_isr(int irq, void *dev_id)
|
||||
ms_to_ktime(bdata->release_delay),
|
||||
HRTIMER_MODE_REL_HARD);
|
||||
out:
|
||||
spin_unlock_irqrestore(&bdata->lock, flags);
|
||||
return IRQ_HANDLED;
|
||||
}
|
||||
|
||||
@ -768,7 +758,6 @@ gpio_keys_get_devtree_pdata(struct device *dev)
|
||||
{
|
||||
struct gpio_keys_platform_data *pdata;
|
||||
struct gpio_keys_button *button;
|
||||
struct fwnode_handle *child;
|
||||
int nbuttons, irq;
|
||||
|
||||
nbuttons = device_get_child_node_count(dev);
|
||||
@ -790,7 +779,7 @@ gpio_keys_get_devtree_pdata(struct device *dev)
|
||||
|
||||
device_property_read_string(dev, "label", &pdata->name);
|
||||
|
||||
device_for_each_child_node(dev, child) {
|
||||
device_for_each_child_node_scoped(dev, child) {
|
||||
if (is_of_node(child)) {
|
||||
irq = of_irq_get_byname(to_of_node(child), "irq");
|
||||
if (irq > 0)
|
||||
@ -808,7 +797,6 @@ gpio_keys_get_devtree_pdata(struct device *dev)
|
||||
if (fwnode_property_read_u32(child, "linux,code",
|
||||
&button->code)) {
|
||||
dev_err(dev, "Button without keycode\n");
|
||||
fwnode_handle_put(child);
|
||||
return ERR_PTR(-EINVAL);
|
||||
}
|
||||
|
||||
@ -1064,10 +1052,10 @@ static int gpio_keys_suspend(struct device *dev)
|
||||
if (error)
|
||||
return error;
|
||||
} else {
|
||||
mutex_lock(&input->mutex);
|
||||
guard(mutex)(&input->mutex);
|
||||
|
||||
if (input_device_enabled(input))
|
||||
gpio_keys_close(input);
|
||||
mutex_unlock(&input->mutex);
|
||||
}
|
||||
|
||||
return 0;
|
||||
@ -1077,19 +1065,19 @@ static int gpio_keys_resume(struct device *dev)
|
||||
{
|
||||
struct gpio_keys_drvdata *ddata = dev_get_drvdata(dev);
|
||||
struct input_dev *input = ddata->input;
|
||||
int error = 0;
|
||||
int error;
|
||||
|
||||
if (device_may_wakeup(dev)) {
|
||||
gpio_keys_disable_wakeup(ddata);
|
||||
} else {
|
||||
mutex_lock(&input->mutex);
|
||||
if (input_device_enabled(input))
|
||||
error = gpio_keys_open(input);
|
||||
mutex_unlock(&input->mutex);
|
||||
}
|
||||
guard(mutex)(&input->mutex);
|
||||
|
||||
if (error)
|
||||
return error;
|
||||
if (input_device_enabled(input)) {
|
||||
error = gpio_keys_open(input);
|
||||
if (error)
|
||||
return error;
|
||||
}
|
||||
}
|
||||
|
||||
gpio_keys_report_state(ddata);
|
||||
return 0;
|
||||
|
@ -144,7 +144,6 @@ gpio_keys_polled_get_devtree_pdata(struct device *dev)
|
||||
{
|
||||
struct gpio_keys_platform_data *pdata;
|
||||
struct gpio_keys_button *button;
|
||||
struct fwnode_handle *child;
|
||||
int nbuttons;
|
||||
|
||||
nbuttons = device_get_child_node_count(dev);
|
||||
@ -166,11 +165,10 @@ gpio_keys_polled_get_devtree_pdata(struct device *dev)
|
||||
|
||||
device_property_read_string(dev, "label", &pdata->name);
|
||||
|
||||
device_for_each_child_node(dev, child) {
|
||||
device_for_each_child_node_scoped(dev, child) {
|
||||
if (fwnode_property_read_u32(child, "linux,code",
|
||||
&button->code)) {
|
||||
dev_err(dev, "button without keycode\n");
|
||||
fwnode_handle_put(child);
|
||||
return ERR_PTR(-EINVAL);
|
||||
}
|
||||
|
||||
|
@ -45,7 +45,6 @@ struct iqs62x_keys_private {
|
||||
static int iqs62x_keys_parse_prop(struct platform_device *pdev,
|
||||
struct iqs62x_keys_private *iqs62x_keys)
|
||||
{
|
||||
struct fwnode_handle *child;
|
||||
unsigned int val;
|
||||
int ret, i;
|
||||
|
||||
@ -68,7 +67,8 @@ static int iqs62x_keys_parse_prop(struct platform_device *pdev,
|
||||
}
|
||||
|
||||
for (i = 0; i < ARRAY_SIZE(iqs62x_keys->switches); i++) {
|
||||
child = device_get_named_child_node(&pdev->dev,
|
||||
struct fwnode_handle *child __free(fwnode_handle) =
|
||||
device_get_named_child_node(&pdev->dev,
|
||||
iqs62x_switch_names[i]);
|
||||
if (!child)
|
||||
continue;
|
||||
@ -77,7 +77,6 @@ static int iqs62x_keys_parse_prop(struct platform_device *pdev,
|
||||
if (ret) {
|
||||
dev_err(&pdev->dev, "Failed to read switch code: %d\n",
|
||||
ret);
|
||||
fwnode_handle_put(child);
|
||||
return ret;
|
||||
}
|
||||
iqs62x_keys->switches[i].code = val;
|
||||
@ -91,8 +90,6 @@ static int iqs62x_keys_parse_prop(struct platform_device *pdev,
|
||||
iqs62x_keys->switches[i].flag = (i == IQS62X_SW_HALL_N ?
|
||||
IQS62X_EVENT_HALL_N_T :
|
||||
IQS62X_EVENT_HALL_S_T);
|
||||
|
||||
fwnode_handle_put(child);
|
||||
}
|
||||
|
||||
return 0;
|
||||
|
@ -17,19 +17,27 @@
|
||||
#include <linux/jiffies.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/gpio.h>
|
||||
#include <linux/gpio/consumer.h>
|
||||
#include <linux/input/matrix_keypad.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/of.h>
|
||||
#include <linux/of_gpio.h>
|
||||
#include <linux/of_platform.h>
|
||||
|
||||
struct matrix_keypad {
|
||||
const struct matrix_keypad_platform_data *pdata;
|
||||
struct input_dev *input_dev;
|
||||
unsigned int row_shift;
|
||||
|
||||
unsigned int col_scan_delay_us;
|
||||
/* key debounce interval in milli-second */
|
||||
unsigned int debounce_ms;
|
||||
bool drive_inactive_cols;
|
||||
|
||||
struct gpio_desc *row_gpios[MATRIX_MAX_ROWS];
|
||||
unsigned int num_row_gpios;
|
||||
|
||||
struct gpio_desc *col_gpios[MATRIX_MAX_ROWS];
|
||||
unsigned int num_col_gpios;
|
||||
|
||||
unsigned int row_irqs[MATRIX_MAX_ROWS];
|
||||
unsigned int num_row_irqs;
|
||||
DECLARE_BITMAP(wakeup_enabled_irqs, MATRIX_MAX_ROWS);
|
||||
|
||||
uint32_t last_key_state[MATRIX_MAX_COLS];
|
||||
@ -45,50 +53,43 @@ struct matrix_keypad {
|
||||
* columns. In that case it is configured here to be input, otherwise it is
|
||||
* driven with the inactive value.
|
||||
*/
|
||||
static void __activate_col(const struct matrix_keypad_platform_data *pdata,
|
||||
int col, bool on)
|
||||
static void __activate_col(struct matrix_keypad *keypad, int col, bool on)
|
||||
{
|
||||
bool level_on = !pdata->active_low;
|
||||
|
||||
if (on) {
|
||||
gpio_direction_output(pdata->col_gpios[col], level_on);
|
||||
gpiod_direction_output(keypad->col_gpios[col], 1);
|
||||
} else {
|
||||
gpio_set_value_cansleep(pdata->col_gpios[col], !level_on);
|
||||
if (!pdata->drive_inactive_cols)
|
||||
gpio_direction_input(pdata->col_gpios[col]);
|
||||
gpiod_set_value_cansleep(keypad->col_gpios[col], 0);
|
||||
if (!keypad->drive_inactive_cols)
|
||||
gpiod_direction_input(keypad->col_gpios[col]);
|
||||
}
|
||||
}
|
||||
|
||||
static void activate_col(const struct matrix_keypad_platform_data *pdata,
|
||||
int col, bool on)
|
||||
static void activate_col(struct matrix_keypad *keypad, int col, bool on)
|
||||
{
|
||||
__activate_col(pdata, col, on);
|
||||
__activate_col(keypad, col, on);
|
||||
|
||||
if (on && pdata->col_scan_delay_us)
|
||||
udelay(pdata->col_scan_delay_us);
|
||||
if (on && keypad->col_scan_delay_us)
|
||||
udelay(keypad->col_scan_delay_us);
|
||||
}
|
||||
|
||||
static void activate_all_cols(const struct matrix_keypad_platform_data *pdata,
|
||||
bool on)
|
||||
static void activate_all_cols(struct matrix_keypad *keypad, bool on)
|
||||
{
|
||||
int col;
|
||||
|
||||
for (col = 0; col < pdata->num_col_gpios; col++)
|
||||
__activate_col(pdata, col, on);
|
||||
for (col = 0; col < keypad->num_col_gpios; col++)
|
||||
__activate_col(keypad, col, on);
|
||||
}
|
||||
|
||||
static bool row_asserted(const struct matrix_keypad_platform_data *pdata,
|
||||
int row)
|
||||
static bool row_asserted(struct matrix_keypad *keypad, int row)
|
||||
{
|
||||
return gpio_get_value_cansleep(pdata->row_gpios[row]) ?
|
||||
!pdata->active_low : pdata->active_low;
|
||||
return gpiod_get_value_cansleep(keypad->row_gpios[row]);
|
||||
}
|
||||
|
||||
static void enable_row_irqs(struct matrix_keypad *keypad)
|
||||
{
|
||||
int i;
|
||||
|
||||
for (i = 0; i < keypad->num_row_irqs; i++)
|
||||
for (i = 0; i < keypad->num_row_gpios; i++)
|
||||
enable_irq(keypad->row_irqs[i]);
|
||||
}
|
||||
|
||||
@ -96,7 +97,7 @@ static void disable_row_irqs(struct matrix_keypad *keypad)
|
||||
{
|
||||
int i;
|
||||
|
||||
for (i = 0; i < keypad->num_row_irqs; i++)
|
||||
for (i = 0; i < keypad->num_row_gpios; i++)
|
||||
disable_irq_nosync(keypad->row_irqs[i]);
|
||||
}
|
||||
|
||||
@ -109,39 +110,38 @@ static void matrix_keypad_scan(struct work_struct *work)
|
||||
container_of(work, struct matrix_keypad, work.work);
|
||||
struct input_dev *input_dev = keypad->input_dev;
|
||||
const unsigned short *keycodes = input_dev->keycode;
|
||||
const struct matrix_keypad_platform_data *pdata = keypad->pdata;
|
||||
uint32_t new_state[MATRIX_MAX_COLS];
|
||||
int row, col, code;
|
||||
|
||||
/* de-activate all columns for scanning */
|
||||
activate_all_cols(pdata, false);
|
||||
activate_all_cols(keypad, false);
|
||||
|
||||
memset(new_state, 0, sizeof(new_state));
|
||||
|
||||
for (row = 0; row < pdata->num_row_gpios; row++)
|
||||
gpio_direction_input(pdata->row_gpios[row]);
|
||||
for (row = 0; row < keypad->num_row_gpios; row++)
|
||||
gpiod_direction_input(keypad->row_gpios[row]);
|
||||
|
||||
/* assert each column and read the row status out */
|
||||
for (col = 0; col < pdata->num_col_gpios; col++) {
|
||||
for (col = 0; col < keypad->num_col_gpios; col++) {
|
||||
|
||||
activate_col(pdata, col, true);
|
||||
activate_col(keypad, col, true);
|
||||
|
||||
for (row = 0; row < pdata->num_row_gpios; row++)
|
||||
for (row = 0; row < keypad->num_row_gpios; row++)
|
||||
new_state[col] |=
|
||||
row_asserted(pdata, row) ? (1 << row) : 0;
|
||||
row_asserted(keypad, row) ? BIT(row) : 0;
|
||||
|
||||
activate_col(pdata, col, false);
|
||||
activate_col(keypad, col, false);
|
||||
}
|
||||
|
||||
for (col = 0; col < pdata->num_col_gpios; col++) {
|
||||
for (col = 0; col < keypad->num_col_gpios; col++) {
|
||||
uint32_t bits_changed;
|
||||
|
||||
bits_changed = keypad->last_key_state[col] ^ new_state[col];
|
||||
if (bits_changed == 0)
|
||||
continue;
|
||||
|
||||
for (row = 0; row < pdata->num_row_gpios; row++) {
|
||||
if ((bits_changed & (1 << row)) == 0)
|
||||
for (row = 0; row < keypad->num_row_gpios; row++) {
|
||||
if (!(bits_changed & BIT(row)))
|
||||
continue;
|
||||
|
||||
code = MATRIX_SCAN_CODE(row, col, keypad->row_shift);
|
||||
@ -155,7 +155,7 @@ static void matrix_keypad_scan(struct work_struct *work)
|
||||
|
||||
memcpy(keypad->last_key_state, new_state, sizeof(new_state));
|
||||
|
||||
activate_all_cols(pdata, true);
|
||||
activate_all_cols(keypad, true);
|
||||
|
||||
/* Enable IRQs again */
|
||||
spin_lock_irq(&keypad->lock);
|
||||
@ -182,7 +182,7 @@ static irqreturn_t matrix_keypad_interrupt(int irq, void *id)
|
||||
disable_row_irqs(keypad);
|
||||
keypad->scan_pending = true;
|
||||
schedule_delayed_work(&keypad->work,
|
||||
msecs_to_jiffies(keypad->pdata->debounce_ms));
|
||||
msecs_to_jiffies(keypad->debounce_ms));
|
||||
|
||||
out:
|
||||
spin_unlock_irqrestore(&keypad->lock, flags);
|
||||
@ -225,7 +225,8 @@ static void matrix_keypad_enable_wakeup(struct matrix_keypad *keypad)
|
||||
{
|
||||
int i;
|
||||
|
||||
for_each_clear_bit(i, keypad->wakeup_enabled_irqs, keypad->num_row_irqs)
|
||||
for_each_clear_bit(i, keypad->wakeup_enabled_irqs,
|
||||
keypad->num_row_gpios)
|
||||
if (enable_irq_wake(keypad->row_irqs[i]) == 0)
|
||||
__set_bit(i, keypad->wakeup_enabled_irqs);
|
||||
}
|
||||
@ -234,7 +235,8 @@ static void matrix_keypad_disable_wakeup(struct matrix_keypad *keypad)
|
||||
{
|
||||
int i;
|
||||
|
||||
for_each_set_bit(i, keypad->wakeup_enabled_irqs, keypad->num_row_irqs) {
|
||||
for_each_set_bit(i, keypad->wakeup_enabled_irqs,
|
||||
keypad->num_row_gpios) {
|
||||
disable_irq_wake(keypad->row_irqs[i]);
|
||||
__clear_bit(i, keypad->wakeup_enabled_irqs);
|
||||
}
|
||||
@ -272,78 +274,93 @@ static DEFINE_SIMPLE_DEV_PM_OPS(matrix_keypad_pm_ops,
|
||||
static int matrix_keypad_init_gpio(struct platform_device *pdev,
|
||||
struct matrix_keypad *keypad)
|
||||
{
|
||||
const struct matrix_keypad_platform_data *pdata = keypad->pdata;
|
||||
int i, irq, err;
|
||||
bool active_low;
|
||||
int nrow, ncol;
|
||||
int err;
|
||||
int i;
|
||||
|
||||
/* initialized strobe lines as outputs, activated */
|
||||
for (i = 0; i < pdata->num_col_gpios; i++) {
|
||||
err = devm_gpio_request(&pdev->dev,
|
||||
pdata->col_gpios[i], "matrix_kbd_col");
|
||||
nrow = gpiod_count(&pdev->dev, "row");
|
||||
ncol = gpiod_count(&pdev->dev, "col");
|
||||
if (nrow < 0 || ncol < 0) {
|
||||
dev_err(&pdev->dev, "missing row or column GPIOs\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
keypad->num_row_gpios = nrow;
|
||||
keypad->num_col_gpios = ncol;
|
||||
|
||||
active_low = device_property_read_bool(&pdev->dev, "gpio-activelow");
|
||||
|
||||
/* initialize strobe lines as outputs, activated */
|
||||
for (i = 0; i < keypad->num_col_gpios; i++) {
|
||||
keypad->col_gpios[i] = devm_gpiod_get_index(&pdev->dev, "col",
|
||||
i, GPIOD_ASIS);
|
||||
err = PTR_ERR_OR_ZERO(keypad->col_gpios[i]);
|
||||
if (err) {
|
||||
dev_err(&pdev->dev,
|
||||
"failed to request GPIO%d for COL%d\n",
|
||||
pdata->col_gpios[i], i);
|
||||
"failed to request GPIO for COL%d: %d\n",
|
||||
i, err);
|
||||
return err;
|
||||
}
|
||||
|
||||
gpio_direction_output(pdata->col_gpios[i], !pdata->active_low);
|
||||
gpiod_set_consumer_name(keypad->col_gpios[i], "matrix_kbd_col");
|
||||
|
||||
if (active_low ^ gpiod_is_active_low(keypad->col_gpios[i]))
|
||||
gpiod_toggle_active_low(keypad->col_gpios[i]);
|
||||
|
||||
gpiod_direction_output(keypad->col_gpios[i], 1);
|
||||
}
|
||||
|
||||
for (i = 0; i < pdata->num_row_gpios; i++) {
|
||||
err = devm_gpio_request(&pdev->dev,
|
||||
pdata->row_gpios[i], "matrix_kbd_row");
|
||||
for (i = 0; i < keypad->num_row_gpios; i++) {
|
||||
keypad->row_gpios[i] = devm_gpiod_get_index(&pdev->dev, "row",
|
||||
i, GPIOD_IN);
|
||||
err = PTR_ERR_OR_ZERO(keypad->row_gpios[i]);
|
||||
if (err) {
|
||||
dev_err(&pdev->dev,
|
||||
"failed to request GPIO%d for ROW%d\n",
|
||||
pdata->row_gpios[i], i);
|
||||
"failed to request GPIO for ROW%d: %d\n",
|
||||
i, err);
|
||||
return err;
|
||||
}
|
||||
|
||||
gpio_direction_input(pdata->row_gpios[i]);
|
||||
gpiod_set_consumer_name(keypad->row_gpios[i], "matrix_kbd_row");
|
||||
|
||||
if (active_low ^ gpiod_is_active_low(keypad->row_gpios[i]))
|
||||
gpiod_toggle_active_low(keypad->row_gpios[i]);
|
||||
}
|
||||
|
||||
if (pdata->clustered_irq > 0) {
|
||||
err = devm_request_any_context_irq(&pdev->dev,
|
||||
pdata->clustered_irq,
|
||||
matrix_keypad_interrupt,
|
||||
pdata->clustered_irq_flags,
|
||||
"matrix-keypad", keypad);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int matrix_keypad_setup_interrupts(struct platform_device *pdev,
|
||||
struct matrix_keypad *keypad)
|
||||
{
|
||||
int err;
|
||||
int irq;
|
||||
int i;
|
||||
|
||||
for (i = 0; i < keypad->num_row_gpios; i++) {
|
||||
irq = gpiod_to_irq(keypad->row_gpios[i]);
|
||||
if (irq < 0) {
|
||||
err = irq;
|
||||
dev_err(&pdev->dev,
|
||||
"Unable to convert GPIO line %i to irq: %d\n",
|
||||
i, err);
|
||||
return err;
|
||||
}
|
||||
|
||||
err = devm_request_any_context_irq(&pdev->dev, irq,
|
||||
matrix_keypad_interrupt,
|
||||
IRQF_TRIGGER_RISING |
|
||||
IRQF_TRIGGER_FALLING,
|
||||
"matrix-keypad", keypad);
|
||||
if (err < 0) {
|
||||
dev_err(&pdev->dev,
|
||||
"Unable to acquire clustered interrupt\n");
|
||||
"Unable to acquire interrupt for row %i: %d\n",
|
||||
i, err);
|
||||
return err;
|
||||
}
|
||||
|
||||
keypad->row_irqs[0] = pdata->clustered_irq;
|
||||
keypad->num_row_irqs = 1;
|
||||
} else {
|
||||
for (i = 0; i < pdata->num_row_gpios; i++) {
|
||||
irq = gpio_to_irq(pdata->row_gpios[i]);
|
||||
if (irq < 0) {
|
||||
err = irq;
|
||||
dev_err(&pdev->dev,
|
||||
"Unable to convert GPIO line %i to irq: %d\n",
|
||||
pdata->row_gpios[i], err);
|
||||
return err;
|
||||
}
|
||||
|
||||
err = devm_request_any_context_irq(&pdev->dev,
|
||||
irq,
|
||||
matrix_keypad_interrupt,
|
||||
IRQF_TRIGGER_RISING |
|
||||
IRQF_TRIGGER_FALLING,
|
||||
"matrix-keypad", keypad);
|
||||
if (err < 0) {
|
||||
dev_err(&pdev->dev,
|
||||
"Unable to acquire interrupt for GPIO line %i\n",
|
||||
pdata->row_gpios[i]);
|
||||
return err;
|
||||
}
|
||||
|
||||
keypad->row_irqs[i] = irq;
|
||||
}
|
||||
|
||||
keypad->num_row_irqs = pdata->num_row_gpios;
|
||||
keypad->row_irqs[i] = irq;
|
||||
}
|
||||
|
||||
/* initialized as disabled - enabled by input->open */
|
||||
@ -352,102 +369,13 @@ static int matrix_keypad_init_gpio(struct platform_device *pdev,
|
||||
return 0;
|
||||
}
|
||||
|
||||
#ifdef CONFIG_OF
|
||||
static struct matrix_keypad_platform_data *
|
||||
matrix_keypad_parse_dt(struct device *dev)
|
||||
{
|
||||
struct matrix_keypad_platform_data *pdata;
|
||||
struct device_node *np = dev->of_node;
|
||||
unsigned int *gpios;
|
||||
int ret, i, nrow, ncol;
|
||||
|
||||
if (!np) {
|
||||
dev_err(dev, "device lacks DT data\n");
|
||||
return ERR_PTR(-ENODEV);
|
||||
}
|
||||
|
||||
pdata = devm_kzalloc(dev, sizeof(*pdata), GFP_KERNEL);
|
||||
if (!pdata) {
|
||||
dev_err(dev, "could not allocate memory for platform data\n");
|
||||
return ERR_PTR(-ENOMEM);
|
||||
}
|
||||
|
||||
pdata->num_row_gpios = nrow = gpiod_count(dev, "row");
|
||||
pdata->num_col_gpios = ncol = gpiod_count(dev, "col");
|
||||
if (nrow < 0 || ncol < 0) {
|
||||
dev_err(dev, "number of keypad rows/columns not specified\n");
|
||||
return ERR_PTR(-EINVAL);
|
||||
}
|
||||
|
||||
pdata->no_autorepeat = of_property_read_bool(np, "linux,no-autorepeat");
|
||||
|
||||
pdata->wakeup = of_property_read_bool(np, "wakeup-source") ||
|
||||
of_property_read_bool(np, "linux,wakeup"); /* legacy */
|
||||
|
||||
pdata->active_low = of_property_read_bool(np, "gpio-activelow");
|
||||
|
||||
pdata->drive_inactive_cols =
|
||||
of_property_read_bool(np, "drive-inactive-cols");
|
||||
|
||||
of_property_read_u32(np, "debounce-delay-ms", &pdata->debounce_ms);
|
||||
of_property_read_u32(np, "col-scan-delay-us",
|
||||
&pdata->col_scan_delay_us);
|
||||
|
||||
gpios = devm_kcalloc(dev,
|
||||
pdata->num_row_gpios + pdata->num_col_gpios,
|
||||
sizeof(unsigned int),
|
||||
GFP_KERNEL);
|
||||
if (!gpios) {
|
||||
dev_err(dev, "could not allocate memory for gpios\n");
|
||||
return ERR_PTR(-ENOMEM);
|
||||
}
|
||||
|
||||
for (i = 0; i < nrow; i++) {
|
||||
ret = of_get_named_gpio(np, "row-gpios", i);
|
||||
if (ret < 0)
|
||||
return ERR_PTR(ret);
|
||||
gpios[i] = ret;
|
||||
}
|
||||
|
||||
for (i = 0; i < ncol; i++) {
|
||||
ret = of_get_named_gpio(np, "col-gpios", i);
|
||||
if (ret < 0)
|
||||
return ERR_PTR(ret);
|
||||
gpios[nrow + i] = ret;
|
||||
}
|
||||
|
||||
pdata->row_gpios = gpios;
|
||||
pdata->col_gpios = &gpios[pdata->num_row_gpios];
|
||||
|
||||
return pdata;
|
||||
}
|
||||
#else
|
||||
static inline struct matrix_keypad_platform_data *
|
||||
matrix_keypad_parse_dt(struct device *dev)
|
||||
{
|
||||
dev_err(dev, "no platform data defined\n");
|
||||
|
||||
return ERR_PTR(-EINVAL);
|
||||
}
|
||||
#endif
|
||||
|
||||
static int matrix_keypad_probe(struct platform_device *pdev)
|
||||
{
|
||||
const struct matrix_keypad_platform_data *pdata;
|
||||
struct matrix_keypad *keypad;
|
||||
struct input_dev *input_dev;
|
||||
bool wakeup;
|
||||
int err;
|
||||
|
||||
pdata = dev_get_platdata(&pdev->dev);
|
||||
if (!pdata) {
|
||||
pdata = matrix_keypad_parse_dt(&pdev->dev);
|
||||
if (IS_ERR(pdata))
|
||||
return PTR_ERR(pdata);
|
||||
} else if (!pdata->keymap_data) {
|
||||
dev_err(&pdev->dev, "no keymap data defined\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
keypad = devm_kzalloc(&pdev->dev, sizeof(*keypad), GFP_KERNEL);
|
||||
if (!keypad)
|
||||
return -ENOMEM;
|
||||
@ -457,40 +385,56 @@ static int matrix_keypad_probe(struct platform_device *pdev)
|
||||
return -ENOMEM;
|
||||
|
||||
keypad->input_dev = input_dev;
|
||||
keypad->pdata = pdata;
|
||||
keypad->row_shift = get_count_order(pdata->num_col_gpios);
|
||||
keypad->stopped = true;
|
||||
INIT_DELAYED_WORK(&keypad->work, matrix_keypad_scan);
|
||||
spin_lock_init(&keypad->lock);
|
||||
|
||||
keypad->drive_inactive_cols =
|
||||
device_property_read_bool(&pdev->dev, "drive-inactive-cols");
|
||||
device_property_read_u32(&pdev->dev, "debounce-delay-ms",
|
||||
&keypad->debounce_ms);
|
||||
device_property_read_u32(&pdev->dev, "col-scan-delay-us",
|
||||
&keypad->col_scan_delay_us);
|
||||
|
||||
err = matrix_keypad_init_gpio(pdev, keypad);
|
||||
if (err)
|
||||
return err;
|
||||
|
||||
keypad->row_shift = get_count_order(keypad->num_col_gpios);
|
||||
|
||||
err = matrix_keypad_setup_interrupts(pdev, keypad);
|
||||
if (err)
|
||||
return err;
|
||||
|
||||
input_dev->name = pdev->name;
|
||||
input_dev->id.bustype = BUS_HOST;
|
||||
input_dev->open = matrix_keypad_start;
|
||||
input_dev->close = matrix_keypad_stop;
|
||||
|
||||
err = matrix_keypad_build_keymap(pdata->keymap_data, NULL,
|
||||
pdata->num_row_gpios,
|
||||
pdata->num_col_gpios,
|
||||
err = matrix_keypad_build_keymap(NULL, NULL,
|
||||
keypad->num_row_gpios,
|
||||
keypad->num_col_gpios,
|
||||
NULL, input_dev);
|
||||
if (err) {
|
||||
dev_err(&pdev->dev, "failed to build keymap\n");
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
if (!pdata->no_autorepeat)
|
||||
if (!device_property_read_bool(&pdev->dev, "linux,no-autorepeat"))
|
||||
__set_bit(EV_REP, input_dev->evbit);
|
||||
|
||||
input_set_capability(input_dev, EV_MSC, MSC_SCAN);
|
||||
input_set_drvdata(input_dev, keypad);
|
||||
|
||||
err = matrix_keypad_init_gpio(pdev, keypad);
|
||||
if (err)
|
||||
return err;
|
||||
|
||||
err = input_register_device(keypad->input_dev);
|
||||
if (err)
|
||||
return err;
|
||||
|
||||
device_init_wakeup(&pdev->dev, pdata->wakeup);
|
||||
wakeup = device_property_read_bool(&pdev->dev, "wakeup-source") ||
|
||||
/* legacy */
|
||||
device_property_read_bool(&pdev->dev, "linux,wakeup");
|
||||
device_init_wakeup(&pdev->dev, wakeup);
|
||||
|
||||
platform_set_drvdata(pdev, keypad);
|
||||
|
||||
return 0;
|
||||
|
@ -1,268 +0,0 @@
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
/*
|
||||
* Touchkey driver for MELFAS MCS5000/5080 controller
|
||||
*
|
||||
* Copyright (C) 2010 Samsung Electronics Co.Ltd
|
||||
* Author: HeungJun Kim <riverful.kim@samsung.com>
|
||||
* Author: Joonyoung Shim <jy0922.shim@samsung.com>
|
||||
*/
|
||||
|
||||
#include <linux/module.h>
|
||||
#include <linux/i2c.h>
|
||||
#include <linux/interrupt.h>
|
||||
#include <linux/input.h>
|
||||
#include <linux/irq.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/platform_data/mcs.h>
|
||||
#include <linux/pm.h>
|
||||
|
||||
/* MCS5000 Touchkey */
|
||||
#define MCS5000_TOUCHKEY_STATUS 0x04
|
||||
#define MCS5000_TOUCHKEY_STATUS_PRESS 7
|
||||
#define MCS5000_TOUCHKEY_FW 0x0a
|
||||
#define MCS5000_TOUCHKEY_BASE_VAL 0x61
|
||||
|
||||
/* MCS5080 Touchkey */
|
||||
#define MCS5080_TOUCHKEY_STATUS 0x00
|
||||
#define MCS5080_TOUCHKEY_STATUS_PRESS 3
|
||||
#define MCS5080_TOUCHKEY_FW 0x01
|
||||
#define MCS5080_TOUCHKEY_BASE_VAL 0x1
|
||||
|
||||
enum mcs_touchkey_type {
|
||||
MCS5000_TOUCHKEY,
|
||||
MCS5080_TOUCHKEY,
|
||||
};
|
||||
|
||||
struct mcs_touchkey_chip {
|
||||
unsigned int status_reg;
|
||||
unsigned int pressbit;
|
||||
unsigned int press_invert;
|
||||
unsigned int baseval;
|
||||
};
|
||||
|
||||
struct mcs_touchkey_data {
|
||||
void (*poweron)(bool);
|
||||
|
||||
struct i2c_client *client;
|
||||
struct input_dev *input_dev;
|
||||
struct mcs_touchkey_chip chip;
|
||||
unsigned int key_code;
|
||||
unsigned int key_val;
|
||||
unsigned short keycodes[];
|
||||
};
|
||||
|
||||
static irqreturn_t mcs_touchkey_interrupt(int irq, void *dev_id)
|
||||
{
|
||||
struct mcs_touchkey_data *data = dev_id;
|
||||
struct mcs_touchkey_chip *chip = &data->chip;
|
||||
struct i2c_client *client = data->client;
|
||||
struct input_dev *input = data->input_dev;
|
||||
unsigned int key_val;
|
||||
unsigned int pressed;
|
||||
int val;
|
||||
|
||||
val = i2c_smbus_read_byte_data(client, chip->status_reg);
|
||||
if (val < 0) {
|
||||
dev_err(&client->dev, "i2c read error [%d]\n", val);
|
||||
goto out;
|
||||
}
|
||||
|
||||
pressed = (val & (1 << chip->pressbit)) >> chip->pressbit;
|
||||
if (chip->press_invert)
|
||||
pressed ^= chip->press_invert;
|
||||
|
||||
/* key_val is 0 when released, so we should use key_val of press. */
|
||||
if (pressed) {
|
||||
key_val = val & (0xff >> (8 - chip->pressbit));
|
||||
if (!key_val)
|
||||
goto out;
|
||||
key_val -= chip->baseval;
|
||||
data->key_code = data->keycodes[key_val];
|
||||
data->key_val = key_val;
|
||||
}
|
||||
|
||||
input_event(input, EV_MSC, MSC_SCAN, data->key_val);
|
||||
input_report_key(input, data->key_code, pressed);
|
||||
input_sync(input);
|
||||
|
||||
dev_dbg(&client->dev, "key %d %d %s\n", data->key_val, data->key_code,
|
||||
pressed ? "pressed" : "released");
|
||||
|
||||
out:
|
||||
return IRQ_HANDLED;
|
||||
}
|
||||
|
||||
static void mcs_touchkey_poweroff(void *data)
|
||||
{
|
||||
struct mcs_touchkey_data *touchkey = data;
|
||||
|
||||
touchkey->poweron(false);
|
||||
}
|
||||
|
||||
static int mcs_touchkey_probe(struct i2c_client *client)
|
||||
{
|
||||
const struct i2c_device_id *id = i2c_client_get_device_id(client);
|
||||
const struct mcs_platform_data *pdata;
|
||||
struct mcs_touchkey_data *data;
|
||||
struct input_dev *input_dev;
|
||||
unsigned int fw_reg;
|
||||
int fw_ver;
|
||||
int error;
|
||||
int i;
|
||||
|
||||
pdata = dev_get_platdata(&client->dev);
|
||||
if (!pdata) {
|
||||
dev_err(&client->dev, "no platform data defined\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
data = devm_kzalloc(&client->dev,
|
||||
struct_size(data, keycodes, pdata->key_maxval + 1),
|
||||
GFP_KERNEL);
|
||||
if (!data)
|
||||
return -ENOMEM;
|
||||
|
||||
input_dev = devm_input_allocate_device(&client->dev);
|
||||
if (!input_dev) {
|
||||
dev_err(&client->dev, "Failed to allocate input device\n");
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
data->client = client;
|
||||
data->input_dev = input_dev;
|
||||
|
||||
if (id->driver_data == MCS5000_TOUCHKEY) {
|
||||
data->chip.status_reg = MCS5000_TOUCHKEY_STATUS;
|
||||
data->chip.pressbit = MCS5000_TOUCHKEY_STATUS_PRESS;
|
||||
data->chip.baseval = MCS5000_TOUCHKEY_BASE_VAL;
|
||||
fw_reg = MCS5000_TOUCHKEY_FW;
|
||||
} else {
|
||||
data->chip.status_reg = MCS5080_TOUCHKEY_STATUS;
|
||||
data->chip.pressbit = MCS5080_TOUCHKEY_STATUS_PRESS;
|
||||
data->chip.press_invert = 1;
|
||||
data->chip.baseval = MCS5080_TOUCHKEY_BASE_VAL;
|
||||
fw_reg = MCS5080_TOUCHKEY_FW;
|
||||
}
|
||||
|
||||
fw_ver = i2c_smbus_read_byte_data(client, fw_reg);
|
||||
if (fw_ver < 0) {
|
||||
dev_err(&client->dev, "i2c read error[%d]\n", fw_ver);
|
||||
return fw_ver;
|
||||
}
|
||||
dev_info(&client->dev, "Firmware version: %d\n", fw_ver);
|
||||
|
||||
input_dev->name = "MELFAS MCS Touchkey";
|
||||
input_dev->id.bustype = BUS_I2C;
|
||||
input_dev->evbit[0] = BIT_MASK(EV_KEY);
|
||||
if (!pdata->no_autorepeat)
|
||||
input_dev->evbit[0] |= BIT_MASK(EV_REP);
|
||||
input_dev->keycode = data->keycodes;
|
||||
input_dev->keycodesize = sizeof(data->keycodes[0]);
|
||||
input_dev->keycodemax = pdata->key_maxval + 1;
|
||||
|
||||
for (i = 0; i < pdata->keymap_size; i++) {
|
||||
unsigned int val = MCS_KEY_VAL(pdata->keymap[i]);
|
||||
unsigned int code = MCS_KEY_CODE(pdata->keymap[i]);
|
||||
|
||||
data->keycodes[val] = code;
|
||||
__set_bit(code, input_dev->keybit);
|
||||
}
|
||||
|
||||
input_set_capability(input_dev, EV_MSC, MSC_SCAN);
|
||||
input_set_drvdata(input_dev, data);
|
||||
|
||||
if (pdata->cfg_pin)
|
||||
pdata->cfg_pin();
|
||||
|
||||
if (pdata->poweron) {
|
||||
data->poweron = pdata->poweron;
|
||||
data->poweron(true);
|
||||
|
||||
error = devm_add_action_or_reset(&client->dev,
|
||||
mcs_touchkey_poweroff, data);
|
||||
if (error)
|
||||
return error;
|
||||
}
|
||||
|
||||
error = devm_request_threaded_irq(&client->dev, client->irq,
|
||||
NULL, mcs_touchkey_interrupt,
|
||||
IRQF_TRIGGER_FALLING | IRQF_ONESHOT,
|
||||
client->dev.driver->name, data);
|
||||
if (error) {
|
||||
dev_err(&client->dev, "Failed to register interrupt\n");
|
||||
return error;
|
||||
}
|
||||
|
||||
error = input_register_device(input_dev);
|
||||
if (error)
|
||||
return error;
|
||||
|
||||
i2c_set_clientdata(client, data);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void mcs_touchkey_shutdown(struct i2c_client *client)
|
||||
{
|
||||
struct mcs_touchkey_data *data = i2c_get_clientdata(client);
|
||||
|
||||
if (data->poweron)
|
||||
data->poweron(false);
|
||||
}
|
||||
|
||||
static int mcs_touchkey_suspend(struct device *dev)
|
||||
{
|
||||
struct mcs_touchkey_data *data = dev_get_drvdata(dev);
|
||||
struct i2c_client *client = data->client;
|
||||
|
||||
/* Disable the work */
|
||||
disable_irq(client->irq);
|
||||
|
||||
/* Finally turn off the power */
|
||||
if (data->poweron)
|
||||
data->poweron(false);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int mcs_touchkey_resume(struct device *dev)
|
||||
{
|
||||
struct mcs_touchkey_data *data = dev_get_drvdata(dev);
|
||||
struct i2c_client *client = data->client;
|
||||
|
||||
/* Enable the device first */
|
||||
if (data->poweron)
|
||||
data->poweron(true);
|
||||
|
||||
/* Enable irq again */
|
||||
enable_irq(client->irq);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static DEFINE_SIMPLE_DEV_PM_OPS(mcs_touchkey_pm_ops,
|
||||
mcs_touchkey_suspend, mcs_touchkey_resume);
|
||||
|
||||
static const struct i2c_device_id mcs_touchkey_id[] = {
|
||||
{ "mcs5000_touchkey", MCS5000_TOUCHKEY },
|
||||
{ "mcs5080_touchkey", MCS5080_TOUCHKEY },
|
||||
{ }
|
||||
};
|
||||
MODULE_DEVICE_TABLE(i2c, mcs_touchkey_id);
|
||||
|
||||
static struct i2c_driver mcs_touchkey_driver = {
|
||||
.driver = {
|
||||
.name = "mcs_touchkey",
|
||||
.pm = pm_sleep_ptr(&mcs_touchkey_pm_ops),
|
||||
},
|
||||
.probe = mcs_touchkey_probe,
|
||||
.shutdown = mcs_touchkey_shutdown,
|
||||
.id_table = mcs_touchkey_id,
|
||||
};
|
||||
|
||||
module_i2c_driver(mcs_touchkey_driver);
|
||||
|
||||
/* Module information */
|
||||
MODULE_AUTHOR("Joonyoung Shim <jy0922.shim@samsung.com>");
|
||||
MODULE_AUTHOR("HeungJun Kim <riverful.kim@samsung.com>");
|
||||
MODULE_DESCRIPTION("Touchkey driver for MELFAS MCS5000/5080 controller");
|
||||
MODULE_LICENSE("GPL");
|
@ -92,11 +92,6 @@ static irqreturn_t mt6779_keypad_irq_handler(int irq, void *dev_id)
|
||||
return IRQ_HANDLED;
|
||||
}
|
||||
|
||||
static void mt6779_keypad_clk_disable(void *data)
|
||||
{
|
||||
clk_disable_unprepare(data);
|
||||
}
|
||||
|
||||
static void mt6779_keypad_calc_row_col_single(unsigned int key,
|
||||
unsigned int *row,
|
||||
unsigned int *col)
|
||||
@ -213,21 +208,10 @@ static int mt6779_keypad_pdrv_probe(struct platform_device *pdev)
|
||||
regmap_update_bits(keypad->regmap, MTK_KPD_SEL, MTK_KPD_SEL_COL,
|
||||
MTK_KPD_SEL_COLMASK(keypad->n_cols));
|
||||
|
||||
keypad->clk = devm_clk_get(&pdev->dev, "kpd");
|
||||
keypad->clk = devm_clk_get_enabled(&pdev->dev, "kpd");
|
||||
if (IS_ERR(keypad->clk))
|
||||
return PTR_ERR(keypad->clk);
|
||||
|
||||
error = clk_prepare_enable(keypad->clk);
|
||||
if (error) {
|
||||
dev_err(&pdev->dev, "cannot prepare/enable keypad clock\n");
|
||||
return error;
|
||||
}
|
||||
|
||||
error = devm_add_action_or_reset(&pdev->dev, mt6779_keypad_clk_disable,
|
||||
keypad->clk);
|
||||
if (error)
|
||||
return error;
|
||||
|
||||
irq = platform_get_irq(pdev, 0);
|
||||
if (irq < 0)
|
||||
return irq;
|
||||
@ -260,6 +244,7 @@ static const struct of_device_id mt6779_keypad_of_match[] = {
|
||||
{ .compatible = "mediatek,mt6873-keypad" },
|
||||
{ /* sentinel */ }
|
||||
};
|
||||
MODULE_DEVICE_TABLE(of, mt6779_keypad_of_match);
|
||||
|
||||
static struct platform_driver mt6779_keypad_pdrv = {
|
||||
.probe = mt6779_keypad_pdrv_probe,
|
||||
|
@ -1,378 +0,0 @@
|
||||
// SPDX-License-Identifier: GPL-2.0-only
|
||||
/*
|
||||
* Copyright (C) ST-Ericsson SA 2010
|
||||
*
|
||||
* Author: Naveen Kumar G <naveen.gaddipati@stericsson.com> for ST-Ericsson
|
||||
* Author: Sundar Iyer <sundar.iyer@stericsson.com> for ST-Ericsson
|
||||
*
|
||||
* Keypad controller driver for the SKE (Scroll Key Encoder) module used in
|
||||
* the Nomadik 8815 and Ux500 platforms.
|
||||
*/
|
||||
|
||||
#include <linux/platform_device.h>
|
||||
#include <linux/interrupt.h>
|
||||
#include <linux/spinlock.h>
|
||||
#include <linux/io.h>
|
||||
#include <linux/delay.h>
|
||||
#include <linux/input.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/clk.h>
|
||||
#include <linux/module.h>
|
||||
|
||||
#include <linux/platform_data/keypad-nomadik-ske.h>
|
||||
|
||||
/* SKE_CR bits */
|
||||
#define SKE_KPMLT (0x1 << 6)
|
||||
#define SKE_KPCN (0x7 << 3)
|
||||
#define SKE_KPASEN (0x1 << 2)
|
||||
#define SKE_KPASON (0x1 << 7)
|
||||
|
||||
/* SKE_IMSC bits */
|
||||
#define SKE_KPIMA (0x1 << 2)
|
||||
|
||||
/* SKE_ICR bits */
|
||||
#define SKE_KPICS (0x1 << 3)
|
||||
#define SKE_KPICA (0x1 << 2)
|
||||
|
||||
/* SKE_RIS bits */
|
||||
#define SKE_KPRISA (0x1 << 2)
|
||||
|
||||
#define SKE_KEYPAD_ROW_SHIFT 3
|
||||
#define SKE_KPD_NUM_ROWS 8
|
||||
#define SKE_KPD_NUM_COLS 8
|
||||
|
||||
/* keypad auto scan registers */
|
||||
#define SKE_ASR0 0x20
|
||||
#define SKE_ASR1 0x24
|
||||
#define SKE_ASR2 0x28
|
||||
#define SKE_ASR3 0x2C
|
||||
|
||||
#define SKE_NUM_ASRX_REGISTERS (4)
|
||||
#define KEY_PRESSED_DELAY 10
|
||||
|
||||
/**
|
||||
* struct ske_keypad - data structure used by keypad driver
|
||||
* @irq: irq no
|
||||
* @reg_base: ske registers base address
|
||||
* @input: pointer to input device object
|
||||
* @board: keypad platform device
|
||||
* @keymap: matrix scan code table for keycodes
|
||||
* @clk: clock structure pointer
|
||||
* @pclk: clock structure pointer
|
||||
* @ske_keypad_lock: spinlock protecting the keypad read/writes
|
||||
*/
|
||||
struct ske_keypad {
|
||||
int irq;
|
||||
void __iomem *reg_base;
|
||||
struct input_dev *input;
|
||||
const struct ske_keypad_platform_data *board;
|
||||
unsigned short keymap[SKE_KPD_NUM_ROWS * SKE_KPD_NUM_COLS];
|
||||
struct clk *clk;
|
||||
struct clk *pclk;
|
||||
spinlock_t ske_keypad_lock;
|
||||
};
|
||||
|
||||
static void ske_keypad_set_bits(struct ske_keypad *keypad, u16 addr,
|
||||
u8 mask, u8 data)
|
||||
{
|
||||
u32 ret;
|
||||
|
||||
spin_lock(&keypad->ske_keypad_lock);
|
||||
|
||||
ret = readl(keypad->reg_base + addr);
|
||||
ret &= ~mask;
|
||||
ret |= data;
|
||||
writel(ret, keypad->reg_base + addr);
|
||||
|
||||
spin_unlock(&keypad->ske_keypad_lock);
|
||||
}
|
||||
|
||||
/*
|
||||
* ske_keypad_chip_init: init keypad controller configuration
|
||||
*
|
||||
* Enable Multi key press detection, auto scan mode
|
||||
*/
|
||||
static int __init ske_keypad_chip_init(struct ske_keypad *keypad)
|
||||
{
|
||||
u32 value;
|
||||
int timeout = keypad->board->debounce_ms;
|
||||
|
||||
/* check SKE_RIS to be 0 */
|
||||
while ((readl(keypad->reg_base + SKE_RIS) != 0x00000000) && timeout--)
|
||||
cpu_relax();
|
||||
|
||||
if (timeout == -1)
|
||||
return -EINVAL;
|
||||
|
||||
/*
|
||||
* set debounce value
|
||||
* keypad dbounce is configured in DBCR[15:8]
|
||||
* dbounce value in steps of 32/32.768 ms
|
||||
*/
|
||||
spin_lock(&keypad->ske_keypad_lock);
|
||||
value = readl(keypad->reg_base + SKE_DBCR);
|
||||
value = value & 0xff;
|
||||
value |= ((keypad->board->debounce_ms * 32000)/32768) << 8;
|
||||
writel(value, keypad->reg_base + SKE_DBCR);
|
||||
spin_unlock(&keypad->ske_keypad_lock);
|
||||
|
||||
/* enable multi key detection */
|
||||
ske_keypad_set_bits(keypad, SKE_CR, 0x0, SKE_KPMLT);
|
||||
|
||||
/*
|
||||
* set up the number of columns
|
||||
* KPCN[5:3] defines no. of keypad columns to be auto scanned
|
||||
*/
|
||||
value = (keypad->board->kcol - 1) << 3;
|
||||
ske_keypad_set_bits(keypad, SKE_CR, SKE_KPCN, value);
|
||||
|
||||
/* clear keypad interrupt for auto(and pending SW) scans */
|
||||
ske_keypad_set_bits(keypad, SKE_ICR, 0x0, SKE_KPICA | SKE_KPICS);
|
||||
|
||||
/* un-mask keypad interrupts */
|
||||
ske_keypad_set_bits(keypad, SKE_IMSC, 0x0, SKE_KPIMA);
|
||||
|
||||
/* enable automatic scan */
|
||||
ske_keypad_set_bits(keypad, SKE_CR, 0x0, SKE_KPASEN);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void ske_keypad_report(struct ske_keypad *keypad, u8 status, int col)
|
||||
{
|
||||
int row = 0, code, pos;
|
||||
struct input_dev *input = keypad->input;
|
||||
u32 ske_ris;
|
||||
int key_pressed;
|
||||
int num_of_rows;
|
||||
|
||||
/* find out the row */
|
||||
num_of_rows = hweight8(status);
|
||||
do {
|
||||
pos = __ffs(status);
|
||||
row = pos;
|
||||
status &= ~(1 << pos);
|
||||
|
||||
code = MATRIX_SCAN_CODE(row, col, SKE_KEYPAD_ROW_SHIFT);
|
||||
ske_ris = readl(keypad->reg_base + SKE_RIS);
|
||||
key_pressed = ske_ris & SKE_KPRISA;
|
||||
|
||||
input_event(input, EV_MSC, MSC_SCAN, code);
|
||||
input_report_key(input, keypad->keymap[code], key_pressed);
|
||||
input_sync(input);
|
||||
num_of_rows--;
|
||||
} while (num_of_rows);
|
||||
}
|
||||
|
||||
static void ske_keypad_read_data(struct ske_keypad *keypad)
|
||||
{
|
||||
u8 status;
|
||||
int col = 0;
|
||||
int ske_asr, i;
|
||||
|
||||
/*
|
||||
* Read the auto scan registers
|
||||
*
|
||||
* Each SKE_ASRx (x=0 to x=3) contains two row values.
|
||||
* lower byte contains row value for column 2*x,
|
||||
* upper byte contains row value for column 2*x + 1
|
||||
*/
|
||||
for (i = 0; i < SKE_NUM_ASRX_REGISTERS; i++) {
|
||||
ske_asr = readl(keypad->reg_base + SKE_ASR0 + (4 * i));
|
||||
if (!ske_asr)
|
||||
continue;
|
||||
|
||||
/* now that ASRx is zero, find out the coloumn x and row y */
|
||||
status = ske_asr & 0xff;
|
||||
if (status) {
|
||||
col = i * 2;
|
||||
ske_keypad_report(keypad, status, col);
|
||||
}
|
||||
status = (ske_asr & 0xff00) >> 8;
|
||||
if (status) {
|
||||
col = (i * 2) + 1;
|
||||
ske_keypad_report(keypad, status, col);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static irqreturn_t ske_keypad_irq(int irq, void *dev_id)
|
||||
{
|
||||
struct ske_keypad *keypad = dev_id;
|
||||
int timeout = keypad->board->debounce_ms;
|
||||
|
||||
/* disable auto scan interrupt; mask the interrupt generated */
|
||||
ske_keypad_set_bits(keypad, SKE_IMSC, ~SKE_KPIMA, 0x0);
|
||||
ske_keypad_set_bits(keypad, SKE_ICR, 0x0, SKE_KPICA);
|
||||
|
||||
while ((readl(keypad->reg_base + SKE_CR) & SKE_KPASON) && --timeout)
|
||||
cpu_relax();
|
||||
|
||||
/* SKEx registers are stable and can be read */
|
||||
ske_keypad_read_data(keypad);
|
||||
|
||||
/* wait until raw interrupt is clear */
|
||||
while ((readl(keypad->reg_base + SKE_RIS)) && --timeout)
|
||||
msleep(KEY_PRESSED_DELAY);
|
||||
|
||||
/* enable auto scan interrupts */
|
||||
ske_keypad_set_bits(keypad, SKE_IMSC, 0x0, SKE_KPIMA);
|
||||
|
||||
return IRQ_HANDLED;
|
||||
}
|
||||
|
||||
static void ske_keypad_board_exit(void *data)
|
||||
{
|
||||
struct ske_keypad *keypad = data;
|
||||
|
||||
keypad->board->exit();
|
||||
}
|
||||
|
||||
static int __init ske_keypad_probe(struct platform_device *pdev)
|
||||
{
|
||||
const struct ske_keypad_platform_data *plat =
|
||||
dev_get_platdata(&pdev->dev);
|
||||
struct device *dev = &pdev->dev;
|
||||
struct ske_keypad *keypad;
|
||||
struct input_dev *input;
|
||||
int irq;
|
||||
int error;
|
||||
|
||||
if (!plat) {
|
||||
dev_err(&pdev->dev, "invalid keypad platform data\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
irq = platform_get_irq(pdev, 0);
|
||||
if (irq < 0)
|
||||
return irq;
|
||||
|
||||
keypad = devm_kzalloc(dev, sizeof(struct ske_keypad),
|
||||
GFP_KERNEL);
|
||||
input = devm_input_allocate_device(dev);
|
||||
if (!keypad || !input) {
|
||||
dev_err(&pdev->dev, "failed to allocate keypad memory\n");
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
keypad->irq = irq;
|
||||
keypad->board = plat;
|
||||
keypad->input = input;
|
||||
spin_lock_init(&keypad->ske_keypad_lock);
|
||||
|
||||
keypad->reg_base = devm_platform_ioremap_resource(pdev, 0);
|
||||
if (IS_ERR(keypad->reg_base))
|
||||
return PTR_ERR(keypad->reg_base);
|
||||
|
||||
keypad->pclk = devm_clk_get_enabled(dev, "apb_pclk");
|
||||
if (IS_ERR(keypad->pclk)) {
|
||||
dev_err(&pdev->dev, "failed to get pclk\n");
|
||||
return PTR_ERR(keypad->pclk);
|
||||
}
|
||||
|
||||
keypad->clk = devm_clk_get_enabled(dev, NULL);
|
||||
if (IS_ERR(keypad->clk)) {
|
||||
dev_err(&pdev->dev, "failed to get clk\n");
|
||||
return PTR_ERR(keypad->clk);
|
||||
}
|
||||
|
||||
input->id.bustype = BUS_HOST;
|
||||
input->name = "ux500-ske-keypad";
|
||||
input->dev.parent = &pdev->dev;
|
||||
|
||||
error = matrix_keypad_build_keymap(plat->keymap_data, NULL,
|
||||
SKE_KPD_NUM_ROWS, SKE_KPD_NUM_COLS,
|
||||
keypad->keymap, input);
|
||||
if (error) {
|
||||
dev_err(&pdev->dev, "Failed to build keymap\n");
|
||||
return error;
|
||||
}
|
||||
|
||||
input_set_capability(input, EV_MSC, MSC_SCAN);
|
||||
if (!plat->no_autorepeat)
|
||||
__set_bit(EV_REP, input->evbit);
|
||||
|
||||
/* go through board initialization helpers */
|
||||
if (keypad->board->init)
|
||||
keypad->board->init();
|
||||
|
||||
if (keypad->board->exit) {
|
||||
error = devm_add_action_or_reset(dev, ske_keypad_board_exit,
|
||||
keypad);
|
||||
if (error)
|
||||
return error;
|
||||
}
|
||||
|
||||
error = ske_keypad_chip_init(keypad);
|
||||
if (error) {
|
||||
dev_err(&pdev->dev, "unable to init keypad hardware\n");
|
||||
return error;
|
||||
}
|
||||
|
||||
error = devm_request_threaded_irq(dev, keypad->irq,
|
||||
NULL, ske_keypad_irq,
|
||||
IRQF_ONESHOT, "ske-keypad", keypad);
|
||||
if (error) {
|
||||
dev_err(&pdev->dev, "allocate irq %d failed\n", keypad->irq);
|
||||
return error;
|
||||
}
|
||||
|
||||
error = input_register_device(input);
|
||||
if (error) {
|
||||
dev_err(&pdev->dev,
|
||||
"unable to register input device: %d\n", error);
|
||||
return error;
|
||||
}
|
||||
|
||||
if (plat->wakeup_enable)
|
||||
device_init_wakeup(&pdev->dev, true);
|
||||
|
||||
platform_set_drvdata(pdev, keypad);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int ske_keypad_suspend(struct device *dev)
|
||||
{
|
||||
struct platform_device *pdev = to_platform_device(dev);
|
||||
struct ske_keypad *keypad = platform_get_drvdata(pdev);
|
||||
int irq = platform_get_irq(pdev, 0);
|
||||
|
||||
if (device_may_wakeup(dev))
|
||||
enable_irq_wake(irq);
|
||||
else
|
||||
ske_keypad_set_bits(keypad, SKE_IMSC, ~SKE_KPIMA, 0x0);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int ske_keypad_resume(struct device *dev)
|
||||
{
|
||||
struct platform_device *pdev = to_platform_device(dev);
|
||||
struct ske_keypad *keypad = platform_get_drvdata(pdev);
|
||||
int irq = platform_get_irq(pdev, 0);
|
||||
|
||||
if (device_may_wakeup(dev))
|
||||
disable_irq_wake(irq);
|
||||
else
|
||||
ske_keypad_set_bits(keypad, SKE_IMSC, 0x0, SKE_KPIMA);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static DEFINE_SIMPLE_DEV_PM_OPS(ske_keypad_dev_pm_ops,
|
||||
ske_keypad_suspend, ske_keypad_resume);
|
||||
|
||||
static struct platform_driver ske_keypad_driver = {
|
||||
.driver = {
|
||||
.name = "nmk-ske-keypad",
|
||||
.pm = pm_sleep_ptr(&ske_keypad_dev_pm_ops),
|
||||
},
|
||||
};
|
||||
|
||||
module_platform_driver_probe(ske_keypad_driver, ske_keypad_probe);
|
||||
|
||||
MODULE_LICENSE("GPL v2");
|
||||
MODULE_AUTHOR("Naveen Kumar <naveen.gaddipati@stericsson.com> / Sundar Iyer <sundar.iyer@stericsson.com>");
|
||||
MODULE_DESCRIPTION("Nomadik Scroll-Key-Encoder Keypad Driver");
|
||||
MODULE_ALIAS("platform:nomadik-ske-keypad");
|
@ -346,35 +346,34 @@ static int qt1050_apply_fw_data(struct qt1050_priv *ts)
|
||||
static int qt1050_parse_fw(struct qt1050_priv *ts)
|
||||
{
|
||||
struct device *dev = &ts->client->dev;
|
||||
struct fwnode_handle *child;
|
||||
int nbuttons;
|
||||
|
||||
nbuttons = device_get_child_node_count(dev);
|
||||
if (nbuttons == 0 || nbuttons > QT1050_MAX_KEYS)
|
||||
return -ENODEV;
|
||||
|
||||
device_for_each_child_node(dev, child) {
|
||||
device_for_each_child_node_scoped(dev, child) {
|
||||
struct qt1050_key button;
|
||||
|
||||
/* Required properties */
|
||||
if (fwnode_property_read_u32(child, "linux,code",
|
||||
&button.keycode)) {
|
||||
dev_err(dev, "Button without keycode\n");
|
||||
goto err;
|
||||
return -EINVAL;
|
||||
}
|
||||
if (button.keycode >= KEY_MAX) {
|
||||
dev_err(dev, "Invalid keycode 0x%x\n",
|
||||
button.keycode);
|
||||
goto err;
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
if (fwnode_property_read_u32(child, "reg",
|
||||
&button.num)) {
|
||||
dev_err(dev, "Button without pad number\n");
|
||||
goto err;
|
||||
return -EINVAL;
|
||||
}
|
||||
if (button.num < 0 || button.num > QT1050_MAX_KEYS - 1)
|
||||
goto err;
|
||||
return -EINVAL;
|
||||
|
||||
ts->reg_keys |= BIT(button.num);
|
||||
|
||||
@ -424,10 +423,6 @@ static int qt1050_parse_fw(struct qt1050_priv *ts)
|
||||
}
|
||||
|
||||
return 0;
|
||||
|
||||
err:
|
||||
fwnode_handle_put(child);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
static int qt1050_probe(struct i2c_client *client)
|
||||
|
@ -100,11 +100,6 @@ static irqreturn_t imx_snvs_pwrkey_interrupt(int irq, void *dev_id)
|
||||
return IRQ_HANDLED;
|
||||
}
|
||||
|
||||
static void imx_snvs_pwrkey_disable_clk(void *data)
|
||||
{
|
||||
clk_disable_unprepare(data);
|
||||
}
|
||||
|
||||
static void imx_snvs_pwrkey_act(void *pdata)
|
||||
{
|
||||
struct pwrkey_drv_data *pd = pdata;
|
||||
@ -141,28 +136,12 @@ static int imx_snvs_pwrkey_probe(struct platform_device *pdev)
|
||||
dev_warn(&pdev->dev, "KEY_POWER without setting in dts\n");
|
||||
}
|
||||
|
||||
clk = devm_clk_get_optional(&pdev->dev, NULL);
|
||||
clk = devm_clk_get_optional_enabled(&pdev->dev, NULL);
|
||||
if (IS_ERR(clk)) {
|
||||
dev_err(&pdev->dev, "Failed to get snvs clock (%pe)\n", clk);
|
||||
return PTR_ERR(clk);
|
||||
}
|
||||
|
||||
error = clk_prepare_enable(clk);
|
||||
if (error) {
|
||||
dev_err(&pdev->dev, "Failed to enable snvs clock (%pe)\n",
|
||||
ERR_PTR(error));
|
||||
return error;
|
||||
}
|
||||
|
||||
error = devm_add_action_or_reset(&pdev->dev,
|
||||
imx_snvs_pwrkey_disable_clk, clk);
|
||||
if (error) {
|
||||
dev_err(&pdev->dev,
|
||||
"Failed to register clock cleanup handler (%pe)\n",
|
||||
ERR_PTR(error));
|
||||
return error;
|
||||
}
|
||||
|
||||
pdata->wakeup = of_property_read_bool(np, "wakeup-source");
|
||||
|
||||
pdata->irq = platform_get_irq(pdev, 0);
|
||||
@ -204,7 +183,6 @@ static int imx_snvs_pwrkey_probe(struct platform_device *pdev)
|
||||
error = devm_request_irq(&pdev->dev, pdata->irq,
|
||||
imx_snvs_pwrkey_interrupt,
|
||||
0, pdev->name, pdev);
|
||||
|
||||
if (error) {
|
||||
dev_err(&pdev->dev, "interrupt not available.\n");
|
||||
return error;
|
||||
|
@ -222,7 +222,7 @@ static int spear_kbd_probe(struct platform_device *pdev)
|
||||
if (IS_ERR(kbd->io_base))
|
||||
return PTR_ERR(kbd->io_base);
|
||||
|
||||
kbd->clk = devm_clk_get(&pdev->dev, NULL);
|
||||
kbd->clk = devm_clk_get_prepared(&pdev->dev, NULL);
|
||||
if (IS_ERR(kbd->clk))
|
||||
return PTR_ERR(kbd->clk);
|
||||
|
||||
@ -255,14 +255,9 @@ static int spear_kbd_probe(struct platform_device *pdev)
|
||||
return error;
|
||||
}
|
||||
|
||||
error = clk_prepare(kbd->clk);
|
||||
if (error)
|
||||
return error;
|
||||
|
||||
error = input_register_device(input_dev);
|
||||
if (error) {
|
||||
dev_err(&pdev->dev, "Unable to register keyboard device\n");
|
||||
clk_unprepare(kbd->clk);
|
||||
return error;
|
||||
}
|
||||
|
||||
@ -272,14 +267,6 @@ static int spear_kbd_probe(struct platform_device *pdev)
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void spear_kbd_remove(struct platform_device *pdev)
|
||||
{
|
||||
struct spear_kbd *kbd = platform_get_drvdata(pdev);
|
||||
|
||||
input_unregister_device(kbd->input);
|
||||
clk_unprepare(kbd->clk);
|
||||
}
|
||||
|
||||
static int spear_kbd_suspend(struct device *dev)
|
||||
{
|
||||
struct platform_device *pdev = to_platform_device(dev);
|
||||
@ -373,7 +360,6 @@ MODULE_DEVICE_TABLE(of, spear_kbd_id_table);
|
||||
|
||||
static struct platform_driver spear_kbd_driver = {
|
||||
.probe = spear_kbd_probe,
|
||||
.remove_new = spear_kbd_remove,
|
||||
.driver = {
|
||||
.name = "keyboard",
|
||||
.pm = pm_sleep_ptr(&spear_kbd_pm_ops),
|
||||
|
@ -325,7 +325,6 @@ tc3589x_keypad_of_probe(struct device *dev)
|
||||
struct tc3589x_keypad_platform_data *plat;
|
||||
u32 cols, rows;
|
||||
u32 debounce_ms;
|
||||
int proplen;
|
||||
|
||||
if (!np)
|
||||
return ERR_PTR(-ENODEV);
|
||||
@ -346,7 +345,7 @@ tc3589x_keypad_of_probe(struct device *dev)
|
||||
return ERR_PTR(-EINVAL);
|
||||
}
|
||||
|
||||
if (!of_get_property(np, "linux,keymap", &proplen)) {
|
||||
if (!of_property_present(np, "linux,keymap")) {
|
||||
dev_err(dev, "property linux,keymap not found\n");
|
||||
return ERR_PTR(-ENOENT);
|
||||
}
|
||||
|
@ -241,11 +241,10 @@ static void tegra_kbc_set_fifo_interrupt(struct tegra_kbc *kbc, bool enable)
|
||||
static void tegra_kbc_keypress_timer(struct timer_list *t)
|
||||
{
|
||||
struct tegra_kbc *kbc = from_timer(kbc, t, timer);
|
||||
unsigned long flags;
|
||||
u32 val;
|
||||
unsigned int i;
|
||||
|
||||
spin_lock_irqsave(&kbc->lock, flags);
|
||||
guard(spinlock_irqsave)(&kbc->lock);
|
||||
|
||||
val = (readl(kbc->mmio + KBC_INT_0) >> 4) & 0xf;
|
||||
if (val) {
|
||||
@ -270,17 +269,14 @@ static void tegra_kbc_keypress_timer(struct timer_list *t)
|
||||
/* All keys are released so enable the keypress interrupt */
|
||||
tegra_kbc_set_fifo_interrupt(kbc, true);
|
||||
}
|
||||
|
||||
spin_unlock_irqrestore(&kbc->lock, flags);
|
||||
}
|
||||
|
||||
static irqreturn_t tegra_kbc_isr(int irq, void *args)
|
||||
{
|
||||
struct tegra_kbc *kbc = args;
|
||||
unsigned long flags;
|
||||
u32 val;
|
||||
|
||||
spin_lock_irqsave(&kbc->lock, flags);
|
||||
guard(spinlock_irqsave)(&kbc->lock);
|
||||
|
||||
/*
|
||||
* Quickly bail out & reenable interrupts if the fifo threshold
|
||||
@ -301,8 +297,6 @@ static irqreturn_t tegra_kbc_isr(int irq, void *args)
|
||||
kbc->keypress_caused_wake = true;
|
||||
}
|
||||
|
||||
spin_unlock_irqrestore(&kbc->lock, flags);
|
||||
|
||||
return IRQ_HANDLED;
|
||||
}
|
||||
|
||||
@ -413,14 +407,13 @@ static int tegra_kbc_start(struct tegra_kbc *kbc)
|
||||
|
||||
static void tegra_kbc_stop(struct tegra_kbc *kbc)
|
||||
{
|
||||
unsigned long flags;
|
||||
u32 val;
|
||||
|
||||
spin_lock_irqsave(&kbc->lock, flags);
|
||||
val = readl(kbc->mmio + KBC_CONTROL_0);
|
||||
val &= ~1;
|
||||
writel(val, kbc->mmio + KBC_CONTROL_0);
|
||||
spin_unlock_irqrestore(&kbc->lock, flags);
|
||||
scoped_guard(spinlock_irqsave, &kbc->lock) {
|
||||
val = readl(kbc->mmio + KBC_CONTROL_0);
|
||||
val &= ~1;
|
||||
writel(val, kbc->mmio + KBC_CONTROL_0);
|
||||
}
|
||||
|
||||
disable_irq(kbc->irq);
|
||||
del_timer_sync(&kbc->timer);
|
||||
@ -491,12 +484,10 @@ static int tegra_kbc_parse_dt(struct tegra_kbc *kbc)
|
||||
struct device_node *np = kbc->dev->of_node;
|
||||
u32 prop;
|
||||
int i;
|
||||
u32 num_rows = 0;
|
||||
u32 num_cols = 0;
|
||||
int num_rows;
|
||||
int num_cols;
|
||||
u32 cols_cfg[KBC_MAX_GPIO];
|
||||
u32 rows_cfg[KBC_MAX_GPIO];
|
||||
int proplen;
|
||||
int ret;
|
||||
|
||||
if (!of_property_read_u32(np, "nvidia,debounce-delay-ms", &prop))
|
||||
kbc->debounce_cnt = prop;
|
||||
@ -510,56 +501,23 @@ static int tegra_kbc_parse_dt(struct tegra_kbc *kbc)
|
||||
of_property_read_bool(np, "nvidia,wakeup-source")) /* legacy */
|
||||
kbc->wakeup = true;
|
||||
|
||||
if (!of_get_property(np, "nvidia,kbc-row-pins", &proplen)) {
|
||||
dev_err(kbc->dev, "property nvidia,kbc-row-pins not found\n");
|
||||
return -ENOENT;
|
||||
}
|
||||
num_rows = proplen / sizeof(u32);
|
||||
|
||||
if (!of_get_property(np, "nvidia,kbc-col-pins", &proplen)) {
|
||||
dev_err(kbc->dev, "property nvidia,kbc-col-pins not found\n");
|
||||
return -ENOENT;
|
||||
}
|
||||
num_cols = proplen / sizeof(u32);
|
||||
|
||||
if (num_rows > kbc->hw_support->max_rows) {
|
||||
dev_err(kbc->dev,
|
||||
"Number of rows is more than supported by hardware\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
if (num_cols > kbc->hw_support->max_columns) {
|
||||
dev_err(kbc->dev,
|
||||
"Number of cols is more than supported by hardware\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
if (!of_get_property(np, "linux,keymap", &proplen)) {
|
||||
if (!of_property_present(np, "linux,keymap")) {
|
||||
dev_err(kbc->dev, "property linux,keymap not found\n");
|
||||
return -ENOENT;
|
||||
}
|
||||
|
||||
if (!num_rows || !num_cols || ((num_rows + num_cols) > KBC_MAX_GPIO)) {
|
||||
dev_err(kbc->dev,
|
||||
"keypad rows/columns not properly specified\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
/* Set all pins as non-configured */
|
||||
for (i = 0; i < kbc->num_rows_and_columns; i++)
|
||||
kbc->pin_cfg[i].type = PIN_CFG_IGNORE;
|
||||
|
||||
ret = of_property_read_u32_array(np, "nvidia,kbc-row-pins",
|
||||
rows_cfg, num_rows);
|
||||
if (ret < 0) {
|
||||
num_rows = of_property_read_variable_u32_array(np, "nvidia,kbc-row-pins",
|
||||
rows_cfg, 1, KBC_MAX_GPIO);
|
||||
if (num_rows < 0) {
|
||||
dev_err(kbc->dev, "Rows configurations are not proper\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
ret = of_property_read_u32_array(np, "nvidia,kbc-col-pins",
|
||||
cols_cfg, num_cols);
|
||||
if (ret < 0) {
|
||||
dev_err(kbc->dev, "Cols configurations are not proper\n");
|
||||
return num_rows;
|
||||
} else if (num_rows > kbc->hw_support->max_rows) {
|
||||
dev_err(kbc->dev,
|
||||
"Number of rows is more than supported by hardware\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
@ -568,11 +526,28 @@ static int tegra_kbc_parse_dt(struct tegra_kbc *kbc)
|
||||
kbc->pin_cfg[rows_cfg[i]].num = i;
|
||||
}
|
||||
|
||||
num_cols = of_property_read_variable_u32_array(np, "nvidia,kbc-col-pins",
|
||||
cols_cfg, 1, KBC_MAX_GPIO);
|
||||
if (num_cols < 0) {
|
||||
dev_err(kbc->dev, "Cols configurations are not proper\n");
|
||||
return num_cols;
|
||||
} else if (num_cols > kbc->hw_support->max_columns) {
|
||||
dev_err(kbc->dev,
|
||||
"Number of cols is more than supported by hardware\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
for (i = 0; i < num_cols; i++) {
|
||||
kbc->pin_cfg[cols_cfg[i]].type = PIN_CFG_COL;
|
||||
kbc->pin_cfg[cols_cfg[i]].num = i;
|
||||
}
|
||||
|
||||
if (!num_rows || !num_cols || ((num_rows + num_cols) > KBC_MAX_GPIO)) {
|
||||
dev_err(kbc->dev,
|
||||
"keypad rows/columns not properly specified\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
@ -724,7 +699,8 @@ static int tegra_kbc_suspend(struct device *dev)
|
||||
struct platform_device *pdev = to_platform_device(dev);
|
||||
struct tegra_kbc *kbc = platform_get_drvdata(pdev);
|
||||
|
||||
mutex_lock(&kbc->idev->mutex);
|
||||
guard(mutex)(&kbc->idev->mutex);
|
||||
|
||||
if (device_may_wakeup(&pdev->dev)) {
|
||||
disable_irq(kbc->irq);
|
||||
del_timer_sync(&kbc->timer);
|
||||
@ -747,11 +723,9 @@ static int tegra_kbc_suspend(struct device *dev)
|
||||
tegra_kbc_set_keypress_interrupt(kbc, true);
|
||||
enable_irq(kbc->irq);
|
||||
enable_irq_wake(kbc->irq);
|
||||
} else {
|
||||
if (input_device_enabled(kbc->idev))
|
||||
tegra_kbc_stop(kbc);
|
||||
} else if (input_device_enabled(kbc->idev)) {
|
||||
tegra_kbc_stop(kbc);
|
||||
}
|
||||
mutex_unlock(&kbc->idev->mutex);
|
||||
|
||||
return 0;
|
||||
}
|
||||
@ -760,9 +734,10 @@ static int tegra_kbc_resume(struct device *dev)
|
||||
{
|
||||
struct platform_device *pdev = to_platform_device(dev);
|
||||
struct tegra_kbc *kbc = platform_get_drvdata(pdev);
|
||||
int err = 0;
|
||||
int err;
|
||||
|
||||
guard(mutex)(&kbc->idev->mutex);
|
||||
|
||||
mutex_lock(&kbc->idev->mutex);
|
||||
if (device_may_wakeup(&pdev->dev)) {
|
||||
disable_irq_wake(kbc->irq);
|
||||
tegra_kbc_setup_wakekeys(kbc, false);
|
||||
@ -787,13 +762,13 @@ static int tegra_kbc_resume(struct device *dev)
|
||||
input_report_key(kbc->idev, kbc->wakeup_key, 0);
|
||||
input_sync(kbc->idev);
|
||||
}
|
||||
} else {
|
||||
if (input_device_enabled(kbc->idev))
|
||||
err = tegra_kbc_start(kbc);
|
||||
} else if (input_device_enabled(kbc->idev)) {
|
||||
err = tegra_kbc_start(kbc);
|
||||
if (err)
|
||||
return err;
|
||||
}
|
||||
mutex_unlock(&kbc->idev->mutex);
|
||||
|
||||
return err;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static DEFINE_SIMPLE_DEV_PM_OPS(tegra_kbc_pm_ops,
|
||||
|
@ -73,10 +73,9 @@ static int matrix_keypad_parse_keymap(const char *propname,
|
||||
struct device *dev = input_dev->dev.parent;
|
||||
unsigned int row_shift = get_count_order(cols);
|
||||
unsigned int max_keys = rows << row_shift;
|
||||
u32 *keys;
|
||||
int i;
|
||||
int size;
|
||||
int retval;
|
||||
int error;
|
||||
|
||||
if (!propname)
|
||||
propname = "linux,keymap";
|
||||
@ -94,30 +93,24 @@ static int matrix_keypad_parse_keymap(const char *propname,
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
keys = kmalloc_array(size, sizeof(u32), GFP_KERNEL);
|
||||
u32 *keys __free(kfree) = kmalloc_array(size, sizeof(*keys), GFP_KERNEL);
|
||||
if (!keys)
|
||||
return -ENOMEM;
|
||||
|
||||
retval = device_property_read_u32_array(dev, propname, keys, size);
|
||||
if (retval) {
|
||||
error = device_property_read_u32_array(dev, propname, keys, size);
|
||||
if (error) {
|
||||
dev_err(dev, "failed to read %s property: %d\n",
|
||||
propname, retval);
|
||||
goto out;
|
||||
propname, error);
|
||||
return error;
|
||||
}
|
||||
|
||||
for (i = 0; i < size; i++) {
|
||||
if (!matrix_keypad_map_key(input_dev, rows, cols,
|
||||
row_shift, keys[i])) {
|
||||
retval = -EINVAL;
|
||||
goto out;
|
||||
}
|
||||
row_shift, keys[i]))
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
retval = 0;
|
||||
|
||||
out:
|
||||
kfree(keys);
|
||||
return retval;
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -1067,7 +1067,7 @@ static ssize_t ims_pcu_attribute_store(struct device *dev,
|
||||
if (data_len > attr->field_length)
|
||||
return -EINVAL;
|
||||
|
||||
scoped_cond_guard(mutex, return -EINTR, &pcu->cmd_mutex) {
|
||||
scoped_cond_guard(mutex_intr, return -EINTR, &pcu->cmd_mutex) {
|
||||
memset(field, 0, attr->field_length);
|
||||
memcpy(field, buf, data_len);
|
||||
|
||||
|
@ -811,7 +811,6 @@ static int iqs269_parse_prop(struct iqs269_private *iqs269)
|
||||
{
|
||||
struct iqs269_sys_reg *sys_reg = &iqs269->sys_reg;
|
||||
struct i2c_client *client = iqs269->client;
|
||||
struct fwnode_handle *ch_node;
|
||||
u16 general, misc_a, misc_b;
|
||||
unsigned int val;
|
||||
int error;
|
||||
@ -1049,12 +1048,10 @@ static int iqs269_parse_prop(struct iqs269_private *iqs269)
|
||||
|
||||
sys_reg->event_mask = ~((u8)IQS269_EVENT_MASK_SYS);
|
||||
|
||||
device_for_each_child_node(&client->dev, ch_node) {
|
||||
device_for_each_child_node_scoped(&client->dev, ch_node) {
|
||||
error = iqs269_parse_chan(iqs269, ch_node);
|
||||
if (error) {
|
||||
fwnode_handle_put(ch_node);
|
||||
if (error)
|
||||
return error;
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
|
@ -38,6 +38,7 @@ struct bbnsm_pwrkey {
|
||||
int irq;
|
||||
int keycode;
|
||||
int keystate; /* 1:pressed */
|
||||
bool suspended;
|
||||
struct timer_list check_timer;
|
||||
struct input_dev *input;
|
||||
};
|
||||
@ -70,6 +71,7 @@ static irqreturn_t bbnsm_pwrkey_interrupt(int irq, void *dev_id)
|
||||
{
|
||||
struct platform_device *pdev = dev_id;
|
||||
struct bbnsm_pwrkey *bbnsm = platform_get_drvdata(pdev);
|
||||
struct input_dev *input = bbnsm->input;
|
||||
u32 event;
|
||||
|
||||
regmap_read(bbnsm->regmap, BBNSM_EVENTS, &event);
|
||||
@ -78,6 +80,18 @@ static irqreturn_t bbnsm_pwrkey_interrupt(int irq, void *dev_id)
|
||||
|
||||
pm_wakeup_event(bbnsm->input->dev.parent, 0);
|
||||
|
||||
/*
|
||||
* Directly report key event after resume to make sure key press
|
||||
* event is never missed.
|
||||
*/
|
||||
if (bbnsm->suspended) {
|
||||
bbnsm->keystate = 1;
|
||||
input_event(input, EV_KEY, bbnsm->keycode, 1);
|
||||
input_sync(input);
|
||||
/* Fire at most once per suspend/resume cycle */
|
||||
bbnsm->suspended = false;
|
||||
}
|
||||
|
||||
mod_timer(&bbnsm->check_timer,
|
||||
jiffies + msecs_to_jiffies(DEBOUNCE_TIME));
|
||||
|
||||
@ -173,6 +187,29 @@ static int bbnsm_pwrkey_probe(struct platform_device *pdev)
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int __maybe_unused bbnsm_pwrkey_suspend(struct device *dev)
|
||||
{
|
||||
struct platform_device *pdev = to_platform_device(dev);
|
||||
struct bbnsm_pwrkey *bbnsm = platform_get_drvdata(pdev);
|
||||
|
||||
bbnsm->suspended = true;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int __maybe_unused bbnsm_pwrkey_resume(struct device *dev)
|
||||
{
|
||||
struct platform_device *pdev = to_platform_device(dev);
|
||||
struct bbnsm_pwrkey *bbnsm = platform_get_drvdata(pdev);
|
||||
|
||||
bbnsm->suspended = false;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static SIMPLE_DEV_PM_OPS(bbnsm_pwrkey_pm_ops, bbnsm_pwrkey_suspend,
|
||||
bbnsm_pwrkey_resume);
|
||||
|
||||
static const struct of_device_id bbnsm_pwrkey_ids[] = {
|
||||
{ .compatible = "nxp,imx93-bbnsm-pwrkey" },
|
||||
{ /* sentinel */ }
|
||||
@ -182,6 +219,7 @@ MODULE_DEVICE_TABLE(of, bbnsm_pwrkey_ids);
|
||||
static struct platform_driver bbnsm_pwrkey_driver = {
|
||||
.driver = {
|
||||
.name = "bbnsm_pwrkey",
|
||||
.pm = &bbnsm_pwrkey_pm_ops,
|
||||
.of_match_table = bbnsm_pwrkey_ids,
|
||||
},
|
||||
.probe = bbnsm_pwrkey_probe,
|
||||
|
@ -990,8 +990,8 @@ static int __init copy_keymap(void)
|
||||
for (key = keymap; key->type != KE_END; key++)
|
||||
length++;
|
||||
|
||||
new_keymap = kmemdup(keymap, length * sizeof(struct key_entry),
|
||||
GFP_KERNEL);
|
||||
new_keymap = kmemdup_array(keymap, length, sizeof(struct key_entry),
|
||||
GFP_KERNEL);
|
||||
if (!new_keymap)
|
||||
return -ENOMEM;
|
||||
|
||||
|
@ -69,6 +69,18 @@ config MOUSE_PS2_LOGIPS2PP
|
||||
|
||||
If unsure, say Y.
|
||||
|
||||
config MOUSE_PS2_PIXART
|
||||
bool "PixArt PS/2 touchpad protocol extension" if EXPERT
|
||||
default y
|
||||
depends on MOUSE_PS2
|
||||
help
|
||||
This driver supports the PixArt PS/2 touchpad found in some
|
||||
laptops.
|
||||
Say Y here if you have a PixArt PS/2 TouchPad connected to
|
||||
your system.
|
||||
|
||||
If unsure, say Y.
|
||||
|
||||
config MOUSE_PS2_SYNAPTICS
|
||||
bool "Synaptics PS/2 mouse protocol extension" if EXPERT
|
||||
default y
|
||||
|
@ -32,6 +32,7 @@ psmouse-$(CONFIG_MOUSE_PS2_ELANTECH) += elantech.o
|
||||
psmouse-$(CONFIG_MOUSE_PS2_OLPC) += hgpk.o
|
||||
psmouse-$(CONFIG_MOUSE_PS2_LOGIPS2PP) += logips2pp.o
|
||||
psmouse-$(CONFIG_MOUSE_PS2_LIFEBOOK) += lifebook.o
|
||||
psmouse-$(CONFIG_MOUSE_PS2_PIXART) += pixart_ps2.o
|
||||
psmouse-$(CONFIG_MOUSE_PS2_SENTELIC) += sentelic.o
|
||||
psmouse-$(CONFIG_MOUSE_PS2_TRACKPOINT) += trackpoint.o
|
||||
psmouse-$(CONFIG_MOUSE_PS2_TOUCHKIT) += touchkit_ps2.o
|
||||
|
@ -1396,24 +1396,16 @@ static bool alps_is_valid_package_ss4_v2(struct psmouse *psmouse)
|
||||
|
||||
static DEFINE_MUTEX(alps_mutex);
|
||||
|
||||
static void alps_register_bare_ps2_mouse(struct work_struct *work)
|
||||
static int alps_do_register_bare_ps2_mouse(struct alps_data *priv)
|
||||
{
|
||||
struct alps_data *priv =
|
||||
container_of(work, struct alps_data, dev3_register_work.work);
|
||||
struct psmouse *psmouse = priv->psmouse;
|
||||
struct input_dev *dev3;
|
||||
int error = 0;
|
||||
|
||||
mutex_lock(&alps_mutex);
|
||||
|
||||
if (priv->dev3)
|
||||
goto out;
|
||||
int error;
|
||||
|
||||
dev3 = input_allocate_device();
|
||||
if (!dev3) {
|
||||
psmouse_err(psmouse, "failed to allocate secondary device\n");
|
||||
error = -ENOMEM;
|
||||
goto out;
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
snprintf(priv->phys3, sizeof(priv->phys3), "%s/%s",
|
||||
@ -1446,21 +1438,35 @@ static void alps_register_bare_ps2_mouse(struct work_struct *work)
|
||||
psmouse_err(psmouse,
|
||||
"failed to register secondary device: %d\n",
|
||||
error);
|
||||
input_free_device(dev3);
|
||||
goto out;
|
||||
goto err_free_input;
|
||||
}
|
||||
|
||||
priv->dev3 = dev3;
|
||||
return 0;
|
||||
|
||||
out:
|
||||
/*
|
||||
* Save the error code so that we can detect that we
|
||||
* already tried to create the device.
|
||||
*/
|
||||
if (error)
|
||||
priv->dev3 = ERR_PTR(error);
|
||||
err_free_input:
|
||||
input_free_device(dev3);
|
||||
return error;
|
||||
}
|
||||
|
||||
mutex_unlock(&alps_mutex);
|
||||
static void alps_register_bare_ps2_mouse(struct work_struct *work)
|
||||
{
|
||||
struct alps_data *priv = container_of(work, struct alps_data,
|
||||
dev3_register_work.work);
|
||||
int error;
|
||||
|
||||
guard(mutex)(&alps_mutex);
|
||||
|
||||
if (!priv->dev3) {
|
||||
error = alps_do_register_bare_ps2_mouse(priv);
|
||||
if (error) {
|
||||
/*
|
||||
* Save the error code so that we can detect that we
|
||||
* already tried to create the device.
|
||||
*/
|
||||
priv->dev3 = ERR_PTR(error);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void alps_report_bare_ps2_packet(struct psmouse *psmouse,
|
||||
|
@ -834,13 +834,11 @@ static int bcm5974_open(struct input_dev *input)
|
||||
if (error)
|
||||
return error;
|
||||
|
||||
mutex_lock(&dev->pm_mutex);
|
||||
|
||||
error = bcm5974_start_traffic(dev);
|
||||
if (!error)
|
||||
dev->opened = 1;
|
||||
|
||||
mutex_unlock(&dev->pm_mutex);
|
||||
scoped_guard(mutex, &dev->pm_mutex) {
|
||||
error = bcm5974_start_traffic(dev);
|
||||
if (!error)
|
||||
dev->opened = 1;
|
||||
}
|
||||
|
||||
if (error)
|
||||
usb_autopm_put_interface(dev->intf);
|
||||
@ -852,12 +850,10 @@ static void bcm5974_close(struct input_dev *input)
|
||||
{
|
||||
struct bcm5974 *dev = input_get_drvdata(input);
|
||||
|
||||
mutex_lock(&dev->pm_mutex);
|
||||
|
||||
bcm5974_pause_traffic(dev);
|
||||
dev->opened = 0;
|
||||
|
||||
mutex_unlock(&dev->pm_mutex);
|
||||
scoped_guard(mutex, &dev->pm_mutex) {
|
||||
bcm5974_pause_traffic(dev);
|
||||
dev->opened = 0;
|
||||
}
|
||||
|
||||
usb_autopm_put_interface(dev->intf);
|
||||
}
|
||||
@ -866,29 +862,24 @@ static int bcm5974_suspend(struct usb_interface *iface, pm_message_t message)
|
||||
{
|
||||
struct bcm5974 *dev = usb_get_intfdata(iface);
|
||||
|
||||
mutex_lock(&dev->pm_mutex);
|
||||
guard(mutex)(&dev->pm_mutex);
|
||||
|
||||
if (dev->opened)
|
||||
bcm5974_pause_traffic(dev);
|
||||
|
||||
mutex_unlock(&dev->pm_mutex);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int bcm5974_resume(struct usb_interface *iface)
|
||||
{
|
||||
struct bcm5974 *dev = usb_get_intfdata(iface);
|
||||
int error = 0;
|
||||
|
||||
mutex_lock(&dev->pm_mutex);
|
||||
guard(mutex)(&dev->pm_mutex);
|
||||
|
||||
if (dev->opened)
|
||||
error = bcm5974_start_traffic(dev);
|
||||
return bcm5974_start_traffic(dev);
|
||||
|
||||
mutex_unlock(&dev->pm_mutex);
|
||||
|
||||
return error;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int bcm5974_probe(struct usb_interface *iface,
|
||||
|
300
drivers/input/mouse/pixart_ps2.c
Normal file
300
drivers/input/mouse/pixart_ps2.c
Normal file
@ -0,0 +1,300 @@
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
/*
|
||||
* Pixart Touchpad Controller 1336U PS2 driver
|
||||
*
|
||||
* Author: Jon Xie <jon_xie@pixart.com>
|
||||
* Jay Lee <jay_lee@pixart.com>
|
||||
* Further cleanup and restructuring by:
|
||||
* Binbin Zhou <zhoubinbin@loongson.cn>
|
||||
*
|
||||
* Copyright (C) 2021-2024 Pixart Imaging.
|
||||
* Copyright (C) 2024 Loongson Technology Corporation Limited.
|
||||
*
|
||||
*/
|
||||
|
||||
#include <linux/bitfield.h>
|
||||
#include <linux/delay.h>
|
||||
#include <linux/device.h>
|
||||
#include <linux/input.h>
|
||||
#include <linux/input/mt.h>
|
||||
#include <linux/libps2.h>
|
||||
#include <linux/serio.h>
|
||||
#include <linux/slab.h>
|
||||
|
||||
#include "pixart_ps2.h"
|
||||
|
||||
static int pixart_read_tp_mode(struct ps2dev *ps2dev, u8 *mode)
|
||||
{
|
||||
int error;
|
||||
u8 param[1] = { 0 };
|
||||
|
||||
error = ps2_command(ps2dev, param, PIXART_CMD_REPORT_FORMAT);
|
||||
if (error)
|
||||
return error;
|
||||
|
||||
*mode = param[0] == 1 ? PIXART_MODE_ABS : PIXART_MODE_REL;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int pixart_read_tp_type(struct ps2dev *ps2dev, u8 *type)
|
||||
{
|
||||
int error;
|
||||
u8 param[3] = { 0 };
|
||||
|
||||
param[0] = 0x0a;
|
||||
error = ps2_command(ps2dev, param, PSMOUSE_CMD_SETRATE);
|
||||
if (error)
|
||||
return error;
|
||||
|
||||
param[0] = 0x0;
|
||||
error = ps2_command(ps2dev, param, PSMOUSE_CMD_SETRES);
|
||||
if (error)
|
||||
return error;
|
||||
|
||||
error = ps2_command(ps2dev, param, PSMOUSE_CMD_SETRES);
|
||||
if (error)
|
||||
return error;
|
||||
|
||||
error = ps2_command(ps2dev, param, PSMOUSE_CMD_SETRES);
|
||||
if (error)
|
||||
return error;
|
||||
|
||||
param[0] = 0x03;
|
||||
error = ps2_command(ps2dev, param, PSMOUSE_CMD_SETRES);
|
||||
if (error)
|
||||
return error;
|
||||
|
||||
error = ps2_command(ps2dev, param, PSMOUSE_CMD_GETINFO);
|
||||
if (error)
|
||||
return error;
|
||||
|
||||
*type = param[0] == 0x0e ? PIXART_TYPE_TOUCHPAD : PIXART_TYPE_CLICKPAD;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void pixart_reset(struct psmouse *psmouse)
|
||||
{
|
||||
ps2_command(&psmouse->ps2dev, NULL, PSMOUSE_CMD_RESET_DIS);
|
||||
|
||||
/* according to PixArt, 100ms is required for the upcoming reset */
|
||||
msleep(100);
|
||||
psmouse_reset(psmouse);
|
||||
}
|
||||
|
||||
static void pixart_process_packet(struct psmouse *psmouse)
|
||||
{
|
||||
struct pixart_data *priv = psmouse->private;
|
||||
struct input_dev *dev = psmouse->dev;
|
||||
const u8 *pkt = psmouse->packet;
|
||||
unsigned int contact_cnt = FIELD_GET(CONTACT_CNT_MASK, pkt[0]);
|
||||
unsigned int i, id, abs_x, abs_y;
|
||||
bool tip;
|
||||
|
||||
for (i = 0; i < contact_cnt; i++) {
|
||||
const u8 *p = &pkt[i * 3];
|
||||
|
||||
id = FIELD_GET(SLOT_ID_MASK, p[3]);
|
||||
abs_y = FIELD_GET(ABS_Y_MASK, p[3]) << 8 | p[1];
|
||||
abs_x = FIELD_GET(ABS_X_MASK, p[3]) << 8 | p[2];
|
||||
|
||||
if (i == PIXART_MAX_FINGERS - 1)
|
||||
tip = pkt[14] & BIT(1);
|
||||
else
|
||||
tip = pkt[3 * contact_cnt + 1] & BIT(2 * i + 1);
|
||||
|
||||
input_mt_slot(dev, id);
|
||||
if (input_mt_report_slot_state(dev, MT_TOOL_FINGER, tip)) {
|
||||
input_report_abs(dev, ABS_MT_POSITION_Y, abs_y);
|
||||
input_report_abs(dev, ABS_MT_POSITION_X, abs_x);
|
||||
}
|
||||
}
|
||||
|
||||
input_mt_sync_frame(dev);
|
||||
|
||||
if (priv->type == PIXART_TYPE_CLICKPAD) {
|
||||
input_report_key(dev, BTN_LEFT, pkt[0] & 0x03);
|
||||
} else {
|
||||
input_report_key(dev, BTN_LEFT, pkt[0] & BIT(0));
|
||||
input_report_key(dev, BTN_RIGHT, pkt[0] & BIT(1));
|
||||
}
|
||||
|
||||
input_sync(dev);
|
||||
}
|
||||
|
||||
static psmouse_ret_t pixart_protocol_handler(struct psmouse *psmouse)
|
||||
{
|
||||
u8 *pkt = psmouse->packet;
|
||||
u8 contact_cnt;
|
||||
|
||||
if ((pkt[0] & 0x8c) != 0x80)
|
||||
return PSMOUSE_BAD_DATA;
|
||||
|
||||
contact_cnt = FIELD_GET(CONTACT_CNT_MASK, pkt[0]);
|
||||
if (contact_cnt > PIXART_MAX_FINGERS)
|
||||
return PSMOUSE_BAD_DATA;
|
||||
|
||||
if (contact_cnt == PIXART_MAX_FINGERS &&
|
||||
psmouse->pktcnt < psmouse->pktsize) {
|
||||
return PSMOUSE_GOOD_DATA;
|
||||
}
|
||||
|
||||
if (contact_cnt == 0 && psmouse->pktcnt < 5)
|
||||
return PSMOUSE_GOOD_DATA;
|
||||
|
||||
if (psmouse->pktcnt < 3 * contact_cnt + 2)
|
||||
return PSMOUSE_GOOD_DATA;
|
||||
|
||||
pixart_process_packet(psmouse);
|
||||
|
||||
return PSMOUSE_FULL_PACKET;
|
||||
}
|
||||
|
||||
static void pixart_disconnect(struct psmouse *psmouse)
|
||||
{
|
||||
pixart_reset(psmouse);
|
||||
kfree(psmouse->private);
|
||||
psmouse->private = NULL;
|
||||
}
|
||||
|
||||
static int pixart_reconnect(struct psmouse *psmouse)
|
||||
{
|
||||
struct ps2dev *ps2dev = &psmouse->ps2dev;
|
||||
u8 mode;
|
||||
int error;
|
||||
|
||||
pixart_reset(psmouse);
|
||||
|
||||
error = pixart_read_tp_mode(ps2dev, &mode);
|
||||
if (error)
|
||||
return error;
|
||||
|
||||
if (mode != PIXART_MODE_ABS)
|
||||
return -EIO;
|
||||
|
||||
error = ps2_command(ps2dev, NULL, PIXART_CMD_SWITCH_PROTO);
|
||||
if (error)
|
||||
return error;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int pixart_set_input_params(struct input_dev *dev,
|
||||
struct pixart_data *priv)
|
||||
{
|
||||
/* No relative support */
|
||||
__clear_bit(EV_REL, dev->evbit);
|
||||
__clear_bit(REL_X, dev->relbit);
|
||||
__clear_bit(REL_Y, dev->relbit);
|
||||
__clear_bit(BTN_MIDDLE, dev->keybit);
|
||||
|
||||
/* Buttons */
|
||||
__set_bit(EV_KEY, dev->evbit);
|
||||
__set_bit(BTN_LEFT, dev->keybit);
|
||||
if (priv->type == PIXART_TYPE_CLICKPAD)
|
||||
__set_bit(INPUT_PROP_BUTTONPAD, dev->propbit);
|
||||
else
|
||||
__set_bit(BTN_RIGHT, dev->keybit);
|
||||
|
||||
/* Absolute position */
|
||||
input_set_abs_params(dev, ABS_X, 0, PIXART_PAD_WIDTH, 0, 0);
|
||||
input_set_abs_params(dev, ABS_Y, 0, PIXART_PAD_HEIGHT, 0, 0);
|
||||
|
||||
input_set_abs_params(dev, ABS_MT_POSITION_X,
|
||||
0, PIXART_PAD_WIDTH, 0, 0);
|
||||
input_set_abs_params(dev, ABS_MT_POSITION_Y,
|
||||
0, PIXART_PAD_HEIGHT, 0, 0);
|
||||
|
||||
return input_mt_init_slots(dev, PIXART_MAX_FINGERS, INPUT_MT_POINTER);
|
||||
}
|
||||
|
||||
static int pixart_query_hardware(struct ps2dev *ps2dev, u8 *mode, u8 *type)
|
||||
{
|
||||
int error;
|
||||
|
||||
error = pixart_read_tp_type(ps2dev, type);
|
||||
if (error)
|
||||
return error;
|
||||
|
||||
error = pixart_read_tp_mode(ps2dev, mode);
|
||||
if (error)
|
||||
return error;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int pixart_detect(struct psmouse *psmouse, bool set_properties)
|
||||
{
|
||||
u8 type;
|
||||
int error;
|
||||
|
||||
pixart_reset(psmouse);
|
||||
|
||||
error = pixart_read_tp_type(&psmouse->ps2dev, &type);
|
||||
if (error)
|
||||
return error;
|
||||
|
||||
if (set_properties) {
|
||||
psmouse->vendor = "PixArt";
|
||||
psmouse->name = (type == PIXART_TYPE_TOUCHPAD) ?
|
||||
"touchpad" : "clickpad";
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int pixart_init(struct psmouse *psmouse)
|
||||
{
|
||||
int error;
|
||||
struct pixart_data *priv;
|
||||
|
||||
priv = kzalloc(sizeof(*priv), GFP_KERNEL);
|
||||
if (!priv)
|
||||
return -ENOMEM;
|
||||
|
||||
psmouse->private = priv;
|
||||
pixart_reset(psmouse);
|
||||
|
||||
error = pixart_query_hardware(&psmouse->ps2dev,
|
||||
&priv->mode, &priv->type);
|
||||
if (error) {
|
||||
psmouse_err(psmouse, "init: Unable to query PixArt touchpad hardware.\n");
|
||||
goto err_exit;
|
||||
}
|
||||
|
||||
/* Relative mode follows standard PS/2 mouse protocol */
|
||||
if (priv->mode != PIXART_MODE_ABS) {
|
||||
error = -EIO;
|
||||
goto err_exit;
|
||||
}
|
||||
|
||||
/* Set absolute mode */
|
||||
error = ps2_command(&psmouse->ps2dev, NULL, PIXART_CMD_SWITCH_PROTO);
|
||||
if (error) {
|
||||
psmouse_err(psmouse, "init: Unable to initialize PixArt absolute mode.\n");
|
||||
goto err_exit;
|
||||
}
|
||||
|
||||
error = pixart_set_input_params(psmouse->dev, priv);
|
||||
if (error) {
|
||||
psmouse_err(psmouse, "init: Unable to set input params.\n");
|
||||
goto err_exit;
|
||||
}
|
||||
|
||||
psmouse->pktsize = 15;
|
||||
psmouse->protocol_handler = pixart_protocol_handler;
|
||||
psmouse->disconnect = pixart_disconnect;
|
||||
psmouse->reconnect = pixart_reconnect;
|
||||
psmouse->cleanup = pixart_reset;
|
||||
/* resync is not supported yet */
|
||||
psmouse->resync_time = 0;
|
||||
|
||||
return 0;
|
||||
|
||||
err_exit:
|
||||
pixart_reset(psmouse);
|
||||
kfree(priv);
|
||||
psmouse->private = NULL;
|
||||
return error;
|
||||
}
|
36
drivers/input/mouse/pixart_ps2.h
Normal file
36
drivers/input/mouse/pixart_ps2.h
Normal file
@ -0,0 +1,36 @@
|
||||
/* SPDX-License-Identifier: GPL-2.0-or-later */
|
||||
#ifndef _PIXART_PS2_H
|
||||
#define _PIXART_PS2_H
|
||||
|
||||
#include "psmouse.h"
|
||||
|
||||
#define PIXART_PAD_WIDTH 1023
|
||||
#define PIXART_PAD_HEIGHT 579
|
||||
#define PIXART_MAX_FINGERS 4
|
||||
|
||||
#define PIXART_CMD_REPORT_FORMAT 0x01d8
|
||||
#define PIXART_CMD_SWITCH_PROTO 0x00de
|
||||
|
||||
#define PIXART_MODE_REL 0
|
||||
#define PIXART_MODE_ABS 1
|
||||
|
||||
#define PIXART_TYPE_CLICKPAD 0
|
||||
#define PIXART_TYPE_TOUCHPAD 1
|
||||
|
||||
#define CONTACT_CNT_MASK GENMASK(6, 4)
|
||||
|
||||
#define SLOT_ID_MASK GENMASK(2, 0)
|
||||
#define ABS_Y_MASK GENMASK(5, 4)
|
||||
#define ABS_X_MASK GENMASK(7, 6)
|
||||
|
||||
struct pixart_data {
|
||||
u8 mode;
|
||||
u8 type;
|
||||
int x_max;
|
||||
int y_max;
|
||||
};
|
||||
|
||||
int pixart_detect(struct psmouse *psmouse, bool set_properties);
|
||||
int pixart_init(struct psmouse *psmouse);
|
||||
|
||||
#endif /* _PIXART_PS2_H */
|
@ -36,6 +36,7 @@
|
||||
#include "focaltech.h"
|
||||
#include "vmmouse.h"
|
||||
#include "byd.h"
|
||||
#include "pixart_ps2.h"
|
||||
|
||||
#define DRIVER_DESC "PS/2 mouse driver"
|
||||
|
||||
@ -905,6 +906,15 @@ static const struct psmouse_protocol psmouse_protocols[] = {
|
||||
.detect = byd_detect,
|
||||
.init = byd_init,
|
||||
},
|
||||
#endif
|
||||
#ifdef CONFIG_MOUSE_PS2_PIXART
|
||||
{
|
||||
.type = PSMOUSE_PIXART,
|
||||
.name = "PixArtPS/2",
|
||||
.alias = "pixart",
|
||||
.detect = pixart_detect,
|
||||
.init = pixart_init,
|
||||
},
|
||||
#endif
|
||||
{
|
||||
.type = PSMOUSE_AUTO,
|
||||
@ -1172,6 +1182,13 @@ static int psmouse_extensions(struct psmouse *psmouse,
|
||||
return ret;
|
||||
}
|
||||
|
||||
/* Try PixArt touchpad */
|
||||
if (max_proto > PSMOUSE_IMEX &&
|
||||
psmouse_try_protocol(psmouse, PSMOUSE_PIXART, &max_proto,
|
||||
set_properties, true)) {
|
||||
return PSMOUSE_PIXART;
|
||||
}
|
||||
|
||||
if (max_proto > PSMOUSE_IMEX) {
|
||||
if (psmouse_try_protocol(psmouse, PSMOUSE_GENPS,
|
||||
&max_proto, set_properties, true))
|
||||
|
@ -69,6 +69,7 @@ enum psmouse_type {
|
||||
PSMOUSE_BYD,
|
||||
PSMOUSE_SYNAPTICS_SMBUS,
|
||||
PSMOUSE_ELANTECH_SMBUS,
|
||||
PSMOUSE_PIXART,
|
||||
PSMOUSE_AUTO /* This one should always be last */
|
||||
};
|
||||
|
||||
@ -94,7 +95,7 @@ struct psmouse {
|
||||
const char *vendor;
|
||||
const char *name;
|
||||
const struct psmouse_protocol *protocol;
|
||||
unsigned char packet[8];
|
||||
unsigned char packet[16];
|
||||
unsigned char badbyte;
|
||||
unsigned char pktcnt;
|
||||
unsigned char pktsize;
|
||||
|
@ -24,6 +24,7 @@ enum rmi_f12_object_type {
|
||||
};
|
||||
|
||||
#define F12_DATA1_BYTES_PER_OBJ 8
|
||||
#define RMI_F12_QUERY_RESOLUTION 29
|
||||
|
||||
struct f12_data {
|
||||
struct rmi_2d_sensor sensor;
|
||||
@ -73,6 +74,8 @@ static int rmi_f12_read_sensor_tuning(struct f12_data *f12)
|
||||
int pitch_y = 0;
|
||||
int rx_receivers = 0;
|
||||
int tx_receivers = 0;
|
||||
u16 query_dpm_addr = 0;
|
||||
int dpm_resolution = 0;
|
||||
|
||||
item = rmi_get_register_desc_item(&f12->control_reg_desc, 8);
|
||||
if (!item) {
|
||||
@ -122,19 +125,39 @@ static int rmi_f12_read_sensor_tuning(struct f12_data *f12)
|
||||
offset += 4;
|
||||
}
|
||||
|
||||
if (rmi_register_desc_has_subpacket(item, 3)) {
|
||||
rx_receivers = buf[offset];
|
||||
tx_receivers = buf[offset + 1];
|
||||
offset += 2;
|
||||
/*
|
||||
* Use the Query DPM feature when the resolution query register
|
||||
* exists.
|
||||
*/
|
||||
if (rmi_get_register_desc_item(&f12->query_reg_desc,
|
||||
RMI_F12_QUERY_RESOLUTION)) {
|
||||
offset = rmi_register_desc_calc_reg_offset(&f12->query_reg_desc,
|
||||
RMI_F12_QUERY_RESOLUTION);
|
||||
query_dpm_addr = fn->fd.query_base_addr + offset;
|
||||
ret = rmi_read(fn->rmi_dev, query_dpm_addr, buf);
|
||||
if (ret < 0) {
|
||||
dev_err(&fn->dev, "Failed to read DPM value: %d\n", ret);
|
||||
return -ENODEV;
|
||||
}
|
||||
dpm_resolution = buf[0];
|
||||
|
||||
sensor->x_mm = sensor->max_x / dpm_resolution;
|
||||
sensor->y_mm = sensor->max_y / dpm_resolution;
|
||||
} else {
|
||||
if (rmi_register_desc_has_subpacket(item, 3)) {
|
||||
rx_receivers = buf[offset];
|
||||
tx_receivers = buf[offset + 1];
|
||||
offset += 2;
|
||||
}
|
||||
|
||||
/* Skip over sensor flags */
|
||||
if (rmi_register_desc_has_subpacket(item, 4))
|
||||
offset += 1;
|
||||
|
||||
sensor->x_mm = (pitch_x * rx_receivers) >> 12;
|
||||
sensor->y_mm = (pitch_y * tx_receivers) >> 12;
|
||||
}
|
||||
|
||||
/* Skip over sensor flags */
|
||||
if (rmi_register_desc_has_subpacket(item, 4))
|
||||
offset += 1;
|
||||
|
||||
sensor->x_mm = (pitch_x * rx_receivers) >> 12;
|
||||
sensor->y_mm = (pitch_y * tx_receivers) >> 12;
|
||||
|
||||
rmi_dbg(RMI_DEBUG_FN, &fn->dev, "%s: x_mm: %d y_mm: %d\n", __func__,
|
||||
sensor->x_mm, sensor->y_mm);
|
||||
|
||||
|
@ -1120,6 +1120,43 @@ static const struct dmi_system_id i8042_dmi_quirk_table[] __initconst = {
|
||||
},
|
||||
.driver_data = (void *)(SERIO_QUIRK_NOLOOP)
|
||||
},
|
||||
/*
|
||||
* Some TongFang barebones have touchpad and/or keyboard issues after
|
||||
* suspend fixable with nomux + reset + noloop + nopnp. Luckily, none of
|
||||
* them have an external PS/2 port so this can safely be set for all of
|
||||
* them.
|
||||
* TongFang barebones come with board_vendor and/or system_vendor set to
|
||||
* a different value for each individual reseller. The only somewhat
|
||||
* universal way to identify them is by board_name.
|
||||
*/
|
||||
{
|
||||
.matches = {
|
||||
DMI_MATCH(DMI_BOARD_NAME, "GM6XGxX"),
|
||||
},
|
||||
.driver_data = (void *)(SERIO_QUIRK_NOMUX | SERIO_QUIRK_RESET_ALWAYS |
|
||||
SERIO_QUIRK_NOLOOP | SERIO_QUIRK_NOPNP)
|
||||
},
|
||||
{
|
||||
.matches = {
|
||||
DMI_MATCH(DMI_BOARD_NAME, "GMxXGxx"),
|
||||
},
|
||||
.driver_data = (void *)(SERIO_QUIRK_NOMUX | SERIO_QUIRK_RESET_ALWAYS |
|
||||
SERIO_QUIRK_NOLOOP | SERIO_QUIRK_NOPNP)
|
||||
},
|
||||
{
|
||||
.matches = {
|
||||
DMI_MATCH(DMI_BOARD_NAME, "GMxXGxX"),
|
||||
},
|
||||
.driver_data = (void *)(SERIO_QUIRK_NOMUX | SERIO_QUIRK_RESET_ALWAYS |
|
||||
SERIO_QUIRK_NOLOOP | SERIO_QUIRK_NOPNP)
|
||||
},
|
||||
{
|
||||
.matches = {
|
||||
DMI_MATCH(DMI_BOARD_NAME, "GMxHGxx"),
|
||||
},
|
||||
.driver_data = (void *)(SERIO_QUIRK_NOMUX | SERIO_QUIRK_RESET_ALWAYS |
|
||||
SERIO_QUIRK_NOLOOP | SERIO_QUIRK_NOPNP)
|
||||
},
|
||||
/*
|
||||
* A lot of modern Clevo barebones have touchpad and/or keyboard issues
|
||||
* after suspend fixable with nomux + reset + noloop + nopnp. Luckily,
|
||||
|
@ -429,16 +429,14 @@ static int ps2_gpio_probe(struct platform_device *pdev)
|
||||
}
|
||||
|
||||
error = devm_request_irq(dev, drvdata->irq, ps2_gpio_irq,
|
||||
IRQF_NO_THREAD, DRIVER_NAME, drvdata);
|
||||
IRQF_NO_THREAD | IRQF_NO_AUTOEN, DRIVER_NAME,
|
||||
drvdata);
|
||||
if (error) {
|
||||
dev_err(dev, "failed to request irq %d: %d\n",
|
||||
drvdata->irq, error);
|
||||
goto err_free_serio;
|
||||
}
|
||||
|
||||
/* Keep irq disabled until serio->open is called. */
|
||||
disable_irq(drvdata->irq);
|
||||
|
||||
serio->id.type = SERIO_8042;
|
||||
serio->open = ps2_gpio_open;
|
||||
serio->close = ps2_gpio_close;
|
||||
|
@ -254,36 +254,6 @@ config TOUCHSCREEN_CYTTSP_SPI
|
||||
To compile this driver as a module, choose M here: the
|
||||
module will be called cyttsp_spi.
|
||||
|
||||
config TOUCHSCREEN_CYTTSP4_CORE
|
||||
tristate "Cypress TrueTouch Gen4 Touchscreen Driver"
|
||||
help
|
||||
Core driver for Cypress TrueTouch(tm) Standard Product
|
||||
Generation4 touchscreen controllers.
|
||||
|
||||
Say Y here if you have a Cypress Gen4 touchscreen.
|
||||
|
||||
If unsure, say N.
|
||||
|
||||
To compile this driver as a module, choose M here.
|
||||
|
||||
config TOUCHSCREEN_CYTTSP4_I2C
|
||||
tristate "support I2C bus connection"
|
||||
depends on TOUCHSCREEN_CYTTSP4_CORE && I2C
|
||||
help
|
||||
Say Y here if the touchscreen is connected via I2C bus.
|
||||
|
||||
To compile this driver as a module, choose M here: the
|
||||
module will be called cyttsp4_i2c.
|
||||
|
||||
config TOUCHSCREEN_CYTTSP4_SPI
|
||||
tristate "support SPI bus connection"
|
||||
depends on TOUCHSCREEN_CYTTSP4_CORE && SPI_MASTER
|
||||
help
|
||||
Say Y here if the touchscreen is connected via SPI bus.
|
||||
|
||||
To compile this driver as a module, choose M here: the
|
||||
module will be called cyttsp4_spi.
|
||||
|
||||
config TOUCHSCREEN_CYTTSP5
|
||||
tristate "Cypress TrueTouch Gen5 Touchscreen Driver"
|
||||
depends on I2C
|
||||
@ -626,18 +596,6 @@ config TOUCHSCREEN_MAX11801
|
||||
To compile this driver as a module, choose M here: the
|
||||
module will be called max11801_ts.
|
||||
|
||||
config TOUCHSCREEN_MCS5000
|
||||
tristate "MELFAS MCS-5000 touchscreen"
|
||||
depends on I2C
|
||||
help
|
||||
Say Y here if you have the MELFAS MCS-5000 touchscreen controller
|
||||
chip in your system.
|
||||
|
||||
If unsure, say N.
|
||||
|
||||
To compile this driver as a module, choose M here: the
|
||||
module will be called mcs5000_ts.
|
||||
|
||||
config TOUCHSCREEN_MMS114
|
||||
tristate "MELFAS MMS114 touchscreen"
|
||||
depends on I2C
|
||||
|
@ -25,11 +25,8 @@ obj-$(CONFIG_TOUCHSCREEN_CHIPONE_ICN8505) += chipone_icn8505.o
|
||||
obj-$(CONFIG_TOUCHSCREEN_CY8CTMA140) += cy8ctma140.o
|
||||
obj-$(CONFIG_TOUCHSCREEN_CY8CTMG110) += cy8ctmg110_ts.o
|
||||
obj-$(CONFIG_TOUCHSCREEN_CYTTSP_CORE) += cyttsp_core.o
|
||||
obj-$(CONFIG_TOUCHSCREEN_CYTTSP_I2C) += cyttsp_i2c.o cyttsp_i2c_common.o
|
||||
obj-$(CONFIG_TOUCHSCREEN_CYTTSP_I2C) += cyttsp_i2c.o
|
||||
obj-$(CONFIG_TOUCHSCREEN_CYTTSP_SPI) += cyttsp_spi.o
|
||||
obj-$(CONFIG_TOUCHSCREEN_CYTTSP4_CORE) += cyttsp4_core.o
|
||||
obj-$(CONFIG_TOUCHSCREEN_CYTTSP4_I2C) += cyttsp4_i2c.o cyttsp_i2c_common.o
|
||||
obj-$(CONFIG_TOUCHSCREEN_CYTTSP4_SPI) += cyttsp4_spi.o
|
||||
obj-$(CONFIG_TOUCHSCREEN_CYTTSP5) += cyttsp5.o
|
||||
obj-$(CONFIG_TOUCHSCREEN_DA9034) += da9034-ts.o
|
||||
obj-$(CONFIG_TOUCHSCREEN_DA9052) += da9052_tsi.o
|
||||
@ -63,7 +60,6 @@ obj-$(CONFIG_TOUCHSCREEN_MAX11801) += max11801_ts.o
|
||||
obj-$(CONFIG_TOUCHSCREEN_MXS_LRADC) += mxs-lradc-ts.o
|
||||
obj-$(CONFIG_TOUCHSCREEN_MX25) += fsl-imx25-tcq.o
|
||||
obj-$(CONFIG_TOUCHSCREEN_MC13783) += mc13783_ts.o
|
||||
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
|
||||
|
@ -239,14 +239,10 @@ static void vf50_ts_close(struct input_dev *dev_input)
|
||||
static int vf50_ts_get_gpiod(struct device *dev, struct gpio_desc **gpio_d,
|
||||
const char *con_id, enum gpiod_flags flags)
|
||||
{
|
||||
int error;
|
||||
|
||||
*gpio_d = devm_gpiod_get(dev, con_id, flags);
|
||||
if (IS_ERR(*gpio_d)) {
|
||||
error = PTR_ERR(*gpio_d);
|
||||
dev_err(dev, "Could not get gpio_%s %d\n", con_id, error);
|
||||
return error;
|
||||
}
|
||||
if (IS_ERR(*gpio_d))
|
||||
return dev_err_probe(dev, PTR_ERR(*gpio_d),
|
||||
"Could not get gpio_%s\n", con_id);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
File diff suppressed because it is too large
Load Diff
@ -1,448 +0,0 @@
|
||||
/* SPDX-License-Identifier: GPL-2.0-only */
|
||||
/*
|
||||
* cyttsp4_core.h
|
||||
* Cypress TrueTouch(TM) Standard Product V4 Core driver module.
|
||||
* For use with Cypress Txx4xx parts.
|
||||
* Supported parts include:
|
||||
* TMA4XX
|
||||
* TMA1036
|
||||
*
|
||||
* Copyright (C) 2012 Cypress Semiconductor
|
||||
*
|
||||
* Contact Cypress Semiconductor at www.cypress.com <ttdrivers@cypress.com>
|
||||
*/
|
||||
|
||||
#ifndef _LINUX_CYTTSP4_CORE_H
|
||||
#define _LINUX_CYTTSP4_CORE_H
|
||||
|
||||
#include <linux/device.h>
|
||||
#include <linux/err.h>
|
||||
#include <linux/input.h>
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/limits.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/stringify.h>
|
||||
#include <linux/types.h>
|
||||
#include <linux/platform_data/cyttsp4.h>
|
||||
|
||||
#define CY_REG_BASE 0x00
|
||||
|
||||
#define CY_POST_CODEL_WDG_RST 0x01
|
||||
#define CY_POST_CODEL_CFG_DATA_CRC_FAIL 0x02
|
||||
#define CY_POST_CODEL_PANEL_TEST_FAIL 0x04
|
||||
|
||||
#define CY_NUM_BTN_PER_REG 4
|
||||
|
||||
/* touch record system information offset masks and shifts */
|
||||
#define CY_BYTE_OFS_MASK 0x1F
|
||||
#define CY_BOFS_MASK 0xE0
|
||||
#define CY_BOFS_SHIFT 5
|
||||
|
||||
#define CY_TMA1036_TCH_REC_SIZE 6
|
||||
#define CY_TMA4XX_TCH_REC_SIZE 9
|
||||
#define CY_TMA1036_MAX_TCH 0x0E
|
||||
#define CY_TMA4XX_MAX_TCH 0x1E
|
||||
|
||||
#define CY_NORMAL_ORIGIN 0 /* upper, left corner */
|
||||
#define CY_INVERT_ORIGIN 1 /* lower, right corner */
|
||||
|
||||
/* helpers */
|
||||
#define GET_NUM_TOUCHES(x) ((x) & 0x1F)
|
||||
#define IS_LARGE_AREA(x) ((x) & 0x20)
|
||||
#define IS_BAD_PKT(x) ((x) & 0x20)
|
||||
#define IS_BOOTLOADER(hst_mode, reset_detect) \
|
||||
((hst_mode) & 0x01 || (reset_detect) != 0)
|
||||
#define IS_TMO(t) ((t) == 0)
|
||||
|
||||
|
||||
enum cyttsp_cmd_bits {
|
||||
CY_CMD_COMPLETE = (1 << 6),
|
||||
};
|
||||
|
||||
/* Timeout in ms. */
|
||||
#define CY_WATCHDOG_TIMEOUT 1000
|
||||
|
||||
#define CY_MAX_PRINT_SIZE 512
|
||||
#ifdef VERBOSE_DEBUG
|
||||
#define CY_MAX_PRBUF_SIZE PIPE_BUF
|
||||
#define CY_PR_TRUNCATED " truncated..."
|
||||
#endif
|
||||
|
||||
enum cyttsp4_ic_grpnum {
|
||||
CY_IC_GRPNUM_RESERVED,
|
||||
CY_IC_GRPNUM_CMD_REGS,
|
||||
CY_IC_GRPNUM_TCH_REP,
|
||||
CY_IC_GRPNUM_DATA_REC,
|
||||
CY_IC_GRPNUM_TEST_REC,
|
||||
CY_IC_GRPNUM_PCFG_REC,
|
||||
CY_IC_GRPNUM_TCH_PARM_VAL,
|
||||
CY_IC_GRPNUM_TCH_PARM_SIZE,
|
||||
CY_IC_GRPNUM_RESERVED1,
|
||||
CY_IC_GRPNUM_RESERVED2,
|
||||
CY_IC_GRPNUM_OPCFG_REC,
|
||||
CY_IC_GRPNUM_DDATA_REC,
|
||||
CY_IC_GRPNUM_MDATA_REC,
|
||||
CY_IC_GRPNUM_TEST_REGS,
|
||||
CY_IC_GRPNUM_BTN_KEYS,
|
||||
CY_IC_GRPNUM_TTHE_REGS,
|
||||
CY_IC_GRPNUM_NUM
|
||||
};
|
||||
|
||||
enum cyttsp4_int_state {
|
||||
CY_INT_NONE,
|
||||
CY_INT_IGNORE = (1 << 0),
|
||||
CY_INT_MODE_CHANGE = (1 << 1),
|
||||
CY_INT_EXEC_CMD = (1 << 2),
|
||||
CY_INT_AWAKE = (1 << 3),
|
||||
};
|
||||
|
||||
enum cyttsp4_mode {
|
||||
CY_MODE_UNKNOWN,
|
||||
CY_MODE_BOOTLOADER = (1 << 1),
|
||||
CY_MODE_OPERATIONAL = (1 << 2),
|
||||
CY_MODE_SYSINFO = (1 << 3),
|
||||
CY_MODE_CAT = (1 << 4),
|
||||
CY_MODE_STARTUP = (1 << 5),
|
||||
CY_MODE_LOADER = (1 << 6),
|
||||
CY_MODE_CHANGE_MODE = (1 << 7),
|
||||
CY_MODE_CHANGED = (1 << 8),
|
||||
CY_MODE_CMD_COMPLETE = (1 << 9),
|
||||
};
|
||||
|
||||
enum cyttsp4_sleep_state {
|
||||
SS_SLEEP_OFF,
|
||||
SS_SLEEP_ON,
|
||||
SS_SLEEPING,
|
||||
SS_WAKING,
|
||||
};
|
||||
|
||||
enum cyttsp4_startup_state {
|
||||
STARTUP_NONE,
|
||||
STARTUP_QUEUED,
|
||||
STARTUP_RUNNING,
|
||||
};
|
||||
|
||||
#define CY_NUM_REVCTRL 8
|
||||
struct cyttsp4_cydata {
|
||||
u8 ttpidh;
|
||||
u8 ttpidl;
|
||||
u8 fw_ver_major;
|
||||
u8 fw_ver_minor;
|
||||
u8 revctrl[CY_NUM_REVCTRL];
|
||||
u8 blver_major;
|
||||
u8 blver_minor;
|
||||
u8 jtag_si_id3;
|
||||
u8 jtag_si_id2;
|
||||
u8 jtag_si_id1;
|
||||
u8 jtag_si_id0;
|
||||
u8 mfgid_sz;
|
||||
u8 cyito_idh;
|
||||
u8 cyito_idl;
|
||||
u8 cyito_verh;
|
||||
u8 cyito_verl;
|
||||
u8 ttsp_ver_major;
|
||||
u8 ttsp_ver_minor;
|
||||
u8 device_info;
|
||||
u8 mfg_id[];
|
||||
} __packed;
|
||||
|
||||
struct cyttsp4_test {
|
||||
u8 post_codeh;
|
||||
u8 post_codel;
|
||||
} __packed;
|
||||
|
||||
struct cyttsp4_pcfg {
|
||||
u8 electrodes_x;
|
||||
u8 electrodes_y;
|
||||
u8 len_xh;
|
||||
u8 len_xl;
|
||||
u8 len_yh;
|
||||
u8 len_yl;
|
||||
u8 res_xh;
|
||||
u8 res_xl;
|
||||
u8 res_yh;
|
||||
u8 res_yl;
|
||||
u8 max_zh;
|
||||
u8 max_zl;
|
||||
u8 panel_info0;
|
||||
} __packed;
|
||||
|
||||
struct cyttsp4_tch_rec_params {
|
||||
u8 loc;
|
||||
u8 size;
|
||||
} __packed;
|
||||
|
||||
#define CY_NUM_TCH_FIELDS 7
|
||||
#define CY_NUM_EXT_TCH_FIELDS 3
|
||||
struct cyttsp4_opcfg {
|
||||
u8 cmd_ofs;
|
||||
u8 rep_ofs;
|
||||
u8 rep_szh;
|
||||
u8 rep_szl;
|
||||
u8 num_btns;
|
||||
u8 tt_stat_ofs;
|
||||
u8 obj_cfg0;
|
||||
u8 max_tchs;
|
||||
u8 tch_rec_size;
|
||||
struct cyttsp4_tch_rec_params tch_rec_old[CY_NUM_TCH_FIELDS];
|
||||
u8 btn_rec_size; /* btn record size (in bytes) */
|
||||
u8 btn_diff_ofs; /* btn data loc, diff counts */
|
||||
u8 btn_diff_size; /* btn size of diff counts (in bits) */
|
||||
struct cyttsp4_tch_rec_params tch_rec_new[CY_NUM_EXT_TCH_FIELDS];
|
||||
} __packed;
|
||||
|
||||
struct cyttsp4_sysinfo_ptr {
|
||||
struct cyttsp4_cydata *cydata;
|
||||
struct cyttsp4_test *test;
|
||||
struct cyttsp4_pcfg *pcfg;
|
||||
struct cyttsp4_opcfg *opcfg;
|
||||
struct cyttsp4_ddata *ddata;
|
||||
struct cyttsp4_mdata *mdata;
|
||||
} __packed;
|
||||
|
||||
struct cyttsp4_sysinfo_data {
|
||||
u8 hst_mode;
|
||||
u8 reserved;
|
||||
u8 map_szh;
|
||||
u8 map_szl;
|
||||
u8 cydata_ofsh;
|
||||
u8 cydata_ofsl;
|
||||
u8 test_ofsh;
|
||||
u8 test_ofsl;
|
||||
u8 pcfg_ofsh;
|
||||
u8 pcfg_ofsl;
|
||||
u8 opcfg_ofsh;
|
||||
u8 opcfg_ofsl;
|
||||
u8 ddata_ofsh;
|
||||
u8 ddata_ofsl;
|
||||
u8 mdata_ofsh;
|
||||
u8 mdata_ofsl;
|
||||
} __packed;
|
||||
|
||||
enum cyttsp4_tch_abs { /* for ordering within the extracted touch data array */
|
||||
CY_TCH_X, /* X */
|
||||
CY_TCH_Y, /* Y */
|
||||
CY_TCH_P, /* P (Z) */
|
||||
CY_TCH_T, /* TOUCH ID */
|
||||
CY_TCH_E, /* EVENT ID */
|
||||
CY_TCH_O, /* OBJECT ID */
|
||||
CY_TCH_W, /* SIZE */
|
||||
CY_TCH_MAJ, /* TOUCH_MAJOR */
|
||||
CY_TCH_MIN, /* TOUCH_MINOR */
|
||||
CY_TCH_OR, /* ORIENTATION */
|
||||
CY_TCH_NUM_ABS
|
||||
};
|
||||
|
||||
struct cyttsp4_touch {
|
||||
int abs[CY_TCH_NUM_ABS];
|
||||
};
|
||||
|
||||
struct cyttsp4_tch_abs_params {
|
||||
size_t ofs; /* abs byte offset */
|
||||
size_t size; /* size in bits */
|
||||
size_t max; /* max value */
|
||||
size_t bofs; /* bit offset */
|
||||
};
|
||||
|
||||
struct cyttsp4_sysinfo_ofs {
|
||||
size_t chip_type;
|
||||
size_t cmd_ofs;
|
||||
size_t rep_ofs;
|
||||
size_t rep_sz;
|
||||
size_t num_btns;
|
||||
size_t num_btn_regs; /* ceil(num_btns/4) */
|
||||
size_t tt_stat_ofs;
|
||||
size_t tch_rec_size;
|
||||
size_t obj_cfg0;
|
||||
size_t max_tchs;
|
||||
size_t mode_size;
|
||||
size_t data_size;
|
||||
size_t map_sz;
|
||||
size_t max_x;
|
||||
size_t x_origin; /* left or right corner */
|
||||
size_t max_y;
|
||||
size_t y_origin; /* upper or lower corner */
|
||||
size_t max_p;
|
||||
size_t cydata_ofs;
|
||||
size_t test_ofs;
|
||||
size_t pcfg_ofs;
|
||||
size_t opcfg_ofs;
|
||||
size_t ddata_ofs;
|
||||
size_t mdata_ofs;
|
||||
size_t cydata_size;
|
||||
size_t test_size;
|
||||
size_t pcfg_size;
|
||||
size_t opcfg_size;
|
||||
size_t ddata_size;
|
||||
size_t mdata_size;
|
||||
size_t btn_keys_size;
|
||||
struct cyttsp4_tch_abs_params tch_abs[CY_TCH_NUM_ABS];
|
||||
size_t btn_rec_size; /* btn record size (in bytes) */
|
||||
size_t btn_diff_ofs;/* btn data loc ,diff counts, (Op-Mode byte ofs) */
|
||||
size_t btn_diff_size;/* btn size of diff counts (in bits) */
|
||||
};
|
||||
|
||||
enum cyttsp4_btn_state {
|
||||
CY_BTN_RELEASED,
|
||||
CY_BTN_PRESSED,
|
||||
CY_BTN_NUM_STATE
|
||||
};
|
||||
|
||||
struct cyttsp4_btn {
|
||||
bool enabled;
|
||||
int state; /* CY_BTN_PRESSED, CY_BTN_RELEASED */
|
||||
int key_code;
|
||||
};
|
||||
|
||||
struct cyttsp4_sysinfo {
|
||||
bool ready;
|
||||
struct cyttsp4_sysinfo_data si_data;
|
||||
struct cyttsp4_sysinfo_ptr si_ptrs;
|
||||
struct cyttsp4_sysinfo_ofs si_ofs;
|
||||
struct cyttsp4_btn *btn; /* button states */
|
||||
u8 *btn_rec_data; /* button diff count data */
|
||||
u8 *xy_mode; /* operational mode and status regs */
|
||||
u8 *xy_data; /* operational touch regs */
|
||||
};
|
||||
|
||||
struct cyttsp4_mt_data {
|
||||
struct cyttsp4_mt_platform_data *pdata;
|
||||
struct cyttsp4_sysinfo *si;
|
||||
struct input_dev *input;
|
||||
struct mutex report_lock;
|
||||
bool is_suspended;
|
||||
char phys[NAME_MAX];
|
||||
int num_prv_tch;
|
||||
};
|
||||
|
||||
struct cyttsp4 {
|
||||
struct device *dev;
|
||||
struct mutex system_lock;
|
||||
struct mutex adap_lock;
|
||||
enum cyttsp4_mode mode;
|
||||
enum cyttsp4_sleep_state sleep_state;
|
||||
enum cyttsp4_startup_state startup_state;
|
||||
int int_status;
|
||||
wait_queue_head_t wait_q;
|
||||
int irq;
|
||||
struct work_struct startup_work;
|
||||
struct work_struct watchdog_work;
|
||||
struct timer_list watchdog_timer;
|
||||
struct cyttsp4_sysinfo sysinfo;
|
||||
void *exclusive_dev;
|
||||
int exclusive_waits;
|
||||
atomic_t ignore_irq;
|
||||
bool invalid_touch_app;
|
||||
struct cyttsp4_mt_data md;
|
||||
struct cyttsp4_platform_data *pdata;
|
||||
struct cyttsp4_core_platform_data *cpdata;
|
||||
const struct cyttsp4_bus_ops *bus_ops;
|
||||
u8 *xfer_buf;
|
||||
#ifdef VERBOSE_DEBUG
|
||||
u8 pr_buf[CY_MAX_PRBUF_SIZE];
|
||||
#endif
|
||||
};
|
||||
|
||||
struct cyttsp4_bus_ops {
|
||||
u16 bustype;
|
||||
int (*write)(struct device *dev, u8 *xfer_buf, u16 addr, u8 length,
|
||||
const void *values);
|
||||
int (*read)(struct device *dev, u8 *xfer_buf, u16 addr, u8 length,
|
||||
void *values);
|
||||
};
|
||||
|
||||
enum cyttsp4_hst_mode_bits {
|
||||
CY_HST_TOGGLE = (1 << 7),
|
||||
CY_HST_MODE_CHANGE = (1 << 3),
|
||||
CY_HST_MODE = (7 << 4),
|
||||
CY_HST_OPERATE = (0 << 4),
|
||||
CY_HST_SYSINFO = (1 << 4),
|
||||
CY_HST_CAT = (2 << 4),
|
||||
CY_HST_LOWPOW = (1 << 2),
|
||||
CY_HST_SLEEP = (1 << 1),
|
||||
CY_HST_RESET = (1 << 0),
|
||||
};
|
||||
|
||||
/* abs settings */
|
||||
#define CY_IGNORE_VALUE 0xFFFF
|
||||
|
||||
/* abs signal capabilities offsets in the frameworks array */
|
||||
enum cyttsp4_sig_caps {
|
||||
CY_SIGNAL_OST,
|
||||
CY_MIN_OST,
|
||||
CY_MAX_OST,
|
||||
CY_FUZZ_OST,
|
||||
CY_FLAT_OST,
|
||||
CY_NUM_ABS_SET /* number of signal capability fields */
|
||||
};
|
||||
|
||||
/* abs axis signal offsets in the framworks array */
|
||||
enum cyttsp4_sig_ost {
|
||||
CY_ABS_X_OST,
|
||||
CY_ABS_Y_OST,
|
||||
CY_ABS_P_OST,
|
||||
CY_ABS_W_OST,
|
||||
CY_ABS_ID_OST,
|
||||
CY_ABS_MAJ_OST,
|
||||
CY_ABS_MIN_OST,
|
||||
CY_ABS_OR_OST,
|
||||
CY_NUM_ABS_OST /* number of abs signals */
|
||||
};
|
||||
|
||||
enum cyttsp4_flags {
|
||||
CY_FLAG_NONE = 0x00,
|
||||
CY_FLAG_HOVER = 0x04,
|
||||
CY_FLAG_FLIP = 0x08,
|
||||
CY_FLAG_INV_X = 0x10,
|
||||
CY_FLAG_INV_Y = 0x20,
|
||||
CY_FLAG_VKEYS = 0x40,
|
||||
};
|
||||
|
||||
enum cyttsp4_object_id {
|
||||
CY_OBJ_STANDARD_FINGER,
|
||||
CY_OBJ_LARGE_OBJECT,
|
||||
CY_OBJ_STYLUS,
|
||||
CY_OBJ_HOVER,
|
||||
};
|
||||
|
||||
enum cyttsp4_event_id {
|
||||
CY_EV_NO_EVENT,
|
||||
CY_EV_TOUCHDOWN,
|
||||
CY_EV_MOVE, /* significant displacement (> act dist) */
|
||||
CY_EV_LIFTOFF, /* record reports last position */
|
||||
};
|
||||
|
||||
/* x-axis resolution of panel in pixels */
|
||||
#define CY_PCFG_RESOLUTION_X_MASK 0x7F
|
||||
|
||||
/* y-axis resolution of panel in pixels */
|
||||
#define CY_PCFG_RESOLUTION_Y_MASK 0x7F
|
||||
|
||||
/* x-axis, 0:origin is on left side of panel, 1: right */
|
||||
#define CY_PCFG_ORIGIN_X_MASK 0x80
|
||||
|
||||
/* y-axis, 0:origin is on top side of panel, 1: bottom */
|
||||
#define CY_PCFG_ORIGIN_Y_MASK 0x80
|
||||
|
||||
static inline int cyttsp4_adap_read(struct cyttsp4 *ts, u16 addr, int size,
|
||||
void *buf)
|
||||
{
|
||||
return ts->bus_ops->read(ts->dev, ts->xfer_buf, addr, size, buf);
|
||||
}
|
||||
|
||||
static inline int cyttsp4_adap_write(struct cyttsp4 *ts, u16 addr, int size,
|
||||
const void *buf)
|
||||
{
|
||||
return ts->bus_ops->write(ts->dev, ts->xfer_buf, addr, size, buf);
|
||||
}
|
||||
|
||||
extern struct cyttsp4 *cyttsp4_probe(const struct cyttsp4_bus_ops *ops,
|
||||
struct device *dev, u16 irq, size_t xfer_buf_size);
|
||||
extern int cyttsp4_remove(struct cyttsp4 *ts);
|
||||
int cyttsp_i2c_write_block_data(struct device *dev, u8 *xfer_buf, u16 addr,
|
||||
u8 length, const void *values);
|
||||
int cyttsp_i2c_read_block_data(struct device *dev, u8 *xfer_buf, u16 addr,
|
||||
u8 length, void *values);
|
||||
extern const struct dev_pm_ops cyttsp4_pm_ops;
|
||||
|
||||
#endif /* _LINUX_CYTTSP4_CORE_H */
|
@ -1,72 +0,0 @@
|
||||
// SPDX-License-Identifier: GPL-2.0-only
|
||||
/*
|
||||
* cyttsp_i2c.c
|
||||
* Cypress TrueTouch(TM) Standard Product (TTSP) I2C touchscreen driver.
|
||||
* For use with Cypress Txx4xx parts.
|
||||
* Supported parts include:
|
||||
* TMA4XX
|
||||
* TMA1036
|
||||
*
|
||||
* Copyright (C) 2009, 2010, 2011 Cypress Semiconductor, Inc.
|
||||
* Copyright (C) 2012 Javier Martinez Canillas <javier@dowhile0.org>
|
||||
* Copyright (C) 2013 Cypress Semiconductor
|
||||
*
|
||||
* Contact Cypress Semiconductor at www.cypress.com <ttdrivers@cypress.com>
|
||||
*/
|
||||
|
||||
#include "cyttsp4_core.h"
|
||||
|
||||
#include <linux/i2c.h>
|
||||
#include <linux/input.h>
|
||||
|
||||
#define CYTTSP4_I2C_DATA_SIZE (3 * 256)
|
||||
|
||||
static const struct cyttsp4_bus_ops cyttsp4_i2c_bus_ops = {
|
||||
.bustype = BUS_I2C,
|
||||
.write = cyttsp_i2c_write_block_data,
|
||||
.read = cyttsp_i2c_read_block_data,
|
||||
};
|
||||
|
||||
static int cyttsp4_i2c_probe(struct i2c_client *client)
|
||||
{
|
||||
struct cyttsp4 *ts;
|
||||
|
||||
if (!i2c_check_functionality(client->adapter, I2C_FUNC_I2C)) {
|
||||
dev_err(&client->dev, "I2C functionality not Supported\n");
|
||||
return -EIO;
|
||||
}
|
||||
|
||||
ts = cyttsp4_probe(&cyttsp4_i2c_bus_ops, &client->dev, client->irq,
|
||||
CYTTSP4_I2C_DATA_SIZE);
|
||||
|
||||
return PTR_ERR_OR_ZERO(ts);
|
||||
}
|
||||
|
||||
static void cyttsp4_i2c_remove(struct i2c_client *client)
|
||||
{
|
||||
struct cyttsp4 *ts = i2c_get_clientdata(client);
|
||||
|
||||
cyttsp4_remove(ts);
|
||||
}
|
||||
|
||||
static const struct i2c_device_id cyttsp4_i2c_id[] = {
|
||||
{ CYTTSP4_I2C_NAME },
|
||||
{ }
|
||||
};
|
||||
MODULE_DEVICE_TABLE(i2c, cyttsp4_i2c_id);
|
||||
|
||||
static struct i2c_driver cyttsp4_i2c_driver = {
|
||||
.driver = {
|
||||
.name = CYTTSP4_I2C_NAME,
|
||||
.pm = pm_ptr(&cyttsp4_pm_ops),
|
||||
},
|
||||
.probe = cyttsp4_i2c_probe,
|
||||
.remove = cyttsp4_i2c_remove,
|
||||
.id_table = cyttsp4_i2c_id,
|
||||
};
|
||||
|
||||
module_i2c_driver(cyttsp4_i2c_driver);
|
||||
|
||||
MODULE_LICENSE("GPL");
|
||||
MODULE_DESCRIPTION("Cypress TrueTouch(R) Standard Product (TTSP) I2C driver");
|
||||
MODULE_AUTHOR("Cypress");
|
@ -1,187 +0,0 @@
|
||||
// SPDX-License-Identifier: GPL-2.0-only
|
||||
/*
|
||||
* Source for:
|
||||
* Cypress TrueTouch(TM) Standard Product (TTSP) SPI touchscreen driver.
|
||||
* For use with Cypress Txx4xx parts.
|
||||
* Supported parts include:
|
||||
* TMA4XX
|
||||
* TMA1036
|
||||
*
|
||||
* Copyright (C) 2009, 2010, 2011 Cypress Semiconductor, Inc.
|
||||
* Copyright (C) 2012 Javier Martinez Canillas <javier@dowhile0.org>
|
||||
* Copyright (C) 2013 Cypress Semiconductor
|
||||
*
|
||||
* Contact Cypress Semiconductor at www.cypress.com <ttdrivers@cypress.com>
|
||||
*/
|
||||
|
||||
#include "cyttsp4_core.h"
|
||||
|
||||
#include <linux/delay.h>
|
||||
#include <linux/input.h>
|
||||
#include <linux/spi/spi.h>
|
||||
|
||||
#define CY_SPI_WR_OP 0x00 /* r/~w */
|
||||
#define CY_SPI_RD_OP 0x01
|
||||
#define CY_SPI_BITS_PER_WORD 8
|
||||
#define CY_SPI_A8_BIT 0x02
|
||||
#define CY_SPI_WR_HEADER_BYTES 2
|
||||
#define CY_SPI_RD_HEADER_BYTES 1
|
||||
#define CY_SPI_CMD_BYTES 2
|
||||
#define CY_SPI_SYNC_BYTE 0
|
||||
#define CY_SPI_SYNC_ACK 0x62 /* from TRM *A protocol */
|
||||
#define CY_SPI_DATA_SIZE (2 * 256)
|
||||
|
||||
#define CY_SPI_DATA_BUF_SIZE (CY_SPI_CMD_BYTES + CY_SPI_DATA_SIZE)
|
||||
|
||||
static int cyttsp_spi_xfer(struct device *dev, u8 *xfer_buf,
|
||||
u8 op, u16 reg, u8 *buf, int length)
|
||||
{
|
||||
struct spi_device *spi = to_spi_device(dev);
|
||||
struct spi_message msg;
|
||||
struct spi_transfer xfer[2];
|
||||
u8 *wr_buf = &xfer_buf[0];
|
||||
u8 rd_buf[CY_SPI_CMD_BYTES];
|
||||
int retval;
|
||||
int i;
|
||||
|
||||
if (length > CY_SPI_DATA_SIZE) {
|
||||
dev_err(dev, "%s: length %d is too big.\n",
|
||||
__func__, length);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
memset(wr_buf, 0, CY_SPI_DATA_BUF_SIZE);
|
||||
memset(rd_buf, 0, CY_SPI_CMD_BYTES);
|
||||
|
||||
wr_buf[0] = op + (((reg >> 8) & 0x1) ? CY_SPI_A8_BIT : 0);
|
||||
if (op == CY_SPI_WR_OP) {
|
||||
wr_buf[1] = reg & 0xFF;
|
||||
if (length > 0)
|
||||
memcpy(wr_buf + CY_SPI_CMD_BYTES, buf, length);
|
||||
}
|
||||
|
||||
memset(xfer, 0, sizeof(xfer));
|
||||
spi_message_init(&msg);
|
||||
|
||||
/*
|
||||
We set both TX and RX buffers because Cypress TTSP
|
||||
requires full duplex operation.
|
||||
*/
|
||||
xfer[0].tx_buf = wr_buf;
|
||||
xfer[0].rx_buf = rd_buf;
|
||||
switch (op) {
|
||||
case CY_SPI_WR_OP:
|
||||
xfer[0].len = length + CY_SPI_CMD_BYTES;
|
||||
spi_message_add_tail(&xfer[0], &msg);
|
||||
break;
|
||||
|
||||
case CY_SPI_RD_OP:
|
||||
xfer[0].len = CY_SPI_RD_HEADER_BYTES;
|
||||
spi_message_add_tail(&xfer[0], &msg);
|
||||
|
||||
xfer[1].rx_buf = buf;
|
||||
xfer[1].len = length;
|
||||
spi_message_add_tail(&xfer[1], &msg);
|
||||
break;
|
||||
|
||||
default:
|
||||
dev_err(dev, "%s: bad operation code=%d\n", __func__, op);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
retval = spi_sync(spi, &msg);
|
||||
if (retval < 0) {
|
||||
dev_dbg(dev, "%s: spi_sync() error %d, len=%d, op=%d\n",
|
||||
__func__, retval, xfer[1].len, op);
|
||||
|
||||
/*
|
||||
* do not return here since was a bad ACK sequence
|
||||
* let the following ACK check handle any errors and
|
||||
* allow silent retries
|
||||
*/
|
||||
}
|
||||
|
||||
if (rd_buf[CY_SPI_SYNC_BYTE] != CY_SPI_SYNC_ACK) {
|
||||
dev_dbg(dev, "%s: operation %d failed\n", __func__, op);
|
||||
|
||||
for (i = 0; i < CY_SPI_CMD_BYTES; i++)
|
||||
dev_dbg(dev, "%s: test rd_buf[%d]:0x%02x\n",
|
||||
__func__, i, rd_buf[i]);
|
||||
for (i = 0; i < length; i++)
|
||||
dev_dbg(dev, "%s: test buf[%d]:0x%02x\n",
|
||||
__func__, i, buf[i]);
|
||||
|
||||
return -EIO;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int cyttsp_spi_read_block_data(struct device *dev, u8 *xfer_buf,
|
||||
u16 addr, u8 length, void *data)
|
||||
{
|
||||
int rc;
|
||||
|
||||
rc = cyttsp_spi_xfer(dev, xfer_buf, CY_SPI_WR_OP, addr, NULL, 0);
|
||||
if (rc)
|
||||
return rc;
|
||||
else
|
||||
return cyttsp_spi_xfer(dev, xfer_buf, CY_SPI_RD_OP, addr, data,
|
||||
length);
|
||||
}
|
||||
|
||||
static int cyttsp_spi_write_block_data(struct device *dev, u8 *xfer_buf,
|
||||
u16 addr, u8 length, const void *data)
|
||||
{
|
||||
return cyttsp_spi_xfer(dev, xfer_buf, CY_SPI_WR_OP, addr, (void *)data,
|
||||
length);
|
||||
}
|
||||
|
||||
static const struct cyttsp4_bus_ops cyttsp_spi_bus_ops = {
|
||||
.bustype = BUS_SPI,
|
||||
.write = cyttsp_spi_write_block_data,
|
||||
.read = cyttsp_spi_read_block_data,
|
||||
};
|
||||
|
||||
static int cyttsp4_spi_probe(struct spi_device *spi)
|
||||
{
|
||||
struct cyttsp4 *ts;
|
||||
int error;
|
||||
|
||||
/* Set up SPI*/
|
||||
spi->bits_per_word = CY_SPI_BITS_PER_WORD;
|
||||
spi->mode = SPI_MODE_0;
|
||||
error = spi_setup(spi);
|
||||
if (error < 0) {
|
||||
dev_err(&spi->dev, "%s: SPI setup error %d\n",
|
||||
__func__, error);
|
||||
return error;
|
||||
}
|
||||
|
||||
ts = cyttsp4_probe(&cyttsp_spi_bus_ops, &spi->dev, spi->irq,
|
||||
CY_SPI_DATA_BUF_SIZE);
|
||||
|
||||
return PTR_ERR_OR_ZERO(ts);
|
||||
}
|
||||
|
||||
static void cyttsp4_spi_remove(struct spi_device *spi)
|
||||
{
|
||||
struct cyttsp4 *ts = spi_get_drvdata(spi);
|
||||
cyttsp4_remove(ts);
|
||||
}
|
||||
|
||||
static struct spi_driver cyttsp4_spi_driver = {
|
||||
.driver = {
|
||||
.name = CYTTSP4_SPI_NAME,
|
||||
.pm = pm_ptr(&cyttsp4_pm_ops),
|
||||
},
|
||||
.probe = cyttsp4_spi_probe,
|
||||
.remove = cyttsp4_spi_remove,
|
||||
};
|
||||
|
||||
module_spi_driver(cyttsp4_spi_driver);
|
||||
|
||||
MODULE_LICENSE("GPL");
|
||||
MODULE_DESCRIPTION("Cypress TrueTouch(R) Standard Product (TTSP) SPI driver");
|
||||
MODULE_AUTHOR("Cypress");
|
||||
MODULE_ALIAS("spi:cyttsp4");
|
@ -17,7 +17,6 @@
|
||||
#include <linux/input.h>
|
||||
#include <linux/input/mt.h>
|
||||
#include <linux/input/touchscreen.h>
|
||||
#include <linux/gpio.h>
|
||||
#include <linux/interrupt.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/property.h>
|
||||
@ -615,17 +614,14 @@ static int cyttsp_parse_properties(struct cyttsp *ts)
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void cyttsp_disable_regulators(void *_ts)
|
||||
{
|
||||
struct cyttsp *ts = _ts;
|
||||
|
||||
regulator_bulk_disable(ARRAY_SIZE(ts->regulators),
|
||||
ts->regulators);
|
||||
}
|
||||
|
||||
struct cyttsp *cyttsp_probe(const struct cyttsp_bus_ops *bus_ops,
|
||||
struct device *dev, int irq, size_t xfer_buf_size)
|
||||
{
|
||||
/*
|
||||
* VCPIN is the analog voltage supply
|
||||
* VDD is the digital voltage supply
|
||||
*/
|
||||
static const char * const supplies[] = { "vcpin", "vdd" };
|
||||
struct cyttsp *ts;
|
||||
struct input_dev *input_dev;
|
||||
int error;
|
||||
@ -643,29 +639,10 @@ struct cyttsp *cyttsp_probe(const struct cyttsp_bus_ops *bus_ops,
|
||||
ts->bus_ops = bus_ops;
|
||||
ts->irq = irq;
|
||||
|
||||
/*
|
||||
* VCPIN is the analog voltage supply
|
||||
* VDD is the digital voltage supply
|
||||
*/
|
||||
ts->regulators[0].supply = "vcpin";
|
||||
ts->regulators[1].supply = "vdd";
|
||||
error = devm_regulator_bulk_get(dev, ARRAY_SIZE(ts->regulators),
|
||||
ts->regulators);
|
||||
error = devm_regulator_bulk_get_enable(dev, ARRAY_SIZE(supplies),
|
||||
supplies);
|
||||
if (error) {
|
||||
dev_err(dev, "Failed to get regulators: %d\n", error);
|
||||
return ERR_PTR(error);
|
||||
}
|
||||
|
||||
error = regulator_bulk_enable(ARRAY_SIZE(ts->regulators),
|
||||
ts->regulators);
|
||||
if (error) {
|
||||
dev_err(dev, "Cannot enable regulators: %d\n", error);
|
||||
return ERR_PTR(error);
|
||||
}
|
||||
|
||||
error = devm_add_action_or_reset(dev, cyttsp_disable_regulators, ts);
|
||||
if (error) {
|
||||
dev_err(dev, "failed to install chip disable handler\n");
|
||||
dev_err(dev, "Failed to enable regulators: %d\n", error);
|
||||
return ERR_PTR(error);
|
||||
}
|
||||
|
||||
|
@ -122,7 +122,6 @@ struct cyttsp {
|
||||
enum cyttsp_state state;
|
||||
bool suspended;
|
||||
|
||||
struct regulator_bulk_data regulators[2];
|
||||
struct gpio_desc *reset_gpio;
|
||||
bool use_hndshk;
|
||||
u8 act_dist;
|
||||
@ -137,10 +136,6 @@ struct cyttsp {
|
||||
struct cyttsp *cyttsp_probe(const struct cyttsp_bus_ops *bus_ops,
|
||||
struct device *dev, int irq, size_t xfer_buf_size);
|
||||
|
||||
int cyttsp_i2c_write_block_data(struct device *dev, u8 *xfer_buf, u16 addr,
|
||||
u8 length, const void *values);
|
||||
int cyttsp_i2c_read_block_data(struct device *dev, u8 *xfer_buf, u16 addr,
|
||||
u8 length, void *values);
|
||||
extern const struct dev_pm_ops cyttsp_pm_ops;
|
||||
|
||||
#endif /* __CYTTSP_CORE_H__ */
|
||||
|
@ -22,6 +22,61 @@
|
||||
|
||||
#define CY_I2C_DATA_SIZE 128
|
||||
|
||||
static int cyttsp_i2c_read_block_data(struct device *dev, u8 *xfer_buf,
|
||||
u16 addr, u8 length, void *values)
|
||||
{
|
||||
struct i2c_client *client = to_i2c_client(dev);
|
||||
u8 client_addr = client->addr | ((addr >> 8) & 0x1);
|
||||
u8 addr_lo = addr & 0xFF;
|
||||
struct i2c_msg msgs[] = {
|
||||
{
|
||||
.addr = client_addr,
|
||||
.flags = 0,
|
||||
.len = 1,
|
||||
.buf = &addr_lo,
|
||||
},
|
||||
{
|
||||
.addr = client_addr,
|
||||
.flags = I2C_M_RD,
|
||||
.len = length,
|
||||
.buf = values,
|
||||
},
|
||||
};
|
||||
int retval;
|
||||
|
||||
retval = i2c_transfer(client->adapter, msgs, ARRAY_SIZE(msgs));
|
||||
if (retval < 0)
|
||||
return retval;
|
||||
|
||||
return retval != ARRAY_SIZE(msgs) ? -EIO : 0;
|
||||
}
|
||||
|
||||
static int cyttsp_i2c_write_block_data(struct device *dev, u8 *xfer_buf,
|
||||
u16 addr, u8 length, const void *values)
|
||||
{
|
||||
struct i2c_client *client = to_i2c_client(dev);
|
||||
u8 client_addr = client->addr | ((addr >> 8) & 0x1);
|
||||
u8 addr_lo = addr & 0xFF;
|
||||
struct i2c_msg msgs[] = {
|
||||
{
|
||||
.addr = client_addr,
|
||||
.flags = 0,
|
||||
.len = length + 1,
|
||||
.buf = xfer_buf,
|
||||
},
|
||||
};
|
||||
int retval;
|
||||
|
||||
xfer_buf[0] = addr_lo;
|
||||
memcpy(&xfer_buf[1], values, length);
|
||||
|
||||
retval = i2c_transfer(client->adapter, msgs, ARRAY_SIZE(msgs));
|
||||
if (retval < 0)
|
||||
return retval;
|
||||
|
||||
return retval != ARRAY_SIZE(msgs) ? -EIO : 0;
|
||||
}
|
||||
|
||||
static const struct cyttsp_bus_ops cyttsp_i2c_bus_ops = {
|
||||
.bustype = BUS_I2C,
|
||||
.write = cyttsp_i2c_write_block_data,
|
||||
|
@ -1,86 +0,0 @@
|
||||
// SPDX-License-Identifier: GPL-2.0-only
|
||||
/*
|
||||
* cyttsp_i2c_common.c
|
||||
* Cypress TrueTouch(TM) Standard Product (TTSP) I2C touchscreen driver.
|
||||
* For use with Cypress Txx3xx and Txx4xx parts.
|
||||
* Supported parts include:
|
||||
* CY8CTST341
|
||||
* CY8CTMA340
|
||||
* TMA4XX
|
||||
* TMA1036
|
||||
*
|
||||
* Copyright (C) 2009, 2010, 2011 Cypress Semiconductor, Inc.
|
||||
* Copyright (C) 2012 Javier Martinez Canillas <javier@dowhile0.org>
|
||||
*
|
||||
* Contact Cypress Semiconductor at www.cypress.com <ttdrivers@cypress.com>
|
||||
*/
|
||||
|
||||
#include <linux/device.h>
|
||||
#include <linux/export.h>
|
||||
#include <linux/i2c.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/types.h>
|
||||
|
||||
#include "cyttsp4_core.h"
|
||||
|
||||
int cyttsp_i2c_read_block_data(struct device *dev, u8 *xfer_buf,
|
||||
u16 addr, u8 length, void *values)
|
||||
{
|
||||
struct i2c_client *client = to_i2c_client(dev);
|
||||
u8 client_addr = client->addr | ((addr >> 8) & 0x1);
|
||||
u8 addr_lo = addr & 0xFF;
|
||||
struct i2c_msg msgs[] = {
|
||||
{
|
||||
.addr = client_addr,
|
||||
.flags = 0,
|
||||
.len = 1,
|
||||
.buf = &addr_lo,
|
||||
},
|
||||
{
|
||||
.addr = client_addr,
|
||||
.flags = I2C_M_RD,
|
||||
.len = length,
|
||||
.buf = values,
|
||||
},
|
||||
};
|
||||
int retval;
|
||||
|
||||
retval = i2c_transfer(client->adapter, msgs, ARRAY_SIZE(msgs));
|
||||
if (retval < 0)
|
||||
return retval;
|
||||
|
||||
return retval != ARRAY_SIZE(msgs) ? -EIO : 0;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(cyttsp_i2c_read_block_data);
|
||||
|
||||
int cyttsp_i2c_write_block_data(struct device *dev, u8 *xfer_buf,
|
||||
u16 addr, u8 length, const void *values)
|
||||
{
|
||||
struct i2c_client *client = to_i2c_client(dev);
|
||||
u8 client_addr = client->addr | ((addr >> 8) & 0x1);
|
||||
u8 addr_lo = addr & 0xFF;
|
||||
struct i2c_msg msgs[] = {
|
||||
{
|
||||
.addr = client_addr,
|
||||
.flags = 0,
|
||||
.len = length + 1,
|
||||
.buf = xfer_buf,
|
||||
},
|
||||
};
|
||||
int retval;
|
||||
|
||||
xfer_buf[0] = addr_lo;
|
||||
memcpy(&xfer_buf[1], values, length);
|
||||
|
||||
retval = i2c_transfer(client->adapter, msgs, ARRAY_SIZE(msgs));
|
||||
if (retval < 0)
|
||||
return retval;
|
||||
|
||||
return retval != ARRAY_SIZE(msgs) ? -EIO : 0;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(cyttsp_i2c_write_block_data);
|
||||
|
||||
|
||||
MODULE_DESCRIPTION("Cypress TrueTouch(TM) Standard Product (TTSP) I2C touchscreen driver");
|
||||
MODULE_LICENSE("GPL");
|
||||
MODULE_AUTHOR("Cypress");
|
@ -20,5 +20,6 @@ int goodix_berlin_probe(struct device *dev, int irq, const struct input_id *id,
|
||||
struct regmap *regmap);
|
||||
|
||||
extern const struct dev_pm_ops goodix_berlin_pm_ops;
|
||||
extern const struct attribute_group *goodix_berlin_groups[];
|
||||
|
||||
#endif
|
||||
|
@ -672,6 +672,49 @@ static void goodix_berlin_power_off_act(void *data)
|
||||
goodix_berlin_power_off(cd);
|
||||
}
|
||||
|
||||
static ssize_t registers_read(struct file *filp, struct kobject *kobj,
|
||||
struct bin_attribute *bin_attr,
|
||||
char *buf, loff_t off, size_t count)
|
||||
{
|
||||
struct device *dev = kobj_to_dev(kobj);
|
||||
struct goodix_berlin_core *cd = dev_get_drvdata(dev);
|
||||
int error;
|
||||
|
||||
error = regmap_raw_read(cd->regmap, off, buf, count);
|
||||
|
||||
return error ? error : count;
|
||||
}
|
||||
|
||||
static ssize_t registers_write(struct file *filp, struct kobject *kobj,
|
||||
struct bin_attribute *bin_attr,
|
||||
char *buf, loff_t off, size_t count)
|
||||
{
|
||||
struct device *dev = kobj_to_dev(kobj);
|
||||
struct goodix_berlin_core *cd = dev_get_drvdata(dev);
|
||||
int error;
|
||||
|
||||
error = regmap_raw_write(cd->regmap, off, buf, count);
|
||||
|
||||
return error ? error : count;
|
||||
}
|
||||
|
||||
static BIN_ATTR_ADMIN_RW(registers, 0);
|
||||
|
||||
static struct bin_attribute *goodix_berlin_bin_attrs[] = {
|
||||
&bin_attr_registers,
|
||||
NULL,
|
||||
};
|
||||
|
||||
static const struct attribute_group goodix_berlin_attr_group = {
|
||||
.bin_attrs = goodix_berlin_bin_attrs,
|
||||
};
|
||||
|
||||
const struct attribute_group *goodix_berlin_groups[] = {
|
||||
&goodix_berlin_attr_group,
|
||||
NULL,
|
||||
};
|
||||
EXPORT_SYMBOL_GPL(goodix_berlin_groups);
|
||||
|
||||
int goodix_berlin_probe(struct device *dev, int irq, const struct input_id *id,
|
||||
struct regmap *regmap)
|
||||
{
|
||||
|
@ -64,6 +64,7 @@ static struct i2c_driver goodix_berlin_i2c_driver = {
|
||||
.name = "goodix-berlin-i2c",
|
||||
.of_match_table = goodix_berlin_i2c_of_match,
|
||||
.pm = pm_sleep_ptr(&goodix_berlin_pm_ops),
|
||||
.dev_groups = goodix_berlin_groups,
|
||||
},
|
||||
.probe = goodix_berlin_i2c_probe,
|
||||
.id_table = goodix_berlin_i2c_id,
|
||||
|
@ -169,6 +169,7 @@ static struct spi_driver goodix_berlin_spi_driver = {
|
||||
.name = "goodix-berlin-spi",
|
||||
.of_match_table = goodix_berlin_spi_of_match,
|
||||
.pm = pm_sleep_ptr(&goodix_berlin_pm_ops),
|
||||
.dev_groups = goodix_berlin_groups,
|
||||
},
|
||||
.probe = goodix_berlin_spi_probe,
|
||||
.id_table = goodix_berlin_spi_ids,
|
||||
|
@ -470,7 +470,7 @@ static const struct hynitron_ts_chip_data cst3xx_data = {
|
||||
};
|
||||
|
||||
static const struct i2c_device_id hyn_tpd_id[] = {
|
||||
{ .name = "hynitron_ts", 0 },
|
||||
{ .name = "hynitron_ts" },
|
||||
{ /* sentinel */ },
|
||||
};
|
||||
MODULE_DEVICE_TABLE(i2c, hyn_tpd_id);
|
||||
|
@ -15,7 +15,6 @@
|
||||
#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>
|
||||
@ -37,6 +36,8 @@
|
||||
#define ILITEK_TP_CMD_GET_MCU_VER 0x61
|
||||
#define ILITEK_TP_CMD_GET_IC_MODE 0xC0
|
||||
|
||||
#define ILITEK_TP_I2C_REPORT_ID 0x48
|
||||
|
||||
#define REPORT_COUNT_ADDRESS 61
|
||||
#define ILITEK_SUPPORT_MAX_POINT 40
|
||||
|
||||
@ -160,15 +161,19 @@ static int ilitek_process_and_report_v6(struct ilitek_ts_data *ts)
|
||||
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;
|
||||
return error;
|
||||
}
|
||||
|
||||
if (buf[0] != ILITEK_TP_I2C_REPORT_ID) {
|
||||
dev_err(dev, "get touch info failed. Wrong id: 0x%02X\n", buf[0]);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
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;
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
count = DIV_ROUND_UP(report_max_point, packet_max_point);
|
||||
@ -178,7 +183,7 @@ static int ilitek_process_and_report_v6(struct ilitek_ts_data *ts)
|
||||
if (error) {
|
||||
dev_err(dev, "get touch info. failed, cnt:%d, err:%d\n",
|
||||
count, error);
|
||||
goto err_sync_frame;
|
||||
return error;
|
||||
}
|
||||
}
|
||||
|
||||
@ -203,10 +208,10 @@ static int ilitek_process_and_report_v6(struct ilitek_ts_data *ts)
|
||||
ilitek_touch_down(ts, id, x, y);
|
||||
}
|
||||
|
||||
err_sync_frame:
|
||||
input_mt_sync_frame(input);
|
||||
input_sync(input);
|
||||
return error;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* APIs of cmds for ILITEK Touch IC */
|
||||
|
@ -1,288 +0,0 @@
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
/*
|
||||
* mcs5000_ts.c - Touchscreen driver for MELFAS MCS-5000 controller
|
||||
*
|
||||
* Copyright (C) 2009 Samsung Electronics Co.Ltd
|
||||
* Author: Joonyoung Shim <jy0922.shim@samsung.com>
|
||||
*
|
||||
* Based on wm97xx-core.c
|
||||
*/
|
||||
|
||||
#include <linux/module.h>
|
||||
#include <linux/i2c.h>
|
||||
#include <linux/interrupt.h>
|
||||
#include <linux/input.h>
|
||||
#include <linux/irq.h>
|
||||
#include <linux/platform_data/mcs.h>
|
||||
#include <linux/slab.h>
|
||||
|
||||
/* Registers */
|
||||
#define MCS5000_TS_STATUS 0x00
|
||||
#define STATUS_OFFSET 0
|
||||
#define STATUS_NO (0 << STATUS_OFFSET)
|
||||
#define STATUS_INIT (1 << STATUS_OFFSET)
|
||||
#define STATUS_SENSING (2 << STATUS_OFFSET)
|
||||
#define STATUS_COORD (3 << STATUS_OFFSET)
|
||||
#define STATUS_GESTURE (4 << STATUS_OFFSET)
|
||||
#define ERROR_OFFSET 4
|
||||
#define ERROR_NO (0 << ERROR_OFFSET)
|
||||
#define ERROR_POWER_ON_RESET (1 << ERROR_OFFSET)
|
||||
#define ERROR_INT_RESET (2 << ERROR_OFFSET)
|
||||
#define ERROR_EXT_RESET (3 << ERROR_OFFSET)
|
||||
#define ERROR_INVALID_REG_ADDRESS (8 << ERROR_OFFSET)
|
||||
#define ERROR_INVALID_REG_VALUE (9 << ERROR_OFFSET)
|
||||
|
||||
#define MCS5000_TS_OP_MODE 0x01
|
||||
#define RESET_OFFSET 0
|
||||
#define RESET_NO (0 << RESET_OFFSET)
|
||||
#define RESET_EXT_SOFT (1 << RESET_OFFSET)
|
||||
#define OP_MODE_OFFSET 1
|
||||
#define OP_MODE_SLEEP (0 << OP_MODE_OFFSET)
|
||||
#define OP_MODE_ACTIVE (1 << OP_MODE_OFFSET)
|
||||
#define GESTURE_OFFSET 4
|
||||
#define GESTURE_DISABLE (0 << GESTURE_OFFSET)
|
||||
#define GESTURE_ENABLE (1 << GESTURE_OFFSET)
|
||||
#define PROXIMITY_OFFSET 5
|
||||
#define PROXIMITY_DISABLE (0 << PROXIMITY_OFFSET)
|
||||
#define PROXIMITY_ENABLE (1 << PROXIMITY_OFFSET)
|
||||
#define SCAN_MODE_OFFSET 6
|
||||
#define SCAN_MODE_INTERRUPT (0 << SCAN_MODE_OFFSET)
|
||||
#define SCAN_MODE_POLLING (1 << SCAN_MODE_OFFSET)
|
||||
#define REPORT_RATE_OFFSET 7
|
||||
#define REPORT_RATE_40 (0 << REPORT_RATE_OFFSET)
|
||||
#define REPORT_RATE_80 (1 << REPORT_RATE_OFFSET)
|
||||
|
||||
#define MCS5000_TS_SENS_CTL 0x02
|
||||
#define MCS5000_TS_FILTER_CTL 0x03
|
||||
#define PRI_FILTER_OFFSET 0
|
||||
#define SEC_FILTER_OFFSET 4
|
||||
|
||||
#define MCS5000_TS_X_SIZE_UPPER 0x08
|
||||
#define MCS5000_TS_X_SIZE_LOWER 0x09
|
||||
#define MCS5000_TS_Y_SIZE_UPPER 0x0A
|
||||
#define MCS5000_TS_Y_SIZE_LOWER 0x0B
|
||||
|
||||
#define MCS5000_TS_INPUT_INFO 0x10
|
||||
#define INPUT_TYPE_OFFSET 0
|
||||
#define INPUT_TYPE_NONTOUCH (0 << INPUT_TYPE_OFFSET)
|
||||
#define INPUT_TYPE_SINGLE (1 << INPUT_TYPE_OFFSET)
|
||||
#define INPUT_TYPE_DUAL (2 << INPUT_TYPE_OFFSET)
|
||||
#define INPUT_TYPE_PALM (3 << INPUT_TYPE_OFFSET)
|
||||
#define INPUT_TYPE_PROXIMITY (7 << INPUT_TYPE_OFFSET)
|
||||
#define GESTURE_CODE_OFFSET 3
|
||||
#define GESTURE_CODE_NO (0 << GESTURE_CODE_OFFSET)
|
||||
|
||||
#define MCS5000_TS_X_POS_UPPER 0x11
|
||||
#define MCS5000_TS_X_POS_LOWER 0x12
|
||||
#define MCS5000_TS_Y_POS_UPPER 0x13
|
||||
#define MCS5000_TS_Y_POS_LOWER 0x14
|
||||
#define MCS5000_TS_Z_POS 0x15
|
||||
#define MCS5000_TS_WIDTH 0x16
|
||||
#define MCS5000_TS_GESTURE_VAL 0x17
|
||||
#define MCS5000_TS_MODULE_REV 0x20
|
||||
#define MCS5000_TS_FIRMWARE_VER 0x21
|
||||
|
||||
/* Touchscreen absolute values */
|
||||
#define MCS5000_MAX_XC 0x3ff
|
||||
#define MCS5000_MAX_YC 0x3ff
|
||||
|
||||
enum mcs5000_ts_read_offset {
|
||||
READ_INPUT_INFO,
|
||||
READ_X_POS_UPPER,
|
||||
READ_X_POS_LOWER,
|
||||
READ_Y_POS_UPPER,
|
||||
READ_Y_POS_LOWER,
|
||||
READ_BLOCK_SIZE,
|
||||
};
|
||||
|
||||
/* Each client has this additional data */
|
||||
struct mcs5000_ts_data {
|
||||
struct i2c_client *client;
|
||||
struct input_dev *input_dev;
|
||||
const struct mcs_platform_data *platform_data;
|
||||
};
|
||||
|
||||
static irqreturn_t mcs5000_ts_interrupt(int irq, void *dev_id)
|
||||
{
|
||||
struct mcs5000_ts_data *data = dev_id;
|
||||
struct i2c_client *client = data->client;
|
||||
u8 buffer[READ_BLOCK_SIZE];
|
||||
int err;
|
||||
int x;
|
||||
int y;
|
||||
|
||||
err = i2c_smbus_read_i2c_block_data(client, MCS5000_TS_INPUT_INFO,
|
||||
READ_BLOCK_SIZE, buffer);
|
||||
if (err < 0) {
|
||||
dev_err(&client->dev, "%s, err[%d]\n", __func__, err);
|
||||
goto out;
|
||||
}
|
||||
|
||||
switch (buffer[READ_INPUT_INFO]) {
|
||||
case INPUT_TYPE_NONTOUCH:
|
||||
input_report_key(data->input_dev, BTN_TOUCH, 0);
|
||||
input_sync(data->input_dev);
|
||||
break;
|
||||
|
||||
case INPUT_TYPE_SINGLE:
|
||||
x = (buffer[READ_X_POS_UPPER] << 8) | buffer[READ_X_POS_LOWER];
|
||||
y = (buffer[READ_Y_POS_UPPER] << 8) | buffer[READ_Y_POS_LOWER];
|
||||
|
||||
input_report_key(data->input_dev, BTN_TOUCH, 1);
|
||||
input_report_abs(data->input_dev, ABS_X, x);
|
||||
input_report_abs(data->input_dev, ABS_Y, y);
|
||||
input_sync(data->input_dev);
|
||||
break;
|
||||
|
||||
case INPUT_TYPE_DUAL:
|
||||
/* TODO */
|
||||
break;
|
||||
|
||||
case INPUT_TYPE_PALM:
|
||||
/* TODO */
|
||||
break;
|
||||
|
||||
case INPUT_TYPE_PROXIMITY:
|
||||
/* TODO */
|
||||
break;
|
||||
|
||||
default:
|
||||
dev_err(&client->dev, "Unknown ts input type %d\n",
|
||||
buffer[READ_INPUT_INFO]);
|
||||
break;
|
||||
}
|
||||
|
||||
out:
|
||||
return IRQ_HANDLED;
|
||||
}
|
||||
|
||||
static void mcs5000_ts_phys_init(struct mcs5000_ts_data *data,
|
||||
const struct mcs_platform_data *platform_data)
|
||||
{
|
||||
struct i2c_client *client = data->client;
|
||||
|
||||
/* Touch reset & sleep mode */
|
||||
i2c_smbus_write_byte_data(client, MCS5000_TS_OP_MODE,
|
||||
RESET_EXT_SOFT | OP_MODE_SLEEP);
|
||||
|
||||
/* Touch size */
|
||||
i2c_smbus_write_byte_data(client, MCS5000_TS_X_SIZE_UPPER,
|
||||
platform_data->x_size >> 8);
|
||||
i2c_smbus_write_byte_data(client, MCS5000_TS_X_SIZE_LOWER,
|
||||
platform_data->x_size & 0xff);
|
||||
i2c_smbus_write_byte_data(client, MCS5000_TS_Y_SIZE_UPPER,
|
||||
platform_data->y_size >> 8);
|
||||
i2c_smbus_write_byte_data(client, MCS5000_TS_Y_SIZE_LOWER,
|
||||
platform_data->y_size & 0xff);
|
||||
|
||||
/* Touch active mode & 80 report rate */
|
||||
i2c_smbus_write_byte_data(data->client, MCS5000_TS_OP_MODE,
|
||||
OP_MODE_ACTIVE | REPORT_RATE_80);
|
||||
}
|
||||
|
||||
static int mcs5000_ts_probe(struct i2c_client *client)
|
||||
{
|
||||
const struct mcs_platform_data *pdata;
|
||||
struct mcs5000_ts_data *data;
|
||||
struct input_dev *input_dev;
|
||||
int error;
|
||||
|
||||
pdata = dev_get_platdata(&client->dev);
|
||||
if (!pdata)
|
||||
return -EINVAL;
|
||||
|
||||
data = devm_kzalloc(&client->dev, sizeof(*data), GFP_KERNEL);
|
||||
if (!data) {
|
||||
dev_err(&client->dev, "Failed to allocate memory\n");
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
data->client = client;
|
||||
|
||||
input_dev = devm_input_allocate_device(&client->dev);
|
||||
if (!input_dev) {
|
||||
dev_err(&client->dev, "Failed to allocate input device\n");
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
input_dev->name = "MELFAS MCS-5000 Touchscreen";
|
||||
input_dev->id.bustype = BUS_I2C;
|
||||
input_dev->dev.parent = &client->dev;
|
||||
|
||||
__set_bit(EV_ABS, input_dev->evbit);
|
||||
__set_bit(EV_KEY, input_dev->evbit);
|
||||
__set_bit(BTN_TOUCH, input_dev->keybit);
|
||||
input_set_abs_params(input_dev, ABS_X, 0, MCS5000_MAX_XC, 0, 0);
|
||||
input_set_abs_params(input_dev, ABS_Y, 0, MCS5000_MAX_YC, 0, 0);
|
||||
|
||||
data->input_dev = input_dev;
|
||||
|
||||
if (pdata->cfg_pin)
|
||||
pdata->cfg_pin();
|
||||
|
||||
error = devm_request_threaded_irq(&client->dev, client->irq,
|
||||
NULL, mcs5000_ts_interrupt,
|
||||
IRQF_TRIGGER_LOW | IRQF_ONESHOT,
|
||||
"mcs5000_ts", data);
|
||||
if (error) {
|
||||
dev_err(&client->dev, "Failed to register interrupt\n");
|
||||
return error;
|
||||
}
|
||||
|
||||
error = input_register_device(data->input_dev);
|
||||
if (error) {
|
||||
dev_err(&client->dev, "Failed to register input device\n");
|
||||
return error;
|
||||
}
|
||||
|
||||
mcs5000_ts_phys_init(data, pdata);
|
||||
i2c_set_clientdata(client, data);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int mcs5000_ts_suspend(struct device *dev)
|
||||
{
|
||||
struct i2c_client *client = to_i2c_client(dev);
|
||||
|
||||
/* Touch sleep mode */
|
||||
i2c_smbus_write_byte_data(client, MCS5000_TS_OP_MODE, OP_MODE_SLEEP);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int mcs5000_ts_resume(struct device *dev)
|
||||
{
|
||||
struct i2c_client *client = to_i2c_client(dev);
|
||||
struct mcs5000_ts_data *data = i2c_get_clientdata(client);
|
||||
const struct mcs_platform_data *pdata = dev_get_platdata(dev);
|
||||
|
||||
mcs5000_ts_phys_init(data, pdata);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static DEFINE_SIMPLE_DEV_PM_OPS(mcs5000_ts_pm,
|
||||
mcs5000_ts_suspend, mcs5000_ts_resume);
|
||||
|
||||
static const struct i2c_device_id mcs5000_ts_id[] = {
|
||||
{ "mcs5000_ts" },
|
||||
{ }
|
||||
};
|
||||
MODULE_DEVICE_TABLE(i2c, mcs5000_ts_id);
|
||||
|
||||
static struct i2c_driver mcs5000_ts_driver = {
|
||||
.probe = mcs5000_ts_probe,
|
||||
.driver = {
|
||||
.name = "mcs5000_ts",
|
||||
.pm = pm_sleep_ptr(&mcs5000_ts_pm),
|
||||
},
|
||||
.id_table = mcs5000_ts_id,
|
||||
};
|
||||
|
||||
module_i2c_driver(mcs5000_ts_driver);
|
||||
|
||||
/* Module information */
|
||||
MODULE_AUTHOR("Joonyoung Shim <jy0922.shim@samsung.com>");
|
||||
MODULE_DESCRIPTION("Touchscreen driver for MELFAS MCS-5000 controller");
|
||||
MODULE_LICENSE("GPL");
|
@ -42,11 +42,6 @@ static int tsc2004_probe(struct i2c_client *i2c)
|
||||
tsc2004_cmd);
|
||||
}
|
||||
|
||||
static void tsc2004_remove(struct i2c_client *i2c)
|
||||
{
|
||||
tsc200x_remove(&i2c->dev);
|
||||
}
|
||||
|
||||
static const struct i2c_device_id tsc2004_idtable[] = {
|
||||
{ "tsc2004" },
|
||||
{ }
|
||||
@ -70,7 +65,6 @@ static struct i2c_driver tsc2004_driver = {
|
||||
},
|
||||
.id_table = tsc2004_idtable,
|
||||
.probe = tsc2004_probe,
|
||||
.remove = tsc2004_remove,
|
||||
};
|
||||
module_i2c_driver(tsc2004_driver);
|
||||
|
||||
|
@ -64,11 +64,6 @@ static int tsc2005_probe(struct spi_device *spi)
|
||||
tsc2005_cmd);
|
||||
}
|
||||
|
||||
static void tsc2005_remove(struct spi_device *spi)
|
||||
{
|
||||
tsc200x_remove(&spi->dev);
|
||||
}
|
||||
|
||||
#ifdef CONFIG_OF
|
||||
static const struct of_device_id tsc2005_of_match[] = {
|
||||
{ .compatible = "ti,tsc2005" },
|
||||
@ -85,7 +80,6 @@ static struct spi_driver tsc2005_driver = {
|
||||
.pm = pm_sleep_ptr(&tsc200x_pm_ops),
|
||||
},
|
||||
.probe = tsc2005_probe,
|
||||
.remove = tsc2005_remove,
|
||||
};
|
||||
module_spi_driver(tsc2005_driver);
|
||||
|
||||
|
@ -104,11 +104,11 @@ struct tsc200x {
|
||||
|
||||
bool pen_down;
|
||||
|
||||
struct regulator *vio;
|
||||
|
||||
struct gpio_desc *reset_gpio;
|
||||
int (*tsc200x_cmd)(struct device *dev, u8 cmd);
|
||||
|
||||
int irq;
|
||||
bool wake_irq_enabled;
|
||||
};
|
||||
|
||||
static void tsc200x_update_pen_state(struct tsc200x *ts,
|
||||
@ -136,7 +136,6 @@ static void tsc200x_update_pen_state(struct tsc200x *ts,
|
||||
static irqreturn_t tsc200x_irq_thread(int irq, void *_ts)
|
||||
{
|
||||
struct tsc200x *ts = _ts;
|
||||
unsigned long flags;
|
||||
unsigned int pressure;
|
||||
struct tsc200x_data tsdata;
|
||||
int error;
|
||||
@ -182,13 +181,11 @@ static irqreturn_t tsc200x_irq_thread(int irq, void *_ts)
|
||||
if (unlikely(pressure > MAX_12BIT))
|
||||
goto out;
|
||||
|
||||
spin_lock_irqsave(&ts->lock, flags);
|
||||
|
||||
tsc200x_update_pen_state(ts, tsdata.x, tsdata.y, pressure);
|
||||
mod_timer(&ts->penup_timer,
|
||||
jiffies + msecs_to_jiffies(TSC200X_PENUP_TIME_MS));
|
||||
|
||||
spin_unlock_irqrestore(&ts->lock, flags);
|
||||
scoped_guard(spinlock_irqsave, &ts->lock) {
|
||||
tsc200x_update_pen_state(ts, tsdata.x, tsdata.y, pressure);
|
||||
mod_timer(&ts->penup_timer,
|
||||
jiffies + msecs_to_jiffies(TSC200X_PENUP_TIME_MS));
|
||||
}
|
||||
|
||||
ts->last_valid_interrupt = jiffies;
|
||||
out:
|
||||
@ -198,11 +195,9 @@ out:
|
||||
static void tsc200x_penup_timer(struct timer_list *t)
|
||||
{
|
||||
struct tsc200x *ts = from_timer(ts, t, penup_timer);
|
||||
unsigned long flags;
|
||||
|
||||
spin_lock_irqsave(&ts->lock, flags);
|
||||
guard(spinlock_irqsave)(&ts->lock);
|
||||
tsc200x_update_pen_state(ts, 0, 0, 0);
|
||||
spin_unlock_irqrestore(&ts->lock, flags);
|
||||
}
|
||||
|
||||
static void tsc200x_start_scan(struct tsc200x *ts)
|
||||
@ -232,12 +227,10 @@ static void __tsc200x_disable(struct tsc200x *ts)
|
||||
{
|
||||
tsc200x_stop_scan(ts);
|
||||
|
||||
disable_irq(ts->irq);
|
||||
guard(disable_irq)(&ts->irq);
|
||||
|
||||
del_timer_sync(&ts->penup_timer);
|
||||
|
||||
cancel_delayed_work_sync(&ts->esd_work);
|
||||
|
||||
enable_irq(ts->irq);
|
||||
}
|
||||
|
||||
/* must be called with ts->mutex held */
|
||||
@ -253,80 +246,79 @@ static void __tsc200x_enable(struct tsc200x *ts)
|
||||
}
|
||||
}
|
||||
|
||||
static ssize_t tsc200x_selftest_show(struct device *dev,
|
||||
struct device_attribute *attr,
|
||||
char *buf)
|
||||
/*
|
||||
* Test TSC200X communications via temp high register.
|
||||
*/
|
||||
static int tsc200x_do_selftest(struct tsc200x *ts)
|
||||
{
|
||||
struct tsc200x *ts = dev_get_drvdata(dev);
|
||||
unsigned int temp_high;
|
||||
unsigned int temp_high_orig;
|
||||
unsigned int temp_high_test;
|
||||
bool success = true;
|
||||
unsigned int temp_high;
|
||||
int error;
|
||||
|
||||
mutex_lock(&ts->mutex);
|
||||
|
||||
/*
|
||||
* Test TSC200X communications via temp high register.
|
||||
*/
|
||||
__tsc200x_disable(ts);
|
||||
|
||||
error = regmap_read(ts->regmap, TSC200X_REG_TEMP_HIGH, &temp_high_orig);
|
||||
if (error) {
|
||||
dev_warn(dev, "selftest failed: read error %d\n", error);
|
||||
success = false;
|
||||
goto out;
|
||||
dev_warn(ts->dev, "selftest failed: read error %d\n", error);
|
||||
return error;
|
||||
}
|
||||
|
||||
temp_high_test = (temp_high_orig - 1) & MAX_12BIT;
|
||||
|
||||
error = regmap_write(ts->regmap, TSC200X_REG_TEMP_HIGH, temp_high_test);
|
||||
if (error) {
|
||||
dev_warn(dev, "selftest failed: write error %d\n", error);
|
||||
success = false;
|
||||
goto out;
|
||||
dev_warn(ts->dev, "selftest failed: write error %d\n", error);
|
||||
return error;
|
||||
}
|
||||
|
||||
error = regmap_read(ts->regmap, TSC200X_REG_TEMP_HIGH, &temp_high);
|
||||
if (error) {
|
||||
dev_warn(dev, "selftest failed: read error %d after write\n",
|
||||
error);
|
||||
success = false;
|
||||
goto out;
|
||||
}
|
||||
|
||||
if (temp_high != temp_high_test) {
|
||||
dev_warn(dev, "selftest failed: %d != %d\n",
|
||||
temp_high, temp_high_test);
|
||||
success = false;
|
||||
dev_warn(ts->dev,
|
||||
"selftest failed: read error %d after write\n", error);
|
||||
return error;
|
||||
}
|
||||
|
||||
/* hardware reset */
|
||||
tsc200x_reset(ts);
|
||||
|
||||
if (!success)
|
||||
goto out;
|
||||
if (temp_high != temp_high_test) {
|
||||
dev_warn(ts->dev, "selftest failed: %d != %d\n",
|
||||
temp_high, temp_high_test);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
/* test that the reset really happened */
|
||||
error = regmap_read(ts->regmap, TSC200X_REG_TEMP_HIGH, &temp_high);
|
||||
if (error) {
|
||||
dev_warn(dev, "selftest failed: read error %d after reset\n",
|
||||
error);
|
||||
success = false;
|
||||
goto out;
|
||||
dev_warn(ts->dev,
|
||||
"selftest failed: read error %d after reset\n", error);
|
||||
return error;
|
||||
}
|
||||
|
||||
if (temp_high != temp_high_orig) {
|
||||
dev_warn(dev, "selftest failed after reset: %d != %d\n",
|
||||
dev_warn(ts->dev, "selftest failed after reset: %d != %d\n",
|
||||
temp_high, temp_high_orig);
|
||||
success = false;
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
out:
|
||||
__tsc200x_enable(ts);
|
||||
mutex_unlock(&ts->mutex);
|
||||
return 0;
|
||||
}
|
||||
|
||||
return sprintf(buf, "%d\n", success);
|
||||
static ssize_t tsc200x_selftest_show(struct device *dev,
|
||||
struct device_attribute *attr,
|
||||
char *buf)
|
||||
{
|
||||
struct tsc200x *ts = dev_get_drvdata(dev);
|
||||
int error;
|
||||
|
||||
scoped_guard(mutex, &ts->mutex) {
|
||||
__tsc200x_disable(ts);
|
||||
|
||||
error = tsc200x_do_selftest(ts);
|
||||
|
||||
__tsc200x_enable(ts);
|
||||
}
|
||||
|
||||
return sprintf(buf, "%d\n", !error);
|
||||
}
|
||||
|
||||
static DEVICE_ATTR(selftest, S_IRUGO, tsc200x_selftest_show, NULL);
|
||||
@ -368,46 +360,42 @@ static void tsc200x_esd_work(struct work_struct *work)
|
||||
int error;
|
||||
unsigned int r;
|
||||
|
||||
if (!mutex_trylock(&ts->mutex)) {
|
||||
/*
|
||||
* If the mutex is taken, it means that disable or enable is in
|
||||
* progress. In that case just reschedule the work. If the work
|
||||
* is not needed, it will be canceled by disable.
|
||||
*/
|
||||
goto reschedule;
|
||||
}
|
||||
|
||||
if (time_is_after_jiffies(ts->last_valid_interrupt +
|
||||
msecs_to_jiffies(ts->esd_timeout)))
|
||||
goto out;
|
||||
|
||||
/* We should be able to read register without disabling interrupts. */
|
||||
error = regmap_read(ts->regmap, TSC200X_REG_CFR0, &r);
|
||||
if (!error &&
|
||||
!((r ^ TSC200X_CFR0_INITVALUE) & TSC200X_CFR0_RW_MASK)) {
|
||||
goto out;
|
||||
}
|
||||
|
||||
/*
|
||||
* If we could not read our known value from configuration register 0
|
||||
* then we should reset the controller as if from power-up and start
|
||||
* scanning again.
|
||||
* If the mutex is taken, it means that disable or enable is in
|
||||
* progress. In that case just reschedule the work. If the work
|
||||
* is not needed, it will be canceled by disable.
|
||||
*/
|
||||
dev_info(ts->dev, "TSC200X not responding - resetting\n");
|
||||
scoped_guard(mutex_try, &ts->mutex) {
|
||||
if (time_is_after_jiffies(ts->last_valid_interrupt +
|
||||
msecs_to_jiffies(ts->esd_timeout)))
|
||||
break;
|
||||
|
||||
disable_irq(ts->irq);
|
||||
del_timer_sync(&ts->penup_timer);
|
||||
/*
|
||||
* We should be able to read register without disabling
|
||||
* interrupts.
|
||||
*/
|
||||
error = regmap_read(ts->regmap, TSC200X_REG_CFR0, &r);
|
||||
if (!error &&
|
||||
!((r ^ TSC200X_CFR0_INITVALUE) & TSC200X_CFR0_RW_MASK)) {
|
||||
break;
|
||||
}
|
||||
|
||||
tsc200x_update_pen_state(ts, 0, 0, 0);
|
||||
/*
|
||||
* If we could not read our known value from configuration
|
||||
* register 0 then we should reset the controller as if from
|
||||
* power-up and start scanning again.
|
||||
*/
|
||||
dev_info(ts->dev, "TSC200X not responding - resetting\n");
|
||||
|
||||
tsc200x_reset(ts);
|
||||
scoped_guard(disable_irq, &ts->irq) {
|
||||
del_timer_sync(&ts->penup_timer);
|
||||
tsc200x_update_pen_state(ts, 0, 0, 0);
|
||||
tsc200x_reset(ts);
|
||||
}
|
||||
|
||||
enable_irq(ts->irq);
|
||||
tsc200x_start_scan(ts);
|
||||
tsc200x_start_scan(ts);
|
||||
}
|
||||
|
||||
out:
|
||||
mutex_unlock(&ts->mutex);
|
||||
reschedule:
|
||||
/* re-arm the watchdog */
|
||||
schedule_delayed_work(&ts->esd_work,
|
||||
round_jiffies_relative(
|
||||
@ -418,15 +406,13 @@ static int tsc200x_open(struct input_dev *input)
|
||||
{
|
||||
struct tsc200x *ts = input_get_drvdata(input);
|
||||
|
||||
mutex_lock(&ts->mutex);
|
||||
guard(mutex)(&ts->mutex);
|
||||
|
||||
if (!ts->suspended)
|
||||
__tsc200x_enable(ts);
|
||||
|
||||
ts->opened = true;
|
||||
|
||||
mutex_unlock(&ts->mutex);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
@ -434,14 +420,12 @@ static void tsc200x_close(struct input_dev *input)
|
||||
{
|
||||
struct tsc200x *ts = input_get_drvdata(input);
|
||||
|
||||
mutex_lock(&ts->mutex);
|
||||
guard(mutex)(&ts->mutex);
|
||||
|
||||
if (!ts->suspended)
|
||||
__tsc200x_disable(ts);
|
||||
|
||||
ts->opened = false;
|
||||
|
||||
mutex_unlock(&ts->mutex);
|
||||
}
|
||||
|
||||
int tsc200x_probe(struct device *dev, int irq, const struct input_id *tsc_id,
|
||||
@ -488,20 +472,6 @@ int tsc200x_probe(struct device *dev, int irq, const struct input_id *tsc_id,
|
||||
&esd_timeout);
|
||||
ts->esd_timeout = error ? 0 : esd_timeout;
|
||||
|
||||
ts->reset_gpio = devm_gpiod_get_optional(dev, "reset", GPIOD_OUT_HIGH);
|
||||
if (IS_ERR(ts->reset_gpio)) {
|
||||
error = PTR_ERR(ts->reset_gpio);
|
||||
dev_err(dev, "error acquiring reset gpio: %d\n", error);
|
||||
return error;
|
||||
}
|
||||
|
||||
ts->vio = devm_regulator_get(dev, "vio");
|
||||
if (IS_ERR(ts->vio)) {
|
||||
error = PTR_ERR(ts->vio);
|
||||
dev_err(dev, "error acquiring vio regulator: %d", error);
|
||||
return error;
|
||||
}
|
||||
|
||||
mutex_init(&ts->mutex);
|
||||
|
||||
spin_lock_init(&ts->lock);
|
||||
@ -542,60 +512,60 @@ int tsc200x_probe(struct device *dev, int irq, const struct input_id *tsc_id,
|
||||
|
||||
touchscreen_parse_properties(input_dev, false, &ts->prop);
|
||||
|
||||
ts->reset_gpio = devm_gpiod_get_optional(dev, "reset", GPIOD_OUT_HIGH);
|
||||
error = PTR_ERR_OR_ZERO(ts->reset_gpio);
|
||||
if (error) {
|
||||
dev_err(dev, "error acquiring reset gpio: %d\n", error);
|
||||
return error;
|
||||
}
|
||||
|
||||
error = devm_regulator_get_enable(dev, "vio");
|
||||
if (error) {
|
||||
dev_err(dev, "error acquiring vio regulator: %d\n", error);
|
||||
return error;
|
||||
}
|
||||
|
||||
tsc200x_reset(ts);
|
||||
|
||||
/* Ensure the touchscreen is off */
|
||||
tsc200x_stop_scan(ts);
|
||||
|
||||
error = devm_request_threaded_irq(dev, irq, NULL,
|
||||
tsc200x_irq_thread,
|
||||
IRQF_TRIGGER_RISING | IRQF_ONESHOT,
|
||||
"tsc200x", ts);
|
||||
error = devm_request_threaded_irq(dev, irq, NULL, tsc200x_irq_thread,
|
||||
IRQF_ONESHOT, "tsc200x", ts);
|
||||
if (error) {
|
||||
dev_err(dev, "Failed to request irq, err: %d\n", error);
|
||||
return error;
|
||||
}
|
||||
|
||||
error = regulator_enable(ts->vio);
|
||||
if (error)
|
||||
return error;
|
||||
|
||||
dev_set_drvdata(dev, ts);
|
||||
|
||||
error = input_register_device(ts->idev);
|
||||
if (error) {
|
||||
dev_err(dev,
|
||||
"Failed to register input device, err: %d\n", error);
|
||||
goto disable_regulator;
|
||||
return error;
|
||||
}
|
||||
|
||||
irq_set_irq_wake(irq, 1);
|
||||
return 0;
|
||||
device_init_wakeup(dev,
|
||||
device_property_read_bool(dev, "wakeup-source"));
|
||||
|
||||
disable_regulator:
|
||||
regulator_disable(ts->vio);
|
||||
return error;
|
||||
return 0;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(tsc200x_probe);
|
||||
|
||||
void tsc200x_remove(struct device *dev)
|
||||
{
|
||||
struct tsc200x *ts = dev_get_drvdata(dev);
|
||||
|
||||
regulator_disable(ts->vio);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(tsc200x_remove);
|
||||
|
||||
static int tsc200x_suspend(struct device *dev)
|
||||
{
|
||||
struct tsc200x *ts = dev_get_drvdata(dev);
|
||||
|
||||
mutex_lock(&ts->mutex);
|
||||
guard(mutex)(&ts->mutex);
|
||||
|
||||
if (!ts->suspended && ts->opened)
|
||||
__tsc200x_disable(ts);
|
||||
|
||||
ts->suspended = true;
|
||||
|
||||
mutex_unlock(&ts->mutex);
|
||||
if (device_may_wakeup(dev))
|
||||
ts->wake_irq_enabled = enable_irq_wake(ts->irq) == 0;
|
||||
|
||||
return 0;
|
||||
}
|
||||
@ -604,15 +574,18 @@ static int tsc200x_resume(struct device *dev)
|
||||
{
|
||||
struct tsc200x *ts = dev_get_drvdata(dev);
|
||||
|
||||
mutex_lock(&ts->mutex);
|
||||
guard(mutex)(&ts->mutex);
|
||||
|
||||
if (ts->wake_irq_enabled) {
|
||||
disable_irq_wake(ts->irq);
|
||||
ts->wake_irq_enabled = false;
|
||||
}
|
||||
|
||||
if (ts->suspended && ts->opened)
|
||||
__tsc200x_enable(ts);
|
||||
|
||||
ts->suspended = false;
|
||||
|
||||
mutex_unlock(&ts->mutex);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
@ -75,6 +75,5 @@ extern const struct attribute_group *tsc200x_groups[];
|
||||
int tsc200x_probe(struct device *dev, int irq, const struct input_id *tsc_id,
|
||||
struct regmap *regmap,
|
||||
int (*tsc200x_cmd)(struct device *dev, u8 cmd));
|
||||
void tsc200x_remove(struct device *dev);
|
||||
|
||||
#endif
|
||||
|
File diff suppressed because it is too large
Load Diff
@ -9,21 +9,20 @@
|
||||
* Author: Pieter Truter<ptruter@intrinsyc.com>
|
||||
*/
|
||||
|
||||
#include <linux/module.h>
|
||||
#include <linux/hrtimer.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/input.h>
|
||||
#include <linux/interrupt.h>
|
||||
#include <linux/i2c.h>
|
||||
#include <linux/delay.h>
|
||||
#include <linux/gpio/consumer.h>
|
||||
#include <linux/device.h>
|
||||
#include <linux/sysfs.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/platform_data/zforce_ts.h>
|
||||
#include <linux/regulator/consumer.h>
|
||||
#include <linux/interrupt.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/of.h>
|
||||
#include <linux/property.h>
|
||||
#include <linux/regulator/consumer.h>
|
||||
#include <linux/slab.h>
|
||||
#include <asm/unaligned.h>
|
||||
|
||||
#define WAIT_TIMEOUT msecs_to_jiffies(1000)
|
||||
|
||||
@ -97,9 +96,7 @@ struct zforce_point {
|
||||
* @suspending in the process of going to suspend (don't emit wakeup
|
||||
* events for commands executed to suspend the device)
|
||||
* @suspended device suspended
|
||||
* @access_mutex serialize i2c-access, to keep multipart reads together
|
||||
* @command_done completion to wait for the command result
|
||||
* @command_mutex serialize commands sent to the ic
|
||||
* @command_waiting the id of the command that is currently waiting
|
||||
* for a result
|
||||
* @command_result returned result of the command
|
||||
@ -108,11 +105,8 @@ struct zforce_ts {
|
||||
struct i2c_client *client;
|
||||
struct input_dev *input;
|
||||
struct touchscreen_properties prop;
|
||||
const struct zforce_ts_platdata *pdata;
|
||||
char phys[32];
|
||||
|
||||
struct regulator *reg_vdd;
|
||||
|
||||
struct gpio_desc *gpio_int;
|
||||
struct gpio_desc *gpio_rst;
|
||||
|
||||
@ -126,10 +120,7 @@ struct zforce_ts {
|
||||
u16 version_build;
|
||||
u16 version_rev;
|
||||
|
||||
struct mutex access_mutex;
|
||||
|
||||
struct completion command_done;
|
||||
struct mutex command_mutex;
|
||||
int command_waiting;
|
||||
int command_result;
|
||||
};
|
||||
@ -146,9 +137,7 @@ static int zforce_command(struct zforce_ts *ts, u8 cmd)
|
||||
buf[1] = 1; /* data size, command only */
|
||||
buf[2] = cmd;
|
||||
|
||||
mutex_lock(&ts->access_mutex);
|
||||
ret = i2c_master_send(client, &buf[0], ARRAY_SIZE(buf));
|
||||
mutex_unlock(&ts->access_mutex);
|
||||
if (ret < 0) {
|
||||
dev_err(&client->dev, "i2c send data request error: %d\n", ret);
|
||||
return ret;
|
||||
@ -157,59 +146,36 @@ static int zforce_command(struct zforce_ts *ts, u8 cmd)
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void zforce_reset_assert(struct zforce_ts *ts)
|
||||
{
|
||||
gpiod_set_value_cansleep(ts->gpio_rst, 1);
|
||||
}
|
||||
|
||||
static void zforce_reset_deassert(struct zforce_ts *ts)
|
||||
{
|
||||
gpiod_set_value_cansleep(ts->gpio_rst, 0);
|
||||
}
|
||||
|
||||
static int zforce_send_wait(struct zforce_ts *ts, const char *buf, int len)
|
||||
{
|
||||
struct i2c_client *client = ts->client;
|
||||
int ret;
|
||||
|
||||
ret = mutex_trylock(&ts->command_mutex);
|
||||
if (!ret) {
|
||||
dev_err(&client->dev, "already waiting for a command\n");
|
||||
return -EBUSY;
|
||||
}
|
||||
|
||||
dev_dbg(&client->dev, "sending %d bytes for command 0x%x\n",
|
||||
buf[1], buf[2]);
|
||||
|
||||
ts->command_waiting = buf[2];
|
||||
|
||||
mutex_lock(&ts->access_mutex);
|
||||
ret = i2c_master_send(client, buf, len);
|
||||
mutex_unlock(&ts->access_mutex);
|
||||
if (ret < 0) {
|
||||
dev_err(&client->dev, "i2c send data request error: %d\n", ret);
|
||||
goto unlock;
|
||||
return ret;
|
||||
}
|
||||
|
||||
dev_dbg(&client->dev, "waiting for result for command 0x%x\n", buf[2]);
|
||||
|
||||
if (wait_for_completion_timeout(&ts->command_done, WAIT_TIMEOUT) == 0) {
|
||||
ret = -ETIME;
|
||||
goto unlock;
|
||||
}
|
||||
if (wait_for_completion_timeout(&ts->command_done, WAIT_TIMEOUT) == 0)
|
||||
return -ETIME;
|
||||
|
||||
ret = ts->command_result;
|
||||
|
||||
unlock:
|
||||
mutex_unlock(&ts->command_mutex);
|
||||
return ret;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int zforce_command_wait(struct zforce_ts *ts, u8 cmd)
|
||||
{
|
||||
struct i2c_client *client = ts->client;
|
||||
char buf[3];
|
||||
int ret;
|
||||
int error;
|
||||
|
||||
dev_dbg(&client->dev, "%s: 0x%x\n", __func__, cmd);
|
||||
|
||||
@ -217,10 +183,11 @@ static int zforce_command_wait(struct zforce_ts *ts, u8 cmd)
|
||||
buf[1] = 1; /* data size, command only */
|
||||
buf[2] = cmd;
|
||||
|
||||
ret = zforce_send_wait(ts, &buf[0], ARRAY_SIZE(buf));
|
||||
if (ret < 0) {
|
||||
dev_err(&client->dev, "i2c send data request error: %d\n", ret);
|
||||
return ret;
|
||||
error = zforce_send_wait(ts, &buf[0], ARRAY_SIZE(buf));
|
||||
if (error) {
|
||||
dev_err(&client->dev, "i2c send data request error: %d\n",
|
||||
error);
|
||||
return error;
|
||||
}
|
||||
|
||||
return 0;
|
||||
@ -268,40 +235,40 @@ static int zforce_setconfig(struct zforce_ts *ts, char b1)
|
||||
static int zforce_start(struct zforce_ts *ts)
|
||||
{
|
||||
struct i2c_client *client = ts->client;
|
||||
int ret;
|
||||
int error;
|
||||
|
||||
dev_dbg(&client->dev, "starting device\n");
|
||||
|
||||
ret = zforce_command_wait(ts, COMMAND_INITIALIZE);
|
||||
if (ret) {
|
||||
dev_err(&client->dev, "Unable to initialize, %d\n", ret);
|
||||
return ret;
|
||||
error = zforce_command_wait(ts, COMMAND_INITIALIZE);
|
||||
if (error) {
|
||||
dev_err(&client->dev, "Unable to initialize, %d\n", error);
|
||||
return error;
|
||||
}
|
||||
|
||||
ret = zforce_resolution(ts, ts->prop.max_x, ts->prop.max_y);
|
||||
if (ret) {
|
||||
dev_err(&client->dev, "Unable to set resolution, %d\n", ret);
|
||||
goto error;
|
||||
error = zforce_resolution(ts, ts->prop.max_x, ts->prop.max_y);
|
||||
if (error) {
|
||||
dev_err(&client->dev, "Unable to set resolution, %d\n", error);
|
||||
goto err_deactivate;
|
||||
}
|
||||
|
||||
ret = zforce_scan_frequency(ts, 10, 50, 50);
|
||||
if (ret) {
|
||||
error = zforce_scan_frequency(ts, 10, 50, 50);
|
||||
if (error) {
|
||||
dev_err(&client->dev, "Unable to set scan frequency, %d\n",
|
||||
ret);
|
||||
goto error;
|
||||
error);
|
||||
goto err_deactivate;
|
||||
}
|
||||
|
||||
ret = zforce_setconfig(ts, SETCONFIG_DUALTOUCH);
|
||||
if (ret) {
|
||||
error = zforce_setconfig(ts, SETCONFIG_DUALTOUCH);
|
||||
if (error) {
|
||||
dev_err(&client->dev, "Unable to set config\n");
|
||||
goto error;
|
||||
goto err_deactivate;
|
||||
}
|
||||
|
||||
/* start sending touch events */
|
||||
ret = zforce_command(ts, COMMAND_DATAREQUEST);
|
||||
if (ret) {
|
||||
error = zforce_command(ts, COMMAND_DATAREQUEST);
|
||||
if (error) {
|
||||
dev_err(&client->dev, "Unable to request data\n");
|
||||
goto error;
|
||||
goto err_deactivate;
|
||||
}
|
||||
|
||||
/*
|
||||
@ -312,24 +279,24 @@ static int zforce_start(struct zforce_ts *ts)
|
||||
|
||||
return 0;
|
||||
|
||||
error:
|
||||
err_deactivate:
|
||||
zforce_command_wait(ts, COMMAND_DEACTIVATE);
|
||||
return ret;
|
||||
return error;
|
||||
}
|
||||
|
||||
static int zforce_stop(struct zforce_ts *ts)
|
||||
{
|
||||
struct i2c_client *client = ts->client;
|
||||
int ret;
|
||||
int error;
|
||||
|
||||
dev_dbg(&client->dev, "stopping device\n");
|
||||
|
||||
/* Deactivates touch sensing and puts the device into sleep. */
|
||||
ret = zforce_command_wait(ts, COMMAND_DEACTIVATE);
|
||||
if (ret != 0) {
|
||||
error = zforce_command_wait(ts, COMMAND_DEACTIVATE);
|
||||
if (error) {
|
||||
dev_err(&client->dev, "could not deactivate device, %d\n",
|
||||
ret);
|
||||
return ret;
|
||||
error);
|
||||
return error;
|
||||
}
|
||||
|
||||
return 0;
|
||||
@ -340,6 +307,7 @@ static int zforce_touch_event(struct zforce_ts *ts, u8 *payload)
|
||||
struct i2c_client *client = ts->client;
|
||||
struct zforce_point point;
|
||||
int count, i, num = 0;
|
||||
u8 *p;
|
||||
|
||||
count = payload[0];
|
||||
if (count > ZFORCE_REPORT_POINTS) {
|
||||
@ -350,10 +318,10 @@ static int zforce_touch_event(struct zforce_ts *ts, u8 *payload)
|
||||
}
|
||||
|
||||
for (i = 0; i < count; i++) {
|
||||
point.coord_x =
|
||||
payload[9 * i + 2] << 8 | payload[9 * i + 1];
|
||||
point.coord_y =
|
||||
payload[9 * i + 4] << 8 | payload[9 * i + 3];
|
||||
p = &payload[i * 9 + 1];
|
||||
|
||||
point.coord_x = get_unaligned_le16(&p[0]);
|
||||
point.coord_y = get_unaligned_le16(&p[2]);
|
||||
|
||||
if (point.coord_x > ts->prop.max_x ||
|
||||
point.coord_y > ts->prop.max_y) {
|
||||
@ -362,18 +330,16 @@ static int zforce_touch_event(struct zforce_ts *ts, u8 *payload)
|
||||
point.coord_x = point.coord_y = 0;
|
||||
}
|
||||
|
||||
point.state = payload[9 * i + 5] & 0x0f;
|
||||
point.id = (payload[9 * i + 5] & 0xf0) >> 4;
|
||||
point.state = p[4] & 0x0f;
|
||||
point.id = (p[4] & 0xf0) >> 4;
|
||||
|
||||
/* determine touch major, minor and orientation */
|
||||
point.area_major = max(payload[9 * i + 6],
|
||||
payload[9 * i + 7]);
|
||||
point.area_minor = min(payload[9 * i + 6],
|
||||
payload[9 * i + 7]);
|
||||
point.orientation = payload[9 * i + 6] > payload[9 * i + 7];
|
||||
point.area_major = max(p[5], p[6]);
|
||||
point.area_minor = min(p[5], p[6]);
|
||||
point.orientation = p[5] > p[6];
|
||||
|
||||
point.pressure = payload[9 * i + 8];
|
||||
point.prblty = payload[9 * i + 9];
|
||||
point.pressure = p[7];
|
||||
point.prblty = p[8];
|
||||
|
||||
dev_dbg(&client->dev,
|
||||
"point %d/%d: state %d, id %d, pressure %d, prblty %d, x %d, y %d, amajor %d, aminor %d, ori %d\n",
|
||||
@ -386,10 +352,8 @@ static int zforce_touch_event(struct zforce_ts *ts, u8 *payload)
|
||||
/* the zforce id starts with "1", so needs to be decreased */
|
||||
input_mt_slot(ts->input, point.id - 1);
|
||||
|
||||
input_mt_report_slot_state(ts->input, MT_TOOL_FINGER,
|
||||
point.state != STATE_UP);
|
||||
|
||||
if (point.state != STATE_UP) {
|
||||
if (input_mt_report_slot_state(ts->input, MT_TOOL_FINGER,
|
||||
point.state != STATE_UP)) {
|
||||
touchscreen_report_pos(ts->input, &ts->prop,
|
||||
point.coord_x, point.coord_y,
|
||||
true);
|
||||
@ -417,41 +381,35 @@ static int zforce_read_packet(struct zforce_ts *ts, u8 *buf)
|
||||
struct i2c_client *client = ts->client;
|
||||
int ret;
|
||||
|
||||
mutex_lock(&ts->access_mutex);
|
||||
|
||||
/* read 2 byte message header */
|
||||
ret = i2c_master_recv(client, buf, 2);
|
||||
if (ret < 0) {
|
||||
dev_err(&client->dev, "error reading header: %d\n", ret);
|
||||
goto unlock;
|
||||
return ret;
|
||||
}
|
||||
|
||||
if (buf[PAYLOAD_HEADER] != FRAME_START) {
|
||||
dev_err(&client->dev, "invalid frame start: %d\n", buf[0]);
|
||||
ret = -EIO;
|
||||
goto unlock;
|
||||
return -EIO;
|
||||
}
|
||||
|
||||
if (buf[PAYLOAD_LENGTH] == 0) {
|
||||
dev_err(&client->dev, "invalid payload length: %d\n",
|
||||
buf[PAYLOAD_LENGTH]);
|
||||
ret = -EIO;
|
||||
goto unlock;
|
||||
return -EIO;
|
||||
}
|
||||
|
||||
/* read the message */
|
||||
ret = i2c_master_recv(client, &buf[PAYLOAD_BODY], buf[PAYLOAD_LENGTH]);
|
||||
if (ret < 0) {
|
||||
dev_err(&client->dev, "error reading payload: %d\n", ret);
|
||||
goto unlock;
|
||||
return ret;
|
||||
}
|
||||
|
||||
dev_dbg(&client->dev, "read %d bytes for response command 0x%x\n",
|
||||
buf[PAYLOAD_LENGTH], buf[PAYLOAD_BODY]);
|
||||
|
||||
unlock:
|
||||
mutex_unlock(&ts->access_mutex);
|
||||
return ret;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void zforce_complete(struct zforce_ts *ts, int cmd, int result)
|
||||
@ -482,9 +440,10 @@ static irqreturn_t zforce_irq_thread(int irq, void *dev_id)
|
||||
{
|
||||
struct zforce_ts *ts = dev_id;
|
||||
struct i2c_client *client = ts->client;
|
||||
int ret;
|
||||
int error;
|
||||
u8 payload_buffer[FRAME_MAXSIZE];
|
||||
u8 *payload;
|
||||
bool suspending;
|
||||
|
||||
/*
|
||||
* When still suspended, return.
|
||||
@ -498,7 +457,8 @@ static irqreturn_t zforce_irq_thread(int irq, void *dev_id)
|
||||
dev_dbg(&client->dev, "handling interrupt\n");
|
||||
|
||||
/* Don't emit wakeup events from commands run by zforce_suspend */
|
||||
if (!ts->suspending && device_may_wakeup(&client->dev))
|
||||
suspending = READ_ONCE(ts->suspending);
|
||||
if (!suspending && device_may_wakeup(&client->dev))
|
||||
pm_stay_awake(&client->dev);
|
||||
|
||||
/*
|
||||
@ -511,10 +471,10 @@ static irqreturn_t zforce_irq_thread(int irq, void *dev_id)
|
||||
* no IRQ any more)
|
||||
*/
|
||||
do {
|
||||
ret = zforce_read_packet(ts, payload_buffer);
|
||||
if (ret < 0) {
|
||||
error = zforce_read_packet(ts, payload_buffer);
|
||||
if (error) {
|
||||
dev_err(&client->dev,
|
||||
"could not read packet, ret: %d\n", ret);
|
||||
"could not read packet, ret: %d\n", error);
|
||||
break;
|
||||
}
|
||||
|
||||
@ -526,7 +486,7 @@ static irqreturn_t zforce_irq_thread(int irq, void *dev_id)
|
||||
* Always report touch-events received while
|
||||
* suspending, when being a wakeup source
|
||||
*/
|
||||
if (ts->suspending && device_may_wakeup(&client->dev))
|
||||
if (suspending && device_may_wakeup(&client->dev))
|
||||
pm_wakeup_event(&client->dev, 500);
|
||||
zforce_touch_event(ts, &payload[RESPONSE_DATA]);
|
||||
break;
|
||||
@ -550,14 +510,15 @@ static irqreturn_t zforce_irq_thread(int irq, void *dev_id)
|
||||
* Version Payload Results
|
||||
* [2:major] [2:minor] [2:build] [2:rev]
|
||||
*/
|
||||
ts->version_major = (payload[RESPONSE_DATA + 1] << 8) |
|
||||
payload[RESPONSE_DATA];
|
||||
ts->version_minor = (payload[RESPONSE_DATA + 3] << 8) |
|
||||
payload[RESPONSE_DATA + 2];
|
||||
ts->version_build = (payload[RESPONSE_DATA + 5] << 8) |
|
||||
payload[RESPONSE_DATA + 4];
|
||||
ts->version_rev = (payload[RESPONSE_DATA + 7] << 8) |
|
||||
payload[RESPONSE_DATA + 6];
|
||||
ts->version_major =
|
||||
get_unaligned_le16(&payload[RESPONSE_DATA]);
|
||||
ts->version_minor =
|
||||
get_unaligned_le16(&payload[RESPONSE_DATA + 2]);
|
||||
ts->version_build =
|
||||
get_unaligned_le16(&payload[RESPONSE_DATA + 4]);
|
||||
ts->version_rev =
|
||||
get_unaligned_le16(&payload[RESPONSE_DATA + 6]);
|
||||
|
||||
dev_dbg(&ts->client->dev,
|
||||
"Firmware Version %04x:%04x %04x:%04x\n",
|
||||
ts->version_major, ts->version_minor,
|
||||
@ -579,7 +540,7 @@ static irqreturn_t zforce_irq_thread(int irq, void *dev_id)
|
||||
}
|
||||
} while (gpiod_get_value_cansleep(ts->gpio_int));
|
||||
|
||||
if (!ts->suspending && device_may_wakeup(&client->dev))
|
||||
if (!suspending && device_may_wakeup(&client->dev))
|
||||
pm_relax(&client->dev);
|
||||
|
||||
dev_dbg(&client->dev, "finished interrupt\n");
|
||||
@ -598,24 +559,20 @@ static void zforce_input_close(struct input_dev *dev)
|
||||
{
|
||||
struct zforce_ts *ts = input_get_drvdata(dev);
|
||||
struct i2c_client *client = ts->client;
|
||||
int ret;
|
||||
int error;
|
||||
|
||||
ret = zforce_stop(ts);
|
||||
if (ret)
|
||||
error = zforce_stop(ts);
|
||||
if (error)
|
||||
dev_warn(&client->dev, "stopping zforce failed\n");
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
static int zforce_suspend(struct device *dev)
|
||||
static int __zforce_suspend(struct zforce_ts *ts)
|
||||
{
|
||||
struct i2c_client *client = to_i2c_client(dev);
|
||||
struct zforce_ts *ts = i2c_get_clientdata(client);
|
||||
struct i2c_client *client = ts->client;
|
||||
struct input_dev *input = ts->input;
|
||||
int ret = 0;
|
||||
int error;
|
||||
|
||||
mutex_lock(&input->mutex);
|
||||
ts->suspending = true;
|
||||
guard(mutex)(&input->mutex);
|
||||
|
||||
/*
|
||||
* When configured as a wakeup source device should always wake
|
||||
@ -626,9 +583,9 @@ static int zforce_suspend(struct device *dev)
|
||||
|
||||
/* Need to start device, if not open, to be a wakeup source. */
|
||||
if (!input_device_enabled(input)) {
|
||||
ret = zforce_start(ts);
|
||||
if (ret)
|
||||
goto unlock;
|
||||
error = zforce_start(ts);
|
||||
if (error)
|
||||
return error;
|
||||
}
|
||||
|
||||
enable_irq_wake(client->irq);
|
||||
@ -636,18 +593,30 @@ static int zforce_suspend(struct device *dev)
|
||||
dev_dbg(&client->dev,
|
||||
"suspend without being a wakeup source\n");
|
||||
|
||||
ret = zforce_stop(ts);
|
||||
if (ret)
|
||||
goto unlock;
|
||||
error = zforce_stop(ts);
|
||||
if (error)
|
||||
return error;
|
||||
|
||||
disable_irq(client->irq);
|
||||
}
|
||||
|
||||
ts->suspended = true;
|
||||
return 0;
|
||||
}
|
||||
|
||||
unlock:
|
||||
ts->suspending = false;
|
||||
mutex_unlock(&input->mutex);
|
||||
static int zforce_suspend(struct device *dev)
|
||||
{
|
||||
struct i2c_client *client = to_i2c_client(dev);
|
||||
struct zforce_ts *ts = i2c_get_clientdata(client);
|
||||
int ret;
|
||||
|
||||
WRITE_ONCE(ts->suspending, true);
|
||||
smp_mb();
|
||||
|
||||
ret = __zforce_suspend(ts);
|
||||
|
||||
smp_mb();
|
||||
WRITE_ONCE(ts->suspending, false);
|
||||
|
||||
return ret;
|
||||
}
|
||||
@ -657,9 +626,9 @@ static int zforce_resume(struct device *dev)
|
||||
struct i2c_client *client = to_i2c_client(dev);
|
||||
struct zforce_ts *ts = i2c_get_clientdata(client);
|
||||
struct input_dev *input = ts->input;
|
||||
int ret = 0;
|
||||
int error;
|
||||
|
||||
mutex_lock(&input->mutex);
|
||||
guard(mutex)(&input->mutex);
|
||||
|
||||
ts->suspended = false;
|
||||
|
||||
@ -670,24 +639,21 @@ static int zforce_resume(struct device *dev)
|
||||
|
||||
/* need to stop device if it was not open on suspend */
|
||||
if (!input_device_enabled(input)) {
|
||||
ret = zforce_stop(ts);
|
||||
if (ret)
|
||||
goto unlock;
|
||||
error = zforce_stop(ts);
|
||||
if (error)
|
||||
return error;
|
||||
}
|
||||
} else if (input_device_enabled(input)) {
|
||||
dev_dbg(&client->dev, "resume without being a wakeup source\n");
|
||||
|
||||
enable_irq(client->irq);
|
||||
|
||||
ret = zforce_start(ts);
|
||||
if (ret < 0)
|
||||
goto unlock;
|
||||
error = zforce_start(ts);
|
||||
if (error)
|
||||
return error;
|
||||
}
|
||||
|
||||
unlock:
|
||||
mutex_unlock(&input->mutex);
|
||||
|
||||
return ret;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static DEFINE_SIMPLE_DEV_PM_OPS(zforce_pm_ops, zforce_suspend, zforce_resume);
|
||||
@ -696,46 +662,27 @@ static void zforce_reset(void *data)
|
||||
{
|
||||
struct zforce_ts *ts = data;
|
||||
|
||||
zforce_reset_assert(ts);
|
||||
|
||||
gpiod_set_value_cansleep(ts->gpio_rst, 1);
|
||||
udelay(10);
|
||||
|
||||
if (!IS_ERR(ts->reg_vdd))
|
||||
regulator_disable(ts->reg_vdd);
|
||||
}
|
||||
|
||||
static struct zforce_ts_platdata *zforce_parse_dt(struct device *dev)
|
||||
static void zforce_ts_parse_legacy_properties(struct zforce_ts *ts)
|
||||
{
|
||||
struct zforce_ts_platdata *pdata;
|
||||
struct device_node *np = dev->of_node;
|
||||
u32 x_max = 0;
|
||||
u32 y_max = 0;
|
||||
|
||||
if (!np)
|
||||
return ERR_PTR(-ENOENT);
|
||||
device_property_read_u32(&ts->client->dev, "x-size", &x_max);
|
||||
input_set_abs_params(ts->input, ABS_MT_POSITION_X, 0, x_max, 0, 0);
|
||||
|
||||
pdata = devm_kzalloc(dev, sizeof(*pdata), GFP_KERNEL);
|
||||
if (!pdata) {
|
||||
dev_err(dev, "failed to allocate platform data\n");
|
||||
return ERR_PTR(-ENOMEM);
|
||||
}
|
||||
|
||||
of_property_read_u32(np, "x-size", &pdata->x_max);
|
||||
of_property_read_u32(np, "y-size", &pdata->y_max);
|
||||
|
||||
return pdata;
|
||||
device_property_read_u32(&ts->client->dev, "y-size", &y_max);
|
||||
input_set_abs_params(ts->input, ABS_MT_POSITION_Y, 0, y_max, 0, 0);
|
||||
}
|
||||
|
||||
static int zforce_probe(struct i2c_client *client)
|
||||
{
|
||||
const struct zforce_ts_platdata *pdata = dev_get_platdata(&client->dev);
|
||||
struct zforce_ts *ts;
|
||||
struct input_dev *input_dev;
|
||||
int ret;
|
||||
|
||||
if (!pdata) {
|
||||
pdata = zforce_parse_dt(&client->dev);
|
||||
if (IS_ERR(pdata))
|
||||
return PTR_ERR(pdata);
|
||||
}
|
||||
int error;
|
||||
|
||||
ts = devm_kzalloc(&client->dev, sizeof(struct zforce_ts), GFP_KERNEL);
|
||||
if (!ts)
|
||||
@ -743,22 +690,18 @@ static int zforce_probe(struct i2c_client *client)
|
||||
|
||||
ts->gpio_rst = devm_gpiod_get_optional(&client->dev, "reset",
|
||||
GPIOD_OUT_HIGH);
|
||||
if (IS_ERR(ts->gpio_rst)) {
|
||||
ret = PTR_ERR(ts->gpio_rst);
|
||||
dev_err(&client->dev,
|
||||
"failed to request reset GPIO: %d\n", ret);
|
||||
return ret;
|
||||
}
|
||||
error = PTR_ERR_OR_ZERO(ts->gpio_rst);
|
||||
if (error)
|
||||
return dev_err_probe(&client->dev, error,
|
||||
"failed to request reset GPIO\n");
|
||||
|
||||
if (ts->gpio_rst) {
|
||||
ts->gpio_int = devm_gpiod_get_optional(&client->dev, "irq",
|
||||
GPIOD_IN);
|
||||
if (IS_ERR(ts->gpio_int)) {
|
||||
ret = PTR_ERR(ts->gpio_int);
|
||||
dev_err(&client->dev,
|
||||
"failed to request interrupt GPIO: %d\n", ret);
|
||||
return ret;
|
||||
}
|
||||
error = PTR_ERR_OR_ZERO(ts->gpio_int);
|
||||
if (error)
|
||||
return dev_err_probe(&client->dev, error,
|
||||
"failed to request interrupt GPIO\n");
|
||||
} else {
|
||||
/*
|
||||
* Deprecated GPIO handling for compatibility
|
||||
@ -768,66 +711,45 @@ static int zforce_probe(struct i2c_client *client)
|
||||
/* INT GPIO */
|
||||
ts->gpio_int = devm_gpiod_get_index(&client->dev, NULL, 0,
|
||||
GPIOD_IN);
|
||||
if (IS_ERR(ts->gpio_int)) {
|
||||
ret = PTR_ERR(ts->gpio_int);
|
||||
dev_err(&client->dev,
|
||||
"failed to request interrupt GPIO: %d\n", ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
error = PTR_ERR_OR_ZERO(ts->gpio_int);
|
||||
if (error)
|
||||
return dev_err_probe(&client->dev, error,
|
||||
"failed to request interrupt GPIO\n");
|
||||
|
||||
/* RST GPIO */
|
||||
ts->gpio_rst = devm_gpiod_get_index(&client->dev, NULL, 1,
|
||||
GPIOD_OUT_HIGH);
|
||||
if (IS_ERR(ts->gpio_rst)) {
|
||||
ret = PTR_ERR(ts->gpio_rst);
|
||||
dev_err(&client->dev,
|
||||
"failed to request reset GPIO: %d\n", ret);
|
||||
return ret;
|
||||
}
|
||||
error = PTR_ERR_OR_ZERO(ts->gpio_rst);
|
||||
if (error)
|
||||
return dev_err_probe(&client->dev, error,
|
||||
"failed to request reset GPIO\n");
|
||||
}
|
||||
|
||||
ts->reg_vdd = devm_regulator_get_optional(&client->dev, "vdd");
|
||||
if (IS_ERR(ts->reg_vdd)) {
|
||||
ret = PTR_ERR(ts->reg_vdd);
|
||||
if (ret == -EPROBE_DEFER)
|
||||
return ret;
|
||||
} else {
|
||||
ret = regulator_enable(ts->reg_vdd);
|
||||
if (ret)
|
||||
return ret;
|
||||
error = devm_regulator_get_enable(&client->dev, "vdd");
|
||||
if (error)
|
||||
return dev_err_probe(&client->dev, error,
|
||||
"failed to request vdd supply\n");
|
||||
|
||||
/*
|
||||
* according to datasheet add 100us grace time after regular
|
||||
* regulator enable delay.
|
||||
*/
|
||||
udelay(100);
|
||||
}
|
||||
/*
|
||||
* According to datasheet add 100us grace time after regular
|
||||
* regulator enable delay.
|
||||
*/
|
||||
usleep_range(100, 200);
|
||||
|
||||
ret = devm_add_action(&client->dev, zforce_reset, ts);
|
||||
if (ret) {
|
||||
dev_err(&client->dev, "failed to register reset action, %d\n",
|
||||
ret);
|
||||
|
||||
/* hereafter the regulator will be disabled by the action */
|
||||
if (!IS_ERR(ts->reg_vdd))
|
||||
regulator_disable(ts->reg_vdd);
|
||||
|
||||
return ret;
|
||||
}
|
||||
error = devm_add_action_or_reset(&client->dev, zforce_reset, ts);
|
||||
if (error)
|
||||
return dev_err_probe(&client->dev, error,
|
||||
"failed to register reset action\n");
|
||||
|
||||
snprintf(ts->phys, sizeof(ts->phys),
|
||||
"%s/input0", dev_name(&client->dev));
|
||||
|
||||
input_dev = devm_input_allocate_device(&client->dev);
|
||||
if (!input_dev) {
|
||||
dev_err(&client->dev, "could not allocate input device\n");
|
||||
return -ENOMEM;
|
||||
}
|
||||
if (!input_dev)
|
||||
return dev_err_probe(&client->dev, -ENOMEM,
|
||||
"could not allocate input device\n");
|
||||
|
||||
mutex_init(&ts->access_mutex);
|
||||
mutex_init(&ts->command_mutex);
|
||||
|
||||
ts->pdata = pdata;
|
||||
ts->client = client;
|
||||
ts->input = input_dev;
|
||||
|
||||
@ -838,28 +760,21 @@ static int zforce_probe(struct i2c_client *client)
|
||||
input_dev->open = zforce_input_open;
|
||||
input_dev->close = zforce_input_close;
|
||||
|
||||
__set_bit(EV_KEY, input_dev->evbit);
|
||||
__set_bit(EV_SYN, input_dev->evbit);
|
||||
__set_bit(EV_ABS, input_dev->evbit);
|
||||
|
||||
/* For multi touch */
|
||||
input_set_abs_params(input_dev, ABS_MT_POSITION_X, 0,
|
||||
pdata->x_max, 0, 0);
|
||||
input_set_abs_params(input_dev, ABS_MT_POSITION_Y, 0,
|
||||
pdata->y_max, 0, 0);
|
||||
|
||||
zforce_ts_parse_legacy_properties(ts);
|
||||
touchscreen_parse_properties(input_dev, true, &ts->prop);
|
||||
if (ts->prop.max_x == 0 || ts->prop.max_y == 0) {
|
||||
dev_err(&client->dev, "no size specified\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
if (ts->prop.max_x == 0 || ts->prop.max_y == 0)
|
||||
return dev_err_probe(&client->dev, -EINVAL, "no size specified");
|
||||
|
||||
input_set_abs_params(input_dev, ABS_MT_TOUCH_MAJOR, 0,
|
||||
ZFORCE_MAX_AREA, 0, 0);
|
||||
input_set_abs_params(input_dev, ABS_MT_TOUCH_MINOR, 0,
|
||||
ZFORCE_MAX_AREA, 0, 0);
|
||||
input_set_abs_params(input_dev, ABS_MT_ORIENTATION, 0, 1, 0, 0);
|
||||
input_mt_init_slots(input_dev, ZFORCE_REPORT_POINTS, INPUT_MT_DIRECT);
|
||||
|
||||
error = input_mt_init_slots(input_dev, ZFORCE_REPORT_POINTS,
|
||||
INPUT_MT_DIRECT);
|
||||
if (error)
|
||||
return error;
|
||||
|
||||
input_set_drvdata(ts->input, ts);
|
||||
|
||||
@ -872,57 +787,51 @@ static int zforce_probe(struct i2c_client *client)
|
||||
* Therefore we can trigger the interrupt anytime it is low and do
|
||||
* not need to limit it to the interrupt edge.
|
||||
*/
|
||||
ret = devm_request_threaded_irq(&client->dev, client->irq,
|
||||
zforce_irq, zforce_irq_thread,
|
||||
IRQF_TRIGGER_LOW | IRQF_ONESHOT,
|
||||
input_dev->name, ts);
|
||||
if (ret) {
|
||||
dev_err(&client->dev, "irq %d request failed\n", client->irq);
|
||||
return ret;
|
||||
}
|
||||
error = devm_request_threaded_irq(&client->dev, client->irq,
|
||||
zforce_irq, zforce_irq_thread,
|
||||
IRQF_ONESHOT, input_dev->name, ts);
|
||||
if (error)
|
||||
return dev_err_probe(&client->dev, error,
|
||||
"irq %d request failed\n", client->irq);
|
||||
|
||||
i2c_set_clientdata(client, ts);
|
||||
|
||||
/* let the controller boot */
|
||||
zforce_reset_deassert(ts);
|
||||
gpiod_set_value_cansleep(ts->gpio_rst, 0);
|
||||
|
||||
ts->command_waiting = NOTIFICATION_BOOTCOMPLETE;
|
||||
if (wait_for_completion_timeout(&ts->command_done, WAIT_TIMEOUT) == 0)
|
||||
dev_warn(&client->dev, "bootcomplete timed out\n");
|
||||
|
||||
/* need to start device to get version information */
|
||||
ret = zforce_command_wait(ts, COMMAND_INITIALIZE);
|
||||
if (ret) {
|
||||
dev_err(&client->dev, "unable to initialize, %d\n", ret);
|
||||
return ret;
|
||||
}
|
||||
error = zforce_command_wait(ts, COMMAND_INITIALIZE);
|
||||
if (error)
|
||||
return dev_err_probe(&client->dev, error, "unable to initialize\n");
|
||||
|
||||
/* this gets the firmware version among other information */
|
||||
ret = zforce_command_wait(ts, COMMAND_STATUS);
|
||||
if (ret < 0) {
|
||||
dev_err(&client->dev, "couldn't get status, %d\n", ret);
|
||||
error = zforce_command_wait(ts, COMMAND_STATUS);
|
||||
if (error) {
|
||||
dev_err_probe(&client->dev, error, "couldn't get status\n");
|
||||
zforce_stop(ts);
|
||||
return ret;
|
||||
return error;
|
||||
}
|
||||
|
||||
/* stop device and put it into sleep until it is opened */
|
||||
ret = zforce_stop(ts);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
error = zforce_stop(ts);
|
||||
if (error)
|
||||
return error;
|
||||
|
||||
device_set_wakeup_capable(&client->dev, true);
|
||||
|
||||
ret = input_register_device(input_dev);
|
||||
if (ret) {
|
||||
dev_err(&client->dev, "could not register input device, %d\n",
|
||||
ret);
|
||||
return ret;
|
||||
}
|
||||
error = input_register_device(input_dev);
|
||||
if (error)
|
||||
return dev_err_probe(&client->dev, error,
|
||||
"could not register input device\n");
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static struct i2c_device_id zforce_idtable[] = {
|
||||
static const struct i2c_device_id zforce_idtable[] = {
|
||||
{ "zforce-ts" },
|
||||
{ }
|
||||
};
|
||||
@ -941,6 +850,7 @@ static struct i2c_driver zforce_driver = {
|
||||
.name = "zforce-ts",
|
||||
.pm = pm_sleep_ptr(&zforce_pm_ops),
|
||||
.of_match_table = of_match_ptr(zforce_dt_idtable),
|
||||
.probe_type = PROBE_PREFER_ASYNCHRONOUS,
|
||||
},
|
||||
.probe = zforce_probe,
|
||||
.id_table = zforce_idtable,
|
||||
|
@ -10,6 +10,7 @@
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/of.h>
|
||||
#include <linux/property.h>
|
||||
#include <linux/regulator/consumer.h>
|
||||
#include <linux/slab.h>
|
||||
|
||||
@ -34,7 +35,13 @@
|
||||
#define ZINITIX_DEBUG_REG 0x0115 /* 0~7 */
|
||||
|
||||
#define ZINITIX_TOUCH_MODE 0x0010
|
||||
|
||||
#define ZINITIX_CHIP_REVISION 0x0011
|
||||
#define ZINITIX_CHIP_BTX0X_MASK 0xF0F0
|
||||
#define ZINITIX_CHIP_BT4X2 0x4020
|
||||
#define ZINITIX_CHIP_BT4X3 0x4030
|
||||
#define ZINITIX_CHIP_BT4X4 0x4040
|
||||
|
||||
#define ZINITIX_FIRMWARE_VERSION 0x0012
|
||||
|
||||
#define ZINITIX_USB_DETECT 0x116
|
||||
@ -62,7 +69,11 @@
|
||||
#define ZINITIX_Y_RESOLUTION 0x00C1
|
||||
|
||||
#define ZINITIX_POINT_STATUS_REG 0x0080
|
||||
#define ZINITIX_ICON_STATUS_REG 0x00AA
|
||||
|
||||
#define ZINITIX_BT4X2_ICON_STATUS_REG 0x009A
|
||||
#define ZINITIX_BT4X3_ICON_STATUS_REG 0x00A0
|
||||
#define ZINITIX_BT4X4_ICON_STATUS_REG 0x00A0
|
||||
#define ZINITIX_BT5XX_ICON_STATUS_REG 0x00AA
|
||||
|
||||
#define ZINITIX_POINT_COORD_REG (ZINITIX_POINT_STATUS_REG + 2)
|
||||
|
||||
@ -119,6 +130,7 @@
|
||||
|
||||
#define DEFAULT_TOUCH_POINT_MODE 2
|
||||
#define MAX_SUPPORTED_FINGER_NUM 5
|
||||
#define MAX_SUPPORTED_BUTTON_NUM 8
|
||||
|
||||
#define CHIP_ON_DELAY 15 // ms
|
||||
#define FIRMWARE_ON_DELAY 40 // ms
|
||||
@ -146,6 +158,13 @@ struct bt541_ts_data {
|
||||
struct touchscreen_properties prop;
|
||||
struct regulator_bulk_data supplies[2];
|
||||
u32 zinitix_mode;
|
||||
u32 keycodes[MAX_SUPPORTED_BUTTON_NUM];
|
||||
int num_keycodes;
|
||||
bool have_versioninfo;
|
||||
u16 chip_revision;
|
||||
u16 firmware_version;
|
||||
u16 regdata_version;
|
||||
u16 icon_status_reg;
|
||||
};
|
||||
|
||||
static int zinitix_read_data(struct i2c_client *client,
|
||||
@ -190,11 +209,25 @@ static int zinitix_write_cmd(struct i2c_client *client, u16 reg)
|
||||
return 0;
|
||||
}
|
||||
|
||||
static u16 zinitix_get_u16_reg(struct bt541_ts_data *bt541, u16 vreg)
|
||||
{
|
||||
struct i2c_client *client = bt541->client;
|
||||
int error;
|
||||
__le16 val;
|
||||
|
||||
error = zinitix_read_data(client, vreg, (void *)&val, 2);
|
||||
if (error)
|
||||
return U8_MAX;
|
||||
|
||||
return le16_to_cpu(val);
|
||||
}
|
||||
|
||||
static int zinitix_init_touch(struct bt541_ts_data *bt541)
|
||||
{
|
||||
struct i2c_client *client = bt541->client;
|
||||
int i;
|
||||
int error;
|
||||
u16 int_flags;
|
||||
|
||||
error = zinitix_write_cmd(client, ZINITIX_SWRESET_CMD);
|
||||
if (error) {
|
||||
@ -202,6 +235,47 @@ static int zinitix_init_touch(struct bt541_ts_data *bt541)
|
||||
return error;
|
||||
}
|
||||
|
||||
/*
|
||||
* Read and cache the chip revision and firmware version the first time
|
||||
* we get here.
|
||||
*/
|
||||
if (!bt541->have_versioninfo) {
|
||||
bt541->chip_revision = zinitix_get_u16_reg(bt541,
|
||||
ZINITIX_CHIP_REVISION);
|
||||
bt541->firmware_version = zinitix_get_u16_reg(bt541,
|
||||
ZINITIX_FIRMWARE_VERSION);
|
||||
bt541->regdata_version = zinitix_get_u16_reg(bt541,
|
||||
ZINITIX_DATA_VERSION_REG);
|
||||
bt541->have_versioninfo = true;
|
||||
|
||||
dev_dbg(&client->dev,
|
||||
"chip revision %04x firmware version %04x regdata version %04x\n",
|
||||
bt541->chip_revision, bt541->firmware_version,
|
||||
bt541->regdata_version);
|
||||
|
||||
/*
|
||||
* Determine the "icon" status register which varies by the
|
||||
* chip.
|
||||
*/
|
||||
switch (bt541->chip_revision & ZINITIX_CHIP_BTX0X_MASK) {
|
||||
case ZINITIX_CHIP_BT4X2:
|
||||
bt541->icon_status_reg = ZINITIX_BT4X2_ICON_STATUS_REG;
|
||||
break;
|
||||
|
||||
case ZINITIX_CHIP_BT4X3:
|
||||
bt541->icon_status_reg = ZINITIX_BT4X3_ICON_STATUS_REG;
|
||||
break;
|
||||
|
||||
case ZINITIX_CHIP_BT4X4:
|
||||
bt541->icon_status_reg = ZINITIX_BT4X4_ICON_STATUS_REG;
|
||||
break;
|
||||
|
||||
default:
|
||||
bt541->icon_status_reg = ZINITIX_BT5XX_ICON_STATUS_REG;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
error = zinitix_write_u16(client, ZINITIX_INT_ENABLE_FLAG, 0x0);
|
||||
if (error) {
|
||||
dev_err(&client->dev,
|
||||
@ -225,6 +299,11 @@ static int zinitix_init_touch(struct bt541_ts_data *bt541)
|
||||
if (error)
|
||||
return error;
|
||||
|
||||
error = zinitix_write_u16(client, ZINITIX_BUTTON_SUPPORTED_NUM,
|
||||
bt541->num_keycodes);
|
||||
if (error)
|
||||
return error;
|
||||
|
||||
error = zinitix_write_u16(client, ZINITIX_INITIAL_TOUCH_MODE,
|
||||
bt541->zinitix_mode);
|
||||
if (error)
|
||||
@ -235,9 +314,11 @@ static int zinitix_init_touch(struct bt541_ts_data *bt541)
|
||||
if (error)
|
||||
return error;
|
||||
|
||||
error = zinitix_write_u16(client, ZINITIX_INT_ENABLE_FLAG,
|
||||
BIT_PT_CNT_CHANGE | BIT_DOWN | BIT_MOVE |
|
||||
BIT_UP);
|
||||
int_flags = BIT_PT_CNT_CHANGE | BIT_DOWN | BIT_MOVE | BIT_UP;
|
||||
if (bt541->num_keycodes)
|
||||
int_flags |= BIT_ICON_EVENT;
|
||||
|
||||
error = zinitix_write_u16(client, ZINITIX_INT_ENABLE_FLAG, int_flags);
|
||||
if (error)
|
||||
return error;
|
||||
|
||||
@ -350,12 +431,22 @@ static void zinitix_report_finger(struct bt541_ts_data *bt541, int slot,
|
||||
}
|
||||
}
|
||||
|
||||
static void zinitix_report_keys(struct bt541_ts_data *bt541, u16 icon_events)
|
||||
{
|
||||
int i;
|
||||
|
||||
for (i = 0; i < bt541->num_keycodes; i++)
|
||||
input_report_key(bt541->input_dev,
|
||||
bt541->keycodes[i], icon_events & BIT(i));
|
||||
}
|
||||
|
||||
static irqreturn_t zinitix_ts_irq_handler(int irq, void *bt541_handler)
|
||||
{
|
||||
struct bt541_ts_data *bt541 = bt541_handler;
|
||||
struct i2c_client *client = bt541->client;
|
||||
struct touch_event touch_event;
|
||||
unsigned long finger_mask;
|
||||
__le16 icon_events;
|
||||
int error;
|
||||
int i;
|
||||
|
||||
@ -368,6 +459,17 @@ static irqreturn_t zinitix_ts_irq_handler(int irq, void *bt541_handler)
|
||||
goto out;
|
||||
}
|
||||
|
||||
if (le16_to_cpu(touch_event.status) & BIT_ICON_EVENT) {
|
||||
error = zinitix_read_data(bt541->client, bt541->icon_status_reg,
|
||||
&icon_events, sizeof(icon_events));
|
||||
if (error) {
|
||||
dev_err(&client->dev, "Failed to read icon events\n");
|
||||
goto out;
|
||||
}
|
||||
|
||||
zinitix_report_keys(bt541, le16_to_cpu(icon_events));
|
||||
}
|
||||
|
||||
finger_mask = touch_event.finger_mask;
|
||||
for_each_set_bit(i, &finger_mask, MAX_SUPPORTED_FINGER_NUM) {
|
||||
const struct point_coord *p = &touch_event.point_coord[i];
|
||||
@ -453,6 +555,7 @@ static int zinitix_init_input_dev(struct bt541_ts_data *bt541)
|
||||
{
|
||||
struct input_dev *input_dev;
|
||||
int error;
|
||||
int i;
|
||||
|
||||
input_dev = devm_input_allocate_device(&bt541->client->dev);
|
||||
if (!input_dev) {
|
||||
@ -470,6 +573,14 @@ static int zinitix_init_input_dev(struct bt541_ts_data *bt541)
|
||||
input_dev->open = zinitix_input_open;
|
||||
input_dev->close = zinitix_input_close;
|
||||
|
||||
if (bt541->num_keycodes) {
|
||||
input_dev->keycode = bt541->keycodes;
|
||||
input_dev->keycodemax = bt541->num_keycodes;
|
||||
input_dev->keycodesize = sizeof(bt541->keycodes[0]);
|
||||
for (i = 0; i < bt541->num_keycodes; i++)
|
||||
input_set_capability(input_dev, EV_KEY, bt541->keycodes[i]);
|
||||
}
|
||||
|
||||
input_set_capability(input_dev, EV_ABS, ABS_MT_POSITION_X);
|
||||
input_set_capability(input_dev, EV_ABS, ABS_MT_POSITION_Y);
|
||||
input_set_abs_params(input_dev, ABS_MT_WIDTH_MAJOR, 0, 255, 0, 0);
|
||||
@ -534,6 +645,21 @@ static int zinitix_ts_probe(struct i2c_client *client)
|
||||
return error;
|
||||
}
|
||||
|
||||
bt541->num_keycodes = device_property_count_u32(&client->dev, "linux,keycodes");
|
||||
if (bt541->num_keycodes > ARRAY_SIZE(bt541->keycodes)) {
|
||||
dev_err(&client->dev, "too many keys defined (%d)\n", bt541->num_keycodes);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
error = device_property_read_u32_array(&client->dev, "linux,keycodes",
|
||||
bt541->keycodes,
|
||||
bt541->num_keycodes);
|
||||
if (error) {
|
||||
dev_err(&client->dev,
|
||||
"Unable to parse \"linux,keycodes\" property: %d\n", error);
|
||||
return error;
|
||||
}
|
||||
|
||||
error = zinitix_init_input_dev(bt541);
|
||||
if (error) {
|
||||
dev_err(&client->dev,
|
||||
|
@ -34,52 +34,6 @@ struct matrix_keymap_data {
|
||||
unsigned int keymap_size;
|
||||
};
|
||||
|
||||
/**
|
||||
* struct matrix_keypad_platform_data - platform-dependent keypad data
|
||||
* @keymap_data: pointer to &matrix_keymap_data
|
||||
* @row_gpios: pointer to array of gpio numbers representing rows
|
||||
* @col_gpios: pointer to array of gpio numbers reporesenting colums
|
||||
* @num_row_gpios: actual number of row gpios used by device
|
||||
* @num_col_gpios: actual number of col gpios used by device
|
||||
* @col_scan_delay_us: delay, measured in microseconds, that is
|
||||
* needed before we can keypad after activating column gpio
|
||||
* @debounce_ms: debounce interval in milliseconds
|
||||
* @clustered_irq: may be specified if interrupts of all row/column GPIOs
|
||||
* are bundled to one single irq
|
||||
* @clustered_irq_flags: flags that are needed for the clustered irq
|
||||
* @active_low: gpio polarity
|
||||
* @wakeup: controls whether the device should be set up as wakeup
|
||||
* source
|
||||
* @no_autorepeat: disable key autorepeat
|
||||
* @drive_inactive_cols: drive inactive columns during scan, rather than
|
||||
* making them inputs.
|
||||
*
|
||||
* This structure represents platform-specific data that use used by
|
||||
* matrix_keypad driver to perform proper initialization.
|
||||
*/
|
||||
struct matrix_keypad_platform_data {
|
||||
const struct matrix_keymap_data *keymap_data;
|
||||
|
||||
const unsigned int *row_gpios;
|
||||
const unsigned int *col_gpios;
|
||||
|
||||
unsigned int num_row_gpios;
|
||||
unsigned int num_col_gpios;
|
||||
|
||||
unsigned int col_scan_delay_us;
|
||||
|
||||
/* key debounce interval in milli-second */
|
||||
unsigned int debounce_ms;
|
||||
|
||||
unsigned int clustered_irq;
|
||||
unsigned int clustered_irq_flags;
|
||||
|
||||
bool active_low;
|
||||
bool wakeup;
|
||||
bool no_autorepeat;
|
||||
bool drive_inactive_cols;
|
||||
};
|
||||
|
||||
int matrix_keypad_build_keymap(const struct matrix_keymap_data *keymap_data,
|
||||
const char *keymap_name,
|
||||
unsigned int rows, unsigned int cols,
|
||||
@ -88,6 +42,4 @@ int matrix_keypad_build_keymap(const struct matrix_keymap_data *keymap_data,
|
||||
int matrix_keypad_parse_properties(struct device *dev,
|
||||
unsigned int *rows, unsigned int *cols);
|
||||
|
||||
#define matrix_keypad_parse_of_params matrix_keypad_parse_properties
|
||||
|
||||
#endif /* _MATRIX_KEYPAD_H */
|
||||
|
@ -1,62 +0,0 @@
|
||||
/* SPDX-License-Identifier: GPL-2.0-only */
|
||||
/*
|
||||
* Header file for:
|
||||
* Cypress TrueTouch(TM) Standard Product (TTSP) touchscreen drivers.
|
||||
* For use with Cypress Txx3xx parts.
|
||||
* Supported parts include:
|
||||
* CY8CTST341
|
||||
* CY8CTMA340
|
||||
*
|
||||
* Copyright (C) 2009, 2010, 2011 Cypress Semiconductor, Inc.
|
||||
* Copyright (C) 2012 Javier Martinez Canillas <javier@dowhile0.org>
|
||||
*
|
||||
* Contact Cypress Semiconductor at www.cypress.com (kev@cypress.com)
|
||||
*/
|
||||
#ifndef _CYTTSP4_H_
|
||||
#define _CYTTSP4_H_
|
||||
|
||||
#define CYTTSP4_MT_NAME "cyttsp4_mt"
|
||||
#define CYTTSP4_I2C_NAME "cyttsp4_i2c_adapter"
|
||||
#define CYTTSP4_SPI_NAME "cyttsp4_spi_adapter"
|
||||
|
||||
#define CY_TOUCH_SETTINGS_MAX 32
|
||||
|
||||
struct touch_framework {
|
||||
const uint16_t *abs;
|
||||
uint8_t size;
|
||||
uint8_t enable_vkeys;
|
||||
} __packed;
|
||||
|
||||
struct cyttsp4_mt_platform_data {
|
||||
struct touch_framework *frmwrk;
|
||||
unsigned short flags;
|
||||
char const *inp_dev_name;
|
||||
};
|
||||
|
||||
struct touch_settings {
|
||||
const uint8_t *data;
|
||||
uint32_t size;
|
||||
uint8_t tag;
|
||||
} __packed;
|
||||
|
||||
struct cyttsp4_core_platform_data {
|
||||
int irq_gpio;
|
||||
int rst_gpio;
|
||||
int level_irq_udelay;
|
||||
int (*xres)(struct cyttsp4_core_platform_data *pdata,
|
||||
struct device *dev);
|
||||
int (*init)(struct cyttsp4_core_platform_data *pdata,
|
||||
int on, struct device *dev);
|
||||
int (*power)(struct cyttsp4_core_platform_data *pdata,
|
||||
int on, struct device *dev, atomic_t *ignore_irq);
|
||||
int (*irq_stat)(struct cyttsp4_core_platform_data *pdata,
|
||||
struct device *dev);
|
||||
struct touch_settings *sett[CY_TOUCH_SETTINGS_MAX];
|
||||
};
|
||||
|
||||
struct cyttsp4_platform_data {
|
||||
struct cyttsp4_core_platform_data *core_pdata;
|
||||
struct cyttsp4_mt_platform_data *mt_pdata;
|
||||
};
|
||||
|
||||
#endif /* _CYTTSP4_H_ */
|
@ -1,50 +0,0 @@
|
||||
/* SPDX-License-Identifier: GPL-2.0-only */
|
||||
/*
|
||||
* Copyright (C) ST-Ericsson SA 2010
|
||||
*
|
||||
* Author: Naveen Kumar Gaddipati <naveen.gaddipati@stericsson.com>
|
||||
*
|
||||
* ux500 Scroll key and Keypad Encoder (SKE) header
|
||||
*/
|
||||
|
||||
#ifndef __SKE_H
|
||||
#define __SKE_H
|
||||
|
||||
#include <linux/input/matrix_keypad.h>
|
||||
|
||||
/* register definitions for SKE peripheral */
|
||||
#define SKE_CR 0x00
|
||||
#define SKE_VAL0 0x04
|
||||
#define SKE_VAL1 0x08
|
||||
#define SKE_DBCR 0x0C
|
||||
#define SKE_IMSC 0x10
|
||||
#define SKE_RIS 0x14
|
||||
#define SKE_MIS 0x18
|
||||
#define SKE_ICR 0x1C
|
||||
|
||||
/*
|
||||
* Keypad module
|
||||
*/
|
||||
|
||||
/**
|
||||
* struct keypad_platform_data - structure for platform specific data
|
||||
* @init: pointer to keypad init function
|
||||
* @exit: pointer to keypad deinitialisation function
|
||||
* @keymap_data: matrix scan code table for keycodes
|
||||
* @krow: maximum number of rows
|
||||
* @kcol: maximum number of columns
|
||||
* @debounce_ms: platform specific debounce time
|
||||
* @no_autorepeat: flag for auto repetition
|
||||
* @wakeup_enable: allow waking up the system
|
||||
*/
|
||||
struct ske_keypad_platform_data {
|
||||
int (*init)(void);
|
||||
int (*exit)(void);
|
||||
const struct matrix_keymap_data *keymap_data;
|
||||
u8 krow;
|
||||
u8 kcol;
|
||||
u8 debounce_ms;
|
||||
bool no_autorepeat;
|
||||
bool wakeup_enable;
|
||||
};
|
||||
#endif /*__SKE_KPD_H*/
|
@ -1,30 +0,0 @@
|
||||
/* SPDX-License-Identifier: GPL-2.0-or-later */
|
||||
/*
|
||||
* Copyright (C) 2009 - 2010 Samsung Electronics Co.Ltd
|
||||
* Author: Joonyoung Shim <jy0922.shim@samsung.com>
|
||||
* Author: HeungJun Kim <riverful.kim@samsung.com>
|
||||
*/
|
||||
|
||||
#ifndef __LINUX_MCS_H
|
||||
#define __LINUX_MCS_H
|
||||
|
||||
#define MCS_KEY_MAP(v, c) ((((v) & 0xff) << 16) | ((c) & 0xffff))
|
||||
#define MCS_KEY_VAL(v) (((v) >> 16) & 0xff)
|
||||
#define MCS_KEY_CODE(v) ((v) & 0xffff)
|
||||
|
||||
struct mcs_platform_data {
|
||||
void (*poweron)(bool);
|
||||
void (*cfg_pin)(void);
|
||||
|
||||
/* touchscreen */
|
||||
unsigned int x_size;
|
||||
unsigned int y_size;
|
||||
|
||||
/* touchkey */
|
||||
const u32 *keymap;
|
||||
unsigned int keymap_size;
|
||||
unsigned int key_maxval;
|
||||
bool no_autorepeat;
|
||||
};
|
||||
|
||||
#endif /* __LINUX_MCS_H */
|
@ -1,15 +0,0 @@
|
||||
/* SPDX-License-Identifier: GPL-2.0-only */
|
||||
/* drivers/input/touchscreen/zforce.c
|
||||
*
|
||||
* Copyright (C) 2012-2013 MundoReader S.L.
|
||||
*/
|
||||
|
||||
#ifndef _LINUX_INPUT_ZFORCE_TS_H
|
||||
#define _LINUX_INPUT_ZFORCE_TS_H
|
||||
|
||||
struct zforce_ts_platdata {
|
||||
unsigned int x_max;
|
||||
unsigned int y_max;
|
||||
};
|
||||
|
||||
#endif /* _LINUX_INPUT_ZFORCE_TS_H */
|
Loading…
Reference in New Issue
Block a user