Input updates for v6.17-rc0

- updates to several drivers consuming GPIO APIs to use setters
   returning error codes
 
 - an infrastructure allowing to define "overlays" for touchscreens
   carving out regions implementing buttons and other elements from a
   bigger sensors and a corresponding update to st1232 driver
 
 - an update to AT/PS2 keyboard driver to map F13-F24 by default
 
 - Samsung keypad driver got a facelift
 
 - evdev input handler will now bind to all devices using EV_SYN event
   instead of abusing id->driver_info
 
 - 2 new sub-drivers implementing 1A (capacitive buttons) and 21
   (forcepad button) functions in Synaptics RMI driver
 
 - support for polling mode in Goodix touchscreen driver
 
 - support for support for FocalTech FT8716 in edt-ft5x06 driver
 
 - support for MT6359 in mtk-pmic-keys driver
 
 - removal of pcf50633-input driver since platform it was used on is gone
 
 - new definitions for game controller "grip" buttons (BTN_GRIP*) and
   corresponding changes to xpad and hid-steam controller drivers
 
 - a new definition for "performance" key
 -----BEGIN PGP SIGNATURE-----
 
 iHUEABYKAB0WIQST2eWILY88ieB2DOtAj56VGEWXnAUCaJOUOwAKCRBAj56VGEWX
 nPJTAQC1hrYgCv1L8hxUukbGH5FFPF2nlk63U78izh/dd7R9fwD+OqrQT7+IEr/5
 VBqxiqdy77R1mmYs8RpmjOSTg0MEwwg=
 =jm8o
 -----END PGP SIGNATURE-----

Merge tag 'input-for-v6.17-rc0' of git://git.kernel.org/pub/scm/linux/kernel/git/dtor/input

Pull input updates from Dmitry Torokhov:

 - updates to several drivers consuming GPIO APIs to use setters
   returning error codes

 - an infrastructure allowing to define "overlays" for touchscreens
   carving out regions implementing buttons and other elements from a
   bigger sensors and a corresponding update to st1232 driver

 - an update to AT/PS2 keyboard driver to map F13-F24 by default

 - Samsung keypad driver got a facelift

 - evdev input handler will now bind to all devices using EV_SYN event
   instead of abusing id->driver_info

 - two new sub-drivers implementing 1A (capacitive buttons) and 21
   (forcepad button) functions in Synaptics RMI driver

 - support for polling mode in Goodix touchscreen driver

 - support for support for FocalTech FT8716 in edt-ft5x06 driver

 - support for MT6359 in mtk-pmic-keys driver

 - removal of pcf50633-input driver since platform it was used on is
   gone

 - new definitions for game controller "grip" buttons (BTN_GRIP*) and
   corresponding changes to xpad and hid-steam controller drivers

 - a new definition for "performance" key

* tag 'input-for-v6.17-rc0' of git://git.kernel.org/pub/scm/linux/kernel/git/dtor/input: (38 commits)
  HID: hid-steam: Use new BTN_GRIP* buttons
  Input: add keycode for performance mode key
  Input: max77693 - convert to atomic pwm operation
  Input: st1232 - add touch-overlay handling
  dt-bindings: input: touchscreen: st1232: add touch-overlay example
  Input: touch-overlay - add touchscreen overlay handling
  dt-bindings: touchscreen: add touch-overlay property
  Input: atkbd - correctly map F13 - F24
  Input: xpad - use new BTN_GRIP* buttons
  Input: Add and document BTN_GRIP*
  Input: xpad - change buttons the D-Pad gets mapped as to BTN_DPAD_*
  Documentation: Fix capitalization of XBox -> Xbox
  Input: synaptics-rmi4 - add support for F1A
  dt-bindings: input: syna,rmi4: Document F1A function
  Input: synaptics-rmi4 - add support for Forcepads (F21)
  Input: mtk-pmic-keys - add support for MT6359 PMIC keys
  Input: remove special handling of id->driver_info when matching
  Input: evdev - switch matching to EV_SYN
  Input: samsung-keypad - use BIT() and GENMASK() where appropriate
  Input: samsung-keypad - use per-chip parameters
  ...
This commit is contained in:
Linus Torvalds 2025-08-07 07:40:01 +03:00
commit 6e64f45803
41 changed files with 1296 additions and 425 deletions

View File

@ -89,6 +89,24 @@ properties:
required:
- reg
rmi4-f1a@1a:
type: object
additionalProperties: false
$ref: input.yaml#
description:
RMI4 Function 1A is for capacitive keys.
properties:
reg:
maxItems: 1
linux,keycodes:
minItems: 1
maxItems: 4
required:
- reg
patternProperties:
"^rmi4-f1[12]@1[12]$":
type: object
@ -201,6 +219,7 @@ allOf:
examples:
- |
#include <dt-bindings/input/linux-event-codes.h>
#include <dt-bindings/interrupt-controller/irq.h>
i2c {
@ -234,6 +253,7 @@ examples:
rmi4-f1a@1a {
reg = <0x1a>;
linux,keycodes = <KEY_BACK KEY_HOME KEY_MENU>;
};
};
};

View File

@ -43,6 +43,7 @@ properties:
- focaltech,ft5452
- focaltech,ft6236
- focaltech,ft8201
- focaltech,ft8716
- focaltech,ft8719
reg:

View File

@ -1,16 +0,0 @@
* NXP LPC32xx SoC Touchscreen Controller (TSC)
Required properties:
- compatible: must be "nxp,lpc3220-tsc"
- reg: physical base address of the controller and length of memory mapped
region.
- interrupts: The TSC/ADC interrupt
Example:
tsc@40048000 {
compatible = "nxp,lpc3220-tsc";
reg = <0x40048000 0x1000>;
interrupt-parent = <&mic>;
interrupts = <39 0>;
};

View File

@ -0,0 +1,43 @@
# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
%YAML 1.2
---
$id: http://devicetree.org/schemas/input/touchscreen/nxp,lpc3220-tsc.yaml#
$schema: http://devicetree.org/meta-schemas/core.yaml#
title: NXP LPC32xx SoC Touchscreen Controller (TSC)
maintainers:
- Frank Li <Frank.Li@nxp.com>
properties:
compatible:
const: nxp,lpc3220-tsc
reg:
maxItems: 1
clocks:
maxItems: 1
interrupts:
maxItems: 1
required:
- compatible
- reg
- clocks
- interrupts
additionalProperties: false
examples:
- |
#include <dt-bindings/clock/lpc32xx-clock.h>
touchscreen@40048000 {
compatible = "nxp,lpc3220-tsc";
reg = <0x40048000 0x1000>;
interrupt-parent = <&mic>;
interrupts = <39 0>;
clocks = <&clk LPC32XX_CLK_ADC>;
};

View File

@ -37,6 +37,7 @@ unevaluatedProperties: false
examples:
- |
#include <dt-bindings/input/linux-event-codes.h>
i2c {
#address-cells = <1>;
#size-cells = <0>;
@ -46,5 +47,33 @@ examples:
reg = <0x55>;
interrupts = <2 0>;
gpios = <&gpio1 166 0>;
touch-overlay {
segment-0 {
label = "Touchscreen";
x-origin = <0>;
x-size = <240>;
y-origin = <40>;
y-size = <280>;
};
segment-1a {
label = "Camera light";
linux,code = <KEY_LIGHTS_TOGGLE>;
x-origin = <40>;
x-size = <40>;
y-origin = <0>;
y-size = <40>;
};
segment-2a {
label = "Power";
linux,code = <KEY_POWER>;
x-origin = <160>;
x-size = <40>;
y-origin = <0>;
y-size = <40>;
};
};
};
};

View File

@ -0,0 +1,75 @@
# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
%YAML 1.2
---
$id: http://devicetree.org/schemas/input/touchscreen/ti.tsc2007.yaml#
$schema: http://devicetree.org/meta-schemas/core.yaml#
title: Texas Instruments tsc2007 touchscreen controller
maintainers:
- Frank Li <Frank.Li@nxp.com>
properties:
compatible:
const: ti,tsc2007
reg:
maxItems: 1
interrupts:
maxItems: 1
ti,x-plate-ohms:
description: X-plate resistance in ohms.
gpios: true
pendown-gpio: true
ti,max-rt:
$ref: /schemas/types.yaml#/definitions/uint32
description: maximum pressure.
ti,fuzzx:
$ref: /schemas/types.yaml#/definitions/uint32
description:
specifies the absolute input fuzz x value.
If set, it will permit noise in the data up to +- the value given to the fuzz
parameter, that is used to filter noise from the event stream.
ti,fuzzy:
$ref: /schemas/types.yaml#/definitions/uint32
description: specifies the absolute input fuzz y value.
ti,fuzzz:
$ref: /schemas/types.yaml#/definitions/uint32
description: specifies the absolute input fuzz z value.
ti,poll-period:
$ref: /schemas/types.yaml#/definitions/uint32
description:
how much time to wait (in milliseconds) before reading again the
values from the tsc2007.
required:
- compatible
- reg
- ti,x-plate-ohms
additionalProperties: false
examples:
- |
i2c {
#address-cells = <1>;
#size-cells = <0>;
touch@49 {
compatible = "ti,tsc2007";
reg = <0x49>;
interrupt-parent = <&gpio4>;
interrupts = <0x0 0x8>;
gpios = <&gpio4 0 0>;
ti,x-plate-ohms = <180>;
};
};

View File

@ -87,6 +87,125 @@ properties:
touchscreen-y-plate-ohms:
description: Resistance of the Y-plate in Ohms
touch-overlay:
description: |
List of nodes defining segments (touch areas) on the touchscreen.
This object can be used to describe a series of segments to restrict
the region within touch events are reported or buttons with a specific
functionality.
This is of special interest if the touchscreen is shipped with a physical
overlay on top of it with a frame that hides some part of the original
touchscreen area. Printed buttons on that overlay are also a typical
use case.
A new touchscreen area is defined as a sub-node without a key code. If a
key code is defined in the sub-node, it will be interpreted as a button.
The x-origin and y-origin properties of a touchscreen area define the
offset of a new origin from where the touchscreen events are referenced.
This offset is applied to the events accordingly. The x-size and y-size
properties define the size of the touchscreen effective area.
The following example shows a new touchscreen area with the new origin
(0',0') for the touch events generated by the device.
Touchscreen (full area)
┌────────────────────────────────────────┐
│ ┌───────────────────────────────┐ │
│ │ │ │
│ ├ y-size │ │
│ │ │ │
│ │ touchscreen area │ │
│ │ (no key code) │ │
│ │ │ │
│ │ x-size │ │
│ ┌└──────────────┴────────────────┘ │
│(0',0') │
┌└────────────────────────────────────────┘
(0,0)
where (0',0') = (0+x-origin,0+y-origin)
Sub-nodes with key codes report the touch events on their surface as key
events instead.
The following example shows a touchscreen with a single button on it.
Touchscreen (full area)
┌───────────────────────────────────┐
│ │
│ │
│ ┌─────────┐ │
│ │button 0 │ │
│ │KEY_POWER│ │
│ └─────────┘ │
│ │
│ │
┌└───────────────────────────────────┘
(0,0)
Segments defining buttons and clipped toushcreen areas can be combined
as shown in the following example.
In that case only the events within the touchscreen area are reported
as touch events. Events within the button areas report their associated
key code. Any events outside the defined areas are ignored.
Touchscreen (full area)
┌─────────┬──────────────────────────────┐
│ │ │
│ │ ┌───────────────────────┐ │
│ button 0│ │ │ │
│KEY_POWER│ │ │ │
│ │ │ │ │
├─────────┤ │ touchscreen area │ │
│ │ │ (no key code) │ │
│ │ │ │ │
│ button 1│ │ │ │
│ KEY_INFO│ ┌└───────────────────────┘ │
│ │(0',0') │
┌└─────────┴──────────────────────────────┘
(0,0)
type: object
patternProperties:
'^segment-':
type: object
description:
Each segment is represented as a sub-node.
properties:
x-origin:
description: horizontal origin of the node area
$ref: /schemas/types.yaml#/definitions/uint32
y-origin:
description: vertical origin of the node area
$ref: /schemas/types.yaml#/definitions/uint32
x-size:
description: horizontal resolution of the node area
$ref: /schemas/types.yaml#/definitions/uint32
y-size:
description: vertical resolution of the node area
$ref: /schemas/types.yaml#/definitions/uint32
label:
description: descriptive name of the segment
$ref: /schemas/types.yaml#/definitions/string
linux,code: true
required:
- x-origin
- y-origin
- x-size
- y-size
unevaluatedProperties: false
dependencies:
touchscreen-size-x: [ touchscreen-size-y ]
touchscreen-size-y: [ touchscreen-size-x ]

