mirror of
https://github.com/Fishwaldo/Star64_linux.git
synced 2025-07-23 23:32:14 +00:00
Merge branch 'for-linus' of git://git.kernel.org/pub/scm/linux/kernel/git/dtor/input
Pull input updates from Dmitry Torokhov: "The first round of updates for the input subsystem. A few new drivers (power button handler for AXP20x PMIC, tps65218 power button driver, sun4i keys driver, regulator haptic driver, NI Ettus Research USRP E3x0 button, Alwinner A10/A20 PS/2 controller). Updates to Synaptics and ALPS touchpad drivers (with more to come later), brand new Focaltech PS/2 support, update to Cypress driver to handle Gen5 (in addition to Gen3) devices, and number of other fixups to various drivers as well as input core" * 'for-linus' of git://git.kernel.org/pub/scm/linux/kernel/git/dtor/input: (54 commits) Input: elan_i2c - fix wrong %p extension Input: evdev - do not queue SYN_DROPPED if queue is empty Input: gscps2 - fix MODULE_DEVICE_TABLE invocation Input: synaptics - use dmax in input_mt_assign_slots Input: pxa27x_keypad - remove unnecessary ARM includes Input: ti_am335x_tsc - replace delta filtering with median filtering ARM: dts: AM335x: Make charge delay a DT parameter for TSC Input: ti_am335x_tsc - read charge delay from DT Input: ti_am335x_tsc - remove udelay in interrupt handler Input: ti_am335x_tsc - interchange touchscreen and ADC steps Input: MT - add support for balanced slot assignment Input: drv2667 - remove wrong and unneeded drv2667-haptics modalias Input: drv260x - remove wrong and unneeded drv260x-haptics modalias Input: cap11xx - remove wrong and unneeded cap11xx modalias Input: sun4i-ts - add support for touchpanel controller on A31 Input: serio - add support for Alwinner A10/A20 PS/2 controller Input: gtco - use sign_extend32() for sign extension Input: elan_i2c - verify firmware signature applying it Input: elantech - remove stale comment from Kconfig Input: cyapa - off by one in cyapa_update_fw_store() ...
This commit is contained in:
commit
718749d562
61 changed files with 7972 additions and 1317 deletions
11
Documentation/ABI/testing/sysfs-driver-input-axp-pek
Normal file
11
Documentation/ABI/testing/sysfs-driver-input-axp-pek
Normal file
|
@ -0,0 +1,11 @@
|
||||||
|
What: /sys/class/input/input(x)/device/startup
|
||||||
|
Date: March 2014
|
||||||
|
Contact: Carlo Caione <carlo@caione.org>
|
||||||
|
Description: Startup time in us. Board is powered on if the button is pressed
|
||||||
|
for more than <startup_time>
|
||||||
|
|
||||||
|
What: /sys/class/input/input(x)/device/shutdown
|
||||||
|
Date: March 2014
|
||||||
|
Contact: Carlo Caione <carlo@caione.org>
|
||||||
|
Description: Shutdown time in us. Board is powered off if the button is pressed
|
||||||
|
for more than <shutdown_time>
|
25
Documentation/devicetree/bindings/input/e3x0-button.txt
Normal file
25
Documentation/devicetree/bindings/input/e3x0-button.txt
Normal file
|
@ -0,0 +1,25 @@
|
||||||
|
National Instruments Ettus Research USRP E3x0 button driver
|
||||||
|
|
||||||
|
This module is part of the NI Ettus Research USRP E3x0 SDR.
|
||||||
|
|
||||||
|
This module provides a simple power button event via two interrupts.
|
||||||
|
|
||||||
|
Required properties:
|
||||||
|
- compatible: should be one of the following
|
||||||
|
- "ettus,e3x0-button": For devices such as the NI Ettus Research USRP E3x0
|
||||||
|
- interrupt-parent:
|
||||||
|
- a phandle to the interrupt controller that it is attached to.
|
||||||
|
- interrupts: should be one of the following
|
||||||
|
- <0 30 1>, <0 31 1>: For devices such as the NI Ettus Research USRP E3x0
|
||||||
|
- interrupt-names: should be one of the following
|
||||||
|
- "press", "release": For devices such as the NI Ettus Research USRP E3x0
|
||||||
|
|
||||||
|
Note: Interrupt numbers might vary depending on the FPGA configuration.
|
||||||
|
|
||||||
|
Example:
|
||||||
|
button {
|
||||||
|
compatible = "ettus,e3x0-button";
|
||||||
|
interrupt-parent = <&intc>;
|
||||||
|
interrupts = <0 30 1>, <0 31 1>;
|
||||||
|
interrupt-names = "press", "release";
|
||||||
|
}
|
21
Documentation/devicetree/bindings/input/regulator-haptic.txt
Normal file
21
Documentation/devicetree/bindings/input/regulator-haptic.txt
Normal file
|
@ -0,0 +1,21 @@
|
||||||
|
* Regulator Haptic Device Tree Bindings
|
||||||
|
|
||||||
|
Required Properties:
|
||||||
|
- compatible : Should be "regulator-haptic"
|
||||||
|
- haptic-supply : Power supply to the haptic motor.
|
||||||
|
[*] refer Documentation/devicetree/bindings/regulator/regulator.txt
|
||||||
|
|
||||||
|
- max-microvolt : The maximum voltage value supplied to the haptic motor.
|
||||||
|
[The unit of the voltage is a micro]
|
||||||
|
|
||||||
|
- min-microvolt : The minimum voltage value supplied to the haptic motor.
|
||||||
|
[The unit of the voltage is a micro]
|
||||||
|
|
||||||
|
Example:
|
||||||
|
|
||||||
|
haptics {
|
||||||
|
compatible = "regulator-haptic";
|
||||||
|
haptic-supply = <&motor_regulator>;
|
||||||
|
max-microvolt = <2700000>;
|
||||||
|
min-microvolt = <1100000>;
|
||||||
|
};
|
62
Documentation/devicetree/bindings/input/sun4i-lradc-keys.txt
Normal file
62
Documentation/devicetree/bindings/input/sun4i-lradc-keys.txt
Normal file
|
@ -0,0 +1,62 @@
|
||||||
|
Allwinner sun4i low res adc attached tablet keys
|
||||||
|
------------------------------------------------
|
||||||
|
|
||||||
|
Required properties:
|
||||||
|
- compatible: "allwinner,sun4i-a10-lradc-keys"
|
||||||
|
- reg: mmio address range of the chip
|
||||||
|
- interrupts: interrupt to which the chip is connected
|
||||||
|
- vref-supply: powersupply for the lradc reference voltage
|
||||||
|
|
||||||
|
Each key is represented as a sub-node of "allwinner,sun4i-a10-lradc-keys":
|
||||||
|
|
||||||
|
Required subnode-properties:
|
||||||
|
- label: Descriptive name of the key.
|
||||||
|
- linux,code: Keycode to emit.
|
||||||
|
- channel: Channel this key is attached to, mut be 0 or 1.
|
||||||
|
- voltage: Voltage in µV at lradc input when this key is pressed.
|
||||||
|
|
||||||
|
Example:
|
||||||
|
|
||||||
|
#include <dt-bindings/input/input.h>
|
||||||
|
|
||||||
|
lradc: lradc@01c22800 {
|
||||||
|
compatible = "allwinner,sun4i-a10-lradc-keys";
|
||||||
|
reg = <0x01c22800 0x100>;
|
||||||
|
interrupts = <31>;
|
||||||
|
vref-supply = <®_vcc3v0>;
|
||||||
|
|
||||||
|
button@191 {
|
||||||
|
label = "Volume Up";
|
||||||
|
linux,code = <KEY_VOLUMEUP>;
|
||||||
|
channel = <0>;
|
||||||
|
voltage = <191274>;
|
||||||
|
};
|
||||||
|
|
||||||
|
button@392 {
|
||||||
|
label = "Volume Down";
|
||||||
|
linux,code = <KEY_VOLUMEDOWN>;
|
||||||
|
channel = <0>;
|
||||||
|
voltage = <392644>;
|
||||||
|
};
|
||||||
|
|
||||||
|
button@601 {
|
||||||
|
label = "Menu";
|
||||||
|
linux,code = <KEY_MENU>;
|
||||||
|
channel = <0>;
|
||||||
|
voltage = <601151>;
|
||||||
|
};
|
||||||
|
|
||||||
|
button@795 {
|
||||||
|
label = "Enter";
|
||||||
|
linux,code = <KEY_ENTER>;
|
||||||
|
channel = <0>;
|
||||||
|
voltage = <795090>;
|
||||||
|
};
|
||||||
|
|
||||||
|
button@987 {
|
||||||
|
label = "Home";
|
||||||
|
linux,code = <KEY_HOMEPAGE>;
|
||||||
|
channel = <0>;
|
||||||
|
voltage = <987387>;
|
||||||
|
};
|
||||||
|
};
|
|
@ -2,9 +2,10 @@ sun4i resistive touchscreen controller
|
||||||
--------------------------------------
|
--------------------------------------
|
||||||
|
|
||||||
Required properties:
|
Required properties:
|
||||||
- compatible: "allwinner,sun4i-a10-ts"
|
- compatible: "allwinner,sun4i-a10-ts" or "allwinner,sun6i-a31-ts"
|
||||||
- reg: mmio address range of the chip
|
- reg: mmio address range of the chip
|
||||||
- interrupts: interrupt to which the chip is connected
|
- interrupts: interrupt to which the chip is connected
|
||||||
|
- #thermal-sensor-cells: shall be 0
|
||||||
|
|
||||||
Optional properties:
|
Optional properties:
|
||||||
- allwinner,ts-attached: boolean indicating that an actual touchscreen is
|
- allwinner,ts-attached: boolean indicating that an actual touchscreen is
|
||||||
|
@ -17,4 +18,5 @@ Example:
|
||||||
reg = <0x01c25000 0x100>;
|
reg = <0x01c25000 0x100>;
|
||||||
interrupts = <29>;
|
interrupts = <29>;
|
||||||
allwinner,ts-attached;
|
allwinner,ts-attached;
|
||||||
|
#thermal-sensor-cells = <0>;
|
||||||
};
|
};
|
||||||
|
|
|
@ -28,6 +28,20 @@ Required properties:
|
||||||
ti,adc-channels: List of analog inputs available for ADC.
|
ti,adc-channels: List of analog inputs available for ADC.
|
||||||
AIN0 = 0, AIN1 = 1 and so on till AIN7 = 7.
|
AIN0 = 0, AIN1 = 1 and so on till AIN7 = 7.
|
||||||
|
|
||||||
|
Optional properties:
|
||||||
|
- child "tsc"
|
||||||
|
ti,charge-delay: Length of touch screen charge delay step in terms of
|
||||||
|
ADC clock cycles. Charge delay value should be large
|
||||||
|
in order to avoid false pen-up events. This value
|
||||||
|
effects the overall sampling speed, hence need to be
|
||||||
|
kept as low as possible, while avoiding false pen-up
|
||||||
|
event. Start from a lower value, say 0x400, and
|
||||||
|
increase value until false pen-up events are avoided.
|
||||||
|
The pen-up detection happens immediately after the
|
||||||
|
charge step, so this does in fact function as a
|
||||||
|
hardware knob for adjusting the amount of "settling
|
||||||
|
time".
|
||||||
|
|
||||||
Example:
|
Example:
|
||||||
tscadc: tscadc@44e0d000 {
|
tscadc: tscadc@44e0d000 {
|
||||||
compatible = "ti,am3359-tscadc";
|
compatible = "ti,am3359-tscadc";
|
||||||
|
@ -36,6 +50,7 @@ Example:
|
||||||
ti,x-plate-resistance = <200>;
|
ti,x-plate-resistance = <200>;
|
||||||
ti,coordiante-readouts = <5>;
|
ti,coordiante-readouts = <5>;
|
||||||
ti,wire-config = <0x00 0x11 0x22 0x33>;
|
ti,wire-config = <0x00 0x11 0x22 0x33>;
|
||||||
|
ti,charge-delay = <0x400>;
|
||||||
};
|
};
|
||||||
|
|
||||||
adc {
|
adc {
|
||||||
|
|
|
@ -0,0 +1,17 @@
|
||||||
|
Texas Instruments TPS65218 power button
|
||||||
|
|
||||||
|
This driver provides a simple power button event via an Interrupt.
|
||||||
|
|
||||||
|
Required properties:
|
||||||
|
- compatible: should be "ti,tps65218-pwrbutton"
|
||||||
|
- interrupts: should be one of the following
|
||||||
|
- <3 IRQ_TYPE_EDGE_BOTH>: For controllers compatible with tps65218
|
||||||
|
|
||||||
|
Example:
|
||||||
|
|
||||||
|
&tps {
|
||||||
|
power-button {
|
||||||
|
compatible = "ti,tps65218-pwrbutton";
|
||||||
|
interrupts = <3 IRQ_TYPE_EDGE_BOTH>;
|
||||||
|
};
|
||||||
|
};
|
|
@ -0,0 +1,23 @@
|
||||||
|
* Device tree bindings for Allwinner A10, A20 PS2 host controller
|
||||||
|
|
||||||
|
A20 PS2 is dual role controller (PS2 host and PS2 device). These bindings are
|
||||||
|
for PS2 A10/A20 host controller. IBM compliant IBM PS2 and AT-compatible keyboard
|
||||||
|
and mouse can be connected.
|
||||||
|
|
||||||
|
Required properties:
|
||||||
|
|
||||||
|
- reg : Offset and length of the register set for the device.
|
||||||
|
- compatible : Should be as of the following:
|
||||||
|
- "allwinner,sun4i-a10-ps2"
|
||||||
|
- interrupts : The interrupt line connected to the PS2.
|
||||||
|
- clocks : The gate clk connected to the PS2.
|
||||||
|
|
||||||
|
|
||||||
|
Example:
|
||||||
|
ps20: ps2@0x01c2a000 {
|
||||||
|
compatible = "allwinner,sun4i-a10-ps2";
|
||||||
|
reg = <0x01c2a000 0x400>;
|
||||||
|
interrupts = <0 62 4>;
|
||||||
|
clocks = <&apb1_gates 6>;
|
||||||
|
status = "disabled";
|
||||||
|
};
|
|
@ -54,6 +54,7 @@ epcos EPCOS AG
|
||||||
epfl Ecole Polytechnique Fédérale de Lausanne
|
epfl Ecole Polytechnique Fédérale de Lausanne
|
||||||
epson Seiko Epson Corp.
|
epson Seiko Epson Corp.
|
||||||
est ESTeem Wireless Modems
|
est ESTeem Wireless Modems
|
||||||
|
ettus NI Ettus Research
|
||||||
eukrea Eukréa Electromatique
|
eukrea Eukréa Electromatique
|
||||||
everest Everest Semiconductor Co. Ltd.
|
everest Everest Semiconductor Co. Ltd.
|
||||||
excito Excito
|
excito Excito
|
||||||
|
|
15
MAINTAINERS
15
MAINTAINERS
|
@ -3479,6 +3479,14 @@ M: "Maciej W. Rozycki" <macro@linux-mips.org>
|
||||||
S: Maintained
|
S: Maintained
|
||||||
F: drivers/tty/serial/dz.*
|
F: drivers/tty/serial/dz.*
|
||||||
|
|
||||||
|
E3X0 POWER BUTTON DRIVER
|
||||||
|
M: Moritz Fischer <moritz.fischer@ettus.com>
|
||||||
|
L: usrp-users@lists.ettus.com
|
||||||
|
W: http://www.ettus.com
|
||||||
|
S: Supported
|
||||||
|
F: drivers/input/misc/e3x0-button.c
|
||||||
|
F: Documentation/devicetree/bindings/input/e3x0-button.txt
|
||||||
|
|
||||||
E4000 MEDIA DRIVER
|
E4000 MEDIA DRIVER
|
||||||
M: Antti Palosaari <crope@iki.fi>
|
M: Antti Palosaari <crope@iki.fi>
|
||||||
L: linux-media@vger.kernel.org
|
L: linux-media@vger.kernel.org
|
||||||
|
@ -9281,6 +9289,13 @@ F: arch/m68k/sun3*/
|
||||||
F: arch/m68k/include/asm/sun3*
|
F: arch/m68k/include/asm/sun3*
|
||||||
F: drivers/net/ethernet/i825xx/sun3*
|
F: drivers/net/ethernet/i825xx/sun3*
|
||||||
|
|
||||||
|
SUN4I LOW RES ADC ATTACHED TABLET KEYS DRIVER
|
||||||
|
M: Hans de Goede <hdegoede@redhat.com>
|
||||||
|
L: linux-input@vger.kernel.org
|
||||||
|
S: Maintained
|
||||||
|
F: Documentation/devicetree/bindings/input/sun4i-lradc-keys.txt
|
||||||
|
F: drivers/input/keyboard/sun4i-lradc-keys.c
|
||||||
|
|
||||||
SUNDANCE NETWORK DRIVER
|
SUNDANCE NETWORK DRIVER
|
||||||
M: Denis Kirjanov <kda@linux-powerpc.org>
|
M: Denis Kirjanov <kda@linux-powerpc.org>
|
||||||
L: netdev@vger.kernel.org
|
L: netdev@vger.kernel.org
|
||||||
|
|
|
@ -648,6 +648,7 @@
|
||||||
ti,x-plate-resistance = <200>;
|
ti,x-plate-resistance = <200>;
|
||||||
ti,coordinate-readouts = <5>;
|
ti,coordinate-readouts = <5>;
|
||||||
ti,wire-config = <0x00 0x11 0x22 0x33>;
|
ti,wire-config = <0x00 0x11 0x22 0x33>;
|
||||||
|
ti,charge-delay = <0x400>;
|
||||||
};
|
};
|
||||||
|
|
||||||
adc {
|
adc {
|
||||||
|
|
|
@ -86,19 +86,18 @@ static void tiadc_step_config(struct iio_dev *indio_dev)
|
||||||
{
|
{
|
||||||
struct tiadc_device *adc_dev = iio_priv(indio_dev);
|
struct tiadc_device *adc_dev = iio_priv(indio_dev);
|
||||||
unsigned int stepconfig;
|
unsigned int stepconfig;
|
||||||
int i, steps;
|
int i, steps = 0;
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* There are 16 configurable steps and 8 analog input
|
* There are 16 configurable steps and 8 analog input
|
||||||
* lines available which are shared between Touchscreen and ADC.
|
* lines available which are shared between Touchscreen and ADC.
|
||||||
*
|
*
|
||||||
* Steps backwards i.e. from 16 towards 0 are used by ADC
|
* Steps forwards i.e. from 0 towards 16 are used by ADC
|
||||||
* depending on number of input lines needed.
|
* depending on number of input lines needed.
|
||||||
* Channel would represent which analog input
|
* Channel would represent which analog input
|
||||||
* needs to be given to ADC to digitalize data.
|
* needs to be given to ADC to digitalize data.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
steps = TOTAL_STEPS - adc_dev->channels;
|
|
||||||
if (iio_buffer_enabled(indio_dev))
|
if (iio_buffer_enabled(indio_dev))
|
||||||
stepconfig = STEPCONFIG_AVG_16 | STEPCONFIG_FIFO1
|
stepconfig = STEPCONFIG_AVG_16 | STEPCONFIG_FIFO1
|
||||||
| STEPCONFIG_MODE_SWCNT;
|
| STEPCONFIG_MODE_SWCNT;
|
||||||
|
|
|
@ -62,26 +62,6 @@ struct evdev_client {
|
||||||
struct input_event buffer[];
|
struct input_event buffer[];
|
||||||
};
|
};
|
||||||
|
|
||||||
static int evdev_set_clk_type(struct evdev_client *client, unsigned int clkid)
|
|
||||||
{
|
|
||||||
switch (clkid) {
|
|
||||||
|
|
||||||
case CLOCK_REALTIME:
|
|
||||||
client->clk_type = EV_CLK_REAL;
|
|
||||||
break;
|
|
||||||
case CLOCK_MONOTONIC:
|
|
||||||
client->clk_type = EV_CLK_MONO;
|
|
||||||
break;
|
|
||||||
case CLOCK_BOOTTIME:
|
|
||||||
client->clk_type = EV_CLK_BOOT;
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
return -EINVAL;
|
|
||||||
}
|
|
||||||
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* flush queued events of type @type, caller must hold client->buffer_lock */
|
/* flush queued events of type @type, caller must hold client->buffer_lock */
|
||||||
static void __evdev_flush_queue(struct evdev_client *client, unsigned int type)
|
static void __evdev_flush_queue(struct evdev_client *client, unsigned int type)
|
||||||
{
|
{
|
||||||
|
@ -128,10 +108,8 @@ static void __evdev_flush_queue(struct evdev_client *client, unsigned int type)
|
||||||
client->head = head;
|
client->head = head;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* queue SYN_DROPPED event */
|
static void __evdev_queue_syn_dropped(struct evdev_client *client)
|
||||||
static void evdev_queue_syn_dropped(struct evdev_client *client)
|
|
||||||
{
|
{
|
||||||
unsigned long flags;
|
|
||||||
struct input_event ev;
|
struct input_event ev;
|
||||||
ktime_t time;
|
ktime_t time;
|
||||||
|
|
||||||
|
@ -146,8 +124,6 @@ static void evdev_queue_syn_dropped(struct evdev_client *client)
|
||||||
ev.code = SYN_DROPPED;
|
ev.code = SYN_DROPPED;
|
||||||
ev.value = 0;
|
ev.value = 0;
|
||||||
|
|
||||||
spin_lock_irqsave(&client->buffer_lock, flags);
|
|
||||||
|
|
||||||
client->buffer[client->head++] = ev;
|
client->buffer[client->head++] = ev;
|
||||||
client->head &= client->bufsize - 1;
|
client->head &= client->bufsize - 1;
|
||||||
|
|
||||||
|
@ -156,8 +132,53 @@ static void evdev_queue_syn_dropped(struct evdev_client *client)
|
||||||
client->tail = (client->head - 1) & (client->bufsize - 1);
|
client->tail = (client->head - 1) & (client->bufsize - 1);
|
||||||
client->packet_head = client->tail;
|
client->packet_head = client->tail;
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static void evdev_queue_syn_dropped(struct evdev_client *client)
|
||||||
|
{
|
||||||
|
unsigned long flags;
|
||||||
|
|
||||||
|
spin_lock_irqsave(&client->buffer_lock, flags);
|
||||||
|
__evdev_queue_syn_dropped(client);
|
||||||
|
spin_unlock_irqrestore(&client->buffer_lock, flags);
|
||||||
|
}
|
||||||
|
|
||||||
|
static int evdev_set_clk_type(struct evdev_client *client, unsigned int clkid)
|
||||||
|
{
|
||||||
|
unsigned long flags;
|
||||||
|
|
||||||
|
if (client->clk_type == clkid)
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
switch (clkid) {
|
||||||
|
|
||||||
|
case CLOCK_REALTIME:
|
||||||
|
client->clk_type = EV_CLK_REAL;
|
||||||
|
break;
|
||||||
|
case CLOCK_MONOTONIC:
|
||||||
|
client->clk_type = EV_CLK_MONO;
|
||||||
|
break;
|
||||||
|
case CLOCK_BOOTTIME:
|
||||||
|
client->clk_type = EV_CLK_BOOT;
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
return -EINVAL;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Flush pending events and queue SYN_DROPPED event,
|
||||||
|
* but only if the queue is not empty.
|
||||||
|
*/
|
||||||
|
spin_lock_irqsave(&client->buffer_lock, flags);
|
||||||
|
|
||||||
|
if (client->head != client->tail) {
|
||||||
|
client->packet_head = client->head = client->tail;
|
||||||
|
__evdev_queue_syn_dropped(client);
|
||||||
|
}
|
||||||
|
|
||||||
spin_unlock_irqrestore(&client->buffer_lock, flags);
|
spin_unlock_irqrestore(&client->buffer_lock, flags);
|
||||||
|
|
||||||
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
static void __pass_event(struct evdev_client *client,
|
static void __pass_event(struct evdev_client *client,
|
||||||
|
|
|
@ -293,7 +293,7 @@ void input_mt_sync_frame(struct input_dev *dev)
|
||||||
}
|
}
|
||||||
EXPORT_SYMBOL(input_mt_sync_frame);
|
EXPORT_SYMBOL(input_mt_sync_frame);
|
||||||
|
|
||||||
static int adjust_dual(int *begin, int step, int *end, int eq)
|
static int adjust_dual(int *begin, int step, int *end, int eq, int mu)
|
||||||
{
|
{
|
||||||
int f, *p, s, c;
|
int f, *p, s, c;
|
||||||
|
|
||||||
|
@ -311,9 +311,10 @@ static int adjust_dual(int *begin, int step, int *end, int eq)
|
||||||
s = *p;
|
s = *p;
|
||||||
|
|
||||||
c = (f + s + 1) / 2;
|
c = (f + s + 1) / 2;
|
||||||
if (c == 0 || (c > 0 && !eq))
|
if (c == 0 || (c > mu && (!eq || mu > 0)))
|
||||||
return 0;
|
return 0;
|
||||||
if (s < 0)
|
/* Improve convergence for positive matrices by penalizing overcovers */
|
||||||
|
if (s < 0 && mu <= 0)
|
||||||
c *= 2;
|
c *= 2;
|
||||||
|
|
||||||
for (p = begin; p != end; p += step)
|
for (p = begin; p != end; p += step)
|
||||||
|
@ -322,23 +323,24 @@ static int adjust_dual(int *begin, int step, int *end, int eq)
|
||||||
return (c < s && s <= 0) || (f >= 0 && f < c);
|
return (c < s && s <= 0) || (f >= 0 && f < c);
|
||||||
}
|
}
|
||||||
|
|
||||||
static void find_reduced_matrix(int *w, int nr, int nc, int nrc)
|
static void find_reduced_matrix(int *w, int nr, int nc, int nrc, int mu)
|
||||||
{
|
{
|
||||||
int i, k, sum;
|
int i, k, sum;
|
||||||
|
|
||||||
for (k = 0; k < nrc; k++) {
|
for (k = 0; k < nrc; k++) {
|
||||||
for (i = 0; i < nr; i++)
|
for (i = 0; i < nr; i++)
|
||||||
adjust_dual(w + i, nr, w + i + nrc, nr <= nc);
|
adjust_dual(w + i, nr, w + i + nrc, nr <= nc, mu);
|
||||||
sum = 0;
|
sum = 0;
|
||||||
for (i = 0; i < nrc; i += nr)
|
for (i = 0; i < nrc; i += nr)
|
||||||
sum += adjust_dual(w + i, 1, w + i + nr, nc <= nr);
|
sum += adjust_dual(w + i, 1, w + i + nr, nc <= nr, mu);
|
||||||
if (!sum)
|
if (!sum)
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static int input_mt_set_matrix(struct input_mt *mt,
|
static int input_mt_set_matrix(struct input_mt *mt,
|
||||||
const struct input_mt_pos *pos, int num_pos)
|
const struct input_mt_pos *pos, int num_pos,
|
||||||
|
int mu)
|
||||||
{
|
{
|
||||||
const struct input_mt_pos *p;
|
const struct input_mt_pos *p;
|
||||||
struct input_mt_slot *s;
|
struct input_mt_slot *s;
|
||||||
|
@ -352,7 +354,7 @@ static int input_mt_set_matrix(struct input_mt *mt,
|
||||||
y = input_mt_get_value(s, ABS_MT_POSITION_Y);
|
y = input_mt_get_value(s, ABS_MT_POSITION_Y);
|
||||||
for (p = pos; p != pos + num_pos; p++) {
|
for (p = pos; p != pos + num_pos; p++) {
|
||||||
int dx = x - p->x, dy = y - p->y;
|
int dx = x - p->x, dy = y - p->y;
|
||||||
*w++ = dx * dx + dy * dy;
|
*w++ = dx * dx + dy * dy - mu;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -393,17 +395,24 @@ static void input_mt_set_slots(struct input_mt *mt,
|
||||||
* @slots: the slot assignment to be filled
|
* @slots: the slot assignment to be filled
|
||||||
* @pos: the position array to match
|
* @pos: the position array to match
|
||||||
* @num_pos: number of positions
|
* @num_pos: number of positions
|
||||||
|
* @dmax: maximum ABS_MT_POSITION displacement (zero for infinite)
|
||||||
*
|
*
|
||||||
* Performs a best match against the current contacts and returns
|
* Performs a best match against the current contacts and returns
|
||||||
* the slot assignment list. New contacts are assigned to unused
|
* the slot assignment list. New contacts are assigned to unused
|
||||||
* slots.
|
* slots.
|
||||||
*
|
*
|
||||||
|
* The assignments are balanced so that all coordinate displacements are
|
||||||
|
* below the euclidian distance dmax. If no such assignment can be found,
|
||||||
|
* some contacts are assigned to unused slots.
|
||||||
|
*
|
||||||
* Returns zero on success, or negative error in case of failure.
|
* Returns zero on success, or negative error in case of failure.
|
||||||
*/
|
*/
|
||||||
int input_mt_assign_slots(struct input_dev *dev, int *slots,
|
int input_mt_assign_slots(struct input_dev *dev, int *slots,
|
||||||
const struct input_mt_pos *pos, int num_pos)
|
const struct input_mt_pos *pos, int num_pos,
|
||||||
|
int dmax)
|
||||||
{
|
{
|
||||||
struct input_mt *mt = dev->mt;
|
struct input_mt *mt = dev->mt;
|
||||||
|
int mu = 2 * dmax * dmax;
|
||||||
int nrc;
|
int nrc;
|
||||||
|
|
||||||
if (!mt || !mt->red)
|
if (!mt || !mt->red)
|
||||||
|
@ -413,8 +422,8 @@ int input_mt_assign_slots(struct input_dev *dev, int *slots,
|
||||||
if (num_pos < 1)
|
if (num_pos < 1)
|
||||||
return 0;
|
return 0;
|
||||||
|
|
||||||
nrc = input_mt_set_matrix(mt, pos, num_pos);
|
nrc = input_mt_set_matrix(mt, pos, num_pos, mu);
|
||||||
find_reduced_matrix(mt->red, num_pos, nrc / num_pos, nrc);
|
find_reduced_matrix(mt->red, num_pos, nrc / num_pos, nrc, mu);
|
||||||
input_mt_set_slots(mt, slots, num_pos);
|
input_mt_set_slots(mt, slots, num_pos);
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
|
|
|
@ -100,23 +100,24 @@ static unsigned int input_to_handler(struct input_handle *handle,
|
||||||
struct input_value *end = vals;
|
struct input_value *end = vals;
|
||||||
struct input_value *v;
|
struct input_value *v;
|
||||||
|
|
||||||
for (v = vals; v != vals + count; v++) {
|
if (handler->filter) {
|
||||||
if (handler->filter &&
|
for (v = vals; v != vals + count; v++) {
|
||||||
handler->filter(handle, v->type, v->code, v->value))
|
if (handler->filter(handle, v->type, v->code, v->value))
|
||||||
continue;
|
continue;
|
||||||
if (end != v)
|
if (end != v)
|
||||||
*end = *v;
|
*end = *v;
|
||||||
end++;
|
end++;
|
||||||
|
}
|
||||||
|
count = end - vals;
|
||||||
}
|
}
|
||||||
|
|
||||||
count = end - vals;
|
|
||||||
if (!count)
|
if (!count)
|
||||||
return 0;
|
return 0;
|
||||||
|
|
||||||
if (handler->events)
|
if (handler->events)
|
||||||
handler->events(handle, vals, count);
|
handler->events(handle, vals, count);
|
||||||
else if (handler->event)
|
else if (handler->event)
|
||||||
for (v = vals; v != end; v++)
|
for (v = vals; v != vals + count; v++)
|
||||||
handler->event(handle, v->type, v->code, v->value);
|
handler->event(handle, v->type, v->code, v->value);
|
||||||
|
|
||||||
return count;
|
return count;
|
||||||
|
@ -143,8 +144,11 @@ static void input_pass_values(struct input_dev *dev,
|
||||||
count = input_to_handler(handle, vals, count);
|
count = input_to_handler(handle, vals, count);
|
||||||
} else {
|
} else {
|
||||||
list_for_each_entry_rcu(handle, &dev->h_list, d_node)
|
list_for_each_entry_rcu(handle, &dev->h_list, d_node)
|
||||||
if (handle->open)
|
if (handle->open) {
|
||||||
count = input_to_handler(handle, vals, count);
|
count = input_to_handler(handle, vals, count);
|
||||||
|
if (!count)
|
||||||
|
break;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
rcu_read_unlock();
|
rcu_read_unlock();
|
||||||
|
@ -152,12 +156,14 @@ static void input_pass_values(struct input_dev *dev,
|
||||||
add_input_randomness(vals->type, vals->code, vals->value);
|
add_input_randomness(vals->type, vals->code, vals->value);
|
||||||
|
|
||||||
/* trigger auto repeat for key events */
|
/* trigger auto repeat for key events */
|
||||||
for (v = vals; v != vals + count; v++) {
|
if (test_bit(EV_REP, dev->evbit) && test_bit(EV_KEY, dev->evbit)) {
|
||||||
if (v->type == EV_KEY && v->value != 2) {
|
for (v = vals; v != vals + count; v++) {
|
||||||
if (v->value)
|
if (v->type == EV_KEY && v->value != 2) {
|
||||||
input_start_autorepeat(dev, v->code);
|
if (v->value)
|
||||||
else
|
input_start_autorepeat(dev, v->code);
|
||||||
input_stop_autorepeat(dev);
|
else
|
||||||
|
input_stop_autorepeat(dev);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -568,6 +568,16 @@ config KEYBOARD_STMPE
|
||||||
To compile this driver as a module, choose M here: the module will be
|
To compile this driver as a module, choose M here: the module will be
|
||||||
called stmpe-keypad.
|
called stmpe-keypad.
|
||||||
|
|
||||||
|
config KEYBOARD_SUN4I_LRADC
|
||||||
|
tristate "Allwinner sun4i low res adc attached tablet keys support"
|
||||||
|
depends on ARCH_SUNXI
|
||||||
|
help
|
||||||
|
This selects support for the Allwinner low res adc attached tablet
|
||||||
|
keys found on Allwinner sunxi SoCs.
|
||||||
|
|
||||||
|
To compile this driver as a module, choose M here: the
|
||||||
|
module will be called sun4i-lradc-keys.
|
||||||
|
|
||||||
config KEYBOARD_DAVINCI
|
config KEYBOARD_DAVINCI
|
||||||
tristate "TI DaVinci Key Scan"
|
tristate "TI DaVinci Key Scan"
|
||||||
depends on ARCH_DAVINCI_DM365
|
depends on ARCH_DAVINCI_DM365
|
||||||
|
|
|
@ -53,6 +53,7 @@ obj-$(CONFIG_KEYBOARD_SPEAR) += spear-keyboard.o
|
||||||
obj-$(CONFIG_KEYBOARD_STMPE) += stmpe-keypad.o
|
obj-$(CONFIG_KEYBOARD_STMPE) += stmpe-keypad.o
|
||||||
obj-$(CONFIG_KEYBOARD_STOWAWAY) += stowaway.o
|
obj-$(CONFIG_KEYBOARD_STOWAWAY) += stowaway.o
|
||||||
obj-$(CONFIG_KEYBOARD_ST_KEYSCAN) += st-keyscan.o
|
obj-$(CONFIG_KEYBOARD_ST_KEYSCAN) += st-keyscan.o
|
||||||
|
obj-$(CONFIG_KEYBOARD_SUN4I_LRADC) += sun4i-lradc-keys.o
|
||||||
obj-$(CONFIG_KEYBOARD_SUNKBD) += sunkbd.o
|
obj-$(CONFIG_KEYBOARD_SUNKBD) += sunkbd.o
|
||||||
obj-$(CONFIG_KEYBOARD_TC3589X) += tc3589x-keypad.o
|
obj-$(CONFIG_KEYBOARD_TC3589X) += tc3589x-keypad.o
|
||||||
obj-$(CONFIG_KEYBOARD_TEGRA) += tegra-kbc.o
|
obj-$(CONFIG_KEYBOARD_TEGRA) += tegra-kbc.o
|
||||||
|
|
|
@ -170,7 +170,7 @@ static unsigned char atakbd_keycode[0x72] = { /* American layout */
|
||||||
[93] = KEY_KPASTERISK,
|
[93] = KEY_KPASTERISK,
|
||||||
[94] = KEY_KPPLUS,
|
[94] = KEY_KPPLUS,
|
||||||
[95] = KEY_HELP,
|
[95] = KEY_HELP,
|
||||||
[96] = KEY_BACKSLASH, /* FIXME: '<' */
|
[96] = KEY_102ND,
|
||||||
[97] = KEY_KPASTERISK, /* FIXME */
|
[97] = KEY_KPASTERISK, /* FIXME */
|
||||||
[98] = KEY_KPSLASH,
|
[98] = KEY_KPSLASH,
|
||||||
[99] = KEY_KPLEFTPAREN,
|
[99] = KEY_KPLEFTPAREN,
|
||||||
|
|
|
@ -370,7 +370,6 @@ static struct i2c_driver cap11xx_i2c_driver = {
|
||||||
|
|
||||||
module_i2c_driver(cap11xx_i2c_driver);
|
module_i2c_driver(cap11xx_i2c_driver);
|
||||||
|
|
||||||
MODULE_ALIAS("platform:cap11xx");
|
|
||||||
MODULE_DESCRIPTION("Microchip CAP11XX driver");
|
MODULE_DESCRIPTION("Microchip CAP11XX driver");
|
||||||
MODULE_AUTHOR("Daniel Mack <linux@zonque.org>");
|
MODULE_AUTHOR("Daniel Mack <linux@zonque.org>");
|
||||||
MODULE_LICENSE("GPL v2");
|
MODULE_LICENSE("GPL v2");
|
||||||
|
|
|
@ -448,8 +448,7 @@ static int imx_keypad_probe(struct platform_device *pdev)
|
||||||
return -ENOMEM;
|
return -ENOMEM;
|
||||||
}
|
}
|
||||||
|
|
||||||
keypad = devm_kzalloc(&pdev->dev, sizeof(struct imx_keypad),
|
keypad = devm_kzalloc(&pdev->dev, sizeof(*keypad), GFP_KERNEL);
|
||||||
GFP_KERNEL);
|
|
||||||
if (!keypad) {
|
if (!keypad) {
|
||||||
dev_err(&pdev->dev, "not enough memory for driver data\n");
|
dev_err(&pdev->dev, "not enough memory for driver data\n");
|
||||||
return -ENOMEM;
|
return -ENOMEM;
|
||||||
|
|
|
@ -20,6 +20,7 @@
|
||||||
#include <linux/module.h>
|
#include <linux/module.h>
|
||||||
#include <linux/interrupt.h>
|
#include <linux/interrupt.h>
|
||||||
#include <linux/input.h>
|
#include <linux/input.h>
|
||||||
|
#include <linux/io.h>
|
||||||
#include <linux/device.h>
|
#include <linux/device.h>
|
||||||
#include <linux/platform_device.h>
|
#include <linux/platform_device.h>
|
||||||
#include <linux/clk.h>
|
#include <linux/clk.h>
|
||||||
|
@ -28,10 +29,6 @@
|
||||||
#include <linux/slab.h>
|
#include <linux/slab.h>
|
||||||
#include <linux/of.h>
|
#include <linux/of.h>
|
||||||
|
|
||||||
#include <asm/mach/arch.h>
|
|
||||||
#include <asm/mach/map.h>
|
|
||||||
|
|
||||||
#include <mach/hardware.h>
|
|
||||||
#include <linux/platform_data/keypad-pxa27x.h>
|
#include <linux/platform_data/keypad-pxa27x.h>
|
||||||
/*
|
/*
|
||||||
* Keypad Controller registers
|
* Keypad Controller registers
|
||||||
|
|
286
drivers/input/keyboard/sun4i-lradc-keys.c
Normal file
286
drivers/input/keyboard/sun4i-lradc-keys.c
Normal file
|
@ -0,0 +1,286 @@
|
||||||
|
/*
|
||||||
|
* Allwinner sun4i low res adc attached tablet keys driver
|
||||||
|
*
|
||||||
|
* Copyright (C) 2014 Hans de Goede <hdegoede@redhat.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.
|
||||||
|
*
|
||||||
|
* 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.
|
||||||
|
*/
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Allwinnner sunxi SoCs have a lradc which is specifically designed to have
|
||||||
|
* various (tablet) keys (ie home, back, search, etc). attached to it using
|
||||||
|
* a resistor network. This driver is for the keys on such boards.
|
||||||
|
*
|
||||||
|
* There are 2 channels, currently this driver only supports channel 0 since
|
||||||
|
* there are no boards known to use channel 1.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <linux/err.h>
|
||||||
|
#include <linux/init.h>
|
||||||
|
#include <linux/input.h>
|
||||||
|
#include <linux/interrupt.h>
|
||||||
|
#include <linux/io.h>
|
||||||
|
#include <linux/module.h>
|
||||||
|
#include <linux/of_platform.h>
|
||||||
|
#include <linux/platform_device.h>
|
||||||
|
#include <linux/regulator/consumer.h>
|
||||||
|
#include <linux/slab.h>
|
||||||
|
|
||||||
|
#define LRADC_CTRL 0x00
|
||||||
|
#define LRADC_INTC 0x04
|
||||||
|
#define LRADC_INTS 0x08
|
||||||
|
#define LRADC_DATA0 0x0c
|
||||||
|
#define LRADC_DATA1 0x10
|
||||||
|
|
||||||
|
/* LRADC_CTRL bits */
|
||||||
|
#define FIRST_CONVERT_DLY(x) ((x) << 24) /* 8 bits */
|
||||||
|
#define CHAN_SELECT(x) ((x) << 22) /* 2 bits */
|
||||||
|
#define CONTINUE_TIME_SEL(x) ((x) << 16) /* 4 bits */
|
||||||
|
#define KEY_MODE_SEL(x) ((x) << 12) /* 2 bits */
|
||||||
|
#define LEVELA_B_CNT(x) ((x) << 8) /* 4 bits */
|
||||||
|
#define HOLD_EN(x) ((x) << 6)
|
||||||
|
#define LEVELB_VOL(x) ((x) << 4) /* 2 bits */
|
||||||
|
#define SAMPLE_RATE(x) ((x) << 2) /* 2 bits */
|
||||||
|
#define ENABLE(x) ((x) << 0)
|
||||||
|
|
||||||
|
/* LRADC_INTC and LRADC_INTS bits */
|
||||||
|
#define CHAN1_KEYUP_IRQ BIT(12)
|
||||||
|
#define CHAN1_ALRDY_HOLD_IRQ BIT(11)
|
||||||
|
#define CHAN1_HOLD_IRQ BIT(10)
|
||||||
|
#define CHAN1_KEYDOWN_IRQ BIT(9)
|
||||||
|
#define CHAN1_DATA_IRQ BIT(8)
|
||||||
|
#define CHAN0_KEYUP_IRQ BIT(4)
|
||||||
|
#define CHAN0_ALRDY_HOLD_IRQ BIT(3)
|
||||||
|
#define CHAN0_HOLD_IRQ BIT(2)
|
||||||
|
#define CHAN0_KEYDOWN_IRQ BIT(1)
|
||||||
|
#define CHAN0_DATA_IRQ BIT(0)
|
||||||
|
|
||||||
|
struct sun4i_lradc_keymap {
|
||||||
|
u32 voltage;
|
||||||
|
u32 keycode;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct sun4i_lradc_data {
|
||||||
|
struct device *dev;
|
||||||
|
struct input_dev *input;
|
||||||
|
void __iomem *base;
|
||||||
|
struct regulator *vref_supply;
|
||||||
|
struct sun4i_lradc_keymap *chan0_map;
|
||||||
|
u32 chan0_map_count;
|
||||||
|
u32 chan0_keycode;
|
||||||
|
u32 vref;
|
||||||
|
};
|
||||||
|
|
||||||
|
static irqreturn_t sun4i_lradc_irq(int irq, void *dev_id)
|
||||||
|
{
|
||||||
|
struct sun4i_lradc_data *lradc = dev_id;
|
||||||
|
u32 i, ints, val, voltage, diff, keycode = 0, closest = 0xffffffff;
|
||||||
|
|
||||||
|
ints = readl(lradc->base + LRADC_INTS);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* lradc supports only one keypress at a time, release does not give
|
||||||
|
* any info as to which key was released, so we cache the keycode.
|
||||||
|
*/
|
||||||
|
|
||||||
|
if (ints & CHAN0_KEYUP_IRQ) {
|
||||||
|
input_report_key(lradc->input, lradc->chan0_keycode, 0);
|
||||||
|
lradc->chan0_keycode = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
if ((ints & CHAN0_KEYDOWN_IRQ) && lradc->chan0_keycode == 0) {
|
||||||
|
val = readl(lradc->base + LRADC_DATA0) & 0x3f;
|
||||||
|
voltage = val * lradc->vref / 63;
|
||||||
|
|
||||||
|
for (i = 0; i < lradc->chan0_map_count; i++) {
|
||||||
|
diff = abs(lradc->chan0_map[i].voltage - voltage);
|
||||||
|
if (diff < closest) {
|
||||||
|
closest = diff;
|
||||||
|
keycode = lradc->chan0_map[i].keycode;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
lradc->chan0_keycode = keycode;
|
||||||
|
input_report_key(lradc->input, lradc->chan0_keycode, 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
input_sync(lradc->input);
|
||||||
|
|
||||||
|
writel(ints, lradc->base + LRADC_INTS);
|
||||||
|
|
||||||
|
return IRQ_HANDLED;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int sun4i_lradc_open(struct input_dev *dev)
|
||||||
|
{
|
||||||
|
struct sun4i_lradc_data *lradc = input_get_drvdata(dev);
|
||||||
|
int error;
|
||||||
|
|
||||||
|
error = regulator_enable(lradc->vref_supply);
|
||||||
|
if (error)
|
||||||
|
return error;
|
||||||
|
|
||||||
|
/* lradc Vref internally is divided by 2/3 */
|
||||||
|
lradc->vref = regulator_get_voltage(lradc->vref_supply) * 2 / 3;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Set sample time to 4 ms / 250 Hz. Wait 2 * 4 ms for key to
|
||||||
|
* stabilize on press, wait (1 + 1) * 4 ms for key release
|
||||||
|
*/
|
||||||
|
writel(FIRST_CONVERT_DLY(2) | LEVELA_B_CNT(1) | HOLD_EN(1) |
|
||||||
|
SAMPLE_RATE(0) | ENABLE(1), lradc->base + LRADC_CTRL);
|
||||||
|
|
||||||
|
writel(CHAN0_KEYUP_IRQ | CHAN0_KEYDOWN_IRQ, lradc->base + LRADC_INTC);
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void sun4i_lradc_close(struct input_dev *dev)
|
||||||
|
{
|
||||||
|
struct sun4i_lradc_data *lradc = input_get_drvdata(dev);
|
||||||
|
|
||||||
|
/* Disable lradc, leave other settings unchanged */
|
||||||
|
writel(FIRST_CONVERT_DLY(2) | LEVELA_B_CNT(1) | HOLD_EN(1) |
|
||||||
|
SAMPLE_RATE(2), lradc->base + LRADC_CTRL);
|
||||||
|
writel(0, lradc->base + LRADC_INTC);
|
||||||
|
|
||||||
|
regulator_disable(lradc->vref_supply);
|
||||||
|
}
|
||||||
|
|
||||||
|
static int sun4i_lradc_load_dt_keymap(struct device *dev,
|
||||||
|
struct sun4i_lradc_data *lradc)
|
||||||
|
{
|
||||||
|
struct device_node *np, *pp;
|
||||||
|
int i;
|
||||||
|
int error;
|
||||||
|
|
||||||
|
np = dev->of_node;
|
||||||
|
if (!np)
|
||||||
|
return -EINVAL;
|
||||||
|
|
||||||
|
lradc->chan0_map_count = of_get_child_count(np);
|
||||||
|
if (lradc->chan0_map_count == 0) {
|
||||||
|
dev_err(dev, "keymap is missing in device tree\n");
|
||||||
|
return -EINVAL;
|
||||||
|
}
|
||||||
|
|
||||||
|
lradc->chan0_map = devm_kmalloc_array(dev, lradc->chan0_map_count,
|
||||||
|
sizeof(struct sun4i_lradc_keymap),
|
||||||
|
GFP_KERNEL);
|
||||||
|
if (!lradc->chan0_map)
|
||||||
|
return -ENOMEM;
|
||||||
|
|
||||||
|
i = 0;
|
||||||
|
for_each_child_of_node(np, pp) {
|
||||||
|
struct sun4i_lradc_keymap *map = &lradc->chan0_map[i];
|
||||||
|
u32 channel;
|
||||||
|
|
||||||
|
error = of_property_read_u32(pp, "channel", &channel);
|
||||||
|
if (error || channel != 0) {
|
||||||
|
dev_err(dev, "%s: Inval channel prop\n", pp->name);
|
||||||
|
return -EINVAL;
|
||||||
|
}
|
||||||
|
|
||||||
|
error = of_property_read_u32(pp, "voltage", &map->voltage);
|
||||||
|
if (error) {
|
||||||
|
dev_err(dev, "%s: Inval voltage prop\n", pp->name);
|
||||||
|
return -EINVAL;
|
||||||
|
}
|
||||||
|
|
||||||
|
error = of_property_read_u32(pp, "linux,code", &map->keycode);
|
||||||
|
if (error) {
|
||||||
|
dev_err(dev, "%s: Inval linux,code prop\n", pp->name);
|
||||||
|
return -EINVAL;
|
||||||
|
}
|
||||||
|
|
||||||
|
i++;
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int sun4i_lradc_probe(struct platform_device *pdev)
|
||||||
|
{
|
||||||
|
struct sun4i_lradc_data *lradc;
|
||||||
|
struct device *dev = &pdev->dev;
|
||||||
|
int i;
|
||||||
|
int error;
|
||||||
|
|
||||||
|
lradc = devm_kzalloc(dev, sizeof(struct sun4i_lradc_data), GFP_KERNEL);
|
||||||
|
if (!lradc)
|
||||||
|
return -ENOMEM;
|
||||||
|
|
||||||
|
error = sun4i_lradc_load_dt_keymap(dev, lradc);
|
||||||
|
if (error)
|
||||||
|
return error;
|
||||||
|
|
||||||
|
lradc->vref_supply = devm_regulator_get(dev, "vref");
|
||||||
|
if (IS_ERR(lradc->vref_supply))
|
||||||
|
return PTR_ERR(lradc->vref_supply);
|
||||||
|
|
||||||
|
lradc->dev = dev;
|
||||||
|
lradc->input = devm_input_allocate_device(dev);
|
||||||
|
if (!lradc->input)
|
||||||
|
return -ENOMEM;
|
||||||
|
|
||||||
|
lradc->input->name = pdev->name;
|
||||||
|
lradc->input->phys = "sun4i_lradc/input0";
|
||||||
|
lradc->input->open = sun4i_lradc_open;
|
||||||
|
lradc->input->close = sun4i_lradc_close;
|
||||||
|
lradc->input->id.bustype = BUS_HOST;
|
||||||
|
lradc->input->id.vendor = 0x0001;
|
||||||
|
lradc->input->id.product = 0x0001;
|
||||||
|
lradc->input->id.version = 0x0100;
|
||||||
|
|
||||||
|
__set_bit(EV_KEY, lradc->input->evbit);
|
||||||
|
for (i = 0; i < lradc->chan0_map_count; i++)
|
||||||
|
__set_bit(lradc->chan0_map[i].keycode, lradc->input->keybit);
|
||||||
|
|
||||||
|
input_set_drvdata(lradc->input, lradc);
|
||||||
|
|
||||||
|
lradc->base = devm_ioremap_resource(dev,
|
||||||
|
platform_get_resource(pdev, IORESOURCE_MEM, 0));
|
||||||
|
if (IS_ERR(lradc->base))
|
||||||
|
return PTR_ERR(lradc->base);
|
||||||
|
|
||||||
|
error = devm_request_irq(dev, platform_get_irq(pdev, 0),
|
||||||
|
sun4i_lradc_irq, 0,
|
||||||
|
"sun4i-a10-lradc-keys", lradc);
|
||||||
|
if (error)
|
||||||
|
return error;
|
||||||
|
|
||||||
|
error = input_register_device(lradc->input);
|
||||||
|
if (error)
|
||||||
|
return error;
|
||||||
|
|
||||||
|
platform_set_drvdata(pdev, lradc);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static const struct of_device_id sun4i_lradc_of_match[] = {
|
||||||
|
{ .compatible = "allwinner,sun4i-a10-lradc-keys", },
|
||||||
|
{ /* sentinel */ }
|
||||||
|
};
|
||||||
|
MODULE_DEVICE_TABLE(of, sun4i_lradc_of_match);
|
||||||
|
|
||||||
|
static struct platform_driver sun4i_lradc_driver = {
|
||||||
|
.driver = {
|
||||||
|
.name = "sun4i-a10-lradc-keys",
|
||||||
|
.of_match_table = of_match_ptr(sun4i_lradc_of_match),
|
||||||
|
},
|
||||||
|
.probe = sun4i_lradc_probe,
|
||||||
|
};
|
||||||
|
|
||||||
|
module_platform_driver(sun4i_lradc_driver);
|
||||||
|
|
||||||
|
MODULE_DESCRIPTION("Allwinner sun4i low res adc attached tablet keys driver");
|
||||||
|
MODULE_AUTHOR("Hans de Goede <hdegoede@redhat.com>");
|
||||||
|
MODULE_LICENSE("GPL");
|
|
@ -93,6 +93,16 @@ config INPUT_BMA150
|
||||||
To compile this driver as a module, choose M here: the
|
To compile this driver as a module, choose M here: the
|
||||||
module will be called bma150.
|
module will be called bma150.
|
||||||
|
|
||||||
|
config INPUT_E3X0_BUTTON
|
||||||
|
tristate "NI Ettus Research USRP E3x0 Button support."
|
||||||
|
default n
|
||||||
|
help
|
||||||
|
Say Y here to enable support for the NI Ettus Research
|
||||||
|
USRP E3x0 Button.
|
||||||
|
|
||||||
|
To compile this driver as a module, choose M here: the
|
||||||
|
module will be called e3x0_button.
|
||||||
|
|
||||||
config INPUT_PCSPKR
|
config INPUT_PCSPKR
|
||||||
tristate "PC Speaker support"
|
tristate "PC Speaker support"
|
||||||
depends on PCSPKR_PLATFORM
|
depends on PCSPKR_PLATFORM
|
||||||
|
@ -394,6 +404,18 @@ config INPUT_CM109
|
||||||
To compile this driver as a module, choose M here: the module will be
|
To compile this driver as a module, choose M here: the module will be
|
||||||
called cm109.
|
called cm109.
|
||||||
|
|
||||||
|
config INPUT_REGULATOR_HAPTIC
|
||||||
|
tristate "Regulator haptics support"
|
||||||
|
depends on REGULATOR
|
||||||
|
select INPUT_FF_MEMLESS
|
||||||
|
help
|
||||||
|
This option enables device driver support for the haptic controlled
|
||||||
|
by a regulator. This driver supports ff-memless interface
|
||||||
|
from input framework.
|
||||||
|
|
||||||
|
To compile this driver as a module, choose M here: the
|
||||||
|
module will be called regulator-haptic.
|
||||||
|
|
||||||
config INPUT_RETU_PWRBUTTON
|
config INPUT_RETU_PWRBUTTON
|
||||||
tristate "Retu Power button Driver"
|
tristate "Retu Power button Driver"
|
||||||
depends on MFD_RETU
|
depends on MFD_RETU
|
||||||
|
@ -404,6 +426,27 @@ config INPUT_RETU_PWRBUTTON
|
||||||
To compile this driver as a module, choose M here. The module will
|
To compile this driver as a module, choose M here. The module will
|
||||||
be called retu-pwrbutton.
|
be called retu-pwrbutton.
|
||||||
|
|
||||||
|
config INPUT_TPS65218_PWRBUTTON
|
||||||
|
tristate "TPS65218 Power button driver"
|
||||||
|
depends on MFD_TPS65218
|
||||||
|
help
|
||||||
|
Say Y here if you want to enable power buttong reporting for
|
||||||
|
the TPS65218 Power Management IC device.
|
||||||
|
|
||||||
|
To compile this driver as a module, choose M here. The module will
|
||||||
|
be called tps65218-pwrbutton.
|
||||||
|
|
||||||
|
config INPUT_AXP20X_PEK
|
||||||
|
tristate "X-Powers AXP20X power button driver"
|
||||||
|
depends on MFD_AXP20X
|
||||||
|
help
|
||||||
|
Say Y here if you want to enable power key reporting via the
|
||||||
|
AXP20X PMIC.
|
||||||
|
|
||||||
|
To compile this driver as a module, choose M here. The module will
|
||||||
|
be called axp20x-pek.
|
||||||
|
|
||||||
|
|
||||||
config INPUT_TWL4030_PWRBUTTON
|
config INPUT_TWL4030_PWRBUTTON
|
||||||
tristate "TWL4030 Power button Driver"
|
tristate "TWL4030 Power button Driver"
|
||||||
depends on TWL4030_CORE
|
depends on TWL4030_CORE
|
||||||
|
|
|
@ -26,6 +26,7 @@ obj-$(CONFIG_INPUT_COBALT_BTNS) += cobalt_btns.o
|
||||||
obj-$(CONFIG_INPUT_DA9052_ONKEY) += da9052_onkey.o
|
obj-$(CONFIG_INPUT_DA9052_ONKEY) += da9052_onkey.o
|
||||||
obj-$(CONFIG_INPUT_DA9055_ONKEY) += da9055_onkey.o
|
obj-$(CONFIG_INPUT_DA9055_ONKEY) += da9055_onkey.o
|
||||||
obj-$(CONFIG_INPUT_DM355EVM) += dm355evm_keys.o
|
obj-$(CONFIG_INPUT_DM355EVM) += dm355evm_keys.o
|
||||||
|
obj-$(CONFIG_INPUT_E3X0_BUTTON) += e3x0-button.o
|
||||||
obj-$(CONFIG_INPUT_DRV260X_HAPTICS) += drv260x.o
|
obj-$(CONFIG_INPUT_DRV260X_HAPTICS) += drv260x.o
|
||||||
obj-$(CONFIG_INPUT_DRV2667_HAPTICS) += drv2667.o
|
obj-$(CONFIG_INPUT_DRV2667_HAPTICS) += drv2667.o
|
||||||
obj-$(CONFIG_INPUT_GP2A) += gp2ap002a00f.o
|
obj-$(CONFIG_INPUT_GP2A) += gp2ap002a00f.o
|
||||||
|
@ -53,12 +54,15 @@ obj-$(CONFIG_INPUT_PMIC8XXX_PWRKEY) += pmic8xxx-pwrkey.o
|
||||||
obj-$(CONFIG_INPUT_POWERMATE) += powermate.o
|
obj-$(CONFIG_INPUT_POWERMATE) += powermate.o
|
||||||
obj-$(CONFIG_INPUT_PWM_BEEPER) += pwm-beeper.o
|
obj-$(CONFIG_INPUT_PWM_BEEPER) += pwm-beeper.o
|
||||||
obj-$(CONFIG_INPUT_RB532_BUTTON) += rb532_button.o
|
obj-$(CONFIG_INPUT_RB532_BUTTON) += rb532_button.o
|
||||||
|
obj-$(CONFIG_INPUT_REGULATOR_HAPTIC) += regulator-haptic.o
|
||||||
obj-$(CONFIG_INPUT_RETU_PWRBUTTON) += retu-pwrbutton.o
|
obj-$(CONFIG_INPUT_RETU_PWRBUTTON) += retu-pwrbutton.o
|
||||||
|
obj-$(CONFIG_INPUT_AXP20X_PEK) += axp20x-pek.o
|
||||||
obj-$(CONFIG_INPUT_GPIO_ROTARY_ENCODER) += rotary_encoder.o
|
obj-$(CONFIG_INPUT_GPIO_ROTARY_ENCODER) += rotary_encoder.o
|
||||||
obj-$(CONFIG_INPUT_SGI_BTNS) += sgi_btns.o
|
obj-$(CONFIG_INPUT_SGI_BTNS) += sgi_btns.o
|
||||||
obj-$(CONFIG_INPUT_SIRFSOC_ONKEY) += sirfsoc-onkey.o
|
obj-$(CONFIG_INPUT_SIRFSOC_ONKEY) += sirfsoc-onkey.o
|
||||||
obj-$(CONFIG_INPUT_SOC_BUTTON_ARRAY) += soc_button_array.o
|
obj-$(CONFIG_INPUT_SOC_BUTTON_ARRAY) += soc_button_array.o
|
||||||
obj-$(CONFIG_INPUT_SPARCSPKR) += sparcspkr.o
|
obj-$(CONFIG_INPUT_SPARCSPKR) += sparcspkr.o
|
||||||
|
obj-$(CONFIG_INPUT_TPS65218_PWRBUTTON) += tps65218-pwrbutton.o
|
||||||
obj-$(CONFIG_INPUT_TWL4030_PWRBUTTON) += twl4030-pwrbutton.o
|
obj-$(CONFIG_INPUT_TWL4030_PWRBUTTON) += twl4030-pwrbutton.o
|
||||||
obj-$(CONFIG_INPUT_TWL4030_VIBRA) += twl4030-vibra.o
|
obj-$(CONFIG_INPUT_TWL4030_VIBRA) += twl4030-vibra.o
|
||||||
obj-$(CONFIG_INPUT_TWL6040_VIBRA) += twl6040-vibra.o
|
obj-$(CONFIG_INPUT_TWL6040_VIBRA) += twl6040-vibra.o
|
||||||
|
|
290
drivers/input/misc/axp20x-pek.c
Normal file
290
drivers/input/misc/axp20x-pek.c
Normal file
|
@ -0,0 +1,290 @@
|
||||||
|
/*
|
||||||
|
* axp20x power button driver.
|
||||||
|
*
|
||||||
|
* Copyright (C) 2013 Carlo Caione <carlo@caione.org>
|
||||||
|
*
|
||||||
|
* This file is subject to the terms and conditions of the GNU General
|
||||||
|
* Public License. See the file "COPYING" in the main directory of this
|
||||||
|
* archive for more details.
|
||||||
|
*
|
||||||
|
* 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/errno.h>
|
||||||
|
#include <linux/irq.h>
|
||||||
|
#include <linux/init.h>
|
||||||
|
#include <linux/input.h>
|
||||||
|
#include <linux/interrupt.h>
|
||||||
|
#include <linux/kernel.h>
|
||||||
|
#include <linux/mfd/axp20x.h>
|
||||||
|
#include <linux/module.h>
|
||||||
|
#include <linux/platform_device.h>
|
||||||
|
#include <linux/regmap.h>
|
||||||
|
#include <linux/slab.h>
|
||||||
|
|
||||||
|
#define AXP20X_PEK_STARTUP_MASK (0xc0)
|
||||||
|
#define AXP20X_PEK_SHUTDOWN_MASK (0x03)
|
||||||
|
|
||||||
|
struct axp20x_pek {
|
||||||
|
struct axp20x_dev *axp20x;
|
||||||
|
struct input_dev *input;
|
||||||
|
int irq_dbr;
|
||||||
|
int irq_dbf;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct axp20x_time {
|
||||||
|
unsigned int time;
|
||||||
|
unsigned int idx;
|
||||||
|
};
|
||||||
|
|
||||||
|
static const struct axp20x_time startup_time[] = {
|
||||||
|
{ .time = 128, .idx = 0 },
|
||||||
|
{ .time = 1000, .idx = 2 },
|
||||||
|
{ .time = 3000, .idx = 1 },
|
||||||
|
{ .time = 2000, .idx = 3 },
|
||||||
|
};
|
||||||
|
|
||||||
|
static const struct axp20x_time shutdown_time[] = {
|
||||||
|
{ .time = 4000, .idx = 0 },
|
||||||
|
{ .time = 6000, .idx = 1 },
|
||||||
|
{ .time = 8000, .idx = 2 },
|
||||||
|
{ .time = 10000, .idx = 3 },
|
||||||
|
};
|
||||||
|
|
||||||
|
struct axp20x_pek_ext_attr {
|
||||||
|
const struct axp20x_time *p_time;
|
||||||
|
unsigned int mask;
|
||||||
|
};
|
||||||
|
|
||||||
|
static struct axp20x_pek_ext_attr axp20x_pek_startup_ext_attr = {
|
||||||
|
.p_time = startup_time,
|
||||||
|
.mask = AXP20X_PEK_STARTUP_MASK,
|
||||||
|
};
|
||||||
|
|
||||||
|
static struct axp20x_pek_ext_attr axp20x_pek_shutdown_ext_attr = {
|
||||||
|
.p_time = shutdown_time,
|
||||||
|
.mask = AXP20X_PEK_SHUTDOWN_MASK,
|
||||||
|
};
|
||||||
|
|
||||||
|
static struct axp20x_pek_ext_attr *get_axp_ext_attr(struct device_attribute *attr)
|
||||||
|
{
|
||||||
|
return container_of(attr, struct dev_ext_attribute, attr)->var;
|
||||||
|
}
|
||||||
|
|
||||||
|
static ssize_t axp20x_show_ext_attr(struct device *dev,
|
||||||
|
struct device_attribute *attr, char *buf)
|
||||||
|
{
|
||||||
|
struct axp20x_pek *axp20x_pek = dev_get_drvdata(dev);
|
||||||
|
struct axp20x_pek_ext_attr *axp20x_ea = get_axp_ext_attr(attr);
|
||||||
|
unsigned int val;
|
||||||
|
int ret, i;
|
||||||
|
|
||||||
|
ret = regmap_read(axp20x_pek->axp20x->regmap, AXP20X_PEK_KEY, &val);
|
||||||
|
if (ret != 0)
|
||||||
|
return ret;
|
||||||
|
|
||||||
|
val &= axp20x_ea->mask;
|
||||||
|
val >>= ffs(axp20x_ea->mask) - 1;
|
||||||
|
|
||||||
|
for (i = 0; i < 4; i++)
|
||||||
|
if (val == axp20x_ea->p_time[i].idx)
|
||||||
|
val = axp20x_ea->p_time[i].time;
|
||||||
|
|
||||||
|
return sprintf(buf, "%u\n", val);
|
||||||
|
}
|
||||||
|
|
||||||
|
static ssize_t axp20x_store_ext_attr(struct device *dev,
|
||||||
|
struct device_attribute *attr,
|
||||||
|
const char *buf, size_t count)
|
||||||
|
{
|
||||||
|
struct axp20x_pek *axp20x_pek = dev_get_drvdata(dev);
|
||||||
|
struct axp20x_pek_ext_attr *axp20x_ea = get_axp_ext_attr(attr);
|
||||||
|
char val_str[20];
|
||||||
|
size_t len;
|
||||||
|
int ret, i;
|
||||||
|
unsigned int val, idx = 0;
|
||||||
|
unsigned int best_err = UINT_MAX;
|
||||||
|
|
||||||
|
val_str[sizeof(val_str) - 1] = '\0';
|
||||||
|
strncpy(val_str, buf, sizeof(val_str) - 1);
|
||||||
|
len = strlen(val_str);
|
||||||
|
|
||||||
|
if (len && val_str[len - 1] == '\n')
|
||||||
|
val_str[len - 1] = '\0';
|
||||||
|
|
||||||
|
ret = kstrtouint(val_str, 10, &val);
|
||||||
|
if (ret)
|
||||||
|
return ret;
|
||||||
|
|
||||||
|
for (i = 3; i >= 0; i--) {
|
||||||
|
unsigned int err;
|
||||||
|
|
||||||
|
err = abs(axp20x_ea->p_time[i].time - val);
|
||||||
|
if (err < best_err) {
|
||||||
|
best_err = err;
|
||||||
|
idx = axp20x_ea->p_time[i].idx;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!err)
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
idx <<= ffs(axp20x_ea->mask) - 1;
|
||||||
|
ret = regmap_update_bits(axp20x_pek->axp20x->regmap,
|
||||||
|
AXP20X_PEK_KEY,
|
||||||
|
axp20x_ea->mask, idx);
|
||||||
|
if (ret != 0)
|
||||||
|
return -EINVAL;
|
||||||
|
|
||||||
|
return count;
|
||||||
|
}
|
||||||
|
|
||||||
|
static struct dev_ext_attribute axp20x_dev_attr_startup = {
|
||||||
|
.attr = __ATTR(startup, 0644, axp20x_show_ext_attr, axp20x_store_ext_attr),
|
||||||
|
.var = &axp20x_pek_startup_ext_attr,
|
||||||
|
};
|
||||||
|
|
||||||
|
static struct dev_ext_attribute axp20x_dev_attr_shutdown = {
|
||||||
|
.attr = __ATTR(shutdown, 0644, axp20x_show_ext_attr, axp20x_store_ext_attr),
|
||||||
|
.var = &axp20x_pek_shutdown_ext_attr,
|
||||||
|
};
|
||||||
|
|
||||||
|
static struct attribute *axp20x_attributes[] = {
|
||||||
|
&axp20x_dev_attr_startup.attr.attr,
|
||||||
|
&axp20x_dev_attr_shutdown.attr.attr,
|
||||||
|
NULL,
|
||||||
|
};
|
||||||
|
|
||||||
|
static const struct attribute_group axp20x_attribute_group = {
|
||||||
|
.attrs = axp20x_attributes,
|
||||||
|
};
|
||||||
|
|
||||||
|
static irqreturn_t axp20x_pek_irq(int irq, void *pwr)
|
||||||
|
{
|
||||||
|
struct input_dev *idev = pwr;
|
||||||
|
struct axp20x_pek *axp20x_pek = input_get_drvdata(idev);
|
||||||
|
|
||||||
|
if (irq == axp20x_pek->irq_dbr)
|
||||||
|
input_report_key(idev, KEY_POWER, true);
|
||||||
|
else if (irq == axp20x_pek->irq_dbf)
|
||||||
|
input_report_key(idev, KEY_POWER, false);
|
||||||
|
|
||||||
|
input_sync(idev);
|
||||||
|
|
||||||
|
return IRQ_HANDLED;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void axp20x_remove_sysfs_group(void *_data)
|
||||||
|
{
|
||||||
|
struct device *dev = _data;
|
||||||
|
|
||||||
|
sysfs_remove_group(&dev->kobj, &axp20x_attribute_group);
|
||||||
|
}
|
||||||
|
|
||||||
|
static int axp20x_pek_probe(struct platform_device *pdev)
|
||||||
|
{
|
||||||
|
struct axp20x_pek *axp20x_pek;
|
||||||
|
struct axp20x_dev *axp20x;
|
||||||
|
struct input_dev *idev;
|
||||||
|
int error;
|
||||||
|
|
||||||
|
axp20x_pek = devm_kzalloc(&pdev->dev, sizeof(struct axp20x_pek),
|
||||||
|
GFP_KERNEL);
|
||||||
|
if (!axp20x_pek)
|
||||||
|
return -ENOMEM;
|
||||||
|
|
||||||
|
axp20x_pek->axp20x = dev_get_drvdata(pdev->dev.parent);
|
||||||
|
axp20x = axp20x_pek->axp20x;
|
||||||
|
|
||||||
|
axp20x_pek->irq_dbr = platform_get_irq_byname(pdev, "PEK_DBR");
|
||||||
|
if (axp20x_pek->irq_dbr < 0) {
|
||||||
|
dev_err(&pdev->dev, "No IRQ for PEK_DBR, error=%d\n",
|
||||||
|
axp20x_pek->irq_dbr);
|
||||||
|
return axp20x_pek->irq_dbr;
|
||||||
|
}
|
||||||
|
axp20x_pek->irq_dbr = regmap_irq_get_virq(axp20x->regmap_irqc,
|
||||||
|
axp20x_pek->irq_dbr);
|
||||||
|
|
||||||
|
axp20x_pek->irq_dbf = platform_get_irq_byname(pdev, "PEK_DBF");
|
||||||
|
if (axp20x_pek->irq_dbf < 0) {
|
||||||
|
dev_err(&pdev->dev, "No IRQ for PEK_DBF, error=%d\n",
|
||||||
|
axp20x_pek->irq_dbf);
|
||||||
|
return axp20x_pek->irq_dbf;
|
||||||
|
}
|
||||||
|
axp20x_pek->irq_dbf = regmap_irq_get_virq(axp20x->regmap_irqc,
|
||||||
|
axp20x_pek->irq_dbf);
|
||||||
|
|
||||||
|
axp20x_pek->input = devm_input_allocate_device(&pdev->dev);
|
||||||
|
if (!axp20x_pek->input)
|
||||||
|
return -ENOMEM;
|
||||||
|
|
||||||
|
idev = axp20x_pek->input;
|
||||||
|
|
||||||
|
idev->name = "axp20x-pek";
|
||||||
|
idev->phys = "m1kbd/input2";
|
||||||
|
idev->dev.parent = &pdev->dev;
|
||||||
|
|
||||||
|
input_set_capability(idev, EV_KEY, KEY_POWER);
|
||||||
|
|
||||||
|
input_set_drvdata(idev, axp20x_pek);
|
||||||
|
|
||||||
|
error = devm_request_any_context_irq(&pdev->dev, axp20x_pek->irq_dbr,
|
||||||
|
axp20x_pek_irq, 0,
|
||||||
|
"axp20x-pek-dbr", idev);
|
||||||
|
if (error < 0) {
|
||||||
|
dev_err(axp20x->dev, "Failed to request dbr IRQ#%d: %d\n",
|
||||||
|
axp20x_pek->irq_dbr, error);
|
||||||
|
return error;
|
||||||
|
}
|
||||||
|
|
||||||
|
error = devm_request_any_context_irq(&pdev->dev, axp20x_pek->irq_dbf,
|
||||||
|
axp20x_pek_irq, 0,
|
||||||
|
"axp20x-pek-dbf", idev);
|
||||||
|
if (error < 0) {
|
||||||
|
dev_err(axp20x->dev, "Failed to request dbf IRQ#%d: %d\n",
|
||||||
|
axp20x_pek->irq_dbf, error);
|
||||||
|
return error;
|
||||||
|
}
|
||||||
|
|
||||||
|
error = sysfs_create_group(&pdev->dev.kobj, &axp20x_attribute_group);
|
||||||
|
if (error) {
|
||||||
|
dev_err(axp20x->dev, "Failed to create sysfs attributes: %d\n",
|
||||||
|
error);
|
||||||
|
return error;
|
||||||
|
}
|
||||||
|
|
||||||
|
error = devm_add_action(&pdev->dev,
|
||||||
|
axp20x_remove_sysfs_group, &pdev->dev);
|
||||||
|
if (error) {
|
||||||
|
axp20x_remove_sysfs_group(&pdev->dev);
|
||||||
|
dev_err(&pdev->dev, "Failed to add sysfs cleanup action: %d\n",
|
||||||
|
error);
|
||||||
|
return error;
|
||||||
|
}
|
||||||
|
|
||||||
|
error = input_register_device(idev);
|
||||||
|
if (error) {
|
||||||
|
dev_err(axp20x->dev, "Can't register input device: %d\n",
|
||||||
|
error);
|
||||||
|
return error;
|
||||||
|
}
|
||||||
|
|
||||||
|
platform_set_drvdata(pdev, axp20x_pek);
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static struct platform_driver axp20x_pek_driver = {
|
||||||
|
.probe = axp20x_pek_probe,
|
||||||
|
.driver = {
|
||||||
|
.name = "axp20x-pek",
|
||||||
|
},
|
||||||
|
};
|
||||||
|
module_platform_driver(axp20x_pek_driver);
|
||||||
|
|
||||||
|
MODULE_DESCRIPTION("axp20x Power Button");
|
||||||
|
MODULE_AUTHOR("Carlo Caione <carlo@caione.org>");
|
||||||
|
MODULE_LICENSE("GPL");
|
|
@ -733,7 +733,6 @@ static struct i2c_driver drv260x_driver = {
|
||||||
};
|
};
|
||||||
module_i2c_driver(drv260x_driver);
|
module_i2c_driver(drv260x_driver);
|
||||||
|
|
||||||
MODULE_ALIAS("platform:drv260x-haptics");
|
|
||||||
MODULE_DESCRIPTION("TI DRV260x haptics driver");
|
MODULE_DESCRIPTION("TI DRV260x haptics driver");
|
||||||
MODULE_LICENSE("GPL");
|
MODULE_LICENSE("GPL");
|
||||||
MODULE_AUTHOR("Dan Murphy <dmurphy@ti.com>");
|
MODULE_AUTHOR("Dan Murphy <dmurphy@ti.com>");
|
||||||
|
|
|
@ -492,7 +492,6 @@ static struct i2c_driver drv2667_driver = {
|
||||||
};
|
};
|
||||||
module_i2c_driver(drv2667_driver);
|
module_i2c_driver(drv2667_driver);
|
||||||
|
|
||||||
MODULE_ALIAS("platform:drv2667-haptics");
|
|
||||||
MODULE_DESCRIPTION("TI DRV2667 haptics driver");
|
MODULE_DESCRIPTION("TI DRV2667 haptics driver");
|
||||||
MODULE_LICENSE("GPL");
|
MODULE_LICENSE("GPL");
|
||||||
MODULE_AUTHOR("Dan Murphy <dmurphy@ti.com>");
|
MODULE_AUTHOR("Dan Murphy <dmurphy@ti.com>");
|
||||||
|
|
157
drivers/input/misc/e3x0-button.c
Normal file
157
drivers/input/misc/e3x0-button.c
Normal file
|
@ -0,0 +1,157 @@
|
||||||
|
/*
|
||||||
|
* Copyright (c) 2014, National Instruments Corp. All rights reserved.
|
||||||
|
*
|
||||||
|
* Driver for NI Ettus Research USRP E3x0 Button Driver
|
||||||
|
*
|
||||||
|
* 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 of the License.
|
||||||
|
*
|
||||||
|
* 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/device.h>
|
||||||
|
#include <linux/kernel.h>
|
||||||
|
#include <linux/module.h>
|
||||||
|
#include <linux/platform_device.h>
|
||||||
|
#include <linux/input.h>
|
||||||
|
#include <linux/interrupt.h>
|
||||||
|
#include <linux/of.h>
|
||||||
|
#include <linux/slab.h>
|
||||||
|
|
||||||
|
static irqreturn_t e3x0_button_release_handler(int irq, void *data)
|
||||||
|
{
|
||||||
|
struct input_dev *idev = data;
|
||||||
|
|
||||||
|
input_report_key(idev, KEY_POWER, 0);
|
||||||
|
input_sync(idev);
|
||||||
|
|
||||||
|
return IRQ_HANDLED;
|
||||||
|
}
|
||||||
|
|
||||||
|
static irqreturn_t e3x0_button_press_handler(int irq, void *data)
|
||||||
|
{
|
||||||
|
struct input_dev *idev = data;
|
||||||
|
|
||||||
|
input_report_key(idev, KEY_POWER, 1);
|
||||||
|
pm_wakeup_event(idev->dev.parent, 0);
|
||||||
|
input_sync(idev);
|
||||||
|
|
||||||
|
return IRQ_HANDLED;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int __maybe_unused e3x0_button_suspend(struct device *dev)
|
||||||
|
{
|
||||||
|
struct platform_device *pdev = to_platform_device(dev);
|
||||||
|
|
||||||
|
if (device_may_wakeup(dev))
|
||||||
|
enable_irq_wake(platform_get_irq_byname(pdev, "press"));
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int __maybe_unused e3x0_button_resume(struct device *dev)
|
||||||
|
{
|
||||||
|
struct platform_device *pdev = to_platform_device(dev);
|
||||||
|
|
||||||
|
if (device_may_wakeup(dev))
|
||||||
|
disable_irq_wake(platform_get_irq_byname(pdev, "press"));
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static SIMPLE_DEV_PM_OPS(e3x0_button_pm_ops,
|
||||||
|
e3x0_button_suspend, e3x0_button_resume);
|
||||||
|
|
||||||
|
static int e3x0_button_probe(struct platform_device *pdev)
|
||||||
|
{
|
||||||
|
struct input_dev *input;
|
||||||
|
int irq_press, irq_release;
|
||||||
|
int error;
|
||||||
|
|
||||||
|
irq_press = platform_get_irq_byname(pdev, "press");
|
||||||
|
if (irq_press < 0) {
|
||||||
|
dev_err(&pdev->dev, "No IRQ for 'press', error=%d\n",
|
||||||
|
irq_press);
|
||||||
|
return irq_press;
|
||||||
|
}
|
||||||
|
|
||||||
|
irq_release = platform_get_irq_byname(pdev, "release");
|
||||||
|
if (irq_release < 0) {
|
||||||
|
dev_err(&pdev->dev, "No IRQ for 'release', error=%d\n",
|
||||||
|
irq_release);
|
||||||
|
return irq_release;
|
||||||
|
}
|
||||||
|
|
||||||
|
input = devm_input_allocate_device(&pdev->dev);
|
||||||
|
if (!input)
|
||||||
|
return -ENOMEM;
|
||||||
|
|
||||||
|
input->name = "NI Ettus Research USRP E3x0 Button Driver";
|
||||||
|
input->phys = "e3x0_button/input0";
|
||||||
|
input->dev.parent = &pdev->dev;
|
||||||
|
|
||||||
|
input_set_capability(input, EV_KEY, KEY_POWER);
|
||||||
|
|
||||||
|
error = devm_request_irq(&pdev->dev, irq_press,
|
||||||
|
e3x0_button_press_handler, 0,
|
||||||
|
"e3x0-button", input);
|
||||||
|
if (error) {
|
||||||
|
dev_err(&pdev->dev, "Failed to request 'press' IRQ#%d: %d\n",
|
||||||
|
irq_press, error);
|
||||||
|
return error;
|
||||||
|
}
|
||||||
|
|
||||||
|
error = devm_request_irq(&pdev->dev, irq_release,
|
||||||
|
e3x0_button_release_handler, 0,
|
||||||
|
"e3x0-button", input);
|
||||||
|
if (error) {
|
||||||
|
dev_err(&pdev->dev, "Failed to request 'release' IRQ#%d: %d\n",
|
||||||
|
irq_release, error);
|
||||||
|
return error;
|
||||||
|
}
|
||||||
|
|
||||||
|
error = input_register_device(input);
|
||||||
|
if (error) {
|
||||||
|
dev_err(&pdev->dev, "Can't register input device: %d\n", error);
|
||||||
|
return error;
|
||||||
|
}
|
||||||
|
|
||||||
|
platform_set_drvdata(pdev, input);
|
||||||
|
device_init_wakeup(&pdev->dev, 1);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int e3x0_button_remove(struct platform_device *pdev)
|
||||||
|
{
|
||||||
|
device_init_wakeup(&pdev->dev, 0);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
#ifdef CONFIG_OF
|
||||||
|
static const struct of_device_id e3x0_button_match[] = {
|
||||||
|
{ .compatible = "ettus,e3x0-button", },
|
||||||
|
{ }
|
||||||
|
};
|
||||||
|
MODULE_DEVICE_TABLE(of, e3x0_button_match);
|
||||||
|
#endif
|
||||||
|
|
||||||
|
static struct platform_driver e3x0_button_driver = {
|
||||||
|
.driver = {
|
||||||
|
.name = "e3x0-button",
|
||||||
|
.of_match_table = of_match_ptr(e3x0_button_match),
|
||||||
|
.pm = &e3x0_button_pm_ops,
|
||||||
|
},
|
||||||
|
.probe = e3x0_button_probe,
|
||||||
|
.remove = e3x0_button_remove,
|
||||||
|
};
|
||||||
|
|
||||||
|
module_platform_driver(e3x0_button_driver);
|
||||||
|
|
||||||
|
MODULE_LICENSE("GPL v2");
|
||||||
|
MODULE_AUTHOR("Moritz Fischer <moritz.fischer@ettus.com>");
|
||||||
|
MODULE_DESCRIPTION("NI Ettus Research USRP E3x0 Button driver");
|
||||||
|
MODULE_ALIAS("platform:e3x0-button");
|
266
drivers/input/misc/regulator-haptic.c
Normal file
266
drivers/input/misc/regulator-haptic.c
Normal file
|
@ -0,0 +1,266 @@
|
||||||
|
/*
|
||||||
|
* Regulator haptic driver
|
||||||
|
*
|
||||||
|
* Copyright (c) 2014 Samsung Electronics Co., Ltd.
|
||||||
|
* Author: Jaewon Kim <jaewon02.kim@samsung.com>
|
||||||
|
* Author: Hyunhee Kim <hyunhee.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 version 2 as
|
||||||
|
* published by the Free Software Foundation.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <linux/input.h>
|
||||||
|
#include <linux/module.h>
|
||||||
|
#include <linux/of.h>
|
||||||
|
#include <linux/platform_data/regulator-haptic.h>
|
||||||
|
#include <linux/platform_device.h>
|
||||||
|
#include <linux/regulator/consumer.h>
|
||||||
|
#include <linux/slab.h>
|
||||||
|
|
||||||
|
#define MAX_MAGNITUDE_SHIFT 16
|
||||||
|
|
||||||
|
struct regulator_haptic {
|
||||||
|
struct device *dev;
|
||||||
|
struct input_dev *input_dev;
|
||||||
|
struct regulator *regulator;
|
||||||
|
|
||||||
|
struct work_struct work;
|
||||||
|
struct mutex mutex;
|
||||||
|
|
||||||
|
bool active;
|
||||||
|
bool suspended;
|
||||||
|
|
||||||
|
unsigned int max_volt;
|
||||||
|
unsigned int min_volt;
|
||||||
|
unsigned int magnitude;
|
||||||
|
};
|
||||||
|
|
||||||
|
static int regulator_haptic_toggle(struct regulator_haptic *haptic, bool on)
|
||||||
|
{
|
||||||
|
int error;
|
||||||
|
|
||||||
|
if (haptic->active != on) {
|
||||||
|
|
||||||
|
error = on ? regulator_enable(haptic->regulator) :
|
||||||
|
regulator_disable(haptic->regulator);
|
||||||
|
if (error) {
|
||||||
|
dev_err(haptic->dev,
|
||||||
|
"failed to switch regulator %s: %d\n",
|
||||||
|
on ? "on" : "off", error);
|
||||||
|
return error;
|
||||||
|
}
|
||||||
|
|
||||||
|
haptic->active = on;
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int regulator_haptic_set_voltage(struct regulator_haptic *haptic,
|
||||||
|
unsigned int magnitude)
|
||||||
|
{
|
||||||
|
u64 volt_mag_multi;
|
||||||
|
unsigned int intensity;
|
||||||
|
int error;
|
||||||
|
|
||||||
|
volt_mag_multi = (u64)(haptic->max_volt - haptic->min_volt) * magnitude;
|
||||||
|
intensity = (unsigned int)(volt_mag_multi >> MAX_MAGNITUDE_SHIFT);
|
||||||
|
|
||||||
|
error = regulator_set_voltage(haptic->regulator,
|
||||||
|
intensity + haptic->min_volt,
|
||||||
|
haptic->max_volt);
|
||||||
|
if (error) {
|
||||||
|
dev_err(haptic->dev, "cannot set regulator voltage to %d: %d\n",
|
||||||
|
intensity + haptic->min_volt, error);
|
||||||
|
return error;
|
||||||
|
}
|
||||||
|
|
||||||
|
regulator_haptic_toggle(haptic, !!magnitude);
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void regulator_haptic_work(struct work_struct *work)
|
||||||
|
{
|
||||||
|
struct regulator_haptic *haptic = container_of(work,
|
||||||
|
struct regulator_haptic, work);
|
||||||
|
|
||||||
|
mutex_lock(&haptic->mutex);
|
||||||
|
|
||||||
|
if (!haptic->suspended)
|
||||||
|
regulator_haptic_set_voltage(haptic, haptic->magnitude);
|
||||||
|
|
||||||
|
mutex_unlock(&haptic->mutex);
|
||||||
|
}
|
||||||
|
|
||||||
|
static int regulator_haptic_play_effect(struct input_dev *input, void *data,
|
||||||
|
struct ff_effect *effect)
|
||||||
|
{
|
||||||
|
struct regulator_haptic *haptic = input_get_drvdata(input);
|
||||||
|
|
||||||
|
haptic->magnitude = effect->u.rumble.strong_magnitude;
|
||||||
|
if (!haptic->magnitude)
|
||||||
|
haptic->magnitude = effect->u.rumble.weak_magnitude;
|
||||||
|
|
||||||
|
schedule_work(&haptic->work);
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void regulator_haptic_close(struct input_dev *input)
|
||||||
|
{
|
||||||
|
struct regulator_haptic *haptic = input_get_drvdata(input);
|
||||||
|
|
||||||
|
cancel_work_sync(&haptic->work);
|
||||||
|
regulator_haptic_set_voltage(haptic, 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
static int __maybe_unused
|
||||||
|
regulator_haptic_parse_dt(struct device *dev, struct regulator_haptic *haptic)
|
||||||
|
{
|
||||||
|
struct device_node *node;
|
||||||
|
int error;
|
||||||
|
|
||||||
|
node = dev->of_node;
|
||||||
|
if(!node) {
|
||||||
|
dev_err(dev, "Missing dveice tree data\n");
|
||||||
|
return -EINVAL;
|
||||||
|
}
|
||||||
|
|
||||||
|
error = of_property_read_u32(node, "max-microvolt", &haptic->max_volt);
|
||||||
|
if (error) {
|
||||||
|
dev_err(dev, "cannot parse max-microvolt\n");
|
||||||
|
return error;
|
||||||
|
}
|
||||||
|
|
||||||
|
error = of_property_read_u32(node, "min-microvolt", &haptic->min_volt);
|
||||||
|
if (error) {
|
||||||
|
dev_err(dev, "cannot parse min-microvolt\n");
|
||||||
|
return error;
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int regulator_haptic_probe(struct platform_device *pdev)
|
||||||
|
{
|
||||||
|
const struct regulator_haptic_data *pdata = dev_get_platdata(&pdev->dev);
|
||||||
|
struct regulator_haptic *haptic;
|
||||||
|
struct input_dev *input_dev;
|
||||||
|
int error;
|
||||||
|
|
||||||
|
haptic = devm_kzalloc(&pdev->dev, sizeof(*haptic), GFP_KERNEL);
|
||||||
|
if (!haptic)
|
||||||
|
return -ENOMEM;
|
||||||
|
|
||||||
|
platform_set_drvdata(pdev, haptic);
|
||||||
|
haptic->dev = &pdev->dev;
|
||||||
|
mutex_init(&haptic->mutex);
|
||||||
|
INIT_WORK(&haptic->work, regulator_haptic_work);
|
||||||
|
|
||||||
|
if (pdata) {
|
||||||
|
haptic->max_volt = pdata->max_volt;
|
||||||
|
haptic->min_volt = pdata->min_volt;
|
||||||
|
} else if (IS_ENABLED(CONFIG_OF)) {
|
||||||
|
error = regulator_haptic_parse_dt(&pdev->dev, haptic);
|
||||||
|
if (error)
|
||||||
|
return error;
|
||||||
|
} else {
|
||||||
|
dev_err(&pdev->dev, "Missing platform data\n");
|
||||||
|
return -EINVAL;
|
||||||
|
}
|
||||||
|
|
||||||
|
haptic->regulator = devm_regulator_get_exclusive(&pdev->dev, "haptic");
|
||||||
|
if (IS_ERR(haptic->regulator)) {
|
||||||
|
dev_err(&pdev->dev, "failed to get regulator\n");
|
||||||
|
return PTR_ERR(haptic->regulator);
|
||||||
|
}
|
||||||
|
|
||||||
|
input_dev = devm_input_allocate_device(&pdev->dev);
|
||||||
|
if (!input_dev)
|
||||||
|
return -ENOMEM;
|
||||||
|
|
||||||
|
haptic->input_dev = input_dev;
|
||||||
|
haptic->input_dev->name = "regulator-haptic";
|
||||||
|
haptic->input_dev->dev.parent = &pdev->dev;
|
||||||
|
haptic->input_dev->close = regulator_haptic_close;
|
||||||
|
input_set_drvdata(haptic->input_dev, haptic);
|
||||||
|
input_set_capability(haptic->input_dev, EV_FF, FF_RUMBLE);
|
||||||
|
|
||||||
|
error = input_ff_create_memless(input_dev, NULL,
|
||||||
|
regulator_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;
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int __maybe_unused regulator_haptic_suspend(struct device *dev)
|
||||||
|
{
|
||||||
|
struct platform_device *pdev = to_platform_device(dev);
|
||||||
|
struct regulator_haptic *haptic = platform_get_drvdata(pdev);
|
||||||
|
int error;
|
||||||
|
|
||||||
|
error = mutex_lock_interruptible(&haptic->mutex);
|
||||||
|
if (error)
|
||||||
|
return error;
|
||||||
|
|
||||||
|
regulator_haptic_set_voltage(haptic, 0);
|
||||||
|
|
||||||
|
haptic->suspended = true;
|
||||||
|
|
||||||
|
mutex_unlock(&haptic->mutex);
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int __maybe_unused regulator_haptic_resume(struct device *dev)
|
||||||
|
{
|
||||||
|
struct platform_device *pdev = to_platform_device(dev);
|
||||||
|
struct regulator_haptic *haptic = platform_get_drvdata(pdev);
|
||||||
|
unsigned int magnitude;
|
||||||
|
|
||||||
|
mutex_lock(&haptic->mutex);
|
||||||
|
|
||||||
|
haptic->suspended = false;
|
||||||
|
|
||||||
|
magnitude = ACCESS_ONCE(haptic->magnitude);
|
||||||
|
if (magnitude)
|
||||||
|
regulator_haptic_set_voltage(haptic, magnitude);
|
||||||
|
|
||||||
|
mutex_unlock(&haptic->mutex);
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static SIMPLE_DEV_PM_OPS(regulator_haptic_pm_ops,
|
||||||
|
regulator_haptic_suspend, regulator_haptic_resume);
|
||||||
|
|
||||||
|
static struct of_device_id regulator_haptic_dt_match[] = {
|
||||||
|
{ .compatible = "regulator-haptic" },
|
||||||
|
{ /* sentinel */ },
|
||||||
|
};
|
||||||
|
|
||||||
|
static struct platform_driver regulator_haptic_driver = {
|
||||||
|
.probe = regulator_haptic_probe,
|
||||||
|
.driver = {
|
||||||
|
.name = "regulator-haptic",
|
||||||
|
.of_match_table = regulator_haptic_dt_match,
|
||||||
|
.pm = ®ulator_haptic_pm_ops,
|
||||||
|
},
|
||||||
|
};
|
||||||
|
module_platform_driver(regulator_haptic_driver);
|
||||||
|
|
||||||
|
MODULE_AUTHOR("Jaewon Kim <jaewon02.kim@samsung.com>");
|
||||||
|
MODULE_AUTHOR("Hyunhee Kim <hyunhee.kim@samsung.com>");
|
||||||
|
MODULE_DESCRIPTION("Regulator haptic driver");
|
||||||
|
MODULE_LICENSE("GPL");
|
126
drivers/input/misc/tps65218-pwrbutton.c
Normal file
126
drivers/input/misc/tps65218-pwrbutton.c
Normal file
|
@ -0,0 +1,126 @@
|
||||||
|
/*
|
||||||
|
* Texas Instruments' TPS65218 Power Button Input Driver
|
||||||
|
*
|
||||||
|
* Copyright (C) 2014 Texas Instruments Incorporated - http://www.ti.com/
|
||||||
|
* Author: Felipe Balbi <balbi@ti.com>
|
||||||
|
*
|
||||||
|
* 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.
|
||||||
|
*
|
||||||
|
* 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/init.h>
|
||||||
|
#include <linux/input.h>
|
||||||
|
#include <linux/interrupt.h>
|
||||||
|
#include <linux/kernel.h>
|
||||||
|
#include <linux/mfd/tps65218.h>
|
||||||
|
#include <linux/module.h>
|
||||||
|
#include <linux/of.h>
|
||||||
|
#include <linux/platform_device.h>
|
||||||
|
#include <linux/slab.h>
|
||||||
|
|
||||||
|
struct tps65218_pwrbutton {
|
||||||
|
struct device *dev;
|
||||||
|
struct tps65218 *tps;
|
||||||
|
struct input_dev *idev;
|
||||||
|
};
|
||||||
|
|
||||||
|
static irqreturn_t tps65218_pwr_irq(int irq, void *_pwr)
|
||||||
|
{
|
||||||
|
struct tps65218_pwrbutton *pwr = _pwr;
|
||||||
|
unsigned int reg;
|
||||||
|
int error;
|
||||||
|
|
||||||
|
error = tps65218_reg_read(pwr->tps, TPS65218_REG_STATUS, ®);
|
||||||
|
if (error) {
|
||||||
|
dev_err(pwr->dev, "can't read register: %d\n", error);
|
||||||
|
goto out;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (reg & TPS65218_STATUS_PB_STATE) {
|
||||||
|
input_report_key(pwr->idev, KEY_POWER, 1);
|
||||||
|
pm_wakeup_event(pwr->dev, 0);
|
||||||
|
} else {
|
||||||
|
input_report_key(pwr->idev, KEY_POWER, 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
input_sync(pwr->idev);
|
||||||
|
|
||||||
|
out:
|
||||||
|
return IRQ_HANDLED;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int tps65218_pwron_probe(struct platform_device *pdev)
|
||||||
|
{
|
||||||
|
struct tps65218 *tps = dev_get_drvdata(pdev->dev.parent);
|
||||||
|
struct device *dev = &pdev->dev;
|
||||||
|
struct tps65218_pwrbutton *pwr;
|
||||||
|
struct input_dev *idev;
|
||||||
|
int error;
|
||||||
|
int irq;
|
||||||
|
|
||||||
|
pwr = devm_kzalloc(dev, sizeof(*pwr), GFP_KERNEL);
|
||||||
|
if (!pwr)
|
||||||
|
return -ENOMEM;
|
||||||
|
|
||||||
|
idev = devm_input_allocate_device(dev);
|
||||||
|
if (!idev)
|
||||||
|
return -ENOMEM;
|
||||||
|
|
||||||
|
idev->name = "tps65218_pwrbutton";
|
||||||
|
idev->phys = "tps65218_pwrbutton/input0";
|
||||||
|
idev->dev.parent = dev;
|
||||||
|
idev->id.bustype = BUS_I2C;
|
||||||
|
|
||||||
|
input_set_capability(idev, EV_KEY, KEY_POWER);
|
||||||
|
|
||||||
|
pwr->tps = tps;
|
||||||
|
pwr->dev = dev;
|
||||||
|
pwr->idev = idev;
|
||||||
|
platform_set_drvdata(pdev, pwr);
|
||||||
|
device_init_wakeup(dev, true);
|
||||||
|
|
||||||
|
irq = platform_get_irq(pdev, 0);
|
||||||
|
error = devm_request_threaded_irq(dev, irq, NULL, tps65218_pwr_irq,
|
||||||
|
IRQF_TRIGGER_RISING |
|
||||||
|
IRQF_TRIGGER_FALLING |
|
||||||
|
IRQF_ONESHOT,
|
||||||
|
"tps65218-pwrbutton", pwr);
|
||||||
|
if (error) {
|
||||||
|
dev_err(dev, "failed to request IRQ #%d: %d\n",
|
||||||
|
irq, error);
|
||||||
|
return error;
|
||||||
|
}
|
||||||
|
|
||||||
|
error= input_register_device(idev);
|
||||||
|
if (error) {
|
||||||
|
dev_err(dev, "Can't register power button: %d\n", error);
|
||||||
|
return error;
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static struct of_device_id of_tps65218_pwr_match[] = {
|
||||||
|
{ .compatible = "ti,tps65218-pwrbutton" },
|
||||||
|
{ },
|
||||||
|
};
|
||||||
|
MODULE_DEVICE_TABLE(of, of_tps65218_pwr_match);
|
||||||
|
|
||||||
|
static struct platform_driver tps65218_pwron_driver = {
|
||||||
|
.probe = tps65218_pwron_probe,
|
||||||
|
.driver = {
|
||||||
|
.name = "tps65218_pwrbutton",
|
||||||
|
.of_match_table = of_tps65218_pwr_match,
|
||||||
|
},
|
||||||
|
};
|
||||||
|
module_platform_driver(tps65218_pwron_driver);
|
||||||
|
|
||||||
|
MODULE_DESCRIPTION("TPS65218 Power Button");
|
||||||
|
MODULE_LICENSE("GPL v2");
|
||||||
|
MODULE_AUTHOR("Felipe Balbi <balbi@ti.com>");
|
|
@ -105,19 +105,12 @@ config MOUSE_PS2_ELANTECH
|
||||||
Say Y here if you have an Elantech PS/2 touchpad connected
|
Say Y here if you have an Elantech PS/2 touchpad connected
|
||||||
to your system.
|
to your system.
|
||||||
|
|
||||||
Note that if you enable this driver you will need an updated
|
|
||||||
X.org Synaptics driver that does not require ABS_PRESSURE
|
|
||||||
reports from the touchpad (i.e. post 1.5.0 version). You can
|
|
||||||
grab a patch for the driver here:
|
|
||||||
|
|
||||||
http://userweb.kernel.org/~dtor/synaptics-no-abspressure.patch
|
|
||||||
|
|
||||||
If unsure, say N.
|
|
||||||
|
|
||||||
This driver exposes some configuration registers via sysfs
|
This driver exposes some configuration registers via sysfs
|
||||||
entries. For further information,
|
entries. For further information,
|
||||||
see <file:Documentation/input/elantech.txt>.
|
see <file:Documentation/input/elantech.txt>.
|
||||||
|
|
||||||
|
If unsure, say N.
|
||||||
|
|
||||||
config MOUSE_PS2_SENTELIC
|
config MOUSE_PS2_SENTELIC
|
||||||
bool "Sentelic Finger Sensing Pad PS/2 protocol extension"
|
bool "Sentelic Finger Sensing Pad PS/2 protocol extension"
|
||||||
depends on MOUSE_PS2
|
depends on MOUSE_PS2
|
||||||
|
@ -146,6 +139,16 @@ config MOUSE_PS2_OLPC
|
||||||
|
|
||||||
If unsure, say N.
|
If unsure, say N.
|
||||||
|
|
||||||
|
config MOUSE_PS2_FOCALTECH
|
||||||
|
bool "FocalTech PS/2 mouse protocol extension" if EXPERT
|
||||||
|
default y
|
||||||
|
depends on MOUSE_PS2
|
||||||
|
help
|
||||||
|
Say Y here if you have a FocalTech PS/2 TouchPad connected to
|
||||||
|
your system.
|
||||||
|
|
||||||
|
If unsure, say Y.
|
||||||
|
|
||||||
config MOUSE_SERIAL
|
config MOUSE_SERIAL
|
||||||
tristate "Serial mouse"
|
tristate "Serial mouse"
|
||||||
select SERIO
|
select SERIO
|
||||||
|
@ -206,6 +209,7 @@ config MOUSE_BCM5974
|
||||||
config MOUSE_CYAPA
|
config MOUSE_CYAPA
|
||||||
tristate "Cypress APA I2C Trackpad support"
|
tristate "Cypress APA I2C Trackpad support"
|
||||||
depends on I2C
|
depends on I2C
|
||||||
|
select CRC_ITU_T
|
||||||
help
|
help
|
||||||
This driver adds support for Cypress All Points Addressable (APA)
|
This driver adds support for Cypress All Points Addressable (APA)
|
||||||
I2C Trackpads, including the ones used in 2012 Samsung Chromebooks.
|
I2C Trackpads, including the ones used in 2012 Samsung Chromebooks.
|
||||||
|
|
|
@ -8,7 +8,7 @@ obj-$(CONFIG_MOUSE_AMIGA) += amimouse.o
|
||||||
obj-$(CONFIG_MOUSE_APPLETOUCH) += appletouch.o
|
obj-$(CONFIG_MOUSE_APPLETOUCH) += appletouch.o
|
||||||
obj-$(CONFIG_MOUSE_ATARI) += atarimouse.o
|
obj-$(CONFIG_MOUSE_ATARI) += atarimouse.o
|
||||||
obj-$(CONFIG_MOUSE_BCM5974) += bcm5974.o
|
obj-$(CONFIG_MOUSE_BCM5974) += bcm5974.o
|
||||||
obj-$(CONFIG_MOUSE_CYAPA) += cyapa.o
|
obj-$(CONFIG_MOUSE_CYAPA) += cyapatp.o
|
||||||
obj-$(CONFIG_MOUSE_ELAN_I2C) += elan_i2c.o
|
obj-$(CONFIG_MOUSE_ELAN_I2C) += elan_i2c.o
|
||||||
obj-$(CONFIG_MOUSE_GPIO) += gpio_mouse.o
|
obj-$(CONFIG_MOUSE_GPIO) += gpio_mouse.o
|
||||||
obj-$(CONFIG_MOUSE_INPORT) += inport.o
|
obj-$(CONFIG_MOUSE_INPORT) += inport.o
|
||||||
|
@ -24,6 +24,7 @@ obj-$(CONFIG_MOUSE_SYNAPTICS_I2C) += synaptics_i2c.o
|
||||||
obj-$(CONFIG_MOUSE_SYNAPTICS_USB) += synaptics_usb.o
|
obj-$(CONFIG_MOUSE_SYNAPTICS_USB) += synaptics_usb.o
|
||||||
obj-$(CONFIG_MOUSE_VSXXXAA) += vsxxxaa.o
|
obj-$(CONFIG_MOUSE_VSXXXAA) += vsxxxaa.o
|
||||||
|
|
||||||
|
cyapatp-objs := cyapa.o cyapa_gen3.o cyapa_gen5.o
|
||||||
psmouse-objs := psmouse-base.o synaptics.o focaltech.o
|
psmouse-objs := psmouse-base.o synaptics.o focaltech.o
|
||||||
|
|
||||||
psmouse-$(CONFIG_MOUSE_PS2_ALPS) += alps.o
|
psmouse-$(CONFIG_MOUSE_PS2_ALPS) += alps.o
|
||||||
|
|
|
@ -435,7 +435,7 @@ static void alps_report_mt_data(struct psmouse *psmouse, int n)
|
||||||
struct alps_fields *f = &priv->f;
|
struct alps_fields *f = &priv->f;
|
||||||
int i, slot[MAX_TOUCHES];
|
int i, slot[MAX_TOUCHES];
|
||||||
|
|
||||||
input_mt_assign_slots(dev, slot, f->mt, n);
|
input_mt_assign_slots(dev, slot, f->mt, n, 0);
|
||||||
for (i = 0; i < n; i++)
|
for (i = 0; i < n; i++)
|
||||||
alps_set_slot(dev, slot[i], f->mt[i].x, f->mt[i].y);
|
alps_set_slot(dev, slot[i], f->mt[i].x, f->mt[i].y);
|
||||||
|
|
||||||
|
@ -475,6 +475,13 @@ static void alps_process_trackstick_packet_v3(struct psmouse *psmouse)
|
||||||
struct input_dev *dev = priv->dev2;
|
struct input_dev *dev = priv->dev2;
|
||||||
int x, y, z, left, right, middle;
|
int x, y, z, left, right, middle;
|
||||||
|
|
||||||
|
/* It should be a DualPoint when received trackstick packet */
|
||||||
|
if (!(priv->flags & ALPS_DUALPOINT)) {
|
||||||
|
psmouse_warn(psmouse,
|
||||||
|
"Rejected trackstick packet from non DualPoint device");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
/* Sanity check packet */
|
/* Sanity check packet */
|
||||||
if (!(packet[0] & 0x40)) {
|
if (!(packet[0] & 0x40)) {
|
||||||
psmouse_dbg(psmouse, "Bad trackstick packet, discarding\n");
|
psmouse_dbg(psmouse, "Bad trackstick packet, discarding\n");
|
||||||
|
@ -699,7 +706,8 @@ static void alps_process_touchpad_packet_v3_v5(struct psmouse *psmouse)
|
||||||
|
|
||||||
alps_report_semi_mt_data(psmouse, fingers);
|
alps_report_semi_mt_data(psmouse, fingers);
|
||||||
|
|
||||||
if (!(priv->quirks & ALPS_QUIRK_TRACKSTICK_BUTTONS)) {
|
if ((priv->flags & ALPS_DUALPOINT) &&
|
||||||
|
!(priv->quirks & ALPS_QUIRK_TRACKSTICK_BUTTONS)) {
|
||||||
input_report_key(dev2, BTN_LEFT, f->ts_left);
|
input_report_key(dev2, BTN_LEFT, f->ts_left);
|
||||||
input_report_key(dev2, BTN_RIGHT, f->ts_right);
|
input_report_key(dev2, BTN_RIGHT, f->ts_right);
|
||||||
input_report_key(dev2, BTN_MIDDLE, f->ts_middle);
|
input_report_key(dev2, BTN_MIDDLE, f->ts_middle);
|
||||||
|
@ -743,8 +751,11 @@ static void alps_process_packet_v6(struct psmouse *psmouse)
|
||||||
*/
|
*/
|
||||||
if (packet[5] == 0x7F) {
|
if (packet[5] == 0x7F) {
|
||||||
/* It should be a DualPoint when received Trackpoint packet */
|
/* It should be a DualPoint when received Trackpoint packet */
|
||||||
if (!(priv->flags & ALPS_DUALPOINT))
|
if (!(priv->flags & ALPS_DUALPOINT)) {
|
||||||
|
psmouse_warn(psmouse,
|
||||||
|
"Rejected trackstick packet from non DualPoint device");
|
||||||
return;
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
/* Trackpoint packet */
|
/* Trackpoint packet */
|
||||||
x = packet[1] | ((packet[3] & 0x20) << 2);
|
x = packet[1] | ((packet[3] & 0x20) << 2);
|
||||||
|
@ -1026,6 +1037,13 @@ static void alps_process_trackstick_packet_v7(struct psmouse *psmouse)
|
||||||
struct input_dev *dev2 = priv->dev2;
|
struct input_dev *dev2 = priv->dev2;
|
||||||
int x, y, z, left, right, middle;
|
int x, y, z, left, right, middle;
|
||||||
|
|
||||||
|
/* It should be a DualPoint when received trackstick packet */
|
||||||
|
if (!(priv->flags & ALPS_DUALPOINT)) {
|
||||||
|
psmouse_warn(psmouse,
|
||||||
|
"Rejected trackstick packet from non DualPoint device");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* b7 b6 b5 b4 b3 b2 b1 b0
|
* b7 b6 b5 b4 b3 b2 b1 b0
|
||||||
* Byte0 0 1 0 0 1 0 0 0
|
* Byte0 0 1 0 0 1 0 0 0
|
||||||
|
@ -2443,14 +2461,24 @@ int alps_init(struct psmouse *psmouse)
|
||||||
dev1->keybit[BIT_WORD(BTN_MIDDLE)] |= BIT_MASK(BTN_MIDDLE);
|
dev1->keybit[BIT_WORD(BTN_MIDDLE)] |= BIT_MASK(BTN_MIDDLE);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (priv->flags & ALPS_DUALPOINT) {
|
||||||
|
/*
|
||||||
|
* format of input device name is: "protocol vendor name"
|
||||||
|
* see function psmouse_switch_protocol() in psmouse-base.c
|
||||||
|
*/
|
||||||
|
dev2->name = "AlpsPS/2 ALPS DualPoint Stick";
|
||||||
|
dev2->id.product = PSMOUSE_ALPS;
|
||||||
|
dev2->id.version = priv->proto_version;
|
||||||
|
} else {
|
||||||
|
dev2->name = "PS/2 ALPS Mouse";
|
||||||
|
dev2->id.product = PSMOUSE_PS2;
|
||||||
|
dev2->id.version = 0x0000;
|
||||||
|
}
|
||||||
|
|
||||||
snprintf(priv->phys, sizeof(priv->phys), "%s/input1", psmouse->ps2dev.serio->phys);
|
snprintf(priv->phys, sizeof(priv->phys), "%s/input1", psmouse->ps2dev.serio->phys);
|
||||||
dev2->phys = priv->phys;
|
dev2->phys = priv->phys;
|
||||||
dev2->name = (priv->flags & ALPS_DUALPOINT) ?
|
|
||||||
"DualPoint Stick" : "ALPS PS/2 Device";
|
|
||||||
dev2->id.bustype = BUS_I8042;
|
dev2->id.bustype = BUS_I8042;
|
||||||
dev2->id.vendor = 0x0002;
|
dev2->id.vendor = 0x0002;
|
||||||
dev2->id.product = PSMOUSE_ALPS;
|
|
||||||
dev2->id.version = 0x0000;
|
|
||||||
dev2->dev.parent = &psmouse->ps2dev.serio->dev;
|
dev2->dev.parent = &psmouse->ps2dev.serio->dev;
|
||||||
|
|
||||||
dev2->evbit[0] = BIT_MASK(EV_KEY) | BIT_MASK(EV_REL);
|
dev2->evbit[0] = BIT_MASK(EV_KEY) | BIT_MASK(EV_REL);
|
||||||
|
|
|
@ -564,7 +564,7 @@ static int report_tp_state(struct bcm5974 *dev, int size)
|
||||||
dev->index[n++] = &f[i];
|
dev->index[n++] = &f[i];
|
||||||
}
|
}
|
||||||
|
|
||||||
input_mt_assign_slots(input, dev->slots, dev->pos, n);
|
input_mt_assign_slots(input, dev->slots, dev->pos, n, 0);
|
||||||
|
|
||||||
for (i = 0; i < n; i++)
|
for (i = 0; i < n; i++)
|
||||||
report_finger_data(input, dev->slots[i],
|
report_finger_data(input, dev->slots[i],
|
||||||
|
|
File diff suppressed because it is too large
Load diff
301
drivers/input/mouse/cyapa.h
Normal file
301
drivers/input/mouse/cyapa.h
Normal file
|
@ -0,0 +1,301 @@
|
||||||
|
/*
|
||||||
|
* Cypress APA trackpad with I2C interface
|
||||||
|
*
|
||||||
|
* Author: Dudley Du <dudl@cypress.com>
|
||||||
|
*
|
||||||
|
* Copyright (C) 2014 Cypress Semiconductor, Inc.
|
||||||
|
*
|
||||||
|
* This file is subject to the terms and conditions of the GNU General Public
|
||||||
|
* License. See the file COPYING in the main directory of this archive for
|
||||||
|
* more details.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef _CYAPA_H
|
||||||
|
#define _CYAPA_H
|
||||||
|
|
||||||
|
#include <linux/firmware.h>
|
||||||
|
|
||||||
|
/* APA trackpad firmware generation number. */
|
||||||
|
#define CYAPA_GEN_UNKNOWN 0x00 /* unknown protocol. */
|
||||||
|
#define CYAPA_GEN3 0x03 /* support MT-protocol B with tracking ID. */
|
||||||
|
#define CYAPA_GEN5 0x05 /* support TrueTouch GEN5 trackpad device. */
|
||||||
|
|
||||||
|
#define CYAPA_NAME "Cypress APA Trackpad (cyapa)"
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Macros for SMBus communication
|
||||||
|
*/
|
||||||
|
#define SMBUS_READ 0x01
|
||||||
|
#define SMBUS_WRITE 0x00
|
||||||
|
#define SMBUS_ENCODE_IDX(cmd, idx) ((cmd) | (((idx) & 0x03) << 1))
|
||||||
|
#define SMBUS_ENCODE_RW(cmd, rw) ((cmd) | ((rw) & 0x01))
|
||||||
|
#define SMBUS_BYTE_BLOCK_CMD_MASK 0x80
|
||||||
|
#define SMBUS_GROUP_BLOCK_CMD_MASK 0x40
|
||||||
|
|
||||||
|
/* Commands for read/write registers of Cypress trackpad */
|
||||||
|
#define CYAPA_CMD_SOFT_RESET 0x00
|
||||||
|
#define CYAPA_CMD_POWER_MODE 0x01
|
||||||
|
#define CYAPA_CMD_DEV_STATUS 0x02
|
||||||
|
#define CYAPA_CMD_GROUP_DATA 0x03
|
||||||
|
#define CYAPA_CMD_GROUP_CMD 0x04
|
||||||
|
#define CYAPA_CMD_GROUP_QUERY 0x05
|
||||||
|
#define CYAPA_CMD_BL_STATUS 0x06
|
||||||
|
#define CYAPA_CMD_BL_HEAD 0x07
|
||||||
|
#define CYAPA_CMD_BL_CMD 0x08
|
||||||
|
#define CYAPA_CMD_BL_DATA 0x09
|
||||||
|
#define CYAPA_CMD_BL_ALL 0x0a
|
||||||
|
#define CYAPA_CMD_BLK_PRODUCT_ID 0x0b
|
||||||
|
#define CYAPA_CMD_BLK_HEAD 0x0c
|
||||||
|
#define CYAPA_CMD_MAX_BASELINE 0x0d
|
||||||
|
#define CYAPA_CMD_MIN_BASELINE 0x0e
|
||||||
|
|
||||||
|
#define BL_HEAD_OFFSET 0x00
|
||||||
|
#define BL_DATA_OFFSET 0x10
|
||||||
|
|
||||||
|
#define BL_STATUS_SIZE 3 /* Length of gen3 bootloader status registers */
|
||||||
|
#define CYAPA_REG_MAP_SIZE 256
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Gen3 Operational Device Status Register
|
||||||
|
*
|
||||||
|
* bit 7: Valid interrupt source
|
||||||
|
* bit 6 - 4: Reserved
|
||||||
|
* bit 3 - 2: Power status
|
||||||
|
* bit 1 - 0: Device status
|
||||||
|
*/
|
||||||
|
#define REG_OP_STATUS 0x00
|
||||||
|
#define OP_STATUS_SRC 0x80
|
||||||
|
#define OP_STATUS_POWER 0x0c
|
||||||
|
#define OP_STATUS_DEV 0x03
|
||||||
|
#define OP_STATUS_MASK (OP_STATUS_SRC | OP_STATUS_POWER | OP_STATUS_DEV)
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Operational Finger Count/Button Flags Register
|
||||||
|
*
|
||||||
|
* bit 7 - 4: Number of touched finger
|
||||||
|
* bit 3: Valid data
|
||||||
|
* bit 2: Middle Physical Button
|
||||||
|
* bit 1: Right Physical Button
|
||||||
|
* bit 0: Left physical Button
|
||||||
|
*/
|
||||||
|
#define REG_OP_DATA1 0x01
|
||||||
|
#define OP_DATA_VALID 0x08
|
||||||
|
#define OP_DATA_MIDDLE_BTN 0x04
|
||||||
|
#define OP_DATA_RIGHT_BTN 0x02
|
||||||
|
#define OP_DATA_LEFT_BTN 0x01
|
||||||
|
#define OP_DATA_BTN_MASK (OP_DATA_MIDDLE_BTN | OP_DATA_RIGHT_BTN | \
|
||||||
|
OP_DATA_LEFT_BTN)
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Write-only command file register used to issue commands and
|
||||||
|
* parameters to the bootloader.
|
||||||
|
* The default value read from it is always 0x00.
|
||||||
|
*/
|
||||||
|
#define REG_BL_FILE 0x00
|
||||||
|
#define BL_FILE 0x00
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Bootloader Status Register
|
||||||
|
*
|
||||||
|
* bit 7: Busy
|
||||||
|
* bit 6 - 5: Reserved
|
||||||
|
* bit 4: Bootloader running
|
||||||
|
* bit 3 - 2: Reserved
|
||||||
|
* bit 1: Watchdog Reset
|
||||||
|
* bit 0: Checksum valid
|
||||||
|
*/
|
||||||
|
#define REG_BL_STATUS 0x01
|
||||||
|
#define BL_STATUS_REV_6_5 0x60
|
||||||
|
#define BL_STATUS_BUSY 0x80
|
||||||
|
#define BL_STATUS_RUNNING 0x10
|
||||||
|
#define BL_STATUS_REV_3_2 0x0c
|
||||||
|
#define BL_STATUS_WATCHDOG 0x02
|
||||||
|
#define BL_STATUS_CSUM_VALID 0x01
|
||||||
|
#define BL_STATUS_REV_MASK (BL_STATUS_WATCHDOG | BL_STATUS_REV_3_2 | \
|
||||||
|
BL_STATUS_REV_6_5)
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Bootloader Error Register
|
||||||
|
*
|
||||||
|
* bit 7: Invalid
|
||||||
|
* bit 6: Invalid security key
|
||||||
|
* bit 5: Bootloading
|
||||||
|
* bit 4: Command checksum
|
||||||
|
* bit 3: Flash protection error
|
||||||
|
* bit 2: Flash checksum error
|
||||||
|
* bit 1 - 0: Reserved
|
||||||
|
*/
|
||||||
|
#define REG_BL_ERROR 0x02
|
||||||
|
#define BL_ERROR_INVALID 0x80
|
||||||
|
#define BL_ERROR_INVALID_KEY 0x40
|
||||||
|
#define BL_ERROR_BOOTLOADING 0x20
|
||||||
|
#define BL_ERROR_CMD_CSUM 0x10
|
||||||
|
#define BL_ERROR_FLASH_PROT 0x08
|
||||||
|
#define BL_ERROR_FLASH_CSUM 0x04
|
||||||
|
#define BL_ERROR_RESERVED 0x03
|
||||||
|
#define BL_ERROR_NO_ERR_IDLE 0x00
|
||||||
|
#define BL_ERROR_NO_ERR_ACTIVE (BL_ERROR_BOOTLOADING)
|
||||||
|
|
||||||
|
#define CAPABILITY_BTN_SHIFT 3
|
||||||
|
#define CAPABILITY_LEFT_BTN_MASK (0x01 << 3)
|
||||||
|
#define CAPABILITY_RIGHT_BTN_MASK (0x01 << 4)
|
||||||
|
#define CAPABILITY_MIDDLE_BTN_MASK (0x01 << 5)
|
||||||
|
#define CAPABILITY_BTN_MASK (CAPABILITY_LEFT_BTN_MASK | \
|
||||||
|
CAPABILITY_RIGHT_BTN_MASK | \
|
||||||
|
CAPABILITY_MIDDLE_BTN_MASK)
|
||||||
|
|
||||||
|
#define PWR_MODE_MASK 0xfc
|
||||||
|
#define PWR_MODE_FULL_ACTIVE (0x3f << 2)
|
||||||
|
#define PWR_MODE_IDLE (0x03 << 2) /* Default rt suspend scanrate: 30ms */
|
||||||
|
#define PWR_MODE_SLEEP (0x05 << 2) /* Default suspend scanrate: 50ms */
|
||||||
|
#define PWR_MODE_BTN_ONLY (0x01 << 2)
|
||||||
|
#define PWR_MODE_OFF (0x00 << 2)
|
||||||
|
|
||||||
|
#define PWR_STATUS_MASK 0x0c
|
||||||
|
#define PWR_STATUS_ACTIVE (0x03 << 2)
|
||||||
|
#define PWR_STATUS_IDLE (0x02 << 2)
|
||||||
|
#define PWR_STATUS_BTN_ONLY (0x01 << 2)
|
||||||
|
#define PWR_STATUS_OFF (0x00 << 2)
|
||||||
|
|
||||||
|
#define AUTOSUSPEND_DELAY 2000 /* unit : ms */
|
||||||
|
|
||||||
|
#define UNINIT_SLEEP_TIME 0xFFFF
|
||||||
|
#define UNINIT_PWR_MODE 0xFF
|
||||||
|
|
||||||
|
#define BTN_ONLY_MODE_NAME "buttononly"
|
||||||
|
#define OFF_MODE_NAME "off"
|
||||||
|
|
||||||
|
/* The touch.id is used as the MT slot id, thus max MT slot is 15 */
|
||||||
|
#define CYAPA_MAX_MT_SLOTS 15
|
||||||
|
|
||||||
|
struct cyapa;
|
||||||
|
|
||||||
|
typedef bool (*cb_sort)(struct cyapa *, u8 *, int);
|
||||||
|
|
||||||
|
struct cyapa_dev_ops {
|
||||||
|
int (*check_fw)(struct cyapa *, const struct firmware *);
|
||||||
|
int (*bl_enter)(struct cyapa *);
|
||||||
|
int (*bl_activate)(struct cyapa *);
|
||||||
|
int (*bl_initiate)(struct cyapa *, const struct firmware *);
|
||||||
|
int (*update_fw)(struct cyapa *, const struct firmware *);
|
||||||
|
int (*bl_deactivate)(struct cyapa *);
|
||||||
|
|
||||||
|
ssize_t (*show_baseline)(struct device *,
|
||||||
|
struct device_attribute *, char *);
|
||||||
|
ssize_t (*calibrate_store)(struct device *,
|
||||||
|
struct device_attribute *, const char *, size_t);
|
||||||
|
|
||||||
|
int (*initialize)(struct cyapa *cyapa);
|
||||||
|
|
||||||
|
int (*state_parse)(struct cyapa *cyapa, u8 *reg_status, int len);
|
||||||
|
int (*operational_check)(struct cyapa *cyapa);
|
||||||
|
|
||||||
|
int (*irq_handler)(struct cyapa *);
|
||||||
|
bool (*irq_cmd_handler)(struct cyapa *);
|
||||||
|
int (*sort_empty_output_data)(struct cyapa *,
|
||||||
|
u8 *, int *, cb_sort);
|
||||||
|
|
||||||
|
int (*set_power_mode)(struct cyapa *, u8, u16);
|
||||||
|
};
|
||||||
|
|
||||||
|
struct cyapa_gen5_cmd_states {
|
||||||
|
struct mutex cmd_lock;
|
||||||
|
struct completion cmd_ready;
|
||||||
|
atomic_t cmd_issued;
|
||||||
|
u8 in_progress_cmd;
|
||||||
|
bool is_irq_mode;
|
||||||
|
|
||||||
|
cb_sort resp_sort_func;
|
||||||
|
u8 *resp_data;
|
||||||
|
int *resp_len;
|
||||||
|
|
||||||
|
u8 irq_cmd_buf[CYAPA_REG_MAP_SIZE];
|
||||||
|
u8 empty_buf[CYAPA_REG_MAP_SIZE];
|
||||||
|
};
|
||||||
|
|
||||||
|
union cyapa_cmd_states {
|
||||||
|
struct cyapa_gen5_cmd_states gen5;
|
||||||
|
};
|
||||||
|
|
||||||
|
enum cyapa_state {
|
||||||
|
CYAPA_STATE_NO_DEVICE,
|
||||||
|
CYAPA_STATE_BL_BUSY,
|
||||||
|
CYAPA_STATE_BL_IDLE,
|
||||||
|
CYAPA_STATE_BL_ACTIVE,
|
||||||
|
CYAPA_STATE_OP,
|
||||||
|
CYAPA_STATE_GEN5_BL,
|
||||||
|
CYAPA_STATE_GEN5_APP,
|
||||||
|
};
|
||||||
|
|
||||||
|
/* The main device structure */
|
||||||
|
struct cyapa {
|
||||||
|
enum cyapa_state state;
|
||||||
|
u8 status[BL_STATUS_SIZE];
|
||||||
|
bool operational; /* true: ready for data reporting; false: not. */
|
||||||
|
|
||||||
|
struct i2c_client *client;
|
||||||
|
struct input_dev *input;
|
||||||
|
char phys[32]; /* Device physical location */
|
||||||
|
bool irq_wake; /* Irq wake is enabled */
|
||||||
|
bool smbus;
|
||||||
|
|
||||||
|
/* power mode settings */
|
||||||
|
u8 suspend_power_mode;
|
||||||
|
u16 suspend_sleep_time;
|
||||||
|
u8 runtime_suspend_power_mode;
|
||||||
|
u16 runtime_suspend_sleep_time;
|
||||||
|
u8 dev_pwr_mode;
|
||||||
|
u16 dev_sleep_time;
|
||||||
|
|
||||||
|
/* Read from query data region. */
|
||||||
|
char product_id[16];
|
||||||
|
u8 fw_maj_ver; /* Firmware major version. */
|
||||||
|
u8 fw_min_ver; /* Firmware minor version. */
|
||||||
|
u8 btn_capability;
|
||||||
|
u8 gen;
|
||||||
|
int max_abs_x;
|
||||||
|
int max_abs_y;
|
||||||
|
int physical_size_x;
|
||||||
|
int physical_size_y;
|
||||||
|
|
||||||
|
/* Used in ttsp and truetouch based trackpad devices. */
|
||||||
|
u8 x_origin; /* X Axis Origin: 0 = left side; 1 = rigth side. */
|
||||||
|
u8 y_origin; /* Y Axis Origin: 0 = top; 1 = bottom. */
|
||||||
|
int electrodes_x; /* Number of electrodes on the X Axis*/
|
||||||
|
int electrodes_y; /* Number of electrodes on the Y Axis*/
|
||||||
|
int electrodes_rx; /* Number of Rx electrodes */
|
||||||
|
int aligned_electrodes_rx; /* 4 aligned */
|
||||||
|
int max_z;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Used to synchronize the access or update the device state.
|
||||||
|
* And since update firmware and read firmware image process will take
|
||||||
|
* quite long time, maybe more than 10 seconds, so use mutex_lock
|
||||||
|
* to sync and wait other interface and detecting are done or ready.
|
||||||
|
*/
|
||||||
|
struct mutex state_sync_lock;
|
||||||
|
|
||||||
|
const struct cyapa_dev_ops *ops;
|
||||||
|
|
||||||
|
union cyapa_cmd_states cmd_states;
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
ssize_t cyapa_i2c_reg_read_block(struct cyapa *cyapa, u8 reg, size_t len,
|
||||||
|
u8 *values);
|
||||||
|
ssize_t cyapa_smbus_read_block(struct cyapa *cyapa, u8 cmd, size_t len,
|
||||||
|
u8 *values);
|
||||||
|
|
||||||
|
ssize_t cyapa_read_block(struct cyapa *cyapa, u8 cmd_idx, u8 *values);
|
||||||
|
|
||||||
|
int cyapa_poll_state(struct cyapa *cyapa, unsigned int timeout);
|
||||||
|
|
||||||
|
u8 cyapa_sleep_time_to_pwr_cmd(u16 sleep_time);
|
||||||
|
u16 cyapa_pwr_cmd_to_sleep_time(u8 pwr_mode);
|
||||||
|
|
||||||
|
|
||||||
|
extern const char product_id[];
|
||||||
|
extern const struct cyapa_dev_ops cyapa_gen3_ops;
|
||||||
|
extern const struct cyapa_dev_ops cyapa_gen5_ops;
|
||||||
|
|
||||||
|
#endif
|
1247
drivers/input/mouse/cyapa_gen3.c
Normal file
1247
drivers/input/mouse/cyapa_gen3.c
Normal file
File diff suppressed because it is too large
Load diff
2777
drivers/input/mouse/cyapa_gen5.c
Normal file
2777
drivers/input/mouse/cyapa_gen5.c
Normal file
File diff suppressed because it is too large
Load diff
|
@ -538,7 +538,7 @@ static void cypress_process_packet(struct psmouse *psmouse, bool zero_pkt)
|
||||||
pos[i].y = contact->y;
|
pos[i].y = contact->y;
|
||||||
}
|
}
|
||||||
|
|
||||||
input_mt_assign_slots(input, slots, pos, n);
|
input_mt_assign_slots(input, slots, pos, n, 0);
|
||||||
|
|
||||||
for (i = 0; i < n; i++) {
|
for (i = 0; i < n; i++) {
|
||||||
contact = &report_data.contacts[i];
|
contact = &report_data.contacts[i];
|
||||||
|
|
|
@ -4,7 +4,6 @@
|
||||||
* Copyright (c) 2013 ELAN Microelectronics Corp.
|
* Copyright (c) 2013 ELAN Microelectronics Corp.
|
||||||
*
|
*
|
||||||
* Author: 林政維 (Duson Lin) <dusonlin@emc.com.tw>
|
* Author: 林政維 (Duson Lin) <dusonlin@emc.com.tw>
|
||||||
* Version: 1.5.5
|
|
||||||
*
|
*
|
||||||
* Based on cyapa driver:
|
* Based on cyapa driver:
|
||||||
* copyright (c) 2011-2012 Cypress Semiconductor, Inc.
|
* copyright (c) 2011-2012 Cypress Semiconductor, Inc.
|
||||||
|
@ -33,8 +32,9 @@
|
||||||
#define ETP_FW_IAP_PAGE_ERR (1 << 5)
|
#define ETP_FW_IAP_PAGE_ERR (1 << 5)
|
||||||
#define ETP_FW_IAP_INTF_ERR (1 << 4)
|
#define ETP_FW_IAP_INTF_ERR (1 << 4)
|
||||||
#define ETP_FW_PAGE_SIZE 64
|
#define ETP_FW_PAGE_SIZE 64
|
||||||
#define ETP_FW_PAGE_COUNT 768
|
#define ETP_FW_VAILDPAGE_COUNT 768
|
||||||
#define ETP_FW_SIZE (ETP_FW_PAGE_SIZE * ETP_FW_PAGE_COUNT)
|
#define ETP_FW_SIGNATURE_SIZE 6
|
||||||
|
#define ETP_FW_SIGNATURE_ADDRESS 0xBFFA
|
||||||
|
|
||||||
struct i2c_client;
|
struct i2c_client;
|
||||||
struct completion;
|
struct completion;
|
||||||
|
|
|
@ -4,7 +4,7 @@
|
||||||
* Copyright (c) 2013 ELAN Microelectronics Corp.
|
* Copyright (c) 2013 ELAN Microelectronics Corp.
|
||||||
*
|
*
|
||||||
* Author: 林政維 (Duson Lin) <dusonlin@emc.com.tw>
|
* Author: 林政維 (Duson Lin) <dusonlin@emc.com.tw>
|
||||||
* Version: 1.5.5
|
* Version: 1.5.6
|
||||||
*
|
*
|
||||||
* Based on cyapa driver:
|
* Based on cyapa driver:
|
||||||
* copyright (c) 2011-2012 Cypress Semiconductor, Inc.
|
* copyright (c) 2011-2012 Cypress Semiconductor, Inc.
|
||||||
|
@ -40,7 +40,7 @@
|
||||||
#include "elan_i2c.h"
|
#include "elan_i2c.h"
|
||||||
|
|
||||||
#define DRIVER_NAME "elan_i2c"
|
#define DRIVER_NAME "elan_i2c"
|
||||||
#define ELAN_DRIVER_VERSION "1.5.5"
|
#define ELAN_DRIVER_VERSION "1.5.6"
|
||||||
#define ETP_PRESSURE_OFFSET 25
|
#define ETP_PRESSURE_OFFSET 25
|
||||||
#define ETP_MAX_PRESSURE 255
|
#define ETP_MAX_PRESSURE 255
|
||||||
#define ETP_FWIDTH_REDUCE 90
|
#define ETP_FWIDTH_REDUCE 90
|
||||||
|
@ -312,7 +312,7 @@ static int __elan_update_firmware(struct elan_tp_data *data,
|
||||||
iap_start_addr = get_unaligned_le16(&fw->data[ETP_IAP_START_ADDR * 2]);
|
iap_start_addr = get_unaligned_le16(&fw->data[ETP_IAP_START_ADDR * 2]);
|
||||||
|
|
||||||
boot_page_count = (iap_start_addr * 2) / ETP_FW_PAGE_SIZE;
|
boot_page_count = (iap_start_addr * 2) / ETP_FW_PAGE_SIZE;
|
||||||
for (i = boot_page_count; i < ETP_FW_PAGE_COUNT; i++) {
|
for (i = boot_page_count; i < ETP_FW_VAILDPAGE_COUNT; i++) {
|
||||||
u16 checksum = 0;
|
u16 checksum = 0;
|
||||||
const u8 *page = &fw->data[i * ETP_FW_PAGE_SIZE];
|
const u8 *page = &fw->data[i * ETP_FW_PAGE_SIZE];
|
||||||
|
|
||||||
|
@ -434,10 +434,11 @@ static ssize_t elan_sysfs_update_fw(struct device *dev,
|
||||||
struct device_attribute *attr,
|
struct device_attribute *attr,
|
||||||
const char *buf, size_t count)
|
const char *buf, size_t count)
|
||||||
{
|
{
|
||||||
struct i2c_client *client = to_i2c_client(dev);
|
struct elan_tp_data *data = dev_get_drvdata(dev);
|
||||||
struct elan_tp_data *data = i2c_get_clientdata(client);
|
|
||||||
const struct firmware *fw;
|
const struct firmware *fw;
|
||||||
int error;
|
int error;
|
||||||
|
const u8 *fw_signature;
|
||||||
|
static const u8 signature[] = {0xAA, 0x55, 0xCC, 0x33, 0xFF, 0xFF};
|
||||||
|
|
||||||
error = request_firmware(&fw, ETP_FW_NAME, dev);
|
error = request_firmware(&fw, ETP_FW_NAME, dev);
|
||||||
if (error) {
|
if (error) {
|
||||||
|
@ -446,10 +447,12 @@ static ssize_t elan_sysfs_update_fw(struct device *dev,
|
||||||
return error;
|
return error;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Firmware must be exactly PAGE_NUM * PAGE_SIZE bytes */
|
/* Firmware file must match signature data */
|
||||||
if (fw->size != ETP_FW_SIZE) {
|
fw_signature = &fw->data[ETP_FW_SIGNATURE_ADDRESS];
|
||||||
dev_err(dev, "invalid firmware size = %zu, expected %d.\n",
|
if (memcmp(fw_signature, signature, sizeof(signature)) != 0) {
|
||||||
fw->size, ETP_FW_SIZE);
|
dev_err(dev, "signature mismatch (expected %*ph, got %*ph)\n",
|
||||||
|
(int)sizeof(signature), signature,
|
||||||
|
(int)sizeof(signature), fw_signature);
|
||||||
error = -EBADF;
|
error = -EBADF;
|
||||||
goto out_release_fw;
|
goto out_release_fw;
|
||||||
}
|
}
|
||||||
|
|
|
@ -4,7 +4,6 @@
|
||||||
* Copyright (c) 2013 ELAN Microelectronics Corp.
|
* Copyright (c) 2013 ELAN Microelectronics Corp.
|
||||||
*
|
*
|
||||||
* Author: 林政維 (Duson Lin) <dusonlin@emc.com.tw>
|
* Author: 林政維 (Duson Lin) <dusonlin@emc.com.tw>
|
||||||
* Version: 1.5.5
|
|
||||||
*
|
*
|
||||||
* Based on cyapa driver:
|
* Based on cyapa driver:
|
||||||
* copyright (c) 2011-2012 Cypress Semiconductor, Inc.
|
* copyright (c) 2011-2012 Cypress Semiconductor, Inc.
|
||||||
|
|
|
@ -4,7 +4,6 @@
|
||||||
* Copyright (c) 2013 ELAN Microelectronics Corp.
|
* Copyright (c) 2013 ELAN Microelectronics Corp.
|
||||||
*
|
*
|
||||||
* Author: 林政維 (Duson Lin) <dusonlin@emc.com.tw>
|
* Author: 林政維 (Duson Lin) <dusonlin@emc.com.tw>
|
||||||
* Version: 1.5.5
|
|
||||||
*
|
*
|
||||||
* Based on cyapa driver:
|
* Based on cyapa driver:
|
||||||
* copyright (c) 2011-2012 Cypress Semiconductor, Inc.
|
* copyright (c) 2011-2012 Cypress Semiconductor, Inc.
|
||||||
|
@ -71,7 +70,7 @@ static int elan_smbus_initialize(struct i2c_client *client)
|
||||||
|
|
||||||
/* compare hello packet */
|
/* compare hello packet */
|
||||||
if (memcmp(values, check, ETP_SMBUS_HELLOPACKET_LEN)) {
|
if (memcmp(values, check, ETP_SMBUS_HELLOPACKET_LEN)) {
|
||||||
dev_err(&client->dev, "hello packet fail [%*px]\n",
|
dev_err(&client->dev, "hello packet fail [%*ph]\n",
|
||||||
ETP_SMBUS_HELLOPACKET_LEN, values);
|
ETP_SMBUS_HELLOPACKET_LEN, values);
|
||||||
return -ENXIO;
|
return -ENXIO;
|
||||||
}
|
}
|
||||||
|
|
|
@ -2,6 +2,7 @@
|
||||||
* Focaltech TouchPad PS/2 mouse driver
|
* Focaltech TouchPad PS/2 mouse driver
|
||||||
*
|
*
|
||||||
* Copyright (c) 2014 Red Hat Inc.
|
* Copyright (c) 2014 Red Hat Inc.
|
||||||
|
* Copyright (c) 2014 Mathias Gottschlag <mgottschlag@gmail.com>
|
||||||
*
|
*
|
||||||
* This program is free software; you can redistribute it and/or modify
|
* 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
|
* it under the terms of the GNU General Public License as published by
|
||||||
|
@ -13,15 +14,14 @@
|
||||||
* Hans de Goede <hdegoede@redhat.com>
|
* Hans de Goede <hdegoede@redhat.com>
|
||||||
*/
|
*/
|
||||||
|
|
||||||
/*
|
|
||||||
* The Focaltech PS/2 touchpad protocol is unknown. This drivers deals with
|
|
||||||
* detection only, to avoid further detection attempts confusing the touchpad
|
|
||||||
* this way it at least works in PS/2 mouse compatibility mode.
|
|
||||||
*/
|
|
||||||
|
|
||||||
#include <linux/device.h>
|
#include <linux/device.h>
|
||||||
#include <linux/libps2.h>
|
#include <linux/libps2.h>
|
||||||
|
#include <linux/input/mt.h>
|
||||||
|
#include <linux/serio.h>
|
||||||
|
#include <linux/slab.h>
|
||||||
#include "psmouse.h"
|
#include "psmouse.h"
|
||||||
|
#include "focaltech.h"
|
||||||
|
|
||||||
static const char * const focaltech_pnp_ids[] = {
|
static const char * const focaltech_pnp_ids[] = {
|
||||||
"FLT0101",
|
"FLT0101",
|
||||||
|
@ -30,6 +30,12 @@ static const char * const focaltech_pnp_ids[] = {
|
||||||
NULL
|
NULL
|
||||||
};
|
};
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Even if the kernel is built without support for Focaltech PS/2 touchpads (or
|
||||||
|
* when the real driver fails to recognize the device), we still have to detect
|
||||||
|
* them in order to avoid further detection attempts confusing the touchpad.
|
||||||
|
* This way it at least works in PS/2 mouse compatibility mode.
|
||||||
|
*/
|
||||||
int focaltech_detect(struct psmouse *psmouse, bool set_properties)
|
int focaltech_detect(struct psmouse *psmouse, bool set_properties)
|
||||||
{
|
{
|
||||||
if (!psmouse_matches_pnp_id(psmouse, focaltech_pnp_ids))
|
if (!psmouse_matches_pnp_id(psmouse, focaltech_pnp_ids))
|
||||||
|
@ -37,16 +43,404 @@ int focaltech_detect(struct psmouse *psmouse, bool set_properties)
|
||||||
|
|
||||||
if (set_properties) {
|
if (set_properties) {
|
||||||
psmouse->vendor = "FocalTech";
|
psmouse->vendor = "FocalTech";
|
||||||
psmouse->name = "FocalTech Touchpad in mouse emulation mode";
|
psmouse->name = "FocalTech Touchpad";
|
||||||
}
|
}
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
int focaltech_init(struct psmouse *psmouse)
|
static void focaltech_reset(struct psmouse *psmouse)
|
||||||
{
|
{
|
||||||
ps2_command(&psmouse->ps2dev, NULL, PSMOUSE_CMD_RESET_DIS);
|
ps2_command(&psmouse->ps2dev, NULL, PSMOUSE_CMD_RESET_DIS);
|
||||||
psmouse_reset(psmouse);
|
psmouse_reset(psmouse);
|
||||||
|
}
|
||||||
|
|
||||||
|
#ifdef CONFIG_MOUSE_PS2_FOCALTECH
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Packet types - the numbers are not consecutive, so we might be missing
|
||||||
|
* something here.
|
||||||
|
*/
|
||||||
|
#define FOC_TOUCH 0x3 /* bitmap of active fingers */
|
||||||
|
#define FOC_ABS 0x6 /* absolute position of one finger */
|
||||||
|
#define FOC_REL 0x9 /* relative position of 1-2 fingers */
|
||||||
|
|
||||||
|
#define FOC_MAX_FINGERS 5
|
||||||
|
|
||||||
|
#define FOC_MAX_X 2431
|
||||||
|
#define FOC_MAX_Y 1663
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Current state of a single finger on the touchpad.
|
||||||
|
*/
|
||||||
|
struct focaltech_finger_state {
|
||||||
|
/* The touchpad has generated a touch event for the finger */
|
||||||
|
bool active;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* The touchpad has sent position data for the finger. The
|
||||||
|
* flag is 0 when the finger is not active, and there is a
|
||||||
|
* time between the first touch event for the finger and the
|
||||||
|
* following absolute position packet for the finger where the
|
||||||
|
* touchpad has declared the finger to be valid, but we do not
|
||||||
|
* have any valid position yet.
|
||||||
|
*/
|
||||||
|
bool valid;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Absolute position (from the bottom left corner) of the
|
||||||
|
* finger.
|
||||||
|
*/
|
||||||
|
unsigned int x;
|
||||||
|
unsigned int y;
|
||||||
|
};
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Description of the current state of the touchpad hardware.
|
||||||
|
*/
|
||||||
|
struct focaltech_hw_state {
|
||||||
|
/*
|
||||||
|
* The touchpad tracks the positions of the fingers for us,
|
||||||
|
* the array indices correspond to the finger indices returned
|
||||||
|
* in the report packages.
|
||||||
|
*/
|
||||||
|
struct focaltech_finger_state fingers[FOC_MAX_FINGERS];
|
||||||
|
|
||||||
|
/* True if the clickpad has been pressed. */
|
||||||
|
bool pressed;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct focaltech_data {
|
||||||
|
unsigned int x_max, y_max;
|
||||||
|
struct focaltech_hw_state state;
|
||||||
|
};
|
||||||
|
|
||||||
|
static void focaltech_report_state(struct psmouse *psmouse)
|
||||||
|
{
|
||||||
|
struct focaltech_data *priv = psmouse->private;
|
||||||
|
struct focaltech_hw_state *state = &priv->state;
|
||||||
|
struct input_dev *dev = psmouse->dev;
|
||||||
|
int i;
|
||||||
|
|
||||||
|
for (i = 0; i < FOC_MAX_FINGERS; i++) {
|
||||||
|
struct focaltech_finger_state *finger = &state->fingers[i];
|
||||||
|
bool active = finger->active && finger->valid;
|
||||||
|
|
||||||
|
input_mt_slot(dev, i);
|
||||||
|
input_mt_report_slot_state(dev, MT_TOOL_FINGER, active);
|
||||||
|
if (active) {
|
||||||
|
input_report_abs(dev, ABS_MT_POSITION_X, finger->x);
|
||||||
|
input_report_abs(dev, ABS_MT_POSITION_Y,
|
||||||
|
FOC_MAX_Y - finger->y);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
input_mt_report_pointer_emulation(dev, true);
|
||||||
|
|
||||||
|
input_report_key(psmouse->dev, BTN_LEFT, state->pressed);
|
||||||
|
input_sync(psmouse->dev);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void focaltech_process_touch_packet(struct psmouse *psmouse,
|
||||||
|
unsigned char *packet)
|
||||||
|
{
|
||||||
|
struct focaltech_data *priv = psmouse->private;
|
||||||
|
struct focaltech_hw_state *state = &priv->state;
|
||||||
|
unsigned char fingers = packet[1];
|
||||||
|
int i;
|
||||||
|
|
||||||
|
state->pressed = (packet[0] >> 4) & 1;
|
||||||
|
|
||||||
|
/* the second byte contains a bitmap of all fingers touching the pad */
|
||||||
|
for (i = 0; i < FOC_MAX_FINGERS; i++) {
|
||||||
|
state->fingers[i].active = fingers & 0x1;
|
||||||
|
if (!state->fingers[i].active) {
|
||||||
|
/*
|
||||||
|
* Even when the finger becomes active again, we still
|
||||||
|
* will have to wait for the first valid position.
|
||||||
|
*/
|
||||||
|
state->fingers[i].valid = false;
|
||||||
|
}
|
||||||
|
fingers >>= 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static void focaltech_process_abs_packet(struct psmouse *psmouse,
|
||||||
|
unsigned char *packet)
|
||||||
|
{
|
||||||
|
struct focaltech_data *priv = psmouse->private;
|
||||||
|
struct focaltech_hw_state *state = &priv->state;
|
||||||
|
unsigned int finger;
|
||||||
|
|
||||||
|
finger = (packet[1] >> 4) - 1;
|
||||||
|
if (finger >= FOC_MAX_FINGERS) {
|
||||||
|
psmouse_err(psmouse, "Invalid finger in abs packet: %d\n",
|
||||||
|
finger);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
state->pressed = (packet[0] >> 4) & 1;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* packet[5] contains some kind of tool size in the most
|
||||||
|
* significant nibble. 0xff is a special value (latching) that
|
||||||
|
* signals a large contact area.
|
||||||
|
*/
|
||||||
|
if (packet[5] == 0xff) {
|
||||||
|
state->fingers[finger].valid = false;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
state->fingers[finger].x = ((packet[1] & 0xf) << 8) | packet[2];
|
||||||
|
state->fingers[finger].y = (packet[3] << 8) | packet[4];
|
||||||
|
state->fingers[finger].valid = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void focaltech_process_rel_packet(struct psmouse *psmouse,
|
||||||
|
unsigned char *packet)
|
||||||
|
{
|
||||||
|
struct focaltech_data *priv = psmouse->private;
|
||||||
|
struct focaltech_hw_state *state = &priv->state;
|
||||||
|
int finger1, finger2;
|
||||||
|
|
||||||
|
state->pressed = packet[0] >> 7;
|
||||||
|
finger1 = ((packet[0] >> 4) & 0x7) - 1;
|
||||||
|
if (finger1 < FOC_MAX_FINGERS) {
|
||||||
|
state->fingers[finger1].x += (char)packet[1];
|
||||||
|
state->fingers[finger1].y += (char)packet[2];
|
||||||
|
} else {
|
||||||
|
psmouse_err(psmouse, "First finger in rel packet invalid: %d\n",
|
||||||
|
finger1);
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* If there is an odd number of fingers, the last relative
|
||||||
|
* packet only contains one finger. In this case, the second
|
||||||
|
* finger index in the packet is 0 (we subtract 1 in the lines
|
||||||
|
* above to create array indices, so the finger will overflow
|
||||||
|
* and be above FOC_MAX_FINGERS).
|
||||||
|
*/
|
||||||
|
finger2 = ((packet[3] >> 4) & 0x7) - 1;
|
||||||
|
if (finger2 < FOC_MAX_FINGERS) {
|
||||||
|
state->fingers[finger2].x += (char)packet[4];
|
||||||
|
state->fingers[finger2].y += (char)packet[5];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static void focaltech_process_packet(struct psmouse *psmouse)
|
||||||
|
{
|
||||||
|
unsigned char *packet = psmouse->packet;
|
||||||
|
|
||||||
|
switch (packet[0] & 0xf) {
|
||||||
|
case FOC_TOUCH:
|
||||||
|
focaltech_process_touch_packet(psmouse, packet);
|
||||||
|
break;
|
||||||
|
|
||||||
|
case FOC_ABS:
|
||||||
|
focaltech_process_abs_packet(psmouse, packet);
|
||||||
|
break;
|
||||||
|
|
||||||
|
case FOC_REL:
|
||||||
|
focaltech_process_rel_packet(psmouse, packet);
|
||||||
|
break;
|
||||||
|
|
||||||
|
default:
|
||||||
|
psmouse_err(psmouse, "Unknown packet type: %02x\n", packet[0]);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
focaltech_report_state(psmouse);
|
||||||
|
}
|
||||||
|
|
||||||
|
static psmouse_ret_t focaltech_process_byte(struct psmouse *psmouse)
|
||||||
|
{
|
||||||
|
if (psmouse->pktcnt >= 6) { /* Full packet received */
|
||||||
|
focaltech_process_packet(psmouse);
|
||||||
|
return PSMOUSE_FULL_PACKET;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* We might want to do some validation of the data here, but
|
||||||
|
* we do not know the protocol well enough
|
||||||
|
*/
|
||||||
|
return PSMOUSE_GOOD_DATA;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int focaltech_switch_protocol(struct psmouse *psmouse)
|
||||||
|
{
|
||||||
|
struct ps2dev *ps2dev = &psmouse->ps2dev;
|
||||||
|
unsigned char param[3];
|
||||||
|
|
||||||
|
param[0] = 0;
|
||||||
|
if (ps2_command(ps2dev, param, 0x10f8))
|
||||||
|
return -EIO;
|
||||||
|
|
||||||
|
if (ps2_command(ps2dev, param, 0x10f8))
|
||||||
|
return -EIO;
|
||||||
|
|
||||||
|
if (ps2_command(ps2dev, param, 0x10f8))
|
||||||
|
return -EIO;
|
||||||
|
|
||||||
|
param[0] = 1;
|
||||||
|
if (ps2_command(ps2dev, param, 0x10f8))
|
||||||
|
return -EIO;
|
||||||
|
|
||||||
|
if (ps2_command(ps2dev, param, PSMOUSE_CMD_SETSCALE11))
|
||||||
|
return -EIO;
|
||||||
|
|
||||||
|
if (ps2_command(ps2dev, param, PSMOUSE_CMD_ENABLE))
|
||||||
|
return -EIO;
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static void focaltech_disconnect(struct psmouse *psmouse)
|
||||||
|
{
|
||||||
|
focaltech_reset(psmouse);
|
||||||
|
kfree(psmouse->private);
|
||||||
|
psmouse->private = NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int focaltech_reconnect(struct psmouse *psmouse)
|
||||||
|
{
|
||||||
|
int error;
|
||||||
|
|
||||||
|
focaltech_reset(psmouse);
|
||||||
|
|
||||||
|
error = focaltech_switch_protocol(psmouse);
|
||||||
|
if (error) {
|
||||||
|
psmouse_err(psmouse, "Unable to initialize the device\n");
|
||||||
|
return error;
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void focaltech_set_input_params(struct psmouse *psmouse)
|
||||||
|
{
|
||||||
|
struct input_dev *dev = psmouse->dev;
|
||||||
|
struct focaltech_data *priv = psmouse->private;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Undo part of setup done for us by psmouse core since touchpad
|
||||||
|
* is not a relative device.
|
||||||
|
*/
|
||||||
|
__clear_bit(EV_REL, dev->evbit);
|
||||||
|
__clear_bit(REL_X, dev->relbit);
|
||||||
|
__clear_bit(REL_Y, dev->relbit);
|
||||||
|
__clear_bit(BTN_RIGHT, dev->keybit);
|
||||||
|
__clear_bit(BTN_MIDDLE, dev->keybit);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Now set up our capabilities.
|
||||||
|
*/
|
||||||
|
__set_bit(EV_ABS, dev->evbit);
|
||||||
|
input_set_abs_params(dev, ABS_MT_POSITION_X, 0, priv->x_max, 0, 0);
|
||||||
|
input_set_abs_params(dev, ABS_MT_POSITION_Y, 0, priv->y_max, 0, 0);
|
||||||
|
input_mt_init_slots(dev, 5, INPUT_MT_POINTER);
|
||||||
|
__set_bit(INPUT_PROP_BUTTONPAD, dev->propbit);
|
||||||
|
}
|
||||||
|
|
||||||
|
static int focaltech_read_register(struct ps2dev *ps2dev, int reg,
|
||||||
|
unsigned char *param)
|
||||||
|
{
|
||||||
|
if (ps2_command(ps2dev, param, PSMOUSE_CMD_SETSCALE11))
|
||||||
|
return -EIO;
|
||||||
|
|
||||||
|
param[0] = 0;
|
||||||
|
if (ps2_command(ps2dev, param, PSMOUSE_CMD_SETRES))
|
||||||
|
return -EIO;
|
||||||
|
|
||||||
|
if (ps2_command(ps2dev, param, PSMOUSE_CMD_SETRES))
|
||||||
|
return -EIO;
|
||||||
|
|
||||||
|
if (ps2_command(ps2dev, param, PSMOUSE_CMD_SETRES))
|
||||||
|
return -EIO;
|
||||||
|
|
||||||
|
param[0] = reg;
|
||||||
|
if (ps2_command(ps2dev, param, PSMOUSE_CMD_SETRES))
|
||||||
|
return -EIO;
|
||||||
|
|
||||||
|
if (ps2_command(ps2dev, param, PSMOUSE_CMD_GETINFO))
|
||||||
|
return -EIO;
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int focaltech_read_size(struct psmouse *psmouse)
|
||||||
|
{
|
||||||
|
struct ps2dev *ps2dev = &psmouse->ps2dev;
|
||||||
|
struct focaltech_data *priv = psmouse->private;
|
||||||
|
char param[3];
|
||||||
|
|
||||||
|
if (focaltech_read_register(ps2dev, 2, param))
|
||||||
|
return -EIO;
|
||||||
|
|
||||||
|
/* not sure whether this is 100% correct */
|
||||||
|
priv->x_max = (unsigned char)param[1] * 128;
|
||||||
|
priv->y_max = (unsigned char)param[2] * 128;
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
int focaltech_init(struct psmouse *psmouse)
|
||||||
|
{
|
||||||
|
struct focaltech_data *priv;
|
||||||
|
int error;
|
||||||
|
|
||||||
|
psmouse->private = priv = kzalloc(sizeof(struct focaltech_data),
|
||||||
|
GFP_KERNEL);
|
||||||
|
if (!priv)
|
||||||
|
return -ENOMEM;
|
||||||
|
|
||||||
|
focaltech_reset(psmouse);
|
||||||
|
|
||||||
|
error = focaltech_read_size(psmouse);
|
||||||
|
if (error) {
|
||||||
|
psmouse_err(psmouse,
|
||||||
|
"Unable to read the size of the touchpad\n");
|
||||||
|
goto fail;
|
||||||
|
}
|
||||||
|
|
||||||
|
error = focaltech_switch_protocol(psmouse);
|
||||||
|
if (error) {
|
||||||
|
psmouse_err(psmouse, "Unable to initialize the device\n");
|
||||||
|
goto fail;
|
||||||
|
}
|
||||||
|
|
||||||
|
focaltech_set_input_params(psmouse);
|
||||||
|
|
||||||
|
psmouse->protocol_handler = focaltech_process_byte;
|
||||||
|
psmouse->pktsize = 6;
|
||||||
|
psmouse->disconnect = focaltech_disconnect;
|
||||||
|
psmouse->reconnect = focaltech_reconnect;
|
||||||
|
psmouse->cleanup = focaltech_reset;
|
||||||
|
/* resync is not supported yet */
|
||||||
|
psmouse->resync_time = 0;
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
fail:
|
||||||
|
focaltech_reset(psmouse);
|
||||||
|
kfree(priv);
|
||||||
|
return error;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool focaltech_supported(void)
|
||||||
|
{
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
#else /* CONFIG_MOUSE_PS2_FOCALTECH */
|
||||||
|
|
||||||
|
int focaltech_init(struct psmouse *psmouse)
|
||||||
|
{
|
||||||
|
focaltech_reset(psmouse);
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool focaltech_supported(void)
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif /* CONFIG_MOUSE_PS2_FOCALTECH */
|
||||||
|
|
|
@ -2,6 +2,7 @@
|
||||||
* Focaltech TouchPad PS/2 mouse driver
|
* Focaltech TouchPad PS/2 mouse driver
|
||||||
*
|
*
|
||||||
* Copyright (c) 2014 Red Hat Inc.
|
* Copyright (c) 2014 Red Hat Inc.
|
||||||
|
* Copyright (c) 2014 Mathias Gottschlag <mgottschlag@gmail.com>
|
||||||
*
|
*
|
||||||
* This program is free software; you can redistribute it and/or modify
|
* 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
|
* it under the terms of the GNU General Public License as published by
|
||||||
|
@ -18,5 +19,6 @@
|
||||||
|
|
||||||
int focaltech_detect(struct psmouse *psmouse, bool set_properties);
|
int focaltech_detect(struct psmouse *psmouse, bool set_properties);
|
||||||
int focaltech_init(struct psmouse *psmouse);
|
int focaltech_init(struct psmouse *psmouse);
|
||||||
|
bool focaltech_supported(void);
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|
|
@ -725,16 +725,19 @@ static int psmouse_extensions(struct psmouse *psmouse,
|
||||||
|
|
||||||
/* Always check for focaltech, this is safe as it uses pnp-id matching */
|
/* Always check for focaltech, this is safe as it uses pnp-id matching */
|
||||||
if (psmouse_do_detect(focaltech_detect, psmouse, set_properties) == 0) {
|
if (psmouse_do_detect(focaltech_detect, psmouse, set_properties) == 0) {
|
||||||
if (!set_properties || focaltech_init(psmouse) == 0) {
|
if (max_proto > PSMOUSE_IMEX) {
|
||||||
/*
|
if (!set_properties || focaltech_init(psmouse) == 0) {
|
||||||
* Not supported yet, use bare protocol.
|
if (focaltech_supported())
|
||||||
* Note that we need to also restrict
|
return PSMOUSE_FOCALTECH;
|
||||||
* psmouse_max_proto so that psmouse_initialize()
|
/*
|
||||||
* does not try to reset rate and resolution,
|
* Note that we need to also restrict
|
||||||
* because even that upsets the device.
|
* psmouse_max_proto so that psmouse_initialize()
|
||||||
*/
|
* does not try to reset rate and resolution,
|
||||||
psmouse_max_proto = PSMOUSE_PS2;
|
* because even that upsets the device.
|
||||||
return PSMOUSE_PS2;
|
*/
|
||||||
|
psmouse_max_proto = PSMOUSE_PS2;
|
||||||
|
return PSMOUSE_PS2;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1063,6 +1066,15 @@ static const struct psmouse_protocol psmouse_protocols[] = {
|
||||||
.alias = "cortps",
|
.alias = "cortps",
|
||||||
.detect = cortron_detect,
|
.detect = cortron_detect,
|
||||||
},
|
},
|
||||||
|
#ifdef CONFIG_MOUSE_PS2_FOCALTECH
|
||||||
|
{
|
||||||
|
.type = PSMOUSE_FOCALTECH,
|
||||||
|
.name = "FocalTechPS/2",
|
||||||
|
.alias = "focaltech",
|
||||||
|
.detect = focaltech_detect,
|
||||||
|
.init = focaltech_init,
|
||||||
|
},
|
||||||
|
#endif
|
||||||
{
|
{
|
||||||
.type = PSMOUSE_AUTO,
|
.type = PSMOUSE_AUTO,
|
||||||
.name = "auto",
|
.name = "auto",
|
||||||
|
|
|
@ -96,6 +96,7 @@ enum psmouse_type {
|
||||||
PSMOUSE_FSP,
|
PSMOUSE_FSP,
|
||||||
PSMOUSE_SYNAPTICS_RELATIVE,
|
PSMOUSE_SYNAPTICS_RELATIVE,
|
||||||
PSMOUSE_CYPRESS,
|
PSMOUSE_CYPRESS,
|
||||||
|
PSMOUSE_FOCALTECH,
|
||||||
PSMOUSE_AUTO /* This one should always be last */
|
PSMOUSE_AUTO /* This one should always be last */
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
@ -67,6 +67,9 @@
|
||||||
#define X_MAX_POSITIVE 8176
|
#define X_MAX_POSITIVE 8176
|
||||||
#define Y_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
|
* Stuff we need even when we do not want native Synaptics support
|
||||||
****************************************************************************/
|
****************************************************************************/
|
||||||
|
@ -575,14 +578,6 @@ static void synaptics_pt_create(struct psmouse *psmouse)
|
||||||
* Functions to interpret the absolute mode packets
|
* Functions to interpret the absolute mode packets
|
||||||
****************************************************************************/
|
****************************************************************************/
|
||||||
|
|
||||||
static void synaptics_mt_state_set(struct synaptics_mt_state *state, int count,
|
|
||||||
int sgm, int agm)
|
|
||||||
{
|
|
||||||
state->count = count;
|
|
||||||
state->sgm = sgm;
|
|
||||||
state->agm = agm;
|
|
||||||
}
|
|
||||||
|
|
||||||
static void synaptics_parse_agm(const unsigned char buf[],
|
static void synaptics_parse_agm(const unsigned char buf[],
|
||||||
struct synaptics_data *priv,
|
struct synaptics_data *priv,
|
||||||
struct synaptics_hw_state *hw)
|
struct synaptics_hw_state *hw)
|
||||||
|
@ -601,16 +596,13 @@ static void synaptics_parse_agm(const unsigned char buf[],
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case 2:
|
case 2:
|
||||||
/* AGM-CONTACT packet: (count, sgm, agm) */
|
/* AGM-CONTACT packet: we are only interested in the count */
|
||||||
synaptics_mt_state_set(&agm->mt_state, buf[1], buf[2], buf[4]);
|
priv->agm_count = buf[1];
|
||||||
break;
|
break;
|
||||||
|
|
||||||
default:
|
default:
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Record that at least one AGM has been received since last SGM */
|
|
||||||
priv->agm_pending = true;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static bool is_forcepad;
|
static bool is_forcepad;
|
||||||
|
@ -804,397 +796,13 @@ static void synaptics_report_buttons(struct psmouse *psmouse,
|
||||||
input_report_key(dev, BTN_0 + i, hw->ext_buttons & (1 << i));
|
input_report_key(dev, BTN_0 + i, hw->ext_buttons & (1 << i));
|
||||||
}
|
}
|
||||||
|
|
||||||
static void synaptics_report_slot(struct input_dev *dev, int slot,
|
|
||||||
const struct synaptics_hw_state *hw)
|
|
||||||
{
|
|
||||||
input_mt_slot(dev, slot);
|
|
||||||
input_mt_report_slot_state(dev, MT_TOOL_FINGER, (hw != NULL));
|
|
||||||
if (!hw)
|
|
||||||
return;
|
|
||||||
|
|
||||||
input_report_abs(dev, ABS_MT_POSITION_X, hw->x);
|
|
||||||
input_report_abs(dev, ABS_MT_POSITION_Y, synaptics_invert_y(hw->y));
|
|
||||||
input_report_abs(dev, ABS_MT_PRESSURE, hw->z);
|
|
||||||
}
|
|
||||||
|
|
||||||
static void synaptics_report_mt_data(struct psmouse *psmouse,
|
static void synaptics_report_mt_data(struct psmouse *psmouse,
|
||||||
struct synaptics_mt_state *mt_state,
|
const struct synaptics_hw_state *sgm,
|
||||||
const struct synaptics_hw_state *sgm)
|
int num_fingers)
|
||||||
{
|
{
|
||||||
struct input_dev *dev = psmouse->dev;
|
struct input_dev *dev = psmouse->dev;
|
||||||
struct synaptics_data *priv = psmouse->private;
|
struct synaptics_data *priv = psmouse->private;
|
||||||
struct synaptics_hw_state *agm = &priv->agm;
|
const struct synaptics_hw_state *hw[2] = { sgm, &priv->agm };
|
||||||
struct synaptics_mt_state *old = &priv->mt_state;
|
|
||||||
|
|
||||||
switch (mt_state->count) {
|
|
||||||
case 0:
|
|
||||||
synaptics_report_slot(dev, 0, NULL);
|
|
||||||
synaptics_report_slot(dev, 1, NULL);
|
|
||||||
break;
|
|
||||||
case 1:
|
|
||||||
if (mt_state->sgm == -1) {
|
|
||||||
synaptics_report_slot(dev, 0, NULL);
|
|
||||||
synaptics_report_slot(dev, 1, NULL);
|
|
||||||
} else if (mt_state->sgm == 0) {
|
|
||||||
synaptics_report_slot(dev, 0, sgm);
|
|
||||||
synaptics_report_slot(dev, 1, NULL);
|
|
||||||
} else {
|
|
||||||
synaptics_report_slot(dev, 0, NULL);
|
|
||||||
synaptics_report_slot(dev, 1, sgm);
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
/*
|
|
||||||
* If the finger slot contained in SGM is valid, and either
|
|
||||||
* hasn't changed, or is new, or the old SGM has now moved to
|
|
||||||
* AGM, then report SGM in MTB slot 0.
|
|
||||||
* Otherwise, empty MTB slot 0.
|
|
||||||
*/
|
|
||||||
if (mt_state->sgm != -1 &&
|
|
||||||
(mt_state->sgm == old->sgm ||
|
|
||||||
old->sgm == -1 || mt_state->agm == old->sgm))
|
|
||||||
synaptics_report_slot(dev, 0, sgm);
|
|
||||||
else
|
|
||||||
synaptics_report_slot(dev, 0, NULL);
|
|
||||||
|
|
||||||
/*
|
|
||||||
* If the finger slot contained in AGM is valid, and either
|
|
||||||
* hasn't changed, or is new, then report AGM in MTB slot 1.
|
|
||||||
* Otherwise, empty MTB slot 1.
|
|
||||||
*
|
|
||||||
* However, in the case where the AGM is new, make sure that
|
|
||||||
* that it is either the same as the old SGM, or there was no
|
|
||||||
* SGM.
|
|
||||||
*
|
|
||||||
* Otherwise, if the SGM was just 1, and the new AGM is 2, then
|
|
||||||
* the new AGM will keep the old SGM's tracking ID, which can
|
|
||||||
* cause apparent drumroll. This happens if in the following
|
|
||||||
* valid finger sequence:
|
|
||||||
*
|
|
||||||
* Action SGM AGM (MTB slot:Contact)
|
|
||||||
* 1. Touch contact 0 (0:0)
|
|
||||||
* 2. Touch contact 1 (0:0, 1:1)
|
|
||||||
* 3. Lift contact 0 (1:1)
|
|
||||||
* 4. Touch contacts 2,3 (0:2, 1:3)
|
|
||||||
*
|
|
||||||
* In step 4, contact 3, in AGM must not be given the same
|
|
||||||
* tracking ID as contact 1 had in step 3. To avoid this,
|
|
||||||
* the first agm with contact 3 is dropped and slot 1 is
|
|
||||||
* invalidated (tracking ID = -1).
|
|
||||||
*/
|
|
||||||
if (mt_state->agm != -1 &&
|
|
||||||
(mt_state->agm == old->agm ||
|
|
||||||
(old->agm == -1 &&
|
|
||||||
(old->sgm == -1 || mt_state->agm == old->sgm))))
|
|
||||||
synaptics_report_slot(dev, 1, agm);
|
|
||||||
else
|
|
||||||
synaptics_report_slot(dev, 1, NULL);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Don't use active slot count to generate BTN_TOOL events. */
|
|
||||||
input_mt_report_pointer_emulation(dev, false);
|
|
||||||
|
|
||||||
/* Send the number of fingers reported by touchpad itself. */
|
|
||||||
input_mt_report_finger_count(dev, mt_state->count);
|
|
||||||
|
|
||||||
synaptics_report_buttons(psmouse, sgm);
|
|
||||||
|
|
||||||
input_sync(dev);
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Handle case where mt_state->count = 0 */
|
|
||||||
static void synaptics_image_sensor_0f(struct synaptics_data *priv,
|
|
||||||
struct synaptics_mt_state *mt_state)
|
|
||||||
{
|
|
||||||
synaptics_mt_state_set(mt_state, 0, -1, -1);
|
|
||||||
priv->mt_state_lost = false;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Handle case where mt_state->count = 1 */
|
|
||||||
static void synaptics_image_sensor_1f(struct synaptics_data *priv,
|
|
||||||
struct synaptics_mt_state *mt_state)
|
|
||||||
{
|
|
||||||
struct synaptics_hw_state *agm = &priv->agm;
|
|
||||||
struct synaptics_mt_state *old = &priv->mt_state;
|
|
||||||
|
|
||||||
/*
|
|
||||||
* If the last AGM was (0,0,0), and there is only one finger left,
|
|
||||||
* then we absolutely know that SGM contains slot 0, and all other
|
|
||||||
* fingers have been removed.
|
|
||||||
*/
|
|
||||||
if (priv->agm_pending && agm->z == 0) {
|
|
||||||
synaptics_mt_state_set(mt_state, 1, 0, -1);
|
|
||||||
priv->mt_state_lost = false;
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
switch (old->count) {
|
|
||||||
case 0:
|
|
||||||
synaptics_mt_state_set(mt_state, 1, 0, -1);
|
|
||||||
break;
|
|
||||||
case 1:
|
|
||||||
/*
|
|
||||||
* If mt_state_lost, then the previous transition was 3->1,
|
|
||||||
* and SGM now contains either slot 0 or 1, but we don't know
|
|
||||||
* which. So, we just assume that the SGM now contains slot 1.
|
|
||||||
*
|
|
||||||
* If pending AGM and either:
|
|
||||||
* (a) the previous SGM slot contains slot 0, or
|
|
||||||
* (b) there was no SGM slot
|
|
||||||
* then, the SGM now contains slot 1
|
|
||||||
*
|
|
||||||
* Case (a) happens with very rapid "drum roll" gestures, where
|
|
||||||
* slot 0 finger is lifted and a new slot 1 finger touches
|
|
||||||
* within one reporting interval.
|
|
||||||
*
|
|
||||||
* Case (b) happens if initially two or more fingers tap
|
|
||||||
* briefly, and all but one lift before the end of the first
|
|
||||||
* reporting interval.
|
|
||||||
*
|
|
||||||
* (In both these cases, slot 0 will becomes empty, so SGM
|
|
||||||
* contains slot 1 with the new finger)
|
|
||||||
*
|
|
||||||
* Else, if there was no previous SGM, it now contains slot 0.
|
|
||||||
*
|
|
||||||
* Otherwise, SGM still contains the same slot.
|
|
||||||
*/
|
|
||||||
if (priv->mt_state_lost ||
|
|
||||||
(priv->agm_pending && old->sgm <= 0))
|
|
||||||
synaptics_mt_state_set(mt_state, 1, 1, -1);
|
|
||||||
else if (old->sgm == -1)
|
|
||||||
synaptics_mt_state_set(mt_state, 1, 0, -1);
|
|
||||||
break;
|
|
||||||
case 2:
|
|
||||||
/*
|
|
||||||
* If mt_state_lost, we don't know which finger SGM contains.
|
|
||||||
*
|
|
||||||
* So, report 1 finger, but with both slots empty.
|
|
||||||
* We will use slot 1 on subsequent 1->1
|
|
||||||
*/
|
|
||||||
if (priv->mt_state_lost) {
|
|
||||||
synaptics_mt_state_set(mt_state, 1, -1, -1);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
/*
|
|
||||||
* Since the last AGM was NOT (0,0,0), it was the finger in
|
|
||||||
* slot 0 that has been removed.
|
|
||||||
* So, SGM now contains previous AGM's slot, and AGM is now
|
|
||||||
* empty.
|
|
||||||
*/
|
|
||||||
synaptics_mt_state_set(mt_state, 1, old->agm, -1);
|
|
||||||
break;
|
|
||||||
case 3:
|
|
||||||
/*
|
|
||||||
* Since last AGM was not (0,0,0), we don't know which finger
|
|
||||||
* is left.
|
|
||||||
*
|
|
||||||
* So, report 1 finger, but with both slots empty.
|
|
||||||
* We will use slot 1 on subsequent 1->1
|
|
||||||
*/
|
|
||||||
synaptics_mt_state_set(mt_state, 1, -1, -1);
|
|
||||||
priv->mt_state_lost = true;
|
|
||||||
break;
|
|
||||||
case 4:
|
|
||||||
case 5:
|
|
||||||
/* mt_state was updated by AGM-CONTACT packet */
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Handle case where mt_state->count = 2 */
|
|
||||||
static void synaptics_image_sensor_2f(struct synaptics_data *priv,
|
|
||||||
struct synaptics_mt_state *mt_state)
|
|
||||||
{
|
|
||||||
struct synaptics_mt_state *old = &priv->mt_state;
|
|
||||||
|
|
||||||
switch (old->count) {
|
|
||||||
case 0:
|
|
||||||
synaptics_mt_state_set(mt_state, 2, 0, 1);
|
|
||||||
break;
|
|
||||||
case 1:
|
|
||||||
/*
|
|
||||||
* If previous SGM contained slot 1 or higher, SGM now contains
|
|
||||||
* slot 0 (the newly touching finger) and AGM contains SGM's
|
|
||||||
* previous slot.
|
|
||||||
*
|
|
||||||
* Otherwise, SGM still contains slot 0 and AGM now contains
|
|
||||||
* slot 1.
|
|
||||||
*/
|
|
||||||
if (old->sgm >= 1)
|
|
||||||
synaptics_mt_state_set(mt_state, 2, 0, old->sgm);
|
|
||||||
else
|
|
||||||
synaptics_mt_state_set(mt_state, 2, 0, 1);
|
|
||||||
break;
|
|
||||||
case 2:
|
|
||||||
/*
|
|
||||||
* If mt_state_lost, SGM now contains either finger 1 or 2, but
|
|
||||||
* we don't know which.
|
|
||||||
* So, we just assume that the SGM contains slot 0 and AGM 1.
|
|
||||||
*/
|
|
||||||
if (priv->mt_state_lost)
|
|
||||||
synaptics_mt_state_set(mt_state, 2, 0, 1);
|
|
||||||
/*
|
|
||||||
* Otherwise, use the same mt_state, since it either hasn't
|
|
||||||
* changed, or was updated by a recently received AGM-CONTACT
|
|
||||||
* packet.
|
|
||||||
*/
|
|
||||||
break;
|
|
||||||
case 3:
|
|
||||||
/*
|
|
||||||
* 3->2 transitions have two unsolvable problems:
|
|
||||||
* 1) no indication is given which finger was removed
|
|
||||||
* 2) no way to tell if agm packet was for finger 3
|
|
||||||
* before 3->2, or finger 2 after 3->2.
|
|
||||||
*
|
|
||||||
* So, report 2 fingers, but empty all slots.
|
|
||||||
* We will guess slots [0,1] on subsequent 2->2.
|
|
||||||
*/
|
|
||||||
synaptics_mt_state_set(mt_state, 2, -1, -1);
|
|
||||||
priv->mt_state_lost = true;
|
|
||||||
break;
|
|
||||||
case 4:
|
|
||||||
case 5:
|
|
||||||
/* mt_state was updated by AGM-CONTACT packet */
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Handle case where mt_state->count = 3 */
|
|
||||||
static void synaptics_image_sensor_3f(struct synaptics_data *priv,
|
|
||||||
struct synaptics_mt_state *mt_state)
|
|
||||||
{
|
|
||||||
struct synaptics_mt_state *old = &priv->mt_state;
|
|
||||||
|
|
||||||
switch (old->count) {
|
|
||||||
case 0:
|
|
||||||
synaptics_mt_state_set(mt_state, 3, 0, 2);
|
|
||||||
break;
|
|
||||||
case 1:
|
|
||||||
/*
|
|
||||||
* If previous SGM contained slot 2 or higher, SGM now contains
|
|
||||||
* slot 0 (one of the newly touching fingers) and AGM contains
|
|
||||||
* SGM's previous slot.
|
|
||||||
*
|
|
||||||
* Otherwise, SGM now contains slot 0 and AGM contains slot 2.
|
|
||||||
*/
|
|
||||||
if (old->sgm >= 2)
|
|
||||||
synaptics_mt_state_set(mt_state, 3, 0, old->sgm);
|
|
||||||
else
|
|
||||||
synaptics_mt_state_set(mt_state, 3, 0, 2);
|
|
||||||
break;
|
|
||||||
case 2:
|
|
||||||
/*
|
|
||||||
* If the AGM previously contained slot 3 or higher, then the
|
|
||||||
* newly touching finger is in the lowest available slot.
|
|
||||||
*
|
|
||||||
* If SGM was previously 1 or higher, then the new SGM is
|
|
||||||
* now slot 0 (with a new finger), otherwise, the new finger
|
|
||||||
* is now in a hidden slot between 0 and AGM's slot.
|
|
||||||
*
|
|
||||||
* In all such cases, the SGM now contains slot 0, and the AGM
|
|
||||||
* continues to contain the same slot as before.
|
|
||||||
*/
|
|
||||||
if (old->agm >= 3) {
|
|
||||||
synaptics_mt_state_set(mt_state, 3, 0, old->agm);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
|
||||||
* After some 3->1 and all 3->2 transitions, we lose track
|
|
||||||
* of which slot is reported by SGM and AGM.
|
|
||||||
*
|
|
||||||
* For 2->3 in this state, report 3 fingers, but empty all
|
|
||||||
* slots, and we will guess (0,2) on a subsequent 0->3.
|
|
||||||
*
|
|
||||||
* To userspace, the resulting transition will look like:
|
|
||||||
* 2:[0,1] -> 3:[-1,-1] -> 3:[0,2]
|
|
||||||
*/
|
|
||||||
if (priv->mt_state_lost) {
|
|
||||||
synaptics_mt_state_set(mt_state, 3, -1, -1);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
|
||||||
* If the (SGM,AGM) really previously contained slots (0, 1),
|
|
||||||
* then we cannot know what slot was just reported by the AGM,
|
|
||||||
* because the 2->3 transition can occur either before or after
|
|
||||||
* the AGM packet. Thus, this most recent AGM could contain
|
|
||||||
* either the same old slot 1 or the new slot 2.
|
|
||||||
* Subsequent AGMs will be reporting slot 2.
|
|
||||||
*
|
|
||||||
* To userspace, the resulting transition will look like:
|
|
||||||
* 2:[0,1] -> 3:[0,-1] -> 3:[0,2]
|
|
||||||
*/
|
|
||||||
synaptics_mt_state_set(mt_state, 3, 0, -1);
|
|
||||||
break;
|
|
||||||
case 3:
|
|
||||||
/*
|
|
||||||
* If, for whatever reason, the previous agm was invalid,
|
|
||||||
* Assume SGM now contains slot 0, AGM now contains slot 2.
|
|
||||||
*/
|
|
||||||
if (old->agm <= 2)
|
|
||||||
synaptics_mt_state_set(mt_state, 3, 0, 2);
|
|
||||||
/*
|
|
||||||
* mt_state either hasn't changed, or was updated by a recently
|
|
||||||
* received AGM-CONTACT packet.
|
|
||||||
*/
|
|
||||||
break;
|
|
||||||
|
|
||||||
case 4:
|
|
||||||
case 5:
|
|
||||||
/* mt_state was updated by AGM-CONTACT packet */
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Handle case where mt_state->count = 4, or = 5 */
|
|
||||||
static void synaptics_image_sensor_45f(struct synaptics_data *priv,
|
|
||||||
struct synaptics_mt_state *mt_state)
|
|
||||||
{
|
|
||||||
/* mt_state was updated correctly by AGM-CONTACT packet */
|
|
||||||
priv->mt_state_lost = false;
|
|
||||||
}
|
|
||||||
|
|
||||||
static void synaptics_image_sensor_process(struct psmouse *psmouse,
|
|
||||||
struct synaptics_hw_state *sgm)
|
|
||||||
{
|
|
||||||
struct synaptics_data *priv = psmouse->private;
|
|
||||||
struct synaptics_hw_state *agm = &priv->agm;
|
|
||||||
struct synaptics_mt_state mt_state;
|
|
||||||
|
|
||||||
/* Initialize using current mt_state (as updated by last agm) */
|
|
||||||
mt_state = agm->mt_state;
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Update mt_state using the new finger count and current mt_state.
|
|
||||||
*/
|
|
||||||
if (sgm->z == 0)
|
|
||||||
synaptics_image_sensor_0f(priv, &mt_state);
|
|
||||||
else if (sgm->w >= 4)
|
|
||||||
synaptics_image_sensor_1f(priv, &mt_state);
|
|
||||||
else if (sgm->w == 0)
|
|
||||||
synaptics_image_sensor_2f(priv, &mt_state);
|
|
||||||
else if (sgm->w == 1 && mt_state.count <= 3)
|
|
||||||
synaptics_image_sensor_3f(priv, &mt_state);
|
|
||||||
else
|
|
||||||
synaptics_image_sensor_45f(priv, &mt_state);
|
|
||||||
|
|
||||||
/* Send resulting input events to user space */
|
|
||||||
synaptics_report_mt_data(psmouse, &mt_state, sgm);
|
|
||||||
|
|
||||||
/* Store updated mt_state */
|
|
||||||
priv->mt_state = agm->mt_state = mt_state;
|
|
||||||
priv->agm_pending = false;
|
|
||||||
}
|
|
||||||
|
|
||||||
static void synaptics_profile_sensor_process(struct psmouse *psmouse,
|
|
||||||
struct synaptics_hw_state *sgm,
|
|
||||||
int num_fingers)
|
|
||||||
{
|
|
||||||
struct input_dev *dev = psmouse->dev;
|
|
||||||
struct synaptics_data *priv = psmouse->private;
|
|
||||||
struct synaptics_hw_state *hw[2] = { sgm, &priv->agm };
|
|
||||||
struct input_mt_pos pos[2];
|
struct input_mt_pos pos[2];
|
||||||
int slot[2], nsemi, i;
|
int slot[2], nsemi, i;
|
||||||
|
|
||||||
|
@ -1205,7 +813,7 @@ static void synaptics_profile_sensor_process(struct psmouse *psmouse,
|
||||||
pos[i].y = synaptics_invert_y(hw[i]->y);
|
pos[i].y = synaptics_invert_y(hw[i]->y);
|
||||||
}
|
}
|
||||||
|
|
||||||
input_mt_assign_slots(dev, slot, pos, nsemi);
|
input_mt_assign_slots(dev, slot, pos, nsemi, DMAX * priv->x_res);
|
||||||
|
|
||||||
for (i = 0; i < nsemi; i++) {
|
for (i = 0; i < nsemi; i++) {
|
||||||
input_mt_slot(dev, slot[i]);
|
input_mt_slot(dev, slot[i]);
|
||||||
|
@ -1216,7 +824,11 @@ static void synaptics_profile_sensor_process(struct psmouse *psmouse,
|
||||||
}
|
}
|
||||||
|
|
||||||
input_mt_drop_unused(dev);
|
input_mt_drop_unused(dev);
|
||||||
|
|
||||||
|
/* Don't use active slot count to generate BTN_TOOL events. */
|
||||||
input_mt_report_pointer_emulation(dev, false);
|
input_mt_report_pointer_emulation(dev, false);
|
||||||
|
|
||||||
|
/* Send the number of fingers reported by touchpad itself. */
|
||||||
input_mt_report_finger_count(dev, num_fingers);
|
input_mt_report_finger_count(dev, num_fingers);
|
||||||
|
|
||||||
synaptics_report_buttons(psmouse, sgm);
|
synaptics_report_buttons(psmouse, sgm);
|
||||||
|
@ -1224,6 +836,30 @@ static void synaptics_profile_sensor_process(struct psmouse *psmouse,
|
||||||
input_sync(dev);
|
input_sync(dev);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static void synaptics_image_sensor_process(struct psmouse *psmouse,
|
||||||
|
struct synaptics_hw_state *sgm)
|
||||||
|
{
|
||||||
|
struct synaptics_data *priv = psmouse->private;
|
||||||
|
int num_fingers;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Update mt_state using the new finger count and current mt_state.
|
||||||
|
*/
|
||||||
|
if (sgm->z == 0)
|
||||||
|
num_fingers = 0;
|
||||||
|
else if (sgm->w >= 4)
|
||||||
|
num_fingers = 1;
|
||||||
|
else if (sgm->w == 0)
|
||||||
|
num_fingers = 2;
|
||||||
|
else if (sgm->w == 1)
|
||||||
|
num_fingers = priv->agm_count ? priv->agm_count : 3;
|
||||||
|
else
|
||||||
|
num_fingers = 4;
|
||||||
|
|
||||||
|
/* Send resulting input events to user space */
|
||||||
|
synaptics_report_mt_data(psmouse, sgm, num_fingers);
|
||||||
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* called for each full received packet from the touchpad
|
* called for each full received packet from the touchpad
|
||||||
*/
|
*/
|
||||||
|
@ -1288,7 +924,7 @@ static void synaptics_process_packet(struct psmouse *psmouse)
|
||||||
}
|
}
|
||||||
|
|
||||||
if (cr48_profile_sensor) {
|
if (cr48_profile_sensor) {
|
||||||
synaptics_profile_sensor_process(psmouse, &hw, num_fingers);
|
synaptics_report_mt_data(psmouse, &hw, num_fingers);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1445,7 +1081,7 @@ static void set_input_params(struct psmouse *psmouse,
|
||||||
ABS_MT_POSITION_Y);
|
ABS_MT_POSITION_Y);
|
||||||
/* Image sensors can report per-contact pressure */
|
/* Image sensors can report per-contact pressure */
|
||||||
input_set_abs_params(dev, ABS_MT_PRESSURE, 0, 255, 0, 0);
|
input_set_abs_params(dev, ABS_MT_PRESSURE, 0, 255, 0, 0);
|
||||||
input_mt_init_slots(dev, 2, INPUT_MT_POINTER);
|
input_mt_init_slots(dev, 2, INPUT_MT_POINTER | INPUT_MT_TRACK);
|
||||||
|
|
||||||
/* Image sensors can signal 4 and 5 finger clicks */
|
/* Image sensors can signal 4 and 5 finger clicks */
|
||||||
__set_bit(BTN_TOOL_QUADTAP, dev->keybit);
|
__set_bit(BTN_TOOL_QUADTAP, dev->keybit);
|
||||||
|
|
|
@ -118,16 +118,6 @@
|
||||||
/* amount to fuzz position data when touchpad reports reduced filtering */
|
/* amount to fuzz position data when touchpad reports reduced filtering */
|
||||||
#define SYN_REDUCED_FILTER_FUZZ 8
|
#define SYN_REDUCED_FILTER_FUZZ 8
|
||||||
|
|
||||||
/*
|
|
||||||
* A structure to describe which internal touchpad finger slots are being
|
|
||||||
* reported in raw packets.
|
|
||||||
*/
|
|
||||||
struct synaptics_mt_state {
|
|
||||||
int count; /* num fingers being tracked */
|
|
||||||
int sgm; /* which slot is reported by sgm pkt */
|
|
||||||
int agm; /* which slot is reported by agm pkt*/
|
|
||||||
};
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* A structure to describe the state of the touchpad hardware (buttons and pad)
|
* A structure to describe the state of the touchpad hardware (buttons and pad)
|
||||||
*/
|
*/
|
||||||
|
@ -143,9 +133,6 @@ struct synaptics_hw_state {
|
||||||
unsigned int down:1;
|
unsigned int down:1;
|
||||||
unsigned char ext_buttons;
|
unsigned char ext_buttons;
|
||||||
signed char scroll;
|
signed char scroll;
|
||||||
|
|
||||||
/* As reported in last AGM-CONTACT packets */
|
|
||||||
struct synaptics_mt_state mt_state;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
struct synaptics_data {
|
struct synaptics_data {
|
||||||
|
@ -170,15 +157,12 @@ struct synaptics_data {
|
||||||
|
|
||||||
struct serio *pt_port; /* Pass-through serio port */
|
struct serio *pt_port; /* Pass-through serio port */
|
||||||
|
|
||||||
struct synaptics_mt_state mt_state; /* Current mt finger state */
|
|
||||||
bool mt_state_lost; /* mt_state may be incorrect */
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Last received Advanced Gesture Mode (AGM) packet. An AGM packet
|
* Last received Advanced Gesture Mode (AGM) packet. An AGM packet
|
||||||
* contains position data for a second contact, at half resolution.
|
* contains position data for a second contact, at half resolution.
|
||||||
*/
|
*/
|
||||||
struct synaptics_hw_state agm;
|
struct synaptics_hw_state agm;
|
||||||
bool agm_pending; /* new AGM packet received */
|
unsigned int agm_count; /* finger count reported by agm */
|
||||||
|
|
||||||
/* ForcePad handling */
|
/* ForcePad handling */
|
||||||
unsigned long press_start;
|
unsigned long press_start;
|
||||||
|
|
|
@ -281,4 +281,14 @@ config HYPERV_KEYBOARD
|
||||||
To compile this driver as a module, choose M here: the module will
|
To compile this driver as a module, choose M here: the module will
|
||||||
be called hyperv_keyboard.
|
be called hyperv_keyboard.
|
||||||
|
|
||||||
|
config SERIO_SUN4I_PS2
|
||||||
|
tristate "Allwinner A10 PS/2 controller support"
|
||||||
|
depends on ARCH_SUNXI || COMPILE_TEST
|
||||||
|
help
|
||||||
|
This selects support for the PS/2 Host Controller on
|
||||||
|
Allwinner A10.
|
||||||
|
|
||||||
|
To compile this driver as a module, choose M here: the
|
||||||
|
module will be called sun4i-ps2.
|
||||||
|
|
||||||
endif
|
endif
|
||||||
|
|
|
@ -29,3 +29,4 @@ obj-$(CONFIG_SERIO_ARC_PS2) += arc_ps2.o
|
||||||
obj-$(CONFIG_SERIO_APBPS2) += apbps2.o
|
obj-$(CONFIG_SERIO_APBPS2) += apbps2.o
|
||||||
obj-$(CONFIG_SERIO_OLPC_APSP) += olpc_apsp.o
|
obj-$(CONFIG_SERIO_OLPC_APSP) += olpc_apsp.o
|
||||||
obj-$(CONFIG_HYPERV_KEYBOARD) += hyperv-keyboard.o
|
obj-$(CONFIG_HYPERV_KEYBOARD) += hyperv-keyboard.o
|
||||||
|
obj-$(CONFIG_SERIO_SUN4I_PS2) += sun4i-ps2.o
|
||||||
|
|
|
@ -40,7 +40,6 @@
|
||||||
MODULE_AUTHOR("Laurent Canet <canetl@esiee.fr>, Thibaut Varene <varenet@parisc-linux.org>, Helge Deller <deller@gmx.de>");
|
MODULE_AUTHOR("Laurent Canet <canetl@esiee.fr>, Thibaut Varene <varenet@parisc-linux.org>, Helge Deller <deller@gmx.de>");
|
||||||
MODULE_DESCRIPTION("HP GSC PS2 port driver");
|
MODULE_DESCRIPTION("HP GSC PS2 port driver");
|
||||||
MODULE_LICENSE("GPL");
|
MODULE_LICENSE("GPL");
|
||||||
MODULE_DEVICE_TABLE(parisc, gscps2_device_tbl);
|
|
||||||
|
|
||||||
#define PFX "gscps2.c: "
|
#define PFX "gscps2.c: "
|
||||||
|
|
||||||
|
@ -439,6 +438,7 @@ static struct parisc_device_id gscps2_device_tbl[] = {
|
||||||
#endif
|
#endif
|
||||||
{ 0, } /* 0 terminated list */
|
{ 0, } /* 0 terminated list */
|
||||||
};
|
};
|
||||||
|
MODULE_DEVICE_TABLE(parisc, gscps2_device_tbl);
|
||||||
|
|
||||||
static struct parisc_driver parisc_ps2_driver = {
|
static struct parisc_driver parisc_ps2_driver = {
|
||||||
.name = "gsc_ps2",
|
.name = "gsc_ps2",
|
||||||
|
|
340
drivers/input/serio/sun4i-ps2.c
Normal file
340
drivers/input/serio/sun4i-ps2.c
Normal file
|
@ -0,0 +1,340 @@
|
||||||
|
/*
|
||||||
|
* Driver for Allwinner A10 PS2 host controller
|
||||||
|
*
|
||||||
|
* Author: Vishnu Patekar <vishnupatekar0510@gmail.com>
|
||||||
|
* Aaron.maoye <leafy.myeh@newbietech.com>
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <linux/module.h>
|
||||||
|
#include <linux/serio.h>
|
||||||
|
#include <linux/interrupt.h>
|
||||||
|
#include <linux/errno.h>
|
||||||
|
#include <linux/slab.h>
|
||||||
|
#include <linux/io.h>
|
||||||
|
#include <linux/clk.h>
|
||||||
|
#include <linux/mod_devicetable.h>
|
||||||
|
#include <linux/platform_device.h>
|
||||||
|
|
||||||
|
#define DRIVER_NAME "sun4i-ps2"
|
||||||
|
|
||||||
|
/* register offset definitions */
|
||||||
|
#define PS2_REG_GCTL 0x00 /* PS2 Module Global Control Reg */
|
||||||
|
#define PS2_REG_DATA 0x04 /* PS2 Module Data Reg */
|
||||||
|
#define PS2_REG_LCTL 0x08 /* PS2 Module Line Control Reg */
|
||||||
|
#define PS2_REG_LSTS 0x0C /* PS2 Module Line Status Reg */
|
||||||
|
#define PS2_REG_FCTL 0x10 /* PS2 Module FIFO Control Reg */
|
||||||
|
#define PS2_REG_FSTS 0x14 /* PS2 Module FIFO Status Reg */
|
||||||
|
#define PS2_REG_CLKDR 0x18 /* PS2 Module Clock Divider Reg*/
|
||||||
|
|
||||||
|
/* PS2 GLOBAL CONTROL REGISTER PS2_GCTL */
|
||||||
|
#define PS2_GCTL_INTFLAG BIT(4)
|
||||||
|
#define PS2_GCTL_INTEN BIT(3)
|
||||||
|
#define PS2_GCTL_RESET BIT(2)
|
||||||
|
#define PS2_GCTL_MASTER BIT(1)
|
||||||
|
#define PS2_GCTL_BUSEN BIT(0)
|
||||||
|
|
||||||
|
/* PS2 LINE CONTROL REGISTER */
|
||||||
|
#define PS2_LCTL_NOACK BIT(18)
|
||||||
|
#define PS2_LCTL_TXDTOEN BIT(8)
|
||||||
|
#define PS2_LCTL_STOPERREN BIT(3)
|
||||||
|
#define PS2_LCTL_ACKERREN BIT(2)
|
||||||
|
#define PS2_LCTL_PARERREN BIT(1)
|
||||||
|
#define PS2_LCTL_RXDTOEN BIT(0)
|
||||||
|
|
||||||
|
/* PS2 LINE STATUS REGISTER */
|
||||||
|
#define PS2_LSTS_TXTDO BIT(8)
|
||||||
|
#define PS2_LSTS_STOPERR BIT(3)
|
||||||
|
#define PS2_LSTS_ACKERR BIT(2)
|
||||||
|
#define PS2_LSTS_PARERR BIT(1)
|
||||||
|
#define PS2_LSTS_RXTDO BIT(0)
|
||||||
|
|
||||||
|
#define PS2_LINE_ERROR_BIT \
|
||||||
|
(PS2_LSTS_TXTDO | PS2_LSTS_STOPERR | PS2_LSTS_ACKERR | \
|
||||||
|
PS2_LSTS_PARERR | PS2_LSTS_RXTDO)
|
||||||
|
|
||||||
|
/* PS2 FIFO CONTROL REGISTER */
|
||||||
|
#define PS2_FCTL_TXRST BIT(17)
|
||||||
|
#define PS2_FCTL_RXRST BIT(16)
|
||||||
|
#define PS2_FCTL_TXUFIEN BIT(10)
|
||||||
|
#define PS2_FCTL_TXOFIEN BIT(9)
|
||||||
|
#define PS2_FCTL_TXRDYIEN BIT(8)
|
||||||
|
#define PS2_FCTL_RXUFIEN BIT(2)
|
||||||
|
#define PS2_FCTL_RXOFIEN BIT(1)
|
||||||
|
#define PS2_FCTL_RXRDYIEN BIT(0)
|
||||||
|
|
||||||
|
/* PS2 FIFO STATUS REGISTER */
|
||||||
|
#define PS2_FSTS_TXUF BIT(10)
|
||||||
|
#define PS2_FSTS_TXOF BIT(9)
|
||||||
|
#define PS2_FSTS_TXRDY BIT(8)
|
||||||
|
#define PS2_FSTS_RXUF BIT(2)
|
||||||
|
#define PS2_FSTS_RXOF BIT(1)
|
||||||
|
#define PS2_FSTS_RXRDY BIT(0)
|
||||||
|
|
||||||
|
#define PS2_FIFO_ERROR_BIT \
|
||||||
|
(PS2_FSTS_TXUF | PS2_FSTS_TXOF | PS2_FSTS_RXUF | PS2_FSTS_RXOF)
|
||||||
|
|
||||||
|
#define PS2_SAMPLE_CLK 1000000
|
||||||
|
#define PS2_SCLK 125000
|
||||||
|
|
||||||
|
struct sun4i_ps2data {
|
||||||
|
struct serio *serio;
|
||||||
|
struct device *dev;
|
||||||
|
|
||||||
|
/* IO mapping base */
|
||||||
|
void __iomem *reg_base;
|
||||||
|
|
||||||
|
/* clock management */
|
||||||
|
struct clk *clk;
|
||||||
|
|
||||||
|
/* irq */
|
||||||
|
spinlock_t lock;
|
||||||
|
int irq;
|
||||||
|
};
|
||||||
|
|
||||||
|
static irqreturn_t sun4i_ps2_interrupt(int irq, void *dev_id)
|
||||||
|
{
|
||||||
|
struct sun4i_ps2data *drvdata = dev_id;
|
||||||
|
u32 intr_status;
|
||||||
|
u32 fifo_status;
|
||||||
|
unsigned char byte;
|
||||||
|
unsigned int rxflags = 0;
|
||||||
|
u32 rval;
|
||||||
|
|
||||||
|
spin_lock(&drvdata->lock);
|
||||||
|
|
||||||
|
/* Get the PS/2 interrupts and clear them */
|
||||||
|
intr_status = readl(drvdata->reg_base + PS2_REG_LSTS);
|
||||||
|
fifo_status = readl(drvdata->reg_base + PS2_REG_FSTS);
|
||||||
|
|
||||||
|
/* Check line status register */
|
||||||
|
if (intr_status & PS2_LINE_ERROR_BIT) {
|
||||||
|
rxflags = (intr_status & PS2_LINE_ERROR_BIT) ? SERIO_FRAME : 0;
|
||||||
|
rxflags |= (intr_status & PS2_LSTS_PARERR) ? SERIO_PARITY : 0;
|
||||||
|
rxflags |= (intr_status & PS2_LSTS_PARERR) ? SERIO_TIMEOUT : 0;
|
||||||
|
|
||||||
|
rval = PS2_LSTS_TXTDO | PS2_LSTS_STOPERR | PS2_LSTS_ACKERR |
|
||||||
|
PS2_LSTS_PARERR | PS2_LSTS_RXTDO;
|
||||||
|
writel(rval, drvdata->reg_base + PS2_REG_LSTS);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Check FIFO status register */
|
||||||
|
if (fifo_status & PS2_FIFO_ERROR_BIT) {
|
||||||
|
rval = PS2_FSTS_TXUF | PS2_FSTS_TXOF | PS2_FSTS_TXRDY |
|
||||||
|
PS2_FSTS_RXUF | PS2_FSTS_RXOF | PS2_FSTS_RXRDY;
|
||||||
|
writel(rval, drvdata->reg_base + PS2_REG_FSTS);
|
||||||
|
}
|
||||||
|
|
||||||
|
rval = (fifo_status >> 16) & 0x3;
|
||||||
|
while (rval--) {
|
||||||
|
byte = readl(drvdata->reg_base + PS2_REG_DATA) & 0xff;
|
||||||
|
serio_interrupt(drvdata->serio, byte, rxflags);
|
||||||
|
}
|
||||||
|
|
||||||
|
writel(intr_status, drvdata->reg_base + PS2_REG_LSTS);
|
||||||
|
writel(fifo_status, drvdata->reg_base + PS2_REG_FSTS);
|
||||||
|
|
||||||
|
spin_unlock(&drvdata->lock);
|
||||||
|
|
||||||
|
return IRQ_HANDLED;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int sun4i_ps2_open(struct serio *serio)
|
||||||
|
{
|
||||||
|
struct sun4i_ps2data *drvdata = serio->port_data;
|
||||||
|
u32 src_clk = 0;
|
||||||
|
u32 clk_scdf;
|
||||||
|
u32 clk_pcdf;
|
||||||
|
u32 rval;
|
||||||
|
unsigned long flags;
|
||||||
|
|
||||||
|
/* Set line control and enable interrupt */
|
||||||
|
rval = PS2_LCTL_STOPERREN | PS2_LCTL_ACKERREN
|
||||||
|
| PS2_LCTL_PARERREN | PS2_LCTL_RXDTOEN;
|
||||||
|
writel(rval, drvdata->reg_base + PS2_REG_LCTL);
|
||||||
|
|
||||||
|
/* Reset FIFO */
|
||||||
|
rval = PS2_FCTL_TXRST | PS2_FCTL_RXRST | PS2_FCTL_TXUFIEN
|
||||||
|
| PS2_FCTL_TXOFIEN | PS2_FCTL_RXUFIEN
|
||||||
|
| PS2_FCTL_RXOFIEN | PS2_FCTL_RXRDYIEN;
|
||||||
|
|
||||||
|
writel(rval, drvdata->reg_base + PS2_REG_FCTL);
|
||||||
|
|
||||||
|
src_clk = clk_get_rate(drvdata->clk);
|
||||||
|
/* Set clock divider register */
|
||||||
|
clk_scdf = src_clk / PS2_SAMPLE_CLK - 1;
|
||||||
|
clk_pcdf = PS2_SAMPLE_CLK / PS2_SCLK - 1;
|
||||||
|
rval = (clk_scdf << 8) | clk_pcdf;
|
||||||
|
writel(rval, drvdata->reg_base + PS2_REG_CLKDR);
|
||||||
|
|
||||||
|
/* Set global control register */
|
||||||
|
rval = PS2_GCTL_RESET | PS2_GCTL_INTEN | PS2_GCTL_MASTER
|
||||||
|
| PS2_GCTL_BUSEN;
|
||||||
|
|
||||||
|
spin_lock_irqsave(&drvdata->lock, flags);
|
||||||
|
writel(rval, drvdata->reg_base + PS2_REG_GCTL);
|
||||||
|
spin_unlock_irqrestore(&drvdata->lock, flags);
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void sun4i_ps2_close(struct serio *serio)
|
||||||
|
{
|
||||||
|
struct sun4i_ps2data *drvdata = serio->port_data;
|
||||||
|
u32 rval;
|
||||||
|
|
||||||
|
/* Shut off the interrupt */
|
||||||
|
rval = readl(drvdata->reg_base + PS2_REG_GCTL);
|
||||||
|
writel(rval & ~(PS2_GCTL_INTEN), drvdata->reg_base + PS2_REG_GCTL);
|
||||||
|
|
||||||
|
synchronize_irq(drvdata->irq);
|
||||||
|
}
|
||||||
|
|
||||||
|
static int sun4i_ps2_write(struct serio *serio, unsigned char val)
|
||||||
|
{
|
||||||
|
unsigned long expire = jiffies + msecs_to_jiffies(10000);
|
||||||
|
struct sun4i_ps2data *drvdata = serio->port_data;
|
||||||
|
|
||||||
|
do {
|
||||||
|
if (readl(drvdata->reg_base + PS2_REG_FSTS) & PS2_FSTS_TXRDY) {
|
||||||
|
writel(val, drvdata->reg_base + PS2_REG_DATA);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
} while (time_before(jiffies, expire));
|
||||||
|
|
||||||
|
return SERIO_TIMEOUT;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int sun4i_ps2_probe(struct platform_device *pdev)
|
||||||
|
{
|
||||||
|
struct resource *res; /* IO mem resources */
|
||||||
|
struct sun4i_ps2data *drvdata;
|
||||||
|
struct serio *serio;
|
||||||
|
struct device *dev = &pdev->dev;
|
||||||
|
unsigned int irq;
|
||||||
|
int error;
|
||||||
|
|
||||||
|
drvdata = kzalloc(sizeof(struct sun4i_ps2data), GFP_KERNEL);
|
||||||
|
serio = kzalloc(sizeof(struct serio), GFP_KERNEL);
|
||||||
|
if (!drvdata || !serio) {
|
||||||
|
error = -ENOMEM;
|
||||||
|
goto err_free_mem;
|
||||||
|
}
|
||||||
|
|
||||||
|
spin_lock_init(&drvdata->lock);
|
||||||
|
|
||||||
|
/* IO */
|
||||||
|
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
|
||||||
|
if (!res) {
|
||||||
|
dev_err(dev, "failed to locate registers\n");
|
||||||
|
error = -ENXIO;
|
||||||
|
goto err_free_mem;
|
||||||
|
}
|
||||||
|
|
||||||
|
drvdata->reg_base = ioremap(res->start, resource_size(res));
|
||||||
|
if (!drvdata->reg_base) {
|
||||||
|
dev_err(dev, "failed to map registers\n");
|
||||||
|
error = -ENOMEM;
|
||||||
|
goto err_free_mem;
|
||||||
|
}
|
||||||
|
|
||||||
|
drvdata->clk = clk_get(dev, NULL);
|
||||||
|
if (IS_ERR(drvdata->clk)) {
|
||||||
|
error = PTR_ERR(drvdata->clk);
|
||||||
|
dev_err(dev, "couldn't get clock %d\n", error);
|
||||||
|
goto err_ioremap;
|
||||||
|
}
|
||||||
|
|
||||||
|
error = clk_prepare_enable(drvdata->clk);
|
||||||
|
if (error) {
|
||||||
|
dev_err(dev, "failed to enable clock %d\n", error);
|
||||||
|
goto err_clk;
|
||||||
|
}
|
||||||
|
|
||||||
|
serio->id.type = SERIO_8042;
|
||||||
|
serio->write = sun4i_ps2_write;
|
||||||
|
serio->open = sun4i_ps2_open;
|
||||||
|
serio->close = sun4i_ps2_close;
|
||||||
|
serio->port_data = drvdata;
|
||||||
|
serio->dev.parent = dev;
|
||||||
|
strlcpy(serio->name, dev_name(dev), sizeof(serio->name));
|
||||||
|
strlcpy(serio->phys, dev_name(dev), sizeof(serio->phys));
|
||||||
|
|
||||||
|
/* shutoff interrupt */
|
||||||
|
writel(0, drvdata->reg_base + PS2_REG_GCTL);
|
||||||
|
|
||||||
|
/* Get IRQ for the device */
|
||||||
|
irq = platform_get_irq(pdev, 0);
|
||||||
|
if (!irq) {
|
||||||
|
dev_err(dev, "no IRQ found\n");
|
||||||
|
error = -ENXIO;
|
||||||
|
goto err_disable_clk;
|
||||||
|
}
|
||||||
|
|
||||||
|
drvdata->irq = irq;
|
||||||
|
drvdata->serio = serio;
|
||||||
|
drvdata->dev = dev;
|
||||||
|
|
||||||
|
error = request_irq(drvdata->irq, sun4i_ps2_interrupt, 0,
|
||||||
|
DRIVER_NAME, drvdata);
|
||||||
|
if (error) {
|
||||||
|
dev_err(drvdata->dev, "failed to allocate interrupt %d: %d\n",
|
||||||
|
drvdata->irq, error);
|
||||||
|
goto err_disable_clk;
|
||||||
|
}
|
||||||
|
|
||||||
|
serio_register_port(serio);
|
||||||
|
platform_set_drvdata(pdev, drvdata);
|
||||||
|
|
||||||
|
return 0; /* success */
|
||||||
|
|
||||||
|
err_disable_clk:
|
||||||
|
clk_disable_unprepare(drvdata->clk);
|
||||||
|
err_clk:
|
||||||
|
clk_put(drvdata->clk);
|
||||||
|
err_ioremap:
|
||||||
|
iounmap(drvdata->reg_base);
|
||||||
|
err_free_mem:
|
||||||
|
kfree(serio);
|
||||||
|
kfree(drvdata);
|
||||||
|
return error;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int sun4i_ps2_remove(struct platform_device *pdev)
|
||||||
|
{
|
||||||
|
struct sun4i_ps2data *drvdata = platform_get_drvdata(pdev);
|
||||||
|
|
||||||
|
serio_unregister_port(drvdata->serio);
|
||||||
|
|
||||||
|
free_irq(drvdata->irq, drvdata);
|
||||||
|
|
||||||
|
clk_disable_unprepare(drvdata->clk);
|
||||||
|
clk_put(drvdata->clk);
|
||||||
|
|
||||||
|
iounmap(drvdata->reg_base);
|
||||||
|
|
||||||
|
kfree(drvdata);
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static const struct of_device_id sun4i_ps2_match[] = {
|
||||||
|
{ .compatible = "allwinner,sun4i-a10-ps2", },
|
||||||
|
{ },
|
||||||
|
};
|
||||||
|
|
||||||
|
MODULE_DEVICE_TABLE(of, sun4i_ps2_match);
|
||||||
|
|
||||||
|
static struct platform_driver sun4i_ps2_driver = {
|
||||||
|
.probe = sun4i_ps2_probe,
|
||||||
|
.remove = sun4i_ps2_remove,
|
||||||
|
.driver = {
|
||||||
|
.name = DRIVER_NAME,
|
||||||
|
.of_match_table = sun4i_ps2_match,
|
||||||
|
},
|
||||||
|
};
|
||||||
|
module_platform_driver(sun4i_ps2_driver);
|
||||||
|
|
||||||
|
MODULE_AUTHOR("Vishnu Patekar <vishnupatekar0510@gmail.com>");
|
||||||
|
MODULE_AUTHOR("Aaron.maoye <leafy.myeh@newbietech.com>");
|
||||||
|
MODULE_DESCRIPTION("Allwinner A10/Sun4i PS/2 driver");
|
||||||
|
MODULE_LICENSE("GPL v2");
|
|
@ -59,7 +59,7 @@ Scott Hill shill@gtcocalcomp.com
|
||||||
#include <asm/uaccess.h>
|
#include <asm/uaccess.h>
|
||||||
#include <asm/unaligned.h>
|
#include <asm/unaligned.h>
|
||||||
#include <asm/byteorder.h>
|
#include <asm/byteorder.h>
|
||||||
|
#include <linux/bitops.h>
|
||||||
|
|
||||||
#include <linux/usb/input.h>
|
#include <linux/usb/input.h>
|
||||||
|
|
||||||
|
@ -614,7 +614,6 @@ static void gtco_urb_callback(struct urb *urbinfo)
|
||||||
struct input_dev *inputdev;
|
struct input_dev *inputdev;
|
||||||
int rc;
|
int rc;
|
||||||
u32 val = 0;
|
u32 val = 0;
|
||||||
s8 valsigned = 0;
|
|
||||||
char le_buffer[2];
|
char le_buffer[2];
|
||||||
|
|
||||||
inputdev = device->inputdevice;
|
inputdev = device->inputdevice;
|
||||||
|
@ -665,20 +664,11 @@ static void gtco_urb_callback(struct urb *urbinfo)
|
||||||
/* Fall thru */
|
/* Fall thru */
|
||||||
case 4:
|
case 4:
|
||||||
/* Tilt */
|
/* Tilt */
|
||||||
|
input_report_abs(inputdev, ABS_TILT_X,
|
||||||
|
sign_extend32(device->buffer[6], 6));
|
||||||
|
|
||||||
/* Sign extend these 7 bit numbers. */
|
input_report_abs(inputdev, ABS_TILT_Y,
|
||||||
if (device->buffer[6] & 0x40)
|
sign_extend32(device->buffer[7], 6));
|
||||||
device->buffer[6] |= 0x80;
|
|
||||||
|
|
||||||
if (device->buffer[7] & 0x40)
|
|
||||||
device->buffer[7] |= 0x80;
|
|
||||||
|
|
||||||
|
|
||||||
valsigned = (device->buffer[6]);
|
|
||||||
input_report_abs(inputdev, ABS_TILT_X, (s32)valsigned);
|
|
||||||
|
|
||||||
valsigned = (device->buffer[7]);
|
|
||||||
input_report_abs(inputdev, ABS_TILT_Y, (s32)valsigned);
|
|
||||||
|
|
||||||
/* Fall thru */
|
/* Fall thru */
|
||||||
case 2:
|
case 2:
|
||||||
|
|
|
@ -33,10 +33,8 @@
|
||||||
#include <linux/delay.h>
|
#include <linux/delay.h>
|
||||||
#include <linux/uaccess.h>
|
#include <linux/uaccess.h>
|
||||||
#include <linux/buffer_head.h>
|
#include <linux/buffer_head.h>
|
||||||
#include <linux/version.h>
|
|
||||||
#include <linux/slab.h>
|
#include <linux/slab.h>
|
||||||
#include <linux/firmware.h>
|
#include <linux/firmware.h>
|
||||||
#include <linux/version.h>
|
|
||||||
#include <linux/input/mt.h>
|
#include <linux/input/mt.h>
|
||||||
#include <linux/acpi.h>
|
#include <linux/acpi.h>
|
||||||
#include <linux/of.h>
|
#include <linux/of.h>
|
||||||
|
|
|
@ -126,7 +126,7 @@ static void pixcir_ts_report(struct pixcir_i2c_ts_data *ts,
|
||||||
pos[i].y = touch->y;
|
pos[i].y = touch->y;
|
||||||
}
|
}
|
||||||
|
|
||||||
input_mt_assign_slots(ts->input, slots, pos, n);
|
input_mt_assign_slots(ts->input, slots, pos, n, 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
for (i = 0; i < n; i++) {
|
for (i = 0; i < n; i++) {
|
||||||
|
|
|
@ -34,6 +34,7 @@
|
||||||
|
|
||||||
#include <linux/err.h>
|
#include <linux/err.h>
|
||||||
#include <linux/hwmon.h>
|
#include <linux/hwmon.h>
|
||||||
|
#include <linux/thermal.h>
|
||||||
#include <linux/init.h>
|
#include <linux/init.h>
|
||||||
#include <linux/input.h>
|
#include <linux/input.h>
|
||||||
#include <linux/interrupt.h>
|
#include <linux/interrupt.h>
|
||||||
|
@ -71,6 +72,9 @@
|
||||||
#define TP_ADC_SELECT(x) ((x) << 3)
|
#define TP_ADC_SELECT(x) ((x) << 3)
|
||||||
#define ADC_CHAN_SELECT(x) ((x) << 0) /* 3 bits */
|
#define ADC_CHAN_SELECT(x) ((x) << 0) /* 3 bits */
|
||||||
|
|
||||||
|
/* on sun6i, bits 3~6 are left shifted by 1 to 4~7 */
|
||||||
|
#define SUN6I_TP_MODE_EN(x) ((x) << 5)
|
||||||
|
|
||||||
/* TP_CTRL2 bits */
|
/* TP_CTRL2 bits */
|
||||||
#define TP_SENSITIVE_ADJUST(x) ((x) << 28) /* 4 bits */
|
#define TP_SENSITIVE_ADJUST(x) ((x) << 28) /* 4 bits */
|
||||||
#define TP_MODE_SELECT(x) ((x) << 26) /* 2 bits */
|
#define TP_MODE_SELECT(x) ((x) << 26) /* 2 bits */
|
||||||
|
@ -107,10 +111,13 @@
|
||||||
struct sun4i_ts_data {
|
struct sun4i_ts_data {
|
||||||
struct device *dev;
|
struct device *dev;
|
||||||
struct input_dev *input;
|
struct input_dev *input;
|
||||||
|
struct thermal_zone_device *tz;
|
||||||
void __iomem *base;
|
void __iomem *base;
|
||||||
unsigned int irq;
|
unsigned int irq;
|
||||||
bool ignore_fifo_data;
|
bool ignore_fifo_data;
|
||||||
int temp_data;
|
int temp_data;
|
||||||
|
int temp_offset;
|
||||||
|
int temp_step;
|
||||||
};
|
};
|
||||||
|
|
||||||
static void sun4i_ts_irq_handle_input(struct sun4i_ts_data *ts, u32 reg_val)
|
static void sun4i_ts_irq_handle_input(struct sun4i_ts_data *ts, u32 reg_val)
|
||||||
|
@ -180,16 +187,38 @@ static void sun4i_ts_close(struct input_dev *dev)
|
||||||
writel(TEMP_IRQ_EN(1), ts->base + TP_INT_FIFOC);
|
writel(TEMP_IRQ_EN(1), ts->base + TP_INT_FIFOC);
|
||||||
}
|
}
|
||||||
|
|
||||||
static ssize_t show_temp(struct device *dev, struct device_attribute *devattr,
|
static int sun4i_get_temp(const struct sun4i_ts_data *ts, long *temp)
|
||||||
char *buf)
|
|
||||||
{
|
{
|
||||||
struct sun4i_ts_data *ts = dev_get_drvdata(dev);
|
|
||||||
|
|
||||||
/* No temp_data until the first irq */
|
/* No temp_data until the first irq */
|
||||||
if (ts->temp_data == -1)
|
if (ts->temp_data == -1)
|
||||||
return -EAGAIN;
|
return -EAGAIN;
|
||||||
|
|
||||||
return sprintf(buf, "%d\n", (ts->temp_data - 1447) * 100);
|
*temp = (ts->temp_data - ts->temp_offset) * ts->temp_step;
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int sun4i_get_tz_temp(void *data, long *temp)
|
||||||
|
{
|
||||||
|
return sun4i_get_temp(data, temp);
|
||||||
|
}
|
||||||
|
|
||||||
|
static struct thermal_zone_of_device_ops sun4i_ts_tz_ops = {
|
||||||
|
.get_temp = sun4i_get_tz_temp,
|
||||||
|
};
|
||||||
|
|
||||||
|
static ssize_t show_temp(struct device *dev, struct device_attribute *devattr,
|
||||||
|
char *buf)
|
||||||
|
{
|
||||||
|
struct sun4i_ts_data *ts = dev_get_drvdata(dev);
|
||||||
|
long temp;
|
||||||
|
int error;
|
||||||
|
|
||||||
|
error = sun4i_get_temp(ts, &temp);
|
||||||
|
if (error)
|
||||||
|
return error;
|
||||||
|
|
||||||
|
return sprintf(buf, "%ld\n", temp);
|
||||||
}
|
}
|
||||||
|
|
||||||
static ssize_t show_temp_label(struct device *dev,
|
static ssize_t show_temp_label(struct device *dev,
|
||||||
|
@ -215,6 +244,7 @@ static int sun4i_ts_probe(struct platform_device *pdev)
|
||||||
struct device_node *np = dev->of_node;
|
struct device_node *np = dev->of_node;
|
||||||
struct device *hwmon;
|
struct device *hwmon;
|
||||||
int error;
|
int error;
|
||||||
|
u32 reg;
|
||||||
bool ts_attached;
|
bool ts_attached;
|
||||||
|
|
||||||
ts = devm_kzalloc(dev, sizeof(struct sun4i_ts_data), GFP_KERNEL);
|
ts = devm_kzalloc(dev, sizeof(struct sun4i_ts_data), GFP_KERNEL);
|
||||||
|
@ -224,6 +254,25 @@ static int sun4i_ts_probe(struct platform_device *pdev)
|
||||||
ts->dev = dev;
|
ts->dev = dev;
|
||||||
ts->ignore_fifo_data = true;
|
ts->ignore_fifo_data = true;
|
||||||
ts->temp_data = -1;
|
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;
|
||||||
|
ts->temp_step = 167;
|
||||||
|
} 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)
|
||||||
|
*
|
||||||
|
* 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_step = 100;
|
||||||
|
}
|
||||||
|
|
||||||
ts_attached = of_property_read_bool(np, "allwinner,ts-attached");
|
ts_attached = of_property_read_bool(np, "allwinner,ts-attached");
|
||||||
if (ts_attached) {
|
if (ts_attached) {
|
||||||
|
@ -280,20 +329,34 @@ static int sun4i_ts_probe(struct platform_device *pdev)
|
||||||
* Set stylus up debounce to aprox 10 ms, enable debounce, and
|
* Set stylus up debounce to aprox 10 ms, enable debounce, and
|
||||||
* finally enable tp mode.
|
* finally enable tp mode.
|
||||||
*/
|
*/
|
||||||
writel(STYLUS_UP_DEBOUN(5) | STYLUS_UP_DEBOUN_EN(1) | TP_MODE_EN(1),
|
reg = STYLUS_UP_DEBOUN(5) | STYLUS_UP_DEBOUN_EN(1);
|
||||||
ts->base + TP_CTRL1);
|
if (of_device_is_compatible(np, "allwinner,sun4i-a10-ts"))
|
||||||
|
reg |= TP_MODE_EN(1);
|
||||||
|
else
|
||||||
|
reg |= SUN6I_TP_MODE_EN(1);
|
||||||
|
writel(reg, ts->base + TP_CTRL1);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* The thermal core does not register hwmon devices for DT-based
|
||||||
|
* thermal zone sensors, such as this one.
|
||||||
|
*/
|
||||||
hwmon = devm_hwmon_device_register_with_groups(ts->dev, "sun4i_ts",
|
hwmon = devm_hwmon_device_register_with_groups(ts->dev, "sun4i_ts",
|
||||||
ts, sun4i_ts_groups);
|
ts, sun4i_ts_groups);
|
||||||
if (IS_ERR(hwmon))
|
if (IS_ERR(hwmon))
|
||||||
return PTR_ERR(hwmon);
|
return PTR_ERR(hwmon);
|
||||||
|
|
||||||
|
ts->tz = thermal_zone_of_sensor_register(ts->dev, 0, ts,
|
||||||
|
&sun4i_ts_tz_ops);
|
||||||
|
if (IS_ERR(ts->tz))
|
||||||
|
ts->tz = NULL;
|
||||||
|
|
||||||
writel(TEMP_IRQ_EN(1), ts->base + TP_INT_FIFOC);
|
writel(TEMP_IRQ_EN(1), ts->base + TP_INT_FIFOC);
|
||||||
|
|
||||||
if (ts_attached) {
|
if (ts_attached) {
|
||||||
error = input_register_device(ts->input);
|
error = input_register_device(ts->input);
|
||||||
if (error) {
|
if (error) {
|
||||||
writel(0, ts->base + TP_INT_FIFOC);
|
writel(0, ts->base + TP_INT_FIFOC);
|
||||||
|
thermal_zone_of_sensor_unregister(ts->dev, ts->tz);
|
||||||
return error;
|
return error;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -310,6 +373,8 @@ static int sun4i_ts_remove(struct platform_device *pdev)
|
||||||
if (ts->input)
|
if (ts->input)
|
||||||
input_unregister_device(ts->input);
|
input_unregister_device(ts->input);
|
||||||
|
|
||||||
|
thermal_zone_of_sensor_unregister(ts->dev, ts->tz);
|
||||||
|
|
||||||
/* Deactivate all IRQs */
|
/* Deactivate all IRQs */
|
||||||
writel(0, ts->base + TP_INT_FIFOC);
|
writel(0, ts->base + TP_INT_FIFOC);
|
||||||
|
|
||||||
|
@ -318,6 +383,7 @@ static int sun4i_ts_remove(struct platform_device *pdev)
|
||||||
|
|
||||||
static const struct of_device_id sun4i_ts_of_match[] = {
|
static const struct of_device_id sun4i_ts_of_match[] = {
|
||||||
{ .compatible = "allwinner,sun4i-a10-ts", },
|
{ .compatible = "allwinner,sun4i-a10-ts", },
|
||||||
|
{ .compatible = "allwinner,sun6i-a31-ts", },
|
||||||
{ /* sentinel */ }
|
{ /* sentinel */ }
|
||||||
};
|
};
|
||||||
MODULE_DEVICE_TABLE(of, sun4i_ts_of_match);
|
MODULE_DEVICE_TABLE(of, sun4i_ts_of_match);
|
||||||
|
|
|
@ -26,6 +26,7 @@
|
||||||
#include <linux/delay.h>
|
#include <linux/delay.h>
|
||||||
#include <linux/of.h>
|
#include <linux/of.h>
|
||||||
#include <linux/of_device.h>
|
#include <linux/of_device.h>
|
||||||
|
#include <linux/sort.h>
|
||||||
|
|
||||||
#include <linux/mfd/ti_am335x_tscadc.h>
|
#include <linux/mfd/ti_am335x_tscadc.h>
|
||||||
|
|
||||||
|
@ -52,6 +53,7 @@ struct titsc {
|
||||||
u32 bit_xp, bit_xn, bit_yp, bit_yn;
|
u32 bit_xp, bit_xn, bit_yp, bit_yn;
|
||||||
u32 inp_xp, inp_xn, inp_yp, inp_yn;
|
u32 inp_xp, inp_xn, inp_yp, inp_yn;
|
||||||
u32 step_mask;
|
u32 step_mask;
|
||||||
|
u32 charge_delay;
|
||||||
};
|
};
|
||||||
|
|
||||||
static unsigned int titsc_readl(struct titsc *ts, unsigned int reg)
|
static unsigned int titsc_readl(struct titsc *ts, unsigned int reg)
|
||||||
|
@ -121,7 +123,7 @@ static void titsc_step_config(struct titsc *ts_dev)
|
||||||
{
|
{
|
||||||
unsigned int config;
|
unsigned int config;
|
||||||
int i;
|
int i;
|
||||||
int end_step;
|
int end_step, first_step, tsc_steps;
|
||||||
u32 stepenable;
|
u32 stepenable;
|
||||||
|
|
||||||
config = STEPCONFIG_MODE_HWSYNC |
|
config = STEPCONFIG_MODE_HWSYNC |
|
||||||
|
@ -140,9 +142,11 @@ static void titsc_step_config(struct titsc *ts_dev)
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* 1 … coordinate_readouts is for X */
|
tsc_steps = ts_dev->coordinate_readouts * 2 + 2;
|
||||||
end_step = ts_dev->coordinate_readouts;
|
first_step = TOTAL_STEPS - tsc_steps;
|
||||||
for (i = 0; i < end_step; i++) {
|
/* Steps 16 to 16-coordinate_readouts is for X */
|
||||||
|
end_step = first_step + tsc_steps;
|
||||||
|
for (i = end_step - ts_dev->coordinate_readouts; i < end_step; i++) {
|
||||||
titsc_writel(ts_dev, REG_STEPCONFIG(i), config);
|
titsc_writel(ts_dev, REG_STEPCONFIG(i), config);
|
||||||
titsc_writel(ts_dev, REG_STEPDELAY(i), STEPCONFIG_OPENDLY);
|
titsc_writel(ts_dev, REG_STEPDELAY(i), STEPCONFIG_OPENDLY);
|
||||||
}
|
}
|
||||||
|
@ -164,22 +168,20 @@ static void titsc_step_config(struct titsc *ts_dev)
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* coordinate_readouts … coordinate_readouts * 2 is for Y */
|
/* 1 ... coordinate_readouts is for Y */
|
||||||
end_step = ts_dev->coordinate_readouts * 2;
|
end_step = first_step + ts_dev->coordinate_readouts;
|
||||||
for (i = ts_dev->coordinate_readouts; i < end_step; i++) {
|
for (i = first_step; i < end_step; i++) {
|
||||||
titsc_writel(ts_dev, REG_STEPCONFIG(i), config);
|
titsc_writel(ts_dev, REG_STEPCONFIG(i), config);
|
||||||
titsc_writel(ts_dev, REG_STEPDELAY(i), STEPCONFIG_OPENDLY);
|
titsc_writel(ts_dev, REG_STEPDELAY(i), STEPCONFIG_OPENDLY);
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Charge step configuration */
|
/* Make CHARGECONFIG same as IDLECONFIG */
|
||||||
config = ts_dev->bit_xp | ts_dev->bit_yn |
|
|
||||||
STEPCHARGE_RFP_XPUL | STEPCHARGE_RFM_XNUR |
|
|
||||||
STEPCHARGE_INM_AN1 | STEPCHARGE_INP(ts_dev->inp_yp);
|
|
||||||
|
|
||||||
|
config = titsc_readl(ts_dev, REG_IDLECONFIG);
|
||||||
titsc_writel(ts_dev, REG_CHARGECONFIG, config);
|
titsc_writel(ts_dev, REG_CHARGECONFIG, config);
|
||||||
titsc_writel(ts_dev, REG_CHARGEDELAY, CHARGEDLY_OPENDLY);
|
titsc_writel(ts_dev, REG_CHARGEDELAY, ts_dev->charge_delay);
|
||||||
|
|
||||||
/* coordinate_readouts * 2 … coordinate_readouts * 2 + 2 is for Z */
|
/* coordinate_readouts + 1 ... coordinate_readouts + 2 is for Z */
|
||||||
config = STEPCONFIG_MODE_HWSYNC |
|
config = STEPCONFIG_MODE_HWSYNC |
|
||||||
STEPCONFIG_AVG_16 | ts_dev->bit_yp |
|
STEPCONFIG_AVG_16 | ts_dev->bit_yp |
|
||||||
ts_dev->bit_xn | STEPCONFIG_INM_ADCREFM |
|
ts_dev->bit_xn | STEPCONFIG_INM_ADCREFM |
|
||||||
|
@ -194,73 +196,104 @@ static void titsc_step_config(struct titsc *ts_dev)
|
||||||
titsc_writel(ts_dev, REG_STEPDELAY(end_step),
|
titsc_writel(ts_dev, REG_STEPDELAY(end_step),
|
||||||
STEPCONFIG_OPENDLY);
|
STEPCONFIG_OPENDLY);
|
||||||
|
|
||||||
/* The steps1 … end and bit 0 for TS_Charge */
|
/* The steps end ... end - readouts * 2 + 2 and bit 0 for TS_Charge */
|
||||||
stepenable = (1 << (end_step + 2)) - 1;
|
stepenable = 1;
|
||||||
|
for (i = 0; i < tsc_steps; i++)
|
||||||
|
stepenable |= 1 << (first_step + i + 1);
|
||||||
|
|
||||||
ts_dev->step_mask = stepenable;
|
ts_dev->step_mask = stepenable;
|
||||||
am335x_tsc_se_set_cache(ts_dev->mfd_tscadc, ts_dev->step_mask);
|
am335x_tsc_se_set_cache(ts_dev->mfd_tscadc, ts_dev->step_mask);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static int titsc_cmp_coord(const void *a, const void *b)
|
||||||
|
{
|
||||||
|
return *(int *)a - *(int *)b;
|
||||||
|
}
|
||||||
|
|
||||||
static void titsc_read_coordinates(struct titsc *ts_dev,
|
static void titsc_read_coordinates(struct titsc *ts_dev,
|
||||||
u32 *x, u32 *y, u32 *z1, u32 *z2)
|
u32 *x, u32 *y, u32 *z1, u32 *z2)
|
||||||
{
|
{
|
||||||
unsigned int fifocount = titsc_readl(ts_dev, REG_FIFO0CNT);
|
unsigned int yvals[7], xvals[7];
|
||||||
unsigned int prev_val_x = ~0, prev_val_y = ~0;
|
unsigned int i, xsum = 0, ysum = 0;
|
||||||
unsigned int prev_diff_x = ~0, prev_diff_y = ~0;
|
|
||||||
unsigned int read, diff;
|
|
||||||
unsigned int i, channel;
|
|
||||||
unsigned int creads = ts_dev->coordinate_readouts;
|
unsigned int creads = ts_dev->coordinate_readouts;
|
||||||
|
|
||||||
*z1 = *z2 = 0;
|
for (i = 0; i < creads; i++) {
|
||||||
if (fifocount % (creads * 2 + 2))
|
yvals[i] = titsc_readl(ts_dev, REG_FIFO0);
|
||||||
fifocount -= fifocount % (creads * 2 + 2);
|
yvals[i] &= 0xfff;
|
||||||
/*
|
|
||||||
* Delta filter is used to remove large variations in sampled
|
|
||||||
* values from ADC. The filter tries to predict where the next
|
|
||||||
* coordinate could be. This is done by taking a previous
|
|
||||||
* coordinate and subtracting it form current one. Further the
|
|
||||||
* algorithm compares the difference with that of a present value,
|
|
||||||
* if true the value is reported to the sub system.
|
|
||||||
*/
|
|
||||||
for (i = 0; i < fifocount; i++) {
|
|
||||||
read = titsc_readl(ts_dev, REG_FIFO0);
|
|
||||||
|
|
||||||
channel = (read & 0xf0000) >> 16;
|
|
||||||
read &= 0xfff;
|
|
||||||
if (channel < creads) {
|
|
||||||
diff = abs(read - prev_val_x);
|
|
||||||
if (diff < prev_diff_x) {
|
|
||||||
prev_diff_x = diff;
|
|
||||||
*x = read;
|
|
||||||
}
|
|
||||||
prev_val_x = read;
|
|
||||||
|
|
||||||
} else if (channel < creads * 2) {
|
|
||||||
diff = abs(read - prev_val_y);
|
|
||||||
if (diff < prev_diff_y) {
|
|
||||||
prev_diff_y = diff;
|
|
||||||
*y = read;
|
|
||||||
}
|
|
||||||
prev_val_y = read;
|
|
||||||
|
|
||||||
} else if (channel < creads * 2 + 1) {
|
|
||||||
*z1 = read;
|
|
||||||
|
|
||||||
} else if (channel < creads * 2 + 2) {
|
|
||||||
*z2 = read;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
*z1 = titsc_readl(ts_dev, REG_FIFO0);
|
||||||
|
*z1 &= 0xfff;
|
||||||
|
*z2 = titsc_readl(ts_dev, REG_FIFO0);
|
||||||
|
*z2 &= 0xfff;
|
||||||
|
|
||||||
|
for (i = 0; i < creads; i++) {
|
||||||
|
xvals[i] = titsc_readl(ts_dev, REG_FIFO0);
|
||||||
|
xvals[i] &= 0xfff;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* If co-ordinates readouts is less than 4 then
|
||||||
|
* report the average. In case of 4 or more
|
||||||
|
* readouts, sort the co-ordinate samples, drop
|
||||||
|
* min and max values and report the average of
|
||||||
|
* remaining values.
|
||||||
|
*/
|
||||||
|
if (creads <= 3) {
|
||||||
|
for (i = 0; i < creads; i++) {
|
||||||
|
ysum += yvals[i];
|
||||||
|
xsum += xvals[i];
|
||||||
|
}
|
||||||
|
ysum /= creads;
|
||||||
|
xsum /= creads;
|
||||||
|
} else {
|
||||||
|
sort(yvals, creads, sizeof(unsigned int),
|
||||||
|
titsc_cmp_coord, NULL);
|
||||||
|
sort(xvals, creads, sizeof(unsigned int),
|
||||||
|
titsc_cmp_coord, NULL);
|
||||||
|
for (i = 1; i < creads - 1; i++) {
|
||||||
|
ysum += yvals[i];
|
||||||
|
xsum += xvals[i];
|
||||||
|
}
|
||||||
|
ysum /= creads - 2;
|
||||||
|
xsum /= creads - 2;
|
||||||
|
}
|
||||||
|
*y = ysum;
|
||||||
|
*x = xsum;
|
||||||
}
|
}
|
||||||
|
|
||||||
static irqreturn_t titsc_irq(int irq, void *dev)
|
static irqreturn_t titsc_irq(int irq, void *dev)
|
||||||
{
|
{
|
||||||
struct titsc *ts_dev = dev;
|
struct titsc *ts_dev = dev;
|
||||||
struct input_dev *input_dev = ts_dev->input;
|
struct input_dev *input_dev = ts_dev->input;
|
||||||
unsigned int status, irqclr = 0;
|
unsigned int fsm, status, irqclr = 0;
|
||||||
unsigned int x = 0, y = 0;
|
unsigned int x = 0, y = 0;
|
||||||
unsigned int z1, z2, z;
|
unsigned int z1, z2, z;
|
||||||
unsigned int fsm;
|
|
||||||
|
|
||||||
status = titsc_readl(ts_dev, REG_IRQSTATUS);
|
status = titsc_readl(ts_dev, REG_RAWIRQSTATUS);
|
||||||
|
if (status & IRQENB_HW_PEN) {
|
||||||
|
ts_dev->pen_down = true;
|
||||||
|
titsc_writel(ts_dev, REG_IRQWAKEUP, 0x00);
|
||||||
|
titsc_writel(ts_dev, REG_IRQCLR, IRQENB_HW_PEN);
|
||||||
|
irqclr |= IRQENB_HW_PEN;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (status & IRQENB_PENUP) {
|
||||||
|
fsm = titsc_readl(ts_dev, REG_ADCFSM);
|
||||||
|
if (fsm == ADCFSM_STEPID) {
|
||||||
|
ts_dev->pen_down = false;
|
||||||
|
input_report_key(input_dev, BTN_TOUCH, 0);
|
||||||
|
input_report_abs(input_dev, ABS_PRESSURE, 0);
|
||||||
|
input_sync(input_dev);
|
||||||
|
} else {
|
||||||
|
ts_dev->pen_down = true;
|
||||||
|
}
|
||||||
|
irqclr |= IRQENB_PENUP;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (status & IRQENB_EOS)
|
||||||
|
irqclr |= IRQENB_EOS;
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* ADC and touchscreen share the IRQ line.
|
* ADC and touchscreen share the IRQ line.
|
||||||
* FIFO1 interrupts are used by ADC. Handle FIFO0 IRQs here only
|
* FIFO1 interrupts are used by ADC. Handle FIFO0 IRQs here only
|
||||||
|
@ -291,37 +324,11 @@ static irqreturn_t titsc_irq(int irq, void *dev)
|
||||||
}
|
}
|
||||||
irqclr |= IRQENB_FIFO0THRES;
|
irqclr |= IRQENB_FIFO0THRES;
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
|
||||||
* Time for sequencer to settle, to read
|
|
||||||
* correct state of the sequencer.
|
|
||||||
*/
|
|
||||||
udelay(SEQ_SETTLE);
|
|
||||||
|
|
||||||
status = titsc_readl(ts_dev, REG_RAWIRQSTATUS);
|
|
||||||
if (status & IRQENB_PENUP) {
|
|
||||||
/* Pen up event */
|
|
||||||
fsm = titsc_readl(ts_dev, REG_ADCFSM);
|
|
||||||
if (fsm == ADCFSM_STEPID) {
|
|
||||||
ts_dev->pen_down = false;
|
|
||||||
input_report_key(input_dev, BTN_TOUCH, 0);
|
|
||||||
input_report_abs(input_dev, ABS_PRESSURE, 0);
|
|
||||||
input_sync(input_dev);
|
|
||||||
} else {
|
|
||||||
ts_dev->pen_down = true;
|
|
||||||
}
|
|
||||||
irqclr |= IRQENB_PENUP;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (status & IRQENB_HW_PEN) {
|
|
||||||
|
|
||||||
titsc_writel(ts_dev, REG_IRQWAKEUP, 0x00);
|
|
||||||
titsc_writel(ts_dev, REG_IRQCLR, IRQENB_HW_PEN);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (irqclr) {
|
if (irqclr) {
|
||||||
titsc_writel(ts_dev, REG_IRQSTATUS, irqclr);
|
titsc_writel(ts_dev, REG_IRQSTATUS, irqclr);
|
||||||
am335x_tsc_se_set_cache(ts_dev->mfd_tscadc, ts_dev->step_mask);
|
if (status & IRQENB_EOS)
|
||||||
|
am335x_tsc_se_set_cache(ts_dev->mfd_tscadc,
|
||||||
|
ts_dev->step_mask);
|
||||||
return IRQ_HANDLED;
|
return IRQ_HANDLED;
|
||||||
}
|
}
|
||||||
return IRQ_NONE;
|
return IRQ_NONE;
|
||||||
|
@ -368,6 +375,23 @@ static int titsc_parse_dt(struct platform_device *pdev,
|
||||||
if (err < 0)
|
if (err < 0)
|
||||||
return err;
|
return err;
|
||||||
|
|
||||||
|
if (ts_dev->coordinate_readouts <= 0) {
|
||||||
|
dev_warn(&pdev->dev,
|
||||||
|
"invalid co-ordinate readouts, resetting it to 5\n");
|
||||||
|
ts_dev->coordinate_readouts = 5;
|
||||||
|
}
|
||||||
|
|
||||||
|
err = of_property_read_u32(node, "ti,charge-delay",
|
||||||
|
&ts_dev->charge_delay);
|
||||||
|
/*
|
||||||
|
* If ti,charge-delay value is not specified, then use
|
||||||
|
* CHARGEDLY_OPENDLY as the default value.
|
||||||
|
*/
|
||||||
|
if (err < 0) {
|
||||||
|
ts_dev->charge_delay = CHARGEDLY_OPENDLY;
|
||||||
|
dev_warn(&pdev->dev, "ti,charge-delay not specified\n");
|
||||||
|
}
|
||||||
|
|
||||||
return of_property_read_u32_array(node, "ti,wire-config",
|
return of_property_read_u32_array(node, "ti,wire-config",
|
||||||
ts_dev->config_inp, ARRAY_SIZE(ts_dev->config_inp));
|
ts_dev->config_inp, ARRAY_SIZE(ts_dev->config_inp));
|
||||||
}
|
}
|
||||||
|
@ -411,6 +435,7 @@ static int titsc_probe(struct platform_device *pdev)
|
||||||
}
|
}
|
||||||
|
|
||||||
titsc_writel(ts_dev, REG_IRQENABLE, IRQENB_FIFO0THRES);
|
titsc_writel(ts_dev, REG_IRQENABLE, IRQENB_FIFO0THRES);
|
||||||
|
titsc_writel(ts_dev, REG_IRQENABLE, IRQENB_EOS);
|
||||||
err = titsc_config_wires(ts_dev);
|
err = titsc_config_wires(ts_dev);
|
||||||
if (err) {
|
if (err) {
|
||||||
dev_err(&pdev->dev, "wrong i/p wire configuration\n");
|
dev_err(&pdev->dev, "wrong i/p wire configuration\n");
|
||||||
|
|
|
@ -119,7 +119,8 @@ struct input_mt_pos {
|
||||||
};
|
};
|
||||||
|
|
||||||
int input_mt_assign_slots(struct input_dev *dev, int *slots,
|
int input_mt_assign_slots(struct input_dev *dev, int *slots,
|
||||||
const struct input_mt_pos *pos, int num_pos);
|
const struct input_mt_pos *pos, int num_pos,
|
||||||
|
int dmax);
|
||||||
|
|
||||||
int input_mt_get_slot_by_key(struct input_dev *dev, int key);
|
int input_mt_get_slot_by_key(struct input_dev *dev, int key);
|
||||||
|
|
||||||
|
|
|
@ -52,6 +52,7 @@
|
||||||
|
|
||||||
/* IRQ enable */
|
/* IRQ enable */
|
||||||
#define IRQENB_HW_PEN BIT(0)
|
#define IRQENB_HW_PEN BIT(0)
|
||||||
|
#define IRQENB_EOS BIT(1)
|
||||||
#define IRQENB_FIFO0THRES BIT(2)
|
#define IRQENB_FIFO0THRES BIT(2)
|
||||||
#define IRQENB_FIFO0OVRRUN BIT(3)
|
#define IRQENB_FIFO0OVRRUN BIT(3)
|
||||||
#define IRQENB_FIFO0UNDRFLW BIT(4)
|
#define IRQENB_FIFO0UNDRFLW BIT(4)
|
||||||
|
@ -107,7 +108,7 @@
|
||||||
/* Charge delay */
|
/* Charge delay */
|
||||||
#define CHARGEDLY_OPEN_MASK (0x3FFFF << 0)
|
#define CHARGEDLY_OPEN_MASK (0x3FFFF << 0)
|
||||||
#define CHARGEDLY_OPEN(val) ((val) << 0)
|
#define CHARGEDLY_OPEN(val) ((val) << 0)
|
||||||
#define CHARGEDLY_OPENDLY CHARGEDLY_OPEN(1)
|
#define CHARGEDLY_OPENDLY CHARGEDLY_OPEN(0x400)
|
||||||
|
|
||||||
/* Control register */
|
/* Control register */
|
||||||
#define CNTRLREG_TSCSSENB BIT(0)
|
#define CNTRLREG_TSCSSENB BIT(0)
|
||||||
|
|
29
include/linux/platform_data/regulator-haptic.h
Normal file
29
include/linux/platform_data/regulator-haptic.h
Normal file
|
@ -0,0 +1,29 @@
|
||||||
|
/*
|
||||||
|
* Regulator Haptic Platform Data
|
||||||
|
*
|
||||||
|
* Copyright (c) 2014 Samsung Electronics Co., Ltd.
|
||||||
|
* Author: Jaewon Kim <jaewon02.kim@samsung.com>
|
||||||
|
* Author: Hyunhee Kim <hyunhee.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 version 2 as
|
||||||
|
* published by the Free Software Foundation.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef _REGULATOR_HAPTIC_H
|
||||||
|
#define _REGULATOR_HAPTIC_H
|
||||||
|
|
||||||
|
/*
|
||||||
|
* struct regulator_haptic_data - Platform device data
|
||||||
|
*
|
||||||
|
* @max_volt: maximum voltage value supplied to the haptic motor.
|
||||||
|
* <The unit of the voltage is a micro>
|
||||||
|
* @min_volt: minimum voltage value supplied to the haptic motor.
|
||||||
|
* <The unit of the voltage is a micro>
|
||||||
|
*/
|
||||||
|
struct regulator_haptic_data {
|
||||||
|
unsigned int max_volt;
|
||||||
|
unsigned int min_volt;
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif /* _REGULATOR_HAPTIC_H */
|
Loading…
Add table
Add a link
Reference in a new issue