Skip to content
Projects
Groups
Snippets
Help
Loading...
Help
Support
Keyboard shortcuts
?
Submit feedback
Contribute to GitLab
Sign in / Register
Toggle navigation
L
linux
Project overview
Project overview
Details
Activity
Releases
Repository
Repository
Files
Commits
Branches
Tags
Contributors
Graph
Compare
Issues
0
Issues
0
List
Boards
Labels
Milestones
Merge Requests
0
Merge Requests
0
Analytics
Analytics
Repository
Value Stream
Wiki
Wiki
Snippets
Snippets
Members
Members
Collapse sidebar
Close sidebar
Activity
Graph
Create a new issue
Commits
Issue Boards
Open sidebar
Kirill Smelkov
linux
Commits
14e0c731
Commit
14e0c731
authored
May 10, 2019
by
Dmitry Torokhov
Browse files
Options
Browse Files
Download
Plain Diff
Merge branch 'next' into for-linus
Prepare input updates for 5.2 merge window.
parents
3a349763
0981949d
Changes
30
Hide whitespace changes
Inline
Side-by-side
Showing
30 changed files
with
2297 additions
and
74 deletions
+2297
-74
Documentation/devicetree/bindings/input/gpio-vibrator.yaml
Documentation/devicetree/bindings/input/gpio-vibrator.yaml
+37
-0
Documentation/devicetree/bindings/input/lpc32xx-key.txt
Documentation/devicetree/bindings/input/lpc32xx-key.txt
+4
-1
Documentation/devicetree/bindings/input/microchip,qt1050.txt
Documentation/devicetree/bindings/input/microchip,qt1050.txt
+78
-0
Documentation/devicetree/bindings/input/sun4i-lradc-keys.txt
Documentation/devicetree/bindings/input/sun4i-lradc-keys.txt
+4
-2
Documentation/devicetree/bindings/input/touchscreen/goodix.txt
...entation/devicetree/bindings/input/touchscreen/goodix.txt
+3
-0
Documentation/devicetree/bindings/input/touchscreen/iqs5xx.txt
...entation/devicetree/bindings/input/touchscreen/iqs5xx.txt
+80
-0
Documentation/devicetree/bindings/vendor-prefixes.txt
Documentation/devicetree/bindings/vendor-prefixes.txt
+1
-0
drivers/hid/hid-input.c
drivers/hid/hid-input.c
+2
-0
drivers/input/evdev.c
drivers/input/evdev.c
+3
-4
drivers/input/keyboard/Kconfig
drivers/input/keyboard/Kconfig
+11
-0
drivers/input/keyboard/Makefile
drivers/input/keyboard/Makefile
+1
-0
drivers/input/keyboard/atkbd.c
drivers/input/keyboard/atkbd.c
+2
-0
drivers/input/keyboard/qt1050.c
drivers/input/keyboard/qt1050.c
+598
-0
drivers/input/keyboard/snvs_pwrkey.c
drivers/input/keyboard/snvs_pwrkey.c
+4
-26
drivers/input/keyboard/sun4i-lradc-keys.c
drivers/input/keyboard/sun4i-lradc-keys.c
+34
-4
drivers/input/misc/Kconfig
drivers/input/misc/Kconfig
+12
-0
drivers/input/misc/Makefile
drivers/input/misc/Makefile
+1
-0
drivers/input/misc/gpio-vibra.c
drivers/input/misc/gpio-vibra.c
+207
-0
drivers/input/mouse/psmouse-base.c
drivers/input/mouse/psmouse-base.c
+2
-0
drivers/input/rmi4/rmi_f54.c
drivers/input/rmi4/rmi_f54.c
+6
-15
drivers/input/serio/Kconfig
drivers/input/serio/Kconfig
+1
-0
drivers/input/serio/hyperv-keyboard.c
drivers/input/serio/hyperv-keyboard.c
+2
-0
drivers/input/serio/i8042.c
drivers/input/serio/i8042.c
+0
-3
drivers/input/serio/libps2.c
drivers/input/serio/libps2.c
+1
-0
drivers/input/touchscreen/Kconfig
drivers/input/touchscreen/Kconfig
+10
-0
drivers/input/touchscreen/Makefile
drivers/input/touchscreen/Makefile
+1
-0
drivers/input/touchscreen/edt-ft5x06.c
drivers/input/touchscreen/edt-ft5x06.c
+4
-19
drivers/input/touchscreen/goodix.c
drivers/input/touchscreen/goodix.c
+54
-0
drivers/input/touchscreen/iqs5xx.c
drivers/input/touchscreen/iqs5xx.c
+1133
-0
include/uapi/linux/input-event-codes.h
include/uapi/linux/input-event-codes.h
+1
-0
No files found.
Documentation/devicetree/bindings/input/gpio-vibrator.yaml
0 → 100644
View file @
14e0c731
# SPDX-License-Identifier: GPL-2.0
%YAML
1.2
---
$id
:
http://devicetree.org/schemas/bindings/input/gpio-vibrator.yaml#
$schema
:
http://devicetree.org/meta-schemas/core.yaml#
title
:
GPIO vibrator
maintainers
:
-
Luca Weiss <luca@z3ntu.xyz>
description
:
|+
Registers a GPIO device as vibrator, where the on/off capability is controlled by a GPIO.
properties
:
compatible
:
const
:
gpio-vibrator
enable-gpios
:
maxItems
:
1
vcc-supply
:
description
:
Regulator that provides power
required
:
-
compatible
-
enable-gpios
examples
:
-
|
#include <dt-bindings/gpio/gpio.h>
vibrator {
compatible = "gpio-vibrator";
enable-gpios = <&msmgpio 86 GPIO_ACTIVE_HIGH>;
vcc-supply = <&pm8941_l18>;
};
Documentation/devicetree/bindings/input/lpc32xx-key.txt
View file @
14e0c731
...
...
@@ -8,6 +8,7 @@ Required Properties:
- reg: Physical base address of the controller and length of memory mapped
region.
- interrupts: The interrupt number to the cpu.
- clocks: phandle to clock controller plus clock-specifier pair
- nxp,debounce-delay-ms: Debounce delay in ms
- nxp,scan-delay-ms: Repeated scan period in ms
- linux,keymap: the key-code to be reported when the key is pressed
...
...
@@ -22,7 +23,9 @@ Example:
key@40050000 {
compatible = "nxp,lpc3220-key";
reg = <0x40050000 0x1000>;
interrupts = <54 0>;
clocks = <&clk LPC32XX_CLK_KEY>;
interrupt-parent = <&sic1>;
interrupts = <22 IRQ_TYPE_LEVEL_HIGH>;
keypad,num-rows = <1>;
keypad,num-columns = <1>;
nxp,debounce-delay-ms = <3>;
...
...
Documentation/devicetree/bindings/input/microchip,qt1050.txt
0 → 100644
View file @
14e0c731
Microchip AT42QT1050 Five-channel Touch Sensor IC
The AT42QT1050 (QT1050) is a QTouchADC sensor device. The device can sense from
one to five keys, dependent on mode. The QT1050 includes all signal processing
functions necessary to provide stable sensing under a wide variety of changing
conditions, and the outputs are fully debounced.
The touchkey device node should be placed inside an I2C bus node.
Required properties:
- compatible: Must be "microchip,qt1050"
- reg: The I2C address of the device
- interrupts: The sink for the touchpad's IRQ output,
see ../interrupt-controller/interrupts.txt
Optional properties:
- wakeup-source: touch keys can be used as a wakeup source
Each button (key) is represented as a sub-node:
Each not specified key or key with linux,code set to KEY_RESERVED gets disabled
in HW.
Subnode properties:
- linux,code: Keycode to emit.
- reg: The key number. Valid values: 0, 1, 2, 3, 4.
Optional subnode-properties:
If a optional property is missing or has a invalid value the default value is
taken.
- microchip,pre-charge-time-ns:
Each touchpad need some time to precharge. The value depends on the mechanical
layout.
Valid value range: 0 - 637500; values must be a multiple of 2500;
default is 0.
- microchip,average-samples:
Number of data samples which are averaged for each read.
Valid values: 1, 4, 16, 64, 256, 1024, 4096, 16384; default is 1.
- microchip,average-scaling:
The scaling factor which is used to scale the average-samples.
Valid values: 1, 2, 4, 8, 16, 32, 64, 128; default is 1.
- microchip,threshold:
Number of counts to register a touch detection.
Valid value range: 0 - 255; default is 20.
Example:
QT1050 with 3 non continuous keys, key2 and key4 are disabled.
touchkeys@41 {
compatible = "microchip,qt1050";
reg = <0x41>;
interrupt-parent = <&gpio0>;
interrupts = <17 IRQ_TYPE_EDGE_FALLING>;
up@0 {
reg = <0>;
linux,code = <KEY_UP>;
microchip,average-samples = <64>;
microchip,average-scaling = <16>;
microchip,pre-charge-time-ns = <10000>;
};
right@1 {
reg = <1>;
linux,code = <KEY_RIGHT>;
microchip,average-samples = <64>;
microchip,average-scaling = <8>;
};
down@3 {
reg = <3>;
linux,code = <KEY_DOWN>;
microchip,average-samples = <256>;
microchip,average-scaling = <16>;
};
};
Documentation/devicetree/bindings/input/sun4i-lradc-keys.txt
View file @
14e0c731
...
...
@@ -2,12 +2,14 @@ Allwinner sun4i low res adc attached tablet keys
------------------------------------------------
Required properties:
- compatible: "allwinner,sun4i-a10-lradc-keys"
- compatible: should be one of the following string:
"allwinner,sun4i-a10-lradc-keys"
"allwinner,sun8i-a83t-r-lradc"
- reg: mmio address range of the chip
- interrupts: interrupt to which the chip is connected
- vref-supply: powersupply for the lradc reference voltage
Each key is represented as a sub-node of
"allwinner,sun4i-a10-lradc-keys"
:
Each key is represented as a sub-node of
the compatible mentioned above
:
Required subnode-properties:
- label: Descriptive name of the key.
...
...
Documentation/devicetree/bindings/input/touchscreen/goodix.txt
View file @
14e0c731
...
...
@@ -3,6 +3,7 @@ Device tree bindings for Goodix GT9xx series touchscreen controller
Required properties:
- compatible : Should be "goodix,gt1151"
or "goodix,gt5663"
or "goodix,gt5688"
or "goodix,gt911"
or "goodix,gt9110"
...
...
@@ -19,6 +20,8 @@ Optional properties:
- irq-gpios : GPIO pin used for IRQ. The driver uses the
interrupt gpio pin as output to reset the device.
- reset-gpios : GPIO pin used for reset
- AVDD28-supply : Analog power supply regulator on AVDD28 pin
- VDDIO-supply : GPIO power supply regulator on VDDIO pin
- touchscreen-inverted-x
- touchscreen-inverted-y
- touchscreen-size-x
...
...
Documentation/devicetree/bindings/input/touchscreen/iqs5xx.txt
0 → 100644
View file @
14e0c731
Azoteq IQS550/572/525 Trackpad/Touchscreen Controller
Required properties:
- compatible : Must be equal to one of the following:
"azoteq,iqs550"
"azoteq,iqs572"
"azoteq,iqs525"
- reg : I2C slave address for the device.
- interrupts : GPIO to which the device's active-high RDY
output is connected (see [0]).
- reset-gpios : GPIO to which the device's active-low NRST
input is connected (see [1]).
Optional properties:
- touchscreen-min-x : See [2].
- touchscreen-min-y : See [2].
- touchscreen-size-x : See [2]. If this property is omitted, the
maximum x-coordinate is specified by the
device's "X Resolution" register.
- touchscreen-size-y : See [2]. If this property is omitted, the
maximum y-coordinate is specified by the
device's "Y Resolution" register.
- touchscreen-max-pressure : See [2]. Pressure is expressed as the sum of
the deltas across all channels impacted by a
touch event. A channel's delta is calculated
as its count value minus a reference, where
the count value is inversely proportional to
the channel's capacitance.
- touchscreen-fuzz-x : See [2].
- touchscreen-fuzz-y : See [2].
- touchscreen-fuzz-pressure : See [2].
- touchscreen-inverted-x : See [2]. Inversion is applied relative to that
which may already be specified by the device's
FLIP_X and FLIP_Y register fields.
- touchscreen-inverted-y : See [2]. Inversion is applied relative to that
which may already be specified by the device's
FLIP_X and FLIP_Y register fields.
- touchscreen-swapped-x-y : See [2]. Swapping is applied relative to that
which may already be specified by the device's
SWITCH_XY_AXIS register field.
[0]: Documentation/devicetree/bindings/interrupt-controller/interrupts.txt
[1]: Documentation/devicetree/bindings/gpio/gpio.txt
[2]: Documentation/devicetree/bindings/input/touchscreen/touchscreen.txt
Example:
&i2c1 {
/* ... */
touchscreen@74 {
compatible = "azoteq,iqs550";
reg = <0x74>;
interrupt-parent = <&gpio>;
interrupts = <17 4>;
reset-gpios = <&gpio 27 1>;
touchscreen-size-x = <640>;
touchscreen-size-y = <480>;
touchscreen-max-pressure = <16000>;
};
/* ... */
};
Documentation/devicetree/bindings/vendor-prefixes.txt
View file @
14e0c731
...
...
@@ -53,6 +53,7 @@ avic Shanghai AVIC Optoelectronics Co., Ltd.
avnet Avnet, Inc.
axentia Axentia Technologies AB
axis Axis Communications AB
azoteq Azoteq (Pty) Ltd
bananapi BIPAI KEJI LIMITED
bhf Beckhoff Automation GmbH & Co. KG
bitmain Bitmain Technologies
...
...
drivers/hid/hid-input.c
View file @
14e0c731
...
...
@@ -1036,6 +1036,8 @@ static void hidinput_configure_usage(struct hid_input *hidinput, struct hid_fiel
case
0x28b
:
map_key_clear
(
KEY_FORWARDMAIL
);
break
;
case
0x28c
:
map_key_clear
(
KEY_SEND
);
break
;
case
0x29d
:
map_key_clear
(
KEY_KBD_LAYOUT_NEXT
);
break
;
case
0x2c7
:
map_key_clear
(
KEY_KBDINPUTASSIST_PREV
);
break
;
case
0x2c8
:
map_key_clear
(
KEY_KBDINPUTASSIST_NEXT
);
break
;
case
0x2c9
:
map_key_clear
(
KEY_KBDINPUTASSIST_PREVGROUP
);
break
;
...
...
drivers/input/evdev.c
View file @
14e0c731
...
...
@@ -503,14 +503,13 @@ static int evdev_open(struct inode *inode, struct file *file)
{
struct
evdev
*
evdev
=
container_of
(
inode
->
i_cdev
,
struct
evdev
,
cdev
);
unsigned
int
bufsize
=
evdev_compute_buffer_size
(
evdev
->
handle
.
dev
);
unsigned
int
size
=
sizeof
(
struct
evdev_client
)
+
bufsize
*
sizeof
(
struct
input_event
);
struct
evdev_client
*
client
;
int
error
;
client
=
kzalloc
(
size
,
GFP_KERNEL
|
__GFP_NOWARN
);
client
=
kzalloc
(
struct_size
(
client
,
buffer
,
bufsize
),
GFP_KERNEL
|
__GFP_NOWARN
);
if
(
!
client
)
client
=
vzalloc
(
s
ize
);
client
=
vzalloc
(
s
truct_size
(
client
,
buffer
,
bufsize
)
);
if
(
!
client
)
return
-
ENOMEM
;
...
...
drivers/input/keyboard/Kconfig
View file @
14e0c731
...
...
@@ -137,6 +137,17 @@ config KEYBOARD_ATKBD_RDI_KEYCODES
right-hand column will be interpreted as the key shown in the
left-hand column.
config KEYBOARD_QT1050
tristate "Microchip AT42QT1050 Touch Sensor Chip"
depends on I2C
select REGMAP_I2C
help
Say Y here if you want to use Microchip AT42QT1050 QTouch
Sensor chip as input device.
To compile this driver as a module, choose M here:
the module will be called qt1050
config KEYBOARD_QT1070
tristate "Atmel AT42QT1070 Touch Sensor Chip"
depends on I2C
...
...
drivers/input/keyboard/Makefile
View file @
14e0c731
...
...
@@ -50,6 +50,7 @@ obj-$(CONFIG_KEYBOARD_OPENCORES) += opencores-kbd.o
obj-$(CONFIG_KEYBOARD_PMIC8XXX)
+=
pmic8xxx-keypad.o
obj-$(CONFIG_KEYBOARD_PXA27x)
+=
pxa27x_keypad.o
obj-$(CONFIG_KEYBOARD_PXA930_ROTARY)
+=
pxa930_rotary.o
obj-$(CONFIG_KEYBOARD_QT1050)
+=
qt1050.o
obj-$(CONFIG_KEYBOARD_QT1070)
+=
qt1070.o
obj-$(CONFIG_KEYBOARD_QT2160)
+=
qt2160.o
obj-$(CONFIG_KEYBOARD_SAMSUNG)
+=
samsung-keypad.o
...
...
drivers/input/keyboard/atkbd.c
View file @
14e0c731
...
...
@@ -401,6 +401,8 @@ static irqreturn_t atkbd_interrupt(struct serio *serio, unsigned char data,
if
(
ps2_handle_response
(
&
atkbd
->
ps2dev
,
data
))
goto
out
;
pm_wakeup_event
(
&
serio
->
dev
,
0
);
if
(
!
atkbd
->
enabled
)
goto
out
;
...
...
drivers/input/keyboard/qt1050.c
0 → 100644
View file @
14e0c731
// SPDX-License-Identifier: GPL-2.0
/*
* Microchip AT42QT1050 QTouch Sensor Controller
*
* Copyright (C) 2019 Pengutronix, Marco Felsch <kernel@pengutronix.de>
*
* Base on AT42QT1070 driver by:
* Bo Shen <voice.shen@atmel.com>
* Copyright (C) 2011 Atmel
*/
#include <linux/delay.h>
#include <linux/i2c.h>
#include <linux/input.h>
#include <linux/interrupt.h>
#include <linux/kernel.h>
#include <linux/log2.h>
#include <linux/module.h>
#include <linux/of.h>
#include <linux/regmap.h>
/* Chip ID */
#define QT1050_CHIP_ID 0x00
#define QT1050_CHIP_ID_VER 0x46
/* Firmware version */
#define QT1050_FW_VERSION 0x01
/* Detection status */
#define QT1050_DET_STATUS 0x02
/* Key status */
#define QT1050_KEY_STATUS 0x03
/* Key Signals */
#define QT1050_KEY_SIGNAL_0_MSB 0x06
#define QT1050_KEY_SIGNAL_0_LSB 0x07
#define QT1050_KEY_SIGNAL_1_MSB 0x08
#define QT1050_KEY_SIGNAL_1_LSB 0x09
#define QT1050_KEY_SIGNAL_2_MSB 0x0c
#define QT1050_KEY_SIGNAL_2_LSB 0x0d
#define QT1050_KEY_SIGNAL_3_MSB 0x0e
#define QT1050_KEY_SIGNAL_3_LSB 0x0f
#define QT1050_KEY_SIGNAL_4_MSB 0x10
#define QT1050_KEY_SIGNAL_4_LSB 0x11
/* Reference data */
#define QT1050_REF_DATA_0_MSB 0x14
#define QT1050_REF_DATA_0_LSB 0x15
#define QT1050_REF_DATA_1_MSB 0x16
#define QT1050_REF_DATA_1_LSB 0x17
#define QT1050_REF_DATA_2_MSB 0x1a
#define QT1050_REF_DATA_2_LSB 0x1b
#define QT1050_REF_DATA_3_MSB 0x1c
#define QT1050_REF_DATA_3_LSB 0x1d
#define QT1050_REF_DATA_4_MSB 0x1e
#define QT1050_REF_DATA_4_LSB 0x1f
/* Negative threshold level */
#define QT1050_NTHR_0 0x21
#define QT1050_NTHR_1 0x22
#define QT1050_NTHR_2 0x24
#define QT1050_NTHR_3 0x25
#define QT1050_NTHR_4 0x26
/* Pulse / Scale */
#define QT1050_PULSE_SCALE_0 0x28
#define QT1050_PULSE_SCALE_1 0x29
#define QT1050_PULSE_SCALE_2 0x2b
#define QT1050_PULSE_SCALE_3 0x2c
#define QT1050_PULSE_SCALE_4 0x2d
/* Detection integrator counter / AKS */
#define QT1050_DI_AKS_0 0x2f
#define QT1050_DI_AKS_1 0x30
#define QT1050_DI_AKS_2 0x32
#define QT1050_DI_AKS_3 0x33
#define QT1050_DI_AKS_4 0x34
/* Charge Share Delay */
#define QT1050_CSD_0 0x36
#define QT1050_CSD_1 0x37
#define QT1050_CSD_2 0x39
#define QT1050_CSD_3 0x3a
#define QT1050_CSD_4 0x3b
/* Low Power Mode */
#define QT1050_LPMODE 0x3d
/* Calibration and Reset */
#define QT1050_RES_CAL 0x3f
#define QT1050_RES_CAL_RESET BIT(7)
#define QT1050_RES_CAL_CALIBRATE BIT(1)
#define QT1050_MAX_KEYS 5
#define QT1050_RESET_TIME 255
struct
qt1050_key_regs
{
unsigned
int
nthr
;
unsigned
int
pulse_scale
;
unsigned
int
di_aks
;
unsigned
int
csd
;
};
struct
qt1050_key
{
u32
num
;
u32
charge_delay
;
u32
thr_cnt
;
u32
samples
;
u32
scale
;
u32
keycode
;
};
struct
qt1050_priv
{
struct
i2c_client
*
client
;
struct
input_dev
*
input
;
struct
regmap
*
regmap
;
struct
qt1050_key
keys
[
QT1050_MAX_KEYS
];
unsigned
short
keycodes
[
QT1050_MAX_KEYS
];
u8
reg_keys
;
u8
last_keys
;
};
static
const
struct
qt1050_key_regs
qt1050_key_regs_data
[]
=
{
{
.
nthr
=
QT1050_NTHR_0
,
.
pulse_scale
=
QT1050_PULSE_SCALE_0
,
.
di_aks
=
QT1050_DI_AKS_0
,
.
csd
=
QT1050_CSD_0
,
},
{
.
nthr
=
QT1050_NTHR_1
,
.
pulse_scale
=
QT1050_PULSE_SCALE_1
,
.
di_aks
=
QT1050_DI_AKS_1
,
.
csd
=
QT1050_CSD_1
,
},
{
.
nthr
=
QT1050_NTHR_2
,
.
pulse_scale
=
QT1050_PULSE_SCALE_2
,
.
di_aks
=
QT1050_DI_AKS_2
,
.
csd
=
QT1050_CSD_2
,
},
{
.
nthr
=
QT1050_NTHR_3
,
.
pulse_scale
=
QT1050_PULSE_SCALE_3
,
.
di_aks
=
QT1050_DI_AKS_3
,
.
csd
=
QT1050_CSD_3
,
},
{
.
nthr
=
QT1050_NTHR_4
,
.
pulse_scale
=
QT1050_PULSE_SCALE_4
,
.
di_aks
=
QT1050_DI_AKS_4
,
.
csd
=
QT1050_CSD_4
,
}
};
static
bool
qt1050_volatile_reg
(
struct
device
*
dev
,
unsigned
int
reg
)
{
switch
(
reg
)
{
case
QT1050_DET_STATUS
:
case
QT1050_KEY_STATUS
:
case
QT1050_KEY_SIGNAL_0_MSB
:
case
QT1050_KEY_SIGNAL_0_LSB
:
case
QT1050_KEY_SIGNAL_1_MSB
:
case
QT1050_KEY_SIGNAL_1_LSB
:
case
QT1050_KEY_SIGNAL_2_MSB
:
case
QT1050_KEY_SIGNAL_2_LSB
:
case
QT1050_KEY_SIGNAL_3_MSB
:
case
QT1050_KEY_SIGNAL_3_LSB
:
case
QT1050_KEY_SIGNAL_4_MSB
:
case
QT1050_KEY_SIGNAL_4_LSB
:
return
true
;
default:
return
false
;
}
}
static
const
struct
regmap_range
qt1050_readable_ranges
[]
=
{
regmap_reg_range
(
QT1050_CHIP_ID
,
QT1050_KEY_STATUS
),
regmap_reg_range
(
QT1050_KEY_SIGNAL_0_MSB
,
QT1050_KEY_SIGNAL_1_LSB
),
regmap_reg_range
(
QT1050_KEY_SIGNAL_2_MSB
,
QT1050_KEY_SIGNAL_4_LSB
),
regmap_reg_range
(
QT1050_REF_DATA_0_MSB
,
QT1050_REF_DATA_1_LSB
),
regmap_reg_range
(
QT1050_REF_DATA_2_MSB
,
QT1050_REF_DATA_4_LSB
),
regmap_reg_range
(
QT1050_NTHR_0
,
QT1050_NTHR_1
),
regmap_reg_range
(
QT1050_NTHR_2
,
QT1050_NTHR_4
),
regmap_reg_range
(
QT1050_PULSE_SCALE_0
,
QT1050_PULSE_SCALE_1
),
regmap_reg_range
(
QT1050_PULSE_SCALE_2
,
QT1050_PULSE_SCALE_4
),
regmap_reg_range
(
QT1050_DI_AKS_0
,
QT1050_DI_AKS_1
),
regmap_reg_range
(
QT1050_DI_AKS_2
,
QT1050_DI_AKS_4
),
regmap_reg_range
(
QT1050_CSD_0
,
QT1050_CSD_1
),
regmap_reg_range
(
QT1050_CSD_2
,
QT1050_RES_CAL
),
};
static
const
struct
regmap_access_table
qt1050_readable_table
=
{
.
yes_ranges
=
qt1050_readable_ranges
,
.
n_yes_ranges
=
ARRAY_SIZE
(
qt1050_readable_ranges
),
};
static
const
struct
regmap_range
qt1050_writeable_ranges
[]
=
{
regmap_reg_range
(
QT1050_NTHR_0
,
QT1050_NTHR_1
),
regmap_reg_range
(
QT1050_NTHR_2
,
QT1050_NTHR_4
),
regmap_reg_range
(
QT1050_PULSE_SCALE_0
,
QT1050_PULSE_SCALE_1
),
regmap_reg_range
(
QT1050_PULSE_SCALE_2
,
QT1050_PULSE_SCALE_4
),
regmap_reg_range
(
QT1050_DI_AKS_0
,
QT1050_DI_AKS_1
),
regmap_reg_range
(
QT1050_DI_AKS_2
,
QT1050_DI_AKS_4
),
regmap_reg_range
(
QT1050_CSD_0
,
QT1050_CSD_1
),
regmap_reg_range
(
QT1050_CSD_2
,
QT1050_RES_CAL
),
};
static
const
struct
regmap_access_table
qt1050_writeable_table
=
{
.
yes_ranges
=
qt1050_writeable_ranges
,
.
n_yes_ranges
=
ARRAY_SIZE
(
qt1050_writeable_ranges
),
};
static
struct
regmap_config
qt1050_regmap_config
=
{
.
reg_bits
=
8
,
.
val_bits
=
8
,
.
max_register
=
QT1050_RES_CAL
,
.
cache_type
=
REGCACHE_RBTREE
,
.
wr_table
=
&
qt1050_writeable_table
,
.
rd_table
=
&
qt1050_readable_table
,
.
volatile_reg
=
qt1050_volatile_reg
,
};
static
bool
qt1050_identify
(
struct
qt1050_priv
*
ts
)
{
unsigned
int
val
;
int
err
;
/* Read Chip ID */
regmap_read
(
ts
->
regmap
,
QT1050_CHIP_ID
,
&
val
);
if
(
val
!=
QT1050_CHIP_ID_VER
)
{
dev_err
(
&
ts
->
client
->
dev
,
"ID %d not supported
\n
"
,
val
);
return
false
;
}
/* Read firmware version */
err
=
regmap_read
(
ts
->
regmap
,
QT1050_FW_VERSION
,
&
val
);
if
(
err
)
{
dev_err
(
&
ts
->
client
->
dev
,
"could not read the firmware version
\n
"
);
return
false
;
}
dev_info
(
&
ts
->
client
->
dev
,
"AT42QT1050 firmware version %1d.%1d
\n
"
,
val
>>
4
,
val
&
0xf
);
return
true
;
}
static
irqreturn_t
qt1050_irq_threaded
(
int
irq
,
void
*
dev_id
)
{
struct
qt1050_priv
*
ts
=
dev_id
;
struct
input_dev
*
input
=
ts
->
input
;
unsigned
long
new_keys
,
changed
;
unsigned
int
val
;
int
i
,
err
;
/* Read the detected status register, thus clearing interrupt */
err
=
regmap_read
(
ts
->
regmap
,
QT1050_DET_STATUS
,
&
val
);
if
(
err
)
{
dev_err
(
&
ts
->
client
->
dev
,
"Fail to read detection status: %d
\n
"
,
err
);
return
IRQ_NONE
;
}
/* Read which key changed, keys are not continuous */
err
=
regmap_read
(
ts
->
regmap
,
QT1050_KEY_STATUS
,
&
val
);
if
(
err
)
{
dev_err
(
&
ts
->
client
->
dev
,
"Fail to determine the key status: %d
\n
"
,
err
);
return
IRQ_NONE
;
}
new_keys
=
(
val
&
0x70
)
>>
2
|
(
val
&
0x6
)
>>
1
;
changed
=
ts
->
last_keys
^
new_keys
;
/* Report registered keys only */
changed
&=
ts
->
reg_keys
;
for_each_set_bit
(
i
,
&
changed
,
QT1050_MAX_KEYS
)
input_report_key
(
input
,
ts
->
keys
[
i
].
keycode
,
test_bit
(
i
,
&
new_keys
));
ts
->
last_keys
=
new_keys
;
input_sync
(
input
);
return
IRQ_HANDLED
;
}
static
const
struct
qt1050_key_regs
*
qt1050_get_key_regs
(
int
key_num
)
{
return
&
qt1050_key_regs_data
[
key_num
];
}
static
int
qt1050_set_key
(
struct
regmap
*
map
,
int
number
,
int
on
)
{
const
struct
qt1050_key_regs
*
key_regs
;
key_regs
=
qt1050_get_key_regs
(
number
);
return
regmap_update_bits
(
map
,
key_regs
->
di_aks
,
0xfc
,
on
?
BIT
(
4
)
:
0x00
);
}
static
int
qt1050_apply_fw_data
(
struct
qt1050_priv
*
ts
)
{
struct
regmap
*
map
=
ts
->
regmap
;
struct
qt1050_key
*
button
=
&
ts
->
keys
[
0
];
const
struct
qt1050_key_regs
*
key_regs
;
int
i
,
err
;
/* Disable all keys and enable only the specified ones */
for
(
i
=
0
;
i
<
QT1050_MAX_KEYS
;
i
++
)
{
err
=
qt1050_set_key
(
map
,
i
,
0
);
if
(
err
)
return
err
;
}
for
(
i
=
0
;
i
<
QT1050_MAX_KEYS
;
i
++
,
button
++
)
{
/* Keep KEY_RESERVED keys off */
if
(
button
->
keycode
==
KEY_RESERVED
)
continue
;
err
=
qt1050_set_key
(
map
,
button
->
num
,
1
);
if
(
err
)
return
err
;
key_regs
=
qt1050_get_key_regs
(
button
->
num
);
err
=
regmap_write
(
map
,
key_regs
->
pulse_scale
,
(
button
->
samples
<<
4
)
|
(
button
->
scale
));
if
(
err
)
return
err
;
err
=
regmap_write
(
map
,
key_regs
->
csd
,
button
->
charge_delay
);
if
(
err
)
return
err
;
err
=
regmap_write
(
map
,
key_regs
->
nthr
,
button
->
thr_cnt
);
if
(
err
)
return
err
;
}
return
0
;
}
static
int
qt1050_parse_fw
(
struct
qt1050_priv
*
ts
)
{
struct
device
*
dev
=
&
ts
->
client
->
dev
;
struct
fwnode_handle
*
child
;
int
nbuttons
;
nbuttons
=
device_get_child_node_count
(
dev
);
if
(
nbuttons
==
0
||
nbuttons
>
QT1050_MAX_KEYS
)
return
-
ENODEV
;
device_for_each_child_node
(
dev
,
child
)
{
struct
qt1050_key
button
;
/* Required properties */
if
(
fwnode_property_read_u32
(
child
,
"linux,code"
,
&
button
.
keycode
))
{
dev_err
(
dev
,
"Button without keycode
\n
"
);
goto
err
;
}
if
(
button
.
keycode
>=
KEY_MAX
)
{
dev_err
(
dev
,
"Invalid keycode 0x%x
\n
"
,
button
.
keycode
);
goto
err
;
}
if
(
fwnode_property_read_u32
(
child
,
"reg"
,
&
button
.
num
))
{
dev_err
(
dev
,
"Button without pad number
\n
"
);
goto
err
;
}
if
(
button
.
num
<
0
||
button
.
num
>
QT1050_MAX_KEYS
-
1
)
goto
err
;
ts
->
reg_keys
|=
BIT
(
button
.
num
);
/* Optional properties */
if
(
fwnode_property_read_u32
(
child
,
"microchip,pre-charge-time-ns"
,
&
button
.
charge_delay
))
{
button
.
charge_delay
=
0
;
}
else
{
if
(
button
.
charge_delay
%
2500
==
0
)
button
.
charge_delay
=
button
.
charge_delay
/
2500
;
else
button
.
charge_delay
=
0
;
}
if
(
fwnode_property_read_u32
(
child
,
"microchip,average-samples"
,
&
button
.
samples
))
{
button
.
samples
=
0
;
}
else
{
if
(
is_power_of_2
(
button
.
samples
))
button
.
samples
=
ilog2
(
button
.
samples
);
else
button
.
samples
=
0
;
}
if
(
fwnode_property_read_u32
(
child
,
"microchip,average-scaling"
,
&
button
.
scale
))
{
button
.
scale
=
0
;
}
else
{
if
(
is_power_of_2
(
button
.
scale
))
button
.
scale
=
ilog2
(
button
.
scale
);
else
button
.
scale
=
0
;
}
if
(
fwnode_property_read_u32
(
child
,
"microchip,threshold"
,
&
button
.
thr_cnt
))
{
button
.
thr_cnt
=
20
;
}
else
{
if
(
button
.
thr_cnt
>
255
)
button
.
thr_cnt
=
20
;
}
ts
->
keys
[
button
.
num
]
=
button
;
}
return
0
;
err:
fwnode_handle_put
(
child
);
return
-
EINVAL
;
}
static
int
qt1050_probe
(
struct
i2c_client
*
client
)
{
struct
qt1050_priv
*
ts
;
struct
input_dev
*
input
;
struct
device
*
dev
=
&
client
->
dev
;
struct
regmap
*
map
;
unsigned
int
status
,
i
;
int
err
;
/* Check basic functionality */
err
=
i2c_check_functionality
(
client
->
adapter
,
I2C_FUNC_SMBUS_BYTE
);
if
(
!
err
)
{
dev_err
(
&
client
->
dev
,
"%s adapter not supported
\n
"
,
dev_driver_string
(
&
client
->
adapter
->
dev
));
return
-
ENODEV
;
}
if
(
!
client
->
irq
)
{
dev_err
(
dev
,
"assign a irq line to this device
\n
"
);
return
-
EINVAL
;
}
ts
=
devm_kzalloc
(
dev
,
sizeof
(
*
ts
),
GFP_KERNEL
);
if
(
!
ts
)
return
-
ENOMEM
;
input
=
devm_input_allocate_device
(
dev
);
if
(
!
input
)
return
-
ENOMEM
;
map
=
devm_regmap_init_i2c
(
client
,
&
qt1050_regmap_config
);
if
(
IS_ERR
(
map
))
return
PTR_ERR
(
map
);
ts
->
client
=
client
;
ts
->
input
=
input
;
ts
->
regmap
=
map
;
i2c_set_clientdata
(
client
,
ts
);
/* Identify the qt1050 chip */
if
(
!
qt1050_identify
(
ts
))
return
-
ENODEV
;
/* Get pdata */
err
=
qt1050_parse_fw
(
ts
);
if
(
err
)
{
dev_err
(
dev
,
"Failed to parse firmware: %d
\n
"
,
err
);
return
err
;
}
input
->
name
=
"AT42QT1050 QTouch Sensor"
;
input
->
dev
.
parent
=
&
client
->
dev
;
input
->
id
.
bustype
=
BUS_I2C
;
/* Add the keycode */
input
->
keycode
=
ts
->
keycodes
;
input
->
keycodesize
=
sizeof
(
ts
->
keycodes
[
0
]);
input
->
keycodemax
=
QT1050_MAX_KEYS
;
__set_bit
(
EV_KEY
,
input
->
evbit
);
for
(
i
=
0
;
i
<
QT1050_MAX_KEYS
;
i
++
)
{
ts
->
keycodes
[
i
]
=
ts
->
keys
[
i
].
keycode
;
__set_bit
(
ts
->
keycodes
[
i
],
input
->
keybit
);
}
/* Trigger re-calibration */
err
=
regmap_update_bits
(
ts
->
regmap
,
QT1050_RES_CAL
,
0x7f
,
QT1050_RES_CAL_CALIBRATE
);
if
(
err
)
{
dev_err
(
dev
,
"Trigger calibration failed: %d
\n
"
,
err
);
return
err
;
}
err
=
regmap_read_poll_timeout
(
ts
->
regmap
,
QT1050_DET_STATUS
,
status
,
status
>>
7
==
1
,
10000
,
200000
);
if
(
err
)
{
dev_err
(
dev
,
"Calibration failed: %d
\n
"
,
err
);
return
err
;
}
/* Soft reset to set defaults */
err
=
regmap_update_bits
(
ts
->
regmap
,
QT1050_RES_CAL
,
QT1050_RES_CAL_RESET
,
QT1050_RES_CAL_RESET
);
if
(
err
)
{
dev_err
(
dev
,
"Trigger soft reset failed: %d
\n
"
,
err
);
return
err
;
}
msleep
(
QT1050_RESET_TIME
);
/* Set pdata */
err
=
qt1050_apply_fw_data
(
ts
);
if
(
err
)
{
dev_err
(
dev
,
"Failed to set firmware data: %d
\n
"
,
err
);
return
err
;
}
err
=
devm_request_threaded_irq
(
dev
,
client
->
irq
,
NULL
,
qt1050_irq_threaded
,
IRQF_ONESHOT
,
"qt1050"
,
ts
);
if
(
err
)
{
dev_err
(
&
client
->
dev
,
"Failed to request irq: %d
\n
"
,
err
);
return
err
;
}
/* Clear #CHANGE line */
err
=
regmap_read
(
ts
->
regmap
,
QT1050_DET_STATUS
,
&
status
);
if
(
err
)
{
dev_err
(
dev
,
"Failed to clear #CHANGE line level: %d
\n
"
,
err
);
return
err
;
}
/* Register the input device */
err
=
input_register_device
(
ts
->
input
);
if
(
err
)
{
dev_err
(
&
client
->
dev
,
"Failed to register input device: %d
\n
"
,
err
);
return
err
;
}
return
0
;
}
static
int
__maybe_unused
qt1050_suspend
(
struct
device
*
dev
)
{
struct
i2c_client
*
client
=
to_i2c_client
(
dev
);
struct
qt1050_priv
*
ts
=
i2c_get_clientdata
(
client
);
disable_irq
(
client
->
irq
);
/*
* Set measurement interval to 1s (125 x 8ms) if wakeup is allowed
* else turn off. The 1s interval seems to be a good compromise between
* low power and response time.
*/
return
regmap_write
(
ts
->
regmap
,
QT1050_LPMODE
,
device_may_wakeup
(
dev
)
?
125
:
0
);
}
static
int
__maybe_unused
qt1050_resume
(
struct
device
*
dev
)
{
struct
i2c_client
*
client
=
to_i2c_client
(
dev
);
struct
qt1050_priv
*
ts
=
i2c_get_clientdata
(
client
);
enable_irq
(
client
->
irq
);
/* Set measurement interval back to 16ms (2 x 8ms) */
return
regmap_write
(
ts
->
regmap
,
QT1050_LPMODE
,
2
);
}
static
SIMPLE_DEV_PM_OPS
(
qt1050_pm_ops
,
qt1050_suspend
,
qt1050_resume
);
static
const
struct
of_device_id
__maybe_unused
qt1050_of_match
[]
=
{
{
.
compatible
=
"microchip,qt1050"
,
},
{
},
};
MODULE_DEVICE_TABLE
(
of
,
qt1050_of_match
);
static
struct
i2c_driver
qt1050_driver
=
{
.
driver
=
{
.
name
=
"qt1050"
,
.
of_match_table
=
of_match_ptr
(
qt1050_of_match
),
.
pm
=
&
qt1050_pm_ops
,
},
.
probe_new
=
qt1050_probe
,
};
module_i2c_driver
(
qt1050_driver
);
MODULE_AUTHOR
(
"Marco Felsch <kernel@pengutronix.de"
);
MODULE_DESCRIPTION
(
"Driver for AT42QT1050 QTouch sensor"
);
MODULE_LICENSE
(
"GPL v2"
);
drivers/input/keyboard/snvs_pwrkey.c
View file @
14e0c731
...
...
@@ -15,6 +15,7 @@
#include <linux/of.h>
#include <linux/of_address.h>
#include <linux/platform_device.h>
#include <linux/pm_wakeirq.h>
#include <linux/mfd/syscon.h>
#include <linux/regmap.h>
...
...
@@ -167,28 +168,9 @@ static int imx_snvs_pwrkey_probe(struct platform_device *pdev)
}
device_init_wakeup
(
&
pdev
->
dev
,
pdata
->
wakeup
);
return
0
;
}
static
int
__maybe_unused
imx_snvs_pwrkey_suspend
(
struct
device
*
dev
)
{
struct
platform_device
*
pdev
=
to_platform_device
(
dev
);
struct
pwrkey_drv_data
*
pdata
=
platform_get_drvdata
(
pdev
);
if
(
device_may_wakeup
(
&
pdev
->
dev
))
enable_irq_wake
(
pdata
->
irq
);
return
0
;
}
static
int
__maybe_unused
imx_snvs_pwrkey_resume
(
struct
device
*
dev
)
{
struct
platform_device
*
pdev
=
to_platform_device
(
dev
);
struct
pwrkey_drv_data
*
pdata
=
platform_get_drvdata
(
pdev
);
if
(
device_may_wakeup
(
&
pdev
->
dev
))
disable_irq_wake
(
pdata
->
irq
);
error
=
dev_pm_set_wake_irq
(
&
pdev
->
dev
,
pdata
->
irq
);
if
(
error
)
dev_err
(
&
pdev
->
dev
,
"irq wake enable failed.
\n
"
);
return
0
;
}
...
...
@@ -199,13 +181,9 @@ static const struct of_device_id imx_snvs_pwrkey_ids[] = {
};
MODULE_DEVICE_TABLE
(
of
,
imx_snvs_pwrkey_ids
);
static
SIMPLE_DEV_PM_OPS
(
imx_snvs_pwrkey_pm_ops
,
imx_snvs_pwrkey_suspend
,
imx_snvs_pwrkey_resume
);
static
struct
platform_driver
imx_snvs_pwrkey_driver
=
{
.
driver
=
{
.
name
=
"snvs_pwrkey"
,
.
pm
=
&
imx_snvs_pwrkey_pm_ops
,
.
of_match_table
=
imx_snvs_pwrkey_ids
,
},
.
probe
=
imx_snvs_pwrkey_probe
,
...
...
drivers/input/keyboard/sun4i-lradc-keys.c
View file @
14e0c731
...
...
@@ -46,6 +46,7 @@
#define CONTINUE_TIME_SEL(x) ((x) << 16)
/* 4 bits */
#define KEY_MODE_SEL(x) ((x) << 12)
/* 2 bits */
#define LEVELA_B_CNT(x) ((x) << 8)
/* 4 bits */
#define HOLD_KEY_EN(x) ((x) << 7)
#define HOLD_EN(x) ((x) << 6)
#define LEVELB_VOL(x) ((x) << 4)
/* 2 bits */
#define SAMPLE_RATE(x) ((x) << 2)
/* 2 bits */
...
...
@@ -63,6 +64,25 @@
#define CHAN0_KEYDOWN_IRQ BIT(1)
#define CHAN0_DATA_IRQ BIT(0)
/* struct lradc_variant - Describe sun4i-a10-lradc-keys hardware variant
* @divisor_numerator: The numerator of lradc Vref internally divisor
* @divisor_denominator: The denominator of lradc Vref internally divisor
*/
struct
lradc_variant
{
u8
divisor_numerator
;
u8
divisor_denominator
;
};
static
const
struct
lradc_variant
lradc_variant_a10
=
{
.
divisor_numerator
=
2
,
.
divisor_denominator
=
3
};
static
const
struct
lradc_variant
r_lradc_variant_a83t
=
{
.
divisor_numerator
=
3
,
.
divisor_denominator
=
4
};
struct
sun4i_lradc_keymap
{
u32
voltage
;
u32
keycode
;
...
...
@@ -74,6 +94,7 @@ struct sun4i_lradc_data {
void
__iomem
*
base
;
struct
regulator
*
vref_supply
;
struct
sun4i_lradc_keymap
*
chan0_map
;
const
struct
lradc_variant
*
variant
;
u32
chan0_map_count
;
u32
chan0_keycode
;
u32
vref
;
...
...
@@ -128,9 +149,9 @@ static int sun4i_lradc_open(struct input_dev *dev)
if
(
error
)
return
error
;
/* lradc Vref internally is divided by 2/3 */
lradc
->
vref
=
regulator_get_voltage
(
lradc
->
vref_supply
)
*
2
/
3
;
lradc
->
vref
=
regulator_get_voltage
(
lradc
->
vref_supply
)
*
lradc
->
variant
->
divisor_numerator
/
lradc
->
variant
->
divisor_denominator
;
/*
* Set sample time to 4 ms / 250 Hz. Wait 2 * 4 ms for key to
* stabilize on press, wait (1 + 1) * 4 ms for key release
...
...
@@ -222,6 +243,12 @@ static int sun4i_lradc_probe(struct platform_device *pdev)
if
(
error
)
return
error
;
lradc
->
variant
=
of_device_get_match_data
(
&
pdev
->
dev
);
if
(
!
lradc
->
variant
)
{
dev_err
(
&
pdev
->
dev
,
"Missing sun4i-a10-lradc-keys variant
\n
"
);
return
-
EINVAL
;
}
lradc
->
vref_supply
=
devm_regulator_get
(
dev
,
"vref"
);
if
(
IS_ERR
(
lradc
->
vref_supply
))
return
PTR_ERR
(
lradc
->
vref_supply
);
...
...
@@ -265,7 +292,10 @@ static int sun4i_lradc_probe(struct platform_device *pdev)
}
static
const
struct
of_device_id
sun4i_lradc_of_match
[]
=
{
{
.
compatible
=
"allwinner,sun4i-a10-lradc-keys"
,
},
{
.
compatible
=
"allwinner,sun4i-a10-lradc-keys"
,
.
data
=
&
lradc_variant_a10
},
{
.
compatible
=
"allwinner,sun8i-a83t-r-lradc"
,
.
data
=
&
r_lradc_variant_a83t
},
{
/* sentinel */
}
};
MODULE_DEVICE_TABLE
(
of
,
sun4i_lradc_of_match
);
...
...
drivers/input/misc/Kconfig
View file @
14e0c731
...
...
@@ -290,6 +290,18 @@ config INPUT_GPIO_DECODER
To compile this driver as a module, choose M here: the module
will be called gpio_decoder.
config INPUT_GPIO_VIBRA
tristate "GPIO vibrator support"
depends on GPIOLIB || COMPILE_TEST
select INPUT_FF_MEMLESS
help
Say Y here to get support for GPIO based vibrator devices.
If unsure, say N.
To compile this driver as a module, choose M here: the module will be
called gpio-vibra.
config INPUT_IXP4XX_BEEPER
tristate "IXP4XX Beeper support"
depends on ARCH_IXP4XX
...
...
drivers/input/misc/Makefile
View file @
14e0c731
...
...
@@ -36,6 +36,7 @@ obj-$(CONFIG_INPUT_DRV2667_HAPTICS) += drv2667.o
obj-$(CONFIG_INPUT_GP2A)
+=
gp2ap002a00f.o
obj-$(CONFIG_INPUT_GPIO_BEEPER)
+=
gpio-beeper.o
obj-$(CONFIG_INPUT_GPIO_DECODER)
+=
gpio_decoder.o
obj-$(CONFIG_INPUT_GPIO_VIBRA)
+=
gpio-vibra.o
obj-$(CONFIG_INPUT_HISI_POWERKEY)
+=
hisi_powerkey.o
obj-$(CONFIG_HP_SDC_RTC)
+=
hp_sdc_rtc.o
obj-$(CONFIG_INPUT_IMS_PCU)
+=
ims-pcu.o
...
...
drivers/input/misc/gpio-vibra.c
0 → 100644
View file @
14e0c731
// SPDX-License-Identifier: GPL-2.0+
/*
* GPIO vibrator driver
*
* Copyright (C) 2019 Luca Weiss <luca@z3ntu.xyz>
*
* Based on PWM vibrator driver:
* Copyright (C) 2017 Collabora Ltd.
*
* Based on previous work from:
* Copyright (C) 2012 Dmitry Torokhov <dmitry.torokhov@gmail.com>
*
* Based on PWM beeper driver:
* Copyright (C) 2010, Lars-Peter Clausen <lars@metafoo.de>
*/
#include <linux/gpio/consumer.h>
#include <linux/input.h>
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/of_device.h>
#include <linux/platform_device.h>
#include <linux/property.h>
#include <linux/regulator/consumer.h>
#include <linux/slab.h>
struct
gpio_vibrator
{
struct
input_dev
*
input
;
struct
gpio_desc
*
gpio
;
struct
regulator
*
vcc
;
struct
work_struct
play_work
;
bool
running
;
bool
vcc_on
;
};
static
int
gpio_vibrator_start
(
struct
gpio_vibrator
*
vibrator
)
{
struct
device
*
pdev
=
vibrator
->
input
->
dev
.
parent
;
int
err
;
if
(
!
vibrator
->
vcc_on
)
{
err
=
regulator_enable
(
vibrator
->
vcc
);
if
(
err
)
{
dev_err
(
pdev
,
"failed to enable regulator: %d
\n
"
,
err
);
return
err
;
}
vibrator
->
vcc_on
=
true
;
}
gpiod_set_value_cansleep
(
vibrator
->
gpio
,
1
);
return
0
;
}
static
void
gpio_vibrator_stop
(
struct
gpio_vibrator
*
vibrator
)
{
gpiod_set_value_cansleep
(
vibrator
->
gpio
,
0
);
if
(
vibrator
->
vcc_on
)
{
regulator_disable
(
vibrator
->
vcc
);
vibrator
->
vcc_on
=
false
;
}
}
static
void
gpio_vibrator_play_work
(
struct
work_struct
*
work
)
{
struct
gpio_vibrator
*
vibrator
=
container_of
(
work
,
struct
gpio_vibrator
,
play_work
);
if
(
vibrator
->
running
)
gpio_vibrator_start
(
vibrator
);
else
gpio_vibrator_stop
(
vibrator
);
}
static
int
gpio_vibrator_play_effect
(
struct
input_dev
*
dev
,
void
*
data
,
struct
ff_effect
*
effect
)
{
struct
gpio_vibrator
*
vibrator
=
input_get_drvdata
(
dev
);
int
level
;
level
=
effect
->
u
.
rumble
.
strong_magnitude
;
if
(
!
level
)
level
=
effect
->
u
.
rumble
.
weak_magnitude
;
vibrator
->
running
=
level
;
schedule_work
(
&
vibrator
->
play_work
);
return
0
;
}
static
void
gpio_vibrator_close
(
struct
input_dev
*
input
)
{
struct
gpio_vibrator
*
vibrator
=
input_get_drvdata
(
input
);
cancel_work_sync
(
&
vibrator
->
play_work
);
gpio_vibrator_stop
(
vibrator
);
vibrator
->
running
=
false
;
}
static
int
gpio_vibrator_probe
(
struct
platform_device
*
pdev
)
{
struct
gpio_vibrator
*
vibrator
;
int
err
;
vibrator
=
devm_kzalloc
(
&
pdev
->
dev
,
sizeof
(
*
vibrator
),
GFP_KERNEL
);
if
(
!
vibrator
)
return
-
ENOMEM
;
vibrator
->
input
=
devm_input_allocate_device
(
&
pdev
->
dev
);
if
(
!
vibrator
->
input
)
return
-
ENOMEM
;
vibrator
->
vcc
=
devm_regulator_get
(
&
pdev
->
dev
,
"vcc"
);
err
=
PTR_ERR_OR_ZERO
(
vibrator
->
vcc
);
if
(
err
)
{
if
(
err
!=
-
EPROBE_DEFER
)
dev_err
(
&
pdev
->
dev
,
"Failed to request regulator: %d
\n
"
,
err
);
return
err
;
}
vibrator
->
gpio
=
devm_gpiod_get
(
&
pdev
->
dev
,
"enable"
,
GPIOD_OUT_LOW
);
err
=
PTR_ERR_OR_ZERO
(
vibrator
->
gpio
);
if
(
err
)
{
if
(
err
!=
-
EPROBE_DEFER
)
dev_err
(
&
pdev
->
dev
,
"Failed to request main gpio: %d
\n
"
,
err
);
return
err
;
}
INIT_WORK
(
&
vibrator
->
play_work
,
gpio_vibrator_play_work
);
vibrator
->
input
->
name
=
"gpio-vibrator"
;
vibrator
->
input
->
id
.
bustype
=
BUS_HOST
;
vibrator
->
input
->
close
=
gpio_vibrator_close
;
input_set_drvdata
(
vibrator
->
input
,
vibrator
);
input_set_capability
(
vibrator
->
input
,
EV_FF
,
FF_RUMBLE
);
err
=
input_ff_create_memless
(
vibrator
->
input
,
NULL
,
gpio_vibrator_play_effect
);
if
(
err
)
{
dev_err
(
&
pdev
->
dev
,
"Couldn't create FF dev: %d
\n
"
,
err
);
return
err
;
}
err
=
input_register_device
(
vibrator
->
input
);
if
(
err
)
{
dev_err
(
&
pdev
->
dev
,
"Couldn't register input dev: %d
\n
"
,
err
);
return
err
;
}
platform_set_drvdata
(
pdev
,
vibrator
);
return
0
;
}
static
int
__maybe_unused
gpio_vibrator_suspend
(
struct
device
*
dev
)
{
struct
platform_device
*
pdev
=
to_platform_device
(
dev
);
struct
gpio_vibrator
*
vibrator
=
platform_get_drvdata
(
pdev
);
cancel_work_sync
(
&
vibrator
->
play_work
);
if
(
vibrator
->
running
)
gpio_vibrator_stop
(
vibrator
);
return
0
;
}
static
int
__maybe_unused
gpio_vibrator_resume
(
struct
device
*
dev
)
{
struct
platform_device
*
pdev
=
to_platform_device
(
dev
);
struct
gpio_vibrator
*
vibrator
=
platform_get_drvdata
(
pdev
);
if
(
vibrator
->
running
)
gpio_vibrator_start
(
vibrator
);
return
0
;
}
static
SIMPLE_DEV_PM_OPS
(
gpio_vibrator_pm_ops
,
gpio_vibrator_suspend
,
gpio_vibrator_resume
);
#ifdef CONFIG_OF
static
const
struct
of_device_id
gpio_vibra_dt_match_table
[]
=
{
{
.
compatible
=
"gpio-vibrator"
},
{}
};
MODULE_DEVICE_TABLE
(
of
,
gpio_vibra_dt_match_table
);
#endif
static
struct
platform_driver
gpio_vibrator_driver
=
{
.
probe
=
gpio_vibrator_probe
,
.
driver
=
{
.
name
=
"gpio-vibrator"
,
.
pm
=
&
gpio_vibrator_pm_ops
,
.
of_match_table
=
of_match_ptr
(
gpio_vibra_dt_match_table
),
},
};
module_platform_driver
(
gpio_vibrator_driver
);
MODULE_AUTHOR
(
"Luca Weiss <luca@z3ntu.xy>"
);
MODULE_DESCRIPTION
(
"GPIO vibrator driver"
);
MODULE_LICENSE
(
"GPL"
);
MODULE_ALIAS
(
"platform:gpio-vibrator"
);
drivers/input/mouse/psmouse-base.c
View file @
14e0c731
...
...
@@ -373,6 +373,8 @@ static irqreturn_t psmouse_interrupt(struct serio *serio,
if
(
ps2_handle_response
(
&
psmouse
->
ps2dev
,
data
))
goto
out
;
pm_wakeup_event
(
&
serio
->
dev
,
0
);
if
(
psmouse
->
state
<=
PSMOUSE_RESYNCING
)
goto
out
;
...
...
drivers/input/rmi4/rmi_f54.c
View file @
14e0c731
...
...
@@ -456,25 +456,15 @@ static int rmi_f54_vidioc_fmt(struct file *file, void *priv,
static
int
rmi_f54_vidioc_enum_fmt
(
struct
file
*
file
,
void
*
priv
,
struct
v4l2_fmtdesc
*
fmt
)
{
struct
f54_data
*
f54
=
video_drvdata
(
file
);
if
(
fmt
->
type
!=
V4L2_BUF_TYPE_VIDEO_CAPTURE
)
return
-
EINVAL
;
switch
(
fmt
->
index
)
{
case
0
:
fmt
->
pixelformat
=
V4L2_TCH_FMT_DELTA_TD16
;
break
;
case
1
:
fmt
->
pixelformat
=
V4L2_TCH_FMT_DELTA_TD08
;
break
;
case
2
:
fmt
->
pixelformat
=
V4L2_TCH_FMT_TU16
;
break
;
default:
if
(
fmt
->
index
)
return
-
EINVAL
;
}
fmt
->
pixelformat
=
f54
->
format
.
pixelformat
;
return
0
;
}
...
...
@@ -692,6 +682,7 @@ static int rmi_f54_probe(struct rmi_function *fn)
return
-
ENOMEM
;
rmi_f54_create_input_map
(
f54
);
rmi_f54_set_input
(
f54
,
0
);
/* register video device */
strlcpy
(
f54
->
v4l2
.
name
,
F54_NAME
,
sizeof
(
f54
->
v4l2
.
name
));
...
...
drivers/input/serio/Kconfig
View file @
14e0c731
...
...
@@ -254,6 +254,7 @@ config SERIO_APBPS2
config SERIO_OLPC_APSP
tristate "OLPC AP-SP input support"
depends on ARCH_MMP || COMPILE_TEST
help
Say Y here if you want support for the keyboard and touchpad included
in the OLPC XO-1.75 and XO-4 laptops.
...
...
drivers/input/serio/hyperv-keyboard.c
View file @
14e0c731
...
...
@@ -440,5 +440,7 @@ static void __exit hv_kbd_exit(void)
}
MODULE_LICENSE
(
"GPL"
);
MODULE_DESCRIPTION
(
"Microsoft Hyper-V Synthetic Keyboard Driver"
);
module_init
(
hv_kbd_init
);
module_exit
(
hv_kbd_exit
);
drivers/input/serio/i8042.c
View file @
14e0c731
...
...
@@ -573,9 +573,6 @@ static irqreturn_t i8042_interrupt(int irq, void *dev_id)
port
=
&
i8042_ports
[
port_no
];
serio
=
port
->
exists
?
port
->
serio
:
NULL
;
if
(
irq
&&
serio
)
pm_wakeup_event
(
&
serio
->
dev
,
0
);
filter_dbg
(
port
->
driver_bound
,
data
,
"<- i8042 (interrupt, %d, %d%s%s)
\n
"
,
port_no
,
irq
,
dfl
&
SERIO_PARITY
?
", bad parity"
:
""
,
...
...
drivers/input/serio/libps2.c
View file @
14e0c731
...
...
@@ -409,6 +409,7 @@ bool ps2_handle_ack(struct ps2dev *ps2dev, u8 data)
ps2dev
->
nak
=
PS2_RET_ERR
;
break
;
}
/* Fall through */
/*
* Workaround for mice which don't ACK the Get ID command.
...
...
drivers/input/touchscreen/Kconfig
View file @
14e0c731
...
...
@@ -1312,4 +1312,14 @@ config TOUCHSCREEN_ROHM_BU21023
To compile this driver as a module, choose M here: the
module will be called bu21023_ts.
config TOUCHSCREEN_IQS5XX
tristate "Azoteq IQS550/572/525 trackpad/touchscreen controller"
depends on I2C
help
Say Y to enable support for the Azoteq IQS550/572/525
family of trackpad/touchscreen controllers.
To compile this driver as a module, choose M here: the
module will be called iqs5xx.
endif
drivers/input/touchscreen/Makefile
View file @
14e0c731
...
...
@@ -110,3 +110,4 @@ obj-$(CONFIG_TOUCHSCREEN_ZFORCE) += zforce_ts.o
obj-$(CONFIG_TOUCHSCREEN_COLIBRI_VF50)
+=
colibri-vf50-ts.o
obj-$(CONFIG_TOUCHSCREEN_ROHM_BU21023)
+=
rohm_bu21023.o
obj-$(CONFIG_TOUCHSCREEN_RASPBERRYPI_FW)
+=
raspberrypi-ts.o
obj-$(CONFIG_TOUCHSCREEN_IQS5XX)
+=
iqs5xx.o
drivers/input/touchscreen/edt-ft5x06.c
View file @
14e0c731
// SPDX-License-Identifier: GPL-2.0
/*
* Copyright (C) 2012 Simon Budig, <simon.budig@kernelconcepts.de>
* Daniel Wagener <daniel.wagener@kernelconcepts.de> (M09 firmware support)
* Lothar Waßmann <LW@KARO-electronics.de> (DT support)
*
* This software is licensed under the terms of the GNU General Public
* License version 2, as published by the Free Software Foundation, and
* may be copied, distributed, and modified under those terms.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
/*
...
...
@@ -39,7 +27,6 @@
#include <linux/gpio/consumer.h>
#include <linux/input/mt.h>
#include <linux/input/touchscreen.h>
#include <linux/of_device.h>
#define WORK_REGISTER_THRESHOLD 0x00
#define WORK_REGISTER_REPORT_RATE 0x08
...
...
@@ -1073,7 +1060,7 @@ static int edt_ft5x06_ts_probe(struct i2c_client *client,
return
-
ENOMEM
;
}
chip_data
=
of_
device_get_match_data
(
&
client
->
dev
);
chip_data
=
device_get_match_data
(
&
client
->
dev
);
if
(
!
chip_data
)
chip_data
=
(
const
struct
edt_i2c_chip_data
*
)
id
->
driver_data
;
if
(
!
chip_data
||
!
chip_data
->
max_support_points
)
{
...
...
@@ -1254,7 +1241,6 @@ static const struct i2c_device_id edt_ft5x06_ts_id[] = {
};
MODULE_DEVICE_TABLE
(
i2c
,
edt_ft5x06_ts_id
);
#ifdef CONFIG_OF
static
const
struct
of_device_id
edt_ft5x06_of_match
[]
=
{
{
.
compatible
=
"edt,edt-ft5206"
,
.
data
=
&
edt_ft5x06_data
},
{
.
compatible
=
"edt,edt-ft5306"
,
.
data
=
&
edt_ft5x06_data
},
...
...
@@ -1266,12 +1252,11 @@ static const struct of_device_id edt_ft5x06_of_match[] = {
{
/* sentinel */
}
};
MODULE_DEVICE_TABLE
(
of
,
edt_ft5x06_of_match
);
#endif
static
struct
i2c_driver
edt_ft5x06_ts_driver
=
{
.
driver
=
{
.
name
=
"edt_ft5x06"
,
.
of_match_table
=
of_match_ptr
(
edt_ft5x06_of_match
)
,
.
of_match_table
=
edt_ft5x06_of_match
,
.
pm
=
&
edt_ft5x06_ts_pm_ops
,
},
.
id_table
=
edt_ft5x06_ts_id
,
...
...
@@ -1283,4 +1268,4 @@ module_i2c_driver(edt_ft5x06_ts_driver);
MODULE_AUTHOR
(
"Simon Budig <simon.budig@kernelconcepts.de>"
);
MODULE_DESCRIPTION
(
"EDT FT5x06 I2C Touchscreen Driver"
);
MODULE_LICENSE
(
"GPL"
);
MODULE_LICENSE
(
"GPL
v2
"
);
drivers/input/touchscreen/goodix.c
View file @
14e0c731
...
...
@@ -27,6 +27,7 @@
#include <linux/delay.h>
#include <linux/irq.h>
#include <linux/interrupt.h>
#include <linux/regulator/consumer.h>
#include <linux/slab.h>
#include <linux/acpi.h>
#include <linux/of.h>
...
...
@@ -47,6 +48,8 @@ struct goodix_ts_data {
struct
touchscreen_properties
prop
;
unsigned
int
max_touch_num
;
unsigned
int
int_trigger_type
;
struct
regulator
*
avdd28
;
struct
regulator
*
vddio
;
struct
gpio_desc
*
gpiod_int
;
struct
gpio_desc
*
gpiod_rst
;
u16
id
;
...
...
@@ -216,6 +219,7 @@ static const struct goodix_chip_data *goodix_get_chip_data(u16 id)
{
switch
(
id
)
{
case
1151
:
case
5663
:
case
5688
:
return
&
gt1x_chip_data
;
...
...
@@ -532,6 +536,24 @@ static int goodix_get_gpio_config(struct goodix_ts_data *ts)
return
-
EINVAL
;
dev
=
&
ts
->
client
->
dev
;
ts
->
avdd28
=
devm_regulator_get
(
dev
,
"AVDD28"
);
if
(
IS_ERR
(
ts
->
avdd28
))
{
error
=
PTR_ERR
(
ts
->
avdd28
);
if
(
error
!=
-
EPROBE_DEFER
)
dev_err
(
dev
,
"Failed to get AVDD28 regulator: %d
\n
"
,
error
);
return
error
;
}
ts
->
vddio
=
devm_regulator_get
(
dev
,
"VDDIO"
);
if
(
IS_ERR
(
ts
->
vddio
))
{
error
=
PTR_ERR
(
ts
->
vddio
);
if
(
error
!=
-
EPROBE_DEFER
)
dev_err
(
dev
,
"Failed to get VDDIO regulator: %d
\n
"
,
error
);
return
error
;
}
/* Get the interrupt GPIO pin number */
gpiod
=
devm_gpiod_get_optional
(
dev
,
GOODIX_GPIO_INT_NAME
,
GPIOD_IN
);
if
(
IS_ERR
(
gpiod
))
{
...
...
@@ -764,6 +786,14 @@ static void goodix_config_cb(const struct firmware *cfg, void *ctx)
complete_all
(
&
ts
->
firmware_loading_complete
);
}
static
void
goodix_disable_regulators
(
void
*
arg
)
{
struct
goodix_ts_data
*
ts
=
arg
;
regulator_disable
(
ts
->
vddio
);
regulator_disable
(
ts
->
avdd28
);
}
static
int
goodix_ts_probe
(
struct
i2c_client
*
client
,
const
struct
i2c_device_id
*
id
)
{
...
...
@@ -789,6 +819,29 @@ static int goodix_ts_probe(struct i2c_client *client,
if
(
error
)
return
error
;
/* power up the controller */
error
=
regulator_enable
(
ts
->
avdd28
);
if
(
error
)
{
dev_err
(
&
client
->
dev
,
"Failed to enable AVDD28 regulator: %d
\n
"
,
error
);
return
error
;
}
error
=
regulator_enable
(
ts
->
vddio
);
if
(
error
)
{
dev_err
(
&
client
->
dev
,
"Failed to enable VDDIO regulator: %d
\n
"
,
error
);
regulator_disable
(
ts
->
avdd28
);
return
error
;
}
error
=
devm_add_action_or_reset
(
&
client
->
dev
,
goodix_disable_regulators
,
ts
);
if
(
error
)
return
error
;
if
(
ts
->
gpiod_int
&&
ts
->
gpiod_rst
)
{
/* reset the controller */
error
=
goodix_reset
(
ts
);
...
...
@@ -945,6 +998,7 @@ MODULE_DEVICE_TABLE(acpi, goodix_acpi_match);
#ifdef CONFIG_OF
static
const
struct
of_device_id
goodix_of_match
[]
=
{
{
.
compatible
=
"goodix,gt1151"
},
{
.
compatible
=
"goodix,gt5663"
},
{
.
compatible
=
"goodix,gt5688"
},
{
.
compatible
=
"goodix,gt911"
},
{
.
compatible
=
"goodix,gt9110"
},
...
...
drivers/input/touchscreen/iqs5xx.c
0 → 100644
View file @
14e0c731
// SPDX-License-Identifier: GPL-2.0+
/*
* Azoteq IQS550/572/525 Trackpad/Touchscreen Controller
*
* Copyright (C) 2018
* Author: Jeff LaBundy <jeff@labundy.com>
*
* These devices require firmware exported from a PC-based configuration tool
* made available by the vendor. Firmware files may be pushed to the device's
* nonvolatile memory by writing the filename to the 'fw_file' sysfs control.
*
* Link to PC-based configuration tool and data sheet: http://www.azoteq.com/
*/
#include <linux/delay.h>
#include <linux/device.h>
#include <linux/err.h>
#include <linux/firmware.h>
#include <linux/gpio/consumer.h>
#include <linux/i2c.h>
#include <linux/input.h>
#include <linux/input/mt.h>
#include <linux/input/touchscreen.h>
#include <linux/interrupt.h>
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/of_device.h>
#include <linux/slab.h>
#include <asm/unaligned.h>
#define IQS5XX_FW_FILE_LEN 64
#define IQS5XX_NUM_RETRIES 10
#define IQS5XX_NUM_POINTS 256
#define IQS5XX_NUM_CONTACTS 5
#define IQS5XX_WR_BYTES_MAX 2
#define IQS5XX_PROD_NUM_IQS550 40
#define IQS5XX_PROD_NUM_IQS572 58
#define IQS5XX_PROD_NUM_IQS525 52
#define IQS5XX_PROJ_NUM_A000 0
#define IQS5XX_PROJ_NUM_B000 15
#define IQS5XX_MAJOR_VER_MIN 2
#define IQS5XX_RESUME 0x00
#define IQS5XX_SUSPEND 0x01
#define IQS5XX_SW_INPUT_EVENT 0x10
#define IQS5XX_SETUP_COMPLETE 0x40
#define IQS5XX_EVENT_MODE 0x01
#define IQS5XX_TP_EVENT 0x04
#define IQS5XX_FLIP_X 0x01
#define IQS5XX_FLIP_Y 0x02
#define IQS5XX_SWITCH_XY_AXIS 0x04
#define IQS5XX_PROD_NUM 0x0000
#define IQS5XX_ABS_X 0x0016
#define IQS5XX_ABS_Y 0x0018
#define IQS5XX_SYS_CTRL0 0x0431
#define IQS5XX_SYS_CTRL1 0x0432
#define IQS5XX_SYS_CFG0 0x058E
#define IQS5XX_SYS_CFG1 0x058F
#define IQS5XX_TOTAL_RX 0x063D
#define IQS5XX_TOTAL_TX 0x063E
#define IQS5XX_XY_CFG0 0x0669
#define IQS5XX_X_RES 0x066E
#define IQS5XX_Y_RES 0x0670
#define IQS5XX_CHKSM 0x83C0
#define IQS5XX_APP 0x8400
#define IQS5XX_CSTM 0xBE00
#define IQS5XX_PMAP_END 0xBFFF
#define IQS5XX_END_COMM 0xEEEE
#define IQS5XX_CHKSM_LEN (IQS5XX_APP - IQS5XX_CHKSM)
#define IQS5XX_APP_LEN (IQS5XX_CSTM - IQS5XX_APP)
#define IQS5XX_CSTM_LEN (IQS5XX_PMAP_END + 1 - IQS5XX_CSTM)
#define IQS5XX_PMAP_LEN (IQS5XX_PMAP_END + 1 - IQS5XX_CHKSM)
#define IQS5XX_REC_HDR_LEN 4
#define IQS5XX_REC_LEN_MAX 255
#define IQS5XX_REC_TYPE_DATA 0x00
#define IQS5XX_REC_TYPE_EOF 0x01
#define IQS5XX_BL_ADDR_MASK 0x40
#define IQS5XX_BL_CMD_VER 0x00
#define IQS5XX_BL_CMD_READ 0x01
#define IQS5XX_BL_CMD_EXEC 0x02
#define IQS5XX_BL_CMD_CRC 0x03
#define IQS5XX_BL_BLK_LEN_MAX 64
#define IQS5XX_BL_ID 0x0200
#define IQS5XX_BL_STATUS_RESET 0x00
#define IQS5XX_BL_STATUS_AVAIL 0xA5
#define IQS5XX_BL_STATUS_NONE 0xEE
#define IQS5XX_BL_CRC_PASS 0x00
#define IQS5XX_BL_CRC_FAIL 0x01
#define IQS5XX_BL_ATTEMPTS 3
struct
iqs5xx_private
{
struct
i2c_client
*
client
;
struct
input_dev
*
input
;
struct
gpio_desc
*
reset_gpio
;
struct
mutex
lock
;
u8
bl_status
;
};
struct
iqs5xx_dev_id_info
{
__be16
prod_num
;
__be16
proj_num
;
u8
major_ver
;
u8
minor_ver
;
u8
bl_status
;
}
__packed
;
struct
iqs5xx_ihex_rec
{
char
start
;
char
len
[
2
];
char
addr
[
4
];
char
type
[
2
];
char
data
[
2
];
}
__packed
;
struct
iqs5xx_touch_data
{
__be16
abs_x
;
__be16
abs_y
;
__be16
strength
;
u8
area
;
}
__packed
;
static
int
iqs5xx_read_burst
(
struct
i2c_client
*
client
,
u16
reg
,
void
*
val
,
u16
len
)
{
__be16
reg_buf
=
cpu_to_be16
(
reg
);
int
ret
,
i
;
struct
i2c_msg
msg
[]
=
{
{
.
addr
=
client
->
addr
,
.
flags
=
0
,
.
len
=
sizeof
(
reg_buf
),
.
buf
=
(
u8
*
)
&
reg_buf
,
},
{
.
addr
=
client
->
addr
,
.
flags
=
I2C_M_RD
,
.
len
=
len
,
.
buf
=
(
u8
*
)
val
,
},
};
/*
* The first addressing attempt outside of a communication window fails
* and must be retried, after which the device clock stretches until it
* is available.
*/
for
(
i
=
0
;
i
<
IQS5XX_NUM_RETRIES
;
i
++
)
{
ret
=
i2c_transfer
(
client
->
adapter
,
msg
,
ARRAY_SIZE
(
msg
));
if
(
ret
==
ARRAY_SIZE
(
msg
))
return
0
;
usleep_range
(
200
,
300
);
}
if
(
ret
>=
0
)
ret
=
-
EIO
;
dev_err
(
&
client
->
dev
,
"Failed to read from address 0x%04X: %d
\n
"
,
reg
,
ret
);
return
ret
;
}
static
int
iqs5xx_read_word
(
struct
i2c_client
*
client
,
u16
reg
,
u16
*
val
)
{
__be16
val_buf
;
int
error
;
error
=
iqs5xx_read_burst
(
client
,
reg
,
&
val_buf
,
sizeof
(
val_buf
));
if
(
error
)
return
error
;
*
val
=
be16_to_cpu
(
val_buf
);
return
0
;
}
static
int
iqs5xx_read_byte
(
struct
i2c_client
*
client
,
u16
reg
,
u8
*
val
)
{
return
iqs5xx_read_burst
(
client
,
reg
,
val
,
sizeof
(
*
val
));
}
static
int
iqs5xx_write_burst
(
struct
i2c_client
*
client
,
u16
reg
,
const
void
*
val
,
u16
len
)
{
int
ret
,
i
;
u16
mlen
=
sizeof
(
reg
)
+
len
;
u8
mbuf
[
sizeof
(
reg
)
+
IQS5XX_WR_BYTES_MAX
];
if
(
len
>
IQS5XX_WR_BYTES_MAX
)
return
-
EINVAL
;
put_unaligned_be16
(
reg
,
mbuf
);
memcpy
(
mbuf
+
sizeof
(
reg
),
val
,
len
);
/*
* The first addressing attempt outside of a communication window fails
* and must be retried, after which the device clock stretches until it
* is available.
*/
for
(
i
=
0
;
i
<
IQS5XX_NUM_RETRIES
;
i
++
)
{
ret
=
i2c_master_send
(
client
,
mbuf
,
mlen
);
if
(
ret
==
mlen
)
return
0
;
usleep_range
(
200
,
300
);
}
if
(
ret
>=
0
)
ret
=
-
EIO
;
dev_err
(
&
client
->
dev
,
"Failed to write to address 0x%04X: %d
\n
"
,
reg
,
ret
);
return
ret
;
}
static
int
iqs5xx_write_word
(
struct
i2c_client
*
client
,
u16
reg
,
u16
val
)
{
__be16
val_buf
=
cpu_to_be16
(
val
);
return
iqs5xx_write_burst
(
client
,
reg
,
&
val_buf
,
sizeof
(
val_buf
));
}
static
int
iqs5xx_write_byte
(
struct
i2c_client
*
client
,
u16
reg
,
u8
val
)
{
return
iqs5xx_write_burst
(
client
,
reg
,
&
val
,
sizeof
(
val
));
}
static
void
iqs5xx_reset
(
struct
i2c_client
*
client
)
{
struct
iqs5xx_private
*
iqs5xx
=
i2c_get_clientdata
(
client
);
gpiod_set_value_cansleep
(
iqs5xx
->
reset_gpio
,
1
);
usleep_range
(
200
,
300
);
gpiod_set_value_cansleep
(
iqs5xx
->
reset_gpio
,
0
);
}
static
int
iqs5xx_bl_cmd
(
struct
i2c_client
*
client
,
u8
bl_cmd
,
u16
bl_addr
)
{
struct
i2c_msg
msg
;
int
ret
;
u8
mbuf
[
sizeof
(
bl_cmd
)
+
sizeof
(
bl_addr
)];
msg
.
addr
=
client
->
addr
^
IQS5XX_BL_ADDR_MASK
;
msg
.
flags
=
0
;
msg
.
len
=
sizeof
(
bl_cmd
);
msg
.
buf
=
mbuf
;
*
mbuf
=
bl_cmd
;
switch
(
bl_cmd
)
{
case
IQS5XX_BL_CMD_VER
:
case
IQS5XX_BL_CMD_CRC
:
case
IQS5XX_BL_CMD_EXEC
:
break
;
case
IQS5XX_BL_CMD_READ
:
msg
.
len
+=
sizeof
(
bl_addr
);
put_unaligned_be16
(
bl_addr
,
mbuf
+
sizeof
(
bl_cmd
));
break
;
default:
return
-
EINVAL
;
}
ret
=
i2c_transfer
(
client
->
adapter
,
&
msg
,
1
);
if
(
ret
!=
1
)
goto
msg_fail
;
switch
(
bl_cmd
)
{
case
IQS5XX_BL_CMD_VER
:
msg
.
len
=
sizeof
(
u16
);
break
;
case
IQS5XX_BL_CMD_CRC
:
msg
.
len
=
sizeof
(
u8
);
/*
* This delay saves the bus controller the trouble of having to
* tolerate a relatively long clock-stretching period while the
* CRC is calculated.
*/
msleep
(
50
);
break
;
case
IQS5XX_BL_CMD_EXEC
:
usleep_range
(
10000
,
10100
);
/* fall through */
default:
return
0
;
}
msg
.
flags
=
I2C_M_RD
;
ret
=
i2c_transfer
(
client
->
adapter
,
&
msg
,
1
);
if
(
ret
!=
1
)
goto
msg_fail
;
if
(
bl_cmd
==
IQS5XX_BL_CMD_VER
&&
get_unaligned_be16
(
mbuf
)
!=
IQS5XX_BL_ID
)
{
dev_err
(
&
client
->
dev
,
"Unrecognized bootloader ID: 0x%04X
\n
"
,
get_unaligned_be16
(
mbuf
));
return
-
EINVAL
;
}
if
(
bl_cmd
==
IQS5XX_BL_CMD_CRC
&&
*
mbuf
!=
IQS5XX_BL_CRC_PASS
)
{
dev_err
(
&
client
->
dev
,
"Bootloader CRC failed
\n
"
);
return
-
EIO
;
}
return
0
;
msg_fail:
if
(
ret
>=
0
)
ret
=
-
EIO
;
if
(
bl_cmd
!=
IQS5XX_BL_CMD_VER
)
dev_err
(
&
client
->
dev
,
"Unsuccessful bootloader command 0x%02X: %d
\n
"
,
bl_cmd
,
ret
);
return
ret
;
}
static
int
iqs5xx_bl_open
(
struct
i2c_client
*
client
)
{
int
error
,
i
,
j
;
/*
* The device opens a bootloader polling window for 2 ms following the
* release of reset. If the host cannot establish communication during
* this time frame, it must cycle reset again.
*/
for
(
i
=
0
;
i
<
IQS5XX_BL_ATTEMPTS
;
i
++
)
{
iqs5xx_reset
(
client
);
for
(
j
=
0
;
j
<
IQS5XX_NUM_RETRIES
;
j
++
)
{
error
=
iqs5xx_bl_cmd
(
client
,
IQS5XX_BL_CMD_VER
,
0
);
if
(
!
error
||
error
==
-
EINVAL
)
return
error
;
}
}
dev_err
(
&
client
->
dev
,
"Failed to open bootloader: %d
\n
"
,
error
);
return
error
;
}
static
int
iqs5xx_bl_write
(
struct
i2c_client
*
client
,
u16
bl_addr
,
u8
*
pmap_data
,
u16
pmap_len
)
{
struct
i2c_msg
msg
;
int
ret
,
i
;
u8
mbuf
[
sizeof
(
bl_addr
)
+
IQS5XX_BL_BLK_LEN_MAX
];
if
(
pmap_len
%
IQS5XX_BL_BLK_LEN_MAX
)
return
-
EINVAL
;
msg
.
addr
=
client
->
addr
^
IQS5XX_BL_ADDR_MASK
;
msg
.
flags
=
0
;
msg
.
len
=
sizeof
(
mbuf
);
msg
.
buf
=
mbuf
;
for
(
i
=
0
;
i
<
pmap_len
;
i
+=
IQS5XX_BL_BLK_LEN_MAX
)
{
put_unaligned_be16
(
bl_addr
+
i
,
mbuf
);
memcpy
(
mbuf
+
sizeof
(
bl_addr
),
pmap_data
+
i
,
sizeof
(
mbuf
)
-
sizeof
(
bl_addr
));
ret
=
i2c_transfer
(
client
->
adapter
,
&
msg
,
1
);
if
(
ret
!=
1
)
goto
msg_fail
;
usleep_range
(
10000
,
10100
);
}
return
0
;
msg_fail:
if
(
ret
>=
0
)
ret
=
-
EIO
;
dev_err
(
&
client
->
dev
,
"Failed to write block at address 0x%04X: %d
\n
"
,
bl_addr
+
i
,
ret
);
return
ret
;
}
static
int
iqs5xx_bl_verify
(
struct
i2c_client
*
client
,
u16
bl_addr
,
u8
*
pmap_data
,
u16
pmap_len
)
{
struct
i2c_msg
msg
;
int
ret
,
i
;
u8
bl_data
[
IQS5XX_BL_BLK_LEN_MAX
];
if
(
pmap_len
%
IQS5XX_BL_BLK_LEN_MAX
)
return
-
EINVAL
;
msg
.
addr
=
client
->
addr
^
IQS5XX_BL_ADDR_MASK
;
msg
.
flags
=
I2C_M_RD
;
msg
.
len
=
sizeof
(
bl_data
);
msg
.
buf
=
bl_data
;
for
(
i
=
0
;
i
<
pmap_len
;
i
+=
IQS5XX_BL_BLK_LEN_MAX
)
{
ret
=
iqs5xx_bl_cmd
(
client
,
IQS5XX_BL_CMD_READ
,
bl_addr
+
i
);
if
(
ret
)
return
ret
;
ret
=
i2c_transfer
(
client
->
adapter
,
&
msg
,
1
);
if
(
ret
!=
1
)
goto
msg_fail
;
if
(
memcmp
(
bl_data
,
pmap_data
+
i
,
sizeof
(
bl_data
)))
{
dev_err
(
&
client
->
dev
,
"Failed to verify block at address 0x%04X
\n
"
,
bl_addr
+
i
);
return
-
EIO
;
}
}
return
0
;
msg_fail:
if
(
ret
>=
0
)
ret
=
-
EIO
;
dev_err
(
&
client
->
dev
,
"Failed to read block at address 0x%04X: %d
\n
"
,
bl_addr
+
i
,
ret
);
return
ret
;
}
static
int
iqs5xx_set_state
(
struct
i2c_client
*
client
,
u8
state
)
{
struct
iqs5xx_private
*
iqs5xx
=
i2c_get_clientdata
(
client
);
int
error1
,
error2
;
if
(
iqs5xx
->
bl_status
==
IQS5XX_BL_STATUS_RESET
)
return
0
;
mutex_lock
(
&
iqs5xx
->
lock
);
/*
* Addressing the device outside of a communication window prompts it
* to assert the RDY output, so disable the interrupt line to prevent
* the handler from servicing a false interrupt.
*/
disable_irq
(
client
->
irq
);
error1
=
iqs5xx_write_byte
(
client
,
IQS5XX_SYS_CTRL1
,
state
);
error2
=
iqs5xx_write_byte
(
client
,
IQS5XX_END_COMM
,
0
);
usleep_range
(
50
,
100
);
enable_irq
(
client
->
irq
);
mutex_unlock
(
&
iqs5xx
->
lock
);
if
(
error1
)
return
error1
;
return
error2
;
}
static
int
iqs5xx_open
(
struct
input_dev
*
input
)
{
struct
iqs5xx_private
*
iqs5xx
=
input_get_drvdata
(
input
);
return
iqs5xx_set_state
(
iqs5xx
->
client
,
IQS5XX_RESUME
);
}
static
void
iqs5xx_close
(
struct
input_dev
*
input
)
{
struct
iqs5xx_private
*
iqs5xx
=
input_get_drvdata
(
input
);
iqs5xx_set_state
(
iqs5xx
->
client
,
IQS5XX_SUSPEND
);
}
static
int
iqs5xx_axis_init
(
struct
i2c_client
*
client
)
{
struct
iqs5xx_private
*
iqs5xx
=
i2c_get_clientdata
(
client
);
struct
touchscreen_properties
prop
;
struct
input_dev
*
input
;
int
error
;
u16
max_x
,
max_x_hw
;
u16
max_y
,
max_y_hw
;
u8
val
;
if
(
!
iqs5xx
->
input
)
{
input
=
devm_input_allocate_device
(
&
client
->
dev
);
if
(
!
input
)
return
-
ENOMEM
;
input
->
name
=
client
->
name
;
input
->
id
.
bustype
=
BUS_I2C
;
input
->
open
=
iqs5xx_open
;
input
->
close
=
iqs5xx_close
;
input_set_capability
(
input
,
EV_ABS
,
ABS_MT_POSITION_X
);
input_set_capability
(
input
,
EV_ABS
,
ABS_MT_POSITION_Y
);
input_set_capability
(
input
,
EV_ABS
,
ABS_MT_PRESSURE
);
error
=
input_mt_init_slots
(
input
,
IQS5XX_NUM_CONTACTS
,
INPUT_MT_DIRECT
);
if
(
error
)
{
dev_err
(
&
client
->
dev
,
"Failed to initialize slots: %d
\n
"
,
error
);
return
error
;
}
input_set_drvdata
(
input
,
iqs5xx
);
iqs5xx
->
input
=
input
;
}
touchscreen_parse_properties
(
iqs5xx
->
input
,
true
,
&
prop
);
error
=
iqs5xx_read_byte
(
client
,
IQS5XX_TOTAL_RX
,
&
val
);
if
(
error
)
return
error
;
max_x_hw
=
(
val
-
1
)
*
IQS5XX_NUM_POINTS
;
error
=
iqs5xx_read_byte
(
client
,
IQS5XX_TOTAL_TX
,
&
val
);
if
(
error
)
return
error
;
max_y_hw
=
(
val
-
1
)
*
IQS5XX_NUM_POINTS
;
error
=
iqs5xx_read_byte
(
client
,
IQS5XX_XY_CFG0
,
&
val
);
if
(
error
)
return
error
;
if
(
val
&
IQS5XX_SWITCH_XY_AXIS
)
swap
(
max_x_hw
,
max_y_hw
);
if
(
prop
.
swap_x_y
)
val
^=
IQS5XX_SWITCH_XY_AXIS
;
if
(
prop
.
invert_x
)
val
^=
prop
.
swap_x_y
?
IQS5XX_FLIP_Y
:
IQS5XX_FLIP_X
;
if
(
prop
.
invert_y
)
val
^=
prop
.
swap_x_y
?
IQS5XX_FLIP_X
:
IQS5XX_FLIP_Y
;
error
=
iqs5xx_write_byte
(
client
,
IQS5XX_XY_CFG0
,
val
);
if
(
error
)
return
error
;
if
(
prop
.
max_x
>
max_x_hw
)
{
dev_err
(
&
client
->
dev
,
"Invalid maximum x-coordinate: %u > %u
\n
"
,
prop
.
max_x
,
max_x_hw
);
return
-
EINVAL
;
}
else
if
(
prop
.
max_x
==
0
)
{
error
=
iqs5xx_read_word
(
client
,
IQS5XX_X_RES
,
&
max_x
);
if
(
error
)
return
error
;
input_abs_set_max
(
iqs5xx
->
input
,
prop
.
swap_x_y
?
ABS_MT_POSITION_Y
:
ABS_MT_POSITION_X
,
max_x
);
}
else
{
max_x
=
(
u16
)
prop
.
max_x
;
}
if
(
prop
.
max_y
>
max_y_hw
)
{
dev_err
(
&
client
->
dev
,
"Invalid maximum y-coordinate: %u > %u
\n
"
,
prop
.
max_y
,
max_y_hw
);
return
-
EINVAL
;
}
else
if
(
prop
.
max_y
==
0
)
{
error
=
iqs5xx_read_word
(
client
,
IQS5XX_Y_RES
,
&
max_y
);
if
(
error
)
return
error
;
input_abs_set_max
(
iqs5xx
->
input
,
prop
.
swap_x_y
?
ABS_MT_POSITION_X
:
ABS_MT_POSITION_Y
,
max_y
);
}
else
{
max_y
=
(
u16
)
prop
.
max_y
;
}
/*
* Write horizontal and vertical resolution to the device in case its
* original defaults were overridden or swapped as per the properties
* specified in the device tree.
*/
error
=
iqs5xx_write_word
(
client
,
prop
.
swap_x_y
?
IQS5XX_Y_RES
:
IQS5XX_X_RES
,
max_x
);
if
(
error
)
return
error
;
return
iqs5xx_write_word
(
client
,
prop
.
swap_x_y
?
IQS5XX_X_RES
:
IQS5XX_Y_RES
,
max_y
);
}
static
int
iqs5xx_dev_init
(
struct
i2c_client
*
client
)
{
struct
iqs5xx_private
*
iqs5xx
=
i2c_get_clientdata
(
client
);
struct
iqs5xx_dev_id_info
*
dev_id_info
;
int
error
;
u8
val
;
u8
buf
[
sizeof
(
*
dev_id_info
)
+
1
];
error
=
iqs5xx_read_burst
(
client
,
IQS5XX_PROD_NUM
,
&
buf
[
1
],
sizeof
(
*
dev_id_info
));
if
(
error
)
return
iqs5xx_bl_open
(
client
);
/*
* A000 and B000 devices use 8-bit and 16-bit addressing, respectively.
* Querying an A000 device's version information with 16-bit addressing
* gives the appearance that the data is shifted by one byte; a nonzero
* leading array element suggests this could be the case (in which case
* the missing zero is prepended).
*/
buf
[
0
]
=
0
;
dev_id_info
=
(
struct
iqs5xx_dev_id_info
*
)
&
buf
[(
buf
[
1
]
>
0
)
?
0
:
1
];
switch
(
be16_to_cpu
(
dev_id_info
->
prod_num
))
{
case
IQS5XX_PROD_NUM_IQS550
:
case
IQS5XX_PROD_NUM_IQS572
:
case
IQS5XX_PROD_NUM_IQS525
:
break
;
default:
dev_err
(
&
client
->
dev
,
"Unrecognized product number: %u
\n
"
,
be16_to_cpu
(
dev_id_info
->
prod_num
));
return
-
EINVAL
;
}
switch
(
be16_to_cpu
(
dev_id_info
->
proj_num
))
{
case
IQS5XX_PROJ_NUM_A000
:
dev_err
(
&
client
->
dev
,
"Unsupported project number: %u
\n
"
,
be16_to_cpu
(
dev_id_info
->
proj_num
));
return
iqs5xx_bl_open
(
client
);
case
IQS5XX_PROJ_NUM_B000
:
break
;
default:
dev_err
(
&
client
->
dev
,
"Unrecognized project number: %u
\n
"
,
be16_to_cpu
(
dev_id_info
->
proj_num
));
return
-
EINVAL
;
}
if
(
dev_id_info
->
major_ver
<
IQS5XX_MAJOR_VER_MIN
)
{
dev_err
(
&
client
->
dev
,
"Unsupported major version: %u
\n
"
,
dev_id_info
->
major_ver
);
return
iqs5xx_bl_open
(
client
);
}
switch
(
dev_id_info
->
bl_status
)
{
case
IQS5XX_BL_STATUS_AVAIL
:
case
IQS5XX_BL_STATUS_NONE
:
break
;
default:
dev_err
(
&
client
->
dev
,
"Unrecognized bootloader status: 0x%02X
\n
"
,
dev_id_info
->
bl_status
);
return
-
EINVAL
;
}
error
=
iqs5xx_axis_init
(
client
);
if
(
error
)
return
error
;
error
=
iqs5xx_read_byte
(
client
,
IQS5XX_SYS_CFG0
,
&
val
);
if
(
error
)
return
error
;
val
|=
IQS5XX_SETUP_COMPLETE
;
val
&=
~
IQS5XX_SW_INPUT_EVENT
;
error
=
iqs5xx_write_byte
(
client
,
IQS5XX_SYS_CFG0
,
val
);
if
(
error
)
return
error
;
val
=
IQS5XX_TP_EVENT
|
IQS5XX_EVENT_MODE
;
error
=
iqs5xx_write_byte
(
client
,
IQS5XX_SYS_CFG1
,
val
);
if
(
error
)
return
error
;
error
=
iqs5xx_write_byte
(
client
,
IQS5XX_END_COMM
,
0
);
if
(
error
)
return
error
;
iqs5xx
->
bl_status
=
dev_id_info
->
bl_status
;
/*
* Closure of the first communication window that appears following the
* release of reset appears to kick off an initialization period during
* which further communication is met with clock stretching. The return
* from this function is delayed so that further communication attempts
* avoid this period.
*/
msleep
(
100
);
return
0
;
}
static
irqreturn_t
iqs5xx_irq
(
int
irq
,
void
*
data
)
{
struct
iqs5xx_private
*
iqs5xx
=
data
;
struct
iqs5xx_touch_data
touch_data
[
IQS5XX_NUM_CONTACTS
];
struct
i2c_client
*
client
=
iqs5xx
->
client
;
struct
input_dev
*
input
=
iqs5xx
->
input
;
int
error
,
i
;
/*
* This check is purely a precaution, as the device does not assert the
* RDY output during bootloader mode. If the device operates outside of
* bootloader mode, the input device is guaranteed to be allocated.
*/
if
(
iqs5xx
->
bl_status
==
IQS5XX_BL_STATUS_RESET
)
return
IRQ_NONE
;
error
=
iqs5xx_read_burst
(
client
,
IQS5XX_ABS_X
,
touch_data
,
sizeof
(
touch_data
));
if
(
error
)
return
IRQ_NONE
;
for
(
i
=
0
;
i
<
ARRAY_SIZE
(
touch_data
);
i
++
)
{
u16
pressure
=
be16_to_cpu
(
touch_data
[
i
].
strength
);
input_mt_slot
(
input
,
i
);
if
(
input_mt_report_slot_state
(
input
,
MT_TOOL_FINGER
,
pressure
!=
0
))
{
input_report_abs
(
input
,
ABS_MT_POSITION_X
,
be16_to_cpu
(
touch_data
[
i
].
abs_x
));
input_report_abs
(
input
,
ABS_MT_POSITION_Y
,
be16_to_cpu
(
touch_data
[
i
].
abs_y
));
input_report_abs
(
input
,
ABS_MT_PRESSURE
,
pressure
);
}
}
input_mt_sync_frame
(
input
);
input_sync
(
input
);
error
=
iqs5xx_write_byte
(
client
,
IQS5XX_END_COMM
,
0
);
if
(
error
)
return
IRQ_NONE
;
/*
* Once the communication window is closed, a small delay is added to
* ensure the device's RDY output has been deasserted by the time the
* interrupt handler returns.
*/
usleep_range
(
50
,
100
);
return
IRQ_HANDLED
;
}
static
int
iqs5xx_fw_file_parse
(
struct
i2c_client
*
client
,
const
char
*
fw_file
,
u8
*
pmap
)
{
const
struct
firmware
*
fw
;
struct
iqs5xx_ihex_rec
*
rec
;
size_t
pos
=
0
;
int
error
,
i
;
u16
rec_num
=
1
;
u16
rec_addr
;
u8
rec_len
,
rec_type
,
rec_chksm
,
chksm
;
u8
rec_hdr
[
IQS5XX_REC_HDR_LEN
];
u8
rec_data
[
IQS5XX_REC_LEN_MAX
];
/*
* Firmware exported from the vendor's configuration tool deviates from
* standard ihex as follows: (1) the checksum for records corresponding
* to user-exported settings is not recalculated, and (2) an address of
* 0xFFFF is used for the EOF record.
*
* Because the ihex2fw tool tolerates neither (1) nor (2), the slightly
* nonstandard ihex firmware is parsed directly by the driver.
*/
error
=
request_firmware
(
&
fw
,
fw_file
,
&
client
->
dev
);
if
(
error
)
{
dev_err
(
&
client
->
dev
,
"Failed to request firmware %s: %d
\n
"
,
fw_file
,
error
);
return
error
;
}
do
{
if
(
pos
+
sizeof
(
*
rec
)
>
fw
->
size
)
{
dev_err
(
&
client
->
dev
,
"Insufficient firmware size
\n
"
);
error
=
-
EINVAL
;
break
;
}
rec
=
(
struct
iqs5xx_ihex_rec
*
)(
fw
->
data
+
pos
);
pos
+=
sizeof
(
*
rec
);
if
(
rec
->
start
!=
':'
)
{
dev_err
(
&
client
->
dev
,
"Invalid start at record %u
\n
"
,
rec_num
);
error
=
-
EINVAL
;
break
;
}
error
=
hex2bin
(
rec_hdr
,
rec
->
len
,
sizeof
(
rec_hdr
));
if
(
error
)
{
dev_err
(
&
client
->
dev
,
"Invalid header at record %u
\n
"
,
rec_num
);
break
;
}
rec_len
=
*
rec_hdr
;
rec_addr
=
get_unaligned_be16
(
rec_hdr
+
sizeof
(
rec_len
));
rec_type
=
*
(
rec_hdr
+
sizeof
(
rec_len
)
+
sizeof
(
rec_addr
));
if
(
pos
+
rec_len
*
2
>
fw
->
size
)
{
dev_err
(
&
client
->
dev
,
"Insufficient firmware size
\n
"
);
error
=
-
EINVAL
;
break
;
}
pos
+=
(
rec_len
*
2
);
error
=
hex2bin
(
rec_data
,
rec
->
data
,
rec_len
);
if
(
error
)
{
dev_err
(
&
client
->
dev
,
"Invalid data at record %u
\n
"
,
rec_num
);
break
;
}
error
=
hex2bin
(
&
rec_chksm
,
rec
->
data
+
rec_len
*
2
,
sizeof
(
rec_chksm
));
if
(
error
)
{
dev_err
(
&
client
->
dev
,
"Invalid checksum at record %u
\n
"
,
rec_num
);
break
;
}
chksm
=
0
;
for
(
i
=
0
;
i
<
sizeof
(
rec_hdr
);
i
++
)
chksm
+=
rec_hdr
[
i
];
for
(
i
=
0
;
i
<
rec_len
;
i
++
)
chksm
+=
rec_data
[
i
];
chksm
=
~
chksm
+
1
;
if
(
chksm
!=
rec_chksm
&&
rec_addr
<
IQS5XX_CSTM
)
{
dev_err
(
&
client
->
dev
,
"Incorrect checksum at record %u
\n
"
,
rec_num
);
error
=
-
EINVAL
;
break
;
}
switch
(
rec_type
)
{
case
IQS5XX_REC_TYPE_DATA
:
if
(
rec_addr
<
IQS5XX_CHKSM
||
rec_addr
>
IQS5XX_PMAP_END
)
{
dev_err
(
&
client
->
dev
,
"Invalid address at record %u
\n
"
,
rec_num
);
error
=
-
EINVAL
;
}
else
{
memcpy
(
pmap
+
rec_addr
-
IQS5XX_CHKSM
,
rec_data
,
rec_len
);
}
break
;
case
IQS5XX_REC_TYPE_EOF
:
break
;
default:
dev_err
(
&
client
->
dev
,
"Invalid type at record %u
\n
"
,
rec_num
);
error
=
-
EINVAL
;
}
if
(
error
)
break
;
rec_num
++
;
while
(
pos
<
fw
->
size
)
{
if
(
*
(
fw
->
data
+
pos
)
==
':'
)
break
;
pos
++
;
}
}
while
(
rec_type
!=
IQS5XX_REC_TYPE_EOF
);
release_firmware
(
fw
);
return
error
;
}
static
int
iqs5xx_fw_file_write
(
struct
i2c_client
*
client
,
const
char
*
fw_file
)
{
struct
iqs5xx_private
*
iqs5xx
=
i2c_get_clientdata
(
client
);
int
error
;
u8
*
pmap
;
if
(
iqs5xx
->
bl_status
==
IQS5XX_BL_STATUS_NONE
)
return
-
EPERM
;
pmap
=
kzalloc
(
IQS5XX_PMAP_LEN
,
GFP_KERNEL
);
if
(
!
pmap
)
return
-
ENOMEM
;
error
=
iqs5xx_fw_file_parse
(
client
,
fw_file
,
pmap
);
if
(
error
)
goto
err_kfree
;
mutex_lock
(
&
iqs5xx
->
lock
);
/*
* Disable the interrupt line in case the first attempt(s) to enter the
* bootloader don't happen quickly enough, in which case the device may
* assert the RDY output until the next attempt.
*/
disable_irq
(
client
->
irq
);
iqs5xx
->
bl_status
=
IQS5XX_BL_STATUS_RESET
;
error
=
iqs5xx_bl_cmd
(
client
,
IQS5XX_BL_CMD_VER
,
0
);
if
(
error
)
{
error
=
iqs5xx_bl_open
(
client
);
if
(
error
)
goto
err_reset
;
}
error
=
iqs5xx_bl_write
(
client
,
IQS5XX_CHKSM
,
pmap
,
IQS5XX_PMAP_LEN
);
if
(
error
)
goto
err_reset
;
error
=
iqs5xx_bl_cmd
(
client
,
IQS5XX_BL_CMD_CRC
,
0
);
if
(
error
)
goto
err_reset
;
error
=
iqs5xx_bl_verify
(
client
,
IQS5XX_CSTM
,
pmap
+
IQS5XX_CHKSM_LEN
+
IQS5XX_APP_LEN
,
IQS5XX_CSTM_LEN
);
if
(
error
)
goto
err_reset
;
error
=
iqs5xx_bl_cmd
(
client
,
IQS5XX_BL_CMD_EXEC
,
0
);
err_reset:
if
(
error
)
{
iqs5xx_reset
(
client
);
usleep_range
(
10000
,
10100
);
}
error
=
iqs5xx_dev_init
(
client
);
if
(
!
error
&&
iqs5xx
->
bl_status
==
IQS5XX_BL_STATUS_RESET
)
error
=
-
EINVAL
;
enable_irq
(
client
->
irq
);
mutex_unlock
(
&
iqs5xx
->
lock
);
err_kfree:
kfree
(
pmap
);
return
error
;
}
static
ssize_t
fw_file_store
(
struct
device
*
dev
,
struct
device_attribute
*
attr
,
const
char
*
buf
,
size_t
count
)
{
struct
iqs5xx_private
*
iqs5xx
=
dev_get_drvdata
(
dev
);
struct
i2c_client
*
client
=
iqs5xx
->
client
;
size_t
len
=
count
;
bool
input_reg
=
!
iqs5xx
->
input
;
char
fw_file
[
IQS5XX_FW_FILE_LEN
+
1
];
int
error
;
if
(
!
len
)
return
-
EINVAL
;
if
(
buf
[
len
-
1
]
==
'\n'
)
len
--
;
if
(
len
>
IQS5XX_FW_FILE_LEN
)
return
-
ENAMETOOLONG
;
memcpy
(
fw_file
,
buf
,
len
);
fw_file
[
len
]
=
'\0'
;
error
=
iqs5xx_fw_file_write
(
client
,
fw_file
);
if
(
error
)
return
error
;
/*
* If the input device was not allocated already, it is guaranteed to
* be allocated by this point and can finally be registered.
*/
if
(
input_reg
)
{
error
=
input_register_device
(
iqs5xx
->
input
);
if
(
error
)
{
dev_err
(
&
client
->
dev
,
"Failed to register device: %d
\n
"
,
error
);
return
error
;
}
}
return
count
;
}
static
DEVICE_ATTR_WO
(
fw_file
);
static
struct
attribute
*
iqs5xx_attrs
[]
=
{
&
dev_attr_fw_file
.
attr
,
NULL
,
};
static
const
struct
attribute_group
iqs5xx_attr_group
=
{
.
attrs
=
iqs5xx_attrs
,
};
static
int
__maybe_unused
iqs5xx_suspend
(
struct
device
*
dev
)
{
struct
iqs5xx_private
*
iqs5xx
=
dev_get_drvdata
(
dev
);
struct
input_dev
*
input
=
iqs5xx
->
input
;
int
error
=
0
;
if
(
!
input
)
return
error
;
mutex_lock
(
&
input
->
mutex
);
if
(
input
->
users
)
error
=
iqs5xx_set_state
(
iqs5xx
->
client
,
IQS5XX_SUSPEND
);
mutex_unlock
(
&
input
->
mutex
);
return
error
;
}
static
int
__maybe_unused
iqs5xx_resume
(
struct
device
*
dev
)
{
struct
iqs5xx_private
*
iqs5xx
=
dev_get_drvdata
(
dev
);
struct
input_dev
*
input
=
iqs5xx
->
input
;
int
error
=
0
;
if
(
!
input
)
return
error
;
mutex_lock
(
&
input
->
mutex
);
if
(
input
->
users
)
error
=
iqs5xx_set_state
(
iqs5xx
->
client
,
IQS5XX_RESUME
);
mutex_unlock
(
&
input
->
mutex
);
return
error
;
}
static
SIMPLE_DEV_PM_OPS
(
iqs5xx_pm
,
iqs5xx_suspend
,
iqs5xx_resume
);
static
int
iqs5xx_probe
(
struct
i2c_client
*
client
,
const
struct
i2c_device_id
*
id
)
{
struct
iqs5xx_private
*
iqs5xx
;
int
error
;
iqs5xx
=
devm_kzalloc
(
&
client
->
dev
,
sizeof
(
*
iqs5xx
),
GFP_KERNEL
);
if
(
!
iqs5xx
)
return
-
ENOMEM
;
dev_set_drvdata
(
&
client
->
dev
,
iqs5xx
);
i2c_set_clientdata
(
client
,
iqs5xx
);
iqs5xx
->
client
=
client
;
iqs5xx
->
reset_gpio
=
devm_gpiod_get
(
&
client
->
dev
,
"reset"
,
GPIOD_OUT_LOW
);
if
(
IS_ERR
(
iqs5xx
->
reset_gpio
))
{
error
=
PTR_ERR
(
iqs5xx
->
reset_gpio
);
dev_err
(
&
client
->
dev
,
"Failed to request GPIO: %d
\n
"
,
error
);
return
error
;
}
mutex_init
(
&
iqs5xx
->
lock
);
iqs5xx_reset
(
client
);
usleep_range
(
10000
,
10100
);
error
=
iqs5xx_dev_init
(
client
);
if
(
error
)
return
error
;
error
=
devm_request_threaded_irq
(
&
client
->
dev
,
client
->
irq
,
NULL
,
iqs5xx_irq
,
IRQF_ONESHOT
,
client
->
name
,
iqs5xx
);
if
(
error
)
{
dev_err
(
&
client
->
dev
,
"Failed to request IRQ: %d
\n
"
,
error
);
return
error
;
}
error
=
devm_device_add_group
(
&
client
->
dev
,
&
iqs5xx_attr_group
);
if
(
error
)
{
dev_err
(
&
client
->
dev
,
"Failed to add attributes: %d
\n
"
,
error
);
return
error
;
}
if
(
iqs5xx
->
input
)
{
error
=
input_register_device
(
iqs5xx
->
input
);
if
(
error
)
dev_err
(
&
client
->
dev
,
"Failed to register device: %d
\n
"
,
error
);
}
return
error
;
}
static
const
struct
i2c_device_id
iqs5xx_id
[]
=
{
{
"iqs550"
,
0
},
{
"iqs572"
,
1
},
{
"iqs525"
,
2
},
{
}
};
MODULE_DEVICE_TABLE
(
i2c
,
iqs5xx_id
);
static
const
struct
of_device_id
iqs5xx_of_match
[]
=
{
{
.
compatible
=
"azoteq,iqs550"
},
{
.
compatible
=
"azoteq,iqs572"
},
{
.
compatible
=
"azoteq,iqs525"
},
{
}
};
MODULE_DEVICE_TABLE
(
of
,
iqs5xx_of_match
);
static
struct
i2c_driver
iqs5xx_i2c_driver
=
{
.
driver
=
{
.
name
=
"iqs5xx"
,
.
of_match_table
=
iqs5xx_of_match
,
.
pm
=
&
iqs5xx_pm
,
},
.
id_table
=
iqs5xx_id
,
.
probe
=
iqs5xx_probe
,
};
module_i2c_driver
(
iqs5xx_i2c_driver
);
MODULE_AUTHOR
(
"Jeff LaBundy <jeff@labundy.com>"
);
MODULE_DESCRIPTION
(
"Azoteq IQS550/572/525 Trackpad/Touchscreen Controller"
);
MODULE_LICENSE
(
"GPL"
);
include/uapi/linux/input-event-codes.h
View file @
14e0c731
...
...
@@ -606,6 +606,7 @@
#define KEY_SCREENSAVER 0x245
/* AL Screen Saver */
#define KEY_VOICECOMMAND 0x246
/* Listening Voice Command */
#define KEY_ASSISTANT 0x247
/* AL Context-aware desktop assistant */
#define KEY_KBD_LAYOUT_NEXT 0x248
/* AC Next Keyboard Layout Select */
#define KEY_BRIGHTNESS_MIN 0x250
/* Set Brightness to Minimum */
#define KEY_BRIGHTNESS_MAX 0x251
/* Set Brightness to Maximum */
...
...
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment