mirror of
https://mirrors.bfsu.edu.cn/git/linux.git
synced 2024-11-23 12:14:10 +08:00
Input updates for v6.9-rc0
- a new driver for Goodix Berlin I2C and SPI touch controllers - support for IQS7222D v1.1 and v1.2 in iqs7222 driver - support for IST3032C and IST3038B parts in Imagis touchscreen driver - support for touch keys for Imagis touchscreen controllers - support for Snakebyte GAMEPADs in xpad driver - various cleanups and conversions to yaml for device tree bindings - assorted fixes and cleanups - old Synaptics navpoint driver has been removed since the only board that used it (HP iPAQ hx4700) was removed a while ago. -----BEGIN PGP SIGNATURE----- iHUEABYIAB0WIQST2eWILY88ieB2DOtAj56VGEWXnAUCZfUPyQAKCRBAj56VGEWX nItKAQCcZvRkO1ZpCxgOXY2/xrJYQ6BbAZAlfQX9bdz/MB/3BAEApDvbgqDb6G8b mQBnojvwkPB2YluN5KEGYF4gzS/f+QY= =n+Lg -----END PGP SIGNATURE----- Merge tag 'input-for-v6.9-rc0' of git://git.kernel.org/pub/scm/linux/kernel/git/dtor/input Pull input updates from Dmitry Torokhov: - a new driver for Goodix Berlin I2C and SPI touch controllers - support for IQS7222D v1.1 and v1.2 in iqs7222 driver - support for IST3032C and IST3038B parts in Imagis touchscreen driver - support for touch keys for Imagis touchscreen controllers - support for Snakebyte GAMEPADs in xpad driver - various cleanups and conversions to yaml for device tree bindings - assorted fixes and cleanups - old Synaptics navpoint driver has been removed since the only board that used it (HP iPAQ hx4700) was removed a while ago. * tag 'input-for-v6.9-rc0' of git://git.kernel.org/pub/scm/linux/kernel/git/dtor/input: (37 commits) Input: xpad - add support for Snakebyte GAMEPADs dt-bindings: input: samsung,s3c6410-keypad: convert to DT Schema Input: imagis - add touch key support dt-bindings: input: imagis: Document touch keys Input: imagis - use FIELD_GET where applicable Input: make input_class constant dt-bindings: input: atmel,captouch: convert bindings to YAML Input: iqs7222 - add support for IQS7222D v1.1 and v1.2 dt-bindings: input: allwinner,sun4i-a10-lrad: drop redundant type from label Input: serio - make serio_bus const Input: synaptics-rmi4 - make rmi_bus_type const Input: xilinx_ps2 - fix kernel-doc for xps2_of_probe function input/touchscreen: imagis: add support for IST3032C dt-bindings: input/touchscreen: imagis: add compatible for IST3032C input/touchscreen: imagis: Add support for Imagis IST3038B dt-bindings: input/touchscreen: Add compatible for IST3038B input/touchscreen: imagis: Correct the maximum touch area value Input: leds - change config symbol dependency for audio mute trigger Input: ti_am335x_tsc - remove redundant assignment to variable config Input: xpad - sort xpad_device by vendor and product ID ...
This commit is contained in:
commit
6885d7263e
@ -49,7 +49,6 @@ patternProperties:
|
||||
$ref: input.yaml#
|
||||
properties:
|
||||
label:
|
||||
$ref: /schemas/types.yaml#/definitions/string
|
||||
description: Descriptive name of the key
|
||||
|
||||
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
|
||||
|
||||
irq-gpios:
|
||||
description: GPIO pin used for IRQ. The driver uses the interrupt gpio pin
|
||||
as output to reset the device.
|
||||
description: GPIO pin used for IRQ input. Additionally, this line is
|
||||
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
|
||||
|
||||
reset-gpios:
|
||||
|
@ -9,15 +9,14 @@ title: Imagis IST30XXC family touchscreen controller
|
||||
maintainers:
|
||||
- Markuss Broks <markuss.broks@gmail.com>
|
||||
|
||||
allOf:
|
||||
- $ref: touchscreen.yaml#
|
||||
|
||||
properties:
|
||||
$nodename:
|
||||
pattern: "^touchscreen@[0-9a-f]+$"
|
||||
|
||||
compatible:
|
||||
enum:
|
||||
- imagis,ist3032c
|
||||
- imagis,ist3038b
|
||||
- imagis,ist3038c
|
||||
|
||||
reg:
|
||||
@ -32,6 +31,10 @@ properties:
|
||||
vddio-supply:
|
||||
description: Power supply regulator for the I2C bus
|
||||
|
||||
linux,keycodes:
|
||||
description: Keycodes for the touch keys
|
||||
maxItems: 5
|
||||
|
||||
touchscreen-size-x: true
|
||||
touchscreen-size-y: true
|
||||
touchscreen-fuzz-x: true
|
||||
@ -42,6 +45,18 @@ properties:
|
||||
|
||||
additionalProperties: false
|
||||
|
||||
allOf:
|
||||
- $ref: touchscreen.yaml#
|
||||
- if:
|
||||
not:
|
||||
properties:
|
||||
compatible:
|
||||
contains:
|
||||
const: imagis,ist3032c
|
||||
then:
|
||||
properties:
|
||||
linux,keycodes: false
|
||||
|
||||
required:
|
||||
- compatible
|
||||
- reg
|
||||
|
@ -17,13 +17,17 @@ properties:
|
||||
pattern: "^touchscreen(@.*)?$"
|
||||
|
||||
compatible:
|
||||
items:
|
||||
oneOf:
|
||||
- enum:
|
||||
- melfas,mms114
|
||||
- melfas,mms134s
|
||||
- melfas,mms136
|
||||
- melfas,mms152
|
||||
- melfas,mms345l
|
||||
- items:
|
||||
- enum:
|
||||
- melfas,mms252
|
||||
- const: melfas,mms114
|
||||
|
||||
reg:
|
||||
description: I2C address
|
||||
|
@ -31,7 +31,7 @@ properties:
|
||||
maxItems: 1
|
||||
|
||||
firmware-name:
|
||||
$ref: /schemas/types.yaml#/definitions/string
|
||||
maxItems: 1
|
||||
description: >
|
||||
File basename for board specific firmware
|
||||
|
||||
|
@ -27,7 +27,7 @@ List of legacy properties and respective binding document
|
||||
Documentation/devicetree/bindings/mfd/tc3589x.txt
|
||||
Documentation/devicetree/bindings/input/touchscreen/ads7846.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
|
||||
|
||||
Examples
|
||||
|
@ -38,7 +38,7 @@ static DEFINE_MUTEX(gameport_mutex);
|
||||
|
||||
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_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;
|
||||
}
|
||||
|
||||
static struct bus_type gameport_bus = {
|
||||
static const struct bus_type gameport_bus = {
|
||||
.name = "gameport",
|
||||
.dev_groups = gameport_device_groups,
|
||||
.drv_groups = gameport_driver_groups,
|
||||
|
@ -18,6 +18,12 @@
|
||||
#define VT_TRIGGER(_name) .trigger = NULL
|
||||
#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 {
|
||||
const char *name;
|
||||
const char *trigger;
|
||||
@ -29,7 +35,7 @@ static const struct {
|
||||
[LED_KANA] = { "kana", VT_TRIGGER("kbd-kanalock") },
|
||||
[LED_SLEEP] = { "sleep" } ,
|
||||
[LED_SUSPEND] = { "suspend" },
|
||||
[LED_MUTE] = { "mute" },
|
||||
[LED_MUTE] = { "mute", AUDIO_TRIGGER("audio-mute") },
|
||||
[LED_MISC] = { "misc" },
|
||||
[LED_MAIL] = { "mail" },
|
||||
[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));
|
||||
}
|
||||
|
||||
struct class input_class = {
|
||||
const struct class input_class = {
|
||||
.name = "input",
|
||||
.devnode = input_devnode,
|
||||
};
|
||||
@ -2629,17 +2629,15 @@ int input_get_new_minor(int legacy_base, unsigned int legacy_num,
|
||||
* locking is needed here.
|
||||
*/
|
||||
if (legacy_base >= 0) {
|
||||
int minor = ida_simple_get(&input_ida,
|
||||
legacy_base,
|
||||
legacy_base + legacy_num,
|
||||
GFP_KERNEL);
|
||||
int minor = ida_alloc_range(&input_ida, legacy_base,
|
||||
legacy_base + legacy_num - 1,
|
||||
GFP_KERNEL);
|
||||
if (minor >= 0 || !allow_dynamic)
|
||||
return minor;
|
||||
}
|
||||
|
||||
return ida_simple_get(&input_ida,
|
||||
INPUT_FIRST_DYNAMIC_DEV, INPUT_MAX_CHAR_DEVICES,
|
||||
GFP_KERNEL);
|
||||
return ida_alloc_range(&input_ida, INPUT_FIRST_DYNAMIC_DEV,
|
||||
INPUT_MAX_CHAR_DEVICES - 1, GFP_KERNEL);
|
||||
}
|
||||
EXPORT_SYMBOL(input_get_new_minor);
|
||||
|
||||
@ -2652,7 +2650,7 @@ EXPORT_SYMBOL(input_get_new_minor);
|
||||
*/
|
||||
void input_free_minor(unsigned int minor)
|
||||
{
|
||||
ida_simple_remove(&input_ida, minor);
|
||||
ida_free(&input_ida, minor);
|
||||
}
|
||||
EXPORT_SYMBOL(input_free_minor);
|
||||
|
||||
|
@ -127,6 +127,7 @@ static const struct xpad_device {
|
||||
u8 mapping;
|
||||
u8 xtype;
|
||||
} xpad_device[] = {
|
||||
/* Please keep this list sorted by vendor and product ID. */
|
||||
{ 0x0079, 0x18d4, "GPD Win 2 X-Box Controller", 0, XTYPE_XBOX360 },
|
||||
{ 0x03eb, 0xff01, "Wooting One (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, 0x02dd, "Microsoft X-Box One pad (Firmware 2015)", 0, 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, 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, 0x0b12, "Microsoft Xbox Series S|X Controller", MAP_SELECT_BUTTON, XTYPE_XBOXONE },
|
||||
{ 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, 0x2009, "PowerA Enhanced Wired Controller for Xbox Series X|S", 0, XTYPE_XBOXONE },
|
||||
{ 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, 0x5300, "PowerA MINI PROEX 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, 0x5503, "Hori Fighting Edge", MAP_TRIGGERS_TO_BUTTONS, 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, 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, 0x561a, "PowerA FUSION Controller", 0, XTYPE_XBOXONE },
|
||||
{ 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, 0xfafe, "Rock Candy Gamepad for Xbox 360", 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, 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, 0x1200, "Wooting Two", 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) }
|
||||
|
||||
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 */
|
||||
XPAD_XBOX360_VENDOR(0x0079), /* GPD Win 2 controller */
|
||||
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_XBOX360_VENDOR(0x2563), /* OneXPlayer Gamepad */
|
||||
XPAD_XBOX360_VENDOR(0x260d), /* Dareu H101 */
|
||||
XPAD_XBOXONE_VENDOR(0x294b), /* Snakebyte */
|
||||
XPAD_XBOX360_VENDOR(0x2c22), /* Qanba Controllers */
|
||||
XPAD_XBOX360_VENDOR(0x2dc8), /* 8BitDo Pro 2 Wired Controller */
|
||||
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,
|
||||
.driver = {
|
||||
.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;
|
||||
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];
|
||||
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)
|
||||
{
|
||||
const struct matrix_keypad_platform_data *pdata = keypad->pdata;
|
||||
int i;
|
||||
|
||||
if (pdata->clustered_irq > 0)
|
||||
enable_irq(pdata->clustered_irq);
|
||||
else {
|
||||
for (i = 0; i < pdata->num_row_gpios; i++)
|
||||
enable_irq(gpio_to_irq(pdata->row_gpios[i]));
|
||||
}
|
||||
for (i = 0; i < keypad->num_row_irqs; i++)
|
||||
enable_irq(keypad->row_irqs[i]);
|
||||
}
|
||||
|
||||
static void disable_row_irqs(struct matrix_keypad *keypad)
|
||||
{
|
||||
const struct matrix_keypad_platform_data *pdata = keypad->pdata;
|
||||
int i;
|
||||
|
||||
if (pdata->clustered_irq > 0)
|
||||
disable_irq_nosync(pdata->clustered_irq);
|
||||
else {
|
||||
for (i = 0; i < pdata->num_row_gpios; i++)
|
||||
disable_irq_nosync(gpio_to_irq(pdata->row_gpios[i]));
|
||||
}
|
||||
for (i = 0; i < keypad->num_row_irqs; i++)
|
||||
disable_irq_nosync(keypad->row_irqs[i]);
|
||||
}
|
||||
|
||||
/*
|
||||
@ -232,44 +224,20 @@ static void matrix_keypad_stop(struct input_dev *dev)
|
||||
|
||||
static void matrix_keypad_enable_wakeup(struct matrix_keypad *keypad)
|
||||
{
|
||||
const struct matrix_keypad_platform_data *pdata = keypad->pdata;
|
||||
unsigned int gpio;
|
||||
int i;
|
||||
|
||||
if (pdata->clustered_irq > 0) {
|
||||
if (enable_irq_wake(pdata->clustered_irq) == 0)
|
||||
keypad->gpio_all_disabled = true;
|
||||
} 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);
|
||||
}
|
||||
}
|
||||
}
|
||||
for_each_clear_bit(i, keypad->wakeup_enabled_irqs, keypad->num_row_irqs)
|
||||
if (enable_irq_wake(keypad->row_irqs[i]) == 0)
|
||||
__set_bit(i, keypad->wakeup_enabled_irqs);
|
||||
}
|
||||
|
||||
static void matrix_keypad_disable_wakeup(struct matrix_keypad *keypad)
|
||||
{
|
||||
const struct matrix_keypad_platform_data *pdata = keypad->pdata;
|
||||
unsigned int gpio;
|
||||
int i;
|
||||
|
||||
if (pdata->clustered_irq > 0) {
|
||||
if (keypad->gpio_all_disabled) {
|
||||
disable_irq_wake(pdata->clustered_irq);
|
||||
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));
|
||||
}
|
||||
}
|
||||
for_each_set_bit(i, keypad->wakeup_enabled_irqs, keypad->num_row_irqs) {
|
||||
disable_irq_wake(keypad->row_irqs[i]);
|
||||
__clear_bit(i, keypad->wakeup_enabled_irqs);
|
||||
}
|
||||
}
|
||||
|
||||
@ -306,96 +274,83 @@ static int matrix_keypad_init_gpio(struct platform_device *pdev,
|
||||
struct matrix_keypad *keypad)
|
||||
{
|
||||
const struct matrix_keypad_platform_data *pdata = keypad->pdata;
|
||||
int i, err;
|
||||
int i, irq, err;
|
||||
|
||||
/* initialized strobe lines as outputs, activated */
|
||||
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) {
|
||||
dev_err(&pdev->dev,
|
||||
"failed to request GPIO%d for COL%d\n",
|
||||
pdata->col_gpios[i], i);
|
||||
goto err_free_cols;
|
||||
return err;
|
||||
}
|
||||
|
||||
gpio_direction_output(pdata->col_gpios[i], !pdata->active_low);
|
||||
}
|
||||
|
||||
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) {
|
||||
dev_err(&pdev->dev,
|
||||
"failed to request GPIO%d for ROW%d\n",
|
||||
pdata->row_gpios[i], i);
|
||||
goto err_free_rows;
|
||||
return err;
|
||||
}
|
||||
|
||||
gpio_direction_input(pdata->row_gpios[i]);
|
||||
}
|
||||
|
||||
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,
|
||||
pdata->clustered_irq_flags,
|
||||
"matrix-keypad", keypad);
|
||||
if (err < 0) {
|
||||
dev_err(&pdev->dev,
|
||||
"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 {
|
||||
for (i = 0; i < pdata->num_row_gpios; i++) {
|
||||
err = request_any_context_irq(
|
||||
gpio_to_irq(pdata->row_gpios[i]),
|
||||
irq = gpio_to_irq(pdata->row_gpios[i]);
|
||||
if (irq < 0) {
|
||||
err = irq;
|
||||
dev_err(&pdev->dev,
|
||||
"Unable to convert GPIO line %i to irq: %d\n",
|
||||
pdata->row_gpios[i], err);
|
||||
return err;
|
||||
}
|
||||
|
||||
err = devm_request_any_context_irq(&pdev->dev,
|
||||
irq,
|
||||
matrix_keypad_interrupt,
|
||||
IRQF_TRIGGER_RISING |
|
||||
IRQF_TRIGGER_FALLING,
|
||||
IRQF_TRIGGER_FALLING,
|
||||
"matrix-keypad", keypad);
|
||||
if (err < 0) {
|
||||
dev_err(&pdev->dev,
|
||||
"Unable to acquire interrupt for GPIO line %i\n",
|
||||
pdata->row_gpios[i]);
|
||||
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 */
|
||||
disable_row_irqs(keypad);
|
||||
|
||||
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
|
||||
@ -494,12 +449,13 @@ static int matrix_keypad_probe(struct platform_device *pdev)
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
keypad = kzalloc(sizeof(struct matrix_keypad), GFP_KERNEL);
|
||||
input_dev = input_allocate_device();
|
||||
if (!keypad || !input_dev) {
|
||||
err = -ENOMEM;
|
||||
goto err_free_mem;
|
||||
}
|
||||
keypad = devm_kzalloc(&pdev->dev, sizeof(*keypad), GFP_KERNEL);
|
||||
if (!keypad)
|
||||
return -ENOMEM;
|
||||
|
||||
input_dev = devm_input_allocate_device(&pdev->dev);
|
||||
if (!input_dev)
|
||||
return -ENOMEM;
|
||||
|
||||
keypad->input_dev = input_dev;
|
||||
keypad->pdata = pdata;
|
||||
@ -510,7 +466,6 @@ static int matrix_keypad_probe(struct platform_device *pdev)
|
||||
|
||||
input_dev->name = pdev->name;
|
||||
input_dev->id.bustype = BUS_HOST;
|
||||
input_dev->dev.parent = &pdev->dev;
|
||||
input_dev->open = matrix_keypad_start;
|
||||
input_dev->close = matrix_keypad_stop;
|
||||
|
||||
@ -520,7 +475,7 @@ static int matrix_keypad_probe(struct platform_device *pdev)
|
||||
NULL, input_dev);
|
||||
if (err) {
|
||||
dev_err(&pdev->dev, "failed to build keymap\n");
|
||||
goto err_free_mem;
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
if (!pdata->no_autorepeat)
|
||||
@ -530,32 +485,16 @@ static int matrix_keypad_probe(struct platform_device *pdev)
|
||||
|
||||
err = matrix_keypad_init_gpio(pdev, keypad);
|
||||
if (err)
|
||||
goto err_free_mem;
|
||||
return err;
|
||||
|
||||
err = input_register_device(keypad->input_dev);
|
||||
if (err)
|
||||
goto err_free_gpio;
|
||||
return err;
|
||||
|
||||
device_init_wakeup(&pdev->dev, pdata->wakeup);
|
||||
platform_set_drvdata(pdev, keypad);
|
||||
|
||||
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
|
||||
@ -568,7 +507,6 @@ MODULE_DEVICE_TABLE(of, matrix_keypad_dt_match);
|
||||
|
||||
static struct platform_driver matrix_keypad_driver = {
|
||||
.probe = matrix_keypad_probe,
|
||||
.remove_new = matrix_keypad_remove,
|
||||
.driver = {
|
||||
.name = "matrix-keypad",
|
||||
.pm = pm_sleep_ptr(&matrix_keypad_pm_ops),
|
||||
|
@ -1,22 +1,10 @@
|
||||
// SPDX-License-Identifier: GPL-2.0-only
|
||||
/*
|
||||
* Marvell 88PM80x ONKEY driver
|
||||
*
|
||||
* Copyright (C) 2012 Marvell International Ltd.
|
||||
* Haojian Zhuang <haojian.zhuang@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>
|
||||
|
@ -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,
|
||||
.fw_major = 0,
|
||||
|
@ -439,16 +439,4 @@ config MOUSE_SYNAPTICS_USB
|
||||
To compile this driver as a module, choose M here: the
|
||||
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
|
||||
|
@ -15,7 +15,6 @@ obj-$(CONFIG_MOUSE_GPIO) += gpio_mouse.o
|
||||
obj-$(CONFIG_MOUSE_INPORT) += inport.o
|
||||
obj-$(CONFIG_MOUSE_LOGIBM) += logibm.o
|
||||
obj-$(CONFIG_MOUSE_MAPLE) += maplemouse.o
|
||||
obj-$(CONFIG_MOUSE_NAVPOINT_PXA27x) += navpoint.o
|
||||
obj-$(CONFIG_MOUSE_PC110PAD) += pc110pad.o
|
||||
obj-$(CONFIG_MOUSE_PS2) += psmouse.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);
|
||||
}
|
||||
|
||||
struct bus_type rmi_bus_type = {
|
||||
const struct bus_type rmi_bus_type = {
|
||||
.match = rmi_bus_match,
|
||||
.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));
|
||||
|
||||
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,
|
||||
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);
|
||||
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);
|
||||
|
@ -1007,7 +1007,7 @@ irqreturn_t serio_interrupt(struct serio *serio,
|
||||
}
|
||||
EXPORT_SYMBOL(serio_interrupt);
|
||||
|
||||
struct bus_type serio_bus = {
|
||||
const struct bus_type serio_bus = {
|
||||
.name = "serio",
|
||||
.drv_groups = serio_driver_groups,
|
||||
.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.
|
||||
* @of_dev: pointer to OF device structure
|
||||
* @match: pointer to the structure used for matching a device
|
||||
* @ofdev: pointer to OF device structure
|
||||
*
|
||||
* This function probes the PS/2 device in the device tree.
|
||||
* 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
|
||||
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
|
||||
tristate "HiDeep Touch IC"
|
||||
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_FUJITSU) += fujitsu_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_HYNITRON_CSTXXX) += hynitron_cstxxx.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
|
||||
|
||||
#include <linux/bitfield.h>
|
||||
#include <linux/bits.h>
|
||||
#include <linux/delay.h>
|
||||
#include <linux/i2c.h>
|
||||
@ -11,9 +12,15 @@
|
||||
#include <linux/property.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_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_TOUCH_STATUS (IST3038C_REG_HIB_BASE | IST3038C_HIB_ACCESS)
|
||||
#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_MAX_FINGER_NUM 10
|
||||
#define IST3038C_X_MASK GENMASK(23, 12)
|
||||
#define IST3038C_X_SHIFT 12
|
||||
#define IST3038C_Y_MASK GENMASK(11, 0)
|
||||
#define IST3038C_AREA_MASK GENMASK(27, 24)
|
||||
#define IST3038C_AREA_SHIFT 24
|
||||
#define IST3038C_FINGER_COUNT_MASK GENMASK(15, 12)
|
||||
#define IST3038C_FINGER_COUNT_SHIFT 12
|
||||
#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 i2c_client *client;
|
||||
const struct imagis_properties *tdata;
|
||||
struct input_dev *input_dev;
|
||||
struct touchscreen_properties prop;
|
||||
struct regulator_bulk_data supplies[2];
|
||||
u32 keycodes[5];
|
||||
int num_keycodes;
|
||||
};
|
||||
|
||||
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;
|
||||
u32 intr_message, finger_status;
|
||||
unsigned int finger_count, finger_pressed;
|
||||
unsigned int finger_count, finger_pressed, key_pressed;
|
||||
int i;
|
||||
int error;
|
||||
|
||||
error = imagis_i2c_read_reg(ts, IST3038C_REG_INTR_MESSAGE,
|
||||
&intr_message);
|
||||
error = imagis_i2c_read_reg(ts, ts->tdata->interrupt_msg_cmd, &intr_message);
|
||||
if (error) {
|
||||
dev_err(&ts->client->dev,
|
||||
"failed to read the interrupt message: %d\n", error);
|
||||
goto out;
|
||||
}
|
||||
|
||||
finger_count = (intr_message & IST3038C_FINGER_COUNT_MASK) >>
|
||||
IST3038C_FINGER_COUNT_SHIFT;
|
||||
finger_count = FIELD_GET(IST3038C_FINGER_COUNT_MASK, intr_message);
|
||||
if (finger_count > IST3038C_MAX_FINGER_NUM) {
|
||||
dev_err(&ts->client->dev,
|
||||
"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;
|
||||
}
|
||||
|
||||
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++) {
|
||||
error = imagis_i2c_read_reg(ts,
|
||||
IST3038C_REG_TOUCH_COORD + (i * 4),
|
||||
&finger_status);
|
||||
if (ts->tdata->protocol_b)
|
||||
error = imagis_i2c_read_reg(ts,
|
||||
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) {
|
||||
dev_err(&ts->client->dev,
|
||||
"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,
|
||||
finger_pressed & BIT(i));
|
||||
touchscreen_report_pos(ts->input_dev, &ts->prop,
|
||||
(finger_status & IST3038C_X_MASK) >>
|
||||
IST3038C_X_SHIFT,
|
||||
finger_status & IST3038C_Y_MASK, 1);
|
||||
FIELD_GET(IST3038C_X_MASK, finger_status),
|
||||
FIELD_GET(IST3038C_Y_MASK, finger_status),
|
||||
true);
|
||||
input_report_abs(ts->input_dev, ABS_MT_TOUCH_MAJOR,
|
||||
(finger_status & IST3038C_AREA_MASK) >>
|
||||
IST3038C_AREA_SHIFT);
|
||||
FIELD_GET(IST3038C_AREA_MASK, finger_status));
|
||||
}
|
||||
|
||||
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_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_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);
|
||||
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->tdata = device_get_match_data(dev);
|
||||
if (!ts->tdata) {
|
||||
dev_err(dev, "missing chip data\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
error = imagis_init_regulators(ts);
|
||||
if (error) {
|
||||
dev_err(dev, "regulator init error: %d\n", error);
|
||||
@ -279,15 +326,13 @@ static int imagis_probe(struct i2c_client *i2c)
|
||||
return error;
|
||||
}
|
||||
|
||||
error = imagis_i2c_read_reg(ts,
|
||||
IST3038C_REG_CHIPID | IST3038C_DIRECT_ACCESS,
|
||||
&chip_id);
|
||||
error = imagis_i2c_read_reg(ts, ts->tdata->whoami_cmd, &chip_id);
|
||||
if (error) {
|
||||
dev_err(dev, "chip ID read failure: %d\n", 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);
|
||||
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 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
|
||||
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);
|
||||
|
@ -157,7 +157,6 @@ static void titsc_step_config(struct titsc *ts_dev)
|
||||
n++ == 0 ? STEPCONFIG_OPENDLY : 0);
|
||||
}
|
||||
|
||||
config = 0;
|
||||
config = STEPCONFIG_MODE_HWSYNC |
|
||||
STEPCONFIG_AVG_16 | ts_dev->bit_yn |
|
||||
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);
|
||||
|
||||
extern struct class input_class;
|
||||
extern const struct class input_class;
|
||||
|
||||
/**
|
||||
* 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 <uapi/linux/serio.h>
|
||||
|
||||
extern struct bus_type serio_bus;
|
||||
extern const struct bus_type serio_bus;
|
||||
|
||||
struct serio {
|
||||
void *port_data;
|
||||
|
Loading…
Reference in New Issue
Block a user