mirror of
https://github.com/torvalds/linux.git
synced 2024-11-10 14:11:52 +00:00
Merge branch 'next' into for-linus
Prepare input updates for 6.9 merge window.
This commit is contained in:
commit
57ed9567e6
@ -49,7 +49,6 @@ patternProperties:
|
|||||||
$ref: input.yaml#
|
$ref: input.yaml#
|
||||||
properties:
|
properties:
|
||||||
label:
|
label:
|
||||||
$ref: /schemas/types.yaml#/definitions/string
|
|
||||||
description: Descriptive name of the key
|
description: Descriptive name of the key
|
||||||
|
|
||||||
linux,code: true
|
linux,code: true
|
||||||
|
@ -1,36 +0,0 @@
|
|||||||
Device tree bindings for Atmel capacitive touch device, typically
|
|
||||||
an Atmel touch sensor connected to AtmegaXX MCU running firmware
|
|
||||||
based on Qtouch library.
|
|
||||||
|
|
||||||
The node for this device must be a child of a I2C controller node, as the
|
|
||||||
device communicates via I2C.
|
|
||||||
|
|
||||||
Required properties:
|
|
||||||
|
|
||||||
compatible: Must be "atmel,captouch".
|
|
||||||
reg: The I2C slave address of the device.
|
|
||||||
interrupts: Property describing the interrupt line the device
|
|
||||||
is connected to. The device only has one interrupt
|
|
||||||
source.
|
|
||||||
linux,keycodes: Specifies an array of numeric keycode values to
|
|
||||||
be used for reporting button presses. The array can
|
|
||||||
contain up to 8 entries.
|
|
||||||
|
|
||||||
Optional properties:
|
|
||||||
|
|
||||||
autorepeat: Enables the Linux input system's autorepeat
|
|
||||||
feature on the input device.
|
|
||||||
|
|
||||||
Example:
|
|
||||||
|
|
||||||
atmel-captouch@51 {
|
|
||||||
compatible = "atmel,captouch";
|
|
||||||
reg = <0x51>;
|
|
||||||
interrupt-parent = <&tlmm>;
|
|
||||||
interrupts = <67 IRQ_TYPE_EDGE_FALLING>;
|
|
||||||
linux,keycodes = <BTN_0>, <BTN_1>,
|
|
||||||
<BTN_2>, <BTN_3>,
|
|
||||||
<BTN_4>, <BTN_5>,
|
|
||||||
<BTN_6>, <BTN_7>;
|
|
||||||
autorepeat;
|
|
||||||
};
|
|
59
Documentation/devicetree/bindings/input/atmel,captouch.yaml
Normal file
59
Documentation/devicetree/bindings/input/atmel,captouch.yaml
Normal file
@ -0,0 +1,59 @@
|
|||||||
|
# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
|
||||||
|
%YAML 1.2
|
||||||
|
---
|
||||||
|
$id: http://devicetree.org/schemas/input/atmel,captouch.yaml#
|
||||||
|
$schema: http://devicetree.org/meta-schemas/core.yaml#
|
||||||
|
|
||||||
|
title: Atmel capacitive touch device
|
||||||
|
|
||||||
|
maintainers:
|
||||||
|
- Dharma balasubiramani <dharma.b@microchip.com>
|
||||||
|
|
||||||
|
description:
|
||||||
|
Atmel capacitive touch device, typically an Atmel touch sensor connected to
|
||||||
|
AtmegaXX MCU running firmware based on Qtouch library.
|
||||||
|
|
||||||
|
allOf:
|
||||||
|
- $ref: input.yaml#
|
||||||
|
|
||||||
|
properties:
|
||||||
|
compatible:
|
||||||
|
const: atmel,captouch
|
||||||
|
|
||||||
|
reg:
|
||||||
|
maxItems: 1
|
||||||
|
|
||||||
|
interrupts:
|
||||||
|
maxItems: 1
|
||||||
|
|
||||||
|
linux,keycodes:
|
||||||
|
minItems: 1
|
||||||
|
maxItems: 8
|
||||||
|
|
||||||
|
required:
|
||||||
|
- compatible
|
||||||
|
- reg
|
||||||
|
- interrupts
|
||||||
|
- linux,keycodes
|
||||||
|
|
||||||
|
unevaluatedProperties: false
|
||||||
|
|
||||||
|
examples:
|
||||||
|
- |
|
||||||
|
#include <dt-bindings/interrupt-controller/irq.h>
|
||||||
|
#include <dt-bindings/input/linux-event-codes.h>
|
||||||
|
i2c {
|
||||||
|
#address-cells = <1>;
|
||||||
|
#size-cells = <0>;
|
||||||
|
touch@51 {
|
||||||
|
compatible = "atmel,captouch";
|
||||||
|
reg = <0x51>;
|
||||||
|
interrupt-parent = <&tlmm>;
|
||||||
|
interrupts = <67 IRQ_TYPE_EDGE_FALLING>;
|
||||||
|
linux,keycodes = <BTN_0>, <BTN_1>,
|
||||||
|
<BTN_2>, <BTN_3>,
|
||||||
|
<BTN_4>, <BTN_5>,
|
||||||
|
<BTN_6>, <BTN_7>;
|
||||||
|
autorepeat;
|
||||||
|
};
|
||||||
|
};
|
@ -0,0 +1,121 @@
|
|||||||
|
# SPDX-License-Identifier: GPL-2.0-only OR BSD-2-Clause
|
||||||
|
%YAML 1.2
|
||||||
|
---
|
||||||
|
$id: http://devicetree.org/schemas/input/samsung,s3c6410-keypad.yaml#
|
||||||
|
$schema: http://devicetree.org/meta-schemas/core.yaml#
|
||||||
|
|
||||||
|
title: Samsung SoC series Keypad Controller
|
||||||
|
|
||||||
|
description:
|
||||||
|
Samsung SoC Keypad controller is used to interface a SoC with a matrix-type
|
||||||
|
keypad device. The keypad controller supports multiple row and column lines.
|
||||||
|
A key can be placed at each intersection of a unique row and a unique column.
|
||||||
|
The keypad controller can sense a key-press and key-release and report the
|
||||||
|
event using a interrupt to the cpu.
|
||||||
|
|
||||||
|
maintainers:
|
||||||
|
- Krzysztof Kozlowski <krzk@kernel.org>
|
||||||
|
|
||||||
|
properties:
|
||||||
|
compatible:
|
||||||
|
enum:
|
||||||
|
- samsung,s3c6410-keypad
|
||||||
|
- samsung,s5pv210-keypad
|
||||||
|
|
||||||
|
reg:
|
||||||
|
maxItems: 1
|
||||||
|
|
||||||
|
clocks:
|
||||||
|
maxItems: 1
|
||||||
|
|
||||||
|
clock-names:
|
||||||
|
items:
|
||||||
|
- const: keypad
|
||||||
|
|
||||||
|
interrupts:
|
||||||
|
maxItems: 1
|
||||||
|
|
||||||
|
wakeup-source: true
|
||||||
|
|
||||||
|
linux,input-no-autorepeat:
|
||||||
|
type: boolean
|
||||||
|
description:
|
||||||
|
Do no enable autorepeat feature.
|
||||||
|
|
||||||
|
linux,input-wakeup:
|
||||||
|
type: boolean
|
||||||
|
deprecated: true
|
||||||
|
|
||||||
|
samsung,keypad-num-columns:
|
||||||
|
$ref: /schemas/types.yaml#/definitions/uint32
|
||||||
|
description:
|
||||||
|
Number of column lines connected to the keypad controller.
|
||||||
|
|
||||||
|
samsung,keypad-num-rows:
|
||||||
|
$ref: /schemas/types.yaml#/definitions/uint32
|
||||||
|
description:
|
||||||
|
Number of row lines connected to the keypad controller.
|
||||||
|
|
||||||
|
patternProperties:
|
||||||
|
'^key-[0-9a-z]+$':
|
||||||
|
type: object
|
||||||
|
$ref: input.yaml#
|
||||||
|
additionalProperties: false
|
||||||
|
description:
|
||||||
|
Each key connected to the keypad controller is represented as a child
|
||||||
|
node to the keypad controller device node.
|
||||||
|
|
||||||
|
properties:
|
||||||
|
keypad,column:
|
||||||
|
$ref: /schemas/types.yaml#/definitions/uint32
|
||||||
|
description: The column number to which the key is connected.
|
||||||
|
|
||||||
|
keypad,row:
|
||||||
|
$ref: /schemas/types.yaml#/definitions/uint32
|
||||||
|
description: The row number to which the key is connected.
|
||||||
|
|
||||||
|
linux,code: true
|
||||||
|
|
||||||
|
required:
|
||||||
|
- keypad,column
|
||||||
|
- keypad,row
|
||||||
|
- linux,code
|
||||||
|
|
||||||
|
required:
|
||||||
|
- compatible
|
||||||
|
- reg
|
||||||
|
- interrupts
|
||||||
|
- samsung,keypad-num-columns
|
||||||
|
- samsung,keypad-num-rows
|
||||||
|
|
||||||
|
additionalProperties: false
|
||||||
|
|
||||||
|
examples:
|
||||||
|
- |
|
||||||
|
#include <dt-bindings/clock/exynos4.h>
|
||||||
|
#include <dt-bindings/interrupt-controller/arm-gic.h>
|
||||||
|
|
||||||
|
keypad@100a0000 {
|
||||||
|
compatible = "samsung,s5pv210-keypad";
|
||||||
|
reg = <0x100a0000 0x100>;
|
||||||
|
interrupts = <GIC_SPI 109 IRQ_TYPE_LEVEL_HIGH>;
|
||||||
|
clocks = <&clock CLK_KEYIF>;
|
||||||
|
clock-names = "keypad";
|
||||||
|
|
||||||
|
samsung,keypad-num-rows = <2>;
|
||||||
|
samsung,keypad-num-columns = <8>;
|
||||||
|
linux,input-no-autorepeat;
|
||||||
|
wakeup-source;
|
||||||
|
|
||||||
|
key-1 {
|
||||||
|
keypad,row = <0>;
|
||||||
|
keypad,column = <3>;
|
||||||
|
linux,code = <2>;
|
||||||
|
};
|
||||||
|
|
||||||
|
key-2 {
|
||||||
|
keypad,row = <0>;
|
||||||
|
keypad,column = <4>;
|
||||||
|
linux,code = <3>;
|
||||||
|
};
|
||||||
|
};
|
@ -1,77 +0,0 @@
|
|||||||
* Samsung's Keypad Controller device tree bindings
|
|
||||||
|
|
||||||
Samsung's Keypad controller is used to interface a SoC with a matrix-type
|
|
||||||
keypad device. The keypad controller supports multiple row and column lines.
|
|
||||||
A key can be placed at each intersection of a unique row and a unique column.
|
|
||||||
The keypad controller can sense a key-press and key-release and report the
|
|
||||||
event using a interrupt to the cpu.
|
|
||||||
|
|
||||||
Required SoC Specific Properties:
|
|
||||||
- compatible: should be one of the following
|
|
||||||
- "samsung,s3c6410-keypad": For controllers compatible with s3c6410 keypad
|
|
||||||
controller.
|
|
||||||
- "samsung,s5pv210-keypad": For controllers compatible with s5pv210 keypad
|
|
||||||
controller.
|
|
||||||
|
|
||||||
- reg: physical base address of the controller and length of memory mapped
|
|
||||||
region.
|
|
||||||
|
|
||||||
- interrupts: The interrupt number to the cpu.
|
|
||||||
|
|
||||||
Required Board Specific Properties:
|
|
||||||
- samsung,keypad-num-rows: Number of row lines connected to the keypad
|
|
||||||
controller.
|
|
||||||
|
|
||||||
- samsung,keypad-num-columns: Number of column lines connected to the
|
|
||||||
keypad controller.
|
|
||||||
|
|
||||||
- Keys represented as child nodes: Each key connected to the keypad
|
|
||||||
controller is represented as a child node to the keypad controller
|
|
||||||
device node and should include the following properties.
|
|
||||||
- keypad,row: the row number to which the key is connected.
|
|
||||||
- keypad,column: the column number to which the key is connected.
|
|
||||||
- linux,code: the key-code to be reported when the key is pressed
|
|
||||||
and released.
|
|
||||||
|
|
||||||
- pinctrl-0: Should specify pin control groups used for this controller.
|
|
||||||
- pinctrl-names: Should contain only one value - "default".
|
|
||||||
|
|
||||||
Optional Properties:
|
|
||||||
- wakeup-source: use any event on keypad as wakeup event.
|
|
||||||
(Legacy property supported: "linux,input-wakeup")
|
|
||||||
|
|
||||||
Optional Properties specific to linux:
|
|
||||||
- linux,keypad-no-autorepeat: do no enable autorepeat feature.
|
|
||||||
|
|
||||||
|
|
||||||
Example:
|
|
||||||
keypad@100a0000 {
|
|
||||||
compatible = "samsung,s5pv210-keypad";
|
|
||||||
reg = <0x100A0000 0x100>;
|
|
||||||
interrupts = <173>;
|
|
||||||
samsung,keypad-num-rows = <2>;
|
|
||||||
samsung,keypad-num-columns = <8>;
|
|
||||||
linux,input-no-autorepeat;
|
|
||||||
wakeup-source;
|
|
||||||
|
|
||||||
pinctrl-names = "default";
|
|
||||||
pinctrl-0 = <&keypad_rows &keypad_columns>;
|
|
||||||
|
|
||||||
key_1 {
|
|
||||||
keypad,row = <0>;
|
|
||||||
keypad,column = <3>;
|
|
||||||
linux,code = <2>;
|
|
||||||
};
|
|
||||||
|
|
||||||
key_2 {
|
|
||||||
keypad,row = <0>;
|
|
||||||
keypad,column = <4>;
|
|
||||||
linux,code = <3>;
|
|
||||||
};
|
|
||||||
|
|
||||||
key_3 {
|
|
||||||
keypad,row = <0>;
|
|
||||||
keypad,column = <5>;
|
|
||||||
linux,code = <4>;
|
|
||||||
};
|
|
||||||
};
|
|
@ -0,0 +1,95 @@
|
|||||||
|
# SPDX-License-Identifier: GPL-2.0-only OR BSD-2-Clause
|
||||||
|
%YAML 1.2
|
||||||
|
---
|
||||||
|
$id: http://devicetree.org/schemas/input/touchscreen/goodix,gt9916.yaml#
|
||||||
|
$schema: http://devicetree.org/meta-schemas/core.yaml#
|
||||||
|
|
||||||
|
title: Goodix Berlin series touchscreen controller
|
||||||
|
|
||||||
|
description: The Goodix Berlin series of touchscreen controllers
|
||||||
|
be connected to either I2C or SPI buses.
|
||||||
|
|
||||||
|
maintainers:
|
||||||
|
- Neil Armstrong <neil.armstrong@linaro.org>
|
||||||
|
|
||||||
|
allOf:
|
||||||
|
- $ref: touchscreen.yaml#
|
||||||
|
- $ref: /schemas/spi/spi-peripheral-props.yaml#
|
||||||
|
|
||||||
|
properties:
|
||||||
|
compatible:
|
||||||
|
enum:
|
||||||
|
- goodix,gt9916
|
||||||
|
|
||||||
|
reg:
|
||||||
|
maxItems: 1
|
||||||
|
|
||||||
|
interrupts:
|
||||||
|
maxItems: 1
|
||||||
|
|
||||||
|
reset-gpios:
|
||||||
|
maxItems: 1
|
||||||
|
|
||||||
|
avdd-supply:
|
||||||
|
description: Analog power supply regulator on AVDD pin
|
||||||
|
|
||||||
|
vddio-supply:
|
||||||
|
description: power supply regulator on VDDIO pin
|
||||||
|
|
||||||
|
spi-max-frequency: true
|
||||||
|
touchscreen-inverted-x: true
|
||||||
|
touchscreen-inverted-y: true
|
||||||
|
touchscreen-size-x: true
|
||||||
|
touchscreen-size-y: true
|
||||||
|
touchscreen-swapped-x-y: true
|
||||||
|
|
||||||
|
additionalProperties: false
|
||||||
|
|
||||||
|
required:
|
||||||
|
- compatible
|
||||||
|
- reg
|
||||||
|
- interrupts
|
||||||
|
- avdd-supply
|
||||||
|
- touchscreen-size-x
|
||||||
|
- touchscreen-size-y
|
||||||
|
|
||||||
|
examples:
|
||||||
|
- |
|
||||||
|
#include <dt-bindings/interrupt-controller/irq.h>
|
||||||
|
#include <dt-bindings/gpio/gpio.h>
|
||||||
|
i2c {
|
||||||
|
#address-cells = <1>;
|
||||||
|
#size-cells = <0>;
|
||||||
|
touchscreen@5d {
|
||||||
|
compatible = "goodix,gt9916";
|
||||||
|
reg = <0x5d>;
|
||||||
|
interrupt-parent = <&gpio>;
|
||||||
|
interrupts = <25 IRQ_TYPE_LEVEL_LOW>;
|
||||||
|
reset-gpios = <&gpio1 1 GPIO_ACTIVE_LOW>;
|
||||||
|
avdd-supply = <&ts_avdd>;
|
||||||
|
touchscreen-size-x = <1024>;
|
||||||
|
touchscreen-size-y = <768>;
|
||||||
|
};
|
||||||
|
};
|
||||||
|
- |
|
||||||
|
#include <dt-bindings/interrupt-controller/irq.h>
|
||||||
|
#include <dt-bindings/gpio/gpio.h>
|
||||||
|
spi {
|
||||||
|
#address-cells = <1>;
|
||||||
|
#size-cells = <0>;
|
||||||
|
num-cs = <1>;
|
||||||
|
cs-gpios = <&gpio 2 GPIO_ACTIVE_HIGH>;
|
||||||
|
touchscreen@0 {
|
||||||
|
compatible = "goodix,gt9916";
|
||||||
|
reg = <0>;
|
||||||
|
interrupt-parent = <&gpio>;
|
||||||
|
interrupts = <25 IRQ_TYPE_LEVEL_LOW>;
|
||||||
|
reset-gpios = <&gpio1 1 GPIO_ACTIVE_LOW>;
|
||||||
|
avdd-supply = <&ts_avdd>;
|
||||||
|
spi-max-frequency = <1000000>;
|
||||||
|
touchscreen-size-x = <1024>;
|
||||||
|
touchscreen-size-y = <768>;
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
...
|
@ -37,8 +37,9 @@ properties:
|
|||||||
maxItems: 1
|
maxItems: 1
|
||||||
|
|
||||||
irq-gpios:
|
irq-gpios:
|
||||||
description: GPIO pin used for IRQ. The driver uses the interrupt gpio pin
|
description: GPIO pin used for IRQ input. Additionally, this line is
|
||||||
as output to reset the device.
|
sampled by the device on reset deassertion to select the I2C client
|
||||||
|
address, thus it can be driven by the host during the reset sequence.
|
||||||
maxItems: 1
|
maxItems: 1
|
||||||
|
|
||||||
reset-gpios:
|
reset-gpios:
|
||||||
|
@ -9,15 +9,14 @@ title: Imagis IST30XXC family touchscreen controller
|
|||||||
maintainers:
|
maintainers:
|
||||||
- Markuss Broks <markuss.broks@gmail.com>
|
- Markuss Broks <markuss.broks@gmail.com>
|
||||||
|
|
||||||
allOf:
|
|
||||||
- $ref: touchscreen.yaml#
|
|
||||||
|
|
||||||
properties:
|
properties:
|
||||||
$nodename:
|
$nodename:
|
||||||
pattern: "^touchscreen@[0-9a-f]+$"
|
pattern: "^touchscreen@[0-9a-f]+$"
|
||||||
|
|
||||||
compatible:
|
compatible:
|
||||||
enum:
|
enum:
|
||||||
|
- imagis,ist3032c
|
||||||
|
- imagis,ist3038b
|
||||||
- imagis,ist3038c
|
- imagis,ist3038c
|
||||||
|
|
||||||
reg:
|
reg:
|
||||||
@ -32,6 +31,10 @@ properties:
|
|||||||
vddio-supply:
|
vddio-supply:
|
||||||
description: Power supply regulator for the I2C bus
|
description: Power supply regulator for the I2C bus
|
||||||
|
|
||||||
|
linux,keycodes:
|
||||||
|
description: Keycodes for the touch keys
|
||||||
|
maxItems: 5
|
||||||
|
|
||||||
touchscreen-size-x: true
|
touchscreen-size-x: true
|
||||||
touchscreen-size-y: true
|
touchscreen-size-y: true
|
||||||
touchscreen-fuzz-x: true
|
touchscreen-fuzz-x: true
|
||||||
@ -42,6 +45,18 @@ properties:
|
|||||||
|
|
||||||
additionalProperties: false
|
additionalProperties: false
|
||||||
|
|
||||||
|
allOf:
|
||||||
|
- $ref: touchscreen.yaml#
|
||||||
|
- if:
|
||||||
|
not:
|
||||||
|
properties:
|
||||||
|
compatible:
|
||||||
|
contains:
|
||||||
|
const: imagis,ist3032c
|
||||||
|
then:
|
||||||
|
properties:
|
||||||
|
linux,keycodes: false
|
||||||
|
|
||||||
required:
|
required:
|
||||||
- compatible
|
- compatible
|
||||||
- reg
|
- reg
|
||||||
|
@ -17,13 +17,17 @@ properties:
|
|||||||
pattern: "^touchscreen(@.*)?$"
|
pattern: "^touchscreen(@.*)?$"
|
||||||
|
|
||||||
compatible:
|
compatible:
|
||||||
items:
|
oneOf:
|
||||||
- enum:
|
- enum:
|
||||||
- melfas,mms114
|
- melfas,mms114
|
||||||
- melfas,mms134s
|
- melfas,mms134s
|
||||||
- melfas,mms136
|
- melfas,mms136
|
||||||
- melfas,mms152
|
- melfas,mms152
|
||||||
- melfas,mms345l
|
- melfas,mms345l
|
||||||
|
- items:
|
||||||
|
- enum:
|
||||||
|
- melfas,mms252
|
||||||
|
- const: melfas,mms114
|
||||||
|
|
||||||
reg:
|
reg:
|
||||||
description: I2C address
|
description: I2C address
|
||||||
|
@ -31,7 +31,7 @@ properties:
|
|||||||
maxItems: 1
|
maxItems: 1
|
||||||
|
|
||||||
firmware-name:
|
firmware-name:
|
||||||
$ref: /schemas/types.yaml#/definitions/string
|
maxItems: 1
|
||||||
description: >
|
description: >
|
||||||
File basename for board specific firmware
|
File basename for board specific firmware
|
||||||
|
|
||||||
|
@ -23,7 +23,7 @@ List of legacy properties and respective binding document
|
|||||||
Documentation/devicetree/bindings/mfd/tc3589x.txt
|
Documentation/devicetree/bindings/mfd/tc3589x.txt
|
||||||
Documentation/devicetree/bindings/input/touchscreen/ads7846.txt
|
Documentation/devicetree/bindings/input/touchscreen/ads7846.txt
|
||||||
4. "linux,keypad-wakeup" Documentation/devicetree/bindings/input/qcom,pm8xxx-keypad.txt
|
4. "linux,keypad-wakeup" Documentation/devicetree/bindings/input/qcom,pm8xxx-keypad.txt
|
||||||
5. "linux,input-wakeup" Documentation/devicetree/bindings/input/samsung-keypad.txt
|
5. "linux,input-wakeup" Documentation/devicetree/bindings/input/samsung,s3c6410-keypad.yaml
|
||||||
6. "nvidia,wakeup-source" Documentation/devicetree/bindings/input/nvidia,tegra20-kbc.txt
|
6. "nvidia,wakeup-source" Documentation/devicetree/bindings/input/nvidia,tegra20-kbc.txt
|
||||||
|
|
||||||
Examples
|
Examples
|
||||||
|
@ -38,7 +38,7 @@ static DEFINE_MUTEX(gameport_mutex);
|
|||||||
|
|
||||||
static LIST_HEAD(gameport_list);
|
static LIST_HEAD(gameport_list);
|
||||||
|
|
||||||
static struct bus_type gameport_bus;
|
static const struct bus_type gameport_bus;
|
||||||
|
|
||||||
static void gameport_add_port(struct gameport *gameport);
|
static void gameport_add_port(struct gameport *gameport);
|
||||||
static void gameport_attach_driver(struct gameport_driver *drv);
|
static void gameport_attach_driver(struct gameport_driver *drv);
|
||||||
@ -813,7 +813,7 @@ static int gameport_bus_match(struct device *dev, struct device_driver *drv)
|
|||||||
return !gameport_drv->ignore;
|
return !gameport_drv->ignore;
|
||||||
}
|
}
|
||||||
|
|
||||||
static struct bus_type gameport_bus = {
|
static const struct bus_type gameport_bus = {
|
||||||
.name = "gameport",
|
.name = "gameport",
|
||||||
.dev_groups = gameport_device_groups,
|
.dev_groups = gameport_device_groups,
|
||||||
.drv_groups = gameport_driver_groups,
|
.drv_groups = gameport_driver_groups,
|
||||||
|
@ -18,6 +18,12 @@
|
|||||||
#define VT_TRIGGER(_name) .trigger = NULL
|
#define VT_TRIGGER(_name) .trigger = NULL
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
#if IS_ENABLED(CONFIG_SND_CTL_LED)
|
||||||
|
#define AUDIO_TRIGGER(_name) .trigger = _name
|
||||||
|
#else
|
||||||
|
#define AUDIO_TRIGGER(_name) .trigger = NULL
|
||||||
|
#endif
|
||||||
|
|
||||||
static const struct {
|
static const struct {
|
||||||
const char *name;
|
const char *name;
|
||||||
const char *trigger;
|
const char *trigger;
|
||||||
@ -29,7 +35,7 @@ static const struct {
|
|||||||
[LED_KANA] = { "kana", VT_TRIGGER("kbd-kanalock") },
|
[LED_KANA] = { "kana", VT_TRIGGER("kbd-kanalock") },
|
||||||
[LED_SLEEP] = { "sleep" } ,
|
[LED_SLEEP] = { "sleep" } ,
|
||||||
[LED_SUSPEND] = { "suspend" },
|
[LED_SUSPEND] = { "suspend" },
|
||||||
[LED_MUTE] = { "mute" },
|
[LED_MUTE] = { "mute", AUDIO_TRIGGER("audio-mute") },
|
||||||
[LED_MISC] = { "misc" },
|
[LED_MISC] = { "misc" },
|
||||||
[LED_MAIL] = { "mail" },
|
[LED_MAIL] = { "mail" },
|
||||||
[LED_CHARGING] = { "charging" },
|
[LED_CHARGING] = { "charging" },
|
||||||
|
@ -1918,7 +1918,7 @@ static char *input_devnode(const struct device *dev, umode_t *mode)
|
|||||||
return kasprintf(GFP_KERNEL, "input/%s", dev_name(dev));
|
return kasprintf(GFP_KERNEL, "input/%s", dev_name(dev));
|
||||||
}
|
}
|
||||||
|
|
||||||
struct class input_class = {
|
const struct class input_class = {
|
||||||
.name = "input",
|
.name = "input",
|
||||||
.devnode = input_devnode,
|
.devnode = input_devnode,
|
||||||
};
|
};
|
||||||
@ -2629,17 +2629,15 @@ int input_get_new_minor(int legacy_base, unsigned int legacy_num,
|
|||||||
* locking is needed here.
|
* locking is needed here.
|
||||||
*/
|
*/
|
||||||
if (legacy_base >= 0) {
|
if (legacy_base >= 0) {
|
||||||
int minor = ida_simple_get(&input_ida,
|
int minor = ida_alloc_range(&input_ida, legacy_base,
|
||||||
legacy_base,
|
legacy_base + legacy_num - 1,
|
||||||
legacy_base + legacy_num,
|
GFP_KERNEL);
|
||||||
GFP_KERNEL);
|
|
||||||
if (minor >= 0 || !allow_dynamic)
|
if (minor >= 0 || !allow_dynamic)
|
||||||
return minor;
|
return minor;
|
||||||
}
|
}
|
||||||
|
|
||||||
return ida_simple_get(&input_ida,
|
return ida_alloc_range(&input_ida, INPUT_FIRST_DYNAMIC_DEV,
|
||||||
INPUT_FIRST_DYNAMIC_DEV, INPUT_MAX_CHAR_DEVICES,
|
INPUT_MAX_CHAR_DEVICES - 1, GFP_KERNEL);
|
||||||
GFP_KERNEL);
|
|
||||||
}
|
}
|
||||||
EXPORT_SYMBOL(input_get_new_minor);
|
EXPORT_SYMBOL(input_get_new_minor);
|
||||||
|
|
||||||
@ -2652,7 +2650,7 @@ EXPORT_SYMBOL(input_get_new_minor);
|
|||||||
*/
|
*/
|
||||||
void input_free_minor(unsigned int minor)
|
void input_free_minor(unsigned int minor)
|
||||||
{
|
{
|
||||||
ida_simple_remove(&input_ida, minor);
|
ida_free(&input_ida, minor);
|
||||||
}
|
}
|
||||||
EXPORT_SYMBOL(input_free_minor);
|
EXPORT_SYMBOL(input_free_minor);
|
||||||
|
|
||||||
|
@ -127,6 +127,7 @@ static const struct xpad_device {
|
|||||||
u8 mapping;
|
u8 mapping;
|
||||||
u8 xtype;
|
u8 xtype;
|
||||||
} xpad_device[] = {
|
} xpad_device[] = {
|
||||||
|
/* Please keep this list sorted by vendor and product ID. */
|
||||||
{ 0x0079, 0x18d4, "GPD Win 2 X-Box Controller", 0, XTYPE_XBOX360 },
|
{ 0x0079, 0x18d4, "GPD Win 2 X-Box Controller", 0, XTYPE_XBOX360 },
|
||||||
{ 0x03eb, 0xff01, "Wooting One (Legacy)", 0, XTYPE_XBOX360 },
|
{ 0x03eb, 0xff01, "Wooting One (Legacy)", 0, XTYPE_XBOX360 },
|
||||||
{ 0x03eb, 0xff02, "Wooting Two (Legacy)", 0, XTYPE_XBOX360 },
|
{ 0x03eb, 0xff02, "Wooting Two (Legacy)", 0, XTYPE_XBOX360 },
|
||||||
@ -152,9 +153,9 @@ static const struct xpad_device {
|
|||||||
{ 0x045e, 0x02d1, "Microsoft X-Box One pad", 0, XTYPE_XBOXONE },
|
{ 0x045e, 0x02d1, "Microsoft X-Box One pad", 0, XTYPE_XBOXONE },
|
||||||
{ 0x045e, 0x02dd, "Microsoft X-Box One pad (Firmware 2015)", 0, XTYPE_XBOXONE },
|
{ 0x045e, 0x02dd, "Microsoft X-Box One pad (Firmware 2015)", 0, XTYPE_XBOXONE },
|
||||||
{ 0x045e, 0x02e3, "Microsoft X-Box One Elite pad", MAP_PADDLES, XTYPE_XBOXONE },
|
{ 0x045e, 0x02e3, "Microsoft X-Box One Elite pad", MAP_PADDLES, XTYPE_XBOXONE },
|
||||||
{ 0x045e, 0x0b00, "Microsoft X-Box One Elite 2 pad", MAP_PADDLES, XTYPE_XBOXONE },
|
|
||||||
{ 0x045e, 0x02ea, "Microsoft X-Box One S pad", 0, XTYPE_XBOXONE },
|
{ 0x045e, 0x02ea, "Microsoft X-Box One S pad", 0, XTYPE_XBOXONE },
|
||||||
{ 0x045e, 0x0719, "Xbox 360 Wireless Receiver", MAP_DPAD_TO_BUTTONS, XTYPE_XBOX360W },
|
{ 0x045e, 0x0719, "Xbox 360 Wireless Receiver", MAP_DPAD_TO_BUTTONS, XTYPE_XBOX360W },
|
||||||
|
{ 0x045e, 0x0b00, "Microsoft X-Box One Elite 2 pad", MAP_PADDLES, XTYPE_XBOXONE },
|
||||||
{ 0x045e, 0x0b0a, "Microsoft X-Box Adaptive Controller", MAP_PROFILE_BUTTON, XTYPE_XBOXONE },
|
{ 0x045e, 0x0b0a, "Microsoft X-Box Adaptive Controller", MAP_PROFILE_BUTTON, XTYPE_XBOXONE },
|
||||||
{ 0x045e, 0x0b12, "Microsoft Xbox Series S|X Controller", MAP_SELECT_BUTTON, XTYPE_XBOXONE },
|
{ 0x045e, 0x0b12, "Microsoft Xbox Series S|X Controller", MAP_SELECT_BUTTON, XTYPE_XBOXONE },
|
||||||
{ 0x046d, 0xc21d, "Logitech Gamepad F310", 0, XTYPE_XBOX360 },
|
{ 0x046d, 0xc21d, "Logitech Gamepad F310", 0, XTYPE_XBOX360 },
|
||||||
@ -340,7 +341,6 @@ static const struct xpad_device {
|
|||||||
{ 0x20d6, 0x2001, "BDA Xbox Series X Wired Controller", 0, XTYPE_XBOXONE },
|
{ 0x20d6, 0x2001, "BDA Xbox Series X Wired Controller", 0, XTYPE_XBOXONE },
|
||||||
{ 0x20d6, 0x2009, "PowerA Enhanced Wired Controller for Xbox Series X|S", 0, XTYPE_XBOXONE },
|
{ 0x20d6, 0x2009, "PowerA Enhanced Wired Controller for Xbox Series X|S", 0, XTYPE_XBOXONE },
|
||||||
{ 0x20d6, 0x281f, "PowerA Wired Controller For Xbox 360", 0, XTYPE_XBOX360 },
|
{ 0x20d6, 0x281f, "PowerA Wired Controller For Xbox 360", 0, XTYPE_XBOX360 },
|
||||||
{ 0x2e24, 0x0652, "Hyperkin Duke X-Box One pad", 0, XTYPE_XBOXONE },
|
|
||||||
{ 0x24c6, 0x5000, "Razer Atrox Arcade Stick", MAP_TRIGGERS_TO_BUTTONS, XTYPE_XBOX360 },
|
{ 0x24c6, 0x5000, "Razer Atrox Arcade Stick", MAP_TRIGGERS_TO_BUTTONS, XTYPE_XBOX360 },
|
||||||
{ 0x24c6, 0x5300, "PowerA MINI PROEX Controller", 0, XTYPE_XBOX360 },
|
{ 0x24c6, 0x5300, "PowerA MINI PROEX Controller", 0, XTYPE_XBOX360 },
|
||||||
{ 0x24c6, 0x5303, "Xbox Airflo wired controller", 0, XTYPE_XBOX360 },
|
{ 0x24c6, 0x5303, "Xbox Airflo wired controller", 0, XTYPE_XBOX360 },
|
||||||
@ -355,9 +355,9 @@ static const struct xpad_device {
|
|||||||
{ 0x24c6, 0x5502, "Hori Fighting Stick VX Alt", MAP_TRIGGERS_TO_BUTTONS, XTYPE_XBOX360 },
|
{ 0x24c6, 0x5502, "Hori Fighting Stick VX Alt", MAP_TRIGGERS_TO_BUTTONS, XTYPE_XBOX360 },
|
||||||
{ 0x24c6, 0x5503, "Hori Fighting Edge", MAP_TRIGGERS_TO_BUTTONS, XTYPE_XBOX360 },
|
{ 0x24c6, 0x5503, "Hori Fighting Edge", MAP_TRIGGERS_TO_BUTTONS, XTYPE_XBOX360 },
|
||||||
{ 0x24c6, 0x5506, "Hori SOULCALIBUR V Stick", 0, XTYPE_XBOX360 },
|
{ 0x24c6, 0x5506, "Hori SOULCALIBUR V Stick", 0, XTYPE_XBOX360 },
|
||||||
{ 0x24c6, 0x5510, "Hori Fighting Commander ONE (Xbox 360/PC Mode)", MAP_TRIGGERS_TO_BUTTONS, XTYPE_XBOX360 },
|
|
||||||
{ 0x24c6, 0x550d, "Hori GEM Xbox controller", 0, XTYPE_XBOX360 },
|
{ 0x24c6, 0x550d, "Hori GEM Xbox controller", 0, XTYPE_XBOX360 },
|
||||||
{ 0x24c6, 0x550e, "Hori Real Arcade Pro V Kai 360", MAP_TRIGGERS_TO_BUTTONS, XTYPE_XBOX360 },
|
{ 0x24c6, 0x550e, "Hori Real Arcade Pro V Kai 360", MAP_TRIGGERS_TO_BUTTONS, XTYPE_XBOX360 },
|
||||||
|
{ 0x24c6, 0x5510, "Hori Fighting Commander ONE (Xbox 360/PC Mode)", MAP_TRIGGERS_TO_BUTTONS, XTYPE_XBOX360 },
|
||||||
{ 0x24c6, 0x551a, "PowerA FUSION Pro Controller", 0, XTYPE_XBOXONE },
|
{ 0x24c6, 0x551a, "PowerA FUSION Pro Controller", 0, XTYPE_XBOXONE },
|
||||||
{ 0x24c6, 0x561a, "PowerA FUSION Controller", 0, XTYPE_XBOXONE },
|
{ 0x24c6, 0x561a, "PowerA FUSION Controller", 0, XTYPE_XBOXONE },
|
||||||
{ 0x24c6, 0x5b00, "ThrustMaster Ferrari 458 Racing Wheel", 0, XTYPE_XBOX360 },
|
{ 0x24c6, 0x5b00, "ThrustMaster Ferrari 458 Racing Wheel", 0, XTYPE_XBOX360 },
|
||||||
@ -366,8 +366,11 @@ static const struct xpad_device {
|
|||||||
{ 0x24c6, 0x5d04, "Razer Sabertooth", 0, XTYPE_XBOX360 },
|
{ 0x24c6, 0x5d04, "Razer Sabertooth", 0, XTYPE_XBOX360 },
|
||||||
{ 0x24c6, 0xfafe, "Rock Candy Gamepad for Xbox 360", 0, XTYPE_XBOX360 },
|
{ 0x24c6, 0xfafe, "Rock Candy Gamepad for Xbox 360", 0, XTYPE_XBOX360 },
|
||||||
{ 0x2563, 0x058d, "OneXPlayer Gamepad", 0, XTYPE_XBOX360 },
|
{ 0x2563, 0x058d, "OneXPlayer Gamepad", 0, XTYPE_XBOX360 },
|
||||||
|
{ 0x294b, 0x3303, "Snakebyte GAMEPAD BASE X", 0, XTYPE_XBOXONE },
|
||||||
|
{ 0x294b, 0x3404, "Snakebyte GAMEPAD RGB X", 0, XTYPE_XBOXONE },
|
||||||
{ 0x2dc8, 0x2000, "8BitDo Pro 2 Wired Controller fox Xbox", 0, XTYPE_XBOXONE },
|
{ 0x2dc8, 0x2000, "8BitDo Pro 2 Wired Controller fox Xbox", 0, XTYPE_XBOXONE },
|
||||||
{ 0x2dc8, 0x3106, "8BitDo Pro 2 Wired Controller", 0, XTYPE_XBOX360 },
|
{ 0x2dc8, 0x3106, "8BitDo Pro 2 Wired Controller", 0, XTYPE_XBOX360 },
|
||||||
|
{ 0x2e24, 0x0652, "Hyperkin Duke X-Box One pad", 0, XTYPE_XBOXONE },
|
||||||
{ 0x31e3, 0x1100, "Wooting One", 0, XTYPE_XBOX360 },
|
{ 0x31e3, 0x1100, "Wooting One", 0, XTYPE_XBOX360 },
|
||||||
{ 0x31e3, 0x1200, "Wooting Two", 0, XTYPE_XBOX360 },
|
{ 0x31e3, 0x1200, "Wooting Two", 0, XTYPE_XBOX360 },
|
||||||
{ 0x31e3, 0x1210, "Wooting Lekker", 0, XTYPE_XBOX360 },
|
{ 0x31e3, 0x1210, "Wooting Lekker", 0, XTYPE_XBOX360 },
|
||||||
@ -465,6 +468,10 @@ static const signed short xpad_btn_paddles[] = {
|
|||||||
{ XPAD_XBOXONE_VENDOR_PROTOCOL((vend), 208) }
|
{ XPAD_XBOXONE_VENDOR_PROTOCOL((vend), 208) }
|
||||||
|
|
||||||
static const struct usb_device_id xpad_table[] = {
|
static const struct usb_device_id xpad_table[] = {
|
||||||
|
/*
|
||||||
|
* Please keep this list sorted by vendor ID. Note that there are 2
|
||||||
|
* macros - XPAD_XBOX360_VENDOR and XPAD_XBOXONE_VENDOR.
|
||||||
|
*/
|
||||||
{ USB_INTERFACE_INFO('X', 'B', 0) }, /* Xbox USB-IF not-approved class */
|
{ USB_INTERFACE_INFO('X', 'B', 0) }, /* Xbox USB-IF not-approved class */
|
||||||
XPAD_XBOX360_VENDOR(0x0079), /* GPD Win 2 controller */
|
XPAD_XBOX360_VENDOR(0x0079), /* GPD Win 2 controller */
|
||||||
XPAD_XBOX360_VENDOR(0x03eb), /* Wooting Keyboards (Legacy) */
|
XPAD_XBOX360_VENDOR(0x03eb), /* Wooting Keyboards (Legacy) */
|
||||||
@ -507,6 +514,7 @@ static const struct usb_device_id xpad_table[] = {
|
|||||||
XPAD_XBOXONE_VENDOR(0x24c6), /* PowerA controllers */
|
XPAD_XBOXONE_VENDOR(0x24c6), /* PowerA controllers */
|
||||||
XPAD_XBOX360_VENDOR(0x2563), /* OneXPlayer Gamepad */
|
XPAD_XBOX360_VENDOR(0x2563), /* OneXPlayer Gamepad */
|
||||||
XPAD_XBOX360_VENDOR(0x260d), /* Dareu H101 */
|
XPAD_XBOX360_VENDOR(0x260d), /* Dareu H101 */
|
||||||
|
XPAD_XBOXONE_VENDOR(0x294b), /* Snakebyte */
|
||||||
XPAD_XBOX360_VENDOR(0x2c22), /* Qanba Controllers */
|
XPAD_XBOX360_VENDOR(0x2c22), /* Qanba Controllers */
|
||||||
XPAD_XBOX360_VENDOR(0x2dc8), /* 8BitDo Pro 2 Wired Controller */
|
XPAD_XBOX360_VENDOR(0x2dc8), /* 8BitDo Pro 2 Wired Controller */
|
||||||
XPAD_XBOXONE_VENDOR(0x2dc8), /* 8BitDo Pro 2 Wired Controller for Xbox */
|
XPAD_XBOXONE_VENDOR(0x2dc8), /* 8BitDo Pro 2 Wired Controller for Xbox */
|
||||||
|
@ -418,7 +418,7 @@ static struct platform_driver bcm_kp_device_driver = {
|
|||||||
.probe = bcm_kp_probe,
|
.probe = bcm_kp_probe,
|
||||||
.driver = {
|
.driver = {
|
||||||
.name = "bcm-keypad",
|
.name = "bcm-keypad",
|
||||||
.of_match_table = of_match_ptr(bcm_kp_of_match),
|
.of_match_table = bcm_kp_of_match,
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -28,7 +28,9 @@ struct matrix_keypad {
|
|||||||
struct input_dev *input_dev;
|
struct input_dev *input_dev;
|
||||||
unsigned int row_shift;
|
unsigned int row_shift;
|
||||||
|
|
||||||
DECLARE_BITMAP(disabled_gpios, MATRIX_MAX_ROWS);
|
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];
|
uint32_t last_key_state[MATRIX_MAX_COLS];
|
||||||
struct delayed_work work;
|
struct delayed_work work;
|
||||||
@ -85,28 +87,18 @@ static bool row_asserted(const struct matrix_keypad_platform_data *pdata,
|
|||||||
|
|
||||||
static void enable_row_irqs(struct matrix_keypad *keypad)
|
static void enable_row_irqs(struct matrix_keypad *keypad)
|
||||||
{
|
{
|
||||||
const struct matrix_keypad_platform_data *pdata = keypad->pdata;
|
|
||||||
int i;
|
int i;
|
||||||
|
|
||||||
if (pdata->clustered_irq > 0)
|
for (i = 0; i < keypad->num_row_irqs; i++)
|
||||||
enable_irq(pdata->clustered_irq);
|
enable_irq(keypad->row_irqs[i]);
|
||||||
else {
|
|
||||||
for (i = 0; i < pdata->num_row_gpios; i++)
|
|
||||||
enable_irq(gpio_to_irq(pdata->row_gpios[i]));
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static void disable_row_irqs(struct matrix_keypad *keypad)
|
static void disable_row_irqs(struct matrix_keypad *keypad)
|
||||||
{
|
{
|
||||||
const struct matrix_keypad_platform_data *pdata = keypad->pdata;
|
|
||||||
int i;
|
int i;
|
||||||
|
|
||||||
if (pdata->clustered_irq > 0)
|
for (i = 0; i < keypad->num_row_irqs; i++)
|
||||||
disable_irq_nosync(pdata->clustered_irq);
|
disable_irq_nosync(keypad->row_irqs[i]);
|
||||||
else {
|
|
||||||
for (i = 0; i < pdata->num_row_gpios; i++)
|
|
||||||
disable_irq_nosync(gpio_to_irq(pdata->row_gpios[i]));
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
@ -232,44 +224,20 @@ static void matrix_keypad_stop(struct input_dev *dev)
|
|||||||
|
|
||||||
static void matrix_keypad_enable_wakeup(struct matrix_keypad *keypad)
|
static void matrix_keypad_enable_wakeup(struct matrix_keypad *keypad)
|
||||||
{
|
{
|
||||||
const struct matrix_keypad_platform_data *pdata = keypad->pdata;
|
|
||||||
unsigned int gpio;
|
|
||||||
int i;
|
int i;
|
||||||
|
|
||||||
if (pdata->clustered_irq > 0) {
|
for_each_clear_bit(i, keypad->wakeup_enabled_irqs, keypad->num_row_irqs)
|
||||||
if (enable_irq_wake(pdata->clustered_irq) == 0)
|
if (enable_irq_wake(keypad->row_irqs[i]) == 0)
|
||||||
keypad->gpio_all_disabled = true;
|
__set_bit(i, keypad->wakeup_enabled_irqs);
|
||||||
} else {
|
|
||||||
|
|
||||||
for (i = 0; i < pdata->num_row_gpios; i++) {
|
|
||||||
if (!test_bit(i, keypad->disabled_gpios)) {
|
|
||||||
gpio = pdata->row_gpios[i];
|
|
||||||
|
|
||||||
if (enable_irq_wake(gpio_to_irq(gpio)) == 0)
|
|
||||||
__set_bit(i, keypad->disabled_gpios);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static void matrix_keypad_disable_wakeup(struct matrix_keypad *keypad)
|
static void matrix_keypad_disable_wakeup(struct matrix_keypad *keypad)
|
||||||
{
|
{
|
||||||
const struct matrix_keypad_platform_data *pdata = keypad->pdata;
|
|
||||||
unsigned int gpio;
|
|
||||||
int i;
|
int i;
|
||||||
|
|
||||||
if (pdata->clustered_irq > 0) {
|
for_each_set_bit(i, keypad->wakeup_enabled_irqs, keypad->num_row_irqs) {
|
||||||
if (keypad->gpio_all_disabled) {
|
disable_irq_wake(keypad->row_irqs[i]);
|
||||||
disable_irq_wake(pdata->clustered_irq);
|
__clear_bit(i, keypad->wakeup_enabled_irqs);
|
||||||
keypad->gpio_all_disabled = false;
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
for (i = 0; i < pdata->num_row_gpios; i++) {
|
|
||||||
if (test_and_clear_bit(i, keypad->disabled_gpios)) {
|
|
||||||
gpio = pdata->row_gpios[i];
|
|
||||||
disable_irq_wake(gpio_to_irq(gpio));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -306,96 +274,83 @@ static int matrix_keypad_init_gpio(struct platform_device *pdev,
|
|||||||
struct matrix_keypad *keypad)
|
struct matrix_keypad *keypad)
|
||||||
{
|
{
|
||||||
const struct matrix_keypad_platform_data *pdata = keypad->pdata;
|
const struct matrix_keypad_platform_data *pdata = keypad->pdata;
|
||||||
int i, err;
|
int i, irq, err;
|
||||||
|
|
||||||
/* initialized strobe lines as outputs, activated */
|
/* initialized strobe lines as outputs, activated */
|
||||||
for (i = 0; i < pdata->num_col_gpios; i++) {
|
for (i = 0; i < pdata->num_col_gpios; i++) {
|
||||||
err = gpio_request(pdata->col_gpios[i], "matrix_kbd_col");
|
err = devm_gpio_request(&pdev->dev,
|
||||||
|
pdata->col_gpios[i], "matrix_kbd_col");
|
||||||
if (err) {
|
if (err) {
|
||||||
dev_err(&pdev->dev,
|
dev_err(&pdev->dev,
|
||||||
"failed to request GPIO%d for COL%d\n",
|
"failed to request GPIO%d for COL%d\n",
|
||||||
pdata->col_gpios[i], i);
|
pdata->col_gpios[i], i);
|
||||||
goto err_free_cols;
|
return err;
|
||||||
}
|
}
|
||||||
|
|
||||||
gpio_direction_output(pdata->col_gpios[i], !pdata->active_low);
|
gpio_direction_output(pdata->col_gpios[i], !pdata->active_low);
|
||||||
}
|
}
|
||||||
|
|
||||||
for (i = 0; i < pdata->num_row_gpios; i++) {
|
for (i = 0; i < pdata->num_row_gpios; i++) {
|
||||||
err = gpio_request(pdata->row_gpios[i], "matrix_kbd_row");
|
err = devm_gpio_request(&pdev->dev,
|
||||||
|
pdata->row_gpios[i], "matrix_kbd_row");
|
||||||
if (err) {
|
if (err) {
|
||||||
dev_err(&pdev->dev,
|
dev_err(&pdev->dev,
|
||||||
"failed to request GPIO%d for ROW%d\n",
|
"failed to request GPIO%d for ROW%d\n",
|
||||||
pdata->row_gpios[i], i);
|
pdata->row_gpios[i], i);
|
||||||
goto err_free_rows;
|
return err;
|
||||||
}
|
}
|
||||||
|
|
||||||
gpio_direction_input(pdata->row_gpios[i]);
|
gpio_direction_input(pdata->row_gpios[i]);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (pdata->clustered_irq > 0) {
|
if (pdata->clustered_irq > 0) {
|
||||||
err = request_any_context_irq(pdata->clustered_irq,
|
err = devm_request_any_context_irq(&pdev->dev,
|
||||||
|
pdata->clustered_irq,
|
||||||
matrix_keypad_interrupt,
|
matrix_keypad_interrupt,
|
||||||
pdata->clustered_irq_flags,
|
pdata->clustered_irq_flags,
|
||||||
"matrix-keypad", keypad);
|
"matrix-keypad", keypad);
|
||||||
if (err < 0) {
|
if (err < 0) {
|
||||||
dev_err(&pdev->dev,
|
dev_err(&pdev->dev,
|
||||||
"Unable to acquire clustered interrupt\n");
|
"Unable to acquire clustered interrupt\n");
|
||||||
goto err_free_rows;
|
return err;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
keypad->row_irqs[0] = pdata->clustered_irq;
|
||||||
|
keypad->num_row_irqs = 1;
|
||||||
} else {
|
} else {
|
||||||
for (i = 0; i < pdata->num_row_gpios; i++) {
|
for (i = 0; i < pdata->num_row_gpios; i++) {
|
||||||
err = request_any_context_irq(
|
irq = gpio_to_irq(pdata->row_gpios[i]);
|
||||||
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,
|
matrix_keypad_interrupt,
|
||||||
IRQF_TRIGGER_RISING |
|
IRQF_TRIGGER_RISING |
|
||||||
IRQF_TRIGGER_FALLING,
|
IRQF_TRIGGER_FALLING,
|
||||||
"matrix-keypad", keypad);
|
"matrix-keypad", keypad);
|
||||||
if (err < 0) {
|
if (err < 0) {
|
||||||
dev_err(&pdev->dev,
|
dev_err(&pdev->dev,
|
||||||
"Unable to acquire interrupt for GPIO line %i\n",
|
"Unable to acquire interrupt for GPIO line %i\n",
|
||||||
pdata->row_gpios[i]);
|
pdata->row_gpios[i]);
|
||||||
goto err_free_irqs;
|
return err;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
keypad->row_irqs[i] = irq;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
keypad->num_row_irqs = pdata->num_row_gpios;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* initialized as disabled - enabled by input->open */
|
/* initialized as disabled - enabled by input->open */
|
||||||
disable_row_irqs(keypad);
|
disable_row_irqs(keypad);
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
|
|
||||||
err_free_irqs:
|
|
||||||
while (--i >= 0)
|
|
||||||
free_irq(gpio_to_irq(pdata->row_gpios[i]), keypad);
|
|
||||||
i = pdata->num_row_gpios;
|
|
||||||
err_free_rows:
|
|
||||||
while (--i >= 0)
|
|
||||||
gpio_free(pdata->row_gpios[i]);
|
|
||||||
i = pdata->num_col_gpios;
|
|
||||||
err_free_cols:
|
|
||||||
while (--i >= 0)
|
|
||||||
gpio_free(pdata->col_gpios[i]);
|
|
||||||
|
|
||||||
return err;
|
|
||||||
}
|
|
||||||
|
|
||||||
static void matrix_keypad_free_gpio(struct matrix_keypad *keypad)
|
|
||||||
{
|
|
||||||
const struct matrix_keypad_platform_data *pdata = keypad->pdata;
|
|
||||||
int i;
|
|
||||||
|
|
||||||
if (pdata->clustered_irq > 0) {
|
|
||||||
free_irq(pdata->clustered_irq, keypad);
|
|
||||||
} else {
|
|
||||||
for (i = 0; i < pdata->num_row_gpios; i++)
|
|
||||||
free_irq(gpio_to_irq(pdata->row_gpios[i]), keypad);
|
|
||||||
}
|
|
||||||
|
|
||||||
for (i = 0; i < pdata->num_row_gpios; i++)
|
|
||||||
gpio_free(pdata->row_gpios[i]);
|
|
||||||
|
|
||||||
for (i = 0; i < pdata->num_col_gpios; i++)
|
|
||||||
gpio_free(pdata->col_gpios[i]);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#ifdef CONFIG_OF
|
#ifdef CONFIG_OF
|
||||||
@ -494,12 +449,13 @@ static int matrix_keypad_probe(struct platform_device *pdev)
|
|||||||
return -EINVAL;
|
return -EINVAL;
|
||||||
}
|
}
|
||||||
|
|
||||||
keypad = kzalloc(sizeof(struct matrix_keypad), GFP_KERNEL);
|
keypad = devm_kzalloc(&pdev->dev, sizeof(*keypad), GFP_KERNEL);
|
||||||
input_dev = input_allocate_device();
|
if (!keypad)
|
||||||
if (!keypad || !input_dev) {
|
return -ENOMEM;
|
||||||
err = -ENOMEM;
|
|
||||||
goto err_free_mem;
|
input_dev = devm_input_allocate_device(&pdev->dev);
|
||||||
}
|
if (!input_dev)
|
||||||
|
return -ENOMEM;
|
||||||
|
|
||||||
keypad->input_dev = input_dev;
|
keypad->input_dev = input_dev;
|
||||||
keypad->pdata = pdata;
|
keypad->pdata = pdata;
|
||||||
@ -510,7 +466,6 @@ static int matrix_keypad_probe(struct platform_device *pdev)
|
|||||||
|
|
||||||
input_dev->name = pdev->name;
|
input_dev->name = pdev->name;
|
||||||
input_dev->id.bustype = BUS_HOST;
|
input_dev->id.bustype = BUS_HOST;
|
||||||
input_dev->dev.parent = &pdev->dev;
|
|
||||||
input_dev->open = matrix_keypad_start;
|
input_dev->open = matrix_keypad_start;
|
||||||
input_dev->close = matrix_keypad_stop;
|
input_dev->close = matrix_keypad_stop;
|
||||||
|
|
||||||
@ -520,7 +475,7 @@ static int matrix_keypad_probe(struct platform_device *pdev)
|
|||||||
NULL, input_dev);
|
NULL, input_dev);
|
||||||
if (err) {
|
if (err) {
|
||||||
dev_err(&pdev->dev, "failed to build keymap\n");
|
dev_err(&pdev->dev, "failed to build keymap\n");
|
||||||
goto err_free_mem;
|
return -ENOMEM;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!pdata->no_autorepeat)
|
if (!pdata->no_autorepeat)
|
||||||
@ -530,32 +485,16 @@ static int matrix_keypad_probe(struct platform_device *pdev)
|
|||||||
|
|
||||||
err = matrix_keypad_init_gpio(pdev, keypad);
|
err = matrix_keypad_init_gpio(pdev, keypad);
|
||||||
if (err)
|
if (err)
|
||||||
goto err_free_mem;
|
return err;
|
||||||
|
|
||||||
err = input_register_device(keypad->input_dev);
|
err = input_register_device(keypad->input_dev);
|
||||||
if (err)
|
if (err)
|
||||||
goto err_free_gpio;
|
return err;
|
||||||
|
|
||||||
device_init_wakeup(&pdev->dev, pdata->wakeup);
|
device_init_wakeup(&pdev->dev, pdata->wakeup);
|
||||||
platform_set_drvdata(pdev, keypad);
|
platform_set_drvdata(pdev, keypad);
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
|
|
||||||
err_free_gpio:
|
|
||||||
matrix_keypad_free_gpio(keypad);
|
|
||||||
err_free_mem:
|
|
||||||
input_free_device(input_dev);
|
|
||||||
kfree(keypad);
|
|
||||||
return err;
|
|
||||||
}
|
|
||||||
|
|
||||||
static void matrix_keypad_remove(struct platform_device *pdev)
|
|
||||||
{
|
|
||||||
struct matrix_keypad *keypad = platform_get_drvdata(pdev);
|
|
||||||
|
|
||||||
matrix_keypad_free_gpio(keypad);
|
|
||||||
input_unregister_device(keypad->input_dev);
|
|
||||||
kfree(keypad);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#ifdef CONFIG_OF
|
#ifdef CONFIG_OF
|
||||||
@ -568,7 +507,6 @@ MODULE_DEVICE_TABLE(of, matrix_keypad_dt_match);
|
|||||||
|
|
||||||
static struct platform_driver matrix_keypad_driver = {
|
static struct platform_driver matrix_keypad_driver = {
|
||||||
.probe = matrix_keypad_probe,
|
.probe = matrix_keypad_probe,
|
||||||
.remove_new = matrix_keypad_remove,
|
|
||||||
.driver = {
|
.driver = {
|
||||||
.name = "matrix-keypad",
|
.name = "matrix-keypad",
|
||||||
.pm = pm_sleep_ptr(&matrix_keypad_pm_ops),
|
.pm = pm_sleep_ptr(&matrix_keypad_pm_ops),
|
||||||
|
@ -1,22 +1,10 @@
|
|||||||
|
// SPDX-License-Identifier: GPL-2.0-only
|
||||||
/*
|
/*
|
||||||
* Marvell 88PM80x ONKEY driver
|
* Marvell 88PM80x ONKEY driver
|
||||||
*
|
*
|
||||||
* Copyright (C) 2012 Marvell International Ltd.
|
* Copyright (C) 2012 Marvell International Ltd.
|
||||||
* Haojian Zhuang <haojian.zhuang@marvell.com>
|
* Haojian Zhuang <haojian.zhuang@marvell.com>
|
||||||
* Qiao Zhou <zhouqiao@marvell.com>
|
* Qiao Zhou <zhouqiao@marvell.com>
|
||||||
*
|
|
||||||
* This file is subject to the terms and conditions of the GNU General
|
|
||||||
* Public License. See the file "COPYING" in the main directory of this
|
|
||||||
* archive for more details.
|
|
||||||
*
|
|
||||||
* This program is distributed in the hope that it will be useful,
|
|
||||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
||||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
||||||
* GNU General Public License for more details.
|
|
||||||
*
|
|
||||||
* You should have received a copy of the GNU General Public License
|
|
||||||
* along with this program; if not, write to the Free Software
|
|
||||||
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#include <linux/kernel.h>
|
#include <linux/kernel.h>
|
||||||
|
@ -620,6 +620,118 @@ static const struct iqs7222_dev_desc iqs7222_devs[] = {
|
|||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
.prod_num = IQS7222_PROD_NUM_D,
|
||||||
|
.fw_major = 1,
|
||||||
|
.fw_minor = 2,
|
||||||
|
.touch_link = 1770,
|
||||||
|
.allow_offset = 9,
|
||||||
|
.event_offset = 10,
|
||||||
|
.comms_offset = 11,
|
||||||
|
.reg_grps = {
|
||||||
|
[IQS7222_REG_GRP_STAT] = {
|
||||||
|
.base = IQS7222_SYS_STATUS,
|
||||||
|
.num_row = 1,
|
||||||
|
.num_col = 7,
|
||||||
|
},
|
||||||
|
[IQS7222_REG_GRP_CYCLE] = {
|
||||||
|
.base = 0x8000,
|
||||||
|
.num_row = 7,
|
||||||
|
.num_col = 2,
|
||||||
|
},
|
||||||
|
[IQS7222_REG_GRP_GLBL] = {
|
||||||
|
.base = 0x8700,
|
||||||
|
.num_row = 1,
|
||||||
|
.num_col = 3,
|
||||||
|
},
|
||||||
|
[IQS7222_REG_GRP_BTN] = {
|
||||||
|
.base = 0x9000,
|
||||||
|
.num_row = 14,
|
||||||
|
.num_col = 3,
|
||||||
|
},
|
||||||
|
[IQS7222_REG_GRP_CHAN] = {
|
||||||
|
.base = 0xA000,
|
||||||
|
.num_row = 14,
|
||||||
|
.num_col = 4,
|
||||||
|
},
|
||||||
|
[IQS7222_REG_GRP_FILT] = {
|
||||||
|
.base = 0xAE00,
|
||||||
|
.num_row = 1,
|
||||||
|
.num_col = 2,
|
||||||
|
},
|
||||||
|
[IQS7222_REG_GRP_TPAD] = {
|
||||||
|
.base = 0xB000,
|
||||||
|
.num_row = 1,
|
||||||
|
.num_col = 24,
|
||||||
|
},
|
||||||
|
[IQS7222_REG_GRP_GPIO] = {
|
||||||
|
.base = 0xC000,
|
||||||
|
.num_row = 3,
|
||||||
|
.num_col = 3,
|
||||||
|
},
|
||||||
|
[IQS7222_REG_GRP_SYS] = {
|
||||||
|
.base = IQS7222_SYS_SETUP,
|
||||||
|
.num_row = 1,
|
||||||
|
.num_col = 12,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
.prod_num = IQS7222_PROD_NUM_D,
|
||||||
|
.fw_major = 1,
|
||||||
|
.fw_minor = 1,
|
||||||
|
.touch_link = 1774,
|
||||||
|
.allow_offset = 9,
|
||||||
|
.event_offset = 10,
|
||||||
|
.comms_offset = 11,
|
||||||
|
.reg_grps = {
|
||||||
|
[IQS7222_REG_GRP_STAT] = {
|
||||||
|
.base = IQS7222_SYS_STATUS,
|
||||||
|
.num_row = 1,
|
||||||
|
.num_col = 7,
|
||||||
|
},
|
||||||
|
[IQS7222_REG_GRP_CYCLE] = {
|
||||||
|
.base = 0x8000,
|
||||||
|
.num_row = 7,
|
||||||
|
.num_col = 2,
|
||||||
|
},
|
||||||
|
[IQS7222_REG_GRP_GLBL] = {
|
||||||
|
.base = 0x8700,
|
||||||
|
.num_row = 1,
|
||||||
|
.num_col = 3,
|
||||||
|
},
|
||||||
|
[IQS7222_REG_GRP_BTN] = {
|
||||||
|
.base = 0x9000,
|
||||||
|
.num_row = 14,
|
||||||
|
.num_col = 3,
|
||||||
|
},
|
||||||
|
[IQS7222_REG_GRP_CHAN] = {
|
||||||
|
.base = 0xA000,
|
||||||
|
.num_row = 14,
|
||||||
|
.num_col = 4,
|
||||||
|
},
|
||||||
|
[IQS7222_REG_GRP_FILT] = {
|
||||||
|
.base = 0xAE00,
|
||||||
|
.num_row = 1,
|
||||||
|
.num_col = 2,
|
||||||
|
},
|
||||||
|
[IQS7222_REG_GRP_TPAD] = {
|
||||||
|
.base = 0xB000,
|
||||||
|
.num_row = 1,
|
||||||
|
.num_col = 24,
|
||||||
|
},
|
||||||
|
[IQS7222_REG_GRP_GPIO] = {
|
||||||
|
.base = 0xC000,
|
||||||
|
.num_row = 3,
|
||||||
|
.num_col = 3,
|
||||||
|
},
|
||||||
|
[IQS7222_REG_GRP_SYS] = {
|
||||||
|
.base = IQS7222_SYS_SETUP,
|
||||||
|
.num_row = 1,
|
||||||
|
.num_col = 12,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
{
|
{
|
||||||
.prod_num = IQS7222_PROD_NUM_D,
|
.prod_num = IQS7222_PROD_NUM_D,
|
||||||
.fw_major = 0,
|
.fw_major = 0,
|
||||||
|
@ -439,16 +439,4 @@ config MOUSE_SYNAPTICS_USB
|
|||||||
To compile this driver as a module, choose M here: the
|
To compile this driver as a module, choose M here: the
|
||||||
module will be called synaptics_usb.
|
module will be called synaptics_usb.
|
||||||
|
|
||||||
config MOUSE_NAVPOINT_PXA27x
|
|
||||||
tristate "Synaptics NavPoint (PXA27x SSP/SPI)"
|
|
||||||
depends on PXA27x && PXA_SSP
|
|
||||||
help
|
|
||||||
This driver adds support for the Synaptics NavPoint touchpad connected
|
|
||||||
to a PXA27x SSP port in SPI slave mode. The device emulates a mouse;
|
|
||||||
a tap or tap-and-a-half drag gesture emulates the left mouse button.
|
|
||||||
For example, use the xf86-input-evdev driver for an X pointing device.
|
|
||||||
|
|
||||||
To compile this driver as a module, choose M here: the
|
|
||||||
module will be called navpoint.
|
|
||||||
|
|
||||||
endif
|
endif
|
||||||
|
@ -15,7 +15,6 @@ obj-$(CONFIG_MOUSE_GPIO) += gpio_mouse.o
|
|||||||
obj-$(CONFIG_MOUSE_INPORT) += inport.o
|
obj-$(CONFIG_MOUSE_INPORT) += inport.o
|
||||||
obj-$(CONFIG_MOUSE_LOGIBM) += logibm.o
|
obj-$(CONFIG_MOUSE_LOGIBM) += logibm.o
|
||||||
obj-$(CONFIG_MOUSE_MAPLE) += maplemouse.o
|
obj-$(CONFIG_MOUSE_MAPLE) += maplemouse.o
|
||||||
obj-$(CONFIG_MOUSE_NAVPOINT_PXA27x) += navpoint.o
|
|
||||||
obj-$(CONFIG_MOUSE_PC110PAD) += pc110pad.o
|
obj-$(CONFIG_MOUSE_PC110PAD) += pc110pad.o
|
||||||
obj-$(CONFIG_MOUSE_PS2) += psmouse.o
|
obj-$(CONFIG_MOUSE_PS2) += psmouse.o
|
||||||
obj-$(CONFIG_MOUSE_RISCPC) += rpcmouse.o
|
obj-$(CONFIG_MOUSE_RISCPC) += rpcmouse.o
|
||||||
|
@ -1,350 +0,0 @@
|
|||||||
// SPDX-License-Identifier: GPL-2.0-only
|
|
||||||
/*
|
|
||||||
* Synaptics NavPoint (PXA27x SSP/SPI) driver.
|
|
||||||
*
|
|
||||||
* Copyright (C) 2012 Paul Parsons <lost.distance@yahoo.com>
|
|
||||||
*/
|
|
||||||
|
|
||||||
#include <linux/kernel.h>
|
|
||||||
#include <linux/module.h>
|
|
||||||
#include <linux/platform_device.h>
|
|
||||||
#include <linux/clk.h>
|
|
||||||
#include <linux/delay.h>
|
|
||||||
#include <linux/gpio/consumer.h>
|
|
||||||
#include <linux/input.h>
|
|
||||||
#include <linux/input/navpoint.h>
|
|
||||||
#include <linux/interrupt.h>
|
|
||||||
#include <linux/mutex.h>
|
|
||||||
#include <linux/pxa2xx_ssp.h>
|
|
||||||
#include <linux/slab.h>
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Synaptics Modular Embedded Protocol: Module Packet Format.
|
|
||||||
* Module header byte 2:0 = Length (# bytes that follow)
|
|
||||||
* Module header byte 4:3 = Control
|
|
||||||
* Module header byte 7:5 = Module Address
|
|
||||||
*/
|
|
||||||
#define HEADER_LENGTH(byte) ((byte) & 0x07)
|
|
||||||
#define HEADER_CONTROL(byte) (((byte) >> 3) & 0x03)
|
|
||||||
#define HEADER_ADDRESS(byte) ((byte) >> 5)
|
|
||||||
|
|
||||||
struct navpoint {
|
|
||||||
struct ssp_device *ssp;
|
|
||||||
struct input_dev *input;
|
|
||||||
struct device *dev;
|
|
||||||
struct gpio_desc *gpiod;
|
|
||||||
int index;
|
|
||||||
u8 data[1 + HEADER_LENGTH(0xff)];
|
|
||||||
};
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Initialization values for SSCR0_x, SSCR1_x, SSSR_x.
|
|
||||||
*/
|
|
||||||
static const u32 sscr0 = 0
|
|
||||||
| SSCR0_TUM /* TIM = 1; No TUR interrupts */
|
|
||||||
| SSCR0_RIM /* RIM = 1; No ROR interrupts */
|
|
||||||
| SSCR0_SSE /* SSE = 1; SSP enabled */
|
|
||||||
| SSCR0_Motorola /* FRF = 0; Motorola SPI */
|
|
||||||
| SSCR0_DataSize(16) /* DSS = 15; Data size = 16-bit */
|
|
||||||
;
|
|
||||||
static const u32 sscr1 = 0
|
|
||||||
| SSCR1_SCFR /* SCFR = 1; SSPSCLK only during transfers */
|
|
||||||
| SSCR1_SCLKDIR /* SCLKDIR = 1; Slave mode */
|
|
||||||
| SSCR1_SFRMDIR /* SFRMDIR = 1; Slave mode */
|
|
||||||
| SSCR1_RWOT /* RWOT = 1; Receive without transmit mode */
|
|
||||||
| SSCR1_RxTresh(1) /* RFT = 0; Receive FIFO threshold = 1 */
|
|
||||||
| SSCR1_SPH /* SPH = 1; SSPSCLK inactive 0.5 + 1 cycles */
|
|
||||||
| SSCR1_RIE /* RIE = 1; Receive FIFO interrupt enabled */
|
|
||||||
;
|
|
||||||
static const u32 sssr = 0
|
|
||||||
| SSSR_BCE /* BCE = 1; Clear BCE */
|
|
||||||
| SSSR_TUR /* TUR = 1; Clear TUR */
|
|
||||||
| SSSR_EOC /* EOC = 1; Clear EOC */
|
|
||||||
| SSSR_TINT /* TINT = 1; Clear TINT */
|
|
||||||
| SSSR_PINT /* PINT = 1; Clear PINT */
|
|
||||||
| SSSR_ROR /* ROR = 1; Clear ROR */
|
|
||||||
;
|
|
||||||
|
|
||||||
/*
|
|
||||||
* MEP Query $22: Touchpad Coordinate Range Query is not supported by
|
|
||||||
* the NavPoint module, so sampled values provide the default limits.
|
|
||||||
*/
|
|
||||||
#define NAVPOINT_X_MIN 1278
|
|
||||||
#define NAVPOINT_X_MAX 5340
|
|
||||||
#define NAVPOINT_Y_MIN 1572
|
|
||||||
#define NAVPOINT_Y_MAX 4396
|
|
||||||
#define NAVPOINT_PRESSURE_MIN 0
|
|
||||||
#define NAVPOINT_PRESSURE_MAX 255
|
|
||||||
|
|
||||||
static void navpoint_packet(struct navpoint *navpoint)
|
|
||||||
{
|
|
||||||
int finger;
|
|
||||||
int gesture;
|
|
||||||
int x, y, z;
|
|
||||||
|
|
||||||
switch (navpoint->data[0]) {
|
|
||||||
case 0xff: /* Garbage (packet?) between reset and Hello packet */
|
|
||||||
case 0x00: /* Module 0, NULL packet */
|
|
||||||
break;
|
|
||||||
|
|
||||||
case 0x0e: /* Module 0, Absolute packet */
|
|
||||||
finger = (navpoint->data[1] & 0x01);
|
|
||||||
gesture = (navpoint->data[1] & 0x02);
|
|
||||||
x = ((navpoint->data[2] & 0x1f) << 8) | navpoint->data[3];
|
|
||||||
y = ((navpoint->data[4] & 0x1f) << 8) | navpoint->data[5];
|
|
||||||
z = navpoint->data[6];
|
|
||||||
input_report_key(navpoint->input, BTN_TOUCH, finger);
|
|
||||||
input_report_abs(navpoint->input, ABS_X, x);
|
|
||||||
input_report_abs(navpoint->input, ABS_Y, y);
|
|
||||||
input_report_abs(navpoint->input, ABS_PRESSURE, z);
|
|
||||||
input_report_key(navpoint->input, BTN_TOOL_FINGER, finger);
|
|
||||||
input_report_key(navpoint->input, BTN_LEFT, gesture);
|
|
||||||
input_sync(navpoint->input);
|
|
||||||
break;
|
|
||||||
|
|
||||||
case 0x19: /* Module 0, Hello packet */
|
|
||||||
if ((navpoint->data[1] & 0xf0) == 0x10)
|
|
||||||
break;
|
|
||||||
fallthrough;
|
|
||||||
default:
|
|
||||||
dev_warn(navpoint->dev,
|
|
||||||
"spurious packet: data=0x%02x,0x%02x,...\n",
|
|
||||||
navpoint->data[0], navpoint->data[1]);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
static irqreturn_t navpoint_irq(int irq, void *dev_id)
|
|
||||||
{
|
|
||||||
struct navpoint *navpoint = dev_id;
|
|
||||||
struct ssp_device *ssp = navpoint->ssp;
|
|
||||||
irqreturn_t ret = IRQ_NONE;
|
|
||||||
u32 status;
|
|
||||||
|
|
||||||
status = pxa_ssp_read_reg(ssp, SSSR);
|
|
||||||
if (status & sssr) {
|
|
||||||
dev_warn(navpoint->dev,
|
|
||||||
"unexpected interrupt: status=0x%08x\n", status);
|
|
||||||
pxa_ssp_write_reg(ssp, SSSR, (status & sssr));
|
|
||||||
ret = IRQ_HANDLED;
|
|
||||||
}
|
|
||||||
|
|
||||||
while (status & SSSR_RNE) {
|
|
||||||
u32 data;
|
|
||||||
|
|
||||||
data = pxa_ssp_read_reg(ssp, SSDR);
|
|
||||||
navpoint->data[navpoint->index + 0] = (data >> 8);
|
|
||||||
navpoint->data[navpoint->index + 1] = data;
|
|
||||||
navpoint->index += 2;
|
|
||||||
if (HEADER_LENGTH(navpoint->data[0]) < navpoint->index) {
|
|
||||||
navpoint_packet(navpoint);
|
|
||||||
navpoint->index = 0;
|
|
||||||
}
|
|
||||||
status = pxa_ssp_read_reg(ssp, SSSR);
|
|
||||||
ret = IRQ_HANDLED;
|
|
||||||
}
|
|
||||||
|
|
||||||
return ret;
|
|
||||||
}
|
|
||||||
|
|
||||||
static void navpoint_up(struct navpoint *navpoint)
|
|
||||||
{
|
|
||||||
struct ssp_device *ssp = navpoint->ssp;
|
|
||||||
int timeout;
|
|
||||||
|
|
||||||
clk_prepare_enable(ssp->clk);
|
|
||||||
|
|
||||||
pxa_ssp_write_reg(ssp, SSCR1, sscr1);
|
|
||||||
pxa_ssp_write_reg(ssp, SSSR, sssr);
|
|
||||||
pxa_ssp_write_reg(ssp, SSTO, 0);
|
|
||||||
pxa_ssp_write_reg(ssp, SSCR0, sscr0); /* SSCR0_SSE written last */
|
|
||||||
|
|
||||||
/* Wait until SSP port is ready for slave clock operations */
|
|
||||||
for (timeout = 100; timeout != 0; --timeout) {
|
|
||||||
if (!(pxa_ssp_read_reg(ssp, SSSR) & SSSR_CSS))
|
|
||||||
break;
|
|
||||||
msleep(1);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (timeout == 0)
|
|
||||||
dev_err(navpoint->dev,
|
|
||||||
"timeout waiting for SSSR[CSS] to clear\n");
|
|
||||||
|
|
||||||
gpiod_set_value(navpoint->gpiod, 1);
|
|
||||||
}
|
|
||||||
|
|
||||||
static void navpoint_down(struct navpoint *navpoint)
|
|
||||||
{
|
|
||||||
struct ssp_device *ssp = navpoint->ssp;
|
|
||||||
|
|
||||||
gpiod_set_value(navpoint->gpiod, 0);
|
|
||||||
|
|
||||||
pxa_ssp_write_reg(ssp, SSCR0, 0);
|
|
||||||
|
|
||||||
clk_disable_unprepare(ssp->clk);
|
|
||||||
}
|
|
||||||
|
|
||||||
static int navpoint_open(struct input_dev *input)
|
|
||||||
{
|
|
||||||
struct navpoint *navpoint = input_get_drvdata(input);
|
|
||||||
|
|
||||||
navpoint_up(navpoint);
|
|
||||||
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
static void navpoint_close(struct input_dev *input)
|
|
||||||
{
|
|
||||||
struct navpoint *navpoint = input_get_drvdata(input);
|
|
||||||
|
|
||||||
navpoint_down(navpoint);
|
|
||||||
}
|
|
||||||
|
|
||||||
static int navpoint_probe(struct platform_device *pdev)
|
|
||||||
{
|
|
||||||
const struct navpoint_platform_data *pdata =
|
|
||||||
dev_get_platdata(&pdev->dev);
|
|
||||||
struct ssp_device *ssp;
|
|
||||||
struct input_dev *input;
|
|
||||||
struct navpoint *navpoint;
|
|
||||||
int error;
|
|
||||||
|
|
||||||
if (!pdata) {
|
|
||||||
dev_err(&pdev->dev, "no platform data\n");
|
|
||||||
return -EINVAL;
|
|
||||||
}
|
|
||||||
|
|
||||||
ssp = pxa_ssp_request(pdata->port, pdev->name);
|
|
||||||
if (!ssp)
|
|
||||||
return -ENODEV;
|
|
||||||
|
|
||||||
/* HaRET does not disable devices before jumping into Linux */
|
|
||||||
if (pxa_ssp_read_reg(ssp, SSCR0) & SSCR0_SSE) {
|
|
||||||
pxa_ssp_write_reg(ssp, SSCR0, 0);
|
|
||||||
dev_warn(&pdev->dev, "ssp%d already enabled\n", pdata->port);
|
|
||||||
}
|
|
||||||
|
|
||||||
navpoint = kzalloc(sizeof(*navpoint), GFP_KERNEL);
|
|
||||||
input = input_allocate_device();
|
|
||||||
if (!navpoint || !input) {
|
|
||||||
error = -ENOMEM;
|
|
||||||
goto err_free_mem;
|
|
||||||
}
|
|
||||||
|
|
||||||
navpoint->gpiod = gpiod_get_optional(&pdev->dev,
|
|
||||||
NULL, GPIOD_OUT_LOW);
|
|
||||||
if (IS_ERR(navpoint->gpiod)) {
|
|
||||||
error = PTR_ERR(navpoint->gpiod);
|
|
||||||
dev_err(&pdev->dev, "error getting GPIO\n");
|
|
||||||
goto err_free_mem;
|
|
||||||
}
|
|
||||||
gpiod_set_consumer_name(navpoint->gpiod, "SYNAPTICS_ON");
|
|
||||||
|
|
||||||
navpoint->ssp = ssp;
|
|
||||||
navpoint->input = input;
|
|
||||||
navpoint->dev = &pdev->dev;
|
|
||||||
|
|
||||||
input->name = pdev->name;
|
|
||||||
input->dev.parent = &pdev->dev;
|
|
||||||
|
|
||||||
__set_bit(EV_KEY, input->evbit);
|
|
||||||
__set_bit(EV_ABS, input->evbit);
|
|
||||||
__set_bit(BTN_LEFT, input->keybit);
|
|
||||||
__set_bit(BTN_TOUCH, input->keybit);
|
|
||||||
__set_bit(BTN_TOOL_FINGER, input->keybit);
|
|
||||||
|
|
||||||
input_set_abs_params(input, ABS_X,
|
|
||||||
NAVPOINT_X_MIN, NAVPOINT_X_MAX, 0, 0);
|
|
||||||
input_set_abs_params(input, ABS_Y,
|
|
||||||
NAVPOINT_Y_MIN, NAVPOINT_Y_MAX, 0, 0);
|
|
||||||
input_set_abs_params(input, ABS_PRESSURE,
|
|
||||||
NAVPOINT_PRESSURE_MIN, NAVPOINT_PRESSURE_MAX,
|
|
||||||
0, 0);
|
|
||||||
|
|
||||||
input->open = navpoint_open;
|
|
||||||
input->close = navpoint_close;
|
|
||||||
|
|
||||||
input_set_drvdata(input, navpoint);
|
|
||||||
|
|
||||||
error = request_irq(ssp->irq, navpoint_irq, 0, pdev->name, navpoint);
|
|
||||||
if (error)
|
|
||||||
goto err_free_mem;
|
|
||||||
|
|
||||||
error = input_register_device(input);
|
|
||||||
if (error)
|
|
||||||
goto err_free_irq;
|
|
||||||
|
|
||||||
platform_set_drvdata(pdev, navpoint);
|
|
||||||
dev_dbg(&pdev->dev, "ssp%d, irq %d\n", pdata->port, ssp->irq);
|
|
||||||
|
|
||||||
return 0;
|
|
||||||
|
|
||||||
err_free_irq:
|
|
||||||
free_irq(ssp->irq, navpoint);
|
|
||||||
err_free_mem:
|
|
||||||
input_free_device(input);
|
|
||||||
kfree(navpoint);
|
|
||||||
pxa_ssp_free(ssp);
|
|
||||||
|
|
||||||
return error;
|
|
||||||
}
|
|
||||||
|
|
||||||
static void navpoint_remove(struct platform_device *pdev)
|
|
||||||
{
|
|
||||||
struct navpoint *navpoint = platform_get_drvdata(pdev);
|
|
||||||
struct ssp_device *ssp = navpoint->ssp;
|
|
||||||
|
|
||||||
free_irq(ssp->irq, navpoint);
|
|
||||||
|
|
||||||
input_unregister_device(navpoint->input);
|
|
||||||
kfree(navpoint);
|
|
||||||
|
|
||||||
pxa_ssp_free(ssp);
|
|
||||||
}
|
|
||||||
|
|
||||||
static int navpoint_suspend(struct device *dev)
|
|
||||||
{
|
|
||||||
struct platform_device *pdev = to_platform_device(dev);
|
|
||||||
struct navpoint *navpoint = platform_get_drvdata(pdev);
|
|
||||||
struct input_dev *input = navpoint->input;
|
|
||||||
|
|
||||||
mutex_lock(&input->mutex);
|
|
||||||
if (input_device_enabled(input))
|
|
||||||
navpoint_down(navpoint);
|
|
||||||
mutex_unlock(&input->mutex);
|
|
||||||
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
static int navpoint_resume(struct device *dev)
|
|
||||||
{
|
|
||||||
struct platform_device *pdev = to_platform_device(dev);
|
|
||||||
struct navpoint *navpoint = platform_get_drvdata(pdev);
|
|
||||||
struct input_dev *input = navpoint->input;
|
|
||||||
|
|
||||||
mutex_lock(&input->mutex);
|
|
||||||
if (input_device_enabled(input))
|
|
||||||
navpoint_up(navpoint);
|
|
||||||
mutex_unlock(&input->mutex);
|
|
||||||
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
static DEFINE_SIMPLE_DEV_PM_OPS(navpoint_pm_ops,
|
|
||||||
navpoint_suspend, navpoint_resume);
|
|
||||||
|
|
||||||
static struct platform_driver navpoint_driver = {
|
|
||||||
.probe = navpoint_probe,
|
|
||||||
.remove_new = navpoint_remove,
|
|
||||||
.driver = {
|
|
||||||
.name = "navpoint",
|
|
||||||
.pm = pm_sleep_ptr(&navpoint_pm_ops),
|
|
||||||
},
|
|
||||||
};
|
|
||||||
|
|
||||||
module_platform_driver(navpoint_driver);
|
|
||||||
|
|
||||||
MODULE_AUTHOR("Paul Parsons <lost.distance@yahoo.com>");
|
|
||||||
MODULE_DESCRIPTION("Synaptics NavPoint (PXA27x SSP/SPI) driver");
|
|
||||||
MODULE_LICENSE("GPL");
|
|
||||||
MODULE_ALIAS("platform:navpoint");
|
|
@ -344,7 +344,7 @@ static int rmi_bus_match(struct device *dev, struct device_driver *drv)
|
|||||||
return physical || rmi_function_match(dev, drv);
|
return physical || rmi_function_match(dev, drv);
|
||||||
}
|
}
|
||||||
|
|
||||||
struct bus_type rmi_bus_type = {
|
const struct bus_type rmi_bus_type = {
|
||||||
.match = rmi_bus_match,
|
.match = rmi_bus_match,
|
||||||
.name = "rmi4",
|
.name = "rmi4",
|
||||||
};
|
};
|
||||||
|
@ -185,7 +185,7 @@ static inline int rmi_write_block(struct rmi_device *d, u16 addr,
|
|||||||
|
|
||||||
int rmi_for_each_dev(void *data, int (*func)(struct device *dev, void *data));
|
int rmi_for_each_dev(void *data, int (*func)(struct device *dev, void *data));
|
||||||
|
|
||||||
extern struct bus_type rmi_bus_type;
|
extern const struct bus_type rmi_bus_type;
|
||||||
|
|
||||||
int rmi_of_property_read_u32(struct device *dev, u32 *result,
|
int rmi_of_property_read_u32(struct device *dev, u32 *result,
|
||||||
const char *prop, bool optional);
|
const char *prop, bool optional);
|
||||||
|
@ -1196,7 +1196,11 @@ static int rmi_driver_probe(struct device *dev)
|
|||||||
}
|
}
|
||||||
rmi_driver_set_input_params(rmi_dev, data->input);
|
rmi_driver_set_input_params(rmi_dev, data->input);
|
||||||
data->input->phys = devm_kasprintf(dev, GFP_KERNEL,
|
data->input->phys = devm_kasprintf(dev, GFP_KERNEL,
|
||||||
"%s/input0", dev_name(dev));
|
"%s/input0", dev_name(dev));
|
||||||
|
if (!data->input->phys) {
|
||||||
|
retval = -ENOMEM;
|
||||||
|
goto err;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
retval = rmi_init_functions(data);
|
retval = rmi_init_functions(data);
|
||||||
|
@ -1007,7 +1007,7 @@ irqreturn_t serio_interrupt(struct serio *serio,
|
|||||||
}
|
}
|
||||||
EXPORT_SYMBOL(serio_interrupt);
|
EXPORT_SYMBOL(serio_interrupt);
|
||||||
|
|
||||||
struct bus_type serio_bus = {
|
const struct bus_type serio_bus = {
|
||||||
.name = "serio",
|
.name = "serio",
|
||||||
.drv_groups = serio_driver_groups,
|
.drv_groups = serio_driver_groups,
|
||||||
.match = serio_bus_match,
|
.match = serio_bus_match,
|
||||||
|
@ -219,8 +219,7 @@ static void sxps2_close(struct serio *pserio)
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* xps2_of_probe - probe method for the PS/2 device.
|
* xps2_of_probe - probe method for the PS/2 device.
|
||||||
* @of_dev: pointer to OF device structure
|
* @ofdev: pointer to OF device structure
|
||||||
* @match: pointer to the structure used for matching a device
|
|
||||||
*
|
*
|
||||||
* This function probes the PS/2 device in the device tree.
|
* This function probes the PS/2 device in the device tree.
|
||||||
* It initializes the driver data structure and the hardware.
|
* It initializes the driver data structure and the hardware.
|
||||||
|
@ -416,6 +416,37 @@ config TOUCHSCREEN_GOODIX
|
|||||||
To compile this driver as a module, choose M here: the
|
To compile this driver as a module, choose M here: the
|
||||||
module will be called goodix.
|
module will be called goodix.
|
||||||
|
|
||||||
|
config TOUCHSCREEN_GOODIX_BERLIN_CORE
|
||||||
|
tristate
|
||||||
|
|
||||||
|
config TOUCHSCREEN_GOODIX_BERLIN_I2C
|
||||||
|
tristate "Goodix Berlin I2C touchscreen"
|
||||||
|
depends on I2C
|
||||||
|
select REGMAP_I2C
|
||||||
|
select TOUCHSCREEN_GOODIX_BERLIN_CORE
|
||||||
|
help
|
||||||
|
Say Y here if you have a Goodix Berlin IC connected to
|
||||||
|
your system via I2C.
|
||||||
|
|
||||||
|
If unsure, say N.
|
||||||
|
|
||||||
|
To compile this driver as a module, choose M here: the
|
||||||
|
module will be called goodix_berlin_i2c.
|
||||||
|
|
||||||
|
config TOUCHSCREEN_GOODIX_BERLIN_SPI
|
||||||
|
tristate "Goodix Berlin SPI touchscreen"
|
||||||
|
depends on SPI_MASTER
|
||||||
|
select REGMAP
|
||||||
|
select TOUCHSCREEN_GOODIX_BERLIN_CORE
|
||||||
|
help
|
||||||
|
Say Y here if you have a Goodix Berlin IC connected to
|
||||||
|
your system via SPI.
|
||||||
|
|
||||||
|
If unsure, say N.
|
||||||
|
|
||||||
|
To compile this driver as a module, choose M here: the
|
||||||
|
module will be called goodix_berlin_spi.
|
||||||
|
|
||||||
config TOUCHSCREEN_HIDEEP
|
config TOUCHSCREEN_HIDEEP
|
||||||
tristate "HiDeep Touch IC"
|
tristate "HiDeep Touch IC"
|
||||||
depends on I2C
|
depends on I2C
|
||||||
|
@ -47,6 +47,9 @@ obj-$(CONFIG_TOUCHSCREEN_EGALAX_SERIAL) += egalax_ts_serial.o
|
|||||||
obj-$(CONFIG_TOUCHSCREEN_EXC3000) += exc3000.o
|
obj-$(CONFIG_TOUCHSCREEN_EXC3000) += exc3000.o
|
||||||
obj-$(CONFIG_TOUCHSCREEN_FUJITSU) += fujitsu_ts.o
|
obj-$(CONFIG_TOUCHSCREEN_FUJITSU) += fujitsu_ts.o
|
||||||
obj-$(CONFIG_TOUCHSCREEN_GOODIX) += goodix_ts.o
|
obj-$(CONFIG_TOUCHSCREEN_GOODIX) += goodix_ts.o
|
||||||
|
obj-$(CONFIG_TOUCHSCREEN_GOODIX_BERLIN_CORE) += goodix_berlin_core.o
|
||||||
|
obj-$(CONFIG_TOUCHSCREEN_GOODIX_BERLIN_I2C) += goodix_berlin_i2c.o
|
||||||
|
obj-$(CONFIG_TOUCHSCREEN_GOODIX_BERLIN_SPI) += goodix_berlin_spi.o
|
||||||
obj-$(CONFIG_TOUCHSCREEN_HIDEEP) += hideep.o
|
obj-$(CONFIG_TOUCHSCREEN_HIDEEP) += hideep.o
|
||||||
obj-$(CONFIG_TOUCHSCREEN_HYNITRON_CSTXXX) += hynitron_cstxxx.o
|
obj-$(CONFIG_TOUCHSCREEN_HYNITRON_CSTXXX) += hynitron_cstxxx.o
|
||||||
obj-$(CONFIG_TOUCHSCREEN_ILI210X) += ili210x.o
|
obj-$(CONFIG_TOUCHSCREEN_ILI210X) += ili210x.o
|
||||||
|
24
drivers/input/touchscreen/goodix_berlin.h
Normal file
24
drivers/input/touchscreen/goodix_berlin.h
Normal file
@ -0,0 +1,24 @@
|
|||||||
|
/* SPDX-License-Identifier: GPL-2.0-or-later */
|
||||||
|
/*
|
||||||
|
* Goodix Touchscreen Driver
|
||||||
|
* Copyright (C) 2020 - 2021 Goodix, Inc.
|
||||||
|
* Copyright (C) 2023 Linaro Ltd.
|
||||||
|
*
|
||||||
|
* Based on goodix_berlin_berlin driver.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef __GOODIX_BERLIN_H_
|
||||||
|
#define __GOODIX_BERLIN_H_
|
||||||
|
|
||||||
|
#include <linux/pm.h>
|
||||||
|
|
||||||
|
struct device;
|
||||||
|
struct input_id;
|
||||||
|
struct regmap;
|
||||||
|
|
||||||
|
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;
|
||||||
|
|
||||||
|
#endif
|
755
drivers/input/touchscreen/goodix_berlin_core.c
Normal file
755
drivers/input/touchscreen/goodix_berlin_core.c
Normal file
@ -0,0 +1,755 @@
|
|||||||
|
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||||
|
/*
|
||||||
|
* Goodix "Berlin" Touchscreen IC driver
|
||||||
|
* Copyright (C) 2020 - 2021 Goodix, Inc.
|
||||||
|
* Copyright (C) 2023 Linaro Ltd.
|
||||||
|
*
|
||||||
|
* Based on goodix_ts_berlin driver.
|
||||||
|
*
|
||||||
|
* This driver is distinct from goodix.c since hardware interface
|
||||||
|
* is different enough to require a new driver.
|
||||||
|
* None of the register address or data structure are close enough
|
||||||
|
* to the previous generations.
|
||||||
|
*
|
||||||
|
* Currently the driver only handles Multitouch events with already
|
||||||
|
* programmed firmware and "config" for "Revision D" Berlin IC.
|
||||||
|
*
|
||||||
|
* Support is missing for:
|
||||||
|
* - ESD Management
|
||||||
|
* - Firmware update/flashing
|
||||||
|
* - "Config" update/flashing
|
||||||
|
* - Stylus Events
|
||||||
|
* - Gesture Events
|
||||||
|
* - Support for older revisions (A & B)
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <linux/bitfield.h>
|
||||||
|
#include <linux/gpio/consumer.h>
|
||||||
|
#include <linux/input.h>
|
||||||
|
#include <linux/input/mt.h>
|
||||||
|
#include <linux/input/touchscreen.h>
|
||||||
|
#include <linux/regmap.h>
|
||||||
|
#include <linux/regulator/consumer.h>
|
||||||
|
#include <linux/sizes.h>
|
||||||
|
#include <asm/unaligned.h>
|
||||||
|
|
||||||
|
#include "goodix_berlin.h"
|
||||||
|
|
||||||
|
#define GOODIX_BERLIN_MAX_TOUCH 10
|
||||||
|
|
||||||
|
#define GOODIX_BERLIN_NORMAL_RESET_DELAY_MS 100
|
||||||
|
|
||||||
|
#define GOODIX_BERLIN_TOUCH_EVENT BIT(7)
|
||||||
|
#define GOODIX_BERLIN_REQUEST_EVENT BIT(6)
|
||||||
|
#define GOODIX_BERLIN_TOUCH_COUNT_MASK GENMASK(3, 0)
|
||||||
|
|
||||||
|
#define GOODIX_BERLIN_REQUEST_CODE_RESET 3
|
||||||
|
|
||||||
|
#define GOODIX_BERLIN_POINT_TYPE_MASK GENMASK(3, 0)
|
||||||
|
#define GOODIX_BERLIN_POINT_TYPE_STYLUS_HOVER 1
|
||||||
|
#define GOODIX_BERLIN_POINT_TYPE_STYLUS 3
|
||||||
|
|
||||||
|
#define GOODIX_BERLIN_TOUCH_ID_MASK GENMASK(7, 4)
|
||||||
|
|
||||||
|
#define GOODIX_BERLIN_DEV_CONFIRM_VAL 0xAA
|
||||||
|
#define GOODIX_BERLIN_BOOTOPTION_ADDR 0x10000
|
||||||
|
#define GOODIX_BERLIN_FW_VERSION_INFO_ADDR 0x10014
|
||||||
|
|
||||||
|
#define GOODIX_BERLIN_IC_INFO_MAX_LEN SZ_1K
|
||||||
|
#define GOODIX_BERLIN_IC_INFO_ADDR 0x10070
|
||||||
|
|
||||||
|
#define GOODIX_BERLIN_CHECKSUM_SIZE sizeof(u16)
|
||||||
|
|
||||||
|
struct goodix_berlin_fw_version {
|
||||||
|
u8 rom_pid[6];
|
||||||
|
u8 rom_vid[3];
|
||||||
|
u8 rom_vid_reserved;
|
||||||
|
u8 patch_pid[8];
|
||||||
|
u8 patch_vid[4];
|
||||||
|
u8 patch_vid_reserved;
|
||||||
|
u8 sensor_id;
|
||||||
|
u8 reserved[2];
|
||||||
|
__le16 checksum;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct goodix_berlin_ic_info_version {
|
||||||
|
u8 info_customer_id;
|
||||||
|
u8 info_version_id;
|
||||||
|
u8 ic_die_id;
|
||||||
|
u8 ic_version_id;
|
||||||
|
__le32 config_id;
|
||||||
|
u8 config_version;
|
||||||
|
u8 frame_data_customer_id;
|
||||||
|
u8 frame_data_version_id;
|
||||||
|
u8 touch_data_customer_id;
|
||||||
|
u8 touch_data_version_id;
|
||||||
|
u8 reserved[3];
|
||||||
|
} __packed;
|
||||||
|
|
||||||
|
struct goodix_berlin_ic_info_feature {
|
||||||
|
__le16 freqhop_feature;
|
||||||
|
__le16 calibration_feature;
|
||||||
|
__le16 gesture_feature;
|
||||||
|
__le16 side_touch_feature;
|
||||||
|
__le16 stylus_feature;
|
||||||
|
} __packed;
|
||||||
|
|
||||||
|
struct goodix_berlin_ic_info_misc {
|
||||||
|
__le32 cmd_addr;
|
||||||
|
__le16 cmd_max_len;
|
||||||
|
__le32 cmd_reply_addr;
|
||||||
|
__le16 cmd_reply_len;
|
||||||
|
__le32 fw_state_addr;
|
||||||
|
__le16 fw_state_len;
|
||||||
|
__le32 fw_buffer_addr;
|
||||||
|
__le16 fw_buffer_max_len;
|
||||||
|
__le32 frame_data_addr;
|
||||||
|
__le16 frame_data_head_len;
|
||||||
|
__le16 fw_attr_len;
|
||||||
|
__le16 fw_log_len;
|
||||||
|
u8 pack_max_num;
|
||||||
|
u8 pack_compress_version;
|
||||||
|
__le16 stylus_struct_len;
|
||||||
|
__le16 mutual_struct_len;
|
||||||
|
__le16 self_struct_len;
|
||||||
|
__le16 noise_struct_len;
|
||||||
|
__le32 touch_data_addr;
|
||||||
|
__le16 touch_data_head_len;
|
||||||
|
__le16 point_struct_len;
|
||||||
|
__le16 reserved1;
|
||||||
|
__le16 reserved2;
|
||||||
|
__le32 mutual_rawdata_addr;
|
||||||
|
__le32 mutual_diffdata_addr;
|
||||||
|
__le32 mutual_refdata_addr;
|
||||||
|
__le32 self_rawdata_addr;
|
||||||
|
__le32 self_diffdata_addr;
|
||||||
|
__le32 self_refdata_addr;
|
||||||
|
__le32 iq_rawdata_addr;
|
||||||
|
__le32 iq_refdata_addr;
|
||||||
|
__le32 im_rawdata_addr;
|
||||||
|
__le16 im_readata_len;
|
||||||
|
__le32 noise_rawdata_addr;
|
||||||
|
__le16 noise_rawdata_len;
|
||||||
|
__le32 stylus_rawdata_addr;
|
||||||
|
__le16 stylus_rawdata_len;
|
||||||
|
__le32 noise_data_addr;
|
||||||
|
__le32 esd_addr;
|
||||||
|
} __packed;
|
||||||
|
|
||||||
|
struct goodix_berlin_touch {
|
||||||
|
u8 status;
|
||||||
|
u8 reserved;
|
||||||
|
__le16 x;
|
||||||
|
__le16 y;
|
||||||
|
__le16 w;
|
||||||
|
};
|
||||||
|
#define GOODIX_BERLIN_TOUCH_SIZE sizeof(struct goodix_berlin_touch)
|
||||||
|
|
||||||
|
struct goodix_berlin_header {
|
||||||
|
u8 status;
|
||||||
|
u8 reserved1;
|
||||||
|
u8 request_type;
|
||||||
|
u8 reserved2[3];
|
||||||
|
__le16 checksum;
|
||||||
|
};
|
||||||
|
#define GOODIX_BERLIN_HEADER_SIZE sizeof(struct goodix_berlin_header)
|
||||||
|
|
||||||
|
struct goodix_berlin_event {
|
||||||
|
struct goodix_berlin_header hdr;
|
||||||
|
/* The data below is u16/__le16 aligned */
|
||||||
|
u8 data[GOODIX_BERLIN_TOUCH_SIZE * GOODIX_BERLIN_MAX_TOUCH +
|
||||||
|
GOODIX_BERLIN_CHECKSUM_SIZE];
|
||||||
|
};
|
||||||
|
|
||||||
|
struct goodix_berlin_core {
|
||||||
|
struct device *dev;
|
||||||
|
struct regmap *regmap;
|
||||||
|
struct regulator *avdd;
|
||||||
|
struct regulator *iovdd;
|
||||||
|
struct gpio_desc *reset_gpio;
|
||||||
|
struct touchscreen_properties props;
|
||||||
|
struct goodix_berlin_fw_version fw_version;
|
||||||
|
struct input_dev *input_dev;
|
||||||
|
int irq;
|
||||||
|
|
||||||
|
/* Runtime parameters extracted from IC_INFO buffer */
|
||||||
|
u32 touch_data_addr;
|
||||||
|
|
||||||
|
struct goodix_berlin_event event;
|
||||||
|
};
|
||||||
|
|
||||||
|
static bool goodix_berlin_checksum_valid(const u8 *data, int size)
|
||||||
|
{
|
||||||
|
u32 cal_checksum = 0;
|
||||||
|
u16 r_checksum;
|
||||||
|
int i;
|
||||||
|
|
||||||
|
if (size < GOODIX_BERLIN_CHECKSUM_SIZE)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
for (i = 0; i < size - GOODIX_BERLIN_CHECKSUM_SIZE; i++)
|
||||||
|
cal_checksum += data[i];
|
||||||
|
|
||||||
|
r_checksum = get_unaligned_le16(&data[i]);
|
||||||
|
|
||||||
|
return (u16)cal_checksum == r_checksum;
|
||||||
|
}
|
||||||
|
|
||||||
|
static bool goodix_berlin_is_dummy_data(struct goodix_berlin_core *cd,
|
||||||
|
const u8 *data, int size)
|
||||||
|
{
|
||||||
|
int i;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* If the device is missing or doesn't respond the buffer
|
||||||
|
* could be filled with bus default line state, 0x00 or 0xff,
|
||||||
|
* so declare success the first time we encounter neither.
|
||||||
|
*/
|
||||||
|
for (i = 0; i < size; i++)
|
||||||
|
if (data[i] > 0 && data[i] < 0xff)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int goodix_berlin_dev_confirm(struct goodix_berlin_core *cd)
|
||||||
|
{
|
||||||
|
u8 tx_buf[8], rx_buf[8];
|
||||||
|
int retry = 3;
|
||||||
|
int error;
|
||||||
|
|
||||||
|
memset(tx_buf, GOODIX_BERLIN_DEV_CONFIRM_VAL, sizeof(tx_buf));
|
||||||
|
while (retry--) {
|
||||||
|
error = regmap_raw_write(cd->regmap,
|
||||||
|
GOODIX_BERLIN_BOOTOPTION_ADDR,
|
||||||
|
tx_buf, sizeof(tx_buf));
|
||||||
|
if (error)
|
||||||
|
return error;
|
||||||
|
|
||||||
|
error = regmap_raw_read(cd->regmap,
|
||||||
|
GOODIX_BERLIN_BOOTOPTION_ADDR,
|
||||||
|
rx_buf, sizeof(rx_buf));
|
||||||
|
if (error)
|
||||||
|
return error;
|
||||||
|
|
||||||
|
if (!memcmp(tx_buf, rx_buf, sizeof(tx_buf)))
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
usleep_range(5000, 5100);
|
||||||
|
}
|
||||||
|
|
||||||
|
dev_err(cd->dev, "device confirm failed, rx_buf: %*ph\n",
|
||||||
|
(int)sizeof(rx_buf), rx_buf);
|
||||||
|
|
||||||
|
return -EINVAL;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int goodix_berlin_power_on(struct goodix_berlin_core *cd)
|
||||||
|
{
|
||||||
|
int error;
|
||||||
|
|
||||||
|
error = regulator_enable(cd->iovdd);
|
||||||
|
if (error) {
|
||||||
|
dev_err(cd->dev, "Failed to enable iovdd: %d\n", error);
|
||||||
|
return error;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Vendor waits 3ms for IOVDD to settle */
|
||||||
|
usleep_range(3000, 3100);
|
||||||
|
|
||||||
|
error = regulator_enable(cd->avdd);
|
||||||
|
if (error) {
|
||||||
|
dev_err(cd->dev, "Failed to enable avdd: %d\n", error);
|
||||||
|
goto err_iovdd_disable;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Vendor waits 15ms for IOVDD to settle */
|
||||||
|
usleep_range(15000, 15100);
|
||||||
|
|
||||||
|
gpiod_set_value_cansleep(cd->reset_gpio, 0);
|
||||||
|
|
||||||
|
/* Vendor waits 4ms for Firmware to initialize */
|
||||||
|
usleep_range(4000, 4100);
|
||||||
|
|
||||||
|
error = goodix_berlin_dev_confirm(cd);
|
||||||
|
if (error)
|
||||||
|
goto err_dev_reset;
|
||||||
|
|
||||||
|
/* Vendor waits 100ms for Firmware to fully boot */
|
||||||
|
msleep(GOODIX_BERLIN_NORMAL_RESET_DELAY_MS);
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
err_dev_reset:
|
||||||
|
gpiod_set_value_cansleep(cd->reset_gpio, 1);
|
||||||
|
regulator_disable(cd->avdd);
|
||||||
|
err_iovdd_disable:
|
||||||
|
regulator_disable(cd->iovdd);
|
||||||
|
return error;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void goodix_berlin_power_off(struct goodix_berlin_core *cd)
|
||||||
|
{
|
||||||
|
gpiod_set_value_cansleep(cd->reset_gpio, 1);
|
||||||
|
regulator_disable(cd->avdd);
|
||||||
|
regulator_disable(cd->iovdd);
|
||||||
|
}
|
||||||
|
|
||||||
|
static int goodix_berlin_read_version(struct goodix_berlin_core *cd)
|
||||||
|
{
|
||||||
|
int error;
|
||||||
|
|
||||||
|
error = regmap_raw_read(cd->regmap, GOODIX_BERLIN_FW_VERSION_INFO_ADDR,
|
||||||
|
&cd->fw_version, sizeof(cd->fw_version));
|
||||||
|
if (error) {
|
||||||
|
dev_err(cd->dev, "error reading fw version, %d\n", error);
|
||||||
|
return error;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!goodix_berlin_checksum_valid((u8 *)&cd->fw_version,
|
||||||
|
sizeof(cd->fw_version))) {
|
||||||
|
dev_err(cd->dev, "invalid fw version: checksum error\n");
|
||||||
|
return -EINVAL;
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Only extract necessary data for runtime */
|
||||||
|
static int goodix_berlin_parse_ic_info(struct goodix_berlin_core *cd,
|
||||||
|
const u8 *data, u16 length)
|
||||||
|
{
|
||||||
|
struct goodix_berlin_ic_info_misc *misc;
|
||||||
|
unsigned int offset = 0;
|
||||||
|
|
||||||
|
offset += sizeof(__le16); /* length */
|
||||||
|
offset += sizeof(struct goodix_berlin_ic_info_version);
|
||||||
|
offset += sizeof(struct goodix_berlin_ic_info_feature);
|
||||||
|
|
||||||
|
/* IC_INFO Parameters, variable width structure */
|
||||||
|
offset += 4 * sizeof(u8); /* drv_num, sen_num, button_num, force_num */
|
||||||
|
if (offset >= length)
|
||||||
|
goto invalid_offset;
|
||||||
|
|
||||||
|
#define ADVANCE_LE16_PARAMS() \
|
||||||
|
do { \
|
||||||
|
u8 param_num = data[offset++]; \
|
||||||
|
offset += param_num * sizeof(__le16); \
|
||||||
|
if (offset >= length) \
|
||||||
|
goto invalid_offset; \
|
||||||
|
} while (0)
|
||||||
|
ADVANCE_LE16_PARAMS(); /* active_scan_rate_num */
|
||||||
|
ADVANCE_LE16_PARAMS(); /* mutual_freq_num*/
|
||||||
|
ADVANCE_LE16_PARAMS(); /* self_tx_freq_num */
|
||||||
|
ADVANCE_LE16_PARAMS(); /* self_rx_freq_num */
|
||||||
|
ADVANCE_LE16_PARAMS(); /* stylus_freq_num */
|
||||||
|
#undef ADVANCE_LE16_PARAMS
|
||||||
|
|
||||||
|
misc = (struct goodix_berlin_ic_info_misc *)&data[offset];
|
||||||
|
cd->touch_data_addr = le32_to_cpu(misc->touch_data_addr);
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
invalid_offset:
|
||||||
|
dev_err(cd->dev, "ic_info length is invalid (offset %d length %d)\n",
|
||||||
|
offset, length);
|
||||||
|
return -EINVAL;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int goodix_berlin_get_ic_info(struct goodix_berlin_core *cd)
|
||||||
|
{
|
||||||
|
u8 *afe_data __free(kfree) = NULL;
|
||||||
|
__le16 length_raw;
|
||||||
|
u16 length;
|
||||||
|
int error;
|
||||||
|
|
||||||
|
afe_data = kzalloc(GOODIX_BERLIN_IC_INFO_MAX_LEN, GFP_KERNEL);
|
||||||
|
if (!afe_data)
|
||||||
|
return -ENOMEM;
|
||||||
|
|
||||||
|
error = regmap_raw_read(cd->regmap, GOODIX_BERLIN_IC_INFO_ADDR,
|
||||||
|
&length_raw, sizeof(length_raw));
|
||||||
|
if (error) {
|
||||||
|
dev_err(cd->dev, "failed get ic info length, %d\n", error);
|
||||||
|
return error;
|
||||||
|
}
|
||||||
|
|
||||||
|
length = le16_to_cpu(length_raw);
|
||||||
|
if (length >= GOODIX_BERLIN_IC_INFO_MAX_LEN) {
|
||||||
|
dev_err(cd->dev, "invalid ic info length %d\n", length);
|
||||||
|
return -EINVAL;
|
||||||
|
}
|
||||||
|
|
||||||
|
error = regmap_raw_read(cd->regmap, GOODIX_BERLIN_IC_INFO_ADDR,
|
||||||
|
afe_data, length);
|
||||||
|
if (error) {
|
||||||
|
dev_err(cd->dev, "failed get ic info data, %d\n", error);
|
||||||
|
return error;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* check whether the data is valid (ex. bus default values) */
|
||||||
|
if (goodix_berlin_is_dummy_data(cd, afe_data, length)) {
|
||||||
|
dev_err(cd->dev, "fw info data invalid\n");
|
||||||
|
return -EINVAL;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!goodix_berlin_checksum_valid(afe_data, length)) {
|
||||||
|
dev_err(cd->dev, "fw info checksum error\n");
|
||||||
|
return -EINVAL;
|
||||||
|
}
|
||||||
|
|
||||||
|
error = goodix_berlin_parse_ic_info(cd, afe_data, length);
|
||||||
|
if (error)
|
||||||
|
return error;
|
||||||
|
|
||||||
|
/* check some key info */
|
||||||
|
if (!cd->touch_data_addr) {
|
||||||
|
dev_err(cd->dev, "touch_data_addr is null\n");
|
||||||
|
return -EINVAL;
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int goodix_berlin_get_remaining_contacts(struct goodix_berlin_core *cd,
|
||||||
|
int n)
|
||||||
|
{
|
||||||
|
size_t offset = 2 * GOODIX_BERLIN_TOUCH_SIZE +
|
||||||
|
GOODIX_BERLIN_CHECKSUM_SIZE;
|
||||||
|
u32 addr = cd->touch_data_addr + GOODIX_BERLIN_HEADER_SIZE + offset;
|
||||||
|
int error;
|
||||||
|
|
||||||
|
error = regmap_raw_read(cd->regmap, addr,
|
||||||
|
&cd->event.data[offset],
|
||||||
|
(n - 2) * GOODIX_BERLIN_TOUCH_SIZE);
|
||||||
|
if (error) {
|
||||||
|
dev_err_ratelimited(cd->dev, "failed to get touch data, %d\n",
|
||||||
|
error);
|
||||||
|
return error;
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void goodix_berlin_report_state(struct goodix_berlin_core *cd, int n)
|
||||||
|
{
|
||||||
|
struct goodix_berlin_touch *touch_data =
|
||||||
|
(struct goodix_berlin_touch *)cd->event.data;
|
||||||
|
struct goodix_berlin_touch *t;
|
||||||
|
int i;
|
||||||
|
u8 type, id;
|
||||||
|
|
||||||
|
for (i = 0; i < n; i++) {
|
||||||
|
t = &touch_data[i];
|
||||||
|
|
||||||
|
type = FIELD_GET(GOODIX_BERLIN_POINT_TYPE_MASK, t->status);
|
||||||
|
if (type == GOODIX_BERLIN_POINT_TYPE_STYLUS ||
|
||||||
|
type == GOODIX_BERLIN_POINT_TYPE_STYLUS_HOVER) {
|
||||||
|
dev_warn_once(cd->dev, "Stylus event type not handled\n");
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
id = FIELD_GET(GOODIX_BERLIN_TOUCH_ID_MASK, t->status);
|
||||||
|
if (id >= GOODIX_BERLIN_MAX_TOUCH) {
|
||||||
|
dev_warn_ratelimited(cd->dev, "invalid finger id %d\n", id);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
input_mt_slot(cd->input_dev, id);
|
||||||
|
input_mt_report_slot_state(cd->input_dev, MT_TOOL_FINGER, true);
|
||||||
|
|
||||||
|
touchscreen_report_pos(cd->input_dev, &cd->props,
|
||||||
|
__le16_to_cpu(t->x), __le16_to_cpu(t->y),
|
||||||
|
true);
|
||||||
|
input_report_abs(cd->input_dev, ABS_MT_TOUCH_MAJOR,
|
||||||
|
__le16_to_cpu(t->w));
|
||||||
|
}
|
||||||
|
|
||||||
|
input_mt_sync_frame(cd->input_dev);
|
||||||
|
input_sync(cd->input_dev);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void goodix_berlin_touch_handler(struct goodix_berlin_core *cd)
|
||||||
|
{
|
||||||
|
u8 touch_num;
|
||||||
|
int error;
|
||||||
|
|
||||||
|
touch_num = FIELD_GET(GOODIX_BERLIN_TOUCH_COUNT_MASK,
|
||||||
|
cd->event.hdr.request_type);
|
||||||
|
if (touch_num > GOODIX_BERLIN_MAX_TOUCH) {
|
||||||
|
dev_warn(cd->dev, "invalid touch num %d\n", touch_num);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (touch_num > 2) {
|
||||||
|
/* read additional contact data if more than 2 touch events */
|
||||||
|
error = goodix_berlin_get_remaining_contacts(cd, touch_num);
|
||||||
|
if (error)
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (touch_num) {
|
||||||
|
int len = touch_num * GOODIX_BERLIN_TOUCH_SIZE +
|
||||||
|
GOODIX_BERLIN_CHECKSUM_SIZE;
|
||||||
|
if (!goodix_berlin_checksum_valid(cd->event.data, len)) {
|
||||||
|
dev_err(cd->dev, "touch data checksum error: %*ph\n",
|
||||||
|
len, cd->event.data);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
goodix_berlin_report_state(cd, touch_num);
|
||||||
|
}
|
||||||
|
|
||||||
|
static int goodix_berlin_request_handle_reset(struct goodix_berlin_core *cd)
|
||||||
|
{
|
||||||
|
gpiod_set_value_cansleep(cd->reset_gpio, 1);
|
||||||
|
usleep_range(2000, 2100);
|
||||||
|
gpiod_set_value_cansleep(cd->reset_gpio, 0);
|
||||||
|
|
||||||
|
msleep(GOODIX_BERLIN_NORMAL_RESET_DELAY_MS);
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static irqreturn_t goodix_berlin_irq(int irq, void *data)
|
||||||
|
{
|
||||||
|
struct goodix_berlin_core *cd = data;
|
||||||
|
int error;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* First, read buffer with space for 2 touch events:
|
||||||
|
* - GOODIX_BERLIN_HEADER_SIZE = 8 bytes
|
||||||
|
* - GOODIX_BERLIN_TOUCH_SIZE * 2 = 16 bytes
|
||||||
|
* - GOODIX_BERLIN_CHECKLSUM_SIZE = 2 bytes
|
||||||
|
* For a total of 26 bytes.
|
||||||
|
*
|
||||||
|
* If only a single finger is reported, we will read 8 bytes more than
|
||||||
|
* needed:
|
||||||
|
* - bytes 0-7: Header (GOODIX_BERLIN_HEADER_SIZE)
|
||||||
|
* - bytes 8-15: Finger 0 Data
|
||||||
|
* - bytes 24-25: Checksum
|
||||||
|
* - bytes 18-25: Unused 8 bytes
|
||||||
|
*
|
||||||
|
* If 2 fingers are reported, we would have read the exact needed
|
||||||
|
* amount of data and checksum would be at the end of the buffer:
|
||||||
|
* - bytes 0-7: Header (GOODIX_BERLIN_HEADER_SIZE)
|
||||||
|
* - bytes 8-15: Finger 0 Bytes 0-7
|
||||||
|
* - bytes 16-23: Finger 1 Bytes 0-7
|
||||||
|
* - bytes 24-25: Checksum
|
||||||
|
*
|
||||||
|
* If more than 2 fingers were reported, the "Checksum" bytes would
|
||||||
|
* in fact contain part of the next finger data, and then
|
||||||
|
* goodix_berlin_get_remaining_contacts() would complete the buffer
|
||||||
|
* with the missing bytes, including the trailing checksum.
|
||||||
|
* For example, if 3 fingers are reported, then we would do:
|
||||||
|
* Read 1:
|
||||||
|
* - bytes 0-7: Header (GOODIX_BERLIN_HEADER_SIZE)
|
||||||
|
* - bytes 8-15: Finger 0 Bytes 0-7
|
||||||
|
* - bytes 16-23: Finger 1 Bytes 0-7
|
||||||
|
* - bytes 24-25: Finger 2 Bytes 0-1
|
||||||
|
* Read 2 (with length of (3 - 2) * 8 = 8 bytes):
|
||||||
|
* - bytes 26-31: Finger 2 Bytes 2-7
|
||||||
|
* - bytes 32-33: Checksum
|
||||||
|
*/
|
||||||
|
error = regmap_raw_read(cd->regmap, cd->touch_data_addr,
|
||||||
|
&cd->event,
|
||||||
|
GOODIX_BERLIN_HEADER_SIZE +
|
||||||
|
2 * GOODIX_BERLIN_TOUCH_SIZE +
|
||||||
|
GOODIX_BERLIN_CHECKSUM_SIZE);
|
||||||
|
if (error) {
|
||||||
|
dev_warn_ratelimited(cd->dev,
|
||||||
|
"failed get event head data: %d\n", error);
|
||||||
|
goto out;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (cd->event.hdr.status == 0)
|
||||||
|
goto out;
|
||||||
|
|
||||||
|
if (!goodix_berlin_checksum_valid((u8 *)&cd->event.hdr,
|
||||||
|
GOODIX_BERLIN_HEADER_SIZE)) {
|
||||||
|
dev_warn_ratelimited(cd->dev,
|
||||||
|
"touch head checksum error: %*ph\n",
|
||||||
|
(int)GOODIX_BERLIN_HEADER_SIZE,
|
||||||
|
&cd->event.hdr);
|
||||||
|
goto out_clear;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (cd->event.hdr.status & GOODIX_BERLIN_TOUCH_EVENT)
|
||||||
|
goodix_berlin_touch_handler(cd);
|
||||||
|
|
||||||
|
if (cd->event.hdr.status & GOODIX_BERLIN_REQUEST_EVENT) {
|
||||||
|
switch (cd->event.hdr.request_type) {
|
||||||
|
case GOODIX_BERLIN_REQUEST_CODE_RESET:
|
||||||
|
if (cd->reset_gpio)
|
||||||
|
goodix_berlin_request_handle_reset(cd);
|
||||||
|
break;
|
||||||
|
|
||||||
|
default:
|
||||||
|
dev_warn(cd->dev, "unsupported request code 0x%x\n",
|
||||||
|
cd->event.hdr.request_type);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
out_clear:
|
||||||
|
/* Clear up status field */
|
||||||
|
regmap_write(cd->regmap, cd->touch_data_addr, 0);
|
||||||
|
|
||||||
|
out:
|
||||||
|
return IRQ_HANDLED;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int goodix_berlin_input_dev_config(struct goodix_berlin_core *cd,
|
||||||
|
const struct input_id *id)
|
||||||
|
{
|
||||||
|
struct input_dev *input_dev;
|
||||||
|
int error;
|
||||||
|
|
||||||
|
input_dev = devm_input_allocate_device(cd->dev);
|
||||||
|
if (!input_dev)
|
||||||
|
return -ENOMEM;
|
||||||
|
|
||||||
|
cd->input_dev = input_dev;
|
||||||
|
input_set_drvdata(input_dev, cd);
|
||||||
|
|
||||||
|
input_dev->name = "Goodix Berlin Capacitive TouchScreen";
|
||||||
|
input_dev->phys = "input/ts";
|
||||||
|
|
||||||
|
input_dev->id = *id;
|
||||||
|
|
||||||
|
input_set_abs_params(cd->input_dev, ABS_MT_POSITION_X,
|
||||||
|
0, SZ_64K - 1, 0, 0);
|
||||||
|
input_set_abs_params(cd->input_dev, ABS_MT_POSITION_Y,
|
||||||
|
0, SZ_64K - 1, 0, 0);
|
||||||
|
input_set_abs_params(cd->input_dev, ABS_MT_TOUCH_MAJOR, 0, 255, 0, 0);
|
||||||
|
|
||||||
|
touchscreen_parse_properties(cd->input_dev, true, &cd->props);
|
||||||
|
|
||||||
|
error = input_mt_init_slots(cd->input_dev, GOODIX_BERLIN_MAX_TOUCH,
|
||||||
|
INPUT_MT_DIRECT | INPUT_MT_DROP_UNUSED);
|
||||||
|
if (error)
|
||||||
|
return error;
|
||||||
|
|
||||||
|
error = input_register_device(cd->input_dev);
|
||||||
|
if (error)
|
||||||
|
return error;
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int goodix_berlin_suspend(struct device *dev)
|
||||||
|
{
|
||||||
|
struct goodix_berlin_core *cd = dev_get_drvdata(dev);
|
||||||
|
|
||||||
|
disable_irq(cd->irq);
|
||||||
|
goodix_berlin_power_off(cd);
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int goodix_berlin_resume(struct device *dev)
|
||||||
|
{
|
||||||
|
struct goodix_berlin_core *cd = dev_get_drvdata(dev);
|
||||||
|
int error;
|
||||||
|
|
||||||
|
error = goodix_berlin_power_on(cd);
|
||||||
|
if (error)
|
||||||
|
return error;
|
||||||
|
|
||||||
|
enable_irq(cd->irq);
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
EXPORT_GPL_SIMPLE_DEV_PM_OPS(goodix_berlin_pm_ops,
|
||||||
|
goodix_berlin_suspend, goodix_berlin_resume);
|
||||||
|
|
||||||
|
static void goodix_berlin_power_off_act(void *data)
|
||||||
|
{
|
||||||
|
struct goodix_berlin_core *cd = data;
|
||||||
|
|
||||||
|
goodix_berlin_power_off(cd);
|
||||||
|
}
|
||||||
|
|
||||||
|
int goodix_berlin_probe(struct device *dev, int irq, const struct input_id *id,
|
||||||
|
struct regmap *regmap)
|
||||||
|
{
|
||||||
|
struct goodix_berlin_core *cd;
|
||||||
|
int error;
|
||||||
|
|
||||||
|
if (irq <= 0) {
|
||||||
|
dev_err(dev, "Missing interrupt number\n");
|
||||||
|
return -EINVAL;
|
||||||
|
}
|
||||||
|
|
||||||
|
cd = devm_kzalloc(dev, sizeof(*cd), GFP_KERNEL);
|
||||||
|
if (!cd)
|
||||||
|
return -ENOMEM;
|
||||||
|
|
||||||
|
cd->dev = dev;
|
||||||
|
cd->regmap = regmap;
|
||||||
|
cd->irq = irq;
|
||||||
|
|
||||||
|
cd->reset_gpio = devm_gpiod_get_optional(dev, "reset", GPIOD_OUT_HIGH);
|
||||||
|
if (IS_ERR(cd->reset_gpio))
|
||||||
|
return dev_err_probe(dev, PTR_ERR(cd->reset_gpio),
|
||||||
|
"Failed to request reset gpio\n");
|
||||||
|
|
||||||
|
cd->avdd = devm_regulator_get(dev, "avdd");
|
||||||
|
if (IS_ERR(cd->avdd))
|
||||||
|
return dev_err_probe(dev, PTR_ERR(cd->avdd),
|
||||||
|
"Failed to request avdd regulator\n");
|
||||||
|
|
||||||
|
cd->iovdd = devm_regulator_get(dev, "iovdd");
|
||||||
|
if (IS_ERR(cd->iovdd))
|
||||||
|
return dev_err_probe(dev, PTR_ERR(cd->iovdd),
|
||||||
|
"Failed to request iovdd regulator\n");
|
||||||
|
|
||||||
|
error = goodix_berlin_power_on(cd);
|
||||||
|
if (error) {
|
||||||
|
dev_err(dev, "failed power on");
|
||||||
|
return error;
|
||||||
|
}
|
||||||
|
|
||||||
|
error = devm_add_action_or_reset(dev, goodix_berlin_power_off_act, cd);
|
||||||
|
if (error)
|
||||||
|
return error;
|
||||||
|
|
||||||
|
error = goodix_berlin_read_version(cd);
|
||||||
|
if (error) {
|
||||||
|
dev_err(dev, "failed to get version info");
|
||||||
|
return error;
|
||||||
|
}
|
||||||
|
|
||||||
|
error = goodix_berlin_get_ic_info(cd);
|
||||||
|
if (error) {
|
||||||
|
dev_err(dev, "invalid ic info, abort");
|
||||||
|
return error;
|
||||||
|
}
|
||||||
|
|
||||||
|
error = goodix_berlin_input_dev_config(cd, id);
|
||||||
|
if (error) {
|
||||||
|
dev_err(dev, "failed set input device");
|
||||||
|
return error;
|
||||||
|
}
|
||||||
|
|
||||||
|
error = devm_request_threaded_irq(dev, cd->irq, NULL, goodix_berlin_irq,
|
||||||
|
IRQF_ONESHOT, "goodix-berlin", cd);
|
||||||
|
if (error) {
|
||||||
|
dev_err(dev, "request threaded irq failed: %d\n", error);
|
||||||
|
return error;
|
||||||
|
}
|
||||||
|
|
||||||
|
dev_set_drvdata(dev, cd);
|
||||||
|
|
||||||
|
dev_dbg(dev, "Goodix Berlin %s Touchscreen Controller",
|
||||||
|
cd->fw_version.patch_pid);
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
EXPORT_SYMBOL_GPL(goodix_berlin_probe);
|
||||||
|
|
||||||
|
MODULE_LICENSE("GPL");
|
||||||
|
MODULE_DESCRIPTION("Goodix Berlin Core Touchscreen driver");
|
||||||
|
MODULE_AUTHOR("Neil Armstrong <neil.armstrong@linaro.org>");
|
75
drivers/input/touchscreen/goodix_berlin_i2c.c
Normal file
75
drivers/input/touchscreen/goodix_berlin_i2c.c
Normal file
@ -0,0 +1,75 @@
|
|||||||
|
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||||
|
/*
|
||||||
|
* Goodix Berlin Touchscreen Driver
|
||||||
|
*
|
||||||
|
* Copyright (C) 2020 - 2021 Goodix, Inc.
|
||||||
|
* Copyright (C) 2023 Linaro Ltd.
|
||||||
|
*
|
||||||
|
* Based on goodix_ts_berlin driver.
|
||||||
|
*/
|
||||||
|
#include <linux/i2c.h>
|
||||||
|
#include <linux/kernel.h>
|
||||||
|
#include <linux/module.h>
|
||||||
|
#include <linux/regmap.h>
|
||||||
|
#include <linux/input.h>
|
||||||
|
|
||||||
|
#include "goodix_berlin.h"
|
||||||
|
|
||||||
|
#define I2C_MAX_TRANSFER_SIZE 256
|
||||||
|
|
||||||
|
static const struct regmap_config goodix_berlin_i2c_regmap_conf = {
|
||||||
|
.reg_bits = 32,
|
||||||
|
.val_bits = 8,
|
||||||
|
.max_raw_read = I2C_MAX_TRANSFER_SIZE,
|
||||||
|
.max_raw_write = I2C_MAX_TRANSFER_SIZE,
|
||||||
|
};
|
||||||
|
|
||||||
|
/* vendor & product left unassigned here, should probably be updated from fw info */
|
||||||
|
static const struct input_id goodix_berlin_i2c_input_id = {
|
||||||
|
.bustype = BUS_I2C,
|
||||||
|
};
|
||||||
|
|
||||||
|
static int goodix_berlin_i2c_probe(struct i2c_client *client)
|
||||||
|
{
|
||||||
|
struct regmap *regmap;
|
||||||
|
int error;
|
||||||
|
|
||||||
|
regmap = devm_regmap_init_i2c(client, &goodix_berlin_i2c_regmap_conf);
|
||||||
|
if (IS_ERR(regmap))
|
||||||
|
return PTR_ERR(regmap);
|
||||||
|
|
||||||
|
error = goodix_berlin_probe(&client->dev, client->irq,
|
||||||
|
&goodix_berlin_i2c_input_id, regmap);
|
||||||
|
if (error)
|
||||||
|
return error;
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static const struct i2c_device_id goodix_berlin_i2c_id[] = {
|
||||||
|
{ "gt9916", 0 },
|
||||||
|
{ }
|
||||||
|
};
|
||||||
|
|
||||||
|
MODULE_DEVICE_TABLE(i2c, goodix_berlin_i2c_id);
|
||||||
|
|
||||||
|
static const struct of_device_id goodix_berlin_i2c_of_match[] = {
|
||||||
|
{ .compatible = "goodix,gt9916", },
|
||||||
|
{ }
|
||||||
|
};
|
||||||
|
MODULE_DEVICE_TABLE(of, goodix_berlin_i2c_of_match);
|
||||||
|
|
||||||
|
static struct i2c_driver goodix_berlin_i2c_driver = {
|
||||||
|
.driver = {
|
||||||
|
.name = "goodix-berlin-i2c",
|
||||||
|
.of_match_table = goodix_berlin_i2c_of_match,
|
||||||
|
.pm = pm_sleep_ptr(&goodix_berlin_pm_ops),
|
||||||
|
},
|
||||||
|
.probe = goodix_berlin_i2c_probe,
|
||||||
|
.id_table = goodix_berlin_i2c_id,
|
||||||
|
};
|
||||||
|
module_i2c_driver(goodix_berlin_i2c_driver);
|
||||||
|
|
||||||
|
MODULE_LICENSE("GPL");
|
||||||
|
MODULE_DESCRIPTION("Goodix Berlin I2C Touchscreen driver");
|
||||||
|
MODULE_AUTHOR("Neil Armstrong <neil.armstrong@linaro.org>");
|
178
drivers/input/touchscreen/goodix_berlin_spi.c
Normal file
178
drivers/input/touchscreen/goodix_berlin_spi.c
Normal file
@ -0,0 +1,178 @@
|
|||||||
|
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||||
|
/*
|
||||||
|
* Goodix Berlin Touchscreen Driver
|
||||||
|
*
|
||||||
|
* Copyright (C) 2020 - 2021 Goodix, Inc.
|
||||||
|
* Copyright (C) 2023 Linaro Ltd.
|
||||||
|
*
|
||||||
|
* Based on goodix_ts_berlin driver.
|
||||||
|
*/
|
||||||
|
#include <asm/unaligned.h>
|
||||||
|
#include <linux/kernel.h>
|
||||||
|
#include <linux/module.h>
|
||||||
|
#include <linux/regmap.h>
|
||||||
|
#include <linux/spi/spi.h>
|
||||||
|
#include <linux/input.h>
|
||||||
|
|
||||||
|
#include "goodix_berlin.h"
|
||||||
|
|
||||||
|
#define GOODIX_BERLIN_SPI_TRANS_PREFIX_LEN 1
|
||||||
|
#define GOODIX_BERLIN_REGISTER_WIDTH 4
|
||||||
|
#define GOODIX_BERLIN_SPI_READ_DUMMY_LEN 3
|
||||||
|
#define GOODIX_BERLIN_SPI_READ_PREFIX_LEN (GOODIX_BERLIN_SPI_TRANS_PREFIX_LEN + \
|
||||||
|
GOODIX_BERLIN_REGISTER_WIDTH + \
|
||||||
|
GOODIX_BERLIN_SPI_READ_DUMMY_LEN)
|
||||||
|
#define GOODIX_BERLIN_SPI_WRITE_PREFIX_LEN (GOODIX_BERLIN_SPI_TRANS_PREFIX_LEN + \
|
||||||
|
GOODIX_BERLIN_REGISTER_WIDTH)
|
||||||
|
|
||||||
|
#define GOODIX_BERLIN_SPI_WRITE_FLAG 0xF0
|
||||||
|
#define GOODIX_BERLIN_SPI_READ_FLAG 0xF1
|
||||||
|
|
||||||
|
static int goodix_berlin_spi_read(void *context, const void *reg_buf,
|
||||||
|
size_t reg_size, void *val_buf,
|
||||||
|
size_t val_size)
|
||||||
|
{
|
||||||
|
struct spi_device *spi = context;
|
||||||
|
struct spi_transfer xfers;
|
||||||
|
struct spi_message spi_msg;
|
||||||
|
const u32 *reg = reg_buf; /* reg is stored as native u32 at start of buffer */
|
||||||
|
u8 *buf;
|
||||||
|
int error;
|
||||||
|
|
||||||
|
if (reg_size != GOODIX_BERLIN_REGISTER_WIDTH)
|
||||||
|
return -EINVAL;
|
||||||
|
|
||||||
|
buf = kzalloc(GOODIX_BERLIN_SPI_READ_PREFIX_LEN + val_size, GFP_KERNEL);
|
||||||
|
if (!buf)
|
||||||
|
return -ENOMEM;
|
||||||
|
|
||||||
|
spi_message_init(&spi_msg);
|
||||||
|
memset(&xfers, 0, sizeof(xfers));
|
||||||
|
|
||||||
|
/* buffer format: 0xF1 + addr(4bytes) + dummy(3bytes) + data */
|
||||||
|
buf[0] = GOODIX_BERLIN_SPI_READ_FLAG;
|
||||||
|
put_unaligned_be32(*reg, buf + GOODIX_BERLIN_SPI_TRANS_PREFIX_LEN);
|
||||||
|
memset(buf + GOODIX_BERLIN_SPI_TRANS_PREFIX_LEN + GOODIX_BERLIN_REGISTER_WIDTH,
|
||||||
|
0xff, GOODIX_BERLIN_SPI_READ_DUMMY_LEN);
|
||||||
|
|
||||||
|
xfers.tx_buf = buf;
|
||||||
|
xfers.rx_buf = buf;
|
||||||
|
xfers.len = GOODIX_BERLIN_SPI_READ_PREFIX_LEN + val_size;
|
||||||
|
xfers.cs_change = 0;
|
||||||
|
spi_message_add_tail(&xfers, &spi_msg);
|
||||||
|
|
||||||
|
error = spi_sync(spi, &spi_msg);
|
||||||
|
if (error < 0)
|
||||||
|
dev_err(&spi->dev, "spi transfer error, %d", error);
|
||||||
|
else
|
||||||
|
memcpy(val_buf, buf + GOODIX_BERLIN_SPI_READ_PREFIX_LEN, val_size);
|
||||||
|
|
||||||
|
kfree(buf);
|
||||||
|
return error;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int goodix_berlin_spi_write(void *context, const void *data,
|
||||||
|
size_t count)
|
||||||
|
{
|
||||||
|
unsigned int len = count - GOODIX_BERLIN_REGISTER_WIDTH;
|
||||||
|
struct spi_device *spi = context;
|
||||||
|
struct spi_transfer xfers;
|
||||||
|
struct spi_message spi_msg;
|
||||||
|
const u32 *reg = data; /* reg is stored as native u32 at start of buffer */
|
||||||
|
u8 *buf;
|
||||||
|
int error;
|
||||||
|
|
||||||
|
buf = kzalloc(GOODIX_BERLIN_SPI_WRITE_PREFIX_LEN + len, GFP_KERNEL);
|
||||||
|
if (!buf)
|
||||||
|
return -ENOMEM;
|
||||||
|
|
||||||
|
spi_message_init(&spi_msg);
|
||||||
|
memset(&xfers, 0, sizeof(xfers));
|
||||||
|
|
||||||
|
buf[0] = GOODIX_BERLIN_SPI_WRITE_FLAG;
|
||||||
|
put_unaligned_be32(*reg, buf + GOODIX_BERLIN_SPI_TRANS_PREFIX_LEN);
|
||||||
|
memcpy(buf + GOODIX_BERLIN_SPI_WRITE_PREFIX_LEN,
|
||||||
|
data + GOODIX_BERLIN_REGISTER_WIDTH, len);
|
||||||
|
|
||||||
|
xfers.tx_buf = buf;
|
||||||
|
xfers.len = GOODIX_BERLIN_SPI_WRITE_PREFIX_LEN + len;
|
||||||
|
xfers.cs_change = 0;
|
||||||
|
spi_message_add_tail(&xfers, &spi_msg);
|
||||||
|
|
||||||
|
error = spi_sync(spi, &spi_msg);
|
||||||
|
if (error < 0)
|
||||||
|
dev_err(&spi->dev, "spi transfer error, %d", error);
|
||||||
|
|
||||||
|
kfree(buf);
|
||||||
|
return error;
|
||||||
|
}
|
||||||
|
|
||||||
|
static const struct regmap_config goodix_berlin_spi_regmap_conf = {
|
||||||
|
.reg_bits = 32,
|
||||||
|
.val_bits = 8,
|
||||||
|
.read = goodix_berlin_spi_read,
|
||||||
|
.write = goodix_berlin_spi_write,
|
||||||
|
};
|
||||||
|
|
||||||
|
/* vendor & product left unassigned here, should probably be updated from fw info */
|
||||||
|
static const struct input_id goodix_berlin_spi_input_id = {
|
||||||
|
.bustype = BUS_SPI,
|
||||||
|
};
|
||||||
|
|
||||||
|
static int goodix_berlin_spi_probe(struct spi_device *spi)
|
||||||
|
{
|
||||||
|
struct regmap_config regmap_config;
|
||||||
|
struct regmap *regmap;
|
||||||
|
size_t max_size;
|
||||||
|
int error = 0;
|
||||||
|
|
||||||
|
spi->mode = SPI_MODE_0;
|
||||||
|
spi->bits_per_word = 8;
|
||||||
|
error = spi_setup(spi);
|
||||||
|
if (error)
|
||||||
|
return error;
|
||||||
|
|
||||||
|
max_size = spi_max_transfer_size(spi);
|
||||||
|
|
||||||
|
regmap_config = goodix_berlin_spi_regmap_conf;
|
||||||
|
regmap_config.max_raw_read = max_size - GOODIX_BERLIN_SPI_READ_PREFIX_LEN;
|
||||||
|
regmap_config.max_raw_write = max_size - GOODIX_BERLIN_SPI_WRITE_PREFIX_LEN;
|
||||||
|
|
||||||
|
regmap = devm_regmap_init(&spi->dev, NULL, spi, ®map_config);
|
||||||
|
if (IS_ERR(regmap))
|
||||||
|
return PTR_ERR(regmap);
|
||||||
|
|
||||||
|
error = goodix_berlin_probe(&spi->dev, spi->irq,
|
||||||
|
&goodix_berlin_spi_input_id, regmap);
|
||||||
|
if (error)
|
||||||
|
return error;
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static const struct spi_device_id goodix_berlin_spi_ids[] = {
|
||||||
|
{ "gt9916" },
|
||||||
|
{ },
|
||||||
|
};
|
||||||
|
MODULE_DEVICE_TABLE(spi, goodix_berlin_spi_ids);
|
||||||
|
|
||||||
|
static const struct of_device_id goodix_berlin_spi_of_match[] = {
|
||||||
|
{ .compatible = "goodix,gt9916", },
|
||||||
|
{ }
|
||||||
|
};
|
||||||
|
MODULE_DEVICE_TABLE(of, goodix_berlin_spi_of_match);
|
||||||
|
|
||||||
|
static struct spi_driver goodix_berlin_spi_driver = {
|
||||||
|
.driver = {
|
||||||
|
.name = "goodix-berlin-spi",
|
||||||
|
.of_match_table = goodix_berlin_spi_of_match,
|
||||||
|
.pm = pm_sleep_ptr(&goodix_berlin_pm_ops),
|
||||||
|
},
|
||||||
|
.probe = goodix_berlin_spi_probe,
|
||||||
|
.id_table = goodix_berlin_spi_ids,
|
||||||
|
};
|
||||||
|
module_spi_driver(goodix_berlin_spi_driver);
|
||||||
|
|
||||||
|
MODULE_LICENSE("GPL");
|
||||||
|
MODULE_DESCRIPTION("Goodix Berlin SPI Touchscreen driver");
|
||||||
|
MODULE_AUTHOR("Neil Armstrong <neil.armstrong@linaro.org>");
|
@ -1,5 +1,6 @@
|
|||||||
// SPDX-License-Identifier: GPL-2.0-only
|
// SPDX-License-Identifier: GPL-2.0-only
|
||||||
|
|
||||||
|
#include <linux/bitfield.h>
|
||||||
#include <linux/bits.h>
|
#include <linux/bits.h>
|
||||||
#include <linux/delay.h>
|
#include <linux/delay.h>
|
||||||
#include <linux/i2c.h>
|
#include <linux/i2c.h>
|
||||||
@ -11,9 +12,15 @@
|
|||||||
#include <linux/property.h>
|
#include <linux/property.h>
|
||||||
#include <linux/regulator/consumer.h>
|
#include <linux/regulator/consumer.h>
|
||||||
|
|
||||||
|
#define IST3032C_WHOAMI 0x32c
|
||||||
|
|
||||||
|
#define IST3038B_REG_STATUS 0x20
|
||||||
|
#define IST3038B_REG_CHIPID 0x30
|
||||||
|
#define IST3038B_WHOAMI 0x30380b
|
||||||
|
|
||||||
#define IST3038C_HIB_ACCESS (0x800B << 16)
|
#define IST3038C_HIB_ACCESS (0x800B << 16)
|
||||||
#define IST3038C_DIRECT_ACCESS BIT(31)
|
#define IST3038C_DIRECT_ACCESS BIT(31)
|
||||||
#define IST3038C_REG_CHIPID 0x40001000
|
#define IST3038C_REG_CHIPID (0x40001000 | IST3038C_DIRECT_ACCESS)
|
||||||
#define IST3038C_REG_HIB_BASE 0x30000100
|
#define IST3038C_REG_HIB_BASE 0x30000100
|
||||||
#define IST3038C_REG_TOUCH_STATUS (IST3038C_REG_HIB_BASE | IST3038C_HIB_ACCESS)
|
#define IST3038C_REG_TOUCH_STATUS (IST3038C_REG_HIB_BASE | IST3038C_HIB_ACCESS)
|
||||||
#define IST3038C_REG_TOUCH_COORD (IST3038C_REG_HIB_BASE | IST3038C_HIB_ACCESS | 0x8)
|
#define IST3038C_REG_TOUCH_COORD (IST3038C_REG_HIB_BASE | IST3038C_HIB_ACCESS | 0x8)
|
||||||
@ -23,19 +30,29 @@
|
|||||||
#define IST3038C_I2C_RETRY_COUNT 3
|
#define IST3038C_I2C_RETRY_COUNT 3
|
||||||
#define IST3038C_MAX_FINGER_NUM 10
|
#define IST3038C_MAX_FINGER_NUM 10
|
||||||
#define IST3038C_X_MASK GENMASK(23, 12)
|
#define IST3038C_X_MASK GENMASK(23, 12)
|
||||||
#define IST3038C_X_SHIFT 12
|
|
||||||
#define IST3038C_Y_MASK GENMASK(11, 0)
|
#define IST3038C_Y_MASK GENMASK(11, 0)
|
||||||
#define IST3038C_AREA_MASK GENMASK(27, 24)
|
#define IST3038C_AREA_MASK GENMASK(27, 24)
|
||||||
#define IST3038C_AREA_SHIFT 24
|
|
||||||
#define IST3038C_FINGER_COUNT_MASK GENMASK(15, 12)
|
#define IST3038C_FINGER_COUNT_MASK GENMASK(15, 12)
|
||||||
#define IST3038C_FINGER_COUNT_SHIFT 12
|
|
||||||
#define IST3038C_FINGER_STATUS_MASK GENMASK(9, 0)
|
#define IST3038C_FINGER_STATUS_MASK GENMASK(9, 0)
|
||||||
|
#define IST3032C_KEY_STATUS_MASK GENMASK(20, 16)
|
||||||
|
|
||||||
|
struct imagis_properties {
|
||||||
|
unsigned int interrupt_msg_cmd;
|
||||||
|
unsigned int touch_coord_cmd;
|
||||||
|
unsigned int whoami_cmd;
|
||||||
|
unsigned int whoami_val;
|
||||||
|
bool protocol_b;
|
||||||
|
bool touch_keys_supported;
|
||||||
|
};
|
||||||
|
|
||||||
struct imagis_ts {
|
struct imagis_ts {
|
||||||
struct i2c_client *client;
|
struct i2c_client *client;
|
||||||
|
const struct imagis_properties *tdata;
|
||||||
struct input_dev *input_dev;
|
struct input_dev *input_dev;
|
||||||
struct touchscreen_properties prop;
|
struct touchscreen_properties prop;
|
||||||
struct regulator_bulk_data supplies[2];
|
struct regulator_bulk_data supplies[2];
|
||||||
|
u32 keycodes[5];
|
||||||
|
int num_keycodes;
|
||||||
};
|
};
|
||||||
|
|
||||||
static int imagis_i2c_read_reg(struct imagis_ts *ts,
|
static int imagis_i2c_read_reg(struct imagis_ts *ts,
|
||||||
@ -80,20 +97,18 @@ static irqreturn_t imagis_interrupt(int irq, void *dev_id)
|
|||||||
{
|
{
|
||||||
struct imagis_ts *ts = dev_id;
|
struct imagis_ts *ts = dev_id;
|
||||||
u32 intr_message, finger_status;
|
u32 intr_message, finger_status;
|
||||||
unsigned int finger_count, finger_pressed;
|
unsigned int finger_count, finger_pressed, key_pressed;
|
||||||
int i;
|
int i;
|
||||||
int error;
|
int error;
|
||||||
|
|
||||||
error = imagis_i2c_read_reg(ts, IST3038C_REG_INTR_MESSAGE,
|
error = imagis_i2c_read_reg(ts, ts->tdata->interrupt_msg_cmd, &intr_message);
|
||||||
&intr_message);
|
|
||||||
if (error) {
|
if (error) {
|
||||||
dev_err(&ts->client->dev,
|
dev_err(&ts->client->dev,
|
||||||
"failed to read the interrupt message: %d\n", error);
|
"failed to read the interrupt message: %d\n", error);
|
||||||
goto out;
|
goto out;
|
||||||
}
|
}
|
||||||
|
|
||||||
finger_count = (intr_message & IST3038C_FINGER_COUNT_MASK) >>
|
finger_count = FIELD_GET(IST3038C_FINGER_COUNT_MASK, intr_message);
|
||||||
IST3038C_FINGER_COUNT_SHIFT;
|
|
||||||
if (finger_count > IST3038C_MAX_FINGER_NUM) {
|
if (finger_count > IST3038C_MAX_FINGER_NUM) {
|
||||||
dev_err(&ts->client->dev,
|
dev_err(&ts->client->dev,
|
||||||
"finger count %d is more than maximum supported\n",
|
"finger count %d is more than maximum supported\n",
|
||||||
@ -101,12 +116,16 @@ static irqreturn_t imagis_interrupt(int irq, void *dev_id)
|
|||||||
goto out;
|
goto out;
|
||||||
}
|
}
|
||||||
|
|
||||||
finger_pressed = intr_message & IST3038C_FINGER_STATUS_MASK;
|
finger_pressed = FIELD_GET(IST3038C_FINGER_STATUS_MASK, intr_message);
|
||||||
|
|
||||||
for (i = 0; i < finger_count; i++) {
|
for (i = 0; i < finger_count; i++) {
|
||||||
error = imagis_i2c_read_reg(ts,
|
if (ts->tdata->protocol_b)
|
||||||
IST3038C_REG_TOUCH_COORD + (i * 4),
|
error = imagis_i2c_read_reg(ts,
|
||||||
&finger_status);
|
ts->tdata->touch_coord_cmd, &finger_status);
|
||||||
|
else
|
||||||
|
error = imagis_i2c_read_reg(ts,
|
||||||
|
ts->tdata->touch_coord_cmd + (i * 4),
|
||||||
|
&finger_status);
|
||||||
if (error) {
|
if (error) {
|
||||||
dev_err(&ts->client->dev,
|
dev_err(&ts->client->dev,
|
||||||
"failed to read coordinates for finger %d: %d\n",
|
"failed to read coordinates for finger %d: %d\n",
|
||||||
@ -118,14 +137,19 @@ static irqreturn_t imagis_interrupt(int irq, void *dev_id)
|
|||||||
input_mt_report_slot_state(ts->input_dev, MT_TOOL_FINGER,
|
input_mt_report_slot_state(ts->input_dev, MT_TOOL_FINGER,
|
||||||
finger_pressed & BIT(i));
|
finger_pressed & BIT(i));
|
||||||
touchscreen_report_pos(ts->input_dev, &ts->prop,
|
touchscreen_report_pos(ts->input_dev, &ts->prop,
|
||||||
(finger_status & IST3038C_X_MASK) >>
|
FIELD_GET(IST3038C_X_MASK, finger_status),
|
||||||
IST3038C_X_SHIFT,
|
FIELD_GET(IST3038C_Y_MASK, finger_status),
|
||||||
finger_status & IST3038C_Y_MASK, 1);
|
true);
|
||||||
input_report_abs(ts->input_dev, ABS_MT_TOUCH_MAJOR,
|
input_report_abs(ts->input_dev, ABS_MT_TOUCH_MAJOR,
|
||||||
(finger_status & IST3038C_AREA_MASK) >>
|
FIELD_GET(IST3038C_AREA_MASK, finger_status));
|
||||||
IST3038C_AREA_SHIFT);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
key_pressed = FIELD_GET(IST3032C_KEY_STATUS_MASK, intr_message);
|
||||||
|
|
||||||
|
for (int i = 0; i < ts->num_keycodes; i++)
|
||||||
|
input_report_key(ts->input_dev, ts->keycodes[i],
|
||||||
|
key_pressed & BIT(i));
|
||||||
|
|
||||||
input_mt_sync_frame(ts->input_dev);
|
input_mt_sync_frame(ts->input_dev);
|
||||||
input_sync(ts->input_dev);
|
input_sync(ts->input_dev);
|
||||||
|
|
||||||
@ -210,7 +234,24 @@ static int imagis_init_input_dev(struct imagis_ts *ts)
|
|||||||
|
|
||||||
input_set_capability(input_dev, EV_ABS, ABS_MT_POSITION_X);
|
input_set_capability(input_dev, EV_ABS, ABS_MT_POSITION_X);
|
||||||
input_set_capability(input_dev, EV_ABS, ABS_MT_POSITION_Y);
|
input_set_capability(input_dev, EV_ABS, ABS_MT_POSITION_Y);
|
||||||
input_set_abs_params(input_dev, ABS_MT_TOUCH_MAJOR, 0, 255, 0, 0);
|
input_set_abs_params(input_dev, ABS_MT_TOUCH_MAJOR, 0, 16, 0, 0);
|
||||||
|
if (ts->tdata->touch_keys_supported) {
|
||||||
|
ts->num_keycodes = of_property_read_variable_u32_array(
|
||||||
|
ts->client->dev.of_node, "linux,keycodes",
|
||||||
|
ts->keycodes, 0, ARRAY_SIZE(ts->keycodes));
|
||||||
|
if (ts->num_keycodes <= 0) {
|
||||||
|
ts->keycodes[0] = KEY_APPSELECT;
|
||||||
|
ts->keycodes[1] = KEY_BACK;
|
||||||
|
ts->num_keycodes = 2;
|
||||||
|
}
|
||||||
|
|
||||||
|
input_dev->keycodemax = ts->num_keycodes;
|
||||||
|
input_dev->keycodesize = sizeof(ts->keycodes[0]);
|
||||||
|
input_dev->keycode = ts->keycodes;
|
||||||
|
}
|
||||||
|
|
||||||
|
for (int i = 0; i < ts->num_keycodes; i++)
|
||||||
|
input_set_capability(input_dev, EV_KEY, ts->keycodes[i]);
|
||||||
|
|
||||||
touchscreen_parse_properties(input_dev, true, &ts->prop);
|
touchscreen_parse_properties(input_dev, true, &ts->prop);
|
||||||
if (!ts->prop.max_x || !ts->prop.max_y) {
|
if (!ts->prop.max_x || !ts->prop.max_y) {
|
||||||
@ -261,6 +302,12 @@ static int imagis_probe(struct i2c_client *i2c)
|
|||||||
|
|
||||||
ts->client = i2c;
|
ts->client = i2c;
|
||||||
|
|
||||||
|
ts->tdata = device_get_match_data(dev);
|
||||||
|
if (!ts->tdata) {
|
||||||
|
dev_err(dev, "missing chip data\n");
|
||||||
|
return -EINVAL;
|
||||||
|
}
|
||||||
|
|
||||||
error = imagis_init_regulators(ts);
|
error = imagis_init_regulators(ts);
|
||||||
if (error) {
|
if (error) {
|
||||||
dev_err(dev, "regulator init error: %d\n", error);
|
dev_err(dev, "regulator init error: %d\n", error);
|
||||||
@ -279,15 +326,13 @@ static int imagis_probe(struct i2c_client *i2c)
|
|||||||
return error;
|
return error;
|
||||||
}
|
}
|
||||||
|
|
||||||
error = imagis_i2c_read_reg(ts,
|
error = imagis_i2c_read_reg(ts, ts->tdata->whoami_cmd, &chip_id);
|
||||||
IST3038C_REG_CHIPID | IST3038C_DIRECT_ACCESS,
|
|
||||||
&chip_id);
|
|
||||||
if (error) {
|
if (error) {
|
||||||
dev_err(dev, "chip ID read failure: %d\n", error);
|
dev_err(dev, "chip ID read failure: %d\n", error);
|
||||||
return error;
|
return error;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (chip_id != IST3038C_WHOAMI) {
|
if (chip_id != ts->tdata->whoami_val) {
|
||||||
dev_err(dev, "unknown chip ID: 0x%x\n", chip_id);
|
dev_err(dev, "unknown chip ID: 0x%x\n", chip_id);
|
||||||
return -EINVAL;
|
return -EINVAL;
|
||||||
}
|
}
|
||||||
@ -343,9 +388,34 @@ static int imagis_resume(struct device *dev)
|
|||||||
|
|
||||||
static DEFINE_SIMPLE_DEV_PM_OPS(imagis_pm_ops, imagis_suspend, imagis_resume);
|
static DEFINE_SIMPLE_DEV_PM_OPS(imagis_pm_ops, imagis_suspend, imagis_resume);
|
||||||
|
|
||||||
|
static const struct imagis_properties imagis_3032c_data = {
|
||||||
|
.interrupt_msg_cmd = IST3038C_REG_INTR_MESSAGE,
|
||||||
|
.touch_coord_cmd = IST3038C_REG_TOUCH_COORD,
|
||||||
|
.whoami_cmd = IST3038C_REG_CHIPID,
|
||||||
|
.whoami_val = IST3032C_WHOAMI,
|
||||||
|
.touch_keys_supported = true,
|
||||||
|
};
|
||||||
|
|
||||||
|
static const struct imagis_properties imagis_3038b_data = {
|
||||||
|
.interrupt_msg_cmd = IST3038B_REG_STATUS,
|
||||||
|
.touch_coord_cmd = IST3038B_REG_STATUS,
|
||||||
|
.whoami_cmd = IST3038B_REG_CHIPID,
|
||||||
|
.whoami_val = IST3038B_WHOAMI,
|
||||||
|
.protocol_b = true,
|
||||||
|
};
|
||||||
|
|
||||||
|
static const struct imagis_properties imagis_3038c_data = {
|
||||||
|
.interrupt_msg_cmd = IST3038C_REG_INTR_MESSAGE,
|
||||||
|
.touch_coord_cmd = IST3038C_REG_TOUCH_COORD,
|
||||||
|
.whoami_cmd = IST3038C_REG_CHIPID,
|
||||||
|
.whoami_val = IST3038C_WHOAMI,
|
||||||
|
};
|
||||||
|
|
||||||
#ifdef CONFIG_OF
|
#ifdef CONFIG_OF
|
||||||
static const struct of_device_id imagis_of_match[] = {
|
static const struct of_device_id imagis_of_match[] = {
|
||||||
{ .compatible = "imagis,ist3038c", },
|
{ .compatible = "imagis,ist3032c", .data = &imagis_3032c_data },
|
||||||
|
{ .compatible = "imagis,ist3038b", .data = &imagis_3038b_data },
|
||||||
|
{ .compatible = "imagis,ist3038c", .data = &imagis_3038c_data },
|
||||||
{ },
|
{ },
|
||||||
};
|
};
|
||||||
MODULE_DEVICE_TABLE(of, imagis_of_match);
|
MODULE_DEVICE_TABLE(of, imagis_of_match);
|
||||||
|
@ -157,7 +157,6 @@ static void titsc_step_config(struct titsc *ts_dev)
|
|||||||
n++ == 0 ? STEPCONFIG_OPENDLY : 0);
|
n++ == 0 ? STEPCONFIG_OPENDLY : 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
config = 0;
|
|
||||||
config = STEPCONFIG_MODE_HWSYNC |
|
config = STEPCONFIG_MODE_HWSYNC |
|
||||||
STEPCONFIG_AVG_16 | ts_dev->bit_yn |
|
STEPCONFIG_AVG_16 | ts_dev->bit_yn |
|
||||||
STEPCONFIG_INM_ADCREFM;
|
STEPCONFIG_INM_ADCREFM;
|
||||||
|
@ -514,7 +514,7 @@ void input_enable_softrepeat(struct input_dev *dev, int delay, int period);
|
|||||||
|
|
||||||
bool input_device_enabled(struct input_dev *dev);
|
bool input_device_enabled(struct input_dev *dev);
|
||||||
|
|
||||||
extern struct class input_class;
|
extern const struct class input_class;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* struct ff_device - force-feedback part of an input device
|
* struct ff_device - force-feedback part of an input device
|
||||||
|
@ -1,8 +0,0 @@
|
|||||||
/* SPDX-License-Identifier: GPL-2.0-only */
|
|
||||||
/*
|
|
||||||
* Copyright (C) 2012 Paul Parsons <lost.distance@yahoo.com>
|
|
||||||
*/
|
|
||||||
|
|
||||||
struct navpoint_platform_data {
|
|
||||||
int port; /* PXA SSP port for pxa_ssp_request() */
|
|
||||||
};
|
|
@ -15,7 +15,7 @@
|
|||||||
#include <linux/mod_devicetable.h>
|
#include <linux/mod_devicetable.h>
|
||||||
#include <uapi/linux/serio.h>
|
#include <uapi/linux/serio.h>
|
||||||
|
|
||||||
extern struct bus_type serio_bus;
|
extern const struct bus_type serio_bus;
|
||||||
|
|
||||||
struct serio {
|
struct serio {
|
||||||
void *port_data;
|
void *port_data;
|
||||||
|
Loading…
Reference in New Issue
Block a user