View File

@ -1,39 +0,0 @@
* Texas Instruments tsc2007 touchscreen controller
Required properties:
- compatible: must be "ti,tsc2007".
- reg: I2C address of the chip.
- ti,x-plate-ohms: X-plate resistance in ohms.
Optional properties:
- gpios: the interrupt gpio the chip is connected to (through the penirq pin).
The penirq pin goes to low when the panel is touched.
(see GPIO binding[1] for more details).
- interrupts: (gpio) interrupt to which the chip is connected
(see interrupt binding[0]).
- ti,max-rt: maximum pressure.
- ti,fuzzx: specifies the absolute input fuzz x value.
If set, it will permit noise in the data up to +- the value given to the fuzz
parameter, that is used to filter noise from the event stream.
- ti,fuzzy: specifies the absolute input fuzz y value.
- ti,fuzzz: specifies the absolute input fuzz z value.
- ti,poll-period: how much time to wait (in milliseconds) before reading again the
values from the tsc2007.
[0]: Documentation/devicetree/bindings/interrupt-controller/interrupts.txt
[1]: Documentation/devicetree/bindings/gpio/gpio.txt
Example:
&i2c1 {
/* ... */
tsc2007@49 {
compatible = "ti,tsc2007";
reg = <0x49>;
interrupt-parent = <&gpio4>;
interrupts = <0x0 0x8>;
gpios = <&gpio4 0 0>;
ti,x-plate-ohms = <180>;
};
/* ... */
};

View File

@ -29,8 +29,25 @@ The driver allows configuration of the touch screen via a set of sysfs files:
For debugging purposes the driver provides a few files in the debug
filesystem (if available in the kernel). In /sys/kernel/debug/edt_ft5x06
you'll find the following files:
filesystem (if available in the kernel). They are located in:
/sys/kernel/debug/i2c/<i2c-bus>/<i2c-device>/
If you don't know the bus and device numbers, you can look them up with this
command:
$ ls -l /sys/bus/i2c/drivers/edt_ft5x06
The dereference of the symlink will contain the needed information. You will
need the last two elements of its path:
0-0038 -> ../../../../devices/platform/soc/fcfee800.i2c/i2c-0/0-0038
So in this case, the location for the debug files is:
/sys/kernel/debug/i2c/i2c-0/0-0038/
There, you'll find the following files:
num_x, num_y:
(readonly) contains the number of sensor fields in X- and

View File

@ -190,8 +190,21 @@ Gamepads report the following events:
Rumble is advertised as FF_RUMBLE.
- Grip buttons:
Many pads include buttons on the rear, usually referred to as either grip or
rear buttons, or paddles. These are often reprogrammable by the firmware to
appear as "normal" buttons, but are sometimes exposed to software too. Some
notable examples of this are the Steam Deck, which has R4, R5, L4, and L5 on
the back; the Xbox Elite pads, which have P1-P4; and the Switch 2 Pro
Controller, which has GL and GR.
For these controllers, BTN_GRIPR and BTN_GRIPR2 should be used for the top
and bottom (if present) right grip button(s), and BTN_GRIPL and BTN_GRIPL2
should be used for the top and bottom (if present) left grip button(s).
- Profile:
Some pads provide a multi-value profile selection switch. An example is the
XBox Adaptive and the XBox Elite 2 controllers. When the active profile is
switched, its newly selected value is emitted as an ABS_PROFILE event.
Some pads provide a multi-value profile selection switch. Examples include
the Xbox Adaptive and the Xbox Elite 2 controllers. When the active profile
is switched, its newly selected value is emitted as an ABS_PROFILE event.

View File

@ -449,6 +449,6 @@ the 32 bits.
xbox-dvd (RC_PROTO_XBOX_DVD)
----------------------------
This protocol is used by XBox DVD Remote, which was made for the original
XBox. There is no in-kernel decoder or encoder for this protocol. The usb
This protocol is used by Xbox DVD Remote, which was made for the original
Xbox. There is no in-kernel decoder or encoder for this protocol. The usb
device decodes the protocol. There is a BPF decoder available in v4l-utils.

View File

@ -25499,6 +25499,13 @@ L: platform-driver-x86@vger.kernel.org
S: Maintained
F: drivers/platform/x86/toshiba-wmi.c
TOUCH OVERLAY
M: Javier Carrasco <javier.carrasco@wolfvision.net>
L: linux-input@vger.kernel.org
S: Maintained
F: drivers/input/touch-overlay.c
F: include/linux/input/touch-overlay.h
TPM DEVICE DRIVER
M: Peter Huewe <peterhuewe@gmx.de>
M: Jarkko Sakkinen <jarkko@kernel.org>

View File

