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
|
Bindings for joystick devices connected to ADC controllers supporting
|
||||||
the Industrial I/O subsystem.
|
the Industrial I/O subsystem.
|
||||||
|
|
||||||
|
allOf:
|
||||||
|
- $ref: input.yaml#
|
||||||
|
|
||||||
properties:
|
properties:
|
||||||
compatible:
|
compatible:
|
||||||
const: adc-joystick
|
const: adc-joystick
|
||||||
@ -28,6 +31,8 @@ properties:
|
|||||||
https://github.com/devicetree-org/dt-schema/blob/master/schemas/iio/iio-consumer.yaml
|
https://github.com/devicetree-org/dt-schema/blob/master/schemas/iio/iio-consumer.yaml
|
||||||
for details.
|
for details.
|
||||||
|
|
||||||
|
poll-interval: true
|
||||||
|
|
||||||
'#address-cells':
|
'#address-cells':
|
||||||
const: 1
|
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
|
maximum: 256
|
||||||
default: 16
|
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:
|
required:
|
||||||
- compatible
|
- compatible
|
||||||
- reg
|
- reg
|
||||||
@ -56,7 +62,7 @@ required:
|
|||||||
- clocks
|
- clocks
|
||||||
- clock-names
|
- clock-names
|
||||||
|
|
||||||
additionalProperties: false
|
unevaluatedProperties: false
|
||||||
|
|
||||||
examples:
|
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 {
|
auo_pixcir_ts@5c {
|
||||||
compatible = "auo,auo_pixcir_ts";
|
compatible = "auo,auo_pixcir_ts";
|
||||||
reg = <0x5c>;
|
reg = <0x5c>;
|
||||||
interrupts = <2 0>;
|
interrupts = <2 IRQ_TYPE_LEVEL_HIGH>;
|
||||||
|
|
||||||
gpios = <&gpf 2 0 2>, /* INT */
|
gpios = <&gpf 2 0 GPIO_LEVEL_HIGH>, /* INT */
|
||||||
<&gpf 5 1 0>; /* RST */
|
<&gpf 5 1 GPIO_LEVEL_LOW>; /* RST */
|
||||||
|
|
||||||
x-size = <800>;
|
x-size = <800>;
|
||||||
y-size = <600>;
|
y-size = <600>;
|
||||||
|
@ -3,15 +3,16 @@
|
|||||||
Required Properties:
|
Required Properties:
|
||||||
- compatible must be toradex,vf50-touchscreen
|
- compatible must be toradex,vf50-touchscreen
|
||||||
- io-channels: adc channels being used by the Colibri VF50 module
|
- 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+
|
- xp-gpios: FET gate driver for input of X+
|
||||||
- xm-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+
|
- yp-gpios: FET gate driver for input of Y+
|
||||||
- ym-gpios: FET gate driver for input of Y-
|
- ym-gpios: FET gate driver for input of Y-
|
||||||
- interrupts: pen irq interrupt for touch detection
|
- interrupts: pen irq interrupt for touch detection, signal from X plate
|
||||||
- pinctrl-names: "idle", "default", "gpios"
|
- pinctrl-names: "idle", "default"
|
||||||
- pinctrl-0: pinctrl node for pen/touch detection state pinmux
|
- 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-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
|
- vf50-ts-min-pressure: pressure level at which to stop measuring X/Y values
|
||||||
|
|
||||||
Example:
|
Example:
|
||||||
@ -26,9 +27,8 @@ Example:
|
|||||||
ym-gpios = <&gpio0 4 GPIO_ACTIVE_HIGH>;
|
ym-gpios = <&gpio0 4 GPIO_ACTIVE_HIGH>;
|
||||||
interrupt-parent = <&gpio0>;
|
interrupt-parent = <&gpio0>;
|
||||||
interrupts = <8 IRQ_TYPE_LEVEL_LOW>;
|
interrupts = <8 IRQ_TYPE_LEVEL_LOW>;
|
||||||
pinctrl-names = "idle","default","gpios";
|
pinctrl-names = "idle","default";
|
||||||
pinctrl-0 = <&pinctrl_touchctrl_idle>;
|
pinctrl-0 = <&pinctrl_touchctrl_idle>, <&pinctrl_touchctrl_gpios>;
|
||||||
pinctrl-1 = <&pinctrl_touchctrl_default>;
|
pinctrl-1 = <&pinctrl_touchctrl_default>, <&pinctrl_touchctrl_gpios>;
|
||||||
pinctrl-2 = <&pinctrl_touchctrl_gpios>;
|
|
||||||
vf50-ts-min-pressure = <200>;
|
vf50-ts-min-pressure = <200>;
|
||||||
};
|
};
|
||||||
|
@ -14,9 +14,13 @@ allOf:
|
|||||||
|
|
||||||
properties:
|
properties:
|
||||||
compatible:
|
compatible:
|
||||||
enum:
|
oneOf:
|
||||||
- elan,ektf3624
|
- enum:
|
||||||
- elan,ekth3500
|
- elan,ektf3624
|
||||||
|
- elan,ekth3500
|
||||||
|
- items:
|
||||||
|
- const: elan,ekth3915
|
||||||
|
- const: elan,ekth3500
|
||||||
|
|
||||||
reg:
|
reg:
|
||||||
maxItems: 1
|
maxItems: 1
|
||||||
|
@ -54,8 +54,7 @@ Optional properties common with MFD (deprecated):
|
|||||||
1 -> 3.25 MHz
|
1 -> 3.25 MHz
|
||||||
2 || 3 -> 6.5 MHz
|
2 || 3 -> 6.5 MHz
|
||||||
|
|
||||||
Node name must be stmpe_touchscreen and should be child node of stmpe node to
|
Node should be child node of stmpe node to which it belongs.
|
||||||
which it belongs.
|
|
||||||
|
|
||||||
Note that common ADC settings of stmpe_touchscreen (child) will take precedence
|
Note that common ADC settings of stmpe_touchscreen (child) will take precedence
|
||||||
over the settings done in MFD.
|
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
|
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.
|
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>:
|
* ABS_MT_<name>:
|
||||||
|
|
||||||
- Used to describe multitouch input events. Please see
|
- Used to describe multitouch input events. Please see
|
||||||
|
@ -189,3 +189,9 @@ Gamepads report the following events:
|
|||||||
- Rumble:
|
- Rumble:
|
||||||
|
|
||||||
Rumble is advertised as FF_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
|
S: Supported
|
||||||
W: http://wiki.analog.com/ADP5588
|
W: http://wiki.analog.com/ADP5588
|
||||||
W: https://ez.analog.com/linux-software-drivers
|
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
|
F: drivers/input/keyboard/adp5588-keys.c
|
||||||
|
|
||||||
ADP8860 BACKLIGHT DRIVER (ADP8860/ADP8861/ADP8863)
|
ADP8860 BACKLIGHT DRIVER (ADP8860/ADP8861/ADP8863)
|
||||||
@ -9713,6 +9713,13 @@ S: Orphan
|
|||||||
F: Documentation/ia64/
|
F: Documentation/ia64/
|
||||||
F: arch/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
|
IBM Power 842 compression accelerator
|
||||||
M: Haren Myneni <haren@us.ibm.com>
|
M: Haren Myneni <haren@us.ibm.com>
|
||||||
S: Supported
|
S: Supported
|
||||||
@ -12925,6 +12932,12 @@ S: Supported
|
|||||||
F: Documentation/devicetree/bindings/media/mediatek-jpeg-*.yaml
|
F: Documentation/devicetree/bindings/media/mediatek-jpeg-*.yaml
|
||||||
F: drivers/media/platform/mediatek/jpeg/
|
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
|
MEDIATEK MDP DRIVER
|
||||||
M: Minghsiu Tsai <minghsiu.tsai@mediatek.com>
|
M: Minghsiu Tsai <minghsiu.tsai@mediatek.com>
|
||||||
M: Houlong Wei <houlong.wei@mediatek.com>
|
M: Houlong Wei <houlong.wei@mediatek.com>
|
||||||
@ -16337,6 +16350,12 @@ F: Documentation/devicetree/bindings/pinctrl/sunplus,*
|
|||||||
F: drivers/pinctrl/sunplus/
|
F: drivers/pinctrl/sunplus/
|
||||||
F: include/dt-bindings/pinctrl/sppctl*.h
|
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
|
PKTCDVD DRIVER
|
||||||
M: linux-block@vger.kernel.org
|
M: linux-block@vger.kernel.org
|
||||||
S: Orphan
|
S: Orphan
|
||||||
|
@ -990,20 +990,6 @@ endmenu
|
|||||||
menu "I2C GPIO expanders"
|
menu "I2C GPIO expanders"
|
||||||
depends on I2C
|
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
|
config GPIO_ADNP
|
||||||
tristate "Avionic Design N-bit GPIO expander"
|
tristate "Avionic Design N-bit GPIO expander"
|
||||||
depends on OF_GPIO
|
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_74XX_MMIO) += gpio-74xx-mmio.o
|
||||||
obj-$(CONFIG_GPIO_ADNP) += gpio-adnp.o
|
obj-$(CONFIG_GPIO_ADNP) += gpio-adnp.o
|
||||||
obj-$(CONFIG_GPIO_ADP5520) += gpio-adp5520.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_AGGREGATOR) += gpio-aggregator.o
|
||||||
obj-$(CONFIG_GPIO_ALTERA_A10SR) += gpio-altera-a10sr.o
|
obj-$(CONFIG_GPIO_ALTERA_A10SR) += gpio-altera-a10sr.o
|
||||||
obj-$(CONFIG_GPIO_ALTERA) += gpio-altera.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_HAT3Y] = "Hat 3Y", [ABS_PRESSURE] = "Pressure",
|
||||||
[ABS_DISTANCE] = "Distance", [ABS_TILT_X] = "XTilt",
|
[ABS_DISTANCE] = "Distance", [ABS_TILT_X] = "XTilt",
|
||||||
[ABS_TILT_Y] = "YTilt", [ABS_TOOL_WIDTH] = "ToolWidth",
|
[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_MAJOR] = "MTMajor",
|
||||||
[ABS_MT_TOUCH_MINOR] = "MTMinor",
|
[ABS_MT_TOUCH_MINOR] = "MTMinor",
|
||||||
[ABS_MT_WIDTH_MAJOR] = "MTMajorW",
|
[ABS_MT_WIDTH_MAJOR] = "MTMajorW",
|
||||||
|
@ -6,9 +6,6 @@
|
|||||||
* Copyright (c) 2006 Dmitry Torokhov <dtor@mail.ru>
|
* Copyright (c) 2006 Dmitry Torokhov <dtor@mail.ru>
|
||||||
*/
|
*/
|
||||||
|
|
||||||
/*
|
|
||||||
*/
|
|
||||||
|
|
||||||
/* #define DEBUG */
|
/* #define DEBUG */
|
||||||
|
|
||||||
#include <linux/input.h>
|
#include <linux/input.h>
|
||||||
|
@ -6,9 +6,6 @@
|
|||||||
* Copyright (c) 2006 Dmitry Torokhov <dtor@mail.ru>
|
* Copyright (c) 2006 Dmitry Torokhov <dtor@mail.ru>
|
||||||
*/
|
*/
|
||||||
|
|
||||||
/*
|
|
||||||
*/
|
|
||||||
|
|
||||||
/* #define DEBUG */
|
/* #define DEBUG */
|
||||||
|
|
||||||
#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
|
#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
|
||||||
|
@ -7,9 +7,6 @@
|
|||||||
* EMU10k1 - SB Live / Audigy - gameport driver for Linux
|
* EMU10k1 - SB Live / Audigy - gameport driver for Linux
|
||||||
*/
|
*/
|
||||||
|
|
||||||
/*
|
|
||||||
*/
|
|
||||||
|
|
||||||
#include <asm/io.h>
|
#include <asm/io.h>
|
||||||
|
|
||||||
#include <linux/module.h>
|
#include <linux/module.h>
|
||||||
|
@ -7,9 +7,6 @@
|
|||||||
* PDPI Lightning 4 gamecard driver for Linux.
|
* PDPI Lightning 4 gamecard driver for Linux.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
/*
|
|
||||||
*/
|
|
||||||
|
|
||||||
#include <asm/io.h>
|
#include <asm/io.h>
|
||||||
#include <linux/delay.h>
|
#include <linux/delay.h>
|
||||||
#include <linux/errno.h>
|
#include <linux/errno.h>
|
||||||
|
@ -8,9 +8,6 @@
|
|||||||
* NS558 based standard IBM game port driver for Linux
|
* NS558 based standard IBM game port driver for Linux
|
||||||
*/
|
*/
|
||||||
|
|
||||||
/*
|
|
||||||
*/
|
|
||||||
|
|
||||||
#include <asm/io.h>
|
#include <asm/io.h>
|
||||||
|
|
||||||
#include <linux/module.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.
|
* usb_ids/bt_ids.h header.
|
||||||
*/
|
*/
|
||||||
#define USB_VENDOR_ID_SONY 0x054c
|
#define USB_VENDOR_ID_SONY 0x054c
|
||||||
|
@ -7,9 +7,6 @@
|
|||||||
* FP-Gaming Assassin 3D joystick driver for Linux
|
* FP-Gaming Assassin 3D joystick driver for Linux
|
||||||
*/
|
*/
|
||||||
|
|
||||||
/*
|
|
||||||
*/
|
|
||||||
|
|
||||||
#include <linux/kernel.h>
|
#include <linux/kernel.h>
|
||||||
#include <linux/module.h>
|
#include <linux/module.h>
|
||||||
#include <linux/slab.h>
|
#include <linux/slab.h>
|
||||||
|
@ -26,8 +26,23 @@ struct adc_joystick {
|
|||||||
struct adc_joystick_axis *axes;
|
struct adc_joystick_axis *axes;
|
||||||
struct iio_channel *chans;
|
struct iio_channel *chans;
|
||||||
int num_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)
|
static int adc_joystick_handle(const void *data, void *private)
|
||||||
{
|
{
|
||||||
struct adc_joystick *joy = private;
|
struct adc_joystick *joy = private;
|
||||||
@ -179,6 +194,7 @@ static int adc_joystick_probe(struct platform_device *pdev)
|
|||||||
int error;
|
int error;
|
||||||
int bits;
|
int bits;
|
||||||
int i;
|
int i;
|
||||||
|
unsigned int poll_interval;
|
||||||
|
|
||||||
joy = devm_kzalloc(dev, sizeof(*joy), GFP_KERNEL);
|
joy = devm_kzalloc(dev, sizeof(*joy), GFP_KERNEL);
|
||||||
if (!joy)
|
if (!joy)
|
||||||
@ -192,8 +208,25 @@ static int adc_joystick_probe(struct platform_device *pdev)
|
|||||||
return error;
|
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++) {
|
for (i = 0; joy->chans[i].indio_dev; i++) {
|
||||||
|
if (joy->polled)
|
||||||
|
continue;
|
||||||
bits = joy->chans[i].channel->scan_type.storagebits;
|
bits = joy->chans[i].channel->scan_type.storagebits;
|
||||||
if (!bits || bits > 16) {
|
if (!bits || bits > 16) {
|
||||||
dev_err(dev, "Unsupported channel storage size\n");
|
dev_err(dev, "Unsupported channel storage size\n");
|
||||||
@ -215,23 +248,31 @@ static int adc_joystick_probe(struct platform_device *pdev)
|
|||||||
joy->input = input;
|
joy->input = input;
|
||||||
input->name = pdev->name;
|
input->name = pdev->name;
|
||||||
input->id.bustype = BUS_HOST;
|
input->id.bustype = BUS_HOST;
|
||||||
input->open = adc_joystick_open;
|
|
||||||
input->close = adc_joystick_close;
|
|
||||||
|
|
||||||
error = adc_joystick_set_axes(dev, joy);
|
error = adc_joystick_set_axes(dev, joy);
|
||||||
if (error)
|
if (error)
|
||||||
return error;
|
return error;
|
||||||
|
|
||||||
joy->buffer = iio_channel_get_all_cb(dev, adc_joystick_handle, joy);
|
if (joy->polled) {
|
||||||
if (IS_ERR(joy->buffer)) {
|
input_setup_polling(input, adc_joystick_poll);
|
||||||
dev_err(dev, "Unable to allocate callback buffer\n");
|
input_set_poll_interval(input, poll_interval);
|
||||||
return PTR_ERR(joy->buffer);
|
} else {
|
||||||
}
|
input->open = adc_joystick_open;
|
||||||
|
input->close = adc_joystick_close;
|
||||||
|
|
||||||
error = devm_add_action_or_reset(dev, adc_joystick_cleanup, joy->buffer);
|
joy->buffer = iio_channel_get_all_cb(dev, adc_joystick_handle,
|
||||||
if (error) {
|
joy);
|
||||||
dev_err(dev, "Unable to add action\n");
|
if (IS_ERR(joy->buffer)) {
|
||||||
return error;
|
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);
|
||||||
|
if (error) {
|
||||||
|
dev_err(dev, "Unable to add action\n");
|
||||||
|
return error;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
input_set_drvdata(input, joy);
|
input_set_drvdata(input, joy);
|
||||||
|
@ -7,9 +7,6 @@
|
|||||||
* Logitech ADI joystick family driver for Linux
|
* Logitech ADI joystick family driver for Linux
|
||||||
*/
|
*/
|
||||||
|
|
||||||
/*
|
|
||||||
*/
|
|
||||||
|
|
||||||
#include <linux/delay.h>
|
#include <linux/delay.h>
|
||||||
#include <linux/kernel.h>
|
#include <linux/kernel.h>
|
||||||
#include <linux/module.h>
|
#include <linux/module.h>
|
||||||
|
@ -7,9 +7,6 @@
|
|||||||
* Driver for Amiga joysticks for Linux/m68k
|
* Driver for Amiga joysticks for Linux/m68k
|
||||||
*/
|
*/
|
||||||
|
|
||||||
/*
|
|
||||||
*/
|
|
||||||
|
|
||||||
#include <linux/types.h>
|
#include <linux/types.h>
|
||||||
#include <linux/errno.h>
|
#include <linux/errno.h>
|
||||||
#include <linux/kernel.h>
|
#include <linux/kernel.h>
|
||||||
|
@ -7,9 +7,6 @@
|
|||||||
* Analog joystick and gamepad driver for Linux
|
* Analog joystick and gamepad driver for Linux
|
||||||
*/
|
*/
|
||||||
|
|
||||||
/*
|
|
||||||
*/
|
|
||||||
|
|
||||||
#include <linux/delay.h>
|
#include <linux/delay.h>
|
||||||
#include <linux/kernel.h>
|
#include <linux/kernel.h>
|
||||||
#include <linux/module.h>
|
#include <linux/module.h>
|
||||||
|
@ -7,9 +7,6 @@
|
|||||||
* Creative Labs Blaster GamePad Cobra driver for Linux
|
* Creative Labs Blaster GamePad Cobra driver for Linux
|
||||||
*/
|
*/
|
||||||
|
|
||||||
/*
|
|
||||||
*/
|
|
||||||
|
|
||||||
#include <linux/kernel.h>
|
#include <linux/kernel.h>
|
||||||
#include <linux/module.h>
|
#include <linux/module.h>
|
||||||
#include <linux/slab.h>
|
#include <linux/slab.h>
|
||||||
|
@ -10,9 +10,6 @@
|
|||||||
* Atari, Amstrad, Commodore, Amiga, Sega, etc. joystick driver for Linux
|
* Atari, Amstrad, Commodore, Amiga, Sega, etc. joystick driver for Linux
|
||||||
*/
|
*/
|
||||||
|
|
||||||
/*
|
|
||||||
*/
|
|
||||||
|
|
||||||
#include <linux/kernel.h>
|
#include <linux/kernel.h>
|
||||||
#include <linux/module.h>
|
#include <linux/module.h>
|
||||||
#include <linux/delay.h>
|
#include <linux/delay.h>
|
||||||
|
@ -11,9 +11,6 @@
|
|||||||
* Raphael Assenat
|
* Raphael Assenat
|
||||||
*/
|
*/
|
||||||
|
|
||||||
/*
|
|
||||||
*/
|
|
||||||
|
|
||||||
#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
|
#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
|
||||||
|
|
||||||
#include <linux/kernel.h>
|
#include <linux/kernel.h>
|
||||||
|
@ -7,9 +7,6 @@
|
|||||||
* Genius Flight 2000 joystick driver for Linux
|
* Genius Flight 2000 joystick driver for Linux
|
||||||
*/
|
*/
|
||||||
|
|
||||||
/*
|
|
||||||
*/
|
|
||||||
|
|
||||||
#include <linux/delay.h>
|
#include <linux/delay.h>
|
||||||
#include <linux/kernel.h>
|
#include <linux/kernel.h>
|
||||||
#include <linux/slab.h>
|
#include <linux/slab.h>
|
||||||
|
@ -7,9 +7,6 @@
|
|||||||
* Gravis/Kensington GrIP protocol joystick and gamepad driver for Linux
|
* Gravis/Kensington GrIP protocol joystick and gamepad driver for Linux
|
||||||
*/
|
*/
|
||||||
|
|
||||||
/*
|
|
||||||
*/
|
|
||||||
|
|
||||||
#include <linux/kernel.h>
|
#include <linux/kernel.h>
|
||||||
#include <linux/module.h>
|
#include <linux/module.h>
|
||||||
#include <linux/slab.h>
|
#include <linux/slab.h>
|
||||||
|
@ -7,9 +7,6 @@
|
|||||||
* Guillemot Digital Interface Protocol driver for Linux
|
* Guillemot Digital Interface Protocol driver for Linux
|
||||||
*/
|
*/
|
||||||
|
|
||||||
/*
|
|
||||||
*/
|
|
||||||
|
|
||||||
#include <linux/kernel.h>
|
#include <linux/kernel.h>
|
||||||
#include <linux/slab.h>
|
#include <linux/slab.h>
|
||||||
#include <linux/module.h>
|
#include <linux/module.h>
|
||||||
|
@ -10,9 +10,6 @@
|
|||||||
* InterAct digital gamepad/joystick driver for Linux
|
* InterAct digital gamepad/joystick driver for Linux
|
||||||
*/
|
*/
|
||||||
|
|
||||||
/*
|
|
||||||
*/
|
|
||||||
|
|
||||||
#include <linux/kernel.h>
|
#include <linux/kernel.h>
|
||||||
#include <linux/slab.h>
|
#include <linux/slab.h>
|
||||||
#include <linux/module.h>
|
#include <linux/module.h>
|
||||||
|
@ -8,9 +8,6 @@
|
|||||||
* out of the joystick port into the syslog ...
|
* out of the joystick port into the syslog ...
|
||||||
*/
|
*/
|
||||||
|
|
||||||
/*
|
|
||||||
*/
|
|
||||||
|
|
||||||
#include <linux/module.h>
|
#include <linux/module.h>
|
||||||
#include <linux/gameport.h>
|
#include <linux/gameport.h>
|
||||||
#include <linux/kernel.h>
|
#include <linux/kernel.h>
|
||||||
|
@ -7,9 +7,6 @@
|
|||||||
* Magellan and Space Mouse 6dof controller driver for Linux
|
* Magellan and Space Mouse 6dof controller driver for Linux
|
||||||
*/
|
*/
|
||||||
|
|
||||||
/*
|
|
||||||
*/
|
|
||||||
|
|
||||||
#include <linux/kernel.h>
|
#include <linux/kernel.h>
|
||||||
#include <linux/module.h>
|
#include <linux/module.h>
|
||||||
#include <linux/slab.h>
|
#include <linux/slab.h>
|
||||||
|
@ -7,9 +7,6 @@
|
|||||||
* Microsoft SideWinder joystick family driver for Linux
|
* Microsoft SideWinder joystick family driver for Linux
|
||||||
*/
|
*/
|
||||||
|
|
||||||
/*
|
|
||||||
*/
|
|
||||||
|
|
||||||
#include <linux/delay.h>
|
#include <linux/delay.h>
|
||||||
#include <linux/kernel.h>
|
#include <linux/kernel.h>
|
||||||
#include <linux/module.h>
|
#include <linux/module.h>
|
||||||
|
@ -11,9 +11,6 @@
|
|||||||
* SpaceTec SpaceBall 2003/3003/4000 FLX driver for Linux
|
* SpaceTec SpaceBall 2003/3003/4000 FLX driver for Linux
|
||||||
*/
|
*/
|
||||||
|
|
||||||
/*
|
|
||||||
*/
|
|
||||||
|
|
||||||
#include <linux/kernel.h>
|
#include <linux/kernel.h>
|
||||||
#include <linux/slab.h>
|
#include <linux/slab.h>
|
||||||
#include <linux/module.h>
|
#include <linux/module.h>
|
||||||
|
@ -10,9 +10,6 @@
|
|||||||
* SpaceTec SpaceOrb 360 and Avenger 6dof controller driver for Linux
|
* SpaceTec SpaceOrb 360 and Avenger 6dof controller driver for Linux
|
||||||
*/
|
*/
|
||||||
|
|
||||||
/*
|
|
||||||
*/
|
|
||||||
|
|
||||||
#include <linux/kernel.h>
|
#include <linux/kernel.h>
|
||||||
#include <linux/slab.h>
|
#include <linux/slab.h>
|
||||||
#include <linux/module.h>
|
#include <linux/module.h>
|
||||||
|
@ -8,9 +8,6 @@
|
|||||||
* Gravis Stinger gamepad driver for Linux
|
* Gravis Stinger gamepad driver for Linux
|
||||||
*/
|
*/
|
||||||
|
|
||||||
/*
|
|
||||||
*/
|
|
||||||
|
|
||||||
#include <linux/kernel.h>
|
#include <linux/kernel.h>
|
||||||
#include <linux/module.h>
|
#include <linux/module.h>
|
||||||
#include <linux/slab.h>
|
#include <linux/slab.h>
|
||||||
|
@ -10,9 +10,6 @@
|
|||||||
* ThrustMaster DirectConnect (BSP) joystick family driver for Linux
|
* ThrustMaster DirectConnect (BSP) joystick family driver for Linux
|
||||||
*/
|
*/
|
||||||
|
|
||||||
/*
|
|
||||||
*/
|
|
||||||
|
|
||||||
#include <linux/delay.h>
|
#include <linux/delay.h>
|
||||||
#include <linux/kernel.h>
|
#include <linux/kernel.h>
|
||||||
#include <linux/slab.h>
|
#include <linux/slab.h>
|
||||||
|
@ -10,9 +10,6 @@
|
|||||||
* TurboGraFX parallel port interface driver for Linux.
|
* TurboGraFX parallel port interface driver for Linux.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
/*
|
|
||||||
*/
|
|
||||||
|
|
||||||
#include <linux/kernel.h>
|
#include <linux/kernel.h>
|
||||||
#include <linux/parport.h>
|
#include <linux/parport.h>
|
||||||
#include <linux/input.h>
|
#include <linux/input.h>
|
||||||
|
@ -32,9 +32,6 @@
|
|||||||
* Arndt Schoenewald <arndt@quelltext.com>
|
* Arndt Schoenewald <arndt@quelltext.com>
|
||||||
*/
|
*/
|
||||||
|
|
||||||
/*
|
|
||||||
*/
|
|
||||||
|
|
||||||
#include <linux/kernel.h>
|
#include <linux/kernel.h>
|
||||||
#include <linux/module.h>
|
#include <linux/module.h>
|
||||||
#include <linux/slab.h>
|
#include <linux/slab.h>
|
||||||
|
@ -7,9 +7,6 @@
|
|||||||
* Logitech WingMan Warrior joystick driver for Linux
|
* Logitech WingMan Warrior joystick driver for Linux
|
||||||
*/
|
*/
|
||||||
|
|
||||||
/*
|
|
||||||
*/
|
|
||||||
|
|
||||||
#include <linux/kernel.h>
|
#include <linux/kernel.h>
|
||||||
#include <linux/module.h>
|
#include <linux/module.h>
|
||||||
#include <linux/slab.h>
|
#include <linux/slab.h>
|
||||||
|
@ -61,6 +61,7 @@
|
|||||||
* Later changes can be tracked in SCM.
|
* Later changes can be tracked in SCM.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
#include <linux/bits.h>
|
||||||
#include <linux/kernel.h>
|
#include <linux/kernel.h>
|
||||||
#include <linux/input.h>
|
#include <linux/input.h>
|
||||||
#include <linux/rcupdate.h>
|
#include <linux/rcupdate.h>
|
||||||
@ -80,6 +81,9 @@
|
|||||||
#define MAP_TRIGGERS_TO_BUTTONS (1 << 1)
|
#define MAP_TRIGGERS_TO_BUTTONS (1 << 1)
|
||||||
#define MAP_STICKS_TO_NULL (1 << 2)
|
#define MAP_STICKS_TO_NULL (1 << 2)
|
||||||
#define MAP_SELECT_BUTTON (1 << 3)
|
#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 | \
|
#define DANCEPAD_MAP_CONFIG (MAP_DPAD_TO_BUTTONS | \
|
||||||
MAP_TRIGGERS_TO_BUTTONS | MAP_STICKS_TO_NULL)
|
MAP_TRIGGERS_TO_BUTTONS | MAP_STICKS_TO_NULL)
|
||||||
|
|
||||||
@ -89,6 +93,17 @@
|
|||||||
#define XTYPE_XBOXONE 3
|
#define XTYPE_XBOXONE 3
|
||||||
#define XTYPE_UNKNOWN 4
|
#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;
|
static bool dpad_to_buttons;
|
||||||
module_param(dpad_to_buttons, bool, S_IRUGO);
|
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");
|
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;
|
char *name;
|
||||||
u8 mapping;
|
u8 mapping;
|
||||||
u8 xtype;
|
u8 xtype;
|
||||||
|
u8 packet_type;
|
||||||
} xpad_device[] = {
|
} xpad_device[] = {
|
||||||
{ 0x0079, 0x18d4, "GPD Win 2 X-Box Controller", 0, XTYPE_XBOX360 },
|
{ 0x0079, 0x18d4, "GPD Win 2 X-Box Controller", 0, XTYPE_XBOX360 },
|
||||||
|
{ 0x03eb, 0xff01, "Wooting One (Legacy)", 0, XTYPE_XBOX360 },
|
||||||
|
{ 0x03eb, 0xff02, "Wooting Two (Legacy)", 0, XTYPE_XBOX360 },
|
||||||
{ 0x044f, 0x0f00, "Thrustmaster Wheel", 0, XTYPE_XBOX },
|
{ 0x044f, 0x0f00, "Thrustmaster Wheel", 0, XTYPE_XBOX },
|
||||||
{ 0x044f, 0x0f03, "Thrustmaster Wheel", 0, XTYPE_XBOX },
|
{ 0x044f, 0x0f03, "Thrustmaster Wheel", 0, XTYPE_XBOX },
|
||||||
{ 0x044f, 0x0f07, "Thrustmaster, Inc. Controller", 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, 0x0291, "Xbox 360 Wireless Receiver (XBOX)", MAP_DPAD_TO_BUTTONS, XTYPE_XBOX360W },
|
||||||
{ 0x045e, 0x02d1, "Microsoft X-Box One pad", 0, XTYPE_XBOXONE },
|
{ 0x045e, 0x02d1, "Microsoft X-Box One pad", 0, XTYPE_XBOXONE },
|
||||||
{ 0x045e, 0x02dd, "Microsoft X-Box One pad (Firmware 2015)", 0, XTYPE_XBOXONE },
|
{ 0x045e, 0x02dd, "Microsoft X-Box One pad (Firmware 2015)", 0, XTYPE_XBOXONE },
|
||||||
{ 0x045e, 0x02e3, "Microsoft X-Box One Elite pad", 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, 0x02ea, "Microsoft X-Box One S pad", 0, XTYPE_XBOXONE },
|
||||||
{ 0x045e, 0x0719, "Xbox 360 Wireless Receiver", MAP_DPAD_TO_BUTTONS, XTYPE_XBOX360W },
|
{ 0x045e, 0x0719, "Xbox 360 Wireless Receiver", MAP_DPAD_TO_BUTTONS, XTYPE_XBOX360W },
|
||||||
|
{ 0x045e, 0x0b0a, "Microsoft X-Box Adaptive Controller", MAP_PROFILE_BUTTON, XTYPE_XBOXONE },
|
||||||
{ 0x045e, 0x0b12, "Microsoft Xbox Series S|X Controller", MAP_SELECT_BUTTON, XTYPE_XBOXONE },
|
{ 0x045e, 0x0b12, "Microsoft Xbox Series S|X Controller", MAP_SELECT_BUTTON, XTYPE_XBOXONE },
|
||||||
{ 0x046d, 0xc21d, "Logitech Gamepad F310", 0, XTYPE_XBOX360 },
|
{ 0x046d, 0xc21d, "Logitech Gamepad F310", 0, XTYPE_XBOX360 },
|
||||||
{ 0x046d, 0xc21e, "Logitech Gamepad F510", 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, 0x0063, "Hori Real Arcade Pro Hayabusa (USA) Xbox One", MAP_TRIGGERS_TO_BUTTONS, XTYPE_XBOXONE },
|
||||||
{ 0x0f0d, 0x0067, "HORIPAD ONE", 0, 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, 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, 0x010b, "Philips Recoil", 0, XTYPE_XBOX },
|
||||||
{ 0x0f30, 0x0202, "Joytech Advanced Controller", 0, XTYPE_XBOX },
|
{ 0x0f30, 0x0202, "Joytech Advanced Controller", 0, XTYPE_XBOX },
|
||||||
{ 0x0f30, 0x8888, "BigBen XBMiniPad 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, 0x8888, "TX6500+ Dance Pad (first generation)", MAP_DPAD_TO_BUTTONS, XTYPE_XBOX },
|
||||||
{ 0x1430, 0xf801, "RedOctane Controller", 0, XTYPE_XBOX360 },
|
{ 0x1430, 0xf801, "RedOctane Controller", 0, XTYPE_XBOX360 },
|
||||||
{ 0x146b, 0x0601, "BigBen Interactive XBOX 360 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, 0x0037, "Razer Sabertooth", 0, XTYPE_XBOX360 },
|
||||||
{ 0x1532, 0x0a00, "Razer Atrox Arcade Stick", MAP_TRIGGERS_TO_BUTTONS, XTYPE_XBOXONE },
|
{ 0x1532, 0x0a00, "Razer Atrox Arcade Stick", MAP_TRIGGERS_TO_BUTTONS, XTYPE_XBOXONE },
|
||||||
{ 0x1532, 0x0a03, "Razer Wildcat", 0, 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, 0x5502, "Hori Fighting Stick VX Alt", MAP_TRIGGERS_TO_BUTTONS, XTYPE_XBOX360 },
|
||||||
{ 0x24c6, 0x5503, "Hori Fighting Edge", MAP_TRIGGERS_TO_BUTTONS, XTYPE_XBOX360 },
|
{ 0x24c6, 0x5503, "Hori Fighting Edge", MAP_TRIGGERS_TO_BUTTONS, XTYPE_XBOX360 },
|
||||||
{ 0x24c6, 0x5506, "Hori SOULCALIBUR V Stick", 0, XTYPE_XBOX360 },
|
{ 0x24c6, 0x5506, "Hori SOULCALIBUR V Stick", 0, XTYPE_XBOX360 },
|
||||||
|
{ 0x24c6, 0x5510, "Hori Fighting Commander ONE (Xbox 360/PC Mode)", MAP_TRIGGERS_TO_BUTTONS, XTYPE_XBOX360 },
|
||||||
{ 0x24c6, 0x550d, "Hori GEM Xbox controller", 0, XTYPE_XBOX360 },
|
{ 0x24c6, 0x550d, "Hori GEM Xbox controller", 0, XTYPE_XBOX360 },
|
||||||
{ 0x24c6, 0x550e, "Hori Real Arcade Pro V Kai 360", MAP_TRIGGERS_TO_BUTTONS, XTYPE_XBOX360 },
|
{ 0x24c6, 0x550e, "Hori Real Arcade Pro V Kai 360", MAP_TRIGGERS_TO_BUTTONS, XTYPE_XBOX360 },
|
||||||
{ 0x24c6, 0x551a, "PowerA FUSION Pro Controller", 0, XTYPE_XBOXONE },
|
{ 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, 0x5b03, "Thrustmaster Ferrari 458 Racing Wheel", 0, XTYPE_XBOX360 },
|
||||||
{ 0x24c6, 0x5d04, "Razer Sabertooth", 0, XTYPE_XBOX360 },
|
{ 0x24c6, 0x5d04, "Razer Sabertooth", 0, XTYPE_XBOX360 },
|
||||||
{ 0x24c6, 0xfafe, "Rock Candy Gamepad for Xbox 360", 0, XTYPE_XBOX360 },
|
{ 0x24c6, 0xfafe, "Rock Candy Gamepad for Xbox 360", 0, XTYPE_XBOX360 },
|
||||||
|
{ 0x2563, 0x058d, "OneXPlayer Gamepad", 0, XTYPE_XBOX360 },
|
||||||
|
{ 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 },
|
{ 0x3285, 0x0607, "Nacon GC-100", 0, XTYPE_XBOX360 },
|
||||||
{ 0x3767, 0x0101, "Fanatec Speedster 3 Forceshock Wheel", 0, XTYPE_XBOX },
|
{ 0x3767, 0x0101, "Fanatec Speedster 3 Forceshock Wheel", 0, XTYPE_XBOX },
|
||||||
{ 0xffff, 0xffff, "Chinese-made Xbox Controller", 0, XTYPE_XBOX },
|
{ 0xffff, 0xffff, "Chinese-made Xbox Controller", 0, XTYPE_XBOX },
|
||||||
@ -390,6 +421,13 @@ static const signed short xpad_abs_triggers[] = {
|
|||||||
-1
|
-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
|
* 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
|
* 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[] = {
|
static const struct usb_device_id xpad_table[] = {
|
||||||
{ USB_INTERFACE_INFO('X', 'B', 0) }, /* X-Box USB-IF not approved class */
|
{ USB_INTERFACE_INFO('X', 'B', 0) }, /* X-Box USB-IF not approved class */
|
||||||
XPAD_XBOX360_VENDOR(0x0079), /* GPD Win 2 Controller */
|
XPAD_XBOX360_VENDOR(0x0079), /* GPD Win 2 Controller */
|
||||||
|
XPAD_XBOX360_VENDOR(0x03eb), /* Wooting Keyboards (Legacy) */
|
||||||
XPAD_XBOX360_VENDOR(0x044f), /* Thrustmaster X-Box 360 controllers */
|
XPAD_XBOX360_VENDOR(0x044f), /* Thrustmaster X-Box 360 controllers */
|
||||||
XPAD_XBOX360_VENDOR(0x045e), /* Microsoft X-Box 360 controllers */
|
XPAD_XBOX360_VENDOR(0x045e), /* Microsoft X-Box 360 controllers */
|
||||||
XPAD_XBOXONE_VENDOR(0x045e), /* Microsoft X-Box One 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 */
|
{ USB_DEVICE(0x0738, 0x4540) }, /* Mad Catz Beat Pad */
|
||||||
XPAD_XBOXONE_VENDOR(0x0738), /* Mad Catz FightStick TE 2 */
|
XPAD_XBOXONE_VENDOR(0x0738), /* Mad Catz FightStick TE 2 */
|
||||||
XPAD_XBOX360_VENDOR(0x07ff), /* Mad Catz GamePad */
|
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_XBOX360_VENDOR(0x0e6f), /* 0x0e6f X-Box 360 controllers */
|
||||||
XPAD_XBOXONE_VENDOR(0x0e6f), /* 0x0e6f X-Box One controllers */
|
XPAD_XBOXONE_VENDOR(0x0e6f), /* 0x0e6f X-Box One controllers */
|
||||||
XPAD_XBOX360_VENDOR(0x0f0d), /* Hori 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_XBOXONE_VENDOR(0x20d6), /* PowerA Controllers */
|
||||||
XPAD_XBOX360_VENDOR(0x24c6), /* PowerA Controllers */
|
XPAD_XBOX360_VENDOR(0x24c6), /* PowerA Controllers */
|
||||||
XPAD_XBOXONE_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_XBOXONE_VENDOR(0x2e24), /* Hyperkin Duke X-Box One pad */
|
||||||
XPAD_XBOX360_VENDOR(0x2f24), /* GameSir Controllers */
|
XPAD_XBOX360_VENDOR(0x2f24), /* GameSir Controllers */
|
||||||
|
XPAD_XBOX360_VENDOR(0x31e3), /* Wooting Keyboards */
|
||||||
XPAD_XBOX360_VENDOR(0x3285), /* Nacon GC-100 */
|
XPAD_XBOX360_VENDOR(0x3285), /* Nacon GC-100 */
|
||||||
{ }
|
{ }
|
||||||
};
|
};
|
||||||
@ -473,13 +517,52 @@ struct xboxone_init_packet {
|
|||||||
.len = ARRAY_SIZE(_data), \
|
.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
|
* This packet is required for all Xbox One pads with 2015
|
||||||
* or later firmware installed (or present from the factory).
|
* or later firmware installed (or present from the factory).
|
||||||
*/
|
*/
|
||||||
static const u8 xboxone_fw2015_init[] = {
|
static const u8 xboxone_power_on[] = {
|
||||||
0x05, 0x20, 0x00, 0x01, 0x00
|
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.
|
* Bluetooth mode.
|
||||||
*/
|
*/
|
||||||
static const u8 xboxone_s_init[] = {
|
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
|
* (0x0e6f:0x0165) to finish initialization and for Hori pads
|
||||||
* (0x0f0d:0x0067) to make the analog sticks work.
|
* (0x0f0d:0x0067) to make the analog sticks work.
|
||||||
*/
|
*/
|
||||||
static const u8 xboxone_hori_init[] = {
|
static const u8 xboxone_hori_ack_id[] = {
|
||||||
0x01, 0x20, 0x00, 0x09, 0x00, 0x04, 0x20, 0x3a,
|
GIP_CMD_ACK, GIP_OPT_INTERNAL, GIP_SEQ0, GIP_PL_LEN(9),
|
||||||
0x00, 0x00, 0x00, 0x80, 0x00
|
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),
|
* sending input reports. These pads include: (0x0e6f:0x02ab),
|
||||||
* (0x0e6f:0x02a4), (0x0e6f:0x02a6).
|
* (0x0e6f:0x02a4), (0x0e6f:0x02a6).
|
||||||
*/
|
*/
|
||||||
static const u8 xboxone_pdp_init1[] = {
|
static const u8 xboxone_pdp_led_on[] = {
|
||||||
0x0a, 0x20, 0x00, 0x03, 0x00, 0x01, 0x14
|
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),
|
* sending input reports. These pads include: (0x0e6f:0x02ab),
|
||||||
* (0x0e6f:0x02a4), (0x0e6f:0x02a6).
|
* (0x0e6f:0x02a4), (0x0e6f:0x02a6).
|
||||||
*/
|
*/
|
||||||
static const u8 xboxone_pdp_init2[] = {
|
static const u8 xboxone_pdp_auth[] = {
|
||||||
0x06, 0x20, 0x00, 0x02, 0x01, 0x00
|
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).
|
* sending input reports. One of those pads is (0x24c6:0x543a).
|
||||||
*/
|
*/
|
||||||
static const u8 xboxone_rumblebegin_init[] = {
|
static const u8 xboxone_rumblebegin_init[] = {
|
||||||
0x09, 0x00, 0x00, 0x09, 0x00, 0x0F, 0x00, 0x00,
|
GIP_CMD_RUMBLE, 0x00, GIP_SEQ0, GIP_PL_LEN(9),
|
||||||
0x1D, 0x1D, 0xFF, 0x00, 0x00
|
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.
|
* spin up to enough speed to actually vibrate the gamepad.
|
||||||
*/
|
*/
|
||||||
static const u8 xboxone_rumbleend_init[] = {
|
static const u8 xboxone_rumbleend_init[] = {
|
||||||
0x09, 0x00, 0x00, 0x09, 0x00, 0x0F, 0x00, 0x00,
|
GIP_CMD_RUMBLE, 0x00, GIP_SEQ0, GIP_PL_LEN(9),
|
||||||
0x00, 0x00, 0x00, 0x00, 0x00
|
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.
|
* packet is going to be sent.
|
||||||
*/
|
*/
|
||||||
static const struct xboxone_init_packet xboxone_init_packets[] = {
|
static const struct xboxone_init_packet xboxone_init_packets[] = {
|
||||||
XBOXONE_INIT_PKT(0x0e6f, 0x0165, xboxone_hori_init),
|
XBOXONE_INIT_PKT(0x0e6f, 0x0165, xboxone_hori_ack_id),
|
||||||
XBOXONE_INIT_PKT(0x0f0d, 0x0067, xboxone_hori_init),
|
XBOXONE_INIT_PKT(0x0f0d, 0x0067, xboxone_hori_ack_id),
|
||||||
XBOXONE_INIT_PKT(0x0000, 0x0000, xboxone_fw2015_init),
|
XBOXONE_INIT_PKT(0x0000, 0x0000, xboxone_power_on),
|
||||||
XBOXONE_INIT_PKT(0x045e, 0x02ea, xboxone_s_init),
|
XBOXONE_INIT_PKT(0x045e, 0x02ea, xboxone_s_init),
|
||||||
XBOXONE_INIT_PKT(0x045e, 0x0b00, xboxone_s_init),
|
XBOXONE_INIT_PKT(0x045e, 0x0b00, xboxone_s_init),
|
||||||
XBOXONE_INIT_PKT(0x0e6f, 0x0000, xboxone_pdp_init1),
|
XBOXONE_INIT_PKT(0x045e, 0x0b00, extra_input_packet_init),
|
||||||
XBOXONE_INIT_PKT(0x0e6f, 0x0000, xboxone_pdp_init2),
|
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, 0x541a, xboxone_rumblebegin_init),
|
||||||
XBOXONE_INIT_PKT(0x24c6, 0x542a, xboxone_rumblebegin_init),
|
XBOXONE_INIT_PKT(0x24c6, 0x542a, xboxone_rumblebegin_init),
|
||||||
XBOXONE_INIT_PKT(0x24c6, 0x543a, 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 mapping; /* map d-pad to buttons or to axes */
|
||||||
int xtype; /* type of xbox device */
|
int xtype; /* type of xbox device */
|
||||||
|
int packet_type; /* type of the extended packet */
|
||||||
int pad_nr; /* the order x360 pads were attached */
|
int pad_nr; /* the order x360 pads were attached */
|
||||||
const char *name; /* name of the device */
|
const char *name; /* name of the device */
|
||||||
struct work_struct work; /* init/remove device from callback */
|
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 int xpad_init_input(struct usb_xpad *xpad);
|
||||||
static void xpad_deinit_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 xpadone_ack_mode_report(struct usb_xpad *xpad, u8 seq_num);
|
||||||
|
static void xpad360w_poweroff_controller(struct usb_xpad *xpad);
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* xpad_process_packet
|
* xpad_process_packet
|
||||||
@ -656,10 +752,10 @@ static void xpad_process_packet(struct usb_xpad *xpad, u16 cmd, unsigned char *d
|
|||||||
/* digital pad */
|
/* digital pad */
|
||||||
if (xpad->mapping & MAP_DPAD_TO_BUTTONS) {
|
if (xpad->mapping & MAP_DPAD_TO_BUTTONS) {
|
||||||
/* dpad as buttons (left, right, up, down) */
|
/* dpad as buttons (left, right, up, down) */
|
||||||
input_report_key(dev, BTN_TRIGGER_HAPPY1, data[2] & 0x04);
|
input_report_key(dev, BTN_TRIGGER_HAPPY1, data[2] & BIT(2));
|
||||||
input_report_key(dev, BTN_TRIGGER_HAPPY2, data[2] & 0x08);
|
input_report_key(dev, BTN_TRIGGER_HAPPY2, data[2] & BIT(3));
|
||||||
input_report_key(dev, BTN_TRIGGER_HAPPY3, data[2] & 0x01);
|
input_report_key(dev, BTN_TRIGGER_HAPPY3, data[2] & BIT(0));
|
||||||
input_report_key(dev, BTN_TRIGGER_HAPPY4, data[2] & 0x02);
|
input_report_key(dev, BTN_TRIGGER_HAPPY4, data[2] & BIT(1));
|
||||||
} else {
|
} else {
|
||||||
input_report_abs(dev, ABS_HAT0X,
|
input_report_abs(dev, ABS_HAT0X,
|
||||||
!!(data[2] & 0x08) - !!(data[2] & 0x04));
|
!!(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 */
|
/* start/back buttons and stick press left/right */
|
||||||
input_report_key(dev, BTN_START, data[2] & 0x10);
|
input_report_key(dev, BTN_START, data[2] & BIT(4));
|
||||||
input_report_key(dev, BTN_SELECT, data[2] & 0x20);
|
input_report_key(dev, BTN_SELECT, data[2] & BIT(5));
|
||||||
input_report_key(dev, BTN_THUMBL, data[2] & 0x40);
|
input_report_key(dev, BTN_THUMBL, data[2] & BIT(6));
|
||||||
input_report_key(dev, BTN_THUMBR, data[2] & 0x80);
|
input_report_key(dev, BTN_THUMBR, data[2] & BIT(7));
|
||||||
|
|
||||||
/* "analog" buttons A, B, X, Y */
|
/* "analog" buttons A, B, X, Y */
|
||||||
input_report_key(dev, BTN_A, data[4]);
|
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_C, data[8]);
|
||||||
input_report_key(dev, BTN_Z, data[9]);
|
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);
|
input_sync(dev);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -706,10 +806,10 @@ static void xpad360_process_packet(struct usb_xpad *xpad, struct input_dev *dev,
|
|||||||
/* digital pad */
|
/* digital pad */
|
||||||
if (xpad->mapping & MAP_DPAD_TO_BUTTONS) {
|
if (xpad->mapping & MAP_DPAD_TO_BUTTONS) {
|
||||||
/* dpad as buttons (left, right, up, down) */
|
/* dpad as buttons (left, right, up, down) */
|
||||||
input_report_key(dev, BTN_TRIGGER_HAPPY1, data[2] & 0x04);
|
input_report_key(dev, BTN_TRIGGER_HAPPY1, data[2] & BIT(2));
|
||||||
input_report_key(dev, BTN_TRIGGER_HAPPY2, data[2] & 0x08);
|
input_report_key(dev, BTN_TRIGGER_HAPPY2, data[2] & BIT(3));
|
||||||
input_report_key(dev, BTN_TRIGGER_HAPPY3, data[2] & 0x01);
|
input_report_key(dev, BTN_TRIGGER_HAPPY3, data[2] & BIT(0));
|
||||||
input_report_key(dev, BTN_TRIGGER_HAPPY4, data[2] & 0x02);
|
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 */
|
/* start/back buttons */
|
||||||
input_report_key(dev, BTN_START, data[2] & 0x10);
|
input_report_key(dev, BTN_START, data[2] & BIT(4));
|
||||||
input_report_key(dev, BTN_SELECT, data[2] & 0x20);
|
input_report_key(dev, BTN_SELECT, data[2] & BIT(5));
|
||||||
|
|
||||||
/* stick press left/right */
|
/* stick press left/right */
|
||||||
input_report_key(dev, BTN_THUMBL, data[2] & 0x40);
|
input_report_key(dev, BTN_THUMBL, data[2] & BIT(6));
|
||||||
input_report_key(dev, BTN_THUMBR, data[2] & 0x80);
|
input_report_key(dev, BTN_THUMBR, data[2] & BIT(7));
|
||||||
|
|
||||||
/* buttons A,B,X,Y,TL,TR and MODE */
|
/* buttons A,B,X,Y,TL,TR and MODE */
|
||||||
input_report_key(dev, BTN_A, data[3] & 0x10);
|
input_report_key(dev, BTN_A, data[3] & BIT(4));
|
||||||
input_report_key(dev, BTN_B, data[3] & 0x20);
|
input_report_key(dev, BTN_B, data[3] & BIT(5));
|
||||||
input_report_key(dev, BTN_X, data[3] & 0x40);
|
input_report_key(dev, BTN_X, data[3] & BIT(6));
|
||||||
input_report_key(dev, BTN_Y, data[3] & 0x80);
|
input_report_key(dev, BTN_Y, data[3] & BIT(7));
|
||||||
input_report_key(dev, BTN_TL, data[3] & 0x01);
|
input_report_key(dev, BTN_TL, data[3] & BIT(0));
|
||||||
input_report_key(dev, BTN_TR, data[3] & 0x02);
|
input_report_key(dev, BTN_TR, data[3] & BIT(1));
|
||||||
input_report_key(dev, BTN_MODE, data[3] & 0x04);
|
input_report_key(dev, BTN_MODE, data[3] & BIT(2));
|
||||||
|
|
||||||
if (!(xpad->mapping & MAP_STICKS_TO_NULL)) {
|
if (!(xpad->mapping & MAP_STICKS_TO_NULL)) {
|
||||||
/* left stick */
|
/* left stick */
|
||||||
@ -767,6 +867,23 @@ static void xpad360_process_packet(struct usb_xpad *xpad, struct input_dev *dev,
|
|||||||
}
|
}
|
||||||
|
|
||||||
input_sync(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)
|
static void xpad_presence_work(struct work_struct *work)
|
||||||
@ -846,87 +963,154 @@ 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)
|
static void xpadone_process_packet(struct usb_xpad *xpad, u16 cmd, unsigned char *data)
|
||||||
{
|
{
|
||||||
struct input_dev *dev = xpad->dev;
|
struct input_dev *dev = xpad->dev;
|
||||||
|
bool do_sync = false;
|
||||||
|
|
||||||
/* the xbox button has its own special report */
|
/* 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
|
* The Xbox One S controller requires these reports to be
|
||||||
* acked otherwise it continues sending them forever and
|
* acked otherwise it continues sending them forever and
|
||||||
* won't report further mode button events.
|
* 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]);
|
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);
|
input_sync(dev);
|
||||||
return;
|
|
||||||
}
|
|
||||||
/* check invalid packet */
|
|
||||||
else if (data[0] != 0X20)
|
|
||||||
return;
|
|
||||||
|
|
||||||
/* menu/view buttons */
|
do_sync = true;
|
||||||
input_report_key(dev, BTN_START, data[4] & 0x04);
|
} else if (data[0] == GIP_CMD_FIRMWARE) {
|
||||||
input_report_key(dev, BTN_SELECT, data[4] & 0x08);
|
/* Some packet formats force us to use this separate to poll paddle inputs */
|
||||||
if (xpad->mapping & MAP_SELECT_BUTTON)
|
if (xpad->packet_type == PKT_XBE2_FW_5_11) {
|
||||||
input_report_key(dev, KEY_RECORD, data[22] & 0x01);
|
/* 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;
|
||||||
|
|
||||||
/* buttons A,B,X,Y */
|
/* Elite Series 2 split packet paddle bits */
|
||||||
input_report_key(dev, BTN_A, data[4] & 0x10);
|
input_report_key(dev, BTN_TRIGGER_HAPPY5, data[18] & BIT(0));
|
||||||
input_report_key(dev, BTN_B, data[4] & 0x20);
|
input_report_key(dev, BTN_TRIGGER_HAPPY6, data[18] & BIT(1));
|
||||||
input_report_key(dev, BTN_X, data[4] & 0x40);
|
input_report_key(dev, BTN_TRIGGER_HAPPY7, data[18] & BIT(2));
|
||||||
input_report_key(dev, BTN_Y, data[4] & 0x80);
|
input_report_key(dev, BTN_TRIGGER_HAPPY8, data[18] & BIT(3));
|
||||||
|
|
||||||
/* digital pad */
|
do_sync = true;
|
||||||
if (xpad->mapping & MAP_DPAD_TO_BUTTONS) {
|
}
|
||||||
/* dpad as buttons (left, right, up, down) */
|
} else if (data[0] == GIP_CMD_INPUT) { /* The main valid packet type for inputs */
|
||||||
input_report_key(dev, BTN_TRIGGER_HAPPY1, data[5] & 0x04);
|
/* menu/view buttons */
|
||||||
input_report_key(dev, BTN_TRIGGER_HAPPY2, data[5] & 0x08);
|
input_report_key(dev, BTN_START, data[4] & BIT(2));
|
||||||
input_report_key(dev, BTN_TRIGGER_HAPPY3, data[5] & 0x01);
|
input_report_key(dev, BTN_SELECT, data[4] & BIT(3));
|
||||||
input_report_key(dev, BTN_TRIGGER_HAPPY4, data[5] & 0x02);
|
if (xpad->mapping & MAP_SELECT_BUTTON)
|
||||||
} else {
|
input_report_key(dev, KEY_RECORD, data[22] & BIT(0));
|
||||||
input_report_abs(dev, ABS_HAT0X,
|
|
||||||
!!(data[5] & 0x08) - !!(data[5] & 0x04));
|
/* buttons A,B,X,Y */
|
||||||
input_report_abs(dev, ABS_HAT0Y,
|
input_report_key(dev, BTN_A, data[4] & BIT(4));
|
||||||
!!(data[5] & 0x02) - !!(data[5] & 0x01));
|
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] & 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));
|
||||||
|
input_report_abs(dev, ABS_HAT0Y,
|
||||||
|
!!(data[5] & 0x02) - !!(data[5] & 0x01));
|
||||||
|
}
|
||||||
|
|
||||||
|
/* TL/TR */
|
||||||
|
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] & BIT(6));
|
||||||
|
input_report_key(dev, BTN_THUMBR, data[5] & BIT(7));
|
||||||
|
|
||||||
|
if (!(xpad->mapping & MAP_STICKS_TO_NULL)) {
|
||||||
|
/* left stick */
|
||||||
|
input_report_abs(dev, ABS_X,
|
||||||
|
(__s16) le16_to_cpup((__le16 *)(data + 10)));
|
||||||
|
input_report_abs(dev, ABS_Y,
|
||||||
|
~(__s16) le16_to_cpup((__le16 *)(data + 12)));
|
||||||
|
|
||||||
|
/* right stick */
|
||||||
|
input_report_abs(dev, ABS_RX,
|
||||||
|
(__s16) le16_to_cpup((__le16 *)(data + 14)));
|
||||||
|
input_report_abs(dev, ABS_RY,
|
||||||
|
~(__s16) le16_to_cpup((__le16 *)(data + 16)));
|
||||||
|
}
|
||||||
|
|
||||||
|
/* triggers left/right */
|
||||||
|
if (xpad->mapping & MAP_TRIGGERS_TO_BUTTONS) {
|
||||||
|
input_report_key(dev, BTN_TL2,
|
||||||
|
(__u16) le16_to_cpup((__le16 *)(data + 6)));
|
||||||
|
input_report_key(dev, BTN_TR2,
|
||||||
|
(__u16) le16_to_cpup((__le16 *)(data + 8)));
|
||||||
|
} else {
|
||||||
|
input_report_abs(dev, ABS_Z,
|
||||||
|
(__u16) le16_to_cpup((__le16 *)(data + 6)));
|
||||||
|
input_report_abs(dev, ABS_RZ,
|
||||||
|
(__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;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* TL/TR */
|
if (do_sync)
|
||||||
input_report_key(dev, BTN_TL, data[5] & 0x10);
|
input_sync(dev);
|
||||||
input_report_key(dev, BTN_TR, data[5] & 0x20);
|
|
||||||
|
|
||||||
/* stick press left/right */
|
|
||||||
input_report_key(dev, BTN_THUMBL, data[5] & 0x40);
|
|
||||||
input_report_key(dev, BTN_THUMBR, data[5] & 0x80);
|
|
||||||
|
|
||||||
if (!(xpad->mapping & MAP_STICKS_TO_NULL)) {
|
|
||||||
/* left stick */
|
|
||||||
input_report_abs(dev, ABS_X,
|
|
||||||
(__s16) le16_to_cpup((__le16 *)(data + 10)));
|
|
||||||
input_report_abs(dev, ABS_Y,
|
|
||||||
~(__s16) le16_to_cpup((__le16 *)(data + 12)));
|
|
||||||
|
|
||||||
/* right stick */
|
|
||||||
input_report_abs(dev, ABS_RX,
|
|
||||||
(__s16) le16_to_cpup((__le16 *)(data + 14)));
|
|
||||||
input_report_abs(dev, ABS_RY,
|
|
||||||
~(__s16) le16_to_cpup((__le16 *)(data + 16)));
|
|
||||||
}
|
|
||||||
|
|
||||||
/* triggers left/right */
|
|
||||||
if (xpad->mapping & MAP_TRIGGERS_TO_BUTTONS) {
|
|
||||||
input_report_key(dev, BTN_TL2,
|
|
||||||
(__u16) le16_to_cpup((__le16 *)(data + 6)));
|
|
||||||
input_report_key(dev, BTN_TR2,
|
|
||||||
(__u16) le16_to_cpup((__le16 *)(data + 8)));
|
|
||||||
} else {
|
|
||||||
input_report_abs(dev, ABS_Z,
|
|
||||||
(__u16) le16_to_cpup((__le16 *)(data + 6)));
|
|
||||||
input_report_abs(dev, ABS_RZ,
|
|
||||||
(__u16) le16_to_cpup((__le16 *)(data + 8)));
|
|
||||||
}
|
|
||||||
|
|
||||||
input_sync(dev);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static void xpad_irq_in(struct urb *urb)
|
static void xpad_irq_in(struct urb *urb)
|
||||||
@ -1226,8 +1410,8 @@ static void xpadone_ack_mode_report(struct usb_xpad *xpad, u8 seq_num)
|
|||||||
struct xpad_output_packet *packet =
|
struct xpad_output_packet *packet =
|
||||||
&xpad->out_packets[XPAD_OUT_CMD_IDX];
|
&xpad->out_packets[XPAD_OUT_CMD_IDX];
|
||||||
static const u8 mode_report_ack[] = {
|
static const u8 mode_report_ack[] = {
|
||||||
0x01, 0x20, 0x00, 0x09, 0x00, 0x07, 0x20, 0x02,
|
GIP_CMD_ACK, GIP_OPT_INTERNAL, GIP_SEQ0, GIP_PL_LEN(9),
|
||||||
0x00, 0x00, 0x00, 0x00, 0x00
|
0x00, GIP_CMD_VIRTUAL_KEY, GIP_OPT_INTERNAL, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00
|
||||||
};
|
};
|
||||||
|
|
||||||
spin_lock_irqsave(&xpad->odata_lock, flags);
|
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;
|
break;
|
||||||
|
|
||||||
case XTYPE_XBOXONE:
|
case XTYPE_XBOXONE:
|
||||||
packet->data[0] = 0x09; /* activate rumble */
|
packet->data[0] = GIP_CMD_RUMBLE; /* activate rumble */
|
||||||
packet->data[1] = 0x00;
|
packet->data[1] = 0x00;
|
||||||
packet->data[2] = xpad->odata_serial++;
|
packet->data[2] = xpad->odata_serial++;
|
||||||
packet->data[3] = 0x09;
|
packet->data[3] = GIP_PL_LEN(9);
|
||||||
packet->data[4] = 0x00;
|
packet->data[4] = 0x00;
|
||||||
packet->data[5] = 0x0F;
|
packet->data[5] = GIP_MOTOR_ALL;
|
||||||
packet->data[6] = 0x00;
|
packet->data[6] = 0x00; /* left trigger */
|
||||||
packet->data[7] = 0x00;
|
packet->data[7] = 0x00; /* right trigger */
|
||||||
packet->data[8] = strong / 512; /* left actuator */
|
packet->data[8] = strong / 512; /* left actuator */
|
||||||
packet->data[9] = weak / 512; /* right actuator */
|
packet->data[9] = weak / 512; /* right actuator */
|
||||||
packet->data[10] = 0xFF; /* on period */
|
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 */
|
case ABS_HAT0Y: /* the d-pad (only if dpad is mapped to axes */
|
||||||
input_set_abs_params(input_dev, abs, -1, 1, 0, 0);
|
input_set_abs_params(input_dev, abs, -1, 1, 0, 0);
|
||||||
break;
|
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:
|
default:
|
||||||
input_set_abs_params(input_dev, abs, 0, 0, 0, 0);
|
input_set_abs_params(input_dev, abs, 0, 0, 0, 0);
|
||||||
break;
|
break;
|
||||||
@ -1693,6 +1880,12 @@ static int xpad_init_input(struct usb_xpad *xpad)
|
|||||||
xpad_btn_pad[i]);
|
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
|
* This should be a simple else block. However historically
|
||||||
* xbox360w has mapped DPAD to buttons while xbox360 did not. This
|
* 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]);
|
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);
|
error = xpad_init_ff(xpad);
|
||||||
if (error)
|
if (error)
|
||||||
goto err_free_input;
|
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->mapping = xpad_device[i].mapping;
|
||||||
xpad->xtype = xpad_device[i].xtype;
|
xpad->xtype = xpad_device[i].xtype;
|
||||||
xpad->name = xpad_device[i].name;
|
xpad->name = xpad_device[i].name;
|
||||||
|
xpad->packet_type = PKT_XB;
|
||||||
INIT_WORK(&xpad->work, xpad_presence_work);
|
INIT_WORK(&xpad->work, xpad_presence_work);
|
||||||
|
|
||||||
if (xpad->xtype == XTYPE_UNKNOWN) {
|
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);
|
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) {
|
if (xpad->xtype == XTYPE_XBOX360W) {
|
||||||
/*
|
/*
|
||||||
* Submit the int URB immediately rather than waiting for open
|
* Submit the int URB immediately rather than waiting for open
|
||||||
@ -1972,7 +2202,6 @@ static struct usb_driver xpad_driver = {
|
|||||||
.disconnect = xpad_disconnect,
|
.disconnect = xpad_disconnect,
|
||||||
.suspend = xpad_suspend,
|
.suspend = xpad_suspend,
|
||||||
.resume = xpad_resume,
|
.resume = xpad_resume,
|
||||||
.reset_resume = xpad_resume,
|
|
||||||
.id_table = xpad_table,
|
.id_table = xpad_table,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -28,9 +28,6 @@
|
|||||||
* coder :-(
|
* coder :-(
|
||||||
*/
|
*/
|
||||||
|
|
||||||
/*
|
|
||||||
*/
|
|
||||||
|
|
||||||
#include <linux/kernel.h>
|
#include <linux/kernel.h>
|
||||||
#include <linux/module.h>
|
#include <linux/module.h>
|
||||||
#include <linux/slab.h>
|
#include <linux/slab.h>
|
||||||
|
@ -40,6 +40,9 @@ config KEYBOARD_ADP5520
|
|||||||
config KEYBOARD_ADP5588
|
config KEYBOARD_ADP5588
|
||||||
tristate "ADP5588/87 I2C QWERTY Keypad and IO Expander"
|
tristate "ADP5588/87 I2C QWERTY Keypad and IO Expander"
|
||||||
depends on I2C
|
depends on I2C
|
||||||
|
select GPIOLIB
|
||||||
|
select GPIOLIB_IRQCHIP
|
||||||
|
select INPUT_MATRIXKMAP
|
||||||
help
|
help
|
||||||
Say Y here if you want to use a ADP5588/87 attached to your
|
Say Y here if you want to use a ADP5588/87 attached to your
|
||||||
system I2C bus.
|
system I2C bus.
|
||||||
@ -186,7 +189,7 @@ config KEYBOARD_QT2160
|
|||||||
|
|
||||||
config KEYBOARD_CLPS711X
|
config KEYBOARD_CLPS711X
|
||||||
tristate "CLPS711X Keypad support"
|
tristate "CLPS711X Keypad support"
|
||||||
depends on OF_GPIO && (ARCH_CLPS711X || COMPILE_TEST)
|
depends on ARCH_CLPS711X || COMPILE_TEST
|
||||||
select INPUT_MATRIXKMAP
|
select INPUT_MATRIXKMAP
|
||||||
help
|
help
|
||||||
Say Y here to enable the matrix keypad on the Cirrus Logic
|
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
|
To compile this driver as a module, choose M here; the
|
||||||
module will be called opencores-kbd.
|
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
|
config KEYBOARD_PXA27x
|
||||||
tristate "PXA27x/PXA3xx keypad support"
|
tristate "PXA27x/PXA3xx keypad support"
|
||||||
depends on PXA27x || PXA3xx || ARCH_MMP
|
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_OMAP) += omap-keypad.o
|
||||||
obj-$(CONFIG_KEYBOARD_OMAP4) += omap4-keypad.o
|
obj-$(CONFIG_KEYBOARD_OMAP4) += omap4-keypad.o
|
||||||
obj-$(CONFIG_KEYBOARD_OPENCORES) += opencores-kbd.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_PMIC8XXX) += pmic8xxx-keypad.o
|
||||||
obj-$(CONFIG_KEYBOARD_PXA27x) += pxa27x_keypad.o
|
obj-$(CONFIG_KEYBOARD_PXA27x) += pxa27x_keypad.o
|
||||||
obj-$(CONFIG_KEYBOARD_PXA930_ROTARY) += pxa930_rotary.o
|
obj-$(CONFIG_KEYBOARD_PXA930_ROTARY) += pxa930_rotary.o
|
||||||
|
@ -8,27 +8,163 @@
|
|||||||
* Copyright (C) 2008-2010 Analog Devices Inc.
|
* Copyright (C) 2008-2010 Analog Devices Inc.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
#include <linux/bits.h>
|
||||||
#include <linux/delay.h>
|
#include <linux/delay.h>
|
||||||
#include <linux/errno.h>
|
#include <linux/errno.h>
|
||||||
|
#include <linux/gpio/consumer.h>
|
||||||
#include <linux/gpio/driver.h>
|
#include <linux/gpio/driver.h>
|
||||||
#include <linux/i2c.h>
|
#include <linux/i2c.h>
|
||||||
#include <linux/input.h>
|
#include <linux/input.h>
|
||||||
|
#include <linux/input/matrix_keypad.h>
|
||||||
#include <linux/interrupt.h>
|
#include <linux/interrupt.h>
|
||||||
#include <linux/irq.h>
|
#include <linux/irq.h>
|
||||||
#include <linux/ktime.h>
|
#include <linux/ktime.h>
|
||||||
#include <linux/module.h>
|
#include <linux/module.h>
|
||||||
|
#include <linux/mod_devicetable.h>
|
||||||
|
#include <linux/pinctrl/pinconf-generic.h>
|
||||||
#include <linux/platform_device.h>
|
#include <linux/platform_device.h>
|
||||||
#include <linux/pm.h>
|
#include <linux/pm.h>
|
||||||
|
#include <linux/regulator/consumer.h>
|
||||||
#include <linux/slab.h>
|
#include <linux/slab.h>
|
||||||
#include <linux/timekeeping.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 */
|
/* Key Event Register xy */
|
||||||
#define KEY_EV_PRESSED (1 << 7)
|
#define KEY_EV_PRESSED BIT(7)
|
||||||
#define KEY_EV_MASK (0x7F)
|
#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
|
#define KEYP_MAX_EVENT 10
|
||||||
|
|
||||||
@ -40,21 +176,27 @@
|
|||||||
#define WA_DELAYED_READOUT_REVID(rev) ((rev) < 4)
|
#define WA_DELAYED_READOUT_REVID(rev) ((rev) < 4)
|
||||||
#define WA_DELAYED_READOUT_TIME 25
|
#define WA_DELAYED_READOUT_TIME 25
|
||||||
|
|
||||||
|
#define ADP5588_INVALID_HWIRQ (~0UL)
|
||||||
|
|
||||||
struct adp5588_kpad {
|
struct adp5588_kpad {
|
||||||
struct i2c_client *client;
|
struct i2c_client *client;
|
||||||
struct input_dev *input;
|
struct input_dev *input;
|
||||||
ktime_t irq_time;
|
ktime_t irq_time;
|
||||||
unsigned long delay;
|
unsigned long delay;
|
||||||
|
u32 row_shift;
|
||||||
|
u32 rows;
|
||||||
|
u32 cols;
|
||||||
|
u32 unlock_keys[2];
|
||||||
|
int nkeys_unlock;
|
||||||
unsigned short keycode[ADP5588_KEYMAPSIZE];
|
unsigned short keycode[ADP5588_KEYMAPSIZE];
|
||||||
const struct adp5588_gpi_map *gpimap;
|
|
||||||
unsigned short gpimapsize;
|
|
||||||
#ifdef CONFIG_GPIOLIB
|
|
||||||
unsigned char gpiomap[ADP5588_MAXGPIO];
|
unsigned char gpiomap[ADP5588_MAXGPIO];
|
||||||
struct gpio_chip gc;
|
struct gpio_chip gc;
|
||||||
struct mutex gpio_lock; /* Protect cached dir, dat_out */
|
struct mutex gpio_lock; /* Protect cached dir, dat_out */
|
||||||
u8 dat_out[3];
|
u8 dat_out[3];
|
||||||
u8 dir[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)
|
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);
|
return i2c_smbus_write_byte_data(client, reg, val);
|
||||||
}
|
}
|
||||||
|
|
||||||
#ifdef CONFIG_GPIOLIB
|
static int adp5588_gpio_get_value(struct gpio_chip *chip, unsigned int off)
|
||||||
static int adp5588_gpio_get_value(struct gpio_chip *chip, unsigned off)
|
|
||||||
{
|
{
|
||||||
struct adp5588_kpad *kpad = gpiochip_get_data(chip);
|
struct adp5588_kpad *kpad = gpiochip_get_data(chip);
|
||||||
unsigned int bank = ADP5588_BANK(kpad->gpiomap[off]);
|
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,
|
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);
|
struct adp5588_kpad *kpad = gpiochip_get_data(chip);
|
||||||
unsigned int bank = ADP5588_BANK(kpad->gpiomap[off]);
|
unsigned int bank = ADP5588_BANK(kpad->gpiomap[off]);
|
||||||
@ -106,13 +247,47 @@ static void adp5588_gpio_set_value(struct gpio_chip *chip,
|
|||||||
else
|
else
|
||||||
kpad->dat_out[bank] &= ~bit;
|
kpad->dat_out[bank] &= ~bit;
|
||||||
|
|
||||||
adp5588_write(kpad->client, GPIO_DAT_OUT1 + bank,
|
adp5588_write(kpad->client, GPIO_DAT_OUT1 + bank, kpad->dat_out[bank]);
|
||||||
kpad->dat_out[bank]);
|
|
||||||
|
|
||||||
mutex_unlock(&kpad->gpio_lock);
|
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);
|
struct adp5588_kpad *kpad = gpiochip_get_data(chip);
|
||||||
unsigned int bank = ADP5588_BANK(kpad->gpiomap[off]);
|
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,
|
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);
|
struct adp5588_kpad *kpad = gpiochip_get_data(chip);
|
||||||
unsigned int bank = ADP5588_BANK(kpad->gpiomap[off]);
|
unsigned int bank = ADP5588_BANK(kpad->gpiomap[off]);
|
||||||
@ -147,17 +322,19 @@ static int adp5588_gpio_direction_output(struct gpio_chip *chip,
|
|||||||
kpad->dat_out[bank] &= ~bit;
|
kpad->dat_out[bank] &= ~bit;
|
||||||
|
|
||||||
ret = adp5588_write(kpad->client, GPIO_DAT_OUT1 + bank,
|
ret = adp5588_write(kpad->client, GPIO_DAT_OUT1 + bank,
|
||||||
kpad->dat_out[bank]);
|
kpad->dat_out[bank]);
|
||||||
ret |= adp5588_write(kpad->client, GPIO_DIR1 + bank,
|
if (ret)
|
||||||
kpad->dir[bank]);
|
goto out_unlock;
|
||||||
|
|
||||||
|
ret = adp5588_write(kpad->client, GPIO_DIR1 + bank, kpad->dir[bank]);
|
||||||
|
|
||||||
|
out_unlock:
|
||||||
mutex_unlock(&kpad->gpio_lock);
|
mutex_unlock(&kpad->gpio_lock);
|
||||||
|
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
static int adp5588_build_gpiomap(struct adp5588_kpad *kpad,
|
static int adp5588_build_gpiomap(struct adp5588_kpad *kpad)
|
||||||
const struct adp5588_kpad_platform_data *pdata)
|
|
||||||
{
|
{
|
||||||
bool pin_used[ADP5588_MAXGPIO];
|
bool pin_used[ADP5588_MAXGPIO];
|
||||||
int n_unused = 0;
|
int n_unused = 0;
|
||||||
@ -165,15 +342,12 @@ static int adp5588_build_gpiomap(struct adp5588_kpad *kpad,
|
|||||||
|
|
||||||
memset(pin_used, 0, sizeof(pin_used));
|
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;
|
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;
|
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++)
|
for (i = 0; i < ADP5588_MAXGPIO; i++)
|
||||||
if (!pin_used[i])
|
if (!pin_used[i])
|
||||||
kpad->gpiomap[n_unused++] = i;
|
kpad->gpiomap[n_unused++] = i;
|
||||||
@ -181,47 +355,101 @@ static int adp5588_build_gpiomap(struct adp5588_kpad *kpad,
|
|||||||
return n_unused;
|
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 gpio_chip *gc = irq_data_get_irq_chip_data(d);
|
||||||
struct device *dev = &kpad->client->dev;
|
struct adp5588_kpad *kpad = gpiochip_get_data(gc);
|
||||||
const struct adp5588_kpad_platform_data *pdata = dev_get_platdata(dev);
|
|
||||||
const struct adp5588_gpio_platform_data *gpio_data = pdata->gpio_data;
|
|
||||||
int error;
|
|
||||||
|
|
||||||
error = gpio_data->teardown(kpad->client,
|
mutex_lock(&kpad->gpio_lock);
|
||||||
kpad->gc.base, kpad->gc.ngpio,
|
|
||||||
gpio_data->context);
|
|
||||||
if (error)
|
|
||||||
dev_warn(&kpad->client->dev, "teardown failed %d\n", error);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
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)
|
static int adp5588_gpio_add(struct adp5588_kpad *kpad)
|
||||||
{
|
{
|
||||||
struct device *dev = &kpad->client->dev;
|
struct device *dev = &kpad->client->dev;
|
||||||
const struct adp5588_kpad_platform_data *pdata = dev_get_platdata(dev);
|
struct gpio_irq_chip *girq;
|
||||||
const struct adp5588_gpio_platform_data *gpio_data = pdata->gpio_data;
|
|
||||||
int i, error;
|
int i, error;
|
||||||
|
|
||||||
if (!gpio_data)
|
kpad->gc.ngpio = adp5588_build_gpiomap(kpad);
|
||||||
return 0;
|
|
||||||
|
|
||||||
kpad->gc.ngpio = adp5588_build_gpiomap(kpad, pdata);
|
|
||||||
if (kpad->gc.ngpio == 0) {
|
if (kpad->gc.ngpio == 0) {
|
||||||
dev_info(dev, "No unused gpios left to export\n");
|
dev_info(dev, "No unused gpios left to export\n");
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
kpad->gc.parent = &kpad->client->dev;
|
||||||
kpad->gc.direction_input = adp5588_gpio_direction_input;
|
kpad->gc.direction_input = adp5588_gpio_direction_input;
|
||||||
kpad->gc.direction_output = adp5588_gpio_direction_output;
|
kpad->gc.direction_output = adp5588_gpio_direction_output;
|
||||||
kpad->gc.get = adp5588_gpio_get_value;
|
kpad->gc.get = adp5588_gpio_get_value;
|
||||||
kpad->gc.set = adp5588_gpio_set_value;
|
kpad->gc.set = adp5588_gpio_set_value;
|
||||||
|
kpad->gc.set_config = adp5588_gpio_set_config;
|
||||||
kpad->gc.can_sleep = 1;
|
kpad->gc.can_sleep = 1;
|
||||||
|
|
||||||
kpad->gc.base = gpio_data->gpio_start;
|
kpad->gc.base = -1;
|
||||||
kpad->gc.label = kpad->client->name;
|
kpad->gc.label = kpad->client->name;
|
||||||
kpad->gc.owner = THIS_MODULE;
|
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);
|
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,
|
kpad->dat_out[i] = adp5588_read(kpad->client,
|
||||||
GPIO_DAT_OUT1 + i);
|
GPIO_DAT_OUT1 + i);
|
||||||
kpad->dir[i] = adp5588_read(kpad->client, GPIO_DIR1 + i);
|
kpad->dir[i] = adp5588_read(kpad->client, GPIO_DIR1 + i);
|
||||||
}
|
kpad->pull_dis[i] = adp5588_read(kpad->client, GPIO_PULL1 + 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);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
#else
|
static unsigned long adp5588_gpiomap_get_hwirq(struct device *dev,
|
||||||
static inline int adp5588_gpio_add(struct adp5588_kpad *kpad)
|
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)
|
static void adp5588_report_events(struct adp5588_kpad *kpad, int ev_cnt)
|
||||||
{
|
{
|
||||||
int i, j;
|
int i;
|
||||||
|
|
||||||
for (i = 0; i < ev_cnt; 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_val = key & KEY_EV_MASK;
|
||||||
|
int key_press = key & KEY_EV_PRESSED;
|
||||||
|
|
||||||
if (key_val >= GPI_PIN_BASE && key_val <= GPI_PIN_END) {
|
if (key_val >= GPI_PIN_BASE && key_val <= GPI_PIN_END) {
|
||||||
for (j = 0; j < kpad->gpimapsize; j++) {
|
/* gpio line used as IRQ source */
|
||||||
if (key_val == kpad->gpimap[j].pin) {
|
adp5588_gpio_irq_handle(kpad, key_val, key_press);
|
||||||
input_report_switch(kpad->input,
|
|
||||||
kpad->gpimap[j].sw_evt,
|
|
||||||
key & KEY_EV_PRESSED);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} else {
|
} 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,
|
input_report_key(kpad->input,
|
||||||
kpad->keycode[key_val - 1],
|
kpad->keycode[code], key_press);
|
||||||
key & KEY_EV_PRESSED);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -335,176 +596,145 @@ static irqreturn_t adp5588_thread_irq(int irq, void *handle)
|
|||||||
return IRQ_HANDLED;
|
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 =
|
struct i2c_client *client = kpad->client;
|
||||||
dev_get_platdata(&client->dev);
|
|
||||||
const struct adp5588_gpio_platform_data *gpio_data = pdata->gpio_data;
|
|
||||||
int i, ret;
|
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_GPIO1, KP_SEL(kpad->rows));
|
||||||
ret |= adp5588_write(client, KP_GPIO2, KP_SEL(pdata->cols) & 0xFF);
|
if (ret)
|
||||||
ret |= adp5588_write(client, KP_GPIO3, KP_SEL(pdata->cols) >> 8);
|
|
||||||
|
|
||||||
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);
|
|
||||||
}
|
|
||||||
|
|
||||||
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 (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);
|
|
||||||
}
|
|
||||||
|
|
||||||
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,
|
|
||||||
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");
|
|
||||||
return ret;
|
return ret;
|
||||||
|
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (kpad->nkeys_unlock) {
|
||||||
|
ret = adp5588_write(client, KEY_LCK_EC_STAT, ADP5588_K_LCK_EN);
|
||||||
|
if (ret)
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
for (i = 0; i < KEYP_MAX_EVENT; i++) {
|
||||||
|
ret = adp5588_read(client, KEY_EVENTA);
|
||||||
|
if (ret)
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
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 */
|
||||||
|
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;
|
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);
|
regulator_disable(reg);
|
||||||
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;
|
|
||||||
}
|
|
||||||
|
|
||||||
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,
|
static int adp5588_probe(struct i2c_client *client,
|
||||||
const struct i2c_device_id *id)
|
const struct i2c_device_id *id)
|
||||||
{
|
{
|
||||||
struct adp5588_kpad *kpad;
|
struct adp5588_kpad *kpad;
|
||||||
const struct adp5588_kpad_platform_data *pdata =
|
|
||||||
dev_get_platdata(&client->dev);
|
|
||||||
struct input_dev *input;
|
struct input_dev *input;
|
||||||
|
struct gpio_desc *gpio;
|
||||||
|
struct regulator *vcc;
|
||||||
unsigned int revid;
|
unsigned int revid;
|
||||||
int ret, i;
|
int ret;
|
||||||
int error;
|
int error;
|
||||||
|
|
||||||
if (!i2c_check_functionality(client->adapter,
|
if (!i2c_check_functionality(client->adapter,
|
||||||
I2C_FUNC_SMBUS_BYTE_DATA)) {
|
I2C_FUNC_SMBUS_BYTE_DATA)) {
|
||||||
dev_err(&client->dev, "SMBUS Byte Data not Supported\n");
|
dev_err(&client->dev, "SMBUS Byte Data not Supported\n");
|
||||||
return -EIO;
|
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);
|
kpad = devm_kzalloc(&client->dev, sizeof(*kpad), GFP_KERNEL);
|
||||||
if (!kpad)
|
if (!kpad)
|
||||||
return -ENOMEM;
|
return -ENOMEM;
|
||||||
@ -516,11 +746,38 @@ static int adp5588_probe(struct i2c_client *client,
|
|||||||
kpad->client = client;
|
kpad->client = client;
|
||||||
kpad->input = input;
|
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);
|
ret = adp5588_read(client, DEV_ID);
|
||||||
if (ret < 0)
|
if (ret < 0)
|
||||||
return ret;
|
return ret;
|
||||||
|
|
||||||
revid = (u8) ret & ADP5588_DEVICE_ID_MASK;
|
revid = ret & ADP5588_DEVICE_ID_MASK;
|
||||||
if (WA_DELAYED_READOUT_REVID(revid))
|
if (WA_DELAYED_READOUT_REVID(revid))
|
||||||
kpad->delay = msecs_to_jiffies(WA_DELAYED_READOUT_TIME);
|
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.product = 0x0001;
|
||||||
input->id.version = revid;
|
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);
|
error = input_register_device(input);
|
||||||
if (error) {
|
if (error) {
|
||||||
dev_err(&client->dev, "unable to register input device: %d\n",
|
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;
|
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,
|
error = devm_request_threaded_irq(&client->dev, client->irq,
|
||||||
adp5588_hard_irq, adp5588_thread_irq,
|
adp5588_hard_irq, adp5588_thread_irq,
|
||||||
IRQF_TRIGGER_FALLING | IRQF_ONESHOT,
|
IRQF_TRIGGER_FALLING | IRQF_ONESHOT,
|
||||||
@ -577,17 +816,6 @@ static int adp5588_probe(struct i2c_client *client,
|
|||||||
return error;
|
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);
|
dev_info(&client->dev, "Rev.%d keypad, irq %d\n", revid, client->irq);
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
@ -599,7 +827,7 @@ static void adp5588_remove(struct i2c_client *client)
|
|||||||
/* all resources will be freed by devm */
|
/* 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);
|
struct i2c_client *client = to_i2c_client(dev);
|
||||||
|
|
||||||
@ -608,7 +836,7 @@ static int __maybe_unused adp5588_suspend(struct device *dev)
|
|||||||
return 0;
|
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);
|
struct i2c_client *client = to_i2c_client(dev);
|
||||||
|
|
||||||
@ -617,7 +845,7 @@ static int __maybe_unused adp5588_resume(struct device *dev)
|
|||||||
return 0;
|
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[] = {
|
static const struct i2c_device_id adp5588_id[] = {
|
||||||
{ "adp5588-keys", 0 },
|
{ "adp5588-keys", 0 },
|
||||||
@ -626,10 +854,18 @@ static const struct i2c_device_id adp5588_id[] = {
|
|||||||
};
|
};
|
||||||
MODULE_DEVICE_TABLE(i2c, 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 = {
|
static struct i2c_driver adp5588_driver = {
|
||||||
.driver = {
|
.driver = {
|
||||||
.name = KBUILD_MODNAME,
|
.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,
|
.probe = adp5588_probe,
|
||||||
.remove = adp5588_remove,
|
.remove = adp5588_remove,
|
||||||
|
@ -10,9 +10,6 @@
|
|||||||
* Amiga keyboard driver for Linux/m68k
|
* Amiga keyboard driver for Linux/m68k
|
||||||
*/
|
*/
|
||||||
|
|
||||||
/*
|
|
||||||
*/
|
|
||||||
|
|
||||||
#include <linux/module.h>
|
#include <linux/module.h>
|
||||||
#include <linux/init.h>
|
#include <linux/init.h>
|
||||||
#include <linux/input.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
|
* message.type = 0x1020, message.length = 0x006e
|
||||||
*
|
*
|
||||||
* @unknown1: unknown
|
* @unknown1: unknown
|
||||||
@ -311,7 +311,7 @@ struct message {
|
|||||||
struct command_protocol_mt_init init_mt_command;
|
struct command_protocol_mt_init init_mt_command;
|
||||||
struct command_protocol_capsl capsl_command;
|
struct command_protocol_capsl capsl_command;
|
||||||
struct command_protocol_bl bl_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.
|
* This driver only deals with handing key events off to the input layer.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
/*
|
|
||||||
*/
|
|
||||||
|
|
||||||
#include <linux/module.h>
|
#include <linux/module.h>
|
||||||
#include <linux/init.h>
|
#include <linux/init.h>
|
||||||
#include <linux/input.h>
|
#include <linux/input.h>
|
||||||
|
@ -323,11 +323,13 @@ static umode_t atkbd_attr_is_visible(struct kobject *kobj,
|
|||||||
return attr->mode;
|
return attr->mode;
|
||||||
}
|
}
|
||||||
|
|
||||||
static struct attribute_group atkbd_attribute_group = {
|
static const struct attribute_group atkbd_attribute_group = {
|
||||||
.attrs = atkbd_attributes,
|
.attrs = atkbd_attributes,
|
||||||
.is_visible = atkbd_attr_is_visible,
|
.is_visible = atkbd_attr_is_visible,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
__ATTRIBUTE_GROUPS(atkbd_attribute);
|
||||||
|
|
||||||
static const unsigned int xl_table[] = {
|
static const unsigned int xl_table[] = {
|
||||||
ATKBD_RET_BAT, ATKBD_RET_ERR, ATKBD_RET_ACK,
|
ATKBD_RET_BAT, ATKBD_RET_ERR, ATKBD_RET_ACK,
|
||||||
ATKBD_RET_NAK, ATKBD_RET_HANJA, ATKBD_RET_HANGEUL,
|
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);
|
struct atkbd *atkbd = serio_get_drvdata(serio);
|
||||||
|
|
||||||
sysfs_remove_group(&serio->dev.kobj, &atkbd_attribute_group);
|
|
||||||
|
|
||||||
atkbd_disable(atkbd);
|
atkbd_disable(atkbd);
|
||||||
|
|
||||||
input_unregister_device(atkbd->dev);
|
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_keycode_table(atkbd);
|
||||||
atkbd_set_device_attrs(atkbd);
|
atkbd_set_device_attrs(atkbd);
|
||||||
|
|
||||||
err = sysfs_create_group(&serio->dev.kobj, &atkbd_attribute_group);
|
|
||||||
if (err)
|
|
||||||
goto fail3;
|
|
||||||
|
|
||||||
atkbd_enable(atkbd);
|
atkbd_enable(atkbd);
|
||||||
if (serio->write)
|
if (serio->write)
|
||||||
atkbd_activate(atkbd);
|
atkbd_activate(atkbd);
|
||||||
|
|
||||||
err = input_register_device(atkbd->dev);
|
err = input_register_device(atkbd->dev);
|
||||||
if (err)
|
if (err)
|
||||||
goto fail4;
|
goto fail3;
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
|
|
||||||
fail4: sysfs_remove_group(&serio->dev.kobj, &atkbd_attribute_group);
|
|
||||||
fail3: serio_close(serio);
|
fail3: serio_close(serio);
|
||||||
fail2: serio_set_drvdata(serio, NULL);
|
fail2: serio_set_drvdata(serio, NULL);
|
||||||
fail1: input_free_device(dev);
|
fail1: input_free_device(dev);
|
||||||
@ -1378,7 +1373,8 @@ MODULE_DEVICE_TABLE(serio, atkbd_serio_ids);
|
|||||||
|
|
||||||
static struct serio_driver atkbd_drv = {
|
static struct serio_driver atkbd_drv = {
|
||||||
.driver = {
|
.driver = {
|
||||||
.name = "atkbd",
|
.name = "atkbd",
|
||||||
|
.dev_groups = atkbd_attribute_groups,
|
||||||
},
|
},
|
||||||
.description = DRIVER_DESC,
|
.description = DRIVER_DESC,
|
||||||
.id_table = atkbd_serio_ids,
|
.id_table = atkbd_serio_ids,
|
||||||
|
@ -6,9 +6,11 @@
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
#include <linux/input.h>
|
#include <linux/input.h>
|
||||||
|
#include <linux/mod_devicetable.h>
|
||||||
#include <linux/module.h>
|
#include <linux/module.h>
|
||||||
#include <linux/of_gpio.h>
|
#include <linux/gpio/consumer.h>
|
||||||
#include <linux/platform_device.h>
|
#include <linux/platform_device.h>
|
||||||
|
#include <linux/property.h>
|
||||||
#include <linux/regmap.h>
|
#include <linux/regmap.h>
|
||||||
#include <linux/sched.h>
|
#include <linux/sched.h>
|
||||||
#include <linux/input/matrix_keypad.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 clps711x_keypad_data *priv;
|
||||||
struct device *dev = &pdev->dev;
|
struct device *dev = &pdev->dev;
|
||||||
struct device_node *np = dev->of_node;
|
|
||||||
struct input_dev *input;
|
struct input_dev *input;
|
||||||
u32 poll_interval;
|
u32 poll_interval;
|
||||||
int i, err;
|
int i, err;
|
||||||
@ -95,11 +96,11 @@ static int clps711x_keypad_probe(struct platform_device *pdev)
|
|||||||
if (!priv)
|
if (!priv)
|
||||||
return -ENOMEM;
|
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))
|
if (IS_ERR(priv->syscon))
|
||||||
return PTR_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)
|
if (priv->row_count < 1)
|
||||||
return -EINVAL;
|
return -EINVAL;
|
||||||
|
|
||||||
@ -119,7 +120,7 @@ static int clps711x_keypad_probe(struct platform_device *pdev)
|
|||||||
return PTR_ERR(data->desc);
|
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)
|
if (err)
|
||||||
return err;
|
return err;
|
||||||
|
|
||||||
@ -143,7 +144,7 @@ static int clps711x_keypad_probe(struct platform_device *pdev)
|
|||||||
return err;
|
return err;
|
||||||
|
|
||||||
input_set_capability(input, EV_MSC, MSC_SCAN);
|
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_bit(EV_REP, input->evbit);
|
||||||
|
|
||||||
/* Set all columns to low */
|
/* Set all columns to low */
|
||||||
|
@ -23,6 +23,7 @@
|
|||||||
#include <linux/interrupt.h>
|
#include <linux/interrupt.h>
|
||||||
#include <linux/clk.h>
|
#include <linux/clk.h>
|
||||||
#include <linux/io.h>
|
#include <linux/io.h>
|
||||||
|
#include <linux/input.h>
|
||||||
#include <linux/input/matrix_keypad.h>
|
#include <linux/input/matrix_keypad.h>
|
||||||
#include <linux/slab.h>
|
#include <linux/slab.h>
|
||||||
#include <linux/soc/cirrus/ep93xx.h>
|
#include <linux/soc/cirrus/ep93xx.h>
|
||||||
|
@ -7,6 +7,7 @@
|
|||||||
#include <linux/delay.h>
|
#include <linux/delay.h>
|
||||||
#include <linux/device.h>
|
#include <linux/device.h>
|
||||||
#include <linux/err.h>
|
#include <linux/err.h>
|
||||||
|
#include <linux/input.h>
|
||||||
#include <linux/input/matrix_keypad.h>
|
#include <linux/input/matrix_keypad.h>
|
||||||
#include <linux/interrupt.h>
|
#include <linux/interrupt.h>
|
||||||
#include <linux/io.h>
|
#include <linux/io.h>
|
||||||
|
@ -46,9 +46,6 @@
|
|||||||
* http://www.vt100.net/manx/details?pn=EK-104AA-TM-001;id=21;cp=1
|
* http://www.vt100.net/manx/details?pn=EK-104AA-TM-001;id=21;cp=1
|
||||||
*/
|
*/
|
||||||
|
|
||||||
/*
|
|
||||||
*/
|
|
||||||
|
|
||||||
#include <linux/delay.h>
|
#include <linux/delay.h>
|
||||||
#include <linux/slab.h>
|
#include <linux/slab.h>
|
||||||
#include <linux/module.h>
|
#include <linux/module.h>
|
||||||
@ -359,18 +356,18 @@ static void lkkbd_detection_done(struct lkkbd *lk)
|
|||||||
*/
|
*/
|
||||||
switch (lk->id[4]) {
|
switch (lk->id[4]) {
|
||||||
case 1:
|
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)
|
if (lk201_compose_is_alt)
|
||||||
lk->keycode[0xb1] = KEY_LEFTALT;
|
lk->keycode[0xb1] = KEY_LEFTALT;
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case 2:
|
case 2:
|
||||||
strlcpy(lk->name, "DEC LK401 keyboard", sizeof(lk->name));
|
strscpy(lk->name, "DEC LK401 keyboard", sizeof(lk->name));
|
||||||
break;
|
break;
|
||||||
|
|
||||||
default:
|
default:
|
||||||
strlcpy(lk->name, "Unknown DEC keyboard", sizeof(lk->name));
|
strscpy(lk->name, "Unknown DEC keyboard", sizeof(lk->name));
|
||||||
printk(KERN_ERR
|
printk(KERN_ERR
|
||||||
"lkkbd: keyboard on %s is unknown, please report to "
|
"lkkbd: keyboard on %s is unknown, please report to "
|
||||||
"Jan-Benedict Glaw <jbglaw@lug-owl.de>\n", lk->phys);
|
"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;
|
lk->ctrlclick_volume = ctrlclick_volume;
|
||||||
memcpy(lk->keycode, lkkbd_keycode, sizeof(lk->keycode));
|
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);
|
snprintf(lk->phys, sizeof(lk->phys), "%s/input0", serio->phys);
|
||||||
|
|
||||||
input_dev->name = lk->name;
|
input_dev->name = lk->name;
|
||||||
|
@ -4,13 +4,13 @@
|
|||||||
* Copyright (C) 2012 Wolfram Sang, Pengutronix <kernel@pengutronix.de>
|
* 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/i2c.h>
|
||||||
#include <linux/interrupt.h>
|
#include <linux/input.h>
|
||||||
#include <linux/input/matrix_keypad.h>
|
#include <linux/input/matrix_keypad.h>
|
||||||
#include <linux/input/lm8333.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_FIFO_READ 0x20
|
||||||
#define LM8333_DEBOUNCE 0x22
|
#define LM8333_DEBOUNCE 0x22
|
||||||
|
@ -9,6 +9,7 @@
|
|||||||
|
|
||||||
#include <linux/types.h>
|
#include <linux/types.h>
|
||||||
#include <linux/delay.h>
|
#include <linux/delay.h>
|
||||||
|
#include <linux/gpio/consumer.h>
|
||||||
#include <linux/platform_device.h>
|
#include <linux/platform_device.h>
|
||||||
#include <linux/input.h>
|
#include <linux/input.h>
|
||||||
#include <linux/irq.h>
|
#include <linux/irq.h>
|
||||||
@ -416,9 +417,9 @@ matrix_keypad_parse_dt(struct device *dev)
|
|||||||
return ERR_PTR(-ENOMEM);
|
return ERR_PTR(-ENOMEM);
|
||||||
}
|
}
|
||||||
|
|
||||||
pdata->num_row_gpios = nrow = of_gpio_named_count(np, "row-gpios");
|
pdata->num_row_gpios = nrow = gpiod_count(dev, "row");
|
||||||
pdata->num_col_gpios = ncol = of_gpio_named_count(np, "col-gpios");
|
pdata->num_col_gpios = ncol = gpiod_count(dev, "col");
|
||||||
if (nrow <= 0 || ncol <= 0) {
|
if (nrow < 0 || ncol < 0) {
|
||||||
dev_err(dev, "number of keypad rows/columns not specified\n");
|
dev_err(dev, "number of keypad rows/columns not specified\n");
|
||||||
return ERR_PTR(-EINVAL);
|
return ERR_PTR(-EINVAL);
|
||||||
}
|
}
|
||||||
|
@ -5,6 +5,7 @@
|
|||||||
*/
|
*/
|
||||||
#include <linux/bitops.h>
|
#include <linux/bitops.h>
|
||||||
#include <linux/clk.h>
|
#include <linux/clk.h>
|
||||||
|
#include <linux/input.h>
|
||||||
#include <linux/input/matrix_keypad.h>
|
#include <linux/input/matrix_keypad.h>
|
||||||
#include <linux/interrupt.h>
|
#include <linux/interrupt.h>
|
||||||
#include <linux/module.h>
|
#include <linux/module.h>
|
||||||
@ -18,6 +19,7 @@
|
|||||||
#define MTK_KPD_DEBOUNCE_MASK GENMASK(13, 0)
|
#define MTK_KPD_DEBOUNCE_MASK GENMASK(13, 0)
|
||||||
#define MTK_KPD_DEBOUNCE_MAX_MS 256
|
#define MTK_KPD_DEBOUNCE_MAX_MS 256
|
||||||
#define MTK_KPD_SEL 0x0020
|
#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_COL GENMASK(15, 10)
|
||||||
#define MTK_KPD_SEL_ROW GENMASK(9, 4)
|
#define MTK_KPD_SEL_ROW GENMASK(9, 4)
|
||||||
#define MTK_KPD_SEL_COLMASK(c) GENMASK((c) + 9, 10)
|
#define MTK_KPD_SEL_COLMASK(c) GENMASK((c) + 9, 10)
|
||||||
@ -31,6 +33,8 @@ struct mt6779_keypad {
|
|||||||
struct clk *clk;
|
struct clk *clk;
|
||||||
u32 n_rows;
|
u32 n_rows;
|
||||||
u32 n_cols;
|
u32 n_cols;
|
||||||
|
void (*calc_row_col)(unsigned int key,
|
||||||
|
unsigned int *row, unsigned int *col);
|
||||||
DECLARE_BITMAP(keymap_state, MTK_KPD_NUM_BITS);
|
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;
|
continue;
|
||||||
|
|
||||||
key = bit_nr / 32 * 16 + bit_nr % 32;
|
key = bit_nr / 32 * 16 + bit_nr % 32;
|
||||||
row = key / 9;
|
keypad->calc_row_col(key, &row, &col);
|
||||||
col = key % 9;
|
|
||||||
|
|
||||||
scancode = MATRIX_SCAN_CODE(row, col, row_shift);
|
scancode = MATRIX_SCAN_CODE(row, col, row_shift);
|
||||||
/* 1: not pressed, 0: pressed */
|
/* 1: not pressed, 0: pressed */
|
||||||
@ -94,12 +97,29 @@ static void mt6779_keypad_clk_disable(void *data)
|
|||||||
clk_disable_unprepare(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)
|
static int mt6779_keypad_pdrv_probe(struct platform_device *pdev)
|
||||||
{
|
{
|
||||||
struct mt6779_keypad *keypad;
|
struct mt6779_keypad *keypad;
|
||||||
void __iomem *base;
|
void __iomem *base;
|
||||||
int irq;
|
int irq;
|
||||||
u32 debounce;
|
u32 debounce;
|
||||||
|
u32 keys_per_group;
|
||||||
bool wakeup;
|
bool wakeup;
|
||||||
int error;
|
int error;
|
||||||
|
|
||||||
@ -148,6 +168,23 @@ static int mt6779_keypad_pdrv_probe(struct platform_device *pdev)
|
|||||||
return -EINVAL;
|
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");
|
wakeup = device_property_read_bool(&pdev->dev, "wakeup-source");
|
||||||
|
|
||||||
dev_dbg(&pdev->dev, "n_row=%d n_col=%d debounce=%d\n",
|
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,
|
regmap_write(keypad->regmap, MTK_KPD_DEBOUNCE,
|
||||||
(debounce * (1 << 5)) & MTK_KPD_DEBOUNCE_MASK);
|
(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,
|
regmap_update_bits(keypad->regmap, MTK_KPD_SEL, MTK_KPD_SEL_ROW,
|
||||||
MTK_KPD_SEL_ROWMASK(keypad->n_rows));
|
MTK_KPD_SEL_ROWMASK(keypad->n_rows));
|
||||||
regmap_update_bits(keypad->regmap, MTK_KPD_SEL, MTK_KPD_SEL_COL,
|
regmap_update_bits(keypad->regmap, MTK_KPD_SEL, MTK_KPD_SEL_COL,
|
||||||
|
@ -9,6 +9,7 @@
|
|||||||
#include <linux/interrupt.h>
|
#include <linux/interrupt.h>
|
||||||
#include <linux/kernel.h>
|
#include <linux/kernel.h>
|
||||||
#include <linux/mfd/mt6323/registers.h>
|
#include <linux/mfd/mt6323/registers.h>
|
||||||
|
#include <linux/mfd/mt6331/registers.h>
|
||||||
#include <linux/mfd/mt6358/registers.h>
|
#include <linux/mfd/mt6358/registers.h>
|
||||||
#include <linux/mfd/mt6397/core.h>
|
#include <linux/mfd/mt6397/core.h>
|
||||||
#include <linux/mfd/mt6397/registers.h>
|
#include <linux/mfd/mt6397/registers.h>
|
||||||
@ -22,6 +23,10 @@
|
|||||||
#define MTK_PMIC_PWRKEY_RST BIT(6)
|
#define MTK_PMIC_PWRKEY_RST BIT(6)
|
||||||
#define MTK_PMIC_HOMEKEY_RST BIT(5)
|
#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_PWRKEY_INDEX 0
|
||||||
#define MTK_PMIC_HOMEKEY_INDEX 1
|
#define MTK_PMIC_HOMEKEY_INDEX 1
|
||||||
#define MTK_PMIC_MAX_KEY_COUNT 2
|
#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,
|
.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 = {
|
static const struct mtk_pmic_regs mt6358_regs = {
|
||||||
.keys_regs[MTK_PMIC_PWRKEY_INDEX] =
|
.keys_regs[MTK_PMIC_PWRKEY_INDEX] =
|
||||||
MTK_PMIC_KEYS_REGS(MT6358_TOPSTATUS,
|
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",
|
.compatible = "mediatek,mt6323-keys",
|
||||||
.data = &mt6323_regs,
|
.data = &mt6323_regs,
|
||||||
|
}, {
|
||||||
|
.compatible = "mediatek,mt6331-keys",
|
||||||
|
.data = &mt6331_regs,
|
||||||
}, {
|
}, {
|
||||||
.compatible = "mediatek,mt6358-keys",
|
.compatible = "mediatek,mt6358-keys",
|
||||||
.data = &mt6358_regs,
|
.data = &mt6358_regs,
|
||||||
|
@ -7,9 +7,6 @@
|
|||||||
* Newton keyboard driver for Linux
|
* Newton keyboard driver for Linux
|
||||||
*/
|
*/
|
||||||
|
|
||||||
/*
|
|
||||||
*/
|
|
||||||
|
|
||||||
#include <linux/slab.h>
|
#include <linux/slab.h>
|
||||||
#include <linux/module.h>
|
#include <linux/module.h>
|
||||||
#include <linux/input.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
|
* 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/clk.h>
|
||||||
#include <linux/io.h>
|
#include <linux/input.h>
|
||||||
#include <linux/input/matrix_keypad.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
|
#define ST_KEYSCAN_MAXKEYS 16
|
||||||
|
|
||||||
|
@ -10,9 +10,6 @@
|
|||||||
* by Justin Cormack
|
* by Justin Cormack
|
||||||
*/
|
*/
|
||||||
|
|
||||||
/*
|
|
||||||
*/
|
|
||||||
|
|
||||||
#include <linux/slab.h>
|
#include <linux/slab.h>
|
||||||
#include <linux/module.h>
|
#include <linux/module.h>
|
||||||
#include <linux/input.h>
|
#include <linux/input.h>
|
||||||
|
@ -7,9 +7,6 @@
|
|||||||
* Sun keyboard driver for Linux
|
* Sun keyboard driver for Linux
|
||||||
*/
|
*/
|
||||||
|
|
||||||
/*
|
|
||||||
*/
|
|
||||||
|
|
||||||
#include <linux/delay.h>
|
#include <linux/delay.h>
|
||||||
#include <linux/sched.h>
|
#include <linux/sched.h>
|
||||||
#include <linux/slab.h>
|
#include <linux/slab.h>
|
||||||
|
@ -70,7 +70,7 @@
|
|||||||
#define TC3589x_KBD_INT_CLR 0x1
|
#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
|
* @keymap_data: matrix scan code table for keycodes
|
||||||
* @krow: mask for available rows, value is 0xFF
|
* @krow: mask for available rows, value is 0xFF
|
||||||
* @kcol: mask for available columns, value is 0xFF
|
* @kcol: mask for available columns, value is 0xFF
|
||||||
|
@ -7,9 +7,6 @@
|
|||||||
* XT keyboard driver for Linux
|
* XT keyboard driver for Linux
|
||||||
*/
|
*/
|
||||||
|
|
||||||
/*
|
|
||||||
*/
|
|
||||||
|
|
||||||
#include <linux/slab.h>
|
#include <linux/slab.h>
|
||||||
#include <linux/module.h>
|
#include <linux/module.h>
|
||||||
#include <linux/input.h>
|
#include <linux/input.h>
|
||||||
|
@ -730,6 +730,24 @@ config INPUT_ADXL34X_SPI
|
|||||||
To compile this driver as a module, choose M here: the
|
To compile this driver as a module, choose M here: the
|
||||||
module will be called adxl34x-spi.
|
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
|
config INPUT_IMS_PCU
|
||||||
tristate "IMS Passenger Control Unit driver"
|
tristate "IMS Passenger Control Unit driver"
|
||||||
depends on USB
|
depends on USB
|
||||||
@ -891,6 +909,15 @@ config INPUT_SC27XX_VIBRA
|
|||||||
To compile this driver as a module, choose M here. The module will
|
To compile this driver as a module, choose M here. The module will
|
||||||
be called sc27xx_vibra.
|
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
|
config INPUT_STPMIC1_ONKEY
|
||||||
tristate "STPMIC1 PMIC Onkey support"
|
tristate "STPMIC1 PMIC Onkey support"
|
||||||
depends on MFD_STPMIC1
|
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_GPIO_VIBRA) += gpio-vibra.o
|
||||||
obj-$(CONFIG_INPUT_HISI_POWERKEY) += hisi_powerkey.o
|
obj-$(CONFIG_INPUT_HISI_POWERKEY) += hisi_powerkey.o
|
||||||
obj-$(CONFIG_HP_SDC_RTC) += hp_sdc_rtc.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_IMS_PCU) += ims-pcu.o
|
||||||
obj-$(CONFIG_INPUT_IQS269A) += iqs269a.o
|
obj-$(CONFIG_INPUT_IQS269A) += iqs269a.o
|
||||||
obj-$(CONFIG_INPUT_IQS626A) += iqs626a.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_RB532_BUTTON) += rb532_button.o
|
||||||
obj-$(CONFIG_INPUT_REGULATOR_HAPTIC) += regulator-haptic.o
|
obj-$(CONFIG_INPUT_REGULATOR_HAPTIC) += regulator-haptic.o
|
||||||
obj-$(CONFIG_INPUT_RETU_PWRBUTTON) += retu-pwrbutton.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_AXP20X_PEK) += axp20x-pek.o
|
||||||
obj-$(CONFIG_INPUT_GPIO_ROTARY_ENCODER) += rotary_encoder.o
|
obj-$(CONFIG_INPUT_GPIO_ROTARY_ENCODER) += rotary_encoder.o
|
||||||
obj-$(CONFIG_INPUT_RK805_PWRKEY) += rk805-pwrkey.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);
|
error = ims_pcu_execute_command(pcu, JUMP_TO_BTLDR, NULL, 0);
|
||||||
if (error) {
|
if (error) {
|
||||||
dev_err(pcu->dev,
|
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);
|
error);
|
||||||
return 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)
|
static int iqs7222_force_comms(struct iqs7222_private *iqs7222)
|
||||||
{
|
{
|
||||||
u8 msg_buf[] = { 0xFF, 0x00, };
|
u8 msg_buf[] = { 0xFF, };
|
||||||
int ret;
|
int ret;
|
||||||
|
|
||||||
/*
|
/*
|
||||||
@ -1771,11 +1771,9 @@ static int iqs7222_parse_chan(struct iqs7222_private *iqs7222, int chan_index)
|
|||||||
if (!chan_node)
|
if (!chan_node)
|
||||||
return 0;
|
return 0;
|
||||||
|
|
||||||
if (dev_desc->allow_offset) {
|
if (dev_desc->allow_offset &&
|
||||||
sys_setup[dev_desc->allow_offset] |= BIT(chan_index);
|
fwnode_property_present(chan_node, "azoteq,ulp-allow"))
|
||||||
if (fwnode_property_present(chan_node, "azoteq,ulp-allow"))
|
sys_setup[dev_desc->allow_offset] &= ~BIT(chan_index);
|
||||||
sys_setup[dev_desc->allow_offset] &= ~BIT(chan_index);
|
|
||||||
}
|
|
||||||
|
|
||||||
chan_setup[0] |= IQS7222_CHAN_SETUP_0_CHAN_EN;
|
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;
|
u16 *sys_setup = iqs7222->sys_setup;
|
||||||
int error, i;
|
int error, i;
|
||||||
|
|
||||||
|
if (dev_desc->allow_offset)
|
||||||
|
sys_setup[dev_desc->allow_offset] = U16_MAX;
|
||||||
|
|
||||||
if (dev_desc->event_offset)
|
if (dev_desc->event_offset)
|
||||||
sys_setup[dev_desc->event_offset] = IQS7222_EVENT_MASK_ATI;
|
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);
|
int k = 2 + j * (num_chan > 16 ? 2 : 1);
|
||||||
u16 state = le16_to_cpu(status[k + i / 16]);
|
u16 state = le16_to_cpu(status[k + i / 16]);
|
||||||
|
|
||||||
|
if (!iqs7222->kp_type[i][j])
|
||||||
|
continue;
|
||||||
|
|
||||||
input_event(iqs7222->keypad,
|
input_event(iqs7222->keypad,
|
||||||
iqs7222->kp_type[i][j],
|
iqs7222->kp_type[i][j],
|
||||||
iqs7222->kp_code[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)
|
if (udev->manufacturer)
|
||||||
strlcpy(remote->name, udev->manufacturer, sizeof(remote->name));
|
strscpy(remote->name, udev->manufacturer, sizeof(remote->name));
|
||||||
|
|
||||||
if (udev->product) {
|
if (udev->product) {
|
||||||
if (udev->manufacturer)
|
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/errno.h>
|
||||||
#include <linux/input.h>
|
#include <linux/input.h>
|
||||||
#include <linux/interrupt.h>
|
#include <linux/interrupt.h>
|
||||||
|
#include <linux/of.h>
|
||||||
#include <linux/platform_device.h>
|
#include <linux/platform_device.h>
|
||||||
#include <linux/mfd/twl.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,
|
static SIMPLE_DEV_PM_OPS(twl4030_vibra_pm_ops,
|
||||||
twl4030_vibra_suspend, twl4030_vibra_resume);
|
twl4030_vibra_suspend, twl4030_vibra_resume);
|
||||||
|
|
||||||
static bool twl4030_vibra_check_coexist(struct twl4030_vibra_data *pdata,
|
static bool twl4030_vibra_check_coexist(struct device_node *parent)
|
||||||
struct device_node *parent)
|
|
||||||
{
|
{
|
||||||
struct device_node *node;
|
struct device_node *node;
|
||||||
|
|
||||||
if (pdata && pdata->coexist)
|
|
||||||
return true;
|
|
||||||
|
|
||||||
node = of_get_child_by_name(parent, "codec");
|
node = of_get_child_by_name(parent, "codec");
|
||||||
if (node) {
|
if (node) {
|
||||||
of_node_put(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)
|
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 device_node *twl4030_core_node = pdev->dev.parent->of_node;
|
||||||
struct vibra_info *info;
|
struct vibra_info *info;
|
||||||
int ret;
|
int ret;
|
||||||
|
|
||||||
if (!pdata && !twl4030_core_node) {
|
if (!twl4030_core_node) {
|
||||||
dev_dbg(&pdev->dev, "platform_data not available\n");
|
dev_dbg(&pdev->dev, "twl4030 OF node is missing\n");
|
||||||
return -EINVAL;
|
return -EINVAL;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -197,7 +192,7 @@ static int twl4030_vibra_probe(struct platform_device *pdev)
|
|||||||
return -ENOMEM;
|
return -ENOMEM;
|
||||||
|
|
||||||
info->dev = &pdev->dev;
|
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);
|
INIT_WORK(&info->play_work, vibra_play_work);
|
||||||
|
|
||||||
info->input_dev = devm_input_allocate_device(&pdev->dev);
|
info->input_dev = devm_input_allocate_device(&pdev->dev);
|
||||||
|
@ -1311,12 +1311,6 @@ static int elan_probe(struct i2c_client *client,
|
|||||||
return error;
|
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);
|
error = input_register_device(data->input);
|
||||||
if (error) {
|
if (error) {
|
||||||
dev_err(dev, "failed to register input device: %d\n", 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),
|
.acpi_match_table = ACPI_PTR(elan_acpi_id),
|
||||||
.of_match_table = of_match_ptr(elan_of_match),
|
.of_match_table = of_match_ptr(elan_of_match),
|
||||||
.probe_type = PROBE_PREFER_ASYNCHRONOUS,
|
.probe_type = PROBE_PREFER_ASYNCHRONOUS,
|
||||||
|
.dev_groups = elan_sysfs_groups,
|
||||||
},
|
},
|
||||||
.probe = elan_probe,
|
.probe = elan_probe,
|
||||||
.id_table = elan_id,
|
.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
|
* 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.
|
* since workqueue provides serialization.
|
||||||
*/
|
*/
|
||||||
psmouse_queue_work(psmouse, &priv->recalib_wq, 0);
|
psmouse_queue_work(psmouse, &priv->recalib_wq, 0);
|
||||||
@ -1057,7 +1057,7 @@ void hgpk_module_init(void)
|
|||||||
strlen(hgpk_mode_name));
|
strlen(hgpk_mode_name));
|
||||||
if (hgpk_default_mode == HGPK_MODE_INVALID) {
|
if (hgpk_default_mode == HGPK_MODE_INVALID) {
|
||||||
hgpk_default_mode = HGPK_MODE_MOUSE;
|
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));
|
sizeof(hgpk_mode_name));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -13,9 +13,6 @@
|
|||||||
* Inport (ATI XL and Microsoft) busmouse driver for Linux
|
* Inport (ATI XL and Microsoft) busmouse driver for Linux
|
||||||
*/
|
*/
|
||||||
|
|
||||||
/*
|
|
||||||
*/
|
|
||||||
|
|
||||||
#include <linux/module.h>
|
#include <linux/module.h>
|
||||||
#include <linux/ioport.h>
|
#include <linux/ioport.h>
|
||||||
#include <linux/init.h>
|
#include <linux/init.h>
|
||||||
|
@ -14,9 +14,6 @@
|
|||||||
* Logitech Bus Mouse Driver for Linux
|
* Logitech Bus Mouse Driver for Linux
|
||||||
*/
|
*/
|
||||||
|
|
||||||
/*
|
|
||||||
*/
|
|
||||||
|
|
||||||
#include <linux/module.h>
|
#include <linux/module.h>
|
||||||
#include <linux/delay.h>
|
#include <linux/delay.h>
|
||||||
#include <linux/ioport.h>
|
#include <linux/ioport.h>
|
||||||
|
@ -10,9 +10,6 @@
|
|||||||
* IBM PC110 touchpad driver for Linux
|
* IBM PC110 touchpad driver for Linux
|
||||||
*/
|
*/
|
||||||
|
|
||||||
/*
|
|
||||||
*/
|
|
||||||
|
|
||||||
#include <linux/module.h>
|
#include <linux/module.h>
|
||||||
#include <linux/kernel.h>
|
#include <linux/kernel.h>
|
||||||
#include <linux/errno.h>
|
#include <linux/errno.h>
|
||||||
|
@ -94,7 +94,7 @@ PSMOUSE_DEFINE_ATTR(resync_time, S_IWUSR | S_IRUGO,
|
|||||||
(void *) offsetof(struct psmouse, resync_time),
|
(void *) offsetof(struct psmouse, resync_time),
|
||||||
psmouse_show_int_attr, psmouse_set_int_attr);
|
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_protocol.dattr.attr,
|
||||||
&psmouse_attr_rate.dattr.attr,
|
&psmouse_attr_rate.dattr.attr,
|
||||||
&psmouse_attr_resolution.dattr.attr,
|
&psmouse_attr_resolution.dattr.attr,
|
||||||
@ -103,9 +103,7 @@ static struct attribute *psmouse_attributes[] = {
|
|||||||
NULL
|
NULL
|
||||||
};
|
};
|
||||||
|
|
||||||
static const struct attribute_group psmouse_attribute_group = {
|
ATTRIBUTE_GROUPS(psmouse_dev);
|
||||||
.attrs = psmouse_attributes,
|
|
||||||
};
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* psmouse_mutex protects all operations changing state of mouse
|
* 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 *psmouse = serio_get_drvdata(serio);
|
||||||
struct psmouse *parent = NULL;
|
struct psmouse *parent = NULL;
|
||||||
|
|
||||||
sysfs_remove_group(&serio->dev.kobj, &psmouse_attribute_group);
|
|
||||||
|
|
||||||
mutex_lock(&psmouse_mutex);
|
mutex_lock(&psmouse_mutex);
|
||||||
|
|
||||||
psmouse_set_state(psmouse, PSMOUSE_CMD_MODE);
|
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)
|
if (parent && parent->pt_activate)
|
||||||
parent->pt_activate(parent);
|
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
|
* PS/2 devices having SMBus companions should stay disabled
|
||||||
* on PS/2 side, in order to have SMBus part operable.
|
* 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);
|
mutex_unlock(&psmouse_mutex);
|
||||||
return retval;
|
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:
|
err_protocol_disconnect:
|
||||||
if (psmouse->disconnect)
|
if (psmouse->disconnect)
|
||||||
psmouse->disconnect(psmouse);
|
psmouse->disconnect(psmouse);
|
||||||
@ -1791,7 +1776,8 @@ MODULE_DEVICE_TABLE(serio, psmouse_serio_ids);
|
|||||||
|
|
||||||
static struct serio_driver psmouse_drv = {
|
static struct serio_driver psmouse_drv = {
|
||||||
.driver = {
|
.driver = {
|
||||||
.name = "psmouse",
|
.name = "psmouse",
|
||||||
|
.dev_groups = psmouse_dev_groups,
|
||||||
},
|
},
|
||||||
.description = DRIVER_DESC,
|
.description = DRIVER_DESC,
|
||||||
.id_table = psmouse_serio_ids,
|
.id_table = psmouse_serio_ids,
|
||||||
|
@ -7,9 +7,6 @@
|
|||||||
* Serial mouse driver for Linux
|
* Serial mouse driver for Linux
|
||||||
*/
|
*/
|
||||||
|
|
||||||
/*
|
|
||||||
*/
|
|
||||||
|
|
||||||
#include <linux/delay.h>
|
#include <linux/delay.h>
|
||||||
#include <linux/module.h>
|
#include <linux/module.h>
|
||||||
#include <linux/slab.h>
|
#include <linux/slab.h>
|
||||||
|
@ -182,6 +182,7 @@ static const char * const smbus_pnp_ids[] = {
|
|||||||
"LEN0099", /* X1 Extreme Gen 1 / P1 Gen 1 */
|
"LEN0099", /* X1 Extreme Gen 1 / P1 Gen 1 */
|
||||||
"LEN009b", /* T580 */
|
"LEN009b", /* T580 */
|
||||||
"LEN0402", /* X1 Extreme Gen 2 / P1 Gen 2 */
|
"LEN0402", /* X1 Extreme Gen 2 / P1 Gen 2 */
|
||||||
|
"LEN040f", /* P1 Gen 3 */
|
||||||
"LEN200f", /* T450s */
|
"LEN200f", /* T450s */
|
||||||
"LEN2044", /* L470 */
|
"LEN2044", /* L470 */
|
||||||
"LEN2054", /* E480 */
|
"LEN2054", /* E480 */
|
||||||
@ -714,8 +715,8 @@ static void synaptics_pt_create(struct psmouse *psmouse)
|
|||||||
}
|
}
|
||||||
|
|
||||||
serio->id.type = SERIO_PS_PSTHRU;
|
serio->id.type = SERIO_PS_PSTHRU;
|
||||||
strlcpy(serio->name, "Synaptics pass-through", sizeof(serio->name));
|
strscpy(serio->name, "Synaptics pass-through", sizeof(serio->name));
|
||||||
strlcpy(serio->phys, "synaptics-pt/serio0", sizeof(serio->phys));
|
strscpy(serio->phys, "synaptics-pt/serio0", sizeof(serio->phys));
|
||||||
serio->write = synaptics_pt_write;
|
serio->write = synaptics_pt_write;
|
||||||
serio->start = synaptics_pt_start;
|
serio->start = synaptics_pt_start;
|
||||||
serio->stop = synaptics_pt_stop;
|
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;
|
synusb->urb->transfer_flags |= URB_NO_TRANSFER_DMA_MAP;
|
||||||
|
|
||||||
if (udev->manufacturer)
|
if (udev->manufacturer)
|
||||||
strlcpy(synusb->name, udev->manufacturer,
|
strscpy(synusb->name, udev->manufacturer,
|
||||||
sizeof(synusb->name));
|
sizeof(synusb->name));
|
||||||
|
|
||||||
if (udev->product) {
|
if (udev->product) {
|
||||||
|
@ -12,9 +12,6 @@
|
|||||||
* Later on, I had access to the device's documentation (referenced below).
|
* Later on, I had access to the device's documentation (referenced below).
|
||||||
*/
|
*/
|
||||||
|
|
||||||
/*
|
|
||||||
*/
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Building an adaptor to DE9 / DB25 RS232
|
* Building an adaptor to DE9 / DB25 RS232
|
||||||
* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||||
@ -138,12 +135,12 @@ static void vsxxxaa_detection_done(struct vsxxxaa *mouse)
|
|||||||
{
|
{
|
||||||
switch (mouse->type) {
|
switch (mouse->type) {
|
||||||
case 0x02:
|
case 0x02:
|
||||||
strlcpy(mouse->name, "DEC VSXXX-AA/-GA mouse",
|
strscpy(mouse->name, "DEC VSXXX-AA/-GA mouse",
|
||||||
sizeof(mouse->name));
|
sizeof(mouse->name));
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case 0x04:
|
case 0x04:
|
||||||
strlcpy(mouse->name, "DEC VSXXX-AB digitizer",
|
strscpy(mouse->name, "DEC VSXXX-AB digitizer",
|
||||||
sizeof(mouse->name));
|
sizeof(mouse->name));
|
||||||
break;
|
break;
|
||||||
|
|
||||||
|
@ -181,7 +181,7 @@ static int rmi_f03_register_pt(struct f03_data *f03)
|
|||||||
serio->close = rmi_f03_pt_close;
|
serio->close = rmi_f03_pt_close;
|
||||||
serio->port_data = f03;
|
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",
|
snprintf(serio->phys, sizeof(serio->phys), "%s/serio0",
|
||||||
dev_name(&f03->fn->dev));
|
dev_name(&f03->fn->dev));
|
||||||
serio->dev.parent = &f03->fn->dev;
|
serio->dev.parent = &f03->fn->dev;
|
||||||
|
@ -114,13 +114,13 @@ static irqreturn_t rmi_f34_attention(int irq, void *ctx)
|
|||||||
complete(&f34->v5.cmd_done);
|
complete(&f34->v5.cmd_done);
|
||||||
} else {
|
} else {
|
||||||
ret = rmi_read_block(f34->fn->rmi_dev,
|
ret = rmi_read_block(f34->fn->rmi_dev,
|
||||||
f34->fn->fd.data_base_addr +
|
f34->fn->fd.data_base_addr +
|
||||||
f34->v7.off.flash_status,
|
V7_COMMAND_OFFSET,
|
||||||
&status, sizeof(status));
|
&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);
|
__func__, status, ret);
|
||||||
|
|
||||||
if (!ret && !(status & 0x1f))
|
if (!ret && status == CMD_V7_IDLE)
|
||||||
complete(&f34->v7.cmd_done);
|
complete(&f34->v7.cmd_done);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -321,13 +321,13 @@ static ssize_t rmi_driver_bootloader_id_show(struct device *dev,
|
|||||||
f34 = dev_get_drvdata(&fn->dev);
|
f34 = dev_get_drvdata(&fn->dev);
|
||||||
|
|
||||||
if (f34->bl_version == 5)
|
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[0],
|
||||||
f34->bootloader_id[1]);
|
f34->bootloader_id[1]);
|
||||||
else
|
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[1],
|
||||||
f34->bootloader_id[0]);
|
f34->bootloader_id[0]);
|
||||||
}
|
}
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
@ -346,7 +346,7 @@ static ssize_t rmi_driver_configuration_id_show(struct device *dev,
|
|||||||
if (fn) {
|
if (fn) {
|
||||||
f34 = dev_get_drvdata(&fn->dev);
|
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;
|
return 0;
|
||||||
@ -370,7 +370,7 @@ static int rmi_firmware_update(struct rmi_driver_data *data,
|
|||||||
|
|
||||||
f34 = dev_get_drvdata(&data->f34_container->dev);
|
f34 = dev_get_drvdata(&data->f34_container->dev);
|
||||||
|
|
||||||
if (f34->bl_version == 7) {
|
if (f34->bl_version >= 7) {
|
||||||
if (data->pdt_props & HAS_BSR) {
|
if (data->pdt_props & HAS_BSR) {
|
||||||
dev_err(dev, "%s: LTS not supported\n", __func__);
|
dev_err(dev, "%s: LTS not supported\n", __func__);
|
||||||
return -ENODEV;
|
return -ENODEV;
|
||||||
@ -382,7 +382,7 @@ static int rmi_firmware_update(struct rmi_driver_data *data,
|
|||||||
}
|
}
|
||||||
|
|
||||||
/* Enter flash mode */
|
/* Enter flash mode */
|
||||||
if (f34->bl_version == 7)
|
if (f34->bl_version >= 7)
|
||||||
ret = rmi_f34v7_start_reflash(f34, fw);
|
ret = rmi_f34v7_start_reflash(f34, fw);
|
||||||
else
|
else
|
||||||
ret = rmi_f34_enable_flash(f34);
|
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);
|
f34 = dev_get_drvdata(&data->f34_container->dev);
|
||||||
|
|
||||||
/* Perform firmware update */
|
/* Perform firmware update */
|
||||||
if (f34->bl_version == 7)
|
if (f34->bl_version >= 7)
|
||||||
ret = rmi_f34v7_do_reflash(f34, fw);
|
ret = rmi_f34v7_do_reflash(f34, fw);
|
||||||
else
|
else
|
||||||
ret = rmi_f34_update_firmware(f34, fw);
|
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)
|
if (data->f34_container)
|
||||||
update_status = rmi_f34_status(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,
|
static DEVICE_ATTR(update_fw_status, 0444,
|
||||||
|
@ -222,20 +222,6 @@ struct image_metadata {
|
|||||||
struct physical_address phyaddr;
|
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 {
|
struct rmi_f34_firmware {
|
||||||
__le32 checksum;
|
__le32 checksum;
|
||||||
u8 pad1[3];
|
u8 pad1[3];
|
||||||
@ -262,7 +248,6 @@ struct f34v5_data {
|
|||||||
struct f34v7_data {
|
struct f34v7_data {
|
||||||
bool has_display_cfg;
|
bool has_display_cfg;
|
||||||
bool has_guest_code;
|
bool has_guest_code;
|
||||||
bool force_update;
|
|
||||||
bool in_bl_mode;
|
bool in_bl_mode;
|
||||||
u8 *read_config_buf;
|
u8 *read_config_buf;
|
||||||
size_t read_config_buf_size;
|
size_t read_config_buf_size;
|
||||||
@ -276,9 +261,7 @@ struct f34v7_data {
|
|||||||
u16 payload_length;
|
u16 payload_length;
|
||||||
u8 partitions;
|
u8 partitions;
|
||||||
u16 partition_table_bytes;
|
u16 partition_table_bytes;
|
||||||
bool new_partition_table;
|
|
||||||
|
|
||||||
struct register_offset off;
|
|
||||||
struct block_count blkcount;
|
struct block_count blkcount;
|
||||||
struct physical_address phyaddr;
|
struct physical_address phyaddr;
|
||||||
struct image_metadata img;
|
struct image_metadata img;
|
||||||
|
@ -25,7 +25,7 @@ static int rmi_f34v7_read_flash_status(struct f34_data *f34)
|
|||||||
int ret;
|
int ret;
|
||||||
|
|
||||||
ret = rmi_read_block(f34->fn->rmi_dev,
|
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,
|
&status,
|
||||||
sizeof(status));
|
sizeof(status));
|
||||||
if (ret < 0) {
|
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,
|
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,
|
&command,
|
||||||
sizeof(command));
|
sizeof(command));
|
||||||
if (ret < 0) {
|
if (ret < 0) {
|
||||||
@ -72,6 +72,24 @@ static int rmi_f34v7_wait_for_idle(struct f34_data *f34, int timeout_ms)
|
|||||||
return 0;
|
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,
|
static int rmi_f34v7_write_command_single_transaction(struct f34_data *f34,
|
||||||
u8 cmd)
|
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];
|
data_1_5.payload[1] = f34->bootloader_id[1];
|
||||||
|
|
||||||
ret = rmi_write_block(f34->fn->rmi_dev,
|
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));
|
&data_1_5, sizeof(data_1_5));
|
||||||
if (ret < 0) {
|
if (ret < 0) {
|
||||||
dev_err(&f34->fn->dev,
|
dev_err(&f34->fn->dev,
|
||||||
@ -195,7 +213,7 @@ static int rmi_f34v7_write_command(struct f34_data *f34, u8 cmd)
|
|||||||
__func__, command);
|
__func__, command);
|
||||||
|
|
||||||
ret = rmi_write_block(f34->fn->rmi_dev,
|
ret = rmi_write_block(f34->fn->rmi_dev,
|
||||||
base + f34->v7.off.flash_cmd,
|
base + V7_COMMAND_OFFSET,
|
||||||
&command, sizeof(command));
|
&command, sizeof(command));
|
||||||
if (ret < 0) {
|
if (ret < 0) {
|
||||||
dev_err(&f34->fn->dev, "%s: Failed to write flash command\n",
|
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,
|
ret = rmi_write_block(f34->fn->rmi_dev,
|
||||||
base + f34->v7.off.partition_id,
|
base + V7_PARTITION_ID_OFFSET,
|
||||||
&partition, sizeof(partition));
|
&partition, sizeof(partition));
|
||||||
if (ret < 0) {
|
if (ret < 0) {
|
||||||
dev_err(&f34->fn->dev, "%s: Failed to write partition ID\n",
|
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;
|
return ret;
|
||||||
|
|
||||||
ret = rmi_write_block(f34->fn->rmi_dev,
|
ret = rmi_write_block(f34->fn->rmi_dev,
|
||||||
base + f34->v7.off.block_number,
|
base + V7_BLOCK_NUMBER_OFFSET,
|
||||||
&block_number, sizeof(block_number));
|
&block_number, sizeof(block_number));
|
||||||
if (ret < 0) {
|
if (ret < 0) {
|
||||||
dev_err(&f34->fn->dev, "%s: Failed to write block number\n",
|
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);
|
put_unaligned_le16(f34->v7.flash_config_length, &length);
|
||||||
|
|
||||||
ret = rmi_write_block(f34->fn->rmi_dev,
|
ret = rmi_write_block(f34->fn->rmi_dev,
|
||||||
base + f34->v7.off.transfer_length,
|
base + V7_TRANSFER_LENGTH_OFFSET,
|
||||||
&length, sizeof(length));
|
&length, sizeof(length));
|
||||||
if (ret < 0) {
|
if (ret < 0) {
|
||||||
dev_err(&f34->fn->dev, "%s: Failed to write transfer length\n",
|
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;
|
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);
|
timeout = msecs_to_jiffies(F34_WRITE_WAIT_MS);
|
||||||
while (time_before(jiffies, timeout)) {
|
while (time_before(jiffies, timeout)) {
|
||||||
usleep_range(5000, 6000);
|
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,
|
ret = rmi_read_block(f34->fn->rmi_dev,
|
||||||
base + f34->v7.off.payload,
|
base + V7_PAYLOAD_OFFSET,
|
||||||
f34->v7.read_config_buf,
|
f34->v7.read_config_buf,
|
||||||
f34->v7.partition_table_bytes);
|
f34->v7.partition_table_bytes);
|
||||||
if (ret < 0) {
|
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",
|
rmi_dbg(RMI_DEBUG_FN, &f34->fn->dev, "%s: f34->v7.block_size = %d\n",
|
||||||
__func__, f34->v7.block_size);
|
__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_display_cfg = query_1_7.partition_support[1] & HAS_DISP_CFG;
|
||||||
f34->v7.has_guest_code =
|
f34->v7.has_guest_code =
|
||||||
query_1_7.partition_support[1] & 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;
|
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)
|
static int rmi_f34v7_check_bl_config_size(struct f34_data *f34)
|
||||||
{
|
{
|
||||||
u16 block_count;
|
u16 block_count;
|
||||||
@ -648,58 +601,6 @@ static int rmi_f34v7_check_bl_config_size(struct f34_data *f34)
|
|||||||
return 0;
|
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)
|
static int rmi_f34v7_erase_all(struct f34_data *f34)
|
||||||
{
|
{
|
||||||
int ret;
|
int ret;
|
||||||
@ -708,32 +609,14 @@ static int rmi_f34v7_erase_all(struct f34_data *f34)
|
|||||||
|
|
||||||
init_completion(&f34->v7.cmd_done);
|
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)
|
if (ret < 0)
|
||||||
return ret;
|
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)
|
if (ret < 0)
|
||||||
return ret;
|
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;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -756,7 +639,7 @@ static int rmi_f34v7_read_blocks(struct f34_data *f34,
|
|||||||
return ret;
|
return ret;
|
||||||
|
|
||||||
ret = rmi_write_block(f34->fn->rmi_dev,
|
ret = rmi_write_block(f34->fn->rmi_dev,
|
||||||
base + f34->v7.off.block_number,
|
base + V7_BLOCK_NUMBER_OFFSET,
|
||||||
&block_number, sizeof(block_number));
|
&block_number, sizeof(block_number));
|
||||||
if (ret < 0) {
|
if (ret < 0) {
|
||||||
dev_err(&f34->fn->dev, "%s: Failed to write block number\n",
|
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);
|
put_unaligned_le16(transfer, &length);
|
||||||
|
|
||||||
ret = rmi_write_block(f34->fn->rmi_dev,
|
ret = rmi_write_block(f34->fn->rmi_dev,
|
||||||
base + f34->v7.off.transfer_length,
|
base + V7_TRANSFER_LENGTH_OFFSET,
|
||||||
&length, sizeof(length));
|
&length, sizeof(length));
|
||||||
if (ret < 0) {
|
if (ret < 0) {
|
||||||
dev_err(&f34->fn->dev,
|
dev_err(&f34->fn->dev,
|
||||||
@ -787,12 +670,12 @@ static int rmi_f34v7_read_blocks(struct f34_data *f34,
|
|||||||
if (ret < 0)
|
if (ret < 0)
|
||||||
return ret;
|
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)
|
if (ret < 0)
|
||||||
return ret;
|
return ret;
|
||||||
|
|
||||||
ret = rmi_read_block(f34->fn->rmi_dev,
|
ret = rmi_read_block(f34->fn->rmi_dev,
|
||||||
base + f34->v7.off.payload,
|
base + V7_PAYLOAD_OFFSET,
|
||||||
&f34->v7.read_config_buf[index],
|
&f34->v7.read_config_buf[index],
|
||||||
transfer * f34->v7.block_size);
|
transfer * f34->v7.block_size);
|
||||||
if (ret < 0) {
|
if (ret < 0) {
|
||||||
@ -828,7 +711,7 @@ static int rmi_f34v7_write_f34v7_blocks(struct f34_data *f34,
|
|||||||
return ret;
|
return ret;
|
||||||
|
|
||||||
ret = rmi_write_block(f34->fn->rmi_dev,
|
ret = rmi_write_block(f34->fn->rmi_dev,
|
||||||
base + f34->v7.off.block_number,
|
base + V7_BLOCK_NUMBER_OFFSET,
|
||||||
&block_number, sizeof(block_number));
|
&block_number, sizeof(block_number));
|
||||||
if (ret < 0) {
|
if (ret < 0) {
|
||||||
dev_err(&f34->fn->dev, "%s: Failed to write block number\n",
|
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);
|
init_completion(&f34->v7.cmd_done);
|
||||||
|
|
||||||
ret = rmi_write_block(f34->fn->rmi_dev,
|
ret = rmi_write_block(f34->fn->rmi_dev,
|
||||||
base + f34->v7.off.transfer_length,
|
base + V7_TRANSFER_LENGTH_OFFSET,
|
||||||
&length, sizeof(length));
|
&length, sizeof(length));
|
||||||
if (ret < 0) {
|
if (ret < 0) {
|
||||||
dev_err(&f34->fn->dev,
|
dev_err(&f34->fn->dev,
|
||||||
@ -862,7 +745,7 @@ static int rmi_f34v7_write_f34v7_blocks(struct f34_data *f34,
|
|||||||
return ret;
|
return ret;
|
||||||
|
|
||||||
ret = rmi_write_block(f34->fn->rmi_dev,
|
ret = rmi_write_block(f34->fn->rmi_dev,
|
||||||
base + f34->v7.off.payload,
|
base + V7_PAYLOAD_OFFSET,
|
||||||
block_ptr, transfer * f34->v7.block_size);
|
block_ptr, transfer * f34->v7.block_size);
|
||||||
if (ret < 0) {
|
if (ret < 0) {
|
||||||
dev_err(&f34->fn->dev,
|
dev_err(&f34->fn->dev,
|
||||||
@ -871,7 +754,7 @@ static int rmi_f34v7_write_f34v7_blocks(struct f34_data *f34,
|
|||||||
return ret;
|
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)
|
if (ret < 0)
|
||||||
return ret;
|
return ret;
|
||||||
|
|
||||||
@ -937,17 +820,6 @@ static int rmi_f34v7_write_flash_config(struct f34_data *f34)
|
|||||||
|
|
||||||
init_completion(&f34->v7.cmd_done);
|
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);
|
ret = rmi_f34v7_write_config(f34);
|
||||||
if (ret < 0)
|
if (ret < 0)
|
||||||
return ret;
|
return ret;
|
||||||
@ -977,10 +849,6 @@ static int rmi_f34v7_write_partition_table(struct f34_data *f34)
|
|||||||
if (ret < 0)
|
if (ret < 0)
|
||||||
return ret;
|
return ret;
|
||||||
|
|
||||||
ret = rmi_f34v7_erase_config(f34);
|
|
||||||
if (ret < 0)
|
|
||||||
return ret;
|
|
||||||
|
|
||||||
ret = rmi_f34v7_write_flash_config(f34);
|
ret = rmi_f34v7_write_flash_config(f34);
|
||||||
if (ret < 0)
|
if (ret < 0)
|
||||||
return ret;
|
return ret;
|
||||||
@ -1007,33 +875,6 @@ static int rmi_f34v7_write_firmware(struct f34_data *f34)
|
|||||||
blk_count, v7_CMD_WRITE_FW);
|
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,
|
static void rmi_f34v7_parse_img_header_10_bl_container(struct f34_data *f34,
|
||||||
const void *image)
|
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,
|
rmi_f34v7_parse_partition_table(f34, f34->v7.img.fl_config.data,
|
||||||
&f34->v7.img.blkcount, &f34->v7.img.phyaddr);
|
&f34->v7.img.blkcount, &f34->v7.img.phyaddr);
|
||||||
|
|
||||||
rmi_f34v7_compare_partition_tables(f34);
|
|
||||||
|
|
||||||
return 0;
|
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);
|
ret = rmi_f34v7_parse_image_info(f34);
|
||||||
if (ret < 0)
|
if (ret < 0)
|
||||||
goto fail;
|
return ret;
|
||||||
|
|
||||||
if (!f34->v7.new_partition_table) {
|
ret = rmi_f34v7_check_bl_config_size(f34);
|
||||||
ret = rmi_f34v7_check_ui_firmware_size(f34);
|
if (ret < 0)
|
||||||
if (ret < 0)
|
return ret;
|
||||||
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;
|
|
||||||
}
|
|
||||||
|
|
||||||
ret = rmi_f34v7_erase_all(f34);
|
ret = rmi_f34v7_erase_all(f34);
|
||||||
if (ret < 0)
|
if (ret < 0)
|
||||||
goto fail;
|
return ret;
|
||||||
|
|
||||||
if (f34->v7.new_partition_table) {
|
ret = rmi_f34v7_write_partition_table(f34);
|
||||||
ret = rmi_f34v7_write_partition_table(f34);
|
if (ret < 0)
|
||||||
if (ret < 0)
|
return ret;
|
||||||
goto fail;
|
dev_info(&f34->fn->dev, "%s: Partition table programmed\n", __func__);
|
||||||
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",
|
dev_info(&f34->fn->dev, "Writing firmware (%d bytes)...\n",
|
||||||
f34->v7.img.ui_firmware.size);
|
f34->v7.img.ui_firmware.size);
|
||||||
|
|
||||||
ret = rmi_f34v7_write_firmware(f34);
|
ret = rmi_f34v7_write_firmware(f34);
|
||||||
if (ret < 0)
|
if (ret < 0)
|
||||||
goto fail;
|
return ret;
|
||||||
|
|
||||||
dev_info(&f34->fn->dev, "Writing config (%d bytes)...\n",
|
dev_info(&f34->fn->dev, "Writing config (%d bytes)...\n",
|
||||||
f34->v7.img.ui_config.size);
|
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;
|
f34->v7.config_area = v7_UI_CONFIG_AREA;
|
||||||
ret = rmi_f34v7_write_ui_config(f34);
|
ret = rmi_f34v7_write_ui_config(f34);
|
||||||
if (ret < 0)
|
if (ret < 0)
|
||||||
goto fail;
|
return ret;
|
||||||
|
|
||||||
if (f34->v7.has_display_cfg && f34->v7.img.contains_display_cfg) {
|
if (f34->v7.has_display_cfg && f34->v7.img.contains_display_cfg) {
|
||||||
dev_info(&f34->fn->dev, "Writing display config...\n");
|
dev_info(&f34->fn->dev, "Writing display config...\n");
|
||||||
|
|
||||||
ret = rmi_f34v7_write_dp_config(f34);
|
ret = rmi_f34v7_write_dp_config(f34);
|
||||||
if (ret < 0)
|
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) {
|
||||||
if (f34->v7.has_guest_code && f34->v7.img.contains_guest_code) {
|
dev_info(&f34->fn->dev, "Writing guest code...\n");
|
||||||
dev_info(&f34->fn->dev, "Writing guest code...\n");
|
|
||||||
|
|
||||||
ret = rmi_f34v7_write_guest_code(f34);
|
ret = rmi_f34v7_write_guest_code(f34);
|
||||||
if (ret < 0)
|
if (ret < 0)
|
||||||
goto fail;
|
return ret;
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fail:
|
return 0;
|
||||||
return ret;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static int rmi_f34v7_enter_flash_prog(struct f34_data *f34)
|
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)
|
if (ret < 0)
|
||||||
return ret;
|
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;
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
init_completion(&f34->v7.cmd_done);
|
init_completion(&f34->v7.cmd_done);
|
||||||
|
|
||||||
@ -1297,7 +1118,7 @@ static int rmi_f34v7_enter_flash_prog(struct f34_data *f34)
|
|||||||
if (ret < 0)
|
if (ret < 0)
|
||||||
return ret;
|
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)
|
if (ret < 0)
|
||||||
return ret;
|
return ret;
|
||||||
|
|
||||||
@ -1308,39 +1129,16 @@ int rmi_f34v7_start_reflash(struct f34_data *f34, const struct firmware *fw)
|
|||||||
{
|
{
|
||||||
int ret = 0;
|
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.config_area = v7_UI_CONFIG_AREA;
|
||||||
f34->v7.image = fw->data;
|
f34->v7.image = fw->data;
|
||||||
|
|
||||||
ret = rmi_f34v7_parse_image_info(f34);
|
ret = rmi_f34v7_parse_image_info(f34);
|
||||||
if (ret < 0)
|
if (ret < 0)
|
||||||
goto exit;
|
return ret;
|
||||||
|
|
||||||
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;
|
|
||||||
}
|
|
||||||
|
|
||||||
dev_info(&f34->fn->dev, "Firmware image OK\n");
|
dev_info(&f34->fn->dev, "Firmware image OK\n");
|
||||||
|
|
||||||
ret = rmi_f34v7_read_flash_status(f34);
|
return rmi_f34v7_enter_flash_prog(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;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
int rmi_f34v7_probe(struct f34_data *f34)
|
int rmi_f34v7_probe(struct f34_data *f34)
|
||||||
@ -1384,6 +1182,5 @@ int rmi_f34v7_probe(struct f34_data *f34)
|
|||||||
if (ret < 0)
|
if (ret < 0)
|
||||||
return ret;
|
return ret;
|
||||||
|
|
||||||
f34->v7.force_update = true;
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
@ -390,8 +390,8 @@ static int rmi_f54_vidioc_querycap(struct file *file, void *priv,
|
|||||||
{
|
{
|
||||||
struct f54_data *f54 = video_drvdata(file);
|
struct f54_data *f54 = video_drvdata(file);
|
||||||
|
|
||||||
strlcpy(cap->driver, F54_NAME, sizeof(cap->driver));
|
strscpy(cap->driver, F54_NAME, sizeof(cap->driver));
|
||||||
strlcpy(cap->card, SYNAPTICS_INPUT_DEVICE_NAME, sizeof(cap->card));
|
strscpy(cap->card, SYNAPTICS_INPUT_DEVICE_NAME, sizeof(cap->card));
|
||||||
snprintf(cap->bus_info, sizeof(cap->bus_info),
|
snprintf(cap->bus_info, sizeof(cap->bus_info),
|
||||||
"rmi4:%s", dev_name(&f54->fn->dev));
|
"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;
|
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;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -696,7 +696,7 @@ static int rmi_f54_probe(struct rmi_function *fn)
|
|||||||
rmi_f54_set_input(f54, 0);
|
rmi_f54_set_input(f54, 0);
|
||||||
|
|
||||||
/* register video device */
|
/* 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);
|
ret = v4l2_device_register(&fn->dev, &f54->v4l2);
|
||||||
if (ret) {
|
if (ret) {
|
||||||
dev_err(&fn->dev, "Unable to register video dev.\n");
|
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->write = altera_ps2_write;
|
||||||
serio->open = altera_ps2_open;
|
serio->open = altera_ps2_open;
|
||||||
serio->close = altera_ps2_close;
|
serio->close = altera_ps2_close;
|
||||||
strlcpy(serio->name, dev_name(&pdev->dev), sizeof(serio->name));
|
strscpy(serio->name, dev_name(&pdev->dev), sizeof(serio->name));
|
||||||
strlcpy(serio->phys, dev_name(&pdev->dev), sizeof(serio->phys));
|
strscpy(serio->phys, dev_name(&pdev->dev), sizeof(serio->phys));
|
||||||
serio->port_data = ps2if;
|
serio->port_data = ps2if;
|
||||||
serio->dev.parent = &pdev->dev;
|
serio->dev.parent = &pdev->dev;
|
||||||
ps2if->io = serio;
|
ps2if->io = serio;
|
||||||
|
@ -126,8 +126,8 @@ static int amba_kmi_probe(struct amba_device *dev,
|
|||||||
io->write = amba_kmi_write;
|
io->write = amba_kmi_write;
|
||||||
io->open = amba_kmi_open;
|
io->open = amba_kmi_open;
|
||||||
io->close = amba_kmi_close;
|
io->close = amba_kmi_close;
|
||||||
strlcpy(io->name, dev_name(&dev->dev), sizeof(io->name));
|
strscpy(io->name, dev_name(&dev->dev), sizeof(io->name));
|
||||||
strlcpy(io->phys, dev_name(&dev->dev), sizeof(io->phys));
|
strscpy(io->phys, dev_name(&dev->dev), sizeof(io->phys));
|
||||||
io->port_data = kmi;
|
io->port_data = kmi;
|
||||||
io->dev.parent = &dev->dev;
|
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