forked from Minki/linux
Input updates for 6.1 merge window:
- a new driver for IBM Operational Panel - a new driver for PinePhone keyboards - RT5120 PMIC power key support - various enhancements and support for new models in xpad (Xbox) driver - a new compatible ID for Elan touchscreen driver - rework of adp5588-keys driver to support configuring via device properties (OF, ACPI, etc) instead of platform data, and proper support of optional gpiochip functionality (and removal of gpio-adp5588 driver) - improvements to firmware update handling in Synaptics RMI4 driver - support for double key matrix in mt6779-keypad - support for polled mode in adc-joystick driver - other assorted driver fixes, cleanups and improvements. -----BEGIN PGP SIGNATURE----- iHUEABYIAB0WIQST2eWILY88ieB2DOtAj56VGEWXnAUCY0O9YwAKCRBAj56VGEWX nICKAQD6/pS5tIWFCyYrM/A0xbkYuPWtmXqJTSGANlevU7nypwEAqaH2tYSDAdxM NylDCPvvduHVmA2ewXgvIlUzjCLr6As= =cUmW -----END PGP SIGNATURE----- Merge tag 'input-for-v6.1-rc0' of git://git.kernel.org/pub/scm/linux/kernel/git/dtor/input Pull input updates from Dmitry Torokhov: - a new driver for IBM Operational Panel - a new driver for PinePhone keyboards - RT5120 PMIC power key support - various enhancements and support for new models in xpad (Xbox) driver - a new compatible ID for Elan touchscreen driver - rework of adp5588-keys driver to support configuring via device properties (OF, ACPI, etc) instead of platform data, and proper support of optional gpiochip functionality (and removal of gpio-adp5588 driver) - improvements to firmware update handling in Synaptics RMI4 driver - support for double key matrix in mt6779-keypad - support for polled mode in adc-joystick driver - other assorted driver fixes, cleanups and improvements * tag 'input-for-v6.1-rc0' of git://git.kernel.org/pub/scm/linux/kernel/git/dtor/input: (90 commits) Input: i8042 - fix refount leak on sparc Input: i8042 - add LoongArch support in i8042-acpipnpio.h Input: i8042 - rename i8042-x86ia64io.h to i8042-acpipnpio.h Input: pinephone-keyboard - support the proxied I2C bus Input: pinephone-keyboard - add PinePhone keyboard driver dt-bindings: input: Add the PinePhone keyboard binding dt-bindings: input: Convert hid-over-i2c to DT schema input: drop empty comment blocks Input: xpad - add X-Box Adaptive Profile button Input: add ABS_PROFILE to uapi and documentation Input: xpad - add X-Box Adaptive XBox button Input: xpad - add X-Box Adaptive support Input: ims-pcu - fix spelling mistake "BOOLTLOADER" -> "BOOTLOADER" Input: ibm-panel - add missing MODULE_DEVICE_TABLE Input: icn8505 - utilize acpi_get_subsystem_id() Input: xpad - decipher xpadone packages with GIP defines Input: xpad - refactor using BIT() macro Input: synaptics-rmi4 - convert to use sysfs_emit() APIs Input: twl4030-pwrbutton - add missing of.h include Input: applespi - replace zero-length array with DECLARE_FLEX_ARRAY() helper ...
This commit is contained in:
commit
694b37a5dd
@ -14,6 +14,9 @@ description: >
|
||||
Bindings for joystick devices connected to ADC controllers supporting
|
||||
the Industrial I/O subsystem.
|
||||
|
||||
allOf:
|
||||
- $ref: input.yaml#
|
||||
|
||||
properties:
|
||||
compatible:
|
||||
const: adc-joystick
|
||||
@ -28,6 +31,8 @@ properties:
|
||||
https://github.com/devicetree-org/dt-schema/blob/master/schemas/iio/iio-consumer.yaml
|
||||
for details.
|
||||
|
||||
poll-interval: true
|
||||
|
||||
'#address-cells':
|
||||
const: 1
|
||||
|
||||
|
111
Documentation/devicetree/bindings/input/adi,adp5588.yaml
Normal file
111
Documentation/devicetree/bindings/input/adi,adp5588.yaml
Normal file
@ -0,0 +1,111 @@
|
||||
# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
|
||||
%YAML 1.2
|
||||
---
|
||||
$id: http://devicetree.org/schemas/input/adi,adp5588.yaml#
|
||||
$schema: http://devicetree.org/meta-schemas/core.yaml#
|
||||
|
||||
title: Analog Devices ADP5588 Keypad Controller
|
||||
|
||||
maintainers:
|
||||
- Nuno Sá <nuno.sa@analog.com>
|
||||
|
||||
description: |
|
||||
Analog Devices Mobile I/O Expander and QWERTY Keypad Controller
|
||||
https://www.analog.com/media/en/technical-documentation/data-sheets/ADP5588.pdf
|
||||
|
||||
allOf:
|
||||
- $ref: matrix-keymap.yaml#
|
||||
- $ref: input.yaml#
|
||||
|
||||
properties:
|
||||
compatible:
|
||||
enum:
|
||||
- adi,adp5587
|
||||
- adi,adp5588
|
||||
|
||||
reg:
|
||||
maxItems: 1
|
||||
|
||||
vcc-supply:
|
||||
description: Supply Voltage Input
|
||||
|
||||
reset-gpios:
|
||||
description:
|
||||
If specified, it will be asserted during driver probe. As the line is
|
||||
active low, it should be marked GPIO_ACTIVE_LOW.
|
||||
maxItems: 1
|
||||
|
||||
interrupts:
|
||||
maxItems: 1
|
||||
|
||||
gpio-controller:
|
||||
description:
|
||||
This property applies if either keypad,num-rows lower than 8 or
|
||||
keypad,num-columns lower than 10.
|
||||
|
||||
'#gpio-cells':
|
||||
const: 2
|
||||
|
||||
interrupt-controller:
|
||||
description:
|
||||
This property applies if either keypad,num-rows lower than 8 or
|
||||
keypad,num-columns lower than 10.
|
||||
|
||||
'#interrupt-cells':
|
||||
const: 2
|
||||
|
||||
adi,unlock-keys:
|
||||
description:
|
||||
Specifies a maximum of 2 keys that can be used to unlock the keypad.
|
||||
If this property is set, the keyboard will be locked and only unlocked
|
||||
after these keys are pressed. If only one key is set, a double click is
|
||||
needed to unlock the keypad. The value of this property cannot be bigger
|
||||
or equal than keypad,num-rows * keypad,num-columns.
|
||||
$ref: /schemas/types.yaml#/definitions/uint32-array
|
||||
minItems: 1
|
||||
maxItems: 2
|
||||
|
||||
required:
|
||||
- compatible
|
||||
- reg
|
||||
- interrupts
|
||||
- keypad,num-rows
|
||||
- keypad,num-columns
|
||||
- linux,keymap
|
||||
|
||||
unevaluatedProperties: false
|
||||
|
||||
examples:
|
||||
- |
|
||||
#include <dt-bindings/interrupt-controller/irq.h>
|
||||
#include <dt-bindings/input/input.h>
|
||||
#include <dt-bindings/gpio/gpio.h>
|
||||
i2c {
|
||||
#address-cells = <1>;
|
||||
#size-cells = <0>;
|
||||
|
||||
keys@34 {
|
||||
compatible = "adi,adp5588";
|
||||
reg = <0x34>;
|
||||
|
||||
vcc-supply = <&vcc>;
|
||||
interrupts = <21 IRQ_TYPE_EDGE_FALLING>;
|
||||
interrupt-parent = <&gpio>;
|
||||
reset-gpios = <&gpio 20 GPIO_ACTIVE_LOW>;
|
||||
|
||||
keypad,num-rows = <1>;
|
||||
keypad,num-columns = <9>;
|
||||
linux,keymap = <
|
||||
MATRIX_KEY(0x00, 0x00, KEY_1)
|
||||
MATRIX_KEY(0x00, 0x01, KEY_2)
|
||||
MATRIX_KEY(0x00, 0x02, KEY_3)
|
||||
MATRIX_KEY(0x00, 0x03, KEY_4)
|
||||
MATRIX_KEY(0x00, 0x04, KEY_5)
|
||||
MATRIX_KEY(0x00, 0x05, KEY_6)
|
||||
MATRIX_KEY(0x00, 0x06, KEY_7)
|
||||
MATRIX_KEY(0x00, 0x07, KEY_8)
|
||||
MATRIX_KEY(0x00, 0x08, KEY_9)
|
||||
>;
|
||||
};
|
||||
};
|
||||
...
|
@ -1,46 +0,0 @@
|
||||
* HID over I2C Device-Tree bindings
|
||||
|
||||
HID over I2C provides support for various Human Interface Devices over the
|
||||
I2C bus. These devices can be for example touchpads, keyboards, touch screens
|
||||
or sensors.
|
||||
|
||||
The specification has been written by Microsoft and is currently available here:
|
||||
http://msdn.microsoft.com/en-us/library/windows/hardware/hh852380.aspx
|
||||
|
||||
If this binding is used, the kernel module i2c-hid will handle the communication
|
||||
with the device and the generic hid core layer will handle the protocol.
|
||||
|
||||
Required properties:
|
||||
- compatible: must be "hid-over-i2c"
|
||||
- reg: i2c slave address
|
||||
- hid-descr-addr: HID descriptor address
|
||||
- interrupts: interrupt line
|
||||
|
||||
Additional optional properties:
|
||||
|
||||
Some devices may support additional optional properties to help with, e.g.,
|
||||
power sequencing. The following properties can be supported by one or more
|
||||
device-specific compatible properties, which should be used in addition to the
|
||||
"hid-over-i2c" string.
|
||||
|
||||
- compatible:
|
||||
* "wacom,w9013" (Wacom W9013 digitizer). Supports:
|
||||
- vdd-supply (3.3V)
|
||||
- vddl-supply (1.8V)
|
||||
- post-power-on-delay-ms
|
||||
|
||||
- vdd-supply: phandle of the regulator that provides the supply voltage.
|
||||
- post-power-on-delay-ms: time required by the device after enabling its regulators
|
||||
or powering it on, before it is ready for communication.
|
||||
- touchscreen-inverted-x: See touchscreen.txt
|
||||
- touchscreen-inverted-y: See touchscreen.txt
|
||||
|
||||
Example:
|
||||
|
||||
i2c-hid-dev@2c {
|
||||
compatible = "hid-over-i2c";
|
||||
reg = <0x2c>;
|
||||
hid-descr-addr = <0x0020>;
|
||||
interrupt-parent = <&gpx3>;
|
||||
interrupts = <3 2>;
|
||||
};
|
83
Documentation/devicetree/bindings/input/hid-over-i2c.yaml
Normal file
83
Documentation/devicetree/bindings/input/hid-over-i2c.yaml
Normal file
@ -0,0 +1,83 @@
|
||||
# SPDX-License-Identifier: GPL-2.0-only
|
||||
%YAML 1.2
|
||||
---
|
||||
$id: http://devicetree.org/schemas/input/hid-over-i2c.yaml#
|
||||
$schema: http://devicetree.org/meta-schemas/core.yaml#
|
||||
|
||||
title: HID over I2C Devices
|
||||
|
||||
maintainers:
|
||||
- Benjamin Tissoires <benjamin.tissoires@redhat.com>
|
||||
- Jiri Kosina <jkosina@suse.cz>
|
||||
|
||||
description: |+
|
||||
HID over I2C provides support for various Human Interface Devices over the
|
||||
I2C bus. These devices can be for example touchpads, keyboards, touch screens
|
||||
or sensors.
|
||||
|
||||
The specification has been written by Microsoft and is currently available here:
|
||||
https://msdn.microsoft.com/en-us/library/windows/hardware/hh852380.aspx
|
||||
|
||||
If this binding is used, the kernel module i2c-hid will handle the communication
|
||||
with the device and the generic hid core layer will handle the protocol.
|
||||
|
||||
allOf:
|
||||
- $ref: /schemas/input/touchscreen/touchscreen.yaml#
|
||||
|
||||
properties:
|
||||
compatible:
|
||||
oneOf:
|
||||
- items:
|
||||
- enum:
|
||||
- wacom,w9013
|
||||
- const: hid-over-i2c
|
||||
- description: Just "hid-over-i2c" alone is allowed, but not recommended.
|
||||
const: hid-over-i2c
|
||||
|
||||
reg:
|
||||
maxItems: 1
|
||||
|
||||
interrupts:
|
||||
maxItems: 1
|
||||
|
||||
hid-descr-addr:
|
||||
description: HID descriptor address
|
||||
$ref: /schemas/types.yaml#/definitions/uint32
|
||||
|
||||
post-power-on-delay-ms:
|
||||
description: Time required by the device after enabling its regulators
|
||||
or powering it on, before it is ready for communication.
|
||||
|
||||
touchscreen-inverted-x: true
|
||||
|
||||
touchscreen-inverted-y: true
|
||||
|
||||
vdd-supply:
|
||||
description: 3.3V supply
|
||||
|
||||
vddl-supply:
|
||||
description: 1.8V supply
|
||||
|
||||
wakeup-source: true
|
||||
|
||||
required:
|
||||
- compatible
|
||||
- reg
|
||||
- interrupts
|
||||
|
||||
additionalProperties: false
|
||||
|
||||
examples:
|
||||
- |
|
||||
i2c {
|
||||
#address-cells = <1>;
|
||||
#size-cells = <0>;
|
||||
|
||||
hid@2c {
|
||||
compatible = "hid-over-i2c";
|
||||
reg = <0x2c>;
|
||||
hid-descr-addr = <0x0020>;
|
||||
interrupts = <3 2>;
|
||||
};
|
||||
};
|
||||
...
|
50
Documentation/devicetree/bindings/input/ibm,op-panel.yaml
Normal file
50
Documentation/devicetree/bindings/input/ibm,op-panel.yaml
Normal file
@ -0,0 +1,50 @@
|
||||
# SPDX-License-Identifier: (GPL-2.0 OR BSD-2-Clause)
|
||||
%YAML 1.2
|
||||
---
|
||||
$id: http://devicetree.org/schemas/input/ibm,op-panel.yaml#
|
||||
$schema: http://devicetree.org/meta-schemas/core.yaml#
|
||||
|
||||
title: IBM Operation Panel
|
||||
|
||||
maintainers:
|
||||
- Eddie James <eajames@linux.ibm.com>
|
||||
|
||||
allOf:
|
||||
- $ref: input.yaml#
|
||||
|
||||
description: |
|
||||
The IBM Operation Panel provides a simple interface to control the connected
|
||||
server. It has a display and three buttons: two directional arrows and one
|
||||
'Enter' button.
|
||||
|
||||
properties:
|
||||
compatible:
|
||||
const: ibm,op-panel
|
||||
|
||||
reg:
|
||||
maxItems: 1
|
||||
|
||||
linux,keycodes:
|
||||
minItems: 1
|
||||
maxItems: 3
|
||||
|
||||
required:
|
||||
- compatible
|
||||
- reg
|
||||
|
||||
additionalProperties: false
|
||||
|
||||
examples:
|
||||
- |
|
||||
#include <dt-bindings/i2c/i2c.h>
|
||||
#include <dt-bindings/input/input.h>
|
||||
i2c {
|
||||
#address-cells = <1>;
|
||||
#size-cells = <0>;
|
||||
|
||||
ibm-op-panel@62 {
|
||||
compatible = "ibm,op-panel";
|
||||
reg = <(0x62 | I2C_OWN_SLAVE_ADDRESS)>;
|
||||
linux,keycodes = <KEY_UP>, <KEY_DOWN>, <KEY_ENTER>;
|
||||
};
|
||||
};
|
@ -49,6 +49,12 @@ properties:
|
||||
maximum: 256
|
||||
default: 16
|
||||
|
||||
mediatek,keys-per-group:
|
||||
description: each (row, column) group has multiple keys
|
||||
$ref: /schemas/types.yaml#/definitions/uint32
|
||||
default: 1
|
||||
maximum: 2
|
||||
|
||||
required:
|
||||
- compatible
|
||||
- reg
|
||||
@ -56,7 +62,7 @@ required:
|
||||
- clocks
|
||||
- clock-names
|
||||
|
||||
additionalProperties: false
|
||||
unevaluatedProperties: false
|
||||
|
||||
examples:
|
||||
- |
|
||||
|
114
Documentation/devicetree/bindings/input/mediatek,pmic-keys.yaml
Normal file
114
Documentation/devicetree/bindings/input/mediatek,pmic-keys.yaml
Normal file
@ -0,0 +1,114 @@
|
||||
# SPDX-License-Identifier: (GPL-2.0 OR BSD-2-Clause)
|
||||
%YAML 1.2
|
||||
---
|
||||
$id: http://devicetree.org/schemas/input/mediatek,pmic-keys.yaml#
|
||||
$schema: http://devicetree.org/meta-schemas/core.yaml#
|
||||
|
||||
title: MediaTek PMIC Keys
|
||||
|
||||
maintainers:
|
||||
- Chen Zhong <chen.zhong@mediatek.com>
|
||||
|
||||
allOf:
|
||||
- $ref: input.yaml#
|
||||
|
||||
description: |
|
||||
There are two key functions provided by MT6397, MT6323 and other MediaTek
|
||||
PMICs: pwrkey and homekey.
|
||||
The key functions are defined as the subnode of the function node provided
|
||||
by the PMIC that is defined as a Multi-Function Device (MFD).
|
||||
|
||||
For MediaTek MT6323/MT6397 PMIC bindings see
|
||||
Documentation/devicetree/bindings/mfd/mt6397.txt
|
||||
|
||||
properties:
|
||||
compatible:
|
||||
enum:
|
||||
- mediatek,mt6323-keys
|
||||
- mediatek,mt6331-keys
|
||||
- mediatek,mt6358-keys
|
||||
- mediatek,mt6397-keys
|
||||
|
||||
power-off-time-sec: true
|
||||
|
||||
mediatek,long-press-mode:
|
||||
description: |
|
||||
Key long-press force shutdown setting
|
||||
0 - disabled
|
||||
1 - pwrkey
|
||||
2 - pwrkey+homekey
|
||||
$ref: /schemas/types.yaml#/definitions/uint32
|
||||
default: 0
|
||||
maximum: 2
|
||||
|
||||
patternProperties:
|
||||
"^((power|home)|(key-[a-z0-9-]+|[a-z0-9-]+-key))$":
|
||||
$ref: input.yaml#
|
||||
|
||||
properties:
|
||||
interrupts:
|
||||
minItems: 1
|
||||
items:
|
||||
- description: Key press interrupt
|
||||
- description: Key release interrupt
|
||||
|
||||
interrupt-names: true
|
||||
|
||||
linux-keycodes:
|
||||
maxItems: 1
|
||||
|
||||
wakeup-source: true
|
||||
|
||||
required:
|
||||
- linux,keycodes
|
||||
|
||||
if:
|
||||
properties:
|
||||
interrupt-names:
|
||||
contains:
|
||||
const: powerkey
|
||||
then:
|
||||
properties:
|
||||
interrupt-names:
|
||||
minItems: 1
|
||||
items:
|
||||
- const: powerkey
|
||||
- const: powerkey_r
|
||||
else:
|
||||
properties:
|
||||
interrupt-names:
|
||||
minItems: 1
|
||||
items:
|
||||
- const: homekey
|
||||
- const: homekey_r
|
||||
|
||||
unevaluatedProperties: false
|
||||
|
||||
required:
|
||||
- compatible
|
||||
|
||||
unevaluatedProperties: false
|
||||
|
||||
examples:
|
||||
- |
|
||||
#include <dt-bindings/input/input.h>
|
||||
#include <dt-bindings/interrupt-controller/arm-gic.h>
|
||||
|
||||
pmic {
|
||||
compatible = "mediatek,mt6397";
|
||||
|
||||
keys {
|
||||
compatible = "mediatek,mt6397-keys";
|
||||
mediatek,long-press-mode = <1>;
|
||||
power-off-time-sec = <0>;
|
||||
|
||||
key-power {
|
||||
linux,keycodes = <KEY_POWER>;
|
||||
wakeup-source;
|
||||
};
|
||||
|
||||
key-home {
|
||||
linux,keycodes = <KEY_VOLUMEDOWN>;
|
||||
};
|
||||
};
|
||||
};
|
@ -1,46 +0,0 @@
|
||||
MediaTek MT6397/MT6323 PMIC Keys Device Driver
|
||||
|
||||
There are two key functions provided by MT6397/MT6323 PMIC, pwrkey
|
||||
and homekey. The key functions are defined as the subnode of the function
|
||||
node provided by MT6397/MT6323 PMIC that is being defined as one kind
|
||||
of Muti-Function Device (MFD)
|
||||
|
||||
For MT6397/MT6323 MFD bindings see:
|
||||
Documentation/devicetree/bindings/mfd/mt6397.txt
|
||||
|
||||
Required properties:
|
||||
- compatible: Should be one of:
|
||||
- "mediatek,mt6397-keys"
|
||||
- "mediatek,mt6323-keys"
|
||||
- "mediatek,mt6358-keys"
|
||||
- linux,keycodes: See Documentation/devicetree/bindings/input/input.yaml
|
||||
|
||||
Optional Properties:
|
||||
- wakeup-source: See Documentation/devicetree/bindings/power/wakeup-source.txt
|
||||
- mediatek,long-press-mode: Long press key shutdown setting, 1 for
|
||||
pwrkey only, 2 for pwrkey/homekey together, others for disabled.
|
||||
- power-off-time-sec: See Documentation/devicetree/bindings/input/input.yaml
|
||||
|
||||
Example:
|
||||
|
||||
pmic: mt6397 {
|
||||
compatible = "mediatek,mt6397";
|
||||
|
||||
...
|
||||
|
||||
mt6397keys: mt6397keys {
|
||||
compatible = "mediatek,mt6397-keys";
|
||||
mediatek,long-press-mode = <1>;
|
||||
power-off-time-sec = <0>;
|
||||
|
||||
power {
|
||||
linux,keycodes = <116>;
|
||||
wakeup-source;
|
||||
};
|
||||
|
||||
home {
|
||||
linux,keycodes = <114>;
|
||||
};
|
||||
};
|
||||
|
||||
};
|
@ -0,0 +1,66 @@
|
||||
# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
|
||||
%YAML 1.2
|
||||
---
|
||||
$id: http://devicetree.org/schemas/input/pine64,pinephone-keyboard.yaml#
|
||||
$schema: http://devicetree.org/meta-schemas/core.yaml#
|
||||
|
||||
title: Pine64 PinePhone keyboard device tree bindings
|
||||
|
||||
maintainers:
|
||||
- Samuel Holland <samuel@sholland.org>
|
||||
|
||||
description:
|
||||
A keyboard accessory is available for the Pine64 PinePhone and PinePhone Pro.
|
||||
It connects via I2C, providing a raw scan matrix, a flashing interface, and a
|
||||
subordinate I2C bus for communication with a battery charger IC.
|
||||
|
||||
properties:
|
||||
compatible:
|
||||
const: pine64,pinephone-keyboard
|
||||
|
||||
reg:
|
||||
const: 0x15
|
||||
|
||||
interrupts:
|
||||
maxItems: 1
|
||||
|
||||
vbat-supply:
|
||||
description: Supply for the keyboard MCU
|
||||
|
||||
wakeup-source: true
|
||||
|
||||
i2c:
|
||||
$ref: /schemas/i2c/i2c-controller.yaml#
|
||||
|
||||
required:
|
||||
- compatible
|
||||
- reg
|
||||
- interrupts
|
||||
|
||||
additionalProperties: false
|
||||
|
||||
examples:
|
||||
- |
|
||||
#include <dt-bindings/input/input.h>
|
||||
#include <dt-bindings/interrupt-controller/irq.h>
|
||||
|
||||
i2c {
|
||||
#address-cells = <1>;
|
||||
#size-cells = <0>;
|
||||
|
||||
keyboard@15 {
|
||||
compatible = "pine64,pinephone-keyboard";
|
||||
reg = <0x15>;
|
||||
interrupt-parent = <&r_pio>;
|
||||
interrupts = <0 12 IRQ_TYPE_EDGE_FALLING>; /* PL12 */
|
||||
|
||||
i2c {
|
||||
#address-cells = <1>;
|
||||
#size-cells = <0>;
|
||||
|
||||
charger@75 {
|
||||
reg = <0x75>;
|
||||
};
|
||||
};
|
||||
};
|
||||
};
|
@ -1,23 +0,0 @@
|
||||
Qualcomm PM8xxx PMIC Vibrator
|
||||
|
||||
PROPERTIES
|
||||
|
||||
- compatible:
|
||||
Usage: required
|
||||
Value type: <string>
|
||||
Definition: must be one of:
|
||||
"qcom,pm8058-vib"
|
||||
"qcom,pm8916-vib"
|
||||
"qcom,pm8921-vib"
|
||||
|
||||
- reg:
|
||||
Usage: required
|
||||
Value type: <prop-encoded-array>
|
||||
Definition: address of vibration control register
|
||||
|
||||
EXAMPLE
|
||||
|
||||
vibrator@4a {
|
||||
compatible = "qcom,pm8058-vib";
|
||||
reg = <0x4a>;
|
||||
};
|
38
Documentation/devicetree/bindings/input/qcom,pm8xxx-vib.yaml
Normal file
38
Documentation/devicetree/bindings/input/qcom,pm8xxx-vib.yaml
Normal file
@ -0,0 +1,38 @@
|
||||
# SPDX-License-Identifier: (GPL-2.0 OR BSD-2-Clause)
|
||||
%YAML 1.2
|
||||
---
|
||||
$id: http://devicetree.org/schemas/input/qcom,pm8xxx-vib.yaml#
|
||||
$schema: http://devicetree.org/meta-schemas/core.yaml#
|
||||
|
||||
title: Qualcomm PM8xxx PMIC Vibrator
|
||||
|
||||
maintainers:
|
||||
- Bjorn Andersson <andersson@kernel.org>
|
||||
|
||||
properties:
|
||||
compatible:
|
||||
enum:
|
||||
- qcom,pm8058-vib
|
||||
- qcom,pm8916-vib
|
||||
- qcom,pm8921-vib
|
||||
|
||||
reg:
|
||||
maxItems: 1
|
||||
|
||||
required:
|
||||
- compatible
|
||||
- reg
|
||||
|
||||
additionalProperties: false
|
||||
|
||||
examples:
|
||||
- |
|
||||
pmic {
|
||||
#address-cells = <1>;
|
||||
#size-cells = <0>;
|
||||
|
||||
vibrator@4a {
|
||||
compatible = "qcom,pm8058-vib";
|
||||
reg = <0x4a>;
|
||||
};
|
||||
};
|
@ -17,10 +17,10 @@ Example:
|
||||
auo_pixcir_ts@5c {
|
||||
compatible = "auo,auo_pixcir_ts";
|
||||
reg = <0x5c>;
|
||||
interrupts = <2 0>;
|
||||
interrupts = <2 IRQ_TYPE_LEVEL_HIGH>;
|
||||
|
||||
gpios = <&gpf 2 0 2>, /* INT */
|
||||
<&gpf 5 1 0>; /* RST */
|
||||
gpios = <&gpf 2 0 GPIO_LEVEL_HIGH>, /* INT */
|
||||
<&gpf 5 1 GPIO_LEVEL_LOW>; /* RST */
|
||||
|
||||
x-size = <800>;
|
||||
y-size = <600>;
|
||||
|
@ -3,15 +3,16 @@
|
||||
Required Properties:
|
||||
- compatible must be toradex,vf50-touchscreen
|
||||
- io-channels: adc channels being used by the Colibri VF50 module
|
||||
IIO ADC for Y-, X-, Y+, X+ connections
|
||||
- xp-gpios: FET gate driver for input of X+
|
||||
- xm-gpios: FET gate driver for input of X-
|
||||
- yp-gpios: FET gate driver for input of Y+
|
||||
- ym-gpios: FET gate driver for input of Y-
|
||||
- interrupts: pen irq interrupt for touch detection
|
||||
- pinctrl-names: "idle", "default", "gpios"
|
||||
- pinctrl-0: pinctrl node for pen/touch detection state pinmux
|
||||
- interrupts: pen irq interrupt for touch detection, signal from X plate
|
||||
- pinctrl-names: "idle", "default"
|
||||
- pinctrl-0: pinctrl node for pen/touch detection, pinctrl must provide
|
||||
pull-up resistor on X+, X-.
|
||||
- pinctrl-1: pinctrl node for X/Y and pressure measurement (ADC) state pinmux
|
||||
- pinctrl-2: pinctrl node for gpios functioning as FET gate drivers
|
||||
- vf50-ts-min-pressure: pressure level at which to stop measuring X/Y values
|
||||
|
||||
Example:
|
||||
@ -26,9 +27,8 @@ Example:
|
||||
ym-gpios = <&gpio0 4 GPIO_ACTIVE_HIGH>;
|
||||
interrupt-parent = <&gpio0>;
|
||||
interrupts = <8 IRQ_TYPE_LEVEL_LOW>;
|
||||
pinctrl-names = "idle","default","gpios";
|
||||
pinctrl-0 = <&pinctrl_touchctrl_idle>;
|
||||
pinctrl-1 = <&pinctrl_touchctrl_default>;
|
||||
pinctrl-2 = <&pinctrl_touchctrl_gpios>;
|
||||
pinctrl-names = "idle","default";
|
||||
pinctrl-0 = <&pinctrl_touchctrl_idle>, <&pinctrl_touchctrl_gpios>;
|
||||
pinctrl-1 = <&pinctrl_touchctrl_default>, <&pinctrl_touchctrl_gpios>;
|
||||
vf50-ts-min-pressure = <200>;
|
||||
};
|
||||
|
@ -14,9 +14,13 @@ allOf:
|
||||
|
||||
properties:
|
||||
compatible:
|
||||
enum:
|
||||
oneOf:
|
||||
- enum:
|
||||
- elan,ektf3624
|
||||
- elan,ekth3500
|
||||
- items:
|
||||
- const: elan,ekth3915
|
||||
- const: elan,ekth3500
|
||||
|
||||
reg:
|
||||
maxItems: 1
|
||||
|
@ -54,8 +54,7 @@ Optional properties common with MFD (deprecated):
|
||||
1 -> 3.25 MHz
|
||||
2 || 3 -> 6.5 MHz
|
||||
|
||||
Node name must be stmpe_touchscreen and should be child node of stmpe node to
|
||||
which it belongs.
|
||||
Node should be child node of stmpe node to which it belongs.
|
||||
|
||||
Note that common ADC settings of stmpe_touchscreen (child) will take precedence
|
||||
over the settings done in MFD.
|
||||
|
@ -235,6 +235,12 @@ A few EV_ABS codes have special meanings:
|
||||
BTN_TOOL_<name> signals the type of tool that is currently detected by the
|
||||
hardware and is otherwise independent of ABS_DISTANCE and/or BTN_TOUCH.
|
||||
|
||||
* ABS_PROFILE:
|
||||
|
||||
- Used to describe the state of a multi-value profile switch. An event is
|
||||
emitted only when the selected profile changes, indicating the newly
|
||||
selected profile value.
|
||||
|
||||
* ABS_MT_<name>:
|
||||
|
||||
- Used to describe multitouch input events. Please see
|
||||
|
@ -189,3 +189,9 @@ Gamepads report the following events:
|
||||
- Rumble:
|
||||
|
||||
Rumble is advertised as FF_RUMBLE.
|
||||
|
||||
- Profile:
|
||||
|
||||
Some pads provide a multi-value profile selection switch. An example is the
|
||||
XBox Adaptive and the XBox Elite 2 controllers. When the active profile is
|
||||
switched, its newly selected value is emitted as an ABS_PROFILE event.
|
||||
|
21
MAINTAINERS
21
MAINTAINERS
@ -554,7 +554,7 @@ M: Michael Hennerich <michael.hennerich@analog.com>
|
||||
S: Supported
|
||||
W: http://wiki.analog.com/ADP5588
|
||||
W: https://ez.analog.com/linux-software-drivers
|
||||
F: drivers/gpio/gpio-adp5588.c
|
||||
F: Documentation/devicetree/bindings/input/adi,adp5588.yaml
|
||||
F: drivers/input/keyboard/adp5588-keys.c
|
||||
|
||||
ADP8860 BACKLIGHT DRIVER (ADP8860/ADP8861/ADP8863)
|
||||
@ -9713,6 +9713,13 @@ S: Orphan
|
||||
F: Documentation/ia64/
|
||||
F: arch/ia64/
|
||||
|
||||
IBM Operation Panel Input Driver
|
||||
M: Eddie James <eajames@linux.ibm.com>
|
||||
L: linux-input@vger.kernel.org
|
||||
S: Maintained
|
||||
F: Documentation/devicetree/bindings/input/ibm,op-panel.yaml
|
||||
F: drivers/input/misc/ibm-panel.c
|
||||
|
||||
IBM Power 842 compression accelerator
|
||||
M: Haren Myneni <haren@us.ibm.com>
|
||||
S: Supported
|
||||
@ -12925,6 +12932,12 @@ S: Supported
|
||||
F: Documentation/devicetree/bindings/media/mediatek-jpeg-*.yaml
|
||||
F: drivers/media/platform/mediatek/jpeg/
|
||||
|
||||
MEDIATEK KEYPAD DRIVER
|
||||
M: Mattijs Korpershoek <mkorpershoek@baylibre.com>
|
||||
S: Supported
|
||||
F: Documentation/devicetree/bindings/input/mediatek,mt6779-keypad.yaml
|
||||
F: drivers/input/keyboard/mt6779-keypad.c
|
||||
|
||||
MEDIATEK MDP DRIVER
|
||||
M: Minghsiu Tsai <minghsiu.tsai@mediatek.com>
|
||||
M: Houlong Wei <houlong.wei@mediatek.com>
|
||||
@ -16337,6 +16350,12 @@ F: Documentation/devicetree/bindings/pinctrl/sunplus,*
|
||||
F: drivers/pinctrl/sunplus/
|
||||
F: include/dt-bindings/pinctrl/sppctl*.h
|
||||
|
||||
PINE64 PINEPHONE KEYBOARD DRIVER
|
||||
M: Samuel Holland <samuel@sholland.org>
|
||||
S: Supported
|
||||
F: Documentation/devicetree/bindings/input/pine64,pinephone-keyboard.yaml
|
||||
F: drivers/input/keyboard/pinephone-keyboard.c
|
||||
|
||||
PKTCDVD DRIVER
|
||||
M: linux-block@vger.kernel.org
|
||||
S: Orphan
|
||||
|
@ -990,20 +990,6 @@ endmenu
|
||||
menu "I2C GPIO expanders"
|
||||
depends on I2C
|
||||
|
||||
config GPIO_ADP5588
|
||||
tristate "ADP5588 I2C GPIO expander"
|
||||
help
|
||||
This option enables support for 18 GPIOs found
|
||||
on Analog Devices ADP5588 GPIO Expanders.
|
||||
|
||||
config GPIO_ADP5588_IRQ
|
||||
bool "Interrupt controller support for ADP5588"
|
||||
depends on GPIO_ADP5588=y
|
||||
select GPIOLIB_IRQCHIP
|
||||
help
|
||||
Say yes here to enable the adp5588 to be used as an interrupt
|
||||
controller. It requires the driver to be built in the kernel.
|
||||
|
||||
config GPIO_ADNP
|
||||
tristate "Avionic Design N-bit GPIO expander"
|
||||
depends on OF_GPIO
|
||||
|
@ -25,7 +25,6 @@ obj-$(CONFIG_GPIO_74X164) += gpio-74x164.o
|
||||
obj-$(CONFIG_GPIO_74XX_MMIO) += gpio-74xx-mmio.o
|
||||
obj-$(CONFIG_GPIO_ADNP) += gpio-adnp.o
|
||||
obj-$(CONFIG_GPIO_ADP5520) += gpio-adp5520.o
|
||||
obj-$(CONFIG_GPIO_ADP5588) += gpio-adp5588.o
|
||||
obj-$(CONFIG_GPIO_AGGREGATOR) += gpio-aggregator.o
|
||||
obj-$(CONFIG_GPIO_ALTERA_A10SR) += gpio-altera-a10sr.o
|
||||
obj-$(CONFIG_GPIO_ALTERA) += gpio-altera.o
|
||||
|
@ -1,446 +0,0 @@
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
/*
|
||||
* GPIO Chip driver for Analog Devices
|
||||
* ADP5588/ADP5587 I/O Expander and QWERTY Keypad Controller
|
||||
*
|
||||
* Copyright 2009-2010 Analog Devices Inc.
|
||||
*/
|
||||
|
||||
#include <linux/gpio/driver.h>
|
||||
#include <linux/i2c.h>
|
||||
#include <linux/init.h>
|
||||
#include <linux/interrupt.h>
|
||||
#include <linux/irq.h>
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/mod_devicetable.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/slab.h>
|
||||
|
||||
#include <linux/platform_data/adp5588.h>
|
||||
|
||||
/*
|
||||
* Early pre 4.0 Silicon required to delay readout by at least 25ms,
|
||||
* since the Event Counter Register updated 25ms after the interrupt
|
||||
* asserted.
|
||||
*/
|
||||
#define WA_DELAYED_READOUT_REVID(rev) ((rev) < 4)
|
||||
|
||||
struct adp5588_gpio {
|
||||
struct i2c_client *client;
|
||||
struct gpio_chip gpio_chip;
|
||||
struct mutex lock; /* protect cached dir, dat_out */
|
||||
/* protect serialized access to the interrupt controller bus */
|
||||
struct mutex irq_lock;
|
||||
uint8_t dat_out[3];
|
||||
uint8_t dir[3];
|
||||
uint8_t int_lvl_low[3];
|
||||
uint8_t int_lvl_high[3];
|
||||
uint8_t int_en[3];
|
||||
uint8_t irq_mask[3];
|
||||
uint8_t int_input_en[3];
|
||||
};
|
||||
|
||||
static int adp5588_gpio_read(struct i2c_client *client, u8 reg)
|
||||
{
|
||||
int ret = i2c_smbus_read_byte_data(client, reg);
|
||||
|
||||
if (ret < 0)
|
||||
dev_err(&client->dev, "Read Error\n");
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int adp5588_gpio_write(struct i2c_client *client, u8 reg, u8 val)
|
||||
{
|
||||
int ret = i2c_smbus_write_byte_data(client, reg, val);
|
||||
|
||||
if (ret < 0)
|
||||
dev_err(&client->dev, "Write Error\n");
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int adp5588_gpio_get_value(struct gpio_chip *chip, unsigned off)
|
||||
{
|
||||
struct adp5588_gpio *dev = gpiochip_get_data(chip);
|
||||
unsigned bank = ADP5588_BANK(off);
|
||||
unsigned bit = ADP5588_BIT(off);
|
||||
int val;
|
||||
|
||||
mutex_lock(&dev->lock);
|
||||
|
||||
if (dev->dir[bank] & bit)
|
||||
val = dev->dat_out[bank];
|
||||
else
|
||||
val = adp5588_gpio_read(dev->client, GPIO_DAT_STAT1 + bank);
|
||||
|
||||
mutex_unlock(&dev->lock);
|
||||
|
||||
return !!(val & bit);
|
||||
}
|
||||
|
||||
static void adp5588_gpio_set_value(struct gpio_chip *chip,
|
||||
unsigned off, int val)
|
||||
{
|
||||
unsigned bank, bit;
|
||||
struct adp5588_gpio *dev = gpiochip_get_data(chip);
|
||||
|
||||
bank = ADP5588_BANK(off);
|
||||
bit = ADP5588_BIT(off);
|
||||
|
||||
mutex_lock(&dev->lock);
|
||||
if (val)
|
||||
dev->dat_out[bank] |= bit;
|
||||
else
|
||||
dev->dat_out[bank] &= ~bit;
|
||||
|
||||
adp5588_gpio_write(dev->client, GPIO_DAT_OUT1 + bank,
|
||||
dev->dat_out[bank]);
|
||||
mutex_unlock(&dev->lock);
|
||||
}
|
||||
|
||||
static int adp5588_gpio_direction_input(struct gpio_chip *chip, unsigned off)
|
||||
{
|
||||
int ret;
|
||||
unsigned bank;
|
||||
struct adp5588_gpio *dev = gpiochip_get_data(chip);
|
||||
|
||||
bank = ADP5588_BANK(off);
|
||||
|
||||
mutex_lock(&dev->lock);
|
||||
dev->dir[bank] &= ~ADP5588_BIT(off);
|
||||
ret = adp5588_gpio_write(dev->client, GPIO_DIR1 + bank, dev->dir[bank]);
|
||||
mutex_unlock(&dev->lock);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int adp5588_gpio_direction_output(struct gpio_chip *chip,
|
||||
unsigned off, int val)
|
||||
{
|
||||
int ret;
|
||||
unsigned bank, bit;
|
||||
struct adp5588_gpio *dev = gpiochip_get_data(chip);
|
||||
|
||||
bank = ADP5588_BANK(off);
|
||||
bit = ADP5588_BIT(off);
|
||||
|
||||
mutex_lock(&dev->lock);
|
||||
dev->dir[bank] |= bit;
|
||||
|
||||
if (val)
|
||||
dev->dat_out[bank] |= bit;
|
||||
else
|
||||
dev->dat_out[bank] &= ~bit;
|
||||
|
||||
ret = adp5588_gpio_write(dev->client, GPIO_DAT_OUT1 + bank,
|
||||
dev->dat_out[bank]);
|
||||
ret |= adp5588_gpio_write(dev->client, GPIO_DIR1 + bank,
|
||||
dev->dir[bank]);
|
||||
mutex_unlock(&dev->lock);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
#ifdef CONFIG_GPIO_ADP5588_IRQ
|
||||
|
||||
static void adp5588_irq_bus_lock(struct irq_data *d)
|
||||
{
|
||||
struct gpio_chip *gc = irq_data_get_irq_chip_data(d);
|
||||
struct adp5588_gpio *dev = gpiochip_get_data(gc);
|
||||
|
||||
mutex_lock(&dev->irq_lock);
|
||||
}
|
||||
|
||||
/*
|
||||
* genirq core code can issue chip->mask/unmask from atomic context.
|
||||
* This doesn't work for slow busses where an access needs to sleep.
|
||||
* bus_sync_unlock() is therefore called outside the atomic context,
|
||||
* syncs the current irq mask state with the slow external controller
|
||||
* and unlocks the bus.
|
||||
*/
|
||||
|
||||
static void adp5588_irq_bus_sync_unlock(struct irq_data *d)
|
||||
{
|
||||
struct gpio_chip *gc = irq_data_get_irq_chip_data(d);
|
||||
struct adp5588_gpio *dev = gpiochip_get_data(gc);
|
||||
int i;
|
||||
|
||||
for (i = 0; i <= ADP5588_BANK(ADP5588_MAXGPIO); i++) {
|
||||
if (dev->int_input_en[i]) {
|
||||
mutex_lock(&dev->lock);
|
||||
dev->dir[i] &= ~dev->int_input_en[i];
|
||||
dev->int_input_en[i] = 0;
|
||||
adp5588_gpio_write(dev->client, GPIO_DIR1 + i,
|
||||
dev->dir[i]);
|
||||
mutex_unlock(&dev->lock);
|
||||
}
|
||||
|
||||
if (dev->int_en[i] ^ dev->irq_mask[i]) {
|
||||
dev->int_en[i] = dev->irq_mask[i];
|
||||
adp5588_gpio_write(dev->client, GPI_EM1 + i,
|
||||
dev->int_en[i]);
|
||||
}
|
||||
}
|
||||
|
||||
mutex_unlock(&dev->irq_lock);
|
||||
}
|
||||
|
||||
static void adp5588_irq_mask(struct irq_data *d)
|
||||
{
|
||||
struct gpio_chip *gc = irq_data_get_irq_chip_data(d);
|
||||
struct adp5588_gpio *dev = gpiochip_get_data(gc);
|
||||
|
||||
dev->irq_mask[ADP5588_BANK(d->hwirq)] &= ~ADP5588_BIT(d->hwirq);
|
||||
}
|
||||
|
||||
static void adp5588_irq_unmask(struct irq_data *d)
|
||||
{
|
||||
struct gpio_chip *gc = irq_data_get_irq_chip_data(d);
|
||||
struct adp5588_gpio *dev = gpiochip_get_data(gc);
|
||||
|
||||
dev->irq_mask[ADP5588_BANK(d->hwirq)] |= ADP5588_BIT(d->hwirq);
|
||||
}
|
||||
|
||||
static int adp5588_irq_set_type(struct irq_data *d, unsigned int type)
|
||||
{
|
||||
struct gpio_chip *gc = irq_data_get_irq_chip_data(d);
|
||||
struct adp5588_gpio *dev = gpiochip_get_data(gc);
|
||||
uint16_t gpio = d->hwirq;
|
||||
unsigned bank, bit;
|
||||
|
||||
bank = ADP5588_BANK(gpio);
|
||||
bit = ADP5588_BIT(gpio);
|
||||
|
||||
dev->int_lvl_low[bank] &= ~bit;
|
||||
dev->int_lvl_high[bank] &= ~bit;
|
||||
|
||||
if (type & IRQ_TYPE_EDGE_BOTH || type & IRQ_TYPE_LEVEL_HIGH)
|
||||
dev->int_lvl_high[bank] |= bit;
|
||||
|
||||
if (type & IRQ_TYPE_EDGE_BOTH || type & IRQ_TYPE_LEVEL_LOW)
|
||||
dev->int_lvl_low[bank] |= bit;
|
||||
|
||||
dev->int_input_en[bank] |= bit;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static struct irq_chip adp5588_irq_chip = {
|
||||
.name = "adp5588",
|
||||
.irq_mask = adp5588_irq_mask,
|
||||
.irq_unmask = adp5588_irq_unmask,
|
||||
.irq_bus_lock = adp5588_irq_bus_lock,
|
||||
.irq_bus_sync_unlock = adp5588_irq_bus_sync_unlock,
|
||||
.irq_set_type = adp5588_irq_set_type,
|
||||
};
|
||||
|
||||
static irqreturn_t adp5588_irq_handler(int irq, void *devid)
|
||||
{
|
||||
struct adp5588_gpio *dev = devid;
|
||||
int status = adp5588_gpio_read(dev->client, INT_STAT);
|
||||
|
||||
if (status & ADP5588_KE_INT) {
|
||||
int ev_cnt = adp5588_gpio_read(dev->client, KEY_LCK_EC_STAT);
|
||||
|
||||
if (ev_cnt > 0) {
|
||||
int i;
|
||||
|
||||
for (i = 0; i < (ev_cnt & ADP5588_KEC); i++) {
|
||||
int key = adp5588_gpio_read(dev->client,
|
||||
Key_EVENTA + i);
|
||||
/* GPIN events begin at 97,
|
||||
* bit 7 indicates logic level
|
||||
*/
|
||||
int gpio = (key & 0x7f) - 97;
|
||||
int lvl = key & (1 << 7);
|
||||
int bank = ADP5588_BANK(gpio);
|
||||
int bit = ADP5588_BIT(gpio);
|
||||
|
||||
if ((lvl && dev->int_lvl_high[bank] & bit) ||
|
||||
(!lvl && dev->int_lvl_low[bank] & bit))
|
||||
handle_nested_irq(irq_find_mapping(
|
||||
dev->gpio_chip.irq.domain, gpio));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
adp5588_gpio_write(dev->client, INT_STAT, status); /* Status is W1C */
|
||||
|
||||
return IRQ_HANDLED;
|
||||
}
|
||||
|
||||
|
||||
static int adp5588_irq_init_hw(struct gpio_chip *gc)
|
||||
{
|
||||
struct adp5588_gpio *dev = gpiochip_get_data(gc);
|
||||
/* Enable IRQs after registering chip */
|
||||
adp5588_gpio_write(dev->client, CFG,
|
||||
ADP5588_AUTO_INC | ADP5588_INT_CFG | ADP5588_KE_IEN);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int adp5588_irq_setup(struct adp5588_gpio *dev)
|
||||
{
|
||||
struct i2c_client *client = dev->client;
|
||||
int ret;
|
||||
struct adp5588_gpio_platform_data *pdata =
|
||||
dev_get_platdata(&client->dev);
|
||||
struct gpio_irq_chip *girq;
|
||||
|
||||
adp5588_gpio_write(client, CFG, ADP5588_AUTO_INC);
|
||||
adp5588_gpio_write(client, INT_STAT, -1); /* status is W1C */
|
||||
|
||||
mutex_init(&dev->irq_lock);
|
||||
|
||||
ret = devm_request_threaded_irq(&client->dev, client->irq,
|
||||
NULL, adp5588_irq_handler, IRQF_ONESHOT
|
||||
| IRQF_TRIGGER_FALLING | IRQF_SHARED,
|
||||
dev_name(&client->dev), dev);
|
||||
if (ret) {
|
||||
dev_err(&client->dev, "failed to request irq %d\n",
|
||||
client->irq);
|
||||
return ret;
|
||||
}
|
||||
|
||||
/* This will be registered in the call to devm_gpiochip_add_data() */
|
||||
girq = &dev->gpio_chip.irq;
|
||||
girq->chip = &adp5588_irq_chip;
|
||||
/* This will let us handle the parent IRQ in the driver */
|
||||
girq->parent_handler = NULL;
|
||||
girq->num_parents = 0;
|
||||
girq->parents = NULL;
|
||||
girq->first = pdata ? pdata->irq_base : 0;
|
||||
girq->default_type = IRQ_TYPE_NONE;
|
||||
girq->handler = handle_simple_irq;
|
||||
girq->init_hw = adp5588_irq_init_hw;
|
||||
girq->threaded = true;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
#else
|
||||
static int adp5588_irq_setup(struct adp5588_gpio *dev)
|
||||
{
|
||||
struct i2c_client *client = dev->client;
|
||||
dev_warn(&client->dev, "interrupt support not compiled in\n");
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
#endif /* CONFIG_GPIO_ADP5588_IRQ */
|
||||
|
||||
static int adp5588_gpio_probe(struct i2c_client *client)
|
||||
{
|
||||
struct adp5588_gpio_platform_data *pdata =
|
||||
dev_get_platdata(&client->dev);
|
||||
struct adp5588_gpio *dev;
|
||||
struct gpio_chip *gc;
|
||||
int ret, i, revid;
|
||||
unsigned int pullup_dis_mask = 0;
|
||||
|
||||
if (!i2c_check_functionality(client->adapter,
|
||||
I2C_FUNC_SMBUS_BYTE_DATA)) {
|
||||
dev_err(&client->dev, "SMBUS Byte Data not Supported\n");
|
||||
return -EIO;
|
||||
}
|
||||
|
||||
dev = devm_kzalloc(&client->dev, sizeof(*dev), GFP_KERNEL);
|
||||
if (!dev)
|
||||
return -ENOMEM;
|
||||
|
||||
dev->client = client;
|
||||
|
||||
gc = &dev->gpio_chip;
|
||||
gc->direction_input = adp5588_gpio_direction_input;
|
||||
gc->direction_output = adp5588_gpio_direction_output;
|
||||
gc->get = adp5588_gpio_get_value;
|
||||
gc->set = adp5588_gpio_set_value;
|
||||
gc->can_sleep = true;
|
||||
gc->base = -1;
|
||||
gc->parent = &client->dev;
|
||||
|
||||
if (pdata) {
|
||||
gc->base = pdata->gpio_start;
|
||||
gc->names = pdata->names;
|
||||
pullup_dis_mask = pdata->pullup_dis_mask;
|
||||
}
|
||||
|
||||
gc->ngpio = ADP5588_MAXGPIO;
|
||||
gc->label = client->name;
|
||||
gc->owner = THIS_MODULE;
|
||||
|
||||
mutex_init(&dev->lock);
|
||||
|
||||
ret = adp5588_gpio_read(dev->client, DEV_ID);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
revid = ret & ADP5588_DEVICE_ID_MASK;
|
||||
|
||||
for (i = 0, ret = 0; i <= ADP5588_BANK(ADP5588_MAXGPIO); i++) {
|
||||
dev->dat_out[i] = adp5588_gpio_read(client, GPIO_DAT_OUT1 + i);
|
||||
dev->dir[i] = adp5588_gpio_read(client, GPIO_DIR1 + i);
|
||||
ret |= adp5588_gpio_write(client, KP_GPIO1 + i, 0);
|
||||
ret |= adp5588_gpio_write(client, GPIO_PULL1 + i,
|
||||
(pullup_dis_mask >> (8 * i)) & 0xFF);
|
||||
ret |= adp5588_gpio_write(client, GPIO_INT_EN1 + i, 0);
|
||||
if (ret)
|
||||
return ret;
|
||||
}
|
||||
|
||||
if (client->irq) {
|
||||
if (WA_DELAYED_READOUT_REVID(revid)) {
|
||||
dev_warn(&client->dev, "GPIO int not supported\n");
|
||||
} else {
|
||||
ret = adp5588_irq_setup(dev);
|
||||
if (ret)
|
||||
return ret;
|
||||
}
|
||||
}
|
||||
|
||||
ret = devm_gpiochip_add_data(&client->dev, &dev->gpio_chip, dev);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
i2c_set_clientdata(client, dev);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void adp5588_gpio_remove(struct i2c_client *client)
|
||||
{
|
||||
struct adp5588_gpio *dev = i2c_get_clientdata(client);
|
||||
|
||||
if (dev->client->irq)
|
||||
free_irq(dev->client->irq, dev);
|
||||
}
|
||||
|
||||
static const struct i2c_device_id adp5588_gpio_id[] = {
|
||||
{ "adp5588-gpio" },
|
||||
{}
|
||||
};
|
||||
MODULE_DEVICE_TABLE(i2c, adp5588_gpio_id);
|
||||
|
||||
static const struct of_device_id adp5588_gpio_of_id[] = {
|
||||
{ .compatible = "adi,adp5588-gpio" },
|
||||
{}
|
||||
};
|
||||
MODULE_DEVICE_TABLE(of, adp5588_gpio_of_id);
|
||||
|
||||
static struct i2c_driver adp5588_gpio_driver = {
|
||||
.driver = {
|
||||
.name = "adp5588-gpio",
|
||||
.of_match_table = adp5588_gpio_of_id,
|
||||
},
|
||||
.probe_new = adp5588_gpio_probe,
|
||||
.remove = adp5588_gpio_remove,
|
||||
.id_table = adp5588_gpio_id,
|
||||
};
|
||||
|
||||
module_i2c_driver(adp5588_gpio_driver);
|
||||
|
||||
MODULE_AUTHOR("Michael Hennerich <michael.hennerich@analog.com>");
|
||||
MODULE_DESCRIPTION("GPIO ADP5588 Driver");
|
||||
MODULE_LICENSE("GPL");
|
@ -1014,7 +1014,8 @@ static const char *absolutes[ABS_CNT] = {
|
||||
[ABS_HAT3Y] = "Hat 3Y", [ABS_PRESSURE] = "Pressure",
|
||||
[ABS_DISTANCE] = "Distance", [ABS_TILT_X] = "XTilt",
|
||||
[ABS_TILT_Y] = "YTilt", [ABS_TOOL_WIDTH] = "ToolWidth",
|
||||
[ABS_VOLUME] = "Volume", [ABS_MISC] = "Misc",
|
||||
[ABS_VOLUME] = "Volume", [ABS_PROFILE] = "Profile",
|
||||
[ABS_MISC] = "Misc",
|
||||
[ABS_MT_TOUCH_MAJOR] = "MTMajor",
|
||||
[ABS_MT_TOUCH_MINOR] = "MTMinor",
|
||||
[ABS_MT_WIDTH_MAJOR] = "MTMajorW",
|
||||
|
@ -6,9 +6,6 @@
|
||||
* Copyright (c) 2006 Dmitry Torokhov <dtor@mail.ru>
|
||||
*/
|
||||
|
||||
/*
|
||||
*/
|
||||
|
||||
/* #define DEBUG */
|
||||
|
||||
#include <linux/input.h>
|
||||
|
@ -6,9 +6,6 @@
|
||||
* Copyright (c) 2006 Dmitry Torokhov <dtor@mail.ru>
|
||||
*/
|
||||
|
||||
/*
|
||||
*/
|
||||
|
||||
/* #define DEBUG */
|
||||
|
||||
#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
|
||||
|
@ -7,9 +7,6 @@
|
||||
* EMU10k1 - SB Live / Audigy - gameport driver for Linux
|
||||
*/
|
||||
|
||||
/*
|
||||
*/
|
||||
|
||||
#include <asm/io.h>
|
||||
|
||||
#include <linux/module.h>
|
||||
|
@ -7,9 +7,6 @@
|
||||
* PDPI Lightning 4 gamecard driver for Linux.
|
||||
*/
|
||||
|
||||
/*
|
||||
*/
|
||||
|
||||
#include <asm/io.h>
|
||||
#include <linux/delay.h>
|
||||
#include <linux/errno.h>
|
||||
|
@ -8,9 +8,6 @@
|
||||
* NS558 based standard IBM game port driver for Linux
|
||||
*/
|
||||
|
||||
/*
|
||||
*/
|
||||
|
||||
#include <asm/io.h>
|
||||
|
||||
#include <linux/module.h>
|
||||
|
@ -746,7 +746,7 @@ static void joydev_cleanup(struct joydev *joydev)
|
||||
}
|
||||
|
||||
/*
|
||||
* These codes are copied from from hid-ids.h, unfortunately there is no common
|
||||
* These codes are copied from hid-ids.h, unfortunately there is no common
|
||||
* usb_ids/bt_ids.h header.
|
||||
*/
|
||||
#define USB_VENDOR_ID_SONY 0x054c
|
||||
|
@ -7,9 +7,6 @@
|
||||
* FP-Gaming Assassin 3D joystick driver for Linux
|
||||
*/
|
||||
|
||||
/*
|
||||
*/
|
||||
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/slab.h>
|
||||
|
@ -26,8 +26,23 @@ struct adc_joystick {
|
||||
struct adc_joystick_axis *axes;
|
||||
struct iio_channel *chans;
|
||||
int num_chans;
|
||||
bool polled;
|
||||
};
|
||||
|
||||
static void adc_joystick_poll(struct input_dev *input)
|
||||
{
|
||||
struct adc_joystick *joy = input_get_drvdata(input);
|
||||
int i, val, ret;
|
||||
|
||||
for (i = 0; i < joy->num_chans; i++) {
|
||||
ret = iio_read_channel_raw(&joy->chans[i], &val);
|
||||
if (ret < 0)
|
||||
return;
|
||||
input_report_abs(input, joy->axes[i].code, val);
|
||||
}
|
||||
input_sync(input);
|
||||
}
|
||||
|
||||
static int adc_joystick_handle(const void *data, void *private)
|
||||
{
|
||||
struct adc_joystick *joy = private;
|
||||
@ -179,6 +194,7 @@ static int adc_joystick_probe(struct platform_device *pdev)
|
||||
int error;
|
||||
int bits;
|
||||
int i;
|
||||
unsigned int poll_interval;
|
||||
|
||||
joy = devm_kzalloc(dev, sizeof(*joy), GFP_KERNEL);
|
||||
if (!joy)
|
||||
@ -192,8 +208,25 @@ static int adc_joystick_probe(struct platform_device *pdev)
|
||||
return error;
|
||||
}
|
||||
|
||||
/* Count how many channels we got. NULL terminated. */
|
||||
error = device_property_read_u32(dev, "poll-interval", &poll_interval);
|
||||
if (error) {
|
||||
/* -EINVAL means the property is absent. */
|
||||
if (error != -EINVAL)
|
||||
return error;
|
||||
} else if (poll_interval == 0) {
|
||||
dev_err(dev, "Unable to get poll-interval\n");
|
||||
return -EINVAL;
|
||||
} else {
|
||||
joy->polled = true;
|
||||
}
|
||||
|
||||
/*
|
||||
* Count how many channels we got. NULL terminated.
|
||||
* Do not check the storage size if using polling.
|
||||
*/
|
||||
for (i = 0; joy->chans[i].indio_dev; i++) {
|
||||
if (joy->polled)
|
||||
continue;
|
||||
bits = joy->chans[i].channel->scan_type.storagebits;
|
||||
if (!bits || bits > 16) {
|
||||
dev_err(dev, "Unsupported channel storage size\n");
|
||||
@ -215,24 +248,32 @@ static int adc_joystick_probe(struct platform_device *pdev)
|
||||
joy->input = input;
|
||||
input->name = pdev->name;
|
||||
input->id.bustype = BUS_HOST;
|
||||
input->open = adc_joystick_open;
|
||||
input->close = adc_joystick_close;
|
||||
|
||||
error = adc_joystick_set_axes(dev, joy);
|
||||
if (error)
|
||||
return error;
|
||||
|
||||
joy->buffer = iio_channel_get_all_cb(dev, adc_joystick_handle, joy);
|
||||
if (joy->polled) {
|
||||
input_setup_polling(input, adc_joystick_poll);
|
||||
input_set_poll_interval(input, poll_interval);
|
||||
} else {
|
||||
input->open = adc_joystick_open;
|
||||
input->close = adc_joystick_close;
|
||||
|
||||
joy->buffer = iio_channel_get_all_cb(dev, adc_joystick_handle,
|
||||
joy);
|
||||
if (IS_ERR(joy->buffer)) {
|
||||
dev_err(dev, "Unable to allocate callback buffer\n");
|
||||
return PTR_ERR(joy->buffer);
|
||||
}
|
||||
|
||||
error = devm_add_action_or_reset(dev, adc_joystick_cleanup, joy->buffer);
|
||||
error = devm_add_action_or_reset(dev, adc_joystick_cleanup,
|
||||
joy->buffer);
|
||||
if (error) {
|
||||
dev_err(dev, "Unable to add action\n");
|
||||
return error;
|
||||
}
|
||||
}
|
||||
|
||||
input_set_drvdata(input, joy);
|
||||
|
||||
|
@ -7,9 +7,6 @@
|
||||
* Logitech ADI joystick family driver for Linux
|
||||
*/
|
||||
|
||||
/*
|
||||
*/
|
||||
|
||||
#include <linux/delay.h>
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/module.h>
|
||||
|
@ -7,9 +7,6 @@
|
||||
* Driver for Amiga joysticks for Linux/m68k
|
||||
*/
|
||||
|
||||
/*
|
||||
*/
|
||||
|
||||
#include <linux/types.h>
|
||||
#include <linux/errno.h>
|
||||
#include <linux/kernel.h>
|
||||
|
@ -7,9 +7,6 @@
|
||||
* Analog joystick and gamepad driver for Linux
|
||||
*/
|
||||
|
||||
/*
|
||||
*/
|
||||
|
||||
#include <linux/delay.h>
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/module.h>
|
||||
|
@ -7,9 +7,6 @@
|
||||
* Creative Labs Blaster GamePad Cobra driver for Linux
|
||||
*/
|
||||
|
||||
/*
|
||||
*/
|
||||
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/slab.h>
|
||||
|
@ -10,9 +10,6 @@
|
||||
* Atari, Amstrad, Commodore, Amiga, Sega, etc. joystick driver for Linux
|
||||
*/
|
||||
|
||||
/*
|
||||
*/
|
||||
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/delay.h>
|
||||
|
@ -11,9 +11,6 @@
|
||||
* Raphael Assenat
|
||||
*/
|
||||
|
||||
/*
|
||||
*/
|
||||
|
||||
#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
|
||||
|
||||
#include <linux/kernel.h>
|
||||
|
@ -7,9 +7,6 @@
|
||||
* Genius Flight 2000 joystick driver for Linux
|
||||
*/
|
||||
|
||||
/*
|
||||
*/
|
||||
|
||||
#include <linux/delay.h>
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/slab.h>
|
||||
|
@ -7,9 +7,6 @@
|
||||
* Gravis/Kensington GrIP protocol joystick and gamepad driver for Linux
|
||||
*/
|
||||
|
||||
/*
|
||||
*/
|
||||
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/slab.h>
|
||||
|
@ -7,9 +7,6 @@
|
||||
* Guillemot Digital Interface Protocol driver for Linux
|
||||
*/
|
||||
|
||||
/*
|
||||
*/
|
||||
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/module.h>
|
||||
|
@ -10,9 +10,6 @@
|
||||
* InterAct digital gamepad/joystick driver for Linux
|
||||
*/
|
||||
|
||||
/*
|
||||
*/
|
||||
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/module.h>
|
||||
|
@ -8,9 +8,6 @@
|
||||
* out of the joystick port into the syslog ...
|
||||
*/
|
||||
|
||||
/*
|
||||
*/
|
||||
|
||||
#include <linux/module.h>
|
||||
#include <linux/gameport.h>
|
||||
#include <linux/kernel.h>
|
||||
|
@ -7,9 +7,6 @@
|
||||
* Magellan and Space Mouse 6dof controller driver for Linux
|
||||
*/
|
||||
|
||||
/*
|
||||
*/
|
||||
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/slab.h>
|
||||
|
@ -7,9 +7,6 @@
|
||||
* Microsoft SideWinder joystick family driver for Linux
|
||||
*/
|
||||
|
||||
/*
|
||||
*/
|
||||
|
||||
#include <linux/delay.h>
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/module.h>
|
||||
|
@ -11,9 +11,6 @@
|
||||
* SpaceTec SpaceBall 2003/3003/4000 FLX driver for Linux
|
||||
*/
|
||||
|
||||
/*
|
||||
*/
|
||||
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/module.h>
|
||||
|
@ -10,9 +10,6 @@
|
||||
* SpaceTec SpaceOrb 360 and Avenger 6dof controller driver for Linux
|
||||
*/
|
||||
|
||||
/*
|
||||
*/
|
||||
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/module.h>
|
||||
|
@ -8,9 +8,6 @@
|
||||
* Gravis Stinger gamepad driver for Linux
|
||||
*/
|
||||
|
||||
/*
|
||||
*/
|
||||
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/slab.h>
|
||||
|
@ -10,9 +10,6 @@
|
||||
* ThrustMaster DirectConnect (BSP) joystick family driver for Linux
|
||||
*/
|
||||
|
||||
/*
|
||||
*/
|
||||
|
||||
#include <linux/delay.h>
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/slab.h>
|
||||
|
@ -10,9 +10,6 @@
|
||||
* TurboGraFX parallel port interface driver for Linux.
|
||||
*/
|
||||
|
||||
/*
|
||||
*/
|
||||
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/parport.h>
|
||||
#include <linux/input.h>
|
||||
|
@ -32,9 +32,6 @@
|
||||
* Arndt Schoenewald <arndt@quelltext.com>
|
||||
*/
|
||||
|
||||
/*
|
||||
*/
|
||||
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/slab.h>
|
||||
|
@ -7,9 +7,6 @@
|
||||
* Logitech WingMan Warrior joystick driver for Linux
|
||||
*/
|
||||
|
||||
/*
|
||||
*/
|
||||
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/slab.h>
|
||||
|
@ -61,6 +61,7 @@
|
||||
* Later changes can be tracked in SCM.
|
||||
*/
|
||||
|
||||
#include <linux/bits.h>
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/input.h>
|
||||
#include <linux/rcupdate.h>
|
||||
@ -80,6 +81,9 @@
|
||||
#define MAP_TRIGGERS_TO_BUTTONS (1 << 1)
|
||||
#define MAP_STICKS_TO_NULL (1 << 2)
|
||||
#define MAP_SELECT_BUTTON (1 << 3)
|
||||
#define MAP_PADDLES (1 << 4)
|
||||
#define MAP_PROFILE_BUTTON (1 << 5)
|
||||
|
||||
#define DANCEPAD_MAP_CONFIG (MAP_DPAD_TO_BUTTONS | \
|
||||
MAP_TRIGGERS_TO_BUTTONS | MAP_STICKS_TO_NULL)
|
||||
|
||||
@ -89,6 +93,17 @@
|
||||
#define XTYPE_XBOXONE 3
|
||||
#define XTYPE_UNKNOWN 4
|
||||
|
||||
/* Send power-off packet to xpad360w after holding the mode button for this many
|
||||
* seconds
|
||||
*/
|
||||
#define XPAD360W_POWEROFF_TIMEOUT 5
|
||||
|
||||
#define PKT_XB 0
|
||||
#define PKT_XBE1 1
|
||||
#define PKT_XBE2_FW_OLD 2
|
||||
#define PKT_XBE2_FW_5_EARLY 3
|
||||
#define PKT_XBE2_FW_5_11 4
|
||||
|
||||
static bool dpad_to_buttons;
|
||||
module_param(dpad_to_buttons, bool, S_IRUGO);
|
||||
MODULE_PARM_DESC(dpad_to_buttons, "Map D-PAD to buttons rather than axes for unknown pads");
|
||||
@ -111,8 +126,11 @@ static const struct xpad_device {
|
||||
char *name;
|
||||
u8 mapping;
|
||||
u8 xtype;
|
||||
u8 packet_type;
|
||||
} xpad_device[] = {
|
||||
{ 0x0079, 0x18d4, "GPD Win 2 X-Box Controller", 0, XTYPE_XBOX360 },
|
||||
{ 0x03eb, 0xff01, "Wooting One (Legacy)", 0, XTYPE_XBOX360 },
|
||||
{ 0x03eb, 0xff02, "Wooting Two (Legacy)", 0, XTYPE_XBOX360 },
|
||||
{ 0x044f, 0x0f00, "Thrustmaster Wheel", 0, XTYPE_XBOX },
|
||||
{ 0x044f, 0x0f03, "Thrustmaster Wheel", 0, XTYPE_XBOX },
|
||||
{ 0x044f, 0x0f07, "Thrustmaster, Inc. Controller", 0, XTYPE_XBOX },
|
||||
@ -128,9 +146,11 @@ static const struct xpad_device {
|
||||
{ 0x045e, 0x0291, "Xbox 360 Wireless Receiver (XBOX)", MAP_DPAD_TO_BUTTONS, XTYPE_XBOX360W },
|
||||
{ 0x045e, 0x02d1, "Microsoft X-Box One pad", 0, XTYPE_XBOXONE },
|
||||
{ 0x045e, 0x02dd, "Microsoft X-Box One pad (Firmware 2015)", 0, XTYPE_XBOXONE },
|
||||
{ 0x045e, 0x02e3, "Microsoft X-Box One Elite pad", 0, XTYPE_XBOXONE },
|
||||
{ 0x045e, 0x02e3, "Microsoft X-Box One Elite pad", MAP_PADDLES, XTYPE_XBOXONE },
|
||||
{ 0x045e, 0x0b00, "Microsoft X-Box One Elite 2 pad", MAP_PADDLES, XTYPE_XBOXONE },
|
||||
{ 0x045e, 0x02ea, "Microsoft X-Box One S pad", 0, XTYPE_XBOXONE },
|
||||
{ 0x045e, 0x0719, "Xbox 360 Wireless Receiver", MAP_DPAD_TO_BUTTONS, XTYPE_XBOX360W },
|
||||
{ 0x045e, 0x0b0a, "Microsoft X-Box Adaptive Controller", MAP_PROFILE_BUTTON, XTYPE_XBOXONE },
|
||||
{ 0x045e, 0x0b12, "Microsoft Xbox Series S|X Controller", MAP_SELECT_BUTTON, XTYPE_XBOXONE },
|
||||
{ 0x046d, 0xc21d, "Logitech Gamepad F310", 0, XTYPE_XBOX360 },
|
||||
{ 0x046d, 0xc21e, "Logitech Gamepad F510", 0, XTYPE_XBOX360 },
|
||||
@ -244,6 +264,7 @@ static const struct xpad_device {
|
||||
{ 0x0f0d, 0x0063, "Hori Real Arcade Pro Hayabusa (USA) Xbox One", MAP_TRIGGERS_TO_BUTTONS, XTYPE_XBOXONE },
|
||||
{ 0x0f0d, 0x0067, "HORIPAD ONE", 0, XTYPE_XBOXONE },
|
||||
{ 0x0f0d, 0x0078, "Hori Real Arcade Pro V Kai Xbox One", MAP_TRIGGERS_TO_BUTTONS, XTYPE_XBOXONE },
|
||||
{ 0x0f0d, 0x00c5, "Hori Fighting Commander ONE", MAP_TRIGGERS_TO_BUTTONS, XTYPE_XBOXONE },
|
||||
{ 0x0f30, 0x010b, "Philips Recoil", 0, XTYPE_XBOX },
|
||||
{ 0x0f30, 0x0202, "Joytech Advanced Controller", 0, XTYPE_XBOX },
|
||||
{ 0x0f30, 0x8888, "BigBen XBMiniPad Controller", 0, XTYPE_XBOX },
|
||||
@ -260,6 +281,7 @@ static const struct xpad_device {
|
||||
{ 0x1430, 0x8888, "TX6500+ Dance Pad (first generation)", MAP_DPAD_TO_BUTTONS, XTYPE_XBOX },
|
||||
{ 0x1430, 0xf801, "RedOctane Controller", 0, XTYPE_XBOX360 },
|
||||
{ 0x146b, 0x0601, "BigBen Interactive XBOX 360 Controller", 0, XTYPE_XBOX360 },
|
||||
{ 0x146b, 0x0604, "Bigben Interactive DAIJA Arcade Stick", MAP_TRIGGERS_TO_BUTTONS, XTYPE_XBOX360 },
|
||||
{ 0x1532, 0x0037, "Razer Sabertooth", 0, XTYPE_XBOX360 },
|
||||
{ 0x1532, 0x0a00, "Razer Atrox Arcade Stick", MAP_TRIGGERS_TO_BUTTONS, XTYPE_XBOXONE },
|
||||
{ 0x1532, 0x0a03, "Razer Wildcat", 0, XTYPE_XBOXONE },
|
||||
@ -325,6 +347,7 @@ static const struct xpad_device {
|
||||
{ 0x24c6, 0x5502, "Hori Fighting Stick VX Alt", MAP_TRIGGERS_TO_BUTTONS, XTYPE_XBOX360 },
|
||||
{ 0x24c6, 0x5503, "Hori Fighting Edge", MAP_TRIGGERS_TO_BUTTONS, XTYPE_XBOX360 },
|
||||
{ 0x24c6, 0x5506, "Hori SOULCALIBUR V Stick", 0, XTYPE_XBOX360 },
|
||||
{ 0x24c6, 0x5510, "Hori Fighting Commander ONE (Xbox 360/PC Mode)", MAP_TRIGGERS_TO_BUTTONS, XTYPE_XBOX360 },
|
||||
{ 0x24c6, 0x550d, "Hori GEM Xbox controller", 0, XTYPE_XBOX360 },
|
||||
{ 0x24c6, 0x550e, "Hori Real Arcade Pro V Kai 360", MAP_TRIGGERS_TO_BUTTONS, XTYPE_XBOX360 },
|
||||
{ 0x24c6, 0x551a, "PowerA FUSION Pro Controller", 0, XTYPE_XBOXONE },
|
||||
@ -334,6 +357,14 @@ static const struct xpad_device {
|
||||
{ 0x24c6, 0x5b03, "Thrustmaster Ferrari 458 Racing Wheel", 0, XTYPE_XBOX360 },
|
||||
{ 0x24c6, 0x5d04, "Razer Sabertooth", 0, XTYPE_XBOX360 },
|
||||
{ 0x24c6, 0xfafe, "Rock Candy Gamepad for Xbox 360", 0, XTYPE_XBOX360 },
|
||||
{ 0x2563, 0x058d, "OneXPlayer Gamepad", 0, XTYPE_XBOX360 },
|
||||
{ 0x2dc8, 0x2000, "8BitDo Pro 2 Wired Controller fox Xbox", 0, XTYPE_XBOXONE },
|
||||
{ 0x31e3, 0x1100, "Wooting One", 0, XTYPE_XBOX360 },
|
||||
{ 0x31e3, 0x1200, "Wooting Two", 0, XTYPE_XBOX360 },
|
||||
{ 0x31e3, 0x1210, "Wooting Lekker", 0, XTYPE_XBOX360 },
|
||||
{ 0x31e3, 0x1220, "Wooting Two HE", 0, XTYPE_XBOX360 },
|
||||
{ 0x31e3, 0x1300, "Wooting 60HE (AVR)", 0, XTYPE_XBOX360 },
|
||||
{ 0x31e3, 0x1310, "Wooting 60HE (ARM)", 0, XTYPE_XBOX360 },
|
||||
{ 0x3285, 0x0607, "Nacon GC-100", 0, XTYPE_XBOX360 },
|
||||
{ 0x3767, 0x0101, "Fanatec Speedster 3 Forceshock Wheel", 0, XTYPE_XBOX },
|
||||
{ 0xffff, 0xffff, "Chinese-made Xbox Controller", 0, XTYPE_XBOX },
|
||||
@ -390,6 +421,13 @@ static const signed short xpad_abs_triggers[] = {
|
||||
-1
|
||||
};
|
||||
|
||||
/* used when the controller has extra paddle buttons */
|
||||
static const signed short xpad_btn_paddles[] = {
|
||||
BTN_TRIGGER_HAPPY5, BTN_TRIGGER_HAPPY6, /* paddle upper right, lower right */
|
||||
BTN_TRIGGER_HAPPY7, BTN_TRIGGER_HAPPY8, /* paddle upper left, lower left */
|
||||
-1 /* terminating entry */
|
||||
};
|
||||
|
||||
/*
|
||||
* Xbox 360 has a vendor-specific class, so we cannot match it with only
|
||||
* USB_INTERFACE_INFO (also specifically refused by USB subsystem), so we
|
||||
@ -419,6 +457,7 @@ static const signed short xpad_abs_triggers[] = {
|
||||
static const struct usb_device_id xpad_table[] = {
|
||||
{ USB_INTERFACE_INFO('X', 'B', 0) }, /* X-Box USB-IF not approved class */
|
||||
XPAD_XBOX360_VENDOR(0x0079), /* GPD Win 2 Controller */
|
||||
XPAD_XBOX360_VENDOR(0x03eb), /* Wooting Keyboards (Legacy) */
|
||||
XPAD_XBOX360_VENDOR(0x044f), /* Thrustmaster X-Box 360 controllers */
|
||||
XPAD_XBOX360_VENDOR(0x045e), /* Microsoft X-Box 360 controllers */
|
||||
XPAD_XBOXONE_VENDOR(0x045e), /* Microsoft X-Box One controllers */
|
||||
@ -429,6 +468,7 @@ static const struct usb_device_id xpad_table[] = {
|
||||
{ USB_DEVICE(0x0738, 0x4540) }, /* Mad Catz Beat Pad */
|
||||
XPAD_XBOXONE_VENDOR(0x0738), /* Mad Catz FightStick TE 2 */
|
||||
XPAD_XBOX360_VENDOR(0x07ff), /* Mad Catz GamePad */
|
||||
XPAD_XBOX360_VENDOR(0x0c12), /* Zeroplus X-Box 360 controllers */
|
||||
XPAD_XBOX360_VENDOR(0x0e6f), /* 0x0e6f X-Box 360 controllers */
|
||||
XPAD_XBOXONE_VENDOR(0x0e6f), /* 0x0e6f X-Box One controllers */
|
||||
XPAD_XBOX360_VENDOR(0x0f0d), /* Hori Controllers */
|
||||
@ -450,8 +490,12 @@ static const struct usb_device_id xpad_table[] = {
|
||||
XPAD_XBOXONE_VENDOR(0x20d6), /* PowerA Controllers */
|
||||
XPAD_XBOX360_VENDOR(0x24c6), /* PowerA Controllers */
|
||||
XPAD_XBOXONE_VENDOR(0x24c6), /* PowerA Controllers */
|
||||
XPAD_XBOX360_VENDOR(0x2563), /* OneXPlayer Gamepad */
|
||||
XPAD_XBOX360_VENDOR(0x260d), /* Dareu H101 */
|
||||
XPAD_XBOXONE_VENDOR(0x2dc8), /* 8BitDo Pro 2 Wired Controller for Xbox */
|
||||
XPAD_XBOXONE_VENDOR(0x2e24), /* Hyperkin Duke X-Box One pad */
|
||||
XPAD_XBOX360_VENDOR(0x2f24), /* GameSir Controllers */
|
||||
XPAD_XBOX360_VENDOR(0x31e3), /* Wooting Keyboards */
|
||||
XPAD_XBOX360_VENDOR(0x3285), /* Nacon GC-100 */
|
||||
{ }
|
||||
};
|
||||
@ -473,13 +517,52 @@ struct xboxone_init_packet {
|
||||
.len = ARRAY_SIZE(_data), \
|
||||
}
|
||||
|
||||
/*
|
||||
* starting with xbox one, the game input protocol is used
|
||||
* magic numbers are taken from
|
||||
* - https://github.com/xpadneo/gip-dissector/blob/main/src/gip-dissector.lua
|
||||
* - https://github.com/medusalix/xone/blob/master/bus/protocol.c
|
||||
*/
|
||||
#define GIP_CMD_ACK 0x01
|
||||
#define GIP_CMD_IDENTIFY 0x04
|
||||
#define GIP_CMD_POWER 0x05
|
||||
#define GIP_CMD_AUTHENTICATE 0x06
|
||||
#define GIP_CMD_VIRTUAL_KEY 0x07
|
||||
#define GIP_CMD_RUMBLE 0x09
|
||||
#define GIP_CMD_LED 0x0a
|
||||
#define GIP_CMD_FIRMWARE 0x0c
|
||||
#define GIP_CMD_INPUT 0x20
|
||||
|
||||
#define GIP_SEQ0 0x00
|
||||
|
||||
#define GIP_OPT_ACK 0x10
|
||||
#define GIP_OPT_INTERNAL 0x20
|
||||
|
||||
/*
|
||||
* length of the command payload encoded with
|
||||
* https://en.wikipedia.org/wiki/LEB128
|
||||
* which is a no-op for N < 128
|
||||
*/
|
||||
#define GIP_PL_LEN(N) (N)
|
||||
|
||||
/*
|
||||
* payload specific defines
|
||||
*/
|
||||
#define GIP_PWR_ON 0x00
|
||||
#define GIP_LED_ON 0x01
|
||||
|
||||
#define GIP_MOTOR_R BIT(0)
|
||||
#define GIP_MOTOR_L BIT(1)
|
||||
#define GIP_MOTOR_RT BIT(2)
|
||||
#define GIP_MOTOR_LT BIT(3)
|
||||
#define GIP_MOTOR_ALL (GIP_MOTOR_R | GIP_MOTOR_L | GIP_MOTOR_RT | GIP_MOTOR_LT)
|
||||
|
||||
/*
|
||||
* This packet is required for all Xbox One pads with 2015
|
||||
* or later firmware installed (or present from the factory).
|
||||
*/
|
||||
static const u8 xboxone_fw2015_init[] = {
|
||||
0x05, 0x20, 0x00, 0x01, 0x00
|
||||
static const u8 xboxone_power_on[] = {
|
||||
GIP_CMD_POWER, GIP_OPT_INTERNAL, GIP_SEQ0, GIP_PL_LEN(1), GIP_PWR_ON
|
||||
};
|
||||
|
||||
/*
|
||||
@ -489,7 +572,16 @@ static const u8 xboxone_fw2015_init[] = {
|
||||
* Bluetooth mode.
|
||||
*/
|
||||
static const u8 xboxone_s_init[] = {
|
||||
0x05, 0x20, 0x00, 0x0f, 0x06
|
||||
GIP_CMD_POWER, GIP_OPT_INTERNAL, GIP_SEQ0, 0x0f, 0x06
|
||||
};
|
||||
|
||||
/*
|
||||
* This packet is required to get additional input data
|
||||
* from Xbox One Elite Series 2 (0x045e:0x0b00) pads.
|
||||
* We mostly do this right now to get paddle data
|
||||
*/
|
||||
static const u8 extra_input_packet_init[] = {
|
||||
0x4d, 0x10, 0x01, 0x02, 0x07, 0x00
|
||||
};
|
||||
|
||||
/*
|
||||
@ -497,9 +589,9 @@ static const u8 xboxone_s_init[] = {
|
||||
* (0x0e6f:0x0165) to finish initialization and for Hori pads
|
||||
* (0x0f0d:0x0067) to make the analog sticks work.
|
||||
*/
|
||||
static const u8 xboxone_hori_init[] = {
|
||||
0x01, 0x20, 0x00, 0x09, 0x00, 0x04, 0x20, 0x3a,
|
||||
0x00, 0x00, 0x00, 0x80, 0x00
|
||||
static const u8 xboxone_hori_ack_id[] = {
|
||||
GIP_CMD_ACK, GIP_OPT_INTERNAL, GIP_SEQ0, GIP_PL_LEN(9),
|
||||
0x00, GIP_CMD_IDENTIFY, GIP_OPT_INTERNAL, 0x3a, 0x00, 0x00, 0x00, 0x80, 0x00
|
||||
};
|
||||
|
||||
/*
|
||||
@ -507,8 +599,8 @@ static const u8 xboxone_hori_init[] = {
|
||||
* sending input reports. These pads include: (0x0e6f:0x02ab),
|
||||
* (0x0e6f:0x02a4), (0x0e6f:0x02a6).
|
||||
*/
|
||||
static const u8 xboxone_pdp_init1[] = {
|
||||
0x0a, 0x20, 0x00, 0x03, 0x00, 0x01, 0x14
|
||||
static const u8 xboxone_pdp_led_on[] = {
|
||||
GIP_CMD_LED, GIP_OPT_INTERNAL, GIP_SEQ0, GIP_PL_LEN(3), 0x00, GIP_LED_ON, 0x14
|
||||
};
|
||||
|
||||
/*
|
||||
@ -516,8 +608,8 @@ static const u8 xboxone_pdp_init1[] = {
|
||||
* sending input reports. These pads include: (0x0e6f:0x02ab),
|
||||
* (0x0e6f:0x02a4), (0x0e6f:0x02a6).
|
||||
*/
|
||||
static const u8 xboxone_pdp_init2[] = {
|
||||
0x06, 0x20, 0x00, 0x02, 0x01, 0x00
|
||||
static const u8 xboxone_pdp_auth[] = {
|
||||
GIP_CMD_AUTHENTICATE, GIP_OPT_INTERNAL, GIP_SEQ0, GIP_PL_LEN(2), 0x01, 0x00
|
||||
};
|
||||
|
||||
/*
|
||||
@ -525,8 +617,8 @@ static const u8 xboxone_pdp_init2[] = {
|
||||
* sending input reports. One of those pads is (0x24c6:0x543a).
|
||||
*/
|
||||
static const u8 xboxone_rumblebegin_init[] = {
|
||||
0x09, 0x00, 0x00, 0x09, 0x00, 0x0F, 0x00, 0x00,
|
||||
0x1D, 0x1D, 0xFF, 0x00, 0x00
|
||||
GIP_CMD_RUMBLE, 0x00, GIP_SEQ0, GIP_PL_LEN(9),
|
||||
0x00, GIP_MOTOR_ALL, 0x00, 0x00, 0x1D, 0x1D, 0xFF, 0x00, 0x00
|
||||
};
|
||||
|
||||
/*
|
||||
@ -536,8 +628,8 @@ static const u8 xboxone_rumblebegin_init[] = {
|
||||
* spin up to enough speed to actually vibrate the gamepad.
|
||||
*/
|
||||
static const u8 xboxone_rumbleend_init[] = {
|
||||
0x09, 0x00, 0x00, 0x09, 0x00, 0x0F, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00
|
||||
GIP_CMD_RUMBLE, 0x00, GIP_SEQ0, GIP_PL_LEN(9),
|
||||
0x00, GIP_MOTOR_ALL, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
|
||||
};
|
||||
|
||||
/*
|
||||
@ -547,13 +639,14 @@ static const u8 xboxone_rumbleend_init[] = {
|
||||
* packet is going to be sent.
|
||||
*/
|
||||
static const struct xboxone_init_packet xboxone_init_packets[] = {
|
||||
XBOXONE_INIT_PKT(0x0e6f, 0x0165, xboxone_hori_init),
|
||||
XBOXONE_INIT_PKT(0x0f0d, 0x0067, xboxone_hori_init),
|
||||
XBOXONE_INIT_PKT(0x0000, 0x0000, xboxone_fw2015_init),
|
||||
XBOXONE_INIT_PKT(0x0e6f, 0x0165, xboxone_hori_ack_id),
|
||||
XBOXONE_INIT_PKT(0x0f0d, 0x0067, xboxone_hori_ack_id),
|
||||
XBOXONE_INIT_PKT(0x0000, 0x0000, xboxone_power_on),
|
||||
XBOXONE_INIT_PKT(0x045e, 0x02ea, xboxone_s_init),
|
||||
XBOXONE_INIT_PKT(0x045e, 0x0b00, xboxone_s_init),
|
||||
XBOXONE_INIT_PKT(0x0e6f, 0x0000, xboxone_pdp_init1),
|
||||
XBOXONE_INIT_PKT(0x0e6f, 0x0000, xboxone_pdp_init2),
|
||||
XBOXONE_INIT_PKT(0x045e, 0x0b00, extra_input_packet_init),
|
||||
XBOXONE_INIT_PKT(0x0e6f, 0x0000, xboxone_pdp_led_on),
|
||||
XBOXONE_INIT_PKT(0x0e6f, 0x0000, xboxone_pdp_auth),
|
||||
XBOXONE_INIT_PKT(0x24c6, 0x541a, xboxone_rumblebegin_init),
|
||||
XBOXONE_INIT_PKT(0x24c6, 0x542a, xboxone_rumblebegin_init),
|
||||
XBOXONE_INIT_PKT(0x24c6, 0x543a, xboxone_rumblebegin_init),
|
||||
@ -608,14 +701,17 @@ struct usb_xpad {
|
||||
|
||||
int mapping; /* map d-pad to buttons or to axes */
|
||||
int xtype; /* type of xbox device */
|
||||
int packet_type; /* type of the extended packet */
|
||||
int pad_nr; /* the order x360 pads were attached */
|
||||
const char *name; /* name of the device */
|
||||
struct work_struct work; /* init/remove device from callback */
|
||||
time64_t mode_btn_down_ts;
|
||||
};
|
||||
|
||||
static int xpad_init_input(struct usb_xpad *xpad);
|
||||
static void xpad_deinit_input(struct usb_xpad *xpad);
|
||||
static void xpadone_ack_mode_report(struct usb_xpad *xpad, u8 seq_num);
|
||||
static void xpad360w_poweroff_controller(struct usb_xpad *xpad);
|
||||
|
||||
/*
|
||||
* xpad_process_packet
|
||||
@ -656,10 +752,10 @@ static void xpad_process_packet(struct usb_xpad *xpad, u16 cmd, unsigned char *d
|
||||
/* digital pad */
|
||||
if (xpad->mapping & MAP_DPAD_TO_BUTTONS) {
|
||||
/* dpad as buttons (left, right, up, down) */
|
||||
input_report_key(dev, BTN_TRIGGER_HAPPY1, data[2] & 0x04);
|
||||
input_report_key(dev, BTN_TRIGGER_HAPPY2, data[2] & 0x08);
|
||||
input_report_key(dev, BTN_TRIGGER_HAPPY3, data[2] & 0x01);
|
||||
input_report_key(dev, BTN_TRIGGER_HAPPY4, data[2] & 0x02);
|
||||
input_report_key(dev, BTN_TRIGGER_HAPPY1, data[2] & BIT(2));
|
||||
input_report_key(dev, BTN_TRIGGER_HAPPY2, data[2] & BIT(3));
|
||||
input_report_key(dev, BTN_TRIGGER_HAPPY3, data[2] & BIT(0));
|
||||
input_report_key(dev, BTN_TRIGGER_HAPPY4, data[2] & BIT(1));
|
||||
} else {
|
||||
input_report_abs(dev, ABS_HAT0X,
|
||||
!!(data[2] & 0x08) - !!(data[2] & 0x04));
|
||||
@ -668,10 +764,10 @@ static void xpad_process_packet(struct usb_xpad *xpad, u16 cmd, unsigned char *d
|
||||
}
|
||||
|
||||
/* start/back buttons and stick press left/right */
|
||||
input_report_key(dev, BTN_START, data[2] & 0x10);
|
||||
input_report_key(dev, BTN_SELECT, data[2] & 0x20);
|
||||
input_report_key(dev, BTN_THUMBL, data[2] & 0x40);
|
||||
input_report_key(dev, BTN_THUMBR, data[2] & 0x80);
|
||||
input_report_key(dev, BTN_START, data[2] & BIT(4));
|
||||
input_report_key(dev, BTN_SELECT, data[2] & BIT(5));
|
||||
input_report_key(dev, BTN_THUMBL, data[2] & BIT(6));
|
||||
input_report_key(dev, BTN_THUMBR, data[2] & BIT(7));
|
||||
|
||||
/* "analog" buttons A, B, X, Y */
|
||||
input_report_key(dev, BTN_A, data[4]);
|
||||
@ -683,6 +779,10 @@ static void xpad_process_packet(struct usb_xpad *xpad, u16 cmd, unsigned char *d
|
||||
input_report_key(dev, BTN_C, data[8]);
|
||||
input_report_key(dev, BTN_Z, data[9]);
|
||||
|
||||
/* Profile button has a value of 0-3, so it is reported as an axis */
|
||||
if (xpad->mapping & MAP_PROFILE_BUTTON)
|
||||
input_report_abs(dev, ABS_PROFILE, data[34]);
|
||||
|
||||
input_sync(dev);
|
||||
}
|
||||
|
||||
@ -706,10 +806,10 @@ static void xpad360_process_packet(struct usb_xpad *xpad, struct input_dev *dev,
|
||||
/* digital pad */
|
||||
if (xpad->mapping & MAP_DPAD_TO_BUTTONS) {
|
||||
/* dpad as buttons (left, right, up, down) */
|
||||
input_report_key(dev, BTN_TRIGGER_HAPPY1, data[2] & 0x04);
|
||||
input_report_key(dev, BTN_TRIGGER_HAPPY2, data[2] & 0x08);
|
||||
input_report_key(dev, BTN_TRIGGER_HAPPY3, data[2] & 0x01);
|
||||
input_report_key(dev, BTN_TRIGGER_HAPPY4, data[2] & 0x02);
|
||||
input_report_key(dev, BTN_TRIGGER_HAPPY1, data[2] & BIT(2));
|
||||
input_report_key(dev, BTN_TRIGGER_HAPPY2, data[2] & BIT(3));
|
||||
input_report_key(dev, BTN_TRIGGER_HAPPY3, data[2] & BIT(0));
|
||||
input_report_key(dev, BTN_TRIGGER_HAPPY4, data[2] & BIT(1));
|
||||
}
|
||||
|
||||
/*
|
||||
@ -727,21 +827,21 @@ static void xpad360_process_packet(struct usb_xpad *xpad, struct input_dev *dev,
|
||||
}
|
||||
|
||||
/* start/back buttons */
|
||||
input_report_key(dev, BTN_START, data[2] & 0x10);
|
||||
input_report_key(dev, BTN_SELECT, data[2] & 0x20);
|
||||
input_report_key(dev, BTN_START, data[2] & BIT(4));
|
||||
input_report_key(dev, BTN_SELECT, data[2] & BIT(5));
|
||||
|
||||
/* stick press left/right */
|
||||
input_report_key(dev, BTN_THUMBL, data[2] & 0x40);
|
||||
input_report_key(dev, BTN_THUMBR, data[2] & 0x80);
|
||||
input_report_key(dev, BTN_THUMBL, data[2] & BIT(6));
|
||||
input_report_key(dev, BTN_THUMBR, data[2] & BIT(7));
|
||||
|
||||
/* buttons A,B,X,Y,TL,TR and MODE */
|
||||
input_report_key(dev, BTN_A, data[3] & 0x10);
|
||||
input_report_key(dev, BTN_B, data[3] & 0x20);
|
||||
input_report_key(dev, BTN_X, data[3] & 0x40);
|
||||
input_report_key(dev, BTN_Y, data[3] & 0x80);
|
||||
input_report_key(dev, BTN_TL, data[3] & 0x01);
|
||||
input_report_key(dev, BTN_TR, data[3] & 0x02);
|
||||
input_report_key(dev, BTN_MODE, data[3] & 0x04);
|
||||
input_report_key(dev, BTN_A, data[3] & BIT(4));
|
||||
input_report_key(dev, BTN_B, data[3] & BIT(5));
|
||||
input_report_key(dev, BTN_X, data[3] & BIT(6));
|
||||
input_report_key(dev, BTN_Y, data[3] & BIT(7));
|
||||
input_report_key(dev, BTN_TL, data[3] & BIT(0));
|
||||
input_report_key(dev, BTN_TR, data[3] & BIT(1));
|
||||
input_report_key(dev, BTN_MODE, data[3] & BIT(2));
|
||||
|
||||
if (!(xpad->mapping & MAP_STICKS_TO_NULL)) {
|
||||
/* left stick */
|
||||
@ -767,6 +867,23 @@ static void xpad360_process_packet(struct usb_xpad *xpad, struct input_dev *dev,
|
||||
}
|
||||
|
||||
input_sync(dev);
|
||||
|
||||
/* XBOX360W controllers can't be turned off without driver assistance */
|
||||
if (xpad->xtype == XTYPE_XBOX360W) {
|
||||
if (xpad->mode_btn_down_ts > 0 && xpad->pad_present &&
|
||||
((ktime_get_seconds() - xpad->mode_btn_down_ts) >=
|
||||
XPAD360W_POWEROFF_TIMEOUT)) {
|
||||
xpad360w_poweroff_controller(xpad);
|
||||
xpad->mode_btn_down_ts = 0;
|
||||
return;
|
||||
}
|
||||
|
||||
/* mode button down/up */
|
||||
if (data[3] & BIT(2))
|
||||
xpad->mode_btn_down_ts = ktime_get_seconds();
|
||||
else
|
||||
xpad->mode_btn_down_ts = 0;
|
||||
}
|
||||
}
|
||||
|
||||
static void xpad_presence_work(struct work_struct *work)
|
||||
@ -846,44 +963,60 @@ static void xpad360w_process_packet(struct usb_xpad *xpad, u16 cmd, unsigned cha
|
||||
static void xpadone_process_packet(struct usb_xpad *xpad, u16 cmd, unsigned char *data)
|
||||
{
|
||||
struct input_dev *dev = xpad->dev;
|
||||
bool do_sync = false;
|
||||
|
||||
/* the xbox button has its own special report */
|
||||
if (data[0] == 0X07) {
|
||||
if (data[0] == GIP_CMD_VIRTUAL_KEY) {
|
||||
/*
|
||||
* The Xbox One S controller requires these reports to be
|
||||
* acked otherwise it continues sending them forever and
|
||||
* won't report further mode button events.
|
||||
*/
|
||||
if (data[1] == 0x30)
|
||||
if (data[1] == (GIP_OPT_ACK | GIP_OPT_INTERNAL))
|
||||
xpadone_ack_mode_report(xpad, data[2]);
|
||||
|
||||
input_report_key(dev, BTN_MODE, data[4] & 0x01);
|
||||
input_report_key(dev, BTN_MODE, data[4] & GENMASK(1, 0));
|
||||
input_sync(dev);
|
||||
return;
|
||||
}
|
||||
/* check invalid packet */
|
||||
else if (data[0] != 0X20)
|
||||
return;
|
||||
|
||||
do_sync = true;
|
||||
} else if (data[0] == GIP_CMD_FIRMWARE) {
|
||||
/* Some packet formats force us to use this separate to poll paddle inputs */
|
||||
if (xpad->packet_type == PKT_XBE2_FW_5_11) {
|
||||
/* Mute paddles if controller is in a custom profile slot
|
||||
* Checked by looking at the active profile slot to
|
||||
* verify it's the default slot
|
||||
*/
|
||||
if (data[19] != 0)
|
||||
data[18] = 0;
|
||||
|
||||
/* Elite Series 2 split packet paddle bits */
|
||||
input_report_key(dev, BTN_TRIGGER_HAPPY5, data[18] & BIT(0));
|
||||
input_report_key(dev, BTN_TRIGGER_HAPPY6, data[18] & BIT(1));
|
||||
input_report_key(dev, BTN_TRIGGER_HAPPY7, data[18] & BIT(2));
|
||||
input_report_key(dev, BTN_TRIGGER_HAPPY8, data[18] & BIT(3));
|
||||
|
||||
do_sync = true;
|
||||
}
|
||||
} else if (data[0] == GIP_CMD_INPUT) { /* The main valid packet type for inputs */
|
||||
/* menu/view buttons */
|
||||
input_report_key(dev, BTN_START, data[4] & 0x04);
|
||||
input_report_key(dev, BTN_SELECT, data[4] & 0x08);
|
||||
input_report_key(dev, BTN_START, data[4] & BIT(2));
|
||||
input_report_key(dev, BTN_SELECT, data[4] & BIT(3));
|
||||
if (xpad->mapping & MAP_SELECT_BUTTON)
|
||||
input_report_key(dev, KEY_RECORD, data[22] & 0x01);
|
||||
input_report_key(dev, KEY_RECORD, data[22] & BIT(0));
|
||||
|
||||
/* buttons A,B,X,Y */
|
||||
input_report_key(dev, BTN_A, data[4] & 0x10);
|
||||
input_report_key(dev, BTN_B, data[4] & 0x20);
|
||||
input_report_key(dev, BTN_X, data[4] & 0x40);
|
||||
input_report_key(dev, BTN_Y, data[4] & 0x80);
|
||||
input_report_key(dev, BTN_A, data[4] & BIT(4));
|
||||
input_report_key(dev, BTN_B, data[4] & BIT(5));
|
||||
input_report_key(dev, BTN_X, data[4] & BIT(6));
|
||||
input_report_key(dev, BTN_Y, data[4] & BIT(7));
|
||||
|
||||
/* digital pad */
|
||||
if (xpad->mapping & MAP_DPAD_TO_BUTTONS) {
|
||||
/* dpad as buttons (left, right, up, down) */
|
||||
input_report_key(dev, BTN_TRIGGER_HAPPY1, data[5] & 0x04);
|
||||
input_report_key(dev, BTN_TRIGGER_HAPPY2, data[5] & 0x08);
|
||||
input_report_key(dev, BTN_TRIGGER_HAPPY3, data[5] & 0x01);
|
||||
input_report_key(dev, BTN_TRIGGER_HAPPY4, data[5] & 0x02);
|
||||
input_report_key(dev, BTN_TRIGGER_HAPPY1, data[5] & BIT(2));
|
||||
input_report_key(dev, BTN_TRIGGER_HAPPY2, data[5] & BIT(3));
|
||||
input_report_key(dev, BTN_TRIGGER_HAPPY3, data[5] & BIT(0));
|
||||
input_report_key(dev, BTN_TRIGGER_HAPPY4, data[5] & BIT(1));
|
||||
} else {
|
||||
input_report_abs(dev, ABS_HAT0X,
|
||||
!!(data[5] & 0x08) - !!(data[5] & 0x04));
|
||||
@ -892,12 +1025,12 @@ static void xpadone_process_packet(struct usb_xpad *xpad, u16 cmd, unsigned char
|
||||
}
|
||||
|
||||
/* TL/TR */
|
||||
input_report_key(dev, BTN_TL, data[5] & 0x10);
|
||||
input_report_key(dev, BTN_TR, data[5] & 0x20);
|
||||
input_report_key(dev, BTN_TL, data[5] & BIT(4));
|
||||
input_report_key(dev, BTN_TR, data[5] & BIT(5));
|
||||
|
||||
/* stick press left/right */
|
||||
input_report_key(dev, BTN_THUMBL, data[5] & 0x40);
|
||||
input_report_key(dev, BTN_THUMBR, data[5] & 0x80);
|
||||
input_report_key(dev, BTN_THUMBL, data[5] & BIT(6));
|
||||
input_report_key(dev, BTN_THUMBR, data[5] & BIT(7));
|
||||
|
||||
if (!(xpad->mapping & MAP_STICKS_TO_NULL)) {
|
||||
/* left stick */
|
||||
@ -926,6 +1059,57 @@ static void xpadone_process_packet(struct usb_xpad *xpad, u16 cmd, unsigned char
|
||||
(__u16) le16_to_cpup((__le16 *)(data + 8)));
|
||||
}
|
||||
|
||||
/* paddle handling */
|
||||
/* based on SDL's SDL_hidapi_xboxone.c */
|
||||
if (xpad->mapping & MAP_PADDLES) {
|
||||
if (xpad->packet_type == PKT_XBE1) {
|
||||
/* Mute paddles if controller has a custom mapping applied.
|
||||
* Checked by comparing the current mapping
|
||||
* config against the factory mapping config
|
||||
*/
|
||||
if (memcmp(&data[4], &data[18], 2) != 0)
|
||||
data[32] = 0;
|
||||
|
||||
/* OG Elite Series Controller paddle bits */
|
||||
input_report_key(dev, BTN_TRIGGER_HAPPY5, data[32] & BIT(1));
|
||||
input_report_key(dev, BTN_TRIGGER_HAPPY6, data[32] & BIT(3));
|
||||
input_report_key(dev, BTN_TRIGGER_HAPPY7, data[32] & BIT(0));
|
||||
input_report_key(dev, BTN_TRIGGER_HAPPY8, data[32] & BIT(2));
|
||||
} else if (xpad->packet_type == PKT_XBE2_FW_OLD) {
|
||||
/* Mute paddles if controller has a custom mapping applied.
|
||||
* Checked by comparing the current mapping
|
||||
* config against the factory mapping config
|
||||
*/
|
||||
if (data[19] != 0)
|
||||
data[18] = 0;
|
||||
|
||||
/* Elite Series 2 4.x firmware paddle bits */
|
||||
input_report_key(dev, BTN_TRIGGER_HAPPY5, data[18] & BIT(0));
|
||||
input_report_key(dev, BTN_TRIGGER_HAPPY6, data[18] & BIT(1));
|
||||
input_report_key(dev, BTN_TRIGGER_HAPPY7, data[18] & BIT(2));
|
||||
input_report_key(dev, BTN_TRIGGER_HAPPY8, data[18] & BIT(3));
|
||||
} else if (xpad->packet_type == PKT_XBE2_FW_5_EARLY) {
|
||||
/* Mute paddles if controller has a custom mapping applied.
|
||||
* Checked by comparing the current mapping
|
||||
* config against the factory mapping config
|
||||
*/
|
||||
if (data[23] != 0)
|
||||
data[22] = 0;
|
||||
|
||||
/* Elite Series 2 5.x firmware paddle bits
|
||||
* (before the packet was split)
|
||||
*/
|
||||
input_report_key(dev, BTN_TRIGGER_HAPPY5, data[22] & BIT(0));
|
||||
input_report_key(dev, BTN_TRIGGER_HAPPY6, data[22] & BIT(1));
|
||||
input_report_key(dev, BTN_TRIGGER_HAPPY7, data[22] & BIT(2));
|
||||
input_report_key(dev, BTN_TRIGGER_HAPPY8, data[22] & BIT(3));
|
||||
}
|
||||
}
|
||||
|
||||
do_sync = true;
|
||||
}
|
||||
|
||||
if (do_sync)
|
||||
input_sync(dev);
|
||||
}
|
||||
|
||||
@ -1226,8 +1410,8 @@ static void xpadone_ack_mode_report(struct usb_xpad *xpad, u8 seq_num)
|
||||
struct xpad_output_packet *packet =
|
||||
&xpad->out_packets[XPAD_OUT_CMD_IDX];
|
||||
static const u8 mode_report_ack[] = {
|
||||
0x01, 0x20, 0x00, 0x09, 0x00, 0x07, 0x20, 0x02,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00
|
||||
GIP_CMD_ACK, GIP_OPT_INTERNAL, GIP_SEQ0, GIP_PL_LEN(9),
|
||||
0x00, GIP_CMD_VIRTUAL_KEY, GIP_OPT_INTERNAL, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00
|
||||
};
|
||||
|
||||
spin_lock_irqsave(&xpad->odata_lock, flags);
|
||||
@ -1305,14 +1489,14 @@ static int xpad_play_effect(struct input_dev *dev, void *data, struct ff_effect
|
||||
break;
|
||||
|
||||
case XTYPE_XBOXONE:
|
||||
packet->data[0] = 0x09; /* activate rumble */
|
||||
packet->data[0] = GIP_CMD_RUMBLE; /* activate rumble */
|
||||
packet->data[1] = 0x00;
|
||||
packet->data[2] = xpad->odata_serial++;
|
||||
packet->data[3] = 0x09;
|
||||
packet->data[3] = GIP_PL_LEN(9);
|
||||
packet->data[4] = 0x00;
|
||||
packet->data[5] = 0x0F;
|
||||
packet->data[6] = 0x00;
|
||||
packet->data[7] = 0x00;
|
||||
packet->data[5] = GIP_MOTOR_ALL;
|
||||
packet->data[6] = 0x00; /* left trigger */
|
||||
packet->data[7] = 0x00; /* right trigger */
|
||||
packet->data[8] = strong / 512; /* left actuator */
|
||||
packet->data[9] = weak / 512; /* right actuator */
|
||||
packet->data[10] = 0xFF; /* on period */
|
||||
@ -1622,6 +1806,9 @@ static void xpad_set_up_abs(struct input_dev *input_dev, signed short abs)
|
||||
case ABS_HAT0Y: /* the d-pad (only if dpad is mapped to axes */
|
||||
input_set_abs_params(input_dev, abs, -1, 1, 0, 0);
|
||||
break;
|
||||
case ABS_PROFILE: /* 4 value profile button (such as on XAC) */
|
||||
input_set_abs_params(input_dev, abs, 0, 4, 0, 0);
|
||||
break;
|
||||
default:
|
||||
input_set_abs_params(input_dev, abs, 0, 0, 0, 0);
|
||||
break;
|
||||
@ -1693,6 +1880,12 @@ static int xpad_init_input(struct usb_xpad *xpad)
|
||||
xpad_btn_pad[i]);
|
||||
}
|
||||
|
||||
/* set up paddles if the controller has them */
|
||||
if (xpad->mapping & MAP_PADDLES) {
|
||||
for (i = 0; xpad_btn_paddles[i] >= 0; i++)
|
||||
input_set_capability(input_dev, EV_KEY, xpad_btn_paddles[i]);
|
||||
}
|
||||
|
||||
/*
|
||||
* This should be a simple else block. However historically
|
||||
* xbox360w has mapped DPAD to buttons while xbox360 did not. This
|
||||
@ -1714,6 +1907,10 @@ static int xpad_init_input(struct usb_xpad *xpad)
|
||||
xpad_set_up_abs(input_dev, xpad_abs_triggers[i]);
|
||||
}
|
||||
|
||||
/* setup profile button as an axis with 4 possible values */
|
||||
if (xpad->mapping & MAP_PROFILE_BUTTON)
|
||||
xpad_set_up_abs(input_dev, ABS_PROFILE);
|
||||
|
||||
error = xpad_init_ff(xpad);
|
||||
if (error)
|
||||
goto err_free_input;
|
||||
@ -1779,6 +1976,7 @@ static int xpad_probe(struct usb_interface *intf, const struct usb_device_id *id
|
||||
xpad->mapping = xpad_device[i].mapping;
|
||||
xpad->xtype = xpad_device[i].xtype;
|
||||
xpad->name = xpad_device[i].name;
|
||||
xpad->packet_type = PKT_XB;
|
||||
INIT_WORK(&xpad->work, xpad_presence_work);
|
||||
|
||||
if (xpad->xtype == XTYPE_UNKNOWN) {
|
||||
@ -1844,6 +2042,38 @@ static int xpad_probe(struct usb_interface *intf, const struct usb_device_id *id
|
||||
|
||||
usb_set_intfdata(intf, xpad);
|
||||
|
||||
/* Packet type detection */
|
||||
if (le16_to_cpu(udev->descriptor.idVendor) == 0x045e) { /* Microsoft controllers */
|
||||
if (le16_to_cpu(udev->descriptor.idProduct) == 0x02e3) {
|
||||
/* The original elite controller always uses the oldest
|
||||
* type of extended packet
|
||||
*/
|
||||
xpad->packet_type = PKT_XBE1;
|
||||
} else if (le16_to_cpu(udev->descriptor.idProduct) == 0x0b00) {
|
||||
/* The elite 2 controller has seen multiple packet
|
||||
* revisions. These are tied to specific firmware
|
||||
* versions
|
||||
*/
|
||||
if (le16_to_cpu(udev->descriptor.bcdDevice) < 0x0500) {
|
||||
/* This is the format that the Elite 2 used
|
||||
* prior to the BLE update
|
||||
*/
|
||||
xpad->packet_type = PKT_XBE2_FW_OLD;
|
||||
} else if (le16_to_cpu(udev->descriptor.bcdDevice) <
|
||||
0x050b) {
|
||||
/* This is the format that the Elite 2 used
|
||||
* prior to the update that split the packet
|
||||
*/
|
||||
xpad->packet_type = PKT_XBE2_FW_5_EARLY;
|
||||
} else {
|
||||
/* The split packet format that was introduced
|
||||
* in firmware v5.11
|
||||
*/
|
||||
xpad->packet_type = PKT_XBE2_FW_5_11;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (xpad->xtype == XTYPE_XBOX360W) {
|
||||
/*
|
||||
* Submit the int URB immediately rather than waiting for open
|
||||
@ -1972,7 +2202,6 @@ static struct usb_driver xpad_driver = {
|
||||
.disconnect = xpad_disconnect,
|
||||
.suspend = xpad_suspend,
|
||||
.resume = xpad_resume,
|
||||
.reset_resume = xpad_resume,
|
||||
.id_table = xpad_table,
|
||||
};
|
||||
|
||||
|
@ -28,9 +28,6 @@
|
||||
* coder :-(
|
||||
*/
|
||||
|
||||
/*
|
||||
*/
|
||||
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/slab.h>
|
||||
|
@ -40,6 +40,9 @@ config KEYBOARD_ADP5520
|
||||
config KEYBOARD_ADP5588
|
||||
tristate "ADP5588/87 I2C QWERTY Keypad and IO Expander"
|
||||
depends on I2C
|
||||
select GPIOLIB
|
||||
select GPIOLIB_IRQCHIP
|
||||
select INPUT_MATRIXKMAP
|
||||
help
|
||||
Say Y here if you want to use a ADP5588/87 attached to your
|
||||
system I2C bus.
|
||||
@ -186,7 +189,7 @@ config KEYBOARD_QT2160
|
||||
|
||||
config KEYBOARD_CLPS711X
|
||||
tristate "CLPS711X Keypad support"
|
||||
depends on OF_GPIO && (ARCH_CLPS711X || COMPILE_TEST)
|
||||
depends on ARCH_CLPS711X || COMPILE_TEST
|
||||
select INPUT_MATRIXKMAP
|
||||
help
|
||||
Say Y here to enable the matrix keypad on the Cirrus Logic
|
||||
@ -524,6 +527,19 @@ config KEYBOARD_OPENCORES
|
||||
To compile this driver as a module, choose M here; the
|
||||
module will be called opencores-kbd.
|
||||
|
||||
config KEYBOARD_PINEPHONE
|
||||
tristate "Pine64 PinePhone Keyboard"
|
||||
depends on I2C && REGULATOR
|
||||
select CRC8
|
||||
select INPUT_MATRIXKMAP
|
||||
help
|
||||
Say Y here to enable support for the keyboard in the Pine64 PinePhone
|
||||
keyboard case. This driver supports the FLOSS firmware available at
|
||||
https://megous.com/git/pinephone-keyboard/
|
||||
|
||||
To compile this driver as a module, choose M here; the
|
||||
module will be called pinephone-keyboard.
|
||||
|
||||
config KEYBOARD_PXA27x
|
||||
tristate "PXA27x/PXA3xx keypad support"
|
||||
depends on PXA27x || PXA3xx || ARCH_MMP
|
||||
|
@ -52,6 +52,7 @@ obj-$(CONFIG_KEYBOARD_NSPIRE) += nspire-keypad.o
|
||||
obj-$(CONFIG_KEYBOARD_OMAP) += omap-keypad.o
|
||||
obj-$(CONFIG_KEYBOARD_OMAP4) += omap4-keypad.o
|
||||
obj-$(CONFIG_KEYBOARD_OPENCORES) += opencores-kbd.o
|
||||
obj-$(CONFIG_KEYBOARD_PINEPHONE) += pinephone-keyboard.o
|
||||
obj-$(CONFIG_KEYBOARD_PMIC8XXX) += pmic8xxx-keypad.o
|
||||
obj-$(CONFIG_KEYBOARD_PXA27x) += pxa27x_keypad.o
|
||||
obj-$(CONFIG_KEYBOARD_PXA930_ROTARY) += pxa930_rotary.o
|
||||
|
@ -8,27 +8,163 @@
|
||||
* Copyright (C) 2008-2010 Analog Devices Inc.
|
||||
*/
|
||||
|
||||
#include <linux/bits.h>
|
||||
#include <linux/delay.h>
|
||||
#include <linux/errno.h>
|
||||
#include <linux/gpio/consumer.h>
|
||||
#include <linux/gpio/driver.h>
|
||||
#include <linux/i2c.h>
|
||||
#include <linux/input.h>
|
||||
#include <linux/input/matrix_keypad.h>
|
||||
#include <linux/interrupt.h>
|
||||
#include <linux/irq.h>
|
||||
#include <linux/ktime.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/mod_devicetable.h>
|
||||
#include <linux/pinctrl/pinconf-generic.h>
|
||||
#include <linux/platform_device.h>
|
||||
#include <linux/pm.h>
|
||||
#include <linux/regulator/consumer.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/timekeeping.h>
|
||||
|
||||
#include <linux/platform_data/adp5588.h>
|
||||
#define DEV_ID 0x00 /* Device ID */
|
||||
#define CFG 0x01 /* Configuration Register1 */
|
||||
#define INT_STAT 0x02 /* Interrupt Status Register */
|
||||
#define KEY_LCK_EC_STAT 0x03 /* Key Lock and Event Counter Register */
|
||||
#define KEY_EVENTA 0x04 /* Key Event Register A */
|
||||
#define KEY_EVENTB 0x05 /* Key Event Register B */
|
||||
#define KEY_EVENTC 0x06 /* Key Event Register C */
|
||||
#define KEY_EVENTD 0x07 /* Key Event Register D */
|
||||
#define KEY_EVENTE 0x08 /* Key Event Register E */
|
||||
#define KEY_EVENTF 0x09 /* Key Event Register F */
|
||||
#define KEY_EVENTG 0x0A /* Key Event Register G */
|
||||
#define KEY_EVENTH 0x0B /* Key Event Register H */
|
||||
#define KEY_EVENTI 0x0C /* Key Event Register I */
|
||||
#define KEY_EVENTJ 0x0D /* Key Event Register J */
|
||||
#define KP_LCK_TMR 0x0E /* Keypad Lock1 to Lock2 Timer */
|
||||
#define UNLOCK1 0x0F /* Unlock Key1 */
|
||||
#define UNLOCK2 0x10 /* Unlock Key2 */
|
||||
#define GPIO_INT_STAT1 0x11 /* GPIO Interrupt Status */
|
||||
#define GPIO_INT_STAT2 0x12 /* GPIO Interrupt Status */
|
||||
#define GPIO_INT_STAT3 0x13 /* GPIO Interrupt Status */
|
||||
#define GPIO_DAT_STAT1 0x14 /* GPIO Data Status, Read twice to clear */
|
||||
#define GPIO_DAT_STAT2 0x15 /* GPIO Data Status, Read twice to clear */
|
||||
#define GPIO_DAT_STAT3 0x16 /* GPIO Data Status, Read twice to clear */
|
||||
#define GPIO_DAT_OUT1 0x17 /* GPIO DATA OUT */
|
||||
#define GPIO_DAT_OUT2 0x18 /* GPIO DATA OUT */
|
||||
#define GPIO_DAT_OUT3 0x19 /* GPIO DATA OUT */
|
||||
#define GPIO_INT_EN1 0x1A /* GPIO Interrupt Enable */
|
||||
#define GPIO_INT_EN2 0x1B /* GPIO Interrupt Enable */
|
||||
#define GPIO_INT_EN3 0x1C /* GPIO Interrupt Enable */
|
||||
#define KP_GPIO1 0x1D /* Keypad or GPIO Selection */
|
||||
#define KP_GPIO2 0x1E /* Keypad or GPIO Selection */
|
||||
#define KP_GPIO3 0x1F /* Keypad or GPIO Selection */
|
||||
#define GPI_EM1 0x20 /* GPI Event Mode 1 */
|
||||
#define GPI_EM2 0x21 /* GPI Event Mode 2 */
|
||||
#define GPI_EM3 0x22 /* GPI Event Mode 3 */
|
||||
#define GPIO_DIR1 0x23 /* GPIO Data Direction */
|
||||
#define GPIO_DIR2 0x24 /* GPIO Data Direction */
|
||||
#define GPIO_DIR3 0x25 /* GPIO Data Direction */
|
||||
#define GPIO_INT_LVL1 0x26 /* GPIO Edge/Level Detect */
|
||||
#define GPIO_INT_LVL2 0x27 /* GPIO Edge/Level Detect */
|
||||
#define GPIO_INT_LVL3 0x28 /* GPIO Edge/Level Detect */
|
||||
#define DEBOUNCE_DIS1 0x29 /* Debounce Disable */
|
||||
#define DEBOUNCE_DIS2 0x2A /* Debounce Disable */
|
||||
#define DEBOUNCE_DIS3 0x2B /* Debounce Disable */
|
||||
#define GPIO_PULL1 0x2C /* GPIO Pull Disable */
|
||||
#define GPIO_PULL2 0x2D /* GPIO Pull Disable */
|
||||
#define GPIO_PULL3 0x2E /* GPIO Pull Disable */
|
||||
#define CMP_CFG_STAT 0x30 /* Comparator Configuration and Status Register */
|
||||
#define CMP_CONFG_SENS1 0x31 /* Sensor1 Comparator Configuration Register */
|
||||
#define CMP_CONFG_SENS2 0x32 /* L2 Light Sensor Reference Level, Output Falling for Sensor 1 */
|
||||
#define CMP1_LVL2_TRIP 0x33 /* L2 Light Sensor Hysteresis (Active when Output Rising) for Sensor 1 */
|
||||
#define CMP1_LVL2_HYS 0x34 /* L3 Light Sensor Reference Level, Output Falling For Sensor 1 */
|
||||
#define CMP1_LVL3_TRIP 0x35 /* L3 Light Sensor Hysteresis (Active when Output Rising) For Sensor 1 */
|
||||
#define CMP1_LVL3_HYS 0x36 /* Sensor 2 Comparator Configuration Register */
|
||||
#define CMP2_LVL2_TRIP 0x37 /* L2 Light Sensor Reference Level, Output Falling for Sensor 2 */
|
||||
#define CMP2_LVL2_HYS 0x38 /* L2 Light Sensor Hysteresis (Active when Output Rising) for Sensor 2 */
|
||||
#define CMP2_LVL3_TRIP 0x39 /* L3 Light Sensor Reference Level, Output Falling For Sensor 2 */
|
||||
#define CMP2_LVL3_HYS 0x3A /* L3 Light Sensor Hysteresis (Active when Output Rising) For Sensor 2 */
|
||||
#define CMP1_ADC_DAT_R1 0x3B /* Comparator 1 ADC data Register1 */
|
||||
#define CMP1_ADC_DAT_R2 0x3C /* Comparator 1 ADC data Register2 */
|
||||
#define CMP2_ADC_DAT_R1 0x3D /* Comparator 2 ADC data Register1 */
|
||||
#define CMP2_ADC_DAT_R2 0x3E /* Comparator 2 ADC data Register2 */
|
||||
|
||||
#define ADP5588_DEVICE_ID_MASK 0xF
|
||||
|
||||
/* Configuration Register1 */
|
||||
#define ADP5588_AUTO_INC BIT(7)
|
||||
#define ADP5588_GPIEM_CFG BIT(6)
|
||||
#define ADP5588_OVR_FLOW_M BIT(5)
|
||||
#define ADP5588_INT_CFG BIT(4)
|
||||
#define ADP5588_OVR_FLOW_IEN BIT(3)
|
||||
#define ADP5588_K_LCK_IM BIT(2)
|
||||
#define ADP5588_GPI_IEN BIT(1)
|
||||
#define ADP5588_KE_IEN BIT(0)
|
||||
|
||||
/* Interrupt Status Register */
|
||||
#define ADP5588_CMP2_INT BIT(5)
|
||||
#define ADP5588_CMP1_INT BIT(4)
|
||||
#define ADP5588_OVR_FLOW_INT BIT(3)
|
||||
#define ADP5588_K_LCK_INT BIT(2)
|
||||
#define ADP5588_GPI_INT BIT(1)
|
||||
#define ADP5588_KE_INT BIT(0)
|
||||
|
||||
/* Key Lock and Event Counter Register */
|
||||
#define ADP5588_K_LCK_EN BIT(6)
|
||||
#define ADP5588_LCK21 0x30
|
||||
#define ADP5588_KEC GENMASK(3, 0)
|
||||
|
||||
#define ADP5588_MAXGPIO 18
|
||||
#define ADP5588_BANK(offs) ((offs) >> 3)
|
||||
#define ADP5588_BIT(offs) (1u << ((offs) & 0x7))
|
||||
|
||||
/* Put one of these structures in i2c_board_info platform_data */
|
||||
|
||||
/*
|
||||
* 128 so it fits matrix-keymap maximum number of keys when the full
|
||||
* 10cols * 8rows are used.
|
||||
*/
|
||||
#define ADP5588_KEYMAPSIZE 128
|
||||
|
||||
#define GPI_PIN_ROW0 97
|
||||
#define GPI_PIN_ROW1 98
|
||||
#define GPI_PIN_ROW2 99
|
||||
#define GPI_PIN_ROW3 100
|
||||
#define GPI_PIN_ROW4 101
|
||||
#define GPI_PIN_ROW5 102
|
||||
#define GPI_PIN_ROW6 103
|
||||
#define GPI_PIN_ROW7 104
|
||||
#define GPI_PIN_COL0 105
|
||||
#define GPI_PIN_COL1 106
|
||||
#define GPI_PIN_COL2 107
|
||||
#define GPI_PIN_COL3 108
|
||||
#define GPI_PIN_COL4 109
|
||||
#define GPI_PIN_COL5 110
|
||||
#define GPI_PIN_COL6 111
|
||||
#define GPI_PIN_COL7 112
|
||||
#define GPI_PIN_COL8 113
|
||||
#define GPI_PIN_COL9 114
|
||||
|
||||
#define GPI_PIN_ROW_BASE GPI_PIN_ROW0
|
||||
#define GPI_PIN_ROW_END GPI_PIN_ROW7
|
||||
#define GPI_PIN_COL_BASE GPI_PIN_COL0
|
||||
#define GPI_PIN_COL_END GPI_PIN_COL9
|
||||
|
||||
#define GPI_PIN_BASE GPI_PIN_ROW_BASE
|
||||
#define GPI_PIN_END GPI_PIN_COL_END
|
||||
|
||||
#define ADP5588_ROWS_MAX (GPI_PIN_ROW7 - GPI_PIN_ROW0 + 1)
|
||||
#define ADP5588_COLS_MAX (GPI_PIN_COL9 - GPI_PIN_COL0 + 1)
|
||||
|
||||
#define ADP5588_GPIMAPSIZE_MAX (GPI_PIN_END - GPI_PIN_BASE + 1)
|
||||
|
||||
/* Key Event Register xy */
|
||||
#define KEY_EV_PRESSED (1 << 7)
|
||||
#define KEY_EV_MASK (0x7F)
|
||||
#define KEY_EV_PRESSED BIT(7)
|
||||
#define KEY_EV_MASK GENMASK(6, 0)
|
||||
|
||||
#define KP_SEL(x) (0xFFFF >> (16 - x)) /* 2^x-1 */
|
||||
#define KP_SEL(x) (BIT(x) - 1) /* 2^x-1 */
|
||||
|
||||
#define KEYP_MAX_EVENT 10
|
||||
|
||||
@ -40,21 +176,27 @@
|
||||
#define WA_DELAYED_READOUT_REVID(rev) ((rev) < 4)
|
||||
#define WA_DELAYED_READOUT_TIME 25
|
||||
|
||||
#define ADP5588_INVALID_HWIRQ (~0UL)
|
||||
|
||||
struct adp5588_kpad {
|
||||
struct i2c_client *client;
|
||||
struct input_dev *input;
|
||||
ktime_t irq_time;
|
||||
unsigned long delay;
|
||||
u32 row_shift;
|
||||
u32 rows;
|
||||
u32 cols;
|
||||
u32 unlock_keys[2];
|
||||
int nkeys_unlock;
|
||||
unsigned short keycode[ADP5588_KEYMAPSIZE];
|
||||
const struct adp5588_gpi_map *gpimap;
|
||||
unsigned short gpimapsize;
|
||||
#ifdef CONFIG_GPIOLIB
|
||||
unsigned char gpiomap[ADP5588_MAXGPIO];
|
||||
struct gpio_chip gc;
|
||||
struct mutex gpio_lock; /* Protect cached dir, dat_out */
|
||||
u8 dat_out[3];
|
||||
u8 dir[3];
|
||||
#endif
|
||||
u8 int_en[3];
|
||||
u8 irq_mask[3];
|
||||
u8 pull_dis[3];
|
||||
};
|
||||
|
||||
static int adp5588_read(struct i2c_client *client, u8 reg)
|
||||
@ -72,8 +214,7 @@ static int adp5588_write(struct i2c_client *client, u8 reg, u8 val)
|
||||
return i2c_smbus_write_byte_data(client, reg, val);
|
||||
}
|
||||
|
||||
#ifdef CONFIG_GPIOLIB
|
||||
static int adp5588_gpio_get_value(struct gpio_chip *chip, unsigned off)
|
||||
static int adp5588_gpio_get_value(struct gpio_chip *chip, unsigned int off)
|
||||
{
|
||||
struct adp5588_kpad *kpad = gpiochip_get_data(chip);
|
||||
unsigned int bank = ADP5588_BANK(kpad->gpiomap[off]);
|
||||
@ -93,7 +234,7 @@ static int adp5588_gpio_get_value(struct gpio_chip *chip, unsigned off)
|
||||
}
|
||||
|
||||
static void adp5588_gpio_set_value(struct gpio_chip *chip,
|
||||
unsigned off, int val)
|
||||
unsigned int off, int val)
|
||||
{
|
||||
struct adp5588_kpad *kpad = gpiochip_get_data(chip);
|
||||
unsigned int bank = ADP5588_BANK(kpad->gpiomap[off]);
|
||||
@ -106,13 +247,47 @@ static void adp5588_gpio_set_value(struct gpio_chip *chip,
|
||||
else
|
||||
kpad->dat_out[bank] &= ~bit;
|
||||
|
||||
adp5588_write(kpad->client, GPIO_DAT_OUT1 + bank,
|
||||
kpad->dat_out[bank]);
|
||||
adp5588_write(kpad->client, GPIO_DAT_OUT1 + bank, kpad->dat_out[bank]);
|
||||
|
||||
mutex_unlock(&kpad->gpio_lock);
|
||||
}
|
||||
|
||||
static int adp5588_gpio_direction_input(struct gpio_chip *chip, unsigned off)
|
||||
static int adp5588_gpio_set_config(struct gpio_chip *chip, unsigned int off,
|
||||
unsigned long config)
|
||||
{
|
||||
struct adp5588_kpad *kpad = gpiochip_get_data(chip);
|
||||
unsigned int bank = ADP5588_BANK(kpad->gpiomap[off]);
|
||||
unsigned int bit = ADP5588_BIT(kpad->gpiomap[off]);
|
||||
bool pull_disable;
|
||||
int ret;
|
||||
|
||||
switch (pinconf_to_config_param(config)) {
|
||||
case PIN_CONFIG_BIAS_PULL_UP:
|
||||
pull_disable = false;
|
||||
break;
|
||||
case PIN_CONFIG_BIAS_DISABLE:
|
||||
pull_disable = true;
|
||||
break;
|
||||
default:
|
||||
return -ENOTSUPP;
|
||||
}
|
||||
|
||||
mutex_lock(&kpad->gpio_lock);
|
||||
|
||||
if (pull_disable)
|
||||
kpad->pull_dis[bank] |= bit;
|
||||
else
|
||||
kpad->pull_dis[bank] &= bit;
|
||||
|
||||
ret = adp5588_write(kpad->client, GPIO_PULL1 + bank,
|
||||
kpad->pull_dis[bank]);
|
||||
|
||||
mutex_unlock(&kpad->gpio_lock);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int adp5588_gpio_direction_input(struct gpio_chip *chip, unsigned int off)
|
||||
{
|
||||
struct adp5588_kpad *kpad = gpiochip_get_data(chip);
|
||||
unsigned int bank = ADP5588_BANK(kpad->gpiomap[off]);
|
||||
@ -130,7 +305,7 @@ static int adp5588_gpio_direction_input(struct gpio_chip *chip, unsigned off)
|
||||
}
|
||||
|
||||
static int adp5588_gpio_direction_output(struct gpio_chip *chip,
|
||||
unsigned off, int val)
|
||||
unsigned int off, int val)
|
||||
{
|
||||
struct adp5588_kpad *kpad = gpiochip_get_data(chip);
|
||||
unsigned int bank = ADP5588_BANK(kpad->gpiomap[off]);
|
||||
@ -148,16 +323,18 @@ static int adp5588_gpio_direction_output(struct gpio_chip *chip,
|
||||
|
||||
ret = adp5588_write(kpad->client, GPIO_DAT_OUT1 + bank,
|
||||
kpad->dat_out[bank]);
|
||||
ret |= adp5588_write(kpad->client, GPIO_DIR1 + bank,
|
||||
kpad->dir[bank]);
|
||||
if (ret)
|
||||
goto out_unlock;
|
||||
|
||||
ret = adp5588_write(kpad->client, GPIO_DIR1 + bank, kpad->dir[bank]);
|
||||
|
||||
out_unlock:
|
||||
mutex_unlock(&kpad->gpio_lock);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int adp5588_build_gpiomap(struct adp5588_kpad *kpad,
|
||||
const struct adp5588_kpad_platform_data *pdata)
|
||||
static int adp5588_build_gpiomap(struct adp5588_kpad *kpad)
|
||||
{
|
||||
bool pin_used[ADP5588_MAXGPIO];
|
||||
int n_unused = 0;
|
||||
@ -165,15 +342,12 @@ static int adp5588_build_gpiomap(struct adp5588_kpad *kpad,
|
||||
|
||||
memset(pin_used, 0, sizeof(pin_used));
|
||||
|
||||
for (i = 0; i < pdata->rows; i++)
|
||||
for (i = 0; i < kpad->rows; i++)
|
||||
pin_used[i] = true;
|
||||
|
||||
for (i = 0; i < pdata->cols; i++)
|
||||
for (i = 0; i < kpad->cols; i++)
|
||||
pin_used[i + GPI_PIN_COL_BASE - GPI_PIN_BASE] = true;
|
||||
|
||||
for (i = 0; i < kpad->gpimapsize; i++)
|
||||
pin_used[kpad->gpimap[i].pin - GPI_PIN_BASE] = true;
|
||||
|
||||
for (i = 0; i < ADP5588_MAXGPIO; i++)
|
||||
if (!pin_used[i])
|
||||
kpad->gpiomap[n_unused++] = i;
|
||||
@ -181,47 +355,101 @@ static int adp5588_build_gpiomap(struct adp5588_kpad *kpad,
|
||||
return n_unused;
|
||||
}
|
||||
|
||||
static void adp5588_gpio_do_teardown(void *_kpad)
|
||||
static void adp5588_irq_bus_lock(struct irq_data *d)
|
||||
{
|
||||
struct adp5588_kpad *kpad = _kpad;
|
||||
struct device *dev = &kpad->client->dev;
|
||||
const struct adp5588_kpad_platform_data *pdata = dev_get_platdata(dev);
|
||||
const struct adp5588_gpio_platform_data *gpio_data = pdata->gpio_data;
|
||||
int error;
|
||||
struct gpio_chip *gc = irq_data_get_irq_chip_data(d);
|
||||
struct adp5588_kpad *kpad = gpiochip_get_data(gc);
|
||||
|
||||
error = gpio_data->teardown(kpad->client,
|
||||
kpad->gc.base, kpad->gc.ngpio,
|
||||
gpio_data->context);
|
||||
if (error)
|
||||
dev_warn(&kpad->client->dev, "teardown failed %d\n", error);
|
||||
mutex_lock(&kpad->gpio_lock);
|
||||
}
|
||||
|
||||
static void adp5588_irq_bus_sync_unlock(struct irq_data *d)
|
||||
{
|
||||
struct gpio_chip *gc = irq_data_get_irq_chip_data(d);
|
||||
struct adp5588_kpad *kpad = gpiochip_get_data(gc);
|
||||
int i;
|
||||
|
||||
for (i = 0; i <= ADP5588_BANK(ADP5588_MAXGPIO); i++) {
|
||||
if (kpad->int_en[i] ^ kpad->irq_mask[i]) {
|
||||
kpad->int_en[i] = kpad->irq_mask[i];
|
||||
adp5588_write(kpad->client, GPI_EM1 + i, kpad->int_en[i]);
|
||||
}
|
||||
}
|
||||
|
||||
mutex_unlock(&kpad->gpio_lock);
|
||||
}
|
||||
|
||||
static void adp5588_irq_mask(struct irq_data *d)
|
||||
{
|
||||
struct gpio_chip *gc = irq_data_get_irq_chip_data(d);
|
||||
struct adp5588_kpad *kpad = gpiochip_get_data(gc);
|
||||
irq_hw_number_t hwirq = irqd_to_hwirq(d);
|
||||
unsigned long real_irq = kpad->gpiomap[hwirq];
|
||||
|
||||
kpad->irq_mask[ADP5588_BANK(real_irq)] &= ~ADP5588_BIT(real_irq);
|
||||
gpiochip_disable_irq(gc, hwirq);
|
||||
}
|
||||
|
||||
static void adp5588_irq_unmask(struct irq_data *d)
|
||||
{
|
||||
struct gpio_chip *gc = irq_data_get_irq_chip_data(d);
|
||||
struct adp5588_kpad *kpad = gpiochip_get_data(gc);
|
||||
irq_hw_number_t hwirq = irqd_to_hwirq(d);
|
||||
unsigned long real_irq = kpad->gpiomap[hwirq];
|
||||
|
||||
gpiochip_enable_irq(gc, hwirq);
|
||||
kpad->irq_mask[ADP5588_BANK(real_irq)] |= ADP5588_BIT(real_irq);
|
||||
}
|
||||
|
||||
static int adp5588_irq_set_type(struct irq_data *d, unsigned int type)
|
||||
{
|
||||
if (!(type & IRQ_TYPE_EDGE_BOTH))
|
||||
return -EINVAL;
|
||||
|
||||
irq_set_handler_locked(d, handle_edge_irq);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct irq_chip adp5588_irq_chip = {
|
||||
.name = "adp5588",
|
||||
.irq_mask = adp5588_irq_mask,
|
||||
.irq_unmask = adp5588_irq_unmask,
|
||||
.irq_bus_lock = adp5588_irq_bus_lock,
|
||||
.irq_bus_sync_unlock = adp5588_irq_bus_sync_unlock,
|
||||
.irq_set_type = adp5588_irq_set_type,
|
||||
.flags = IRQCHIP_SKIP_SET_WAKE | IRQCHIP_IMMUTABLE,
|
||||
GPIOCHIP_IRQ_RESOURCE_HELPERS,
|
||||
};
|
||||
|
||||
static int adp5588_gpio_add(struct adp5588_kpad *kpad)
|
||||
{
|
||||
struct device *dev = &kpad->client->dev;
|
||||
const struct adp5588_kpad_platform_data *pdata = dev_get_platdata(dev);
|
||||
const struct adp5588_gpio_platform_data *gpio_data = pdata->gpio_data;
|
||||
struct gpio_irq_chip *girq;
|
||||
int i, error;
|
||||
|
||||
if (!gpio_data)
|
||||
return 0;
|
||||
|
||||
kpad->gc.ngpio = adp5588_build_gpiomap(kpad, pdata);
|
||||
kpad->gc.ngpio = adp5588_build_gpiomap(kpad);
|
||||
if (kpad->gc.ngpio == 0) {
|
||||
dev_info(dev, "No unused gpios left to export\n");
|
||||
return 0;
|
||||
}
|
||||
|
||||
kpad->gc.parent = &kpad->client->dev;
|
||||
kpad->gc.direction_input = adp5588_gpio_direction_input;
|
||||
kpad->gc.direction_output = adp5588_gpio_direction_output;
|
||||
kpad->gc.get = adp5588_gpio_get_value;
|
||||
kpad->gc.set = adp5588_gpio_set_value;
|
||||
kpad->gc.set_config = adp5588_gpio_set_config;
|
||||
kpad->gc.can_sleep = 1;
|
||||
|
||||
kpad->gc.base = gpio_data->gpio_start;
|
||||
kpad->gc.base = -1;
|
||||
kpad->gc.label = kpad->client->name;
|
||||
kpad->gc.owner = THIS_MODULE;
|
||||
kpad->gc.names = gpio_data->names;
|
||||
|
||||
girq = &kpad->gc.irq;
|
||||
gpio_irq_chip_set_chip(girq, &adp5588_irq_chip);
|
||||
girq->handler = handle_bad_irq;
|
||||
girq->threaded = true;
|
||||
|
||||
mutex_init(&kpad->gpio_lock);
|
||||
|
||||
@ -235,54 +463,87 @@ static int adp5588_gpio_add(struct adp5588_kpad *kpad)
|
||||
kpad->dat_out[i] = adp5588_read(kpad->client,
|
||||
GPIO_DAT_OUT1 + i);
|
||||
kpad->dir[i] = adp5588_read(kpad->client, GPIO_DIR1 + i);
|
||||
}
|
||||
|
||||
if (gpio_data->setup) {
|
||||
error = gpio_data->setup(kpad->client,
|
||||
kpad->gc.base, kpad->gc.ngpio,
|
||||
gpio_data->context);
|
||||
if (error)
|
||||
dev_warn(dev, "setup failed: %d\n", error);
|
||||
}
|
||||
|
||||
if (gpio_data->teardown) {
|
||||
error = devm_add_action(dev, adp5588_gpio_do_teardown, kpad);
|
||||
if (error)
|
||||
dev_warn(dev, "failed to schedule teardown: %d\n",
|
||||
error);
|
||||
kpad->pull_dis[i] = adp5588_read(kpad->client, GPIO_PULL1 + i);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
#else
|
||||
static inline int adp5588_gpio_add(struct adp5588_kpad *kpad)
|
||||
static unsigned long adp5588_gpiomap_get_hwirq(struct device *dev,
|
||||
const u8 *map, unsigned int gpio,
|
||||
unsigned int ngpios)
|
||||
{
|
||||
return 0;
|
||||
unsigned int hwirq;
|
||||
|
||||
for (hwirq = 0; hwirq < ngpios; hwirq++)
|
||||
if (map[hwirq] == gpio)
|
||||
return hwirq;
|
||||
|
||||
/* should never happen */
|
||||
dev_warn_ratelimited(dev, "could not find the hwirq for gpio(%u)\n", gpio);
|
||||
|
||||
return ADP5588_INVALID_HWIRQ;
|
||||
}
|
||||
|
||||
static void adp5588_gpio_irq_handle(struct adp5588_kpad *kpad, int key_val,
|
||||
int key_press)
|
||||
{
|
||||
unsigned int irq, gpio = key_val - GPI_PIN_BASE, irq_type;
|
||||
struct i2c_client *client = kpad->client;
|
||||
struct irq_data *irqd;
|
||||
unsigned long hwirq;
|
||||
|
||||
hwirq = adp5588_gpiomap_get_hwirq(&client->dev, kpad->gpiomap,
|
||||
gpio, kpad->gc.ngpio);
|
||||
if (hwirq == ADP5588_INVALID_HWIRQ) {
|
||||
dev_err(&client->dev, "Could not get hwirq for key(%u)\n", key_val);
|
||||
return;
|
||||
}
|
||||
|
||||
irq = irq_find_mapping(kpad->gc.irq.domain, hwirq);
|
||||
if (!irq)
|
||||
return;
|
||||
|
||||
irqd = irq_get_irq_data(irq);
|
||||
if (!irqd) {
|
||||
dev_err(&client->dev, "Could not get irq(%u) data\n", irq);
|
||||
return;
|
||||
}
|
||||
|
||||
irq_type = irqd_get_trigger_type(irqd);
|
||||
|
||||
/*
|
||||
* Default is active low which means key_press is asserted on
|
||||
* the falling edge.
|
||||
*/
|
||||
if ((irq_type & IRQ_TYPE_EDGE_RISING && !key_press) ||
|
||||
(irq_type & IRQ_TYPE_EDGE_FALLING && key_press))
|
||||
handle_nested_irq(irq);
|
||||
}
|
||||
#endif
|
||||
|
||||
static void adp5588_report_events(struct adp5588_kpad *kpad, int ev_cnt)
|
||||
{
|
||||
int i, j;
|
||||
int i;
|
||||
|
||||
for (i = 0; i < ev_cnt; i++) {
|
||||
int key = adp5588_read(kpad->client, Key_EVENTA + i);
|
||||
int key = adp5588_read(kpad->client, KEY_EVENTA + i);
|
||||
int key_val = key & KEY_EV_MASK;
|
||||
int key_press = key & KEY_EV_PRESSED;
|
||||
|
||||
if (key_val >= GPI_PIN_BASE && key_val <= GPI_PIN_END) {
|
||||
for (j = 0; j < kpad->gpimapsize; j++) {
|
||||
if (key_val == kpad->gpimap[j].pin) {
|
||||
input_report_switch(kpad->input,
|
||||
kpad->gpimap[j].sw_evt,
|
||||
key & KEY_EV_PRESSED);
|
||||
break;
|
||||
}
|
||||
}
|
||||
/* gpio line used as IRQ source */
|
||||
adp5588_gpio_irq_handle(kpad, key_val, key_press);
|
||||
} else {
|
||||
int row = (key_val - 1) / ADP5588_COLS_MAX;
|
||||
int col = (key_val - 1) % ADP5588_COLS_MAX;
|
||||
int code = MATRIX_SCAN_CODE(row, col, kpad->row_shift);
|
||||
|
||||
dev_dbg_ratelimited(&kpad->client->dev,
|
||||
"report key(%d) r(%d) c(%d) code(%d)\n",
|
||||
key_val, row, col, kpad->keycode[code]);
|
||||
|
||||
input_report_key(kpad->input,
|
||||
kpad->keycode[key_val - 1],
|
||||
key & KEY_EV_PRESSED);
|
||||
kpad->keycode[code], key_press);
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -335,117 +596,137 @@ static irqreturn_t adp5588_thread_irq(int irq, void *handle)
|
||||
return IRQ_HANDLED;
|
||||
}
|
||||
|
||||
static int adp5588_setup(struct i2c_client *client)
|
||||
static int adp5588_setup(struct adp5588_kpad *kpad)
|
||||
{
|
||||
const struct adp5588_kpad_platform_data *pdata =
|
||||
dev_get_platdata(&client->dev);
|
||||
const struct adp5588_gpio_platform_data *gpio_data = pdata->gpio_data;
|
||||
struct i2c_client *client = kpad->client;
|
||||
int i, ret;
|
||||
unsigned char evt_mode1 = 0, evt_mode2 = 0, evt_mode3 = 0;
|
||||
|
||||
ret = adp5588_write(client, KP_GPIO1, KP_SEL(pdata->rows));
|
||||
ret |= adp5588_write(client, KP_GPIO2, KP_SEL(pdata->cols) & 0xFF);
|
||||
ret |= adp5588_write(client, KP_GPIO3, KP_SEL(pdata->cols) >> 8);
|
||||
ret = adp5588_write(client, KP_GPIO1, KP_SEL(kpad->rows));
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
if (pdata->en_keylock) {
|
||||
ret |= adp5588_write(client, UNLOCK1, pdata->unlock_key1);
|
||||
ret |= adp5588_write(client, UNLOCK2, pdata->unlock_key2);
|
||||
ret |= adp5588_write(client, KEY_LCK_EC_STAT, ADP5588_K_LCK_EN);
|
||||
ret = adp5588_write(client, KP_GPIO2, KP_SEL(kpad->cols) & 0xFF);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
ret = adp5588_write(client, KP_GPIO3, KP_SEL(kpad->cols) >> 8);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
for (i = 0; i < kpad->nkeys_unlock; i++) {
|
||||
ret = adp5588_write(client, UNLOCK1 + i, kpad->unlock_keys[i]);
|
||||
if (ret)
|
||||
return ret;
|
||||
}
|
||||
|
||||
for (i = 0; i < KEYP_MAX_EVENT; i++)
|
||||
ret |= adp5588_read(client, Key_EVENTA);
|
||||
|
||||
for (i = 0; i < pdata->gpimapsize; i++) {
|
||||
unsigned short pin = pdata->gpimap[i].pin;
|
||||
|
||||
if (pin <= GPI_PIN_ROW_END) {
|
||||
evt_mode1 |= (1 << (pin - GPI_PIN_ROW_BASE));
|
||||
} else {
|
||||
evt_mode2 |= ((1 << (pin - GPI_PIN_COL_BASE)) & 0xFF);
|
||||
evt_mode3 |= ((1 << (pin - GPI_PIN_COL_BASE)) >> 8);
|
||||
}
|
||||
if (kpad->nkeys_unlock) {
|
||||
ret = adp5588_write(client, KEY_LCK_EC_STAT, ADP5588_K_LCK_EN);
|
||||
if (ret)
|
||||
return ret;
|
||||
}
|
||||
|
||||
if (pdata->gpimapsize) {
|
||||
ret |= adp5588_write(client, GPI_EM1, evt_mode1);
|
||||
ret |= adp5588_write(client, GPI_EM2, evt_mode2);
|
||||
ret |= adp5588_write(client, GPI_EM3, evt_mode3);
|
||||
for (i = 0; i < KEYP_MAX_EVENT; i++) {
|
||||
ret = adp5588_read(client, KEY_EVENTA);
|
||||
if (ret)
|
||||
return ret;
|
||||
}
|
||||
|
||||
if (gpio_data) {
|
||||
for (i = 0; i <= ADP5588_BANK(ADP5588_MAXGPIO); i++) {
|
||||
int pull_mask = gpio_data->pullup_dis_mask;
|
||||
|
||||
ret |= adp5588_write(client, GPIO_PULL1 + i,
|
||||
(pull_mask >> (8 * i)) & 0xFF);
|
||||
}
|
||||
}
|
||||
|
||||
ret |= adp5588_write(client, INT_STAT,
|
||||
ret = adp5588_write(client, INT_STAT,
|
||||
ADP5588_CMP2_INT | ADP5588_CMP1_INT |
|
||||
ADP5588_OVR_FLOW_INT | ADP5588_K_LCK_INT |
|
||||
ADP5588_GPI_INT | ADP5588_KE_INT); /* Status is W1C */
|
||||
|
||||
ret |= adp5588_write(client, CFG, ADP5588_INT_CFG |
|
||||
ADP5588_OVR_FLOW_IEN |
|
||||
ADP5588_KE_IEN);
|
||||
|
||||
if (ret < 0) {
|
||||
dev_err(&client->dev, "Write Error\n");
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
return adp5588_write(client, CFG, ADP5588_INT_CFG |
|
||||
ADP5588_OVR_FLOW_IEN | ADP5588_KE_IEN);
|
||||
}
|
||||
|
||||
static int adp5588_fw_parse(struct adp5588_kpad *kpad)
|
||||
{
|
||||
struct i2c_client *client = kpad->client;
|
||||
int ret, i;
|
||||
|
||||
ret = matrix_keypad_parse_properties(&client->dev, &kpad->rows,
|
||||
&kpad->cols);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
if (kpad->rows > ADP5588_ROWS_MAX || kpad->cols > ADP5588_COLS_MAX) {
|
||||
dev_err(&client->dev, "Invalid nr of rows(%u) or cols(%u)\n",
|
||||
kpad->rows, kpad->cols);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
ret = matrix_keypad_build_keymap(NULL, NULL, kpad->rows, kpad->cols,
|
||||
kpad->keycode, kpad->input);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
kpad->row_shift = get_count_order(kpad->cols);
|
||||
|
||||
if (device_property_read_bool(&client->dev, "autorepeat"))
|
||||
__set_bit(EV_REP, kpad->input->evbit);
|
||||
|
||||
kpad->nkeys_unlock = device_property_count_u32(&client->dev,
|
||||
"adi,unlock-keys");
|
||||
if (kpad->nkeys_unlock <= 0) {
|
||||
/* so that we don't end up enabling key lock */
|
||||
kpad->nkeys_unlock = 0;
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (kpad->nkeys_unlock > ARRAY_SIZE(kpad->unlock_keys)) {
|
||||
dev_err(&client->dev, "number of unlock keys(%d) > (%zu)\n",
|
||||
kpad->nkeys_unlock, ARRAY_SIZE(kpad->unlock_keys));
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
ret = device_property_read_u32_array(&client->dev, "adi,unlock-keys",
|
||||
kpad->unlock_keys,
|
||||
kpad->nkeys_unlock);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
for (i = 0; i < kpad->nkeys_unlock; i++) {
|
||||
/*
|
||||
* Even though it should be possible (as stated in the datasheet)
|
||||
* to use GPIs (which are part of the keys event) as unlock keys,
|
||||
* it was not working at all and was leading to overflow events
|
||||
* at some point. Hence, for now, let's just allow keys which are
|
||||
* part of keypad matrix to be used and if a reliable way of
|
||||
* using GPIs is found, this condition can be removed/lightened.
|
||||
*/
|
||||
if (kpad->unlock_keys[i] >= kpad->cols * kpad->rows) {
|
||||
dev_err(&client->dev, "Invalid unlock key(%d)\n",
|
||||
kpad->unlock_keys[i]);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
/*
|
||||
* Firmware properties keys start from 0 but on the device they
|
||||
* start from 1.
|
||||
*/
|
||||
kpad->unlock_keys[i] += 1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void adp5588_report_switch_state(struct adp5588_kpad *kpad)
|
||||
static void adp5588_disable_regulator(void *reg)
|
||||
{
|
||||
int gpi_stat1 = adp5588_read(kpad->client, GPIO_DAT_STAT1);
|
||||
int gpi_stat2 = adp5588_read(kpad->client, GPIO_DAT_STAT2);
|
||||
int gpi_stat3 = adp5588_read(kpad->client, GPIO_DAT_STAT3);
|
||||
int gpi_stat_tmp, pin_loc;
|
||||
int i;
|
||||
|
||||
for (i = 0; i < kpad->gpimapsize; i++) {
|
||||
unsigned short pin = kpad->gpimap[i].pin;
|
||||
|
||||
if (pin <= GPI_PIN_ROW_END) {
|
||||
gpi_stat_tmp = gpi_stat1;
|
||||
pin_loc = pin - GPI_PIN_ROW_BASE;
|
||||
} else if ((pin - GPI_PIN_COL_BASE) < 8) {
|
||||
gpi_stat_tmp = gpi_stat2;
|
||||
pin_loc = pin - GPI_PIN_COL_BASE;
|
||||
} else {
|
||||
gpi_stat_tmp = gpi_stat3;
|
||||
pin_loc = pin - GPI_PIN_COL_BASE - 8;
|
||||
regulator_disable(reg);
|
||||
}
|
||||
|
||||
if (gpi_stat_tmp < 0) {
|
||||
dev_err(&kpad->client->dev,
|
||||
"Can't read GPIO_DAT_STAT switch %d default to OFF\n",
|
||||
pin);
|
||||
gpi_stat_tmp = 0;
|
||||
}
|
||||
|
||||
input_report_switch(kpad->input,
|
||||
kpad->gpimap[i].sw_evt,
|
||||
!(gpi_stat_tmp & (1 << pin_loc)));
|
||||
}
|
||||
|
||||
input_sync(kpad->input);
|
||||
}
|
||||
|
||||
|
||||
static int adp5588_probe(struct i2c_client *client,
|
||||
const struct i2c_device_id *id)
|
||||
{
|
||||
struct adp5588_kpad *kpad;
|
||||
const struct adp5588_kpad_platform_data *pdata =
|
||||
dev_get_platdata(&client->dev);
|
||||
struct input_dev *input;
|
||||
struct gpio_desc *gpio;
|
||||
struct regulator *vcc;
|
||||
unsigned int revid;
|
||||
int ret, i;
|
||||
int ret;
|
||||
int error;
|
||||
|
||||
if (!i2c_check_functionality(client->adapter,
|
||||
@ -454,57 +735,6 @@ static int adp5588_probe(struct i2c_client *client,
|
||||
return -EIO;
|
||||
}
|
||||
|
||||
if (!pdata) {
|
||||
dev_err(&client->dev, "no platform data?\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
if (!pdata->rows || !pdata->cols || !pdata->keymap) {
|
||||
dev_err(&client->dev, "no rows, cols or keymap from pdata\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
if (pdata->keymapsize != ADP5588_KEYMAPSIZE) {
|
||||
dev_err(&client->dev, "invalid keymapsize\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
if (!pdata->gpimap && pdata->gpimapsize) {
|
||||
dev_err(&client->dev, "invalid gpimap from pdata\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
if (pdata->gpimapsize > ADP5588_GPIMAPSIZE_MAX) {
|
||||
dev_err(&client->dev, "invalid gpimapsize\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
for (i = 0; i < pdata->gpimapsize; i++) {
|
||||
unsigned short pin = pdata->gpimap[i].pin;
|
||||
|
||||
if (pin < GPI_PIN_BASE || pin > GPI_PIN_END) {
|
||||
dev_err(&client->dev, "invalid gpi pin data\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
if (pin <= GPI_PIN_ROW_END) {
|
||||
if (pin - GPI_PIN_ROW_BASE + 1 <= pdata->rows) {
|
||||
dev_err(&client->dev, "invalid gpi row data\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
} else {
|
||||
if (pin - GPI_PIN_COL_BASE + 1 <= pdata->cols) {
|
||||
dev_err(&client->dev, "invalid gpi col data\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (!client->irq) {
|
||||
dev_err(&client->dev, "no IRQ?\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
kpad = devm_kzalloc(&client->dev, sizeof(*kpad), GFP_KERNEL);
|
||||
if (!kpad)
|
||||
return -ENOMEM;
|
||||
@ -516,11 +746,38 @@ static int adp5588_probe(struct i2c_client *client,
|
||||
kpad->client = client;
|
||||
kpad->input = input;
|
||||
|
||||
error = adp5588_fw_parse(kpad);
|
||||
if (error)
|
||||
return error;
|
||||
|
||||
vcc = devm_regulator_get(&client->dev, "vcc");
|
||||
if (IS_ERR(vcc))
|
||||
return PTR_ERR(vcc);
|
||||
|
||||
error = regulator_enable(vcc);
|
||||
if (error)
|
||||
return error;
|
||||
|
||||
error = devm_add_action_or_reset(&client->dev,
|
||||
adp5588_disable_regulator, vcc);
|
||||
if (error)
|
||||
return error;
|
||||
|
||||
gpio = devm_gpiod_get_optional(&client->dev, "reset", GPIOD_OUT_HIGH);
|
||||
if (IS_ERR(gpio))
|
||||
return PTR_ERR(gpio);
|
||||
|
||||
if (gpio) {
|
||||
fsleep(30);
|
||||
gpiod_set_value_cansleep(gpio, 0);
|
||||
fsleep(60);
|
||||
}
|
||||
|
||||
ret = adp5588_read(client, DEV_ID);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
revid = (u8) ret & ADP5588_DEVICE_ID_MASK;
|
||||
revid = ret & ADP5588_DEVICE_ID_MASK;
|
||||
if (WA_DELAYED_READOUT_REVID(revid))
|
||||
kpad->delay = msecs_to_jiffies(WA_DELAYED_READOUT_TIME);
|
||||
|
||||
@ -534,32 +791,6 @@ static int adp5588_probe(struct i2c_client *client,
|
||||
input->id.product = 0x0001;
|
||||
input->id.version = revid;
|
||||
|
||||
input->keycodesize = sizeof(kpad->keycode[0]);
|
||||
input->keycodemax = pdata->keymapsize;
|
||||
input->keycode = kpad->keycode;
|
||||
|
||||
memcpy(kpad->keycode, pdata->keymap,
|
||||
pdata->keymapsize * input->keycodesize);
|
||||
|
||||
kpad->gpimap = pdata->gpimap;
|
||||
kpad->gpimapsize = pdata->gpimapsize;
|
||||
|
||||
/* setup input device */
|
||||
__set_bit(EV_KEY, input->evbit);
|
||||
|
||||
if (pdata->repeat)
|
||||
__set_bit(EV_REP, input->evbit);
|
||||
|
||||
for (i = 0; i < input->keycodemax; i++)
|
||||
if (kpad->keycode[i] <= KEY_MAX)
|
||||
__set_bit(kpad->keycode[i], input->keybit);
|
||||
__clear_bit(KEY_RESERVED, input->keybit);
|
||||
|
||||
if (kpad->gpimapsize)
|
||||
__set_bit(EV_SW, input->evbit);
|
||||
for (i = 0; i < kpad->gpimapsize; i++)
|
||||
__set_bit(kpad->gpimap[i].sw_evt, input->swbit);
|
||||
|
||||
error = input_register_device(input);
|
||||
if (error) {
|
||||
dev_err(&client->dev, "unable to register input device: %d\n",
|
||||
@ -567,6 +798,14 @@ static int adp5588_probe(struct i2c_client *client,
|
||||
return error;
|
||||
}
|
||||
|
||||
error = adp5588_setup(kpad);
|
||||
if (error)
|
||||
return error;
|
||||
|
||||
error = adp5588_gpio_add(kpad);
|
||||
if (error)
|
||||
return error;
|
||||
|
||||
error = devm_request_threaded_irq(&client->dev, client->irq,
|
||||
adp5588_hard_irq, adp5588_thread_irq,
|
||||
IRQF_TRIGGER_FALLING | IRQF_ONESHOT,
|
||||
@ -577,17 +816,6 @@ static int adp5588_probe(struct i2c_client *client,
|
||||
return error;
|
||||
}
|
||||
|
||||
error = adp5588_setup(client);
|
||||
if (error)
|
||||
return error;
|
||||
|
||||
if (kpad->gpimapsize)
|
||||
adp5588_report_switch_state(kpad);
|
||||
|
||||
error = adp5588_gpio_add(kpad);
|
||||
if (error)
|
||||
return error;
|
||||
|
||||
dev_info(&client->dev, "Rev.%d keypad, irq %d\n", revid, client->irq);
|
||||
return 0;
|
||||
}
|
||||
@ -599,7 +827,7 @@ static void adp5588_remove(struct i2c_client *client)
|
||||
/* all resources will be freed by devm */
|
||||
}
|
||||
|
||||
static int __maybe_unused adp5588_suspend(struct device *dev)
|
||||
static int adp5588_suspend(struct device *dev)
|
||||
{
|
||||
struct i2c_client *client = to_i2c_client(dev);
|
||||
|
||||
@ -608,7 +836,7 @@ static int __maybe_unused adp5588_suspend(struct device *dev)
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int __maybe_unused adp5588_resume(struct device *dev)
|
||||
static int adp5588_resume(struct device *dev)
|
||||
{
|
||||
struct i2c_client *client = to_i2c_client(dev);
|
||||
|
||||
@ -617,7 +845,7 @@ static int __maybe_unused adp5588_resume(struct device *dev)
|
||||
return 0;
|
||||
}
|
||||
|
||||
static SIMPLE_DEV_PM_OPS(adp5588_dev_pm_ops, adp5588_suspend, adp5588_resume);
|
||||
static DEFINE_SIMPLE_DEV_PM_OPS(adp5588_dev_pm_ops, adp5588_suspend, adp5588_resume);
|
||||
|
||||
static const struct i2c_device_id adp5588_id[] = {
|
||||
{ "adp5588-keys", 0 },
|
||||
@ -626,10 +854,18 @@ static const struct i2c_device_id adp5588_id[] = {
|
||||
};
|
||||
MODULE_DEVICE_TABLE(i2c, adp5588_id);
|
||||
|
||||
static const struct of_device_id adp5588_of_match[] = {
|
||||
{ .compatible = "adi,adp5588" },
|
||||
{ .compatible = "adi,adp5587" },
|
||||
{}
|
||||
};
|
||||
MODULE_DEVICE_TABLE(of, adp5588_of_match);
|
||||
|
||||
static struct i2c_driver adp5588_driver = {
|
||||
.driver = {
|
||||
.name = KBUILD_MODNAME,
|
||||
.pm = &adp5588_dev_pm_ops,
|
||||
.of_match_table = adp5588_of_match,
|
||||
.pm = pm_sleep_ptr(&adp5588_dev_pm_ops),
|
||||
},
|
||||
.probe = adp5588_probe,
|
||||
.remove = adp5588_remove,
|
||||
|
@ -10,9 +10,6 @@
|
||||
* Amiga keyboard driver for Linux/m68k
|
||||
*/
|
||||
|
||||
/*
|
||||
*/
|
||||
|
||||
#include <linux/module.h>
|
||||
#include <linux/init.h>
|
||||
#include <linux/input.h>
|
||||
|
@ -202,7 +202,7 @@ struct command_protocol_tp_info {
|
||||
};
|
||||
|
||||
/**
|
||||
* struct touchpad_info - touchpad info response.
|
||||
* struct touchpad_info_protocol - touchpad info response.
|
||||
* message.type = 0x1020, message.length = 0x006e
|
||||
*
|
||||
* @unknown1: unknown
|
||||
@ -311,7 +311,7 @@ struct message {
|
||||
struct command_protocol_mt_init init_mt_command;
|
||||
struct command_protocol_capsl capsl_command;
|
||||
struct command_protocol_bl bl_command;
|
||||
u8 data[0];
|
||||
DECLARE_FLEX_ARRAY(u8, data);
|
||||
};
|
||||
};
|
||||
|
||||
|
@ -21,9 +21,6 @@
|
||||
* This driver only deals with handing key events off to the input layer.
|
||||
*/
|
||||
|
||||
/*
|
||||
*/
|
||||
|
||||
#include <linux/module.h>
|
||||
#include <linux/init.h>
|
||||
#include <linux/input.h>
|
||||
|
@ -323,11 +323,13 @@ static umode_t atkbd_attr_is_visible(struct kobject *kobj,
|
||||
return attr->mode;
|
||||
}
|
||||
|
||||
static struct attribute_group atkbd_attribute_group = {
|
||||
static const struct attribute_group atkbd_attribute_group = {
|
||||
.attrs = atkbd_attributes,
|
||||
.is_visible = atkbd_attr_is_visible,
|
||||
};
|
||||
|
||||
__ATTRIBUTE_GROUPS(atkbd_attribute);
|
||||
|
||||
static const unsigned int xl_table[] = {
|
||||
ATKBD_RET_BAT, ATKBD_RET_ERR, ATKBD_RET_ACK,
|
||||
ATKBD_RET_NAK, ATKBD_RET_HANJA, ATKBD_RET_HANGEUL,
|
||||
@ -922,8 +924,6 @@ static void atkbd_disconnect(struct serio *serio)
|
||||
{
|
||||
struct atkbd *atkbd = serio_get_drvdata(serio);
|
||||
|
||||
sysfs_remove_group(&serio->dev.kobj, &atkbd_attribute_group);
|
||||
|
||||
atkbd_disable(atkbd);
|
||||
|
||||
input_unregister_device(atkbd->dev);
|
||||
@ -1271,21 +1271,16 @@ static int atkbd_connect(struct serio *serio, struct serio_driver *drv)
|
||||
atkbd_set_keycode_table(atkbd);
|
||||
atkbd_set_device_attrs(atkbd);
|
||||
|
||||
err = sysfs_create_group(&serio->dev.kobj, &atkbd_attribute_group);
|
||||
if (err)
|
||||
goto fail3;
|
||||
|
||||
atkbd_enable(atkbd);
|
||||
if (serio->write)
|
||||
atkbd_activate(atkbd);
|
||||
|
||||
err = input_register_device(atkbd->dev);
|
||||
if (err)
|
||||
goto fail4;
|
||||
goto fail3;
|
||||
|
||||
return 0;
|
||||
|
||||
fail4: sysfs_remove_group(&serio->dev.kobj, &atkbd_attribute_group);
|
||||
fail3: serio_close(serio);
|
||||
fail2: serio_set_drvdata(serio, NULL);
|
||||
fail1: input_free_device(dev);
|
||||
@ -1379,6 +1374,7 @@ MODULE_DEVICE_TABLE(serio, atkbd_serio_ids);
|
||||
static struct serio_driver atkbd_drv = {
|
||||
.driver = {
|
||||
.name = "atkbd",
|
||||
.dev_groups = atkbd_attribute_groups,
|
||||
},
|
||||
.description = DRIVER_DESC,
|
||||
.id_table = atkbd_serio_ids,
|
||||
|
@ -6,9 +6,11 @@
|
||||
*/
|
||||
|
||||
#include <linux/input.h>
|
||||
#include <linux/mod_devicetable.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/of_gpio.h>
|
||||
#include <linux/gpio/consumer.h>
|
||||
#include <linux/platform_device.h>
|
||||
#include <linux/property.h>
|
||||
#include <linux/regmap.h>
|
||||
#include <linux/sched.h>
|
||||
#include <linux/input/matrix_keypad.h>
|
||||
@ -86,7 +88,6 @@ static int clps711x_keypad_probe(struct platform_device *pdev)
|
||||
{
|
||||
struct clps711x_keypad_data *priv;
|
||||
struct device *dev = &pdev->dev;
|
||||
struct device_node *np = dev->of_node;
|
||||
struct input_dev *input;
|
||||
u32 poll_interval;
|
||||
int i, err;
|
||||
@ -95,11 +96,11 @@ static int clps711x_keypad_probe(struct platform_device *pdev)
|
||||
if (!priv)
|
||||
return -ENOMEM;
|
||||
|
||||
priv->syscon = syscon_regmap_lookup_by_phandle(np, "syscon");
|
||||
priv->syscon = syscon_regmap_lookup_by_phandle(dev->of_node, "syscon");
|
||||
if (IS_ERR(priv->syscon))
|
||||
return PTR_ERR(priv->syscon);
|
||||
|
||||
priv->row_count = of_gpio_named_count(np, "row-gpios");
|
||||
priv->row_count = gpiod_count(dev, "row");
|
||||
if (priv->row_count < 1)
|
||||
return -EINVAL;
|
||||
|
||||
@ -119,7 +120,7 @@ static int clps711x_keypad_probe(struct platform_device *pdev)
|
||||
return PTR_ERR(data->desc);
|
||||
}
|
||||
|
||||
err = of_property_read_u32(np, "poll-interval", &poll_interval);
|
||||
err = device_property_read_u32(dev, "poll-interval", &poll_interval);
|
||||
if (err)
|
||||
return err;
|
||||
|
||||
@ -143,7 +144,7 @@ static int clps711x_keypad_probe(struct platform_device *pdev)
|
||||
return err;
|
||||
|
||||
input_set_capability(input, EV_MSC, MSC_SCAN);
|
||||
if (of_property_read_bool(np, "autorepeat"))
|
||||
if (device_property_read_bool(dev, "autorepeat"))
|
||||
__set_bit(EV_REP, input->evbit);
|
||||
|
||||
/* Set all columns to low */
|
||||
|
@ -23,6 +23,7 @@
|
||||
#include <linux/interrupt.h>
|
||||
#include <linux/clk.h>
|
||||
#include <linux/io.h>
|
||||
#include <linux/input.h>
|
||||
#include <linux/input/matrix_keypad.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/soc/cirrus/ep93xx.h>
|
||||
|
@ -7,6 +7,7 @@
|
||||
#include <linux/delay.h>
|
||||
#include <linux/device.h>
|
||||
#include <linux/err.h>
|
||||
#include <linux/input.h>
|
||||
#include <linux/input/matrix_keypad.h>
|
||||
#include <linux/interrupt.h>
|
||||
#include <linux/io.h>
|
||||
|
@ -46,9 +46,6 @@
|
||||
* http://www.vt100.net/manx/details?pn=EK-104AA-TM-001;id=21;cp=1
|
||||
*/
|
||||
|
||||
/*
|
||||
*/
|
||||
|
||||
#include <linux/delay.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/module.h>
|
||||
@ -359,18 +356,18 @@ static void lkkbd_detection_done(struct lkkbd *lk)
|
||||
*/
|
||||
switch (lk->id[4]) {
|
||||
case 1:
|
||||
strlcpy(lk->name, "DEC LK201 keyboard", sizeof(lk->name));
|
||||
strscpy(lk->name, "DEC LK201 keyboard", sizeof(lk->name));
|
||||
|
||||
if (lk201_compose_is_alt)
|
||||
lk->keycode[0xb1] = KEY_LEFTALT;
|
||||
break;
|
||||
|
||||
case 2:
|
||||
strlcpy(lk->name, "DEC LK401 keyboard", sizeof(lk->name));
|
||||
strscpy(lk->name, "DEC LK401 keyboard", sizeof(lk->name));
|
||||
break;
|
||||
|
||||
default:
|
||||
strlcpy(lk->name, "Unknown DEC keyboard", sizeof(lk->name));
|
||||
strscpy(lk->name, "Unknown DEC keyboard", sizeof(lk->name));
|
||||
printk(KERN_ERR
|
||||
"lkkbd: keyboard on %s is unknown, please report to "
|
||||
"Jan-Benedict Glaw <jbglaw@lug-owl.de>\n", lk->phys);
|
||||
@ -626,7 +623,7 @@ static int lkkbd_connect(struct serio *serio, struct serio_driver *drv)
|
||||
lk->ctrlclick_volume = ctrlclick_volume;
|
||||
memcpy(lk->keycode, lkkbd_keycode, sizeof(lk->keycode));
|
||||
|
||||
strlcpy(lk->name, "DEC LK keyboard", sizeof(lk->name));
|
||||
strscpy(lk->name, "DEC LK keyboard", sizeof(lk->name));
|
||||
snprintf(lk->phys, sizeof(lk->phys), "%s/input0", serio->phys);
|
||||
|
||||
input_dev->name = lk->name;
|
||||
|
@ -4,13 +4,13 @@
|
||||
* Copyright (C) 2012 Wolfram Sang, Pengutronix <kernel@pengutronix.de>
|
||||
*/
|
||||
|
||||
#include <linux/module.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/irq.h>
|
||||
#include <linux/i2c.h>
|
||||
#include <linux/interrupt.h>
|
||||
#include <linux/input.h>
|
||||
#include <linux/input/matrix_keypad.h>
|
||||
#include <linux/input/lm8333.h>
|
||||
#include <linux/interrupt.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/slab.h>
|
||||
|
||||
#define LM8333_FIFO_READ 0x20
|
||||
#define LM8333_DEBOUNCE 0x22
|
||||
|
@ -9,6 +9,7 @@
|
||||
|
||||
#include <linux/types.h>
|
||||
#include <linux/delay.h>
|
||||
#include <linux/gpio/consumer.h>
|
||||
#include <linux/platform_device.h>
|
||||
#include <linux/input.h>
|
||||
#include <linux/irq.h>
|
||||
@ -416,9 +417,9 @@ matrix_keypad_parse_dt(struct device *dev)
|
||||
return ERR_PTR(-ENOMEM);
|
||||
}
|
||||
|
||||
pdata->num_row_gpios = nrow = of_gpio_named_count(np, "row-gpios");
|
||||
pdata->num_col_gpios = ncol = of_gpio_named_count(np, "col-gpios");
|
||||
if (nrow <= 0 || ncol <= 0) {
|
||||
pdata->num_row_gpios = nrow = gpiod_count(dev, "row");
|
||||
pdata->num_col_gpios = ncol = gpiod_count(dev, "col");
|
||||
if (nrow < 0 || ncol < 0) {
|
||||
dev_err(dev, "number of keypad rows/columns not specified\n");
|
||||
return ERR_PTR(-EINVAL);
|
||||
}
|
||||
|
@ -5,6 +5,7 @@
|
||||
*/
|
||||
#include <linux/bitops.h>
|
||||
#include <linux/clk.h>
|
||||
#include <linux/input.h>
|
||||
#include <linux/input/matrix_keypad.h>
|
||||
#include <linux/interrupt.h>
|
||||
#include <linux/module.h>
|
||||
@ -18,6 +19,7 @@
|
||||
#define MTK_KPD_DEBOUNCE_MASK GENMASK(13, 0)
|
||||
#define MTK_KPD_DEBOUNCE_MAX_MS 256
|
||||
#define MTK_KPD_SEL 0x0020
|
||||
#define MTK_KPD_SEL_DOUBLE_KP_MODE BIT(0)
|
||||
#define MTK_KPD_SEL_COL GENMASK(15, 10)
|
||||
#define MTK_KPD_SEL_ROW GENMASK(9, 4)
|
||||
#define MTK_KPD_SEL_COLMASK(c) GENMASK((c) + 9, 10)
|
||||
@ -31,6 +33,8 @@ struct mt6779_keypad {
|
||||
struct clk *clk;
|
||||
u32 n_rows;
|
||||
u32 n_cols;
|
||||
void (*calc_row_col)(unsigned int key,
|
||||
unsigned int *row, unsigned int *col);
|
||||
DECLARE_BITMAP(keymap_state, MTK_KPD_NUM_BITS);
|
||||
};
|
||||
|
||||
@ -67,8 +71,7 @@ static irqreturn_t mt6779_keypad_irq_handler(int irq, void *dev_id)
|
||||
continue;
|
||||
|
||||
key = bit_nr / 32 * 16 + bit_nr % 32;
|
||||
row = key / 9;
|
||||
col = key % 9;
|
||||
keypad->calc_row_col(key, &row, &col);
|
||||
|
||||
scancode = MATRIX_SCAN_CODE(row, col, row_shift);
|
||||
/* 1: not pressed, 0: pressed */
|
||||
@ -94,12 +97,29 @@ static void mt6779_keypad_clk_disable(void *data)
|
||||
clk_disable_unprepare(data);
|
||||
}
|
||||
|
||||
static void mt6779_keypad_calc_row_col_single(unsigned int key,
|
||||
unsigned int *row,
|
||||
unsigned int *col)
|
||||
{
|
||||
*row = key / 9;
|
||||
*col = key % 9;
|
||||
}
|
||||
|
||||
static void mt6779_keypad_calc_row_col_double(unsigned int key,
|
||||
unsigned int *row,
|
||||
unsigned int *col)
|
||||
{
|
||||
*row = key / 13;
|
||||
*col = (key % 13) / 2;
|
||||
}
|
||||
|
||||
static int mt6779_keypad_pdrv_probe(struct platform_device *pdev)
|
||||
{
|
||||
struct mt6779_keypad *keypad;
|
||||
void __iomem *base;
|
||||
int irq;
|
||||
u32 debounce;
|
||||
u32 keys_per_group;
|
||||
bool wakeup;
|
||||
int error;
|
||||
|
||||
@ -148,6 +168,23 @@ static int mt6779_keypad_pdrv_probe(struct platform_device *pdev)
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
if (device_property_read_u32(&pdev->dev, "mediatek,keys-per-group",
|
||||
&keys_per_group))
|
||||
keys_per_group = 1;
|
||||
|
||||
switch (keys_per_group) {
|
||||
case 1:
|
||||
keypad->calc_row_col = mt6779_keypad_calc_row_col_single;
|
||||
break;
|
||||
case 2:
|
||||
keypad->calc_row_col = mt6779_keypad_calc_row_col_double;
|
||||
break;
|
||||
default:
|
||||
dev_err(&pdev->dev,
|
||||
"Invalid keys-per-group: %d\n", keys_per_group);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
wakeup = device_property_read_bool(&pdev->dev, "wakeup-source");
|
||||
|
||||
dev_dbg(&pdev->dev, "n_row=%d n_col=%d debounce=%d\n",
|
||||
@ -166,6 +203,11 @@ static int mt6779_keypad_pdrv_probe(struct platform_device *pdev)
|
||||
regmap_write(keypad->regmap, MTK_KPD_DEBOUNCE,
|
||||
(debounce * (1 << 5)) & MTK_KPD_DEBOUNCE_MASK);
|
||||
|
||||
if (keys_per_group == 2)
|
||||
regmap_update_bits(keypad->regmap, MTK_KPD_SEL,
|
||||
MTK_KPD_SEL_DOUBLE_KP_MODE,
|
||||
MTK_KPD_SEL_DOUBLE_KP_MODE);
|
||||
|
||||
regmap_update_bits(keypad->regmap, MTK_KPD_SEL, MTK_KPD_SEL_ROW,
|
||||
MTK_KPD_SEL_ROWMASK(keypad->n_rows));
|
||||
regmap_update_bits(keypad->regmap, MTK_KPD_SEL, MTK_KPD_SEL_COL,
|
||||
|
@ -9,6 +9,7 @@
|
||||
#include <linux/interrupt.h>
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/mfd/mt6323/registers.h>
|
||||
#include <linux/mfd/mt6331/registers.h>
|
||||
#include <linux/mfd/mt6358/registers.h>
|
||||
#include <linux/mfd/mt6397/core.h>
|
||||
#include <linux/mfd/mt6397/registers.h>
|
||||
@ -22,6 +23,10 @@
|
||||
#define MTK_PMIC_PWRKEY_RST BIT(6)
|
||||
#define MTK_PMIC_HOMEKEY_RST BIT(5)
|
||||
|
||||
#define MTK_PMIC_MT6331_RST_DU_MASK GENMASK(13, 12)
|
||||
#define MTK_PMIC_MT6331_PWRKEY_RST BIT(9)
|
||||
#define MTK_PMIC_MT6331_HOMEKEY_RST BIT(8)
|
||||
|
||||
#define MTK_PMIC_PWRKEY_INDEX 0
|
||||
#define MTK_PMIC_HOMEKEY_INDEX 1
|
||||
#define MTK_PMIC_MAX_KEY_COUNT 2
|
||||
@ -72,6 +77,19 @@ static const struct mtk_pmic_regs mt6323_regs = {
|
||||
.rst_lprst_mask = MTK_PMIC_RST_DU_MASK,
|
||||
};
|
||||
|
||||
static const struct mtk_pmic_regs mt6331_regs = {
|
||||
.keys_regs[MTK_PMIC_PWRKEY_INDEX] =
|
||||
MTK_PMIC_KEYS_REGS(MT6331_TOPSTATUS, 0x2,
|
||||
MT6331_INT_MISC_CON, 0x4,
|
||||
MTK_PMIC_MT6331_PWRKEY_RST),
|
||||
.keys_regs[MTK_PMIC_HOMEKEY_INDEX] =
|
||||
MTK_PMIC_KEYS_REGS(MT6331_TOPSTATUS, 0x4,
|
||||
MT6331_INT_MISC_CON, 0x2,
|
||||
MTK_PMIC_MT6331_HOMEKEY_RST),
|
||||
.pmic_rst_reg = MT6331_TOP_RST_MISC,
|
||||
.rst_lprst_mask = MTK_PMIC_MT6331_RST_DU_MASK,
|
||||
};
|
||||
|
||||
static const struct mtk_pmic_regs mt6358_regs = {
|
||||
.keys_regs[MTK_PMIC_PWRKEY_INDEX] =
|
||||
MTK_PMIC_KEYS_REGS(MT6358_TOPSTATUS,
|
||||
@ -255,6 +273,9 @@ static const struct of_device_id of_mtk_pmic_keys_match_tbl[] = {
|
||||
}, {
|
||||
.compatible = "mediatek,mt6323-keys",
|
||||
.data = &mt6323_regs,
|
||||
}, {
|
||||
.compatible = "mediatek,mt6331-keys",
|
||||
.data = &mt6331_regs,
|
||||
}, {
|
||||
.compatible = "mediatek,mt6358-keys",
|
||||
.data = &mt6358_regs,
|
||||
|
@ -7,9 +7,6 @@
|
||||
* Newton keyboard driver for Linux
|
||||
*/
|
||||
|
||||
/*
|
||||
*/
|
||||
|
||||
#include <linux/slab.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/input.h>
|
||||
|
468
drivers/input/keyboard/pinephone-keyboard.c
Normal file
468
drivers/input/keyboard/pinephone-keyboard.c
Normal file
@ -0,0 +1,468 @@
|
||||
// SPDX-License-Identifier: GPL-2.0-only
|
||||
//
|
||||
// Copyright (C) 2021-2022 Samuel Holland <samuel@sholland.org>
|
||||
|
||||
#include <linux/crc8.h>
|
||||
#include <linux/delay.h>
|
||||
#include <linux/err.h>
|
||||
#include <linux/i2c.h>
|
||||
#include <linux/input.h>
|
||||
#include <linux/input/matrix_keypad.h>
|
||||
#include <linux/interrupt.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/mod_devicetable.h>
|
||||
#include <linux/of.h>
|
||||
#include <linux/regulator/consumer.h>
|
||||
#include <linux/types.h>
|
||||
|
||||
#define DRV_NAME "pinephone-keyboard"
|
||||
|
||||
#define PPKB_CRC8_POLYNOMIAL 0x07
|
||||
|
||||
#define PPKB_DEVICE_ID_HI 0x00
|
||||
#define PPKB_DEVICE_ID_HI_VALUE 'K'
|
||||
#define PPKB_DEVICE_ID_LO 0x01
|
||||
#define PPKB_DEVICE_ID_LO_VALUE 'B'
|
||||
#define PPKB_FW_REVISION 0x02
|
||||
#define PPKB_FW_FEATURES 0x03
|
||||
#define PPKB_MATRIX_SIZE 0x06
|
||||
#define PPKB_SCAN_CRC 0x07
|
||||
#define PPKB_SCAN_DATA 0x08
|
||||
#define PPKB_SYS_CONFIG 0x20
|
||||
#define PPKB_SYS_CONFIG_DISABLE_SCAN BIT(0)
|
||||
#define PPKB_SYS_SMBUS_COMMAND 0x21
|
||||
#define PPKB_SYS_SMBUS_DATA 0x22
|
||||
#define PPKB_SYS_COMMAND 0x23
|
||||
#define PPKB_SYS_COMMAND_SMBUS_READ 0x91
|
||||
#define PPKB_SYS_COMMAND_SMBUS_WRITE 0xa1
|
||||
|
||||
#define PPKB_ROWS 6
|
||||
#define PPKB_COLS 12
|
||||
|
||||
/* Size of the scan buffer, including the CRC byte at the beginning. */
|
||||
#define PPKB_BUF_LEN (1 + PPKB_COLS)
|
||||
|
||||
static const uint32_t ppkb_keymap[] = {
|
||||
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(0, 9, KEY_9),
|
||||
KEY(0, 10, KEY_0),
|
||||
KEY(0, 11, KEY_BACKSPACE),
|
||||
|
||||
KEY(1, 0, KEY_TAB),
|
||||
KEY(1, 1, KEY_Q),
|
||||
KEY(1, 2, KEY_W),
|
||||
KEY(1, 3, KEY_E),
|
||||
KEY(1, 4, KEY_R),
|
||||
KEY(1, 5, KEY_T),
|
||||
KEY(1, 6, KEY_Y),
|
||||
KEY(1, 7, KEY_U),
|
||||
KEY(1, 8, KEY_I),
|
||||
KEY(1, 9, KEY_O),
|
||||
KEY(1, 10, KEY_P),
|
||||
KEY(1, 11, KEY_ENTER),
|
||||
|
||||
KEY(2, 0, KEY_LEFTMETA),
|
||||
KEY(2, 1, KEY_A),
|
||||
KEY(2, 2, KEY_S),
|
||||
KEY(2, 3, KEY_D),
|
||||
KEY(2, 4, KEY_F),
|
||||
KEY(2, 5, KEY_G),
|
||||
KEY(2, 6, KEY_H),
|
||||
KEY(2, 7, KEY_J),
|
||||
KEY(2, 8, KEY_K),
|
||||
KEY(2, 9, KEY_L),
|
||||
KEY(2, 10, KEY_SEMICOLON),
|
||||
|
||||
KEY(3, 0, KEY_LEFTSHIFT),
|
||||
KEY(3, 1, KEY_Z),
|
||||
KEY(3, 2, KEY_X),
|
||||
KEY(3, 3, KEY_C),
|
||||
KEY(3, 4, KEY_V),
|
||||
KEY(3, 5, KEY_B),
|
||||
KEY(3, 6, KEY_N),
|
||||
KEY(3, 7, KEY_M),
|
||||
KEY(3, 8, KEY_COMMA),
|
||||
KEY(3, 9, KEY_DOT),
|
||||
KEY(3, 10, KEY_SLASH),
|
||||
|
||||
KEY(4, 1, KEY_LEFTCTRL),
|
||||
KEY(4, 4, KEY_SPACE),
|
||||
KEY(4, 6, KEY_APOSTROPHE),
|
||||
KEY(4, 8, KEY_RIGHTBRACE),
|
||||
KEY(4, 9, KEY_LEFTBRACE),
|
||||
|
||||
KEY(5, 2, KEY_FN),
|
||||
KEY(5, 3, KEY_LEFTALT),
|
||||
KEY(5, 5, KEY_RIGHTALT),
|
||||
|
||||
/* FN layer */
|
||||
KEY(PPKB_ROWS + 0, 0, KEY_FN_ESC),
|
||||
KEY(PPKB_ROWS + 0, 1, KEY_F1),
|
||||
KEY(PPKB_ROWS + 0, 2, KEY_F2),
|
||||
KEY(PPKB_ROWS + 0, 3, KEY_F3),
|
||||
KEY(PPKB_ROWS + 0, 4, KEY_F4),
|
||||
KEY(PPKB_ROWS + 0, 5, KEY_F5),
|
||||
KEY(PPKB_ROWS + 0, 6, KEY_F6),
|
||||
KEY(PPKB_ROWS + 0, 7, KEY_F7),
|
||||
KEY(PPKB_ROWS + 0, 8, KEY_F8),
|
||||
KEY(PPKB_ROWS + 0, 9, KEY_F9),
|
||||
KEY(PPKB_ROWS + 0, 10, KEY_F10),
|
||||
KEY(PPKB_ROWS + 0, 11, KEY_DELETE),
|
||||
|
||||
KEY(PPKB_ROWS + 1, 10, KEY_PAGEUP),
|
||||
|
||||
KEY(PPKB_ROWS + 2, 0, KEY_SYSRQ),
|
||||
KEY(PPKB_ROWS + 2, 9, KEY_PAGEDOWN),
|
||||
KEY(PPKB_ROWS + 2, 10, KEY_INSERT),
|
||||
|
||||
KEY(PPKB_ROWS + 3, 0, KEY_LEFTSHIFT),
|
||||
KEY(PPKB_ROWS + 3, 8, KEY_HOME),
|
||||
KEY(PPKB_ROWS + 3, 9, KEY_UP),
|
||||
KEY(PPKB_ROWS + 3, 10, KEY_END),
|
||||
|
||||
KEY(PPKB_ROWS + 4, 1, KEY_LEFTCTRL),
|
||||
KEY(PPKB_ROWS + 4, 6, KEY_LEFT),
|
||||
KEY(PPKB_ROWS + 4, 8, KEY_RIGHT),
|
||||
KEY(PPKB_ROWS + 4, 9, KEY_DOWN),
|
||||
|
||||
KEY(PPKB_ROWS + 5, 3, KEY_LEFTALT),
|
||||
KEY(PPKB_ROWS + 5, 5, KEY_RIGHTALT),
|
||||
};
|
||||
|
||||
static const struct matrix_keymap_data ppkb_keymap_data = {
|
||||
.keymap = ppkb_keymap,
|
||||
.keymap_size = ARRAY_SIZE(ppkb_keymap),
|
||||
};
|
||||
|
||||
struct pinephone_keyboard {
|
||||
struct i2c_adapter adapter;
|
||||
struct input_dev *input;
|
||||
u8 buf[2][PPKB_BUF_LEN];
|
||||
u8 crc_table[CRC8_TABLE_SIZE];
|
||||
u8 fn_state[PPKB_COLS];
|
||||
bool buf_swap;
|
||||
bool fn_pressed;
|
||||
};
|
||||
|
||||
static int ppkb_adap_smbus_xfer(struct i2c_adapter *adap, u16 addr,
|
||||
unsigned short flags, char read_write,
|
||||
u8 command, int size,
|
||||
union i2c_smbus_data *data)
|
||||
{
|
||||
struct i2c_client *client = adap->algo_data;
|
||||
u8 buf[3];
|
||||
int ret;
|
||||
|
||||
buf[0] = command;
|
||||
buf[1] = data->byte;
|
||||
buf[2] = read_write == I2C_SMBUS_READ ? PPKB_SYS_COMMAND_SMBUS_READ
|
||||
: PPKB_SYS_COMMAND_SMBUS_WRITE;
|
||||
|
||||
ret = i2c_smbus_write_i2c_block_data(client, PPKB_SYS_SMBUS_COMMAND,
|
||||
sizeof(buf), buf);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
/* Read back the command status until it passes or fails. */
|
||||
do {
|
||||
usleep_range(300, 500);
|
||||
ret = i2c_smbus_read_byte_data(client, PPKB_SYS_COMMAND);
|
||||
} while (ret == buf[2]);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
/* Commands return 0x00 on success and 0xff on failure. */
|
||||
if (ret)
|
||||
return -EIO;
|
||||
|
||||
if (read_write == I2C_SMBUS_READ) {
|
||||
ret = i2c_smbus_read_byte_data(client, PPKB_SYS_SMBUS_DATA);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
data->byte = ret;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static u32 ppkg_adap_functionality(struct i2c_adapter *adap)
|
||||
{
|
||||
return I2C_FUNC_SMBUS_BYTE_DATA;
|
||||
}
|
||||
|
||||
static const struct i2c_algorithm ppkb_adap_algo = {
|
||||
.smbus_xfer = ppkb_adap_smbus_xfer,
|
||||
.functionality = ppkg_adap_functionality,
|
||||
};
|
||||
|
||||
static void ppkb_update(struct i2c_client *client)
|
||||
{
|
||||
struct pinephone_keyboard *ppkb = i2c_get_clientdata(client);
|
||||
unsigned short *keymap = ppkb->input->keycode;
|
||||
int row_shift = get_count_order(PPKB_COLS);
|
||||
u8 *old_buf = ppkb->buf[!ppkb->buf_swap];
|
||||
u8 *new_buf = ppkb->buf[ppkb->buf_swap];
|
||||
int col, crc, ret, row;
|
||||
struct device *dev = &client->dev;
|
||||
|
||||
ret = i2c_smbus_read_i2c_block_data(client, PPKB_SCAN_CRC,
|
||||
PPKB_BUF_LEN, new_buf);
|
||||
if (ret != PPKB_BUF_LEN) {
|
||||
dev_err(dev, "Failed to read scan data: %d\n", ret);
|
||||
return;
|
||||
}
|
||||
|
||||
crc = crc8(ppkb->crc_table, &new_buf[1], PPKB_COLS, CRC8_INIT_VALUE);
|
||||
if (crc != new_buf[0]) {
|
||||
dev_err(dev, "Bad scan data (%02x != %02x)\n", crc, new_buf[0]);
|
||||
return;
|
||||
}
|
||||
|
||||
ppkb->buf_swap = !ppkb->buf_swap;
|
||||
|
||||
for (col = 0; col < PPKB_COLS; ++col) {
|
||||
u8 old = old_buf[1 + col];
|
||||
u8 new = new_buf[1 + col];
|
||||
u8 changed = old ^ new;
|
||||
|
||||
if (!changed)
|
||||
continue;
|
||||
|
||||
for (row = 0; row < PPKB_ROWS; ++row) {
|
||||
u8 mask = BIT(row);
|
||||
u8 value = new & mask;
|
||||
unsigned short code;
|
||||
bool fn_state;
|
||||
|
||||
if (!(changed & mask))
|
||||
continue;
|
||||
|
||||
/*
|
||||
* Save off the FN key state when the key was pressed,
|
||||
* and use that to determine the code during a release.
|
||||
*/
|
||||
fn_state = value ? ppkb->fn_pressed : ppkb->fn_state[col] & mask;
|
||||
if (fn_state)
|
||||
ppkb->fn_state[col] ^= mask;
|
||||
|
||||
/* The FN layer is a second set of rows. */
|
||||
code = MATRIX_SCAN_CODE(fn_state ? PPKB_ROWS + row : row,
|
||||
col, row_shift);
|
||||
input_event(ppkb->input, EV_MSC, MSC_SCAN, code);
|
||||
input_report_key(ppkb->input, keymap[code], value);
|
||||
if (keymap[code] == KEY_FN)
|
||||
ppkb->fn_pressed = value;
|
||||
}
|
||||
}
|
||||
input_sync(ppkb->input);
|
||||
}
|
||||
|
||||
static irqreturn_t ppkb_irq_thread(int irq, void *data)
|
||||
{
|
||||
struct i2c_client *client = data;
|
||||
|
||||
ppkb_update(client);
|
||||
|
||||
return IRQ_HANDLED;
|
||||
}
|
||||
|
||||
static int ppkb_set_scan(struct i2c_client *client, bool enable)
|
||||
{
|
||||
struct device *dev = &client->dev;
|
||||
int ret, val;
|
||||
|
||||
ret = i2c_smbus_read_byte_data(client, PPKB_SYS_CONFIG);
|
||||
if (ret < 0) {
|
||||
dev_err(dev, "Failed to read config: %d\n", ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
if (enable)
|
||||
val = ret & ~PPKB_SYS_CONFIG_DISABLE_SCAN;
|
||||
else
|
||||
val = ret | PPKB_SYS_CONFIG_DISABLE_SCAN;
|
||||
|
||||
ret = i2c_smbus_write_byte_data(client, PPKB_SYS_CONFIG, val);
|
||||
if (ret) {
|
||||
dev_err(dev, "Failed to write config: %d\n", ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int ppkb_open(struct input_dev *input)
|
||||
{
|
||||
struct i2c_client *client = input_get_drvdata(input);
|
||||
int error;
|
||||
|
||||
error = ppkb_set_scan(client, true);
|
||||
if (error)
|
||||
return error;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void ppkb_close(struct input_dev *input)
|
||||
{
|
||||
struct i2c_client *client = input_get_drvdata(input);
|
||||
|
||||
ppkb_set_scan(client, false);
|
||||
}
|
||||
|
||||
static void ppkb_regulator_disable(void *regulator)
|
||||
{
|
||||
regulator_disable(regulator);
|
||||
}
|
||||
|
||||
static int ppkb_probe(struct i2c_client *client)
|
||||
{
|
||||
struct device *dev = &client->dev;
|
||||
unsigned int phys_rows, phys_cols;
|
||||
struct pinephone_keyboard *ppkb;
|
||||
struct regulator *vbat_supply;
|
||||
u8 info[PPKB_MATRIX_SIZE + 1];
|
||||
struct device_node *i2c_bus;
|
||||
int ret;
|
||||
int error;
|
||||
|
||||
vbat_supply = devm_regulator_get(dev, "vbat");
|
||||
error = PTR_ERR_OR_ZERO(vbat_supply);
|
||||
if (error) {
|
||||
dev_err(dev, "Failed to get VBAT supply: %d\n", error);
|
||||
return error;
|
||||
}
|
||||
|
||||
error = regulator_enable(vbat_supply);
|
||||
if (error) {
|
||||
dev_err(dev, "Failed to enable VBAT: %d\n", error);
|
||||
return error;
|
||||
}
|
||||
|
||||
error = devm_add_action_or_reset(dev, ppkb_regulator_disable,
|
||||
vbat_supply);
|
||||
if (error)
|
||||
return error;
|
||||
|
||||
ret = i2c_smbus_read_i2c_block_data(client, 0, sizeof(info), info);
|
||||
if (ret != sizeof(info)) {
|
||||
error = ret < 0 ? ret : -EIO;
|
||||
dev_err(dev, "Failed to read device ID: %d\n", error);
|
||||
return error;
|
||||
}
|
||||
|
||||
if (info[PPKB_DEVICE_ID_HI] != PPKB_DEVICE_ID_HI_VALUE ||
|
||||
info[PPKB_DEVICE_ID_LO] != PPKB_DEVICE_ID_LO_VALUE) {
|
||||
dev_warn(dev, "Unexpected device ID: %#02x %#02x\n",
|
||||
info[PPKB_DEVICE_ID_HI], info[PPKB_DEVICE_ID_LO]);
|
||||
return -ENODEV;
|
||||
}
|
||||
|
||||
dev_info(dev, "Found firmware version %d.%d features %#x\n",
|
||||
info[PPKB_FW_REVISION] >> 4,
|
||||
info[PPKB_FW_REVISION] & 0xf,
|
||||
info[PPKB_FW_FEATURES]);
|
||||
|
||||
phys_rows = info[PPKB_MATRIX_SIZE] & 0xf;
|
||||
phys_cols = info[PPKB_MATRIX_SIZE] >> 4;
|
||||
if (phys_rows != PPKB_ROWS || phys_cols != PPKB_COLS) {
|
||||
dev_err(dev, "Unexpected keyboard size %ux%u\n",
|
||||
phys_rows, phys_cols);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
/* Disable scan by default to save power. */
|
||||
error = ppkb_set_scan(client, false);
|
||||
if (error)
|
||||
return error;
|
||||
|
||||
ppkb = devm_kzalloc(dev, sizeof(*ppkb), GFP_KERNEL);
|
||||
if (!ppkb)
|
||||
return -ENOMEM;
|
||||
|
||||
i2c_set_clientdata(client, ppkb);
|
||||
|
||||
i2c_bus = of_get_child_by_name(dev->of_node, "i2c");
|
||||
if (i2c_bus) {
|
||||
ppkb->adapter.owner = THIS_MODULE;
|
||||
ppkb->adapter.algo = &ppkb_adap_algo;
|
||||
ppkb->adapter.algo_data = client;
|
||||
ppkb->adapter.dev.parent = dev;
|
||||
ppkb->adapter.dev.of_node = i2c_bus;
|
||||
strscpy(ppkb->adapter.name, DRV_NAME, sizeof(ppkb->adapter.name));
|
||||
|
||||
error = devm_i2c_add_adapter(dev, &ppkb->adapter);
|
||||
if (error) {
|
||||
dev_err(dev, "Failed to add I2C adapter: %d\n", error);
|
||||
return error;
|
||||
}
|
||||
}
|
||||
|
||||
crc8_populate_msb(ppkb->crc_table, PPKB_CRC8_POLYNOMIAL);
|
||||
|
||||
ppkb->input = devm_input_allocate_device(dev);
|
||||
if (!ppkb->input)
|
||||
return -ENOMEM;
|
||||
|
||||
input_set_drvdata(ppkb->input, client);
|
||||
|
||||
ppkb->input->name = "PinePhone Keyboard";
|
||||
ppkb->input->phys = DRV_NAME "/input0";
|
||||
ppkb->input->id.bustype = BUS_I2C;
|
||||
ppkb->input->open = ppkb_open;
|
||||
ppkb->input->close = ppkb_close;
|
||||
|
||||
input_set_capability(ppkb->input, EV_MSC, MSC_SCAN);
|
||||
__set_bit(EV_REP, ppkb->input->evbit);
|
||||
|
||||
error = matrix_keypad_build_keymap(&ppkb_keymap_data, NULL,
|
||||
2 * PPKB_ROWS, PPKB_COLS, NULL,
|
||||
ppkb->input);
|
||||
if (error) {
|
||||
dev_err(dev, "Failed to build keymap: %d\n", error);
|
||||
return error;
|
||||
}
|
||||
|
||||
error = input_register_device(ppkb->input);
|
||||
if (error) {
|
||||
dev_err(dev, "Failed to register input: %d\n", error);
|
||||
return error;
|
||||
}
|
||||
|
||||
error = devm_request_threaded_irq(dev, client->irq,
|
||||
NULL, ppkb_irq_thread,
|
||||
IRQF_ONESHOT, client->name, client);
|
||||
if (error) {
|
||||
dev_err(dev, "Failed to request IRQ: %d\n", error);
|
||||
return error;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct of_device_id ppkb_of_match[] = {
|
||||
{ .compatible = "pine64,pinephone-keyboard" },
|
||||
{ }
|
||||
};
|
||||
MODULE_DEVICE_TABLE(of, ppkb_of_match);
|
||||
|
||||
static struct i2c_driver ppkb_driver = {
|
||||
.probe_new = ppkb_probe,
|
||||
.driver = {
|
||||
.name = DRV_NAME,
|
||||
.of_match_table = ppkb_of_match,
|
||||
},
|
||||
};
|
||||
module_i2c_driver(ppkb_driver);
|
||||
|
||||
MODULE_AUTHOR("Samuel Holland <samuel@sholland.org>");
|
||||
MODULE_DESCRIPTION("Pine64 PinePhone keyboard driver");
|
||||
MODULE_LICENSE("GPL");
|
@ -8,12 +8,14 @@
|
||||
* Based on sh_keysc.c, copyright 2008 Magnus Damm
|
||||
*/
|
||||
|
||||
#include <linux/module.h>
|
||||
#include <linux/interrupt.h>
|
||||
#include <linux/platform_device.h>
|
||||
#include <linux/clk.h>
|
||||
#include <linux/io.h>
|
||||
#include <linux/input.h>
|
||||
#include <linux/input/matrix_keypad.h>
|
||||
#include <linux/io.h>
|
||||
#include <linux/interrupt.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/of.h>
|
||||
#include <linux/platform_device.h>
|
||||
|
||||
#define ST_KEYSCAN_MAXKEYS 16
|
||||
|
||||
|
@ -10,9 +10,6 @@
|
||||
* by Justin Cormack
|
||||
*/
|
||||
|
||||
/*
|
||||
*/
|
||||
|
||||
#include <linux/slab.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/input.h>
|
||||
|
@ -7,9 +7,6 @@
|
||||
* Sun keyboard driver for Linux
|
||||
*/
|
||||
|
||||
/*
|
||||
*/
|
||||
|
||||
#include <linux/delay.h>
|
||||
#include <linux/sched.h>
|
||||
#include <linux/slab.h>
|
||||
|
@ -70,7 +70,7 @@
|
||||
#define TC3589x_KBD_INT_CLR 0x1
|
||||
|
||||
/**
|
||||
* struct tc35893_keypad_platform_data - platform specific keypad data
|
||||
* struct tc3589x_keypad_platform_data - platform specific keypad data
|
||||
* @keymap_data: matrix scan code table for keycodes
|
||||
* @krow: mask for available rows, value is 0xFF
|
||||
* @kcol: mask for available columns, value is 0xFF
|
||||
|
@ -7,9 +7,6 @@
|
||||
* XT keyboard driver for Linux
|
||||
*/
|
||||
|
||||
/*
|
||||
*/
|
||||
|
||||
#include <linux/slab.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/input.h>
|
||||
|
@ -730,6 +730,24 @@ config INPUT_ADXL34X_SPI
|
||||
To compile this driver as a module, choose M here: the
|
||||
module will be called adxl34x-spi.
|
||||
|
||||
config INPUT_IBM_PANEL
|
||||
tristate "IBM Operation Panel driver"
|
||||
depends on I2C && I2C_SLAVE
|
||||
help
|
||||
Say Y here if you have an IBM Operation Panel connected to your system
|
||||
over I2C. The panel is typically connected only to a system's service
|
||||
processor (BMC).
|
||||
|
||||
If unsure, say N.
|
||||
|
||||
The Operation Panel is a controller with some buttons and an LCD
|
||||
display that allows someone with physical access to the system to
|
||||
perform various administrative tasks. This driver only supports the part
|
||||
of the controller that sends commands to the system.
|
||||
|
||||
To compile this driver as a module, choose M here: the module will be
|
||||
called ibm-panel.
|
||||
|
||||
config INPUT_IMS_PCU
|
||||
tristate "IMS Passenger Control Unit driver"
|
||||
depends on USB
|
||||
@ -891,6 +909,15 @@ config INPUT_SC27XX_VIBRA
|
||||
To compile this driver as a module, choose M here. The module will
|
||||
be called sc27xx_vibra.
|
||||
|
||||
config INPUT_RT5120_PWRKEY
|
||||
tristate "RT5120 PMIC power key support"
|
||||
depends on MFD_RT5120 || COMPILE_TEST
|
||||
help
|
||||
This enables support for RT5120 PMIC power key driver.
|
||||
|
||||
To compile this driver as a module, choose M here. the module will
|
||||
be called rt5120-pwrkey.
|
||||
|
||||
config INPUT_STPMIC1_ONKEY
|
||||
tristate "STPMIC1 PMIC Onkey support"
|
||||
depends on MFD_STPMIC1
|
||||
|
@ -41,6 +41,7 @@ obj-$(CONFIG_INPUT_GPIO_DECODER) += gpio_decoder.o
|
||||
obj-$(CONFIG_INPUT_GPIO_VIBRA) += gpio-vibra.o
|
||||
obj-$(CONFIG_INPUT_HISI_POWERKEY) += hisi_powerkey.o
|
||||
obj-$(CONFIG_HP_SDC_RTC) += hp_sdc_rtc.o
|
||||
obj-$(CONFIG_INPUT_IBM_PANEL) += ibm-panel.o
|
||||
obj-$(CONFIG_INPUT_IMS_PCU) += ims-pcu.o
|
||||
obj-$(CONFIG_INPUT_IQS269A) += iqs269a.o
|
||||
obj-$(CONFIG_INPUT_IQS626A) += iqs626a.o
|
||||
@ -69,6 +70,7 @@ obj-$(CONFIG_INPUT_RAVE_SP_PWRBUTTON) += rave-sp-pwrbutton.o
|
||||
obj-$(CONFIG_INPUT_RB532_BUTTON) += rb532_button.o
|
||||
obj-$(CONFIG_INPUT_REGULATOR_HAPTIC) += regulator-haptic.o
|
||||
obj-$(CONFIG_INPUT_RETU_PWRBUTTON) += retu-pwrbutton.o
|
||||
obj-$(CONFIG_INPUT_RT5120_PWRKEY) += rt5120-pwrkey.o
|
||||
obj-$(CONFIG_INPUT_AXP20X_PEK) += axp20x-pek.o
|
||||
obj-$(CONFIG_INPUT_GPIO_ROTARY_ENCODER) += rotary_encoder.o
|
||||
obj-$(CONFIG_INPUT_RK805_PWRKEY) += rk805-pwrkey.o
|
||||
|
200
drivers/input/misc/ibm-panel.c
Normal file
200
drivers/input/misc/ibm-panel.c
Normal file
@ -0,0 +1,200 @@
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
/*
|
||||
* Copyright (C) IBM Corporation 2020
|
||||
*/
|
||||
|
||||
#include <linux/i2c.h>
|
||||
#include <linux/init.h>
|
||||
#include <linux/input.h>
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/limits.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/of.h>
|
||||
#include <linux/spinlock.h>
|
||||
|
||||
#define DEVICE_NAME "ibm-panel"
|
||||
#define PANEL_KEYCODES_COUNT 3
|
||||
|
||||
struct ibm_panel {
|
||||
u8 idx;
|
||||
u8 command[11];
|
||||
u32 keycodes[PANEL_KEYCODES_COUNT];
|
||||
spinlock_t lock; /* protects writes to idx and command */
|
||||
struct input_dev *input;
|
||||
};
|
||||
|
||||
static u8 ibm_panel_calculate_checksum(struct ibm_panel *panel)
|
||||
{
|
||||
u8 chksum;
|
||||
u16 sum = 0;
|
||||
unsigned int i;
|
||||
|
||||
for (i = 0; i < sizeof(panel->command) - 1; ++i) {
|
||||
sum += panel->command[i];
|
||||
if (sum & 0xff00) {
|
||||
sum &= 0xff;
|
||||
sum++;
|
||||
}
|
||||
}
|
||||
|
||||
chksum = sum & 0xff;
|
||||
chksum = ~chksum;
|
||||
chksum++;
|
||||
|
||||
return chksum;
|
||||
}
|
||||
|
||||
static void ibm_panel_process_command(struct ibm_panel *panel)
|
||||
{
|
||||
u8 button;
|
||||
u8 chksum;
|
||||
|
||||
if (panel->command[0] != 0xff && panel->command[1] != 0xf0) {
|
||||
dev_dbg(&panel->input->dev, "command invalid: %02x %02x\n",
|
||||
panel->command[0], panel->command[1]);
|
||||
return;
|
||||
}
|
||||
|
||||
chksum = ibm_panel_calculate_checksum(panel);
|
||||
if (chksum != panel->command[sizeof(panel->command) - 1]) {
|
||||
dev_dbg(&panel->input->dev,
|
||||
"command failed checksum: %u != %u\n", chksum,
|
||||
panel->command[sizeof(panel->command) - 1]);
|
||||
return;
|
||||
}
|
||||
|
||||
button = panel->command[2] & 0xf;
|
||||
if (button < PANEL_KEYCODES_COUNT) {
|
||||
input_report_key(panel->input, panel->keycodes[button],
|
||||
!(panel->command[2] & 0x80));
|
||||
input_sync(panel->input);
|
||||
} else {
|
||||
dev_dbg(&panel->input->dev, "unknown button %u\n",
|
||||
button);
|
||||
}
|
||||
}
|
||||
|
||||
static int ibm_panel_i2c_slave_cb(struct i2c_client *client,
|
||||
enum i2c_slave_event event, u8 *val)
|
||||
{
|
||||
unsigned long flags;
|
||||
struct ibm_panel *panel = i2c_get_clientdata(client);
|
||||
|
||||
dev_dbg(&panel->input->dev, "event: %u data: %02x\n", event, *val);
|
||||
|
||||
spin_lock_irqsave(&panel->lock, flags);
|
||||
|
||||
switch (event) {
|
||||
case I2C_SLAVE_STOP:
|
||||
if (panel->idx == sizeof(panel->command))
|
||||
ibm_panel_process_command(panel);
|
||||
else
|
||||
dev_dbg(&panel->input->dev,
|
||||
"command incorrect size %u\n", panel->idx);
|
||||
fallthrough;
|
||||
case I2C_SLAVE_WRITE_REQUESTED:
|
||||
panel->idx = 0;
|
||||
break;
|
||||
case I2C_SLAVE_WRITE_RECEIVED:
|
||||
if (panel->idx < sizeof(panel->command))
|
||||
panel->command[panel->idx++] = *val;
|
||||
else
|
||||
/*
|
||||
* The command is too long and therefore invalid, so set the index
|
||||
* to it's largest possible value. When a STOP is finally received,
|
||||
* the command will be rejected upon processing.
|
||||
*/
|
||||
panel->idx = U8_MAX;
|
||||
break;
|
||||
case I2C_SLAVE_READ_REQUESTED:
|
||||
case I2C_SLAVE_READ_PROCESSED:
|
||||
*val = 0xff;
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
spin_unlock_irqrestore(&panel->lock, flags);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int ibm_panel_probe(struct i2c_client *client,
|
||||
const struct i2c_device_id *id)
|
||||
{
|
||||
struct ibm_panel *panel;
|
||||
int i;
|
||||
int error;
|
||||
|
||||
panel = devm_kzalloc(&client->dev, sizeof(*panel), GFP_KERNEL);
|
||||
if (!panel)
|
||||
return -ENOMEM;
|
||||
|
||||
spin_lock_init(&panel->lock);
|
||||
|
||||
panel->input = devm_input_allocate_device(&client->dev);
|
||||
if (!panel->input)
|
||||
return -ENOMEM;
|
||||
|
||||
panel->input->name = client->name;
|
||||
panel->input->id.bustype = BUS_I2C;
|
||||
|
||||
error = device_property_read_u32_array(&client->dev,
|
||||
"linux,keycodes",
|
||||
panel->keycodes,
|
||||
PANEL_KEYCODES_COUNT);
|
||||
if (error) {
|
||||
/*
|
||||
* Use gamepad buttons as defaults for compatibility with
|
||||
* existing applications.
|
||||
*/
|
||||
panel->keycodes[0] = BTN_NORTH;
|
||||
panel->keycodes[1] = BTN_SOUTH;
|
||||
panel->keycodes[2] = BTN_SELECT;
|
||||
}
|
||||
|
||||
for (i = 0; i < PANEL_KEYCODES_COUNT; ++i)
|
||||
input_set_capability(panel->input, EV_KEY, panel->keycodes[i]);
|
||||
|
||||
error = input_register_device(panel->input);
|
||||
if (error) {
|
||||
dev_err(&client->dev,
|
||||
"Failed to register input device: %d\n", error);
|
||||
return error;
|
||||
}
|
||||
|
||||
i2c_set_clientdata(client, panel);
|
||||
error = i2c_slave_register(client, ibm_panel_i2c_slave_cb);
|
||||
if (error) {
|
||||
dev_err(&client->dev,
|
||||
"Failed to register as i2c slave: %d\n", error);
|
||||
return error;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void ibm_panel_remove(struct i2c_client *client)
|
||||
{
|
||||
i2c_slave_unregister(client);
|
||||
}
|
||||
|
||||
static const struct of_device_id ibm_panel_match[] = {
|
||||
{ .compatible = "ibm,op-panel" },
|
||||
{ }
|
||||
};
|
||||
MODULE_DEVICE_TABLE(of, ibm_panel_match);
|
||||
|
||||
static struct i2c_driver ibm_panel_driver = {
|
||||
.driver = {
|
||||
.name = DEVICE_NAME,
|
||||
.of_match_table = ibm_panel_match,
|
||||
},
|
||||
.probe = ibm_panel_probe,
|
||||
.remove = ibm_panel_remove,
|
||||
};
|
||||
module_i2c_driver(ibm_panel_driver);
|
||||
|
||||
MODULE_AUTHOR("Eddie James <eajames@linux.ibm.com>");
|
||||
MODULE_DESCRIPTION("IBM Operation Panel Driver");
|
||||
MODULE_LICENSE("GPL");
|
@ -744,7 +744,7 @@ static int ims_pcu_switch_to_bootloader(struct ims_pcu *pcu)
|
||||
error = ims_pcu_execute_command(pcu, JUMP_TO_BTLDR, NULL, 0);
|
||||
if (error) {
|
||||
dev_err(pcu->dev,
|
||||
"Failure when sending JUMP TO BOOLTLOADER command, error: %d\n",
|
||||
"Failure when sending JUMP TO BOOTLOADER command, error: %d\n",
|
||||
error);
|
||||
return error;
|
||||
}
|
||||
|
@ -1077,7 +1077,7 @@ static int iqs7222_hard_reset(struct iqs7222_private *iqs7222)
|
||||
|
||||
static int iqs7222_force_comms(struct iqs7222_private *iqs7222)
|
||||
{
|
||||
u8 msg_buf[] = { 0xFF, 0x00, };
|
||||
u8 msg_buf[] = { 0xFF, };
|
||||
int ret;
|
||||
|
||||
/*
|
||||
@ -1771,11 +1771,9 @@ static int iqs7222_parse_chan(struct iqs7222_private *iqs7222, int chan_index)
|
||||
if (!chan_node)
|
||||
return 0;
|
||||
|
||||
if (dev_desc->allow_offset) {
|
||||
sys_setup[dev_desc->allow_offset] |= BIT(chan_index);
|
||||
if (fwnode_property_present(chan_node, "azoteq,ulp-allow"))
|
||||
if (dev_desc->allow_offset &&
|
||||
fwnode_property_present(chan_node, "azoteq,ulp-allow"))
|
||||
sys_setup[dev_desc->allow_offset] &= ~BIT(chan_index);
|
||||
}
|
||||
|
||||
chan_setup[0] |= IQS7222_CHAN_SETUP_0_CHAN_EN;
|
||||
|
||||
@ -2206,6 +2204,9 @@ static int iqs7222_parse_all(struct iqs7222_private *iqs7222)
|
||||
u16 *sys_setup = iqs7222->sys_setup;
|
||||
int error, i;
|
||||
|
||||
if (dev_desc->allow_offset)
|
||||
sys_setup[dev_desc->allow_offset] = U16_MAX;
|
||||
|
||||
if (dev_desc->event_offset)
|
||||
sys_setup[dev_desc->event_offset] = IQS7222_EVENT_MASK_ATI;
|
||||
|
||||
@ -2326,6 +2327,9 @@ static int iqs7222_report(struct iqs7222_private *iqs7222)
|
||||
int k = 2 + j * (num_chan > 16 ? 2 : 1);
|
||||
u16 state = le16_to_cpu(status[k + i / 16]);
|
||||
|
||||
if (!iqs7222->kp_type[i][j])
|
||||
continue;
|
||||
|
||||
input_event(iqs7222->keypad,
|
||||
iqs7222->kp_type[i][j],
|
||||
iqs7222->kp_code[i][j],
|
||||
|
@ -485,7 +485,7 @@ static int keyspan_probe(struct usb_interface *interface, const struct usb_devic
|
||||
}
|
||||
|
||||
if (udev->manufacturer)
|
||||
strlcpy(remote->name, udev->manufacturer, sizeof(remote->name));
|
||||
strscpy(remote->name, udev->manufacturer, sizeof(remote->name));
|
||||
|
||||
if (udev->product) {
|
||||
if (udev->manufacturer)
|
||||
|
120
drivers/input/misc/rt5120-pwrkey.c
Normal file
120
drivers/input/misc/rt5120-pwrkey.c
Normal file
@ -0,0 +1,120 @@
|
||||
// SPDX-License-Identifier: GPL-2.0-only
|
||||
/*
|
||||
* Copyright (C) 2022 Richtek Technology Corp.
|
||||
* Author: ChiYuan Huang <cy_huang@richtek.com>
|
||||
*/
|
||||
|
||||
#include <linux/bits.h>
|
||||
#include <linux/input.h>
|
||||
#include <linux/interrupt.h>
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/mod_devicetable.h>
|
||||
#include <linux/platform_device.h>
|
||||
#include <linux/regmap.h>
|
||||
|
||||
#define RT5120_REG_INTSTAT 0x1E
|
||||
#define RT5120_PWRKEYSTAT_MASK BIT(7)
|
||||
|
||||
struct rt5120_priv {
|
||||
struct regmap *regmap;
|
||||
struct input_dev *input;
|
||||
};
|
||||
|
||||
static irqreturn_t rt5120_pwrkey_handler(int irq, void *devid)
|
||||
{
|
||||
struct rt5120_priv *priv = devid;
|
||||
unsigned int stat;
|
||||
int error;
|
||||
|
||||
error = regmap_read(priv->regmap, RT5120_REG_INTSTAT, &stat);
|
||||
if (error)
|
||||
return IRQ_NONE;
|
||||
|
||||
input_report_key(priv->input, KEY_POWER,
|
||||
!(stat & RT5120_PWRKEYSTAT_MASK));
|
||||
input_sync(priv->input);
|
||||
|
||||
return IRQ_HANDLED;
|
||||
}
|
||||
|
||||
static int rt5120_pwrkey_probe(struct platform_device *pdev)
|
||||
{
|
||||
struct rt5120_priv *priv;
|
||||
struct device *dev = &pdev->dev;
|
||||
int press_irq, release_irq;
|
||||
int error;
|
||||
|
||||
priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL);
|
||||
if (!priv)
|
||||
return -ENOMEM;
|
||||
|
||||
priv->regmap = dev_get_regmap(dev->parent, NULL);
|
||||
if (!priv->regmap) {
|
||||
dev_err(dev, "Failed to init regmap\n");
|
||||
return -ENODEV;
|
||||
}
|
||||
|
||||
press_irq = platform_get_irq_byname(pdev, "pwrkey-press");
|
||||
if (press_irq < 0)
|
||||
return press_irq;
|
||||
|
||||
release_irq = platform_get_irq_byname(pdev, "pwrkey-release");
|
||||
if (release_irq < 0)
|
||||
return release_irq;
|
||||
|
||||
/* Make input device be device resource managed */
|
||||
priv->input = devm_input_allocate_device(dev);
|
||||
if (!priv->input)
|
||||
return -ENOMEM;
|
||||
|
||||
priv->input->name = "rt5120_pwrkey";
|
||||
priv->input->phys = "rt5120_pwrkey/input0";
|
||||
priv->input->id.bustype = BUS_I2C;
|
||||
input_set_capability(priv->input, EV_KEY, KEY_POWER);
|
||||
|
||||
error = input_register_device(priv->input);
|
||||
if (error) {
|
||||
dev_err(dev, "Failed to register input device: %d\n", error);
|
||||
return error;
|
||||
}
|
||||
|
||||
error = devm_request_threaded_irq(dev, press_irq,
|
||||
NULL, rt5120_pwrkey_handler,
|
||||
0, "pwrkey-press", priv);
|
||||
if (error) {
|
||||
dev_err(dev,
|
||||
"Failed to register pwrkey press irq: %d\n", error);
|
||||
return error;
|
||||
}
|
||||
|
||||
error = devm_request_threaded_irq(dev, release_irq,
|
||||
NULL, rt5120_pwrkey_handler,
|
||||
0, "pwrkey-release", priv);
|
||||
if (error) {
|
||||
dev_err(dev,
|
||||
"Failed to register pwrkey release irq: %d\n", error);
|
||||
return error;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct of_device_id r5120_pwrkey_match_table[] = {
|
||||
{ .compatible = "richtek,rt5120-pwrkey" },
|
||||
{}
|
||||
};
|
||||
MODULE_DEVICE_TABLE(of, r5120_pwrkey_match_table);
|
||||
|
||||
static struct platform_driver rt5120_pwrkey_driver = {
|
||||
.driver = {
|
||||
.name = "rt5120-pwrkey",
|
||||
.of_match_table = r5120_pwrkey_match_table,
|
||||
},
|
||||
.probe = rt5120_pwrkey_probe,
|
||||
};
|
||||
module_platform_driver(rt5120_pwrkey_driver);
|
||||
|
||||
MODULE_AUTHOR("ChiYuan Huang <cy_huang@richtek.com>");
|
||||
MODULE_DESCRIPTION("Richtek RT5120 power key driver");
|
||||
MODULE_LICENSE("GPL");
|
@ -26,6 +26,7 @@
|
||||
#include <linux/errno.h>
|
||||
#include <linux/input.h>
|
||||
#include <linux/interrupt.h>
|
||||
#include <linux/of.h>
|
||||
#include <linux/platform_device.h>
|
||||
#include <linux/mfd/twl.h>
|
||||
|
||||
|
@ -163,14 +163,10 @@ static int __maybe_unused twl4030_vibra_resume(struct device *dev)
|
||||
static SIMPLE_DEV_PM_OPS(twl4030_vibra_pm_ops,
|
||||
twl4030_vibra_suspend, twl4030_vibra_resume);
|
||||
|
||||
static bool twl4030_vibra_check_coexist(struct twl4030_vibra_data *pdata,
|
||||
struct device_node *parent)
|
||||
static bool twl4030_vibra_check_coexist(struct device_node *parent)
|
||||
{
|
||||
struct device_node *node;
|
||||
|
||||
if (pdata && pdata->coexist)
|
||||
return true;
|
||||
|
||||
node = of_get_child_by_name(parent, "codec");
|
||||
if (node) {
|
||||
of_node_put(node);
|
||||
@ -182,13 +178,12 @@ static bool twl4030_vibra_check_coexist(struct twl4030_vibra_data *pdata,
|
||||
|
||||
static int twl4030_vibra_probe(struct platform_device *pdev)
|
||||
{
|
||||
struct twl4030_vibra_data *pdata = dev_get_platdata(&pdev->dev);
|
||||
struct device_node *twl4030_core_node = pdev->dev.parent->of_node;
|
||||
struct vibra_info *info;
|
||||
int ret;
|
||||
|
||||
if (!pdata && !twl4030_core_node) {
|
||||
dev_dbg(&pdev->dev, "platform_data not available\n");
|
||||
if (!twl4030_core_node) {
|
||||
dev_dbg(&pdev->dev, "twl4030 OF node is missing\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
@ -197,7 +192,7 @@ static int twl4030_vibra_probe(struct platform_device *pdev)
|
||||
return -ENOMEM;
|
||||
|
||||
info->dev = &pdev->dev;
|
||||
info->coexist = twl4030_vibra_check_coexist(pdata, twl4030_core_node);
|
||||
info->coexist = twl4030_vibra_check_coexist(twl4030_core_node);
|
||||
INIT_WORK(&info->play_work, vibra_play_work);
|
||||
|
||||
info->input_dev = devm_input_allocate_device(&pdev->dev);
|
||||
|
@ -1311,12 +1311,6 @@ static int elan_probe(struct i2c_client *client,
|
||||
return error;
|
||||
}
|
||||
|
||||
error = devm_device_add_groups(dev, elan_sysfs_groups);
|
||||
if (error) {
|
||||
dev_err(dev, "failed to create sysfs attributes: %d\n", error);
|
||||
return error;
|
||||
}
|
||||
|
||||
error = input_register_device(data->input);
|
||||
if (error) {
|
||||
dev_err(dev, "failed to register input device: %d\n", error);
|
||||
@ -1442,6 +1436,7 @@ static struct i2c_driver elan_driver = {
|
||||
.acpi_match_table = ACPI_PTR(elan_acpi_id),
|
||||
.of_match_table = of_match_ptr(elan_of_match),
|
||||
.probe_type = PROBE_PREFER_ASYNCHRONOUS,
|
||||
.dev_groups = elan_sysfs_groups,
|
||||
},
|
||||
.probe = elan_probe,
|
||||
.id_table = elan_id,
|
||||
|
@ -884,7 +884,7 @@ static ssize_t hgpk_trigger_recal(struct psmouse *psmouse, void *data,
|
||||
|
||||
/*
|
||||
* We queue work instead of doing recalibration right here
|
||||
* to avoid adding locking to to hgpk_force_recalibrate()
|
||||
* to avoid adding locking to hgpk_force_recalibrate()
|
||||
* since workqueue provides serialization.
|
||||
*/
|
||||
psmouse_queue_work(psmouse, &priv->recalib_wq, 0);
|
||||
@ -1057,7 +1057,7 @@ void hgpk_module_init(void)
|
||||
strlen(hgpk_mode_name));
|
||||
if (hgpk_default_mode == HGPK_MODE_INVALID) {
|
||||
hgpk_default_mode = HGPK_MODE_MOUSE;
|
||||
strlcpy(hgpk_mode_name, hgpk_mode_names[HGPK_MODE_MOUSE],
|
||||
strscpy(hgpk_mode_name, hgpk_mode_names[HGPK_MODE_MOUSE],
|
||||
sizeof(hgpk_mode_name));
|
||||
}
|
||||
}
|
||||
|
@ -13,9 +13,6 @@
|
||||
* Inport (ATI XL and Microsoft) busmouse driver for Linux
|
||||
*/
|
||||
|
||||
/*
|
||||
*/
|
||||
|
||||
#include <linux/module.h>
|
||||
#include <linux/ioport.h>
|
||||
#include <linux/init.h>
|
||||
|
@ -14,9 +14,6 @@
|
||||
* Logitech Bus Mouse Driver for Linux
|
||||
*/
|
||||
|
||||
/*
|
||||
*/
|
||||
|
||||
#include <linux/module.h>
|
||||
#include <linux/delay.h>
|
||||
#include <linux/ioport.h>
|
||||
|
@ -10,9 +10,6 @@
|
||||
* IBM PC110 touchpad driver for Linux
|
||||
*/
|
||||
|
||||
/*
|
||||
*/
|
||||
|
||||
#include <linux/module.h>
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/errno.h>
|
||||
|
@ -94,7 +94,7 @@ PSMOUSE_DEFINE_ATTR(resync_time, S_IWUSR | S_IRUGO,
|
||||
(void *) offsetof(struct psmouse, resync_time),
|
||||
psmouse_show_int_attr, psmouse_set_int_attr);
|
||||
|
||||
static struct attribute *psmouse_attributes[] = {
|
||||
static struct attribute *psmouse_dev_attrs[] = {
|
||||
&psmouse_attr_protocol.dattr.attr,
|
||||
&psmouse_attr_rate.dattr.attr,
|
||||
&psmouse_attr_resolution.dattr.attr,
|
||||
@ -103,9 +103,7 @@ static struct attribute *psmouse_attributes[] = {
|
||||
NULL
|
||||
};
|
||||
|
||||
static const struct attribute_group psmouse_attribute_group = {
|
||||
.attrs = psmouse_attributes,
|
||||
};
|
||||
ATTRIBUTE_GROUPS(psmouse_dev);
|
||||
|
||||
/*
|
||||
* psmouse_mutex protects all operations changing state of mouse
|
||||
@ -1481,8 +1479,6 @@ static void psmouse_disconnect(struct serio *serio)
|
||||
struct psmouse *psmouse = serio_get_drvdata(serio);
|
||||
struct psmouse *parent = NULL;
|
||||
|
||||
sysfs_remove_group(&serio->dev.kobj, &psmouse_attribute_group);
|
||||
|
||||
mutex_lock(&psmouse_mutex);
|
||||
|
||||
psmouse_set_state(psmouse, PSMOUSE_CMD_MODE);
|
||||
@ -1647,10 +1643,6 @@ static int psmouse_connect(struct serio *serio, struct serio_driver *drv)
|
||||
if (parent && parent->pt_activate)
|
||||
parent->pt_activate(parent);
|
||||
|
||||
error = sysfs_create_group(&serio->dev.kobj, &psmouse_attribute_group);
|
||||
if (error)
|
||||
goto err_pt_deactivate;
|
||||
|
||||
/*
|
||||
* PS/2 devices having SMBus companions should stay disabled
|
||||
* on PS/2 side, in order to have SMBus part operable.
|
||||
@ -1666,13 +1658,6 @@ static int psmouse_connect(struct serio *serio, struct serio_driver *drv)
|
||||
mutex_unlock(&psmouse_mutex);
|
||||
return retval;
|
||||
|
||||
err_pt_deactivate:
|
||||
if (parent && parent->pt_deactivate)
|
||||
parent->pt_deactivate(parent);
|
||||
if (input_dev) {
|
||||
input_unregister_device(input_dev);
|
||||
input_dev = NULL; /* so we don't try to free it below */
|
||||
}
|
||||
err_protocol_disconnect:
|
||||
if (psmouse->disconnect)
|
||||
psmouse->disconnect(psmouse);
|
||||
@ -1792,6 +1777,7 @@ MODULE_DEVICE_TABLE(serio, psmouse_serio_ids);
|
||||
static struct serio_driver psmouse_drv = {
|
||||
.driver = {
|
||||
.name = "psmouse",
|
||||
.dev_groups = psmouse_dev_groups,
|
||||
},
|
||||
.description = DRIVER_DESC,
|
||||
.id_table = psmouse_serio_ids,
|
||||
|
@ -7,9 +7,6 @@
|
||||
* Serial mouse driver for Linux
|
||||
*/
|
||||
|
||||
/*
|
||||
*/
|
||||
|
||||
#include <linux/delay.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/slab.h>
|
||||
|
@ -182,6 +182,7 @@ static const char * const smbus_pnp_ids[] = {
|
||||
"LEN0099", /* X1 Extreme Gen 1 / P1 Gen 1 */
|
||||
"LEN009b", /* T580 */
|
||||
"LEN0402", /* X1 Extreme Gen 2 / P1 Gen 2 */
|
||||
"LEN040f", /* P1 Gen 3 */
|
||||
"LEN200f", /* T450s */
|
||||
"LEN2044", /* L470 */
|
||||
"LEN2054", /* E480 */
|
||||
@ -714,8 +715,8 @@ static void synaptics_pt_create(struct psmouse *psmouse)
|
||||
}
|
||||
|
||||
serio->id.type = SERIO_PS_PSTHRU;
|
||||
strlcpy(serio->name, "Synaptics pass-through", sizeof(serio->name));
|
||||
strlcpy(serio->phys, "synaptics-pt/serio0", sizeof(serio->phys));
|
||||
strscpy(serio->name, "Synaptics pass-through", sizeof(serio->name));
|
||||
strscpy(serio->phys, "synaptics-pt/serio0", sizeof(serio->phys));
|
||||
serio->write = synaptics_pt_write;
|
||||
serio->start = synaptics_pt_start;
|
||||
serio->stop = synaptics_pt_stop;
|
||||
|
@ -354,7 +354,7 @@ static int synusb_probe(struct usb_interface *intf,
|
||||
synusb->urb->transfer_flags |= URB_NO_TRANSFER_DMA_MAP;
|
||||
|
||||
if (udev->manufacturer)
|
||||
strlcpy(synusb->name, udev->manufacturer,
|
||||
strscpy(synusb->name, udev->manufacturer,
|
||||
sizeof(synusb->name));
|
||||
|
||||
if (udev->product) {
|
||||
|
@ -12,9 +12,6 @@
|
||||
* Later on, I had access to the device's documentation (referenced below).
|
||||
*/
|
||||
|
||||
/*
|
||||
*/
|
||||
|
||||
/*
|
||||
* Building an adaptor to DE9 / DB25 RS232
|
||||
* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
@ -138,12 +135,12 @@ static void vsxxxaa_detection_done(struct vsxxxaa *mouse)
|
||||
{
|
||||
switch (mouse->type) {
|
||||
case 0x02:
|
||||
strlcpy(mouse->name, "DEC VSXXX-AA/-GA mouse",
|
||||
strscpy(mouse->name, "DEC VSXXX-AA/-GA mouse",
|
||||
sizeof(mouse->name));
|
||||
break;
|
||||
|
||||
case 0x04:
|
||||
strlcpy(mouse->name, "DEC VSXXX-AB digitizer",
|
||||
strscpy(mouse->name, "DEC VSXXX-AB digitizer",
|
||||
sizeof(mouse->name));
|
||||
break;
|
||||
|
||||
|
@ -181,7 +181,7 @@ static int rmi_f03_register_pt(struct f03_data *f03)
|
||||
serio->close = rmi_f03_pt_close;
|
||||
serio->port_data = f03;
|
||||
|
||||
strlcpy(serio->name, "RMI4 PS/2 pass-through", sizeof(serio->name));
|
||||
strscpy(serio->name, "RMI4 PS/2 pass-through", sizeof(serio->name));
|
||||
snprintf(serio->phys, sizeof(serio->phys), "%s/serio0",
|
||||
dev_name(&f03->fn->dev));
|
||||
serio->dev.parent = &f03->fn->dev;
|
||||
|
@ -115,12 +115,12 @@ static irqreturn_t rmi_f34_attention(int irq, void *ctx)
|
||||
} else {
|
||||
ret = rmi_read_block(f34->fn->rmi_dev,
|
||||
f34->fn->fd.data_base_addr +
|
||||
f34->v7.off.flash_status,
|
||||
V7_COMMAND_OFFSET,
|
||||
&status, sizeof(status));
|
||||
rmi_dbg(RMI_DEBUG_FN, &fn->dev, "%s: status: %#02x, ret: %d\n",
|
||||
rmi_dbg(RMI_DEBUG_FN, &f34->fn->dev, "%s: cmd: %#02x, ret: %d\n",
|
||||
__func__, status, ret);
|
||||
|
||||
if (!ret && !(status & 0x1f))
|
||||
if (!ret && status == CMD_V7_IDLE)
|
||||
complete(&f34->v7.cmd_done);
|
||||
}
|
||||
|
||||
@ -321,11 +321,11 @@ static ssize_t rmi_driver_bootloader_id_show(struct device *dev,
|
||||
f34 = dev_get_drvdata(&fn->dev);
|
||||
|
||||
if (f34->bl_version == 5)
|
||||
return scnprintf(buf, PAGE_SIZE, "%c%c\n",
|
||||
return sysfs_emit(buf, "%c%c\n",
|
||||
f34->bootloader_id[0],
|
||||
f34->bootloader_id[1]);
|
||||
else
|
||||
return scnprintf(buf, PAGE_SIZE, "V%d.%d\n",
|
||||
return sysfs_emit(buf, "V%d.%d\n",
|
||||
f34->bootloader_id[1],
|
||||
f34->bootloader_id[0]);
|
||||
}
|
||||
@ -346,7 +346,7 @@ static ssize_t rmi_driver_configuration_id_show(struct device *dev,
|
||||
if (fn) {
|
||||
f34 = dev_get_drvdata(&fn->dev);
|
||||
|
||||
return scnprintf(buf, PAGE_SIZE, "%s\n", f34->configuration_id);
|
||||
return sysfs_emit(buf, "%s\n", f34->configuration_id);
|
||||
}
|
||||
|
||||
return 0;
|
||||
@ -370,7 +370,7 @@ static int rmi_firmware_update(struct rmi_driver_data *data,
|
||||
|
||||
f34 = dev_get_drvdata(&data->f34_container->dev);
|
||||
|
||||
if (f34->bl_version == 7) {
|
||||
if (f34->bl_version >= 7) {
|
||||
if (data->pdt_props & HAS_BSR) {
|
||||
dev_err(dev, "%s: LTS not supported\n", __func__);
|
||||
return -ENODEV;
|
||||
@ -382,7 +382,7 @@ static int rmi_firmware_update(struct rmi_driver_data *data,
|
||||
}
|
||||
|
||||
/* Enter flash mode */
|
||||
if (f34->bl_version == 7)
|
||||
if (f34->bl_version >= 7)
|
||||
ret = rmi_f34v7_start_reflash(f34, fw);
|
||||
else
|
||||
ret = rmi_f34_enable_flash(f34);
|
||||
@ -413,7 +413,7 @@ static int rmi_firmware_update(struct rmi_driver_data *data,
|
||||
f34 = dev_get_drvdata(&data->f34_container->dev);
|
||||
|
||||
/* Perform firmware update */
|
||||
if (f34->bl_version == 7)
|
||||
if (f34->bl_version >= 7)
|
||||
ret = rmi_f34v7_do_reflash(f34, fw);
|
||||
else
|
||||
ret = rmi_f34_update_firmware(f34, fw);
|
||||
@ -499,7 +499,7 @@ static ssize_t rmi_driver_update_fw_status_show(struct device *dev,
|
||||
if (data->f34_container)
|
||||
update_status = rmi_f34_status(data->f34_container);
|
||||
|
||||
return scnprintf(buf, PAGE_SIZE, "%d\n", update_status);
|
||||
return sysfs_emit(buf, "%d\n", update_status);
|
||||
}
|
||||
|
||||
static DEVICE_ATTR(update_fw_status, 0444,
|
||||
|
@ -222,20 +222,6 @@ struct image_metadata {
|
||||
struct physical_address phyaddr;
|
||||
};
|
||||
|
||||
struct register_offset {
|
||||
u8 properties;
|
||||
u8 properties_2;
|
||||
u8 block_size;
|
||||
u8 block_count;
|
||||
u8 gc_block_count;
|
||||
u8 flash_status;
|
||||
u8 partition_id;
|
||||
u8 block_number;
|
||||
u8 transfer_length;
|
||||
u8 flash_cmd;
|
||||
u8 payload;
|
||||
};
|
||||
|
||||
struct rmi_f34_firmware {
|
||||
__le32 checksum;
|
||||
u8 pad1[3];
|
||||
@ -262,7 +248,6 @@ struct f34v5_data {
|
||||
struct f34v7_data {
|
||||
bool has_display_cfg;
|
||||
bool has_guest_code;
|
||||
bool force_update;
|
||||
bool in_bl_mode;
|
||||
u8 *read_config_buf;
|
||||
size_t read_config_buf_size;
|
||||
@ -276,9 +261,7 @@ struct f34v7_data {
|
||||
u16 payload_length;
|
||||
u8 partitions;
|
||||
u16 partition_table_bytes;
|
||||
bool new_partition_table;
|
||||
|
||||
struct register_offset off;
|
||||
struct block_count blkcount;
|
||||
struct physical_address phyaddr;
|
||||
struct image_metadata img;
|
||||
|
@ -25,7 +25,7 @@ static int rmi_f34v7_read_flash_status(struct f34_data *f34)
|
||||
int ret;
|
||||
|
||||
ret = rmi_read_block(f34->fn->rmi_dev,
|
||||
f34->fn->fd.data_base_addr + f34->v7.off.flash_status,
|
||||
f34->fn->fd.data_base_addr + V7_FLASH_STATUS_OFFSET,
|
||||
&status,
|
||||
sizeof(status));
|
||||
if (ret < 0) {
|
||||
@ -43,7 +43,7 @@ static int rmi_f34v7_read_flash_status(struct f34_data *f34)
|
||||
}
|
||||
|
||||
ret = rmi_read_block(f34->fn->rmi_dev,
|
||||
f34->fn->fd.data_base_addr + f34->v7.off.flash_cmd,
|
||||
f34->fn->fd.data_base_addr + V7_COMMAND_OFFSET,
|
||||
&command,
|
||||
sizeof(command));
|
||||
if (ret < 0) {
|
||||
@ -72,6 +72,24 @@ static int rmi_f34v7_wait_for_idle(struct f34_data *f34, int timeout_ms)
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int rmi_f34v7_check_command_status(struct f34_data *f34, int timeout_ms)
|
||||
{
|
||||
int ret;
|
||||
|
||||
ret = rmi_f34v7_wait_for_idle(f34, timeout_ms);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
ret = rmi_f34v7_read_flash_status(f34);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
if (f34->v7.flash_status != 0x00)
|
||||
return -EIO;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int rmi_f34v7_write_command_single_transaction(struct f34_data *f34,
|
||||
u8 cmd)
|
||||
{
|
||||
@ -122,7 +140,7 @@ static int rmi_f34v7_write_command_single_transaction(struct f34_data *f34,
|
||||
data_1_5.payload[1] = f34->bootloader_id[1];
|
||||
|
||||
ret = rmi_write_block(f34->fn->rmi_dev,
|
||||
base + f34->v7.off.partition_id,
|
||||
base + V7_PARTITION_ID_OFFSET,
|
||||
&data_1_5, sizeof(data_1_5));
|
||||
if (ret < 0) {
|
||||
dev_err(&f34->fn->dev,
|
||||
@ -195,7 +213,7 @@ static int rmi_f34v7_write_command(struct f34_data *f34, u8 cmd)
|
||||
__func__, command);
|
||||
|
||||
ret = rmi_write_block(f34->fn->rmi_dev,
|
||||
base + f34->v7.off.flash_cmd,
|
||||
base + V7_COMMAND_OFFSET,
|
||||
&command, sizeof(command));
|
||||
if (ret < 0) {
|
||||
dev_err(&f34->fn->dev, "%s: Failed to write flash command\n",
|
||||
@ -262,7 +280,7 @@ static int rmi_f34v7_write_partition_id(struct f34_data *f34, u8 cmd)
|
||||
}
|
||||
|
||||
ret = rmi_write_block(f34->fn->rmi_dev,
|
||||
base + f34->v7.off.partition_id,
|
||||
base + V7_PARTITION_ID_OFFSET,
|
||||
&partition, sizeof(partition));
|
||||
if (ret < 0) {
|
||||
dev_err(&f34->fn->dev, "%s: Failed to write partition ID\n",
|
||||
@ -290,7 +308,7 @@ static int rmi_f34v7_read_partition_table(struct f34_data *f34)
|
||||
return ret;
|
||||
|
||||
ret = rmi_write_block(f34->fn->rmi_dev,
|
||||
base + f34->v7.off.block_number,
|
||||
base + V7_BLOCK_NUMBER_OFFSET,
|
||||
&block_number, sizeof(block_number));
|
||||
if (ret < 0) {
|
||||
dev_err(&f34->fn->dev, "%s: Failed to write block number\n",
|
||||
@ -301,7 +319,7 @@ static int rmi_f34v7_read_partition_table(struct f34_data *f34)
|
||||
put_unaligned_le16(f34->v7.flash_config_length, &length);
|
||||
|
||||
ret = rmi_write_block(f34->fn->rmi_dev,
|
||||
base + f34->v7.off.transfer_length,
|
||||
base + V7_TRANSFER_LENGTH_OFFSET,
|
||||
&length, sizeof(length));
|
||||
if (ret < 0) {
|
||||
dev_err(&f34->fn->dev, "%s: Failed to write transfer length\n",
|
||||
@ -318,6 +336,10 @@ static int rmi_f34v7_read_partition_table(struct f34_data *f34)
|
||||
return ret;
|
||||
}
|
||||
|
||||
/*
|
||||
* rmi_f34v7_check_command_status() can't be used here, as this
|
||||
* function is called before IRQs are available
|
||||
*/
|
||||
timeout = msecs_to_jiffies(F34_WRITE_WAIT_MS);
|
||||
while (time_before(jiffies, timeout)) {
|
||||
usleep_range(5000, 6000);
|
||||
@ -330,7 +352,7 @@ static int rmi_f34v7_read_partition_table(struct f34_data *f34)
|
||||
}
|
||||
|
||||
ret = rmi_read_block(f34->fn->rmi_dev,
|
||||
base + f34->v7.off.payload,
|
||||
base + V7_PAYLOAD_OFFSET,
|
||||
f34->v7.read_config_buf,
|
||||
f34->v7.partition_table_bytes);
|
||||
if (ret < 0) {
|
||||
@ -504,13 +526,6 @@ static int rmi_f34v7_read_queries(struct f34_data *f34)
|
||||
rmi_dbg(RMI_DEBUG_FN, &f34->fn->dev, "%s: f34->v7.block_size = %d\n",
|
||||
__func__, f34->v7.block_size);
|
||||
|
||||
f34->v7.off.flash_status = V7_FLASH_STATUS_OFFSET;
|
||||
f34->v7.off.partition_id = V7_PARTITION_ID_OFFSET;
|
||||
f34->v7.off.block_number = V7_BLOCK_NUMBER_OFFSET;
|
||||
f34->v7.off.transfer_length = V7_TRANSFER_LENGTH_OFFSET;
|
||||
f34->v7.off.flash_cmd = V7_COMMAND_OFFSET;
|
||||
f34->v7.off.payload = V7_PAYLOAD_OFFSET;
|
||||
|
||||
f34->v7.has_display_cfg = query_1_7.partition_support[1] & HAS_DISP_CFG;
|
||||
f34->v7.has_guest_code =
|
||||
query_1_7.partition_support[1] & HAS_GUEST_CODE;
|
||||
@ -571,68 +586,6 @@ static int rmi_f34v7_read_queries(struct f34_data *f34)
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int rmi_f34v7_check_ui_firmware_size(struct f34_data *f34)
|
||||
{
|
||||
u16 block_count;
|
||||
|
||||
block_count = f34->v7.img.ui_firmware.size / f34->v7.block_size;
|
||||
f34->update_size += block_count;
|
||||
|
||||
if (block_count != f34->v7.blkcount.ui_firmware) {
|
||||
dev_err(&f34->fn->dev,
|
||||
"UI firmware size mismatch: %d != %d\n",
|
||||
block_count, f34->v7.blkcount.ui_firmware);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int rmi_f34v7_check_ui_config_size(struct f34_data *f34)
|
||||
{
|
||||
u16 block_count;
|
||||
|
||||
block_count = f34->v7.img.ui_config.size / f34->v7.block_size;
|
||||
f34->update_size += block_count;
|
||||
|
||||
if (block_count != f34->v7.blkcount.ui_config) {
|
||||
dev_err(&f34->fn->dev, "UI config size mismatch\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int rmi_f34v7_check_dp_config_size(struct f34_data *f34)
|
||||
{
|
||||
u16 block_count;
|
||||
|
||||
block_count = f34->v7.img.dp_config.size / f34->v7.block_size;
|
||||
f34->update_size += block_count;
|
||||
|
||||
if (block_count != f34->v7.blkcount.dp_config) {
|
||||
dev_err(&f34->fn->dev, "Display config size mismatch\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int rmi_f34v7_check_guest_code_size(struct f34_data *f34)
|
||||
{
|
||||
u16 block_count;
|
||||
|
||||
block_count = f34->v7.img.guest_code.size / f34->v7.block_size;
|
||||
f34->update_size += block_count;
|
||||
|
||||
if (block_count != f34->v7.blkcount.guest_code) {
|
||||
dev_err(&f34->fn->dev, "Guest code size mismatch\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int rmi_f34v7_check_bl_config_size(struct f34_data *f34)
|
||||
{
|
||||
u16 block_count;
|
||||
@ -648,58 +601,6 @@ static int rmi_f34v7_check_bl_config_size(struct f34_data *f34)
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int rmi_f34v7_erase_config(struct f34_data *f34)
|
||||
{
|
||||
int ret;
|
||||
|
||||
dev_info(&f34->fn->dev, "Erasing config...\n");
|
||||
|
||||
init_completion(&f34->v7.cmd_done);
|
||||
|
||||
switch (f34->v7.config_area) {
|
||||
case v7_UI_CONFIG_AREA:
|
||||
ret = rmi_f34v7_write_command(f34, v7_CMD_ERASE_UI_CONFIG);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
break;
|
||||
case v7_DP_CONFIG_AREA:
|
||||
ret = rmi_f34v7_write_command(f34, v7_CMD_ERASE_DISP_CONFIG);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
break;
|
||||
case v7_BL_CONFIG_AREA:
|
||||
ret = rmi_f34v7_write_command(f34, v7_CMD_ERASE_BL_CONFIG);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
break;
|
||||
}
|
||||
|
||||
ret = rmi_f34v7_wait_for_idle(f34, F34_ERASE_WAIT_MS);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int rmi_f34v7_erase_guest_code(struct f34_data *f34)
|
||||
{
|
||||
int ret;
|
||||
|
||||
dev_info(&f34->fn->dev, "Erasing guest code...\n");
|
||||
|
||||
init_completion(&f34->v7.cmd_done);
|
||||
|
||||
ret = rmi_f34v7_write_command(f34, v7_CMD_ERASE_GUEST_CODE);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
ret = rmi_f34v7_wait_for_idle(f34, F34_ERASE_WAIT_MS);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int rmi_f34v7_erase_all(struct f34_data *f34)
|
||||
{
|
||||
int ret;
|
||||
@ -708,32 +609,14 @@ static int rmi_f34v7_erase_all(struct f34_data *f34)
|
||||
|
||||
init_completion(&f34->v7.cmd_done);
|
||||
|
||||
ret = rmi_f34v7_write_command(f34, v7_CMD_ERASE_UI_FIRMWARE);
|
||||
ret = rmi_f34v7_write_command(f34, v7_CMD_ERASE_ALL);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
ret = rmi_f34v7_wait_for_idle(f34, F34_ERASE_WAIT_MS);
|
||||
ret = rmi_f34v7_check_command_status(f34, F34_ERASE_WAIT_MS);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
f34->v7.config_area = v7_UI_CONFIG_AREA;
|
||||
ret = rmi_f34v7_erase_config(f34);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
if (f34->v7.has_display_cfg) {
|
||||
f34->v7.config_area = v7_DP_CONFIG_AREA;
|
||||
ret = rmi_f34v7_erase_config(f34);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
}
|
||||
|
||||
if (f34->v7.new_partition_table && f34->v7.has_guest_code) {
|
||||
ret = rmi_f34v7_erase_guest_code(f34);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
@ -756,7 +639,7 @@ static int rmi_f34v7_read_blocks(struct f34_data *f34,
|
||||
return ret;
|
||||
|
||||
ret = rmi_write_block(f34->fn->rmi_dev,
|
||||
base + f34->v7.off.block_number,
|
||||
base + V7_BLOCK_NUMBER_OFFSET,
|
||||
&block_number, sizeof(block_number));
|
||||
if (ret < 0) {
|
||||
dev_err(&f34->fn->dev, "%s: Failed to write block number\n",
|
||||
@ -772,7 +655,7 @@ static int rmi_f34v7_read_blocks(struct f34_data *f34,
|
||||
put_unaligned_le16(transfer, &length);
|
||||
|
||||
ret = rmi_write_block(f34->fn->rmi_dev,
|
||||
base + f34->v7.off.transfer_length,
|
||||
base + V7_TRANSFER_LENGTH_OFFSET,
|
||||
&length, sizeof(length));
|
||||
if (ret < 0) {
|
||||
dev_err(&f34->fn->dev,
|
||||
@ -787,12 +670,12 @@ static int rmi_f34v7_read_blocks(struct f34_data *f34,
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
ret = rmi_f34v7_wait_for_idle(f34, F34_ENABLE_WAIT_MS);
|
||||
ret = rmi_f34v7_check_command_status(f34, F34_ENABLE_WAIT_MS);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
ret = rmi_read_block(f34->fn->rmi_dev,
|
||||
base + f34->v7.off.payload,
|
||||
base + V7_PAYLOAD_OFFSET,
|
||||
&f34->v7.read_config_buf[index],
|
||||
transfer * f34->v7.block_size);
|
||||
if (ret < 0) {
|
||||
@ -828,7 +711,7 @@ static int rmi_f34v7_write_f34v7_blocks(struct f34_data *f34,
|
||||
return ret;
|
||||
|
||||
ret = rmi_write_block(f34->fn->rmi_dev,
|
||||
base + f34->v7.off.block_number,
|
||||
base + V7_BLOCK_NUMBER_OFFSET,
|
||||
&block_number, sizeof(block_number));
|
||||
if (ret < 0) {
|
||||
dev_err(&f34->fn->dev, "%s: Failed to write block number\n",
|
||||
@ -848,7 +731,7 @@ static int rmi_f34v7_write_f34v7_blocks(struct f34_data *f34,
|
||||
init_completion(&f34->v7.cmd_done);
|
||||
|
||||
ret = rmi_write_block(f34->fn->rmi_dev,
|
||||
base + f34->v7.off.transfer_length,
|
||||
base + V7_TRANSFER_LENGTH_OFFSET,
|
||||
&length, sizeof(length));
|
||||
if (ret < 0) {
|
||||
dev_err(&f34->fn->dev,
|
||||
@ -862,7 +745,7 @@ static int rmi_f34v7_write_f34v7_blocks(struct f34_data *f34,
|
||||
return ret;
|
||||
|
||||
ret = rmi_write_block(f34->fn->rmi_dev,
|
||||
base + f34->v7.off.payload,
|
||||
base + V7_PAYLOAD_OFFSET,
|
||||
block_ptr, transfer * f34->v7.block_size);
|
||||
if (ret < 0) {
|
||||
dev_err(&f34->fn->dev,
|
||||
@ -871,7 +754,7 @@ static int rmi_f34v7_write_f34v7_blocks(struct f34_data *f34,
|
||||
return ret;
|
||||
}
|
||||
|
||||
ret = rmi_f34v7_wait_for_idle(f34, F34_ENABLE_WAIT_MS);
|
||||
ret = rmi_f34v7_check_command_status(f34, F34_ENABLE_WAIT_MS);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
@ -937,17 +820,6 @@ static int rmi_f34v7_write_flash_config(struct f34_data *f34)
|
||||
|
||||
init_completion(&f34->v7.cmd_done);
|
||||
|
||||
ret = rmi_f34v7_write_command(f34, v7_CMD_ERASE_FLASH_CONFIG);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
rmi_dbg(RMI_DEBUG_FN, &f34->fn->dev,
|
||||
"%s: Erase flash config command written\n", __func__);
|
||||
|
||||
ret = rmi_f34v7_wait_for_idle(f34, F34_WRITE_WAIT_MS);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
ret = rmi_f34v7_write_config(f34);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
@ -977,10 +849,6 @@ static int rmi_f34v7_write_partition_table(struct f34_data *f34)
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
ret = rmi_f34v7_erase_config(f34);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
ret = rmi_f34v7_write_flash_config(f34);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
@ -1007,33 +875,6 @@ static int rmi_f34v7_write_firmware(struct f34_data *f34)
|
||||
blk_count, v7_CMD_WRITE_FW);
|
||||
}
|
||||
|
||||
static void rmi_f34v7_compare_partition_tables(struct f34_data *f34)
|
||||
{
|
||||
if (f34->v7.phyaddr.ui_firmware != f34->v7.img.phyaddr.ui_firmware) {
|
||||
f34->v7.new_partition_table = true;
|
||||
return;
|
||||
}
|
||||
|
||||
if (f34->v7.phyaddr.ui_config != f34->v7.img.phyaddr.ui_config) {
|
||||
f34->v7.new_partition_table = true;
|
||||
return;
|
||||
}
|
||||
|
||||
if (f34->v7.has_display_cfg &&
|
||||
f34->v7.phyaddr.dp_config != f34->v7.img.phyaddr.dp_config) {
|
||||
f34->v7.new_partition_table = true;
|
||||
return;
|
||||
}
|
||||
|
||||
if (f34->v7.has_guest_code &&
|
||||
f34->v7.phyaddr.guest_code != f34->v7.img.phyaddr.guest_code) {
|
||||
f34->v7.new_partition_table = true;
|
||||
return;
|
||||
}
|
||||
|
||||
f34->v7.new_partition_table = false;
|
||||
}
|
||||
|
||||
static void rmi_f34v7_parse_img_header_10_bl_container(struct f34_data *f34,
|
||||
const void *image)
|
||||
{
|
||||
@ -1180,8 +1021,6 @@ static int rmi_f34v7_parse_image_info(struct f34_data *f34)
|
||||
rmi_f34v7_parse_partition_table(f34, f34->v7.img.fl_config.data,
|
||||
&f34->v7.img.blkcount, &f34->v7.img.phyaddr);
|
||||
|
||||
rmi_f34v7_compare_partition_tables(f34);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
@ -1200,53 +1039,35 @@ int rmi_f34v7_do_reflash(struct f34_data *f34, const struct firmware *fw)
|
||||
|
||||
ret = rmi_f34v7_parse_image_info(f34);
|
||||
if (ret < 0)
|
||||
goto fail;
|
||||
return ret;
|
||||
|
||||
if (!f34->v7.new_partition_table) {
|
||||
ret = rmi_f34v7_check_ui_firmware_size(f34);
|
||||
if (ret < 0)
|
||||
goto fail;
|
||||
|
||||
ret = rmi_f34v7_check_ui_config_size(f34);
|
||||
if (ret < 0)
|
||||
goto fail;
|
||||
|
||||
if (f34->v7.has_display_cfg &&
|
||||
f34->v7.img.contains_display_cfg) {
|
||||
ret = rmi_f34v7_check_dp_config_size(f34);
|
||||
if (ret < 0)
|
||||
goto fail;
|
||||
}
|
||||
|
||||
if (f34->v7.has_guest_code && f34->v7.img.contains_guest_code) {
|
||||
ret = rmi_f34v7_check_guest_code_size(f34);
|
||||
if (ret < 0)
|
||||
goto fail;
|
||||
}
|
||||
} else {
|
||||
ret = rmi_f34v7_check_bl_config_size(f34);
|
||||
if (ret < 0)
|
||||
goto fail;
|
||||
}
|
||||
return ret;
|
||||
|
||||
ret = rmi_f34v7_erase_all(f34);
|
||||
if (ret < 0)
|
||||
goto fail;
|
||||
return ret;
|
||||
|
||||
if (f34->v7.new_partition_table) {
|
||||
ret = rmi_f34v7_write_partition_table(f34);
|
||||
if (ret < 0)
|
||||
goto fail;
|
||||
dev_info(&f34->fn->dev, "%s: Partition table programmed\n",
|
||||
__func__);
|
||||
}
|
||||
return ret;
|
||||
dev_info(&f34->fn->dev, "%s: Partition table programmed\n", __func__);
|
||||
|
||||
/*
|
||||
* Reset to reload partition table - as the previous firmware has been
|
||||
* erased, we remain in bootloader mode.
|
||||
*/
|
||||
ret = rmi_scan_pdt(f34->fn->rmi_dev, NULL, rmi_initial_reset);
|
||||
if (ret < 0)
|
||||
dev_warn(&f34->fn->dev, "RMI reset failed!\n");
|
||||
|
||||
dev_info(&f34->fn->dev, "Writing firmware (%d bytes)...\n",
|
||||
f34->v7.img.ui_firmware.size);
|
||||
|
||||
ret = rmi_f34v7_write_firmware(f34);
|
||||
if (ret < 0)
|
||||
goto fail;
|
||||
return ret;
|
||||
|
||||
dev_info(&f34->fn->dev, "Writing config (%d bytes)...\n",
|
||||
f34->v7.img.ui_config.size);
|
||||
@ -1254,28 +1075,25 @@ int rmi_f34v7_do_reflash(struct f34_data *f34, const struct firmware *fw)
|
||||
f34->v7.config_area = v7_UI_CONFIG_AREA;
|
||||
ret = rmi_f34v7_write_ui_config(f34);
|
||||
if (ret < 0)
|
||||
goto fail;
|
||||
return ret;
|
||||
|
||||
if (f34->v7.has_display_cfg && f34->v7.img.contains_display_cfg) {
|
||||
dev_info(&f34->fn->dev, "Writing display config...\n");
|
||||
|
||||
ret = rmi_f34v7_write_dp_config(f34);
|
||||
if (ret < 0)
|
||||
goto fail;
|
||||
return ret;
|
||||
}
|
||||
|
||||
if (f34->v7.new_partition_table) {
|
||||
if (f34->v7.has_guest_code && f34->v7.img.contains_guest_code) {
|
||||
dev_info(&f34->fn->dev, "Writing guest code...\n");
|
||||
|
||||
ret = rmi_f34v7_write_guest_code(f34);
|
||||
if (ret < 0)
|
||||
goto fail;
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
fail:
|
||||
return ret;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int rmi_f34v7_enter_flash_prog(struct f34_data *f34)
|
||||
@ -1288,8 +1106,11 @@ static int rmi_f34v7_enter_flash_prog(struct f34_data *f34)
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
if (f34->v7.in_bl_mode)
|
||||
if (f34->v7.in_bl_mode) {
|
||||
dev_info(&f34->fn->dev, "%s: Device in bootloader mode\n",
|
||||
__func__);
|
||||
return 0;
|
||||
}
|
||||
|
||||
init_completion(&f34->v7.cmd_done);
|
||||
|
||||
@ -1297,7 +1118,7 @@ static int rmi_f34v7_enter_flash_prog(struct f34_data *f34)
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
ret = rmi_f34v7_wait_for_idle(f34, F34_ENABLE_WAIT_MS);
|
||||
ret = rmi_f34v7_check_command_status(f34, F34_ENABLE_WAIT_MS);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
@ -1308,39 +1129,16 @@ int rmi_f34v7_start_reflash(struct f34_data *f34, const struct firmware *fw)
|
||||
{
|
||||
int ret = 0;
|
||||
|
||||
f34->fn->rmi_dev->driver->set_irq_bits(f34->fn->rmi_dev, f34->fn->irq_mask);
|
||||
|
||||
f34->v7.config_area = v7_UI_CONFIG_AREA;
|
||||
f34->v7.image = fw->data;
|
||||
|
||||
ret = rmi_f34v7_parse_image_info(f34);
|
||||
if (ret < 0)
|
||||
goto exit;
|
||||
|
||||
if (!f34->v7.force_update && f34->v7.new_partition_table) {
|
||||
dev_err(&f34->fn->dev, "%s: Partition table mismatch\n",
|
||||
__func__);
|
||||
ret = -EINVAL;
|
||||
goto exit;
|
||||
}
|
||||
return ret;
|
||||
|
||||
dev_info(&f34->fn->dev, "Firmware image OK\n");
|
||||
|
||||
ret = rmi_f34v7_read_flash_status(f34);
|
||||
if (ret < 0)
|
||||
goto exit;
|
||||
|
||||
if (f34->v7.in_bl_mode) {
|
||||
dev_info(&f34->fn->dev, "%s: Device in bootloader mode\n",
|
||||
__func__);
|
||||
}
|
||||
|
||||
rmi_f34v7_enter_flash_prog(f34);
|
||||
|
||||
return 0;
|
||||
|
||||
exit:
|
||||
return ret;
|
||||
return rmi_f34v7_enter_flash_prog(f34);
|
||||
}
|
||||
|
||||
int rmi_f34v7_probe(struct f34_data *f34)
|
||||
@ -1384,6 +1182,5 @@ int rmi_f34v7_probe(struct f34_data *f34)
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
f34->v7.force_update = true;
|
||||
return 0;
|
||||
}
|
||||
|
@ -390,8 +390,8 @@ static int rmi_f54_vidioc_querycap(struct file *file, void *priv,
|
||||
{
|
||||
struct f54_data *f54 = video_drvdata(file);
|
||||
|
||||
strlcpy(cap->driver, F54_NAME, sizeof(cap->driver));
|
||||
strlcpy(cap->card, SYNAPTICS_INPUT_DEVICE_NAME, sizeof(cap->card));
|
||||
strscpy(cap->driver, F54_NAME, sizeof(cap->driver));
|
||||
strscpy(cap->card, SYNAPTICS_INPUT_DEVICE_NAME, sizeof(cap->card));
|
||||
snprintf(cap->bus_info, sizeof(cap->bus_info),
|
||||
"rmi4:%s", dev_name(&f54->fn->dev));
|
||||
|
||||
@ -410,7 +410,7 @@ static int rmi_f54_vidioc_enum_input(struct file *file, void *priv,
|
||||
|
||||
i->type = V4L2_INPUT_TYPE_TOUCH;
|
||||
|
||||
strlcpy(i->name, rmi_f54_report_type_names[reptype], sizeof(i->name));
|
||||
strscpy(i->name, rmi_f54_report_type_names[reptype], sizeof(i->name));
|
||||
return 0;
|
||||
}
|
||||
|
||||
@ -696,7 +696,7 @@ static int rmi_f54_probe(struct rmi_function *fn)
|
||||
rmi_f54_set_input(f54, 0);
|
||||
|
||||
/* register video device */
|
||||
strlcpy(f54->v4l2.name, F54_NAME, sizeof(f54->v4l2.name));
|
||||
strscpy(f54->v4l2.name, F54_NAME, sizeof(f54->v4l2.name));
|
||||
ret = v4l2_device_register(&fn->dev, &f54->v4l2);
|
||||
if (ret) {
|
||||
dev_err(&fn->dev, "Unable to register video dev.\n");
|
||||
|
@ -110,8 +110,8 @@ static int altera_ps2_probe(struct platform_device *pdev)
|
||||
serio->write = altera_ps2_write;
|
||||
serio->open = altera_ps2_open;
|
||||
serio->close = altera_ps2_close;
|
||||
strlcpy(serio->name, dev_name(&pdev->dev), sizeof(serio->name));
|
||||
strlcpy(serio->phys, dev_name(&pdev->dev), sizeof(serio->phys));
|
||||
strscpy(serio->name, dev_name(&pdev->dev), sizeof(serio->name));
|
||||
strscpy(serio->phys, dev_name(&pdev->dev), sizeof(serio->phys));
|
||||
serio->port_data = ps2if;
|
||||
serio->dev.parent = &pdev->dev;
|
||||
ps2if->io = serio;
|
||||
|
@ -126,8 +126,8 @@ static int amba_kmi_probe(struct amba_device *dev,
|
||||
io->write = amba_kmi_write;
|
||||
io->open = amba_kmi_open;
|
||||
io->close = amba_kmi_close;
|
||||
strlcpy(io->name, dev_name(&dev->dev), sizeof(io->name));
|
||||
strlcpy(io->phys, dev_name(&dev->dev), sizeof(io->phys));
|
||||
strscpy(io->name, dev_name(&dev->dev), sizeof(io->name));
|
||||
strscpy(io->phys, dev_name(&dev->dev), sizeof(io->phys));
|
||||
io->port_data = kmi;
|
||||
io->dev.parent = &dev->dev;
|
||||
|
||||
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue
Block a user