Merge 7f8de2bf07 ("Merge tag 'linux-cpupower-6.12-rc1-fixes' of git://git.kernel.org/pub/scm/linux/kernel/git/shuah/linux") into android-mainline
Steps on the way to 6.12-rc1 Bug: 367265496 Change-Id: Ibefca303fccca4fbc4e8b2a87c930c77a1b9d4cd Signed-off-by: Matthias Maennich <maennich@google.com>
This commit is contained in:
@@ -0,0 +1,50 @@
|
||||
# SPDX-License-Identifier: GPL-2.0-only OR BSD-2-Clause
|
||||
%YAML 1.2
|
||||
---
|
||||
$id: http://devicetree.org/schemas/hwlock/sprd,hwspinlock-r3p0.yaml#
|
||||
$schema: http://devicetree.org/meta-schemas/core.yaml#
|
||||
|
||||
title: Spreadtrum hardware spinlock
|
||||
|
||||
maintainers:
|
||||
- Orson Zhai <orsonzhai@gmail.com>
|
||||
- Baolin Wang <baolin.wang7@gmail.com>
|
||||
- Chunyan Zhang <zhang.lyra@gmail.com>
|
||||
|
||||
properties:
|
||||
compatible:
|
||||
const: sprd,hwspinlock-r3p0
|
||||
|
||||
reg:
|
||||
maxItems: 1
|
||||
|
||||
clocks:
|
||||
maxItems: 1
|
||||
|
||||
clock-names:
|
||||
const: enable
|
||||
|
||||
'#hwlock-cells':
|
||||
const: 1
|
||||
|
||||
required:
|
||||
- compatible
|
||||
- reg
|
||||
- clocks
|
||||
- clock-names
|
||||
- '#hwlock-cells'
|
||||
|
||||
additionalProperties: false
|
||||
|
||||
examples:
|
||||
- |
|
||||
#include <dt-bindings/clock/sprd,sc9860-clk.h>
|
||||
|
||||
hwlock@40500000 {
|
||||
compatible = "sprd,hwspinlock-r3p0";
|
||||
reg = <0x40500000 0x1000>;
|
||||
clocks = <&aon_gate CLK_SPLK_EB>;
|
||||
clock-names = "enable";
|
||||
#hwlock-cells = <1>;
|
||||
};
|
||||
...
|
||||
@@ -1,23 +0,0 @@
|
||||
SPRD Hardware Spinlock Device Binding
|
||||
-------------------------------------
|
||||
|
||||
Required properties :
|
||||
- compatible : should be "sprd,hwspinlock-r3p0".
|
||||
- reg : the register address of hwspinlock.
|
||||
- #hwlock-cells : hwlock users only use the hwlock id to represent a specific
|
||||
hwlock, so the number of cells should be <1> here.
|
||||
- clock-names : Must contain "enable".
|
||||
- clocks : Must contain a phandle entry for the clock in clock-names, see the
|
||||
common clock bindings.
|
||||
|
||||
Please look at the generic hwlock binding for usage information for consumers,
|
||||
"Documentation/devicetree/bindings/hwlock/hwlock.txt"
|
||||
|
||||
Example of hwlock provider:
|
||||
hwspinlock@40500000 {
|
||||
compatible = "sprd,hwspinlock-r3p0";
|
||||
reg = <0 0x40500000 0 0x1000>;
|
||||
#hwlock-cells = <1>;
|
||||
clock-names = "enable";
|
||||
clocks = <&clk_aon_apb_gates0 22>;
|
||||
};
|
||||
@@ -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;
|
||||
};
|
||||
@@ -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
|
||||
|
||||
@@ -16,6 +16,7 @@ description:
|
||||
properties:
|
||||
compatible:
|
||||
enum:
|
||||
- qcom,sdx75-mpss-pas
|
||||
- qcom,sm8550-adsp-pas
|
||||
- qcom,sm8550-cdsp-pas
|
||||
- qcom,sm8550-mpss-pas
|
||||
@@ -113,6 +114,7 @@ allOf:
|
||||
properties:
|
||||
compatible:
|
||||
enum:
|
||||
- qcom,sdx75-mpss-pas
|
||||
- qcom,sm8650-mpss-pas
|
||||
then:
|
||||
properties:
|
||||
@@ -146,6 +148,7 @@ allOf:
|
||||
properties:
|
||||
compatible:
|
||||
enum:
|
||||
- qcom,sdx75-mpss-pas
|
||||
- qcom,sm8550-mpss-pas
|
||||
- qcom,sm8650-mpss-pas
|
||||
then:
|
||||
|
||||
@@ -0,0 +1,125 @@
|
||||
# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
|
||||
%YAML 1.2
|
||||
---
|
||||
$id: http://devicetree.org/schemas/remoteproc/ti,k3-m4f-rproc.yaml#
|
||||
$schema: http://devicetree.org/meta-schemas/core.yaml#
|
||||
|
||||
title: TI K3 M4F processor subsystems
|
||||
|
||||
maintainers:
|
||||
- Hari Nagalla <hnagalla@ti.com>
|
||||
- Mathieu Poirier <mathieu.poirier@linaro.org>
|
||||
|
||||
description: |
|
||||
Some K3 family SoCs have Arm Cortex M4F cores. AM64x is a SoC in K3
|
||||
family with a M4F core. Typically safety oriented applications may use
|
||||
the M4F core in isolation without an IPC. Where as some industrial and
|
||||
home automation applications, may use the M4F core as a remote processor
|
||||
with IPC communications.
|
||||
|
||||
$ref: /schemas/arm/keystone/ti,k3-sci-common.yaml#
|
||||
|
||||
properties:
|
||||
compatible:
|
||||
enum:
|
||||
- ti,am64-m4fss
|
||||
|
||||
power-domains:
|
||||
maxItems: 1
|
||||
|
||||
"#address-cells":
|
||||
const: 2
|
||||
|
||||
"#size-cells":
|
||||
const: 2
|
||||
|
||||
reg:
|
||||
items:
|
||||
- description: IRAM internal memory region
|
||||
- description: DRAM internal memory region
|
||||
|
||||
reg-names:
|
||||
items:
|
||||
- const: iram
|
||||
- const: dram
|
||||
|
||||
resets:
|
||||
maxItems: 1
|
||||
|
||||
firmware-name:
|
||||
maxItems: 1
|
||||
description: Name of firmware to load for the M4F core
|
||||
|
||||
mboxes:
|
||||
description:
|
||||
OMAP Mailbox specifier denoting the sub-mailbox, to be used for
|
||||
communication with the remote processor. This property should match
|
||||
with the sub-mailbox node used in the firmware image.
|
||||
maxItems: 1
|
||||
|
||||
memory-region:
|
||||
description:
|
||||
phandle to the reserved memory nodes to be associated with the
|
||||
remoteproc device. Optional memory regions available for firmware
|
||||
specific purposes.
|
||||
(see reserved-memory/reserved-memory.yaml in dtschema project)
|
||||
maxItems: 8
|
||||
items:
|
||||
- description: regions used for DMA allocations like vrings, vring buffers
|
||||
and memory dedicated to firmware's specific purposes.
|
||||
additionalItems: true
|
||||
|
||||
required:
|
||||
- compatible
|
||||
- reg
|
||||
- reg-names
|
||||
- ti,sci
|
||||
- ti,sci-dev-id
|
||||
- ti,sci-proc-ids
|
||||
- resets
|
||||
- firmware-name
|
||||
|
||||
unevaluatedProperties: false
|
||||
|
||||
examples:
|
||||
- |
|
||||
reserved-memory {
|
||||
#address-cells = <2>;
|
||||
#size-cells = <2>;
|
||||
|
||||
mcu_m4fss_dma_memory_region: m4f-dma-memory@9cb00000 {
|
||||
compatible = "shared-dma-pool";
|
||||
reg = <0x00 0x9cb00000 0x00 0x100000>;
|
||||
no-map;
|
||||
};
|
||||
|
||||
mcu_m4fss_memory_region: m4f-memory@9cc00000 {
|
||||
compatible = "shared-dma-pool";
|
||||
reg = <0x00 0x9cc00000 0x00 0xe00000>;
|
||||
no-map;
|
||||
};
|
||||
};
|
||||
|
||||
soc {
|
||||
#address-cells = <2>;
|
||||
#size-cells = <2>;
|
||||
|
||||
mailbox0_cluster0: mailbox-0 {
|
||||
#mbox-cells = <1>;
|
||||
};
|
||||
|
||||
remoteproc@5000000 {
|
||||
compatible = "ti,am64-m4fss";
|
||||
reg = <0x00 0x5000000 0x00 0x30000>,
|
||||
<0x00 0x5040000 0x00 0x10000>;
|
||||
reg-names = "iram", "dram";
|
||||
resets = <&k3_reset 9 1>;
|
||||
firmware-name = "am62-mcu-m4f0_0-fw";
|
||||
mboxes = <&mailbox0_cluster0>, <&mbox_m4_0>;
|
||||
memory-region = <&mcu_m4fss_dma_memory_region>,
|
||||
<&mcu_m4fss_memory_region>;
|
||||
ti,sci = <&dmsc>;
|
||||
ti,sci-dev-id = <9>;
|
||||
ti,sci-proc-ids = <0x18 0xff>;
|
||||
};
|
||||
};
|
||||
@@ -62,6 +62,7 @@ properties:
|
||||
patternProperties:
|
||||
"^r(.*)@[0-9a-f]+$":
|
||||
type: object
|
||||
additionalProperties: false
|
||||
description: |
|
||||
The RPU is located in the Low Power Domain of the Processor Subsystem.
|
||||
Each processor includes separate L1 instruction and data caches and
|
||||
|
||||
+7
-1
@@ -11205,10 +11205,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>
|
||||
@@ -25539,7 +25546,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.*
|
||||
|
||||
+109
-54
@@ -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) {}
|
||||
|
||||
@@ -1868,6 +1868,12 @@ static int i3c_master_bus_init(struct i3c_master_controller *master)
|
||||
goto err_bus_cleanup;
|
||||
}
|
||||
|
||||
if (master->ops->set_speed) {
|
||||
ret = master->ops->set_speed(master, I3C_OPEN_DRAIN_SLOW_SPEED);
|
||||
if (ret)
|
||||
goto err_bus_cleanup;
|
||||
}
|
||||
|
||||
/*
|
||||
* Reset all dynamic address that may have been assigned before
|
||||
* (assigned by the bootloader for example).
|
||||
@@ -1876,6 +1882,12 @@ static int i3c_master_bus_init(struct i3c_master_controller *master)
|
||||
if (ret && ret != I3C_ERROR_M2)
|
||||
goto err_bus_cleanup;
|
||||
|
||||
if (master->ops->set_speed) {
|
||||
master->ops->set_speed(master, I3C_OPEN_DRAIN_NORMAL_SPEED);
|
||||
if (ret)
|
||||
goto err_bus_cleanup;
|
||||
}
|
||||
|
||||
/* Disable all slave events before starting DAA. */
|
||||
ret = i3c_master_disec_locked(master, I3C_BROADCAST_ADDR,
|
||||
I3C_CCC_EVENT_SIR | I3C_CCC_EVENT_MR |
|
||||
|
||||
@@ -1562,6 +1562,7 @@ static const struct of_device_id cdns_i3c_master_of_ids[] = {
|
||||
{ .compatible = "cdns,i3c-master", .data = &cdns_i3c_devdata },
|
||||
{ /* sentinel */ },
|
||||
};
|
||||
MODULE_DEVICE_TABLE(of, cdns_i3c_master_of_ids);
|
||||
|
||||
static int cdns_i3c_master_probe(struct platform_device *pdev)
|
||||
{
|
||||
@@ -1666,6 +1667,7 @@ static void cdns_i3c_master_remove(struct platform_device *pdev)
|
||||
{
|
||||
struct cdns_i3c_master *master = platform_get_drvdata(pdev);
|
||||
|
||||
cancel_work_sync(&master->hj_work);
|
||||
i3c_master_unregister(&master->base);
|
||||
|
||||
clk_disable_unprepare(master->sysclk);
|
||||
|
||||
@@ -3,4 +3,5 @@
|
||||
obj-$(CONFIG_MIPI_I3C_HCI) += mipi-i3c-hci.o
|
||||
mipi-i3c-hci-y := core.o ext_caps.o pio.o dma.o \
|
||||
cmd_v1.o cmd_v2.o \
|
||||
dat_v1.o dct_v1.o
|
||||
dat_v1.o dct_v1.o \
|
||||
hci_quirks.o
|
||||
|
||||
@@ -123,17 +123,15 @@ static enum hci_cmd_mode get_i3c_mode(struct i3c_hci *hci)
|
||||
{
|
||||
struct i3c_bus *bus = i3c_master_get_bus(&hci->master);
|
||||
|
||||
if (bus->scl_rate.i3c >= 12500000)
|
||||
return MODE_I3C_SDR0;
|
||||
if (bus->scl_rate.i3c > 8000000)
|
||||
return MODE_I3C_SDR1;
|
||||
return MODE_I3C_SDR0;
|
||||
if (bus->scl_rate.i3c > 6000000)
|
||||
return MODE_I3C_SDR2;
|
||||
return MODE_I3C_SDR1;
|
||||
if (bus->scl_rate.i3c > 4000000)
|
||||
return MODE_I3C_SDR3;
|
||||
return MODE_I3C_SDR2;
|
||||
if (bus->scl_rate.i3c > 2000000)
|
||||
return MODE_I3C_SDR4;
|
||||
return MODE_I3C_Fm_FmP;
|
||||
return MODE_I3C_SDR3;
|
||||
return MODE_I3C_SDR4;
|
||||
}
|
||||
|
||||
static enum hci_cmd_mode get_i2c_mode(struct i3c_hci *hci)
|
||||
|
||||
@@ -12,7 +12,6 @@
|
||||
#include <linux/errno.h>
|
||||
#include <linux/i3c/master.h>
|
||||
#include <linux/interrupt.h>
|
||||
#include <linux/io.h>
|
||||
#include <linux/iopoll.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/platform_device.h>
|
||||
@@ -27,11 +26,6 @@
|
||||
* Host Controller Capabilities and Operation Registers
|
||||
*/
|
||||
|
||||
#define reg_read(r) readl(hci->base_regs + (r))
|
||||
#define reg_write(r, v) writel(v, hci->base_regs + (r))
|
||||
#define reg_set(r, v) reg_write(r, reg_read(r) | (v))
|
||||
#define reg_clear(r, v) reg_write(r, reg_read(r) & ~(v))
|
||||
|
||||
#define HCI_VERSION 0x00 /* HCI Version (in BCD) */
|
||||
|
||||
#define HC_CONTROL 0x04
|
||||
@@ -152,6 +146,10 @@ static int i3c_hci_bus_init(struct i3c_master_controller *m)
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
/* Set RESP_BUF_THLD to 0(n) to get 1(n+1) response */
|
||||
if (hci->quirks & HCI_QUIRK_RESP_BUF_THLD)
|
||||
amd_set_resp_buf_thld(hci);
|
||||
|
||||
reg_set(HC_CONTROL, HC_CONTROL_BUS_ENABLE);
|
||||
DBG("HC_CONTROL = %#x", reg_read(HC_CONTROL));
|
||||
|
||||
@@ -630,8 +628,8 @@ static irqreturn_t i3c_hci_irq_handler(int irq, void *dev_id)
|
||||
|
||||
static int i3c_hci_init(struct i3c_hci *hci)
|
||||
{
|
||||
bool size_in_dwords, mode_selector;
|
||||
u32 regval, offset;
|
||||
bool size_in_dwords;
|
||||
int ret;
|
||||
|
||||
/* Validate HCI hardware version */
|
||||
@@ -753,10 +751,17 @@ static int i3c_hci_init(struct i3c_hci *hci)
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
mode_selector = hci->version_major > 1 ||
|
||||
(hci->version_major == 1 && hci->version_minor > 0);
|
||||
|
||||
/* Quirk for HCI_QUIRK_PIO_MODE on AMD platforms */
|
||||
if (hci->quirks & HCI_QUIRK_PIO_MODE)
|
||||
hci->RHS_regs = NULL;
|
||||
|
||||
/* Try activating DMA operations first */
|
||||
if (hci->RHS_regs) {
|
||||
reg_clear(HC_CONTROL, HC_CONTROL_PIO_MODE);
|
||||
if (reg_read(HC_CONTROL) & HC_CONTROL_PIO_MODE) {
|
||||
if (mode_selector && (reg_read(HC_CONTROL) & HC_CONTROL_PIO_MODE)) {
|
||||
dev_err(&hci->master.dev, "PIO mode is stuck\n");
|
||||
ret = -EIO;
|
||||
} else {
|
||||
@@ -768,7 +773,7 @@ static int i3c_hci_init(struct i3c_hci *hci)
|
||||
/* If no DMA, try PIO */
|
||||
if (!hci->io && hci->PIO_regs) {
|
||||
reg_set(HC_CONTROL, HC_CONTROL_PIO_MODE);
|
||||
if (!(reg_read(HC_CONTROL) & HC_CONTROL_PIO_MODE)) {
|
||||
if (mode_selector && !(reg_read(HC_CONTROL) & HC_CONTROL_PIO_MODE)) {
|
||||
dev_err(&hci->master.dev, "DMA mode is stuck\n");
|
||||
ret = -EIO;
|
||||
} else {
|
||||
@@ -784,6 +789,10 @@ static int i3c_hci_init(struct i3c_hci *hci)
|
||||
return ret;
|
||||
}
|
||||
|
||||
/* Configure OD and PP timings for AMD platforms */
|
||||
if (hci->quirks & HCI_QUIRK_OD_PP_TIMING)
|
||||
amd_set_od_pp_timing(hci);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
@@ -803,6 +812,8 @@ static int i3c_hci_probe(struct platform_device *pdev)
|
||||
/* temporary for dev_printk's, to be replaced in i3c_master_register */
|
||||
hci->master.dev.init_name = dev_name(&pdev->dev);
|
||||
|
||||
hci->quirks = (unsigned long)device_get_match_data(&pdev->dev);
|
||||
|
||||
ret = i3c_hci_init(hci);
|
||||
if (ret)
|
||||
return ret;
|
||||
@@ -834,12 +845,19 @@ static const __maybe_unused struct of_device_id i3c_hci_of_match[] = {
|
||||
};
|
||||
MODULE_DEVICE_TABLE(of, i3c_hci_of_match);
|
||||
|
||||
static const struct acpi_device_id i3c_hci_acpi_match[] = {
|
||||
{ "AMDI5017", HCI_QUIRK_PIO_MODE | HCI_QUIRK_OD_PP_TIMING | HCI_QUIRK_RESP_BUF_THLD },
|
||||
{}
|
||||
};
|
||||
MODULE_DEVICE_TABLE(acpi, i3c_hci_acpi_match);
|
||||
|
||||
static struct platform_driver i3c_hci_driver = {
|
||||
.probe = i3c_hci_probe,
|
||||
.remove_new = i3c_hci_remove,
|
||||
.driver = {
|
||||
.name = "mipi-i3c-hci",
|
||||
.of_match_table = of_match_ptr(i3c_hci_of_match),
|
||||
.acpi_match_table = i3c_hci_acpi_match,
|
||||
},
|
||||
};
|
||||
module_platform_driver(i3c_hci_driver);
|
||||
|
||||
@@ -10,6 +10,7 @@
|
||||
#ifndef HCI_H
|
||||
#define HCI_H
|
||||
|
||||
#include <linux/io.h>
|
||||
|
||||
/* Handy logging macro to save on line length */
|
||||
#define DBG(x, ...) pr_devel("%s: " x "\n", __func__, ##__VA_ARGS__)
|
||||
@@ -26,6 +27,10 @@
|
||||
#define W2_BIT_(x) BIT((x) - 64)
|
||||
#define W3_BIT_(x) BIT((x) - 96)
|
||||
|
||||
#define reg_read(r) readl(hci->base_regs + (r))
|
||||
#define reg_write(r, v) writel(v, hci->base_regs + (r))
|
||||
#define reg_set(r, v) reg_write(r, reg_read(r) | (v))
|
||||
#define reg_clear(r, v) reg_write(r, reg_read(r) & ~(v))
|
||||
|
||||
struct hci_cmd_ops;
|
||||
|
||||
@@ -135,11 +140,16 @@ struct i3c_hci_dev_data {
|
||||
|
||||
/* list of quirks */
|
||||
#define HCI_QUIRK_RAW_CCC BIT(1) /* CCC framing must be explicit */
|
||||
#define HCI_QUIRK_PIO_MODE BIT(2) /* Set PIO mode for AMD platforms */
|
||||
#define HCI_QUIRK_OD_PP_TIMING BIT(3) /* Set OD and PP timings for AMD platforms */
|
||||
#define HCI_QUIRK_RESP_BUF_THLD BIT(4) /* Set resp buf thld to 0 for AMD platforms */
|
||||
|
||||
|
||||
/* global functions */
|
||||
void mipi_i3c_hci_resume(struct i3c_hci *hci);
|
||||
void mipi_i3c_hci_pio_reset(struct i3c_hci *hci);
|
||||
void mipi_i3c_hci_dct_index_reset(struct i3c_hci *hci);
|
||||
void amd_set_od_pp_timing(struct i3c_hci *hci);
|
||||
void amd_set_resp_buf_thld(struct i3c_hci *hci);
|
||||
|
||||
#endif
|
||||
|
||||
@@ -0,0 +1,44 @@
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
/*
|
||||
* I3C HCI Quirks
|
||||
*
|
||||
* Copyright 2024 Advanced Micro Devices, Inc.
|
||||
*
|
||||
* Authors: Shyam Sundar S K <Shyam-sundar.S-k@amd.com>
|
||||
* Guruvendra Punugupati <Guruvendra.Punugupati@amd.com>
|
||||
*/
|
||||
|
||||
#include <linux/i3c/master.h>
|
||||
#include "hci.h"
|
||||
|
||||
/* Timing registers */
|
||||
#define HCI_SCL_I3C_OD_TIMING 0x214
|
||||
#define HCI_SCL_I3C_PP_TIMING 0x218
|
||||
#define HCI_SDA_HOLD_SWITCH_DLY_TIMING 0x230
|
||||
|
||||
/* Timing values to configure 9MHz frequency */
|
||||
#define AMD_SCL_I3C_OD_TIMING 0x00cf00cf
|
||||
#define AMD_SCL_I3C_PP_TIMING 0x00160016
|
||||
|
||||
#define QUEUE_THLD_CTRL 0xD0
|
||||
|
||||
void amd_set_od_pp_timing(struct i3c_hci *hci)
|
||||
{
|
||||
u32 data;
|
||||
|
||||
reg_write(HCI_SCL_I3C_OD_TIMING, AMD_SCL_I3C_OD_TIMING);
|
||||
reg_write(HCI_SCL_I3C_PP_TIMING, AMD_SCL_I3C_PP_TIMING);
|
||||
data = reg_read(HCI_SDA_HOLD_SWITCH_DLY_TIMING);
|
||||
/* Configure maximum TX hold time */
|
||||
data |= W0_MASK(18, 16);
|
||||
reg_write(HCI_SDA_HOLD_SWITCH_DLY_TIMING, data);
|
||||
}
|
||||
|
||||
void amd_set_resp_buf_thld(struct i3c_hci *hci)
|
||||
{
|
||||
u32 data;
|
||||
|
||||
data = reg_read(QUEUE_THLD_CTRL);
|
||||
data = data & ~W0_MASK(15, 8);
|
||||
reg_write(QUEUE_THLD_CTRL, data);
|
||||
}
|
||||
@@ -127,6 +127,8 @@
|
||||
|
||||
/* This parameter depends on the implementation and may be tuned */
|
||||
#define SVC_I3C_FIFO_SIZE 16
|
||||
#define SVC_I3C_PPBAUD_MAX 15
|
||||
#define SVC_I3C_QUICK_I2C_CLK 4170000
|
||||
|
||||
#define SVC_I3C_EVENT_IBI BIT(0)
|
||||
#define SVC_I3C_EVENT_HOTJOIN BIT(1)
|
||||
@@ -182,6 +184,7 @@ struct svc_i3c_regs_save {
|
||||
* @ibi.lock: IBI lock
|
||||
* @lock: Transfer lock, protect between IBI work thread and callbacks from master
|
||||
* @enabled_events: Bit masks for enable events (IBI, HotJoin).
|
||||
* @mctrl_config: Configuration value in SVC_I3C_MCTRL for setting speed back.
|
||||
*/
|
||||
struct svc_i3c_master {
|
||||
struct i3c_master_controller base;
|
||||
@@ -212,6 +215,7 @@ struct svc_i3c_master {
|
||||
} ibi;
|
||||
struct mutex lock;
|
||||
int enabled_events;
|
||||
u32 mctrl_config;
|
||||
};
|
||||
|
||||
/**
|
||||
@@ -529,12 +533,61 @@ static irqreturn_t svc_i3c_master_irq_handler(int irq, void *dev_id)
|
||||
return IRQ_HANDLED;
|
||||
}
|
||||
|
||||
static int svc_i3c_master_set_speed(struct i3c_master_controller *m,
|
||||
enum i3c_open_drain_speed speed)
|
||||
{
|
||||
struct svc_i3c_master *master = to_svc_i3c_master(m);
|
||||
struct i3c_bus *bus = i3c_master_get_bus(&master->base);
|
||||
u32 ppbaud, odbaud, odhpp, mconfig;
|
||||
unsigned long fclk_rate;
|
||||
int ret;
|
||||
|
||||
ret = pm_runtime_resume_and_get(master->dev);
|
||||
if (ret < 0) {
|
||||
dev_err(master->dev, "<%s> Cannot get runtime PM.\n", __func__);
|
||||
return ret;
|
||||
}
|
||||
|
||||
switch (speed) {
|
||||
case I3C_OPEN_DRAIN_SLOW_SPEED:
|
||||
fclk_rate = clk_get_rate(master->fclk);
|
||||
if (!fclk_rate) {
|
||||
ret = -EINVAL;
|
||||
goto rpm_out;
|
||||
}
|
||||
/*
|
||||
* Set 50% duty-cycle I2C speed to I3C OPEN-DRAIN mode, so the first
|
||||
* broadcast address is visible to all I2C/I3C devices on the I3C bus.
|
||||
* I3C device working as a I2C device will turn off its 50ns Spike
|
||||
* Filter to change to I3C mode.
|
||||
*/
|
||||
mconfig = master->mctrl_config;
|
||||
ppbaud = FIELD_GET(GENMASK(11, 8), mconfig);
|
||||
odhpp = 0;
|
||||
odbaud = DIV_ROUND_UP(fclk_rate, bus->scl_rate.i2c * (2 + 2 * ppbaud)) - 1;
|
||||
mconfig &= ~GENMASK(24, 16);
|
||||
mconfig |= SVC_I3C_MCONFIG_ODBAUD(odbaud) | SVC_I3C_MCONFIG_ODHPP(odhpp);
|
||||
writel(mconfig, master->regs + SVC_I3C_MCONFIG);
|
||||
break;
|
||||
case I3C_OPEN_DRAIN_NORMAL_SPEED:
|
||||
writel(master->mctrl_config, master->regs + SVC_I3C_MCONFIG);
|
||||
break;
|
||||
}
|
||||
|
||||
rpm_out:
|
||||
pm_runtime_mark_last_busy(master->dev);
|
||||
pm_runtime_put_autosuspend(master->dev);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int svc_i3c_master_bus_init(struct i3c_master_controller *m)
|
||||
{
|
||||
struct svc_i3c_master *master = to_svc_i3c_master(m);
|
||||
struct i3c_bus *bus = i3c_master_get_bus(m);
|
||||
struct i3c_device_info info = {};
|
||||
unsigned long fclk_rate, fclk_period_ns;
|
||||
unsigned long i2c_period_ns, i2c_scl_rate, i3c_scl_rate;
|
||||
unsigned int high_period_ns, od_low_period_ns;
|
||||
u32 ppbaud, pplow, odhpp, odbaud, odstop, i2cbaud, reg;
|
||||
int ret;
|
||||
@@ -555,12 +608,15 @@ static int svc_i3c_master_bus_init(struct i3c_master_controller *m)
|
||||
}
|
||||
|
||||
fclk_period_ns = DIV_ROUND_UP(1000000000, fclk_rate);
|
||||
i2c_period_ns = DIV_ROUND_UP(1000000000, bus->scl_rate.i2c);
|
||||
i2c_scl_rate = bus->scl_rate.i2c;
|
||||
i3c_scl_rate = bus->scl_rate.i3c;
|
||||
|
||||
/*
|
||||
* Using I3C Push-Pull mode, target is 12.5MHz/80ns period.
|
||||
* Simplest configuration is using a 50% duty-cycle of 40ns.
|
||||
*/
|
||||
ppbaud = DIV_ROUND_UP(40, fclk_period_ns) - 1;
|
||||
ppbaud = DIV_ROUND_UP(fclk_rate / 2, i3c_scl_rate) - 1;
|
||||
pplow = 0;
|
||||
|
||||
/*
|
||||
@@ -570,7 +626,7 @@ static int svc_i3c_master_bus_init(struct i3c_master_controller *m)
|
||||
*/
|
||||
odhpp = 1;
|
||||
high_period_ns = (ppbaud + 1) * fclk_period_ns;
|
||||
odbaud = DIV_ROUND_UP(240 - high_period_ns, high_period_ns) - 1;
|
||||
odbaud = DIV_ROUND_UP(fclk_rate, SVC_I3C_QUICK_I2C_CLK * (1 + ppbaud)) - 2;
|
||||
od_low_period_ns = (odbaud + 1) * high_period_ns;
|
||||
|
||||
switch (bus->mode) {
|
||||
@@ -579,20 +635,27 @@ static int svc_i3c_master_bus_init(struct i3c_master_controller *m)
|
||||
odstop = 0;
|
||||
break;
|
||||
case I3C_BUS_MODE_MIXED_FAST:
|
||||
case I3C_BUS_MODE_MIXED_LIMITED:
|
||||
/*
|
||||
* Using I2C Fm+ mode, target is 1MHz/1000ns, the difference
|
||||
* between the high and low period does not really matter.
|
||||
*/
|
||||
i2cbaud = DIV_ROUND_UP(1000, od_low_period_ns) - 2;
|
||||
i2cbaud = DIV_ROUND_UP(i2c_period_ns, od_low_period_ns) - 2;
|
||||
odstop = 1;
|
||||
break;
|
||||
case I3C_BUS_MODE_MIXED_LIMITED:
|
||||
case I3C_BUS_MODE_MIXED_SLOW:
|
||||
/*
|
||||
* Using I2C Fm mode, target is 0.4MHz/2500ns, with the same
|
||||
* constraints as the FM+ mode.
|
||||
*/
|
||||
i2cbaud = DIV_ROUND_UP(2500, od_low_period_ns) - 2;
|
||||
/* I3C PP + I3C OP + I2C OP both use i2c clk rate */
|
||||
if (ppbaud > SVC_I3C_PPBAUD_MAX) {
|
||||
ppbaud = SVC_I3C_PPBAUD_MAX;
|
||||
pplow = DIV_ROUND_UP(fclk_rate, i3c_scl_rate) - (2 + 2 * ppbaud);
|
||||
}
|
||||
|
||||
high_period_ns = (ppbaud + 1) * fclk_period_ns;
|
||||
odhpp = 0;
|
||||
odbaud = DIV_ROUND_UP(fclk_rate, i2c_scl_rate * (2 + 2 * ppbaud)) - 1;
|
||||
|
||||
od_low_period_ns = (odbaud + 1) * high_period_ns;
|
||||
i2cbaud = DIV_ROUND_UP(i2c_period_ns, od_low_period_ns) - 2;
|
||||
odstop = 1;
|
||||
break;
|
||||
default:
|
||||
@@ -611,6 +674,7 @@ static int svc_i3c_master_bus_init(struct i3c_master_controller *m)
|
||||
SVC_I3C_MCONFIG_I2CBAUD(i2cbaud);
|
||||
writel(reg, master->regs + SVC_I3C_MCONFIG);
|
||||
|
||||
master->mctrl_config = reg;
|
||||
/* Master core's registration */
|
||||
ret = i3c_master_get_free_addr(m, 0);
|
||||
if (ret < 0)
|
||||
@@ -1645,6 +1709,7 @@ static const struct i3c_master_controller_ops svc_i3c_master_ops = {
|
||||
.disable_ibi = svc_i3c_master_disable_ibi,
|
||||
.enable_hotjoin = svc_i3c_master_enable_hotjoin,
|
||||
.disable_hotjoin = svc_i3c_master_disable_hotjoin,
|
||||
.set_speed = svc_i3c_master_set_speed,
|
||||
};
|
||||
|
||||
static int svc_i3c_master_prepare_clks(struct svc_i3c_master *master)
|
||||
@@ -1775,6 +1840,7 @@ static void svc_i3c_master_remove(struct platform_device *pdev)
|
||||
{
|
||||
struct svc_i3c_master *master = platform_get_drvdata(pdev);
|
||||
|
||||
cancel_work_sync(&master->hj_work);
|
||||
i3c_master_unregister(&master->base);
|
||||
|
||||
pm_runtime_dont_use_autosuspend(&pdev->dev);
|
||||
|
||||
@@ -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
|
||||
|
||||
+27
-21
@@ -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,
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
@@ -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,
|
||||
|
||||
@@ -340,6 +340,19 @@ config TI_K3_DSP_REMOTEPROC
|
||||
It's safe to say N here if you're not interested in utilizing
|
||||
the DSP slave processors.
|
||||
|
||||
config TI_K3_M4_REMOTEPROC
|
||||
tristate "TI K3 M4 remoteproc support"
|
||||
depends on ARCH_OMAP2PLUS || ARCH_K3
|
||||
select MAILBOX
|
||||
select OMAP2PLUS_MBOX
|
||||
help
|
||||
Say m here to support TI's M4 remote processor subsystems
|
||||
on various TI K3 family of SoCs through the remote processor
|
||||
framework.
|
||||
|
||||
It's safe to say N here if you're not interested in utilizing
|
||||
a remote processor.
|
||||
|
||||
config TI_K3_R5_REMOTEPROC
|
||||
tristate "TI K3 R5 remoteproc support"
|
||||
depends on ARCH_K3
|
||||
|
||||
@@ -37,5 +37,6 @@ obj-$(CONFIG_ST_REMOTEPROC) += st_remoteproc.o
|
||||
obj-$(CONFIG_ST_SLIM_REMOTEPROC) += st_slim_rproc.o
|
||||
obj-$(CONFIG_STM32_RPROC) += stm32_rproc.o
|
||||
obj-$(CONFIG_TI_K3_DSP_REMOTEPROC) += ti_k3_dsp_remoteproc.o
|
||||
obj-$(CONFIG_TI_K3_M4_REMOTEPROC) += ti_k3_m4_remoteproc.o
|
||||
obj-$(CONFIG_TI_K3_R5_REMOTEPROC) += ti_k3_r5_remoteproc.o
|
||||
obj-$(CONFIG_XLNX_R5_REMOTEPROC) += xlnx_r5_remoteproc.o
|
||||
|
||||
@@ -239,8 +239,6 @@ static int da8xx_rproc_probe(struct platform_device *pdev)
|
||||
struct da8xx_rproc *drproc;
|
||||
struct rproc *rproc;
|
||||
struct irq_data *irq_data;
|
||||
struct resource *bootreg_res;
|
||||
struct resource *chipsig_res;
|
||||
struct clk *dsp_clk;
|
||||
struct reset_control *dsp_reset;
|
||||
void __iomem *chipsig;
|
||||
@@ -258,15 +256,11 @@ static int da8xx_rproc_probe(struct platform_device *pdev)
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
bootreg_res = platform_get_resource_byname(pdev, IORESOURCE_MEM,
|
||||
"host1cfg");
|
||||
bootreg = devm_ioremap_resource(dev, bootreg_res);
|
||||
bootreg = devm_platform_ioremap_resource_byname(pdev, "host1cfg");
|
||||
if (IS_ERR(bootreg))
|
||||
return PTR_ERR(bootreg);
|
||||
|
||||
chipsig_res = platform_get_resource_byname(pdev, IORESOURCE_MEM,
|
||||
"chipsig");
|
||||
chipsig = devm_ioremap_resource(dev, chipsig_res);
|
||||
chipsig = devm_platform_ioremap_resource_byname(pdev, "chipsig");
|
||||
if (IS_ERR(chipsig))
|
||||
return PTR_ERR(chipsig);
|
||||
|
||||
|
||||
@@ -509,7 +509,7 @@ static int imx_dsp_rproc_mbox_alloc(struct imx_dsp_rproc *priv)
|
||||
struct mbox_client *cl;
|
||||
int ret;
|
||||
|
||||
if (!of_get_property(dev->of_node, "mbox-names", NULL))
|
||||
if (!of_property_present(dev->of_node, "mbox-names"))
|
||||
return 0;
|
||||
|
||||
cl = &priv->cl;
|
||||
|
||||
@@ -18,6 +18,7 @@
|
||||
#include <linux/of_reserved_mem.h>
|
||||
#include <linux/platform_device.h>
|
||||
#include <linux/pm_domain.h>
|
||||
#include <linux/reboot.h>
|
||||
#include <linux/regmap.h>
|
||||
#include <linux/remoteproc.h>
|
||||
#include <linux/workqueue.h>
|
||||
@@ -90,7 +91,7 @@ struct imx_rproc_mem {
|
||||
#define ATT_CORE_MASK 0xffff
|
||||
#define ATT_CORE(I) BIT((I))
|
||||
|
||||
static int imx_rproc_xtr_mbox_init(struct rproc *rproc);
|
||||
static int imx_rproc_xtr_mbox_init(struct rproc *rproc, bool tx_block);
|
||||
static void imx_rproc_free_mbox(struct rproc *rproc);
|
||||
|
||||
struct imx_rproc {
|
||||
@@ -119,20 +120,16 @@ struct imx_rproc {
|
||||
static const struct imx_rproc_att imx_rproc_att_imx93[] = {
|
||||
/* dev addr , sys addr , size , flags */
|
||||
/* TCM CODE NON-SECURE */
|
||||
{ 0x0FFC0000, 0x201C0000, 0x00020000, ATT_OWN | ATT_IOMEM },
|
||||
{ 0x0FFE0000, 0x201E0000, 0x00020000, ATT_OWN | ATT_IOMEM },
|
||||
{ 0x0FFC0000, 0x201C0000, 0x00040000, ATT_OWN | ATT_IOMEM },
|
||||
|
||||
/* TCM CODE SECURE */
|
||||
{ 0x1FFC0000, 0x201C0000, 0x00020000, ATT_OWN | ATT_IOMEM },
|
||||
{ 0x1FFE0000, 0x201E0000, 0x00020000, ATT_OWN | ATT_IOMEM },
|
||||
{ 0x1FFC0000, 0x201C0000, 0x00040000, ATT_OWN | ATT_IOMEM },
|
||||
|
||||
/* TCM SYS NON-SECURE*/
|
||||
{ 0x20000000, 0x20200000, 0x00020000, ATT_OWN | ATT_IOMEM },
|
||||
{ 0x20020000, 0x20220000, 0x00020000, ATT_OWN | ATT_IOMEM },
|
||||
{ 0x20000000, 0x20200000, 0x00040000, ATT_OWN | ATT_IOMEM },
|
||||
|
||||
/* TCM SYS SECURE*/
|
||||
{ 0x30000000, 0x20200000, 0x00020000, ATT_OWN | ATT_IOMEM },
|
||||
{ 0x30020000, 0x20220000, 0x00020000, ATT_OWN | ATT_IOMEM },
|
||||
{ 0x30000000, 0x20200000, 0x00040000, ATT_OWN | ATT_IOMEM },
|
||||
|
||||
/* DDR */
|
||||
{ 0x80000000, 0x80000000, 0x10000000, 0 },
|
||||
@@ -210,11 +207,9 @@ static const struct imx_rproc_att imx_rproc_att_imx8mq[] = {
|
||||
/* QSPI Code - alias */
|
||||
{ 0x08000000, 0x08000000, 0x08000000, 0 },
|
||||
/* DDR (Code) - alias */
|
||||
{ 0x10000000, 0x80000000, 0x0FFE0000, 0 },
|
||||
/* TCML */
|
||||
{ 0x1FFE0000, 0x007E0000, 0x00020000, ATT_OWN | ATT_IOMEM},
|
||||
/* TCMU */
|
||||
{ 0x20000000, 0x00800000, 0x00020000, ATT_OWN | ATT_IOMEM},
|
||||
{ 0x10000000, 0x40000000, 0x0FFE0000, 0 },
|
||||
/* TCML/U */
|
||||
{ 0x1FFE0000, 0x007E0000, 0x00040000, ATT_OWN | ATT_IOMEM},
|
||||
/* OCRAM_S */
|
||||
{ 0x20180000, 0x00180000, 0x00008000, ATT_OWN },
|
||||
/* OCRAM */
|
||||
@@ -339,6 +334,7 @@ static const struct imx_rproc_dcfg imx_rproc_cfg_imx7ulp = {
|
||||
.att = imx_rproc_att_imx7ulp,
|
||||
.att_size = ARRAY_SIZE(imx_rproc_att_imx7ulp),
|
||||
.method = IMX_RPROC_NONE,
|
||||
.flags = IMX_RPROC_NEED_SYSTEM_OFF,
|
||||
};
|
||||
|
||||
static const struct imx_rproc_dcfg imx_rproc_cfg_imx7d = {
|
||||
@@ -375,7 +371,7 @@ static int imx_rproc_start(struct rproc *rproc)
|
||||
struct arm_smccc_res res;
|
||||
int ret;
|
||||
|
||||
ret = imx_rproc_xtr_mbox_init(rproc);
|
||||
ret = imx_rproc_xtr_mbox_init(rproc, true);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
@@ -635,7 +631,7 @@ static void imx_rproc_kick(struct rproc *rproc, int vqid)
|
||||
|
||||
static int imx_rproc_attach(struct rproc *rproc)
|
||||
{
|
||||
return imx_rproc_xtr_mbox_init(rproc);
|
||||
return imx_rproc_xtr_mbox_init(rproc, true);
|
||||
}
|
||||
|
||||
static int imx_rproc_detach(struct rproc *rproc)
|
||||
@@ -666,6 +662,17 @@ static struct resource_table *imx_rproc_get_loaded_rsc_table(struct rproc *rproc
|
||||
return (struct resource_table *)priv->rsc_table;
|
||||
}
|
||||
|
||||
static struct resource_table *
|
||||
imx_rproc_elf_find_loaded_rsc_table(struct rproc *rproc, const struct firmware *fw)
|
||||
{
|
||||
struct imx_rproc *priv = rproc->priv;
|
||||
|
||||
if (priv->rsc_table)
|
||||
return (struct resource_table *)priv->rsc_table;
|
||||
|
||||
return rproc_elf_find_loaded_rsc_table(rproc, fw);
|
||||
}
|
||||
|
||||
static const struct rproc_ops imx_rproc_ops = {
|
||||
.prepare = imx_rproc_prepare,
|
||||
.attach = imx_rproc_attach,
|
||||
@@ -676,7 +683,7 @@ static const struct rproc_ops imx_rproc_ops = {
|
||||
.da_to_va = imx_rproc_da_to_va,
|
||||
.load = rproc_elf_load_segments,
|
||||
.parse_fw = imx_rproc_parse_fw,
|
||||
.find_loaded_rsc_table = rproc_elf_find_loaded_rsc_table,
|
||||
.find_loaded_rsc_table = imx_rproc_elf_find_loaded_rsc_table,
|
||||
.get_loaded_rsc_table = imx_rproc_get_loaded_rsc_table,
|
||||
.sanity_check = rproc_elf_sanity_check,
|
||||
.get_boot_addr = rproc_elf_get_boot_addr,
|
||||
@@ -789,7 +796,7 @@ static void imx_rproc_rx_callback(struct mbox_client *cl, void *msg)
|
||||
queue_work(priv->workqueue, &priv->rproc_work);
|
||||
}
|
||||
|
||||
static int imx_rproc_xtr_mbox_init(struct rproc *rproc)
|
||||
static int imx_rproc_xtr_mbox_init(struct rproc *rproc, bool tx_block)
|
||||
{
|
||||
struct imx_rproc *priv = rproc->priv;
|
||||
struct device *dev = priv->dev;
|
||||
@@ -807,12 +814,12 @@ static int imx_rproc_xtr_mbox_init(struct rproc *rproc)
|
||||
if (priv->tx_ch && priv->rx_ch)
|
||||
return 0;
|
||||
|
||||
if (!of_get_property(dev->of_node, "mbox-names", NULL))
|
||||
if (!of_property_present(dev->of_node, "mbox-names"))
|
||||
return 0;
|
||||
|
||||
cl = &priv->cl;
|
||||
cl->dev = dev;
|
||||
cl->tx_block = true;
|
||||
cl->tx_block = tx_block;
|
||||
cl->tx_tout = 100;
|
||||
cl->knows_txdone = false;
|
||||
cl->rx_callback = imx_rproc_rx_callback;
|
||||
@@ -1045,6 +1052,22 @@ static int imx_rproc_clk_enable(struct imx_rproc *priv)
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int imx_rproc_sys_off_handler(struct sys_off_data *data)
|
||||
{
|
||||
struct rproc *rproc = data->cb_data;
|
||||
int ret;
|
||||
|
||||
imx_rproc_free_mbox(rproc);
|
||||
|
||||
ret = imx_rproc_xtr_mbox_init(rproc, false);
|
||||
if (ret) {
|
||||
dev_err(&rproc->dev, "Failed to request non-blocking mbox\n");
|
||||
return NOTIFY_BAD;
|
||||
}
|
||||
|
||||
return NOTIFY_DONE;
|
||||
}
|
||||
|
||||
static int imx_rproc_probe(struct platform_device *pdev)
|
||||
{
|
||||
struct device *dev = &pdev->dev;
|
||||
@@ -1076,7 +1099,9 @@ static int imx_rproc_probe(struct platform_device *pdev)
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
ret = imx_rproc_xtr_mbox_init(rproc);
|
||||
INIT_WORK(&priv->rproc_work, imx_rproc_vq_work);
|
||||
|
||||
ret = imx_rproc_xtr_mbox_init(rproc, true);
|
||||
if (ret)
|
||||
goto err_put_wkq;
|
||||
|
||||
@@ -1094,11 +1119,33 @@ static int imx_rproc_probe(struct platform_device *pdev)
|
||||
if (ret)
|
||||
goto err_put_scu;
|
||||
|
||||
INIT_WORK(&priv->rproc_work, imx_rproc_vq_work);
|
||||
|
||||
if (rproc->state != RPROC_DETACHED)
|
||||
rproc->auto_boot = of_property_read_bool(np, "fsl,auto-boot");
|
||||
|
||||
if (dcfg->flags & IMX_RPROC_NEED_SYSTEM_OFF) {
|
||||
/*
|
||||
* setup mailbox to non-blocking mode in
|
||||
* [SYS_OFF_MODE_POWER_OFF_PREPARE, SYS_OFF_MODE_RESTART_PREPARE]
|
||||
* phase before invoking [SYS_OFF_MODE_POWER_OFF, SYS_OFF_MODE_RESTART]
|
||||
* atomic chain, see kernel/reboot.c.
|
||||
*/
|
||||
ret = devm_register_sys_off_handler(dev, SYS_OFF_MODE_POWER_OFF_PREPARE,
|
||||
SYS_OFF_PRIO_DEFAULT,
|
||||
imx_rproc_sys_off_handler, rproc);
|
||||
if (ret) {
|
||||
dev_err(dev, "register power off handler failure\n");
|
||||
goto err_put_clk;
|
||||
}
|
||||
|
||||
ret = devm_register_sys_off_handler(dev, SYS_OFF_MODE_RESTART_PREPARE,
|
||||
SYS_OFF_PRIO_DEFAULT,
|
||||
imx_rproc_sys_off_handler, rproc);
|
||||
if (ret) {
|
||||
dev_err(dev, "register restart handler failure\n");
|
||||
goto err_put_clk;
|
||||
}
|
||||
}
|
||||
|
||||
ret = rproc_add(rproc);
|
||||
if (ret) {
|
||||
dev_err(dev, "rproc_add failed\n");
|
||||
|
||||
@@ -26,6 +26,9 @@ enum imx_rproc_method {
|
||||
IMX_RPROC_SCU_API,
|
||||
};
|
||||
|
||||
/* dcfg flags */
|
||||
#define IMX_RPROC_NEED_SYSTEM_OFF BIT(0)
|
||||
|
||||
struct imx_rproc_dcfg {
|
||||
u32 src_reg;
|
||||
u32 src_mask;
|
||||
@@ -36,6 +39,7 @@ struct imx_rproc_dcfg {
|
||||
const struct imx_rproc_att *att;
|
||||
size_t att_size;
|
||||
enum imx_rproc_method method;
|
||||
u32 flags;
|
||||
};
|
||||
|
||||
#endif /* _IMX_RPROC_H */
|
||||
|
||||
@@ -183,8 +183,7 @@ static int ingenic_rproc_probe(struct platform_device *pdev)
|
||||
vpu->dev = &pdev->dev;
|
||||
platform_set_drvdata(pdev, vpu);
|
||||
|
||||
mem = platform_get_resource_byname(pdev, IORESOURCE_MEM, "aux");
|
||||
vpu->aux_base = devm_ioremap_resource(dev, mem);
|
||||
vpu->aux_base = devm_platform_ioremap_resource_byname(pdev, "aux");
|
||||
if (IS_ERR(vpu->aux_base)) {
|
||||
dev_err(dev, "Failed to ioremap\n");
|
||||
return PTR_ERR(vpu->aux_base);
|
||||
|
||||
@@ -366,8 +366,6 @@ static int keystone_rproc_probe(struct platform_device *pdev)
|
||||
struct rproc *rproc;
|
||||
int dsp_id;
|
||||
char *fw_name = NULL;
|
||||
char *template = "keystone-dsp%d-fw";
|
||||
int name_len = 0;
|
||||
int ret = 0;
|
||||
|
||||
if (!np) {
|
||||
@@ -382,14 +380,12 @@ static int keystone_rproc_probe(struct platform_device *pdev)
|
||||
}
|
||||
|
||||
/* construct a custom default fw name - subject to change in future */
|
||||
name_len = strlen(template); /* assuming a single digit alias */
|
||||
fw_name = devm_kzalloc(dev, name_len, GFP_KERNEL);
|
||||
fw_name = devm_kasprintf(dev, GFP_KERNEL, "keystone-dsp%d-fw", dsp_id);
|
||||
if (!fw_name)
|
||||
return -ENOMEM;
|
||||
snprintf(fw_name, name_len, template, dsp_id);
|
||||
|
||||
rproc = rproc_alloc(dev, dev_name(dev), &keystone_rproc_ops, fw_name,
|
||||
sizeof(*ksproc));
|
||||
rproc = devm_rproc_alloc(dev, dev_name(dev), &keystone_rproc_ops,
|
||||
fw_name, sizeof(*ksproc));
|
||||
if (!rproc)
|
||||
return -ENOMEM;
|
||||
|
||||
@@ -400,13 +396,11 @@ static int keystone_rproc_probe(struct platform_device *pdev)
|
||||
|
||||
ret = keystone_rproc_of_get_dev_syscon(pdev, ksproc);
|
||||
if (ret)
|
||||
goto free_rproc;
|
||||
return ret;
|
||||
|
||||
ksproc->reset = devm_reset_control_get_exclusive(dev, NULL);
|
||||
if (IS_ERR(ksproc->reset)) {
|
||||
ret = PTR_ERR(ksproc->reset);
|
||||
goto free_rproc;
|
||||
}
|
||||
if (IS_ERR(ksproc->reset))
|
||||
return PTR_ERR(ksproc->reset);
|
||||
|
||||
/* enable clock for accessing DSP internal memories */
|
||||
pm_runtime_enable(dev);
|
||||
@@ -471,8 +465,6 @@ disable_clk:
|
||||
pm_runtime_put_sync(dev);
|
||||
disable_rpm:
|
||||
pm_runtime_disable(dev);
|
||||
free_rproc:
|
||||
rproc_free(rproc);
|
||||
return ret;
|
||||
}
|
||||
|
||||
@@ -484,7 +476,6 @@ static void keystone_rproc_remove(struct platform_device *pdev)
|
||||
gpiod_put(ksproc->kick_gpio);
|
||||
pm_runtime_put_sync(&pdev->dev);
|
||||
pm_runtime_disable(&pdev->dev);
|
||||
rproc_free(ksproc->rproc);
|
||||
of_reserved_mem_device_release(&pdev->dev);
|
||||
}
|
||||
|
||||
|
||||
@@ -829,6 +829,23 @@ static const struct adsp_data adsp_resource_init = {
|
||||
.ssctl_id = 0x14,
|
||||
};
|
||||
|
||||
static const struct adsp_data sa8775p_adsp_resource = {
|
||||
.crash_reason_smem = 423,
|
||||
.firmware_name = "adsp.mbn",
|
||||
.pas_id = 1,
|
||||
.minidump_id = 5,
|
||||
.auto_boot = true,
|
||||
.proxy_pd_names = (char*[]){
|
||||
"lcx",
|
||||
"lmx",
|
||||
NULL
|
||||
},
|
||||
.load_state = "adsp",
|
||||
.ssr_name = "lpass",
|
||||
.sysmon_name = "adsp",
|
||||
.ssctl_id = 0x14,
|
||||
};
|
||||
|
||||
static const struct adsp_data sdm845_adsp_resource_init = {
|
||||
.crash_reason_smem = 423,
|
||||
.firmware_name = "adsp.mdt",
|
||||
@@ -942,6 +959,42 @@ static const struct adsp_data cdsp_resource_init = {
|
||||
.ssctl_id = 0x17,
|
||||
};
|
||||
|
||||
static const struct adsp_data sa8775p_cdsp0_resource = {
|
||||
.crash_reason_smem = 601,
|
||||
.firmware_name = "cdsp0.mbn",
|
||||
.pas_id = 18,
|
||||
.minidump_id = 7,
|
||||
.auto_boot = true,
|
||||
.proxy_pd_names = (char*[]){
|
||||
"cx",
|
||||
"mxc",
|
||||
"nsp",
|
||||
NULL
|
||||
},
|
||||
.load_state = "cdsp",
|
||||
.ssr_name = "cdsp",
|
||||
.sysmon_name = "cdsp",
|
||||
.ssctl_id = 0x17,
|
||||
};
|
||||
|
||||
static const struct adsp_data sa8775p_cdsp1_resource = {
|
||||
.crash_reason_smem = 633,
|
||||
.firmware_name = "cdsp1.mbn",
|
||||
.pas_id = 30,
|
||||
.minidump_id = 20,
|
||||
.auto_boot = true,
|
||||
.proxy_pd_names = (char*[]){
|
||||
"cx",
|
||||
"mxc",
|
||||
"nsp",
|
||||
NULL
|
||||
},
|
||||
.load_state = "nsp",
|
||||
.ssr_name = "cdsp1",
|
||||
.sysmon_name = "cdsp1",
|
||||
.ssctl_id = 0x20,
|
||||
};
|
||||
|
||||
static const struct adsp_data sdm845_cdsp_resource_init = {
|
||||
.crash_reason_smem = 601,
|
||||
.firmware_name = "cdsp.mdt",
|
||||
@@ -1083,6 +1136,40 @@ static const struct adsp_data sm8350_cdsp_resource = {
|
||||
.ssctl_id = 0x17,
|
||||
};
|
||||
|
||||
static const struct adsp_data sa8775p_gpdsp0_resource = {
|
||||
.crash_reason_smem = 640,
|
||||
.firmware_name = "gpdsp0.mbn",
|
||||
.pas_id = 39,
|
||||
.minidump_id = 21,
|
||||
.auto_boot = true,
|
||||
.proxy_pd_names = (char*[]){
|
||||
"cx",
|
||||
"mxc",
|
||||
NULL
|
||||
},
|
||||
.load_state = "gpdsp0",
|
||||
.ssr_name = "gpdsp0",
|
||||
.sysmon_name = "gpdsp0",
|
||||
.ssctl_id = 0x21,
|
||||
};
|
||||
|
||||
static const struct adsp_data sa8775p_gpdsp1_resource = {
|
||||
.crash_reason_smem = 641,
|
||||
.firmware_name = "gpdsp1.mbn",
|
||||
.pas_id = 40,
|
||||
.minidump_id = 22,
|
||||
.auto_boot = true,
|
||||
.proxy_pd_names = (char*[]){
|
||||
"cx",
|
||||
"mxc",
|
||||
NULL
|
||||
},
|
||||
.load_state = "gpdsp1",
|
||||
.ssr_name = "gpdsp1",
|
||||
.sysmon_name = "gpdsp1",
|
||||
.ssctl_id = 0x22,
|
||||
};
|
||||
|
||||
static const struct adsp_data mpss_resource_init = {
|
||||
.crash_reason_smem = 421,
|
||||
.firmware_name = "modem.mdt",
|
||||
@@ -1329,6 +1416,11 @@ static const struct of_device_id adsp_of_match[] = {
|
||||
{ .compatible = "qcom,qcs404-adsp-pas", .data = &adsp_resource_init },
|
||||
{ .compatible = "qcom,qcs404-cdsp-pas", .data = &cdsp_resource_init },
|
||||
{ .compatible = "qcom,qcs404-wcss-pas", .data = &wcss_resource_init },
|
||||
{ .compatible = "qcom,sa8775p-adsp-pas", .data = &sa8775p_adsp_resource},
|
||||
{ .compatible = "qcom,sa8775p-cdsp0-pas", .data = &sa8775p_cdsp0_resource},
|
||||
{ .compatible = "qcom,sa8775p-cdsp1-pas", .data = &sa8775p_cdsp1_resource},
|
||||
{ .compatible = "qcom,sa8775p-gpdsp0-pas", .data = &sa8775p_gpdsp0_resource},
|
||||
{ .compatible = "qcom,sa8775p-gpdsp1-pas", .data = &sa8775p_gpdsp1_resource},
|
||||
{ .compatible = "qcom,sc7180-adsp-pas", .data = &sm8250_adsp_resource},
|
||||
{ .compatible = "qcom,sc7180-mpss-pas", .data = &mpss_resource_init},
|
||||
{ .compatible = "qcom,sc7280-adsp-pas", .data = &sm8350_adsp_resource},
|
||||
@@ -1346,6 +1438,7 @@ static const struct of_device_id adsp_of_match[] = {
|
||||
{ .compatible = "qcom,sdm845-cdsp-pas", .data = &sdm845_cdsp_resource_init},
|
||||
{ .compatible = "qcom,sdm845-slpi-pas", .data = &sdm845_slpi_resource_init},
|
||||
{ .compatible = "qcom,sdx55-mpss-pas", .data = &sdx55_mpss_resource},
|
||||
{ .compatible = "qcom,sdx75-mpss-pas", .data = &sm8650_mpss_resource},
|
||||
{ .compatible = "qcom,sm6115-adsp-pas", .data = &adsp_resource_init},
|
||||
{ .compatible = "qcom,sm6115-cdsp-pas", .data = &cdsp_resource_init},
|
||||
{ .compatible = "qcom,sm6115-mpss-pas", .data = &sc8180x_mpss_resource},
|
||||
|
||||
@@ -259,16 +259,14 @@ struct st_slim_rproc *st_slim_rproc_alloc(struct platform_device *pdev,
|
||||
slim_rproc->mem[i].size = resource_size(res);
|
||||
}
|
||||
|
||||
res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "slimcore");
|
||||
slim_rproc->slimcore = devm_ioremap_resource(dev, res);
|
||||
slim_rproc->slimcore = devm_platform_ioremap_resource_byname(pdev, "slimcore");
|
||||
if (IS_ERR(slim_rproc->slimcore)) {
|
||||
dev_err(&pdev->dev, "failed to ioremap slimcore IO\n");
|
||||
err = PTR_ERR(slim_rproc->slimcore);
|
||||
goto err;
|
||||
}
|
||||
|
||||
res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "peripherals");
|
||||
slim_rproc->peri = devm_ioremap_resource(dev, res);
|
||||
slim_rproc->peri = devm_platform_ioremap_resource_byname(pdev, "peripherals");
|
||||
if (IS_ERR(slim_rproc->peri)) {
|
||||
dev_err(&pdev->dev, "failed to ioremap peripherals IO\n");
|
||||
err = PTR_ERR(slim_rproc->peri);
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user