Input updates for v6.18-rc0

- a number of conversions to yaml/json schema and fixes for
   input-related device tree bindings
 
 - a new driver for Awinic AW86927 haptic chip
 
 - a new driver for Hynitron CST816x series controller
 
 - a new driver for add Himax HX852x(ES) touchscreen controller
 
 - a fix to uinput to not leak kernel memory via a gap in
   uinput_ff_upload_compat structure
 
 - a fix to prevent overflow in pressure calculation in tsc2007 driver
   causing phantom touches
 
 - a change to Atmel maxTouch driver to support generic touchscreen
   configuration (flip, rotate, etc.)
 
 - support for platform data was dropped in tca8418_keypad,
   pxa27x-keypad, spear-keyboard and twl4030_keypad drivers, they all
   now rely on generic device properties for configuration
 
 - other assorted changes and fixes.
 -----BEGIN PGP SIGNATURE-----
 
 iHUEABYKAB0WIQST2eWILY88ieB2DOtAj56VGEWXnAUCaOX1HQAKCRBAj56VGEWX
 nC29AQCsDm6HBKXJZl6nAI8WHQiSvASFvyFlF6scVnfueX54mQEA6eE428+Cr1Sn
 mDiwL38Es+PlZbdsi0J2q0vYzpr4yQU=
 =JZIn
 -----END PGP SIGNATURE-----

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

Pull input updates from Dmitry Torokhov:

 - Conversions to yaml/json schema and fixes for input-related device
   tree bindings

 - New drivers:
     - Awinic AW86927 haptic chip
     - Hynitron CST816x series controller
     - Himax HX852x(ES) touchscreen controller

 - Fix uinput to not leak kernel memory via a gap in
   uinput_ff_upload_compat structure

 - Prevent overflow in pressure calculation in tsc2007 driver causing
   phantom touches

 - Make the Atmel maxTouch driver support generic touchscreen
   configuration (flip, rotate, etc)

 - Drop support for platform data in tca8418_keypad, pxa27x-keypad,
   spear-keyboard and twl4030_keypad drivers, they all now rely on
   generic device properties for configuration

 - Other assorted changes and fixes

* tag 'input-for-v6.18-rc0' of git://git.kernel.org/pub/scm/linux/kernel/git/dtor/input: (50 commits)
  Input: atmel_mxt_ts - allow reset GPIO to sleep
  Input: aw86927 - fix error code in probe()
  Input: psxpad-spi - add a check for the return value of spi_setup()
  Input: uinput - zero-initialize uinput_ff_upload_compat to avoid info leak
  Input: aw86927 - add driver for Awinic AW86927
  dt-bindings: input: Add Awinic AW86927
  dt-bindings: touchscreen: remove touchscreen.txt
  dt-bindings: arm: bcm: raspberrypi,bcm2835-firmware: Add touchscreen child node
  dt-bindings: touchscreen: convert eeti bindings to json schema
  Input: pm8941-pwrkey - disable wakeup for resin by default
  dt-bindings: input: pm8941-pwrkey: Document wakeup-source property
  Input: add driver for Hynitron CST816x series
  dt-bindings: input: touchscreen: add hynitron cst816x series
  Input: imx6ul_tsc - set glitch threshold by DTS property
  dt-bindings: touchscreen: fsl,imx6ul-tsc: support glitch thresold
  dt-bindings: touchscreen: add debounce-delay-us property
  Input: ps2-gpio - fix typo
  Input: atmel_mxt_ts - add support for generic touchscreen configurations
  dt-bindings: input: maxtouch: add common touchscreen properties
  dt-bindings: touchscreen: convert zet6223 bindings to json schema
  ...
This commit is contained in:
Linus Torvalds 2025-10-08 09:44:38 -07:00
commit 99cedb6b8f
87 changed files with 2754 additions and 1368 deletions

View File

@ -103,6 +103,28 @@ properties:
- compatible
- "#pwm-cells"
touchscreen:
type: object
$ref: /schemas/input/touchscreen/touchscreen.yaml#
additionalProperties: false
properties:
compatible:
const: raspberrypi,firmware-ts
firmware:
deprecated: true
description: Phandle to RPi's firmware device node.
touchscreen-size-x: true
touchscreen-size-y: true
touchscreen-inverted-x: true
touchscreen-inverted-y: true
touchscreen-swapped-x-y: true
required:
- compatible
required:
- compatible
- mboxes
@ -135,5 +157,11 @@ examples:
compatible = "raspberrypi,firmware-poe-pwm";
#pwm-cells = <2>;
};
ts: touchscreen {
compatible = "raspberrypi,firmware-ts";
touchscreen-size-x = <800>;
touchscreen-size-y = <480>;
};
};
...

View File

@ -16,6 +16,7 @@ description: |
allOf:
- $ref: input.yaml#
- $ref: touchscreen/touchscreen.yaml#
properties:
compatible:
@ -95,7 +96,7 @@ required:
- reg
- interrupts
additionalProperties: false
unevaluatedProperties: false
examples:
- |

View File

@ -0,0 +1,48 @@
# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
%YAML 1.2
---
$id: http://devicetree.org/schemas/input/awinic,aw86927.yaml#
$schema: http://devicetree.org/meta-schemas/core.yaml#
title: Awinic AW86927 LRA Haptic IC
maintainers:
- Griffin Kroah-Hartman <griffin.kroah@fairphone.com>
properties:
compatible:
const: awinic,aw86927
reg:
maxItems: 1
reset-gpios:
maxItems: 1
interrupts:
maxItems: 1
required:
- compatible
- reg
- reset-gpios
- interrupts
additionalProperties: false
examples:
- |
#include <dt-bindings/gpio/gpio.h>
#include <dt-bindings/interrupt-controller/irq.h>
i2c {
#address-cells = <1>;
#size-cells = <0>;
vibrator@5a {
compatible = "awinic,aw86927";
reg = <0x5a>;
interrupts-extended = <&tlmm 101 IRQ_TYPE_EDGE_FALLING>;
reset-gpios = <&tlmm 100 GPIO_ACTIVE_LOW>;
};
};

View File

@ -1,34 +0,0 @@
NXP LPC32xx Key Scan Interface
This binding is based on the matrix-keymap binding with the following
changes:
Required Properties:
- compatible: Should be "nxp,lpc3220-key"
- reg: Physical base address of the controller and length of memory mapped
region.
- interrupts: The interrupt number to the cpu.
- clocks: phandle to clock controller plus clock-specifier pair
- nxp,debounce-delay-ms: Debounce delay in ms
- nxp,scan-delay-ms: Repeated scan period in ms
- linux,keymap: the key-code to be reported when the key is pressed
and released, see also
Documentation/devicetree/bindings/input/matrix-keymap.txt
Note: keypad,num-rows and keypad,num-columns are required, and must be equal
since LPC32xx only supports square matrices
Example:
key@40050000 {
compatible = "nxp,lpc3220-key";
reg = <0x40050000 0x1000>;
clocks = <&clk LPC32XX_CLK_KEY>;
interrupt-parent = <&sic1>;
interrupts = <22 IRQ_TYPE_LEVEL_HIGH>;
keypad,num-rows = <1>;
keypad,num-columns = <1>;
nxp,debounce-delay-ms = <3>;
nxp,scan-delay-ms = <34>;
linux,keymap = <0x00000002>;
};

View File

@ -0,0 +1,61 @@
# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
%YAML 1.2
---
$id: http://devicetree.org/schemas/input/nxp,lpc3220-key.yaml#
$schema: http://devicetree.org/meta-schemas/core.yaml#
title: NXP LPC32xx Key Scan Interface
maintainers:
- Frank Li <Frank.Li@nxp.com>
properties:
compatible:
const: nxp,lpc3220-key
reg:
maxItems: 1
interrupts:
maxItems: 1
clocks:
maxItems: 1
nxp,debounce-delay-ms:
description: Debounce delay in ms
nxp,scan-delay-ms:
description: Repeated scan period in ms
required:
- compatible
- reg
- interrupts
- clocks
- nxp,debounce-delay-ms
- nxp,scan-delay-ms
- linux,keymap
allOf:
- $ref: matrix-keymap.yaml#
unevaluatedProperties: false
examples:
- |
#include <dt-bindings/interrupt-controller/irq.h>
#include <dt-bindings/clock/lpc32xx-clock.h>
key@40050000 {
compatible = "nxp,lpc3220-key";
reg = <0x40050000 0x1000>;
clocks = <&clk LPC32XX_CLK_KEY>;
interrupt-parent = <&sic1>;
interrupts = <22 IRQ_TYPE_LEVEL_HIGH>;
keypad,num-rows = <1>;
keypad,num-columns = <1>;
nxp,debounce-delay-ms = <3>;
nxp,scan-delay-ms = <34>;
linux,keymap = <0x00000002>;
};

View File

@ -10,9 +10,6 @@ maintainers:
- Courtney Cavin <courtney.cavin@sonymobile.com>
- Vinod Koul <vkoul@kernel.org>
allOf:
- $ref: input.yaml#
properties:
compatible:
enum:
@ -25,23 +22,40 @@ properties:
maxItems: 1
debounce:
description: |
Time in microseconds that key must be pressed or
released for state change interrupt to trigger.
description:
Time in microseconds that key must be pressed or released for state
change interrupt to trigger.
$ref: /schemas/types.yaml#/definitions/uint32
bias-pull-up:
description: |
Presence of this property indicates that the KPDPWR_N
pin should be configured for pull up.
description:
Presence of this property indicates that the KPDPWR_N pin should be
configured for pull up.
$ref: /schemas/types.yaml#/definitions/flag
wakeup-source:
description:
Button can wake-up the system. Only applicable for 'resin', 'pwrkey'
always wakes the system by default.
linux,code:
description: |
The input key-code associated with the power key.
Use the linux event codes defined in
include/dt-bindings/input/linux-event-codes.h
When property is omitted KEY_POWER is assumed.
description:
The input key-code associated with the power key. Use the linux event
codes defined in include/dt-bindings/input/linux-event-codes.h.
When property is omitted KEY_POWER is assumed.
allOf:
- $ref: input.yaml#
- if:
properties:
compatible:
contains:
enum:
- qcom,pm8941-pwrkey
- qcom,pmk8350-pwrkey
then:
properties:
wakeup-source: false
required:
- compatible

View File

@ -1,10 +0,0 @@
This binding is based on the matrix-keymap binding with the following
changes:
keypad,num-rows and keypad,num-columns are required.
Required properties:
- compatible: "ti,tca8418"
- reg: the I2C address
- interrupts: IRQ line number, should trigger on falling edge
- linux,keymap: Keys definitions, see keypad-matrix.

View File

@ -0,0 +1,61 @@
# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
%YAML 1.2
---
$id: http://devicetree.org/schemas/input/ti,tca8418.yaml#
$schema: http://devicetree.org/meta-schemas/core.yaml#
title: TI TCA8418 I2C/SMBus keypad scanner
maintainers:
- Frank Li <Frank.Li@nxp.com>
properties:
compatible:
enum:
- ti,tca8418
reg:
maxItems: 1
interrupts:
maxItems: 1
required:
- compatible
- reg
- interrupts
allOf:
- $ref: matrix-keymap.yaml#
unevaluatedProperties: false
examples:
- |
#include <dt-bindings/input/input.h>
#include <dt-bindings/interrupt-controller/irq.h>
i2c {
#address-cells = <1>;
#size-cells = <0>;
keypad@34 {
compatible = "ti,tca8418";
reg = <0x34>;
interrupt-parent = <&gpio5>;
interrupts = <11 IRQ_TYPE_EDGE_FALLING>;
keypad,num-rows = <4>;
keypad,num-columns = <4>;
linux,keymap = < MATRIX_KEY(0x00, 0x01, BTN_0)
MATRIX_KEY(0x00, 0x00, BTN_1)
MATRIX_KEY(0x01, 0x01, BTN_2)
MATRIX_KEY(0x01, 0x00, BTN_3)
MATRIX_KEY(0x02, 0x00, BTN_4)
MATRIX_KEY(0x00, 0x03, BTN_5)
MATRIX_KEY(0x00, 0x02, BTN_6)
MATRIX_KEY(0x01, 0x03, BTN_7)
MATRIX_KEY(0x01, 0x02, BTN_8)
MATRIX_KEY(0x02, 0x02, BTN_9)
>;
};
};

View File

@ -1,43 +0,0 @@
* Rohm BU21013 Touch Screen
Required properties:
- compatible : "rohm,bu21013_tp"
- reg : I2C device address
- reset-gpios : GPIO pin enabling (selecting) chip (CS)
- interrupt-parent : the phandle for the gpio controller
- interrupts : (gpio) interrupt to which the chip is connected
Optional properties:
- touch-gpios : GPIO pin registering a touch event
- <supply_name>-supply : Phandle to a regulator supply
- touchscreen-size-x : General touchscreen binding, see [1].
- touchscreen-size-y : General touchscreen binding, see [1].
- touchscreen-inverted-x : General touchscreen binding, see [1].
- touchscreen-inverted-y : General touchscreen binding, see [1].
- touchscreen-swapped-x-y : General touchscreen binding, see [1].
[1] All general touchscreen properties are described in
Documentation/devicetree/bindings/input/touchscreen/touchscreen.txt.
Deprecated properties:
- rohm,touch-max-x : Maximum outward permitted limit in the X axis
- rohm,touch-max-y : Maximum outward permitted limit in the Y axis
- rohm,flip-x : Flip touch coordinates on the X axis
- rohm,flip-y : Flip touch coordinates on the Y axis
Example:
i2c@80110000 {
bu21013_tp@5c {
compatible = "rohm,bu21013_tp";
reg = <0x5c>;
interrupt-parent = <&gpio2>;
interrupts <&20 IRQ_TYPE_LEVEL_LOW>;
touch-gpio = <&gpio2 20 GPIO_ACTIVE_LOW>;
avdd-supply = <&ab8500_ldo_aux1_reg>;
touchscreen-size-x = <384>;
touchscreen-size-y = <704>;
touchscreen-inverted-y;
};
};

View File

@ -9,27 +9,35 @@ title: EETI EXC3000 series touchscreen controller
maintainers:
- Dmitry Torokhov <dmitry.torokhov@gmail.com>
allOf:
- $ref: touchscreen.yaml#
properties:
compatible:
oneOf:
- const: eeti,exc3000
- const: eeti,exc80h60
- const: eeti,exc80h84
- const: eeti,egalax_ts # Do NOT use for new binding
- const: eeti,exc3000-i2c
deprecated: true
- items:
- enum:
- eeti,exc81w32
- const: eeti,exc80h84
reg:
const: 0x2a
enum: [0x4, 0xa, 0x2a]
interrupts:
maxItems: 1
reset-gpios:
maxItems: 1
wakeup-gpios:
maxItems: 1
vdd-supply:
description: Power supply regulator for the chip
attn-gpios:
deprecated: true
maxItems: 1
description: Phandle to a GPIO to check whether interrupt is still
latched. This is necessary for platforms that lack
support for level-triggered IRQs.
touchscreen-size-x: true
touchscreen-size-y: true
touchscreen-inverted-x: true
@ -40,11 +48,33 @@ required:
- compatible
- reg
- interrupts
- touchscreen-size-x
- touchscreen-size-y
additionalProperties: false
allOf:
- $ref: touchscreen.yaml#
- if:
properties:
compatible:
not:
contains:
enum:
- eeti,egalax_ts
- eeti,exc3000-i2c
then:
properties:
reg:
const: 0x2a
wakeup-gpios: false
attn-gpios: false
required:
- touchscreen-size-x
- touchscreen-size-y
examples:
- |
#include "dt-bindings/interrupt-controller/irq.h"

View File

@ -1,30 +0,0 @@
Bindings for EETI touchscreen controller
Required properties:
- compatible: should be "eeti,exc3000-i2c"
- reg: I2C address of the chip. Should be set to <0xa>
- interrupts: interrupt to which the chip is connected
Optional properties:
- attn-gpios: A handle to a GPIO to check whether interrupt is still
latched. This is necessary for platforms that lack
support for level-triggered IRQs.
The following optional properties described in touchscreen.txt are
also supported:
- touchscreen-inverted-x
- touchscreen-inverted-y
- touchscreen-swapped-x-y
Example:
i2c-master {
touchscreen@a {
compatible = "eeti,exc3000-i2c";
reg = <0xa>;
interrupt-parent = <&gpio>;
interrupts = <123 IRQ_TYPE_EDGE_RISING>;
attn-gpios = <&gpio 123 GPIO_ACTIVE_HIGH>;
};
};

View File

@ -1,18 +0,0 @@
* EETI eGalax Multiple Touch Controller
Required properties:
- compatible: must be "eeti,egalax_ts"
- reg: i2c slave address
- interrupts: touch controller interrupt
- wakeup-gpios: the gpio pin to be used for waking up the controller
and also used as irq pin
Example:
touchscreen@4 {
compatible = "eeti,egalax_ts";
reg = <0x04>;
interrupt-parent = <&gpio1>;
interrupts = <9 IRQ_TYPE_LEVEL_LOW>;
wakeup-gpios = <&gpio1 9 GPIO_ACTIVE_LOW>;
};

View File

@ -62,6 +62,20 @@ properties:
description: Number of data samples which are averaged for each read.
enum: [ 1, 4, 8, 16, 32 ]
debounce-delay-us:
description: |
Minimum duration in microseconds a signal must remain stable
to be considered valid.
Drivers must convert this value to IPG clock cycles and map
it to one of the four discrete thresholds exposed by the
TSC_DEBUG_MODE2 register:
0: 8191 IPG cycles
1: 4095 IPG cycles
2: 2047 IPG cycles
3: 1023 IPG cycles
required:
- compatible
- reg

View File

@ -62,7 +62,6 @@ additionalProperties: false
required:
- compatible
- reg
- interrupts
examples:
- |

View File

@ -0,0 +1,81 @@
# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
%YAML 1.2
---
$id: http://devicetree.org/schemas/input/touchscreen/himax,hx852es.yaml#
$schema: http://devicetree.org/meta-schemas/core.yaml#
title: Himax HX852x(ES) touch panel controller
maintainers:
- Stephan Gerhold <stephan@gerhold.net>
allOf:
- $ref: touchscreen.yaml#
properties:
compatible:
items:
- enum:
- himax,hx8525e
- himax,hx8526e
- himax,hx8527e
- const: himax,hx852es
reg:
maxItems: 1
interrupts:
maxItems: 1
description: Touch Screen Interrupt (TSIX), active low
reset-gpios:
maxItems: 1
description: External Reset (XRES), active low
vcca-supply:
description: Analog power supply (VCCA)
vccd-supply:
description: Digital power supply (VCCD)
touchscreen-inverted-x: true
touchscreen-inverted-y: true
touchscreen-size-x: true
touchscreen-size-y: true
touchscreen-swapped-x-y: true
linux,keycodes:
minItems: 1
maxItems: 4
required:
- compatible
- reg
- interrupts
- reset-gpios
additionalProperties: false
examples:
- |
#include <dt-bindings/gpio/gpio.h>
#include <dt-bindings/input/input.h>
#include <dt-bindings/interrupt-controller/irq.h>
i2c {
#address-cells = <1>;
#size-cells = <0>;
touchscreen@48 {
compatible = "himax,hx8527e", "himax,hx852es";
reg = <0x48>;
interrupt-parent = <&tlmm>;
interrupts = <13 IRQ_TYPE_LEVEL_LOW>;
reset-gpios = <&tlmm 12 GPIO_ACTIVE_LOW>;
vcca-supply = <&reg_ts_vcca>;
vccd-supply = <&pm8916_l6>;
linux,keycodes = <KEY_BACK KEY_HOMEPAGE KEY_APPSELECT>;
};
};
...

View File

@ -0,0 +1,65 @@
# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
%YAML 1.2
---
$id: http://devicetree.org/schemas/input/touchscreen/hynitron,cst816x.yaml#
$schema: http://devicetree.org/meta-schemas/core.yaml#
title: Hynitron CST816x Series Capacitive Touch controller
maintainers:
- Oleh Kuzhylnyi <kuzhylol@gmail.com>
description: |
Bindings for CST816x high performance self-capacitance touch chip series
with single point gesture and real two-point operation.
properties:
compatible:
enum:
- hynitron,cst816s
reg:
maxItems: 1
interrupts:
maxItems: 1
reset-gpios:
maxItems: 1
linux,keycodes:
minItems: 1
items:
- description: Slide up gesture
- description: Slide down gesture
- description: Slide left gesture
- description: Slide right gesture
- description: Long press gesture
required:
- compatible
- reg
- interrupts
additionalProperties: false
examples:
- |
#include <dt-bindings/gpio/gpio.h>
#include <dt-bindings/input/linux-event-codes.h>
#include <dt-bindings/interrupt-controller/irq.h>
i2c {
#address-cells = <1>;
#size-cells = <0>;
touchscreen@15 {
compatible = "hynitron,cst816s";
reg = <0x15>;
interrupt-parent = <&gpio0>;
interrupts = <4 IRQ_TYPE_EDGE_RISING>;
reset-gpios = <&gpio 17 GPIO_ACTIVE_LOW>;
linux,keycodes = <KEY_UP>, <KEY_DOWN>, <KEY_LEFT>, <KEY_RIGHT>,
<BTN_TOOL_TRIPLETAP>;
};
};
...

View File

@ -35,6 +35,7 @@ properties:
linux,keycodes:
description: Keycodes for the touch keys
minItems: 2
maxItems: 5
touchscreen-size-x: true
@ -87,5 +88,22 @@ examples:
touchscreen-inverted-y;
};
};
- |
#include <dt-bindings/input/linux-event-codes.h>
#include <dt-bindings/interrupt-controller/irq.h>
i2c {
#address-cells = <1>;
#size-cells = <0>;
touchscreen@50 {
compatible = "imagis,ist3032c";
reg = <0x50>;
interrupt-parent = <&gpio>;
interrupts = <72 IRQ_TYPE_EDGE_FALLING>;
vdd-supply = <&ldo2>;
touchscreen-size-x = <480>;
touchscreen-size-y = <800>;
linux,keycodes = <KEY_APPSELECT>, <KEY_BACK>;
};
};
...

View File

@ -1,17 +0,0 @@
* MAXI MAX11801 Resistive touch screen controller with i2c interface
Required properties:
- compatible: must be "maxim,max11801"
- reg: i2c slave address
- interrupts: touch controller interrupt
Example:
&i2c1 {
max11801: touchscreen@48 {
compatible = "maxim,max11801";
reg = <0x48>;
interrupt-parent = <&gpio3>;
interrupts = <31 IRQ_TYPE_EDGE_FALLING>;
};
};

View File

@ -0,0 +1,46 @@
# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
%YAML 1.2
---
$id: http://devicetree.org/schemas/input/touchscreen/maxim,max11801.yaml#
$schema: http://devicetree.org/meta-schemas/core.yaml#
title: MAXI MAX11801 Resistive touch screen controller with i2c interface
maintainers:
- Frank Li <Frank.Li@nxp.com>
properties:
compatible:
const: maxim,max11801
reg:
maxItems: 1
interrupts:
maxItems: 1
allOf:
- $ref: touchscreen.yaml
required:
- compatible
- reg
- interrupts
unevaluatedProperties: false
examples:
- |
#include <dt-bindings/interrupt-controller/irq.h>
i2c {
#address-cells = <1>;
#size-cells = <0>;
touchscreen@48 {
compatible = "maxim,max11801";
reg = <0x48>;
interrupt-parent = <&gpio3>;
interrupts = <31 IRQ_TYPE_EDGE_FALLING>;
};
};

View File

@ -1,26 +0,0 @@
Raspberry Pi firmware based 7" touchscreen
=====================================
Required properties:
- compatible: "raspberrypi,firmware-ts"
Optional properties:
- firmware: Reference to RPi's firmware device node
- touchscreen-size-x: See touchscreen.txt
- touchscreen-size-y: See touchscreen.txt
- touchscreen-inverted-x: See touchscreen.txt
- touchscreen-inverted-y: See touchscreen.txt
- touchscreen-swapped-x-y: See touchscreen.txt
Example:
firmware: firmware-rpi {
compatible = "raspberrypi,bcm2835-firmware";
mboxes = <&mailbox>;
ts: touchscreen {
compatible = "raspberrypi,firmware-ts";
touchscreen-size-x = <800>;
touchscreen-size-y = <480>;
};
};

View File

@ -55,7 +55,7 @@ properties:
touchscreen-min-pressure: true
touchscreen-x-plate-ohms: true
additionalProperties: false
unevaluatedProperties: false
required:
- compatible

View File

@ -0,0 +1,95 @@
# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
%YAML 1.2
---
$id: http://devicetree.org/schemas/input/touchscreen/rohm,bu21013.yaml#
$schema: http://devicetree.org/meta-schemas/core.yaml#
title: Rohm BU21013 touchscreen
description:
Rohm BU21013 I2C driven touchscreen controller.
maintainers:
- Dario Binacchi <dario.binacchi@amarulasolutions.com>
allOf:
- $ref: touchscreen.yaml#
properties:
compatible:
enum:
- rohm,bu21013_tp
reg:
maxItems: 1
interrupts:
maxItems: 1
reset-gpios:
maxItems: 1
touch-gpios:
maxItems: 1
description: GPIO registering a touch event.
avdd-supply:
description: Analogic power supply
rohm,touch-max-x:
deprecated: true
description: Maximum value on the X axis.
$ref: /schemas/types.yaml#/definitions/uint32
rohm,touch-max-y:
deprecated: true
description: Maximum value on the Y axis.
$ref: /schemas/types.yaml#/definitions/uint32
rohm,flip-x:
deprecated: true
description: Flip touch coordinates on the X axis
type: boolean
rohm,flip-y:
deprecated: true
description: Flip touch coordinates on the Y axis
type: boolean
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
- reset-gpios
- interrupts
examples:
- |
#include <dt-bindings/gpio/gpio.h>
#include <dt-bindings/interrupt-controller/irq.h>
i2c {
#address-cells = <1>;
#size-cells = <0>;
touchscreen@5c {
compatible = "rohm,bu21013_tp";
reg = <0x5c>;
interrupt-parent = <&gpio2>;
interrupts = <0x20 IRQ_TYPE_LEVEL_LOW>;
reset-gpios = <&gpio2 19 GPIO_ACTIVE_LOW>;
touch-gpios = <&gpio2 20 GPIO_ACTIVE_LOW>;
avdd-supply = <&ab8500_ldo_aux1_reg>;
touchscreen-size-x = <384>;
touchscreen-size-y = <704>;
touchscreen-inverted-y;
};
};

View File

@ -0,0 +1,52 @@
# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
%YAML 1.2
---
$id: http://devicetree.org/schemas/input/touchscreen/semtech,sx8654.yaml#
$schema: http://devicetree.org/meta-schemas/core.yaml#
title: Semtech SX8654 I2C Touchscreen Controller
maintainers:
- Frank Li <Frank.Li@nxp.com>
properties:
compatible:
enum:
- semtech,sx8650
- semtech,sx8654
- semtech,sx8655
- semtech,sx8656
reg:
maxItems: 1
interrupts:
maxItems: 1
reset-gpios:
maxItems: 1
required:
- compatible
- reg
- interrupts
additionalProperties: false
examples:
- |
#include <dt-bindings/gpio/gpio.h>
#include <dt-bindings/interrupt-controller/irq.h>
i2c {
#address-cells = <1>;
#size-cells = <0>;
touchscreen@48 {
compatible = "semtech,sx8654";
reg = <0x48>;
interrupt-parent = <&gpio6>;
interrupts = <3 IRQ_TYPE_EDGE_FALLING>;
reset-gpios = <&gpio4 2 GPIO_ACTIVE_LOW>;
};
};

View File

@ -1,23 +0,0 @@
* Semtech SX8654 I2C Touchscreen Controller
Required properties:
- compatible: must be one of the following, depending on the model:
"semtech,sx8650"
"semtech,sx8654"
"semtech,sx8655"
"semtech,sx8656"
- reg: i2c slave address
- interrupts: touch controller interrupt
Optional properties:
- reset-gpios: GPIO specification for the NRST input
Example:
sx8654@48 {
compatible = "semtech,sx8654";
reg = <0x48>;
interrupt-parent = <&gpio6>;
interrupts = <3 IRQ_TYPE_EDGE_FALLING>;
reset-gpios = <&gpio4 2 GPIO_ACTIVE_LOW>;
};

View File

@ -1,7 +1,7 @@
# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
%YAML 1.2
---
$id: http://devicetree.org/schemas/input/touchscreen/ti.tsc2007.yaml#
$id: http://devicetree.org/schemas/input/touchscreen/ti,tsc2007.yaml#
$schema: http://devicetree.org/meta-schemas/core.yaml#
title: Texas Instruments tsc2007 touchscreen controller
@ -26,6 +26,8 @@ properties:
pendown-gpio: true
wakeup-source: true
ti,max-rt:
$ref: /schemas/types.yaml#/definitions/uint32
description: maximum pressure.

View File

@ -1 +0,0 @@
See touchscreen.yaml

View File

@ -206,6 +206,10 @@ properties:
unevaluatedProperties: false
debounce-delay-us:
description: Minimum duration in microseconds a signal must remain stable
to be considered valid.
dependencies:
touchscreen-size-x: [ touchscreen-size-y ]
touchscreen-size-y: [ touchscreen-size-x ]

View File

@ -0,0 +1,62 @@
# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
%YAML 1.2
---
$id: http://devicetree.org/schemas/input/touchscreen/zeitec,zet6223.yaml#
$schema: http://devicetree.org/meta-schemas/core.yaml#
title: Zeitec ZET6223 touchscreen controller
description:
Zeitec ZET6223 I2C driven touchscreen controller.
maintainers:
- Dario Binacchi <dario.binacchi@amarulasolutions.com>
allOf:
- $ref: touchscreen.yaml#
properties:
compatible:
enum:
- zeitec,zet6223
reg:
maxItems: 1
interrupts:
maxItems: 1
vio-supply:
description: 1.8V or 3.3V VIO supply.
vcc-supply:
description: 3.3V VCC supply.
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
examples:
- |
#include <dt-bindings/gpio/gpio.h>
#include <dt-bindings/interrupt-controller/irq.h>
i2c {
#address-cells = <1>;
#size-cells = <0>;
touchscreen@76 {
compatible = "zeitec,zet6223";
reg = <0x76>;
interrupt-parent = <&pio>;
interrupts = <6 11 IRQ_TYPE_EDGE_FALLING>;
};
};

View File

@ -1,30 +0,0 @@
Zeitec ZET6223 I2C touchscreen controller
Required properties:
- compatible : "zeitec,zet6223"
- reg : I2C slave address of the chip (0x76)
- interrupts : interrupt specification for the zet6223 interrupt
Optional properties:
- vio-supply : Specification for VIO supply (1.8V or 3.3V,
depending on system interface needs).
- vcc-supply : Specification for 3.3V VCC supply.
- touchscreen-size-x : See touchscreen.txt
- touchscreen-size-y : See touchscreen.txt
- touchscreen-inverted-x : See touchscreen.txt
- touchscreen-inverted-y : See touchscreen.txt
- touchscreen-swapped-x-y : See touchscreen.txt
Example:
i2c@00000000 {
zet6223: touchscreen@76 {
compatible = "zeitec,zet6223";
reg = <0x76>;
interrupt-parent = <&pio>;
interrupts = <6 11 IRQ_TYPE_EDGE_FALLING>
};
};

View File

@ -11168,6 +11168,13 @@ S: Maintained
F: Documentation/devicetree/bindings/input/touchscreen/himax,hx83112b.yaml
F: drivers/input/touchscreen/himax_hx83112b.c
HIMAX HX852X TOUCHSCREEN DRIVER
M: Stephan Gerhold <stephan@gerhold.net>
L: linux-input@vger.kernel.org
S: Maintained
F: Documentation/devicetree/bindings/input/touchscreen/himax,hx852es.yaml
F: drivers/input/touchscreen/himax_hx852x.c
HIPPI
M: Jes Sorensen <jes@trained-monkey.org>
S: Maintained

View File

@ -8,9 +8,9 @@
/* #define DEBUG */
#include <linux/export.h>
#include <linux/input.h>
#include <linux/limits.h>
#include <linux/module.h>
#include <linux/mutex.h>
#include <linux/overflow.h>
#include <linux/sched.h>

View File

@ -10,6 +10,7 @@
#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
#include <linux/export.h>
#include <linux/slab.h>
#include <linux/input.h>
#include <linux/module.h>

View File

@ -9,6 +9,7 @@
#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
#include <linux/export.h>
#include <linux/stddef.h>
#include <linux/module.h>
#include <linux/io.h>

View File

