mirror of
https://mirrors.bfsu.edu.cn/git/linux.git
synced 2024-11-11 04:18:39 +08:00
Merge branch 'for-linus' of git://git.kernel.org/pub/scm/linux/kernel/git/dtor/input
Pull input subsystem updates from Dmitry Torokhov: "You will get the following new drivers: - Qualcomm PM8941 power key drver - ChipOne icn8318 touchscreen controller driver - Broadcom iProc touchscreen and keypad drivers - Semtech SX8654 I2C touchscreen controller driver ALPS driver now supports newer SS4 devices; Elantech got a fix that should make it work on some ASUS laptops; and a slew of other enhancements and random fixes" * 'for-linus' of git://git.kernel.org/pub/scm/linux/kernel/git/dtor/input: (51 commits) Input: alps - non interleaved V2 dualpoint has separate stick button bits Input: alps - fix touchpad buttons getting stuck when used with trackpoint Input: atkbd - document "no new force-release quirks" policy Input: ALPS - make alps_get_pkt_id_ss4_v2() and others static Input: ALPS - V7 devices can report 5-finger taps Input: ALPS - add support for SS4 touchpad devices Input: ALPS - refactor alps_set_abs_params_mt() Input: elantech - fix absolute mode setting on some ASUS laptops Input: atmel_mxt_ts - split out touchpad initialisation logic Input: atmel_mxt_ts - implement support for T100 touch object Input: cros_ec_keyb - fix clearing keyboard state on wakeup Input: gscps2 - drop pci_ids dependency Input: synaptics - allocate 3 slots to keep stability in image sensors Input: Revert "Revert "synaptics - use dmax in input_mt_assign_slots"" Input: MT - make slot assignment work for overcovered solutions mfd: tc3589x: enforce device-tree only mode Input: tc3589x - localize platform data Input: tsc2007 - Convert msecs to jiffies only once Input: edt-ft5x06 - remove EV_SYN event report Input: edt-ft5x06 - allow to setting the maximum axes value through the DT ...
This commit is contained in:
commit
8691c130fa
108
Documentation/devicetree/bindings/input/brcm,bcm-keypad.txt
Normal file
108
Documentation/devicetree/bindings/input/brcm,bcm-keypad.txt
Normal file
@ -0,0 +1,108 @@
|
||||
* Broadcom Keypad Controller device tree bindings
|
||||
|
||||
Broadcom Keypad controller is used to interface a SoC with a matrix-type
|
||||
keypad device. The keypad controller supports multiple row and column lines.
|
||||
A key can be placed at each intersection of a unique row and a unique column.
|
||||
The keypad controller can sense a key-press and key-release and report the
|
||||
event using a interrupt to the cpu.
|
||||
|
||||
This binding is based on the matrix-keymap binding with the following
|
||||
changes:
|
||||
|
||||
keypad,num-rows and keypad,num-columns are required.
|
||||
|
||||
Required SoC Specific Properties:
|
||||
- compatible: should be "brcm,bcm-keypad"
|
||||
|
||||
- reg: physical base address of the controller and length of memory mapped
|
||||
region.
|
||||
|
||||
- interrupts: The interrupt number to the cpu.
|
||||
|
||||
Board Specific Properties:
|
||||
- keypad,num-rows: Number of row lines connected to the keypad
|
||||
controller.
|
||||
|
||||
- keypad,num-columns: Number of column lines connected to the
|
||||
keypad controller.
|
||||
|
||||
- col-debounce-filter-period: The debounce period for the Column filter.
|
||||
|
||||
KEYPAD_DEBOUNCE_1_ms = 0
|
||||
KEYPAD_DEBOUNCE_2_ms = 1
|
||||
KEYPAD_DEBOUNCE_4_ms = 2
|
||||
KEYPAD_DEBOUNCE_8_ms = 3
|
||||
KEYPAD_DEBOUNCE_16_ms = 4
|
||||
KEYPAD_DEBOUNCE_32_ms = 5
|
||||
KEYPAD_DEBOUNCE_64_ms = 6
|
||||
KEYPAD_DEBOUNCE_128_ms = 7
|
||||
|
||||
- status-debounce-filter-period: The debounce period for the Status filter.
|
||||
|
||||
KEYPAD_DEBOUNCE_1_ms = 0
|
||||
KEYPAD_DEBOUNCE_2_ms = 1
|
||||
KEYPAD_DEBOUNCE_4_ms = 2
|
||||
KEYPAD_DEBOUNCE_8_ms = 3
|
||||
KEYPAD_DEBOUNCE_16_ms = 4
|
||||
KEYPAD_DEBOUNCE_32_ms = 5
|
||||
KEYPAD_DEBOUNCE_64_ms = 6
|
||||
KEYPAD_DEBOUNCE_128_ms = 7
|
||||
|
||||
- row-output-enabled: An optional property indicating whether the row or
|
||||
column is being used as output. If specified the row is being used
|
||||
as the output. Else defaults to column.
|
||||
|
||||
- pull-up-enabled: An optional property indicating the Keypad scan mode.
|
||||
If specified implies the keypad scan pull-up has been enabled.
|
||||
|
||||
- autorepeat: Boolean, Enable auto repeat feature of Linux input
|
||||
subsystem (optional).
|
||||
|
||||
- linux,keymap: The keymap for keys as described in the binding document
|
||||
devicetree/bindings/input/matrix-keymap.txt.
|
||||
|
||||
Example:
|
||||
#include "dt-bindings/input/input.h"
|
||||
|
||||
/ {
|
||||
keypad: keypad@180ac000 {
|
||||
/* Required SoC specific properties */
|
||||
compatible = "brcm,bcm-keypad";
|
||||
|
||||
/* Required Board specific properties */
|
||||
keypad,num-rows = <5>;
|
||||
keypad,num-columns = <5>;
|
||||
status = "okay";
|
||||
|
||||
linux,keymap = <MATRIX_KEY(0x00, 0x02, KEY_F) /* key_forward */
|
||||
MATRIX_KEY(0x00, 0x03, KEY_HOME) /* key_home */
|
||||
MATRIX_KEY(0x00, 0x04, KEY_M) /* key_message */
|
||||
MATRIX_KEY(0x01, 0x00, KEY_A) /* key_contacts */
|
||||
MATRIX_KEY(0x01, 0x01, KEY_1) /* key_1 */
|
||||
MATRIX_KEY(0x01, 0x02, KEY_2) /* key_2 */
|
||||
MATRIX_KEY(0x01, 0x03, KEY_3) /* key_3 */
|
||||
MATRIX_KEY(0x01, 0x04, KEY_S) /* key_speaker */
|
||||
MATRIX_KEY(0x02, 0x00, KEY_P) /* key_phone */
|
||||
MATRIX_KEY(0x02, 0x01, KEY_4) /* key_4 */
|
||||
MATRIX_KEY(0x02, 0x02, KEY_5) /* key_5 */
|
||||
MATRIX_KEY(0x02, 0x03, KEY_6) /* key_6 */
|
||||
MATRIX_KEY(0x02, 0x04, KEY_VOLUMEUP) /* key_vol_up */
|
||||
MATRIX_KEY(0x03, 0x00, KEY_C) /* key_call_log */
|
||||
MATRIX_KEY(0x03, 0x01, KEY_7) /* key_7 */
|
||||
MATRIX_KEY(0x03, 0x02, KEY_8) /* key_8 */
|
||||
MATRIX_KEY(0x03, 0x03, KEY_9) /* key_9 */
|
||||
MATRIX_KEY(0x03, 0x04, KEY_VOLUMEDOWN) /* key_vol_down */
|
||||
MATRIX_KEY(0x04, 0x00, KEY_H) /* key_headset */
|
||||
MATRIX_KEY(0x04, 0x01, KEY_KPASTERISK) /* key_* */
|
||||
MATRIX_KEY(0x04, 0x02, KEY_0) /* key_0 */
|
||||
MATRIX_KEY(0x04, 0x03, KEY_GRAVE) /* key_# */
|
||||
MATRIX_KEY(0x04, 0x04, KEY_MUTE) /* key_mute */
|
||||
>;
|
||||
|
||||
/* Optional board specific properties */
|
||||
col-debounce-filter-period = <5>;
|
||||
row-output-enabled;
|
||||
pull-up-enabled;
|
||||
|
||||
};
|
||||
};
|
@ -0,0 +1,43 @@
|
||||
Qualcomm PM8941 PMIC Power Key
|
||||
|
||||
PROPERTIES
|
||||
|
||||
- compatible:
|
||||
Usage: required
|
||||
Value type: <string>
|
||||
Definition: must be one of:
|
||||
"qcom,pm8941-pwrkey"
|
||||
|
||||
- reg:
|
||||
Usage: required
|
||||
Value type: <prop-encoded-array>
|
||||
Definition: base address of registers for block
|
||||
|
||||
- interrupts:
|
||||
Usage: required
|
||||
Value type: <prop-encoded-array>
|
||||
Definition: key change interrupt; The format of the specifier is
|
||||
defined by the binding document describing the node's
|
||||
interrupt parent.
|
||||
|
||||
- debounce:
|
||||
Usage: optional
|
||||
Value type: <u32>
|
||||
Definition: time in microseconds that key must be pressed or released
|
||||
for state change interrupt to trigger.
|
||||
|
||||
- bias-pull-up:
|
||||
Usage: optional
|
||||
Value type: <empty>
|
||||
Definition: presence of this property indicates that the KPDPWR_N pin
|
||||
should be configured for pull up.
|
||||
|
||||
EXAMPLE
|
||||
|
||||
pwrkey@800 {
|
||||
compatible = "qcom,pm8941-pwrkey";
|
||||
reg = <0x800>;
|
||||
interrupts = <0x0 0x8 0 IRQ_TYPE_EDGE_BOTH>;
|
||||
debounce = <15625>;
|
||||
bias-pull-up;
|
||||
};
|
@ -0,0 +1,76 @@
|
||||
* Broadcom's IPROC Touchscreen Controller
|
||||
|
||||
Required properties:
|
||||
- compatible: must be "brcm,iproc-touchscreen"
|
||||
- reg: physical base address of the controller and length of memory mapped
|
||||
region.
|
||||
- clocks: The clock provided by the SOC to driver the tsc
|
||||
- clock-name: name for the clock
|
||||
- interrupts: The touchscreen controller's interrupt
|
||||
|
||||
Optional properties:
|
||||
- scanning_period: Time between scans. Each step is 1024 us. Valid 1-256.
|
||||
- debounce_timeout: Each step is 512 us. Valid 0-255
|
||||
- settling_timeout: The settling duration (in ms) is the amount of time
|
||||
the tsc waits to allow the voltage to settle after
|
||||
turning on the drivers in detection mode.
|
||||
Valid values: 0-11
|
||||
0 = 0.008 ms
|
||||
1 = 0.01 ms
|
||||
2 = 0.02 ms
|
||||
3 = 0.04 ms
|
||||
4 = 0.08 ms
|
||||
5 = 0.16 ms
|
||||
6 = 0.32 ms
|
||||
7 = 0.64 ms
|
||||
8 = 1.28 ms
|
||||
9 = 2.56 ms
|
||||
10 = 5.12 ms
|
||||
11 = 10.24 ms
|
||||
- touch_timeout: The continuous number of scan periods in which touch is
|
||||
not detected before the controller returns to idle state.
|
||||
Valid values 0-255.
|
||||
- average_data: Number of data samples which are averaged before a final
|
||||
data point is placed into the FIFO
|
||||
Valid values 0-7
|
||||
0 = 1 sample
|
||||
1 = 2 samples
|
||||
2 = 4 samples
|
||||
3 = 8 samples
|
||||
4 = 16 samples
|
||||
5 = 32 samples
|
||||
6 = 64 samples
|
||||
7 = 128 samples
|
||||
- fifo_threshold: Interrupt is generated whenever the number of fifo
|
||||
entries exceeds this value
|
||||
Valid values 0-31
|
||||
- touchscreen-size-x: horizontal resolution of touchscreen (in pixels)
|
||||
- touchscreen-size-y: vertical resolution of touchscreen (in pixels)
|
||||
- touchscreen-fuzz-x: horizontal noise value of the absolute input
|
||||
device (in pixels)
|
||||
- touchscreen-fuzz-y: vertical noise value of the absolute input
|
||||
device (in pixels)
|
||||
- touchscreen-inverted-x: X axis is inverted (boolean)
|
||||
- touchscreen-inverted-y: Y axis is inverted (boolean)
|
||||
|
||||
Example:
|
||||
|
||||
touchscreen: tsc@0x180A6000 {
|
||||
compatible = "brcm,iproc-touchscreen";
|
||||
#address-cells = <1>;
|
||||
#size-cells = <1>;
|
||||
reg = <0x180A6000 0x40>;
|
||||
clocks = <&adc_clk>;
|
||||
clock-names = "tsc_clk";
|
||||
interrupts = <GIC_SPI 164 IRQ_TYPE_LEVEL_HIGH>;
|
||||
|
||||
scanning_period = <5>;
|
||||
debounce_timeout = <40>;
|
||||
settling_timeout = <7>;
|
||||
touch_timeout = <10>;
|
||||
average_data = <5>;
|
||||
fifo_threshold = <1>;
|
||||
/* Touchscreen is rotated 180 degrees. */
|
||||
touchscreen-inverted-x;
|
||||
touchscreen-inverted-y;
|
||||
};
|
@ -0,0 +1,46 @@
|
||||
* ChipOne icn8318 I2C touchscreen controller
|
||||
|
||||
Required properties:
|
||||
- compatible : "chipone,icn8318"
|
||||
- reg : I2C slave address of the chip (0x40)
|
||||
- interrupt-parent : a phandle pointing to the interrupt controller
|
||||
serving the interrupt for this chip
|
||||
- interrupts : interrupt specification for the icn8318 interrupt
|
||||
- wake-gpios : GPIO specification for the WAKE input
|
||||
- touchscreen-size-x : horizontal resolution of touchscreen (in pixels)
|
||||
- touchscreen-size-y : vertical resolution of touchscreen (in pixels)
|
||||
|
||||
Optional properties:
|
||||
- pinctrl-names : should be "default"
|
||||
- pinctrl-0: : a phandle pointing to the pin settings for the
|
||||
control gpios
|
||||
- touchscreen-fuzz-x : horizontal noise value of the absolute input
|
||||
device (in pixels)
|
||||
- touchscreen-fuzz-y : vertical noise value of the absolute input
|
||||
device (in pixels)
|
||||
- touchscreen-inverted-x : X axis is inverted (boolean)
|
||||
- touchscreen-inverted-y : Y axis is inverted (boolean)
|
||||
- touchscreen-swapped-x-y : X and Y axis are swapped (boolean)
|
||||
Swapping is done after inverting the axis
|
||||
|
||||
Example:
|
||||
|
||||
i2c@00000000 {
|
||||
/* ... */
|
||||
|
||||
chipone_icn8318@40 {
|
||||
compatible = "chipone,icn8318";
|
||||
reg = <0x40>;
|
||||
interrupt-parent = <&pio>;
|
||||
interrupts = <9 IRQ_TYPE_EDGE_FALLING>; /* EINT9 (PG9) */
|
||||
pinctrl-names = "default";
|
||||
pinctrl-0 = <&ts_wake_pin_p66>;
|
||||
wake-gpios = <&pio 1 3 GPIO_ACTIVE_HIGH>; /* PB3 */
|
||||
touchscreen-size-x = <800>;
|
||||
touchscreen-size-y = <480>;
|
||||
touchscreen-inverted-x;
|
||||
touchscreen-swapped-x-y;
|
||||
};
|
||||
|
||||
/* ... */
|
||||
};
|
@ -0,0 +1,29 @@
|
||||
Device tree bindings for Goodix GT9xx series touchscreen controller
|
||||
|
||||
Required properties:
|
||||
|
||||
- compatible : Should be "goodix,gt911"
|
||||
or "goodix,gt9110"
|
||||
or "goodix,gt912"
|
||||
or "goodix,gt927"
|
||||
or "goodix,gt9271"
|
||||
or "goodix,gt928"
|
||||
or "goodix,gt967"
|
||||
- reg : I2C address of the chip. Should be 0x5d or 0x14
|
||||
- interrupt-parent : Interrupt controller to which the chip is connected
|
||||
- interrupts : Interrupt to which the chip is connected
|
||||
|
||||
Example:
|
||||
|
||||
i2c@00000000 {
|
||||
/* ... */
|
||||
|
||||
gt928@5d {
|
||||
compatible = "goodix,gt928";
|
||||
reg = <0x5d>;
|
||||
interrupt-parent = <&gpio>;
|
||||
interrupts = <0 0>;
|
||||
};
|
||||
|
||||
/* ... */
|
||||
};
|
@ -2,14 +2,27 @@ sun4i resistive touchscreen controller
|
||||
--------------------------------------
|
||||
|
||||
Required properties:
|
||||
- compatible: "allwinner,sun4i-a10-ts" or "allwinner,sun6i-a31-ts"
|
||||
- compatible: "allwinner,sun4i-a10-ts", "allwinner,sun5i-a13-ts" or
|
||||
"allwinner,sun6i-a31-ts"
|
||||
- reg: mmio address range of the chip
|
||||
- interrupts: interrupt to which the chip is connected
|
||||
- #thermal-sensor-cells: shall be 0
|
||||
|
||||
Optional properties:
|
||||
- allwinner,ts-attached: boolean indicating that an actual touchscreen is
|
||||
attached to the controller
|
||||
- allwinner,ts-attached : boolean indicating that an actual touchscreen
|
||||
is attached to the controller
|
||||
- allwinner,tp-sensitive-adjust : integer (4 bits)
|
||||
adjust sensitivity of pen down detection
|
||||
between 0 (least sensitive) and 15
|
||||
(defaults to 15)
|
||||
- allwinner,filter-type : integer (2 bits)
|
||||
select median and averaging filter
|
||||
samples used for median / averaging filter
|
||||
0: 4/2
|
||||
1: 5/3
|
||||
2: 8/4
|
||||
3: 16/8
|
||||
(defaults to 1)
|
||||
|
||||
Example:
|
||||
|
||||
@ -19,4 +32,7 @@ Example:
|
||||
interrupts = <29>;
|
||||
allwinner,ts-attached;
|
||||
#thermal-sensor-cells = <0>;
|
||||
/* sensitive/noisy touch panel */
|
||||
allwinner,tp-sensitive-adjust = <0>;
|
||||
allwinner,filter-type = <3>;
|
||||
};
|
||||
|
@ -0,0 +1,16 @@
|
||||
* Semtech SX8654 I2C Touchscreen Controller
|
||||
|
||||
Required properties:
|
||||
- compatible: must be "semtech,sx8654"
|
||||
- reg: i2c slave address
|
||||
- interrupt-parent: the phandle for the interrupt controller
|
||||
- interrupts: touch controller interrupt
|
||||
|
||||
Example:
|
||||
|
||||
sx8654@48 {
|
||||
compatible = "semtech,sx8654";
|
||||
reg = <0x48>;
|
||||
interrupt-parent = <&gpio6>;
|
||||
interrupts = <3 IRQ_TYPE_EDGE_FALLING>;
|
||||
};
|
@ -16,6 +16,8 @@ Optional properties for Touchscreens:
|
||||
controller)
|
||||
- touchscreen-inverted-x : X axis is inverted (boolean)
|
||||
- touchscreen-inverted-y : Y axis is inverted (boolean)
|
||||
- touchscreen-swapped-x-y : X and Y axis are swapped (boolean)
|
||||
Swapping is done after inverting the axis
|
||||
|
||||
Deprecated properties for Touchscreens:
|
||||
- x-size : deprecated name for touchscreen-size-x
|
||||
|
@ -37,6 +37,7 @@ capella Capella Microsystems, Inc
|
||||
cavium Cavium, Inc.
|
||||
cdns Cadence Design Systems Inc.
|
||||
chipidea Chipidea, Inc
|
||||
chipone ChipOne
|
||||
chipspark ChipSPARK
|
||||
chrp Common Hardware Reference Platform
|
||||
chunghwa Chunghwa Picture Tubes Ltd.
|
||||
@ -78,6 +79,7 @@ geniatech Geniatech, Inc.
|
||||
giantplus Giantplus Technology Co., Ltd.
|
||||
globalscale Globalscale Technologies, Inc.
|
||||
gmt Global Mixed-mode Technology, Inc.
|
||||
goodix Shenzhen Huiding Technology Co., Ltd.
|
||||
google Google, Inc.
|
||||
gumstix Gumstix, Inc.
|
||||
gw Gateworks Corporation
|
||||
|
@ -2525,6 +2525,13 @@ L: linux-usb@vger.kernel.org
|
||||
S: Maintained
|
||||
F: drivers/usb/chipidea/
|
||||
|
||||
CHIPONE ICN8318 I2C TOUCHSCREEN DRIVER
|
||||
M: Hans de Goede <hdegoede@redhat.com>
|
||||
L: linux-input@vger.kernel.org
|
||||
S: Maintained
|
||||
F: Documentation/devicetree/bindings/input/touchscreen/chipone_icn8318.txt
|
||||
F: drivers/input/touchscreen/chipone_icn8318.c
|
||||
|
||||
CHROME HARDWARE PLATFORM SUPPORT
|
||||
M: Olof Johansson <olof@lixom.net>
|
||||
S: Maintained
|
||||
|
@ -815,7 +815,7 @@ static const char *keys[KEY_MAX + 1] = {
|
||||
[KEY_DELETEFILE] = "DeleteFile", [KEY_XFER] = "X-fer",
|
||||
[KEY_PROG1] = "Prog1", [KEY_PROG2] = "Prog2",
|
||||
[KEY_WWW] = "WWW", [KEY_MSDOS] = "MSDOS",
|
||||
[KEY_COFFEE] = "Coffee", [KEY_DIRECTION] = "Direction",
|
||||
[KEY_COFFEE] = "Coffee", [KEY_ROTATE_DISPLAY] = "RotateDisplay",
|
||||
[KEY_CYCLEWINDOWS] = "CycleWindows", [KEY_MAIL] = "Mail",
|
||||
[KEY_BOOKMARKS] = "Bookmarks", [KEY_COMPUTER] = "Computer",
|
||||
[KEY_BACK] = "Back", [KEY_FORWARD] = "Forward",
|
||||
|
@ -368,27 +368,35 @@ static void input_mt_set_slots(struct input_mt *mt,
|
||||
int *slots, int num_pos)
|
||||
{
|
||||
struct input_mt_slot *s;
|
||||
int *w = mt->red, *p;
|
||||
int *w = mt->red, j;
|
||||
|
||||
for (p = slots; p != slots + num_pos; p++)
|
||||
*p = -1;
|
||||
for (j = 0; j != num_pos; j++)
|
||||
slots[j] = -1;
|
||||
|
||||
for (s = mt->slots; s != mt->slots + mt->num_slots; s++) {
|
||||
if (!input_mt_is_active(s))
|
||||
continue;
|
||||
for (p = slots; p != slots + num_pos; p++)
|
||||
if (*w++ < 0)
|
||||
*p = s - mt->slots;
|
||||
|
||||
for (j = 0; j != num_pos; j++) {
|
||||
if (w[j] < 0) {
|
||||
slots[j] = s - mt->slots;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
w += num_pos;
|
||||
}
|
||||
|
||||
for (s = mt->slots; s != mt->slots + mt->num_slots; s++) {
|
||||
if (input_mt_is_active(s))
|
||||
continue;
|
||||
for (p = slots; p != slots + num_pos; p++)
|
||||
if (*p < 0) {
|
||||
*p = s - mt->slots;
|
||||
|
||||
for (j = 0; j != num_pos; j++) {
|
||||
if (slots[j] < 0) {
|
||||
slots[j] = s - mt->slots;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -588,6 +588,16 @@ config KEYBOARD_DAVINCI
|
||||
To compile this driver as a module, choose M here: the
|
||||
module will be called davinci_keyscan.
|
||||
|
||||
config KEYBOARD_IPAQ_MICRO
|
||||
tristate "Buttons on Micro SoC (iPaq h3100,h3600,h3700)"
|
||||
depends on MFD_IPAQ_MICRO
|
||||
help
|
||||
Say Y to enable support for the buttons attached to
|
||||
Micro peripheral controller on iPAQ h3100/h3600/h3700
|
||||
|
||||
To compile this driver as a module, choose M here: the
|
||||
module will be called ipaq-micro-keys.
|
||||
|
||||
config KEYBOARD_OMAP
|
||||
tristate "TI OMAP keypad support"
|
||||
depends on ARCH_OMAP1
|
||||
@ -686,4 +696,15 @@ config KEYBOARD_CAP11XX
|
||||
To compile this driver as a module, choose M here: the
|
||||
module will be called cap11xx.
|
||||
|
||||
config KEYBOARD_BCM
|
||||
tristate "Broadcom keypad driver"
|
||||
depends on OF && HAVE_CLK
|
||||
select INPUT_MATRIXKMAP
|
||||
default ARCH_BCM_CYGNUS
|
||||
help
|
||||
Say Y here if you want to use Broadcom keypad.
|
||||
|
||||
To compile this driver as a module, choose M here: the
|
||||
module will be called bcm-keypad.
|
||||
|
||||
endif
|
||||
|
@ -10,6 +10,7 @@ obj-$(CONFIG_KEYBOARD_ADP5589) += adp5589-keys.o
|
||||
obj-$(CONFIG_KEYBOARD_AMIGA) += amikbd.o
|
||||
obj-$(CONFIG_KEYBOARD_ATARI) += atakbd.o
|
||||
obj-$(CONFIG_KEYBOARD_ATKBD) += atkbd.o
|
||||
obj-$(CONFIG_KEYBOARD_BCM) += bcm-keypad.o
|
||||
obj-$(CONFIG_KEYBOARD_BFIN) += bf54x-keys.o
|
||||
obj-$(CONFIG_KEYBOARD_CAP11XX) += cap11xx.o
|
||||
obj-$(CONFIG_KEYBOARD_CLPS711X) += clps711x-keypad.o
|
||||
@ -23,6 +24,7 @@ obj-$(CONFIG_KEYBOARD_TCA6416) += tca6416-keypad.o
|
||||
obj-$(CONFIG_KEYBOARD_TCA8418) += tca8418_keypad.o
|
||||
obj-$(CONFIG_KEYBOARD_HIL) += hil_kbd.o
|
||||
obj-$(CONFIG_KEYBOARD_HIL_OLD) += hilkbd.o
|
||||
obj-$(CONFIG_KEYBOARD_IPAQ_MICRO) += ipaq-micro-keys.o
|
||||
obj-$(CONFIG_KEYBOARD_IMX) += imx_keypad.o
|
||||
obj-$(CONFIG_KEYBOARD_HP6XX) += jornada680_kbd.o
|
||||
obj-$(CONFIG_KEYBOARD_HP7XX) += jornada720_kbd.o
|
||||
|
@ -1653,6 +1653,12 @@ static int __init atkbd_deactivate_fixup(const struct dmi_system_id *id)
|
||||
return 1;
|
||||
}
|
||||
|
||||
/*
|
||||
* NOTE: do not add any more "force release" quirks to this table. The
|
||||
* task of adjusting list of keys that should be "released" automatically
|
||||
* by the driver is now delegated to userspace tools, such as udev, so
|
||||
* submit such quirks there.
|
||||
*/
|
||||
static const struct dmi_system_id atkbd_dmi_quirk_table[] __initconst = {
|
||||
{
|
||||
.matches = {
|
||||
|
458
drivers/input/keyboard/bcm-keypad.c
Normal file
458
drivers/input/keyboard/bcm-keypad.c
Normal file
@ -0,0 +1,458 @@
|
||||
/*
|
||||
* Copyright (C) 2014 Broadcom Corporation
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU General Public License as
|
||||
* published by the Free Software Foundation version 2.
|
||||
*
|
||||
* This program is distributed "as is" WITHOUT ANY WARRANTY of any
|
||||
* kind, whether express or implied; without even the implied warranty
|
||||
* of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*/
|
||||
|
||||
#include <linux/bitops.h>
|
||||
#include <linux/clk.h>
|
||||
#include <linux/gfp.h>
|
||||
#include <linux/io.h>
|
||||
#include <linux/input.h>
|
||||
#include <linux/input/matrix_keypad.h>
|
||||
#include <linux/interrupt.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/of.h>
|
||||
#include <linux/platform_device.h>
|
||||
#include <linux/stddef.h>
|
||||
#include <linux/types.h>
|
||||
|
||||
#define DEFAULT_CLK_HZ 31250
|
||||
#define MAX_ROWS 8
|
||||
#define MAX_COLS 8
|
||||
|
||||
/* Register/field definitions */
|
||||
#define KPCR_OFFSET 0x00000080
|
||||
#define KPCR_MODE 0x00000002
|
||||
#define KPCR_MODE_SHIFT 1
|
||||
#define KPCR_MODE_MASK 1
|
||||
#define KPCR_ENABLE 0x00000001
|
||||
#define KPCR_STATUSFILTERENABLE 0x00008000
|
||||
#define KPCR_STATUSFILTERTYPE_SHIFT 12
|
||||
#define KPCR_COLFILTERENABLE 0x00000800
|
||||
#define KPCR_COLFILTERTYPE_SHIFT 8
|
||||
#define KPCR_ROWWIDTH_SHIFT 20
|
||||
#define KPCR_COLUMNWIDTH_SHIFT 16
|
||||
|
||||
#define KPIOR_OFFSET 0x00000084
|
||||
#define KPIOR_ROWOCONTRL_SHIFT 24
|
||||
#define KPIOR_ROWOCONTRL_MASK 0xFF000000
|
||||
#define KPIOR_COLUMNOCONTRL_SHIFT 16
|
||||
#define KPIOR_COLUMNOCONTRL_MASK 0x00FF0000
|
||||
#define KPIOR_COLUMN_IO_DATA_SHIFT 0
|
||||
|
||||
#define KPEMR0_OFFSET 0x00000090
|
||||
#define KPEMR1_OFFSET 0x00000094
|
||||
#define KPEMR2_OFFSET 0x00000098
|
||||
#define KPEMR3_OFFSET 0x0000009C
|
||||
#define KPEMR_EDGETYPE_BOTH 3
|
||||
|
||||
#define KPSSR0_OFFSET 0x000000A0
|
||||
#define KPSSR1_OFFSET 0x000000A4
|
||||
#define KPSSRN_OFFSET(reg_n) (KPSSR0_OFFSET + 4 * (reg_n))
|
||||
#define KPIMR0_OFFSET 0x000000B0
|
||||
#define KPIMR1_OFFSET 0x000000B4
|
||||
#define KPICR0_OFFSET 0x000000B8
|
||||
#define KPICR1_OFFSET 0x000000BC
|
||||
#define KPICRN_OFFSET(reg_n) (KPICR0_OFFSET + 4 * (reg_n))
|
||||
#define KPISR0_OFFSET 0x000000C0
|
||||
#define KPISR1_OFFSET 0x000000C4
|
||||
|
||||
#define KPCR_STATUSFILTERTYPE_MAX 7
|
||||
#define KPCR_COLFILTERTYPE_MAX 7
|
||||
|
||||
/* Macros to determine the row/column from a bit that is set in SSR0/1. */
|
||||
#define BIT_TO_ROW_SSRN(bit_nr, reg_n) (((bit_nr) >> 3) + 4 * (reg_n))
|
||||
#define BIT_TO_COL(bit_nr) ((bit_nr) % 8)
|
||||
|
||||
/* Structure representing various run-time entities */
|
||||
struct bcm_kp {
|
||||
void __iomem *base;
|
||||
int irq;
|
||||
struct clk *clk;
|
||||
struct input_dev *input_dev;
|
||||
unsigned long last_state[2];
|
||||
unsigned int n_rows;
|
||||
unsigned int n_cols;
|
||||
u32 kpcr;
|
||||
u32 kpior;
|
||||
u32 kpemr;
|
||||
u32 imr0_val;
|
||||
u32 imr1_val;
|
||||
};
|
||||
|
||||
/*
|
||||
* Returns the keycode from the input device keymap given the row and
|
||||
* column.
|
||||
*/
|
||||
static int bcm_kp_get_keycode(struct bcm_kp *kp, int row, int col)
|
||||
{
|
||||
unsigned int row_shift = get_count_order(kp->n_cols);
|
||||
unsigned short *keymap = kp->input_dev->keycode;
|
||||
|
||||
return keymap[MATRIX_SCAN_CODE(row, col, row_shift)];
|
||||
}
|
||||
|
||||
static void bcm_kp_report_keys(struct bcm_kp *kp, int reg_num, int pull_mode)
|
||||
{
|
||||
unsigned long state, change;
|
||||
int bit_nr;
|
||||
int key_press;
|
||||
int row, col;
|
||||
unsigned int keycode;
|
||||
|
||||
/* Clear interrupts */
|
||||
writel(0xFFFFFFFF, kp->base + KPICRN_OFFSET(reg_num));
|
||||
|
||||
state = readl(kp->base + KPSSRN_OFFSET(reg_num));
|
||||
change = kp->last_state[reg_num] ^ state;
|
||||
kp->last_state[reg_num] = state;
|
||||
|
||||
for_each_set_bit(bit_nr, &change, BITS_PER_LONG) {
|
||||
key_press = state & BIT(bit_nr);
|
||||
/* The meaning of SSR register depends on pull mode. */
|
||||
key_press = pull_mode ? !key_press : key_press;
|
||||
row = BIT_TO_ROW_SSRN(bit_nr, reg_num);
|
||||
col = BIT_TO_COL(bit_nr);
|
||||
keycode = bcm_kp_get_keycode(kp, row, col);
|
||||
input_report_key(kp->input_dev, keycode, key_press);
|
||||
}
|
||||
}
|
||||
|
||||
static irqreturn_t bcm_kp_isr_thread(int irq, void *dev_id)
|
||||
{
|
||||
struct bcm_kp *kp = dev_id;
|
||||
int pull_mode = (kp->kpcr >> KPCR_MODE_SHIFT) & KPCR_MODE_MASK;
|
||||
int reg_num;
|
||||
|
||||
for (reg_num = 0; reg_num <= 1; reg_num++)
|
||||
bcm_kp_report_keys(kp, reg_num, pull_mode);
|
||||
|
||||
input_sync(kp->input_dev);
|
||||
|
||||
return IRQ_HANDLED;
|
||||
}
|
||||
|
||||
static int bcm_kp_start(struct bcm_kp *kp)
|
||||
{
|
||||
int error;
|
||||
|
||||
if (kp->clk) {
|
||||
error = clk_prepare_enable(kp->clk);
|
||||
if (error)
|
||||
return error;
|
||||
}
|
||||
|
||||
writel(kp->kpior, kp->base + KPIOR_OFFSET);
|
||||
|
||||
writel(kp->imr0_val, kp->base + KPIMR0_OFFSET);
|
||||
writel(kp->imr1_val, kp->base + KPIMR1_OFFSET);
|
||||
|
||||
writel(kp->kpemr, kp->base + KPEMR0_OFFSET);
|
||||
writel(kp->kpemr, kp->base + KPEMR1_OFFSET);
|
||||
writel(kp->kpemr, kp->base + KPEMR2_OFFSET);
|
||||
writel(kp->kpemr, kp->base + KPEMR3_OFFSET);
|
||||
|
||||
writel(0xFFFFFFFF, kp->base + KPICR0_OFFSET);
|
||||
writel(0xFFFFFFFF, kp->base + KPICR1_OFFSET);
|
||||
|
||||
kp->last_state[0] = readl(kp->base + KPSSR0_OFFSET);
|
||||
kp->last_state[0] = readl(kp->base + KPSSR1_OFFSET);
|
||||
|
||||
writel(kp->kpcr | KPCR_ENABLE, kp->base + KPCR_OFFSET);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void bcm_kp_stop(const struct bcm_kp *kp)
|
||||
{
|
||||
u32 val;
|
||||
|
||||
val = readl(kp->base + KPCR_OFFSET);
|
||||
val &= ~KPCR_ENABLE;
|
||||
writel(0, kp->base + KPCR_OFFSET);
|
||||
writel(0, kp->base + KPIMR0_OFFSET);
|
||||
writel(0, kp->base + KPIMR1_OFFSET);
|
||||
writel(0xFFFFFFFF, kp->base + KPICR0_OFFSET);
|
||||
writel(0xFFFFFFFF, kp->base + KPICR1_OFFSET);
|
||||
|
||||
if (kp->clk)
|
||||
clk_disable_unprepare(kp->clk);
|
||||
}
|
||||
|
||||
static int bcm_kp_open(struct input_dev *dev)
|
||||
{
|
||||
struct bcm_kp *kp = input_get_drvdata(dev);
|
||||
|
||||
return bcm_kp_start(kp);
|
||||
}
|
||||
|
||||
static void bcm_kp_close(struct input_dev *dev)
|
||||
{
|
||||
struct bcm_kp *kp = input_get_drvdata(dev);
|
||||
|
||||
bcm_kp_stop(kp);
|
||||
}
|
||||
|
||||
static int bcm_kp_matrix_key_parse_dt(struct bcm_kp *kp)
|
||||
{
|
||||
struct device *dev = kp->input_dev->dev.parent;
|
||||
struct device_node *np = dev->of_node;
|
||||
int error;
|
||||
unsigned int dt_val;
|
||||
unsigned int i;
|
||||
unsigned int num_rows, col_mask, rows_set;
|
||||
|
||||
/* Initialize the KPCR Keypad Configuration Register */
|
||||
kp->kpcr = KPCR_STATUSFILTERENABLE | KPCR_COLFILTERENABLE;
|
||||
|
||||
error = matrix_keypad_parse_of_params(dev, &kp->n_rows, &kp->n_cols);
|
||||
if (error) {
|
||||
dev_err(dev, "failed to parse kp params\n");
|
||||
return error;
|
||||
}
|
||||
|
||||
/* Set row width for the ASIC block. */
|
||||
kp->kpcr |= (kp->n_rows - 1) << KPCR_ROWWIDTH_SHIFT;
|
||||
|
||||
/* Set column width for the ASIC block. */
|
||||
kp->kpcr |= (kp->n_cols - 1) << KPCR_COLUMNWIDTH_SHIFT;
|
||||
|
||||
/* Configure the IMR registers */
|
||||
|
||||
/*
|
||||
* IMR registers contain interrupt enable bits for 8x8 matrix
|
||||
* IMR0 register format: <row3> <row2> <row1> <row0>
|
||||
* IMR1 register format: <row7> <row6> <row5> <row4>
|
||||
*/
|
||||
col_mask = (1 << (kp->n_cols)) - 1;
|
||||
num_rows = kp->n_rows;
|
||||
|
||||
/* Set column bits in rows 0 to 3 in IMR0 */
|
||||
kp->imr0_val = col_mask;
|
||||
|
||||
rows_set = 1;
|
||||
while (--num_rows && rows_set++ < 4)
|
||||
kp->imr0_val |= kp->imr0_val << MAX_COLS;
|
||||
|
||||
/* Set column bits in rows 4 to 7 in IMR1 */
|
||||
kp->imr1_val = 0;
|
||||
if (num_rows) {
|
||||
kp->imr1_val = col_mask;
|
||||
while (--num_rows)
|
||||
kp->imr1_val |= kp->imr1_val << MAX_COLS;
|
||||
}
|
||||
|
||||
/* Initialize the KPEMR Keypress Edge Mode Registers */
|
||||
/* Trigger on both edges */
|
||||
kp->kpemr = 0;
|
||||
for (i = 0; i <= 30; i += 2)
|
||||
kp->kpemr |= (KPEMR_EDGETYPE_BOTH << i);
|
||||
|
||||
/*
|
||||
* Obtain the Status filter debounce value and verify against the
|
||||
* possible values specified in the DT binding.
|
||||
*/
|
||||
of_property_read_u32(np, "status-debounce-filter-period", &dt_val);
|
||||
|
||||
if (dt_val > KPCR_STATUSFILTERTYPE_MAX) {
|
||||
dev_err(dev, "Invalid Status filter debounce value %d\n",
|
||||
dt_val);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
kp->kpcr |= dt_val << KPCR_STATUSFILTERTYPE_SHIFT;
|
||||
|
||||
/*
|
||||
* Obtain the Column filter debounce value and verify against the
|
||||
* possible values specified in the DT binding.
|
||||
*/
|
||||
of_property_read_u32(np, "col-debounce-filter-period", &dt_val);
|
||||
|
||||
if (dt_val > KPCR_COLFILTERTYPE_MAX) {
|
||||
dev_err(dev, "Invalid Column filter debounce value %d\n",
|
||||
dt_val);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
kp->kpcr |= dt_val << KPCR_COLFILTERTYPE_SHIFT;
|
||||
|
||||
/*
|
||||
* Determine between the row and column,
|
||||
* which should be configured as output.
|
||||
*/
|
||||
if (of_property_read_bool(np, "row-output-enabled")) {
|
||||
/*
|
||||
* Set RowOContrl or ColumnOContrl in KPIOR
|
||||
* to the number of pins to drive as outputs
|
||||
*/
|
||||
kp->kpior = ((1 << kp->n_rows) - 1) <<
|
||||
KPIOR_ROWOCONTRL_SHIFT;
|
||||
} else {
|
||||
kp->kpior = ((1 << kp->n_cols) - 1) <<
|
||||
KPIOR_COLUMNOCONTRL_SHIFT;
|
||||
}
|
||||
|
||||
/*
|
||||
* Determine if the scan pull up needs to be enabled
|
||||
*/
|
||||
if (of_property_read_bool(np, "pull-up-enabled"))
|
||||
kp->kpcr |= KPCR_MODE;
|
||||
|
||||
dev_dbg(dev, "n_rows=%d n_col=%d kpcr=%x kpior=%x kpemr=%x\n",
|
||||
kp->n_rows, kp->n_cols,
|
||||
kp->kpcr, kp->kpior, kp->kpemr);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
static int bcm_kp_probe(struct platform_device *pdev)
|
||||
{
|
||||
struct bcm_kp *kp;
|
||||
struct input_dev *input_dev;
|
||||
struct resource *res;
|
||||
int error;
|
||||
|
||||
kp = devm_kzalloc(&pdev->dev, sizeof(*kp), GFP_KERNEL);
|
||||
if (!kp)
|
||||
return -ENOMEM;
|
||||
|
||||
input_dev = devm_input_allocate_device(&pdev->dev);
|
||||
if (!input_dev) {
|
||||
dev_err(&pdev->dev, "failed to allocate the input device\n");
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
__set_bit(EV_KEY, input_dev->evbit);
|
||||
|
||||
/* Enable auto repeat feature of Linux input subsystem */
|
||||
if (of_property_read_bool(pdev->dev.of_node, "autorepeat"))
|
||||
__set_bit(EV_REP, input_dev->evbit);
|
||||
|
||||
input_dev->name = pdev->name;
|
||||
input_dev->phys = "keypad/input0";
|
||||
input_dev->dev.parent = &pdev->dev;
|
||||
input_dev->open = bcm_kp_open;
|
||||
input_dev->close = bcm_kp_close;
|
||||
|
||||
input_dev->id.bustype = BUS_HOST;
|
||||
input_dev->id.vendor = 0x0001;
|
||||
input_dev->id.product = 0x0001;
|
||||
input_dev->id.version = 0x0100;
|
||||
|
||||
input_set_drvdata(input_dev, kp);
|
||||
|
||||
kp->input_dev = input_dev;
|
||||
|
||||
platform_set_drvdata(pdev, kp);
|
||||
|
||||
error = bcm_kp_matrix_key_parse_dt(kp);
|
||||
if (error)
|
||||
return error;
|
||||
|
||||
error = matrix_keypad_build_keymap(NULL, NULL,
|
||||
kp->n_rows, kp->n_cols,
|
||||
NULL, input_dev);
|
||||
if (error) {
|
||||
dev_err(&pdev->dev, "failed to build keymap\n");
|
||||
return error;
|
||||
}
|
||||
|
||||
/* Get the KEYPAD base address */
|
||||
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
|
||||
if (!res) {
|
||||
dev_err(&pdev->dev, "Missing keypad base address resource\n");
|
||||
return -ENODEV;
|
||||
}
|
||||
|
||||
kp->base = devm_ioremap_resource(&pdev->dev, res);
|
||||
if (IS_ERR(kp->base))
|
||||
return PTR_ERR(kp->base);
|
||||
|
||||
/* Enable clock */
|
||||
kp->clk = devm_clk_get(&pdev->dev, "peri_clk");
|
||||
if (IS_ERR(kp->clk)) {
|
||||
error = PTR_ERR(kp->clk);
|
||||
if (error != -ENOENT) {
|
||||
if (error != -EPROBE_DEFER)
|
||||
dev_err(&pdev->dev, "Failed to get clock\n");
|
||||
return error;
|
||||
}
|
||||
dev_dbg(&pdev->dev,
|
||||
"No clock specified. Assuming it's enabled\n");
|
||||
kp->clk = NULL;
|
||||
} else {
|
||||
unsigned int desired_rate;
|
||||
long actual_rate;
|
||||
|
||||
error = of_property_read_u32(pdev->dev.of_node,
|
||||
"clock-frequency", &desired_rate);
|
||||
if (error < 0)
|
||||
desired_rate = DEFAULT_CLK_HZ;
|
||||
|
||||
actual_rate = clk_round_rate(kp->clk, desired_rate);
|
||||
if (actual_rate <= 0)
|
||||
return -EINVAL;
|
||||
|
||||
error = clk_set_rate(kp->clk, actual_rate);
|
||||
if (error)
|
||||
return error;
|
||||
|
||||
error = clk_prepare_enable(kp->clk);
|
||||
if (error)
|
||||
return error;
|
||||
}
|
||||
|
||||
/* Put the kp into a known sane state */
|
||||
bcm_kp_stop(kp);
|
||||
|
||||
kp->irq = platform_get_irq(pdev, 0);
|
||||
if (kp->irq < 0) {
|
||||
dev_err(&pdev->dev, "no IRQ specified\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
error = devm_request_threaded_irq(&pdev->dev, kp->irq,
|
||||
NULL, bcm_kp_isr_thread,
|
||||
IRQF_ONESHOT, pdev->name, kp);
|
||||
if (error) {
|
||||
dev_err(&pdev->dev, "failed to request IRQ\n");
|
||||
return error;
|
||||
}
|
||||
|
||||
error = input_register_device(input_dev);
|
||||
if (error) {
|
||||
dev_err(&pdev->dev, "failed to register input device\n");
|
||||
return error;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct of_device_id bcm_kp_of_match[] = {
|
||||
{ .compatible = "brcm,bcm-keypad" },
|
||||
{ },
|
||||
};
|
||||
MODULE_DEVICE_TABLE(of, bcm_kp_of_match);
|
||||
|
||||
static struct platform_driver bcm_kp_device_driver = {
|
||||
.probe = bcm_kp_probe,
|
||||
.driver = {
|
||||
.name = "bcm-keypad",
|
||||
.of_match_table = of_match_ptr(bcm_kp_of_match),
|
||||
}
|
||||
};
|
||||
|
||||
module_platform_driver(bcm_kp_device_driver);
|
||||
|
||||
MODULE_AUTHOR("Broadcom Corporation");
|
||||
MODULE_DESCRIPTION("BCM Keypad Driver");
|
||||
MODULE_LICENSE("GPL v2");
|
@ -338,7 +338,7 @@ static int cros_ec_keyb_resume(struct device *dev)
|
||||
* wake source (e.g. the lid is open and the user might press a key to
|
||||
* wake) then the key scan buffer should be preserved.
|
||||
*/
|
||||
if (ckdev->ec->was_wake_device)
|
||||
if (!ckdev->ec->was_wake_device)
|
||||
cros_ec_keyb_clear_keyboard(ckdev);
|
||||
|
||||
return 0;
|
||||
|
168
drivers/input/keyboard/ipaq-micro-keys.c
Normal file
168
drivers/input/keyboard/ipaq-micro-keys.c
Normal file
@ -0,0 +1,168 @@
|
||||
/*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License version 2 as
|
||||
* published by the Free Software Foundation.
|
||||
*
|
||||
* h3600 atmel micro companion support, key subdevice
|
||||
* based on previous kernel 2.4 version
|
||||
* Author : Alessandro Gardich <gremlin@gremlin.it>
|
||||
* Author : Linus Walleij <linus.walleij@linaro.org>
|
||||
*
|
||||
*/
|
||||
#include <linux/module.h>
|
||||
#include <linux/init.h>
|
||||
#include <linux/fs.h>
|
||||
#include <linux/interrupt.h>
|
||||
#include <linux/sched.h>
|
||||
#include <linux/pm.h>
|
||||
#include <linux/sysctl.h>
|
||||
#include <linux/proc_fs.h>
|
||||
#include <linux/delay.h>
|
||||
#include <linux/device.h>
|
||||
#include <linux/input.h>
|
||||
#include <linux/platform_device.h>
|
||||
#include <linux/mfd/ipaq-micro.h>
|
||||
|
||||
struct ipaq_micro_keys {
|
||||
struct ipaq_micro *micro;
|
||||
struct input_dev *input;
|
||||
u16 *codes;
|
||||
};
|
||||
|
||||
static const u16 micro_keycodes[] = {
|
||||
KEY_RECORD, /* 1: Record button */
|
||||
KEY_CALENDAR, /* 2: Calendar */
|
||||
KEY_ADDRESSBOOK, /* 3: Contacts (looks like Outlook) */
|
||||
KEY_MAIL, /* 4: Envelope (Q on older iPAQs) */
|
||||
KEY_HOMEPAGE, /* 5: Start (looks like swoopy arrow) */
|
||||
KEY_UP, /* 6: Up */
|
||||
KEY_RIGHT, /* 7: Right */
|
||||
KEY_LEFT, /* 8: Left */
|
||||
KEY_DOWN, /* 9: Down */
|
||||
};
|
||||
|
||||
static void micro_key_receive(void *data, int len, unsigned char *msg)
|
||||
{
|
||||
struct ipaq_micro_keys *keys = data;
|
||||
int key, down;
|
||||
|
||||
down = 0x80 & msg[0];
|
||||
key = 0x7f & msg[0];
|
||||
|
||||
if (key < ARRAY_SIZE(micro_keycodes)) {
|
||||
input_report_key(keys->input, keys->codes[key], down);
|
||||
input_sync(keys->input);
|
||||
}
|
||||
}
|
||||
|
||||
static void micro_key_start(struct ipaq_micro_keys *keys)
|
||||
{
|
||||
spin_lock(&keys->micro->lock);
|
||||
keys->micro->key = micro_key_receive;
|
||||
keys->micro->key_data = keys;
|
||||
spin_unlock(&keys->micro->lock);
|
||||
}
|
||||
|
||||
static void micro_key_stop(struct ipaq_micro_keys *keys)
|
||||
{
|
||||
spin_lock(&keys->micro->lock);
|
||||
keys->micro->key = NULL;
|
||||
keys->micro->key_data = NULL;
|
||||
spin_unlock(&keys->micro->lock);
|
||||
}
|
||||
|
||||
static int micro_key_open(struct input_dev *input)
|
||||
{
|
||||
struct ipaq_micro_keys *keys = input_get_drvdata(input);
|
||||
|
||||
micro_key_start(keys);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void micro_key_close(struct input_dev *input)
|
||||
{
|
||||
struct ipaq_micro_keys *keys = input_get_drvdata(input);
|
||||
|
||||
micro_key_stop(keys);
|
||||
}
|
||||
|
||||
static int micro_key_probe(struct platform_device *pdev)
|
||||
{
|
||||
struct ipaq_micro_keys *keys;
|
||||
int error;
|
||||
int i;
|
||||
|
||||
keys = devm_kzalloc(&pdev->dev, sizeof(*keys), GFP_KERNEL);
|
||||
if (!keys)
|
||||
return -ENOMEM;
|
||||
|
||||
keys->micro = dev_get_drvdata(pdev->dev.parent);
|
||||
|
||||
keys->input = devm_input_allocate_device(&pdev->dev);
|
||||
if (!keys->input)
|
||||
return -ENOMEM;
|
||||
|
||||
keys->input->keycodesize = sizeof(micro_keycodes[0]);
|
||||
keys->input->keycodemax = ARRAY_SIZE(micro_keycodes);
|
||||
keys->codes = devm_kmemdup(&pdev->dev, micro_keycodes,
|
||||
keys->input->keycodesize * keys->input->keycodemax,
|
||||
GFP_KERNEL);
|
||||
keys->input->keycode = keys->codes;
|
||||
|
||||
__set_bit(EV_KEY, keys->input->evbit);
|
||||
for (i = 0; i < ARRAY_SIZE(micro_keycodes); i++)
|
||||
__set_bit(micro_keycodes[i], keys->input->keybit);
|
||||
|
||||
keys->input->name = "h3600 micro keys";
|
||||
keys->input->open = micro_key_open;
|
||||
keys->input->close = micro_key_close;
|
||||
input_set_drvdata(keys->input, keys);
|
||||
|
||||
error = input_register_device(keys->input);
|
||||
if (error)
|
||||
return error;
|
||||
|
||||
platform_set_drvdata(pdev, keys);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int __maybe_unused micro_key_suspend(struct device *dev)
|
||||
{
|
||||
struct ipaq_micro_keys *keys = dev_get_drvdata(dev);
|
||||
|
||||
micro_key_stop(keys);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int __maybe_unused micro_key_resume(struct device *dev)
|
||||
{
|
||||
struct ipaq_micro_keys *keys = dev_get_drvdata(dev);
|
||||
struct input_dev *input = keys->input;
|
||||
|
||||
mutex_lock(&input->mutex);
|
||||
|
||||
if (input->users)
|
||||
micro_key_start(keys);
|
||||
|
||||
mutex_unlock(&input->mutex);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static SIMPLE_DEV_PM_OPS(micro_key_dev_pm_ops,
|
||||
micro_key_suspend, micro_key_resume);
|
||||
|
||||
static struct platform_driver micro_key_device_driver = {
|
||||
.driver = {
|
||||
.name = "ipaq-micro-keys",
|
||||
.pm = µ_key_dev_pm_ops,
|
||||
},
|
||||
.probe = micro_key_probe,
|
||||
};
|
||||
module_platform_driver(micro_key_device_driver);
|
||||
|
||||
MODULE_LICENSE("GPL");
|
||||
MODULE_DESCRIPTION("driver for iPAQ Atmel micro keys");
|
||||
MODULE_ALIAS("platform:ipaq-micro-keys");
|
@ -69,6 +69,28 @@
|
||||
#define TC3589x_EVT_INT_CLR 0x2
|
||||
#define TC3589x_KBD_INT_CLR 0x1
|
||||
|
||||
/**
|
||||
* struct tc35893_keypad_platform_data - platform specific keypad data
|
||||
* @keymap_data: matrix scan code table for keycodes
|
||||
* @krow: mask for available rows, value is 0xFF
|
||||
* @kcol: mask for available columns, value is 0xFF
|
||||
* @debounce_period: platform specific debounce time
|
||||
* @settle_time: platform specific settle down time
|
||||
* @irqtype: type of interrupt, falling or rising edge
|
||||
* @enable_wakeup: specifies if keypad event can wake up system from sleep
|
||||
* @no_autorepeat: flag for auto repetition
|
||||
*/
|
||||
struct tc3589x_keypad_platform_data {
|
||||
const struct matrix_keymap_data *keymap_data;
|
||||
u8 krow;
|
||||
u8 kcol;
|
||||
u8 debounce_period;
|
||||
u8 settle_time;
|
||||
unsigned long irqtype;
|
||||
bool enable_wakeup;
|
||||
bool no_autorepeat;
|
||||
};
|
||||
|
||||
/**
|
||||
* struct tc_keypad - data structure used by keypad driver
|
||||
* @tc3589x: pointer to tc35893
|
||||
@ -354,13 +376,10 @@ static int tc3589x_keypad_probe(struct platform_device *pdev)
|
||||
const struct tc3589x_keypad_platform_data *plat;
|
||||
int error, irq;
|
||||
|
||||
plat = tc3589x->pdata->keypad;
|
||||
if (!plat) {
|
||||
plat = tc3589x_keypad_of_probe(&pdev->dev);
|
||||
if (IS_ERR(plat)) {
|
||||
dev_err(&pdev->dev, "invalid keypad platform data\n");
|
||||
return PTR_ERR(plat);
|
||||
}
|
||||
plat = tc3589x_keypad_of_probe(&pdev->dev);
|
||||
if (IS_ERR(plat)) {
|
||||
dev_err(&pdev->dev, "invalid keypad platform data\n");
|
||||
return PTR_ERR(plat);
|
||||
}
|
||||
|
||||
irq = platform_get_irq(pdev, 0);
|
||||
|
@ -115,6 +115,18 @@ config INPUT_PCSPKR
|
||||
To compile this driver as a module, choose M here: the
|
||||
module will be called pcspkr.
|
||||
|
||||
config INPUT_PM8941_PWRKEY
|
||||
tristate "Qualcomm PM8941 power key support"
|
||||
depends on MFD_SPMI_PMIC
|
||||
help
|
||||
Say Y here if you want support for the power key usually found
|
||||
on boards using a Qualcomm PM8941 compatible PMIC.
|
||||
|
||||
If unsure, say Y.
|
||||
|
||||
To compile this driver as a module, choose M here: the module
|
||||
will be called pm8941-pwrkey.
|
||||
|
||||
config INPUT_PM8XXX_VIBRATOR
|
||||
tristate "Qualcomm PM8XXX vibrator support"
|
||||
depends on MFD_PM8XXX
|
||||
@ -165,6 +177,18 @@ config INPUT_MAX77693_HAPTIC
|
||||
To compile this driver as module, choose M here: the
|
||||
module will be called max77693-haptic.
|
||||
|
||||
config INPUT_MAX77843_HAPTIC
|
||||
tristate "MAXIM MAX77843 haptic controller support"
|
||||
depends on MFD_MAX77843 && REGULATOR
|
||||
select INPUT_FF_MEMLESS
|
||||
help
|
||||
This option enables support for the haptic controller on
|
||||
MAXIM MAX77843 chip. The driver supports ff-memless interface
|
||||
from input framework.
|
||||
|
||||
To compile this driver as module, choose M here: the
|
||||
module will be called max77843-haptic.
|
||||
|
||||
config INPUT_MAX8925_ONKEY
|
||||
tristate "MAX8925 ONKEY support"
|
||||
depends on MFD_MAX8925
|
||||
|
@ -39,6 +39,7 @@ obj-$(CONFIG_INPUT_KEYSPAN_REMOTE) += keyspan_remote.o
|
||||
obj-$(CONFIG_INPUT_KXTJ9) += kxtj9.o
|
||||
obj-$(CONFIG_INPUT_M68K_BEEP) += m68kspkr.o
|
||||
obj-$(CONFIG_INPUT_MAX77693_HAPTIC) += max77693-haptic.o
|
||||
obj-$(CONFIG_INPUT_MAX77843_HAPTIC) += max77843-haptic.o
|
||||
obj-$(CONFIG_INPUT_MAX8925_ONKEY) += max8925_onkey.o
|
||||
obj-$(CONFIG_INPUT_MAX8997_HAPTIC) += max8997_haptic.o
|
||||
obj-$(CONFIG_INPUT_MC13783_PWRBUTTON) += mc13783-pwrbutton.o
|
||||
@ -49,6 +50,7 @@ obj-$(CONFIG_INPUT_PCAP) += pcap_keys.o
|
||||
obj-$(CONFIG_INPUT_PCF50633_PMU) += pcf50633-input.o
|
||||
obj-$(CONFIG_INPUT_PCF8574) += pcf8574_keypad.o
|
||||
obj-$(CONFIG_INPUT_PCSPKR) += pcspkr.o
|
||||
obj-$(CONFIG_INPUT_PM8941_PWRKEY) += pm8941-pwrkey.o
|
||||
obj-$(CONFIG_INPUT_PM8XXX_VIBRATOR) += pm8xxx-vibrator.o
|
||||
obj-$(CONFIG_INPUT_PMIC8XXX_PWRKEY) += pmic8xxx-pwrkey.o
|
||||
obj-$(CONFIG_INPUT_POWERMATE) += powermate.o
|
||||
|
358
drivers/input/misc/max77843-haptic.c
Normal file
358
drivers/input/misc/max77843-haptic.c
Normal file
@ -0,0 +1,358 @@
|
||||
/*
|
||||
* MAXIM MAX77693 Haptic device driver
|
||||
*
|
||||
* Copyright (C) 2015 Samsung Electronics
|
||||
* Author: Jaewon Kim <jaewon02.kim@samsung.com>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*/
|
||||
|
||||
#include <linux/err.h>
|
||||
#include <linux/i2c.h>
|
||||
#include <linux/init.h>
|
||||
#include <linux/input.h>
|
||||
#include <linux/mfd/max77843-private.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/platform_device.h>
|
||||
#include <linux/pwm.h>
|
||||
#include <linux/regmap.h>
|
||||
#include <linux/regulator/consumer.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/workqueue.h>
|
||||
|
||||
#define MAX_MAGNITUDE_SHIFT 16
|
||||
|
||||
enum max77843_haptic_motor_type {
|
||||
MAX77843_HAPTIC_ERM = 0,
|
||||
MAX77843_HAPTIC_LRA,
|
||||
};
|
||||
|
||||
enum max77843_haptic_pwm_divisor {
|
||||
MAX77843_HAPTIC_PWM_DIVISOR_32 = 0,
|
||||
MAX77843_HAPTIC_PWM_DIVISOR_64,
|
||||
MAX77843_HAPTIC_PWM_DIVISOR_128,
|
||||
MAX77843_HAPTIC_PWM_DIVISOR_256,
|
||||
};
|
||||
|
||||
struct max77843_haptic {
|
||||
struct regmap *regmap_haptic;
|
||||
struct device *dev;
|
||||
struct input_dev *input_dev;
|
||||
struct pwm_device *pwm_dev;
|
||||
struct regulator *motor_reg;
|
||||
struct work_struct work;
|
||||
struct mutex mutex;
|
||||
|
||||
unsigned int magnitude;
|
||||
unsigned int pwm_duty;
|
||||
|
||||
bool active;
|
||||
bool suspended;
|
||||
|
||||
enum max77843_haptic_motor_type type;
|
||||
enum max77843_haptic_pwm_divisor pwm_divisor;
|
||||
};
|
||||
|
||||
static int max77843_haptic_set_duty_cycle(struct max77843_haptic *haptic)
|
||||
{
|
||||
int delta = (haptic->pwm_dev->period + haptic->pwm_duty) / 2;
|
||||
int error;
|
||||
|
||||
error = pwm_config(haptic->pwm_dev, delta, haptic->pwm_dev->period);
|
||||
if (error) {
|
||||
dev_err(haptic->dev, "failed to configure pwm: %d\n", error);
|
||||
return error;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int max77843_haptic_bias(struct max77843_haptic *haptic, bool on)
|
||||
{
|
||||
int error;
|
||||
|
||||
error = regmap_update_bits(haptic->regmap_haptic,
|
||||
MAX77843_SYS_REG_MAINCTRL1,
|
||||
MAX77843_MAINCTRL1_BIASEN_MASK,
|
||||
on << MAINCTRL1_BIASEN_SHIFT);
|
||||
if (error) {
|
||||
dev_err(haptic->dev, "failed to %s bias: %d\n",
|
||||
on ? "enable" : "disable", error);
|
||||
return error;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int max77843_haptic_config(struct max77843_haptic *haptic, bool enable)
|
||||
{
|
||||
unsigned int value;
|
||||
int error;
|
||||
|
||||
value = (haptic->type << MCONFIG_MODE_SHIFT) |
|
||||
(enable << MCONFIG_MEN_SHIFT) |
|
||||
(haptic->pwm_divisor << MCONFIG_PDIV_SHIFT);
|
||||
|
||||
error = regmap_write(haptic->regmap_haptic,
|
||||
MAX77843_HAP_REG_MCONFIG, value);
|
||||
if (error) {
|
||||
dev_err(haptic->dev,
|
||||
"failed to update haptic config: %d\n", error);
|
||||
return error;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int max77843_haptic_enable(struct max77843_haptic *haptic)
|
||||
{
|
||||
int error;
|
||||
|
||||
if (haptic->active)
|
||||
return 0;
|
||||
|
||||
error = pwm_enable(haptic->pwm_dev);
|
||||
if (error) {
|
||||
dev_err(haptic->dev,
|
||||
"failed to enable pwm device: %d\n", error);
|
||||
return error;
|
||||
}
|
||||
|
||||
error = max77843_haptic_config(haptic, true);
|
||||
if (error)
|
||||
goto err_config;
|
||||
|
||||
haptic->active = true;
|
||||
|
||||
return 0;
|
||||
|
||||
err_config:
|
||||
pwm_disable(haptic->pwm_dev);
|
||||
|
||||
return error;
|
||||
}
|
||||
|
||||
static int max77843_haptic_disable(struct max77843_haptic *haptic)
|
||||
{
|
||||
int error;
|
||||
|
||||
if (!haptic->active)
|
||||
return 0;
|
||||
|
||||
error = max77843_haptic_config(haptic, false);
|
||||
if (error)
|
||||
return error;
|
||||
|
||||
pwm_disable(haptic->pwm_dev);
|
||||
|
||||
haptic->active = false;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void max77843_haptic_play_work(struct work_struct *work)
|
||||
{
|
||||
struct max77843_haptic *haptic =
|
||||
container_of(work, struct max77843_haptic, work);
|
||||
int error;
|
||||
|
||||
mutex_lock(&haptic->mutex);
|
||||
|
||||
if (haptic->suspended)
|
||||
goto out_unlock;
|
||||
|
||||
if (haptic->magnitude) {
|
||||
error = max77843_haptic_set_duty_cycle(haptic);
|
||||
if (error) {
|
||||
dev_err(haptic->dev,
|
||||
"failed to set duty cycle: %d\n", error);
|
||||
goto out_unlock;
|
||||
}
|
||||
|
||||
error = max77843_haptic_enable(haptic);
|
||||
if (error)
|
||||
dev_err(haptic->dev,
|
||||
"cannot enable haptic: %d\n", error);
|
||||
} else {
|
||||
error = max77843_haptic_disable(haptic);
|
||||
if (error)
|
||||
dev_err(haptic->dev,
|
||||
"cannot disable haptic: %d\n", error);
|
||||
}
|
||||
|
||||
out_unlock:
|
||||
mutex_unlock(&haptic->mutex);
|
||||
}
|
||||
|
||||
static int max77843_haptic_play_effect(struct input_dev *dev, void *data,
|
||||
struct ff_effect *effect)
|
||||
{
|
||||
struct max77843_haptic *haptic = input_get_drvdata(dev);
|
||||
u64 period_mag_multi;
|
||||
|
||||
haptic->magnitude = effect->u.rumble.strong_magnitude;
|
||||
if (!haptic->magnitude)
|
||||
haptic->magnitude = effect->u.rumble.weak_magnitude;
|
||||
|
||||
period_mag_multi = (u64)haptic->pwm_dev->period * haptic->magnitude;
|
||||
haptic->pwm_duty = (unsigned int)(period_mag_multi >>
|
||||
MAX_MAGNITUDE_SHIFT);
|
||||
|
||||
schedule_work(&haptic->work);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int max77843_haptic_open(struct input_dev *dev)
|
||||
{
|
||||
struct max77843_haptic *haptic = input_get_drvdata(dev);
|
||||
int error;
|
||||
|
||||
error = max77843_haptic_bias(haptic, true);
|
||||
if (error)
|
||||
return error;
|
||||
|
||||
error = regulator_enable(haptic->motor_reg);
|
||||
if (error) {
|
||||
dev_err(haptic->dev,
|
||||
"failed to enable regulator: %d\n", error);
|
||||
return error;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void max77843_haptic_close(struct input_dev *dev)
|
||||
{
|
||||
struct max77843_haptic *haptic = input_get_drvdata(dev);
|
||||
int error;
|
||||
|
||||
cancel_work_sync(&haptic->work);
|
||||
max77843_haptic_disable(haptic);
|
||||
|
||||
error = regulator_disable(haptic->motor_reg);
|
||||
if (error)
|
||||
dev_err(haptic->dev,
|
||||
"failed to disable regulator: %d\n", error);
|
||||
|
||||
max77843_haptic_bias(haptic, false);
|
||||
}
|
||||
|
||||
static int max77843_haptic_probe(struct platform_device *pdev)
|
||||
{
|
||||
struct max77843 *max77843 = dev_get_drvdata(pdev->dev.parent);
|
||||
struct max77843_haptic *haptic;
|
||||
int error;
|
||||
|
||||
haptic = devm_kzalloc(&pdev->dev, sizeof(*haptic), GFP_KERNEL);
|
||||
if (!haptic)
|
||||
return -ENOMEM;
|
||||
|
||||
haptic->regmap_haptic = max77843->regmap;
|
||||
haptic->dev = &pdev->dev;
|
||||
haptic->type = MAX77843_HAPTIC_LRA;
|
||||
haptic->pwm_divisor = MAX77843_HAPTIC_PWM_DIVISOR_128;
|
||||
|
||||
INIT_WORK(&haptic->work, max77843_haptic_play_work);
|
||||
mutex_init(&haptic->mutex);
|
||||
|
||||
haptic->pwm_dev = devm_pwm_get(&pdev->dev, NULL);
|
||||
if (IS_ERR(haptic->pwm_dev)) {
|
||||
dev_err(&pdev->dev, "failed to get pwm device\n");
|
||||
return PTR_ERR(haptic->pwm_dev);
|
||||
}
|
||||
|
||||
haptic->motor_reg = devm_regulator_get_exclusive(&pdev->dev, "haptic");
|
||||
if (IS_ERR(haptic->motor_reg)) {
|
||||
dev_err(&pdev->dev, "failed to get regulator\n");
|
||||
return PTR_ERR(haptic->motor_reg);
|
||||
}
|
||||
|
||||
haptic->input_dev = devm_input_allocate_device(&pdev->dev);
|
||||
if (!haptic->input_dev) {
|
||||
dev_err(&pdev->dev, "failed to allocate input device\n");
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
haptic->input_dev->name = "max77843-haptic";
|
||||
haptic->input_dev->id.version = 1;
|
||||
haptic->input_dev->dev.parent = &pdev->dev;
|
||||
haptic->input_dev->open = max77843_haptic_open;
|
||||
haptic->input_dev->close = max77843_haptic_close;
|
||||
input_set_drvdata(haptic->input_dev, haptic);
|
||||
input_set_capability(haptic->input_dev, EV_FF, FF_RUMBLE);
|
||||
|
||||
error = input_ff_create_memless(haptic->input_dev, NULL,
|
||||
max77843_haptic_play_effect);
|
||||
if (error) {
|
||||
dev_err(&pdev->dev, "failed to create force-feedback\n");
|
||||
return error;
|
||||
}
|
||||
|
||||
error = input_register_device(haptic->input_dev);
|
||||
if (error) {
|
||||
dev_err(&pdev->dev, "failed to register input device\n");
|
||||
return error;
|
||||
}
|
||||
|
||||
platform_set_drvdata(pdev, haptic);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int __maybe_unused max77843_haptic_suspend(struct device *dev)
|
||||
{
|
||||
struct platform_device *pdev = to_platform_device(dev);
|
||||
struct max77843_haptic *haptic = platform_get_drvdata(pdev);
|
||||
int error;
|
||||
|
||||
error = mutex_lock_interruptible(&haptic->mutex);
|
||||
if (error)
|
||||
return error;
|
||||
|
||||
max77843_haptic_disable(haptic);
|
||||
|
||||
haptic->suspended = true;
|
||||
|
||||
mutex_unlock(&haptic->mutex);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int __maybe_unused max77843_haptic_resume(struct device *dev)
|
||||
{
|
||||
struct platform_device *pdev = to_platform_device(dev);
|
||||
struct max77843_haptic *haptic = platform_get_drvdata(pdev);
|
||||
unsigned int magnitude;
|
||||
|
||||
mutex_lock(&haptic->mutex);
|
||||
|
||||
haptic->suspended = false;
|
||||
|
||||
magnitude = ACCESS_ONCE(haptic->magnitude);
|
||||
if (magnitude)
|
||||
max77843_haptic_enable(haptic);
|
||||
|
||||
mutex_unlock(&haptic->mutex);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static SIMPLE_DEV_PM_OPS(max77843_haptic_pm_ops,
|
||||
max77843_haptic_suspend, max77843_haptic_resume);
|
||||
|
||||
static struct platform_driver max77843_haptic_driver = {
|
||||
.driver = {
|
||||
.name = "max77843-haptic",
|
||||
.pm = &max77843_haptic_pm_ops,
|
||||
},
|
||||
.probe = max77843_haptic_probe,
|
||||
};
|
||||
module_platform_driver(max77843_haptic_driver);
|
||||
|
||||
MODULE_AUTHOR("Jaewon Kim <jaewon02.kim@samsung.com>");
|
||||
MODULE_DESCRIPTION("MAXIM MAX77843 Haptic driver");
|
||||
MODULE_LICENSE("GPL");
|
@ -174,12 +174,13 @@ static int mma8450_probe(struct i2c_client *c,
|
||||
struct mma8450 *m;
|
||||
int err;
|
||||
|
||||
m = kzalloc(sizeof(struct mma8450), GFP_KERNEL);
|
||||
idev = input_allocate_polled_device();
|
||||
if (!m || !idev) {
|
||||
err = -ENOMEM;
|
||||
goto err_free_mem;
|
||||
}
|
||||
m = devm_kzalloc(&c->dev, sizeof(*m), GFP_KERNEL);
|
||||
if (!m)
|
||||
return -ENOMEM;
|
||||
|
||||
idev = devm_input_allocate_polled_device(&c->dev);
|
||||
if (!idev)
|
||||
return -ENOMEM;
|
||||
|
||||
m->client = c;
|
||||
m->idev = idev;
|
||||
@ -187,7 +188,6 @@ static int mma8450_probe(struct i2c_client *c,
|
||||
idev->private = m;
|
||||
idev->input->name = MMA8450_DRV_NAME;
|
||||
idev->input->id.bustype = BUS_I2C;
|
||||
idev->input->dev.parent = &c->dev;
|
||||
idev->poll = mma8450_poll;
|
||||
idev->poll_interval = POLL_INTERVAL;
|
||||
idev->poll_interval_max = POLL_INTERVAL_MAX;
|
||||
@ -202,28 +202,11 @@ static int mma8450_probe(struct i2c_client *c,
|
||||
err = input_register_polled_device(idev);
|
||||
if (err) {
|
||||
dev_err(&c->dev, "failed to register polled input device\n");
|
||||
goto err_free_mem;
|
||||
return err;
|
||||
}
|
||||
|
||||
i2c_set_clientdata(c, m);
|
||||
|
||||
return 0;
|
||||
|
||||
err_free_mem:
|
||||
input_free_polled_device(idev);
|
||||
kfree(m);
|
||||
return err;
|
||||
}
|
||||
|
||||
static int mma8450_remove(struct i2c_client *c)
|
||||
{
|
||||
struct mma8450 *m = i2c_get_clientdata(c);
|
||||
struct input_polled_dev *idev = m->idev;
|
||||
|
||||
input_unregister_polled_device(idev);
|
||||
input_free_polled_device(idev);
|
||||
kfree(m);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
@ -242,11 +225,9 @@ MODULE_DEVICE_TABLE(of, mma8450_dt_ids);
|
||||
static struct i2c_driver mma8450_driver = {
|
||||
.driver = {
|
||||
.name = MMA8450_DRV_NAME,
|
||||
.owner = THIS_MODULE,
|
||||
.of_match_table = mma8450_dt_ids,
|
||||
},
|
||||
.probe = mma8450_probe,
|
||||
.remove = mma8450_remove,
|
||||
.id_table = mma8450_id,
|
||||
};
|
||||
|
||||
|
@ -304,7 +304,7 @@ static SIMPLE_DEV_PM_OPS(palmas_pwron_pm,
|
||||
palmas_pwron_suspend, palmas_pwron_resume);
|
||||
|
||||
#ifdef CONFIG_OF
|
||||
static struct of_device_id of_palmas_pwr_match[] = {
|
||||
static const struct of_device_id of_palmas_pwr_match[] = {
|
||||
{ .compatible = "ti,palmas-pwrbutton" },
|
||||
{ },
|
||||
};
|
||||
|
293
drivers/input/misc/pm8941-pwrkey.c
Normal file
293
drivers/input/misc/pm8941-pwrkey.c
Normal file
@ -0,0 +1,293 @@
|
||||
/*
|
||||
* Copyright (c) 2010-2011, Code Aurora Forum. All rights reserved.
|
||||
* Copyright (c) 2014, Sony Mobile Communications Inc.
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License version 2 and
|
||||
* only version 2 as published by the Free Software Foundation.
|
||||
*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
#include <linux/delay.h>
|
||||
#include <linux/errno.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/platform_device.h>
|
||||
#include <linux/reboot.h>
|
||||
#include <linux/regmap.h>
|
||||
|
||||
#define PON_REV2 0x01
|
||||
|
||||
#define PON_RT_STS 0x10
|
||||
#define PON_KPDPWR_N_SET BIT(0)
|
||||
|
||||
#define PON_PS_HOLD_RST_CTL 0x5a
|
||||
#define PON_PS_HOLD_RST_CTL2 0x5b
|
||||
#define PON_PS_HOLD_ENABLE BIT(7)
|
||||
#define PON_PS_HOLD_TYPE_MASK 0x0f
|
||||
#define PON_PS_HOLD_TYPE_SHUTDOWN 4
|
||||
#define PON_PS_HOLD_TYPE_HARD_RESET 7
|
||||
|
||||
#define PON_PULL_CTL 0x70
|
||||
#define PON_KPDPWR_PULL_UP BIT(1)
|
||||
|
||||
#define PON_DBC_CTL 0x71
|
||||
#define PON_DBC_DELAY_MASK 0x7
|
||||
|
||||
|
||||
struct pm8941_pwrkey {
|
||||
struct device *dev;
|
||||
int irq;
|
||||
u32 baseaddr;
|
||||
struct regmap *regmap;
|
||||
struct input_dev *input;
|
||||
|
||||
unsigned int revision;
|
||||
struct notifier_block reboot_notifier;
|
||||
};
|
||||
|
||||
static int pm8941_reboot_notify(struct notifier_block *nb,
|
||||
unsigned long code, void *unused)
|
||||
{
|
||||
struct pm8941_pwrkey *pwrkey = container_of(nb, struct pm8941_pwrkey,
|
||||
reboot_notifier);
|
||||
unsigned int enable_reg;
|
||||
unsigned int reset_type;
|
||||
int error;
|
||||
|
||||
/* PMICs with revision 0 have the enable bit in same register as ctrl */
|
||||
if (pwrkey->revision == 0)
|
||||
enable_reg = PON_PS_HOLD_RST_CTL;
|
||||
else
|
||||
enable_reg = PON_PS_HOLD_RST_CTL2;
|
||||
|
||||
error = regmap_update_bits(pwrkey->regmap,
|
||||
pwrkey->baseaddr + enable_reg,
|
||||
PON_PS_HOLD_ENABLE,
|
||||
0);
|
||||
if (error)
|
||||
dev_err(pwrkey->dev,
|
||||
"unable to clear ps hold reset enable: %d\n",
|
||||
error);
|
||||
|
||||
/*
|
||||
* Updates of PON_PS_HOLD_ENABLE requires 3 sleep cycles between
|
||||
* writes.
|
||||
*/
|
||||
usleep_range(100, 1000);
|
||||
|
||||
switch (code) {
|
||||
case SYS_HALT:
|
||||
case SYS_POWER_OFF:
|
||||
reset_type = PON_PS_HOLD_TYPE_SHUTDOWN;
|
||||
break;
|
||||
case SYS_RESTART:
|
||||
default:
|
||||
reset_type = PON_PS_HOLD_TYPE_HARD_RESET;
|
||||
break;
|
||||
};
|
||||
|
||||
error = regmap_update_bits(pwrkey->regmap,
|
||||
pwrkey->baseaddr + PON_PS_HOLD_RST_CTL,
|
||||
PON_PS_HOLD_TYPE_MASK,
|
||||
reset_type);
|
||||
if (error)
|
||||
dev_err(pwrkey->dev, "unable to set ps hold reset type: %d\n",
|
||||
error);
|
||||
|
||||
error = regmap_update_bits(pwrkey->regmap,
|
||||
pwrkey->baseaddr + enable_reg,
|
||||
PON_PS_HOLD_ENABLE,
|
||||
PON_PS_HOLD_ENABLE);
|
||||
if (error)
|
||||
dev_err(pwrkey->dev, "unable to re-set enable: %d\n", error);
|
||||
|
||||
return NOTIFY_DONE;
|
||||
}
|
||||
|
||||
static irqreturn_t pm8941_pwrkey_irq(int irq, void *_data)
|
||||
{
|
||||
struct pm8941_pwrkey *pwrkey = _data;
|
||||
unsigned int sts;
|
||||
int error;
|
||||
|
||||
error = regmap_read(pwrkey->regmap,
|
||||
pwrkey->baseaddr + PON_RT_STS, &sts);
|
||||
if (error)
|
||||
return IRQ_HANDLED;
|
||||
|
||||
input_report_key(pwrkey->input, KEY_POWER, !!(sts & PON_KPDPWR_N_SET));
|
||||
input_sync(pwrkey->input);
|
||||
|
||||
return IRQ_HANDLED;
|
||||
}
|
||||
|
||||
static int __maybe_unused pm8941_pwrkey_suspend(struct device *dev)
|
||||
{
|
||||
struct pm8941_pwrkey *pwrkey = dev_get_drvdata(dev);
|
||||
|
||||
if (device_may_wakeup(dev))
|
||||
enable_irq_wake(pwrkey->irq);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int __maybe_unused pm8941_pwrkey_resume(struct device *dev)
|
||||
{
|
||||
struct pm8941_pwrkey *pwrkey = dev_get_drvdata(dev);
|
||||
|
||||
if (device_may_wakeup(dev))
|
||||
disable_irq_wake(pwrkey->irq);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static SIMPLE_DEV_PM_OPS(pm8941_pwr_key_pm_ops,
|
||||
pm8941_pwrkey_suspend, pm8941_pwrkey_resume);
|
||||
|
||||
static int pm8941_pwrkey_probe(struct platform_device *pdev)
|
||||
{
|
||||
struct pm8941_pwrkey *pwrkey;
|
||||
bool pull_up;
|
||||
u32 req_delay;
|
||||
int error;
|
||||
|
||||
if (of_property_read_u32(pdev->dev.of_node, "debounce", &req_delay))
|
||||
req_delay = 15625;
|
||||
|
||||
if (req_delay > 2000000 || req_delay == 0) {
|
||||
dev_err(&pdev->dev, "invalid debounce time: %u\n", req_delay);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
pull_up = of_property_read_bool(pdev->dev.of_node, "bias-pull-up");
|
||||
|
||||
pwrkey = devm_kzalloc(&pdev->dev, sizeof(*pwrkey), GFP_KERNEL);
|
||||
if (!pwrkey)
|
||||
return -ENOMEM;
|
||||
|
||||
pwrkey->dev = &pdev->dev;
|
||||
|
||||
pwrkey->regmap = dev_get_regmap(pdev->dev.parent, NULL);
|
||||
if (!pwrkey->regmap) {
|
||||
dev_err(&pdev->dev, "failed to locate regmap\n");
|
||||
return -ENODEV;
|
||||
}
|
||||
|
||||
pwrkey->irq = platform_get_irq(pdev, 0);
|
||||
if (pwrkey->irq < 0) {
|
||||
dev_err(&pdev->dev, "failed to get irq\n");
|
||||
return pwrkey->irq;
|
||||
}
|
||||
|
||||
error = of_property_read_u32(pdev->dev.of_node, "reg",
|
||||
&pwrkey->baseaddr);
|
||||
if (error)
|
||||
return error;
|
||||
|
||||
error = regmap_read(pwrkey->regmap, pwrkey->baseaddr + PON_REV2,
|
||||
&pwrkey->revision);
|
||||
if (error) {
|
||||
dev_err(&pdev->dev, "failed to set debounce: %d\n", error);
|
||||
return error;
|
||||
}
|
||||
|
||||
pwrkey->input = devm_input_allocate_device(&pdev->dev);
|
||||
if (!pwrkey->input) {
|
||||
dev_dbg(&pdev->dev, "unable to allocate input device\n");
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
input_set_capability(pwrkey->input, EV_KEY, KEY_POWER);
|
||||
|
||||
pwrkey->input->name = "pm8941_pwrkey";
|
||||
pwrkey->input->phys = "pm8941_pwrkey/input0";
|
||||
|
||||
req_delay = (req_delay << 6) / USEC_PER_SEC;
|
||||
req_delay = ilog2(req_delay);
|
||||
|
||||
error = regmap_update_bits(pwrkey->regmap,
|
||||
pwrkey->baseaddr + PON_DBC_CTL,
|
||||
PON_DBC_DELAY_MASK,
|
||||
req_delay);
|
||||
if (error) {
|
||||
dev_err(&pdev->dev, "failed to set debounce: %d\n", error);
|
||||
return error;
|
||||
}
|
||||
|
||||
error = regmap_update_bits(pwrkey->regmap,
|
||||
pwrkey->baseaddr + PON_PULL_CTL,
|
||||
PON_KPDPWR_PULL_UP,
|
||||
pull_up ? PON_KPDPWR_PULL_UP : 0);
|
||||
if (error) {
|
||||
dev_err(&pdev->dev, "failed to set pull: %d\n", error);
|
||||
return error;
|
||||
}
|
||||
|
||||
error = devm_request_threaded_irq(&pdev->dev, pwrkey->irq,
|
||||
NULL, pm8941_pwrkey_irq,
|
||||
IRQF_ONESHOT,
|
||||
"pm8941_pwrkey", pwrkey);
|
||||
if (error) {
|
||||
dev_err(&pdev->dev, "failed requesting IRQ: %d\n", error);
|
||||
return error;
|
||||
}
|
||||
|
||||
error = input_register_device(pwrkey->input);
|
||||
if (error) {
|
||||
dev_err(&pdev->dev, "failed to register input device: %d\n",
|
||||
error);
|
||||
return error;
|
||||
}
|
||||
|
||||
pwrkey->reboot_notifier.notifier_call = pm8941_reboot_notify,
|
||||
error = register_reboot_notifier(&pwrkey->reboot_notifier);
|
||||
if (error) {
|
||||
dev_err(&pdev->dev, "failed to register reboot notifier: %d\n",
|
||||
error);
|
||||
return error;
|
||||
}
|
||||
|
||||
platform_set_drvdata(pdev, pwrkey);
|
||||
device_init_wakeup(&pdev->dev, 1);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int pm8941_pwrkey_remove(struct platform_device *pdev)
|
||||
{
|
||||
struct pm8941_pwrkey *pwrkey = platform_get_drvdata(pdev);
|
||||
|
||||
device_init_wakeup(&pdev->dev, 0);
|
||||
unregister_reboot_notifier(&pwrkey->reboot_notifier);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct of_device_id pm8941_pwr_key_id_table[] = {
|
||||
{ .compatible = "qcom,pm8941-pwrkey" },
|
||||
{ }
|
||||
};
|
||||
MODULE_DEVICE_TABLE(of, pm8941_pwr_key_id_table);
|
||||
|
||||
static struct platform_driver pm8941_pwrkey_driver = {
|
||||
.probe = pm8941_pwrkey_probe,
|
||||
.remove = pm8941_pwrkey_remove,
|
||||
.driver = {
|
||||
.name = "pm8941-pwrkey",
|
||||
.pm = &pm8941_pwr_key_pm_ops,
|
||||
.of_match_table = of_match_ptr(pm8941_pwr_key_id_table),
|
||||
},
|
||||
};
|
||||
module_platform_driver(pm8941_pwrkey_driver);
|
||||
|
||||
MODULE_DESCRIPTION("PM8941 Power Key driver");
|
||||
MODULE_LICENSE("GPL v2");
|
@ -50,7 +50,6 @@ static int pwm_beeper_event(struct input_dev *input,
|
||||
}
|
||||
|
||||
if (value == 0) {
|
||||
pwm_config(beeper->pwm, 0, 0);
|
||||
pwm_disable(beeper->pwm);
|
||||
} else {
|
||||
period = HZ_TO_NANOSECONDS(value);
|
||||
@ -169,12 +168,6 @@ static int __maybe_unused pwm_beeper_resume(struct device *dev)
|
||||
static SIMPLE_DEV_PM_OPS(pwm_beeper_pm_ops,
|
||||
pwm_beeper_suspend, pwm_beeper_resume);
|
||||
|
||||
#ifdef CONFIG_PM_SLEEP
|
||||
#define PWM_BEEPER_PM_OPS (&pwm_beeper_pm_ops)
|
||||
#else
|
||||
#define PWM_BEEPER_PM_OPS NULL
|
||||
#endif
|
||||
|
||||
#ifdef CONFIG_OF
|
||||
static const struct of_device_id pwm_beeper_match[] = {
|
||||
{ .compatible = "pwm-beeper", },
|
||||
@ -187,7 +180,7 @@ static struct platform_driver pwm_beeper_driver = {
|
||||
.remove = pwm_beeper_remove,
|
||||
.driver = {
|
||||
.name = "pwm-beeper",
|
||||
.pm = PWM_BEEPER_PM_OPS,
|
||||
.pm = &pwm_beeper_pm_ops,
|
||||
.of_match_table = of_match_ptr(pwm_beeper_match),
|
||||
},
|
||||
};
|
||||
|
@ -245,7 +245,7 @@ static int __maybe_unused regulator_haptic_resume(struct device *dev)
|
||||
static SIMPLE_DEV_PM_OPS(regulator_haptic_pm_ops,
|
||||
regulator_haptic_suspend, regulator_haptic_resume);
|
||||
|
||||
static struct of_device_id regulator_haptic_dt_match[] = {
|
||||
static const struct of_device_id regulator_haptic_dt_match[] = {
|
||||
{ .compatible = "regulator-haptic" },
|
||||
{ /* sentinel */ },
|
||||
};
|
||||
|
@ -106,7 +106,7 @@ static int tps65218_pwron_probe(struct platform_device *pdev)
|
||||
return 0;
|
||||
}
|
||||
|
||||
static struct of_device_id of_tps65218_pwr_match[] = {
|
||||
static const struct of_device_id of_tps65218_pwr_match[] = {
|
||||
{ .compatible = "ti,tps65218-pwrbutton" },
|
||||
{ },
|
||||
};
|
||||
|
@ -153,10 +153,18 @@ static const struct alps_protocol_info alps_v7_protocol_data = {
|
||||
ALPS_PROTO_V7, 0x48, 0x48, ALPS_DUALPOINT
|
||||
};
|
||||
|
||||
static const struct alps_protocol_info alps_v8_protocol_data = {
|
||||
ALPS_PROTO_V8, 0x18, 0x18, 0
|
||||
};
|
||||
|
||||
static void alps_set_abs_params_st(struct alps_data *priv,
|
||||
struct input_dev *dev1);
|
||||
static void alps_set_abs_params_mt(struct alps_data *priv,
|
||||
struct input_dev *dev1);
|
||||
static void alps_set_abs_params_v7(struct alps_data *priv,
|
||||
struct input_dev *dev1);
|
||||
static void alps_set_abs_params_ss4_v2(struct alps_data *priv,
|
||||
struct input_dev *dev1);
|
||||
|
||||
/* Packet formats are described in Documentation/input/alps.txt */
|
||||
|
||||
@ -243,6 +251,14 @@ static void alps_process_packet_v1_v2(struct psmouse *psmouse)
|
||||
return;
|
||||
}
|
||||
|
||||
/* Non interleaved V2 dualpoint has separate stick button bits */
|
||||
if (priv->proto_version == ALPS_PROTO_V2 &&
|
||||
priv->flags == (ALPS_PASS | ALPS_DUALPOINT)) {
|
||||
left |= packet[0] & 1;
|
||||
right |= packet[0] & 2;
|
||||
middle |= packet[0] & 4;
|
||||
}
|
||||
|
||||
alps_report_buttons(dev, dev2, left, right, middle);
|
||||
|
||||
/* Convert hardware tap to a reasonable Z value */
|
||||
@ -1085,6 +1101,176 @@ static void alps_process_packet_v7(struct psmouse *psmouse)
|
||||
alps_process_touchpad_packet_v7(psmouse);
|
||||
}
|
||||
|
||||
static unsigned char alps_get_pkt_id_ss4_v2(unsigned char *byte)
|
||||
{
|
||||
unsigned char pkt_id = SS4_PACKET_ID_IDLE;
|
||||
|
||||
if (byte[0] == 0x18 && byte[1] == 0x10 && byte[2] == 0x00 &&
|
||||
(byte[3] & 0x88) == 0x08 && byte[4] == 0x10 && byte[5] == 0x00) {
|
||||
pkt_id = SS4_PACKET_ID_IDLE;
|
||||
} else if (!(byte[3] & 0x10)) {
|
||||
pkt_id = SS4_PACKET_ID_ONE;
|
||||
} else if (!(byte[3] & 0x20)) {
|
||||
pkt_id = SS4_PACKET_ID_TWO;
|
||||
} else {
|
||||
pkt_id = SS4_PACKET_ID_MULTI;
|
||||
}
|
||||
|
||||
return pkt_id;
|
||||
}
|
||||
|
||||
static int alps_decode_ss4_v2(struct alps_fields *f,
|
||||
unsigned char *p, struct psmouse *psmouse)
|
||||
{
|
||||
struct alps_data *priv = psmouse->private;
|
||||
unsigned char pkt_id;
|
||||
unsigned int no_data_x, no_data_y;
|
||||
|
||||
pkt_id = alps_get_pkt_id_ss4_v2(p);
|
||||
|
||||
/* Current packet is 1Finger coordinate packet */
|
||||
switch (pkt_id) {
|
||||
case SS4_PACKET_ID_ONE:
|
||||
f->mt[0].x = SS4_1F_X_V2(p);
|
||||
f->mt[0].y = SS4_1F_Y_V2(p);
|
||||
f->pressure = ((SS4_1F_Z_V2(p)) * 2) & 0x7f;
|
||||
f->fingers = 1;
|
||||
f->first_mp = 0;
|
||||
f->is_mp = 0;
|
||||
break;
|
||||
|
||||
case SS4_PACKET_ID_TWO:
|
||||
if (priv->flags & ALPS_BUTTONPAD) {
|
||||
f->mt[0].x = SS4_BTL_MF_X_V2(p, 0);
|
||||
f->mt[0].y = SS4_BTL_MF_Y_V2(p, 0);
|
||||
f->mt[1].x = SS4_BTL_MF_X_V2(p, 1);
|
||||
f->mt[1].y = SS4_BTL_MF_Y_V2(p, 1);
|
||||
} else {
|
||||
f->mt[0].x = SS4_STD_MF_X_V2(p, 0);
|
||||
f->mt[0].y = SS4_STD_MF_Y_V2(p, 0);
|
||||
f->mt[1].x = SS4_STD_MF_X_V2(p, 1);
|
||||
f->mt[1].y = SS4_STD_MF_Y_V2(p, 1);
|
||||
}
|
||||
f->pressure = SS4_MF_Z_V2(p, 0) ? 0x30 : 0;
|
||||
|
||||
if (SS4_IS_MF_CONTINUE(p)) {
|
||||
f->first_mp = 1;
|
||||
} else {
|
||||
f->fingers = 2;
|
||||
f->first_mp = 0;
|
||||
}
|
||||
f->is_mp = 0;
|
||||
|
||||
break;
|
||||
|
||||
case SS4_PACKET_ID_MULTI:
|
||||
if (priv->flags & ALPS_BUTTONPAD) {
|
||||
f->mt[2].x = SS4_BTL_MF_X_V2(p, 0);
|
||||
f->mt[2].y = SS4_BTL_MF_Y_V2(p, 0);
|
||||
f->mt[3].x = SS4_BTL_MF_X_V2(p, 1);
|
||||
f->mt[3].y = SS4_BTL_MF_Y_V2(p, 1);
|
||||
no_data_x = SS4_MFPACKET_NO_AX_BL;
|
||||
no_data_y = SS4_MFPACKET_NO_AY_BL;
|
||||
} else {
|
||||
f->mt[2].x = SS4_STD_MF_X_V2(p, 0);
|
||||
f->mt[2].y = SS4_STD_MF_Y_V2(p, 0);
|
||||
f->mt[3].x = SS4_STD_MF_X_V2(p, 1);
|
||||
f->mt[3].y = SS4_STD_MF_Y_V2(p, 1);
|
||||
no_data_x = SS4_MFPACKET_NO_AX;
|
||||
no_data_y = SS4_MFPACKET_NO_AY;
|
||||
}
|
||||
|
||||
f->first_mp = 0;
|
||||
f->is_mp = 1;
|
||||
|
||||
if (SS4_IS_5F_DETECTED(p)) {
|
||||
f->fingers = 5;
|
||||
} else if (f->mt[3].x == no_data_x &&
|
||||
f->mt[3].y == no_data_y) {
|
||||
f->mt[3].x = 0;
|
||||
f->mt[3].y = 0;
|
||||
f->fingers = 3;
|
||||
} else {
|
||||
f->fingers = 4;
|
||||
}
|
||||
break;
|
||||
|
||||
case SS4_PACKET_ID_IDLE:
|
||||
default:
|
||||
memset(f, 0, sizeof(struct alps_fields));
|
||||
break;
|
||||
}
|
||||
|
||||
f->left = !!(SS4_BTN_V2(p) & 0x01);
|
||||
if (!(priv->flags & ALPS_BUTTONPAD)) {
|
||||
f->right = !!(SS4_BTN_V2(p) & 0x02);
|
||||
f->middle = !!(SS4_BTN_V2(p) & 0x04);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void alps_process_packet_ss4_v2(struct psmouse *psmouse)
|
||||
{
|
||||
struct alps_data *priv = psmouse->private;
|
||||
unsigned char *packet = psmouse->packet;
|
||||
struct input_dev *dev = psmouse->dev;
|
||||
struct alps_fields *f = &priv->f;
|
||||
|
||||
memset(f, 0, sizeof(struct alps_fields));
|
||||
priv->decode_fields(f, packet, psmouse);
|
||||
if (priv->multi_packet) {
|
||||
/*
|
||||
* Sometimes the first packet will indicate a multi-packet
|
||||
* sequence, but sometimes the next multi-packet would not
|
||||
* come. Check for this, and when it happens process the
|
||||
* position packet as usual.
|
||||
*/
|
||||
if (f->is_mp) {
|
||||
/* Now process the 1st packet */
|
||||
priv->decode_fields(f, priv->multi_data, psmouse);
|
||||
} else {
|
||||
priv->multi_packet = 0;
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* "f.is_mp" would always be '0' after merging the 1st and 2nd packet.
|
||||
* When it is set, it means 2nd packet comes without 1st packet come.
|
||||
*/
|
||||
if (f->is_mp)
|
||||
return;
|
||||
|
||||
/* Save the first packet */
|
||||
if (!priv->multi_packet && f->first_mp) {
|
||||
priv->multi_packet = 1;
|
||||
memcpy(priv->multi_data, packet, sizeof(priv->multi_data));
|
||||
return;
|
||||
}
|
||||
|
||||
priv->multi_packet = 0;
|
||||
|
||||
alps_report_mt_data(psmouse, (f->fingers <= 4) ? f->fingers : 4);
|
||||
|
||||
input_mt_report_finger_count(dev, f->fingers);
|
||||
|
||||
input_report_key(dev, BTN_LEFT, f->left);
|
||||
input_report_key(dev, BTN_RIGHT, f->right);
|
||||
input_report_key(dev, BTN_MIDDLE, f->middle);
|
||||
|
||||
input_report_abs(dev, ABS_PRESSURE, f->pressure);
|
||||
input_sync(dev);
|
||||
}
|
||||
|
||||
static bool alps_is_valid_package_ss4_v2(struct psmouse *psmouse)
|
||||
{
|
||||
if (psmouse->pktcnt == 4 && ((psmouse->packet[3] & 0x08) != 0x08))
|
||||
return false;
|
||||
if (psmouse->pktcnt == 6 && ((psmouse->packet[5] & 0x10) != 0x0))
|
||||
return false;
|
||||
return true;
|
||||
}
|
||||
|
||||
static DEFINE_MUTEX(alps_mutex);
|
||||
|
||||
static void alps_register_bare_ps2_mouse(struct work_struct *work)
|
||||
@ -1159,13 +1345,14 @@ static void alps_report_bare_ps2_packet(struct psmouse *psmouse,
|
||||
bool report_buttons)
|
||||
{
|
||||
struct alps_data *priv = psmouse->private;
|
||||
struct input_dev *dev;
|
||||
struct input_dev *dev, *dev2 = NULL;
|
||||
|
||||
/* Figure out which device to use to report the bare packet */
|
||||
if (priv->proto_version == ALPS_PROTO_V2 &&
|
||||
(priv->flags & ALPS_DUALPOINT)) {
|
||||
/* On V2 devices the DualPoint Stick reports bare packets */
|
||||
dev = priv->dev2;
|
||||
dev2 = psmouse->dev;
|
||||
} else if (unlikely(IS_ERR_OR_NULL(priv->dev3))) {
|
||||
/* Register dev3 mouse if we received PS/2 packet first time */
|
||||
if (!IS_ERR(priv->dev3))
|
||||
@ -1177,7 +1364,7 @@ static void alps_report_bare_ps2_packet(struct psmouse *psmouse,
|
||||
}
|
||||
|
||||
if (report_buttons)
|
||||
alps_report_buttons(dev, NULL,
|
||||
alps_report_buttons(dev, dev2,
|
||||
packet[0] & 1, packet[0] & 2, packet[0] & 4);
|
||||
|
||||
input_report_rel(dev, REL_X,
|
||||
@ -1305,8 +1492,12 @@ static psmouse_ret_t alps_process_byte(struct psmouse *psmouse)
|
||||
* a device connected to the external PS/2 port. Because bare PS/2
|
||||
* protocol does not have enough constant bits to self-synchronize
|
||||
* properly we only do this if the device is fully synchronized.
|
||||
* Can not distinguish V8's first byte from PS/2 packet's
|
||||
*/
|
||||
if (!psmouse->out_of_sync_cnt && (psmouse->packet[0] & 0xc8) == 0x08) {
|
||||
if (priv->proto_version != ALPS_PROTO_V8 &&
|
||||
!psmouse->out_of_sync_cnt &&
|
||||
(psmouse->packet[0] & 0xc8) == 0x08) {
|
||||
|
||||
if (psmouse->pktcnt == 3) {
|
||||
alps_report_bare_ps2_packet(psmouse, psmouse->packet,
|
||||
true);
|
||||
@ -1354,8 +1545,10 @@ static psmouse_ret_t alps_process_byte(struct psmouse *psmouse)
|
||||
return PSMOUSE_BAD_DATA;
|
||||
}
|
||||
|
||||
if (priv->proto_version == ALPS_PROTO_V7 &&
|
||||
!alps_is_valid_package_v7(psmouse)) {
|
||||
if ((priv->proto_version == ALPS_PROTO_V7 &&
|
||||
!alps_is_valid_package_v7(psmouse)) ||
|
||||
(priv->proto_version == ALPS_PROTO_V8 &&
|
||||
!alps_is_valid_package_ss4_v2(psmouse))) {
|
||||
psmouse_dbg(psmouse, "refusing packet[%i] = %x\n",
|
||||
psmouse->pktcnt - 1,
|
||||
psmouse->packet[psmouse->pktcnt - 1]);
|
||||
@ -2130,6 +2323,88 @@ error:
|
||||
return -1;
|
||||
}
|
||||
|
||||
static int alps_get_otp_values_ss4_v2(struct psmouse *psmouse,
|
||||
unsigned char index, unsigned char otp[])
|
||||
{
|
||||
struct ps2dev *ps2dev = &psmouse->ps2dev;
|
||||
|
||||
switch (index) {
|
||||
case 0:
|
||||
if (ps2_command(ps2dev, NULL, PSMOUSE_CMD_SETSTREAM) ||
|
||||
ps2_command(ps2dev, NULL, PSMOUSE_CMD_SETSTREAM) ||
|
||||
ps2_command(ps2dev, otp, PSMOUSE_CMD_GETINFO))
|
||||
return -1;
|
||||
|
||||
break;
|
||||
|
||||
case 1:
|
||||
if (ps2_command(ps2dev, NULL, PSMOUSE_CMD_SETPOLL) ||
|
||||
ps2_command(ps2dev, NULL, PSMOUSE_CMD_SETPOLL) ||
|
||||
ps2_command(ps2dev, otp, PSMOUSE_CMD_GETINFO))
|
||||
return -1;
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int alps_update_device_area_ss4_v2(unsigned char otp[][4],
|
||||
struct alps_data *priv)
|
||||
{
|
||||
int num_x_electrode;
|
||||
int num_y_electrode;
|
||||
int x_pitch, y_pitch, x_phys, y_phys;
|
||||
|
||||
num_x_electrode = SS4_NUMSENSOR_XOFFSET + (otp[1][0] & 0x0F);
|
||||
num_y_electrode = SS4_NUMSENSOR_YOFFSET + ((otp[1][0] >> 4) & 0x0F);
|
||||
|
||||
priv->x_max = (num_x_electrode - 1) * SS4_COUNT_PER_ELECTRODE;
|
||||
priv->y_max = (num_y_electrode - 1) * SS4_COUNT_PER_ELECTRODE;
|
||||
|
||||
x_pitch = ((otp[1][2] >> 2) & 0x07) + SS4_MIN_PITCH_MM;
|
||||
y_pitch = ((otp[1][2] >> 5) & 0x07) + SS4_MIN_PITCH_MM;
|
||||
|
||||
x_phys = x_pitch * (num_x_electrode - 1); /* In 0.1 mm units */
|
||||
y_phys = y_pitch * (num_y_electrode - 1); /* In 0.1 mm units */
|
||||
|
||||
priv->x_res = priv->x_max * 10 / x_phys; /* units / mm */
|
||||
priv->y_res = priv->y_max * 10 / y_phys; /* units / mm */
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int alps_update_btn_info_ss4_v2(unsigned char otp[][4],
|
||||
struct alps_data *priv)
|
||||
{
|
||||
unsigned char is_btnless;
|
||||
|
||||
is_btnless = (otp[1][1] >> 3) & 0x01;
|
||||
|
||||
if (is_btnless)
|
||||
priv->flags |= ALPS_BUTTONPAD;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int alps_set_defaults_ss4_v2(struct psmouse *psmouse,
|
||||
struct alps_data *priv)
|
||||
{
|
||||
unsigned char otp[2][4];
|
||||
|
||||
memset(otp, 0, sizeof(otp));
|
||||
|
||||
if (alps_get_otp_values_ss4_v2(psmouse, 0, &otp[0][0]) ||
|
||||
alps_get_otp_values_ss4_v2(psmouse, 1, &otp[1][0]))
|
||||
return -1;
|
||||
|
||||
alps_update_device_area_ss4_v2(otp, priv);
|
||||
|
||||
alps_update_btn_info_ss4_v2(otp, priv);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int alps_dolphin_get_device_area(struct psmouse *psmouse,
|
||||
struct alps_data *priv)
|
||||
{
|
||||
@ -2222,6 +2497,35 @@ error:
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int alps_hw_init_ss4_v2(struct psmouse *psmouse)
|
||||
{
|
||||
struct ps2dev *ps2dev = &psmouse->ps2dev;
|
||||
char param[2] = {0x64, 0x28};
|
||||
int ret = -1;
|
||||
|
||||
/* enter absolute mode */
|
||||
if (ps2_command(ps2dev, NULL, PSMOUSE_CMD_SETSTREAM) ||
|
||||
ps2_command(ps2dev, NULL, PSMOUSE_CMD_SETSTREAM) ||
|
||||
ps2_command(ps2dev, ¶m[0], PSMOUSE_CMD_SETRATE) ||
|
||||
ps2_command(ps2dev, ¶m[1], PSMOUSE_CMD_SETRATE)) {
|
||||
goto error;
|
||||
}
|
||||
|
||||
/* T.B.D. Decread noise packet number, delete in the future */
|
||||
if (alps_exit_command_mode(psmouse) ||
|
||||
alps_enter_command_mode(psmouse) ||
|
||||
alps_command_mode_write_reg(psmouse, 0x001D, 0x20)) {
|
||||
goto error;
|
||||
}
|
||||
alps_exit_command_mode(psmouse);
|
||||
|
||||
return ps2_command(ps2dev, NULL, PSMOUSE_CMD_ENABLE);
|
||||
|
||||
error:
|
||||
alps_exit_command_mode(psmouse);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int alps_set_protocol(struct psmouse *psmouse,
|
||||
struct alps_data *priv,
|
||||
const struct alps_protocol_info *protocol)
|
||||
@ -2311,7 +2615,7 @@ static int alps_set_protocol(struct psmouse *psmouse,
|
||||
priv->hw_init = alps_hw_init_v7;
|
||||
priv->process_packet = alps_process_packet_v7;
|
||||
priv->decode_fields = alps_decode_packet_v7;
|
||||
priv->set_abs_params = alps_set_abs_params_mt;
|
||||
priv->set_abs_params = alps_set_abs_params_v7;
|
||||
priv->nibble_commands = alps_v3_nibble_commands;
|
||||
priv->addr_command = PSMOUSE_CMD_RESET_WRAP;
|
||||
priv->x_max = 0xfff;
|
||||
@ -2321,6 +2625,19 @@ static int alps_set_protocol(struct psmouse *psmouse,
|
||||
priv->flags |= ALPS_BUTTONPAD;
|
||||
|
||||
break;
|
||||
|
||||
case ALPS_PROTO_V8:
|
||||
priv->hw_init = alps_hw_init_ss4_v2;
|
||||
priv->process_packet = alps_process_packet_ss4_v2;
|
||||
priv->decode_fields = alps_decode_ss4_v2;
|
||||
priv->set_abs_params = alps_set_abs_params_ss4_v2;
|
||||
priv->nibble_commands = alps_v3_nibble_commands;
|
||||
priv->addr_command = PSMOUSE_CMD_RESET_WRAP;
|
||||
|
||||
if (alps_set_defaults_ss4_v2(psmouse, priv))
|
||||
return -EIO;
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
return 0;
|
||||
@ -2389,6 +2706,9 @@ static int alps_identify(struct psmouse *psmouse, struct alps_data *priv)
|
||||
} else if (ec[0] == 0x88 && ec[1] == 0x07 &&
|
||||
ec[2] >= 0x90 && ec[2] <= 0x9d) {
|
||||
protocol = &alps_v3_protocol_data;
|
||||
} else if (e7[0] == 0x73 && e7[1] == 0x03 &&
|
||||
e7[2] == 0x14 && ec[1] == 0x02) {
|
||||
protocol = &alps_v8_protocol_data;
|
||||
} else {
|
||||
psmouse_dbg(psmouse,
|
||||
"Likely not an ALPS touchpad: E7=%3ph, EC=%3ph\n", e7, ec);
|
||||
@ -2437,10 +2757,11 @@ static void alps_set_abs_params_st(struct alps_data *priv,
|
||||
{
|
||||
input_set_abs_params(dev1, ABS_X, 0, priv->x_max, 0, 0);
|
||||
input_set_abs_params(dev1, ABS_Y, 0, priv->y_max, 0, 0);
|
||||
input_set_abs_params(dev1, ABS_PRESSURE, 0, 127, 0, 0);
|
||||
}
|
||||
|
||||
static void alps_set_abs_params_mt(struct alps_data *priv,
|
||||
struct input_dev *dev1)
|
||||
static void alps_set_abs_params_mt_common(struct alps_data *priv,
|
||||
struct input_dev *dev1)
|
||||
{
|
||||
input_set_abs_params(dev1, ABS_MT_POSITION_X, 0, priv->x_max, 0, 0);
|
||||
input_set_abs_params(dev1, ABS_MT_POSITION_Y, 0, priv->y_max, 0, 0);
|
||||
@ -2448,15 +2769,44 @@ static void alps_set_abs_params_mt(struct alps_data *priv,
|
||||
input_abs_set_res(dev1, ABS_MT_POSITION_X, priv->x_res);
|
||||
input_abs_set_res(dev1, ABS_MT_POSITION_Y, priv->y_res);
|
||||
|
||||
input_mt_init_slots(dev1, MAX_TOUCHES, INPUT_MT_POINTER |
|
||||
INPUT_MT_DROP_UNUSED | INPUT_MT_TRACK | INPUT_MT_SEMI_MT);
|
||||
|
||||
set_bit(BTN_TOOL_TRIPLETAP, dev1->keybit);
|
||||
set_bit(BTN_TOOL_QUADTAP, dev1->keybit);
|
||||
}
|
||||
|
||||
/* V7 is real multi-touch */
|
||||
if (priv->proto_version == ALPS_PROTO_V7)
|
||||
clear_bit(INPUT_PROP_SEMI_MT, dev1->propbit);
|
||||
static void alps_set_abs_params_mt(struct alps_data *priv,
|
||||
struct input_dev *dev1)
|
||||
{
|
||||
alps_set_abs_params_mt_common(priv, dev1);
|
||||
input_set_abs_params(dev1, ABS_PRESSURE, 0, 127, 0, 0);
|
||||
|
||||
input_mt_init_slots(dev1, MAX_TOUCHES,
|
||||
INPUT_MT_POINTER | INPUT_MT_DROP_UNUSED |
|
||||
INPUT_MT_TRACK | INPUT_MT_SEMI_MT);
|
||||
}
|
||||
|
||||
static void alps_set_abs_params_v7(struct alps_data *priv,
|
||||
struct input_dev *dev1)
|
||||
{
|
||||
alps_set_abs_params_mt_common(priv, dev1);
|
||||
set_bit(BTN_TOOL_QUINTTAP, dev1->keybit);
|
||||
|
||||
input_mt_init_slots(dev1, MAX_TOUCHES,
|
||||
INPUT_MT_POINTER | INPUT_MT_DROP_UNUSED |
|
||||
INPUT_MT_TRACK);
|
||||
|
||||
set_bit(BTN_TOOL_QUINTTAP, dev1->keybit);
|
||||
}
|
||||
|
||||
static void alps_set_abs_params_ss4_v2(struct alps_data *priv,
|
||||
struct input_dev *dev1)
|
||||
{
|
||||
alps_set_abs_params_mt_common(priv, dev1);
|
||||
input_set_abs_params(dev1, ABS_PRESSURE, 0, 127, 0, 0);
|
||||
set_bit(BTN_TOOL_QUINTTAP, dev1->keybit);
|
||||
|
||||
input_mt_init_slots(dev1, MAX_TOUCHES,
|
||||
INPUT_MT_POINTER | INPUT_MT_DROP_UNUSED |
|
||||
INPUT_MT_TRACK);
|
||||
}
|
||||
|
||||
int alps_init(struct psmouse *psmouse)
|
||||
@ -2489,9 +2839,6 @@ int alps_init(struct psmouse *psmouse)
|
||||
dev1->evbit[BIT_WORD(EV_ABS)] |= BIT_MASK(EV_ABS);
|
||||
|
||||
priv->set_abs_params(priv, dev1);
|
||||
/* No pressure on V7 */
|
||||
if (priv->proto_version != ALPS_PROTO_V7)
|
||||
input_set_abs_params(dev1, ABS_PRESSURE, 0, 127, 0, 0);
|
||||
|
||||
if (priv->flags & ALPS_WHEEL) {
|
||||
dev1->evbit[BIT_WORD(EV_REL)] |= BIT_MASK(EV_REL);
|
||||
|
@ -22,13 +22,89 @@
|
||||
#define ALPS_PROTO_V5 0x500
|
||||
#define ALPS_PROTO_V6 0x600
|
||||
#define ALPS_PROTO_V7 0x700 /* t3btl t4s */
|
||||
#define ALPS_PROTO_V8 0x800 /* SS4btl SS4s */
|
||||
|
||||
#define MAX_TOUCHES 2
|
||||
#define MAX_TOUCHES 4
|
||||
|
||||
#define DOLPHIN_COUNT_PER_ELECTRODE 64
|
||||
#define DOLPHIN_PROFILE_XOFFSET 8 /* x-electrode offset */
|
||||
#define DOLPHIN_PROFILE_YOFFSET 1 /* y-electrode offset */
|
||||
|
||||
/*
|
||||
* enum SS4_PACKET_ID - defines the packet type for V8
|
||||
* SS4_PACKET_ID_IDLE: There's no finger and no button activity.
|
||||
* SS4_PACKET_ID_ONE: There's one finger on touchpad
|
||||
* or there's button activities.
|
||||
* SS4_PACKET_ID_TWO: There's two or more fingers on touchpad
|
||||
* SS4_PACKET_ID_MULTI: There's three or more fingers on touchpad
|
||||
*/
|
||||
enum SS4_PACKET_ID {
|
||||
SS4_PACKET_ID_IDLE = 0,
|
||||
SS4_PACKET_ID_ONE,
|
||||
SS4_PACKET_ID_TWO,
|
||||
SS4_PACKET_ID_MULTI,
|
||||
};
|
||||
|
||||
#define SS4_COUNT_PER_ELECTRODE 256
|
||||
#define SS4_NUMSENSOR_XOFFSET 7
|
||||
#define SS4_NUMSENSOR_YOFFSET 7
|
||||
#define SS4_MIN_PITCH_MM 50
|
||||
|
||||
#define SS4_MASK_NORMAL_BUTTONS 0x07
|
||||
|
||||
#define SS4_1F_X_V2(_b) ((_b[0] & 0x0007) | \
|
||||
((_b[1] << 3) & 0x0078) | \
|
||||
((_b[1] << 2) & 0x0380) | \
|
||||
((_b[2] << 5) & 0x1C00) \
|
||||
)
|
||||
|
||||
#define SS4_1F_Y_V2(_b) (((_b[2]) & 0x000F) | \
|
||||
((_b[3] >> 2) & 0x0030) | \
|
||||
((_b[4] << 6) & 0x03C0) | \
|
||||
((_b[4] << 5) & 0x0C00) \
|
||||
)
|
||||
|
||||
#define SS4_1F_Z_V2(_b) (((_b[5]) & 0x0F) | \
|
||||
((_b[5] >> 1) & 0x70) | \
|
||||
((_b[4]) & 0x80) \
|
||||
)
|
||||
|
||||
#define SS4_1F_LFB_V2(_b) (((_b[2] >> 4) & 0x01) == 0x01)
|
||||
|
||||
#define SS4_MF_LF_V2(_b, _i) ((_b[1 + (_i) * 3] & 0x0004) == 0x0004)
|
||||
|
||||
#define SS4_BTN_V2(_b) ((_b[0] >> 5) & SS4_MASK_NORMAL_BUTTONS)
|
||||
|
||||
#define SS4_STD_MF_X_V2(_b, _i) (((_b[0 + (_i) * 3] << 5) & 0x00E0) | \
|
||||
((_b[1 + _i * 3] << 5) & 0x1F00) \
|
||||
)
|
||||
|
||||
#define SS4_STD_MF_Y_V2(_b, _i) (((_b[1 + (_i) * 3] << 3) & 0x0010) | \
|
||||
((_b[2 + (_i) * 3] << 5) & 0x01E0) | \
|
||||
((_b[2 + (_i) * 3] << 4) & 0x0E00) \
|
||||
)
|
||||
|
||||
#define SS4_BTL_MF_X_V2(_b, _i) (SS4_STD_MF_X_V2(_b, _i) | \
|
||||
((_b[0 + (_i) * 3] >> 3) & 0x0010) \
|
||||
)
|
||||
|
||||
#define SS4_BTL_MF_Y_V2(_b, _i) (SS4_STD_MF_Y_V2(_b, _i) | \
|
||||
((_b[0 + (_i) * 3] >> 3) & 0x0008) \
|
||||
)
|
||||
|
||||
#define SS4_MF_Z_V2(_b, _i) (((_b[1 + (_i) * 3]) & 0x0001) | \
|
||||
((_b[1 + (_i) * 3] >> 1) & 0x0002) \
|
||||
)
|
||||
|
||||
#define SS4_IS_MF_CONTINUE(_b) ((_b[2] & 0x10) == 0x10)
|
||||
#define SS4_IS_5F_DETECTED(_b) ((_b[2] & 0x10) == 0x10)
|
||||
|
||||
|
||||
#define SS4_MFPACKET_NO_AX 8160 /* X-Coordinate value */
|
||||
#define SS4_MFPACKET_NO_AY 4080 /* Y-Coordinate value */
|
||||
#define SS4_MFPACKET_NO_AX_BL 8176 /* Buttonless X-Coordinate value */
|
||||
#define SS4_MFPACKET_NO_AY_BL 4088 /* Buttonless Y-Coordinate value */
|
||||
|
||||
/*
|
||||
* enum V7_PACKET_ID - defines the packet type for V7
|
||||
* V7_PACKET_ID_IDLE: There's no finger and no button activity.
|
||||
|
@ -17,7 +17,7 @@
|
||||
*/
|
||||
|
||||
#ifndef _ELAN_I2C_H
|
||||
#define _ELAN_i2C_H
|
||||
#define _ELAN_I2C_H
|
||||
|
||||
#include <linux/types.h>
|
||||
|
||||
|
@ -99,7 +99,7 @@ static int elan_enable_power(struct elan_tp_data *data)
|
||||
error = regulator_enable(data->vcc);
|
||||
if (error) {
|
||||
dev_err(&data->client->dev,
|
||||
"Failed to enable regulator: %d\n", error);
|
||||
"failed to enable regulator: %d\n", error);
|
||||
return error;
|
||||
}
|
||||
|
||||
@ -111,6 +111,7 @@ static int elan_enable_power(struct elan_tp_data *data)
|
||||
msleep(30);
|
||||
} while (--repeat > 0);
|
||||
|
||||
dev_err(&data->client->dev, "failed to enable power: %d\n", error);
|
||||
return error;
|
||||
}
|
||||
|
||||
@ -125,7 +126,7 @@ static int elan_disable_power(struct elan_tp_data *data)
|
||||
error = regulator_disable(data->vcc);
|
||||
if (error) {
|
||||
dev_err(&data->client->dev,
|
||||
"Failed to disable regulator: %d\n",
|
||||
"failed to disable regulator: %d\n",
|
||||
error);
|
||||
/* Attempt to power the chip back up */
|
||||
data->ops->power_control(data->client, true);
|
||||
@ -138,6 +139,7 @@ static int elan_disable_power(struct elan_tp_data *data)
|
||||
msleep(30);
|
||||
} while (--repeat > 0);
|
||||
|
||||
dev_err(&data->client->dev, "failed to disable power: %d\n", error);
|
||||
return error;
|
||||
}
|
||||
|
||||
@ -196,7 +198,6 @@ static int elan_initialize(struct elan_tp_data *data)
|
||||
if (!error)
|
||||
return 0;
|
||||
|
||||
repeat--;
|
||||
msleep(30);
|
||||
} while (--repeat > 0);
|
||||
|
||||
@ -1084,16 +1085,18 @@ static int __maybe_unused elan_resume(struct device *dev)
|
||||
}
|
||||
|
||||
error = elan_enable_power(data);
|
||||
if (error)
|
||||
if (error) {
|
||||
dev_err(dev, "power up when resuming failed: %d\n", error);
|
||||
goto err;
|
||||
}
|
||||
|
||||
error = elan_initialize(data);
|
||||
if (error)
|
||||
dev_err(dev, "initialize when resuming failed: %d\n", error);
|
||||
|
||||
err:
|
||||
enable_irq(data->client->irq);
|
||||
|
||||
return 0;
|
||||
return error;
|
||||
}
|
||||
|
||||
static SIMPLE_DEV_PM_OPS(elan_pm_ops, elan_suspend, elan_resume);
|
||||
|
@ -117,7 +117,15 @@ static int elan_i2c_write_cmd(struct i2c_client *client, u16 reg, u16 cmd)
|
||||
int ret;
|
||||
|
||||
ret = i2c_transfer(client->adapter, &msg, 1);
|
||||
return ret == 1 ? 0 : (ret < 0 ? ret : -EIO);
|
||||
if (ret != 1) {
|
||||
if (ret >= 0)
|
||||
ret = -EIO;
|
||||
dev_err(&client->dev, "writing cmd (0x%04x) failed: %d\n",
|
||||
reg, ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int elan_i2c_initialize(struct i2c_client *client)
|
||||
|
@ -892,6 +892,21 @@ static psmouse_ret_t elantech_process_byte(struct psmouse *psmouse)
|
||||
return PSMOUSE_FULL_PACKET;
|
||||
}
|
||||
|
||||
/*
|
||||
* This writes the reg_07 value again to the hardware at the end of every
|
||||
* set_rate call because the register loses its value. reg_07 allows setting
|
||||
* absolute mode on v4 hardware
|
||||
*/
|
||||
static void elantech_set_rate_restore_reg_07(struct psmouse *psmouse,
|
||||
unsigned int rate)
|
||||
{
|
||||
struct elantech_data *etd = psmouse->private;
|
||||
|
||||
etd->original_set_rate(psmouse, rate);
|
||||
if (elantech_write_reg(psmouse, 0x07, etd->reg_07))
|
||||
psmouse_err(psmouse, "restoring reg_07 failed\n");
|
||||
}
|
||||
|
||||
/*
|
||||
* Put the touchpad into absolute mode
|
||||
*/
|
||||
@ -1094,6 +1109,8 @@ static int elantech_get_resolution_v4(struct psmouse *psmouse,
|
||||
* Asus K53SV 0x450f01 78, 15, 0c 2 hw buttons
|
||||
* Asus G46VW 0x460f02 00, 18, 0c 2 hw buttons
|
||||
* Asus G750JX 0x360f00 00, 16, 0c 2 hw buttons
|
||||
* Asus TP500LN 0x381f17 10, 14, 0e clickpad
|
||||
* Asus X750JN 0x381f17 10, 14, 0e clickpad
|
||||
* Asus UX31 0x361f00 20, 15, 0e clickpad
|
||||
* Asus UX32VD 0x361f02 00, 15, 0e clickpad
|
||||
* Avatar AVIU-145A2 0x361f00 ? clickpad
|
||||
@ -1635,6 +1652,11 @@ int elantech_init(struct psmouse *psmouse)
|
||||
goto init_fail;
|
||||
}
|
||||
|
||||
if (etd->fw_version == 0x381f17) {
|
||||
etd->original_set_rate = psmouse->set_rate;
|
||||
psmouse->set_rate = elantech_set_rate_restore_reg_07;
|
||||
}
|
||||
|
||||
if (elantech_set_input_params(psmouse)) {
|
||||
psmouse_err(psmouse, "failed to query touchpad range.\n");
|
||||
goto init_fail;
|
||||
|
@ -142,6 +142,7 @@ struct elantech_data {
|
||||
struct finger_pos mt[ETP_MAX_FINGERS];
|
||||
unsigned char parity[256];
|
||||
int (*send_cmd)(struct psmouse *psmouse, unsigned char c, unsigned char *param);
|
||||
void (*original_set_rate)(struct psmouse *psmouse, unsigned int rate);
|
||||
};
|
||||
|
||||
#ifdef CONFIG_MOUSE_PS2_ELANTECH
|
||||
|
@ -256,8 +256,8 @@ static void lifebook_disconnect(struct psmouse *psmouse)
|
||||
|
||||
int lifebook_detect(struct psmouse *psmouse, bool set_properties)
|
||||
{
|
||||
if (!lifebook_present)
|
||||
return -1;
|
||||
if (!lifebook_present)
|
||||
return -1;
|
||||
|
||||
if (desired_serio_phys &&
|
||||
strcmp(psmouse->ps2dev.serio->phys, desired_serio_phys))
|
||||
@ -268,7 +268,7 @@ int lifebook_detect(struct psmouse *psmouse, bool set_properties)
|
||||
psmouse->name = "Lifebook TouchScreen";
|
||||
}
|
||||
|
||||
return 0;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int lifebook_create_relative_device(struct psmouse *psmouse)
|
||||
|
@ -474,19 +474,45 @@ static int psmouse_poll(struct psmouse *psmouse)
|
||||
PSMOUSE_CMD_POLL | (psmouse->pktsize << 8));
|
||||
}
|
||||
|
||||
static bool psmouse_check_pnp_id(const char *id, const char * const ids[])
|
||||
{
|
||||
int i;
|
||||
|
||||
for (i = 0; ids[i]; i++)
|
||||
if (!strcasecmp(id, ids[i]))
|
||||
return true;
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/*
|
||||
* psmouse_matches_pnp_id - check if psmouse matches one of the passed in ids.
|
||||
*/
|
||||
bool psmouse_matches_pnp_id(struct psmouse *psmouse, const char * const ids[])
|
||||
{
|
||||
int i;
|
||||
struct serio *serio = psmouse->ps2dev.serio;
|
||||
char *p, *fw_id_copy, *save_ptr;
|
||||
bool found = false;
|
||||
|
||||
if (!strncmp(psmouse->ps2dev.serio->firmware_id, "PNP:", 4))
|
||||
for (i = 0; ids[i]; i++)
|
||||
if (strstr(psmouse->ps2dev.serio->firmware_id, ids[i]))
|
||||
return true;
|
||||
if (strncmp(serio->firmware_id, "PNP: ", 5))
|
||||
return false;
|
||||
|
||||
return false;
|
||||
fw_id_copy = kstrndup(&serio->firmware_id[5],
|
||||
sizeof(serio->firmware_id) - 5,
|
||||
GFP_KERNEL);
|
||||
if (!fw_id_copy)
|
||||
return false;
|
||||
|
||||
save_ptr = fw_id_copy;
|
||||
while ((p = strsep(&fw_id_copy, " ")) != NULL) {
|
||||
if (psmouse_check_pnp_id(p, ids)) {
|
||||
found = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
kfree(save_ptr);
|
||||
return found;
|
||||
}
|
||||
|
||||
/*
|
||||
|
@ -67,6 +67,9 @@
|
||||
#define X_MAX_POSITIVE 8176
|
||||
#define Y_MAX_POSITIVE 8176
|
||||
|
||||
/* maximum ABS_MT_POSITION displacement (in mm) */
|
||||
#define DMAX 10
|
||||
|
||||
/*****************************************************************************
|
||||
* Stuff we need even when we do not want native Synaptics support
|
||||
****************************************************************************/
|
||||
@ -203,6 +206,13 @@ static const char * const topbuttonpad_pnp_ids[] = {
|
||||
NULL
|
||||
};
|
||||
|
||||
/* This list has been kindly provided by Synaptics. */
|
||||
static const char * const forcepad_pnp_ids[] = {
|
||||
"SYN300D",
|
||||
"SYN3014",
|
||||
NULL
|
||||
};
|
||||
|
||||
/*****************************************************************************
|
||||
* Synaptics communications functions
|
||||
****************************************************************************/
|
||||
@ -687,8 +697,6 @@ static void synaptics_parse_ext_buttons(const unsigned char buf[],
|
||||
hw->ext_buttons |= (buf[5] & ext_mask) << ext_bits;
|
||||
}
|
||||
|
||||
static bool is_forcepad;
|
||||
|
||||
static int synaptics_parse_hw_state(const unsigned char buf[],
|
||||
struct synaptics_data *priv,
|
||||
struct synaptics_hw_state *hw)
|
||||
@ -718,7 +726,7 @@ static int synaptics_parse_hw_state(const unsigned char buf[],
|
||||
hw->left = (buf[0] & 0x01) ? 1 : 0;
|
||||
hw->right = (buf[0] & 0x02) ? 1 : 0;
|
||||
|
||||
if (is_forcepad) {
|
||||
if (priv->is_forcepad) {
|
||||
/*
|
||||
* ForcePads, like Clickpads, use middle button
|
||||
* bits to report primary button clicks.
|
||||
@ -917,7 +925,7 @@ static void synaptics_report_mt_data(struct psmouse *psmouse,
|
||||
pos[i].y = synaptics_invert_y(hw[i]->y);
|
||||
}
|
||||
|
||||
input_mt_assign_slots(dev, slot, pos, nsemi, 0);
|
||||
input_mt_assign_slots(dev, slot, pos, nsemi, DMAX * priv->x_res);
|
||||
|
||||
for (i = 0; i < nsemi; i++) {
|
||||
input_mt_slot(dev, slot[i]);
|
||||
@ -1186,7 +1194,7 @@ static void set_input_params(struct psmouse *psmouse,
|
||||
ABS_MT_POSITION_Y);
|
||||
/* Image sensors can report per-contact pressure */
|
||||
input_set_abs_params(dev, ABS_MT_PRESSURE, 0, 255, 0, 0);
|
||||
input_mt_init_slots(dev, 2, INPUT_MT_POINTER | INPUT_MT_TRACK);
|
||||
input_mt_init_slots(dev, 3, INPUT_MT_POINTER | INPUT_MT_TRACK);
|
||||
|
||||
/* Image sensors can signal 4 and 5 finger clicks */
|
||||
__set_bit(BTN_TOOL_QUADTAP, dev->keybit);
|
||||
@ -1418,29 +1426,11 @@ static const struct dmi_system_id __initconst cr48_dmi_table[] = {
|
||||
{ }
|
||||
};
|
||||
|
||||
static const struct dmi_system_id forcepad_dmi_table[] __initconst = {
|
||||
#if defined(CONFIG_DMI) && defined(CONFIG_X86)
|
||||
{
|
||||
.matches = {
|
||||
DMI_MATCH(DMI_SYS_VENDOR, "Hewlett-Packard"),
|
||||
DMI_MATCH(DMI_PRODUCT_NAME, "HP EliteBook Folio 1040 G1"),
|
||||
},
|
||||
},
|
||||
#endif
|
||||
{ }
|
||||
};
|
||||
|
||||
void __init synaptics_module_init(void)
|
||||
{
|
||||
impaired_toshiba_kbc = dmi_check_system(toshiba_dmi_table);
|
||||
broken_olpc_ec = dmi_check_system(olpc_dmi_table);
|
||||
cr48_profile_sensor = dmi_check_system(cr48_dmi_table);
|
||||
|
||||
/*
|
||||
* Unfortunately ForcePad capability is not exported over PS/2,
|
||||
* so we have to resort to checking DMI.
|
||||
*/
|
||||
is_forcepad = dmi_check_system(forcepad_dmi_table);
|
||||
}
|
||||
|
||||
static int __synaptics_init(struct psmouse *psmouse, bool absolute_mode)
|
||||
@ -1475,6 +1465,12 @@ static int __synaptics_init(struct psmouse *psmouse, bool absolute_mode)
|
||||
if (SYN_ID_DISGEST_SUPPORTED(priv->identity))
|
||||
priv->disable_gesture = true;
|
||||
|
||||
/*
|
||||
* Unfortunately ForcePad capability is not exported over PS/2,
|
||||
* so we have to resort to checking PNP IDs.
|
||||
*/
|
||||
priv->is_forcepad = psmouse_matches_pnp_id(psmouse, forcepad_pnp_ids);
|
||||
|
||||
if (synaptics_set_mode(psmouse)) {
|
||||
psmouse_err(psmouse, "Unable to initialize device.\n");
|
||||
goto init_fail;
|
||||
|
@ -196,6 +196,7 @@ struct synaptics_data {
|
||||
unsigned long press_start;
|
||||
bool press;
|
||||
bool report_press;
|
||||
bool is_forcepad;
|
||||
};
|
||||
|
||||
void synaptics_module_init(void);
|
||||
|
@ -31,7 +31,6 @@
|
||||
#include <linux/spinlock.h>
|
||||
#include <linux/delay.h>
|
||||
#include <linux/ioport.h>
|
||||
#include <linux/pci_ids.h>
|
||||
|
||||
#include <asm/irq.h>
|
||||
#include <asm/io.h>
|
||||
|
@ -1162,13 +1162,32 @@ static int i8042_controller_resume(bool force_reset)
|
||||
|
||||
static int i8042_pm_suspend(struct device *dev)
|
||||
{
|
||||
int i;
|
||||
|
||||
i8042_controller_reset(true);
|
||||
|
||||
/* Set up serio interrupts for system wakeup. */
|
||||
for (i = 0; i < I8042_NUM_PORTS; i++) {
|
||||
struct serio *serio = i8042_ports[i].serio;
|
||||
|
||||
if (serio && device_may_wakeup(&serio->dev))
|
||||
enable_irq_wake(i8042_ports[i].irq);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int i8042_pm_resume(struct device *dev)
|
||||
{
|
||||
int i;
|
||||
|
||||
for (i = 0; i < I8042_NUM_PORTS; i++) {
|
||||
struct serio *serio = i8042_ports[i].serio;
|
||||
|
||||
if (serio && device_may_wakeup(&serio->dev))
|
||||
disable_irq_wake(i8042_ports[i].irq);
|
||||
}
|
||||
|
||||
/*
|
||||
* On resume from S2R we always try to reset the controller
|
||||
* to bring it in a sane state. (In case of S2D we expect
|
||||
@ -1300,13 +1319,16 @@ static void __init i8042_register_ports(void)
|
||||
int i;
|
||||
|
||||
for (i = 0; i < I8042_NUM_PORTS; i++) {
|
||||
if (i8042_ports[i].serio) {
|
||||
struct serio *serio = i8042_ports[i].serio;
|
||||
|
||||
if (serio) {
|
||||
printk(KERN_INFO "serio: %s at %#lx,%#lx irq %d\n",
|
||||
i8042_ports[i].serio->name,
|
||||
serio->name,
|
||||
(unsigned long) I8042_DATA_REG,
|
||||
(unsigned long) I8042_COMMAND_REG,
|
||||
i8042_ports[i].irq);
|
||||
serio_register_port(i8042_ports[i].serio);
|
||||
serio_register_port(serio);
|
||||
device_set_wakeup_capable(&serio->dev, true);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -140,6 +140,19 @@ config TOUCHSCREEN_BU21013
|
||||
To compile this driver as a module, choose M here: the
|
||||
module will be called bu21013_ts.
|
||||
|
||||
config TOUCHSCREEN_CHIPONE_ICN8318
|
||||
tristate "chipone icn8318 touchscreen controller"
|
||||
depends on GPIOLIB
|
||||
depends on I2C
|
||||
depends on OF
|
||||
help
|
||||
Say Y here if you have a ChipOne icn8318 based I2C touchscreen.
|
||||
|
||||
If unsure, say N.
|
||||
|
||||
To compile this driver as a module, choose M here: the
|
||||
module will be called chipone_icn8318.
|
||||
|
||||
config TOUCHSCREEN_CY8CTMG110
|
||||
tristate "cy8ctmg110 touchscreen"
|
||||
depends on I2C
|
||||
@ -297,11 +310,12 @@ config TOUCHSCREEN_FUJITSU
|
||||
|
||||
config TOUCHSCREEN_GOODIX
|
||||
tristate "Goodix I2C touchscreen"
|
||||
depends on I2C && ACPI
|
||||
depends on I2C
|
||||
help
|
||||
Say Y here if you have the Goodix touchscreen (such as one
|
||||
installed in Onda v975w tablets) connected to your
|
||||
system.
|
||||
system. It also supports 5-finger chip models, which can be
|
||||
found on ARM tablets, like Wexler TAB7200 and MSI Primo73.
|
||||
|
||||
If unsure, say N.
|
||||
|
||||
@ -323,6 +337,18 @@ config TOUCHSCREEN_ILI210X
|
||||
To compile this driver as a module, choose M here: the
|
||||
module will be called ili210x.
|
||||
|
||||
config TOUCHSCREEN_IPROC
|
||||
tristate "IPROC touch panel driver support"
|
||||
depends on ARCH_BCM_IPROC || COMPILE_TEST
|
||||
help
|
||||
Say Y here if you want to add support for the IPROC touch
|
||||
controller to your system.
|
||||
|
||||
If unsure, say N.
|
||||
|
||||
To compile this driver as a module, choose M here: the
|
||||
module will be called bcm_iproc_tsc.
|
||||
|
||||
config TOUCHSCREEN_S3C2410
|
||||
tristate "Samsung S3C2410/generic touchscreen input driver"
|
||||
depends on ARCH_S3C24XX || SAMSUNG_DEV_TS
|
||||
@ -962,6 +988,17 @@ config TOUCHSCREEN_SUR40
|
||||
To compile this driver as a module, choose M here: the
|
||||
module will be called sur40.
|
||||
|
||||
config TOUCHSCREEN_SX8654
|
||||
tristate "Semtech SX8654 touchscreen"
|
||||
depends on I2C
|
||||
help
|
||||
Say Y here if you have a Semtech SX8654 touchscreen controller.
|
||||
|
||||
If unsure, say N
|
||||
|
||||
To compile this driver as a module, choose M here: the
|
||||
module will be called sx8654.
|
||||
|
||||
config TOUCHSCREEN_TPS6507X
|
||||
tristate "TPS6507x based touchscreens"
|
||||
depends on I2C
|
||||
|
@ -17,6 +17,7 @@ obj-$(CONFIG_TOUCHSCREEN_AR1021_I2C) += ar1021_i2c.o
|
||||
obj-$(CONFIG_TOUCHSCREEN_ATMEL_MXT) += atmel_mxt_ts.o
|
||||
obj-$(CONFIG_TOUCHSCREEN_AUO_PIXCIR) += auo-pixcir-ts.o
|
||||
obj-$(CONFIG_TOUCHSCREEN_BU21013) += bu21013_ts.o
|
||||
obj-$(CONFIG_TOUCHSCREEN_CHIPONE_ICN8318) += chipone_icn8318.o
|
||||
obj-$(CONFIG_TOUCHSCREEN_CY8CTMG110) += cy8ctmg110_ts.o
|
||||
obj-$(CONFIG_TOUCHSCREEN_CYTTSP_CORE) += cyttsp_core.o
|
||||
obj-$(CONFIG_TOUCHSCREEN_CYTTSP_I2C) += cyttsp_i2c.o cyttsp_i2c_common.o
|
||||
@ -39,6 +40,7 @@ obj-$(CONFIG_TOUCHSCREEN_GOODIX) += goodix.o
|
||||
obj-$(CONFIG_TOUCHSCREEN_ILI210X) += ili210x.o
|
||||
obj-$(CONFIG_TOUCHSCREEN_INEXIO) += inexio.o
|
||||
obj-$(CONFIG_TOUCHSCREEN_INTEL_MID) += intel-mid-touch.o
|
||||
obj-$(CONFIG_TOUCHSCREEN_IPROC) += bcm_iproc_tsc.o
|
||||
obj-$(CONFIG_TOUCHSCREEN_LPC32XX) += lpc32xx_ts.o
|
||||
obj-$(CONFIG_TOUCHSCREEN_MAX11801) += max11801_ts.o
|
||||
obj-$(CONFIG_TOUCHSCREEN_MC13783) += mc13783_ts.o
|
||||
@ -79,5 +81,6 @@ obj-$(CONFIG_TOUCHSCREEN_WM97XX_ATMEL) += atmel-wm97xx.o
|
||||
obj-$(CONFIG_TOUCHSCREEN_WM97XX_MAINSTONE) += mainstone-wm97xx.o
|
||||
obj-$(CONFIG_TOUCHSCREEN_WM97XX_ZYLONITE) += zylonite-wm97xx.o
|
||||
obj-$(CONFIG_TOUCHSCREEN_W90X900) += w90p910_ts.o
|
||||
obj-$(CONFIG_TOUCHSCREEN_SX8654) += sx8654.o
|
||||
obj-$(CONFIG_TOUCHSCREEN_TPS6507X) += tps6507x-ts.o
|
||||
obj-$(CONFIG_TOUCHSCREEN_ZFORCE) += zforce_ts.o
|
||||
|
@ -157,7 +157,7 @@ static const struct i2c_device_id ar1021_i2c_id[] = {
|
||||
};
|
||||
MODULE_DEVICE_TABLE(i2c, ar1021_i2c_id);
|
||||
|
||||
static struct of_device_id ar1021_i2c_of_match[] = {
|
||||
static const struct of_device_id ar1021_i2c_of_match[] = {
|
||||
{ .compatible = "microchip,ar1021-i2c", },
|
||||
{ }
|
||||
};
|
||||
|
@ -25,6 +25,7 @@
|
||||
#include <linux/interrupt.h>
|
||||
#include <linux/of.h>
|
||||
#include <linux/slab.h>
|
||||
#include <asm/unaligned.h>
|
||||
|
||||
/* Version */
|
||||
#define MXT_VER_20 20
|
||||
@ -79,6 +80,7 @@
|
||||
#define MXT_SPT_DIGITIZER_T43 43
|
||||
#define MXT_SPT_MESSAGECOUNT_T44 44
|
||||
#define MXT_SPT_CTECONFIG_T46 46
|
||||
#define MXT_TOUCH_MULTITOUCHSCREEN_T100 100
|
||||
|
||||
/* MXT_GEN_MESSAGE_T5 object */
|
||||
#define MXT_RPTID_NOMSG 0xff
|
||||
@ -185,6 +187,36 @@ struct t9_range {
|
||||
#define MXT_RESET_VALUE 0x01
|
||||
#define MXT_BACKUP_VALUE 0x55
|
||||
|
||||
/* T100 Multiple Touch Touchscreen */
|
||||
#define MXT_T100_CTRL 0
|
||||
#define MXT_T100_CFG1 1
|
||||
#define MXT_T100_TCHAUX 3
|
||||
#define MXT_T100_XRANGE 13
|
||||
#define MXT_T100_YRANGE 24
|
||||
|
||||
#define MXT_T100_CFG_SWITCHXY BIT(5)
|
||||
|
||||
#define MXT_T100_TCHAUX_VECT BIT(0)
|
||||
#define MXT_T100_TCHAUX_AMPL BIT(1)
|
||||
#define MXT_T100_TCHAUX_AREA BIT(2)
|
||||
|
||||
#define MXT_T100_DETECT BIT(7)
|
||||
#define MXT_T100_TYPE_MASK 0x70
|
||||
|
||||
enum t100_type {
|
||||
MXT_T100_TYPE_FINGER = 1,
|
||||
MXT_T100_TYPE_PASSIVE_STYLUS = 2,
|
||||
MXT_T100_TYPE_HOVERING_FINGER = 4,
|
||||
MXT_T100_TYPE_GLOVE = 5,
|
||||
MXT_T100_TYPE_LARGE_TOUCH = 6,
|
||||
};
|
||||
|
||||
#define MXT_DISTANCE_ACTIVE_TOUCH 0
|
||||
#define MXT_DISTANCE_HOVERING 1
|
||||
|
||||
#define MXT_TOUCH_MAJOR_DEFAULT 1
|
||||
#define MXT_PRESSURE_DEFAULT 1
|
||||
|
||||
/* Delay times */
|
||||
#define MXT_BACKUP_TIME 50 /* msec */
|
||||
#define MXT_RESET_TIME 200 /* msec */
|
||||
@ -244,6 +276,9 @@ struct mxt_data {
|
||||
unsigned int max_y;
|
||||
bool in_bootloader;
|
||||
u16 mem_size;
|
||||
u8 t100_aux_ampl;
|
||||
u8 t100_aux_area;
|
||||
u8 t100_aux_vect;
|
||||
u8 max_reportid;
|
||||
u32 config_crc;
|
||||
u32 info_crc;
|
||||
@ -253,6 +288,7 @@ struct mxt_data {
|
||||
bool update_input;
|
||||
u8 last_message_count;
|
||||
u8 num_touchids;
|
||||
u8 multitouch;
|
||||
|
||||
/* Cached parameters from object table */
|
||||
u16 T5_address;
|
||||
@ -264,6 +300,8 @@ struct mxt_data {
|
||||
u8 T9_reportid_max;
|
||||
u8 T19_reportid;
|
||||
u16 T44_address;
|
||||
u8 T100_reportid_min;
|
||||
u8 T100_reportid_max;
|
||||
|
||||
/* for fw update in bootloader */
|
||||
struct completion bl_completion;
|
||||
@ -771,6 +809,114 @@ static void mxt_proc_t9_message(struct mxt_data *data, u8 *message)
|
||||
data->update_input = true;
|
||||
}
|
||||
|
||||
static void mxt_proc_t100_message(struct mxt_data *data, u8 *message)
|
||||
{
|
||||
struct device *dev = &data->client->dev;
|
||||
struct input_dev *input_dev = data->input_dev;
|
||||
int id;
|
||||
u8 status;
|
||||
u8 type = 0;
|
||||
u16 x;
|
||||
u16 y;
|
||||
int distance = 0;
|
||||
int tool = 0;
|
||||
u8 major = 0;
|
||||
u8 pressure = 0;
|
||||
u8 orientation = 0;
|
||||
|
||||
id = message[0] - data->T100_reportid_min - 2;
|
||||
|
||||
/* ignore SCRSTATUS events */
|
||||
if (id < 0)
|
||||
return;
|
||||
|
||||
status = message[1];
|
||||
x = get_unaligned_le16(&message[2]);
|
||||
y = get_unaligned_le16(&message[4]);
|
||||
|
||||
if (status & MXT_T100_DETECT) {
|
||||
type = (status & MXT_T100_TYPE_MASK) >> 4;
|
||||
|
||||
switch (type) {
|
||||
case MXT_T100_TYPE_HOVERING_FINGER:
|
||||
tool = MT_TOOL_FINGER;
|
||||
distance = MXT_DISTANCE_HOVERING;
|
||||
|
||||
if (data->t100_aux_vect)
|
||||
orientation = message[data->t100_aux_vect];
|
||||
|
||||
break;
|
||||
|
||||
case MXT_T100_TYPE_FINGER:
|
||||
case MXT_T100_TYPE_GLOVE:
|
||||
tool = MT_TOOL_FINGER;
|
||||
distance = MXT_DISTANCE_ACTIVE_TOUCH;
|
||||
|
||||
if (data->t100_aux_area)
|
||||
major = message[data->t100_aux_area];
|
||||
|
||||
if (data->t100_aux_ampl)
|
||||
pressure = message[data->t100_aux_ampl];
|
||||
|
||||
if (data->t100_aux_vect)
|
||||
orientation = message[data->t100_aux_vect];
|
||||
|
||||
break;
|
||||
|
||||
case MXT_T100_TYPE_PASSIVE_STYLUS:
|
||||
tool = MT_TOOL_PEN;
|
||||
|
||||
/*
|
||||
* Passive stylus is reported with size zero so
|
||||
* hardcode.
|
||||
*/
|
||||
major = MXT_TOUCH_MAJOR_DEFAULT;
|
||||
|
||||
if (data->t100_aux_ampl)
|
||||
pressure = message[data->t100_aux_ampl];
|
||||
|
||||
break;
|
||||
|
||||
case MXT_T100_TYPE_LARGE_TOUCH:
|
||||
/* Ignore suppressed touch */
|
||||
break;
|
||||
|
||||
default:
|
||||
dev_dbg(dev, "Unexpected T100 type\n");
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Values reported should be non-zero if tool is touching the
|
||||
* device
|
||||
*/
|
||||
if (!pressure && type != MXT_T100_TYPE_HOVERING_FINGER)
|
||||
pressure = MXT_PRESSURE_DEFAULT;
|
||||
|
||||
input_mt_slot(input_dev, id);
|
||||
|
||||
if (status & MXT_T100_DETECT) {
|
||||
dev_dbg(dev, "[%u] type:%u x:%u y:%u a:%02X p:%02X v:%02X\n",
|
||||
id, type, x, y, major, pressure, orientation);
|
||||
|
||||
input_mt_report_slot_state(input_dev, tool, 1);
|
||||
input_report_abs(input_dev, ABS_MT_POSITION_X, x);
|
||||
input_report_abs(input_dev, ABS_MT_POSITION_Y, y);
|
||||
input_report_abs(input_dev, ABS_MT_TOUCH_MAJOR, major);
|
||||
input_report_abs(input_dev, ABS_MT_PRESSURE, pressure);
|
||||
input_report_abs(input_dev, ABS_MT_DISTANCE, distance);
|
||||
input_report_abs(input_dev, ABS_MT_ORIENTATION, orientation);
|
||||
} else {
|
||||
dev_dbg(dev, "[%u] release\n", id);
|
||||
|
||||
/* close out slot */
|
||||
input_mt_report_slot_state(input_dev, 0, 0);
|
||||
}
|
||||
|
||||
data->update_input = true;
|
||||
}
|
||||
|
||||
static int mxt_proc_message(struct mxt_data *data, u8 *message)
|
||||
{
|
||||
u8 report_id = message[0];
|
||||
@ -786,9 +932,12 @@ static int mxt_proc_message(struct mxt_data *data, u8 *message)
|
||||
* is not yet registered.
|
||||
*/
|
||||
mxt_dump_message(data, message);
|
||||
} else if (report_id >= data->T9_reportid_min
|
||||
&& report_id <= data->T9_reportid_max) {
|
||||
} else if (report_id >= data->T9_reportid_min &&
|
||||
report_id <= data->T9_reportid_max) {
|
||||
mxt_proc_t9_message(data, message);
|
||||
} else if (report_id >= data->T100_reportid_min &&
|
||||
report_id <= data->T100_reportid_max) {
|
||||
mxt_proc_t100_message(data, message);
|
||||
} else if (report_id == data->T19_reportid) {
|
||||
mxt_input_button(data, message);
|
||||
data->update_input = true;
|
||||
@ -1411,6 +1560,8 @@ static void mxt_free_object_table(struct mxt_data *data)
|
||||
data->T9_reportid_max = 0;
|
||||
data->T19_reportid = 0;
|
||||
data->T44_address = 0;
|
||||
data->T100_reportid_min = 0;
|
||||
data->T100_reportid_max = 0;
|
||||
data->max_reportid = 0;
|
||||
}
|
||||
|
||||
@ -1487,6 +1638,7 @@ static int mxt_get_object_table(struct mxt_data *data)
|
||||
data->T7_address = object->start_address;
|
||||
break;
|
||||
case MXT_TOUCH_MULTI_T9:
|
||||
data->multitouch = MXT_TOUCH_MULTI_T9;
|
||||
data->T9_reportid_min = min_id;
|
||||
data->T9_reportid_max = max_id;
|
||||
data->num_touchids = object->num_report_ids
|
||||
@ -1498,6 +1650,13 @@ static int mxt_get_object_table(struct mxt_data *data)
|
||||
case MXT_SPT_GPIOPWM_T19:
|
||||
data->T19_reportid = min_id;
|
||||
break;
|
||||
case MXT_TOUCH_MULTITOUCHSCREEN_T100:
|
||||
data->multitouch = MXT_TOUCH_MULTITOUCHSCREEN_T100;
|
||||
data->T100_reportid_min = min_id;
|
||||
data->T100_reportid_max = max_id;
|
||||
/* first two report IDs reserved */
|
||||
data->num_touchids = object->num_report_ids - 2;
|
||||
break;
|
||||
}
|
||||
|
||||
end_address = object->start_address
|
||||
@ -1582,22 +1741,138 @@ static int mxt_read_t9_resolution(struct mxt_data *data)
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int mxt_read_t100_config(struct mxt_data *data)
|
||||
{
|
||||
struct i2c_client *client = data->client;
|
||||
int error;
|
||||
struct mxt_object *object;
|
||||
u16 range_x, range_y;
|
||||
u8 cfg, tchaux;
|
||||
u8 aux;
|
||||
|
||||
object = mxt_get_object(data, MXT_TOUCH_MULTITOUCHSCREEN_T100);
|
||||
if (!object)
|
||||
return -EINVAL;
|
||||
|
||||
error = __mxt_read_reg(client,
|
||||
object->start_address + MXT_T100_XRANGE,
|
||||
sizeof(range_x), &range_x);
|
||||
if (error)
|
||||
return error;
|
||||
|
||||
le16_to_cpus(&range_x);
|
||||
|
||||
error = __mxt_read_reg(client,
|
||||
object->start_address + MXT_T100_YRANGE,
|
||||
sizeof(range_y), &range_y);
|
||||
if (error)
|
||||
return error;
|
||||
|
||||
le16_to_cpus(&range_y);
|
||||
|
||||
error = __mxt_read_reg(client,
|
||||
object->start_address + MXT_T100_CFG1,
|
||||
1, &cfg);
|
||||
if (error)
|
||||
return error;
|
||||
|
||||
error = __mxt_read_reg(client,
|
||||
object->start_address + MXT_T100_TCHAUX,
|
||||
1, &tchaux);
|
||||
if (error)
|
||||
return error;
|
||||
|
||||
/* Handle default values */
|
||||
if (range_x == 0)
|
||||
range_x = 1023;
|
||||
|
||||
if (range_y == 0)
|
||||
range_y = 1023;
|
||||
|
||||
if (cfg & MXT_T100_CFG_SWITCHXY) {
|
||||
data->max_x = range_y;
|
||||
data->max_y = range_x;
|
||||
} else {
|
||||
data->max_x = range_x;
|
||||
data->max_y = range_y;
|
||||
}
|
||||
|
||||
/* allocate aux bytes */
|
||||
aux = 6;
|
||||
|
||||
if (tchaux & MXT_T100_TCHAUX_VECT)
|
||||
data->t100_aux_vect = aux++;
|
||||
|
||||
if (tchaux & MXT_T100_TCHAUX_AMPL)
|
||||
data->t100_aux_ampl = aux++;
|
||||
|
||||
if (tchaux & MXT_T100_TCHAUX_AREA)
|
||||
data->t100_aux_area = aux++;
|
||||
|
||||
dev_dbg(&client->dev,
|
||||
"T100 aux mappings vect:%u ampl:%u area:%u\n",
|
||||
data->t100_aux_vect, data->t100_aux_ampl, data->t100_aux_area);
|
||||
|
||||
dev_info(&client->dev,
|
||||
"T100 Touchscreen size X%uY%u\n", data->max_x, data->max_y);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int mxt_input_open(struct input_dev *dev);
|
||||
static void mxt_input_close(struct input_dev *dev);
|
||||
|
||||
static int mxt_initialize_t9_input_device(struct mxt_data *data)
|
||||
static void mxt_set_up_as_touchpad(struct input_dev *input_dev,
|
||||
struct mxt_data *data)
|
||||
{
|
||||
struct device *dev = &data->client->dev;
|
||||
const struct mxt_platform_data *pdata = data->pdata;
|
||||
int i;
|
||||
|
||||
input_dev->name = "Atmel maXTouch Touchpad";
|
||||
|
||||
__set_bit(INPUT_PROP_BUTTONPAD, input_dev->propbit);
|
||||
|
||||
input_abs_set_res(input_dev, ABS_X, MXT_PIXELS_PER_MM);
|
||||
input_abs_set_res(input_dev, ABS_Y, MXT_PIXELS_PER_MM);
|
||||
input_abs_set_res(input_dev, ABS_MT_POSITION_X,
|
||||
MXT_PIXELS_PER_MM);
|
||||
input_abs_set_res(input_dev, ABS_MT_POSITION_Y,
|
||||
MXT_PIXELS_PER_MM);
|
||||
|
||||
for (i = 0; i < pdata->t19_num_keys; i++)
|
||||
if (pdata->t19_keymap[i] != KEY_RESERVED)
|
||||
input_set_capability(input_dev, EV_KEY,
|
||||
pdata->t19_keymap[i]);
|
||||
}
|
||||
|
||||
static int mxt_initialize_input_device(struct mxt_data *data)
|
||||
{
|
||||
const struct mxt_platform_data *pdata = data->pdata;
|
||||
struct device *dev = &data->client->dev;
|
||||
struct input_dev *input_dev;
|
||||
int error;
|
||||
unsigned int num_mt_slots;
|
||||
unsigned int mt_flags = 0;
|
||||
int i;
|
||||
|
||||
error = mxt_read_t9_resolution(data);
|
||||
if (error)
|
||||
dev_warn(dev, "Failed to initialize T9 resolution\n");
|
||||
switch (data->multitouch) {
|
||||
case MXT_TOUCH_MULTI_T9:
|
||||
num_mt_slots = data->T9_reportid_max - data->T9_reportid_min + 1;
|
||||
error = mxt_read_t9_resolution(data);
|
||||
if (error)
|
||||
dev_warn(dev, "Failed to initialize T9 resolution\n");
|
||||
break;
|
||||
|
||||
case MXT_TOUCH_MULTITOUCHSCREEN_T100:
|
||||
num_mt_slots = data->num_touchids;
|
||||
error = mxt_read_t100_config(data);
|
||||
if (error)
|
||||
dev_warn(dev, "Failed to read T100 config\n");
|
||||
break;
|
||||
|
||||
default:
|
||||
dev_err(dev, "Invalid multitouch object\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
input_dev = input_allocate_device();
|
||||
if (!input_dev) {
|
||||
@ -1612,54 +1887,76 @@ static int mxt_initialize_t9_input_device(struct mxt_data *data)
|
||||
input_dev->open = mxt_input_open;
|
||||
input_dev->close = mxt_input_close;
|
||||
|
||||
__set_bit(EV_ABS, input_dev->evbit);
|
||||
__set_bit(EV_KEY, input_dev->evbit);
|
||||
__set_bit(BTN_TOUCH, input_dev->keybit);
|
||||
|
||||
if (pdata->t19_num_keys) {
|
||||
__set_bit(INPUT_PROP_BUTTONPAD, input_dev->propbit);
|
||||
|
||||
for (i = 0; i < pdata->t19_num_keys; i++)
|
||||
if (pdata->t19_keymap[i] != KEY_RESERVED)
|
||||
input_set_capability(input_dev, EV_KEY,
|
||||
pdata->t19_keymap[i]);
|
||||
|
||||
mt_flags |= INPUT_MT_POINTER;
|
||||
|
||||
input_abs_set_res(input_dev, ABS_X, MXT_PIXELS_PER_MM);
|
||||
input_abs_set_res(input_dev, ABS_Y, MXT_PIXELS_PER_MM);
|
||||
input_abs_set_res(input_dev, ABS_MT_POSITION_X,
|
||||
MXT_PIXELS_PER_MM);
|
||||
input_abs_set_res(input_dev, ABS_MT_POSITION_Y,
|
||||
MXT_PIXELS_PER_MM);
|
||||
|
||||
input_dev->name = "Atmel maXTouch Touchpad";
|
||||
}
|
||||
input_set_capability(input_dev, EV_KEY, BTN_TOUCH);
|
||||
|
||||
/* For single touch */
|
||||
input_set_abs_params(input_dev, ABS_X,
|
||||
0, data->max_x, 0, 0);
|
||||
input_set_abs_params(input_dev, ABS_Y,
|
||||
0, data->max_y, 0, 0);
|
||||
input_set_abs_params(input_dev, ABS_PRESSURE,
|
||||
0, 255, 0, 0);
|
||||
input_set_abs_params(input_dev, ABS_X, 0, data->max_x, 0, 0);
|
||||
input_set_abs_params(input_dev, ABS_Y, 0, data->max_y, 0, 0);
|
||||
|
||||
if (data->multitouch == MXT_TOUCH_MULTI_T9 ||
|
||||
(data->multitouch == MXT_TOUCH_MULTITOUCHSCREEN_T100 &&
|
||||
data->t100_aux_ampl)) {
|
||||
input_set_abs_params(input_dev, ABS_PRESSURE, 0, 255, 0, 0);
|
||||
}
|
||||
|
||||
/* If device has buttons we assume it is a touchpad */
|
||||
if (pdata->t19_num_keys) {
|
||||
mxt_set_up_as_touchpad(input_dev, data);
|
||||
mt_flags |= INPUT_MT_POINTER;
|
||||
}
|
||||
|
||||
/* For multi touch */
|
||||
num_mt_slots = data->T9_reportid_max - data->T9_reportid_min + 1;
|
||||
error = input_mt_init_slots(input_dev, num_mt_slots, mt_flags);
|
||||
if (error) {
|
||||
dev_err(dev, "Error %d initialising slots\n", error);
|
||||
goto err_free_mem;
|
||||
}
|
||||
|
||||
input_set_abs_params(input_dev, ABS_MT_TOUCH_MAJOR,
|
||||
0, MXT_MAX_AREA, 0, 0);
|
||||
if (data->multitouch == MXT_TOUCH_MULTITOUCHSCREEN_T100) {
|
||||
input_set_abs_params(input_dev, ABS_MT_TOOL_TYPE,
|
||||
0, MT_TOOL_MAX, 0, 0);
|
||||
input_set_abs_params(input_dev, ABS_MT_DISTANCE,
|
||||
MXT_DISTANCE_ACTIVE_TOUCH,
|
||||
MXT_DISTANCE_HOVERING,
|
||||
0, 0);
|
||||
}
|
||||
|
||||
input_set_abs_params(input_dev, ABS_MT_POSITION_X,
|
||||
0, data->max_x, 0, 0);
|
||||
input_set_abs_params(input_dev, ABS_MT_POSITION_Y,
|
||||
0, data->max_y, 0, 0);
|
||||
input_set_abs_params(input_dev, ABS_MT_PRESSURE,
|
||||
0, 255, 0, 0);
|
||||
|
||||
if (data->multitouch == MXT_TOUCH_MULTI_T9 ||
|
||||
(data->multitouch == MXT_TOUCH_MULTITOUCHSCREEN_T100 &&
|
||||
data->t100_aux_area)) {
|
||||
input_set_abs_params(input_dev, ABS_MT_TOUCH_MAJOR,
|
||||
0, MXT_MAX_AREA, 0, 0);
|
||||
}
|
||||
|
||||
if (data->multitouch == MXT_TOUCH_MULTI_T9 ||
|
||||
(data->multitouch == MXT_TOUCH_MULTITOUCHSCREEN_T100 &&
|
||||
data->t100_aux_ampl)) {
|
||||
input_set_abs_params(input_dev, ABS_MT_PRESSURE,
|
||||
0, 255, 0, 0);
|
||||
}
|
||||
|
||||
if (data->multitouch == MXT_TOUCH_MULTITOUCHSCREEN_T100 &&
|
||||
data->t100_aux_vect) {
|
||||
input_set_abs_params(input_dev, ABS_MT_ORIENTATION,
|
||||
0, 255, 0, 0);
|
||||
}
|
||||
|
||||
if (data->multitouch == MXT_TOUCH_MULTITOUCHSCREEN_T100 &&
|
||||
data->t100_aux_ampl) {
|
||||
input_set_abs_params(input_dev, ABS_MT_PRESSURE,
|
||||
0, 255, 0, 0);
|
||||
}
|
||||
|
||||
if (data->multitouch == MXT_TOUCH_MULTITOUCHSCREEN_T100 &&
|
||||
data->t100_aux_vect) {
|
||||
input_set_abs_params(input_dev, ABS_MT_ORIENTATION,
|
||||
0, 255, 0, 0);
|
||||
}
|
||||
|
||||
input_set_drvdata(input_dev, data);
|
||||
|
||||
@ -1765,9 +2062,13 @@ static int mxt_configure_objects(struct mxt_data *data,
|
||||
dev_warn(dev, "Error %d updating config\n", error);
|
||||
}
|
||||
|
||||
error = mxt_initialize_t9_input_device(data);
|
||||
if (error)
|
||||
return error;
|
||||
if (data->multitouch) {
|
||||
error = mxt_initialize_input_device(data);
|
||||
if (error)
|
||||
return error;
|
||||
} else {
|
||||
dev_warn(dev, "No touch object detected\n");
|
||||
}
|
||||
|
||||
dev_info(dev,
|
||||
"Family: %u Variant: %u Firmware V%u.%u.%02X Objects: %u\n",
|
||||
@ -2044,15 +2345,13 @@ static const struct attribute_group mxt_attr_group = {
|
||||
static void mxt_start(struct mxt_data *data)
|
||||
{
|
||||
/* Touch enable */
|
||||
mxt_write_object(data,
|
||||
MXT_TOUCH_MULTI_T9, MXT_TOUCH_CTRL, 0x83);
|
||||
mxt_write_object(data, data->multitouch, MXT_TOUCH_CTRL, 0x83);
|
||||
}
|
||||
|
||||
static void mxt_stop(struct mxt_data *data)
|
||||
{
|
||||
/* Touch disable */
|
||||
mxt_write_object(data,
|
||||
MXT_TOUCH_MULTI_T9, MXT_TOUCH_CTRL, 0);
|
||||
mxt_write_object(data, data->multitouch, MXT_TOUCH_CTRL, 0);
|
||||
}
|
||||
|
||||
static int mxt_input_open(struct input_dev *dev)
|
||||
|
522
drivers/input/touchscreen/bcm_iproc_tsc.c
Normal file
522
drivers/input/touchscreen/bcm_iproc_tsc.c
Normal file
@ -0,0 +1,522 @@
|
||||
/*
|
||||
* Copyright (C) 2015 Broadcom Corporation
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU General Public License as
|
||||
* published by the Free Software Foundation version 2.
|
||||
*
|
||||
* This program is distributed "as is" WITHOUT ANY WARRANTY of any
|
||||
* kind, whether express or implied; without even the implied warranty
|
||||
* of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*/
|
||||
#include <linux/module.h>
|
||||
#include <linux/init.h>
|
||||
#include <linux/input.h>
|
||||
#include <linux/delay.h>
|
||||
#include <linux/interrupt.h>
|
||||
#include <linux/keyboard.h>
|
||||
#include <linux/platform_device.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/of.h>
|
||||
#include <asm/irq.h>
|
||||
#include <linux/io.h>
|
||||
#include <linux/clk.h>
|
||||
#include <linux/serio.h>
|
||||
|
||||
#define IPROC_TS_NAME "iproc-ts"
|
||||
|
||||
#define PEN_DOWN_STATUS 1
|
||||
#define PEN_UP_STATUS 0
|
||||
|
||||
#define X_MIN 0
|
||||
#define Y_MIN 0
|
||||
#define X_MAX 0xFFF
|
||||
#define Y_MAX 0xFFF
|
||||
|
||||
/* Value given by controller for invalid coordinate. */
|
||||
#define INVALID_COORD 0xFFFFFFFF
|
||||
|
||||
/* Register offsets */
|
||||
#define REGCTL1 0x00
|
||||
#define REGCTL2 0x04
|
||||
#define INTERRUPT_THRES 0x08
|
||||
#define INTERRUPT_MASK 0x0c
|
||||
|
||||
#define INTERRUPT_STATUS 0x10
|
||||
#define CONTROLLER_STATUS 0x14
|
||||
#define FIFO_DATA 0x18
|
||||
#define FIFO_DATA_X_Y_MASK 0xFFFF
|
||||
#define ANALOG_CONTROL 0x1c
|
||||
|
||||
#define AUX_DATA 0x20
|
||||
#define DEBOUNCE_CNTR_STAT 0x24
|
||||
#define SCAN_CNTR_STAT 0x28
|
||||
#define REM_CNTR_STAT 0x2c
|
||||
|
||||
#define SETTLING_TIMER_STAT 0x30
|
||||
#define SPARE_REG 0x34
|
||||
#define SOFT_BYPASS_CONTROL 0x38
|
||||
#define SOFT_BYPASS_DATA 0x3c
|
||||
|
||||
|
||||
/* Bit values for INTERRUPT_MASK and INTERRUPT_STATUS regs */
|
||||
#define TS_PEN_INTR_MASK BIT(0)
|
||||
#define TS_FIFO_INTR_MASK BIT(2)
|
||||
|
||||
/* Bit values for CONTROLLER_STATUS reg1 */
|
||||
#define TS_PEN_DOWN BIT(0)
|
||||
|
||||
/* Shift values for control reg1 */
|
||||
#define SCANNING_PERIOD_SHIFT 24
|
||||
#define DEBOUNCE_TIMEOUT_SHIFT 16
|
||||
#define SETTLING_TIMEOUT_SHIFT 8
|
||||
#define TOUCH_TIMEOUT_SHIFT 0
|
||||
|
||||
/* Shift values for coordinates from fifo */
|
||||
#define X_COORD_SHIFT 0
|
||||
#define Y_COORD_SHIFT 16
|
||||
|
||||
/* Bit values for REGCTL2 */
|
||||
#define TS_CONTROLLER_EN_BIT BIT(16)
|
||||
#define TS_CONTROLLER_AVGDATA_SHIFT 8
|
||||
#define TS_CONTROLLER_AVGDATA_MASK (0x7 << TS_CONTROLLER_AVGDATA_SHIFT)
|
||||
#define TS_CONTROLLER_PWR_LDO BIT(5)
|
||||
#define TS_CONTROLLER_PWR_ADC BIT(4)
|
||||
#define TS_CONTROLLER_PWR_BGP BIT(3)
|
||||
#define TS_CONTROLLER_PWR_TS BIT(2)
|
||||
#define TS_WIRE_MODE_BIT BIT(1)
|
||||
|
||||
#define dbg_reg(dev, priv, reg) \
|
||||
dev_dbg(dev, "%20s= 0x%08x\n", #reg, readl((priv)->regs + reg))
|
||||
|
||||
struct tsc_param {
|
||||
/* Each step is 1024 us. Valid 1-256 */
|
||||
u32 scanning_period;
|
||||
|
||||
/* Each step is 512 us. Valid 0-255 */
|
||||
u32 debounce_timeout;
|
||||
|
||||
/*
|
||||
* The settling duration (in ms) is the amount of time the tsc
|
||||
* waits to allow the voltage to settle after turning on the
|
||||
* drivers in detection mode. Valid values: 0-11
|
||||
* 0 = 0.008 ms
|
||||
* 1 = 0.01 ms
|
||||
* 2 = 0.02 ms
|
||||
* 3 = 0.04 ms
|
||||
* 4 = 0.08 ms
|
||||
* 5 = 0.16 ms
|
||||
* 6 = 0.32 ms
|
||||
* 7 = 0.64 ms
|
||||
* 8 = 1.28 ms
|
||||
* 9 = 2.56 ms
|
||||
* 10 = 5.12 ms
|
||||
* 11 = 10.24 ms
|
||||
*/
|
||||
u32 settling_timeout;
|
||||
|
||||
/* touch timeout in sample counts */
|
||||
u32 touch_timeout;
|
||||
|
||||
/*
|
||||
* Number of data samples which are averaged before a final data point
|
||||
* is placed into the FIFO
|
||||
*/
|
||||
u32 average_data;
|
||||
|
||||
/* FIFO threshold */
|
||||
u32 fifo_threshold;
|
||||
|
||||
/* Optional standard touchscreen properties. */
|
||||
u32 max_x;
|
||||
u32 max_y;
|
||||
u32 fuzz_x;
|
||||
u32 fuzz_y;
|
||||
bool invert_x;
|
||||
bool invert_y;
|
||||
};
|
||||
|
||||
struct iproc_ts_priv {
|
||||
struct platform_device *pdev;
|
||||
struct input_dev *idev;
|
||||
|
||||
void __iomem *regs;
|
||||
struct clk *tsc_clk;
|
||||
|
||||
int pen_status;
|
||||
struct tsc_param cfg_params;
|
||||
};
|
||||
|
||||
/*
|
||||
* Set default values the same as hardware reset values
|
||||
* except for fifo_threshold with is set to 1.
|
||||
*/
|
||||
static const struct tsc_param iproc_default_config = {
|
||||
.scanning_period = 0x5, /* 1 to 256 */
|
||||
.debounce_timeout = 0x28, /* 0 to 255 */
|
||||
.settling_timeout = 0x7, /* 0 to 11 */
|
||||
.touch_timeout = 0xa, /* 0 to 255 */
|
||||
.average_data = 5, /* entry 5 = 32 pts */
|
||||
.fifo_threshold = 1, /* 0 to 31 */
|
||||
.max_x = X_MAX,
|
||||
.max_y = Y_MAX,
|
||||
};
|
||||
|
||||
static void ts_reg_dump(struct iproc_ts_priv *priv)
|
||||
{
|
||||
struct device *dev = &priv->pdev->dev;
|
||||
|
||||
dbg_reg(dev, priv, REGCTL1);
|
||||
dbg_reg(dev, priv, REGCTL2);
|
||||
dbg_reg(dev, priv, INTERRUPT_THRES);
|
||||
dbg_reg(dev, priv, INTERRUPT_MASK);
|
||||
dbg_reg(dev, priv, INTERRUPT_STATUS);
|
||||
dbg_reg(dev, priv, CONTROLLER_STATUS);
|
||||
dbg_reg(dev, priv, FIFO_DATA);
|
||||
dbg_reg(dev, priv, ANALOG_CONTROL);
|
||||
dbg_reg(dev, priv, AUX_DATA);
|
||||
dbg_reg(dev, priv, DEBOUNCE_CNTR_STAT);
|
||||
dbg_reg(dev, priv, SCAN_CNTR_STAT);
|
||||
dbg_reg(dev, priv, REM_CNTR_STAT);
|
||||
dbg_reg(dev, priv, SETTLING_TIMER_STAT);
|
||||
dbg_reg(dev, priv, SPARE_REG);
|
||||
dbg_reg(dev, priv, SOFT_BYPASS_CONTROL);
|
||||
dbg_reg(dev, priv, SOFT_BYPASS_DATA);
|
||||
}
|
||||
|
||||
static irqreturn_t iproc_touchscreen_interrupt(int irq, void *data)
|
||||
{
|
||||
struct platform_device *pdev = data;
|
||||
struct iproc_ts_priv *priv = platform_get_drvdata(pdev);
|
||||
u32 intr_status;
|
||||
u32 raw_coordinate;
|
||||
u16 x;
|
||||
u16 y;
|
||||
int i;
|
||||
bool needs_sync = false;
|
||||
|
||||
intr_status = readl(priv->regs + INTERRUPT_STATUS);
|
||||
intr_status &= TS_PEN_INTR_MASK | TS_FIFO_INTR_MASK;
|
||||
if (intr_status == 0)
|
||||
return IRQ_NONE;
|
||||
|
||||
/* Clear all interrupt status bits, write-1-clear */
|
||||
writel(intr_status, priv->regs + INTERRUPT_STATUS);
|
||||
|
||||
/* Pen up/down */
|
||||
if (intr_status & TS_PEN_INTR_MASK) {
|
||||
if (readl(priv->regs + CONTROLLER_STATUS) & TS_PEN_DOWN)
|
||||
priv->pen_status = PEN_DOWN_STATUS;
|
||||
else
|
||||
priv->pen_status = PEN_UP_STATUS;
|
||||
|
||||
input_report_key(priv->idev, BTN_TOUCH, priv->pen_status);
|
||||
needs_sync = true;
|
||||
|
||||
dev_dbg(&priv->pdev->dev,
|
||||
"pen up-down (%d)\n", priv->pen_status);
|
||||
}
|
||||
|
||||
/* coordinates in FIFO exceed the theshold */
|
||||
if (intr_status & TS_FIFO_INTR_MASK) {
|
||||
for (i = 0; i < priv->cfg_params.fifo_threshold; i++) {
|
||||
raw_coordinate = readl(priv->regs + FIFO_DATA);
|
||||
if (raw_coordinate == INVALID_COORD)
|
||||
continue;
|
||||
|
||||
/*
|
||||
* The x and y coordinate are 16 bits each
|
||||
* with the x in the lower 16 bits and y in the
|
||||
* upper 16 bits.
|
||||
*/
|
||||
x = (raw_coordinate >> X_COORD_SHIFT) &
|
||||
FIFO_DATA_X_Y_MASK;
|
||||
y = (raw_coordinate >> Y_COORD_SHIFT) &
|
||||
FIFO_DATA_X_Y_MASK;
|
||||
|
||||
/* We only want to retain the 12 msb of the 16 */
|
||||
x = (x >> 4) & 0x0FFF;
|
||||
y = (y >> 4) & 0x0FFF;
|
||||
|
||||
/* adjust x y according to lcd tsc mount angle */
|
||||
if (priv->cfg_params.invert_x)
|
||||
x = priv->cfg_params.max_x - x;
|
||||
|
||||
if (priv->cfg_params.invert_y)
|
||||
y = priv->cfg_params.max_y - y;
|
||||
|
||||
input_report_abs(priv->idev, ABS_X, x);
|
||||
input_report_abs(priv->idev, ABS_Y, y);
|
||||
needs_sync = true;
|
||||
|
||||
dev_dbg(&priv->pdev->dev, "xy (0x%x 0x%x)\n", x, y);
|
||||
}
|
||||
}
|
||||
|
||||
if (needs_sync)
|
||||
input_sync(priv->idev);
|
||||
|
||||
return IRQ_HANDLED;
|
||||
}
|
||||
|
||||
static int iproc_ts_start(struct input_dev *idev)
|
||||
{
|
||||
struct iproc_ts_priv *priv = input_get_drvdata(idev);
|
||||
u32 val;
|
||||
int error;
|
||||
|
||||
/* Enable clock */
|
||||
error = clk_prepare_enable(priv->tsc_clk);
|
||||
if (error) {
|
||||
dev_err(&priv->pdev->dev, "%s clk_prepare_enable failed %d\n",
|
||||
__func__, error);
|
||||
return error;
|
||||
}
|
||||
|
||||
/*
|
||||
* Interrupt is generated when:
|
||||
* FIFO reaches the int_th value, and pen event(up/down)
|
||||
*/
|
||||
val = TS_PEN_INTR_MASK | TS_FIFO_INTR_MASK;
|
||||
writel(val, priv->regs + INTERRUPT_MASK);
|
||||
|
||||
writel(priv->cfg_params.fifo_threshold, priv->regs + INTERRUPT_THRES);
|
||||
|
||||
/* Initialize control reg1 */
|
||||
val = 0;
|
||||
val |= priv->cfg_params.scanning_period << SCANNING_PERIOD_SHIFT;
|
||||
val |= priv->cfg_params.debounce_timeout << DEBOUNCE_TIMEOUT_SHIFT;
|
||||
val |= priv->cfg_params.settling_timeout << SETTLING_TIMEOUT_SHIFT;
|
||||
val |= priv->cfg_params.touch_timeout << TOUCH_TIMEOUT_SHIFT;
|
||||
writel(val, priv->regs + REGCTL1);
|
||||
|
||||
/* Try to clear all interrupt status */
|
||||
val = readl(priv->regs + INTERRUPT_STATUS);
|
||||
val |= TS_FIFO_INTR_MASK | TS_PEN_INTR_MASK;
|
||||
writel(val, priv->regs + INTERRUPT_STATUS);
|
||||
|
||||
/* Initialize control reg2 */
|
||||
val = readl(priv->regs + REGCTL2);
|
||||
val |= TS_CONTROLLER_EN_BIT | TS_WIRE_MODE_BIT;
|
||||
|
||||
val &= ~TS_CONTROLLER_AVGDATA_MASK;
|
||||
val |= priv->cfg_params.average_data << TS_CONTROLLER_AVGDATA_SHIFT;
|
||||
|
||||
val &= ~(TS_CONTROLLER_PWR_LDO | /* PWR up LDO */
|
||||
TS_CONTROLLER_PWR_ADC | /* PWR up ADC */
|
||||
TS_CONTROLLER_PWR_BGP | /* PWR up BGP */
|
||||
TS_CONTROLLER_PWR_TS); /* PWR up TS */
|
||||
|
||||
writel(val, priv->regs + REGCTL2);
|
||||
|
||||
ts_reg_dump(priv);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void iproc_ts_stop(struct input_dev *dev)
|
||||
{
|
||||
u32 val;
|
||||
struct iproc_ts_priv *priv = input_get_drvdata(dev);
|
||||
|
||||
writel(0, priv->regs + INTERRUPT_MASK); /* Disable all interrupts */
|
||||
|
||||
/* Only power down touch screen controller */
|
||||
val = readl(priv->regs + REGCTL2);
|
||||
val |= TS_CONTROLLER_PWR_TS;
|
||||
writel(val, priv->regs + REGCTL2);
|
||||
|
||||
clk_disable(priv->tsc_clk);
|
||||
}
|
||||
|
||||
static int iproc_get_tsc_config(struct device *dev, struct iproc_ts_priv *priv)
|
||||
{
|
||||
struct device_node *np = dev->of_node;
|
||||
u32 val;
|
||||
|
||||
priv->cfg_params = iproc_default_config;
|
||||
|
||||
if (!np)
|
||||
return 0;
|
||||
|
||||
if (of_property_read_u32(np, "scanning_period", &val) >= 0) {
|
||||
if (val < 1 || val > 256) {
|
||||
dev_err(dev, "scanning_period (%u) must be [1-256]\n",
|
||||
val);
|
||||
return -EINVAL;
|
||||
}
|
||||
priv->cfg_params.scanning_period = val;
|
||||
}
|
||||
|
||||
if (of_property_read_u32(np, "debounce_timeout", &val) >= 0) {
|
||||
if (val > 255) {
|
||||
dev_err(dev, "debounce_timeout (%u) must be [0-255]\n",
|
||||
val);
|
||||
return -EINVAL;
|
||||
}
|
||||
priv->cfg_params.debounce_timeout = val;
|
||||
}
|
||||
|
||||
if (of_property_read_u32(np, "settling_timeout", &val) >= 0) {
|
||||
if (val > 11) {
|
||||
dev_err(dev, "settling_timeout (%u) must be [0-11]\n",
|
||||
val);
|
||||
return -EINVAL;
|
||||
}
|
||||
priv->cfg_params.settling_timeout = val;
|
||||
}
|
||||
|
||||
if (of_property_read_u32(np, "touch_timeout", &val) >= 0) {
|
||||
if (val > 255) {
|
||||
dev_err(dev, "touch_timeout (%u) must be [0-255]\n",
|
||||
val);
|
||||
return -EINVAL;
|
||||
}
|
||||
priv->cfg_params.touch_timeout = val;
|
||||
}
|
||||
|
||||
if (of_property_read_u32(np, "average_data", &val) >= 0) {
|
||||
if (val > 8) {
|
||||
dev_err(dev, "average_data (%u) must be [0-8]\n", val);
|
||||
return -EINVAL;
|
||||
}
|
||||
priv->cfg_params.average_data = val;
|
||||
}
|
||||
|
||||
if (of_property_read_u32(np, "fifo_threshold", &val) >= 0) {
|
||||
if (val > 31) {
|
||||
dev_err(dev, "fifo_threshold (%u)) must be [0-31]\n",
|
||||
val);
|
||||
return -EINVAL;
|
||||
}
|
||||
priv->cfg_params.fifo_threshold = val;
|
||||
}
|
||||
|
||||
/* Parse optional properties. */
|
||||
of_property_read_u32(np, "touchscreen-size-x", &priv->cfg_params.max_x);
|
||||
of_property_read_u32(np, "touchscreen-size-y", &priv->cfg_params.max_y);
|
||||
|
||||
of_property_read_u32(np, "touchscreen-fuzz-x",
|
||||
&priv->cfg_params.fuzz_x);
|
||||
of_property_read_u32(np, "touchscreen-fuzz-y",
|
||||
&priv->cfg_params.fuzz_y);
|
||||
|
||||
priv->cfg_params.invert_x =
|
||||
of_property_read_bool(np, "touchscreen-inverted-x");
|
||||
priv->cfg_params.invert_y =
|
||||
of_property_read_bool(np, "touchscreen-inverted-y");
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int iproc_ts_probe(struct platform_device *pdev)
|
||||
{
|
||||
struct iproc_ts_priv *priv;
|
||||
struct input_dev *idev;
|
||||
struct resource *res;
|
||||
int irq;
|
||||
int error;
|
||||
|
||||
priv = devm_kzalloc(&pdev->dev, sizeof(*priv), GFP_KERNEL);
|
||||
if (!priv)
|
||||
return -ENOMEM;
|
||||
|
||||
/* touchscreen controller memory mapped regs */
|
||||
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
|
||||
priv->regs = devm_ioremap_resource(&pdev->dev, res);
|
||||
if (IS_ERR(priv->regs)) {
|
||||
error = PTR_ERR(priv->regs);
|
||||
dev_err(&pdev->dev, "unable to map I/O memory: %d\n", error);
|
||||
return error;
|
||||
}
|
||||
|
||||
priv->tsc_clk = devm_clk_get(&pdev->dev, "tsc_clk");
|
||||
if (IS_ERR(priv->tsc_clk)) {
|
||||
error = PTR_ERR(priv->tsc_clk);
|
||||
dev_err(&pdev->dev,
|
||||
"failed getting clock tsc_clk: %d\n", error);
|
||||
return error;
|
||||
}
|
||||
|
||||
priv->pdev = pdev;
|
||||
error = iproc_get_tsc_config(&pdev->dev, priv);
|
||||
if (error) {
|
||||
dev_err(&pdev->dev, "get_tsc_config failed: %d\n", error);
|
||||
return error;
|
||||
}
|
||||
|
||||
idev = devm_input_allocate_device(&pdev->dev);
|
||||
if (!idev) {
|
||||
dev_err(&pdev->dev, "failed to allocate input device\n");
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
priv->idev = idev;
|
||||
priv->pen_status = PEN_UP_STATUS;
|
||||
|
||||
/* Set input device info */
|
||||
idev->name = IPROC_TS_NAME;
|
||||
idev->dev.parent = &pdev->dev;
|
||||
|
||||
idev->id.bustype = BUS_HOST;
|
||||
idev->id.vendor = SERIO_UNKNOWN;
|
||||
idev->id.product = 0;
|
||||
idev->id.version = 0;
|
||||
|
||||
idev->evbit[0] = BIT_MASK(EV_KEY) | BIT_MASK(EV_ABS);
|
||||
__set_bit(BTN_TOUCH, idev->keybit);
|
||||
|
||||
input_set_abs_params(idev, ABS_X, X_MIN, priv->cfg_params.max_x,
|
||||
priv->cfg_params.fuzz_x, 0);
|
||||
input_set_abs_params(idev, ABS_Y, Y_MIN, priv->cfg_params.max_y,
|
||||
priv->cfg_params.fuzz_y, 0);
|
||||
|
||||
idev->open = iproc_ts_start;
|
||||
idev->close = iproc_ts_stop;
|
||||
|
||||
input_set_drvdata(idev, priv);
|
||||
platform_set_drvdata(pdev, priv);
|
||||
|
||||
/* get interrupt */
|
||||
irq = platform_get_irq(pdev, 0);
|
||||
if (irq < 0) {
|
||||
dev_err(&pdev->dev, "platform_get_irq failed: %d\n", irq);
|
||||
return irq;
|
||||
}
|
||||
|
||||
error = devm_request_irq(&pdev->dev, irq,
|
||||
iproc_touchscreen_interrupt,
|
||||
IRQF_SHARED, IPROC_TS_NAME, pdev);
|
||||
if (error)
|
||||
return error;
|
||||
|
||||
error = input_register_device(priv->idev);
|
||||
if (error) {
|
||||
dev_err(&pdev->dev,
|
||||
"failed to register input device: %d\n", error);
|
||||
return error;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct of_device_id iproc_ts_of_match[] = {
|
||||
{.compatible = "brcm,iproc-touchscreen", },
|
||||
{ },
|
||||
};
|
||||
MODULE_DEVICE_TABLE(of, iproc_ts_of_match);
|
||||
|
||||
static struct platform_driver iproc_ts_driver = {
|
||||
.probe = iproc_ts_probe,
|
||||
.driver = {
|
||||
.name = IPROC_TS_NAME,
|
||||
.of_match_table = of_match_ptr(iproc_ts_of_match),
|
||||
},
|
||||
};
|
||||
|
||||
module_platform_driver(iproc_ts_driver);
|
||||
|
||||
MODULE_DESCRIPTION("IPROC Touchscreen driver");
|
||||
MODULE_AUTHOR("Broadcom");
|
||||
MODULE_LICENSE("GPL v2");
|
316
drivers/input/touchscreen/chipone_icn8318.c
Normal file
316
drivers/input/touchscreen/chipone_icn8318.c
Normal file
@ -0,0 +1,316 @@
|
||||
/*
|
||||
* Driver for ChipOne icn8318 i2c touchscreen controller
|
||||
*
|
||||
* Copyright (c) 2015 Red Hat Inc.
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* Red Hat authors:
|
||||
* Hans de Goede <hdegoede@redhat.com>
|
||||
*/
|
||||
|
||||
#include <linux/gpio/consumer.h>
|
||||
#include <linux/interrupt.h>
|
||||
#include <linux/i2c.h>
|
||||
#include <linux/input.h>
|
||||
#include <linux/input/mt.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/of.h>
|
||||
|
||||
#define ICN8318_REG_POWER 4
|
||||
#define ICN8318_REG_TOUCHDATA 16
|
||||
|
||||
#define ICN8318_POWER_ACTIVE 0
|
||||
#define ICN8318_POWER_MONITOR 1
|
||||
#define ICN8318_POWER_HIBERNATE 2
|
||||
|
||||
#define ICN8318_MAX_TOUCHES 5
|
||||
|
||||
struct icn8318_touch {
|
||||
__u8 slot;
|
||||
__be16 x;
|
||||
__be16 y;
|
||||
__u8 pressure; /* Seems more like finger width then pressure really */
|
||||
__u8 event;
|
||||
/* The difference between 2 and 3 is unclear */
|
||||
#define ICN8318_EVENT_NO_DATA 1 /* No finger seen yet since wakeup */
|
||||
#define ICN8318_EVENT_UPDATE1 2 /* New or updated coordinates */
|
||||
#define ICN8318_EVENT_UPDATE2 3 /* New or updated coordinates */
|
||||
#define ICN8318_EVENT_END 4 /* Finger lifted */
|
||||
} __packed;
|
||||
|
||||
struct icn8318_touch_data {
|
||||
__u8 softbutton;
|
||||
__u8 touch_count;
|
||||
struct icn8318_touch touches[ICN8318_MAX_TOUCHES];
|
||||
} __packed;
|
||||
|
||||
struct icn8318_data {
|
||||
struct i2c_client *client;
|
||||
struct input_dev *input;
|
||||
struct gpio_desc *wake_gpio;
|
||||
u32 max_x;
|
||||
u32 max_y;
|
||||
bool invert_x;
|
||||
bool invert_y;
|
||||
bool swap_x_y;
|
||||
};
|
||||
|
||||
static int icn8318_read_touch_data(struct i2c_client *client,
|
||||
struct icn8318_touch_data *touch_data)
|
||||
{
|
||||
u8 reg = ICN8318_REG_TOUCHDATA;
|
||||
struct i2c_msg msg[2] = {
|
||||
{
|
||||
.addr = client->addr,
|
||||
.len = 1,
|
||||
.buf = ®
|
||||
},
|
||||
{
|
||||
.addr = client->addr,
|
||||
.flags = I2C_M_RD,
|
||||
.len = sizeof(struct icn8318_touch_data),
|
||||
.buf = (u8 *)touch_data
|
||||
}
|
||||
};
|
||||
|
||||
return i2c_transfer(client->adapter, msg, 2);
|
||||
}
|
||||
|
||||
static inline bool icn8318_touch_active(u8 event)
|
||||
{
|
||||
return (event == ICN8318_EVENT_UPDATE1) ||
|
||||
(event == ICN8318_EVENT_UPDATE2);
|
||||
}
|
||||
|
||||
static irqreturn_t icn8318_irq(int irq, void *dev_id)
|
||||
{
|
||||
struct icn8318_data *data = dev_id;
|
||||
struct device *dev = &data->client->dev;
|
||||
struct icn8318_touch_data touch_data;
|
||||
int i, ret, x, y;
|
||||
|
||||
ret = icn8318_read_touch_data(data->client, &touch_data);
|
||||
if (ret < 0) {
|
||||
dev_err(dev, "Error reading touch data: %d\n", ret);
|
||||
return IRQ_HANDLED;
|
||||
}
|
||||
|
||||
if (touch_data.softbutton) {
|
||||
/*
|
||||
* Other data is invalid when a softbutton is pressed.
|
||||
* This needs some extra devicetree bindings to map the icn8318
|
||||
* softbutton codes to evdev codes. Currently no known devices
|
||||
* use this.
|
||||
*/
|
||||
return IRQ_HANDLED;
|
||||
}
|
||||
|
||||
if (touch_data.touch_count > ICN8318_MAX_TOUCHES) {
|
||||
dev_warn(dev, "Too much touches %d > %d\n",
|
||||
touch_data.touch_count, ICN8318_MAX_TOUCHES);
|
||||
touch_data.touch_count = ICN8318_MAX_TOUCHES;
|
||||
}
|
||||
|
||||
for (i = 0; i < touch_data.touch_count; i++) {
|
||||
struct icn8318_touch *touch = &touch_data.touches[i];
|
||||
bool act = icn8318_touch_active(touch->event);
|
||||
|
||||
input_mt_slot(data->input, touch->slot);
|
||||
input_mt_report_slot_state(data->input, MT_TOOL_FINGER, act);
|
||||
if (!act)
|
||||
continue;
|
||||
|
||||
x = be16_to_cpu(touch->x);
|
||||
y = be16_to_cpu(touch->y);
|
||||
|
||||
if (data->invert_x)
|
||||
x = data->max_x - x;
|
||||
|
||||
if (data->invert_y)
|
||||
y = data->max_y - y;
|
||||
|
||||
if (!data->swap_x_y) {
|
||||
input_event(data->input, EV_ABS, ABS_MT_POSITION_X, x);
|
||||
input_event(data->input, EV_ABS, ABS_MT_POSITION_Y, y);
|
||||
} else {
|
||||
input_event(data->input, EV_ABS, ABS_MT_POSITION_X, y);
|
||||
input_event(data->input, EV_ABS, ABS_MT_POSITION_Y, x);
|
||||
}
|
||||
}
|
||||
|
||||
input_mt_sync_frame(data->input);
|
||||
input_sync(data->input);
|
||||
|
||||
return IRQ_HANDLED;
|
||||
}
|
||||
|
||||
static int icn8318_start(struct input_dev *dev)
|
||||
{
|
||||
struct icn8318_data *data = input_get_drvdata(dev);
|
||||
|
||||
enable_irq(data->client->irq);
|
||||
gpiod_set_value_cansleep(data->wake_gpio, 1);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void icn8318_stop(struct input_dev *dev)
|
||||
{
|
||||
struct icn8318_data *data = input_get_drvdata(dev);
|
||||
|
||||
disable_irq(data->client->irq);
|
||||
i2c_smbus_write_byte_data(data->client, ICN8318_REG_POWER,
|
||||
ICN8318_POWER_HIBERNATE);
|
||||
gpiod_set_value_cansleep(data->wake_gpio, 0);
|
||||
}
|
||||
|
||||
#ifdef CONFIG_PM_SLEEP
|
||||
static int icn8318_suspend(struct device *dev)
|
||||
{
|
||||
struct icn8318_data *data = i2c_get_clientdata(to_i2c_client(dev));
|
||||
|
||||
mutex_lock(&data->input->mutex);
|
||||
if (data->input->users)
|
||||
icn8318_stop(data->input);
|
||||
mutex_unlock(&data->input->mutex);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int icn8318_resume(struct device *dev)
|
||||
{
|
||||
struct icn8318_data *data = i2c_get_clientdata(to_i2c_client(dev));
|
||||
|
||||
mutex_lock(&data->input->mutex);
|
||||
if (data->input->users)
|
||||
icn8318_start(data->input);
|
||||
mutex_unlock(&data->input->mutex);
|
||||
|
||||
return 0;
|
||||
}
|
||||
#endif
|
||||
|
||||
static SIMPLE_DEV_PM_OPS(icn8318_pm_ops, icn8318_suspend, icn8318_resume);
|
||||
|
||||
static int icn8318_probe(struct i2c_client *client,
|
||||
const struct i2c_device_id *id)
|
||||
{
|
||||
struct device *dev = &client->dev;
|
||||
struct device_node *np = dev->of_node;
|
||||
struct icn8318_data *data;
|
||||
struct input_dev *input;
|
||||
u32 fuzz_x = 0, fuzz_y = 0;
|
||||
int error;
|
||||
|
||||
if (!client->irq) {
|
||||
dev_err(dev, "Error no irq specified\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
data = devm_kzalloc(dev, sizeof(*data), GFP_KERNEL);
|
||||
if (!data)
|
||||
return -ENOMEM;
|
||||
|
||||
data->wake_gpio = devm_gpiod_get(dev, "wake", GPIOD_OUT_LOW);
|
||||
if (IS_ERR(data->wake_gpio)) {
|
||||
error = PTR_ERR(data->wake_gpio);
|
||||
if (error != -EPROBE_DEFER)
|
||||
dev_err(dev, "Error getting wake gpio: %d\n", error);
|
||||
return error;
|
||||
}
|
||||
|
||||
if (of_property_read_u32(np, "touchscreen-size-x", &data->max_x) ||
|
||||
of_property_read_u32(np, "touchscreen-size-y", &data->max_y)) {
|
||||
dev_err(dev, "Error touchscreen-size-x and/or -y missing\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
/* Optional */
|
||||
of_property_read_u32(np, "touchscreen-fuzz-x", &fuzz_x);
|
||||
of_property_read_u32(np, "touchscreen-fuzz-y", &fuzz_y);
|
||||
data->invert_x = of_property_read_bool(np, "touchscreen-inverted-x");
|
||||
data->invert_y = of_property_read_bool(np, "touchscreen-inverted-y");
|
||||
data->swap_x_y = of_property_read_bool(np, "touchscreen-swapped-x-y");
|
||||
|
||||
input = devm_input_allocate_device(dev);
|
||||
if (!input)
|
||||
return -ENOMEM;
|
||||
|
||||
input->name = client->name;
|
||||
input->id.bustype = BUS_I2C;
|
||||
input->open = icn8318_start;
|
||||
input->close = icn8318_stop;
|
||||
input->dev.parent = dev;
|
||||
|
||||
if (!data->swap_x_y) {
|
||||
input_set_abs_params(input, ABS_MT_POSITION_X, 0,
|
||||
data->max_x, fuzz_x, 0);
|
||||
input_set_abs_params(input, ABS_MT_POSITION_Y, 0,
|
||||
data->max_y, fuzz_y, 0);
|
||||
} else {
|
||||
input_set_abs_params(input, ABS_MT_POSITION_X, 0,
|
||||
data->max_y, fuzz_y, 0);
|
||||
input_set_abs_params(input, ABS_MT_POSITION_Y, 0,
|
||||
data->max_x, fuzz_x, 0);
|
||||
}
|
||||
|
||||
error = input_mt_init_slots(input, ICN8318_MAX_TOUCHES,
|
||||
INPUT_MT_DIRECT | INPUT_MT_DROP_UNUSED);
|
||||
if (error)
|
||||
return error;
|
||||
|
||||
data->client = client;
|
||||
data->input = input;
|
||||
input_set_drvdata(input, data);
|
||||
|
||||
error = devm_request_threaded_irq(dev, client->irq, NULL, icn8318_irq,
|
||||
IRQF_ONESHOT, client->name, data);
|
||||
if (error) {
|
||||
dev_err(dev, "Error requesting irq: %d\n", error);
|
||||
return error;
|
||||
}
|
||||
|
||||
/* Stop device till opened */
|
||||
icn8318_stop(data->input);
|
||||
|
||||
error = input_register_device(input);
|
||||
if (error)
|
||||
return error;
|
||||
|
||||
i2c_set_clientdata(client, data);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct of_device_id icn8318_of_match[] = {
|
||||
{ .compatible = "chipone,icn8318" },
|
||||
{ }
|
||||
};
|
||||
MODULE_DEVICE_TABLE(of, icn8318_of_match);
|
||||
|
||||
/* This is useless for OF-enabled devices, but it is needed by I2C subsystem */
|
||||
static const struct i2c_device_id icn8318_i2c_id[] = {
|
||||
{ },
|
||||
};
|
||||
MODULE_DEVICE_TABLE(i2c, icn8318_i2c_id);
|
||||
|
||||
static struct i2c_driver icn8318_driver = {
|
||||
.driver = {
|
||||
.owner = THIS_MODULE,
|
||||
.name = "chipone_icn8318",
|
||||
.pm = &icn8318_pm_ops,
|
||||
.of_match_table = icn8318_of_match,
|
||||
},
|
||||
.probe = icn8318_probe,
|
||||
.id_table = icn8318_i2c_id,
|
||||
};
|
||||
|
||||
module_i2c_driver(icn8318_driver);
|
||||
|
||||
MODULE_DESCRIPTION("ChipOne icn8318 I2C Touchscreen Driver");
|
||||
MODULE_AUTHOR("Hans de Goede <hdegoede@redhat.com>");
|
||||
MODULE_LICENSE("GPL");
|
@ -37,6 +37,7 @@
|
||||
#include <linux/gpio.h>
|
||||
#include <linux/of_gpio.h>
|
||||
#include <linux/input/mt.h>
|
||||
#include <linux/input/touchscreen.h>
|
||||
#include <linux/input/edt-ft5x06.h>
|
||||
|
||||
#define MAX_SUPPORT_POINTS 5
|
||||
@ -1034,7 +1035,6 @@ static int edt_ft5x06_ts_probe(struct i2c_client *client,
|
||||
input->id.bustype = BUS_I2C;
|
||||
input->dev.parent = &client->dev;
|
||||
|
||||
__set_bit(EV_SYN, input->evbit);
|
||||
__set_bit(EV_KEY, input->evbit);
|
||||
__set_bit(EV_ABS, input->evbit);
|
||||
__set_bit(BTN_TOUCH, input->keybit);
|
||||
@ -1044,6 +1044,10 @@ static int edt_ft5x06_ts_probe(struct i2c_client *client,
|
||||
0, tsdata->num_x * 64 - 1, 0, 0);
|
||||
input_set_abs_params(input, ABS_MT_POSITION_Y,
|
||||
0, tsdata->num_y * 64 - 1, 0, 0);
|
||||
|
||||
if (!pdata)
|
||||
touchscreen_parse_of_params(input);
|
||||
|
||||
error = input_mt_init_slots(input, MAX_SUPPORT_POINTS, 0);
|
||||
if (error) {
|
||||
dev_err(&client->dev, "Unable to init MT slots.\n");
|
||||
|
@ -98,7 +98,6 @@
|
||||
#define MAX_FW_UPDATE_RETRIES 30
|
||||
|
||||
#define ELAN_FW_PAGESIZE 132
|
||||
#define ELAN_FW_FILENAME "elants_i2c.bin"
|
||||
|
||||
/* calibration timeout definition */
|
||||
#define ELAN_CALI_TIMEOUT_MSEC 10000
|
||||
@ -697,12 +696,19 @@ static int elants_i2c_fw_update(struct elants_data *ts)
|
||||
{
|
||||
struct i2c_client *client = ts->client;
|
||||
const struct firmware *fw;
|
||||
char *fw_name;
|
||||
int error;
|
||||
|
||||
error = request_firmware(&fw, ELAN_FW_FILENAME, &client->dev);
|
||||
fw_name = kasprintf(GFP_KERNEL, "elants_i2c_%4x.bin", ts->hw_version);
|
||||
if (!fw_name)
|
||||
return -ENOMEM;
|
||||
|
||||
dev_info(&client->dev, "requesting fw name = %s\n", fw_name);
|
||||
error = request_firmware(&fw, fw_name, &client->dev);
|
||||
kfree(fw_name);
|
||||
if (error) {
|
||||
dev_err(&client->dev, "failed to request firmware %s: %d\n",
|
||||
ELAN_FW_FILENAME, error);
|
||||
dev_err(&client->dev, "failed to request firmware: %d\n",
|
||||
error);
|
||||
return error;
|
||||
}
|
||||
|
||||
|
@ -23,6 +23,8 @@
|
||||
#include <linux/irq.h>
|
||||
#include <linux/interrupt.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/acpi.h>
|
||||
#include <linux/of.h>
|
||||
#include <asm/unaligned.h>
|
||||
|
||||
struct goodix_ts_data {
|
||||
@ -48,6 +50,7 @@ struct goodix_ts_data {
|
||||
#define GOODIX_REG_VERSION 0x8140
|
||||
|
||||
#define RESOLUTION_LOC 1
|
||||
#define MAX_CONTACTS_LOC 5
|
||||
#define TRIGGER_LOC 6
|
||||
|
||||
static const unsigned long goodix_irq_flags[] = {
|
||||
@ -99,7 +102,7 @@ static int goodix_ts_read_input_report(struct goodix_ts_data *ts, u8 *data)
|
||||
}
|
||||
|
||||
touch_num = data[0] & 0x0f;
|
||||
if (touch_num > GOODIX_MAX_CONTACTS)
|
||||
if (touch_num > ts->max_touch_num)
|
||||
return -EPROTO;
|
||||
|
||||
if (touch_num > 1) {
|
||||
@ -141,7 +144,7 @@ static void goodix_ts_report_touch(struct goodix_ts_data *ts, u8 *coor_data)
|
||||
*/
|
||||
static void goodix_process_events(struct goodix_ts_data *ts)
|
||||
{
|
||||
u8 point_data[1 + GOODIX_CONTACT_SIZE * GOODIX_MAX_CONTACTS];
|
||||
u8 point_data[1 + GOODIX_CONTACT_SIZE * ts->max_touch_num];
|
||||
int touch_num;
|
||||
int i;
|
||||
|
||||
@ -202,21 +205,23 @@ static void goodix_read_config(struct goodix_ts_data *ts)
|
||||
ts->abs_x_max = GOODIX_MAX_WIDTH;
|
||||
ts->abs_y_max = GOODIX_MAX_HEIGHT;
|
||||
ts->int_trigger_type = GOODIX_INT_TRIGGER;
|
||||
ts->max_touch_num = GOODIX_MAX_CONTACTS;
|
||||
return;
|
||||
}
|
||||
|
||||
ts->abs_x_max = get_unaligned_le16(&config[RESOLUTION_LOC]);
|
||||
ts->abs_y_max = get_unaligned_le16(&config[RESOLUTION_LOC + 2]);
|
||||
ts->int_trigger_type = (config[TRIGGER_LOC]) & 0x03;
|
||||
if (!ts->abs_x_max || !ts->abs_y_max) {
|
||||
ts->int_trigger_type = config[TRIGGER_LOC] & 0x03;
|
||||
ts->max_touch_num = config[MAX_CONTACTS_LOC] & 0x0f;
|
||||
if (!ts->abs_x_max || !ts->abs_y_max || !ts->max_touch_num) {
|
||||
dev_err(&ts->client->dev,
|
||||
"Invalid config, using defaults\n");
|
||||
ts->abs_x_max = GOODIX_MAX_WIDTH;
|
||||
ts->abs_y_max = GOODIX_MAX_HEIGHT;
|
||||
ts->max_touch_num = GOODIX_MAX_CONTACTS;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* goodix_read_version - Read goodix touchscreen version
|
||||
*
|
||||
@ -295,7 +300,7 @@ static int goodix_request_input_dev(struct goodix_ts_data *ts)
|
||||
input_set_abs_params(ts->input_dev, ABS_MT_WIDTH_MAJOR, 0, 255, 0, 0);
|
||||
input_set_abs_params(ts->input_dev, ABS_MT_TOUCH_MAJOR, 0, 255, 0, 0);
|
||||
|
||||
input_mt_init_slots(ts->input_dev, GOODIX_MAX_CONTACTS,
|
||||
input_mt_init_slots(ts->input_dev, ts->max_touch_num,
|
||||
INPUT_MT_DIRECT | INPUT_MT_DROP_UNUSED);
|
||||
|
||||
ts->input_dev->name = "Goodix Capacitive TouchScreen";
|
||||
@ -372,11 +377,27 @@ static const struct i2c_device_id goodix_ts_id[] = {
|
||||
{ }
|
||||
};
|
||||
|
||||
#ifdef CONFIG_ACPI
|
||||
static const struct acpi_device_id goodix_acpi_match[] = {
|
||||
{ "GDIX1001", 0 },
|
||||
{ }
|
||||
};
|
||||
MODULE_DEVICE_TABLE(acpi, goodix_acpi_match);
|
||||
#endif
|
||||
|
||||
#ifdef CONFIG_OF
|
||||
static const struct of_device_id goodix_of_match[] = {
|
||||
{ .compatible = "goodix,gt911" },
|
||||
{ .compatible = "goodix,gt9110" },
|
||||
{ .compatible = "goodix,gt912" },
|
||||
{ .compatible = "goodix,gt927" },
|
||||
{ .compatible = "goodix,gt9271" },
|
||||
{ .compatible = "goodix,gt928" },
|
||||
{ .compatible = "goodix,gt967" },
|
||||
{ }
|
||||
};
|
||||
MODULE_DEVICE_TABLE(of, goodix_of_match);
|
||||
#endif
|
||||
|
||||
static struct i2c_driver goodix_ts_driver = {
|
||||
.probe = goodix_ts_probe,
|
||||
@ -384,7 +405,8 @@ static struct i2c_driver goodix_ts_driver = {
|
||||
.driver = {
|
||||
.name = "Goodix-TS",
|
||||
.owner = THIS_MODULE,
|
||||
.acpi_match_table = goodix_acpi_match,
|
||||
.acpi_match_table = ACPI_PTR(goodix_acpi_match),
|
||||
.of_match_table = of_match_ptr(goodix_of_match),
|
||||
},
|
||||
};
|
||||
module_i2c_driver(goodix_ts_driver);
|
||||
|
@ -11,8 +11,41 @@
|
||||
|
||||
#include <linux/of.h>
|
||||
#include <linux/input.h>
|
||||
#include <linux/input/mt.h>
|
||||
#include <linux/input/touchscreen.h>
|
||||
|
||||
static u32 of_get_optional_u32(struct device_node *np,
|
||||
const char *property)
|
||||
{
|
||||
u32 val = 0;
|
||||
|
||||
of_property_read_u32(np, property, &val);
|
||||
|
||||
return val;
|
||||
}
|
||||
|
||||
static void touchscreen_set_params(struct input_dev *dev,
|
||||
unsigned long axis,
|
||||
int max, int fuzz)
|
||||
{
|
||||
struct input_absinfo *absinfo;
|
||||
|
||||
if (!test_bit(axis, dev->absbit)) {
|
||||
/*
|
||||
* Emit a warning only if the axis is not a multitouch
|
||||
* axis, which might not be set by the driver.
|
||||
*/
|
||||
if (!input_is_mt_axis(axis))
|
||||
dev_warn(&dev->dev,
|
||||
"DT specifies parameters but the axis is not set up\n");
|
||||
return;
|
||||
}
|
||||
|
||||
absinfo = &dev->absinfo[axis];
|
||||
absinfo->maximum = max;
|
||||
absinfo->fuzz = fuzz;
|
||||
}
|
||||
|
||||
/**
|
||||
* touchscreen_parse_of_params - parse common touchscreen DT properties
|
||||
* @dev: device that should be parsed
|
||||
@ -24,22 +57,31 @@
|
||||
void touchscreen_parse_of_params(struct input_dev *dev)
|
||||
{
|
||||
struct device_node *np = dev->dev.parent->of_node;
|
||||
struct input_absinfo *absinfo;
|
||||
u32 maximum, fuzz;
|
||||
|
||||
input_alloc_absinfo(dev);
|
||||
if (!dev->absinfo)
|
||||
return;
|
||||
|
||||
absinfo = &dev->absinfo[ABS_X];
|
||||
of_property_read_u32(np, "touchscreen-size-x", &absinfo->maximum);
|
||||
of_property_read_u32(np, "touchscreen-fuzz-x", &absinfo->fuzz);
|
||||
maximum = of_get_optional_u32(np, "touchscreen-size-x");
|
||||
fuzz = of_get_optional_u32(np, "touchscreen-fuzz-x");
|
||||
if (maximum || fuzz) {
|
||||
touchscreen_set_params(dev, ABS_X, maximum, fuzz);
|
||||
touchscreen_set_params(dev, ABS_MT_POSITION_X, maximum, fuzz);
|
||||
}
|
||||
|
||||
absinfo = &dev->absinfo[ABS_Y];
|
||||
of_property_read_u32(np, "touchscreen-size-y", &absinfo->maximum);
|
||||
of_property_read_u32(np, "touchscreen-fuzz-y", &absinfo->fuzz);
|
||||
maximum = of_get_optional_u32(np, "touchscreen-size-y");
|
||||
fuzz = of_get_optional_u32(np, "touchscreen-fuzz-y");
|
||||
if (maximum || fuzz) {
|
||||
touchscreen_set_params(dev, ABS_Y, maximum, fuzz);
|
||||
touchscreen_set_params(dev, ABS_MT_POSITION_Y, maximum, fuzz);
|
||||
}
|
||||
|
||||
absinfo = &dev->absinfo[ABS_PRESSURE];
|
||||
of_property_read_u32(np, "touchscreen-max-pressure", &absinfo->maximum);
|
||||
of_property_read_u32(np, "touchscreen-fuzz-pressure", &absinfo->fuzz);
|
||||
maximum = of_get_optional_u32(np, "touchscreen-max-pressure");
|
||||
fuzz = of_get_optional_u32(np, "touchscreen-fuzz-pressure");
|
||||
if (maximum || fuzz) {
|
||||
touchscreen_set_params(dev, ABS_PRESSURE, maximum, fuzz);
|
||||
touchscreen_set_params(dev, ABS_MT_PRESSURE, maximum, fuzz);
|
||||
}
|
||||
}
|
||||
EXPORT_SYMBOL(touchscreen_parse_of_params);
|
||||
|
@ -30,6 +30,10 @@
|
||||
* These kinds of heuristics are just asking for trouble (and don't belong
|
||||
* in the kernel). So this driver offers straight forward, reliable single
|
||||
* touch functionality only.
|
||||
*
|
||||
* s.a. A20 User Manual "1.15 TP" (Documentation/arm/sunxi/README)
|
||||
* (looks like the description in the A20 User Manual v1.3 is better
|
||||
* than the one in the A10 User Manual v.1.5)
|
||||
*/
|
||||
|
||||
#include <linux/err.h>
|
||||
@ -193,7 +197,7 @@ static int sun4i_get_temp(const struct sun4i_ts_data *ts, long *temp)
|
||||
if (ts->temp_data == -1)
|
||||
return -EAGAIN;
|
||||
|
||||
*temp = (ts->temp_data - ts->temp_offset) * ts->temp_step;
|
||||
*temp = ts->temp_data * ts->temp_step - ts->temp_offset;
|
||||
|
||||
return 0;
|
||||
}
|
||||
@ -246,6 +250,8 @@ static int sun4i_ts_probe(struct platform_device *pdev)
|
||||
int error;
|
||||
u32 reg;
|
||||
bool ts_attached;
|
||||
u32 tp_sensitive_adjust = 15;
|
||||
u32 filter_type = 1;
|
||||
|
||||
ts = devm_kzalloc(dev, sizeof(struct sun4i_ts_data), GFP_KERNEL);
|
||||
if (!ts)
|
||||
@ -255,22 +261,31 @@ static int sun4i_ts_probe(struct platform_device *pdev)
|
||||
ts->ignore_fifo_data = true;
|
||||
ts->temp_data = -1;
|
||||
if (of_device_is_compatible(np, "allwinner,sun6i-a31-ts")) {
|
||||
/* Allwinner SDK has temperature = -271 + (value / 6) (C) */
|
||||
ts->temp_offset = 1626;
|
||||
/* Allwinner SDK has temperature (C) = (value / 6) - 271 */
|
||||
ts->temp_offset = 271000;
|
||||
ts->temp_step = 167;
|
||||
} else if (of_device_is_compatible(np, "allwinner,sun4i-a10-ts")) {
|
||||
/*
|
||||
* The A10 temperature sensor has quite a wide spread, these
|
||||
* parameters are based on the averaging of the calibration
|
||||
* results of 4 completely different boards, with a spread of
|
||||
* temp_step from 0.096 - 0.170 and temp_offset from 176 - 331.
|
||||
*/
|
||||
ts->temp_offset = 257000;
|
||||
ts->temp_step = 133;
|
||||
} else {
|
||||
/*
|
||||
* The user manuals do not contain the formula for calculating
|
||||
* the temperature. The formula used here is from the AXP209,
|
||||
* which is designed by X-Powers, an affiliate of Allwinner:
|
||||
*
|
||||
* temperature = -144.7 + (value * 0.1)
|
||||
* temperature (C) = (value * 0.1) - 144.7
|
||||
*
|
||||
* Allwinner does not have any documentation whatsoever for
|
||||
* this hardware. Moreover, it is claimed that the sensor
|
||||
* is inaccurate and cannot work properly.
|
||||
*/
|
||||
ts->temp_offset = 1447;
|
||||
ts->temp_offset = 144700;
|
||||
ts->temp_step = 100;
|
||||
}
|
||||
|
||||
@ -313,14 +328,20 @@ static int sun4i_ts_probe(struct platform_device *pdev)
|
||||
ts->base + TP_CTRL0);
|
||||
|
||||
/*
|
||||
* sensitive_adjust = 15 : max, which is not all that sensitive,
|
||||
* tp_sensitive_adjust is an optional property
|
||||
* tp_mode = 0 : only x and y coordinates, as we don't use dual touch
|
||||
*/
|
||||
writel(TP_SENSITIVE_ADJUST(15) | TP_MODE_SELECT(0),
|
||||
of_property_read_u32(np, "allwinner,tp-sensitive-adjust",
|
||||
&tp_sensitive_adjust);
|
||||
writel(TP_SENSITIVE_ADJUST(tp_sensitive_adjust) | TP_MODE_SELECT(0),
|
||||
ts->base + TP_CTRL2);
|
||||
|
||||
/* Enable median filter, type 1 : 5/3 */
|
||||
writel(FILTER_EN(1) | FILTER_TYPE(1), ts->base + TP_CTRL3);
|
||||
/*
|
||||
* Enable median and averaging filter, optional property for
|
||||
* filter type.
|
||||
*/
|
||||
of_property_read_u32(np, "allwinner,filter-type", &filter_type);
|
||||
writel(FILTER_EN(1) | FILTER_TYPE(filter_type), ts->base + TP_CTRL3);
|
||||
|
||||
/* Enable temperature measurement, period 1953 (2 seconds) */
|
||||
writel(TEMP_ENABLE(1) | TEMP_PERIOD(1953), ts->base + TP_TPR);
|
||||
@ -330,10 +351,10 @@ static int sun4i_ts_probe(struct platform_device *pdev)
|
||||
* finally enable tp mode.
|
||||
*/
|
||||
reg = STYLUS_UP_DEBOUN(5) | STYLUS_UP_DEBOUN_EN(1);
|
||||
if (of_device_is_compatible(np, "allwinner,sun4i-a10-ts"))
|
||||
reg |= TP_MODE_EN(1);
|
||||
else
|
||||
if (of_device_is_compatible(np, "allwinner,sun6i-a31-ts"))
|
||||
reg |= SUN6I_TP_MODE_EN(1);
|
||||
else
|
||||
reg |= TP_MODE_EN(1);
|
||||
writel(reg, ts->base + TP_CTRL1);
|
||||
|
||||
/*
|
||||
@ -383,6 +404,7 @@ static int sun4i_ts_remove(struct platform_device *pdev)
|
||||
|
||||
static const struct of_device_id sun4i_ts_of_match[] = {
|
||||
{ .compatible = "allwinner,sun4i-a10-ts", },
|
||||
{ .compatible = "allwinner,sun5i-a13-ts", },
|
||||
{ .compatible = "allwinner,sun6i-a31-ts", },
|
||||
{ /* sentinel */ }
|
||||
};
|
||||
|
286
drivers/input/touchscreen/sx8654.c
Normal file
286
drivers/input/touchscreen/sx8654.c
Normal file
@ -0,0 +1,286 @@
|
||||
/*
|
||||
* Driver for Semtech SX8654 I2C touchscreen controller.
|
||||
*
|
||||
* Copyright (c) 2015 Armadeus Systems
|
||||
* Sébastien Szymanski <sebastien.szymanski@armadeus.com>
|
||||
*
|
||||
* Using code from:
|
||||
* - sx865x.c
|
||||
* Copyright (c) 2013 U-MoBo Srl
|
||||
* Pierluigi Passaro <p.passaro@u-mobo.com>
|
||||
* - sx8650.c
|
||||
* Copyright (c) 2009 Wayne Roberts
|
||||
* - tsc2007.c
|
||||
* Copyright (c) 2008 Kwangwoo Lee
|
||||
* - ads7846.c
|
||||
* Copyright (c) 2005 David Brownell
|
||||
* Copyright (c) 2006 Nokia Corporation
|
||||
* - corgi_ts.c
|
||||
* Copyright (C) 2004-2005 Richard Purdie
|
||||
* - omap_ts.[hc], ads7846.h, ts_osk.c
|
||||
* Copyright (C) 2002 MontaVista Software
|
||||
* Copyright (C) 2004 Texas Instruments
|
||||
* Copyright (C) 2005 Dirk Behme
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License version 2 as
|
||||
* published by the Free Software Foundation.
|
||||
*/
|
||||
|
||||
#include <linux/input.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/of.h>
|
||||
#include <linux/i2c.h>
|
||||
#include <linux/interrupt.h>
|
||||
#include <linux/irq.h>
|
||||
|
||||
/* register addresses */
|
||||
#define I2C_REG_TOUCH0 0x00
|
||||
#define I2C_REG_TOUCH1 0x01
|
||||
#define I2C_REG_CHANMASK 0x04
|
||||
#define I2C_REG_IRQMASK 0x22
|
||||
#define I2C_REG_IRQSRC 0x23
|
||||
#define I2C_REG_SOFTRESET 0x3f
|
||||
|
||||
/* commands */
|
||||
#define CMD_READ_REGISTER 0x40
|
||||
#define CMD_MANUAL 0xc0
|
||||
#define CMD_PENTRG 0xe0
|
||||
|
||||
/* value for I2C_REG_SOFTRESET */
|
||||
#define SOFTRESET_VALUE 0xde
|
||||
|
||||
/* bits for I2C_REG_IRQSRC */
|
||||
#define IRQ_PENTOUCH_TOUCHCONVDONE 0x08
|
||||
#define IRQ_PENRELEASE 0x04
|
||||
|
||||
/* bits for RegTouch1 */
|
||||
#define CONDIRQ 0x20
|
||||
#define FILT_7SA 0x03
|
||||
|
||||
/* bits for I2C_REG_CHANMASK */
|
||||
#define CONV_X 0x80
|
||||
#define CONV_Y 0x40
|
||||
|
||||
/* coordinates rate: higher nibble of CTRL0 register */
|
||||
#define RATE_MANUAL 0x00
|
||||
#define RATE_5000CPS 0xf0
|
||||
|
||||
/* power delay: lower nibble of CTRL0 register */
|
||||
#define POWDLY_1_1MS 0x0b
|
||||
|
||||
#define MAX_12BIT ((1 << 12) - 1)
|
||||
|
||||
struct sx8654 {
|
||||
struct input_dev *input;
|
||||
struct i2c_client *client;
|
||||
};
|
||||
|
||||
static irqreturn_t sx8654_irq(int irq, void *handle)
|
||||
{
|
||||
struct sx8654 *sx8654 = handle;
|
||||
int irqsrc;
|
||||
u8 data[4];
|
||||
unsigned int x, y;
|
||||
int retval;
|
||||
|
||||
irqsrc = i2c_smbus_read_byte_data(sx8654->client,
|
||||
CMD_READ_REGISTER | I2C_REG_IRQSRC);
|
||||
dev_dbg(&sx8654->client->dev, "irqsrc = 0x%x", irqsrc);
|
||||
|
||||
if (irqsrc < 0)
|
||||
goto out;
|
||||
|
||||
if (irqsrc & IRQ_PENRELEASE) {
|
||||
dev_dbg(&sx8654->client->dev, "pen release interrupt");
|
||||
|
||||
input_report_key(sx8654->input, BTN_TOUCH, 0);
|
||||
input_sync(sx8654->input);
|
||||
}
|
||||
|
||||
if (irqsrc & IRQ_PENTOUCH_TOUCHCONVDONE) {
|
||||
dev_dbg(&sx8654->client->dev, "pen touch interrupt");
|
||||
|
||||
retval = i2c_master_recv(sx8654->client, data, sizeof(data));
|
||||
if (retval != sizeof(data))
|
||||
goto out;
|
||||
|
||||
/* invalid data */
|
||||
if (unlikely(data[0] & 0x80 || data[2] & 0x80))
|
||||
goto out;
|
||||
|
||||
x = ((data[0] & 0xf) << 8) | (data[1]);
|
||||
y = ((data[2] & 0xf) << 8) | (data[3]);
|
||||
|
||||
input_report_abs(sx8654->input, ABS_X, x);
|
||||
input_report_abs(sx8654->input, ABS_Y, y);
|
||||
input_report_key(sx8654->input, BTN_TOUCH, 1);
|
||||
input_sync(sx8654->input);
|
||||
|
||||
dev_dbg(&sx8654->client->dev, "point(%4d,%4d)\n", x, y);
|
||||
}
|
||||
|
||||
out:
|
||||
return IRQ_HANDLED;
|
||||
}
|
||||
|
||||
static int sx8654_open(struct input_dev *dev)
|
||||
{
|
||||
struct sx8654 *sx8654 = input_get_drvdata(dev);
|
||||
struct i2c_client *client = sx8654->client;
|
||||
int error;
|
||||
|
||||
/* enable pen trigger mode */
|
||||
error = i2c_smbus_write_byte_data(client, I2C_REG_TOUCH0,
|
||||
RATE_5000CPS | POWDLY_1_1MS);
|
||||
if (error) {
|
||||
dev_err(&client->dev, "writing to I2C_REG_TOUCH0 failed");
|
||||
return error;
|
||||
}
|
||||
|
||||
error = i2c_smbus_write_byte(client, CMD_PENTRG);
|
||||
if (error) {
|
||||
dev_err(&client->dev, "writing command CMD_PENTRG failed");
|
||||
return error;
|
||||
}
|
||||
|
||||
enable_irq(client->irq);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void sx8654_close(struct input_dev *dev)
|
||||
{
|
||||
struct sx8654 *sx8654 = input_get_drvdata(dev);
|
||||
struct i2c_client *client = sx8654->client;
|
||||
int error;
|
||||
|
||||
disable_irq(client->irq);
|
||||
|
||||
/* enable manual mode mode */
|
||||
error = i2c_smbus_write_byte(client, CMD_MANUAL);
|
||||
if (error) {
|
||||
dev_err(&client->dev, "writing command CMD_MANUAL failed");
|
||||
return;
|
||||
}
|
||||
|
||||
error = i2c_smbus_write_byte_data(client, I2C_REG_TOUCH0, 0);
|
||||
if (error) {
|
||||
dev_err(&client->dev, "writing to I2C_REG_TOUCH0 failed");
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
static int sx8654_probe(struct i2c_client *client,
|
||||
const struct i2c_device_id *id)
|
||||
{
|
||||
struct sx8654 *sx8654;
|
||||
struct input_dev *input;
|
||||
int error;
|
||||
|
||||
if (!i2c_check_functionality(client->adapter,
|
||||
I2C_FUNC_SMBUS_READ_WORD_DATA))
|
||||
return -ENXIO;
|
||||
|
||||
sx8654 = devm_kzalloc(&client->dev, sizeof(*sx8654), GFP_KERNEL);
|
||||
if (!sx8654)
|
||||
return -ENOMEM;
|
||||
|
||||
input = devm_input_allocate_device(&client->dev);
|
||||
if (!sx8654)
|
||||
return -ENOMEM;
|
||||
|
||||
input->name = "SX8654 I2C Touchscreen";
|
||||
input->id.bustype = BUS_I2C;
|
||||
input->dev.parent = &client->dev;
|
||||
input->open = sx8654_open;
|
||||
input->close = sx8654_close;
|
||||
|
||||
__set_bit(INPUT_PROP_DIRECT, input->propbit);
|
||||
input_set_capability(input, EV_KEY, BTN_TOUCH);
|
||||
input_set_abs_params(input, ABS_X, 0, MAX_12BIT, 0, 0);
|
||||
input_set_abs_params(input, ABS_Y, 0, MAX_12BIT, 0, 0);
|
||||
|
||||
sx8654->client = client;
|
||||
sx8654->input = input;
|
||||
|
||||
input_set_drvdata(sx8654->input, sx8654);
|
||||
|
||||
error = i2c_smbus_write_byte_data(client, I2C_REG_SOFTRESET,
|
||||
SOFTRESET_VALUE);
|
||||
if (error) {
|
||||
dev_err(&client->dev, "writing softreset value failed");
|
||||
return error;
|
||||
}
|
||||
|
||||
error = i2c_smbus_write_byte_data(client, I2C_REG_CHANMASK,
|
||||
CONV_X | CONV_Y);
|
||||
if (error) {
|
||||
dev_err(&client->dev, "writing to I2C_REG_CHANMASK failed");
|
||||
return error;
|
||||
}
|
||||
|
||||
error = i2c_smbus_write_byte_data(client, I2C_REG_IRQMASK,
|
||||
IRQ_PENTOUCH_TOUCHCONVDONE |
|
||||
IRQ_PENRELEASE);
|
||||
if (error) {
|
||||
dev_err(&client->dev, "writing to I2C_REG_IRQMASK failed");
|
||||
return error;
|
||||
}
|
||||
|
||||
error = i2c_smbus_write_byte_data(client, I2C_REG_TOUCH1,
|
||||
CONDIRQ | FILT_7SA);
|
||||
if (error) {
|
||||
dev_err(&client->dev, "writing to I2C_REG_TOUCH1 failed");
|
||||
return error;
|
||||
}
|
||||
|
||||
error = devm_request_threaded_irq(&client->dev, client->irq,
|
||||
NULL, sx8654_irq,
|
||||
IRQF_TRIGGER_FALLING | IRQF_ONESHOT,
|
||||
client->name, sx8654);
|
||||
if (error) {
|
||||
dev_err(&client->dev,
|
||||
"Failed to enable IRQ %d, error: %d\n",
|
||||
client->irq, error);
|
||||
return error;
|
||||
}
|
||||
|
||||
/* Disable the IRQ, we'll enable it in sx8654_open() */
|
||||
disable_irq(client->irq);
|
||||
|
||||
error = input_register_device(sx8654->input);
|
||||
if (error)
|
||||
return error;
|
||||
|
||||
i2c_set_clientdata(client, sx8654);
|
||||
return 0;
|
||||
}
|
||||
|
||||
#ifdef CONFIG_OF
|
||||
static const struct of_device_id sx8654_of_match[] = {
|
||||
{ .compatible = "semtech,sx8654", },
|
||||
{ },
|
||||
};
|
||||
MODULE_DEVICE_TABLE(of, sx8654_of_match);
|
||||
#endif
|
||||
|
||||
static const struct i2c_device_id sx8654_id_table[] = {
|
||||
{ "semtech_sx8654", 0 },
|
||||
{ },
|
||||
};
|
||||
MODULE_DEVICE_TABLE(i2c, sx8654_id_table);
|
||||
|
||||
static struct i2c_driver sx8654_driver = {
|
||||
.driver = {
|
||||
.name = "sx8654",
|
||||
.of_match_table = of_match_ptr(sx8654_of_match),
|
||||
},
|
||||
.id_table = sx8654_id_table,
|
||||
.probe = sx8654_probe,
|
||||
};
|
||||
module_i2c_driver(sx8654_driver);
|
||||
|
||||
MODULE_AUTHOR("Sébastien Szymanski <sebastien.szymanski@armadeus.com>");
|
||||
MODULE_DESCRIPTION("Semtech SX8654 I2C Touchscreen Driver");
|
||||
MODULE_LICENSE("GPL");
|
@ -75,7 +75,7 @@ struct tsc2007 {
|
||||
u16 model;
|
||||
u16 x_plate_ohms;
|
||||
u16 max_rt;
|
||||
unsigned long poll_period;
|
||||
unsigned long poll_period; /* in jiffies */
|
||||
int fuzzx;
|
||||
int fuzzy;
|
||||
int fuzzz;
|
||||
@ -214,8 +214,7 @@ static irqreturn_t tsc2007_soft_irq(int irq, void *handle)
|
||||
dev_dbg(&ts->client->dev, "ignored pressure %d\n", rt);
|
||||
}
|
||||
|
||||
wait_event_timeout(ts->wait, ts->stopped,
|
||||
msecs_to_jiffies(ts->poll_period));
|
||||
wait_event_timeout(ts->wait, ts->stopped, ts->poll_period);
|
||||
}
|
||||
|
||||
dev_dbg(&ts->client->dev, "UP\n");
|
||||
@ -314,9 +313,9 @@ static int tsc2007_probe_dt(struct i2c_client *client, struct tsc2007 *ts)
|
||||
ts->fuzzz = val32;
|
||||
|
||||
if (!of_property_read_u64(np, "ti,poll-period", &val64))
|
||||
ts->poll_period = val64;
|
||||
ts->poll_period = msecs_to_jiffies(val64);
|
||||
else
|
||||
ts->poll_period = 1;
|
||||
ts->poll_period = msecs_to_jiffies(1);
|
||||
|
||||
if (!of_property_read_u32(np, "ti,x-plate-ohms", &val32)) {
|
||||
ts->x_plate_ohms = val32;
|
||||
@ -350,7 +349,7 @@ static int tsc2007_probe_pdev(struct i2c_client *client, struct tsc2007 *ts,
|
||||
ts->model = pdata->model;
|
||||
ts->x_plate_ohms = pdata->x_plate_ohms;
|
||||
ts->max_rt = pdata->max_rt ? : MAX_12BIT;
|
||||
ts->poll_period = pdata->poll_period ? : 1;
|
||||
ts->poll_period = msecs_to_jiffies(pdata->poll_period ? : 1);
|
||||
ts->get_pendown_state = pdata->get_pendown_state;
|
||||
ts->clear_penirq = pdata->clear_penirq;
|
||||
ts->fuzzx = pdata->fuzzx;
|
||||
|
@ -132,6 +132,7 @@ enum {
|
||||
DEVTYPE_GUNZE,
|
||||
DEVTYPE_DMC_TSC10,
|
||||
DEVTYPE_IRTOUCH,
|
||||
DEVTYPE_IRTOUCH_HIRES,
|
||||
DEVTYPE_IDEALTEK,
|
||||
DEVTYPE_GENERAL_TOUCH,
|
||||
DEVTYPE_GOTOP,
|
||||
@ -198,6 +199,7 @@ static const struct usb_device_id usbtouch_devices[] = {
|
||||
#ifdef CONFIG_TOUCHSCREEN_USB_IRTOUCH
|
||||
{USB_DEVICE(0x595a, 0x0001), .driver_info = DEVTYPE_IRTOUCH},
|
||||
{USB_DEVICE(0x6615, 0x0001), .driver_info = DEVTYPE_IRTOUCH},
|
||||
{USB_DEVICE(0x6615, 0x0012), .driver_info = DEVTYPE_IRTOUCH_HIRES},
|
||||
#endif
|
||||
|
||||
#ifdef CONFIG_TOUCHSCREEN_USB_IDEALTEK
|
||||
@ -1177,6 +1179,15 @@ static struct usbtouch_device_info usbtouch_dev_info[] = {
|
||||
.rept_size = 8,
|
||||
.read_data = irtouch_read_data,
|
||||
},
|
||||
|
||||
[DEVTYPE_IRTOUCH_HIRES] = {
|
||||
.min_xc = 0x0,
|
||||
.max_xc = 0x7fff,
|
||||
.min_yc = 0x0,
|
||||
.max_yc = 0x7fff,
|
||||
.rept_size = 8,
|
||||
.read_data = irtouch_read_data,
|
||||
},
|
||||
#endif
|
||||
|
||||
#ifdef CONFIG_TOUCHSCREEN_USB_IDEALTEK
|
||||
|
@ -59,7 +59,7 @@ static unsigned short keymap_Lifebook_Tseries[KEYMAP_LEN] __initdata = {
|
||||
KEY_RESERVED,
|
||||
KEY_SCROLLDOWN,
|
||||
KEY_SCROLLUP,
|
||||
KEY_DIRECTION,
|
||||
KEY_ROTATE_DISPLAY,
|
||||
KEY_LEFTCTRL,
|
||||
KEY_BRIGHTNESSUP,
|
||||
KEY_BRIGHTNESSDOWN,
|
||||
@ -116,7 +116,7 @@ static unsigned short keymap_Lifebook_U810[KEYMAP_LEN] __initdata = {
|
||||
KEY_RESERVED,
|
||||
KEY_PROG1,
|
||||
KEY_PROG2,
|
||||
KEY_DIRECTION,
|
||||
KEY_ROTATE_DISPLAY,
|
||||
KEY_RESERVED,
|
||||
KEY_RESERVED,
|
||||
KEY_RESERVED,
|
||||
@ -153,7 +153,7 @@ static unsigned short keymap_Stylistic_ST5xxx[KEYMAP_LEN] __initdata = {
|
||||
KEY_RESERVED,
|
||||
KEY_RESERVED,
|
||||
KEY_MAIL,
|
||||
KEY_DIRECTION,
|
||||
KEY_ROTATE_DISPLAY,
|
||||
KEY_ESC,
|
||||
KEY_ENTER,
|
||||
KEY_BRIGHTNESSUP,
|
||||
|
@ -144,7 +144,7 @@ static const struct key_entry hp_wmi_keymap[] = {
|
||||
{ KE_KEY, 0x20e8, { KEY_MEDIA } },
|
||||
{ KE_KEY, 0x2142, { KEY_MEDIA } },
|
||||
{ KE_KEY, 0x213b, { KEY_INFO } },
|
||||
{ KE_KEY, 0x2169, { KEY_DIRECTION } },
|
||||
{ KE_KEY, 0x2169, { KEY_ROTATE_DISPLAY } },
|
||||
{ KE_KEY, 0x216a, { KEY_SETUP } },
|
||||
{ KE_KEY, 0x231b, { KEY_HELP } },
|
||||
{ KE_END, 0 }
|
||||
|
@ -140,36 +140,13 @@ extern int tc3589x_set_bits(struct tc3589x *tc3589x, u8 reg, u8 mask, u8 val);
|
||||
#define TC_KPD_DEBOUNCE_PERIOD 0xA3
|
||||
#define TC_KPD_SETTLE_TIME 0xA3
|
||||
|
||||
/**
|
||||
* struct tc35893_platform_data - data structure for platform specific data
|
||||
* @keymap_data: matrix scan code table for keycodes
|
||||
* @krow: mask for available rows, value is 0xFF
|
||||
* @kcol: mask for available columns, value is 0xFF
|
||||
* @debounce_period: platform specific debounce time
|
||||
* @settle_time: platform specific settle down time
|
||||
* @irqtype: type of interrupt, falling or rising edge
|
||||
* @enable_wakeup: specifies if keypad event can wake up system from sleep
|
||||
* @no_autorepeat: flag for auto repetition
|
||||
*/
|
||||
struct tc3589x_keypad_platform_data {
|
||||
const struct matrix_keymap_data *keymap_data;
|
||||
u8 krow;
|
||||
u8 kcol;
|
||||
u8 debounce_period;
|
||||
u8 settle_time;
|
||||
unsigned long irqtype;
|
||||
bool enable_wakeup;
|
||||
bool no_autorepeat;
|
||||
};
|
||||
|
||||
/**
|
||||
* struct tc3589x_platform_data - TC3589x platform data
|
||||
* @block: bitmask of blocks to enable (use TC3589x_BLOCK_*)
|
||||
* @keypad: keypad-specific platform data
|
||||
*/
|
||||
struct tc3589x_platform_data {
|
||||
unsigned int block;
|
||||
const struct tc3589x_keypad_platform_data *keypad;
|
||||
};
|
||||
|
||||
#endif
|
||||
|
@ -369,7 +369,8 @@ struct input_keymap_entry {
|
||||
#define KEY_MSDOS 151
|
||||
#define KEY_COFFEE 152 /* AL Terminal Lock/Screensaver */
|
||||
#define KEY_SCREENLOCK KEY_COFFEE
|
||||
#define KEY_DIRECTION 153
|
||||
#define KEY_ROTATE_DISPLAY 153 /* Display orientation for e.g. tablets */
|
||||
#define KEY_DIRECTION KEY_ROTATE_DISPLAY
|
||||
#define KEY_CYCLEWINDOWS 154
|
||||
#define KEY_MAIL 155
|
||||
#define KEY_BOOKMARKS 156 /* AC Bookmarks */
|
||||
|
Loading…
Reference in New Issue
Block a user