@ -3291,6 +3291,8 @@ static const char *keys[KEY_MAX + 1] = {
[BTN_TR2] = "BtnTR2", [BTN_SELECT] = "BtnSelect",
[BTN_START] = "BtnStart", [BTN_MODE] = "BtnMode",
[BTN_THUMBL] = "BtnThumbL", [BTN_THUMBR] = "BtnThumbR",
[BTN_GRIPL] = "BtnGripL", [BTN_GRIPR] = "BtnGripR",
[BTN_GRIPL2] = "BtnGripL2", [BTN_GRIPR2] = "BtnGripR2",
[BTN_TOOL_PEN] = "ToolPen", [BTN_TOOL_RUBBER] = "ToolRubber",
[BTN_TOOL_BRUSH] = "ToolBrush", [BTN_TOOL_PENCIL] = "ToolPencil",
[BTN_TOOL_AIRBRUSH] = "ToolAirbrush", [BTN_TOOL_FINGER] = "ToolFinger",

View File

@ -755,15 +755,12 @@ static int steam_input_register(struct steam_device *steam)
input_set_capability(input, EV_KEY, BTN_THUMBL);
input_set_capability(input, EV_KEY, BTN_THUMB);
input_set_capability(input, EV_KEY, BTN_THUMB2);
input_set_capability(input, EV_KEY, BTN_GRIPL);
input_set_capability(input, EV_KEY, BTN_GRIPR);
if (steam->quirks & STEAM_QUIRK_DECK) {
input_set_capability(input, EV_KEY, BTN_BASE);
input_set_capability(input, EV_KEY, BTN_TRIGGER_HAPPY1);
input_set_capability(input, EV_KEY, BTN_TRIGGER_HAPPY2);
input_set_capability(input, EV_KEY, BTN_TRIGGER_HAPPY3);
input_set_capability(input, EV_KEY, BTN_TRIGGER_HAPPY4);
} else {
input_set_capability(input, EV_KEY, BTN_GEAR_DOWN);
input_set_capability(input, EV_KEY, BTN_GEAR_UP);
input_set_capability(input, EV_KEY, BTN_GRIPL2);
input_set_capability(input, EV_KEY, BTN_GRIPR2);
}
input_set_abs_params(input, ABS_X, -32767, 32767, 0, 0);
@ -1419,8 +1416,8 @@ static inline s16 steam_le16(u8 *data)
* 9.4 | BTN_SELECT | menu left
* 9.5 | BTN_MODE | steam logo
* 9.6 | BTN_START | menu right
* 9.7 | BTN_GEAR_DOWN | left back lever
* 10.0 | BTN_GEAR_UP | right back lever
* 9.7 | BTN_GRIPL | left back lever
* 10.0 | BTN_GRIPR | right back lever
* 10.1 | -- | left-pad clicked
* 10.2 | BTN_THUMBR | right-pad clicked
* 10.3 | BTN_THUMB | left-pad touched (but see explanation below)
@ -1485,8 +1482,8 @@ static void steam_do_input_event(struct steam_device *steam,
input_event(input, EV_KEY, BTN_SELECT, !!(b9 & BIT(4)));
input_event(input, EV_KEY, BTN_MODE, !!(b9 & BIT(5)));
input_event(input, EV_KEY, BTN_START, !!(b9 & BIT(6)));
input_event(input, EV_KEY, BTN_GEAR_DOWN, !!(b9 & BIT(7)));
input_event(input, EV_KEY, BTN_GEAR_UP, !!(b10 & BIT(0)));
input_event(input, EV_KEY, BTN_GRIPL, !!(b9 & BIT(7)));
input_event(input, EV_KEY, BTN_GRIPR, !!(b10 & BIT(0)));
input_event(input, EV_KEY, BTN_THUMBR, !!(b10 & BIT(2)));
input_event(input, EV_KEY, BTN_THUMBL, !!(b10 & BIT(6)));
input_event(input, EV_KEY, BTN_THUMB, lpad_touched || lpad_and_joy);
@ -1547,8 +1544,8 @@ static void steam_do_input_event(struct steam_device *steam,
* 9.4 | BTN_SELECT | menu left
* 9.5 | BTN_MODE | steam logo
* 9.6 | BTN_START | menu right
* 9.7 | BTN_TRIGGER_HAPPY3 | left bottom grip button
* 10.0 | BTN_TRIGGER_HAPPY4 | right bottom grip button
* 9.7 | BTN_GRIPL2 | left bottom grip button
* 10.0 | BTN_GRIPR2 | right bottom grip button
* 10.1 | BTN_THUMB | left pad pressed
* 10.2 | BTN_THUMB2 | right pad pressed
* 10.3 | -- | left pad touched
@ -1573,8 +1570,8 @@ static void steam_do_input_event(struct steam_device *steam,
* 12.6 | -- | unknown
* 12.7 | -- | unknown
* 13.0 | -- | unknown
* 13.1 | BTN_TRIGGER_HAPPY1 | left top grip button
* 13.2 | BTN_TRIGGER_HAPPY2 | right top grip button
* 13.1 | BTN_GRIPL | left top grip button
* 13.2 | BTN_GRIPR | right top grip button
* 13.3 | -- | unknown
* 13.4 | -- | unknown
* 13.5 | -- | unknown
@ -1659,8 +1656,8 @@ static void steam_do_deck_input_event(struct steam_device *steam,
input_event(input, EV_KEY, BTN_SELECT, !!(b9 & BIT(4)));
input_event(input, EV_KEY, BTN_MODE, !!(b9 & BIT(5)));
input_event(input, EV_KEY, BTN_START, !!(b9 & BIT(6)));
input_event(input, EV_KEY, BTN_TRIGGER_HAPPY3, !!(b9 & BIT(7)));
input_event(input, EV_KEY, BTN_TRIGGER_HAPPY4, !!(b10 & BIT(0)));
input_event(input, EV_KEY, BTN_GRIPL2, !!(b9 & BIT(7)));
input_event(input, EV_KEY, BTN_GRIPR2, !!(b10 & BIT(0)));
input_event(input, EV_KEY, BTN_THUMBL, !!(b10 & BIT(6)));
input_event(input, EV_KEY, BTN_THUMBR, !!(b11 & BIT(2)));
input_event(input, EV_KEY, BTN_DPAD_UP, !!(b9 & BIT(0)));
@ -1669,8 +1666,8 @@ static void steam_do_deck_input_event(struct steam_device *steam,
input_event(input, EV_KEY, BTN_DPAD_DOWN, !!(b9 & BIT(3)));
input_event(input, EV_KEY, BTN_THUMB, !!(b10 & BIT(1)));
input_event(input, EV_KEY, BTN_THUMB2, !!(b10 & BIT(2)));
input_event(input, EV_KEY, BTN_TRIGGER_HAPPY1, !!(b13 & BIT(1)));
input_event(input, EV_KEY, BTN_TRIGGER_HAPPY2, !!(b13 & BIT(2)));
input_event(input, EV_KEY, BTN_GRIPL, !!(b13 & BIT(1)));
input_event(input, EV_KEY, BTN_GRIPR, !!(b13 & BIT(2)));
input_event(input, EV_KEY, BTN_BASE, !!(b14 & BIT(2)));
input_sync(input);

View File

@ -7,7 +7,7 @@
obj-$(CONFIG_INPUT) += input-core.o
input-core-y := input.o input-compat.o input-mt.o input-poller.o ff-core.o
input-core-y += touchscreen.o
input-core-y += touchscreen.o touch-overlay.o
obj-$(CONFIG_INPUT_FF_MEMLESS) += ff-memless.o
obj-$(CONFIG_INPUT_SPARSEKMAP) += sparse-keymap.o

View File

@ -1408,8 +1408,12 @@ static void evdev_disconnect(struct input_handle *handle)
}
static const struct input_device_id evdev_ids[] = {
{ .driver_info = 1 }, /* Matches all devices */
{ }, /* Terminating zero entry */
{
/* Matches all devices */
.flags = INPUT_DEVICE_ID_MATCH_EVBIT,
.evbit = { BIT_MASK(EV_SYN) },
},
{ } /* Terminating zero entry */
};
MODULE_DEVICE_TABLE(input, evdev_ids);

View File

@ -971,7 +971,7 @@ static const struct input_device_id *input_match_device(struct input_handler *ha
{
const struct input_device_id *id;
for (id = handler->id_table; id->flags || id->driver_info; id++) {
for (id = handler->id_table; id->flags; id++) {
if (input_match_device_id(dev, id) &&
(!handler->match || handler->match(handler, dev))) {
return id;

View File

@ -442,8 +442,8 @@ static const signed short xpad_btn[] = {
/* used when dpad is mapped to buttons */
static const signed short xpad_btn_pad[] = {
BTN_TRIGGER_HAPPY1, BTN_TRIGGER_HAPPY2, /* d-pad left, right */
BTN_TRIGGER_HAPPY3, BTN_TRIGGER_HAPPY4, /* d-pad up, down */
BTN_DPAD_LEFT, BTN_DPAD_RIGHT, /* d-pad left, right */
BTN_DPAD_UP, BTN_DPAD_DOWN, /* d-pad up, down */
-1 /* terminating entry */
};
@ -479,8 +479,8 @@ static const signed short xpad_abs_triggers[] = {
/* used when the controller has extra paddle buttons */
static const signed short xpad_btn_paddles[] = {
BTN_TRIGGER_HAPPY5, BTN_TRIGGER_HAPPY6, /* paddle upper right, lower right */
BTN_TRIGGER_HAPPY7, BTN_TRIGGER_HAPPY8, /* paddle upper left, lower left */
BTN_GRIPR, BTN_GRIPR2, /* paddle upper right, lower right */
BTN_GRIPL, BTN_GRIPL2, /* paddle upper left, lower left */
-1 /* terminating entry */
};
@ -840,10 +840,10 @@ static void xpad_process_packet(struct usb_xpad *xpad, u16 cmd, unsigned char *d
/* digital pad */
if (xpad->mapping & MAP_DPAD_TO_BUTTONS) {
/* dpad as buttons (left, right, up, down) */
input_report_key(dev, BTN_TRIGGER_HAPPY1, data[2] & BIT(2));
input_report_key(dev, BTN_TRIGGER_HAPPY2, data[2] & BIT(3));
input_report_key(dev, BTN_TRIGGER_HAPPY3, data[2] & BIT(0));
input_report_key(dev, BTN_TRIGGER_HAPPY4, data[2] & BIT(1));
input_report_key(dev, BTN_DPAD_LEFT, data[2] & BIT(2));
input_report_key(dev, BTN_DPAD_RIGHT, data[2] & BIT(3));
input_report_key(dev, BTN_DPAD_UP, data[2] & BIT(0));
input_report_key(dev, BTN_DPAD_DOWN, data[2] & BIT(1));
} else {
input_report_abs(dev, ABS_HAT0X,
!!(data[2] & 0x08) - !!(data[2] & 0x04));
@ -891,10 +891,10 @@ static void xpad360_process_packet(struct usb_xpad *xpad, struct input_dev *dev,
/* digital pad */
if (xpad->mapping & MAP_DPAD_TO_BUTTONS) {
/* dpad as buttons (left, right, up, down) */
input_report_key(dev, BTN_TRIGGER_HAPPY1, data[2] & BIT(2));
input_report_key(dev, BTN_TRIGGER_HAPPY2, data[2] & BIT(3));
input_report_key(dev, BTN_TRIGGER_HAPPY3, data[2] & BIT(0));
input_report_key(dev, BTN_TRIGGER_HAPPY4, data[2] & BIT(1));
input_report_key(dev, BTN_DPAD_LEFT, data[2] & BIT(2));
input_report_key(dev, BTN_DPAD_RIGHT, data[2] & BIT(3));
input_report_key(dev, BTN_DPAD_UP, data[2] & BIT(0));
input_report_key(dev, BTN_DPAD_DOWN, data[2] & BIT(1));
}
/*
@ -1075,10 +1075,10 @@ static void xpadone_process_packet(struct usb_xpad *xpad, u16 cmd, unsigned char
data[18] = 0;
/* Elite Series 2 split packet paddle bits */
input_report_key(dev, BTN_TRIGGER_HAPPY5, data[18] & BIT(0));
input_report_key(dev, BTN_TRIGGER_HAPPY6, data[18] & BIT(1));
input_report_key(dev, BTN_TRIGGER_HAPPY7, data[18] & BIT(2));
input_report_key(dev, BTN_TRIGGER_HAPPY8, data[18] & BIT(3));
input_report_key(dev, BTN_GRIPR, data[18] & BIT(0));
input_report_key(dev, BTN_GRIPR2, data[18] & BIT(1));
input_report_key(dev, BTN_GRIPL, data[18] & BIT(2));
input_report_key(dev, BTN_GRIPL2, data[18] & BIT(3));
do_sync = true;
}
@ -1113,10 +1113,10 @@ static void xpadone_process_packet(struct usb_xpad *xpad, u16 cmd, unsigned char
/* digital pad */
if (xpad->mapping & MAP_DPAD_TO_BUTTONS) {
/* dpad as buttons (left, right, up, down) */
input_report_key(dev, BTN_TRIGGER_HAPPY1, data[5] & BIT(2));
input_report_key(dev, BTN_TRIGGER_HAPPY2, data[5] & BIT(3));
input_report_key(dev, BTN_TRIGGER_HAPPY3, data[5] & BIT(0));
input_report_key(dev, BTN_TRIGGER_HAPPY4, data[5] & BIT(1));
input_report_key(dev, BTN_DPAD_LEFT, data[5] & BIT(2));
input_report_key(dev, BTN_DPAD_RIGHT, data[5] & BIT(3));
input_report_key(dev, BTN_DPAD_UP, data[5] & BIT(0));
input_report_key(dev, BTN_DPAD_DOWN, data[5] & BIT(1));
} else {
input_report_abs(dev, ABS_HAT0X,
!!(data[5] & 0x08) - !!(data[5] & 0x04));
@ -1175,10 +1175,10 @@ static void xpadone_process_packet(struct usb_xpad *xpad, u16 cmd, unsigned char
data[32] = 0;
/* OG Elite Series Controller paddle bits */
input_report_key(dev, BTN_TRIGGER_HAPPY5, data[32] & BIT(1));
input_report_key(dev, BTN_TRIGGER_HAPPY6, data[32] & BIT(3));
input_report_key(dev, BTN_TRIGGER_HAPPY7, data[32] & BIT(0));
input_report_key(dev, BTN_TRIGGER_HAPPY8, data[32] & BIT(2));
input_report_key(dev, BTN_GRIPR, data[32] & BIT(1));
input_report_key(dev, BTN_GRIPR2, data[32] & BIT(3));
input_report_key(dev, BTN_GRIPL, data[32] & BIT(0));
input_report_key(dev, BTN_GRIPL2, data[32] & BIT(2));
} else if (xpad->packet_type == PKT_XBE2_FW_OLD) {
/* Mute paddles if controller has a custom mapping applied.
* Checked by comparing the current mapping
@ -1188,10 +1188,10 @@ static void xpadone_process_packet(struct usb_xpad *xpad, u16 cmd, unsigned char
data[18] = 0;
/* Elite Series 2 4.x firmware paddle bits */
input_report_key(dev, BTN_TRIGGER_HAPPY5, data[18] & BIT(0));
input_report_key(dev, BTN_TRIGGER_HAPPY6, data[18] & BIT(1));
input_report_key(dev, BTN_TRIGGER_HAPPY7, data[18] & BIT(2));
input_report_key(dev, BTN_TRIGGER_HAPPY8, data[18] & BIT(3));
input_report_key(dev, BTN_GRIPR, data[18] & BIT(0));
input_report_key(dev, BTN_GRIPR2, data[18] & BIT(1));
input_report_key(dev, BTN_GRIPL, data[18] & BIT(2));
input_report_key(dev, BTN_GRIPL2, data[18] & BIT(3));
} else if (xpad->packet_type == PKT_XBE2_FW_5_EARLY) {
/* Mute paddles if controller has a custom mapping applied.
* Checked by comparing the current mapping
@ -1203,10 +1203,10 @@ static void xpadone_process_packet(struct usb_xpad *xpad, u16 cmd, unsigned char
/* Elite Series 2 5.x firmware paddle bits
* (before the packet was split)
*/
input_report_key(dev, BTN_TRIGGER_HAPPY5, data[22] & BIT(0));
input_report_key(dev, BTN_TRIGGER_HAPPY6, data[22] & BIT(1));
input_report_key(dev, BTN_TRIGGER_HAPPY7, data[22] & BIT(2));
input_report_key(dev, BTN_TRIGGER_HAPPY8, data[22] & BIT(3));
input_report_key(dev, BTN_GRIPR, data[22] & BIT(0));
input_report_key(dev, BTN_GRIPR2, data[22] & BIT(1));
input_report_key(dev, BTN_GRIPL, data[22] & BIT(2));
input_report_key(dev, BTN_GRIPL2, data[22] & BIT(3));
}
}

View File

@ -232,8 +232,8 @@ static int adp5588_gpio_get_value(struct gpio_chip *chip, unsigned int off)
return !!(val & bit);
}
static void adp5588_gpio_set_value(struct gpio_chip *chip,
unsigned int off, int val)
static int adp5588_gpio_set_value(struct gpio_chip *chip, unsigned int off,
int val)
{
struct adp5588_kpad *kpad = gpiochip_get_data(chip);
unsigned int bank = ADP5588_BANK(kpad->gpiomap[off]);
@ -246,7 +246,8 @@ static void adp5588_gpio_set_value(struct gpio_chip *chip,
else
kpad->dat_out[bank] &= ~bit;
adp5588_write(kpad->client, GPIO_DAT_OUT1 + bank, kpad->dat_out[bank]);
return adp5588_write(kpad->client, GPIO_DAT_OUT1 + bank,
kpad->dat_out[bank]);
}
static int adp5588_gpio_set_config(struct gpio_chip *chip, unsigned int off,
@ -424,7 +425,7 @@ static int adp5588_gpio_add(struct adp5588_kpad *kpad)
kpad->gc.direction_input = adp5588_gpio_direction_input;
kpad->gc.direction_output = adp5588_gpio_direction_output;
kpad->gc.get = adp5588_gpio_get_value;
kpad->gc.set = adp5588_gpio_set_value;
kpad->gc.set_rv = adp5588_gpio_set_value;
kpad->gc.set_config = adp5588_gpio_set_config;
kpad->gc.can_sleep = 1;

View File

@ -84,12 +84,12 @@ static const unsigned short atkbd_set2_keycode[ATKBD_KEYMAP_SIZE] = {
#include "hpps2atkbd.h" /* include the keyboard scancodes */
#else
0, 67, 65, 63, 61, 59, 60, 88, 0, 68, 66, 64, 62, 15, 41,117,
0, 56, 42, 93, 29, 16, 2, 0, 0, 0, 44, 31, 30, 17, 3, 0,
0, 46, 45, 32, 18, 5, 4, 95, 0, 57, 47, 33, 20, 19, 6,183,
0, 49, 48, 35, 34, 21, 7,184, 0, 0, 50, 36, 22, 8, 9,185,
0, 51, 37, 23, 24, 11, 10, 0, 0, 52, 53, 38, 39, 25, 12, 0,
0, 89, 40, 0, 26, 13, 0,193, 58, 54, 28, 27, 0, 43, 0, 85,
0, 67, 65, 63, 61, 59, 60, 88,183, 68, 66, 64, 62, 15, 41,117,
184, 56, 42, 93, 29, 16, 2, 0,185, 0, 44, 31, 30, 17, 3, 0,
186, 46, 45, 32, 18, 5, 4, 95,187, 57, 47, 33, 20, 19, 6,183,
188, 49, 48, 35, 34, 21, 7,184,189, 0, 50, 36, 22, 8, 9,185,
190, 51, 37, 23, 24, 11, 10, 0,191, 52, 53, 38, 39, 25, 12, 0,
192, 89, 40, 0, 26, 13, 0,193, 58, 54, 28, 27, 0, 43, 0,194,
0, 86, 91, 90, 92, 0, 14, 94, 0, 79,124, 75, 71,121, 0, 0,
82, 83, 80, 76, 77, 72, 1, 69, 87, 78, 81, 74, 55, 73, 70, 99,

View File

@ -12,6 +12,7 @@
#include <linux/mfd/mt6331/registers.h>
#include <linux/mfd/mt6357/registers.h>
#include <linux/mfd/mt6358/registers.h>
#include <linux/mfd/mt6359/registers.h>
#include <linux/mfd/mt6397/core.h>
#include <linux/mfd/mt6397/registers.h>
#include <linux/module.h>
@ -117,6 +118,19 @@ static const struct mtk_pmic_regs mt6358_regs = {
.rst_lprst_mask = MTK_PMIC_RST_DU_MASK,
};
static const struct mtk_pmic_regs mt6359_regs = {
.keys_regs[MTK_PMIC_PWRKEY_INDEX] =
MTK_PMIC_KEYS_REGS(MT6359_TOPSTATUS,
0x2, MT6359_PSC_TOP_INT_CON0, 0x5,
MTK_PMIC_PWRKEY_RST),
.keys_regs[MTK_PMIC_HOMEKEY_INDEX] =
MTK_PMIC_KEYS_REGS(MT6359_TOPSTATUS,
0x8, MT6359_PSC_TOP_INT_CON0, 0xa,
MTK_PMIC_HOMEKEY_RST),
.pmic_rst_reg = MT6359_TOP_RST_MISC,
.rst_lprst_mask = MTK_PMIC_RST_DU_MASK,
};
struct mtk_pmic_keys_info {
struct mtk_pmic_keys *keys;
const struct mtk_pmic_keys_regs *regs;
@ -296,6 +310,9 @@ static const struct of_device_id of_mtk_pmic_keys_match_tbl[] = {
}, {
.compatible = "mediatek,mt6358-keys",
.data = &mt6358_regs,
}, {
.compatible = "mediatek,mt6359-keys",
.data = &mt6359_regs,
}, {
/* sentinel */
}

View File

@ -7,6 +7,7 @@
* Author: Donghwa Lee <dh09.lee@samsung.com>
*/
#include <linux/bits.h>
#include <linux/clk.h>
#include <linux/delay.h>
#include <linux/err.h>
@ -29,11 +30,11 @@
#define SAMSUNG_KEYIFFC 0x10
/* SAMSUNG_KEYIFCON */
#define SAMSUNG_KEYIFCON_INT_F_EN (1 << 0)
#define SAMSUNG_KEYIFCON_INT_R_EN (1 << 1)
#define SAMSUNG_KEYIFCON_DF_EN (1 << 2)
#define SAMSUNG_KEYIFCON_FC_EN (1 << 3)
#define SAMSUNG_KEYIFCON_WAKEUPEN (1 << 4)
#define SAMSUNG_KEYIFCON_INT_F_EN BIT(0)
#define SAMSUNG_KEYIFCON_INT_R_EN BIT(1)
#define SAMSUNG_KEYIFCON_DF_EN BIT(2)
#define SAMSUNG_KEYIFCON_FC_EN BIT(3)
#define SAMSUNG_KEYIFCON_WAKEUPEN BIT(4)
/* SAMSUNG_KEYIFSTSCLR */
#define SAMSUNG_KEYIFSTSCLR_P_INT_MASK (0xff << 0)
@ -44,8 +45,7 @@
#define S5PV210_KEYIFSTSCLR_R_INT_OFFSET 16
/* SAMSUNG_KEYIFCOL */
#define SAMSUNG_KEYIFCOL_MASK (0xff << 0)
#define S5PV210_KEYIFCOLEN_MASK (0xff << 8)
#define SAMSUNG_KEYIFCOL_MASK 0xff
/* SAMSUNG_KEYIFROW */
#define SAMSUNG_KEYIFROW_MASK (0xff << 0)
@ -54,12 +54,12 @@
/* SAMSUNG_KEYIFFC */
#define SAMSUNG_KEYIFFC_MASK (0x3ff << 0)
enum samsung_keypad_type {
KEYPAD_TYPE_SAMSUNG,
KEYPAD_TYPE_S5PV210,
struct samsung_chip_info {
unsigned int column_shift;
};
struct samsung_keypad {
const struct samsung_chip_info *chip;
struct input_dev *input_dev;
struct platform_device *pdev;
struct clk *clk;
@ -68,7 +68,6 @@ struct samsung_keypad {
bool stopped;
bool wake_enabled;
int irq;
enum samsung_keypad_type type;
unsigned int row_shift;
unsigned int rows;
unsigned int cols;
@ -83,19 +82,14 @@ static void samsung_keypad_scan(struct samsung_keypad *keypad,
unsigned int val;
for (col = 0; col < keypad->cols; col++) {
if (keypad->type == KEYPAD_TYPE_S5PV210) {
val = S5PV210_KEYIFCOLEN_MASK;
val &= ~(1 << col) << 8;
} else {
val = SAMSUNG_KEYIFCOL_MASK;
val &= ~(1 << col);
}
val = SAMSUNG_KEYIFCOL_MASK & ~BIT(col);
val <<= keypad->chip->column_shift;
writel(val, keypad->base + SAMSUNG_KEYIFCOL);
mdelay(1);
val = readl(keypad->base + SAMSUNG_KEYIFROW);
row_state[col] = ~val & ((1 << keypad->rows) - 1);
row_state[col] = ~val & GENMASK(keypad->rows - 1, 0);
}
/* KEYIFCOL reg clear */
@ -119,10 +113,10 @@ static bool samsung_keypad_report(struct samsung_keypad *keypad,
continue;
for (row = 0; row < keypad->rows; row++) {
if (!(changed & (1 << row)))
if (!(changed & BIT(row)))
continue;
pressed = row_state[col] & (1 << row);
pressed = row_state[col] & BIT(row);
dev_dbg(&keypad->input_dev->dev,
"key %s, row: %d, col: %d\n",
@ -314,11 +308,11 @@ static int samsung_keypad_probe(struct platform_device *pdev)
{
const struct samsung_keypad_platdata *pdata;
const struct matrix_keymap_data *keymap_data;
const struct platform_device_id *id;
struct samsung_keypad *keypad;
struct resource *res;
struct input_dev *input_dev;
unsigned int row_shift;
unsigned int keymap_size;
int error;
pdata = dev_get_platdata(&pdev->dev);
@ -345,12 +339,16 @@ static int samsung_keypad_probe(struct platform_device *pdev)
pdata->cfg_gpio(pdata->rows, pdata->cols);
row_shift = get_count_order(pdata->cols);
keymap_size = (pdata->rows << row_shift) * sizeof(keypad->keycodes[0]);
keypad = devm_kzalloc(&pdev->dev, sizeof(*keypad) + keymap_size,
keypad = devm_kzalloc(&pdev->dev,
struct_size(keypad, keycodes,
pdata->rows << row_shift),
GFP_KERNEL);
if (!keypad)
return -ENOMEM;
input_dev = devm_input_allocate_device(&pdev->dev);
if (!keypad || !input_dev)
if (!input_dev)
return -ENOMEM;
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
@ -361,18 +359,12 @@ static int samsung_keypad_probe(struct platform_device *pdev)
if (!keypad->base)
return -EBUSY;
keypad->clk = devm_clk_get(&pdev->dev, "keypad");
keypad->clk = devm_clk_get_prepared(&pdev->dev, "keypad");
if (IS_ERR(keypad->clk)) {
dev_err(&pdev->dev, "failed to get keypad clk\n");
return PTR_ERR(keypad->clk);
}
error = clk_prepare(keypad->clk);
if (error) {
dev_err(&pdev->dev, "keypad clock prepare failed\n");
return error;
}
keypad->input_dev = input_dev;
keypad->pdev = pdev;
keypad->row_shift = row_shift;
@ -381,15 +373,20 @@ static int samsung_keypad_probe(struct platform_device *pdev)
keypad->stopped = true;
init_waitqueue_head(&keypad->wait);
if (pdev->dev.of_node)
keypad->type = of_device_is_compatible(pdev->dev.of_node,
"samsung,s5pv210-keypad");
else
keypad->type = platform_get_device_id(pdev)->driver_data;
keypad->chip = device_get_match_data(&pdev->dev);
if (!keypad->chip) {
id = platform_get_device_id(pdev);
if (id)
keypad->chip = (const void *)id->driver_data;
}
if (!keypad->chip) {
dev_err(&pdev->dev, "Unable to determine chip type");
return -EINVAL;
}
input_dev->name = pdev->name;
input_dev->id.bustype = BUS_HOST;
input_dev->dev.parent = &pdev->dev;
input_dev->open = samsung_keypad_open;
input_dev->close = samsung_keypad_close;
@ -399,7 +396,7 @@ static int samsung_keypad_probe(struct platform_device *pdev)
keypad->keycodes, input_dev);
if (error) {
dev_err(&pdev->dev, "failed to build keymap\n");
goto err_unprepare_clk;
return error;
}
input_set_capability(input_dev, EV_MSC, MSC_SCAN);
@ -411,7 +408,7 @@ static int samsung_keypad_probe(struct platform_device *pdev)
keypad->irq = platform_get_irq(pdev, 0);
if (keypad->irq < 0) {
error = keypad->irq;
goto err_unprepare_clk;
return error;
}
error = devm_request_threaded_irq(&pdev->dev, keypad->irq, NULL,
@ -419,16 +416,19 @@ static int samsung_keypad_probe(struct platform_device *pdev)
dev_name(&pdev->dev), keypad);
if (error) {
dev_err(&pdev->dev, "failed to register keypad interrupt\n");
goto err_unprepare_clk;
return error;
}
device_init_wakeup(&pdev->dev, pdata->wakeup);
platform_set_drvdata(pdev, keypad);
pm_runtime_enable(&pdev->dev);
error = devm_pm_runtime_enable(&pdev->dev);
if (error)
return error;
error = input_register_device(keypad->input_dev);
if (error)
goto err_disable_runtime_pm;
return error;
if (pdev->dev.of_node) {
devm_kfree(&pdev->dev, (void *)pdata->keymap_data->keymap);
@ -436,23 +436,6 @@ static int samsung_keypad_probe(struct platform_device *pdev)
devm_kfree(&pdev->dev, (void *)pdata);
}
return 0;
err_disable_runtime_pm:
pm_runtime_disable(&pdev->dev);
err_unprepare_clk:
clk_unprepare(keypad->clk);
return error;
}
static void samsung_keypad_remove(struct platform_device *pdev)
{
struct samsung_keypad *keypad = platform_get_drvdata(pdev);
pm_runtime_disable(&pdev->dev);
input_unregister_device(keypad->input_dev);
clk_unprepare(keypad->clk);
}
static int samsung_keypad_runtime_suspend(struct device *dev)
@ -528,15 +511,13 @@ static int samsung_keypad_suspend(struct device *dev)
struct samsung_keypad *keypad = platform_get_drvdata(pdev);
struct input_dev *input_dev = keypad->input_dev;
mutex_lock(&input_dev->mutex);
guard(mutex)(&input_dev->mutex);
if (input_device_enabled(input_dev))
samsung_keypad_stop(keypad);
samsung_keypad_toggle_wakeup(keypad, true);
mutex_unlock(&input_dev->mutex);
return 0;
}
@ -546,15 +527,13 @@ static int samsung_keypad_resume(struct device *dev)
struct samsung_keypad *keypad = platform_get_drvdata(pdev);
struct input_dev *input_dev = keypad->input_dev;
mutex_lock(&input_dev->mutex);
guard(mutex)(&input_dev->mutex);
samsung_keypad_toggle_wakeup(keypad, false);
if (input_device_enabled(input_dev))
samsung_keypad_start(keypad);
mutex_unlock(&input_dev->mutex);
return 0;
}
@ -564,11 +543,24 @@ static const struct dev_pm_ops samsung_keypad_pm_ops = {
samsung_keypad_runtime_resume, NULL)
};
static const struct samsung_chip_info samsung_s3c6410_chip_info = {
.column_shift = 0,
};
static const struct samsung_chip_info samsung_s5pv210_chip_info = {
.column_shift = 8,
};
#ifdef CONFIG_OF
static const struct of_device_id samsung_keypad_dt_match[] = {
{ .compatible = "samsung,s3c6410-keypad" },
{ .compatible = "samsung,s5pv210-keypad" },
{},
{
.compatible = "samsung,s3c6410-keypad",
.data = &samsung_s3c6410_chip_info,
}, {
.compatible = "samsung,s5pv210-keypad",
.data = &samsung_s5pv210_chip_info,
},
{ }
};
MODULE_DEVICE_TABLE(of, samsung_keypad_dt_match);
#endif
@ -576,18 +568,17 @@ MODULE_DEVICE_TABLE(of, samsung_keypad_dt_match);
static const struct platform_device_id samsung_keypad_driver_ids[] = {
{
.name = "samsung-keypad",
.driver_data = KEYPAD_TYPE_SAMSUNG,
.driver_data = (kernel_ulong_t)&samsung_s3c6410_chip_info,
}, {
.name = "s5pv210-keypad",
.driver_data = KEYPAD_TYPE_S5PV210,
.driver_data = (kernel_ulong_t)&samsung_s5pv210_chip_info,
},
{ },
{ }
};
MODULE_DEVICE_TABLE(platform, samsung_keypad_driver_ids);
static struct platform_driver samsung_keypad_driver = {
.probe = samsung_keypad_probe,
.remove = samsung_keypad_remove,
.driver = {
.name = "samsung-keypad",
.of_match_table = of_match_ptr(samsung_keypad_dt_match),

View File

@ -584,13 +584,6 @@ config INPUT_PALMAS_PWRBUTTON
To compile this driver as a module, choose M here. The module will
be called palmas_pwrbutton.
config INPUT_PCF50633_PMU
tristate "PCF50633 PMU events"
depends on MFD_PCF50633
help
Say Y to include support for delivering PMU events via input
layer on NXP PCF50633.
config INPUT_PCF8574
tristate "PCF8574 Keypad input device"
depends on I2C

View File

@ -59,7 +59,6 @@ obj-$(CONFIG_INPUT_MC13783_PWRBUTTON) += mc13783-pwrbutton.o
obj-$(CONFIG_INPUT_MMA8450) += mma8450.o
obj-$(CONFIG_INPUT_PALMAS_PWRBUTTON) += palmas-pwrbutton.o
obj-$(CONFIG_INPUT_PCAP) += pcap_keys.o
obj-$(CONFIG_INPUT_PCF50633_PMU) += pcf50633-input.o
obj-$(CONFIG_INPUT_PCF8574) += pcf8574_keypad.o
obj-$(CONFIG_INPUT_PCSPKR) += pcspkr.o
obj-$(CONFIG_INPUT_PM8941_PWRKEY) += pm8941-pwrkey.o

View File

@ -482,7 +482,6 @@ static int cs40l50_erase(struct input_dev *dev, int effect_id)
static void cs40l50_remove_wq(void *data)
{
flush_workqueue(data);
destroy_workqueue(data);
}

View File

@ -68,15 +68,16 @@ struct max77693_haptic {
static int max77693_haptic_set_duty_cycle(struct max77693_haptic *haptic)
{
struct pwm_args pargs;
int delta;
struct pwm_state state;
int error;
pwm_get_args(haptic->pwm_dev, &pargs);
delta = (pargs.period + haptic->pwm_duty) / 2;
error = pwm_config(haptic->pwm_dev, delta, pargs.period);
pwm_init_state(haptic->pwm_dev, &state);
state.duty_cycle = (state.period + haptic->pwm_duty) / 2;
error = pwm_apply_might_sleep(haptic->pwm_dev, &state);
if (error) {
dev_err(haptic->dev, "failed to configure pwm: %d\n", error);
dev_err(haptic->dev,
"failed to set pwm duty cycle: %d\n", error);
return error;
}
@ -166,12 +167,17 @@ static int max77693_haptic_lowsys(struct max77693_haptic *haptic, bool enable)
static void max77693_haptic_enable(struct max77693_haptic *haptic)
{
struct pwm_state state;
int error;
if (haptic->enabled)
return;
error = pwm_enable(haptic->pwm_dev);
pwm_init_state(haptic->pwm_dev, &state);
state.duty_cycle = (state.period + haptic->pwm_duty) / 2;
state.enabled = true;
error = pwm_apply_might_sleep(haptic->pwm_dev, &state);
if (error) {
dev_err(haptic->dev,
"failed to enable haptic pwm device: %d\n", error);
@ -224,18 +230,13 @@ static void max77693_haptic_play_work(struct work_struct *work)
{
struct max77693_haptic *haptic =
container_of(work, struct max77693_haptic, work);
int error;
error = max77693_haptic_set_duty_cycle(haptic);
if (error) {
dev_err(haptic->dev, "failed to set duty cycle: %d\n", error);
return;
}
if (haptic->magnitude)
max77693_haptic_enable(haptic);
else
if (!haptic->magnitude)
max77693_haptic_disable(haptic);
else if (haptic->enabled)
max77693_haptic_set_duty_cycle(haptic);
else
max77693_haptic_enable(haptic);
}
static int max77693_haptic_play_effect(struct input_dev *dev, void *data,
@ -340,12 +341,6 @@ static int max77693_haptic_probe(struct platform_device *pdev)
return PTR_ERR(haptic->pwm_dev);
}
/*
* FIXME: pwm_apply_args() should be removed when switching to the
* atomic PWM API.
*/
pwm_apply_args(haptic->pwm_dev);
haptic->motor_reg = devm_regulator_get(&pdev->dev, "haptic");
if (IS_ERR(haptic->motor_reg)) {
dev_err(&pdev->dev, "failed to get regulator\n");

View File

@ -53,40 +53,30 @@ struct max8997_haptic {
unsigned int pattern_signal_period;
};
static int max8997_haptic_set_duty_cycle(struct max8997_haptic *chip)
static void max8997_haptic_set_internal_duty_cycle(struct max8997_haptic *chip)
{
int ret = 0;
u8 duty_index = DIV_ROUND_UP(chip->level * 64, 100);
if (chip->mode == MAX8997_EXTERNAL_MODE) {
unsigned int duty = chip->pwm_period * chip->level / 100;
ret = pwm_config(chip->pwm, duty, chip->pwm_period);
} else {
u8 duty_index = 0;
duty_index = DIV_ROUND_UP(chip->level * 64, 100);
switch (chip->internal_mode_pattern) {
case 0:
max8997_write_reg(chip->client,
MAX8997_HAPTIC_REG_SIGPWMDC1, duty_index);
break;
case 1:
max8997_write_reg(chip->client,
MAX8997_HAPTIC_REG_SIGPWMDC2, duty_index);
break;
case 2:
max8997_write_reg(chip->client,
MAX8997_HAPTIC_REG_SIGPWMDC3, duty_index);
break;
case 3:
max8997_write_reg(chip->client,
MAX8997_HAPTIC_REG_SIGPWMDC4, duty_index);
break;
default:
break;
}
switch (chip->internal_mode_pattern) {
case 0:
max8997_write_reg(chip->client,
MAX8997_HAPTIC_REG_SIGPWMDC1, duty_index);
break;
case 1:
max8997_write_reg(chip->client,
MAX8997_HAPTIC_REG_SIGPWMDC2, duty_index);
break;
case 2:
max8997_write_reg(chip->client,
MAX8997_HAPTIC_REG_SIGPWMDC3, duty_index);
break;
case 3:
max8997_write_reg(chip->client,
MAX8997_HAPTIC_REG_SIGPWMDC4, duty_index);
break;
default:
break;
}
return ret;
}
static void max8997_haptic_configure(struct max8997_haptic *chip)
@ -155,11 +145,8 @@ static void max8997_haptic_enable(struct max8997_haptic *chip)
guard(mutex)(&chip->mutex);
error = max8997_haptic_set_duty_cycle(chip);
if (error) {
dev_err(chip->dev, "set_pwm_cycle failed, error: %d\n", error);
return;
}
if (chip->mode != MAX8997_EXTERNAL_MODE)
max8997_haptic_set_internal_duty_cycle(chip);
if (!chip->enabled) {
error = regulator_enable(chip->regulator);
@ -168,16 +155,32 @@ static void max8997_haptic_enable(struct max8997_haptic *chip)
return;
}
max8997_haptic_configure(chip);
if (chip->mode == MAX8997_EXTERNAL_MODE) {
error = pwm_enable(chip->pwm);
if (error) {
dev_err(chip->dev, "Failed to enable PWM\n");
regulator_disable(chip->regulator);
return;
}
}
chip->enabled = true;
}
/*
* It would be more straight forward to configure the external PWM
* earlier i.e. when the internal duty_cycle is setup in internal mode.
* But historically this is done only after the regulator was enabled
* and max8997_haptic_configure() set the enable bit in
* MAX8997_HAPTIC_REG_CONF2. So better keep it this way.
*/
if (chip->mode == MAX8997_EXTERNAL_MODE) {
struct pwm_state state;
pwm_init_state(chip->pwm, &state);
state.period = chip->pwm_period;
state.duty_cycle = chip->pwm_period * chip->level / 100;
state.enabled = true;
error = pwm_apply_might_sleep(chip->pwm, &state);
if (error) {
dev_err(chip->dev, "Failed to enable PWM\n");
regulator_disable(chip->regulator);
return;
}
}
chip->enabled = true;
}
static void max8997_haptic_disable(struct max8997_haptic *chip)
@ -282,11 +285,6 @@ static int max8997_haptic_probe(struct platform_device *pdev)
goto err_free_mem;
}
/*
* FIXME: pwm_apply_args() should be removed when switching to
* the atomic PWM API.
*/
pwm_apply_args(chip->pwm);
break;
default:

View File

@ -1,113 +0,0 @@
// SPDX-License-Identifier: GPL-2.0-or-later
/* NXP PCF50633 Input Driver
*
* (C) 2006-2008 by Openmoko, Inc.
* Author: Balaji Rao <balajirrao@openmoko.org>
* All rights reserved.
*
* Broken down from monstrous PCF50633 driver mainly by
* Harald Welte, Andy Green and Werner Almesberger
*/
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/device.h>
#include <linux/platform_device.h>
#include <linux/input.h>
#include <linux/slab.h>
#include <linux/mfd/pcf50633/core.h>
#define PCF50633_OOCSTAT_ONKEY 0x01
#define PCF50633_REG_OOCSTAT 0x12
#define PCF50633_REG_OOCMODE 0x10
struct pcf50633_input {
struct pcf50633 *pcf;
struct input_dev *input_dev;
};
static void
pcf50633_input_irq(int irq, void *data)
{
struct pcf50633_input *input;
int onkey_released;
input = data;
/* We report only one event depending on the key press status */
onkey_released = pcf50633_reg_read(input->pcf, PCF50633_REG_OOCSTAT)
& PCF50633_OOCSTAT_ONKEY;
if (irq == PCF50633_IRQ_ONKEYF && !onkey_released)
input_report_key(input->input_dev, KEY_POWER, 1);
else if (irq == PCF50633_IRQ_ONKEYR && onkey_released)
input_report_key(input->input_dev, KEY_POWER, 0);
input_sync(input->input_dev);
}
static int pcf50633_input_probe(struct platform_device *pdev)
{
struct pcf50633_input *input;
struct input_dev *input_dev;
int ret;
input = kzalloc(sizeof(*input), GFP_KERNEL);
if (!input)
return -ENOMEM;
input_dev = input_allocate_device();
if (!input_dev) {
kfree(input);
return -ENOMEM;
}
platform_set_drvdata(pdev, input);
input->pcf = dev_to_pcf50633(pdev->dev.parent);
input->input_dev = input_dev;
input_dev->name = "PCF50633 PMU events";
input_dev->id.bustype = BUS_I2C;
input_dev->evbit[0] = BIT(EV_KEY) | BIT(EV_PWR);
set_bit(KEY_POWER, input_dev->keybit);
ret = input_register_device(input_dev);
if (ret) {
input_free_device(input_dev);
kfree(input);
return ret;
}
pcf50633_register_irq(input->pcf, PCF50633_IRQ_ONKEYR,
pcf50633_input_irq, input);
pcf50633_register_irq(input->pcf, PCF50633_IRQ_ONKEYF,
pcf50633_input_irq, input);
return 0;
}
static void pcf50633_input_remove(struct platform_device *pdev)
{
struct pcf50633_input *input = platform_get_drvdata(pdev);
pcf50633_free_irq(input->pcf, PCF50633_IRQ_ONKEYR);
pcf50633_free_irq(input->pcf, PCF50633_IRQ_ONKEYF);
input_unregister_device(input->input_dev);
kfree(input);
}
static struct platform_driver pcf50633_input_driver = {
.driver = {
.name = "pcf50633-input",
},
.probe = pcf50633_input_probe,
.remove = pcf50633_input_remove,
};
module_platform_driver(pcf50633_input_driver);
MODULE_AUTHOR("Balaji Rao <balajirrao@openmoko.org>");
MODULE_DESCRIPTION("PCF50633 input driver");
MODULE_LICENSE("GPL");
MODULE_ALIAS("platform:pcf50633-input");

View File

@ -82,6 +82,21 @@ config RMI4_F12
touchpads. For sensors that support relative pointing, F12 also
provides mouse input.
config RMI4_F1A
bool "RMI4 Function 1A (0D pointing)"
help
Say Y here if you want to add support for RMI4 function 1A.
Function 1A provides capacitive keys support for RMI4 devices.
config RMI4_F21
bool "RMI4 Function 21 (PRESSURE)"
help
Say Y here if you want to add support for RMI4 function 21.
Function 21 provides buttons/pressure handling for RMI4 devices.
This includes support for buttons/pressure on PressurePad.
config RMI4_F30
bool "RMI4 Function 30 (GPIO LED)"
help

View File

@ -8,6 +8,8 @@ rmi_core-$(CONFIG_RMI4_2D_SENSOR) += rmi_2d_sensor.o
rmi_core-$(CONFIG_RMI4_F03) += rmi_f03.o
rmi_core-$(CONFIG_RMI4_F11) += rmi_f11.o
rmi_core-$(CONFIG_RMI4_F12) += rmi_f12.o
rmi_core-$(CONFIG_RMI4_F1A) += rmi_f1a.o
rmi_core-$(CONFIG_RMI4_F21) += rmi_f21.o
rmi_core-$(CONFIG_RMI4_F30) += rmi_f30.o
rmi_core-$(CONFIG_RMI4_F34) += rmi_f34.o rmi_f34v7.o
rmi_core-$(CONFIG_RMI4_F3A) += rmi_f3a.o

View File

@ -360,6 +360,12 @@ static struct rmi_function_handler *fn_handlers[] = {
#ifdef CONFIG_RMI4_F12
&rmi_f12_handler,
#endif
#ifdef CONFIG_RMI4_F1A
&rmi_f1a_handler,
#endif
#ifdef CONFIG_RMI4_F21
&rmi_f21_handler,
#endif
#ifdef CONFIG_RMI4_F30
&rmi_f30_handler,
#endif

View File

@ -133,6 +133,8 @@ extern struct rmi_function_handler rmi_f01_handler;
extern struct rmi_function_handler rmi_f03_handler;
extern struct rmi_function_handler rmi_f11_handler;
extern struct rmi_function_handler rmi_f12_handler;
extern struct rmi_function_handler rmi_f1a_handler;
extern struct rmi_function_handler rmi_f21_handler;
extern struct rmi_function_handler rmi_f30_handler;
extern struct rmi_function_handler rmi_f34_handler;
extern struct rmi_function_handler rmi_f3a_handler;

View File

@ -0,0 +1,143 @@
// SPDX-License-Identifier: GPL-2.0-only
/*
* Copyright (c) 2025 André Apitzsch <git@apitzsch.eu>
*/
#include <linux/input.h>
#include <linux/property.h>
#include "rmi_driver.h"
struct f1a_data {
struct input_dev *input;
u32 *keymap;
unsigned int num_keys;
};
static int rmi_f1a_parse_device_properties(struct rmi_function *fn, struct f1a_data *f1a)
{
static const char buttons_property[] = "linux,keycodes";
struct device *dev = &fn->dev;
u32 *buttonmap;
int n_keys;
int error;
if (!device_property_present(dev, buttons_property))
return 0;
n_keys = device_property_count_u32(dev, buttons_property);
if (n_keys <= 0) {
error = n_keys < 0 ? n_keys : -EINVAL;
dev_err(dev, "Invalid/malformed '%s' property: %d\n",
buttons_property, error);
return error;
}
buttonmap = devm_kmalloc_array(dev, n_keys, sizeof(*buttonmap),
GFP_KERNEL);
if (!buttonmap)
return -ENOMEM;
error = device_property_read_u32_array(dev, buttons_property,
buttonmap, n_keys);
if (error) {
dev_err(dev, "Failed to parse '%s' property: %d\n",
buttons_property, error);
return error;
}
f1a->keymap = buttonmap;
f1a->num_keys = n_keys;
return 0;
}
static irqreturn_t rmi_f1a_attention(int irq, void *ctx)
{
struct rmi_function *fn = ctx;
struct f1a_data *f1a = dev_get_drvdata(&fn->dev);
char button_bitmask;
int key;
int error;
error = rmi_read_block(fn->rmi_dev, fn->fd.data_base_addr,
&button_bitmask, sizeof(button_bitmask));
if (error) {
dev_err(&fn->dev, "Failed to read object data. Code: %d.\n",
error);
return IRQ_RETVAL(error);
}
for (key = 0; key < f1a->num_keys; key++)
input_report_key(f1a->input, f1a->keymap[key],
button_bitmask & BIT(key));
return IRQ_HANDLED;
}
static int rmi_f1a_config(struct rmi_function *fn)
{
struct f1a_data *f1a = dev_get_drvdata(&fn->dev);
struct rmi_driver *drv = fn->rmi_dev->driver;
if (f1a->num_keys)
drv->set_irq_bits(fn->rmi_dev, fn->irq_mask);
return 0;
}
static int rmi_f1a_initialize(struct rmi_function *fn, struct f1a_data *f1a)
{
int error;
int i;
error = rmi_f1a_parse_device_properties(fn, f1a);
if (error)
return error;
for (i = 0; i < f1a->num_keys; i++)
input_set_capability(f1a->input, EV_KEY, f1a->keymap[i]);
f1a->input->keycode = f1a->keymap;
f1a->input->keycodemax = f1a->num_keys;
f1a->input->keycodesize = sizeof(f1a->keymap[0]);
return 0;
}
static int rmi_f1a_probe(struct rmi_function *fn)
{
struct rmi_device *rmi_dev = fn->rmi_dev;
struct rmi_driver_data *drv_data = dev_get_drvdata(&rmi_dev->dev);
struct f1a_data *f1a;
int error;
if (!drv_data->input) {
dev_info(&fn->dev, "F1A: no input device found, ignoring\n");
return -ENXIO;
}
f1a = devm_kzalloc(&fn->dev, sizeof(*f1a), GFP_KERNEL);
if (!f1a)
return -ENOMEM;
f1a->input = drv_data->input;
error = rmi_f1a_initialize(fn, f1a);
if (error)
return error;
dev_set_drvdata(&fn->dev, f1a);
return 0;
}
struct rmi_function_handler rmi_f1a_handler = {
.driver = {
.name = "rmi4_f1a",
},
.func = 0x1a,
.probe = rmi_f1a_probe,
.config = rmi_f1a_config,
.attention = rmi_f1a_attention,
};

View File

@ -0,0 +1,179 @@
// SPDX-License-Identifier: GPL-2.0-only
/*
* Copyright (c) 2012-2025 Synaptics Incorporated
*/
#include <linux/bits.h>
#include <linux/dev_printk.h>
#include <linux/kernel.h>
#include <linux/rmi.h>
#include <linux/input.h>
#include <linux/slab.h>
#include "rmi_driver.h"
#define RMI_F21_SENSOR_COUNT_MASK GENMASK(3, 0)
#define RMI_F21_FINGER_COUNT_PRESENT BIT(5)
#define RMI_F21_NEW_REPORT_FORMAT BIT(6)
#define RMI_F21_FINGER_COUNT_MASK GENMASK(3, 0)
#define RMI_F21_MAX_SENSORS 16
#define RMI_F21_MAX_FINGERS 16
#define RMI_F21_DATA_REGS_MAX_SIZE (RMI_F21_MAX_SENSORS * 2 + \
RMI_F21_MAX_FINGERS * 2 + 1)
#define RMI_F21_FORCE_CLICK_BIT BIT(0)
#define RMI_F21_FORCEPAD_BUTTON_COUNT 1
struct f21_data {
struct input_dev *input;
u16 key_code;
unsigned int attn_data_size;
unsigned int attn_data_button_offset;
unsigned int data_reg_size;
unsigned int data_reg_button_offset;
u8 data_regs[RMI_F21_DATA_REGS_MAX_SIZE];
};
static irqreturn_t rmi_f21_attention(int irq, void *ctx)
{
struct rmi_function *fn = ctx;
struct f21_data *f21 = dev_get_drvdata(&fn->dev);
struct rmi_driver_data *drvdata = dev_get_drvdata(&fn->rmi_dev->dev);
u8 *pdata;
int error;
bool pressed;
if (drvdata->attn_data.data) {
if (drvdata->attn_data.size < f21->attn_data_size) {
dev_warn(&fn->dev, "f21 interrupt, but data is missing\n");
return IRQ_HANDLED;
}
pdata = drvdata->attn_data.data + f21->attn_data_button_offset;
drvdata->attn_data.data += f21->attn_data_size;
drvdata->attn_data.size -= f21->attn_data_size;
} else {
error = rmi_read_block(fn->rmi_dev, fn->fd.data_base_addr,
f21->data_regs, f21->data_reg_size);
if (error) {
dev_err(&fn->dev, "failed to read f21 data registers: %d\n",
error);
return IRQ_RETVAL(error);
}
pdata = f21->data_regs + f21->data_reg_button_offset;
}
pressed = *pdata & RMI_F21_FORCE_CLICK_BIT;
input_report_key(f21->input, f21->key_code, pressed);
return IRQ_HANDLED;
}
static int rmi_f21_config(struct rmi_function *fn)
{
struct rmi_driver *drv = fn->rmi_dev->driver;
drv->set_irq_bits(fn->rmi_dev, fn->irq_mask);
return 0;
}
static int rmi_f21_initialize(struct rmi_function *fn, struct f21_data *f21)
{
struct input_dev *input = f21->input;
f21->key_code = BTN_LEFT;
input->keycode = &f21->key_code;
input->keycodesize = sizeof(f21->key_code);
input->keycodemax = RMI_F21_FORCEPAD_BUTTON_COUNT;
input_set_capability(input, EV_KEY, f21->key_code);
__set_bit(INPUT_PROP_BUTTONPAD, input->propbit);
return 0;
}
static int rmi_f21_probe(struct rmi_function *fn)
{
struct rmi_device *rmi_dev = fn->rmi_dev;
struct rmi_driver_data *drv_data = dev_get_drvdata(&rmi_dev->dev);
struct f21_data *f21;
unsigned int sensor_count;
unsigned int max_fingers;
unsigned int query15_offset;
u8 query15_data;
int error;
if (!drv_data->input) {
dev_info(&fn->dev, "f21: no input device found, ignoring\n");
return -ENXIO;
}
f21 = devm_kzalloc(&fn->dev, sizeof(*f21), GFP_KERNEL);
if (!f21)
return -ENOMEM;
f21->input = drv_data->input;
error = rmi_f21_initialize(fn, f21);
if (error)
return error;
dev_set_drvdata(&fn->dev, f21);
sensor_count = fn->fd.query_base_addr & RMI_F21_SENSOR_COUNT_MASK;
if (fn->fd.query_base_addr & RMI_F21_FINGER_COUNT_PRESENT) {
query15_offset = fn->fd.query_base_addr & RMI_F21_NEW_REPORT_FORMAT ? 2 : 1;
error = rmi_read_block(fn->rmi_dev,
fn->fd.query_base_addr + query15_offset,
&query15_data, sizeof(query15_data));
if (error)
return dev_err_probe(&fn->dev, error,
"failed to read 'query15' data");
max_fingers = query15_data & RMI_F21_FINGER_COUNT_MASK;
} else {
max_fingers = 5;
}
if (fn->fd.query_base_addr & RMI_F21_NEW_REPORT_FORMAT) {
/* Each finger uses one byte, and the button state uses one byte.*/
f21->attn_data_size = max_fingers + 1;
f21->attn_data_button_offset = f21->attn_data_size - 1;
/*
* Each sensor uses two bytes, the button state uses one byte,
* and each finger uses two bytes.
*/
f21->data_reg_size = sensor_count * 2 + 1 + max_fingers * 2;
f21->data_reg_button_offset = sensor_count * 2;
} else {
/*
* Regardless of the transport each finger uses two bytes,
* and the button state uses one byte.
*/
f21->attn_data_size = sensor_count * 2 + 1;
f21->attn_data_button_offset = sensor_count * 2;
f21->data_reg_size = f21->attn_data_size;
f21->data_reg_button_offset = f21->attn_data_button_offset;
}
return 0;
}
struct rmi_function_handler rmi_f21_handler = {
.driver = {
.name = "rmi4_f21",
},
.func = 0x21,
.probe = rmi_f21_probe,
.config = rmi_f21_config,
.attention = rmi_f21_attention,
};

View File

@ -0,0 +1,277 @@
// SPDX-License-Identifier: GPL-2.0-only
/*
* Helper functions for overlay objects on touchscreens
*
* Copyright (c) 2023 Javier Carrasco <javier.carrasco@wolfvision.net>
*/
#include <linux/input.h>
#include <linux/input/mt.h>
#include <linux/input/touch-overlay.h>
#include <linux/list.h>
#include <linux/module.h>
#include <linux/property.h>
struct touch_overlay_segment {
struct list_head list;
u32 x_origin;
u32 y_origin;
u32 x_size;
u32 y_size;
u32 key;
bool pressed;
int slot;
};
static int touch_overlay_get_segment(struct fwnode_handle *segment_node,
struct touch_overlay_segment *segment,
struct input_dev *input)
{
int error;
error = fwnode_property_read_u32(segment_node, "x-origin",
&segment->x_origin);
if (error)
return error;
error = fwnode_property_read_u32(segment_node, "y-origin",
&segment->y_origin);
if (error)
return error;
error = fwnode_property_read_u32(segment_node, "x-size",
&segment->x_size);
if (error)
return error;
error = fwnode_property_read_u32(segment_node, "y-size",
&segment->y_size);
if (error)
return error;
error = fwnode_property_read_u32(segment_node, "linux,code",
&segment->key);
if (!error)
input_set_capability(input, EV_KEY, segment->key);
else if (error != -EINVAL)
return error;
return 0;
}
/**
* touch_overlay_map - map overlay objects from the device tree and set
* key capabilities if buttons are defined.
* @list: pointer to the list that will hold the segments
* @input: pointer to the already allocated input_dev
*
* Returns 0 on success and error number otherwise.
*
* If buttons are defined, key capabilities are set accordingly.
*/
int touch_overlay_map(struct list_head *list, struct input_dev *input)
{
struct fwnode_handle *fw_segment;
struct device *dev = input->dev.parent;
struct touch_overlay_segment *segment;
int error;
struct fwnode_handle *overlay __free(fwnode_handle) =
device_get_named_child_node(dev, "touch-overlay");
if (!overlay)
return 0;
fwnode_for_each_available_child_node(overlay, fw_segment) {
segment = devm_kzalloc(dev, sizeof(*segment), GFP_KERNEL);
if (!segment) {
fwnode_handle_put(fw_segment);
return -ENOMEM;
}
error = touch_overlay_get_segment(fw_segment, segment, input);
if (error) {
fwnode_handle_put(fw_segment);
return error;
}
list_add_tail(&segment->list, list);
}
return 0;
}
EXPORT_SYMBOL(touch_overlay_map);
/**
* touch_overlay_get_touchscreen_abs - get abs size from the touchscreen area.
* @list: pointer to the list that holds the segments
* @x: horizontal abs
* @y: vertical abs
*/
void touch_overlay_get_touchscreen_abs(struct list_head *list, u16 *x, u16 *y)
{
struct touch_overlay_segment *segment;
struct list_head *ptr;
list_for_each(ptr, list) {
segment = list_entry(ptr, struct touch_overlay_segment, list);
if (!segment->key) {
*x = segment->x_size - 1;
*y = segment->y_size - 1;
break;
}
}
}
EXPORT_SYMBOL(touch_overlay_get_touchscreen_abs);
static bool touch_overlay_segment_event(struct touch_overlay_segment *seg,
struct input_mt_pos *pos)
{
if (pos->x >= seg->x_origin && pos->x < (seg->x_origin + seg->x_size) &&
pos->y >= seg->y_origin && pos->y < (seg->y_origin + seg->y_size))
return true;
return false;
}
/**
* touch_overlay_mapped_touchscreen - check if a touchscreen area is mapped
* @list: pointer to the list that holds the segments
*
* Returns true if a touchscreen area is mapped or false otherwise.
*/
bool touch_overlay_mapped_touchscreen(struct list_head *list)
{
struct touch_overlay_segment *segment;
struct list_head *ptr;
list_for_each(ptr, list) {
segment = list_entry(ptr, struct touch_overlay_segment, list);
if (!segment->key)
return true;
}
return false;
}
EXPORT_SYMBOL(touch_overlay_mapped_touchscreen);
static bool touch_overlay_event_on_ts(struct list_head *list,
struct input_mt_pos *pos)
{
struct touch_overlay_segment *segment;
struct list_head *ptr;
list_for_each(ptr, list) {
segment = list_entry(ptr, struct touch_overlay_segment, list);
if (segment->key)
continue;
if (touch_overlay_segment_event(segment, pos)) {
pos->x -= segment->x_origin;
pos->y -= segment->y_origin;
return true;
}
/* ignore touch events outside the defined area */
return false;
}
return true;
}
static bool touch_overlay_button_event(struct input_dev *input,
struct touch_overlay_segment *segment,
struct input_mt_pos *pos, int slot)
{
struct input_mt *mt = input->mt;
struct input_mt_slot *s = &mt->slots[slot];
bool button_contact = touch_overlay_segment_event(segment, pos);
if (segment->slot == slot && segment->pressed) {
/* sliding out of the button releases it */
if (!button_contact) {
input_report_key(input, segment->key, false);
segment->pressed = false;
/* keep available for a possible touch event */
return false;
}
/* ignore sliding on the button while pressed */
s->frame = mt->frame;
return true;
} else if (button_contact) {
input_report_key(input, segment->key, true);
s->frame = mt->frame;
segment->slot = slot;
segment->pressed = true;
return true;
}
return false;
}
/**
* touch_overlay_sync_frame - update the status of the segments and report
* buttons whose tracked slot is unused.
* @list: pointer to the list that holds the segments
* @input: pointer to the input device associated to the contact
*/
void touch_overlay_sync_frame(struct list_head *list, struct input_dev *input)
{
struct touch_overlay_segment *segment;
struct input_mt *mt = input->mt;
struct input_mt_slot *s;
struct list_head *ptr;
list_for_each(ptr, list) {
segment = list_entry(ptr, struct touch_overlay_segment, list);
if (!segment->key)
continue;
s = &mt->slots[segment->slot];
if (!input_mt_is_used(mt, s) && segment->pressed) {
input_report_key(input, segment->key, false);
segment->pressed = false;
}
}
}
EXPORT_SYMBOL(touch_overlay_sync_frame);
/**
* touch_overlay_process_contact - process contacts according to the overlay
* mapping. This function acts as a filter to release the calling driver
* from the contacts that are either related to overlay buttons or out of the
* overlay touchscreen area, if defined.
* @list: pointer to the list that holds the segments
* @input: pointer to the input device associated to the contact
* @pos: pointer to the contact position
* @slot: slot associated to the contact (0 if multitouch is not supported)
*
* Returns true if the contact was processed (reported for valid key events
* and dropped for contacts outside the overlay touchscreen area) or false
* if the contact must be processed by the caller. In that case this function
* shifts the (x,y) coordinates to the overlay touchscreen axis if required.
*/
bool touch_overlay_process_contact(struct list_head *list,
struct input_dev *input,
struct input_mt_pos *pos, int slot)
{
struct touch_overlay_segment *segment;
struct list_head *ptr;
/*
* buttons must be prioritized over overlay touchscreens to account for
* overlappings e.g. a button inside the touchscreen area.
*/
list_for_each(ptr, list) {
segment = list_entry(ptr, struct touch_overlay_segment, list);
if (segment->key &&
touch_overlay_button_event(input, segment, pos, slot))
return true;
}
/*
* valid contacts on the overlay touchscreen are left for the client
* to be processed/reported according to its (possibly) unique features.
*/
return !touch_overlay_event_on_ts(list, pos);
}
EXPORT_SYMBOL(touch_overlay_process_contact);
MODULE_LICENSE("GPL");
MODULE_DESCRIPTION("Helper functions for overlay objects on touch devices");

View File

@ -444,10 +444,11 @@ static int ad7879_gpio_get_value(struct gpio_chip *chip, unsigned gpio)
return !!(val & AD7879_GPIO_DATA);
}
static void ad7879_gpio_set_value(struct gpio_chip *chip,
unsigned gpio, int value)
static int ad7879_gpio_set_value(struct gpio_chip *chip, unsigned int gpio,
int value)
{
struct ad7879 *ts = gpiochip_get_data(chip);
int ret;
mutex_lock(&ts->mutex);
if (value)
@ -455,8 +456,10 @@ static void ad7879_gpio_set_value(struct gpio_chip *chip,
else
ts->cmd_crtl2 &= ~AD7879_GPIO_DATA;
ad7879_write(ts, AD7879_REG_CTRL2, ts->cmd_crtl2);
ret = ad7879_write(ts, AD7879_REG_CTRL2, ts->cmd_crtl2);
mutex_unlock(&ts->mutex);
return ret;
}
static int ad7879_gpio_add(struct ad7879 *ts)
@ -472,7 +475,7 @@ static int ad7879_gpio_add(struct ad7879 *ts)
ts->gc.direction_input = ad7879_gpio_direction_input;
ts->gc.direction_output = ad7879_gpio_direction_output;
ts->gc.get = ad7879_gpio_get_value;
ts->gc.set = ad7879_gpio_set_value;
ts->gc.set_rv = ad7879_gpio_set_value;
ts->gc.can_sleep = 1;
ts->gc.base = -1;
ts->gc.ngpio = 1;

View File

@ -120,7 +120,6 @@ struct edt_ft5x06_ts_data {
struct regmap *regmap;
#if defined(CONFIG_DEBUG_FS)
struct dentry *debug_dir;
u8 *raw_buffer;
size_t raw_bufsize;
#endif
@ -815,23 +814,21 @@ static const struct file_operations debugfs_raw_data_fops = {
.read = edt_ft5x06_debugfs_raw_data_read,
};
static void edt_ft5x06_ts_prepare_debugfs(struct edt_ft5x06_ts_data *tsdata,
const char *debugfs_name)
static void edt_ft5x06_ts_prepare_debugfs(struct edt_ft5x06_ts_data *tsdata)
{
tsdata->debug_dir = debugfs_create_dir(debugfs_name, NULL);
struct dentry *debug_dir = tsdata->client->debugfs;
debugfs_create_u16("num_x", S_IRUSR, tsdata->debug_dir, &tsdata->num_x);
debugfs_create_u16("num_y", S_IRUSR, tsdata->debug_dir, &tsdata->num_y);
debugfs_create_u16("num_x", S_IRUSR, debug_dir, &tsdata->num_x);
debugfs_create_u16("num_y", S_IRUSR, debug_dir, &tsdata->num_y);
debugfs_create_file("mode", S_IRUSR | S_IWUSR,
tsdata->debug_dir, tsdata, &debugfs_mode_fops);
debug_dir, tsdata, &debugfs_mode_fops);
debugfs_create_file("raw_data", S_IRUSR,
tsdata->debug_dir, tsdata, &debugfs_raw_data_fops);
debug_dir, tsdata, &debugfs_raw_data_fops);
}
static void edt_ft5x06_ts_teardown_debugfs(struct edt_ft5x06_ts_data *tsdata)
{
debugfs_remove_recursive(tsdata->debug_dir);
kfree(tsdata->raw_buffer);
}
@ -842,8 +839,7 @@ static int edt_ft5x06_factory_mode(struct edt_ft5x06_ts_data *tsdata)
return -ENOSYS;
}
static void edt_ft5x06_ts_prepare_debugfs(struct edt_ft5x06_ts_data *tsdata,
const char *debugfs_name)
static void edt_ft5x06_ts_prepare_debugfs(struct edt_ft5x06_ts_data *tsdata)
{
}
@ -1349,7 +1345,7 @@ static int edt_ft5x06_ts_probe(struct i2c_client *client)
if (error)
return error;
edt_ft5x06_ts_prepare_debugfs(tsdata, dev_driver_string(&client->dev));
edt_ft5x06_ts_prepare_debugfs(tsdata);
dev_dbg(&client->dev,
"EDT FT5x06 initialized: IRQ %d, WAKE pin %d, Reset pin %d.\n",
@ -1495,6 +1491,10 @@ static const struct edt_i2c_chip_data edt_ft8201_data = {
.max_support_points = 10,
};
static const struct edt_i2c_chip_data edt_ft8716_data = {
.max_support_points = 10,
};
static const struct edt_i2c_chip_data edt_ft8719_data = {
.max_support_points = 10,
};
@ -1507,6 +1507,7 @@ static const struct i2c_device_id edt_ft5x06_ts_id[] = {
/* Note no edt- prefix for compatibility with the ft6236.c driver */
{ .name = "ft6236", .driver_data = (long)&edt_ft6236_data },
{ .name = "ft8201", .driver_data = (long)&edt_ft8201_data },
{ .name = "ft8716", .driver_data = (long)&edt_ft8716_data },
{ .name = "ft8719", .driver_data = (long)&edt_ft8719_data },
{ /* sentinel */ }
};
@ -1523,6 +1524,7 @@ static const struct of_device_id edt_ft5x06_of_match[] = {
/* Note focaltech vendor prefix for compatibility with ft6236.c */
{ .compatible = "focaltech,ft6236", .data = &edt_ft6236_data },
{ .compatible = "focaltech,ft8201", .data = &edt_ft8201_data },
{ .compatible = "focaltech,ft8716", .data = &edt_ft8716_data },
{ .compatible = "focaltech,ft8719", .data = &edt_ft8719_data },
{ /* sentinel */ }
};

View File

@ -44,9 +44,11 @@
#define GOODIX_HAVE_KEY BIT(4)
#define GOODIX_BUFFER_STATUS_TIMEOUT 20
#define RESOLUTION_LOC 1
#define MAX_CONTACTS_LOC 5
#define TRIGGER_LOC 6
#define RESOLUTION_LOC 1
#define MAX_CONTACTS_LOC 5
#define TRIGGER_LOC 6
#define GOODIX_POLL_INTERVAL_MS 17 /* 17ms = 60fps */
/* Our special handling for GPIO accesses through ACPI is x86 specific */
#if defined CONFIG_X86 && defined CONFIG_ACPI
@ -497,6 +499,14 @@ static void goodix_process_events(struct goodix_ts_data *ts)
input_sync(ts->input_dev);
}
static void goodix_ts_work_i2c_poll(struct input_dev *input)
{
struct goodix_ts_data *ts = input_get_drvdata(input);
goodix_process_events(ts);
goodix_i2c_write_u8(ts->client, GOODIX_READ_COOR_ADDR, 0);
}
/**
* goodix_ts_irq_handler - The IRQ handler
*
@ -513,13 +523,29 @@ static irqreturn_t goodix_ts_irq_handler(int irq, void *dev_id)
return IRQ_HANDLED;
}
static void goodix_enable_irq(struct goodix_ts_data *ts)
{
if (ts->client->irq)
enable_irq(ts->client->irq);
}
static void goodix_disable_irq(struct goodix_ts_data *ts)
{
if (ts->client->irq)
disable_irq(ts->client->irq);
}
static void goodix_free_irq(struct goodix_ts_data *ts)
{
devm_free_irq(&ts->client->dev, ts->client->irq, ts);
if (ts->client->irq)
devm_free_irq(&ts->client->dev, ts->client->irq, ts);
}
static int goodix_request_irq(struct goodix_ts_data *ts)
{
if (!ts->client->irq)
return 0;
return devm_request_threaded_irq(&ts->client->dev, ts->client->irq,
NULL, goodix_ts_irq_handler,
ts->irq_flags, ts->client->name, ts);
@ -1219,6 +1245,18 @@ static int goodix_configure_dev(struct goodix_ts_data *ts)
return error;
}
input_set_drvdata(ts->input_dev, ts);
if (!ts->client->irq) {
error = input_setup_polling(ts->input_dev, goodix_ts_work_i2c_poll);
if (error) {
dev_err(&ts->client->dev,
"could not set up polling mode, %d\n", error);
return error;
}
input_set_poll_interval(ts->input_dev, GOODIX_POLL_INTERVAL_MS);
}
error = input_register_device(ts->input_dev);
if (error) {
dev_err(&ts->client->dev,
@ -1422,7 +1460,7 @@ static int goodix_suspend(struct device *dev)
/* We need gpio pins to suspend/resume */
if (ts->irq_pin_access_method == IRQ_PIN_ACCESS_NONE) {
disable_irq(client->irq);
goodix_disable_irq(ts);
return 0;
}
@ -1466,7 +1504,7 @@ static int goodix_resume(struct device *dev)
int error;
if (ts->irq_pin_access_method == IRQ_PIN_ACCESS_NONE) {
enable_irq(client->irq);
goodix_enable_irq(ts);
return 0;
}

View File

@ -22,6 +22,7 @@
#include <linux/pm_qos.h>
#include <linux/slab.h>
#include <linux/types.h>
#include <linux/input/touch-overlay.h>
#define ST1232_TS_NAME "st1232-ts"
#define ST1633_TS_NAME "st1633-ts"
@ -57,6 +58,7 @@ struct st1232_ts_data {
struct dev_pm_qos_request low_latency_req;
struct gpio_desc *reset_gpio;
const struct st_chip_info *chip_info;
struct list_head touch_overlay_list;
int read_buf_len;
u8 *read_buf;
};
@ -156,6 +158,10 @@ static int st1232_ts_parse_and_report(struct st1232_ts_data *ts)
input_mt_assign_slots(input, slots, pos, n_contacts, 0);
for (i = 0; i < n_contacts; i++) {
if (touch_overlay_process_contact(&ts->touch_overlay_list,
input, &pos[i], slots[i]))
continue;
input_mt_slot(input, slots[i]);
input_mt_report_slot_state(input, MT_TOOL_FINGER, true);
input_report_abs(input, ABS_MT_POSITION_X, pos[i].x);
@ -164,6 +170,7 @@ static int st1232_ts_parse_and_report(struct st1232_ts_data *ts)
input_report_abs(input, ABS_MT_TOUCH_MAJOR, z[i]);
}
touch_overlay_sync_frame(&ts->touch_overlay_list, input);
input_mt_sync_frame(input);
input_sync(input);
@ -292,18 +299,30 @@ static int st1232_ts_probe(struct i2c_client *client)
if (error)
return error;
/* Read resolution from the chip */
error = st1232_ts_read_resolution(ts, &max_x, &max_y);
if (error) {
dev_err(&client->dev,
"Failed to read resolution: %d\n", error);
return error;
}
if (ts->chip_info->have_z)
input_set_abs_params(input_dev, ABS_MT_TOUCH_MAJOR, 0,
ts->chip_info->max_area, 0, 0);
/* map overlay objects if defined in the device tree */
INIT_LIST_HEAD(&ts->touch_overlay_list);
error = touch_overlay_map(&ts->touch_overlay_list, input_dev);
if (error)
return error;
if (touch_overlay_mapped_touchscreen(&ts->touch_overlay_list)) {
/* Read resolution from the overlay touchscreen if defined */
touch_overlay_get_touchscreen_abs(&ts->touch_overlay_list,
&max_x, &max_y);
} else {
/* Read resolution from the chip */
error = st1232_ts_read_resolution(ts, &max_x, &max_y);
if (error) {
dev_err(&client->dev,
"Failed to read resolution: %d\n", error);
return error;
}
}
input_set_abs_params(input_dev, ABS_MT_POSITION_X,
0, max_x, 0, 0);
input_set_abs_params(input_dev, ABS_MT_POSITION_Y,

View File

@ -0,0 +1,25 @@
/* SPDX-License-Identifier: GPL-2.0-only */
/*
* Copyright (c) 2023 Javier Carrasco <javier.carrasco@wolfvision.net>
*/
#ifndef _TOUCH_OVERLAY
#define _TOUCH_OVERLAY
#include <linux/types.h>
struct input_dev;
int touch_overlay_map(struct list_head *list, struct input_dev *input);
void touch_overlay_get_touchscreen_abs(struct list_head *list, u16 *x, u16 *y);
bool touch_overlay_mapped_touchscreen(struct list_head *list);
bool touch_overlay_process_contact(struct list_head *list,
struct input_dev *input,
struct input_mt_pos *pos, int slot);
void touch_overlay_sync_frame(struct list_head *list, struct input_dev *input);
#endif

View File

@ -601,6 +601,11 @@
#define BTN_DPAD_LEFT 0x222
#define BTN_DPAD_RIGHT 0x223
#define BTN_GRIPL 0x224
#define BTN_GRIPR 0x225
#define BTN_GRIPL2 0x226
#define BTN_GRIPR2 0x227
#define KEY_ALS_TOGGLE 0x230 /* Ambient light sensor */
#define KEY_ROTATE_LOCK_TOGGLE 0x231 /* Display rotation lock */
#define KEY_REFRESH_RATE_TOGGLE 0x232 /* Display refresh rate toggle */
@ -765,6 +770,9 @@
#define KEY_KBD_LCD_MENU4 0x2bb
#define KEY_KBD_LCD_MENU5 0x2bc
/* Performance Boost key (Alienware)/G-Mode key (Dell) */
#define KEY_PERFORMANCE 0x2bd
#define BTN_TRIGGER_HAPPY 0x2c0
#define BTN_TRIGGER_HAPPY1 0x2c0
#define BTN_TRIGGER_HAPPY2 0x2c1