@ -6,6 +6,7 @@
*/
#include <linux/export.h>
#include <linux/sprintf.h>
#include <linux/uaccess.h>
#include "input-compat.h"
@ -94,6 +95,28 @@ int input_ff_effect_from_user(const char __user *buffer, size_t size,
return 0;
}
int input_bits_to_string(char *buf, int buf_size, unsigned long bits,
bool skip_empty)
{
int len = 0;
if (in_compat_syscall()) {
u32 dword = bits >> 32;
if (dword || !skip_empty)
len += snprintf(buf, buf_size, "%x ", dword);
dword = bits & 0xffffffffUL;
if (dword || !skip_empty || len)
len += snprintf(buf + len, max(buf_size - len, 0),
"%x", dword);
} else {
if (bits || !skip_empty)
len += snprintf(buf, buf_size, "%lx", bits);
}
return len;
}
#else
int input_event_from_user(const char __user *buffer,
@ -126,6 +149,13 @@ int input_ff_effect_from_user(const char __user *buffer, size_t size,
return 0;
}
int input_bits_to_string(char *buf, int buf_size, unsigned long bits,
bool skip_empty)
{
return bits || !skip_empty ?
snprintf(buf, buf_size, "%lx", bits) : 0;
}
#endif /* CONFIG_COMPAT */
EXPORT_SYMBOL_GPL(input_event_from_user);

View File

@ -75,4 +75,7 @@ int input_event_to_user(char __user *buffer,
int input_ff_effect_from_user(const char __user *buffer, size_t size,
struct ff_effect *effect);
int input_bits_to_string(char *buf, int buf_size, unsigned long bits,
bool skip_empty);
#endif /* _INPUT_COMPAT_H */

View File

@ -4,6 +4,7 @@
*/
#include <linux/device.h>
#include <linux/export.h>
#include <linux/input.h>
#include <linux/jiffies.h>
#include <linux/mutex.h>

View File

@ -8,6 +8,7 @@
#define pr_fmt(fmt) KBUILD_BASENAME ": " fmt
#include <linux/export.h>
#include <linux/init.h>
#include <linux/types.h>
#include <linux/idr.h>
@ -998,41 +999,6 @@ static int input_attach_handler(struct input_dev *dev, struct input_handler *han
return error;
}
#ifdef CONFIG_COMPAT
static int input_bits_to_string(char *buf, int buf_size,
unsigned long bits, bool skip_empty)
{
int len = 0;
if (in_compat_syscall()) {
u32 dword = bits >> 32;
if (dword || !skip_empty)
len += snprintf(buf, buf_size, "%x ", dword);
dword = bits & 0xffffffffUL;
if (dword || !skip_empty || len)
len += snprintf(buf + len, max(buf_size - len, 0),
"%x", dword);
} else {
if (bits || !skip_empty)
len += snprintf(buf, buf_size, "%lx", bits);
}
return len;
}
#else /* !CONFIG_COMPAT */
static int input_bits_to_string(char *buf, int buf_size,
unsigned long bits, bool skip_empty)
{
return bits || !skip_empty ?
snprintf(buf, buf_size, "%lx", bits) : 0;
}
#endif
#ifdef CONFIG_PROC_FS
static struct proc_dir_entry *proc_bus_input_dir;

View File

@ -6,6 +6,7 @@
* USB/RS232 I-Force joysticks and wheels.
*/
#include <linux/export.h>
#include <linux/unaligned.h>
#include "iforce.h"

View File

@ -6,6 +6,7 @@
* USB/RS232 I-Force joysticks and wheels.
*/
#include <linux/export.h>
#include <linux/unaligned.h>
#include "iforce.h"

View File

@ -344,7 +344,11 @@ static int psxpad_spi_probe(struct spi_device *spi)
/* (PlayStation 1/2 joypad might be possible works 250kHz/500kHz) */
spi->controller->min_speed_hz = 125000;
spi->controller->max_speed_hz = 125000;
spi_setup(spi);
err = spi_setup(spi);
if (err) {
dev_err(&spi->dev, "failed to set up SPI: %d\n", err);
return err;
}
/* pad settings */
psxpad_set_motor_level(pad, 0, 0);

View File

@ -262,24 +262,6 @@ config KEYBOARD_GPIO_POLLED
To compile this driver as a module, choose M here: the
module will be called gpio_keys_polled.
config KEYBOARD_TCA6416
tristate "TCA6416/TCA6408A Keypad Support"
depends on I2C
help
This driver implements basic keypad functionality
for keys connected through TCA6416/TCA6408A IO expanders.
Say Y here if your device has keys connected to
TCA6416/TCA6408A IO expander. Your board-specific setup logic
must also provide pin-mask details(of which TCA6416 pins
are used for keypad).
If enabled the entire TCA6416 device will be managed through
this driver.
To compile this driver as a module, choose M here: the
module will be called tca6416_keypad.
config KEYBOARD_TCA8418
tristate "TCA8418 Keypad Support"
depends on I2C

View File

@ -23,7 +23,6 @@ obj-$(CONFIG_KEYBOARD_EP93XX) += ep93xx_keypad.o
obj-$(CONFIG_KEYBOARD_GOLDFISH_EVENTS) += goldfish_events.o
obj-$(CONFIG_KEYBOARD_GPIO) += gpio_keys.o
obj-$(CONFIG_KEYBOARD_GPIO_POLLED) += gpio_keys_polled.o
obj-$(CONFIG_KEYBOARD_TCA6416) += tca6416-keypad.o
obj-$(CONFIG_KEYBOARD_TCA8418) += tca8418_keypad.o
obj-$(CONFIG_KEYBOARD_HIL) += hil_kbd.o
obj-$(CONFIG_KEYBOARD_HIL_OLD) += hilkbd.o

View File

@ -12,7 +12,8 @@
* on some suggestions by Nicolas Pitre <nico@fluxnic.net>.
*/
#include <linux/bits.h>
#include <linux/bitfield.h>
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/interrupt.h>
@ -20,124 +21,148 @@
#include <linux/io.h>
#include <linux/device.h>
#include <linux/platform_device.h>
#include <linux/property.h>
#include <linux/clk.h>
#include <linux/err.h>
#include <linux/input/matrix_keypad.h>
#include <linux/slab.h>
#include <linux/of.h>
#include <linux/platform_data/keypad-pxa27x.h>
/*
* Keypad Controller registers
*/
#define KPC 0x0000 /* Keypad Control register */
#define KPDK 0x0008 /* Keypad Direct Key register */
#define KPREC 0x0010 /* Keypad Rotary Encoder register */
#define KPMK 0x0018 /* Keypad Matrix Key register */
#define KPAS 0x0020 /* Keypad Automatic Scan register */
#define KPC 0x0000 /* Keypad Control register */
#define KPDK 0x0008 /* Keypad Direct Key register */
#define KPREC 0x0010 /* Keypad Rotary Encoder register */
#define KPMK 0x0018 /* Keypad Matrix Key register */
#define KPAS 0x0020 /* Keypad Automatic Scan register */
/* Keypad Automatic Scan Multiple Key Presser register 0-3 */
#define KPASMKP0 0x0028
#define KPASMKP1 0x0030
#define KPASMKP2 0x0038
#define KPASMKP3 0x0040
#define KPKDI 0x0048
#define KPASMKP0 0x0028
#define KPASMKP1 0x0030
#define KPASMKP2 0x0038
#define KPASMKP3 0x0040
#define KPKDI 0x0048
/* bit definitions */
#define KPC_MKRN(n) ((((n) - 1) & 0x7) << 26) /* matrix key row number */
#define KPC_MKCN(n) ((((n) - 1) & 0x7) << 23) /* matrix key column number */
#define KPC_DKN(n) ((((n) - 1) & 0x7) << 6) /* direct key number */
#define KPC_MKRN_MASK GENMASK(28, 26)
#define KPC_MKCN_MASK GENMASK(25, 23)
#define KPC_DKN_MASK GENMASK(8, 6)
#define KPC_MKRN(n) FIELD_PREP(KPC_MKRN_MASK, (n) - 1)
#define KPC_MKCN(n) FIELD_PREP(KPC_MKCN_MASK, (n) - 1)
#define KPC_DKN(n) FIELD_PREP(KPC_DKN_MASK, (n) - 1)
#define KPC_AS (0x1 << 30) /* Automatic Scan bit */
#define KPC_ASACT (0x1 << 29) /* Automatic Scan on Activity */
#define KPC_MI (0x1 << 22) /* Matrix interrupt bit */
#define KPC_IMKP (0x1 << 21) /* Ignore Multiple Key Press */
#define KPC_AS BIT(30) /* Automatic Scan bit */
#define KPC_ASACT BIT(29) /* Automatic Scan on Activity */
#define KPC_MI BIT(22) /* Matrix interrupt bit */
#define KPC_IMKP BIT(21) /* Ignore Multiple Key Press */
#define KPC_MS(n) (0x1 << (13 + (n))) /* Matrix scan line 'n' */
#define KPC_MS_ALL (0xff << 13)
#define KPC_MS(n) BIT(13 + (n)) /* Matrix scan line 'n' */
#define KPC_MS_ALL GENMASK(20, 13)
#define KPC_ME (0x1 << 12) /* Matrix Keypad Enable */
#define KPC_MIE (0x1 << 11) /* Matrix Interrupt Enable */
#define KPC_DK_DEB_SEL (0x1 << 9) /* Direct Keypad Debounce Select */
#define KPC_DI (0x1 << 5) /* Direct key interrupt bit */
#define KPC_RE_ZERO_DEB (0x1 << 4) /* Rotary Encoder Zero Debounce */
#define KPC_REE1 (0x1 << 3) /* Rotary Encoder1 Enable */
#define KPC_REE0 (0x1 << 2) /* Rotary Encoder0 Enable */
#define KPC_DE (0x1 << 1) /* Direct Keypad Enable */
#define KPC_DIE (0x1 << 0) /* Direct Keypad interrupt Enable */
#define KPC_ME BIT(12) /* Matrix Keypad Enable */
#define KPC_MIE BIT(11) /* Matrix Interrupt Enable */
#define KPC_DK_DEB_SEL BIT(9) /* Direct Keypad Debounce Select */
#define KPC_DI BIT(5) /* Direct key interrupt bit */
#define KPC_RE_ZERO_DEB BIT(4) /* Rotary Encoder Zero Debounce */
#define KPC_REE1 BIT(3) /* Rotary Encoder1 Enable */
#define KPC_REE0 BIT(2) /* Rotary Encoder0 Enable */
#define KPC_DE BIT(1) /* Direct Keypad Enable */
#define KPC_DIE BIT(0) /* Direct Keypad interrupt Enable */
#define KPDK_DKP (0x1 << 31)
#define KPDK_DK(n) ((n) & 0xff)
#define KPDK_DKP BIT(31)
#define KPDK_DK_MASK GENMASK(7, 0)
#define KPDK_DK(n) FIELD_GET(KPDK_DK_MASK, n)
#define KPREC_OF1 (0x1 << 31)
#define kPREC_UF1 (0x1 << 30)
#define KPREC_OF0 (0x1 << 15)
#define KPREC_UF0 (0x1 << 14)
#define KPREC_OF1 BIT(31)
#define KPREC_UF1 BIT(30)
#define KPREC_OF0 BIT(15)
#define KPREC_UF0 BIT(14)
#define KPREC_RECOUNT0(n) ((n) & 0xff)
#define KPREC_RECOUNT1(n) (((n) >> 16) & 0xff)
#define KPREC_RECOUNT0_MASK GENMASK(7, 0)
#define KPREC_RECOUNT1_MASK GENMASK(23, 16)
#define KPREC_RECOUNT0(n) FIELD_GET(KPREC_RECOUNT0_MASK, n)
#define KPREC_RECOUNT1(n) FIELD_GET(KPREC_RECOUNT1_MASK, n)
#define KPMK_MKP (0x1 << 31)
#define KPAS_SO (0x1 << 31)
#define KPASMKPx_SO (0x1 << 31)
#define KPMK_MKP BIT(31)
#define KPAS_SO BIT(31)
#define KPASMKPx_SO BIT(31)
#define KPAS_MUKP(n) (((n) >> 26) & 0x1f)
#define KPAS_RP(n) (((n) >> 4) & 0xf)
#define KPAS_CP(n) ((n) & 0xf)
#define KPAS_MUKP_MASK GENMASK(30, 26)
#define KPAS_RP_MASK GENMASK(7, 4)
#define KPAS_CP_MASK GENMASK(3, 0)
#define KPAS_MUKP(n) FIELD_GET(KPAS_MUKP_MASK, n)
#define KPAS_RP(n) FIELD_GET(KPAS_RP_MASK, n)
#define KPAS_CP(n) FIELD_GET(KPAS_CP_MASK, n)
#define KPASMKP_MKC_MASK (0xff)
#define KPASMKP_MKC_MASK GENMASK(7, 0)
#define keypad_readl(off) __raw_readl(keypad->mmio_base + (off))
#define keypad_writel(off, v) __raw_writel((v), keypad->mmio_base + (off))
#define MAX_MATRIX_KEY_ROWS 8
#define MAX_MATRIX_KEY_COLS 8
#define MAX_DIRECT_KEY_NUM 8
#define MAX_ROTARY_ENCODERS 2
#define MAX_MATRIX_KEY_NUM (MAX_MATRIX_KEY_ROWS * MAX_MATRIX_KEY_COLS)
#define MAX_KEYPAD_KEYS (MAX_MATRIX_KEY_NUM + MAX_DIRECT_KEY_NUM)
struct pxa27x_keypad {
const struct pxa27x_keypad_platform_data *pdata;
struct pxa27x_keypad_rotary {
unsigned short *key_codes;
int rel_code;
bool enabled;
};
struct pxa27x_keypad {
struct clk *clk;
struct input_dev *input_dev;
void __iomem *mmio_base;
int irq;
unsigned short keycodes[MAX_KEYPAD_KEYS];
int rotary_rel_code[2];
unsigned int matrix_key_rows;
unsigned int matrix_key_cols;
unsigned int row_shift;
/* state row bits of each column scan */
uint32_t matrix_key_state[MAX_MATRIX_KEY_COLS];
uint32_t direct_key_state;
unsigned int direct_key_num;
unsigned int direct_key_mask;
bool direct_key_low_active;
/* key debounce interval */
unsigned int debounce_interval;
unsigned short keycodes[MAX_KEYPAD_KEYS];
/* state row bits of each column scan */
u32 matrix_key_state[MAX_MATRIX_KEY_COLS];
u32 direct_key_state;
struct pxa27x_keypad_rotary rotary[MAX_ROTARY_ENCODERS];
};
#ifdef CONFIG_OF
static int pxa27x_keypad_matrix_key_parse_dt(struct pxa27x_keypad *keypad,
struct pxa27x_keypad_platform_data *pdata)
static int pxa27x_keypad_matrix_key_parse(struct pxa27x_keypad *keypad)
{
struct input_dev *input_dev = keypad->input_dev;
struct device *dev = input_dev->dev.parent;
u32 rows, cols;
int error;
error = matrix_keypad_parse_properties(dev, &rows, &cols);
error = matrix_keypad_parse_properties(dev, &keypad->matrix_key_rows,
&keypad->matrix_key_cols);
if (error)
return error;
if (rows > MAX_MATRIX_KEY_ROWS || cols > MAX_MATRIX_KEY_COLS) {
if (keypad->matrix_key_rows > MAX_MATRIX_KEY_ROWS ||
keypad->matrix_key_cols > MAX_MATRIX_KEY_COLS) {
dev_err(dev, "rows or cols exceeds maximum value\n");
return -EINVAL;
}
pdata->matrix_key_rows = rows;
pdata->matrix_key_cols = cols;
keypad->row_shift = get_count_order(keypad->matrix_key_cols);
error = matrix_keypad_build_keymap(NULL, NULL,
pdata->matrix_key_rows,
pdata->matrix_key_cols,
keypad->matrix_key_rows,
keypad->matrix_key_cols,
keypad->keycodes, input_dev);
if (error)
return error;
@ -145,20 +170,17 @@ static int pxa27x_keypad_matrix_key_parse_dt(struct pxa27x_keypad *keypad,
return 0;
}
static int pxa27x_keypad_direct_key_parse_dt(struct pxa27x_keypad *keypad,
struct pxa27x_keypad_platform_data *pdata)
static int pxa27x_keypad_direct_key_parse(struct pxa27x_keypad *keypad)
{
struct input_dev *input_dev = keypad->input_dev;
struct device *dev = input_dev->dev.parent;
struct device_node *np = dev->of_node;
const __be16 *prop;
unsigned short code;
unsigned int proplen, size;
int count;
int i;
int error;
error = of_property_read_u32(np, "marvell,direct-key-count",
&pdata->direct_key_num);
error = device_property_read_u32(dev, "marvell,direct-key-count",
&keypad->direct_key_num);
if (error) {
/*
* If do not have marvel,direct-key-count defined,
@ -167,151 +189,121 @@ static int pxa27x_keypad_direct_key_parse_dt(struct pxa27x_keypad *keypad,
return error == -EINVAL ? 0 : error;
}
error = of_property_read_u32(np, "marvell,direct-key-mask",
&pdata->direct_key_mask);
error = device_property_read_u32(dev, "marvell,direct-key-mask",
&keypad->direct_key_mask);
if (error) {
if (error != -EINVAL)
return error;
/*
* If marvell,direct-key-mask is not defined, driver will use
* default value. Default value is set when configure the keypad.
* a default value based on number of direct keys set up.
* The default value is calculated in pxa27x_keypad_config().
*/
pdata->direct_key_mask = 0;
keypad->direct_key_mask = 0;
}
pdata->direct_key_low_active = of_property_read_bool(np,
"marvell,direct-key-low-active");
keypad->direct_key_low_active =
device_property_read_bool(dev, "marvell,direct-key-low-active");
prop = of_get_property(np, "marvell,direct-key-map", &proplen);
if (!prop)
count = device_property_count_u16(dev, "marvell,direct-key-map");
if (count <= 0 || count > MAX_DIRECT_KEY_NUM)
return -EINVAL;
if (proplen % sizeof(u16))
return -EINVAL;
error = device_property_read_u16_array(dev, "marvell,direct-key-map",
&keypad->keycodes[MAX_MATRIX_KEY_NUM],
count);
size = proplen / sizeof(u16);
/* Only MAX_DIRECT_KEY_NUM is accepted.*/
if (size > MAX_DIRECT_KEY_NUM)
return -EINVAL;
for (i = 0; i < size; i++) {
code = be16_to_cpup(prop + i);
keypad->keycodes[MAX_MATRIX_KEY_NUM + i] = code;
for (i = 0; i < count; i++) {
code = keypad->keycodes[MAX_MATRIX_KEY_NUM + i];
__set_bit(code, input_dev->keybit);
}
return 0;
}
static int pxa27x_keypad_rotary_parse_dt(struct pxa27x_keypad *keypad,
struct pxa27x_keypad_platform_data *pdata)
static int pxa27x_keypad_rotary_parse(struct pxa27x_keypad *keypad)
{
const __be32 *prop;
int i, relkey_ret;
unsigned int code, proplen;
const char *rotaryname[2] = {
"marvell,rotary0", "marvell,rotary1"};
const char relkeyname[] = {"marvell,rotary-rel-key"};
static const char * const rotaryname[] = { "marvell,rotary0", "marvell,rotary1" };
struct input_dev *input_dev = keypad->input_dev;
struct device *dev = input_dev->dev.parent;
struct device_node *np = dev->of_node;
struct pxa27x_keypad_rotary *encoder;
unsigned int code;
int i;
int error;
relkey_ret = of_property_read_u32(np, relkeyname, &code);
/* if can read correct rotary key-code, we do not need this. */
if (relkey_ret == 0) {
unsigned short relcode;
error = device_property_read_u32(dev, "marvell,rotary-rel-key", &code);
if (!error) {
for (i = 0; i < MAX_ROTARY_ENCODERS; i++, code >>= 16) {
encoder = &keypad->rotary[i];
encoder->enabled = true;
encoder->rel_code = code & 0xffff;
input_set_capability(input_dev, EV_REL, encoder->rel_code);
}
/* rotary0 taks lower half, rotary1 taks upper half. */
relcode = code & 0xffff;
pdata->rotary0_rel_code = (code & 0xffff);
__set_bit(relcode, input_dev->relbit);
relcode = code >> 16;
pdata->rotary1_rel_code = relcode;
__set_bit(relcode, input_dev->relbit);
return 0;
}
for (i = 0; i < 2; i++) {
prop = of_get_property(np, rotaryname[i], &proplen);
for (i = 0; i < MAX_ROTARY_ENCODERS; i++) {
encoder = &keypad->rotary[i];
/*
* If the prop is not set, it means keypad does not need
* initialize the rotaryX.
*/
if (!prop)
if (!device_property_present(dev, rotaryname[i]))
continue;
code = be32_to_cpup(prop);
error = device_property_read_u32(dev, rotaryname[i], &code);
if (error)
return error;
/*
* Not all up/down key code are valid.
* Now we depends on direct-rel-code.
*/
if ((!(code & 0xffff) || !(code >> 16)) && relkey_ret) {
return relkey_ret;
} else {
unsigned int n = MAX_MATRIX_KEY_NUM + (i << 1);
unsigned short keycode;
if (!(code & 0xffff) || !(code >> 16))
return -EINVAL;
keycode = code & 0xffff;
keypad->keycodes[n] = keycode;
__set_bit(keycode, input_dev->keybit);
encoder->enabled = true;
encoder->rel_code = -1;
encoder->key_codes = &keypad->keycodes[MAX_MATRIX_KEY_NUM + i * 2];
encoder->key_codes[0] = code & 0xffff;
encoder->key_codes[1] = code >> 16;
keycode = code >> 16;
keypad->keycodes[n + 1] = keycode;
__set_bit(keycode, input_dev->keybit);
if (i == 0)
pdata->rotary0_rel_code = -1;
else
pdata->rotary1_rel_code = -1;
}
if (i == 0)
pdata->enable_rotary0 = 1;
else
pdata->enable_rotary1 = 1;
input_set_capability(input_dev, EV_KEY, encoder->key_codes[0]);
input_set_capability(input_dev, EV_KEY, encoder->key_codes[1]);
}
keypad->rotary_rel_code[0] = pdata->rotary0_rel_code;
keypad->rotary_rel_code[1] = pdata->rotary1_rel_code;
return 0;
}
static int pxa27x_keypad_build_keycode_from_dt(struct pxa27x_keypad *keypad)
static int pxa27x_keypad_parse_properties(struct pxa27x_keypad *keypad)
{
struct input_dev *input_dev = keypad->input_dev;
struct device *dev = input_dev->dev.parent;
struct device_node *np = dev->of_node;
struct pxa27x_keypad_platform_data *pdata;
int error;
pdata = devm_kzalloc(dev, sizeof(*pdata), GFP_KERNEL);
if (!pdata) {
dev_err(dev, "failed to allocate memory for pdata\n");
return -ENOMEM;
}
error = pxa27x_keypad_matrix_key_parse_dt(keypad, pdata);
error = pxa27x_keypad_matrix_key_parse(keypad);
if (error) {
dev_err(dev, "failed to parse matrix key\n");
return error;
}
error = pxa27x_keypad_direct_key_parse_dt(keypad, pdata);
error = pxa27x_keypad_direct_key_parse(keypad);
if (error) {
dev_err(dev, "failed to parse direct key\n");
return error;
}
error = pxa27x_keypad_rotary_parse_dt(keypad, pdata);
error = pxa27x_keypad_rotary_parse(keypad);
if (error) {
dev_err(dev, "failed to parse rotary key\n");
return error;
}
error = of_property_read_u32(np, "marvell,debounce-interval",
&pdata->debounce_interval);
error = device_property_read_u32(dev, "marvell,debounce-interval",
&keypad->debounce_interval);
if (error) {
dev_err(dev, "failed to parse debounce-interval\n");
return error;
@ -323,95 +315,15 @@ static int pxa27x_keypad_build_keycode_from_dt(struct pxa27x_keypad *keypad)
*/
input_dev->keycodemax = ARRAY_SIZE(keypad->keycodes);
keypad->pdata = pdata;
return 0;
}
#else
static int pxa27x_keypad_build_keycode_from_dt(struct pxa27x_keypad *keypad)
{
dev_info(keypad->input_dev->dev.parent, "missing platform data\n");
return -EINVAL;
}
#endif
static int pxa27x_keypad_build_keycode(struct pxa27x_keypad *keypad)
{
const struct pxa27x_keypad_platform_data *pdata = keypad->pdata;
struct input_dev *input_dev = keypad->input_dev;
unsigned short keycode;
int i;
int error;
error = matrix_keypad_build_keymap(pdata->matrix_keymap_data, NULL,
pdata->matrix_key_rows,
pdata->matrix_key_cols,
keypad->keycodes, input_dev);
if (error)
return error;
/*
* The keycodes may not only include matrix keys but also the direct
* or rotary keys.
*/
input_dev->keycodemax = ARRAY_SIZE(keypad->keycodes);
/* For direct keys. */
for (i = 0; i < pdata->direct_key_num; i++) {
keycode = pdata->direct_key_map[i];
keypad->keycodes[MAX_MATRIX_KEY_NUM + i] = keycode;
__set_bit(keycode, input_dev->keybit);
}
if (pdata->enable_rotary0) {
if (pdata->rotary0_up_key && pdata->rotary0_down_key) {
keycode = pdata->rotary0_up_key;
keypad->keycodes[MAX_MATRIX_KEY_NUM + 0] = keycode;
__set_bit(keycode, input_dev->keybit);
keycode = pdata->rotary0_down_key;
keypad->keycodes[MAX_MATRIX_KEY_NUM + 1] = keycode;
__set_bit(keycode, input_dev->keybit);
keypad->rotary_rel_code[0] = -1;
} else {
keypad->rotary_rel_code[0] = pdata->rotary0_rel_code;
__set_bit(pdata->rotary0_rel_code, input_dev->relbit);
}
}
if (pdata->enable_rotary1) {
if (pdata->rotary1_up_key && pdata->rotary1_down_key) {
keycode = pdata->rotary1_up_key;
keypad->keycodes[MAX_MATRIX_KEY_NUM + 2] = keycode;
__set_bit(keycode, input_dev->keybit);
keycode = pdata->rotary1_down_key;
keypad->keycodes[MAX_MATRIX_KEY_NUM + 3] = keycode;
__set_bit(keycode, input_dev->keybit);
keypad->rotary_rel_code[1] = -1;
} else {
keypad->rotary_rel_code[1] = pdata->rotary1_rel_code;
__set_bit(pdata->rotary1_rel_code, input_dev->relbit);
}
}
__clear_bit(KEY_RESERVED, input_dev->keybit);
return 0;
}
static void pxa27x_keypad_scan_matrix(struct pxa27x_keypad *keypad)
{
const struct pxa27x_keypad_platform_data *pdata = keypad->pdata;
struct input_dev *input_dev = keypad->input_dev;
int row, col, num_keys_pressed = 0;
uint32_t new_state[MAX_MATRIX_KEY_COLS];
uint32_t kpas = keypad_readl(KPAS);
u32 new_state[MAX_MATRIX_KEY_COLS];
u32 kpas = keypad_readl(KPAS);
num_keys_pressed = KPAS_MUKP(kpas);
@ -425,19 +337,19 @@ static void pxa27x_keypad_scan_matrix(struct pxa27x_keypad *keypad)
row = KPAS_RP(kpas);
/* if invalid row/col, treat as no key pressed */
if (col >= pdata->matrix_key_cols ||
row >= pdata->matrix_key_rows)
if (col >= keypad->matrix_key_cols ||
row >= keypad->matrix_key_rows)
goto scan;
new_state[col] = (1 << row);
new_state[col] = BIT(row);
goto scan;
}
if (num_keys_pressed > 1) {
uint32_t kpasmkp0 = keypad_readl(KPASMKP0);
uint32_t kpasmkp1 = keypad_readl(KPASMKP1);
uint32_t kpasmkp2 = keypad_readl(KPASMKP2);
uint32_t kpasmkp3 = keypad_readl(KPASMKP3);
u32 kpasmkp0 = keypad_readl(KPASMKP0);
u32 kpasmkp1 = keypad_readl(KPASMKP1);
u32 kpasmkp2 = keypad_readl(KPASMKP2);
u32 kpasmkp3 = keypad_readl(KPASMKP3);
new_state[0] = kpasmkp0 & KPASMKP_MKC_MASK;
new_state[1] = (kpasmkp0 >> 16) & KPASMKP_MKC_MASK;
@ -449,23 +361,23 @@ static void pxa27x_keypad_scan_matrix(struct pxa27x_keypad *keypad)
new_state[7] = (kpasmkp3 >> 16) & KPASMKP_MKC_MASK;
}
scan:
for (col = 0; col < pdata->matrix_key_cols; col++) {
uint32_t bits_changed;
for (col = 0; col < keypad->matrix_key_cols; col++) {
u32 bits_changed;
int code;
bits_changed = keypad->matrix_key_state[col] ^ new_state[col];
if (bits_changed == 0)
continue;
for (row = 0; row < pdata->matrix_key_rows; row++) {
if ((bits_changed & (1 << row)) == 0)
for (row = 0; row < keypad->matrix_key_rows; row++) {
if ((bits_changed & BIT(row)) == 0)
continue;
code = MATRIX_SCAN_CODE(row, col, keypad->row_shift);
input_event(input_dev, EV_MSC, MSC_SCAN, code);
input_report_key(input_dev, keypad->keycodes[code],
new_state[col] & (1 << row));
new_state[col] & BIT(row));
}
}
input_sync(input_dev);
@ -474,7 +386,7 @@ scan:
#define DEFAULT_KPREC (0x007f007f)
static inline int rotary_delta(uint32_t kprec)
static inline int rotary_delta(u32 kprec)
{
if (kprec & KPREC_OF0)
return (kprec & 0xff) + 0x7f;
@ -486,14 +398,16 @@ static inline int rotary_delta(uint32_t kprec)
static void report_rotary_event(struct pxa27x_keypad *keypad, int r, int delta)
{
struct pxa27x_keypad_rotary *encoder = &keypad->rotary[r];
struct input_dev *dev = keypad->input_dev;
if (delta == 0)
if (!encoder->enabled || delta == 0)
return;
if (keypad->rotary_rel_code[r] == -1) {
int code = MAX_MATRIX_KEY_NUM + 2 * r + (delta > 0 ? 0 : 1);
unsigned char keycode = keypad->keycodes[code];
if (encoder->rel_code == -1) {
int idx = delta > 0 ? 0 : 1;
int code = MAX_MATRIX_KEY_NUM + 2 * r + idx;
unsigned char keycode = encoder->key_codes[idx];
/* simulate a press-n-release */
input_event(dev, EV_MSC, MSC_SCAN, code);
@ -503,45 +417,43 @@ static void report_rotary_event(struct pxa27x_keypad *keypad, int r, int delta)
input_report_key(dev, keycode, 0);
input_sync(dev);
} else {
input_report_rel(dev, keypad->rotary_rel_code[r], delta);
input_report_rel(dev, encoder->rel_code, delta);
input_sync(dev);
}
}
static void pxa27x_keypad_scan_rotary(struct pxa27x_keypad *keypad)
{
const struct pxa27x_keypad_platform_data *pdata = keypad->pdata;
uint32_t kprec;
u32 kprec;
int i;
/* read and reset to default count value */
kprec = keypad_readl(KPREC);
keypad_writel(KPREC, DEFAULT_KPREC);
if (pdata->enable_rotary0)
for (i = 0; i < MAX_ROTARY_ENCODERS; i++) {
report_rotary_event(keypad, 0, rotary_delta(kprec));
if (pdata->enable_rotary1)
report_rotary_event(keypad, 1, rotary_delta(kprec >> 16));
kprec >>= 16;
}
}
static void pxa27x_keypad_scan_direct(struct pxa27x_keypad *keypad)
{
const struct pxa27x_keypad_platform_data *pdata = keypad->pdata;
struct input_dev *input_dev = keypad->input_dev;
unsigned int new_state;
uint32_t kpdk, bits_changed;
u32 kpdk, bits_changed;
int i;
kpdk = keypad_readl(KPDK);
if (pdata->enable_rotary0 || pdata->enable_rotary1)
if (keypad->rotary[0].enabled || keypad->rotary[1].enabled)
pxa27x_keypad_scan_rotary(keypad);
/*
* The KPDR_DK only output the key pin level, so it relates to board,
* and low level may be active.
*/
if (pdata->direct_key_low_active)
if (keypad->direct_key_low_active)
new_state = ~KPDK_DK(kpdk) & keypad->direct_key_mask;
else
new_state = KPDK_DK(kpdk) & keypad->direct_key_mask;
@ -551,34 +463,24 @@ static void pxa27x_keypad_scan_direct(struct pxa27x_keypad *keypad)
if (bits_changed == 0)
return;
for (i = 0; i < pdata->direct_key_num; i++) {
if (bits_changed & (1 << i)) {
for (i = 0; i < keypad->direct_key_num; i++) {
if (bits_changed & BIT(i)) {
int code = MAX_MATRIX_KEY_NUM + i;
input_event(input_dev, EV_MSC, MSC_SCAN, code);
input_report_key(input_dev, keypad->keycodes[code],
new_state & (1 << i));
new_state & BIT(i));
}
}
input_sync(input_dev);
keypad->direct_key_state = new_state;
}
static void clear_wakeup_event(struct pxa27x_keypad *keypad)
{
const struct pxa27x_keypad_platform_data *pdata = keypad->pdata;
if (pdata->clear_wakeup_event)
(pdata->clear_wakeup_event)();
}
static irqreturn_t pxa27x_keypad_irq_handler(int irq, void *dev_id)
{
struct pxa27x_keypad *keypad = dev_id;
unsigned long kpc = keypad_readl(KPC);
clear_wakeup_event(keypad);
if (kpc & KPC_DI)
pxa27x_keypad_scan_direct(keypad);
@ -590,7 +492,6 @@ static irqreturn_t pxa27x_keypad_irq_handler(int irq, void *dev_id)
static void pxa27x_keypad_config(struct pxa27x_keypad *keypad)
{
const struct pxa27x_keypad_platform_data *pdata = keypad->pdata;
unsigned int mask = 0, direct_key_num = 0;
unsigned long kpc = 0;
@ -598,36 +499,34 @@ static void pxa27x_keypad_config(struct pxa27x_keypad *keypad)
keypad_readl(KPC);
/* enable matrix keys with automatic scan */
if (pdata->matrix_key_rows && pdata->matrix_key_cols) {
if (keypad->matrix_key_rows && keypad->matrix_key_cols) {
kpc |= KPC_ASACT | KPC_MIE | KPC_ME | KPC_MS_ALL;
kpc |= KPC_MKRN(pdata->matrix_key_rows) |
KPC_MKCN(pdata->matrix_key_cols);
kpc |= KPC_MKRN(keypad->matrix_key_rows) |
KPC_MKCN(keypad->matrix_key_cols);
}
/* enable rotary key, debounce interval same as direct keys */
if (pdata->enable_rotary0) {
if (keypad->rotary[0].enabled) {
mask |= 0x03;
direct_key_num = 2;
kpc |= KPC_REE0;
}
if (pdata->enable_rotary1) {
if (keypad->rotary[1].enabled) {
mask |= 0x0c;
direct_key_num = 4;
kpc |= KPC_REE1;
}
if (pdata->direct_key_num > direct_key_num)
direct_key_num = pdata->direct_key_num;
if (keypad->direct_key_num > direct_key_num)
direct_key_num = keypad->direct_key_num;
/*
* Direct keys usage may not start from KP_DKIN0, check the platfrom
* mask data to config the specific.
*/
if (pdata->direct_key_mask)
keypad->direct_key_mask = pdata->direct_key_mask;
else
keypad->direct_key_mask = ((1 << direct_key_num) - 1) & ~mask;
if (!keypad->direct_key_mask)
keypad->direct_key_mask = GENMASK(direct_key_num - 1, 0) & ~mask;
/* enable direct key */
if (direct_key_num)
@ -635,7 +534,7 @@ static void pxa27x_keypad_config(struct pxa27x_keypad *keypad)
keypad_writel(KPC, kpc | KPC_RE_ZERO_DEB);
keypad_writel(KPREC, DEFAULT_KPREC);
keypad_writel(KPKDI, pdata->debounce_interval);
keypad_writel(KPKDI, keypad->debounce_interval);
}
static int pxa27x_keypad_open(struct input_dev *dev)
@ -709,19 +608,12 @@ static int pxa27x_keypad_resume(struct device *dev)
static DEFINE_SIMPLE_DEV_PM_OPS(pxa27x_keypad_pm_ops,
pxa27x_keypad_suspend, pxa27x_keypad_resume);
static int pxa27x_keypad_probe(struct platform_device *pdev)
{
const struct pxa27x_keypad_platform_data *pdata =
dev_get_platdata(&pdev->dev);
struct device_node *np = pdev->dev.of_node;
struct pxa27x_keypad *keypad;
struct input_dev *input_dev;
int irq, error;
/* Driver need build keycode from device tree or pdata */
if (!np && !pdata)
return -EINVAL;
int irq;
int error;
irq = platform_get_irq(pdev, 0);
if (irq < 0)
@ -736,7 +628,6 @@ static int pxa27x_keypad_probe(struct platform_device *pdev)
if (!input_dev)
return -ENOMEM;
keypad->pdata = pdata;
keypad->input_dev = input_dev;
keypad->irq = irq;
@ -765,29 +656,12 @@ static int pxa27x_keypad_probe(struct platform_device *pdev)
input_dev->evbit[0] = BIT_MASK(EV_KEY) | BIT_MASK(EV_REP);
input_set_capability(input_dev, EV_MSC, MSC_SCAN);
if (pdata) {
error = pxa27x_keypad_build_keycode(keypad);
} else {
error = pxa27x_keypad_build_keycode_from_dt(keypad);
/*
* Data that we get from DT resides in dynamically
* allocated memory so we need to update our pdata
* pointer.
*/
pdata = keypad->pdata;
}
error = pxa27x_keypad_parse_properties(keypad);
if (error) {
dev_err(&pdev->dev, "failed to build keycode\n");
dev_err(&pdev->dev, "failed to parse keypad properties\n");
return error;
}
keypad->row_shift = get_count_order(pdata->matrix_key_cols);
if ((pdata->enable_rotary0 && keypad->rotary_rel_code[0] != -1) ||
(pdata->enable_rotary1 && keypad->rotary_rel_code[1] != -1)) {
input_dev->evbit[0] |= BIT_MASK(EV_REL);
}
error = devm_request_irq(&pdev->dev, irq, pxa27x_keypad_irq_handler,
0, pdev->name, keypad);
if (error) {

View File

@ -14,6 +14,7 @@
#include <linux/errno.h>
#include <linux/interrupt.h>
#include <linux/input.h>
#include <linux/input/matrix_keypad.h>
#include <linux/io.h>
#include <linux/irq.h>
#include <linux/kernel.h>
@ -22,7 +23,6 @@
#include <linux/platform_device.h>
#include <linux/slab.h>
#include <linux/types.h>
#include <linux/platform_data/keyboard-spear.h>
/* Keyboard Registers */
#define MODE_CTL_REG 0x00
@ -56,13 +56,12 @@ struct spear_kbd {
void __iomem *io_base;
struct clk *clk;
unsigned int irq;
unsigned int mode;
unsigned int suspended_rate;
u32 mode;
u32 suspended_rate;
u32 mode_ctl_reg;
unsigned short last_key;
unsigned short keycodes[NUM_ROWS * NUM_COLS];
bool rep;
bool irq_wake_enabled;
u32 mode_ctl_reg;
};
static irqreturn_t spear_kbd_interrupt(int irq, void *dev_id)
@ -143,46 +142,8 @@ static void spear_kbd_close(struct input_dev *dev)
kbd->last_key = KEY_RESERVED;
}
#ifdef CONFIG_OF
static int spear_kbd_parse_dt(struct platform_device *pdev,
struct spear_kbd *kbd)
{
struct device_node *np = pdev->dev.of_node;
int error;
u32 val, suspended_rate;
if (!np) {
dev_err(&pdev->dev, "Missing DT data\n");
return -EINVAL;
}
if (of_property_read_bool(np, "autorepeat"))
kbd->rep = true;
if (of_property_read_u32(np, "suspended_rate", &suspended_rate))
kbd->suspended_rate = suspended_rate;
error = of_property_read_u32(np, "st,mode", &val);
if (error) {
dev_err(&pdev->dev, "DT: Invalid or missing mode\n");
return error;
}
kbd->mode = val;
return 0;
}
#else
static inline int spear_kbd_parse_dt(struct platform_device *pdev,
struct spear_kbd *kbd)
{
return -ENOSYS;
}
#endif
static int spear_kbd_probe(struct platform_device *pdev)
{
struct kbd_platform_data *pdata = dev_get_platdata(&pdev->dev);
const struct matrix_keymap_data *keymap = pdata ? pdata->keymap : NULL;
struct spear_kbd *kbd;
struct input_dev *input_dev;
int irq;
@ -198,6 +159,14 @@ static int spear_kbd_probe(struct platform_device *pdev)
return -ENOMEM;
}
error = device_property_read_u32(&pdev->dev, "st,mode", &kbd->mode);
if (error) {
dev_err(&pdev->dev, "Invalid or missing mode\n");
return error;
}
device_property_read_u32(&pdev->dev, "suspended_rate", &kbd->suspended_rate);
input_dev = devm_input_allocate_device(&pdev->dev);
if (!input_dev) {
dev_err(&pdev->dev, "unable to allocate input device\n");
@ -207,16 +176,6 @@ static int spear_kbd_probe(struct platform_device *pdev)
kbd->input = input_dev;
kbd->irq = irq;
if (!pdata) {
error = spear_kbd_parse_dt(pdev, kbd);
if (error)
return error;
} else {
kbd->mode = pdata->mode;
kbd->rep = pdata->rep;
kbd->suspended_rate = pdata->suspended_rate;
}
kbd->io_base = devm_platform_get_and_ioremap_resource(pdev, 0, NULL);
if (IS_ERR(kbd->io_base))
return PTR_ERR(kbd->io_base);
@ -234,21 +193,21 @@ static int spear_kbd_probe(struct platform_device *pdev)
input_dev->open = spear_kbd_open;
input_dev->close = spear_kbd_close;
error = matrix_keypad_build_keymap(keymap, NULL, NUM_ROWS, NUM_COLS,
error = matrix_keypad_build_keymap(NULL, NULL, NUM_ROWS, NUM_COLS,
kbd->keycodes, input_dev);
if (error) {
dev_err(&pdev->dev, "Failed to build keymap\n");
return error;
}
if (kbd->rep)
if (device_property_read_bool(&pdev->dev, "autorepeat"))
__set_bit(EV_REP, input_dev->evbit);
input_set_capability(input_dev, EV_MSC, MSC_SCAN);
input_set_drvdata(input_dev, kbd);
error = devm_request_irq(&pdev->dev, irq, spear_kbd_interrupt, 0,
"keyboard", kbd);
"keyboard", kbd);
if (error) {
dev_err(&pdev->dev, "request_irq failed\n");
return error;

View File

@ -1,305 +0,0 @@
// SPDX-License-Identifier: GPL-2.0-only
/*
* Driver for keys on TCA6416 I2C IO expander
*
* Copyright (C) 2010 Texas Instruments
*
* Author : Sriramakrishnan.A.G. <srk@ti.com>
*/
#include <linux/types.h>
#include <linux/module.h>
#include <linux/init.h>
#include <linux/delay.h>
#include <linux/slab.h>
#include <linux/interrupt.h>
#include <linux/workqueue.h>
#include <linux/i2c.h>
#include <linux/input.h>
#include <linux/tca6416_keypad.h>
#define TCA6416_INPUT 0
#define TCA6416_OUTPUT 1
#define TCA6416_INVERT 2
#define TCA6416_DIRECTION 3
#define TCA6416_POLL_INTERVAL 100 /* msec */
static const struct i2c_device_id tca6416_id[] = {
{ "tca6416-keys", 16, },
{ "tca6408-keys", 8, },
{ }
};
MODULE_DEVICE_TABLE(i2c, tca6416_id);
struct tca6416_keypad_chip {
uint16_t reg_output;
uint16_t reg_direction;
uint16_t reg_input;
struct i2c_client *client;
struct input_dev *input;
int io_size;
u16 pinmask;
bool use_polling;
struct tca6416_button buttons[];
};
static int tca6416_write_reg(struct tca6416_keypad_chip *chip, int reg, u16 val)
{
int error;
error = chip->io_size > 8 ?
i2c_smbus_write_word_data(chip->client, reg << 1, val) :
i2c_smbus_write_byte_data(chip->client, reg, val);
if (error < 0) {
dev_err(&chip->client->dev,
"%s failed, reg: %d, val: %d, error: %d\n",
__func__, reg, val, error);
return error;
}
return 0;
}
static int tca6416_read_reg(struct tca6416_keypad_chip *chip, int reg, u16 *val)
{
int retval;
retval = chip->io_size > 8 ?
i2c_smbus_read_word_data(chip->client, reg << 1) :
i2c_smbus_read_byte_data(chip->client, reg);
if (retval < 0) {
dev_err(&chip->client->dev, "%s failed, reg: %d, error: %d\n",
__func__, reg, retval);
return retval;
}
*val = (u16)retval;
return 0;
}
static void tca6416_keys_scan(struct input_dev *input)
{
struct tca6416_keypad_chip *chip = input_get_drvdata(input);
u16 reg_val, val;
int error, i, pin_index;
error = tca6416_read_reg(chip, TCA6416_INPUT, &reg_val);
if (error)
return;
reg_val &= chip->pinmask;
/* Figure out which lines have changed */
val = reg_val ^ chip->reg_input;
chip->reg_input = reg_val;
for (i = 0, pin_index = 0; i < 16; i++) {
if (val & (1 << i)) {
struct tca6416_button *button = &chip->buttons[pin_index];
unsigned int type = button->type ?: EV_KEY;
int state = ((reg_val & (1 << i)) ? 1 : 0)
^ button->active_low;
input_event(input, type, button->code, !!state);
input_sync(input);
}
if (chip->pinmask & (1 << i))
pin_index++;
}
}
/*
* This is threaded IRQ handler and this can (and will) sleep.
*/
static irqreturn_t tca6416_keys_isr(int irq, void *dev_id)
{
tca6416_keys_scan(dev_id);
return IRQ_HANDLED;
}
static int tca6416_keys_open(struct input_dev *dev)
{
struct tca6416_keypad_chip *chip = input_get_drvdata(dev);
if (!chip->use_polling) {
/* Get initial device state in case it has switches */
tca6416_keys_scan(dev);
enable_irq(chip->client->irq);
}
return 0;
}
static void tca6416_keys_close(struct input_dev *dev)
{
struct tca6416_keypad_chip *chip = input_get_drvdata(dev);
if (!chip->use_polling)
disable_irq(chip->client->irq);
}
static int tca6416_setup_registers(struct tca6416_keypad_chip *chip)
{
int error;
error = tca6416_read_reg(chip, TCA6416_OUTPUT, &chip->reg_output);
if (error)
return error;
error = tca6416_read_reg(chip, TCA6416_DIRECTION, &chip->reg_direction);
if (error)
return error;
/* ensure that keypad pins are set to input */
error = tca6416_write_reg(chip, TCA6416_DIRECTION,
chip->reg_direction | chip->pinmask);
if (error)
return error;
error = tca6416_read_reg(chip, TCA6416_DIRECTION, &chip->reg_direction);
if (error)
return error;
error = tca6416_read_reg(chip, TCA6416_INPUT, &chip->reg_input);
if (error)
return error;
chip->reg_input &= chip->pinmask;
return 0;
}
static int tca6416_keypad_probe(struct i2c_client *client)
{
const struct i2c_device_id *id = i2c_client_get_device_id(client);
struct tca6416_keys_platform_data *pdata;
struct tca6416_keypad_chip *chip;
struct input_dev *input;
int error;
int i;
/* Check functionality */
if (!i2c_check_functionality(client->adapter, I2C_FUNC_SMBUS_BYTE)) {
dev_err(&client->dev, "%s adapter not supported\n",
dev_driver_string(&client->adapter->dev));
return -ENODEV;
}
pdata = dev_get_platdata(&client->dev);
if (!pdata) {
dev_dbg(&client->dev, "no platform data\n");
return -EINVAL;
}
chip = devm_kzalloc(&client->dev,
struct_size(chip, buttons, pdata->nbuttons),
GFP_KERNEL);
if (!chip)
return -ENOMEM;
input = devm_input_allocate_device(&client->dev);
if (!input)
return -ENOMEM;
chip->client = client;
chip->input = input;
chip->io_size = id->driver_data;
chip->pinmask = pdata->pinmask;
chip->use_polling = pdata->use_polling;
input->phys = "tca6416-keys/input0";
input->name = client->name;
input->open = tca6416_keys_open;
input->close = tca6416_keys_close;
input->id.bustype = BUS_HOST;
input->id.vendor = 0x0001;
input->id.product = 0x0001;
input->id.version = 0x0100;
/* Enable auto repeat feature of Linux input subsystem */
if (pdata->rep)
__set_bit(EV_REP, input->evbit);
for (i = 0; i < pdata->nbuttons; i++) {
unsigned int type;
chip->buttons[i] = pdata->buttons[i];
type = (pdata->buttons[i].type) ?: EV_KEY;
input_set_capability(input, type, pdata->buttons[i].code);
}
input_set_drvdata(input, chip);
/*
* Initialize cached registers from their original values.
* we can't share this chip with another i2c master.
*/
error = tca6416_setup_registers(chip);
if (error)
return error;
if (chip->use_polling) {
error = input_setup_polling(input, tca6416_keys_scan);
if (error) {
dev_err(&client->dev, "Failed to setup polling\n");
return error;
}
input_set_poll_interval(input, TCA6416_POLL_INTERVAL);
} else {
error = devm_request_threaded_irq(&client->dev, client->irq,
NULL, tca6416_keys_isr,
IRQF_TRIGGER_FALLING |
IRQF_ONESHOT |
IRQF_NO_AUTOEN,
"tca6416-keypad", input);
if (error) {
dev_dbg(&client->dev,
"Unable to claim irq %d; error %d\n",
client->irq, error);
return error;
}
}
error = input_register_device(input);
if (error) {
dev_dbg(&client->dev,
"Unable to register input device, error: %d\n", error);
return error;
}
i2c_set_clientdata(client, chip);
return 0;
}
static struct i2c_driver tca6416_keypad_driver = {
.driver = {
.name = "tca6416-keypad",
},
.probe = tca6416_keypad_probe,
.id_table = tca6416_id,
};
static int __init tca6416_keypad_init(void)
{
return i2c_add_driver(&tca6416_keypad_driver);
}
subsys_initcall(tca6416_keypad_init);
static void __exit tca6416_keypad_exit(void)
{
i2c_del_driver(&tca6416_keypad_driver);
}
module_exit(tca6416_keypad_exit);
MODULE_AUTHOR("Sriramakrishnan <srk@ti.com>");
MODULE_DESCRIPTION("Keypad driver over tca6416 IO expander");
MODULE_LICENSE("GPL");

View File

@ -373,18 +373,7 @@ static struct i2c_driver tca8418_keypad_driver = {
.probe = tca8418_keypad_probe,
.id_table = tca8418_id,
};
static int __init tca8418_keypad_init(void)
{
return i2c_add_driver(&tca8418_keypad_driver);
}
subsys_initcall(tca8418_keypad_init);
static void __exit tca8418_keypad_exit(void)
{
i2c_del_driver(&tca8418_keypad_driver);
}
module_exit(tca8418_keypad_exit);
module_i2c_driver(tca8418_keypad_driver);
MODULE_AUTHOR("Kyle Manna <kyle.manna@fuel7.com>");
MODULE_DESCRIPTION("Keypad driver for TCA8418");

View File

@ -28,10 +28,6 @@
* an internal state machine that decodes pressed keys, including
* multi-key combinations.
*
* This driver lets boards define what keycodes they wish to report for
* which scancodes, as part of the "struct twl4030_keypad_data" used in
* the probe() routine.
*
* See the TPS65950 documentation; that's the general availability
* version of the TWL5030 second generation part.
*/
@ -47,7 +43,6 @@
struct twl4030_keypad {
unsigned short keymap[TWL4030_KEYMAP_SIZE];
u16 kp_state[TWL4030_MAX_ROWS];
bool autorepeat;
unsigned int n_rows;
unsigned int n_cols;
int irq;
@ -322,8 +317,6 @@ static int twl4030_kp_program(struct twl4030_keypad *kp)
*/
static int twl4030_kp_probe(struct platform_device *pdev)
{
struct twl4030_keypad_data *pdata = dev_get_platdata(&pdev->dev);
const struct matrix_keymap_data *keymap_data = NULL;
struct twl4030_keypad *kp;
struct input_dev *input;
u8 reg;
@ -350,24 +343,10 @@ static int twl4030_kp_probe(struct platform_device *pdev)
input->id.product = 0x0001;
input->id.version = 0x0003;
if (pdata) {
if (!pdata->rows || !pdata->cols || !pdata->keymap_data) {
dev_err(&pdev->dev, "Missing platform_data\n");
return -EINVAL;
}
kp->n_rows = pdata->rows;
kp->n_cols = pdata->cols;
kp->autorepeat = pdata->rep;
keymap_data = pdata->keymap_data;
} else {
error = matrix_keypad_parse_properties(&pdev->dev, &kp->n_rows,
&kp->n_cols);
if (error)
return error;
kp->autorepeat = true;
}
error = matrix_keypad_parse_properties(&pdev->dev,
&kp->n_rows, &kp->n_cols);
if (error)
return error;
if (kp->n_rows > TWL4030_MAX_ROWS || kp->n_cols > TWL4030_MAX_COLS) {
dev_err(&pdev->dev,
@ -379,7 +358,7 @@ static int twl4030_kp_probe(struct platform_device *pdev)
if (kp->irq < 0)
return kp->irq;
error = matrix_keypad_build_keymap(keymap_data, NULL,
error = matrix_keypad_build_keymap(NULL, NULL,
TWL4030_MAX_ROWS,
1 << TWL4030_ROW_SHIFT,
kp->keymap, input);
@ -389,9 +368,7 @@ static int twl4030_kp_probe(struct platform_device *pdev)
}
input_set_capability(input, EV_MSC, MSC_SCAN);
/* Enable auto repeat feature of Linux input subsystem */
if (kp->autorepeat)
__set_bit(EV_REP, input->evbit);
__set_bit(EV_REP, input->evbit);
error = input_register_device(input);
if (error) {

View File

@ -126,6 +126,17 @@ config INPUT_ATMEL_CAPTOUCH
To compile this driver as a module, choose M here: the
module will be called atmel_captouch.
config INPUT_AW86927
tristate "Awinic AW86927 Haptic Driver Support"
depends on I2C && INPUT
select INPUT_FF_MEMLESS
select REGMAP_I2C
help
Say Y here if you have an Awinic AW86927 haptic chip.
To compile this driver as a module, choose M here: the
module will be called aw86927.
config INPUT_BBNSM_PWRKEY
tristate "NXP BBNSM Power Key Driver"
depends on ARCH_MXC || COMPILE_TEST

View File

@ -22,6 +22,7 @@ obj-$(CONFIG_INPUT_ATC260X_ONKEY) += atc260x-onkey.o
obj-$(CONFIG_INPUT_ATI_REMOTE2) += ati_remote2.o
obj-$(CONFIG_INPUT_ATLAS_BTNS) += atlas_btns.o
obj-$(CONFIG_INPUT_ATMEL_CAPTOUCH) += atmel_captouch.o
obj-$(CONFIG_INPUT_AW86927) += aw86927.o
obj-$(CONFIG_INPUT_BBNSM_PWRKEY) += nxp-bbnsm-pwrkey.o
obj-$(CONFIG_INPUT_BMA150) += bma150.o
obj-$(CONFIG_INPUT_CM109) += cm109.o

View File

@ -6,6 +6,7 @@
*/
#include <linux/device.h>
#include <linux/export.h>
#include <linux/input.h>
#include <linux/interrupt.h>
#include <linux/slab.h>

View File

@ -9,6 +9,7 @@
#include <linux/device.h>
#include <linux/delay.h>
#include <linux/export.h>
#include <linux/input.h>
#include <linux/interrupt.h>
#include <linux/irq.h>

View File

@ -0,0 +1,846 @@
// SPDX-License-Identifier: GPL-2.0-only
/*
* Copyright (C) 2025 Griffin Kroah-Hartman <griffin.kroah@fairphone.com>
*
* Partially based on vendor driver:
* Copyright (c) 2021 AWINIC Technology CO., LTD
*
*/
#include <linux/bitfield.h>
#include <linux/bits.h>
#include <linux/delay.h>
#include <linux/gpio/consumer.h>
#include <linux/i2c.h>
#include <linux/input.h>
#include <linux/module.h>
#include <linux/regmap.h>
#include <linux/regulator/consumer.h>
#include <linux/types.h>
#define AW86927_RSTCFG_REG 0x00
#define AW86927_RSTCFG_SOFTRST 0xaa
#define AW86927_SYSINT_REG 0x02
#define AW86927_SYSINT_BST_SCPI BIT(7)
#define AW86927_SYSINT_BST_OVPI BIT(6)
#define AW86927_SYSINT_UVLI BIT(5)
#define AW86927_SYSINT_FF_AEI BIT(4)
#define AW86927_SYSINT_FF_AFI BIT(3)
#define AW86927_SYSINT_OCDI BIT(2)
#define AW86927_SYSINT_OTI BIT(1)
#define AW86927_SYSINT_DONEI BIT(0)
#define AW86927_SYSINTM_REG 0x03
#define AW86927_SYSINTM_BST_OVPM BIT(6)
#define AW86927_SYSINTM_FF_AEM BIT(4)
#define AW86927_SYSINTM_FF_AFM BIT(3)
#define AW86927_SYSINTM_DONEM BIT(0)
#define AW86927_PLAYCFG1_REG 0x06
#define AW86927_PLAYCFG1_BST_MODE_MASK GENMASK(7, 7)
#define AW86927_PLAYCFG1_BST_MODE_BYPASS 0
#define AW86927_PLAYCFG1_BST_VOUT_VREFSET_MASK GENMASK(6, 0)
#define AW86927_PLAYCFG1_BST_8500MV 0x50
#define AW86927_PLAYCFG2_REG 0x07
#define AW86927_PLAYCFG3_REG 0x08
#define AW86927_PLAYCFG3_AUTO_BST_MASK GENMASK(4, 4)
#define AW86927_PLAYCFG3_AUTO_BST_ENABLE 1
#define AW86927_PLAYCFG3_AUTO_BST_DISABLE 0
#define AW86927_PLAYCFG3_PLAY_MODE_MASK GENMASK(1, 0)
#define AW86927_PLAYCFG3_PLAY_MODE_RAM 0
#define AW86927_PLAYCFG4_REG 0x09
#define AW86927_PLAYCFG4_STOP BIT(1)
#define AW86927_PLAYCFG4_GO BIT(0)
#define AW86927_WAVCFG1_REG 0x0a
#define AW86927_WAVCFG1_WAVSEQ1_MASK GENMASK(6, 0)
#define AW86927_WAVCFG2_REG 0x0b
#define AW86927_WAVCFG2_WAVSEQ2_MASK GENMASK(6, 0)
#define AW86927_WAVCFG9_REG 0x12
#define AW86927_WAVCFG9_SEQ1LOOP_MASK GENMASK(7, 4)
#define AW86927_WAVCFG9_SEQ1LOOP_INFINITELY 0x0f
#define AW86927_CONTCFG1_REG 0x18
#define AW86927_CONTCFG1_BRK_BST_MD_MASK GENMASK(6, 6)
#define AW86927_CONTCFG5_REG 0x1c
#define AW86927_CONTCFG5_BST_BRK_GAIN_MASK GENMASK(7, 4)
#define AW86927_CONTCFG5_BRK_GAIN_MASK GENMASK(3, 0)
#define AW86927_CONTCFG10_REG 0x21
#define AW86927_CONTCFG10_BRK_TIME_MASK GENMASK(7, 0)
#define AW86927_CONTCFG10_BRK_TIME_DEFAULT 8
#define AW86927_CONTCFG13_REG 0x24
#define AW86927_CONTCFG13_TSET_MASK GENMASK(7, 4)
#define AW86927_CONTCFG13_BEME_SET_MASK GENMASK(3, 0)
#define AW86927_BASEADDRH_REG 0x2d
#define AW86927_BASEADDRL_REG 0x2e
#define AW86927_GLBRD5_REG 0x3f
#define AW86927_GLBRD5_STATE_MASK GENMASK(3, 0)
#define AW86927_GLBRD5_STATE_STANDBY 0
#define AW86927_RAMADDRH_REG 0x40
#define AW86927_RAMADDRL_REG 0x41
#define AW86927_RAMDATA_REG 0x42
#define AW86927_SYSCTRL3_REG 0x45
#define AW86927_SYSCTRL3_STANDBY_MASK GENMASK(5, 5)
#define AW86927_SYSCTRL3_STANDBY_ON 1
#define AW86927_SYSCTRL3_STANDBY_OFF 0
#define AW86927_SYSCTRL3_EN_RAMINIT_MASK GENMASK(2, 2)
#define AW86927_SYSCTRL3_EN_RAMINIT_ON 1
#define AW86927_SYSCTRL3_EN_RAMINIT_OFF 0
#define AW86927_SYSCTRL4_REG 0x46
#define AW86927_SYSCTRL4_WAVDAT_MODE_MASK GENMASK(6, 5)
#define AW86927_SYSCTRL4_WAVDAT_24K 0
#define AW86927_SYSCTRL4_INT_EDGE_MODE_MASK GENMASK(4, 4)
#define AW86927_SYSCTRL4_INT_EDGE_MODE_POS 0
#define AW86927_SYSCTRL4_INT_MODE_MASK GENMASK(3, 3)
#define AW86927_SYSCTRL4_INT_MODE_EDGE 1
#define AW86927_SYSCTRL4_GAIN_BYPASS_MASK GENMASK(0, 0)
#define AW86927_PWMCFG1_REG 0x48
#define AW86927_PWMCFG1_PRC_EN_MASK GENMASK(7, 7)
#define AW86927_PWMCFG1_PRC_DISABLE 0
#define AW86927_PWMCFG3_REG 0x4a
#define AW86927_PWMCFG3_PR_EN_MASK GENMASK(7, 7)
#define AW86927_PWMCFG3_PRCTIME_MASK GENMASK(6, 0)
#define AW86927_PWMCFG4_REG 0x4b
#define AW86927_PWMCFG4_PRTIME_MASK GENMASK(7, 0)
#define AW86927_VBATCTRL_REG 0x4c
#define AW86927_VBATCTRL_VBAT_MODE_MASK GENMASK(6, 6)
#define AW86927_VBATCTRL_VBAT_MODE_SW 0
#define AW86927_DETCFG1_REG 0x4d
#define AW86927_DETCFG1_DET_GO_MASK GENMASK(1, 0)
#define AW86927_DETCFG1_DET_GO_DET_SEQ0 1
#define AW86927_DETCFG1_DET_GO_NA 0
#define AW86927_DETCFG2_REG 0x4e
#define AW86927_DETCFG2_DET_SEQ0_MASK GENMASK(6, 3)
#define AW86927_DETCFG2_DET_SEQ0_VBAT 0
#define AW86927_DETCFG2_D2S_GAIN_MASK GENMASK(2, 0)
#define AW86927_DETCFG2_D2S_GAIN_10 4
#define AW86927_CHIPIDH_REG 0x57
#define AW86927_CHIPIDL_REG 0x58
#define AW86927_CHIPID 0x9270
#define AW86927_TMCFG_REG 0x5b
#define AW86927_TMCFG_UNLOCK 0x7d
#define AW86927_TMCFG_LOCK 0x00
#define AW86927_ANACFG11_REG 0x70
#define AW86927_ANACFG12_REG 0x71
#define AW86927_ANACFG12_BST_SKIP_MASK GENMASK(7, 7)
#define AW86927_ANACFG12_BST_SKIP_SHUTDOWN 1
#define AW86927_ANACFG13_REG 0x72
#define AW86927_ANACFG13_BST_PC_MASK GENMASK(7, 4)
#define AW86927_ANACFG13_BST_PEAKCUR_3P45A 6
#define AW86927_ANACFG15_REG 0x74
#define AW86927_ANACFG15_BST_PEAK_MODE_MASK GENMASK(7, 7)
#define AW86927_ANACFG15_BST_PEAK_BACK 1
#define AW86927_ANACFG16_REG 0x75
#define AW86927_ANACFG16_BST_SRC_MASK GENMASK(4, 4)
#define AW86927_ANACFG16_BST_SRC_3NS 0
/* default value of base addr */
#define AW86927_RAM_BASE_ADDR 0x800
#define AW86927_BASEADDRH_VAL 0x08
#define AW86927_BASEADDRL_VAL 0x00
enum aw86927_work_mode {
AW86927_STANDBY_MODE,
AW86927_RAM_MODE,
};
struct aw86927_data {
struct work_struct play_work;
struct device *dev;
struct input_dev *input_dev;
struct i2c_client *client;
struct regmap *regmap;
struct gpio_desc *reset_gpio;
bool running;
};
static const struct regmap_config aw86927_regmap_config = {
.reg_bits = 8,
.val_bits = 8,
.cache_type = REGCACHE_NONE,
.max_register = 0x80,
};
/*
* Sine wave representing the magnitude of the drive to be used.
* Data is encoded in two's complement.
* round(84 * sin(x / 16.25))
*/
static const u8 aw86927_waveform[] = {
0x00, 0x05, 0x0a, 0x0f, 0x14, 0x1a, 0x1f, 0x23, 0x28, 0x2d, 0x31, 0x35,
0x39, 0x3d, 0x41, 0x44, 0x47, 0x4a, 0x4c, 0x4f, 0x51, 0x52, 0x53, 0x54,
0x55, 0x55, 0x55, 0x55, 0x55, 0x54, 0x52, 0x51, 0x4f, 0x4d, 0x4a, 0x47,
0x44, 0x41, 0x3d, 0x3a, 0x36, 0x31, 0x2d, 0x28, 0x24, 0x1f, 0x1a, 0x15,
0x10, 0x0a, 0x05, 0x00, 0xfc, 0xf6, 0xf1, 0xec, 0xe7, 0xe2, 0xdd, 0xd8,
0xd4, 0xcf, 0xcb, 0xc7, 0xc3, 0xbf, 0xbc, 0xb9, 0xb6, 0xb4, 0xb1, 0xb0,
0xae, 0xad, 0xac, 0xab, 0xab, 0xab, 0xab, 0xab, 0xac, 0xae, 0xaf, 0xb1,
0xb3, 0xb6, 0xb8, 0xbc, 0xbf, 0xc2, 0xc6, 0xca, 0xce, 0xd3, 0xd7, 0xdc,
0xe1, 0xe6, 0xeb, 0xf0, 0xf5, 0xfb
};
struct aw86927_sram_waveform_header {
u8 version;
__be16 start_address;
__be16 end_address;
} __packed;
static const struct aw86927_sram_waveform_header sram_waveform_header = {
.version = 0x01,
.start_address = cpu_to_be16(AW86927_RAM_BASE_ADDR +
sizeof(struct aw86927_sram_waveform_header)),
.end_address = cpu_to_be16(AW86927_RAM_BASE_ADDR +
sizeof(struct aw86927_sram_waveform_header) +
ARRAY_SIZE(aw86927_waveform) - 1),
};
static int aw86927_wait_enter_standby(struct aw86927_data *haptics)
{
unsigned int reg_val;
int err;
err = regmap_read_poll_timeout(haptics->regmap, AW86927_GLBRD5_REG, reg_val,
(FIELD_GET(AW86927_GLBRD5_STATE_MASK, reg_val) ==
AW86927_GLBRD5_STATE_STANDBY),
2500, 2500 * 100);
if (err) {
dev_err(haptics->dev, "did not enter standby: %d\n", err);
return err;
}
return 0;
}
static int aw86927_play_mode(struct aw86927_data *haptics, u8 play_mode)
{
int err;
switch (play_mode) {
case AW86927_STANDBY_MODE:
/* Briefly toggle standby, then toggle back to standby off */
err = regmap_update_bits(haptics->regmap,
AW86927_SYSCTRL3_REG,
AW86927_SYSCTRL3_STANDBY_MASK,
FIELD_PREP(AW86927_SYSCTRL3_STANDBY_MASK,
AW86927_SYSCTRL3_STANDBY_ON));
if (err)
return err;
err = regmap_update_bits(haptics->regmap,
AW86927_SYSCTRL3_REG,
AW86927_SYSCTRL3_STANDBY_MASK,
FIELD_PREP(AW86927_SYSCTRL3_STANDBY_MASK,
AW86927_SYSCTRL3_STANDBY_OFF));
if (err)
return err;
break;
case AW86927_RAM_MODE:
err = regmap_update_bits(haptics->regmap,
AW86927_PLAYCFG3_REG,
AW86927_PLAYCFG3_PLAY_MODE_MASK,
FIELD_PREP(AW86927_PLAYCFG3_PLAY_MODE_MASK,
AW86927_PLAYCFG3_PLAY_MODE_RAM));
if (err)
return err;
err = regmap_update_bits(haptics->regmap,
AW86927_PLAYCFG1_REG,
AW86927_PLAYCFG1_BST_MODE_MASK,
FIELD_PREP(AW86927_PLAYCFG1_BST_MODE_MASK,
AW86927_PLAYCFG1_BST_MODE_BYPASS));
if (err)
return err;
err = regmap_update_bits(haptics->regmap,
AW86927_VBATCTRL_REG,
AW86927_VBATCTRL_VBAT_MODE_MASK,
FIELD_PREP(AW86927_VBATCTRL_VBAT_MODE_MASK,
AW86927_VBATCTRL_VBAT_MODE_SW));
if (err)
return err;
break;
}
return 0;
}
static int aw86927_stop(struct aw86927_data *haptics)
{
int err;
err = regmap_write(haptics->regmap, AW86927_PLAYCFG4_REG, AW86927_PLAYCFG4_STOP);
if (err) {
dev_err(haptics->dev, "Failed to stop playback: %d\n", err);
return err;
}
err = aw86927_wait_enter_standby(haptics);
if (err) {
dev_err(haptics->dev, "Failed to enter standby, trying to force it\n");
err = aw86927_play_mode(haptics, AW86927_STANDBY_MODE);
if (err)
return err;
}
return 0;
}
static int aw86927_haptics_play(struct input_dev *dev, void *data, struct ff_effect *effect)
{
struct aw86927_data *haptics = input_get_drvdata(dev);
int level;
level = effect->u.rumble.strong_magnitude;
if (!level)
level = effect->u.rumble.weak_magnitude;
/* If already running, don't restart playback */
if (haptics->running && level)
return 0;
haptics->running = level;
schedule_work(&haptics->play_work);
return 0;
}
static int aw86927_play_sine(struct aw86927_data *haptics)
{
int err;
err = aw86927_stop(haptics);
if (err)
return err;
err = aw86927_play_mode(haptics, AW86927_RAM_MODE);
if (err)
return err;
err = regmap_update_bits(haptics->regmap, AW86927_PLAYCFG3_REG,
AW86927_PLAYCFG3_AUTO_BST_MASK,
FIELD_PREP(AW86927_PLAYCFG3_AUTO_BST_MASK,
AW86927_PLAYCFG3_AUTO_BST_ENABLE));
if (err)
return err;
/* Set waveseq 1 to the first wave */
err = regmap_update_bits(haptics->regmap, AW86927_WAVCFG1_REG,
AW86927_WAVCFG1_WAVSEQ1_MASK,
FIELD_PREP(AW86927_WAVCFG1_WAVSEQ1_MASK, 1));
if (err)
return err;
/* set wavseq 2 to zero */
err = regmap_update_bits(haptics->regmap, AW86927_WAVCFG2_REG,
AW86927_WAVCFG2_WAVSEQ2_MASK,
FIELD_PREP(AW86927_WAVCFG2_WAVSEQ2_MASK, 0));
if (err)
return err;
err = regmap_update_bits(haptics->regmap,
AW86927_WAVCFG9_REG,
AW86927_WAVCFG9_SEQ1LOOP_MASK,
FIELD_PREP(AW86927_WAVCFG9_SEQ1LOOP_MASK,
AW86927_WAVCFG9_SEQ1LOOP_INFINITELY));
if (err)
return err;
/* set gain to value lower than 0x80 to avoid distorted playback */
err = regmap_write(haptics->regmap, AW86927_PLAYCFG2_REG, 0x7c);
if (err)
return err;
/* Start playback */
err = regmap_write(haptics->regmap, AW86927_PLAYCFG4_REG, AW86927_PLAYCFG4_GO);
if (err)
return err;
return 0;
}
static void aw86927_close(struct input_dev *input)
{
struct aw86927_data *haptics = input_get_drvdata(input);
struct device *dev = &haptics->client->dev;
int err;
cancel_work_sync(&haptics->play_work);
err = aw86927_stop(haptics);
if (err)
dev_err(dev, "Failed to close the Driver: %d\n", err);
}
static void aw86927_haptics_play_work(struct work_struct *work)
{
struct aw86927_data *haptics =
container_of(work, struct aw86927_data, play_work);
struct device *dev = &haptics->client->dev;
int err;
if (haptics->running)
err = aw86927_play_sine(haptics);
else
err = aw86927_stop(haptics);
if (err)
dev_err(dev, "Failed to execute work command: %d\n", err);
}
static void aw86927_hw_reset(struct aw86927_data *haptics)
{
/* Assert reset */
gpiod_set_value_cansleep(haptics->reset_gpio, 1);
/* Wait ~1ms */
usleep_range(1000, 2000);
/* Deassert reset */
gpiod_set_value_cansleep(haptics->reset_gpio, 0);
/* Wait ~8ms until I2C is accessible */
usleep_range(8000, 8500);
}
static int aw86927_haptic_init(struct aw86927_data *haptics)
{
int err;
err = regmap_update_bits(haptics->regmap,
AW86927_SYSCTRL4_REG,
AW86927_SYSCTRL4_WAVDAT_MODE_MASK,
FIELD_PREP(AW86927_SYSCTRL4_WAVDAT_MODE_MASK,
AW86927_SYSCTRL4_WAVDAT_24K));
if (err)
return err;
/* enable gain bypass */
err = regmap_update_bits(haptics->regmap,
AW86927_SYSCTRL4_REG,
AW86927_SYSCTRL4_GAIN_BYPASS_MASK,
FIELD_PREP(AW86927_SYSCTRL4_GAIN_BYPASS_MASK,
0x01));
if (err)
return err;
err = regmap_write(haptics->regmap,
AW86927_TMCFG_REG, AW86927_TMCFG_UNLOCK);
if (err)
return err;
err = regmap_write(haptics->regmap, AW86927_ANACFG11_REG, 0x0f);
if (err)
return err;
err = regmap_update_bits(haptics->regmap,
AW86927_ANACFG12_REG,
AW86927_ANACFG12_BST_SKIP_MASK,
FIELD_PREP(AW86927_ANACFG12_BST_SKIP_MASK,
AW86927_ANACFG12_BST_SKIP_SHUTDOWN));
if (err)
return err;
err = regmap_update_bits(haptics->regmap,
AW86927_ANACFG15_REG,
AW86927_ANACFG15_BST_PEAK_MODE_MASK,
FIELD_PREP(AW86927_ANACFG15_BST_PEAK_MODE_MASK,
AW86927_ANACFG15_BST_PEAK_BACK));
if (err)
return err;
err = regmap_update_bits(haptics->regmap,
AW86927_ANACFG16_REG,
AW86927_ANACFG16_BST_SRC_MASK,
FIELD_PREP(AW86927_ANACFG16_BST_SRC_MASK,
AW86927_ANACFG16_BST_SRC_3NS));
if (err)
return err;
err = regmap_write(haptics->regmap,
AW86927_TMCFG_REG, AW86927_TMCFG_LOCK);
if (err)
return err;
err = regmap_update_bits(haptics->regmap,
AW86927_CONTCFG1_REG,
AW86927_CONTCFG1_BRK_BST_MD_MASK,
FIELD_PREP(AW86927_CONTCFG1_BRK_BST_MD_MASK, 0x00));
if (err)
return err;
err = regmap_write(haptics->regmap,
AW86927_CONTCFG5_REG,
FIELD_PREP(AW86927_CONTCFG5_BST_BRK_GAIN_MASK, 0x05) |
FIELD_PREP(AW86927_CONTCFG5_BRK_GAIN_MASK, 0x08));
if (err)
return err;
err = regmap_update_bits(haptics->regmap, AW86927_CONTCFG10_REG,
AW86927_CONTCFG10_BRK_TIME_MASK,
FIELD_PREP(AW86927_CONTCFG10_BRK_TIME_MASK,
AW86927_CONTCFG10_BRK_TIME_DEFAULT));
if (err)
return err;
err = regmap_write(haptics->regmap,
AW86927_CONTCFG13_REG,
FIELD_PREP(AW86927_CONTCFG13_TSET_MASK, 0x06) |
FIELD_PREP(AW86927_CONTCFG13_BEME_SET_MASK, 0x02));
if (err)
return err;
err = regmap_update_bits(haptics->regmap,
AW86927_DETCFG2_REG,
AW86927_DETCFG2_D2S_GAIN_MASK,
FIELD_PREP(AW86927_DETCFG2_D2S_GAIN_MASK,
AW86927_DETCFG2_D2S_GAIN_10));
if (err)
return err;
err = regmap_update_bits(haptics->regmap,
AW86927_PWMCFG1_REG,
AW86927_PWMCFG1_PRC_EN_MASK,
FIELD_PREP(AW86927_PWMCFG1_PRC_EN_MASK,
AW86927_PWMCFG1_PRC_DISABLE));
if (err)
return err;
err = regmap_write(haptics->regmap,
AW86927_PWMCFG3_REG,
FIELD_PREP(AW86927_PWMCFG3_PR_EN_MASK, 0x01) |
FIELD_PREP(AW86927_PWMCFG3_PRCTIME_MASK, 0x3f));
if (err)
return err;
err = regmap_update_bits(haptics->regmap,
AW86927_PWMCFG4_REG,
AW86927_PWMCFG4_PRTIME_MASK,
FIELD_PREP(AW86927_PWMCFG4_PRTIME_MASK, 0x32));
if (err)
return err;
err = regmap_write(haptics->regmap,
AW86927_TMCFG_REG, AW86927_TMCFG_UNLOCK);
if (err)
return err;
err = regmap_update_bits(haptics->regmap,
AW86927_ANACFG13_REG,
AW86927_ANACFG13_BST_PC_MASK,
FIELD_PREP(AW86927_ANACFG13_BST_PC_MASK,
AW86927_ANACFG13_BST_PEAKCUR_3P45A));
if (err)
return err;
err = regmap_write(haptics->regmap,
AW86927_TMCFG_REG, AW86927_TMCFG_LOCK);
if (err)
return err;
err = regmap_update_bits(haptics->regmap,
AW86927_PLAYCFG1_REG,
AW86927_PLAYCFG1_BST_VOUT_VREFSET_MASK,
FIELD_PREP(AW86927_PLAYCFG1_BST_VOUT_VREFSET_MASK,
AW86927_PLAYCFG1_BST_8500MV));
if (err)
return err;
err = regmap_update_bits(haptics->regmap,
AW86927_PLAYCFG3_REG,
AW86927_PLAYCFG3_AUTO_BST_MASK,
FIELD_PREP(AW86927_PLAYCFG3_AUTO_BST_MASK,
AW86927_PLAYCFG3_AUTO_BST_DISABLE));
if (err)
return err;
return 0;
}
static int aw86927_ram_init(struct aw86927_data *haptics)
{
int err;
err = aw86927_wait_enter_standby(haptics);
if (err)
return err;
/* Enable SRAM init */
err = regmap_update_bits(haptics->regmap,
AW86927_SYSCTRL3_REG,
AW86927_SYSCTRL3_EN_RAMINIT_MASK,
FIELD_PREP(AW86927_SYSCTRL3_EN_RAMINIT_MASK,
AW86927_SYSCTRL3_EN_RAMINIT_ON));
/* Set base address for the start of the SRAM waveforms */
err = regmap_write(haptics->regmap,
AW86927_BASEADDRH_REG, AW86927_BASEADDRH_VAL);
if (err)
return err;
err = regmap_write(haptics->regmap,
AW86927_BASEADDRL_REG, AW86927_BASEADDRL_VAL);
if (err)
return err;
/* Set start of SRAM, before the data is written it will be the same as the base */
err = regmap_write(haptics->regmap,
AW86927_RAMADDRH_REG, AW86927_BASEADDRH_VAL);
if (err)
return err;
err = regmap_write(haptics->regmap,
AW86927_RAMADDRL_REG, AW86927_BASEADDRL_VAL);
if (err)
return err;
/* Write waveform header to SRAM */
err = regmap_noinc_write(haptics->regmap, AW86927_RAMDATA_REG,
&sram_waveform_header, sizeof(sram_waveform_header));
if (err)
return err;
/* Write waveform to SRAM */
err = regmap_noinc_write(haptics->regmap, AW86927_RAMDATA_REG,
aw86927_waveform, ARRAY_SIZE(aw86927_waveform));
if (err)
return err;
err = regmap_update_bits(haptics->regmap,
AW86927_DETCFG2_REG,
AW86927_DETCFG2_DET_SEQ0_MASK,
FIELD_PREP(AW86927_DETCFG2_DET_SEQ0_MASK,
AW86927_DETCFG2_DET_SEQ0_VBAT));
if (err)
return err;
err = regmap_update_bits(haptics->regmap,
AW86927_DETCFG1_REG,
AW86927_DETCFG1_DET_GO_MASK,
FIELD_PREP(AW86927_DETCFG1_DET_GO_MASK,
AW86927_DETCFG1_DET_GO_DET_SEQ0));
if (err)
return err;
usleep_range(3000, 3500);
err = regmap_update_bits(haptics->regmap,
AW86927_DETCFG1_REG,
AW86927_DETCFG1_DET_GO_MASK,
FIELD_PREP(AW86927_DETCFG1_DET_GO_MASK,
AW86927_DETCFG1_DET_GO_NA));
if (err)
return err;
/* Disable SRAM init */
err = regmap_update_bits(haptics->regmap,
AW86927_SYSCTRL3_REG,
AW86927_SYSCTRL3_EN_RAMINIT_MASK,
FIELD_PREP(AW86927_SYSCTRL3_EN_RAMINIT_MASK,
AW86927_SYSCTRL3_EN_RAMINIT_OFF));
if (err)
return err;
return 0;
}
static irqreturn_t aw86927_irq(int irq, void *data)
{
struct aw86927_data *haptics = data;
struct device *dev = &haptics->client->dev;
unsigned int reg_val;
int err;
err = regmap_read(haptics->regmap, AW86927_SYSINT_REG, &reg_val);
if (err) {
dev_err(dev, "Failed to read SYSINT register: %d\n", err);
return IRQ_NONE;
}
if (reg_val & AW86927_SYSINT_BST_SCPI)
dev_err(dev, "Received a Short Circuit Protection interrupt\n");
if (reg_val & AW86927_SYSINT_BST_OVPI)
dev_err(dev, "Received an Over Voltage Protection interrupt\n");
if (reg_val & AW86927_SYSINT_UVLI)
dev_err(dev, "Received an Under Voltage Lock Out interrupt\n");
if (reg_val & AW86927_SYSINT_OCDI)
dev_err(dev, "Received an Over Current interrupt\n");
if (reg_val & AW86927_SYSINT_OTI)
dev_err(dev, "Received an Over Temperature interrupt\n");
if (reg_val & AW86927_SYSINT_DONEI)
dev_dbg(dev, "Chip playback done!\n");
if (reg_val & AW86927_SYSINT_FF_AFI)
dev_dbg(dev, "The RTP mode FIFO is almost full!\n");
if (reg_val & AW86927_SYSINT_FF_AEI)
dev_dbg(dev, "The RTP mode FIFO is almost empty!\n");
return IRQ_HANDLED;
}
static int aw86927_detect(struct aw86927_data *haptics)
{
__be16 read_buf;
u16 chip_id;
int err;
err = regmap_bulk_read(haptics->regmap, AW86927_CHIPIDH_REG, &read_buf, 2);
if (err)
return dev_err_probe(haptics->dev, err, "Failed to read CHIPID registers\n");
chip_id = be16_to_cpu(read_buf);
if (chip_id != AW86927_CHIPID) {
dev_err(haptics->dev, "Unexpected CHIPID value 0x%x\n", chip_id);
return -ENODEV;
}
return 0;
}
static int aw86927_probe(struct i2c_client *client)
{
struct aw86927_data *haptics;
int err;
haptics = devm_kzalloc(&client->dev, sizeof(struct aw86927_data), GFP_KERNEL);
if (!haptics)
return -ENOMEM;
haptics->dev = &client->dev;
haptics->client = client;
i2c_set_clientdata(client, haptics);
haptics->regmap = devm_regmap_init_i2c(client, &aw86927_regmap_config);
if (IS_ERR(haptics->regmap))
return dev_err_probe(haptics->dev, PTR_ERR(haptics->regmap),
"Failed to allocate register map\n");
haptics->input_dev = devm_input_allocate_device(haptics->dev);
if (!haptics->input_dev)
return -ENOMEM;
haptics->reset_gpio = devm_gpiod_get(haptics->dev, "reset", GPIOD_OUT_HIGH);
if (IS_ERR(haptics->reset_gpio))
return dev_err_probe(haptics->dev, PTR_ERR(haptics->reset_gpio),
"Failed to get reset gpio\n");
/* Hardware reset */
aw86927_hw_reset(haptics);
/* Software reset */
err = regmap_write(haptics->regmap, AW86927_RSTCFG_REG, AW86927_RSTCFG_SOFTRST);
if (err)
return dev_err_probe(haptics->dev, err, "Failed Software reset\n");
/* Wait ~3ms until I2C is accessible */
usleep_range(3000, 3500);
err = aw86927_detect(haptics);
if (err)
return dev_err_probe(haptics->dev, err, "Failed to find chip\n");
/* IRQ config */
err = regmap_write(haptics->regmap, AW86927_SYSCTRL4_REG,
FIELD_PREP(AW86927_SYSCTRL4_INT_MODE_MASK,
AW86927_SYSCTRL4_INT_MODE_EDGE) |
FIELD_PREP(AW86927_SYSCTRL4_INT_EDGE_MODE_MASK,
AW86927_SYSCTRL4_INT_EDGE_MODE_POS));
if (err)
return dev_err_probe(haptics->dev, err, "Failed to configure interrupt modes\n");
err = regmap_write(haptics->regmap, AW86927_SYSINTM_REG,
AW86927_SYSINTM_BST_OVPM |
AW86927_SYSINTM_FF_AEM |
AW86927_SYSINTM_FF_AFM |
AW86927_SYSINTM_DONEM);
if (err)
return dev_err_probe(haptics->dev, err, "Failed to configure interrupt masks\n");
err = devm_request_threaded_irq(haptics->dev, client->irq, NULL,
aw86927_irq, IRQF_ONESHOT, NULL, haptics);
if (err)
return dev_err_probe(haptics->dev, err, "Failed to request threaded irq\n");
INIT_WORK(&haptics->play_work, aw86927_haptics_play_work);
haptics->input_dev->name = "aw86927-haptics";
haptics->input_dev->close = aw86927_close;
input_set_drvdata(haptics->input_dev, haptics);
input_set_capability(haptics->input_dev, EV_FF, FF_RUMBLE);
err = input_ff_create_memless(haptics->input_dev, NULL, aw86927_haptics_play);
if (err)
return dev_err_probe(haptics->dev, err, "Failed to create FF dev\n");
/* Set up registers */
err = aw86927_play_mode(haptics, AW86927_STANDBY_MODE);
if (err)
return dev_err_probe(haptics->dev, err,
"Failed to enter standby for Haptic init\n");
err = aw86927_haptic_init(haptics);
if (err)
return dev_err_probe(haptics->dev, err, "Haptic init failed\n");
/* RAM init, upload the waveform for playback */
err = aw86927_ram_init(haptics);
if (err)
return dev_err_probe(haptics->dev, err, "Failed to init aw86927 sram\n");
err = input_register_device(haptics->input_dev);
if (err)
return dev_err_probe(haptics->dev, err, "Failed to register input device\n");
return 0;
}
static const struct of_device_id aw86927_of_id[] = {
{ .compatible = "awinic,aw86927" },
{ /* sentinel */ }
};
MODULE_DEVICE_TABLE(of, aw86927_of_id);
static struct i2c_driver aw86927_driver = {
.driver = {
.name = "aw86927-haptics",
.of_match_table = aw86927_of_id,
},
.probe = aw86927_probe,
};
module_i2c_driver(aw86927_driver);
MODULE_AUTHOR("Griffin Kroah-Hartman <griffin.kroah@fairphone.com>");
MODULE_DESCRIPTION("AWINIC AW86927 LRA Haptic Driver");
MODULE_LICENSE("GPL");

View File

@ -6,6 +6,7 @@
* Author: Hemanth V <hemanthv@ti.com>
*/
#include <linux/export.h>
#include <linux/types.h>
#include <linux/interrupt.h>
#include <linux/delay.h>

View File

@ -60,6 +60,7 @@ struct pm8941_data {
bool supports_ps_hold_poff_config;
bool supports_debounce_config;
bool has_pon_pbs;
bool wakeup_source_default;
const char *name;
const char *phys;
};
@ -245,7 +246,7 @@ static DEFINE_SIMPLE_DEV_PM_OPS(pm8941_pwr_key_pm_ops,
static int pm8941_pwrkey_probe(struct platform_device *pdev)
{
struct pm8941_pwrkey *pwrkey;
bool pull_up;
bool pull_up, wakeup;
struct device *parent;
struct device_node *regmap_node;
const __be32 *addr;
@ -402,8 +403,11 @@ static int pm8941_pwrkey_probe(struct platform_device *pdev)
}
}
wakeup = pwrkey->data->wakeup_source_default ||
of_property_read_bool(pdev->dev.of_node, "wakeup-source");
platform_set_drvdata(pdev, pwrkey);
device_init_wakeup(&pdev->dev, 1);
device_init_wakeup(&pdev->dev, wakeup);
return 0;
}
@ -424,6 +428,7 @@ static const struct pm8941_data pwrkey_data = {
.supports_ps_hold_poff_config = true,
.supports_debounce_config = true,
.has_pon_pbs = false,
.wakeup_source_default = true,
};
static const struct pm8941_data resin_data = {
@ -434,6 +439,7 @@ static const struct pm8941_data resin_data = {
.supports_ps_hold_poff_config = true,
.supports_debounce_config = true,
.has_pon_pbs = false,
.wakeup_source_default = false,
};
static const struct pm8941_data pon_gen3_pwrkey_data = {
@ -443,6 +449,7 @@ static const struct pm8941_data pon_gen3_pwrkey_data = {
.supports_ps_hold_poff_config = false,
.supports_debounce_config = false,
.has_pon_pbs = true,
.wakeup_source_default = true,
};
static const struct pm8941_data pon_gen3_resin_data = {
@ -452,6 +459,7 @@ static const struct pm8941_data pon_gen3_resin_data = {
.supports_ps_hold_poff_config = false,
.supports_debounce_config = false,
.has_pon_pbs = true,
.wakeup_source_default = false,
};
static const struct of_device_id pm8941_pwr_key_id_table[] = {

View File

@ -775,6 +775,7 @@ static int uinput_ff_upload_to_user(char __user *buffer,
if (in_compat_syscall()) {
struct uinput_ff_upload_compat ff_up_compat;
memset(&ff_up_compat, 0, sizeof(ff_up_compat));
ff_up_compat.request_id = ff_up->request_id;
ff_up_compat.retval = ff_up->retval;
/*

View File

@ -4,6 +4,7 @@
* Copyright (c) 2011 Unixphere
*/
#include <linux/export.h>
#include <linux/kernel.h>
#include <linux/device.h>
#include <linux/of.h>

View File

@ -7,6 +7,9 @@
#ifndef _RMI_2D_SENSOR_H
#define _RMI_2D_SENSOR_H
#include <linux/rmi.h>
#include <linux/types.h>
enum rmi_2d_sensor_object_type {
RMI_2D_OBJECT_NONE,
RMI_2D_OBJECT_FINGER,

View File

@ -4,6 +4,7 @@
* Copyright (c) 2011 Unixphere
*/
#include <linux/export.h>
#include <linux/kernel.h>
#include <linux/device.h>
#include <linux/irq.h>

View File

@ -21,6 +21,7 @@
#include <linux/irqdomain.h>
#include <uapi/linux/input.h>
#include <linux/rmi.h>
#include <linux/export.h>
#include "rmi_bus.h"
#include "rmi_driver.h"

View File

@ -54,6 +54,7 @@
#include <linux/hil_mlc.h>
#include <linux/errno.h>
#include <linux/export.h>
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/init.h>

View File

@ -63,6 +63,7 @@
#include <linux/hp_sdc.h>
#include <linux/errno.h>
#include <linux/export.h>
#include <linux/init.h>
#include <linux/module.h>
#include <linux/ioport.h>

View File

@ -10,6 +10,7 @@
#include <linux/types.h>
#include <linux/delay.h>
#include <linux/export.h>
#include <linux/module.h>
#include <linux/interrupt.h>
#include <linux/ioport.h>

View File

@ -8,6 +8,7 @@
#include <linux/delay.h>
#include <linux/export.h>
#include <linux/module.h>
#include <linux/sched.h>
#include <linux/interrupt.h>

View File

@ -50,7 +50,7 @@
* interrupt interval should be ~60us. Let's allow +/- 20us for frequency
* deviations and interrupt latency.
*
* The data line must be samples after ~30us to 50us after the falling edge,
* The data line must be sampled after ~30us to 50us after the falling edge,
* since the device updates the data line at the rising edge.
*
* ___ ______ ______ ______ ___

View File

@ -9,6 +9,7 @@
#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
#include <linux/export.h>
#include <linux/stddef.h>
#include <linux/module.h>
#include <linux/serio.h>

View File

@ -10,6 +10,7 @@
* Copyright (C) 2005 Dmitry Torokhov <dtor@mail.ru>
*/
#include <linux/export.h>
#include <linux/input.h>
#include <linux/input/sparse-keymap.h>
#include <linux/module.h>

View File

@ -5,6 +5,7 @@
* Copyright (c) 2023 Javier Carrasco <javier.carrasco@wolfvision.net>
*/
#include <linux/export.h>
#include <linux/input.h>
#include <linux/input/mt.h>
#include <linux/input/touch-overlay.h>

View File

@ -6,6 +6,7 @@
* Copyright (c) 2014 Sebastian Reichel <sre@kernel.org>
*/
#include <linux/export.h>
#include <linux/property.h>
#include <linux/input.h>
#include <linux/input/mt.h>

View File

@ -441,6 +441,16 @@ config TOUCHSCREEN_HIDEEP
To compile this driver as a module, choose M here : the
module will be called hideep_ts.
config TOUCHSCREEN_HIMAX_HX852X
tristate "Himax HX852x(ES) touchscreen"
depends on I2C
help
Say Y here if you have a Himax HX852x(ES) touchscreen.
If unsure, say N.
To compile this driver as a module, choose M here: the module
will be called himax_hx852x.
config TOUCHSCREEN_HYCON_HY46XX
tristate "Hycon hy46xx touchscreen support"
depends on I2C
@ -465,6 +475,18 @@ config TOUCHSCREEN_HYNITRON_CSTXXX
To compile this driver as a module, choose M here: the
module will be called hynitron-cstxxx.
config TOUCHSCREEN_HYNITRON_CST816X
tristate "Hynitron CST816x touchscreen"
depends on I2C
help
Say Y here if you have a touchscreen using a Hynitron
CST816x series touchscreen controller.
If unsure, say N.
To compile this driver as a module, choose M here: the
module will be called hynitron-cst816x.
config TOUCHSCREEN_ILI210X
tristate "Ilitek ILI210X based touchscreen"
depends on I2C

View File

@ -49,7 +49,9 @@ 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_HIMAX_HX852X) += himax_hx852x.o
obj-$(CONFIG_TOUCHSCREEN_HYNITRON_CSTXXX) += hynitron_cstxxx.o
obj-$(CONFIG_TOUCHSCREEN_HYNITRON_CST816X) += hynitron-cst816x.o
obj-$(CONFIG_TOUCHSCREEN_ILI210X) += ili210x.o
obj-$(CONFIG_TOUCHSCREEN_ILITEK) += ilitek_ts_i2c.o
obj-$(CONFIG_TOUCHSCREEN_IMAGIS) += imagis.o

View File

@ -22,6 +22,7 @@
#include <linux/device.h>
#include <linux/delay.h>
#include <linux/export.h>
#include <linux/input.h>
#include <linux/interrupt.h>
#include <linux/irq.h>

View File

@ -19,6 +19,7 @@
#include <linux/firmware.h>
#include <linux/i2c.h>
#include <linux/input/mt.h>
#include <linux/input/touchscreen.h>
#include <linux/interrupt.h>
#include <linux/irq.h>
#include <linux/of.h>
@ -355,6 +356,8 @@ struct mxt_data {
enum mxt_suspend_mode suspend_mode;
u32 wakeup_method;
struct touchscreen_properties prop;
};
struct mxt_vb2_buffer {
@ -888,8 +891,7 @@ static void mxt_proc_t9_message(struct mxt_data *data, u8 *message)
/* Touch active */
input_mt_report_slot_state(input_dev, MT_TOOL_FINGER, 1);
input_report_abs(input_dev, ABS_MT_POSITION_X, x);
input_report_abs(input_dev, ABS_MT_POSITION_Y, y);
touchscreen_report_pos(input_dev, &data->prop, x, y, true);
input_report_abs(input_dev, ABS_MT_PRESSURE, amplitude);
input_report_abs(input_dev, ABS_MT_TOUCH_MAJOR, area);
} else {
@ -1010,8 +1012,7 @@ static void mxt_proc_t100_message(struct mxt_data *data, u8 *message)
id, type, x, y, major, pressure, orientation);
input_mt_report_slot_state(input_dev, tool, 1);
input_report_abs(input_dev, ABS_MT_POSITION_X, x);
input_report_abs(input_dev, ABS_MT_POSITION_Y, y);
touchscreen_report_pos(input_dev, &data->prop, x, y, true);
input_report_abs(input_dev, ABS_MT_TOUCH_MAJOR, major);
input_report_abs(input_dev, ABS_MT_PRESSURE, pressure);
input_report_abs(input_dev, ABS_MT_DISTANCE, distance);
@ -2212,6 +2213,8 @@ static int mxt_initialize_input_device(struct mxt_data *data)
0, 255, 0, 0);
}
touchscreen_parse_properties(input_dev, true, &data->prop);
/* For T15 and T97 Key Array */
if (data->T15_reportid_min || data->T97_reportid_min) {
for (i = 0; i < data->t15_num_keys; i++)
@ -3317,7 +3320,7 @@ static int mxt_probe(struct i2c_client *client)
if (data->reset_gpio) {
/* Wait a while and then de-assert the RESET GPIO line */
msleep(MXT_RESET_GPIO_TIME);
gpiod_set_value(data->reset_gpio, 0);
gpiod_set_value_cansleep(data->reset_gpio, 0);
msleep(MXT_RESET_INVALID_CHG);
}

View File

@ -14,6 +14,7 @@
*/
#include <linux/delay.h>
#include <linux/export.h>
#include <linux/input.h>
#include <linux/input/mt.h>
#include <linux/input/touchscreen.h>

View File

@ -39,7 +39,6 @@ struct mx25_tcq_priv {
};
static const struct regmap_config mx25_tcq_regconfig = {
.fast_io = true,
.max_register = 0x5c,
.reg_bits = 32,
.val_bits = 32,

View File

@ -24,6 +24,7 @@
*/
#include <linux/bitfield.h>
#include <linux/export.h>
#include <linux/gpio/consumer.h>
#include <linux/input.h>
#include <linux/input/mt.h>

View File

@ -0,0 +1,503 @@
// SPDX-License-Identifier: GPL-2.0-only
/*
* Himax HX852x(ES) Touchscreen Driver
* Copyright (c) 2020-2024 Stephan Gerhold <stephan@gerhold.net>
* Copyright (c) 2020 Jonathan Albrieux <jonathan.albrieux@gmail.com>
*
* Based on the Himax Android Driver Sample Code Ver 0.3 for HMX852xES chipset:
* Copyright (c) 2014 Himax Corporation.
*/
#include <linux/delay.h>
#include <linux/gpio/consumer.h>
#include <linux/i2c.h>
#include <linux/input.h>
#include <linux/input/mt.h>
#include <linux/input/touchscreen.h>
#include <linux/interrupt.h>
#include <linux/kernel.h>
#include <linux/mod_devicetable.h>
#include <linux/module.h>
#include <linux/of.h>
#include <linux/regulator/consumer.h>
#include <linux/unaligned.h>
#define HX852X_COORD_SIZE(fingers) ((fingers) * sizeof(struct hx852x_coord))
#define HX852X_WIDTH_SIZE(fingers) ALIGN(fingers, 4)
#define HX852X_BUF_SIZE(fingers) (HX852X_COORD_SIZE(fingers) + \
HX852X_WIDTH_SIZE(fingers) + \
sizeof(struct hx852x_touch_info))
#define HX852X_MAX_FINGERS 12
#define HX852X_MAX_KEY_COUNT 4
#define HX852X_MAX_BUF_SIZE HX852X_BUF_SIZE(HX852X_MAX_FINGERS)
#define HX852X_TS_SLEEP_IN 0x80
#define HX852X_TS_SLEEP_OUT 0x81
#define HX852X_TS_SENSE_OFF 0x82
#define HX852X_TS_SENSE_ON 0x83
#define HX852X_READ_ONE_EVENT 0x85
#define HX852X_READ_ALL_EVENTS 0x86
#define HX852X_READ_LATEST_EVENT 0x87
#define HX852X_CLEAR_EVENT_STACK 0x88
#define HX852X_REG_SRAM_SWITCH 0x8c
#define HX852X_REG_SRAM_ADDR 0x8b
#define HX852X_REG_FLASH_RPLACE 0x5a
#define HX852X_SRAM_SWITCH_TEST_MODE 0x14
#define HX852X_SRAM_ADDR_CONFIG 0x7000
struct hx852x {
struct i2c_client *client;
struct input_dev *input_dev;
struct touchscreen_properties props;
struct gpio_desc *reset_gpiod;
struct regulator_bulk_data supplies[2];
unsigned int max_fingers;
unsigned int keycount;
unsigned int keycodes[HX852X_MAX_KEY_COUNT];
};
struct hx852x_config {
u8 rx_num;
u8 tx_num;
u8 max_pt;
u8 padding1[3];
__be16 x_res;
__be16 y_res;
u8 padding2[2];
} __packed __aligned(4);
struct hx852x_coord {
__be16 x;
__be16 y;
} __packed __aligned(4);
struct hx852x_touch_info {
u8 finger_num;
__le16 finger_pressed;
u8 padding;
} __packed __aligned(4);
static int hx852x_i2c_read(struct hx852x *hx, u8 cmd, void *data, u16 len)
{
struct i2c_client *client = hx->client;
int error;
int ret;
struct i2c_msg msg[] = {
{
.addr = client->addr,
.flags = 0,
.len = 1,
.buf = &cmd,
},
{
.addr = client->addr,
.flags = I2C_M_RD,
.len = len,
.buf = data,
},
};
ret = i2c_transfer(client->adapter, msg, ARRAY_SIZE(msg));
if (ret != ARRAY_SIZE(msg)) {
error = ret < 0 ? ret : -EIO;
dev_err(&client->dev, "failed to read %#x: %d\n", cmd, error);
return error;
}
return 0;
}
static int hx852x_power_on(struct hx852x *hx)
{
struct device *dev = &hx->client->dev;
int error;
error = regulator_bulk_enable(ARRAY_SIZE(hx->supplies), hx->supplies);
if (error) {
dev_err(dev, "failed to enable regulators: %d\n", error);
return error;
}
gpiod_set_value_cansleep(hx->reset_gpiod, 1);
msleep(20);
gpiod_set_value_cansleep(hx->reset_gpiod, 0);
msleep(50);
return 0;
}
static int hx852x_start(struct hx852x *hx)
{
struct device *dev = &hx->client->dev;
int error;
error = i2c_smbus_write_byte(hx->client, HX852X_TS_SLEEP_OUT);
if (error) {
dev_err(dev, "failed to send TS_SLEEP_OUT: %d\n", error);
return error;
}
msleep(30);
error = i2c_smbus_write_byte(hx->client, HX852X_TS_SENSE_ON);
if (error) {
dev_err(dev, "failed to send TS_SENSE_ON: %d\n", error);
return error;
}
msleep(20);
return 0;
}
static int hx852x_stop(struct hx852x *hx)
{
struct device *dev = &hx->client->dev;
int error;
error = i2c_smbus_write_byte(hx->client, HX852X_TS_SENSE_OFF);
if (error) {
dev_err(dev, "failed to send TS_SENSE_OFF: %d\n", error);
return error;
}
msleep(20);
error = i2c_smbus_write_byte(hx->client, HX852X_TS_SLEEP_IN);
if (error) {
dev_err(dev, "failed to send TS_SLEEP_IN: %d\n", error);
return error;
}
msleep(30);
return 0;
}
static int hx852x_power_off(struct hx852x *hx)
{
struct device *dev = &hx->client->dev;
int error;
error = regulator_bulk_disable(ARRAY_SIZE(hx->supplies), hx->supplies);
if (error) {
dev_err(dev, "failed to disable regulators: %d\n", error);
return error;
}
return 0;
}
static int hx852x_read_config(struct hx852x *hx)
{
struct device *dev = &hx->client->dev;
struct hx852x_config conf;
int x_res, y_res;
int error, error2;
error = hx852x_power_on(hx);
if (error)
return error;
/* Sensing must be turned on briefly to load the config */
error = hx852x_start(hx);
if (error)
goto err_power_off;
error = hx852x_stop(hx);
if (error)
goto err_power_off;
error = i2c_smbus_write_byte_data(hx->client, HX852X_REG_SRAM_SWITCH,
HX852X_SRAM_SWITCH_TEST_MODE);
if (error)
goto err_power_off;
error = i2c_smbus_write_word_data(hx->client, HX852X_REG_SRAM_ADDR,
HX852X_SRAM_ADDR_CONFIG);
if (error)
goto err_test_mode;
error = hx852x_i2c_read(hx, HX852X_REG_FLASH_RPLACE, &conf, sizeof(conf));
if (error)
goto err_test_mode;
x_res = be16_to_cpu(conf.x_res);
y_res = be16_to_cpu(conf.y_res);
hx->max_fingers = (conf.max_pt & 0xf0) >> 4;
dev_dbg(dev, "x res: %u, y res: %u, max fingers: %u\n",
x_res, y_res, hx->max_fingers);
if (hx->max_fingers > HX852X_MAX_FINGERS) {
dev_err(dev, "max supported fingers: %u, found: %u\n",
HX852X_MAX_FINGERS, hx->max_fingers);
error = -EINVAL;
goto err_test_mode;
}
if (x_res && y_res) {
input_set_abs_params(hx->input_dev, ABS_MT_POSITION_X, 0, x_res - 1, 0, 0);
input_set_abs_params(hx->input_dev, ABS_MT_POSITION_Y, 0, y_res - 1, 0, 0);
}
err_test_mode:
error2 = i2c_smbus_write_byte_data(hx->client, HX852X_REG_SRAM_SWITCH, 0);
error = error ?: error2;
err_power_off:
error2 = hx852x_power_off(hx);
return error ?: error2;
}
static int hx852x_handle_events(struct hx852x *hx)
{
/*
* The event packets have variable size, depending on the amount of
* supported fingers (hx->max_fingers). They are laid out as follows:
* - struct hx852x_coord[hx->max_fingers]: Coordinates for each finger
* - u8[ALIGN(hx->max_fingers, 4)]: Touch width for each finger
* with padding for 32-bit alignment
* - struct hx852x_touch_info
*
* Load everything into a 32-bit aligned buffer so the coordinates
* can be assigned directly, without using get_unaligned_*().
*/
u8 buf[HX852X_MAX_BUF_SIZE] __aligned(4);
struct hx852x_coord *coord = (struct hx852x_coord *)buf;
u8 *width = &buf[HX852X_COORD_SIZE(hx->max_fingers)];
struct hx852x_touch_info *info = (struct hx852x_touch_info *)
&width[HX852X_WIDTH_SIZE(hx->max_fingers)];
unsigned long finger_pressed, key_pressed;
unsigned int i, x, y, w;
int error;
error = hx852x_i2c_read(hx, HX852X_READ_ALL_EVENTS, buf,
HX852X_BUF_SIZE(hx->max_fingers));
if (error)
return error;
finger_pressed = get_unaligned_le16(&info->finger_pressed);
key_pressed = finger_pressed >> HX852X_MAX_FINGERS;
/* All bits are set when no touch is detected */
if (info->finger_num == 0xff || !(info->finger_num & 0x0f))
finger_pressed = 0;
if (key_pressed == 0xf)
key_pressed = 0;
for_each_set_bit(i, &finger_pressed, hx->max_fingers) {
x = be16_to_cpu(coord[i].x);
y = be16_to_cpu(coord[i].y);
w = width[i];
input_mt_slot(hx->input_dev, i);
input_mt_report_slot_state(hx->input_dev, MT_TOOL_FINGER, 1);
touchscreen_report_pos(hx->input_dev, &hx->props, x, y, true);
input_report_abs(hx->input_dev, ABS_MT_TOUCH_MAJOR, w);
}
input_mt_sync_frame(hx->input_dev);
for (i = 0; i < hx->keycount; i++)
input_report_key(hx->input_dev, hx->keycodes[i], key_pressed & BIT(i));
input_sync(hx->input_dev);
return 0;
}
static irqreturn_t hx852x_interrupt(int irq, void *ptr)
{
struct hx852x *hx = ptr;
int error;
error = hx852x_handle_events(hx);
if (error) {
dev_err_ratelimited(&hx->client->dev,
"failed to handle events: %d\n", error);
return IRQ_NONE;
}
return IRQ_HANDLED;
}
static int hx852x_input_open(struct input_dev *dev)
{
struct hx852x *hx = input_get_drvdata(dev);
int error;
error = hx852x_power_on(hx);
if (error)
return error;
error = hx852x_start(hx);
if (error) {
hx852x_power_off(hx);
return error;
}
enable_irq(hx->client->irq);
return 0;
}
static void hx852x_input_close(struct input_dev *dev)
{
struct hx852x *hx = input_get_drvdata(dev);
hx852x_stop(hx);
disable_irq(hx->client->irq);
hx852x_power_off(hx);
}
static int hx852x_parse_properties(struct hx852x *hx)
{
struct device *dev = &hx->client->dev;
int error, count;
count = device_property_count_u32(dev, "linux,keycodes");
if (count == -EINVAL) {
/* Property does not exist, keycodes are optional */
return 0;
} else if (count < 0) {
dev_err(dev, "Failed to read linux,keycodes: %d\n", count);
return count;
} else if (count > HX852X_MAX_KEY_COUNT) {
dev_err(dev, "max supported keys: %u, found: %u\n",
HX852X_MAX_KEY_COUNT, hx->keycount);
return -EINVAL;
}
hx->keycount = count;
error = device_property_read_u32_array(dev, "linux,keycodes",
hx->keycodes, hx->keycount);
if (error) {
dev_err(dev, "failed to read linux,keycodes: %d\n", error);
return error;
}
return 0;
}
static int hx852x_probe(struct i2c_client *client)
{
struct device *dev = &client->dev;
struct hx852x *hx;
int error, i;
if (!i2c_check_functionality(client->adapter, I2C_FUNC_I2C |
I2C_FUNC_SMBUS_WRITE_BYTE |
I2C_FUNC_SMBUS_WRITE_BYTE_DATA |
I2C_FUNC_SMBUS_WRITE_WORD_DATA)) {
dev_err(dev, "not all required i2c functionality supported\n");
return -ENXIO;
}
hx = devm_kzalloc(dev, sizeof(*hx), GFP_KERNEL);
if (!hx)
return -ENOMEM;
hx->client = client;
hx->input_dev = devm_input_allocate_device(dev);
if (!hx->input_dev)
return -ENOMEM;
hx->input_dev->name = "Himax HX852x";
hx->input_dev->id.bustype = BUS_I2C;
hx->input_dev->open = hx852x_input_open;
hx->input_dev->close = hx852x_input_close;
i2c_set_clientdata(client, hx);
input_set_drvdata(hx->input_dev, hx);
hx->supplies[0].supply = "vcca";
hx->supplies[1].supply = "vccd";
error = devm_regulator_bulk_get(dev, ARRAY_SIZE(hx->supplies), hx->supplies);
if (error)
return dev_err_probe(dev, error, "failed to get regulators\n");
hx->reset_gpiod = devm_gpiod_get(dev, "reset", GPIOD_OUT_HIGH);
if (IS_ERR(hx->reset_gpiod))
return dev_err_probe(dev, PTR_ERR(hx->reset_gpiod),
"failed to get reset gpio\n");
error = devm_request_threaded_irq(dev, client->irq, NULL, hx852x_interrupt,
IRQF_ONESHOT | IRQF_NO_AUTOEN, NULL, hx);
if (error)
return dev_err_probe(dev, error, "failed to request irq %d", client->irq);
error = hx852x_read_config(hx);
if (error)
return error;
input_set_capability(hx->input_dev, EV_ABS, ABS_MT_POSITION_X);
input_set_capability(hx->input_dev, EV_ABS, ABS_MT_POSITION_Y);
input_set_abs_params(hx->input_dev, ABS_MT_TOUCH_MAJOR, 0, 255, 0, 0);
touchscreen_parse_properties(hx->input_dev, true, &hx->props);
error = hx852x_parse_properties(hx);
if (error)
return error;
hx->input_dev->keycode = hx->keycodes;
hx->input_dev->keycodemax = hx->keycount;
hx->input_dev->keycodesize = sizeof(hx->keycodes[0]);
for (i = 0; i < hx->keycount; i++)
input_set_capability(hx->input_dev, EV_KEY, hx->keycodes[i]);
error = input_mt_init_slots(hx->input_dev, hx->max_fingers,
INPUT_MT_DIRECT | INPUT_MT_DROP_UNUSED);
if (error)
return dev_err_probe(dev, error, "failed to init MT slots\n");
error = input_register_device(hx->input_dev);
if (error)
return dev_err_probe(dev, error, "failed to register input device\n");
return 0;
}
static int hx852x_suspend(struct device *dev)
{
struct hx852x *hx = dev_get_drvdata(dev);
guard(mutex)(&hx->input_dev->mutex);
if (input_device_enabled(hx->input_dev))
return hx852x_stop(hx);
return 0;
}
static int hx852x_resume(struct device *dev)
{
struct hx852x *hx = dev_get_drvdata(dev);
guard(mutex)(&hx->input_dev->mutex);
if (input_device_enabled(hx->input_dev))
return hx852x_start(hx);
return 0;
}
static DEFINE_SIMPLE_DEV_PM_OPS(hx852x_pm_ops, hx852x_suspend, hx852x_resume);
#ifdef CONFIG_OF
static const struct of_device_id hx852x_of_match[] = {
{ .compatible = "himax,hx852es" },
{ }
};
MODULE_DEVICE_TABLE(of, hx852x_of_match);
#endif
static struct i2c_driver hx852x_driver = {
.probe = hx852x_probe,
.driver = {
.name = "himax_hx852x",
.pm = pm_sleep_ptr(&hx852x_pm_ops),
.of_match_table = of_match_ptr(hx852x_of_match),
},
};
module_i2c_driver(hx852x_driver);
MODULE_DESCRIPTION("Himax HX852x(ES) Touchscreen Driver");
MODULE_AUTHOR("Jonathan Albrieux <jonathan.albrieux@gmail.com>");
MODULE_AUTHOR("Stephan Gerhold <stephan@gerhold.net>");
MODULE_LICENSE("GPL");

View File

@ -0,0 +1,253 @@
// SPDX-License-Identifier: GPL-2.0-or-later
/*
* Driver for I2C connected Hynitron CST816x Series Touchscreen
*
* Copyright (C) 2025 Oleh Kuzhylnyi <kuzhylol@gmail.com>
*/
#include <linux/delay.h>
#include <linux/device.h>
#include <linux/err.h>
#include <linux/gpio/consumer.h>
#include <linux/i2c.h>
#include <linux/input.h>
#include <linux/unaligned.h>
#include <linux/interrupt.h>
#include <linux/module.h>
#define CST816X_RD_REG 0x01
#define CST816X_NUM_KEYS 5
struct cst816x_touch {
u8 gest;
u8 active;
u16 abs_x;
u16 abs_y;
} __packed;
struct cst816x_priv {
struct i2c_client *client;
struct gpio_desc *reset;
struct input_dev *input;
unsigned int keycode[CST816X_NUM_KEYS];
unsigned int keycodemax;
};
static int cst816x_parse_keycodes(struct device *dev, struct cst816x_priv *priv)
{
int count;
int error;
if (device_property_present(dev, "linux,keycodes")) {
count = device_property_count_u32(dev, "linux,keycodes");
if (count < 0) {
error = count;
dev_err(dev, "failed to count keys: %d\n", error);
return error;
} else if (count > ARRAY_SIZE(priv->keycode)) {
dev_err(dev, "too many keys defined: %d\n", count);
return -EINVAL;
}
priv->keycodemax = count;
error = device_property_read_u32_array(dev, "linux,keycodes",
priv->keycode,
priv->keycodemax);
if (error) {
dev_err(dev, "failed to read keycodes: %d\n", error);
return error;
}
}
return 0;
}
static int cst816x_i2c_read_register(struct cst816x_priv *priv, u8 reg,
void *buf, size_t len)
{
struct i2c_msg xfer[] = {
{
.addr = priv->client->addr,
.flags = 0,
.buf = &reg,
.len = sizeof(reg),
},
{
.addr = priv->client->addr,
.flags = I2C_M_RD,
.buf = buf,
.len = len,
},
};
int error;
int ret;
ret = i2c_transfer(priv->client->adapter, xfer, ARRAY_SIZE(xfer));
if (ret != ARRAY_SIZE(xfer)) {
error = ret < 0 ? ret : -EIO;
dev_err(&priv->client->dev, "i2c rx err: %d\n", error);
return error;
}
return 0;
}
static u8 cst816x_gest_idx(u8 gest)
{
u8 index;
switch (gest) {
case 0x01: /* Slide up gesture */
case 0x02: /* Slide down gesture */
case 0x03: /* Slide left gesture */
case 0x04: /* Slide right gesture */
index = gest;
break;
case 0x0c: /* Long press gesture */
default:
index = CST816X_NUM_KEYS;
break;
}
return index - 1;
}
static bool cst816x_process_touch(struct cst816x_priv *priv,
struct cst816x_touch *tch)
{
if (cst816x_i2c_read_register(priv, CST816X_RD_REG, tch, sizeof(*tch)))
return false;
tch->abs_x = get_unaligned_be16(&tch->abs_x) & GENMASK(11, 0);
tch->abs_y = get_unaligned_be16(&tch->abs_y) & GENMASK(11, 0);
dev_dbg(&priv->client->dev, "x: %u, y: %u, t: %u, g: 0x%x\n",
tch->abs_x, tch->abs_y, tch->active, tch->gest);
return true;
}
static int cst816x_register_input(struct cst816x_priv *priv)
{
priv->input = devm_input_allocate_device(&priv->client->dev);
if (!priv->input)
return -ENOMEM;
priv->input->name = "Hynitron CST816x Series Touchscreen";
priv->input->phys = "input/ts";
priv->input->id.bustype = BUS_I2C;
input_set_drvdata(priv->input, priv);
input_set_abs_params(priv->input, ABS_X, 0, 240, 0, 0);
input_set_abs_params(priv->input, ABS_Y, 0, 240, 0, 0);
input_set_capability(priv->input, EV_KEY, BTN_TOUCH);
priv->input->keycode = priv->keycode;
priv->input->keycodesize = sizeof(priv->keycode[0]);
priv->input->keycodemax = priv->keycodemax;
for (int i = 0; i < priv->keycodemax; i++) {
if (priv->keycode[i] == KEY_RESERVED)
continue;
input_set_capability(priv->input, EV_KEY, priv->keycode[i]);
}
return input_register_device(priv->input);
}
static void cst816x_reset(struct cst816x_priv *priv)
{
gpiod_set_value_cansleep(priv->reset, 1);
msleep(50);
gpiod_set_value_cansleep(priv->reset, 0);
msleep(100);
}
static irqreturn_t cst816x_irq_cb(int irq, void *cookie)
{
struct cst816x_priv *priv = cookie;
struct cst816x_touch tch;
if (!cst816x_process_touch(priv, &tch))
return IRQ_HANDLED;
input_report_abs(priv->input, ABS_X, tch.abs_x);
input_report_abs(priv->input, ABS_Y, tch.abs_y);
if (tch.gest)
input_report_key(priv->input,
priv->keycode[cst816x_gest_idx(tch.gest)],
tch.active);
input_report_key(priv->input, BTN_TOUCH, tch.active);
input_sync(priv->input);
return IRQ_HANDLED;
}
static int cst816x_probe(struct i2c_client *client)
{
struct device *dev = &client->dev;
struct cst816x_priv *priv;
int error;
priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL);
if (!priv)
return -ENOMEM;
priv->client = client;
priv->reset = devm_gpiod_get_optional(dev, "reset", GPIOD_OUT_HIGH);
if (IS_ERR(priv->reset))
return dev_err_probe(dev, PTR_ERR(priv->reset),
"gpio reset request failed\n");
if (priv->reset)
cst816x_reset(priv);
error = cst816x_parse_keycodes(dev, priv);
if (error)
dev_warn(dev, "no gestures found in dt\n");
error = cst816x_register_input(priv);
if (error)
return dev_err_probe(dev, error, "input register failed\n");
error = devm_request_threaded_irq(dev, client->irq,
NULL, cst816x_irq_cb, IRQF_ONESHOT,
dev_driver_string(dev), priv);
if (error)
return dev_err_probe(dev, error, "irq request failed\n");
return 0;
}
static const struct i2c_device_id cst816x_id[] = {
{ .name = "cst816s", 0 },
{ }
};
MODULE_DEVICE_TABLE(i2c, cst816x_id);
static const struct of_device_id cst816x_of_match[] = {
{ .compatible = "hynitron,cst816s", },
{ }
};
MODULE_DEVICE_TABLE(of, cst816x_of_match);
static struct i2c_driver cst816x_driver = {
.driver = {
.name = "cst816x",
.of_match_table = cst816x_of_match,
},
.id_table = cst816x_id,
.probe = cst816x_probe,
};
module_i2c_driver(cst816x_driver);
MODULE_AUTHOR("Oleh Kuzhylnyi <kuzhylol@gmail.com>");
MODULE_DESCRIPTION("Hynitron CST816x Series Touchscreen Driver");
MODULE_LICENSE("GPL");

View File

@ -7,6 +7,7 @@
#include <linux/errno.h>
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/bitfield.h>
#include <linux/gpio/consumer.h>
#include <linux/input.h>
#include <linux/slab.h>
@ -20,25 +21,23 @@
#include <linux/log2.h>
/* ADC configuration registers field define */
#define ADC_AIEN (0x1 << 7)
#define ADC_AIEN BIT(7)
#define ADC_ADCH_MASK GENMASK(4, 0)
#define ADC_CONV_DISABLE 0x1F
#define ADC_AVGE (0x1 << 5)
#define ADC_CAL (0x1 << 7)
#define ADC_CALF 0x2
#define ADC_12BIT_MODE (0x2 << 2)
#define ADC_CONV_MODE_MASK (0x3 << 2)
#define ADC_AVGE BIT(5)
#define ADC_CAL BIT(7)
#define ADC_CALF BIT(1)
#define ADC_CONV_MODE_MASK GENMASK(3, 2)
#define ADC_12BIT_MODE 0x2
#define ADC_IPG_CLK 0x00
#define ADC_INPUT_CLK_MASK 0x3
#define ADC_CLK_DIV_8 (0x03 << 5)
#define ADC_CLK_DIV_MASK (0x3 << 5)
#define ADC_SHORT_SAMPLE_MODE (0x0 << 4)
#define ADC_SAMPLE_MODE_MASK (0x1 << 4)
#define ADC_HARDWARE_TRIGGER (0x1 << 13)
#define ADC_AVGS_SHIFT 14
#define ADC_AVGS_MASK (0x3 << 14)
#define ADC_INPUT_CLK_MASK GENMASK(1, 0)
#define ADC_CLK_DIV_8 0x03
#define ADC_CLK_DIV_MASK GENMASK(6, 5)
#define ADC_SAMPLE_MODE BIT(4)
#define ADC_HARDWARE_TRIGGER BIT(13)
#define ADC_AVGS_MASK GENMASK(15, 14)
#define SELECT_CHANNEL_4 0x04
#define SELECT_CHANNEL_1 0x01
#define DISABLE_CONVERSION_INT (0x0 << 7)
/* ADC registers */
#define REG_ADC_HC0 0x00
@ -55,7 +54,7 @@
#define ADC_TIMEOUT msecs_to_jiffies(100)
/* TSC registers */
#define REG_TSC_BASIC_SETING 0x00
#define REG_TSC_BASIC_SETTING 0x00
#define REG_TSC_PRE_CHARGE_TIME 0x10
#define REG_TSC_FLOW_CONTROL 0x20
#define REG_TSC_MEASURE_VALUE 0x30
@ -65,19 +64,26 @@
#define REG_TSC_DEBUG_MODE 0x70
#define REG_TSC_DEBUG_MODE2 0x80
/* TSC_MEASURE_VALUE register field define */
#define X_VALUE_MASK GENMASK(27, 16)
#define Y_VALUE_MASK GENMASK(11, 0)
/* TSC configuration registers field define */
#define DETECT_4_WIRE_MODE (0x0 << 4)
#define AUTO_MEASURE 0x1
#define MEASURE_SIGNAL 0x1
#define DETECT_SIGNAL (0x1 << 4)
#define VALID_SIGNAL (0x1 << 8)
#define MEASURE_INT_EN 0x1
#define MEASURE_SIG_EN 0x1
#define VALID_SIG_EN (0x1 << 8)
#define DE_GLITCH_2 (0x2 << 29)
#define START_SENSE (0x1 << 12)
#define TSC_DISABLE (0x1 << 16)
#define MEASURE_DELAY_TIME_MASK GENMASK(31, 8)
#define DETECT_5_WIRE_MODE BIT(4)
#define AUTO_MEASURE BIT(0)
#define MEASURE_SIGNAL BIT(0)
#define DETECT_SIGNAL BIT(4)
#define VALID_SIGNAL BIT(8)
#define MEASURE_INT_EN BIT(0)
#define MEASURE_SIG_EN BIT(0)
#define VALID_SIG_EN BIT(8)
#define DE_GLITCH_MASK GENMASK(30, 29)
#define DE_GLITCH_DEF 0x02
#define START_SENSE BIT(12)
#define TSC_DISABLE BIT(16)
#define DETECT_MODE 0x2
#define STATE_MACHINE_MASK GENMASK(22, 20)
struct imx6ul_tsc {
struct device *dev;
@ -92,6 +98,7 @@ struct imx6ul_tsc {
u32 pre_charge_time;
bool average_enable;
u32 average_select;
u32 de_glitch;
struct completion completion;
};
@ -112,19 +119,20 @@ static int imx6ul_adc_init(struct imx6ul_tsc *tsc)
adc_cfg = readl(tsc->adc_regs + REG_ADC_CFG);
adc_cfg &= ~(ADC_CONV_MODE_MASK | ADC_INPUT_CLK_MASK);
adc_cfg |= ADC_12BIT_MODE | ADC_IPG_CLK;
adc_cfg &= ~(ADC_CLK_DIV_MASK | ADC_SAMPLE_MODE_MASK);
adc_cfg |= ADC_CLK_DIV_8 | ADC_SHORT_SAMPLE_MODE;
adc_cfg |= FIELD_PREP(ADC_CONV_MODE_MASK, ADC_12BIT_MODE) |
FIELD_PREP(ADC_INPUT_CLK_MASK, ADC_IPG_CLK);
adc_cfg &= ~(ADC_CLK_DIV_MASK | ADC_SAMPLE_MODE);
adc_cfg |= FIELD_PREP(ADC_CLK_DIV_MASK, ADC_CLK_DIV_8);
if (tsc->average_enable) {
adc_cfg &= ~ADC_AVGS_MASK;
adc_cfg |= (tsc->average_select) << ADC_AVGS_SHIFT;
adc_cfg |= FIELD_PREP(ADC_AVGS_MASK, tsc->average_select);
}
adc_cfg &= ~ADC_HARDWARE_TRIGGER;
writel(adc_cfg, tsc->adc_regs + REG_ADC_CFG);
/* enable calibration interrupt */
adc_hc |= ADC_AIEN;
adc_hc |= ADC_CONV_DISABLE;
adc_hc |= FIELD_PREP(ADC_ADCH_MASK, ADC_CONV_DISABLE);
writel(adc_hc, tsc->adc_regs + REG_ADC_HC0);
/* start ADC calibration */
@ -164,19 +172,21 @@ static void imx6ul_tsc_channel_config(struct imx6ul_tsc *tsc)
{
u32 adc_hc0, adc_hc1, adc_hc2, adc_hc3, adc_hc4;
adc_hc0 = DISABLE_CONVERSION_INT;
adc_hc0 = FIELD_PREP(ADC_AIEN, 0);
writel(adc_hc0, tsc->adc_regs + REG_ADC_HC0);
adc_hc1 = DISABLE_CONVERSION_INT | SELECT_CHANNEL_4;
adc_hc1 = FIELD_PREP(ADC_AIEN, 0) |
FIELD_PREP(ADC_ADCH_MASK, SELECT_CHANNEL_4);
writel(adc_hc1, tsc->adc_regs + REG_ADC_HC1);
adc_hc2 = DISABLE_CONVERSION_INT;
adc_hc2 = FIELD_PREP(ADC_AIEN, 0);
writel(adc_hc2, tsc->adc_regs + REG_ADC_HC2);
adc_hc3 = DISABLE_CONVERSION_INT | SELECT_CHANNEL_1;
adc_hc3 = FIELD_PREP(ADC_AIEN, 0) |
FIELD_PREP(ADC_ADCH_MASK, SELECT_CHANNEL_1);
writel(adc_hc3, tsc->adc_regs + REG_ADC_HC3);
adc_hc4 = DISABLE_CONVERSION_INT;
adc_hc4 = FIELD_PREP(ADC_AIEN, 0);
writel(adc_hc4, tsc->adc_regs + REG_ADC_HC4);
}
@ -188,13 +198,16 @@ static void imx6ul_tsc_channel_config(struct imx6ul_tsc *tsc)
static void imx6ul_tsc_set(struct imx6ul_tsc *tsc)
{
u32 basic_setting = 0;
u32 debug_mode2;
u32 start;
basic_setting |= tsc->measure_delay_time << 8;
basic_setting |= DETECT_4_WIRE_MODE | AUTO_MEASURE;
writel(basic_setting, tsc->tsc_regs + REG_TSC_BASIC_SETING);
basic_setting |= FIELD_PREP(MEASURE_DELAY_TIME_MASK,
tsc->measure_delay_time);
basic_setting |= AUTO_MEASURE;
writel(basic_setting, tsc->tsc_regs + REG_TSC_BASIC_SETTING);
writel(DE_GLITCH_2, tsc->tsc_regs + REG_TSC_DEBUG_MODE2);
debug_mode2 = FIELD_PREP(DE_GLITCH_MASK, tsc->de_glitch);
writel(debug_mode2, tsc->tsc_regs + REG_TSC_DEBUG_MODE2);
writel(tsc->pre_charge_time, tsc->tsc_regs + REG_TSC_PRE_CHARGE_TIME);
writel(MEASURE_INT_EN, tsc->tsc_regs + REG_TSC_INT_EN);
@ -250,7 +263,7 @@ static bool tsc_wait_detect_mode(struct imx6ul_tsc *tsc)
usleep_range(200, 400);
debug_mode2 = readl(tsc->tsc_regs + REG_TSC_DEBUG_MODE2);
state_machine = (debug_mode2 >> 20) & 0x7;
state_machine = FIELD_GET(STATE_MACHINE_MASK, debug_mode2);
} while (state_machine != DETECT_MODE);
usleep_range(200, 400);
@ -278,8 +291,8 @@ static irqreturn_t tsc_irq_fn(int irq, void *dev_id)
if (status & MEASURE_SIGNAL) {
value = readl(tsc->tsc_regs + REG_TSC_MEASURE_VALUE);
x = (value >> 16) & 0x0fff;
y = value & 0x0fff;
x = FIELD_GET(X_VALUE_MASK, value);
y = FIELD_GET(Y_VALUE_MASK, value);
/*
* In detect mode, we can get the xnur gpio value,
@ -379,6 +392,7 @@ static int imx6ul_tsc_probe(struct platform_device *pdev)
int tsc_irq;
int adc_irq;
u32 average_samples;
u32 de_glitch;
tsc = devm_kzalloc(&pdev->dev, sizeof(*tsc), GFP_KERNEL);
if (!tsc)
@ -501,6 +515,25 @@ static int imx6ul_tsc_probe(struct platform_device *pdev)
return -EINVAL;
}
err = of_property_read_u32(np, "debounce-delay-us", &de_glitch);
if (err) {
tsc->de_glitch = DE_GLITCH_DEF;
} else {
u64 cycles;
unsigned long rate = clk_get_rate(tsc->tsc_clk);
cycles = DIV64_U64_ROUND_UP((u64)de_glitch * rate, USEC_PER_SEC);
if (cycles <= 0x3ff)
tsc->de_glitch = 3;
else if (cycles <= 0x7ff)
tsc->de_glitch = 2;
else if (cycles <= 0xfff)
tsc->de_glitch = 1;
else
tsc->de_glitch = 0;
}
err = input_register_device(tsc->input);
if (err) {
dev_err(&pdev->dev,

View File

@ -23,6 +23,7 @@
#include <linux/input.h>
#include <linux/interrupt.h>
#include <linux/i2c.h>
#include <linux/math64.h>
#include <linux/mod_devicetable.h>
#include <linux/property.h>
#include <linux/platform_data/tsc2007.h>
@ -68,7 +69,7 @@ static void tsc2007_read_values(struct tsc2007 *tsc, struct ts_event *tc)
u32 tsc2007_calculate_resistance(struct tsc2007 *tsc, struct ts_event *tc)
{
u32 rt = 0;
u64 rt = 0;
/* range filtering */
if (tc->x == MAX_12BIT)
@ -79,11 +80,13 @@ u32 tsc2007_calculate_resistance(struct tsc2007 *tsc, struct ts_event *tc)
rt = tc->z2 - tc->z1;
rt *= tc->x;
rt *= tsc->x_plate_ohms;
rt /= tc->z1;
rt = div_u64(rt, tc->z1);
rt = (rt + 2047) >> 12;
}
return rt;
if (rt > U32_MAX)
return U32_MAX;
return (u32) rt;
}
bool tsc2007_is_pen_down(struct tsc2007 *ts)
@ -177,7 +180,8 @@ static void tsc2007_stop(struct tsc2007 *ts)
mb();
wake_up(&ts->wait);
disable_irq(ts->irq);
if (ts->irq)
disable_irq(ts->irq);
}
static int tsc2007_open(struct input_dev *input_dev)
@ -188,7 +192,8 @@ static int tsc2007_open(struct input_dev *input_dev)
ts->stopped = false;
mb();
enable_irq(ts->irq);
if (ts->irq)
enable_irq(ts->irq);
/* Prepare for touch readings - power down ADC and enable PENIRQ */
err = tsc2007_xfer(ts, PWRDOWN);
@ -253,7 +258,7 @@ static int tsc2007_probe_properties(struct device *dev, struct tsc2007 *ts)
if (ts->gpiod)
ts->get_pendown_state = tsc2007_get_pendown_state_gpio;
else
dev_warn(dev, "Pen down GPIO is not specified in properties\n");
dev_dbg(dev, "Pen down GPIO is not specified in properties\n");
return 0;
}
@ -361,17 +366,19 @@ static int tsc2007_probe(struct i2c_client *client)
pdata->init_platform_hw();
}
err = devm_request_threaded_irq(&client->dev, ts->irq,
NULL, tsc2007_soft_irq,
IRQF_ONESHOT,
client->dev.driver->name, ts);
if (err) {
dev_err(&client->dev, "Failed to request irq %d: %d\n",
ts->irq, err);
return err;
}
if (ts->irq) {
err = devm_request_threaded_irq(&client->dev, ts->irq,
NULL, tsc2007_soft_irq,
IRQF_ONESHOT,
client->dev.driver->name, ts);
if (err) {
dev_err(&client->dev, "Failed to request irq %d: %d\n",
ts->irq, err);
return err;
}
tsc2007_stop(ts);
tsc2007_stop(ts);
}
/* power down the chip (TSC2007_SETUP does not ACK on I2C) */
err = tsc2007_xfer(ts, PWRDOWN);

View File

@ -10,6 +10,7 @@
* based on TSC2301 driver by Klaus K. Pedersen <klaus.k.pedersen@nokia.com>
*/
#include <linux/export.h>
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/input.h>

View File

@ -9,6 +9,7 @@
* Russell King <rmk@arm.linux.org.uk>
*/
#include <linux/export.h>
#include <linux/module.h>
#include <linux/moduleparam.h>
#include <linux/kernel.h>

View File

@ -9,6 +9,7 @@
* Russell King <rmk@arm.linux.org.uk>
*/
#include <linux/export.h>
#include <linux/module.h>
#include <linux/moduleparam.h>
#include <linux/kernel.h>

View File

@ -9,6 +9,7 @@
* Russell King <rmk@arm.linux.org.uk>
*/
#include <linux/export.h>
#include <linux/module.h>
#include <linux/moduleparam.h>
#include <linux/kernel.h>

View File

@ -29,6 +29,7 @@
* - Support for async sampling control for noisy LCDs.
*/
#include <linux/export.h>
#include <linux/module.h>
#include <linux/moduleparam.h>
#include <linux/kernel.h>

View File

@ -1,164 +0,0 @@
/*
* Copyright (C) 2010 ST Microelectronics
* Rajeev Kumar <rajeevkumar.linux@gmail.com>
*
* This file is licensed under the terms of the GNU General Public
* License version 2. This program is licensed "as is" without any
* warranty of any kind, whether express or implied.
*/
#ifndef __PLAT_KEYBOARD_H
#define __PLAT_KEYBOARD_H
#include <linux/bitops.h>
#include <linux/input.h>
#include <linux/input/matrix_keypad.h>
#include <linux/types.h>
#define DECLARE_9x9_KEYMAP(_name) \
int _name[] = { \
KEY(0, 0, KEY_ESC), \
KEY(0, 1, KEY_1), \
KEY(0, 2, KEY_2), \
KEY(0, 3, KEY_3), \
KEY(0, 4, KEY_4), \
KEY(0, 5, KEY_5), \
KEY(0, 6, KEY_6), \
KEY(0, 7, KEY_7), \
KEY(0, 8, KEY_8), \
KEY(1, 0, KEY_9), \
KEY(1, 1, KEY_MINUS), \
KEY(1, 2, KEY_EQUAL), \
KEY(1, 3, KEY_BACKSPACE), \
KEY(1, 4, KEY_TAB), \
KEY(1, 5, KEY_Q), \
KEY(1, 6, KEY_W), \
KEY(1, 7, KEY_E), \
KEY(1, 8, KEY_R), \
KEY(2, 0, KEY_T), \
KEY(2, 1, KEY_Y), \
KEY(2, 2, KEY_U), \
KEY(2, 3, KEY_I), \
KEY(2, 4, KEY_O), \
KEY(2, 5, KEY_P), \
KEY(2, 6, KEY_LEFTBRACE), \
KEY(2, 7, KEY_RIGHTBRACE), \
KEY(2, 8, KEY_ENTER), \
KEY(3, 0, KEY_LEFTCTRL), \
KEY(3, 1, KEY_A), \
KEY(3, 2, KEY_S), \
KEY(3, 3, KEY_D), \
KEY(3, 4, KEY_F), \
KEY(3, 5, KEY_G), \
KEY(3, 6, KEY_H), \
KEY(3, 7, KEY_J), \
KEY(3, 8, KEY_K), \
KEY(4, 0, KEY_L), \
KEY(4, 1, KEY_SEMICOLON), \
KEY(4, 2, KEY_APOSTROPHE), \
KEY(4, 3, KEY_GRAVE), \
KEY(4, 4, KEY_LEFTSHIFT), \
KEY(4, 5, KEY_BACKSLASH), \
KEY(4, 6, KEY_Z), \
KEY(4, 7, KEY_X), \
KEY(4, 8, KEY_C), \
KEY(5, 0, KEY_V), \
KEY(5, 1, KEY_B), \
KEY(5, 2, KEY_N), \
KEY(5, 3, KEY_M), \
KEY(5, 4, KEY_COMMA), \
KEY(5, 5, KEY_DOT), \
KEY(5, 6, KEY_SLASH), \
KEY(5, 7, KEY_RIGHTSHIFT), \
KEY(5, 8, KEY_KPASTERISK), \
KEY(6, 0, KEY_LEFTALT), \
KEY(6, 1, KEY_SPACE), \
KEY(6, 2, KEY_CAPSLOCK), \
KEY(6, 3, KEY_F1), \
KEY(6, 4, KEY_F2), \
KEY(6, 5, KEY_F3), \
KEY(6, 6, KEY_F4), \
KEY(6, 7, KEY_F5), \
KEY(6, 8, KEY_F6), \
KEY(7, 0, KEY_F7), \
KEY(7, 1, KEY_F8), \
KEY(7, 2, KEY_F9), \
KEY(7, 3, KEY_F10), \
KEY(7, 4, KEY_NUMLOCK), \
KEY(7, 5, KEY_SCROLLLOCK), \
KEY(7, 6, KEY_KP7), \
KEY(7, 7, KEY_KP8), \
KEY(7, 8, KEY_KP9), \
KEY(8, 0, KEY_KPMINUS), \
KEY(8, 1, KEY_KP4), \
KEY(8, 2, KEY_KP5), \
KEY(8, 3, KEY_KP6), \
KEY(8, 4, KEY_KPPLUS), \
KEY(8, 5, KEY_KP1), \
KEY(8, 6, KEY_KP2), \
KEY(8, 7, KEY_KP3), \
KEY(8, 8, KEY_KP0), \
}
#define DECLARE_6x6_KEYMAP(_name) \
int _name[] = { \
KEY(0, 0, KEY_RESERVED), \
KEY(0, 1, KEY_1), \
KEY(0, 2, KEY_2), \
KEY(0, 3, KEY_3), \
KEY(0, 4, KEY_4), \
KEY(0, 5, KEY_5), \
KEY(1, 0, KEY_Q), \
KEY(1, 1, KEY_W), \
KEY(1, 2, KEY_E), \
KEY(1, 3, KEY_R), \
KEY(1, 4, KEY_T), \
KEY(1, 5, KEY_Y), \
KEY(2, 0, KEY_D), \
KEY(2, 1, KEY_F), \
KEY(2, 2, KEY_G), \
KEY(2, 3, KEY_H), \
KEY(2, 4, KEY_J), \
KEY(2, 5, KEY_K), \
KEY(3, 0, KEY_B), \
KEY(3, 1, KEY_N), \
KEY(3, 2, KEY_M), \
KEY(3, 3, KEY_COMMA), \
KEY(3, 4, KEY_DOT), \
KEY(3, 5, KEY_SLASH), \
KEY(4, 0, KEY_F6), \
KEY(4, 1, KEY_F7), \
KEY(4, 2, KEY_F8), \
KEY(4, 3, KEY_F9), \
KEY(4, 4, KEY_F10), \
KEY(4, 5, KEY_NUMLOCK), \
KEY(5, 0, KEY_KP2), \
KEY(5, 1, KEY_KP3), \
KEY(5, 2, KEY_KP0), \
KEY(5, 3, KEY_KPDOT), \
KEY(5, 4, KEY_RO), \
KEY(5, 5, KEY_ZENKAKUHANKAKU), \
}
#define KEYPAD_9x9 0
#define KEYPAD_6x6 1
#define KEYPAD_2x2 2
/**
* struct kbd_platform_data - spear keyboard platform data
* keymap: pointer to keymap data (table and size)
* rep: enables key autorepeat
* mode: choose keyboard support(9x9, 6x6, 2x2)
* suspended_rate: rate at which keyboard would operate in suspended mode
*
* This structure is supposed to be used by platform code to supply
* keymaps to drivers that implement keyboards.
*/
struct kbd_platform_data {
const struct matrix_keymap_data *keymap;
bool rep;
unsigned int mode;
unsigned int suspended_rate;
};
#endif /* __PLAT_KEYBOARD_H */

View File

@ -1,73 +0,0 @@
/* SPDX-License-Identifier: GPL-2.0 */
#ifndef __ASM_ARCH_PXA27x_KEYPAD_H
#define __ASM_ARCH_PXA27x_KEYPAD_H
#include <linux/input.h>
#include <linux/input/matrix_keypad.h>
#define MAX_MATRIX_KEY_ROWS (8)
#define MAX_MATRIX_KEY_COLS (8)
#define MATRIX_ROW_SHIFT (3)
#define MAX_DIRECT_KEY_NUM (8)
/* pxa3xx keypad platform specific parameters
*
* NOTE:
* 1. direct_key_num indicates the number of keys in the direct keypad
* _plus_ the number of rotary-encoder sensor inputs, this can be
* left as 0 if only rotary encoders are enabled, the driver will
* automatically calculate this
*
* 2. direct_key_map is the key code map for the direct keys, if rotary
* encoder(s) are enabled, direct key 0/1(2/3) will be ignored
*
* 3. rotary can be either interpreted as a relative input event (e.g.
* REL_WHEEL/REL_HWHEEL) or specific keys (e.g. UP/DOWN/LEFT/RIGHT)
*
* 4. matrix key and direct key will use the same debounce_interval by
* default, which should be sufficient in most cases
*
* pxa168 keypad platform specific parameter
*
* NOTE:
* clear_wakeup_event callback is a workaround required to clear the
* keypad interrupt. The keypad wake must be cleared in addition to
* reading the MI/DI bits in the KPC register.
*/
struct pxa27x_keypad_platform_data {
/* code map for the matrix keys */
const struct matrix_keymap_data *matrix_keymap_data;
unsigned int matrix_key_rows;
unsigned int matrix_key_cols;
/* direct keys */
int direct_key_num;
unsigned int direct_key_map[MAX_DIRECT_KEY_NUM];
/* the key output may be low active */
int direct_key_low_active;
/* give board a chance to choose the start direct key */
unsigned int direct_key_mask;
/* rotary encoders 0 */
int enable_rotary0;
int rotary0_rel_code;
int rotary0_up_key;
int rotary0_down_key;
/* rotary encoders 1 */
int enable_rotary1;
int rotary1_rel_code;
int rotary1_up_key;
int rotary1_down_key;
/* key debounce interval */
unsigned int debounce_interval;
/* clear wakeup event requirement for pxa168 */
void (*clear_wakeup_event)(void);
};
extern void pxa_set_keypad_info(struct pxa27x_keypad_platform_data *info);
#endif /* __ASM_ARCH_PXA27x_KEYPAD_H */

View File

@ -1,30 +0,0 @@
/* SPDX-License-Identifier: GPL-2.0-only */
/*
* tca6416 keypad platform support
*
* Copyright (C) 2010 Texas Instruments
*
* Author: Sriramakrishnan <srk@ti.com>
*/
#ifndef _TCA6416_KEYS_H
#define _TCA6416_KEYS_H
#include <linux/types.h>
struct tca6416_button {
/* Configuration parameters */
int code; /* input event code (KEY_*, SW_*) */
int active_low;
int type; /* input event type (EV_KEY, EV_SW) */
};
struct tca6416_keys_platform_data {
struct tca6416_button *buttons;
int nbuttons;
unsigned int rep:1; /* enable input subsystem auto repeat */
uint16_t pinmask;
uint16_t invert;
int use_polling; /* use polling if Interrupt is not connected*/
};
#endif