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
nexedi
linux
Commits
bb0bc0cf
Commit
bb0bc0cf
authored
Oct 18, 2020
by
Dmitry Torokhov
Browse files
Options
Browse Files
Download
Plain Diff
Merge branch 'next' into for-linus
Prepare input updates for 5.10 merge window.
parents
5fc27b09
8a30c6eb
Changes
33
Hide whitespace changes
Inline
Side-by-side
Showing
33 changed files
with
1531 additions
and
171 deletions
+1531
-171
Documentation/devicetree/bindings/input/adc-joystick.yaml
Documentation/devicetree/bindings/input/adc-joystick.yaml
+121
-0
Documentation/devicetree/bindings/input/touchscreen/zinitix.txt
...ntation/devicetree/bindings/input/touchscreen/zinitix.txt
+40
-0
Documentation/devicetree/bindings/vendor-prefixes.yaml
Documentation/devicetree/bindings/vendor-prefixes.yaml
+2
-0
MAINTAINERS
MAINTAINERS
+7
-0
drivers/hid/hid-rmi.c
drivers/hid/hid-rmi.c
+1
-1
drivers/input/evdev.c
drivers/input/evdev.c
+9
-10
drivers/input/input-mt.c
drivers/input/input-mt.c
+7
-4
drivers/input/joystick/Kconfig
drivers/input/joystick/Kconfig
+10
-0
drivers/input/joystick/Makefile
drivers/input/joystick/Makefile
+1
-0
drivers/input/joystick/adc-joystick.c
drivers/input/joystick/adc-joystick.c
+264
-0
drivers/input/keyboard/ep93xx_keypad.c
drivers/input/keyboard/ep93xx_keypad.c
+2
-2
drivers/input/keyboard/omap4-keypad.c
drivers/input/keyboard/omap4-keypad.c
+2
-4
drivers/input/keyboard/twl4030_keypad.c
drivers/input/keyboard/twl4030_keypad.c
+3
-5
drivers/input/misc/soc_button_array.c
drivers/input/misc/soc_button_array.c
+82
-18
drivers/input/mouse/synaptics.c
drivers/input/mouse/synaptics.c
+4
-2
drivers/input/rmi4/Kconfig
drivers/input/rmi4/Kconfig
+8
-0
drivers/input/rmi4/Makefile
drivers/input/rmi4/Makefile
+1
-0
drivers/input/rmi4/rmi_bus.c
drivers/input/rmi4/rmi_bus.c
+3
-0
drivers/input/rmi4/rmi_driver.h
drivers/input/rmi4/rmi_driver.h
+1
-0
drivers/input/rmi4/rmi_f30.c
drivers/input/rmi4/rmi_f30.c
+7
-7
drivers/input/rmi4/rmi_f34v7.c
drivers/input/rmi4/rmi_f34v7.c
+7
-2
drivers/input/rmi4/rmi_f3a.c
drivers/input/rmi4/rmi_f3a.c
+241
-0
drivers/input/serio/sun4i-ps2.c
drivers/input/serio/sun4i-ps2.c
+3
-6
drivers/input/touchscreen/Kconfig
drivers/input/touchscreen/Kconfig
+12
-0
drivers/input/touchscreen/Makefile
drivers/input/touchscreen/Makefile
+1
-0
drivers/input/touchscreen/elants_i2c.c
drivers/input/touchscreen/elants_i2c.c
+7
-1
drivers/input/touchscreen/imx6ul_tsc.c
drivers/input/touchscreen/imx6ul_tsc.c
+22
-25
drivers/input/touchscreen/raydium_i2c_ts.c
drivers/input/touchscreen/raydium_i2c_ts.c
+58
-73
drivers/input/touchscreen/stmfts.c
drivers/input/touchscreen/stmfts.c
+1
-1
drivers/input/touchscreen/zinitix.c
drivers/input/touchscreen/zinitix.c
+581
-0
drivers/platform/x86/thinkpad_acpi.c
drivers/platform/x86/thinkpad_acpi.c
+13
-5
include/linux/rmi.h
include/linux/rmi.h
+6
-5
include/uapi/linux/input-event-codes.h
include/uapi/linux/input-event-codes.h
+4
-0
No files found.
Documentation/devicetree/bindings/input/adc-joystick.yaml
0 → 100644
View file @
bb0bc0cf
# SPDX-License-Identifier: (GPL-2.0 OR BSD-2-Clause)
# Copyright 2019-2020 Artur Rojek
%YAML
1.2
---
$id
:
"
http://devicetree.org/schemas/input/adc-joystick.yaml#"
$schema
:
"
http://devicetree.org/meta-schemas/core.yaml#"
title
:
ADC attached joystick
maintainers
:
-
Artur Rojek <contact@artur-rojek.eu>
description
:
>
Bindings for joystick devices connected to ADC controllers supporting
the Industrial I/O subsystem.
properties
:
compatible
:
const
:
adc-joystick
io-channels
:
minItems
:
1
maxItems
:
1024
description
:
>
List of phandle and IIO specifier pairs.
Each pair defines one ADC channel to which a joystick axis is connected.
See Documentation/devicetree/bindings/iio/iio-bindings.txt for details.
'
#address-cells'
:
const
:
1
'
#size-cells'
:
const
:
0
required
:
-
compatible
-
io-channels
-
'
#address-cells'
-
'
#size-cells'
additionalProperties
:
false
patternProperties
:
"
^axis@[0-9a-f]+$"
:
type
:
object
description
:
>
Represents a joystick axis bound to the given ADC channel.
For each entry in the io-channels list, one axis subnode with a matching
reg property must be specified.
properties
:
reg
:
minimum
:
0
maximum
:
1023
description
:
Index of an io-channels list entry bound to this axis.
linux,code
:
$ref
:
/schemas/types.yaml#/definitions/uint32
description
:
EV_ABS specific event code generated by the axis.
abs-range
:
allOf
:
-
$ref
:
/schemas/types.yaml#/definitions/uint32-array
-
items
:
-
description
:
minimum value
-
description
:
maximum value
description
:
>
Minimum and maximum values produced by the axis.
For an ABS_X axis this will be the left-most and right-most
inclination of the joystick. If min > max, it is left to userspace to
treat the axis as inverted.
This property is interpreted as two signed 32 bit values.
abs-fuzz
:
$ref
:
/schemas/types.yaml#/definitions/uint32
description
:
>
Amount of noise in the input value.
Omitting this property indicates the axis is precise.
abs-flat
:
$ref
:
/schemas/types.yaml#/definitions/uint32
description
:
>
Axial "deadzone", or area around the center position, where the axis
is considered to be at rest.
Omitting this property indicates the axis always returns to exactly
the center position.
required
:
-
reg
-
linux,code
-
abs-range
additionalProperties
:
false
examples
:
-
|
#include <dt-bindings/iio/adc/ingenic,adc.h>
#include <dt-bindings/input/input.h>
joystick: adc-joystick {
compatible = "adc-joystick";
io-channels = <&adc INGENIC_ADC_TOUCH_XP>,
<&adc INGENIC_ADC_TOUCH_YP>;
#address-cells = <1>;
#size-cells = <0>;
axis@0 {
reg = <0>;
linux,code = <ABS_X>;
abs-range = <3300 0>;
abs-fuzz = <4>;
abs-flat = <200>;
};
axis@1 {
reg = <1>;
linux,code = <ABS_Y>;
abs-range = <0 3300>;
abs-fuzz = <4>;
abs-flat = <200>;
};
};
Documentation/devicetree/bindings/input/touchscreen/zinitix.txt
0 → 100644
View file @
bb0bc0cf
Device tree bindings for Zinitx BT541 touchscreen controller
Required properties:
- compatible : Should be "zinitix,bt541"
- reg : I2C address of the chip. Should be 0x20
- interrupts : Interrupt to which the chip is connected
Optional properties:
- vdd-supply : Analog power supply regulator on VCCA pin
- vddo-supply : Digital power supply regulator on VDD pin
- zinitix,mode : Mode of reporting touch points. Some modes may not work
with a particular ts firmware for unknown reasons. Available
modes are 1 and 2. Mode 2 is the default and preferred.
The touchscreen-* properties are documented in touchscreen.txt in this
directory.
Example:
i2c@00000000 {
/* ... */
bt541@20 {
compatible = "zinitix,bt541";
reg = <0x20>;
interrupt-parent = <&msmgpio>;
interrupts = <13 IRQ_TYPE_EDGE_FALLING>;
pinctrl-names = "default";
pinctrl-0 = <&tsp_default>;
vdd-supply = <®_vdd_tsp>;
vddo-supply = <&pm8916_l6>;
touchscreen-size-x = <540>;
touchscreen-size-y = <960>;
zinitix,mode = <2>;
};
/* ... */
};
Documentation/devicetree/bindings/vendor-prefixes.yaml
View file @
bb0bc0cf
...
...
@@ -1143,6 +1143,8 @@ patternProperties:
description
:
Shenzhen Zidoo Technology Co., Ltd.
"
^zii,.*"
:
description
:
Zodiac Inflight Innovations
"
^zinitix,.*"
:
description
:
Zinitix Co., Ltd
"
^zte,.*"
:
description
:
ZTE Corp.
"
^zyxel,.*"
:
...
...
MAINTAINERS
View file @
bb0bc0cf
...
...
@@ -15441,6 +15441,13 @@ F: Documentation/fb/sisfb.rst
F: drivers/video/fbdev/sis/
F: include/video/sisfb.h
SIS I2C TOUCHSCREEN DRIVER
M: Mika Penttilä <mika.penttila@nextfour.com>
L: linux-input@vger.kernel.org
S: Maintained
F: Documentation/devicetree/bindings/input/touchscreen/sis_i2c.txt
F: drivers/input/touchscreen/sis_i2c.c
SIS USB2VGA DRIVER
M: Thomas Winischhofer <thomas@winischhofer.net>
S: Maintained
...
...
drivers/hid/hid-rmi.c
View file @
bb0bc0cf
...
...
@@ -721,7 +721,7 @@ static int rmi_probe(struct hid_device *hdev, const struct hid_device_id *id)
}
if
(
data
->
device_flags
&
RMI_DEVICE_HAS_PHYS_BUTTONS
)
rmi_hid_pdata
.
f30
_data
.
disable
=
true
;
rmi_hid_pdata
.
gpio
_data
.
disable
=
true
;
data
->
xport
.
dev
=
hdev
->
dev
.
parent
;
data
->
xport
.
pdata
=
rmi_hid_pdata
;
...
...
drivers/input/evdev.c
View file @
bb0bc0cf
...
...
@@ -28,7 +28,6 @@
struct
evdev
{
int
open
;
struct
input_handle
handle
;
wait_queue_head_t
wait
;
struct
evdev_client
__rcu
*
grab
;
struct
list_head
client_list
;
spinlock_t
client_lock
;
/* protects client_list */
...
...
@@ -43,6 +42,7 @@ struct evdev_client {
unsigned
int
tail
;
unsigned
int
packet_head
;
/* [future] position of the first element of next packet */
spinlock_t
buffer_lock
;
/* protects access to buffer, head and tail */
wait_queue_head_t
wait
;
struct
fasync_struct
*
fasync
;
struct
evdev
*
evdev
;
struct
list_head
node
;
...
...
@@ -245,7 +245,6 @@ static void evdev_pass_values(struct evdev_client *client,
const
struct
input_value
*
vals
,
unsigned
int
count
,
ktime_t
*
ev_time
)
{
struct
evdev
*
evdev
=
client
->
evdev
;
const
struct
input_value
*
v
;
struct
input_event
event
;
struct
timespec64
ts
;
...
...
@@ -282,7 +281,7 @@ static void evdev_pass_values(struct evdev_client *client,
spin_unlock
(
&
client
->
buffer_lock
);
if
(
wakeup
)
wake_up_interruptible_poll
(
&
evdev
->
wait
,
wake_up_interruptible_poll
(
&
client
->
wait
,
EPOLLIN
|
EPOLLOUT
|
EPOLLRDNORM
|
EPOLLWRNORM
);
}
...
...
@@ -426,11 +425,11 @@ static void evdev_hangup(struct evdev *evdev)
struct
evdev_client
*
client
;
spin_lock
(
&
evdev
->
client_lock
);
list_for_each_entry
(
client
,
&
evdev
->
client_list
,
node
)
list_for_each_entry
(
client
,
&
evdev
->
client_list
,
node
)
{
kill_fasync
(
&
client
->
fasync
,
SIGIO
,
POLL_HUP
);
wake_up_interruptible_poll
(
&
client
->
wait
,
EPOLLHUP
|
EPOLLERR
);
}
spin_unlock
(
&
evdev
->
client_lock
);
wake_up_interruptible_poll
(
&
evdev
->
wait
,
EPOLLHUP
|
EPOLLERR
);
}
static
int
evdev_release
(
struct
inode
*
inode
,
struct
file
*
file
)
...
...
@@ -479,6 +478,7 @@ static int evdev_open(struct inode *inode, struct file *file)
if
(
!
client
)
return
-
ENOMEM
;
init_waitqueue_head
(
&
client
->
wait
);
client
->
bufsize
=
bufsize
;
spin_lock_init
(
&
client
->
buffer_lock
);
client
->
evdev
=
evdev
;
...
...
@@ -595,7 +595,7 @@ static ssize_t evdev_read(struct file *file, char __user *buffer,
break
;
if
(
!
(
file
->
f_flags
&
O_NONBLOCK
))
{
error
=
wait_event_interruptible
(
evdev
->
wait
,
error
=
wait_event_interruptible
(
client
->
wait
,
client
->
packet_head
!=
client
->
tail
||
!
evdev
->
exist
||
client
->
revoked
);
if
(
error
)
...
...
@@ -613,7 +613,7 @@ static __poll_t evdev_poll(struct file *file, poll_table *wait)
struct
evdev
*
evdev
=
client
->
evdev
;
__poll_t
mask
;
poll_wait
(
file
,
&
evdev
->
wait
,
wait
);
poll_wait
(
file
,
&
client
->
wait
,
wait
);
if
(
evdev
->
exist
&&
!
client
->
revoked
)
mask
=
EPOLLOUT
|
EPOLLWRNORM
;
...
...
@@ -946,7 +946,7 @@ static int evdev_revoke(struct evdev *evdev, struct evdev_client *client,
client
->
revoked
=
true
;
evdev_ungrab
(
evdev
,
client
);
input_flush_device
(
&
evdev
->
handle
,
file
);
wake_up_interruptible_poll
(
&
evdev
->
wait
,
EPOLLHUP
|
EPOLLERR
);
wake_up_interruptible_poll
(
&
client
->
wait
,
EPOLLHUP
|
EPOLLERR
);
return
0
;
}
...
...
@@ -1358,7 +1358,6 @@ static int evdev_connect(struct input_handler *handler, struct input_dev *dev,
INIT_LIST_HEAD
(
&
evdev
->
client_list
);
spin_lock_init
(
&
evdev
->
client_lock
);
mutex_init
(
&
evdev
->
mutex
);
init_waitqueue_head
(
&
evdev
->
wait
);
evdev
->
exist
=
true
;
dev_no
=
minor
;
...
...
drivers/input/input-mt.c
View file @
bb0bc0cf
...
...
@@ -323,11 +323,14 @@ static int adjust_dual(int *begin, int step, int *end, int eq, int mu)
p
=
begin
+
step
;
s
=
p
==
end
?
f
+
1
:
*
p
;
for
(;
p
!=
end
;
p
+=
step
)
if
(
*
p
<
f
)
s
=
f
,
f
=
*
p
;
else
if
(
*
p
<
s
)
for
(;
p
!=
end
;
p
+=
step
)
{
if
(
*
p
<
f
)
{
s
=
f
;
f
=
*
p
;
}
else
if
(
*
p
<
s
)
{
s
=
*
p
;
}
}
c
=
(
f
+
s
+
1
)
/
2
;
if
(
c
==
0
||
(
c
>
mu
&&
(
!
eq
||
mu
>
0
)))
...
...
drivers/input/joystick/Kconfig
View file @
bb0bc0cf
...
...
@@ -42,6 +42,16 @@ config JOYSTICK_A3D
To compile this driver as a module, choose M here: the
module will be called a3d.
config JOYSTICK_ADC
tristate "Simple joystick connected over ADC"
depends on IIO
select IIO_BUFFER_CB
help
Say Y here if you have a simple joystick connected over ADC.
To compile this driver as a module, choose M here: the
module will be called adc-joystick.
config JOYSTICK_ADI
tristate "Logitech ADI digital joysticks and gamepads"
select GAMEPORT
...
...
drivers/input/joystick/Makefile
View file @
bb0bc0cf
...
...
@@ -6,6 +6,7 @@
# Each configuration option enables a list of files.
obj-$(CONFIG_JOYSTICK_A3D)
+=
a3d.o
obj-$(CONFIG_JOYSTICK_ADC)
+=
adc-joystick.o
obj-$(CONFIG_JOYSTICK_ADI)
+=
adi.o
obj-$(CONFIG_JOYSTICK_AMIGA)
+=
amijoy.o
obj-$(CONFIG_JOYSTICK_AS5011)
+=
as5011.o
...
...
drivers/input/joystick/adc-joystick.c
0 → 100644
View file @
bb0bc0cf
// SPDX-License-Identifier: GPL-2.0
/*
* Input driver for joysticks connected over ADC.
* Copyright (c) 2019-2020 Artur Rojek <contact@artur-rojek.eu>
*/
#include <linux/ctype.h>
#include <linux/input.h>
#include <linux/iio/iio.h>
#include <linux/iio/consumer.h>
#include <linux/module.h>
#include <linux/platform_device.h>
#include <linux/property.h>
#include <asm/unaligned.h>
struct
adc_joystick_axis
{
u32
code
;
s32
range
[
2
];
s32
fuzz
;
s32
flat
;
};
struct
adc_joystick
{
struct
input_dev
*
input
;
struct
iio_cb_buffer
*
buffer
;
struct
adc_joystick_axis
*
axes
;
struct
iio_channel
*
chans
;
int
num_chans
;
};
static
int
adc_joystick_handle
(
const
void
*
data
,
void
*
private
)
{
struct
adc_joystick
*
joy
=
private
;
enum
iio_endian
endianness
;
int
bytes
,
msb
,
val
,
idx
,
i
;
const
u16
*
data_u16
;
bool
sign
;
bytes
=
joy
->
chans
[
0
].
channel
->
scan_type
.
storagebits
>>
3
;
for
(
i
=
0
;
i
<
joy
->
num_chans
;
++
i
)
{
idx
=
joy
->
chans
[
i
].
channel
->
scan_index
;
endianness
=
joy
->
chans
[
i
].
channel
->
scan_type
.
endianness
;
msb
=
joy
->
chans
[
i
].
channel
->
scan_type
.
realbits
-
1
;
sign
=
tolower
(
joy
->
chans
[
i
].
channel
->
scan_type
.
sign
)
==
's'
;
switch
(
bytes
)
{
case
1
:
val
=
((
const
u8
*
)
data
)[
idx
];
break
;
case
2
:
data_u16
=
(
const
u16
*
)
data
+
idx
;
/*
* Data is aligned to the sample size by IIO core.
* Call `get_unaligned_xe16` to hide type casting.
*/
if
(
endianness
==
IIO_BE
)
val
=
get_unaligned_be16
(
data_u16
);
else
if
(
endianness
==
IIO_LE
)
val
=
get_unaligned_le16
(
data_u16
);
else
/* IIO_CPU */
val
=
*
data_u16
;
break
;
default:
return
-
EINVAL
;
}
val
>>=
joy
->
chans
[
i
].
channel
->
scan_type
.
shift
;
if
(
sign
)
val
=
sign_extend32
(
val
,
msb
);
else
val
&=
GENMASK
(
msb
,
0
);
input_report_abs
(
joy
->
input
,
joy
->
axes
[
i
].
code
,
val
);
}
input_sync
(
joy
->
input
);
return
0
;
}
static
int
adc_joystick_open
(
struct
input_dev
*
dev
)
{
struct
adc_joystick
*
joy
=
input_get_drvdata
(
dev
);
struct
device
*
devp
=
&
dev
->
dev
;
int
ret
;
ret
=
iio_channel_start_all_cb
(
joy
->
buffer
);
if
(
ret
)
dev_err
(
devp
,
"Unable to start callback buffer: %d
\n
"
,
ret
);
return
ret
;
}
static
void
adc_joystick_close
(
struct
input_dev
*
dev
)
{
struct
adc_joystick
*
joy
=
input_get_drvdata
(
dev
);
iio_channel_stop_all_cb
(
joy
->
buffer
);
}
static
void
adc_joystick_cleanup
(
void
*
data
)
{
iio_channel_release_all_cb
(
data
);
}
static
int
adc_joystick_set_axes
(
struct
device
*
dev
,
struct
adc_joystick
*
joy
)
{
struct
adc_joystick_axis
*
axes
;
struct
fwnode_handle
*
child
;
int
num_axes
,
error
,
i
;
num_axes
=
device_get_child_node_count
(
dev
);
if
(
!
num_axes
)
{
dev_err
(
dev
,
"Unable to find child nodes
\n
"
);
return
-
EINVAL
;
}
if
(
num_axes
!=
joy
->
num_chans
)
{
dev_err
(
dev
,
"Got %d child nodes for %d channels
\n
"
,
num_axes
,
joy
->
num_chans
);
return
-
EINVAL
;
}
axes
=
devm_kmalloc_array
(
dev
,
num_axes
,
sizeof
(
*
axes
),
GFP_KERNEL
);
if
(
!
axes
)
return
-
ENOMEM
;
device_for_each_child_node
(
dev
,
child
)
{
error
=
fwnode_property_read_u32
(
child
,
"reg"
,
&
i
);
if
(
error
)
{
dev_err
(
dev
,
"reg invalid or missing
\n
"
);
goto
err_fwnode_put
;
}
if
(
i
>=
num_axes
)
{
error
=
-
EINVAL
;
dev_err
(
dev
,
"No matching axis for reg %d
\n
"
,
i
);
goto
err_fwnode_put
;
}
error
=
fwnode_property_read_u32
(
child
,
"linux,code"
,
&
axes
[
i
].
code
);
if
(
error
)
{
dev_err
(
dev
,
"linux,code invalid or missing
\n
"
);
goto
err_fwnode_put
;
}
error
=
fwnode_property_read_u32_array
(
child
,
"abs-range"
,
axes
[
i
].
range
,
2
);
if
(
error
)
{
dev_err
(
dev
,
"abs-range invalid or missing
\n
"
);
goto
err_fwnode_put
;
}
fwnode_property_read_u32
(
child
,
"abs-fuzz"
,
&
axes
[
i
].
fuzz
);
fwnode_property_read_u32
(
child
,
"abs-flat"
,
&
axes
[
i
].
flat
);
input_set_abs_params
(
joy
->
input
,
axes
[
i
].
code
,
axes
[
i
].
range
[
0
],
axes
[
i
].
range
[
1
],
axes
[
i
].
fuzz
,
axes
[
i
].
flat
);
input_set_capability
(
joy
->
input
,
EV_ABS
,
axes
[
i
].
code
);
}
joy
->
axes
=
axes
;
return
0
;
err_fwnode_put:
fwnode_handle_put
(
child
);
return
error
;
}
static
int
adc_joystick_probe
(
struct
platform_device
*
pdev
)
{
struct
device
*
dev
=
&
pdev
->
dev
;
struct
adc_joystick
*
joy
;
struct
input_dev
*
input
;
int
error
;
int
bits
;
int
i
;
joy
=
devm_kzalloc
(
dev
,
sizeof
(
*
joy
),
GFP_KERNEL
);
if
(
!
joy
)
return
-
ENOMEM
;
joy
->
chans
=
devm_iio_channel_get_all
(
dev
);
if
(
IS_ERR
(
joy
->
chans
))
{
error
=
PTR_ERR
(
joy
->
chans
);
if
(
error
!=
-
EPROBE_DEFER
)
dev_err
(
dev
,
"Unable to get IIO channels"
);
return
error
;
}
/* Count how many channels we got. NULL terminated. */
for
(
i
=
0
;
joy
->
chans
[
i
].
indio_dev
;
i
++
)
{
bits
=
joy
->
chans
[
i
].
channel
->
scan_type
.
storagebits
;
if
(
!
bits
||
bits
>
16
)
{
dev_err
(
dev
,
"Unsupported channel storage size
\n
"
);
return
-
EINVAL
;
}
if
(
bits
!=
joy
->
chans
[
0
].
channel
->
scan_type
.
storagebits
)
{
dev_err
(
dev
,
"Channels must have equal storage size
\n
"
);
return
-
EINVAL
;
}
}
joy
->
num_chans
=
i
;
input
=
devm_input_allocate_device
(
dev
);
if
(
!
input
)
{
dev_err
(
dev
,
"Unable to allocate input device
\n
"
);
return
-
ENOMEM
;
}
joy
->
input
=
input
;
input
->
name
=
pdev
->
name
;
input
->
id
.
bustype
=
BUS_HOST
;
input
->
open
=
adc_joystick_open
;
input
->
close
=
adc_joystick_close
;
error
=
adc_joystick_set_axes
(
dev
,
joy
);
if
(
error
)
return
error
;
input_set_drvdata
(
input
,
joy
);
error
=
input_register_device
(
input
);
if
(
error
)
{
dev_err
(
dev
,
"Unable to register input device
\n
"
);
return
error
;
}
joy
->
buffer
=
iio_channel_get_all_cb
(
dev
,
adc_joystick_handle
,
joy
);
if
(
IS_ERR
(
joy
->
buffer
))
{
dev_err
(
dev
,
"Unable to allocate callback buffer
\n
"
);
return
PTR_ERR
(
joy
->
buffer
);
}
error
=
devm_add_action_or_reset
(
dev
,
adc_joystick_cleanup
,
joy
->
buffer
);
if
(
error
)
{
dev_err
(
dev
,
"Unable to add action
\n
"
);
return
error
;
}
return
0
;
}
static
const
struct
of_device_id
adc_joystick_of_match
[]
=
{
{
.
compatible
=
"adc-joystick"
,
},
{
}
};
MODULE_DEVICE_TABLE
(
of
,
adc_joystick_of_match
);
static
struct
platform_driver
adc_joystick_driver
=
{
.
driver
=
{
.
name
=
"adc-joystick"
,
.
of_match_table
=
adc_joystick_of_match
,
},
.
probe
=
adc_joystick_probe
,
};
module_platform_driver
(
adc_joystick_driver
);
MODULE_DESCRIPTION
(
"Input driver for joysticks connected over ADC"
);
MODULE_AUTHOR
(
"Artur Rojek <contact@artur-rojek.eu>"
);
MODULE_LICENSE
(
"GPL"
);
drivers/input/keyboard/ep93xx_keypad.c
View file @
bb0bc0cf
...
...
@@ -250,8 +250,8 @@ static int ep93xx_keypad_probe(struct platform_device *pdev)
}
keypad
->
irq
=
platform_get_irq
(
pdev
,
0
);
if
(
!
keypad
->
irq
)
{
err
=
-
ENXIO
;
if
(
keypad
->
irq
<
0
)
{
err
=
keypad
->
irq
;
goto
failed_free
;
}
...
...
drivers/input/keyboard/omap4-keypad.c
View file @
bb0bc0cf
...
...
@@ -240,10 +240,8 @@ static int omap4_keypad_probe(struct platform_device *pdev)
}
irq
=
platform_get_irq
(
pdev
,
0
);
if
(
!
irq
)
{
dev_err
(
&
pdev
->
dev
,
"no keyboard irq assigned
\n
"
);
return
-
EINVAL
;
}
if
(
irq
<
0
)
return
irq
;
keypad_data
=
kzalloc
(
sizeof
(
struct
omap4_keypad
),
GFP_KERNEL
);
if
(
!
keypad_data
)
{
...
...
drivers/input/keyboard/twl4030_keypad.c
View file @
bb0bc0cf
...
...
@@ -50,7 +50,7 @@ struct twl4030_keypad {
bool
autorepeat
;
unsigned
int
n_rows
;
unsigned
int
n_cols
;
unsigned
int
irq
;
int
irq
;
struct
device
*
dbg_dev
;
struct
input_dev
*
input
;
...
...
@@ -376,10 +376,8 @@ static int twl4030_kp_probe(struct platform_device *pdev)
}
kp
->
irq
=
platform_get_irq
(
pdev
,
0
);
if
(
!
kp
->
irq
)
{
dev_err
(
&
pdev
->
dev
,
"no keyboard irq assigned
\n
"
);
return
-
EINVAL
;
}
if
(
kp
->
irq
<
0
)
return
kp
->
irq
;
error
=
matrix_keypad_build_keymap
(
keymap_data
,
NULL
,
TWL4030_MAX_ROWS
,
...
...
drivers/input/misc/soc_button_array.c
View file @
bb0bc0cf
...
...
@@ -11,6 +11,7 @@
#include <linux/init.h>
#include <linux/kernel.h>
#include <linux/acpi.h>
#include <linux/dmi.h>
#include <linux/gpio/consumer.h>
#include <linux/gpio_keys.h>
#include <linux/gpio.h>
...
...
@@ -23,6 +24,7 @@ struct soc_button_info {
unsigned
int
event_code
;
bool
autorepeat
;
bool
wakeup
;
bool
active_low
;
};
struct
soc_device_data
{
...
...
@@ -41,23 +43,66 @@ struct soc_button_data {
struct
platform_device
*
children
[
BUTTON_TYPES
];
};
/*
* Some 2-in-1s which use the soc_button_array driver have this ugly issue in
* their DSDT where the _LID method modifies the irq-type settings of the GPIOs
* used for the power and home buttons. The intend of this AML code is to
* disable these buttons when the lid is closed.
* The AML does this by directly poking the GPIO controllers registers. This is
* problematic because when re-enabling the irq, which happens whenever _LID
* gets called with the lid open (e.g. on boot and on resume), it sets the
* irq-type to IRQ_TYPE_LEVEL_LOW. Where as the gpio-keys driver programs the
* type to, and expects it to be, IRQ_TYPE_EDGE_BOTH.
* To work around this we don't set gpio_keys_button.gpio on these 2-in-1s,
* instead we get the irq for the GPIO ourselves, configure it as
* IRQ_TYPE_LEVEL_LOW (to match how the _LID AML code configures it) and pass
* the irq in gpio_keys_button.irq. Below is a list of affected devices.
*/
static
const
struct
dmi_system_id
dmi_use_low_level_irq
[]
=
{
{
/*
* Acer Switch 10 SW5-012. _LID method messes with home- and
* power-button GPIO IRQ settings. When (re-)enabling the irq
* it ors in its own flags without clearing the previous set
* ones, leading to an irq-type of IRQ_TYPE_LEVEL_LOW |
* IRQ_TYPE_LEVEL_HIGH causing a continuous interrupt storm.
*/
.
matches
=
{
DMI_MATCH
(
DMI_SYS_VENDOR
,
"Acer"
),
DMI_MATCH
(
DMI_PRODUCT_NAME
,
"Aspire SW5-012"
),
},
},
{
/*
* Acer One S1003. _LID method messes with power-button GPIO
* IRQ settings, leading to a non working power-button.
*/
.
matches
=
{
DMI_MATCH
(
DMI_SYS_VENDOR
,
"Acer"
),
DMI_MATCH
(
DMI_PRODUCT_NAME
,
"One S1003"
),
},
},
{}
/* Terminating entry */
};
/*
* Get the Nth GPIO number from the ACPI object.
*/
static
int
soc_button_lookup_gpio
(
struct
device
*
dev
,
int
acpi_index
)
static
int
soc_button_lookup_gpio
(
struct
device
*
dev
,
int
acpi_index
,
int
*
gpio_ret
,
int
*
irq_ret
)
{
struct
gpio_desc
*
desc
;
int
gpio
;
desc
=
gpiod_get_index
(
dev
,
NULL
,
acpi_index
,
GPIOD_ASIS
);
if
(
IS_ERR
(
desc
))
return
PTR_ERR
(
desc
);
gpio
=
desc_to_gpio
(
desc
);
*
gpio_ret
=
desc_to_gpio
(
desc
);
*
irq_ret
=
gpiod_to_irq
(
desc
);
gpiod_put
(
desc
);
return
gpio
;
return
0
;
}
static
struct
platform_device
*
...
...
@@ -69,9 +114,8 @@ soc_button_device_create(struct platform_device *pdev,
struct
platform_device
*
pd
;
struct
gpio_keys_button
*
gpio_keys
;
struct
gpio_keys_platform_data
*
gpio_keys_pdata
;
int
error
,
gpio
,
irq
;
int
n_buttons
=
0
;
int
gpio
;
int
error
;
for
(
info
=
button_info
;
info
->
name
;
info
++
)
if
(
info
->
autorepeat
==
autorepeat
)
...
...
@@ -91,8 +135,8 @@ soc_button_device_create(struct platform_device *pdev,
if
(
info
->
autorepeat
!=
autorepeat
)
continue
;
gpio
=
soc_button_lookup_gpio
(
&
pdev
->
dev
,
info
->
acpi_index
);
if
(
!
gpio_is_valid
(
gpio
)
)
{
error
=
soc_button_lookup_gpio
(
&
pdev
->
dev
,
info
->
acpi_index
,
&
gpio
,
&
irq
);
if
(
error
||
irq
<
0
)
{
/*
* Skip GPIO if not present. Note we deliberately
* ignore -EPROBE_DEFER errors here. On some devices
...
...
@@ -107,10 +151,18 @@ soc_button_device_create(struct platform_device *pdev,
continue
;
}
/* See dmi_use_low_level_irq[] comment */
if
(
!
autorepeat
&&
dmi_check_system
(
dmi_use_low_level_irq
))
{
irq_set_irq_type
(
irq
,
IRQ_TYPE_LEVEL_LOW
);
gpio_keys
[
n_buttons
].
irq
=
irq
;
gpio_keys
[
n_buttons
].
gpio
=
-
ENOENT
;
}
else
{
gpio_keys
[
n_buttons
].
gpio
=
gpio
;
}
gpio_keys
[
n_buttons
].
type
=
info
->
event_type
;
gpio_keys
[
n_buttons
].
code
=
info
->
event_code
;
gpio_keys
[
n_buttons
].
gpio
=
gpio
;
gpio_keys
[
n_buttons
].
active_low
=
1
;
gpio_keys
[
n_buttons
].
active_low
=
info
->
active_low
;
gpio_keys
[
n_buttons
].
desc
=
info
->
name
;
gpio_keys
[
n_buttons
].
wakeup
=
info
->
wakeup
;
/* These devices often use cheap buttons, use 50 ms debounce */
...
...
@@ -173,6 +225,7 @@ static int soc_button_parse_btn_desc(struct device *dev,
}
info
->
event_type
=
EV_KEY
;
info
->
active_low
=
true
;
info
->
acpi_index
=
soc_button_get_acpi_object_int
(
&
desc
->
package
.
elements
[
1
]);
upage
=
soc_button_get_acpi_object_int
(
&
desc
->
package
.
elements
[
3
]);
...
...
@@ -383,11 +436,11 @@ static int soc_button_probe(struct platform_device *pdev)
* Platforms"
*/
static
const
struct
soc_button_info
soc_button_PNP0C40
[]
=
{
{
"power"
,
0
,
EV_KEY
,
KEY_POWER
,
false
,
true
},
{
"home"
,
1
,
EV_KEY
,
KEY_LEFTMETA
,
false
,
true
},
{
"volume_up"
,
2
,
EV_KEY
,
KEY_VOLUMEUP
,
true
,
false
},
{
"volume_down"
,
3
,
EV_KEY
,
KEY_VOLUMEDOWN
,
true
,
false
},
{
"rotation_lock"
,
4
,
EV_KEY
,
KEY_ROTATE_LOCK_TOGGLE
,
false
,
false
},
{
"power"
,
0
,
EV_KEY
,
KEY_POWER
,
false
,
true
,
true
},
{
"home"
,
1
,
EV_KEY
,
KEY_LEFTMETA
,
false
,
true
,
true
},
{
"volume_up"
,
2
,
EV_KEY
,
KEY_VOLUMEUP
,
true
,
false
,
true
},
{
"volume_down"
,
3
,
EV_KEY
,
KEY_VOLUMEDOWN
,
true
,
false
,
true
},
{
"rotation_lock"
,
4
,
EV_KEY
,
KEY_ROTATE_LOCK_TOGGLE
,
false
,
false
,
true
},
{
}
};
...
...
@@ -395,6 +448,15 @@ static const struct soc_device_data soc_device_PNP0C40 = {
.
button_info
=
soc_button_PNP0C40
,
};
static
const
struct
soc_button_info
soc_button_INT33D3
[]
=
{
{
"tablet_mode"
,
0
,
EV_SW
,
SW_TABLET_MODE
,
false
,
false
,
false
},
{
}
};
static
const
struct
soc_device_data
soc_device_INT33D3
=
{
.
button_info
=
soc_button_INT33D3
,
};
/*
* Special device check for Surface Book 2 and Surface Pro (2017).
* Both, the Surface Pro 4 (surfacepro3_button.c) and the above mentioned
...
...
@@ -444,9 +506,9 @@ static int soc_device_check_MSHW0040(struct device *dev)
* Obtained from DSDT/testing.
*/
static
const
struct
soc_button_info
soc_button_MSHW0040
[]
=
{
{
"power"
,
0
,
EV_KEY
,
KEY_POWER
,
false
,
true
},
{
"volume_up"
,
2
,
EV_KEY
,
KEY_VOLUMEUP
,
true
,
false
},
{
"volume_down"
,
4
,
EV_KEY
,
KEY_VOLUMEDOWN
,
true
,
false
},
{
"power"
,
0
,
EV_KEY
,
KEY_POWER
,
false
,
true
,
true
},
{
"volume_up"
,
2
,
EV_KEY
,
KEY_VOLUMEUP
,
true
,
false
,
true
},
{
"volume_down"
,
4
,
EV_KEY
,
KEY_VOLUMEDOWN
,
true
,
false
,
true
},
{
}
};
...
...
@@ -457,6 +519,8 @@ static const struct soc_device_data soc_device_MSHW0040 = {
static
const
struct
acpi_device_id
soc_button_acpi_match
[]
=
{
{
"PNP0C40"
,
(
unsigned
long
)
&
soc_device_PNP0C40
},
{
"INT33D3"
,
(
unsigned
long
)
&
soc_device_INT33D3
},
{
"ID9001"
,
(
unsigned
long
)
&
soc_device_INT33D3
},
{
"ACPI0011"
,
0
},
/* Microsoft Surface Devices (5th and 6th generation) */
...
...
drivers/input/mouse/synaptics.c
View file @
bb0bc0cf
...
...
@@ -179,12 +179,14 @@ static const char * const smbus_pnp_ids[] = {
"LEN0093"
,
/* T480 */
"LEN0096"
,
/* X280 */
"LEN0097"
,
/* X280 -> ALPS trackpoint */
"LEN0099"
,
/* X1 Extreme
1st
*/
"LEN0099"
,
/* X1 Extreme
Gen 1 / P1 Gen 1
*/
"LEN009b"
,
/* T580 */
"LEN0402"
,
/* X1 Extreme Gen 2 / P1 Gen 2 */
"LEN200f"
,
/* T450s */
"LEN2044"
,
/* L470 */
"LEN2054"
,
/* E480 */
"LEN2055"
,
/* E580 */
"LEN2068"
,
/* T14 Gen 1 */
"SYN3052"
,
/* HP EliteBook 840 G4 */
"SYN3221"
,
/* HP 15-ay000 */
"SYN323d"
,
/* HP Spectre X360 13-w013dx */
...
...
@@ -1752,7 +1754,7 @@ static int synaptics_create_intertouch(struct psmouse *psmouse,
.
kernel_tracking
=
false
,
.
topbuttonpad
=
topbuttonpad
,
},
.
f30
_data
=
{
.
gpio
_data
=
{
.
buttonpad
=
SYN_CAP_CLICKPAD
(
info
->
ext_cap_0c
),
.
trackstick_buttons
=
!!
SYN_CAP_EXT_BUTTONS_STICK
(
info
->
ext_cap_10
),
...
...
drivers/input/rmi4/Kconfig
View file @
bb0bc0cf
...
...
@@ -100,6 +100,14 @@ config RMI4_F34
device via the firmware loader interface. This is triggered using a
sysfs attribute.
config RMI4_F3A
bool "RMI4 Function 3A (GPIO)"
help
Say Y here if you want to add support for RMI4 function 3A.
Function 3A provides GPIO support for RMI4 devices. This includes
support for buttons on TouchPads and ClickPads.
config RMI4_F54
bool "RMI4 Function 54 (Analog diagnostics)"
depends on VIDEO_V4L2=y || (RMI4_CORE=m && VIDEO_V4L2=m)
...
...
drivers/input/rmi4/Makefile
View file @
bb0bc0cf
...
...
@@ -10,6 +10,7 @@ rmi_core-$(CONFIG_RMI4_F11) += rmi_f11.o
rmi_core-$(CONFIG_RMI4_F12)
+=
rmi_f12.o
rmi_core-$(CONFIG_RMI4_F30)
+=
rmi_f30.o
rmi_core-$(CONFIG_RMI4_F34)
+=
rmi_f34.o rmi_f34v7.o
rmi_core-$(CONFIG_RMI4_F3A)
+=
rmi_f3a.o
rmi_core-$(CONFIG_RMI4_F54)
+=
rmi_f54.o
rmi_core-$(CONFIG_RMI4_F55)
+=
rmi_f55.o
...
...
drivers/input/rmi4/rmi_bus.c
View file @
bb0bc0cf
...
...
@@ -365,6 +365,9 @@ static struct rmi_function_handler *fn_handlers[] = {
#ifdef CONFIG_RMI4_F34
&
rmi_f34_handler
,
#endif
#ifdef CONFIG_RMI4_F3A
&
rmi_f3a_handler
,
#endif
#ifdef CONFIG_RMI4_F54
&
rmi_f54_handler
,
#endif
...
...
drivers/input/rmi4/rmi_driver.h
View file @
bb0bc0cf
...
...
@@ -135,6 +135,7 @@ extern struct rmi_function_handler rmi_f11_handler;
extern
struct
rmi_function_handler
rmi_f12_handler
;
extern
struct
rmi_function_handler
rmi_f30_handler
;
extern
struct
rmi_function_handler
rmi_f34_handler
;
extern
struct
rmi_function_handler
rmi_f3a_handler
;
extern
struct
rmi_function_handler
rmi_f54_handler
;
extern
struct
rmi_function_handler
rmi_f55_handler
;
#endif
drivers/input/rmi4/rmi_f30.c
View file @
bb0bc0cf
...
...
@@ -168,17 +168,17 @@ static int rmi_f30_config(struct rmi_function *fn)
rmi_get_platform_data
(
fn
->
rmi_dev
);
int
error
;
/* can happen if
f30
_data.disable is set */
/* can happen if
gpio
_data.disable is set */
if
(
!
f30
)
return
0
;
if
(
pdata
->
f30
_data
.
trackstick_buttons
)
{
if
(
pdata
->
gpio
_data
.
trackstick_buttons
)
{
/* Try [re-]establish link to F03. */
f30
->
f03
=
rmi_find_function
(
fn
->
rmi_dev
,
0x03
);
f30
->
trackstick_buttons
=
f30
->
f03
!=
NULL
;
}
if
(
pdata
->
f30
_data
.
disable
)
{
if
(
pdata
->
gpio
_data
.
disable
)
{
drv
->
clear_irq_bits
(
fn
->
rmi_dev
,
fn
->
irq_mask
);
}
else
{
/* Write Control Register values back to device */
...
...
@@ -245,10 +245,10 @@ static int rmi_f30_map_gpios(struct rmi_function *fn,
if
(
!
rmi_f30_is_valid_button
(
i
,
f30
->
ctrl
))
continue
;
if
(
pdata
->
f30
_data
.
trackstick_buttons
&&
if
(
pdata
->
gpio
_data
.
trackstick_buttons
&&
i
>=
TRACKSTICK_RANGE_START
&&
i
<
TRACKSTICK_RANGE_END
)
{
f30
->
gpioled_key_map
[
i
]
=
trackstick_button
++
;
}
else
if
(
!
pdata
->
f30
_data
.
buttonpad
||
!
button_mapped
)
{
}
else
if
(
!
pdata
->
gpio
_data
.
buttonpad
||
!
button_mapped
)
{
f30
->
gpioled_key_map
[
i
]
=
button
;
input_set_capability
(
input
,
EV_KEY
,
button
++
);
button_mapped
=
true
;
...
...
@@ -264,7 +264,7 @@ static int rmi_f30_map_gpios(struct rmi_function *fn,
* but I am not sure, so use only the pdata info and the number of
* mapped buttons.
*/
if
(
pdata
->
f30
_data
.
buttonpad
||
(
button
-
BTN_LEFT
==
1
))
if
(
pdata
->
gpio
_data
.
buttonpad
||
(
button
-
BTN_LEFT
==
1
))
__set_bit
(
INPUT_PROP_BUTTONPAD
,
input
->
propbit
);
return
0
;
...
...
@@ -372,7 +372,7 @@ static int rmi_f30_probe(struct rmi_function *fn)
struct
f30_data
*
f30
;
int
error
;
if
(
pdata
->
f30
_data
.
disable
)
if
(
pdata
->
gpio
_data
.
disable
)
return
0
;
if
(
!
drv_data
->
input
)
{
...
...
drivers/input/rmi4/rmi_f34v7.c
View file @
bb0bc0cf
...
...
@@ -1364,9 +1364,14 @@ int rmi_f34v7_probe(struct f34_data *f34)
f34
->
bl_version
=
6
;
}
else
if
(
f34
->
bootloader_id
[
1
]
==
7
)
{
f34
->
bl_version
=
7
;
}
else
if
(
f34
->
bootloader_id
[
1
]
==
8
)
{
f34
->
bl_version
=
8
;
}
else
{
dev_err
(
&
f34
->
fn
->
dev
,
"%s: Unrecognized bootloader version
\n
"
,
__func__
);
dev_err
(
&
f34
->
fn
->
dev
,
"%s: Unrecognized bootloader version: %d (%c) %d (%c)
\n
"
,
__func__
,
f34
->
bootloader_id
[
0
],
f34
->
bootloader_id
[
0
],
f34
->
bootloader_id
[
1
],
f34
->
bootloader_id
[
1
]);
return
-
EINVAL
;
}
...
...
drivers/input/rmi4/rmi_f3a.c
0 → 100644
View file @
bb0bc0cf
// SPDX-License-Identifier: GPL-2.0-only
/*
* Copyright (c) 2012-2020 Synaptics Incorporated
*/
#include <linux/kernel.h>
#include <linux/rmi.h>
#include <linux/input.h>
#include <linux/slab.h>
#include "rmi_driver.h"
#define RMI_F3A_MAX_GPIO_COUNT 128
#define RMI_F3A_MAX_REG_SIZE DIV_ROUND_UP(RMI_F3A_MAX_GPIO_COUNT, 8)
/* Defs for Query 0 */
#define RMI_F3A_GPIO_COUNT 0x7F
#define RMI_F3A_DATA_REGS_MAX_SIZE RMI_F3A_MAX_REG_SIZE
#define TRACKSTICK_RANGE_START 3
#define TRACKSTICK_RANGE_END 6
struct
f3a_data
{
/* Query Data */
u8
gpio_count
;
u8
register_count
;
u8
data_regs
[
RMI_F3A_DATA_REGS_MAX_SIZE
];
u16
*
gpio_key_map
;
struct
input_dev
*
input
;
struct
rmi_function
*
f03
;
bool
trackstick_buttons
;
};
static
void
rmi_f3a_report_button
(
struct
rmi_function
*
fn
,
struct
f3a_data
*
f3a
,
unsigned
int
button
)
{
u16
key_code
=
f3a
->
gpio_key_map
[
button
];
bool
key_down
=
!
(
f3a
->
data_regs
[
0
]
&
BIT
(
button
));
if
(
f3a
->
trackstick_buttons
&&
button
>=
TRACKSTICK_RANGE_START
&&
button
<=
TRACKSTICK_RANGE_END
)
{
rmi_f03_overwrite_button
(
f3a
->
f03
,
key_code
,
key_down
);
}
else
{
rmi_dbg
(
RMI_DEBUG_FN
,
&
fn
->
dev
,
"%s: call input report key (0x%04x) value (0x%02x)"
,
__func__
,
key_code
,
key_down
);
input_report_key
(
f3a
->
input
,
key_code
,
key_down
);
}
}
static
irqreturn_t
rmi_f3a_attention
(
int
irq
,
void
*
ctx
)
{
struct
rmi_function
*
fn
=
ctx
;
struct
f3a_data
*
f3a
=
dev_get_drvdata
(
&
fn
->
dev
);
struct
rmi_driver_data
*
drvdata
=
dev_get_drvdata
(
&
fn
->
rmi_dev
->
dev
);
int
error
;
int
i
;
if
(
drvdata
->
attn_data
.
data
)
{
if
(
drvdata
->
attn_data
.
size
<
f3a
->
register_count
)
{
dev_warn
(
&
fn
->
dev
,
"F3A interrupted, but data is missing
\n
"
);
return
IRQ_HANDLED
;
}
memcpy
(
f3a
->
data_regs
,
drvdata
->
attn_data
.
data
,
f3a
->
register_count
);
drvdata
->
attn_data
.
data
+=
f3a
->
register_count
;
drvdata
->
attn_data
.
size
-=
f3a
->
register_count
;
}
else
{
error
=
rmi_read_block
(
fn
->
rmi_dev
,
fn
->
fd
.
data_base_addr
,
f3a
->
data_regs
,
f3a
->
register_count
);
if
(
error
)
{
dev_err
(
&
fn
->
dev
,
"%s: Failed to read F3a data registers: %d
\n
"
,
__func__
,
error
);
return
IRQ_RETVAL
(
error
);
}
}
for
(
i
=
0
;
i
<
f3a
->
gpio_count
;
i
++
)
if
(
f3a
->
gpio_key_map
[
i
]
!=
KEY_RESERVED
)
rmi_f3a_report_button
(
fn
,
f3a
,
i
);
if
(
f3a
->
trackstick_buttons
)
rmi_f03_commit_buttons
(
f3a
->
f03
);
return
IRQ_HANDLED
;
}
static
int
rmi_f3a_config
(
struct
rmi_function
*
fn
)
{
struct
f3a_data
*
f3a
=
dev_get_drvdata
(
&
fn
->
dev
);
struct
rmi_driver
*
drv
=
fn
->
rmi_dev
->
driver
;
const
struct
rmi_device_platform_data
*
pdata
=
rmi_get_platform_data
(
fn
->
rmi_dev
);
if
(
!
f3a
)
return
0
;
if
(
pdata
->
gpio_data
.
trackstick_buttons
)
{
/* Try [re-]establish link to F03. */
f3a
->
f03
=
rmi_find_function
(
fn
->
rmi_dev
,
0x03
);
f3a
->
trackstick_buttons
=
f3a
->
f03
!=
NULL
;
}
drv
->
set_irq_bits
(
fn
->
rmi_dev
,
fn
->
irq_mask
);
return
0
;
}
static
bool
rmi_f3a_is_valid_button
(
int
button
,
struct
f3a_data
*
f3a
,
u8
*
query1_regs
,
u8
*
ctrl1_regs
)
{
/* gpio exist && direction input */
return
(
query1_regs
[
0
]
&
BIT
(
button
))
&&
!
(
ctrl1_regs
[
0
]
&
BIT
(
button
));
}
static
int
rmi_f3a_map_gpios
(
struct
rmi_function
*
fn
,
struct
f3a_data
*
f3a
,
u8
*
query1_regs
,
u8
*
ctrl1_regs
)
{
const
struct
rmi_device_platform_data
*
pdata
=
rmi_get_platform_data
(
fn
->
rmi_dev
);
struct
input_dev
*
input
=
f3a
->
input
;
unsigned
int
button
=
BTN_LEFT
;
unsigned
int
trackstick_button
=
BTN_LEFT
;
bool
button_mapped
=
false
;
int
i
;
int
button_count
=
min_t
(
u8
,
f3a
->
gpio_count
,
TRACKSTICK_RANGE_END
);
f3a
->
gpio_key_map
=
devm_kcalloc
(
&
fn
->
dev
,
button_count
,
sizeof
(
f3a
->
gpio_key_map
[
0
]),
GFP_KERNEL
);
if
(
!
f3a
->
gpio_key_map
)
{
dev_err
(
&
fn
->
dev
,
"Failed to allocate gpio map memory.
\n
"
);
return
-
ENOMEM
;
}
for
(
i
=
0
;
i
<
button_count
;
i
++
)
{
if
(
!
rmi_f3a_is_valid_button
(
i
,
f3a
,
query1_regs
,
ctrl1_regs
))
continue
;
if
(
pdata
->
gpio_data
.
trackstick_buttons
&&
i
>=
TRACKSTICK_RANGE_START
&&
i
<
TRACKSTICK_RANGE_END
)
{
f3a
->
gpio_key_map
[
i
]
=
trackstick_button
++
;
}
else
if
(
!
pdata
->
gpio_data
.
buttonpad
||
!
button_mapped
)
{
f3a
->
gpio_key_map
[
i
]
=
button
;
input_set_capability
(
input
,
EV_KEY
,
button
++
);
button_mapped
=
true
;
}
}
input
->
keycode
=
f3a
->
gpio_key_map
;
input
->
keycodesize
=
sizeof
(
f3a
->
gpio_key_map
[
0
]);
input
->
keycodemax
=
f3a
->
gpio_count
;
if
(
pdata
->
gpio_data
.
buttonpad
||
(
button
-
BTN_LEFT
==
1
))
__set_bit
(
INPUT_PROP_BUTTONPAD
,
input
->
propbit
);
return
0
;
}
static
int
rmi_f3a_initialize
(
struct
rmi_function
*
fn
,
struct
f3a_data
*
f3a
)
{
u8
query1
[
RMI_F3A_MAX_REG_SIZE
];
u8
ctrl1
[
RMI_F3A_MAX_REG_SIZE
];
u8
buf
;
int
error
;
error
=
rmi_read
(
fn
->
rmi_dev
,
fn
->
fd
.
query_base_addr
,
&
buf
);
if
(
error
<
0
)
{
dev_err
(
&
fn
->
dev
,
"Failed to read general info register: %d
\n
"
,
error
);
return
-
ENODEV
;
}
f3a
->
gpio_count
=
buf
&
RMI_F3A_GPIO_COUNT
;
f3a
->
register_count
=
DIV_ROUND_UP
(
f3a
->
gpio_count
,
8
);
/* Query1 -> gpio exist */
error
=
rmi_read_block
(
fn
->
rmi_dev
,
fn
->
fd
.
query_base_addr
+
1
,
query1
,
f3a
->
register_count
);
if
(
error
)
{
dev_err
(
&
fn
->
dev
,
"Failed to read query1 register
\n
"
);
return
error
;
}
/* Ctrl1 -> gpio direction */
error
=
rmi_read_block
(
fn
->
rmi_dev
,
fn
->
fd
.
control_base_addr
+
1
,
ctrl1
,
f3a
->
register_count
);
if
(
error
)
{
dev_err
(
&
fn
->
dev
,
"Failed to read control1 register
\n
"
);
return
error
;
}
error
=
rmi_f3a_map_gpios
(
fn
,
f3a
,
query1
,
ctrl1
);
if
(
error
)
return
error
;
return
0
;
}
static
int
rmi_f3a_probe
(
struct
rmi_function
*
fn
)
{
struct
rmi_device
*
rmi_dev
=
fn
->
rmi_dev
;
struct
rmi_driver_data
*
drv_data
=
dev_get_drvdata
(
&
rmi_dev
->
dev
);
struct
f3a_data
*
f3a
;
int
error
;
if
(
!
drv_data
->
input
)
{
dev_info
(
&
fn
->
dev
,
"F3A: no input device found, ignoring
\n
"
);
return
-
ENXIO
;
}
f3a
=
devm_kzalloc
(
&
fn
->
dev
,
sizeof
(
*
f3a
),
GFP_KERNEL
);
if
(
!
f3a
)
return
-
ENOMEM
;
f3a
->
input
=
drv_data
->
input
;
error
=
rmi_f3a_initialize
(
fn
,
f3a
);
if
(
error
)
return
error
;
dev_set_drvdata
(
&
fn
->
dev
,
f3a
);
return
0
;
}
struct
rmi_function_handler
rmi_f3a_handler
=
{
.
driver
=
{
.
name
=
"rmi4_f3a"
,
},
.
func
=
0x3a
,
.
probe
=
rmi_f3a_probe
,
.
config
=
rmi_f3a_config
,
.
attention
=
rmi_f3a_attention
,
};
drivers/input/serio/sun4i-ps2.c
View file @
bb0bc0cf
...
...
@@ -211,7 +211,6 @@ static int sun4i_ps2_probe(struct platform_device *pdev)
struct
sun4i_ps2data
*
drvdata
;
struct
serio
*
serio
;
struct
device
*
dev
=
&
pdev
->
dev
;
unsigned
int
irq
;
int
error
;
drvdata
=
kzalloc
(
sizeof
(
struct
sun4i_ps2data
),
GFP_KERNEL
);
...
...
@@ -264,14 +263,12 @@ static int sun4i_ps2_probe(struct platform_device *pdev)
writel
(
0
,
drvdata
->
reg_base
+
PS2_REG_GCTL
);
/* Get IRQ for the device */
irq
=
platform_get_irq
(
pdev
,
0
);
if
(
!
irq
)
{
dev_err
(
dev
,
"no IRQ found
\n
"
);
error
=
-
ENXIO
;
drvdata
->
irq
=
platform_get_irq
(
pdev
,
0
);
if
(
drvdata
->
irq
<
0
)
{
error
=
drvdata
->
irq
;
goto
err_disable_clk
;
}
drvdata
->
irq
=
irq
;
drvdata
->
serio
=
serio
;
drvdata
->
dev
=
dev
;
...
...
drivers/input/touchscreen/Kconfig
View file @
bb0bc0cf
...
...
@@ -1322,4 +1322,16 @@ config TOUCHSCREEN_IQS5XX
To compile this driver as a module, choose M here: the
module will be called iqs5xx.
config TOUCHSCREEN_ZINITIX
tristate "Zinitix touchscreen support"
depends on I2C
help
Say Y here if you have a touchscreen using Zinitix bt541,
or something similar enough.
If unsure, say N.
To compile this driver as a module, choose M here: the
module will be called zinitix.
endif
drivers/input/touchscreen/Makefile
View file @
bb0bc0cf
...
...
@@ -111,3 +111,4 @@ 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
obj-$(CONFIG_TOUCHSCREEN_ZINITIX)
+=
zinitix.o
drivers/input/touchscreen/elants_i2c.c
View file @
bb0bc0cf
...
...
@@ -90,7 +90,7 @@
/* FW read command, 0x53 0x?? 0x0, 0x01 */
#define E_ELAN_INFO_FW_VER 0x00
#define E_ELAN_INFO_BC_VER 0x10
#define E_ELAN_INFO_REK 0x
E
0
#define E_ELAN_INFO_REK 0x
D
0
#define E_ELAN_INFO_TEST_VER 0xE0
#define E_ELAN_INFO_FW_ID 0xF0
#define E_INFO_OSR 0xD6
...
...
@@ -134,6 +134,7 @@ struct elants_data {
u8
bc_version
;
u8
iap_version
;
u16
hw_version
;
u8
major_res
;
unsigned
int
x_res
;
/* resolution in units/mm */
unsigned
int
y_res
;
unsigned
int
x_max
;
...
...
@@ -459,6 +460,9 @@ static int elants_i2c_query_ts_info(struct elants_data *ts)
rows
=
resp
[
2
]
+
resp
[
6
]
+
resp
[
10
];
cols
=
resp
[
3
]
+
resp
[
7
]
+
resp
[
11
];
/* Get report resolution value of ABS_MT_TOUCH_MAJOR */
ts
->
major_res
=
resp
[
16
];
/* Process mm_to_pixel information */
error
=
elants_i2c_execute_command
(
client
,
get_osr_cmd
,
sizeof
(
get_osr_cmd
),
...
...
@@ -1325,6 +1329,8 @@ static int elants_i2c_probe(struct i2c_client *client,
0
,
MT_TOOL_PALM
,
0
,
0
);
input_abs_set_res
(
ts
->
input
,
ABS_MT_POSITION_X
,
ts
->
x_res
);
input_abs_set_res
(
ts
->
input
,
ABS_MT_POSITION_Y
,
ts
->
y_res
);
if
(
ts
->
major_res
>
0
)
input_abs_set_res
(
ts
->
input
,
ABS_MT_TOUCH_MAJOR
,
ts
->
major_res
);
touchscreen_parse_properties
(
ts
->
input
,
true
,
&
ts
->
prop
);
...
...
drivers/input/touchscreen/imx6ul_tsc.c
View file @
bb0bc0cf
...
...
@@ -315,9 +315,8 @@ static irqreturn_t adc_irq_fn(int irq, void *dev_id)
return
IRQ_HANDLED
;
}
static
int
imx6ul_tsc_
open
(
struct
input_dev
*
input_dev
)
static
int
imx6ul_tsc_
start
(
struct
imx6ul_tsc
*
tsc
)
{
struct
imx6ul_tsc
*
tsc
=
input_get_drvdata
(
input_dev
);
int
err
;
err
=
clk_prepare_enable
(
tsc
->
adc_clk
);
...
...
@@ -349,16 +348,29 @@ static int imx6ul_tsc_open(struct input_dev *input_dev)
return
err
;
}
static
void
imx6ul_tsc_
close
(
struct
input_dev
*
input_dev
)
static
void
imx6ul_tsc_
stop
(
struct
imx6ul_tsc
*
tsc
)
{
struct
imx6ul_tsc
*
tsc
=
input_get_drvdata
(
input_dev
);
imx6ul_tsc_disable
(
tsc
);
clk_disable_unprepare
(
tsc
->
tsc_clk
);
clk_disable_unprepare
(
tsc
->
adc_clk
);
}
static
int
imx6ul_tsc_open
(
struct
input_dev
*
input_dev
)
{
struct
imx6ul_tsc
*
tsc
=
input_get_drvdata
(
input_dev
);
return
imx6ul_tsc_start
(
tsc
);
}
static
void
imx6ul_tsc_close
(
struct
input_dev
*
input_dev
)
{
struct
imx6ul_tsc
*
tsc
=
input_get_drvdata
(
input_dev
);
imx6ul_tsc_stop
(
tsc
);
}
static
int
imx6ul_tsc_probe
(
struct
platform_device
*
pdev
)
{
struct
device_node
*
np
=
pdev
->
dev
.
of_node
;
...
...
@@ -509,12 +521,8 @@ static int __maybe_unused imx6ul_tsc_suspend(struct device *dev)
mutex_lock
(
&
input_dev
->
mutex
);
if
(
input_dev
->
users
)
{
imx6ul_tsc_disable
(
tsc
);
clk_disable_unprepare
(
tsc
->
tsc_clk
);
clk_disable_unprepare
(
tsc
->
adc_clk
);
}
if
(
input_dev
->
users
)
imx6ul_tsc_stop
(
tsc
);
mutex_unlock
(
&
input_dev
->
mutex
);
...
...
@@ -530,22 +538,11 @@ static int __maybe_unused imx6ul_tsc_resume(struct device *dev)
mutex_lock
(
&
input_dev
->
mutex
);
if
(
input_dev
->
users
)
{
retval
=
clk_prepare_enable
(
tsc
->
adc_clk
);
if
(
retval
)
goto
out
;
retval
=
clk_prepare_enable
(
tsc
->
tsc_clk
);
if
(
retval
)
{
clk_disable_unprepare
(
tsc
->
adc_clk
);
goto
out
;
}
retval
=
imx6ul_tsc_init
(
tsc
);
}
if
(
input_dev
->
users
)
retval
=
imx6ul_tsc_start
(
tsc
);
out:
mutex_unlock
(
&
input_dev
->
mutex
);
return
retval
;
}
...
...
drivers/input/touchscreen/raydium_i2c_ts.c
View file @
bb0bc0cf
...
...
@@ -51,6 +51,7 @@
/* Touch relative info */
#define RM_MAX_RETRIES 3
#define RM_RETRY_DELAY_MS 20
#define RM_MAX_TOUCH_NUM 10
#define RM_BOOT_DELAY_MS 100
...
...
@@ -136,83 +137,82 @@ struct raydium_data {
bool
wake_irq_enabled
;
};
static
int
raydium_i2c_
send
(
struct
i2c_client
*
client
,
u
8
addr
,
const
void
*
data
,
size_t
len
)
static
int
raydium_i2c_
xfer
(
struct
i2c_client
*
client
,
u
32
addr
,
void
*
data
,
size_t
len
,
bool
is_read
)
{
u8
*
buf
;
int
tries
=
0
;
int
ret
;
buf
=
kmalloc
(
len
+
1
,
GFP_KERNEL
);
if
(
!
buf
)
return
-
ENOMEM
;
buf
[
0
]
=
addr
;
memcpy
(
buf
+
1
,
data
,
len
);
do
{
ret
=
i2c_master_send
(
client
,
buf
,
len
+
1
);
if
(
likely
(
ret
==
len
+
1
))
break
;
msleep
(
20
);
}
while
(
++
tries
<
RM_MAX_RETRIES
);
kfree
(
buf
);
if
(
unlikely
(
ret
!=
len
+
1
))
{
if
(
ret
>=
0
)
ret
=
-
EIO
;
dev_err
(
&
client
->
dev
,
"%s failed: %d
\n
"
,
__func__
,
ret
);
return
ret
;
}
struct
raydium_bank_switch_header
{
u8
cmd
;
__be32
be_addr
;
}
__packed
header
=
{
.
cmd
=
RM_CMD_BANK_SWITCH
,
.
be_addr
=
cpu_to_be32
(
addr
),
};
return
0
;
}
u8
reg_addr
=
addr
&
0xff
;
static
int
raydium_i2c_read
(
struct
i2c_client
*
client
,
u8
addr
,
void
*
data
,
size_t
len
)
{
struct
i2c_msg
xfer
[]
=
{
{
.
addr
=
client
->
addr
,
.
len
=
sizeof
(
header
),
.
buf
=
(
u8
*
)
&
header
,
},
{
.
addr
=
client
->
addr
,
.
len
=
1
,
.
buf
=
&
addr
,
.
buf
=
&
reg_
addr
,
},
{
.
addr
=
client
->
addr
,
.
flags
=
I2C_M_RD
,
.
len
=
len
,
.
buf
=
data
,
.
flags
=
is_read
?
I2C_M_RD
:
0
,
}
};
/*
* If address is greater than 255, then RM_CMD_BANK_SWITCH needs to be
* sent first. Else, skip the header i.e. xfer[0].
*/
int
xfer_start_idx
=
(
addr
>
0xff
)
?
0
:
1
;
size_t
xfer_count
=
ARRAY_SIZE
(
xfer
)
-
xfer_start_idx
;
int
ret
;
ret
=
i2c_transfer
(
client
->
adapter
,
xfer
,
ARRAY_SIZE
(
xfer
));
if
(
unlikely
(
ret
!=
ARRAY_SIZE
(
xfer
)))
return
ret
<
0
?
ret
:
-
EIO
;
ret
=
i2c_transfer
(
client
->
adapter
,
&
xfer
[
xfer_start_idx
],
xfer_count
);
if
(
likely
(
ret
==
xfer_count
))
return
0
;
return
ret
<
0
?
ret
:
-
EIO
;
}
return
0
;
static
int
raydium_i2c_send
(
struct
i2c_client
*
client
,
u32
addr
,
const
void
*
data
,
size_t
len
)
{
int
tries
=
0
;
int
error
;
do
{
error
=
raydium_i2c_xfer
(
client
,
addr
,
(
void
*
)
data
,
len
,
false
);
if
(
likely
(
!
error
))
return
0
;
msleep
(
RM_RETRY_DELAY_MS
);
}
while
(
++
tries
<
RM_MAX_RETRIES
);
dev_err
(
&
client
->
dev
,
"%s failed: %d
\n
"
,
__func__
,
error
);
return
error
;
}
static
int
raydium_i2c_read
_message
(
struct
i2c_client
*
client
,
u32
addr
,
void
*
data
,
size_t
len
)
static
int
raydium_i2c_read
(
struct
i2c_client
*
client
,
u32
addr
,
void
*
data
,
size_t
len
)
{
__be32
be_addr
;
size_t
xfer_len
;
int
error
;
while
(
len
)
{
xfer_len
=
min_t
(
size_t
,
len
,
RM_MAX_READ_SIZE
);
be_addr
=
cpu_to_be32
(
addr
);
error
=
raydium_i2c_send
(
client
,
RM_CMD_BANK_SWITCH
,
&
be_addr
,
sizeof
(
be_addr
));
if
(
!
error
)
error
=
raydium_i2c_read
(
client
,
addr
&
0xff
,
data
,
xfer_len
);
if
(
error
)
error
=
raydium_i2c_xfer
(
client
,
addr
,
data
,
xfer_len
,
true
);
if
(
unlikely
(
error
))
return
error
;
len
-=
xfer_len
;
...
...
@@ -223,27 +223,13 @@ static int raydium_i2c_read_message(struct i2c_client *client,
return
0
;
}
static
int
raydium_i2c_send_message
(
struct
i2c_client
*
client
,
u32
addr
,
const
void
*
data
,
size_t
len
)
{
__be32
be_addr
=
cpu_to_be32
(
addr
);
int
error
;
error
=
raydium_i2c_send
(
client
,
RM_CMD_BANK_SWITCH
,
&
be_addr
,
sizeof
(
be_addr
));
if
(
!
error
)
error
=
raydium_i2c_send
(
client
,
addr
&
0xff
,
data
,
len
);
return
error
;
}
static
int
raydium_i2c_sw_reset
(
struct
i2c_client
*
client
)
{
const
u8
soft_rst_cmd
=
0x01
;
int
error
;
error
=
raydium_i2c_send
_message
(
client
,
RM_RESET_MSG_ADDR
,
&
soft_rst_cmd
,
sizeof
(
soft_rst_cmd
));
error
=
raydium_i2c_send
(
client
,
RM_RESET_MSG_ADDR
,
&
soft_rst_cmd
,
sizeof
(
soft_rst_cmd
));
if
(
error
)
{
dev_err
(
&
client
->
dev
,
"software reset failed: %d
\n
"
,
error
);
return
error
;
...
...
@@ -295,9 +281,8 @@ static int raydium_i2c_query_ts_info(struct raydium_data *ts)
if
(
error
)
continue
;
error
=
raydium_i2c_read_message
(
client
,
le32_to_cpu
(
query_bank_addr
),
&
ts
->
info
,
sizeof
(
ts
->
info
));
error
=
raydium_i2c_read
(
client
,
le32_to_cpu
(
query_bank_addr
),
&
ts
->
info
,
sizeof
(
ts
->
info
));
if
(
error
)
continue
;
...
...
@@ -834,8 +819,8 @@ static irqreturn_t raydium_i2c_irq(int irq, void *_dev)
if
(
ts
->
boot_mode
!=
RAYDIUM_TS_MAIN
)
goto
out
;
error
=
raydium_i2c_read
_message
(
ts
->
client
,
ts
->
data_bank_addr
,
ts
->
report_data
,
ts
->
pkg_size
);
error
=
raydium_i2c_read
(
ts
->
client
,
ts
->
data_bank_addr
,
ts
->
report_data
,
ts
->
pkg_size
);
if
(
error
)
goto
out
;
...
...
drivers/input/touchscreen/stmfts.c
View file @
bb0bc0cf
...
...
@@ -479,7 +479,7 @@ static ssize_t stmfts_sysfs_hover_enable_write(struct device *dev,
mutex_lock
(
&
sdata
->
mutex
);
if
(
value
&
sdata
->
hover_enabled
)
if
(
value
&
&
sdata
->
hover_enabled
)
goto
out
;
if
(
sdata
->
running
)
...
...
drivers/input/touchscreen/zinitix.c
0 → 100644
View file @
bb0bc0cf
// SPDX-License-Identifier: GPL-2.0-only
#include <linux/delay.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/irq.h>
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/of.h>
#include <linux/regulator/consumer.h>
#include <linux/slab.h>
/* Register Map */
#define BT541_SWRESET_CMD 0x0000
#define BT541_WAKEUP_CMD 0x0001
#define BT541_IDLE_CMD 0x0004
#define BT541_SLEEP_CMD 0x0005
#define BT541_CLEAR_INT_STATUS_CMD 0x0003
#define BT541_CALIBRATE_CMD 0x0006
#define BT541_SAVE_STATUS_CMD 0x0007
#define BT541_SAVE_CALIBRATION_CMD 0x0008
#define BT541_RECALL_FACTORY_CMD 0x000f
#define BT541_THRESHOLD 0x0020
#define BT541_LARGE_PALM_REJECT_AREA_TH 0x003F
#define BT541_DEBUG_REG 0x0115
/* 0~7 */
#define BT541_TOUCH_MODE 0x0010
#define BT541_CHIP_REVISION 0x0011
#define BT541_FIRMWARE_VERSION 0x0012
#define ZINITIX_USB_DETECT 0x116
#define BT541_MINOR_FW_VERSION 0x0121
#define BT541_VENDOR_ID 0x001C
#define BT541_HW_ID 0x0014
#define BT541_DATA_VERSION_REG 0x0013
#define BT541_SUPPORTED_FINGER_NUM 0x0015
#define BT541_EEPROM_INFO 0x0018
#define BT541_INITIAL_TOUCH_MODE 0x0019
#define BT541_TOTAL_NUMBER_OF_X 0x0060
#define BT541_TOTAL_NUMBER_OF_Y 0x0061
#define BT541_DELAY_RAW_FOR_HOST 0x007f
#define BT541_BUTTON_SUPPORTED_NUM 0x00B0
#define BT541_BUTTON_SENSITIVITY 0x00B2
#define BT541_DUMMY_BUTTON_SENSITIVITY 0X00C8
#define BT541_X_RESOLUTION 0x00C0
#define BT541_Y_RESOLUTION 0x00C1
#define BT541_POINT_STATUS_REG 0x0080
#define BT541_ICON_STATUS_REG 0x00AA
#define BT541_POINT_COORD_REG (BT541_POINT_STATUS_REG + 2)
#define BT541_AFE_FREQUENCY 0x0100
#define BT541_DND_N_COUNT 0x0122
#define BT541_DND_U_COUNT 0x0135
#define BT541_RAWDATA_REG 0x0200
#define BT541_EEPROM_INFO_REG 0x0018
#define BT541_INT_ENABLE_FLAG 0x00f0
#define BT541_PERIODICAL_INTERRUPT_INTERVAL 0x00f1
#define BT541_BTN_WIDTH 0x016d
#define BT541_CHECKSUM_RESULT 0x012c
#define BT541_INIT_FLASH 0x01d0
#define BT541_WRITE_FLASH 0x01d1
#define BT541_READ_FLASH 0x01d2
#define ZINITIX_INTERNAL_FLAG_02 0x011e
#define ZINITIX_INTERNAL_FLAG_03 0x011f
#define ZINITIX_I2C_CHECKSUM_WCNT 0x016a
#define ZINITIX_I2C_CHECKSUM_RESULT 0x016c
/* Interrupt & status register flags */
#define BIT_PT_CNT_CHANGE BIT(0)
#define BIT_DOWN BIT(1)
#define BIT_MOVE BIT(2)
#define BIT_UP BIT(3)
#define BIT_PALM BIT(4)
#define BIT_PALM_REJECT BIT(5)
#define BIT_RESERVED_0 BIT(6)
#define BIT_RESERVED_1 BIT(7)
#define BIT_WEIGHT_CHANGE BIT(8)
#define BIT_PT_NO_CHANGE BIT(9)
#define BIT_REJECT BIT(10)
#define BIT_PT_EXIST BIT(11)
#define BIT_RESERVED_2 BIT(12)
#define BIT_ERROR BIT(13)
#define BIT_DEBUG BIT(14)
#define BIT_ICON_EVENT BIT(15)
#define SUB_BIT_EXIST BIT(0)
#define SUB_BIT_DOWN BIT(1)
#define SUB_BIT_MOVE BIT(2)
#define SUB_BIT_UP BIT(3)
#define SUB_BIT_UPDATE BIT(4)
#define SUB_BIT_WAIT BIT(5)
#define DEFAULT_TOUCH_POINT_MODE 2
#define MAX_SUPPORTED_FINGER_NUM 5
#define CHIP_ON_DELAY 15 // ms
#define FIRMWARE_ON_DELAY 40 // ms
struct
point_coord
{
__le16
x
;
__le16
y
;
u8
width
;
u8
sub_status
;
// currently unused, but needed as padding:
u8
minor_width
;
u8
angle
;
};
struct
touch_event
{
__le16
status
;
u8
finger_cnt
;
u8
time_stamp
;
struct
point_coord
point_coord
[
MAX_SUPPORTED_FINGER_NUM
];
};
struct
bt541_ts_data
{
struct
i2c_client
*
client
;
struct
input_dev
*
input_dev
;
struct
touchscreen_properties
prop
;
struct
regulator_bulk_data
supplies
[
2
];
u32
zinitix_mode
;
};
static
int
zinitix_read_data
(
struct
i2c_client
*
client
,
u16
reg
,
void
*
values
,
size_t
length
)
{
__le16
reg_le
=
cpu_to_le16
(
reg
);
int
ret
;
/* A single i2c_transfer() transaction does not work here. */
ret
=
i2c_master_send
(
client
,
(
u8
*
)
&
reg_le
,
sizeof
(
reg_le
));
if
(
ret
!=
sizeof
(
reg_le
))
return
ret
<
0
?
ret
:
-
EIO
;
ret
=
i2c_master_recv
(
client
,
(
u8
*
)
values
,
length
);
if
(
ret
!=
length
)
return
ret
<
0
?
ret
:
-
EIO
;
;
return
0
;
}
static
int
zinitix_write_u16
(
struct
i2c_client
*
client
,
u16
reg
,
u16
value
)
{
__le16
packet
[
2
]
=
{
cpu_to_le16
(
reg
),
cpu_to_le16
(
value
)};
int
ret
;
ret
=
i2c_master_send
(
client
,
(
u8
*
)
packet
,
sizeof
(
packet
));
if
(
ret
!=
sizeof
(
packet
))
return
ret
<
0
?
ret
:
-
EIO
;
return
0
;
}
static
int
zinitix_write_cmd
(
struct
i2c_client
*
client
,
u16
reg
)
{
__le16
reg_le
=
cpu_to_le16
(
reg
);
int
ret
;
ret
=
i2c_master_send
(
client
,
(
u8
*
)
&
reg_le
,
sizeof
(
reg_le
));
if
(
ret
!=
sizeof
(
reg_le
))
return
ret
<
0
?
ret
:
-
EIO
;
return
0
;
}
static
bool
zinitix_init_touch
(
struct
bt541_ts_data
*
bt541
)
{
struct
i2c_client
*
client
=
bt541
->
client
;
int
i
;
int
error
;
error
=
zinitix_write_cmd
(
client
,
BT541_SWRESET_CMD
);
if
(
error
)
{
dev_err
(
&
client
->
dev
,
"Failed to write reset command
\n
"
);
return
error
;
}
error
=
zinitix_write_u16
(
client
,
BT541_INT_ENABLE_FLAG
,
0x0
);
if
(
error
)
{
dev_err
(
&
client
->
dev
,
"Failed to reset interrupt enable flag
\n
"
);
return
error
;
}
/* initialize */
error
=
zinitix_write_u16
(
client
,
BT541_X_RESOLUTION
,
bt541
->
prop
.
max_x
);
if
(
error
)
return
error
;
error
=
zinitix_write_u16
(
client
,
BT541_Y_RESOLUTION
,
bt541
->
prop
.
max_y
);
if
(
error
)
return
error
;
error
=
zinitix_write_u16
(
client
,
BT541_SUPPORTED_FINGER_NUM
,
MAX_SUPPORTED_FINGER_NUM
);
if
(
error
)
return
error
;
error
=
zinitix_write_u16
(
client
,
BT541_INITIAL_TOUCH_MODE
,
bt541
->
zinitix_mode
);
if
(
error
)
return
error
;
error
=
zinitix_write_u16
(
client
,
BT541_TOUCH_MODE
,
bt541
->
zinitix_mode
);
if
(
error
)
return
error
;
error
=
zinitix_write_u16
(
client
,
BT541_INT_ENABLE_FLAG
,
BIT_PT_CNT_CHANGE
|
BIT_DOWN
|
BIT_MOVE
|
BIT_UP
);
if
(
error
)
return
error
;
/* clear queue */
for
(
i
=
0
;
i
<
10
;
i
++
)
{
zinitix_write_cmd
(
client
,
BT541_CLEAR_INT_STATUS_CMD
);
udelay
(
10
);
}
return
0
;
}
static
int
zinitix_init_regulators
(
struct
bt541_ts_data
*
bt541
)
{
struct
i2c_client
*
client
=
bt541
->
client
;
int
error
;
bt541
->
supplies
[
0
].
supply
=
"vdd"
;
bt541
->
supplies
[
1
].
supply
=
"vddo"
;
error
=
devm_regulator_bulk_get
(
&
client
->
dev
,
ARRAY_SIZE
(
bt541
->
supplies
),
bt541
->
supplies
);
if
(
error
<
0
)
{
dev_err
(
&
client
->
dev
,
"Failed to get regulators: %d
\n
"
,
error
);
return
error
;
}
return
0
;
}
static
int
zinitix_send_power_on_sequence
(
struct
bt541_ts_data
*
bt541
)
{
int
error
;
struct
i2c_client
*
client
=
bt541
->
client
;
error
=
zinitix_write_u16
(
client
,
0xc000
,
0x0001
);
if
(
error
)
{
dev_err
(
&
client
->
dev
,
"Failed to send power sequence(vendor cmd enable)
\n
"
);
return
error
;
}
udelay
(
10
);
error
=
zinitix_write_cmd
(
client
,
0xc004
);
if
(
error
)
{
dev_err
(
&
client
->
dev
,
"Failed to send power sequence (intn clear)
\n
"
);
return
error
;
}
udelay
(
10
);
error
=
zinitix_write_u16
(
client
,
0xc002
,
0x0001
);
if
(
error
)
{
dev_err
(
&
client
->
dev
,
"Failed to send power sequence (nvm init)
\n
"
);
return
error
;
}
mdelay
(
2
);
error
=
zinitix_write_u16
(
client
,
0xc001
,
0x0001
);
if
(
error
)
{
dev_err
(
&
client
->
dev
,
"Failed to send power sequence (program start)
\n
"
);
return
error
;
}
msleep
(
FIRMWARE_ON_DELAY
);
return
0
;
}
static
void
zinitix_report_finger
(
struct
bt541_ts_data
*
bt541
,
int
slot
,
const
struct
point_coord
*
p
)
{
input_mt_slot
(
bt541
->
input_dev
,
slot
);
input_mt_report_slot_state
(
bt541
->
input_dev
,
MT_TOOL_FINGER
,
true
);
touchscreen_report_pos
(
bt541
->
input_dev
,
&
bt541
->
prop
,
le16_to_cpu
(
p
->
x
),
le16_to_cpu
(
p
->
y
),
true
);
input_report_abs
(
bt541
->
input_dev
,
ABS_MT_TOUCH_MAJOR
,
p
->
width
);
}
static
irqreturn_t
zinitix_ts_irq_handler
(
int
irq
,
void
*
bt541_handler
)
{
struct
bt541_ts_data
*
bt541
=
bt541_handler
;
struct
i2c_client
*
client
=
bt541
->
client
;
struct
touch_event
touch_event
;
int
error
;
int
i
;
memset
(
&
touch_event
,
0
,
sizeof
(
struct
touch_event
));
error
=
zinitix_read_data
(
bt541
->
client
,
BT541_POINT_STATUS_REG
,
&
touch_event
,
sizeof
(
struct
touch_event
));
if
(
error
)
{
dev_err
(
&
client
->
dev
,
"Failed to read in touchpoint struct
\n
"
);
goto
out
;
}
for
(
i
=
0
;
i
<
MAX_SUPPORTED_FINGER_NUM
;
i
++
)
if
(
touch_event
.
point_coord
[
i
].
sub_status
&
SUB_BIT_EXIST
)
zinitix_report_finger
(
bt541
,
i
,
&
touch_event
.
point_coord
[
i
]);
input_mt_sync_frame
(
bt541
->
input_dev
);
input_sync
(
bt541
->
input_dev
);
out:
zinitix_write_cmd
(
bt541
->
client
,
BT541_CLEAR_INT_STATUS_CMD
);
return
IRQ_HANDLED
;
}
static
int
zinitix_start
(
struct
bt541_ts_data
*
bt541
)
{
int
error
;
error
=
regulator_bulk_enable
(
ARRAY_SIZE
(
bt541
->
supplies
),
bt541
->
supplies
);
if
(
error
)
{
dev_err
(
&
bt541
->
client
->
dev
,
"Failed to enable regulators: %d
\n
"
,
error
);
return
error
;
}
msleep
(
CHIP_ON_DELAY
);
error
=
zinitix_send_power_on_sequence
(
bt541
);
if
(
error
)
{
dev_err
(
&
bt541
->
client
->
dev
,
"Error while sending power-on sequence: %d
\n
"
,
error
);
return
error
;
}
error
=
zinitix_init_touch
(
bt541
);
if
(
error
)
{
dev_err
(
&
bt541
->
client
->
dev
,
"Error while configuring touch IC
\n
"
);
return
error
;
}
enable_irq
(
bt541
->
client
->
irq
);
return
0
;
}
static
int
zinitix_stop
(
struct
bt541_ts_data
*
bt541
)
{
int
error
;
disable_irq
(
bt541
->
client
->
irq
);
error
=
regulator_bulk_disable
(
ARRAY_SIZE
(
bt541
->
supplies
),
bt541
->
supplies
);
if
(
error
)
{
dev_err
(
&
bt541
->
client
->
dev
,
"Failed to disable regulators: %d
\n
"
,
error
);
return
error
;
}
return
0
;
}
static
int
zinitix_input_open
(
struct
input_dev
*
dev
)
{
struct
bt541_ts_data
*
bt541
=
input_get_drvdata
(
dev
);
return
zinitix_start
(
bt541
);
}
static
void
zinitix_input_close
(
struct
input_dev
*
dev
)
{
struct
bt541_ts_data
*
bt541
=
input_get_drvdata
(
dev
);
zinitix_stop
(
bt541
);
}
static
int
zinitix_init_input_dev
(
struct
bt541_ts_data
*
bt541
)
{
struct
input_dev
*
input_dev
;
int
error
;
input_dev
=
devm_input_allocate_device
(
&
bt541
->
client
->
dev
);
if
(
!
input_dev
)
{
dev_err
(
&
bt541
->
client
->
dev
,
"Failed to allocate input device."
);
return
-
ENOMEM
;
}
input_set_drvdata
(
input_dev
,
bt541
);
bt541
->
input_dev
=
input_dev
;
input_dev
->
name
=
"Zinitix Capacitive TouchScreen"
;
input_dev
->
phys
=
"input/ts"
;
input_dev
->
id
.
bustype
=
BUS_I2C
;
input_dev
->
open
=
zinitix_input_open
;
input_dev
->
close
=
zinitix_input_close
;
input_set_capability
(
input_dev
,
EV_ABS
,
ABS_MT_POSITION_X
);
input_set_capability
(
input_dev
,
EV_ABS
,
ABS_MT_POSITION_Y
);
input_set_abs_params
(
input_dev
,
ABS_MT_WIDTH_MAJOR
,
0
,
255
,
0
,
0
);
input_set_abs_params
(
input_dev
,
ABS_MT_TOUCH_MAJOR
,
0
,
255
,
0
,
0
);
touchscreen_parse_properties
(
input_dev
,
true
,
&
bt541
->
prop
);
if
(
!
bt541
->
prop
.
max_x
||
!
bt541
->
prop
.
max_y
)
{
dev_err
(
&
bt541
->
client
->
dev
,
"Touchscreen-size-x and/or touchscreen-size-y not set in dts
\n
"
);
return
-
EINVAL
;
}
error
=
input_mt_init_slots
(
input_dev
,
MAX_SUPPORTED_FINGER_NUM
,
INPUT_MT_DIRECT
|
INPUT_MT_DROP_UNUSED
);
if
(
error
)
{
dev_err
(
&
bt541
->
client
->
dev
,
"Failed to initialize MT slots: %d"
,
error
);
return
error
;
}
error
=
input_register_device
(
input_dev
);
if
(
error
)
{
dev_err
(
&
bt541
->
client
->
dev
,
"Failed to register input device: %d"
,
error
);
return
error
;
}
return
0
;
}
static
int
zinitix_ts_probe
(
struct
i2c_client
*
client
)
{
struct
bt541_ts_data
*
bt541
;
int
error
;
if
(
!
i2c_check_functionality
(
client
->
adapter
,
I2C_FUNC_I2C
))
{
dev_err
(
&
client
->
dev
,
"Failed to assert adapter's support for plain I2C.
\n
"
);
return
-
ENXIO
;
}
bt541
=
devm_kzalloc
(
&
client
->
dev
,
sizeof
(
*
bt541
),
GFP_KERNEL
);
if
(
!
bt541
)
return
-
ENOMEM
;
bt541
->
client
=
client
;
i2c_set_clientdata
(
client
,
bt541
);
error
=
zinitix_init_regulators
(
bt541
);
if
(
error
)
{
dev_err
(
&
client
->
dev
,
"Failed to initialize regulators: %d
\n
"
,
error
);
return
error
;
}
error
=
zinitix_init_input_dev
(
bt541
);
if
(
error
)
{
dev_err
(
&
client
->
dev
,
"Failed to initialize input device: %d
\n
"
,
error
);
return
error
;
}
error
=
device_property_read_u32
(
&
client
->
dev
,
"zinitix,mode"
,
&
bt541
->
zinitix_mode
);
if
(
error
<
0
)
{
/* fall back to mode 2 */
bt541
->
zinitix_mode
=
DEFAULT_TOUCH_POINT_MODE
;
}
if
(
bt541
->
zinitix_mode
!=
2
)
{
/*
* If there are devices that don't support mode 2, support
* for other modes (0, 1) will be needed.
*/
dev_err
(
&
client
->
dev
,
"Malformed zinitix,mode property, must be 2 (supplied: %d)
\n
"
,
bt541
->
zinitix_mode
);
return
-
EINVAL
;
}
irq_set_status_flags
(
client
->
irq
,
IRQ_NOAUTOEN
);
error
=
devm_request_threaded_irq
(
&
client
->
dev
,
client
->
irq
,
NULL
,
zinitix_ts_irq_handler
,
IRQF_ONESHOT
,
client
->
name
,
bt541
);
if
(
error
)
{
dev_err
(
&
client
->
dev
,
"Failed to request IRQ: %d
\n
"
,
error
);
return
error
;
}
return
0
;
}
static
int
__maybe_unused
zinitix_suspend
(
struct
device
*
dev
)
{
struct
i2c_client
*
client
=
to_i2c_client
(
dev
);
struct
bt541_ts_data
*
bt541
=
i2c_get_clientdata
(
client
);
mutex_lock
(
&
bt541
->
input_dev
->
mutex
);
if
(
bt541
->
input_dev
->
users
)
zinitix_stop
(
bt541
);
mutex_unlock
(
&
bt541
->
input_dev
->
mutex
);
return
0
;
}
static
int
__maybe_unused
zinitix_resume
(
struct
device
*
dev
)
{
struct
i2c_client
*
client
=
to_i2c_client
(
dev
);
struct
bt541_ts_data
*
bt541
=
i2c_get_clientdata
(
client
);
int
ret
=
0
;
mutex_lock
(
&
bt541
->
input_dev
->
mutex
);
if
(
bt541
->
input_dev
->
users
)
ret
=
zinitix_start
(
bt541
);
mutex_unlock
(
&
bt541
->
input_dev
->
mutex
);
return
ret
;
}
static
SIMPLE_DEV_PM_OPS
(
zinitix_pm_ops
,
zinitix_suspend
,
zinitix_resume
);
#ifdef CONFIG_OF
static
const
struct
of_device_id
zinitix_of_match
[]
=
{
{
.
compatible
=
"zinitix,bt541"
},
{
}
};
MODULE_DEVICE_TABLE
(
of
,
zinitix_of_match
);
#endif
static
struct
i2c_driver
zinitix_ts_driver
=
{
.
probe_new
=
zinitix_ts_probe
,
.
driver
=
{
.
name
=
"Zinitix-TS"
,
.
pm
=
&
zinitix_pm_ops
,
.
of_match_table
=
of_match_ptr
(
zinitix_of_match
),
},
};
module_i2c_driver
(
zinitix_ts_driver
);
MODULE_AUTHOR
(
"Michael Srba <Michael.Srba@seznam.cz>"
);
MODULE_DESCRIPTION
(
"Zinitix touchscreen driver"
);
MODULE_LICENSE
(
"GPL v2"
);
drivers/platform/x86/thinkpad_acpi.c
View file @
bb0bc0cf
...
...
@@ -1930,6 +1930,10 @@ enum { /* hot key scan codes (derived from ACPI DSDT) */
TP_ACPI_HOTKEYSCAN_CALCULATOR
,
TP_ACPI_HOTKEYSCAN_BLUETOOTH
,
TP_ACPI_HOTKEYSCAN_KEYBOARD
,
TP_ACPI_HOTKEYSCAN_FN_RIGHT_SHIFT
,
/* Used by "Lenovo Quick Clean" */
TP_ACPI_HOTKEYSCAN_NOTIFICATION_CENTER
,
TP_ACPI_HOTKEYSCAN_PICKUP_PHONE
,
TP_ACPI_HOTKEYSCAN_HANGUP_PHONE
,
/* Hotkey keymap size */
TPACPI_HOTKEY_MAP_LEN
...
...
@@ -3446,11 +3450,15 @@ static int __init hotkey_init(struct ibm_init_struct *iibm)
KEY_UNKNOWN
,
KEY_UNKNOWN
,
KEY_UNKNOWN
,
KEY_UNKNOWN
,
KEY_UNKNOWN
,
KEY_BOOKMARKS
,
/* Favorite app, 0x311 */
KEY_RESERVED
,
/* Clipping tool */
KEY_CALC
,
/* Calculator (above numpad, P52) */
KEY_BLUETOOTH
,
/* Bluetooth */
KEY_KEYBOARD
/* Keyboard, 0x315 */
KEY_BOOKMARKS
,
/* Favorite app, 0x311 */
KEY_SELECTIVE_SCREENSHOT
,
/* Clipping tool */
KEY_CALC
,
/* Calculator (above numpad, P52) */
KEY_BLUETOOTH
,
/* Bluetooth */
KEY_KEYBOARD
,
/* Keyboard, 0x315 */
KEY_FN_RIGHT_SHIFT
,
/* Fn + right Shift */
KEY_NOTIFICATION_CENTER
,
/* Notification Center */
KEY_PICKUP_PHONE
,
/* Answer incoming call */
KEY_HANGUP_PHONE
,
/* Decline incoming call */
},
};
...
...
include/linux/rmi.h
View file @
bb0bc0cf
...
...
@@ -102,15 +102,16 @@ struct rmi_2d_sensor_platform_data {
};
/**
* struct rmi_f30_data - overrides defaults for a single F30 GPIOs/LED chip.
* struct rmi_gpio_data - overrides defaults for a single F30/F3A GPIOs/LED
* chip.
* @buttonpad - the touchpad is a buttonpad, so enable only the first actual
* button that is found.
* @trackstick_buttons - Set when the function 30 is handling the physical
* @trackstick_buttons - Set when the function 30
or 3a
is handling the physical
* buttons of the trackstick (as a PS/2 passthrough device).
* @disable - the touchpad incorrectly reports F30 and it should be ignored.
* @disable - the touchpad incorrectly reports F30
/F3A
and it should be ignored.
* This is a special case which is due to misconfigured firmware.
*/
struct
rmi_
f30
_data
{
struct
rmi_
gpio
_data
{
bool
buttonpad
;
bool
trackstick_buttons
;
bool
disable
;
...
...
@@ -218,7 +219,7 @@ struct rmi_device_platform_data {
/* function handler pdata */
struct
rmi_2d_sensor_platform_data
sensor_pdata
;
struct
rmi_f01_power_management
power_management
;
struct
rmi_
f30_data
f30
_data
;
struct
rmi_
gpio_data
gpio
_data
;
};
/**
...
...
include/uapi/linux/input-event-codes.h
View file @
bb0bc0cf
...
...
@@ -515,6 +515,9 @@
#define KEY_10CHANNELSUP 0x1b8
/* 10 channels up (10+) */
#define KEY_10CHANNELSDOWN 0x1b9
/* 10 channels down (10-) */
#define KEY_IMAGES 0x1ba
/* AL Image Browser */
#define KEY_NOTIFICATION_CENTER 0x1bc
/* Show/hide the notification center */
#define KEY_PICKUP_PHONE 0x1bd
/* Answer incoming call */
#define KEY_HANGUP_PHONE 0x1be
/* Decline incoming call */
#define KEY_DEL_EOL 0x1c0
#define KEY_DEL_EOS 0x1c1
...
...
@@ -542,6 +545,7 @@
#define KEY_FN_F 0x1e2
#define KEY_FN_S 0x1e3
#define KEY_FN_B 0x1e4
#define KEY_FN_RIGHT_SHIFT 0x1e5
#define KEY_BRL_DOT1 0x1f1
#define KEY_BRL_DOT2 0x1f2
...
...
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