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:
commit
99cedb6b8f
|
@ -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>;
|
||||
};
|
||||
};
|
||||
...
|
||||
|
|
|
@ -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:
|
||||
- |
|
||||
|
|
|
@ -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>;
|
||||
};
|
||||
};
|
|
@ -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>;
|
||||
};
|
|
@ -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>;
|
||||
};
|
|
@ -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
|
||||
|
|
|
@ -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.
|
|
@ -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)
|
||||
>;
|
||||
};
|
||||
};
|
|
@ -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;
|
||||
};
|
||||
};
|
|
@ -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"
|
||||
|
|
|
@ -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>;
|
||||
};
|
||||
};
|
|
@ -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>;
|
||||
};
|
|
@ -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
|
||||
|
|
|
@ -62,7 +62,6 @@ additionalProperties: false
|
|||
required:
|
||||
- compatible
|
||||
- reg
|
||||
- interrupts
|
||||
|
||||
examples:
|
||||
- |
|
||||
|
|
|
@ -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 = <®_ts_vcca>;
|
||||
vccd-supply = <&pm8916_l6>;
|
||||
linux,keycodes = <KEY_BACK KEY_HOMEPAGE KEY_APPSELECT>;
|
||||
};
|
||||
};
|
||||
|
||||
...
|
|
@ -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>;
|
||||
};
|
||||
};
|
||||
|
||||
...
|
|
@ -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>;
|
||||
};
|
||||
};
|
||||
|
||||
...
|
||||
|
|
|
@ -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>;
|
||||
};
|
||||
};
|
|
@ -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>;
|
||||
};
|
||||
};
|
|
@ -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>;
|
||||
};
|
||||
};
|
|
@ -55,7 +55,7 @@ properties:
|
|||
touchscreen-min-pressure: true
|
||||
touchscreen-x-plate-ohms: true
|
||||
|
||||
additionalProperties: false
|
||||
unevaluatedProperties: false
|
||||
|
||||
required:
|
||||
- compatible
|
||||
|
|
|
@ -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;
|
||||
};
|
||||
};
|
|
@ -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>;
|
||||
};
|
||||
};
|
|
@ -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>;
|
||||
};
|
|
@ -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.
|
|
@ -1 +0,0 @@
|
|||
See touchscreen.yaml
|
|
@ -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 ]
|
||||
|
|
|
@ -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>;
|
||||
};
|
||||
};
|
|
@ -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>
|
||||
};
|
||||
|
||||
};
|
|
@ -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
|
||||
|
|
|
@ -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>
|
||||
|
|
|
@ -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>
|
||||
|
|
|
@ -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>
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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 */
|
||||
|
|
|
@ -4,6 +4,7 @@
|
|||
*/
|
||||
|
||||
#include <linux/device.h>
|
||||
#include <linux/export.h>
|
||||
#include <linux/input.h>
|
||||
#include <linux/jiffies.h>
|
||||
#include <linux/mutex.h>
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -6,6 +6,7 @@
|
|||
* USB/RS232 I-Force joysticks and wheels.
|
||||
*/
|
||||
|
||||
#include <linux/export.h>
|
||||
#include <linux/unaligned.h>
|
||||
#include "iforce.h"
|
||||
|
||||
|
|
|
@ -6,6 +6,7 @@
|
|||
* USB/RS232 I-Force joysticks and wheels.
|
||||
*/
|
||||
|
||||
#include <linux/export.h>
|
||||
#include <linux/unaligned.h>
|
||||
#include "iforce.h"
|
||||
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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) {
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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, ®_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");
|
|
@ -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");
|
||||
|
|
|
@ -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) {
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -6,6 +6,7 @@
|
|||
*/
|
||||
|
||||
#include <linux/device.h>
|
||||
#include <linux/export.h>
|
||||
#include <linux/input.h>
|
||||
#include <linux/interrupt.h>
|
||||
#include <linux/slab.h>
|
||||
|
|
|
@ -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>
|
||||
|
|
|
@ -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, ®_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");
|
|
@ -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>
|
||||
|
|
|
@ -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[] = {
|
||||
|
|
|
@ -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;
|
||||
/*
|
||||
|
|
|
@ -4,6 +4,7 @@
|
|||
* Copyright (c) 2011 Unixphere
|
||||
*/
|
||||
|
||||
#include <linux/export.h>
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/device.h>
|
||||
#include <linux/of.h>
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -4,6 +4,7 @@
|
|||
* Copyright (c) 2011 Unixphere
|
||||
*/
|
||||
|
||||
#include <linux/export.h>
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/device.h>
|
||||
#include <linux/irq.h>
|
||||
|
|
|
@ -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"
|
||||
|
||||
|
|
|
@ -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>
|
||||
|
|
|
@ -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>
|
||||
|
|
|
@ -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>
|
||||
|
|
|
@ -8,6 +8,7 @@
|
|||
|
||||
|
||||
#include <linux/delay.h>
|
||||
#include <linux/export.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/sched.h>
|
||||
#include <linux/interrupt.h>
|
||||
|
|
|
@ -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.
|
||||
*
|
||||
* ___ ______ ______ ______ ___
|
||||
|
|
|
@ -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>
|
||||
|
|
|
@ -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>
|
||||
|
|
|
@ -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>
|
||||
|
|
|
@ -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>
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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>
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
||||
|
|
|
@ -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>
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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>
|
||||
|
|
|
@ -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");
|
|
@ -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 = ®,
|
||||
.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");
|
|
@ -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,
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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>
|
||||
|
|
|
@ -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>
|
||||
|
|
|
@ -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>
|
||||
|
|
|
@ -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>
|
||||
|
|
|
@ -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>
|
||||
|
|
|
@ -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 */
|
|
@ -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 */
|
|
@ -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
|
Loading…
Reference in New Issue