Commit 57ed9567 authored by Dmitry Torokhov's avatar Dmitry Torokhov

Merge branch 'next' into for-linus

Prepare input updates for 6.9 merge window.
parents fbf8d717 81c32343
...@@ -49,7 +49,6 @@ patternProperties: ...@@ -49,7 +49,6 @@ patternProperties:
$ref: input.yaml# $ref: input.yaml#
properties: properties:
label: label:
$ref: /schemas/types.yaml#/definitions/string
description: Descriptive name of the key description: Descriptive name of the key
linux,code: true linux,code: true
......
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;
};
# 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;
};
};
# 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>;
};
};
* 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>;
};
};
# 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: ...@@ -37,8 +37,9 @@ properties:
maxItems: 1 maxItems: 1
irq-gpios: irq-gpios:
description: GPIO pin used for IRQ. The driver uses the interrupt gpio pin description: GPIO pin used for IRQ input. Additionally, this line is
as output to reset the device. sampled by the device on reset deassertion to select the I2C client
address, thus it can be driven by the host during the reset sequence.
maxItems: 1 maxItems: 1
reset-gpios: reset-gpios:
......
...@@ -9,15 +9,14 @@ title: Imagis IST30XXC family touchscreen controller ...@@ -9,15 +9,14 @@ title: Imagis IST30XXC family touchscreen controller
maintainers: maintainers:
- Markuss Broks <markuss.broks@gmail.com> - Markuss Broks <markuss.broks@gmail.com>
allOf:
- $ref: touchscreen.yaml#
properties: properties:
$nodename: $nodename:
pattern: "^touchscreen@[0-9a-f]+$" pattern: "^touchscreen@[0-9a-f]+$"
compatible: compatible:
enum: enum:
- imagis,ist3032c
- imagis,ist3038b
- imagis,ist3038c - imagis,ist3038c
reg: reg:
...@@ -32,6 +31,10 @@ properties: ...@@ -32,6 +31,10 @@ properties:
vddio-supply: vddio-supply:
description: Power supply regulator for the I2C bus description: Power supply regulator for the I2C bus
linux,keycodes:
description: Keycodes for the touch keys
maxItems: 5
touchscreen-size-x: true touchscreen-size-x: true
touchscreen-size-y: true touchscreen-size-y: true
touchscreen-fuzz-x: true touchscreen-fuzz-x: true
...@@ -42,6 +45,18 @@ properties: ...@@ -42,6 +45,18 @@ properties:
additionalProperties: false additionalProperties: false
allOf:
- $ref: touchscreen.yaml#
- if:
not:
properties:
compatible:
contains:
const: imagis,ist3032c
then:
properties:
linux,keycodes: false
required: required:
- compatible - compatible
- reg - reg
......
...@@ -17,13 +17,17 @@ properties: ...@@ -17,13 +17,17 @@ properties:
pattern: "^touchscreen(@.*)?$" pattern: "^touchscreen(@.*)?$"
compatible: compatible:
items: oneOf:
- enum: - enum:
- melfas,mms114 - melfas,mms114
- melfas,mms134s - melfas,mms134s
- melfas,mms136 - melfas,mms136
- melfas,mms152 - melfas,mms152
- melfas,mms345l - melfas,mms345l
- items:
- enum:
- melfas,mms252
- const: melfas,mms114
reg: reg:
description: I2C address description: I2C address
......
...@@ -31,7 +31,7 @@ properties: ...@@ -31,7 +31,7 @@ properties:
maxItems: 1 maxItems: 1
firmware-name: firmware-name:
$ref: /schemas/types.yaml#/definitions/string maxItems: 1
description: > description: >
File basename for board specific firmware File basename for board specific firmware
......
...@@ -23,7 +23,7 @@ List of legacy properties and respective binding document ...@@ -23,7 +23,7 @@ List of legacy properties and respective binding document
Documentation/devicetree/bindings/mfd/tc3589x.txt Documentation/devicetree/bindings/mfd/tc3589x.txt
Documentation/devicetree/bindings/input/touchscreen/ads7846.txt Documentation/devicetree/bindings/input/touchscreen/ads7846.txt
4. "linux,keypad-wakeup" Documentation/devicetree/bindings/input/qcom,pm8xxx-keypad.txt 4. "linux,keypad-wakeup" Documentation/devicetree/bindings/input/qcom,pm8xxx-keypad.txt
5. "linux,input-wakeup" Documentation/devicetree/bindings/input/samsung-keypad.txt 5. "linux,input-wakeup" Documentation/devicetree/bindings/input/samsung,s3c6410-keypad.yaml
6. "nvidia,wakeup-source" Documentation/devicetree/bindings/input/nvidia,tegra20-kbc.txt 6. "nvidia,wakeup-source" Documentation/devicetree/bindings/input/nvidia,tegra20-kbc.txt
Examples Examples
......
...@@ -38,7 +38,7 @@ static DEFINE_MUTEX(gameport_mutex); ...@@ -38,7 +38,7 @@ static DEFINE_MUTEX(gameport_mutex);
static LIST_HEAD(gameport_list); static LIST_HEAD(gameport_list);
static struct bus_type gameport_bus; static const struct bus_type gameport_bus;
static void gameport_add_port(struct gameport *gameport); static void gameport_add_port(struct gameport *gameport);
static void gameport_attach_driver(struct gameport_driver *drv); static void gameport_attach_driver(struct gameport_driver *drv);
...@@ -813,7 +813,7 @@ static int gameport_bus_match(struct device *dev, struct device_driver *drv) ...@@ -813,7 +813,7 @@ static int gameport_bus_match(struct device *dev, struct device_driver *drv)
return !gameport_drv->ignore; return !gameport_drv->ignore;
} }
static struct bus_type gameport_bus = { static const struct bus_type gameport_bus = {
.name = "gameport", .name = "gameport",
.dev_groups = gameport_device_groups, .dev_groups = gameport_device_groups,
.drv_groups = gameport_driver_groups, .drv_groups = gameport_driver_groups,
......
...@@ -18,6 +18,12 @@ ...@@ -18,6 +18,12 @@
#define VT_TRIGGER(_name) .trigger = NULL #define VT_TRIGGER(_name) .trigger = NULL
#endif #endif
#if IS_ENABLED(CONFIG_SND_CTL_LED)
#define AUDIO_TRIGGER(_name) .trigger = _name
#else
#define AUDIO_TRIGGER(_name) .trigger = NULL
#endif
static const struct { static const struct {
const char *name; const char *name;
const char *trigger; const char *trigger;
...@@ -29,7 +35,7 @@ static const struct { ...@@ -29,7 +35,7 @@ static const struct {
[LED_KANA] = { "kana", VT_TRIGGER("kbd-kanalock") }, [LED_KANA] = { "kana", VT_TRIGGER("kbd-kanalock") },
[LED_SLEEP] = { "sleep" } , [LED_SLEEP] = { "sleep" } ,
[LED_SUSPEND] = { "suspend" }, [LED_SUSPEND] = { "suspend" },
[LED_MUTE] = { "mute" }, [LED_MUTE] = { "mute", AUDIO_TRIGGER("audio-mute") },
[LED_MISC] = { "misc" }, [LED_MISC] = { "misc" },
[LED_MAIL] = { "mail" }, [LED_MAIL] = { "mail" },
[LED_CHARGING] = { "charging" }, [LED_CHARGING] = { "charging" },
......
...@@ -1918,7 +1918,7 @@ static char *input_devnode(const struct device *dev, umode_t *mode) ...@@ -1918,7 +1918,7 @@ static char *input_devnode(const struct device *dev, umode_t *mode)
return kasprintf(GFP_KERNEL, "input/%s", dev_name(dev)); return kasprintf(GFP_KERNEL, "input/%s", dev_name(dev));
} }
struct class input_class = { const struct class input_class = {
.name = "input", .name = "input",
.devnode = input_devnode, .devnode = input_devnode,
}; };
...@@ -2629,17 +2629,15 @@ int input_get_new_minor(int legacy_base, unsigned int legacy_num, ...@@ -2629,17 +2629,15 @@ int input_get_new_minor(int legacy_base, unsigned int legacy_num,
* locking is needed here. * locking is needed here.
*/ */
if (legacy_base >= 0) { if (legacy_base >= 0) {
int minor = ida_simple_get(&input_ida, int minor = ida_alloc_range(&input_ida, legacy_base,
legacy_base, legacy_base + legacy_num - 1,
legacy_base + legacy_num, GFP_KERNEL);
GFP_KERNEL);
if (minor >= 0 || !allow_dynamic) if (minor >= 0 || !allow_dynamic)
return minor; return minor;
} }
return ida_simple_get(&input_ida, return ida_alloc_range(&input_ida, INPUT_FIRST_DYNAMIC_DEV,
INPUT_FIRST_DYNAMIC_DEV, INPUT_MAX_CHAR_DEVICES, INPUT_MAX_CHAR_DEVICES - 1, GFP_KERNEL);
GFP_KERNEL);
} }
EXPORT_SYMBOL(input_get_new_minor); EXPORT_SYMBOL(input_get_new_minor);
...@@ -2652,7 +2650,7 @@ EXPORT_SYMBOL(input_get_new_minor); ...@@ -2652,7 +2650,7 @@ EXPORT_SYMBOL(input_get_new_minor);
*/ */
void input_free_minor(unsigned int minor) void input_free_minor(unsigned int minor)
{ {
ida_simple_remove(&input_ida, minor); ida_free(&input_ida, minor);
} }
EXPORT_SYMBOL(input_free_minor); EXPORT_SYMBOL(input_free_minor);
......
...@@ -127,6 +127,7 @@ static const struct xpad_device { ...@@ -127,6 +127,7 @@ static const struct xpad_device {
u8 mapping; u8 mapping;
u8 xtype; u8 xtype;
} xpad_device[] = { } xpad_device[] = {
/* Please keep this list sorted by vendor and product ID. */
{ 0x0079, 0x18d4, "GPD Win 2 X-Box Controller", 0, XTYPE_XBOX360 }, { 0x0079, 0x18d4, "GPD Win 2 X-Box Controller", 0, XTYPE_XBOX360 },
{ 0x03eb, 0xff01, "Wooting One (Legacy)", 0, XTYPE_XBOX360 }, { 0x03eb, 0xff01, "Wooting One (Legacy)", 0, XTYPE_XBOX360 },
{ 0x03eb, 0xff02, "Wooting Two (Legacy)", 0, XTYPE_XBOX360 }, { 0x03eb, 0xff02, "Wooting Two (Legacy)", 0, XTYPE_XBOX360 },
...@@ -152,9 +153,9 @@ static const struct xpad_device { ...@@ -152,9 +153,9 @@ static const struct xpad_device {
{ 0x045e, 0x02d1, "Microsoft X-Box One pad", 0, XTYPE_XBOXONE }, { 0x045e, 0x02d1, "Microsoft X-Box One pad", 0, XTYPE_XBOXONE },
{ 0x045e, 0x02dd, "Microsoft X-Box One pad (Firmware 2015)", 0, XTYPE_XBOXONE }, { 0x045e, 0x02dd, "Microsoft X-Box One pad (Firmware 2015)", 0, XTYPE_XBOXONE },
{ 0x045e, 0x02e3, "Microsoft X-Box One Elite pad", MAP_PADDLES, XTYPE_XBOXONE }, { 0x045e, 0x02e3, "Microsoft X-Box One Elite pad", MAP_PADDLES, XTYPE_XBOXONE },
{ 0x045e, 0x0b00, "Microsoft X-Box One Elite 2 pad", MAP_PADDLES, XTYPE_XBOXONE },
{ 0x045e, 0x02ea, "Microsoft X-Box One S pad", 0, XTYPE_XBOXONE }, { 0x045e, 0x02ea, "Microsoft X-Box One S pad", 0, XTYPE_XBOXONE },
{ 0x045e, 0x0719, "Xbox 360 Wireless Receiver", MAP_DPAD_TO_BUTTONS, XTYPE_XBOX360W }, { 0x045e, 0x0719, "Xbox 360 Wireless Receiver", MAP_DPAD_TO_BUTTONS, XTYPE_XBOX360W },
{ 0x045e, 0x0b00, "Microsoft X-Box One Elite 2 pad", MAP_PADDLES, XTYPE_XBOXONE },
{ 0x045e, 0x0b0a, "Microsoft X-Box Adaptive Controller", MAP_PROFILE_BUTTON, XTYPE_XBOXONE }, { 0x045e, 0x0b0a, "Microsoft X-Box Adaptive Controller", MAP_PROFILE_BUTTON, XTYPE_XBOXONE },
{ 0x045e, 0x0b12, "Microsoft Xbox Series S|X Controller", MAP_SELECT_BUTTON, XTYPE_XBOXONE }, { 0x045e, 0x0b12, "Microsoft Xbox Series S|X Controller", MAP_SELECT_BUTTON, XTYPE_XBOXONE },
{ 0x046d, 0xc21d, "Logitech Gamepad F310", 0, XTYPE_XBOX360 }, { 0x046d, 0xc21d, "Logitech Gamepad F310", 0, XTYPE_XBOX360 },
...@@ -340,7 +341,6 @@ static const struct xpad_device { ...@@ -340,7 +341,6 @@ static const struct xpad_device {
{ 0x20d6, 0x2001, "BDA Xbox Series X Wired Controller", 0, XTYPE_XBOXONE }, { 0x20d6, 0x2001, "BDA Xbox Series X Wired Controller", 0, XTYPE_XBOXONE },
{ 0x20d6, 0x2009, "PowerA Enhanced Wired Controller for Xbox Series X|S", 0, XTYPE_XBOXONE }, { 0x20d6, 0x2009, "PowerA Enhanced Wired Controller for Xbox Series X|S", 0, XTYPE_XBOXONE },
{ 0x20d6, 0x281f, "PowerA Wired Controller For Xbox 360", 0, XTYPE_XBOX360 }, { 0x20d6, 0x281f, "PowerA Wired Controller For Xbox 360", 0, XTYPE_XBOX360 },
{ 0x2e24, 0x0652, "Hyperkin Duke X-Box One pad", 0, XTYPE_XBOXONE },
{ 0x24c6, 0x5000, "Razer Atrox Arcade Stick", MAP_TRIGGERS_TO_BUTTONS, XTYPE_XBOX360 }, { 0x24c6, 0x5000, "Razer Atrox Arcade Stick", MAP_TRIGGERS_TO_BUTTONS, XTYPE_XBOX360 },
{ 0x24c6, 0x5300, "PowerA MINI PROEX Controller", 0, XTYPE_XBOX360 }, { 0x24c6, 0x5300, "PowerA MINI PROEX Controller", 0, XTYPE_XBOX360 },
{ 0x24c6, 0x5303, "Xbox Airflo wired controller", 0, XTYPE_XBOX360 }, { 0x24c6, 0x5303, "Xbox Airflo wired controller", 0, XTYPE_XBOX360 },
...@@ -355,9 +355,9 @@ static const struct xpad_device { ...@@ -355,9 +355,9 @@ static const struct xpad_device {
{ 0x24c6, 0x5502, "Hori Fighting Stick VX Alt", MAP_TRIGGERS_TO_BUTTONS, XTYPE_XBOX360 }, { 0x24c6, 0x5502, "Hori Fighting Stick VX Alt", MAP_TRIGGERS_TO_BUTTONS, XTYPE_XBOX360 },
{ 0x24c6, 0x5503, "Hori Fighting Edge", MAP_TRIGGERS_TO_BUTTONS, XTYPE_XBOX360 }, { 0x24c6, 0x5503, "Hori Fighting Edge", MAP_TRIGGERS_TO_BUTTONS, XTYPE_XBOX360 },
{ 0x24c6, 0x5506, "Hori SOULCALIBUR V Stick", 0, XTYPE_XBOX360 }, { 0x24c6, 0x5506, "Hori SOULCALIBUR V Stick", 0, XTYPE_XBOX360 },
{ 0x24c6, 0x5510, "Hori Fighting Commander ONE (Xbox 360/PC Mode)", MAP_TRIGGERS_TO_BUTTONS, XTYPE_XBOX360 },
{ 0x24c6, 0x550d, "Hori GEM Xbox controller", 0, XTYPE_XBOX360 }, { 0x24c6, 0x550d, "Hori GEM Xbox controller", 0, XTYPE_XBOX360 },
{ 0x24c6, 0x550e, "Hori Real Arcade Pro V Kai 360", MAP_TRIGGERS_TO_BUTTONS, XTYPE_XBOX360 }, { 0x24c6, 0x550e, "Hori Real Arcade Pro V Kai 360", MAP_TRIGGERS_TO_BUTTONS, XTYPE_XBOX360 },
{ 0x24c6, 0x5510, "Hori Fighting Commander ONE (Xbox 360/PC Mode)", MAP_TRIGGERS_TO_BUTTONS, XTYPE_XBOX360 },
{ 0x24c6, 0x551a, "PowerA FUSION Pro Controller", 0, XTYPE_XBOXONE }, { 0x24c6, 0x551a, "PowerA FUSION Pro Controller", 0, XTYPE_XBOXONE },
{ 0x24c6, 0x561a, "PowerA FUSION Controller", 0, XTYPE_XBOXONE }, { 0x24c6, 0x561a, "PowerA FUSION Controller", 0, XTYPE_XBOXONE },
{ 0x24c6, 0x5b00, "ThrustMaster Ferrari 458 Racing Wheel", 0, XTYPE_XBOX360 }, { 0x24c6, 0x5b00, "ThrustMaster Ferrari 458 Racing Wheel", 0, XTYPE_XBOX360 },
...@@ -366,8 +366,11 @@ static const struct xpad_device { ...@@ -366,8 +366,11 @@ static const struct xpad_device {
{ 0x24c6, 0x5d04, "Razer Sabertooth", 0, XTYPE_XBOX360 }, { 0x24c6, 0x5d04, "Razer Sabertooth", 0, XTYPE_XBOX360 },
{ 0x24c6, 0xfafe, "Rock Candy Gamepad for Xbox 360", 0, XTYPE_XBOX360 }, { 0x24c6, 0xfafe, "Rock Candy Gamepad for Xbox 360", 0, XTYPE_XBOX360 },
{ 0x2563, 0x058d, "OneXPlayer Gamepad", 0, XTYPE_XBOX360 }, { 0x2563, 0x058d, "OneXPlayer Gamepad", 0, XTYPE_XBOX360 },
{ 0x294b, 0x3303, "Snakebyte GAMEPAD BASE X", 0, XTYPE_XBOXONE },
{ 0x294b, 0x3404, "Snakebyte GAMEPAD RGB X", 0, XTYPE_XBOXONE },
{ 0x2dc8, 0x2000, "8BitDo Pro 2 Wired Controller fox Xbox", 0, XTYPE_XBOXONE }, { 0x2dc8, 0x2000, "8BitDo Pro 2 Wired Controller fox Xbox", 0, XTYPE_XBOXONE },
{ 0x2dc8, 0x3106, "8BitDo Pro 2 Wired Controller", 0, XTYPE_XBOX360 }, { 0x2dc8, 0x3106, "8BitDo Pro 2 Wired Controller", 0, XTYPE_XBOX360 },
{ 0x2e24, 0x0652, "Hyperkin Duke X-Box One pad", 0, XTYPE_XBOXONE },
{ 0x31e3, 0x1100, "Wooting One", 0, XTYPE_XBOX360 }, { 0x31e3, 0x1100, "Wooting One", 0, XTYPE_XBOX360 },
{ 0x31e3, 0x1200, "Wooting Two", 0, XTYPE_XBOX360 }, { 0x31e3, 0x1200, "Wooting Two", 0, XTYPE_XBOX360 },
{ 0x31e3, 0x1210, "Wooting Lekker", 0, XTYPE_XBOX360 }, { 0x31e3, 0x1210, "Wooting Lekker", 0, XTYPE_XBOX360 },
...@@ -465,6 +468,10 @@ static const signed short xpad_btn_paddles[] = { ...@@ -465,6 +468,10 @@ static const signed short xpad_btn_paddles[] = {
{ XPAD_XBOXONE_VENDOR_PROTOCOL((vend), 208) } { XPAD_XBOXONE_VENDOR_PROTOCOL((vend), 208) }
static const struct usb_device_id xpad_table[] = { static const struct usb_device_id xpad_table[] = {
/*
* Please keep this list sorted by vendor ID. Note that there are 2
* macros - XPAD_XBOX360_VENDOR and XPAD_XBOXONE_VENDOR.
*/
{ USB_INTERFACE_INFO('X', 'B', 0) }, /* Xbox USB-IF not-approved class */ { USB_INTERFACE_INFO('X', 'B', 0) }, /* Xbox USB-IF not-approved class */
XPAD_XBOX360_VENDOR(0x0079), /* GPD Win 2 controller */ XPAD_XBOX360_VENDOR(0x0079), /* GPD Win 2 controller */
XPAD_XBOX360_VENDOR(0x03eb), /* Wooting Keyboards (Legacy) */ XPAD_XBOX360_VENDOR(0x03eb), /* Wooting Keyboards (Legacy) */
...@@ -507,6 +514,7 @@ static const struct usb_device_id xpad_table[] = { ...@@ -507,6 +514,7 @@ static const struct usb_device_id xpad_table[] = {
XPAD_XBOXONE_VENDOR(0x24c6), /* PowerA controllers */ XPAD_XBOXONE_VENDOR(0x24c6), /* PowerA controllers */
XPAD_XBOX360_VENDOR(0x2563), /* OneXPlayer Gamepad */ XPAD_XBOX360_VENDOR(0x2563), /* OneXPlayer Gamepad */
XPAD_XBOX360_VENDOR(0x260d), /* Dareu H101 */ XPAD_XBOX360_VENDOR(0x260d), /* Dareu H101 */
XPAD_XBOXONE_VENDOR(0x294b), /* Snakebyte */
XPAD_XBOX360_VENDOR(0x2c22), /* Qanba Controllers */ XPAD_XBOX360_VENDOR(0x2c22), /* Qanba Controllers */
XPAD_XBOX360_VENDOR(0x2dc8), /* 8BitDo Pro 2 Wired Controller */ XPAD_XBOX360_VENDOR(0x2dc8), /* 8BitDo Pro 2 Wired Controller */
XPAD_XBOXONE_VENDOR(0x2dc8), /* 8BitDo Pro 2 Wired Controller for Xbox */ XPAD_XBOXONE_VENDOR(0x2dc8), /* 8BitDo Pro 2 Wired Controller for Xbox */
......
...@@ -418,7 +418,7 @@ static struct platform_driver bcm_kp_device_driver = { ...@@ -418,7 +418,7 @@ static struct platform_driver bcm_kp_device_driver = {
.probe = bcm_kp_probe, .probe = bcm_kp_probe,
.driver = { .driver = {
.name = "bcm-keypad", .name = "bcm-keypad",
.of_match_table = of_match_ptr(bcm_kp_of_match), .of_match_table = bcm_kp_of_match,
} }
}; };
......
...@@ -28,7 +28,9 @@ struct matrix_keypad { ...@@ -28,7 +28,9 @@ struct matrix_keypad {
struct input_dev *input_dev; struct input_dev *input_dev;
unsigned int row_shift; unsigned int row_shift;
DECLARE_BITMAP(disabled_gpios, MATRIX_MAX_ROWS); unsigned int row_irqs[MATRIX_MAX_ROWS];
unsigned int num_row_irqs;
DECLARE_BITMAP(wakeup_enabled_irqs, MATRIX_MAX_ROWS);
uint32_t last_key_state[MATRIX_MAX_COLS]; uint32_t last_key_state[MATRIX_MAX_COLS];
struct delayed_work work; struct delayed_work work;
...@@ -85,28 +87,18 @@ static bool row_asserted(const struct matrix_keypad_platform_data *pdata, ...@@ -85,28 +87,18 @@ static bool row_asserted(const struct matrix_keypad_platform_data *pdata,
static void enable_row_irqs(struct matrix_keypad *keypad) static void enable_row_irqs(struct matrix_keypad *keypad)
{ {
const struct matrix_keypad_platform_data *pdata = keypad->pdata;
int i; int i;
if (pdata->clustered_irq > 0) for (i = 0; i < keypad->num_row_irqs; i++)
enable_irq(pdata->clustered_irq); enable_irq(keypad->row_irqs[i]);
else {
for (i = 0; i < pdata->num_row_gpios; i++)
enable_irq(gpio_to_irq(pdata->row_gpios[i]));
}
} }
static void disable_row_irqs(struct matrix_keypad *keypad) static void disable_row_irqs(struct matrix_keypad *keypad)
{ {
const struct matrix_keypad_platform_data *pdata = keypad->pdata;
int i; int i;
if (pdata->clustered_irq > 0) for (i = 0; i < keypad->num_row_irqs; i++)
disable_irq_nosync(pdata->clustered_irq); disable_irq_nosync(keypad->row_irqs[i]);
else {
for (i = 0; i < pdata->num_row_gpios; i++)
disable_irq_nosync(gpio_to_irq(pdata->row_gpios[i]));
}
} }
/* /*
...@@ -232,44 +224,20 @@ static void matrix_keypad_stop(struct input_dev *dev) ...@@ -232,44 +224,20 @@ static void matrix_keypad_stop(struct input_dev *dev)
static void matrix_keypad_enable_wakeup(struct matrix_keypad *keypad) static void matrix_keypad_enable_wakeup(struct matrix_keypad *keypad)
{ {
const struct matrix_keypad_platform_data *pdata = keypad->pdata;
unsigned int gpio;
int i; int i;
if (pdata->clustered_irq > 0) { for_each_clear_bit(i, keypad->wakeup_enabled_irqs, keypad->num_row_irqs)
if (enable_irq_wake(pdata->clustered_irq) == 0) if (enable_irq_wake(keypad->row_irqs[i]) == 0)
keypad->gpio_all_disabled = true; __set_bit(i, keypad->wakeup_enabled_irqs);
} else {
for (i = 0; i < pdata->num_row_gpios; i++) {
if (!test_bit(i, keypad->disabled_gpios)) {
gpio = pdata->row_gpios[i];
if (enable_irq_wake(gpio_to_irq(gpio)) == 0)
__set_bit(i, keypad->disabled_gpios);
}
}
}
} }
static void matrix_keypad_disable_wakeup(struct matrix_keypad *keypad) static void matrix_keypad_disable_wakeup(struct matrix_keypad *keypad)
{ {
const struct matrix_keypad_platform_data *pdata = keypad->pdata;
unsigned int gpio;
int i; int i;
if (pdata->clustered_irq > 0) { for_each_set_bit(i, keypad->wakeup_enabled_irqs, keypad->num_row_irqs) {
if (keypad->gpio_all_disabled) { disable_irq_wake(keypad->row_irqs[i]);
disable_irq_wake(pdata->clustered_irq); __clear_bit(i, keypad->wakeup_enabled_irqs);
keypad->gpio_all_disabled = false;
}
} else {
for (i = 0; i < pdata->num_row_gpios; i++) {
if (test_and_clear_bit(i, keypad->disabled_gpios)) {
gpio = pdata->row_gpios[i];
disable_irq_wake(gpio_to_irq(gpio));
}
}
} }
} }
...@@ -306,96 +274,83 @@ static int matrix_keypad_init_gpio(struct platform_device *pdev, ...@@ -306,96 +274,83 @@ static int matrix_keypad_init_gpio(struct platform_device *pdev,
struct matrix_keypad *keypad) struct matrix_keypad *keypad)
{ {
const struct matrix_keypad_platform_data *pdata = keypad->pdata; const struct matrix_keypad_platform_data *pdata = keypad->pdata;
int i, err; int i, irq, err;
/* initialized strobe lines as outputs, activated */ /* initialized strobe lines as outputs, activated */
for (i = 0; i < pdata->num_col_gpios; i++) { for (i = 0; i < pdata->num_col_gpios; i++) {
err = gpio_request(pdata->col_gpios[i], "matrix_kbd_col"); err = devm_gpio_request(&pdev->dev,
pdata->col_gpios[i], "matrix_kbd_col");
if (err) { if (err) {
dev_err(&pdev->dev, dev_err(&pdev->dev,
"failed to request GPIO%d for COL%d\n", "failed to request GPIO%d for COL%d\n",
pdata->col_gpios[i], i); pdata->col_gpios[i], i);
goto err_free_cols; return err;
} }
gpio_direction_output(pdata->col_gpios[i], !pdata->active_low); gpio_direction_output(pdata->col_gpios[i], !pdata->active_low);
} }
for (i = 0; i < pdata->num_row_gpios; i++) { for (i = 0; i < pdata->num_row_gpios; i++) {
err = gpio_request(pdata->row_gpios[i], "matrix_kbd_row"); err = devm_gpio_request(&pdev->dev,
pdata->row_gpios[i], "matrix_kbd_row");
if (err) { if (err) {
dev_err(&pdev->dev, dev_err(&pdev->dev,
"failed to request GPIO%d for ROW%d\n", "failed to request GPIO%d for ROW%d\n",
pdata->row_gpios[i], i); pdata->row_gpios[i], i);
goto err_free_rows; return err;
} }
gpio_direction_input(pdata->row_gpios[i]); gpio_direction_input(pdata->row_gpios[i]);
} }
if (pdata->clustered_irq > 0) { if (pdata->clustered_irq > 0) {
err = request_any_context_irq(pdata->clustered_irq, err = devm_request_any_context_irq(&pdev->dev,
pdata->clustered_irq,
matrix_keypad_interrupt, matrix_keypad_interrupt,
pdata->clustered_irq_flags, pdata->clustered_irq_flags,
"matrix-keypad", keypad); "matrix-keypad", keypad);
if (err < 0) { if (err < 0) {
dev_err(&pdev->dev, dev_err(&pdev->dev,
"Unable to acquire clustered interrupt\n"); "Unable to acquire clustered interrupt\n");
goto err_free_rows; return err;
} }
keypad->row_irqs[0] = pdata->clustered_irq;
keypad->num_row_irqs = 1;
} else { } else {
for (i = 0; i < pdata->num_row_gpios; i++) { for (i = 0; i < pdata->num_row_gpios; i++) {
err = request_any_context_irq( irq = gpio_to_irq(pdata->row_gpios[i]);
gpio_to_irq(pdata->row_gpios[i]), if (irq < 0) {
err = irq;
dev_err(&pdev->dev,
"Unable to convert GPIO line %i to irq: %d\n",
pdata->row_gpios[i], err);
return err;
}
err = devm_request_any_context_irq(&pdev->dev,
irq,
matrix_keypad_interrupt, matrix_keypad_interrupt,
IRQF_TRIGGER_RISING | IRQF_TRIGGER_RISING |
IRQF_TRIGGER_FALLING, IRQF_TRIGGER_FALLING,
"matrix-keypad", keypad); "matrix-keypad", keypad);
if (err < 0) { if (err < 0) {
dev_err(&pdev->dev, dev_err(&pdev->dev,
"Unable to acquire interrupt for GPIO line %i\n", "Unable to acquire interrupt for GPIO line %i\n",
pdata->row_gpios[i]); pdata->row_gpios[i]);
goto err_free_irqs; return err;
} }
keypad->row_irqs[i] = irq;
} }
keypad->num_row_irqs = pdata->num_row_gpios;
} }
/* initialized as disabled - enabled by input->open */ /* initialized as disabled - enabled by input->open */
disable_row_irqs(keypad); disable_row_irqs(keypad);
return 0;
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) { return 0;
free_irq(pdata->clustered_irq, keypad);
} else {
for (i = 0; i < pdata->num_row_gpios; i++)
free_irq(gpio_to_irq(pdata->row_gpios[i]), keypad);
}
for (i = 0; i < pdata->num_row_gpios; i++)
gpio_free(pdata->row_gpios[i]);
for (i = 0; i < pdata->num_col_gpios; i++)
gpio_free(pdata->col_gpios[i]);
} }
#ifdef CONFIG_OF #ifdef CONFIG_OF
...@@ -494,12 +449,13 @@ static int matrix_keypad_probe(struct platform_device *pdev) ...@@ -494,12 +449,13 @@ static int matrix_keypad_probe(struct platform_device *pdev)
return -EINVAL; return -EINVAL;
} }
keypad = kzalloc(sizeof(struct matrix_keypad), GFP_KERNEL); keypad = devm_kzalloc(&pdev->dev, sizeof(*keypad), GFP_KERNEL);
input_dev = input_allocate_device(); if (!keypad)
if (!keypad || !input_dev) { return -ENOMEM;
err = -ENOMEM;
goto err_free_mem; input_dev = devm_input_allocate_device(&pdev->dev);
} if (!input_dev)
return -ENOMEM;
keypad->input_dev = input_dev; keypad->input_dev = input_dev;
keypad->pdata = pdata; keypad->pdata = pdata;
...@@ -510,7 +466,6 @@ static int matrix_keypad_probe(struct platform_device *pdev) ...@@ -510,7 +466,6 @@ static int matrix_keypad_probe(struct platform_device *pdev)
input_dev->name = pdev->name; input_dev->name = pdev->name;
input_dev->id.bustype = BUS_HOST; input_dev->id.bustype = BUS_HOST;
input_dev->dev.parent = &pdev->dev;
input_dev->open = matrix_keypad_start; input_dev->open = matrix_keypad_start;
input_dev->close = matrix_keypad_stop; input_dev->close = matrix_keypad_stop;
...@@ -520,7 +475,7 @@ static int matrix_keypad_probe(struct platform_device *pdev) ...@@ -520,7 +475,7 @@ static int matrix_keypad_probe(struct platform_device *pdev)
NULL, input_dev); NULL, input_dev);
if (err) { if (err) {
dev_err(&pdev->dev, "failed to build keymap\n"); dev_err(&pdev->dev, "failed to build keymap\n");
goto err_free_mem; return -ENOMEM;
} }
if (!pdata->no_autorepeat) if (!pdata->no_autorepeat)
...@@ -530,32 +485,16 @@ static int matrix_keypad_probe(struct platform_device *pdev) ...@@ -530,32 +485,16 @@ static int matrix_keypad_probe(struct platform_device *pdev)
err = matrix_keypad_init_gpio(pdev, keypad); err = matrix_keypad_init_gpio(pdev, keypad);
if (err) if (err)
goto err_free_mem; return err;
err = input_register_device(keypad->input_dev); err = input_register_device(keypad->input_dev);
if (err) if (err)
goto err_free_gpio; return err;
device_init_wakeup(&pdev->dev, pdata->wakeup); device_init_wakeup(&pdev->dev, pdata->wakeup);
platform_set_drvdata(pdev, keypad); platform_set_drvdata(pdev, keypad);
return 0; return 0;
err_free_gpio:
matrix_keypad_free_gpio(keypad);
err_free_mem:
input_free_device(input_dev);
kfree(keypad);
return err;
}
static void matrix_keypad_remove(struct platform_device *pdev)
{
struct matrix_keypad *keypad = platform_get_drvdata(pdev);
matrix_keypad_free_gpio(keypad);
input_unregister_device(keypad->input_dev);
kfree(keypad);
} }
#ifdef CONFIG_OF #ifdef CONFIG_OF
...@@ -568,7 +507,6 @@ MODULE_DEVICE_TABLE(of, matrix_keypad_dt_match); ...@@ -568,7 +507,6 @@ MODULE_DEVICE_TABLE(of, matrix_keypad_dt_match);
static struct platform_driver matrix_keypad_driver = { static struct platform_driver matrix_keypad_driver = {
.probe = matrix_keypad_probe, .probe = matrix_keypad_probe,
.remove_new = matrix_keypad_remove,
.driver = { .driver = {
.name = "matrix-keypad", .name = "matrix-keypad",
.pm = pm_sleep_ptr(&matrix_keypad_pm_ops), .pm = pm_sleep_ptr(&matrix_keypad_pm_ops),
......
// SPDX-License-Identifier: GPL-2.0-only
/* /*
* Marvell 88PM80x ONKEY driver * Marvell 88PM80x ONKEY driver
* *
* Copyright (C) 2012 Marvell International Ltd. * Copyright (C) 2012 Marvell International Ltd.
* Haojian Zhuang <haojian.zhuang@marvell.com> * Haojian Zhuang <haojian.zhuang@marvell.com>
* Qiao Zhou <zhouqiao@marvell.com> * Qiao Zhou <zhouqiao@marvell.com>
*
* This file is subject to the terms and conditions of the GNU General
* Public License. See the file "COPYING" in the main directory of this
* archive for more details.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/ */
#include <linux/kernel.h> #include <linux/kernel.h>
......
...@@ -620,6 +620,118 @@ static const struct iqs7222_dev_desc iqs7222_devs[] = { ...@@ -620,6 +620,118 @@ static const struct iqs7222_dev_desc iqs7222_devs[] = {
}, },
}, },
}, },
{
.prod_num = IQS7222_PROD_NUM_D,
.fw_major = 1,
.fw_minor = 2,
.touch_link = 1770,
.allow_offset = 9,
.event_offset = 10,
.comms_offset = 11,
.reg_grps = {
[IQS7222_REG_GRP_STAT] = {
.base = IQS7222_SYS_STATUS,
.num_row = 1,
.num_col = 7,
},
[IQS7222_REG_GRP_CYCLE] = {
.base = 0x8000,
.num_row = 7,
.num_col = 2,
},
[IQS7222_REG_GRP_GLBL] = {
.base = 0x8700,
.num_row = 1,
.num_col = 3,
},
[IQS7222_REG_GRP_BTN] = {
.base = 0x9000,
.num_row = 14,
.num_col = 3,
},
[IQS7222_REG_GRP_CHAN] = {
.base = 0xA000,
.num_row = 14,
.num_col = 4,
},
[IQS7222_REG_GRP_FILT] = {
.base = 0xAE00,
.num_row = 1,
.num_col = 2,
},
[IQS7222_REG_GRP_TPAD] = {
.base = 0xB000,
.num_row = 1,
.num_col = 24,
},
[IQS7222_REG_GRP_GPIO] = {
.base = 0xC000,
.num_row = 3,
.num_col = 3,
},
[IQS7222_REG_GRP_SYS] = {
.base = IQS7222_SYS_SETUP,
.num_row = 1,
.num_col = 12,
},
},
},
{
.prod_num = IQS7222_PROD_NUM_D,
.fw_major = 1,
.fw_minor = 1,
.touch_link = 1774,
.allow_offset = 9,
.event_offset = 10,
.comms_offset = 11,
.reg_grps = {
[IQS7222_REG_GRP_STAT] = {
.base = IQS7222_SYS_STATUS,
.num_row = 1,
.num_col = 7,
},
[IQS7222_REG_GRP_CYCLE] = {
.base = 0x8000,
.num_row = 7,
.num_col = 2,
},
[IQS7222_REG_GRP_GLBL] = {
.base = 0x8700,
.num_row = 1,
.num_col = 3,
},
[IQS7222_REG_GRP_BTN] = {
.base = 0x9000,
.num_row = 14,
.num_col = 3,
},
[IQS7222_REG_GRP_CHAN] = {
.base = 0xA000,
.num_row = 14,
.num_col = 4,
},
[IQS7222_REG_GRP_FILT] = {
.base = 0xAE00,
.num_row = 1,
.num_col = 2,
},
[IQS7222_REG_GRP_TPAD] = {
.base = 0xB000,
.num_row = 1,
.num_col = 24,
},
[IQS7222_REG_GRP_GPIO] = {
.base = 0xC000,
.num_row = 3,
.num_col = 3,
},
[IQS7222_REG_GRP_SYS] = {
.base = IQS7222_SYS_SETUP,
.num_row = 1,
.num_col = 12,
},
},
},
{ {
.prod_num = IQS7222_PROD_NUM_D, .prod_num = IQS7222_PROD_NUM_D,
.fw_major = 0, .fw_major = 0,
......
...@@ -439,16 +439,4 @@ config MOUSE_SYNAPTICS_USB ...@@ -439,16 +439,4 @@ config MOUSE_SYNAPTICS_USB
To compile this driver as a module, choose M here: the To compile this driver as a module, choose M here: the
module will be called synaptics_usb. module will be called synaptics_usb.
config MOUSE_NAVPOINT_PXA27x
tristate "Synaptics NavPoint (PXA27x SSP/SPI)"
depends on PXA27x && PXA_SSP
help
This driver adds support for the Synaptics NavPoint touchpad connected
to a PXA27x SSP port in SPI slave mode. The device emulates a mouse;
a tap or tap-and-a-half drag gesture emulates the left mouse button.
For example, use the xf86-input-evdev driver for an X pointing device.
To compile this driver as a module, choose M here: the
module will be called navpoint.
endif endif
...@@ -15,7 +15,6 @@ obj-$(CONFIG_MOUSE_GPIO) += gpio_mouse.o ...@@ -15,7 +15,6 @@ obj-$(CONFIG_MOUSE_GPIO) += gpio_mouse.o
obj-$(CONFIG_MOUSE_INPORT) += inport.o obj-$(CONFIG_MOUSE_INPORT) += inport.o
obj-$(CONFIG_MOUSE_LOGIBM) += logibm.o obj-$(CONFIG_MOUSE_LOGIBM) += logibm.o
obj-$(CONFIG_MOUSE_MAPLE) += maplemouse.o obj-$(CONFIG_MOUSE_MAPLE) += maplemouse.o
obj-$(CONFIG_MOUSE_NAVPOINT_PXA27x) += navpoint.o
obj-$(CONFIG_MOUSE_PC110PAD) += pc110pad.o obj-$(CONFIG_MOUSE_PC110PAD) += pc110pad.o
obj-$(CONFIG_MOUSE_PS2) += psmouse.o obj-$(CONFIG_MOUSE_PS2) += psmouse.o
obj-$(CONFIG_MOUSE_RISCPC) += rpcmouse.o obj-$(CONFIG_MOUSE_RISCPC) += rpcmouse.o
......
// 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) ...@@ -344,7 +344,7 @@ static int rmi_bus_match(struct device *dev, struct device_driver *drv)
return physical || rmi_function_match(dev, drv); return physical || rmi_function_match(dev, drv);
} }
struct bus_type rmi_bus_type = { const struct bus_type rmi_bus_type = {
.match = rmi_bus_match, .match = rmi_bus_match,
.name = "rmi4", .name = "rmi4",
}; };
......
...@@ -185,7 +185,7 @@ static inline int rmi_write_block(struct rmi_device *d, u16 addr, ...@@ -185,7 +185,7 @@ static inline int rmi_write_block(struct rmi_device *d, u16 addr,
int rmi_for_each_dev(void *data, int (*func)(struct device *dev, void *data)); int rmi_for_each_dev(void *data, int (*func)(struct device *dev, void *data));
extern struct bus_type rmi_bus_type; extern const struct bus_type rmi_bus_type;
int rmi_of_property_read_u32(struct device *dev, u32 *result, int rmi_of_property_read_u32(struct device *dev, u32 *result,
const char *prop, bool optional); const char *prop, bool optional);
......
...@@ -1196,7 +1196,11 @@ static int rmi_driver_probe(struct device *dev) ...@@ -1196,7 +1196,11 @@ static int rmi_driver_probe(struct device *dev)
} }
rmi_driver_set_input_params(rmi_dev, data->input); rmi_driver_set_input_params(rmi_dev, data->input);
data->input->phys = devm_kasprintf(dev, GFP_KERNEL, data->input->phys = devm_kasprintf(dev, GFP_KERNEL,
"%s/input0", dev_name(dev)); "%s/input0", dev_name(dev));
if (!data->input->phys) {
retval = -ENOMEM;
goto err;
}
} }
retval = rmi_init_functions(data); retval = rmi_init_functions(data);
......
...@@ -1007,7 +1007,7 @@ irqreturn_t serio_interrupt(struct serio *serio, ...@@ -1007,7 +1007,7 @@ irqreturn_t serio_interrupt(struct serio *serio,
} }
EXPORT_SYMBOL(serio_interrupt); EXPORT_SYMBOL(serio_interrupt);
struct bus_type serio_bus = { const struct bus_type serio_bus = {
.name = "serio", .name = "serio",
.drv_groups = serio_driver_groups, .drv_groups = serio_driver_groups,
.match = serio_bus_match, .match = serio_bus_match,
......
...@@ -219,8 +219,7 @@ static void sxps2_close(struct serio *pserio) ...@@ -219,8 +219,7 @@ static void sxps2_close(struct serio *pserio)
/** /**
* xps2_of_probe - probe method for the PS/2 device. * xps2_of_probe - probe method for the PS/2 device.
* @of_dev: pointer to OF device structure * @ofdev: pointer to OF device structure
* @match: pointer to the structure used for matching a device
* *
* This function probes the PS/2 device in the device tree. * This function probes the PS/2 device in the device tree.
* It initializes the driver data structure and the hardware. * It initializes the driver data structure and the hardware.
......
...@@ -416,6 +416,37 @@ config TOUCHSCREEN_GOODIX ...@@ -416,6 +416,37 @@ config TOUCHSCREEN_GOODIX
To compile this driver as a module, choose M here: the To compile this driver as a module, choose M here: the
module will be called goodix. module will be called goodix.
config TOUCHSCREEN_GOODIX_BERLIN_CORE
tristate
config TOUCHSCREEN_GOODIX_BERLIN_I2C
tristate "Goodix Berlin I2C touchscreen"
depends on I2C
select REGMAP_I2C
select TOUCHSCREEN_GOODIX_BERLIN_CORE
help
Say Y here if you have a Goodix Berlin IC connected to
your system via I2C.
If unsure, say N.
To compile this driver as a module, choose M here: the
module will be called goodix_berlin_i2c.
config TOUCHSCREEN_GOODIX_BERLIN_SPI
tristate "Goodix Berlin SPI touchscreen"
depends on SPI_MASTER
select REGMAP
select TOUCHSCREEN_GOODIX_BERLIN_CORE
help
Say Y here if you have a Goodix Berlin IC connected to
your system via SPI.
If unsure, say N.
To compile this driver as a module, choose M here: the
module will be called goodix_berlin_spi.
config TOUCHSCREEN_HIDEEP config TOUCHSCREEN_HIDEEP
tristate "HiDeep Touch IC" tristate "HiDeep Touch IC"
depends on I2C depends on I2C
......
...@@ -47,6 +47,9 @@ obj-$(CONFIG_TOUCHSCREEN_EGALAX_SERIAL) += egalax_ts_serial.o ...@@ -47,6 +47,9 @@ obj-$(CONFIG_TOUCHSCREEN_EGALAX_SERIAL) += egalax_ts_serial.o
obj-$(CONFIG_TOUCHSCREEN_EXC3000) += exc3000.o obj-$(CONFIG_TOUCHSCREEN_EXC3000) += exc3000.o
obj-$(CONFIG_TOUCHSCREEN_FUJITSU) += fujitsu_ts.o obj-$(CONFIG_TOUCHSCREEN_FUJITSU) += fujitsu_ts.o
obj-$(CONFIG_TOUCHSCREEN_GOODIX) += goodix_ts.o obj-$(CONFIG_TOUCHSCREEN_GOODIX) += goodix_ts.o
obj-$(CONFIG_TOUCHSCREEN_GOODIX_BERLIN_CORE) += goodix_berlin_core.o
obj-$(CONFIG_TOUCHSCREEN_GOODIX_BERLIN_I2C) += goodix_berlin_i2c.o
obj-$(CONFIG_TOUCHSCREEN_GOODIX_BERLIN_SPI) += goodix_berlin_spi.o
obj-$(CONFIG_TOUCHSCREEN_HIDEEP) += hideep.o obj-$(CONFIG_TOUCHSCREEN_HIDEEP) += hideep.o
obj-$(CONFIG_TOUCHSCREEN_HYNITRON_CSTXXX) += hynitron_cstxxx.o obj-$(CONFIG_TOUCHSCREEN_HYNITRON_CSTXXX) += hynitron_cstxxx.o
obj-$(CONFIG_TOUCHSCREEN_ILI210X) += ili210x.o obj-$(CONFIG_TOUCHSCREEN_ILI210X) += ili210x.o
......
/* 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
// 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>");
// 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>");
// 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, &regmap_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>");
// SPDX-License-Identifier: GPL-2.0-only // SPDX-License-Identifier: GPL-2.0-only
#include <linux/bitfield.h>
#include <linux/bits.h> #include <linux/bits.h>
#include <linux/delay.h> #include <linux/delay.h>
#include <linux/i2c.h> #include <linux/i2c.h>
...@@ -11,9 +12,15 @@ ...@@ -11,9 +12,15 @@
#include <linux/property.h> #include <linux/property.h>
#include <linux/regulator/consumer.h> #include <linux/regulator/consumer.h>
#define IST3032C_WHOAMI 0x32c
#define IST3038B_REG_STATUS 0x20
#define IST3038B_REG_CHIPID 0x30
#define IST3038B_WHOAMI 0x30380b
#define IST3038C_HIB_ACCESS (0x800B << 16) #define IST3038C_HIB_ACCESS (0x800B << 16)
#define IST3038C_DIRECT_ACCESS BIT(31) #define IST3038C_DIRECT_ACCESS BIT(31)
#define IST3038C_REG_CHIPID 0x40001000 #define IST3038C_REG_CHIPID (0x40001000 | IST3038C_DIRECT_ACCESS)
#define IST3038C_REG_HIB_BASE 0x30000100 #define IST3038C_REG_HIB_BASE 0x30000100
#define IST3038C_REG_TOUCH_STATUS (IST3038C_REG_HIB_BASE | IST3038C_HIB_ACCESS) #define IST3038C_REG_TOUCH_STATUS (IST3038C_REG_HIB_BASE | IST3038C_HIB_ACCESS)
#define IST3038C_REG_TOUCH_COORD (IST3038C_REG_HIB_BASE | IST3038C_HIB_ACCESS | 0x8) #define IST3038C_REG_TOUCH_COORD (IST3038C_REG_HIB_BASE | IST3038C_HIB_ACCESS | 0x8)
...@@ -23,19 +30,29 @@ ...@@ -23,19 +30,29 @@
#define IST3038C_I2C_RETRY_COUNT 3 #define IST3038C_I2C_RETRY_COUNT 3
#define IST3038C_MAX_FINGER_NUM 10 #define IST3038C_MAX_FINGER_NUM 10
#define IST3038C_X_MASK GENMASK(23, 12) #define IST3038C_X_MASK GENMASK(23, 12)
#define IST3038C_X_SHIFT 12
#define IST3038C_Y_MASK GENMASK(11, 0) #define IST3038C_Y_MASK GENMASK(11, 0)
#define IST3038C_AREA_MASK GENMASK(27, 24) #define IST3038C_AREA_MASK GENMASK(27, 24)
#define IST3038C_AREA_SHIFT 24
#define IST3038C_FINGER_COUNT_MASK GENMASK(15, 12) #define IST3038C_FINGER_COUNT_MASK GENMASK(15, 12)
#define IST3038C_FINGER_COUNT_SHIFT 12
#define IST3038C_FINGER_STATUS_MASK GENMASK(9, 0) #define IST3038C_FINGER_STATUS_MASK GENMASK(9, 0)
#define IST3032C_KEY_STATUS_MASK GENMASK(20, 16)
struct imagis_properties {
unsigned int interrupt_msg_cmd;
unsigned int touch_coord_cmd;
unsigned int whoami_cmd;
unsigned int whoami_val;
bool protocol_b;
bool touch_keys_supported;
};
struct imagis_ts { struct imagis_ts {
struct i2c_client *client; struct i2c_client *client;
const struct imagis_properties *tdata;
struct input_dev *input_dev; struct input_dev *input_dev;
struct touchscreen_properties prop; struct touchscreen_properties prop;
struct regulator_bulk_data supplies[2]; struct regulator_bulk_data supplies[2];
u32 keycodes[5];
int num_keycodes;
}; };
static int imagis_i2c_read_reg(struct imagis_ts *ts, static int imagis_i2c_read_reg(struct imagis_ts *ts,
...@@ -80,20 +97,18 @@ static irqreturn_t imagis_interrupt(int irq, void *dev_id) ...@@ -80,20 +97,18 @@ static irqreturn_t imagis_interrupt(int irq, void *dev_id)
{ {
struct imagis_ts *ts = dev_id; struct imagis_ts *ts = dev_id;
u32 intr_message, finger_status; u32 intr_message, finger_status;
unsigned int finger_count, finger_pressed; unsigned int finger_count, finger_pressed, key_pressed;
int i; int i;
int error; int error;
error = imagis_i2c_read_reg(ts, IST3038C_REG_INTR_MESSAGE, error = imagis_i2c_read_reg(ts, ts->tdata->interrupt_msg_cmd, &intr_message);
&intr_message);
if (error) { if (error) {
dev_err(&ts->client->dev, dev_err(&ts->client->dev,
"failed to read the interrupt message: %d\n", error); "failed to read the interrupt message: %d\n", error);
goto out; goto out;
} }
finger_count = (intr_message & IST3038C_FINGER_COUNT_MASK) >> finger_count = FIELD_GET(IST3038C_FINGER_COUNT_MASK, intr_message);
IST3038C_FINGER_COUNT_SHIFT;
if (finger_count > IST3038C_MAX_FINGER_NUM) { if (finger_count > IST3038C_MAX_FINGER_NUM) {
dev_err(&ts->client->dev, dev_err(&ts->client->dev,
"finger count %d is more than maximum supported\n", "finger count %d is more than maximum supported\n",
...@@ -101,12 +116,16 @@ static irqreturn_t imagis_interrupt(int irq, void *dev_id) ...@@ -101,12 +116,16 @@ static irqreturn_t imagis_interrupt(int irq, void *dev_id)
goto out; goto out;
} }
finger_pressed = intr_message & IST3038C_FINGER_STATUS_MASK; finger_pressed = FIELD_GET(IST3038C_FINGER_STATUS_MASK, intr_message);
for (i = 0; i < finger_count; i++) { for (i = 0; i < finger_count; i++) {
error = imagis_i2c_read_reg(ts, if (ts->tdata->protocol_b)
IST3038C_REG_TOUCH_COORD + (i * 4), error = imagis_i2c_read_reg(ts,
&finger_status); ts->tdata->touch_coord_cmd, &finger_status);
else
error = imagis_i2c_read_reg(ts,
ts->tdata->touch_coord_cmd + (i * 4),
&finger_status);
if (error) { if (error) {
dev_err(&ts->client->dev, dev_err(&ts->client->dev,
"failed to read coordinates for finger %d: %d\n", "failed to read coordinates for finger %d: %d\n",
...@@ -118,14 +137,19 @@ static irqreturn_t imagis_interrupt(int irq, void *dev_id) ...@@ -118,14 +137,19 @@ static irqreturn_t imagis_interrupt(int irq, void *dev_id)
input_mt_report_slot_state(ts->input_dev, MT_TOOL_FINGER, input_mt_report_slot_state(ts->input_dev, MT_TOOL_FINGER,
finger_pressed & BIT(i)); finger_pressed & BIT(i));
touchscreen_report_pos(ts->input_dev, &ts->prop, touchscreen_report_pos(ts->input_dev, &ts->prop,
(finger_status & IST3038C_X_MASK) >> FIELD_GET(IST3038C_X_MASK, finger_status),
IST3038C_X_SHIFT, FIELD_GET(IST3038C_Y_MASK, finger_status),
finger_status & IST3038C_Y_MASK, 1); true);
input_report_abs(ts->input_dev, ABS_MT_TOUCH_MAJOR, input_report_abs(ts->input_dev, ABS_MT_TOUCH_MAJOR,
(finger_status & IST3038C_AREA_MASK) >> FIELD_GET(IST3038C_AREA_MASK, finger_status));
IST3038C_AREA_SHIFT);
} }
key_pressed = FIELD_GET(IST3032C_KEY_STATUS_MASK, intr_message);
for (int i = 0; i < ts->num_keycodes; i++)
input_report_key(ts->input_dev, ts->keycodes[i],
key_pressed & BIT(i));
input_mt_sync_frame(ts->input_dev); input_mt_sync_frame(ts->input_dev);
input_sync(ts->input_dev); input_sync(ts->input_dev);
...@@ -210,7 +234,24 @@ static int imagis_init_input_dev(struct imagis_ts *ts) ...@@ -210,7 +234,24 @@ static int imagis_init_input_dev(struct imagis_ts *ts)
input_set_capability(input_dev, EV_ABS, ABS_MT_POSITION_X); input_set_capability(input_dev, EV_ABS, ABS_MT_POSITION_X);
input_set_capability(input_dev, EV_ABS, ABS_MT_POSITION_Y); input_set_capability(input_dev, EV_ABS, ABS_MT_POSITION_Y);
input_set_abs_params(input_dev, ABS_MT_TOUCH_MAJOR, 0, 255, 0, 0); input_set_abs_params(input_dev, ABS_MT_TOUCH_MAJOR, 0, 16, 0, 0);
if (ts->tdata->touch_keys_supported) {
ts->num_keycodes = of_property_read_variable_u32_array(
ts->client->dev.of_node, "linux,keycodes",
ts->keycodes, 0, ARRAY_SIZE(ts->keycodes));
if (ts->num_keycodes <= 0) {
ts->keycodes[0] = KEY_APPSELECT;
ts->keycodes[1] = KEY_BACK;
ts->num_keycodes = 2;
}
input_dev->keycodemax = ts->num_keycodes;
input_dev->keycodesize = sizeof(ts->keycodes[0]);
input_dev->keycode = ts->keycodes;
}
for (int i = 0; i < ts->num_keycodes; i++)
input_set_capability(input_dev, EV_KEY, ts->keycodes[i]);
touchscreen_parse_properties(input_dev, true, &ts->prop); touchscreen_parse_properties(input_dev, true, &ts->prop);
if (!ts->prop.max_x || !ts->prop.max_y) { if (!ts->prop.max_x || !ts->prop.max_y) {
...@@ -261,6 +302,12 @@ static int imagis_probe(struct i2c_client *i2c) ...@@ -261,6 +302,12 @@ static int imagis_probe(struct i2c_client *i2c)
ts->client = i2c; ts->client = i2c;
ts->tdata = device_get_match_data(dev);
if (!ts->tdata) {
dev_err(dev, "missing chip data\n");
return -EINVAL;
}
error = imagis_init_regulators(ts); error = imagis_init_regulators(ts);
if (error) { if (error) {
dev_err(dev, "regulator init error: %d\n", error); dev_err(dev, "regulator init error: %d\n", error);
...@@ -279,15 +326,13 @@ static int imagis_probe(struct i2c_client *i2c) ...@@ -279,15 +326,13 @@ static int imagis_probe(struct i2c_client *i2c)
return error; return error;
} }
error = imagis_i2c_read_reg(ts, error = imagis_i2c_read_reg(ts, ts->tdata->whoami_cmd, &chip_id);
IST3038C_REG_CHIPID | IST3038C_DIRECT_ACCESS,
&chip_id);
if (error) { if (error) {
dev_err(dev, "chip ID read failure: %d\n", error); dev_err(dev, "chip ID read failure: %d\n", error);
return error; return error;
} }
if (chip_id != IST3038C_WHOAMI) { if (chip_id != ts->tdata->whoami_val) {
dev_err(dev, "unknown chip ID: 0x%x\n", chip_id); dev_err(dev, "unknown chip ID: 0x%x\n", chip_id);
return -EINVAL; return -EINVAL;
} }
...@@ -343,9 +388,34 @@ static int imagis_resume(struct device *dev) ...@@ -343,9 +388,34 @@ static int imagis_resume(struct device *dev)
static DEFINE_SIMPLE_DEV_PM_OPS(imagis_pm_ops, imagis_suspend, imagis_resume); static DEFINE_SIMPLE_DEV_PM_OPS(imagis_pm_ops, imagis_suspend, imagis_resume);
static const struct imagis_properties imagis_3032c_data = {
.interrupt_msg_cmd = IST3038C_REG_INTR_MESSAGE,
.touch_coord_cmd = IST3038C_REG_TOUCH_COORD,
.whoami_cmd = IST3038C_REG_CHIPID,
.whoami_val = IST3032C_WHOAMI,
.touch_keys_supported = true,
};
static const struct imagis_properties imagis_3038b_data = {
.interrupt_msg_cmd = IST3038B_REG_STATUS,
.touch_coord_cmd = IST3038B_REG_STATUS,
.whoami_cmd = IST3038B_REG_CHIPID,
.whoami_val = IST3038B_WHOAMI,
.protocol_b = true,
};
static const struct imagis_properties imagis_3038c_data = {
.interrupt_msg_cmd = IST3038C_REG_INTR_MESSAGE,
.touch_coord_cmd = IST3038C_REG_TOUCH_COORD,
.whoami_cmd = IST3038C_REG_CHIPID,
.whoami_val = IST3038C_WHOAMI,
};
#ifdef CONFIG_OF #ifdef CONFIG_OF
static const struct of_device_id imagis_of_match[] = { static const struct of_device_id imagis_of_match[] = {
{ .compatible = "imagis,ist3038c", }, { .compatible = "imagis,ist3032c", .data = &imagis_3032c_data },
{ .compatible = "imagis,ist3038b", .data = &imagis_3038b_data },
{ .compatible = "imagis,ist3038c", .data = &imagis_3038c_data },
{ }, { },
}; };
MODULE_DEVICE_TABLE(of, imagis_of_match); MODULE_DEVICE_TABLE(of, imagis_of_match);
......
...@@ -157,7 +157,6 @@ static void titsc_step_config(struct titsc *ts_dev) ...@@ -157,7 +157,6 @@ static void titsc_step_config(struct titsc *ts_dev)
n++ == 0 ? STEPCONFIG_OPENDLY : 0); n++ == 0 ? STEPCONFIG_OPENDLY : 0);
} }
config = 0;
config = STEPCONFIG_MODE_HWSYNC | config = STEPCONFIG_MODE_HWSYNC |
STEPCONFIG_AVG_16 | ts_dev->bit_yn | STEPCONFIG_AVG_16 | ts_dev->bit_yn |
STEPCONFIG_INM_ADCREFM; STEPCONFIG_INM_ADCREFM;
......
...@@ -514,7 +514,7 @@ void input_enable_softrepeat(struct input_dev *dev, int delay, int period); ...@@ -514,7 +514,7 @@ void input_enable_softrepeat(struct input_dev *dev, int delay, int period);
bool input_device_enabled(struct input_dev *dev); bool input_device_enabled(struct input_dev *dev);
extern struct class input_class; extern const struct class input_class;
/** /**
* struct ff_device - force-feedback part of an input device * struct ff_device - force-feedback part of an input device
......
/* 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 @@ ...@@ -15,7 +15,7 @@
#include <linux/mod_devicetable.h> #include <linux/mod_devicetable.h>
#include <uapi/linux/serio.h> #include <uapi/linux/serio.h>
extern struct bus_type serio_bus; extern const struct bus_type serio_bus;
struct serio { struct serio {
void *port_data; void *port_data;
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment