forked from Minki/linux
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
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:
|
||||
- compatible: "allwinner,sun4i-a10-ts"
|
||||
- compatible: "allwinner,sun4i-a10-ts" or "allwinner,sun6i-a31-ts"
|
||||
- reg: mmio address range of the chip
|
||||
- interrupts: interrupt to which the chip is connected
|
||||
- #thermal-sensor-cells: shall be 0
|
||||
|
||||
Optional properties:
|
||||
- allwinner,ts-attached: boolean indicating that an actual touchscreen is
|
||||
@ -17,4 +18,5 @@ Example:
|
||||
reg = <0x01c25000 0x100>;
|
||||
interrupts = <29>;
|
||||
allwinner,ts-attached;
|
||||
#thermal-sensor-cells = <0>;
|
||||
};
|
||||
|
@ -28,6 +28,20 @@ Required properties:
|
||||
ti,adc-channels: List of analog inputs available for ADC.
|
||||
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:
|
||||
tscadc: tscadc@44e0d000 {
|
||||
compatible = "ti,am3359-tscadc";
|
||||
@ -36,6 +50,7 @@ Example:
|
||||
ti,x-plate-resistance = <200>;
|
||||
ti,coordiante-readouts = <5>;
|
||||
ti,wire-config = <0x00 0x11 0x22 0x33>;
|
||||
ti,charge-delay = <0x400>;
|
||||
};
|
||||
|
||||
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
|
||||
epson Seiko Epson Corp.
|
||||
est ESTeem Wireless Modems
|
||||
ettus NI Ettus Research
|
||||
eukrea Eukréa Electromatique
|
||||
everest Everest Semiconductor Co. Ltd.
|
||||
excito Excito
|
||||
|
15
MAINTAINERS
15
MAINTAINERS
@ -3479,6 +3479,14 @@ M: "Maciej W. Rozycki" <macro@linux-mips.org>
|
||||
S: Maintained
|
||||
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
|
||||
M: Antti Palosaari <crope@iki.fi>
|
||||
L: linux-media@vger.kernel.org
|
||||
@ -9281,6 +9289,13 @@ F: arch/m68k/sun3*/
|
||||
F: arch/m68k/include/asm/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
|
||||
M: Denis Kirjanov <kda@linux-powerpc.org>
|
||||
L: netdev@vger.kernel.org
|
||||
|
@ -648,6 +648,7 @@
|
||||
ti,x-plate-resistance = <200>;
|
||||
ti,coordinate-readouts = <5>;
|
||||
ti,wire-config = <0x00 0x11 0x22 0x33>;
|
||||
ti,charge-delay = <0x400>;
|
||||
};
|
||||
|
||||
adc {
|
||||
|
@ -86,19 +86,18 @@ static void tiadc_step_config(struct iio_dev *indio_dev)
|
||||
{
|
||||
struct tiadc_device *adc_dev = iio_priv(indio_dev);
|
||||
unsigned int stepconfig;
|
||||
int i, steps;
|
||||
int i, steps = 0;
|
||||
|
||||
/*
|
||||
* There are 16 configurable steps and 8 analog input
|
||||
* 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.
|
||||
* Channel would represent which analog input
|
||||
* needs to be given to ADC to digitalize data.
|
||||
*/
|
||||
|
||||
steps = TOTAL_STEPS - adc_dev->channels;
|
||||
if (iio_buffer_enabled(indio_dev))
|
||||
stepconfig = STEPCONFIG_AVG_16 | STEPCONFIG_FIFO1
|
||||
| STEPCONFIG_MODE_SWCNT;
|
||||
|
@ -62,26 +62,6 @@ struct evdev_client {
|
||||
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 */
|
||||
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;
|
||||
}
|
||||
|
||||
/* 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;
|
||||
ktime_t time;
|
||||
|
||||
@ -146,8 +124,6 @@ static void evdev_queue_syn_dropped(struct evdev_client *client)
|
||||
ev.code = SYN_DROPPED;
|
||||
ev.value = 0;
|
||||
|
||||
spin_lock_irqsave(&client->buffer_lock, flags);
|
||||
|
||||
client->buffer[client->head++] = ev;
|
||||
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->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);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
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);
|
||||
|
||||
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;
|
||||
|
||||
@ -311,9 +311,10 @@ static int adjust_dual(int *begin, int step, int *end, int eq)
|
||||
s = *p;
|
||||
|
||||
c = (f + s + 1) / 2;
|
||||
if (c == 0 || (c > 0 && !eq))
|
||||
if (c == 0 || (c > mu && (!eq || mu > 0)))
|
||||
return 0;
|
||||
if (s < 0)
|
||||
/* Improve convergence for positive matrices by penalizing overcovers */
|
||||
if (s < 0 && mu <= 0)
|
||||
c *= 2;
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
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;
|
||||
|
||||
for (k = 0; k < nrc; k++) {
|
||||
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;
|
||||
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)
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
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;
|
||||
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);
|
||||
for (p = pos; p != pos + num_pos; p++) {
|
||||
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
|
||||
* @pos: the position array to match
|
||||
* @num_pos: number of positions
|
||||
* @dmax: maximum ABS_MT_POSITION displacement (zero for infinite)
|
||||
*
|
||||
* Performs a best match against the current contacts and returns
|
||||
* the slot assignment list. New contacts are assigned to unused
|
||||
* 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.
|
||||
*/
|
||||
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;
|
||||
int mu = 2 * dmax * dmax;
|
||||
int nrc;
|
||||
|
||||
if (!mt || !mt->red)
|
||||
@ -413,8 +422,8 @@ int input_mt_assign_slots(struct input_dev *dev, int *slots,
|
||||
if (num_pos < 1)
|
||||
return 0;
|
||||
|
||||
nrc = input_mt_set_matrix(mt, pos, num_pos);
|
||||
find_reduced_matrix(mt->red, num_pos, nrc / num_pos, nrc);
|
||||
nrc = input_mt_set_matrix(mt, pos, num_pos, mu);
|
||||
find_reduced_matrix(mt->red, num_pos, nrc / num_pos, nrc, mu);
|
||||
input_mt_set_slots(mt, slots, num_pos);
|
||||
|
||||
return 0;
|
||||
|
@ -100,23 +100,24 @@ static unsigned int input_to_handler(struct input_handle *handle,
|
||||
struct input_value *end = vals;
|
||||
struct input_value *v;
|
||||
|
||||
for (v = vals; v != vals + count; v++) {
|
||||
if (handler->filter &&
|
||||
handler->filter(handle, v->type, v->code, v->value))
|
||||
continue;
|
||||
if (end != v)
|
||||
*end = *v;
|
||||
end++;
|
||||
if (handler->filter) {
|
||||
for (v = vals; v != vals + count; v++) {
|
||||
if (handler->filter(handle, v->type, v->code, v->value))
|
||||
continue;
|
||||
if (end != v)
|
||||
*end = *v;
|
||||
end++;
|
||||
}
|
||||
count = end - vals;
|
||||
}
|
||||
|
||||
count = end - vals;
|
||||
if (!count)
|
||||
return 0;
|
||||
|
||||
if (handler->events)
|
||||
handler->events(handle, vals, count);
|
||||
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);
|
||||
|
||||
return count;
|
||||
@ -143,8 +144,11 @@ static void input_pass_values(struct input_dev *dev,
|
||||
count = input_to_handler(handle, vals, count);
|
||||
} else {
|
||||
list_for_each_entry_rcu(handle, &dev->h_list, d_node)
|
||||
if (handle->open)
|
||||
if (handle->open) {
|
||||
count = input_to_handler(handle, vals, count);
|
||||
if (!count)
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
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);
|
||||
|
||||
/* trigger auto repeat for key events */
|
||||
for (v = vals; v != vals + count; v++) {
|
||||
if (v->type == EV_KEY && v->value != 2) {
|
||||
if (v->value)
|
||||
input_start_autorepeat(dev, v->code);
|
||||
else
|
||||
input_stop_autorepeat(dev);
|
||||
if (test_bit(EV_REP, dev->evbit) && test_bit(EV_KEY, dev->evbit)) {
|
||||
for (v = vals; v != vals + count; v++) {
|
||||
if (v->type == EV_KEY && v->value != 2) {
|
||||
if (v->value)
|
||||
input_start_autorepeat(dev, v->code);
|
||||
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
|
||||
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
|
||||
tristate "TI DaVinci Key Scan"
|
||||
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_STOWAWAY) += stowaway.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_TC3589X) += tc3589x-keypad.o
|
||||
obj-$(CONFIG_KEYBOARD_TEGRA) += tegra-kbc.o
|
||||
|
@ -170,7 +170,7 @@ static unsigned char atakbd_keycode[0x72] = { /* American layout */
|
||||
[93] = KEY_KPASTERISK,
|
||||
[94] = KEY_KPPLUS,
|
||||
[95] = KEY_HELP,
|
||||
[96] = KEY_BACKSLASH, /* FIXME: '<' */
|
||||
[96] = KEY_102ND,
|
||||
[97] = KEY_KPASTERISK, /* FIXME */
|
||||
[98] = KEY_KPSLASH,
|
||||
[99] = KEY_KPLEFTPAREN,
|
||||
|
@ -370,7 +370,6 @@ static struct i2c_driver cap11xx_i2c_driver = {
|
||||
|
||||
module_i2c_driver(cap11xx_i2c_driver);
|
||||
|
||||
MODULE_ALIAS("platform:cap11xx");
|
||||
MODULE_DESCRIPTION("Microchip CAP11XX driver");
|
||||
MODULE_AUTHOR("Daniel Mack <linux@zonque.org>");
|
||||
MODULE_LICENSE("GPL v2");
|
||||
|
@ -448,8 +448,7 @@ static int imx_keypad_probe(struct platform_device *pdev)
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
keypad = devm_kzalloc(&pdev->dev, sizeof(struct imx_keypad),
|
||||
GFP_KERNEL);
|
||||
keypad = devm_kzalloc(&pdev->dev, sizeof(*keypad), GFP_KERNEL);
|
||||
if (!keypad) {
|
||||
dev_err(&pdev->dev, "not enough memory for driver data\n");
|
||||
return -ENOMEM;
|
||||
|
@ -20,6 +20,7 @@
|
||||
#include <linux/module.h>
|
||||
#include <linux/interrupt.h>
|
||||
#include <linux/input.h>
|
||||
#include <linux/io.h>
|
||||
#include <linux/device.h>
|
||||
#include <linux/platform_device.h>
|
||||
#include <linux/clk.h>
|
||||
@ -28,10 +29,6 @@
|
||||
#include <linux/slab.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>
|
||||
/*
|
||||
* 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
|
||||
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
|
||||
tristate "PC Speaker support"
|
||||
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
|
||||
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
|
||||
tristate "Retu Power button Driver"
|
||||
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
|
||||
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
|
||||
tristate "TWL4030 Power button Driver"
|
||||
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_DA9055_ONKEY) += da9055_onkey.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_DRV2667_HAPTICS) += drv2667.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_PWM_BEEPER) += pwm-beeper.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_AXP20X_PEK) += axp20x-pek.o
|
||||
obj-$(CONFIG_INPUT_GPIO_ROTARY_ENCODER) += rotary_encoder.o
|
||||
obj-$(CONFIG_INPUT_SGI_BTNS) += sgi_btns.o
|
||||
obj-$(CONFIG_INPUT_SIRFSOC_ONKEY) += sirfsoc-onkey.o
|
||||
obj-$(CONFIG_INPUT_SOC_BUTTON_ARRAY) += soc_button_array.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_VIBRA) += twl4030-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_ALIAS("platform:drv260x-haptics");
|
||||
MODULE_DESCRIPTION("TI DRV260x haptics driver");
|
||||
MODULE_LICENSE("GPL");
|
||||
MODULE_AUTHOR("Dan Murphy <dmurphy@ti.com>");
|
||||
|
@ -492,7 +492,6 @@ static struct i2c_driver drv2667_driver = {
|
||||
};
|
||||
module_i2c_driver(drv2667_driver);
|
||||
|
||||
MODULE_ALIAS("platform:drv2667-haptics");
|
||||
MODULE_DESCRIPTION("TI DRV2667 haptics driver");
|
||||
MODULE_LICENSE("GPL");
|
||||
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
|
||||
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
|
||||
entries. For further information,
|
||||
see <file:Documentation/input/elantech.txt>.
|
||||
|
||||
If unsure, say N.
|
||||
|
||||
config MOUSE_PS2_SENTELIC
|
||||
bool "Sentelic Finger Sensing Pad PS/2 protocol extension"
|
||||
depends on MOUSE_PS2
|
||||
@ -146,6 +139,16 @@ config MOUSE_PS2_OLPC
|
||||
|
||||
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
|
||||
tristate "Serial mouse"
|
||||
select SERIO
|
||||
@ -206,6 +209,7 @@ config MOUSE_BCM5974
|
||||
config MOUSE_CYAPA
|
||||
tristate "Cypress APA I2C Trackpad support"
|
||||
depends on I2C
|
||||
select CRC_ITU_T
|
||||
help
|
||||
This driver adds support for Cypress All Points Addressable (APA)
|
||||
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_ATARI) += atarimouse.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_GPIO) += gpio_mouse.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_VSXXXAA) += vsxxxaa.o
|
||||
|
||||
cyapatp-objs := cyapa.o cyapa_gen3.o cyapa_gen5.o
|
||||
psmouse-objs := psmouse-base.o synaptics.o focaltech.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;
|
||||
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++)
|
||||
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;
|
||||
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 */
|
||||
if (!(packet[0] & 0x40)) {
|
||||
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);
|
||||
|
||||
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_RIGHT, f->ts_right);
|
||||
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) {
|
||||
/* 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;
|
||||
}
|
||||
|
||||
/* Trackpoint packet */
|
||||
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;
|
||||
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
|
||||
* 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);
|
||||
}
|
||||
|
||||
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);
|
||||
dev2->phys = priv->phys;
|
||||
dev2->name = (priv->flags & ALPS_DUALPOINT) ?
|
||||
"DualPoint Stick" : "ALPS PS/2 Device";
|
||||
dev2->id.bustype = BUS_I8042;
|
||||
dev2->id.vendor = 0x0002;
|
||||
dev2->id.product = PSMOUSE_ALPS;
|
||||
dev2->id.version = 0x0000;
|
||||
dev2->dev.parent = &psmouse->ps2dev.serio->dev;
|
||||
|
||||
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];
|
||||
}
|
||||
|
||||
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++)
|
||||
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;
|
||||
}
|
||||
|
||||
input_mt_assign_slots(input, slots, pos, n);
|
||||
input_mt_assign_slots(input, slots, pos, n, 0);
|
||||
|
||||
for (i = 0; i < n; i++) {
|
||||
contact = &report_data.contacts[i];
|
||||
|
@ -4,7 +4,6 @@
|
||||
* Copyright (c) 2013 ELAN Microelectronics Corp.
|
||||
*
|
||||
* Author: 林政維 (Duson Lin) <dusonlin@emc.com.tw>
|
||||
* Version: 1.5.5
|
||||
*
|
||||
* Based on cyapa driver:
|
||||
* copyright (c) 2011-2012 Cypress Semiconductor, Inc.
|
||||
@ -33,8 +32,9 @@
|
||||
#define ETP_FW_IAP_PAGE_ERR (1 << 5)
|
||||
#define ETP_FW_IAP_INTF_ERR (1 << 4)
|
||||
#define ETP_FW_PAGE_SIZE 64
|
||||
#define ETP_FW_PAGE_COUNT 768
|
||||
#define ETP_FW_SIZE (ETP_FW_PAGE_SIZE * ETP_FW_PAGE_COUNT)
|
||||
#define ETP_FW_VAILDPAGE_COUNT 768
|
||||
#define ETP_FW_SIGNATURE_SIZE 6
|
||||
#define ETP_FW_SIGNATURE_ADDRESS 0xBFFA
|
||||
|
||||
struct i2c_client;
|
||||
struct completion;
|
||||
|
@ -4,7 +4,7 @@
|
||||
* Copyright (c) 2013 ELAN Microelectronics Corp.
|
||||
*
|
||||
* Author: 林政維 (Duson Lin) <dusonlin@emc.com.tw>
|
||||
* Version: 1.5.5
|
||||
* Version: 1.5.6
|
||||
*
|
||||
* Based on cyapa driver:
|
||||
* copyright (c) 2011-2012 Cypress Semiconductor, Inc.
|
||||
@ -40,7 +40,7 @@
|
||||
#include "elan_i2c.h"
|
||||
|
||||
#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_MAX_PRESSURE 255
|
||||
#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]);
|
||||
|
||||
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;
|
||||
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,
|
||||
const char *buf, size_t count)
|
||||
{
|
||||
struct i2c_client *client = to_i2c_client(dev);
|
||||
struct elan_tp_data *data = i2c_get_clientdata(client);
|
||||
struct elan_tp_data *data = dev_get_drvdata(dev);
|
||||
const struct firmware *fw;
|
||||
int error;
|
||||
const u8 *fw_signature;
|
||||
static const u8 signature[] = {0xAA, 0x55, 0xCC, 0x33, 0xFF, 0xFF};
|
||||
|
||||
error = request_firmware(&fw, ETP_FW_NAME, dev);
|
||||
if (error) {
|
||||
@ -446,10 +447,12 @@ static ssize_t elan_sysfs_update_fw(struct device *dev,
|
||||
return error;
|
||||
}
|
||||
|
||||
/* Firmware must be exactly PAGE_NUM * PAGE_SIZE bytes */
|
||||
if (fw->size != ETP_FW_SIZE) {
|
||||
dev_err(dev, "invalid firmware size = %zu, expected %d.\n",
|
||||
fw->size, ETP_FW_SIZE);
|
||||
/* Firmware file must match signature data */
|
||||
fw_signature = &fw->data[ETP_FW_SIGNATURE_ADDRESS];
|
||||
if (memcmp(fw_signature, signature, sizeof(signature)) != 0) {
|
||||
dev_err(dev, "signature mismatch (expected %*ph, got %*ph)\n",
|
||||
(int)sizeof(signature), signature,
|
||||
(int)sizeof(signature), fw_signature);
|
||||
error = -EBADF;
|
||||
goto out_release_fw;
|
||||
}
|
||||
|
@ -4,7 +4,6 @@
|
||||
* Copyright (c) 2013 ELAN Microelectronics Corp.
|
||||
*
|
||||
* Author: 林政維 (Duson Lin) <dusonlin@emc.com.tw>
|
||||
* Version: 1.5.5
|
||||
*
|
||||
* Based on cyapa driver:
|
||||
* copyright (c) 2011-2012 Cypress Semiconductor, Inc.
|
||||
|
@ -4,7 +4,6 @@
|
||||
* Copyright (c) 2013 ELAN Microelectronics Corp.
|
||||
*
|
||||
* Author: 林政維 (Duson Lin) <dusonlin@emc.com.tw>
|
||||
* Version: 1.5.5
|
||||
*
|
||||
* Based on cyapa driver:
|
||||
* copyright (c) 2011-2012 Cypress Semiconductor, Inc.
|
||||
@ -71,7 +70,7 @@ static int elan_smbus_initialize(struct i2c_client *client)
|
||||
|
||||
/* compare hello packet */
|
||||
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);
|
||||
return -ENXIO;
|
||||
}
|
||||
|
@ -2,6 +2,7 @@
|
||||
* Focaltech TouchPad PS/2 mouse driver
|
||||
*
|
||||
* 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
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
@ -13,15 +14,14 @@
|
||||
* 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/libps2.h>
|
||||
#include <linux/input/mt.h>
|
||||
#include <linux/serio.h>
|
||||
#include <linux/slab.h>
|
||||
#include "psmouse.h"
|
||||
#include "focaltech.h"
|
||||
|
||||
static const char * const focaltech_pnp_ids[] = {
|
||||
"FLT0101",
|
||||
@ -30,6 +30,12 @@ static const char * const focaltech_pnp_ids[] = {
|
||||
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)
|
||||
{
|
||||
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) {
|
||||
psmouse->vendor = "FocalTech";
|
||||
psmouse->name = "FocalTech Touchpad in mouse emulation mode";
|
||||
psmouse->name = "FocalTech Touchpad";
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int focaltech_init(struct psmouse *psmouse)
|
||||
static void focaltech_reset(struct psmouse *psmouse)
|
||||
{
|
||||
ps2_command(&psmouse->ps2dev, NULL, PSMOUSE_CMD_RESET_DIS);
|
||||
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;
|
||||
}
|
||||
|
||||
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
|
||||
*
|
||||
* 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
|
||||
* 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_init(struct psmouse *psmouse);
|
||||
bool focaltech_supported(void);
|
||||
|
||||
#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 */
|
||||
if (psmouse_do_detect(focaltech_detect, psmouse, set_properties) == 0) {
|
||||
if (!set_properties || focaltech_init(psmouse) == 0) {
|
||||
/*
|
||||
* Not supported yet, use bare protocol.
|
||||
* Note that we need to also restrict
|
||||
* psmouse_max_proto so that psmouse_initialize()
|
||||
* does not try to reset rate and resolution,
|
||||
* because even that upsets the device.
|
||||
*/
|
||||
psmouse_max_proto = PSMOUSE_PS2;
|
||||
return PSMOUSE_PS2;
|
||||
if (max_proto > PSMOUSE_IMEX) {
|
||||
if (!set_properties || focaltech_init(psmouse) == 0) {
|
||||
if (focaltech_supported())
|
||||
return PSMOUSE_FOCALTECH;
|
||||
/*
|
||||
* Note that we need to also restrict
|
||||
* psmouse_max_proto so that psmouse_initialize()
|
||||
* does not try to reset rate and resolution,
|
||||
* because even that upsets the device.
|
||||
*/
|
||||
psmouse_max_proto = PSMOUSE_PS2;
|
||||
return PSMOUSE_PS2;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -1063,6 +1066,15 @@ static const struct psmouse_protocol psmouse_protocols[] = {
|
||||
.alias = "cortps",
|
||||
.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,
|
||||
.name = "auto",
|
||||
|
@ -96,6 +96,7 @@ enum psmouse_type {
|
||||
PSMOUSE_FSP,
|
||||
PSMOUSE_SYNAPTICS_RELATIVE,
|
||||
PSMOUSE_CYPRESS,
|
||||
PSMOUSE_FOCALTECH,
|
||||
PSMOUSE_AUTO /* This one should always be last */
|
||||
};
|
||||
|
||||
|
@ -67,6 +67,9 @@
|
||||
#define X_MAX_POSITIVE 8176
|
||||
#define Y_MAX_POSITIVE 8176
|
||||
|
||||
/* maximum ABS_MT_POSITION displacement (in mm) */
|
||||
#define DMAX 10
|
||||
|
||||
/*****************************************************************************
|
||||
* Stuff we need even when we do not want native Synaptics support
|
||||
****************************************************************************/
|
||||
@ -575,14 +578,6 @@ static void synaptics_pt_create(struct psmouse *psmouse)
|
||||
* 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[],
|
||||
struct synaptics_data *priv,
|
||||
struct synaptics_hw_state *hw)
|
||||
@ -601,16 +596,13 @@ static void synaptics_parse_agm(const unsigned char buf[],
|
||||
break;
|
||||
|
||||
case 2:
|
||||
/* AGM-CONTACT packet: (count, sgm, agm) */
|
||||
synaptics_mt_state_set(&agm->mt_state, buf[1], buf[2], buf[4]);
|
||||
/* AGM-CONTACT packet: we are only interested in the count */
|
||||
priv->agm_count = buf[1];
|
||||
break;
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
/* Record that at least one AGM has been received since last SGM */
|
||||
priv->agm_pending = true;
|
||||
}
|
||||
|
||||
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));
|
||||
}
|
||||
|
||||
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,
|
||||
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 synaptics_data *priv = psmouse->private;
|
||||
struct synaptics_hw_state *agm = &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 };
|
||||
const struct synaptics_hw_state *hw[2] = { sgm, &priv->agm };
|
||||
struct input_mt_pos pos[2];
|
||||
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);
|
||||
}
|
||||
|
||||
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++) {
|
||||
input_mt_slot(dev, slot[i]);
|
||||
@ -1216,7 +824,11 @@ static void synaptics_profile_sensor_process(struct psmouse *psmouse,
|
||||
}
|
||||
|
||||
input_mt_drop_unused(dev);
|
||||
|
||||
/* 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, num_fingers);
|
||||
|
||||
synaptics_report_buttons(psmouse, sgm);
|
||||
@ -1224,6 +836,30 @@ static void synaptics_profile_sensor_process(struct psmouse *psmouse,
|
||||
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
|
||||
*/
|
||||
@ -1288,7 +924,7 @@ static void synaptics_process_packet(struct psmouse *psmouse)
|
||||
}
|
||||
|
||||
if (cr48_profile_sensor) {
|
||||
synaptics_profile_sensor_process(psmouse, &hw, num_fingers);
|
||||
synaptics_report_mt_data(psmouse, &hw, num_fingers);
|
||||
return;
|
||||
}
|
||||
|
||||
@ -1445,7 +1081,7 @@ static void set_input_params(struct psmouse *psmouse,
|
||||
ABS_MT_POSITION_Y);
|
||||
/* Image sensors can report per-contact pressure */
|
||||
input_set_abs_params(dev, ABS_MT_PRESSURE, 0, 255, 0, 0);
|
||||
input_mt_init_slots(dev, 2, INPUT_MT_POINTER);
|
||||
input_mt_init_slots(dev, 2, INPUT_MT_POINTER | INPUT_MT_TRACK);
|
||||
|
||||
/* Image sensors can signal 4 and 5 finger clicks */
|
||||
__set_bit(BTN_TOOL_QUADTAP, dev->keybit);
|
||||
|
@ -118,16 +118,6 @@
|
||||
/* amount to fuzz position data when touchpad reports reduced filtering */
|
||||
#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)
|
||||
*/
|
||||
@ -143,9 +133,6 @@ struct synaptics_hw_state {
|
||||
unsigned int down:1;
|
||||
unsigned char ext_buttons;
|
||||
signed char scroll;
|
||||
|
||||
/* As reported in last AGM-CONTACT packets */
|
||||
struct synaptics_mt_state mt_state;
|
||||
};
|
||||
|
||||
struct synaptics_data {
|
||||
@ -170,15 +157,12 @@ struct synaptics_data {
|
||||
|
||||
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
|
||||
* contains position data for a second contact, at half resolution.
|
||||
*/
|
||||
struct synaptics_hw_state agm;
|
||||
bool agm_pending; /* new AGM packet received */
|
||||
unsigned int agm_count; /* finger count reported by agm */
|
||||
|
||||
/* ForcePad handling */
|
||||
unsigned long press_start;
|
||||
|
@ -281,4 +281,14 @@ config HYPERV_KEYBOARD
|
||||
To compile this driver as a module, choose M here: the module will
|
||||
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
|
||||
|
@ -29,3 +29,4 @@ obj-$(CONFIG_SERIO_ARC_PS2) += arc_ps2.o
|
||||
obj-$(CONFIG_SERIO_APBPS2) += apbps2.o
|
||||
obj-$(CONFIG_SERIO_OLPC_APSP) += olpc_apsp.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_DESCRIPTION("HP GSC PS2 port driver");
|
||||
MODULE_LICENSE("GPL");
|
||||
MODULE_DEVICE_TABLE(parisc, gscps2_device_tbl);
|
||||
|
||||
#define PFX "gscps2.c: "
|
||||
|
||||
@ -439,6 +438,7 @@ static struct parisc_device_id gscps2_device_tbl[] = {
|
||||
#endif
|
||||
{ 0, } /* 0 terminated list */
|
||||
};
|
||||
MODULE_DEVICE_TABLE(parisc, gscps2_device_tbl);
|
||||
|
||||
static struct parisc_driver parisc_ps2_driver = {
|
||||
.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/unaligned.h>
|
||||
#include <asm/byteorder.h>
|
||||
|
||||
#include <linux/bitops.h>
|
||||
|
||||
#include <linux/usb/input.h>
|
||||
|
||||
@ -614,7 +614,6 @@ static void gtco_urb_callback(struct urb *urbinfo)
|
||||
struct input_dev *inputdev;
|
||||
int rc;
|
||||
u32 val = 0;
|
||||
s8 valsigned = 0;
|
||||
char le_buffer[2];
|
||||
|
||||
inputdev = device->inputdevice;
|
||||
@ -665,20 +664,11 @@ static void gtco_urb_callback(struct urb *urbinfo)
|
||||
/* Fall thru */
|
||||
case 4:
|
||||
/* Tilt */
|
||||
input_report_abs(inputdev, ABS_TILT_X,
|
||||
sign_extend32(device->buffer[6], 6));
|
||||
|
||||
/* Sign extend these 7 bit numbers. */
|
||||
if (device->buffer[6] & 0x40)
|
||||
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);
|
||||
input_report_abs(inputdev, ABS_TILT_Y,
|
||||
sign_extend32(device->buffer[7], 6));
|
||||
|
||||
/* Fall thru */
|
||||
case 2:
|
||||
|
@ -33,10 +33,8 @@
|
||||
#include <linux/delay.h>
|
||||
#include <linux/uaccess.h>
|
||||
#include <linux/buffer_head.h>
|
||||
#include <linux/version.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/firmware.h>
|
||||
#include <linux/version.h>
|
||||
#include <linux/input/mt.h>
|
||||
#include <linux/acpi.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;
|
||||
}
|
||||
|
||||
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++) {
|
||||
|
@ -34,6 +34,7 @@
|
||||
|
||||
#include <linux/err.h>
|
||||
#include <linux/hwmon.h>
|
||||
#include <linux/thermal.h>
|
||||
#include <linux/init.h>
|
||||
#include <linux/input.h>
|
||||
#include <linux/interrupt.h>
|
||||
@ -71,6 +72,9 @@
|
||||
#define TP_ADC_SELECT(x) ((x) << 3)
|
||||
#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 */
|
||||
#define TP_SENSITIVE_ADJUST(x) ((x) << 28) /* 4 bits */
|
||||
#define TP_MODE_SELECT(x) ((x) << 26) /* 2 bits */
|
||||
@ -107,10 +111,13 @@
|
||||
struct sun4i_ts_data {
|
||||
struct device *dev;
|
||||
struct input_dev *input;
|
||||
struct thermal_zone_device *tz;
|
||||
void __iomem *base;
|
||||
unsigned int irq;
|
||||
bool ignore_fifo_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)
|
||||
@ -180,16 +187,38 @@ static void sun4i_ts_close(struct input_dev *dev)
|
||||
writel(TEMP_IRQ_EN(1), ts->base + TP_INT_FIFOC);
|
||||
}
|
||||
|
||||
static ssize_t show_temp(struct device *dev, struct device_attribute *devattr,
|
||||
char *buf)
|
||||
static int sun4i_get_temp(const struct sun4i_ts_data *ts, long *temp)
|
||||
{
|
||||
struct sun4i_ts_data *ts = dev_get_drvdata(dev);
|
||||
|
||||
/* No temp_data until the first irq */
|
||||
if (ts->temp_data == -1)
|
||||
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,
|
||||
@ -215,6 +244,7 @@ static int sun4i_ts_probe(struct platform_device *pdev)
|
||||
struct device_node *np = dev->of_node;
|
||||
struct device *hwmon;
|
||||
int error;
|
||||
u32 reg;
|
||||
bool ts_attached;
|
||||
|
||||
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->ignore_fifo_data = true;
|
||||
ts->temp_data = -1;
|
||||
if (of_device_is_compatible(np, "allwinner,sun6i-a31-ts")) {
|
||||
/* Allwinner SDK has temperature = -271 + (value / 6) (C) */
|
||||
ts->temp_offset = 1626;
|
||||
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");
|
||||
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
|
||||
* finally enable tp mode.
|
||||
*/
|
||||
writel(STYLUS_UP_DEBOUN(5) | STYLUS_UP_DEBOUN_EN(1) | TP_MODE_EN(1),
|
||||
ts->base + TP_CTRL1);
|
||||
reg = STYLUS_UP_DEBOUN(5) | STYLUS_UP_DEBOUN_EN(1);
|
||||
if (of_device_is_compatible(np, "allwinner,sun4i-a10-ts"))
|
||||
reg |= TP_MODE_EN(1);
|
||||
else
|
||||
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",
|
||||
ts, sun4i_ts_groups);
|
||||
if (IS_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);
|
||||
|
||||
if (ts_attached) {
|
||||
error = input_register_device(ts->input);
|
||||
if (error) {
|
||||
writel(0, ts->base + TP_INT_FIFOC);
|
||||
thermal_zone_of_sensor_unregister(ts->dev, ts->tz);
|
||||
return error;
|
||||
}
|
||||
}
|
||||
@ -310,6 +373,8 @@ static int sun4i_ts_remove(struct platform_device *pdev)
|
||||
if (ts->input)
|
||||
input_unregister_device(ts->input);
|
||||
|
||||
thermal_zone_of_sensor_unregister(ts->dev, ts->tz);
|
||||
|
||||
/* Deactivate all IRQs */
|
||||
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[] = {
|
||||
{ .compatible = "allwinner,sun4i-a10-ts", },
|
||||
{ .compatible = "allwinner,sun6i-a31-ts", },
|
||||
{ /* sentinel */ }
|
||||
};
|
||||
MODULE_DEVICE_TABLE(of, sun4i_ts_of_match);
|
||||
|
@ -26,6 +26,7 @@
|
||||
#include <linux/delay.h>
|
||||
#include <linux/of.h>
|
||||
#include <linux/of_device.h>
|
||||
#include <linux/sort.h>
|
||||
|
||||
#include <linux/mfd/ti_am335x_tscadc.h>
|
||||
|
||||
@ -52,6 +53,7 @@ struct titsc {
|
||||
u32 bit_xp, bit_xn, bit_yp, bit_yn;
|
||||
u32 inp_xp, inp_xn, inp_yp, inp_yn;
|
||||
u32 step_mask;
|
||||
u32 charge_delay;
|
||||
};
|
||||
|
||||
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;
|
||||
int i;
|
||||
int end_step;
|
||||
int end_step, first_step, tsc_steps;
|
||||
u32 stepenable;
|
||||
|
||||
config = STEPCONFIG_MODE_HWSYNC |
|
||||
@ -140,9 +142,11 @@ static void titsc_step_config(struct titsc *ts_dev)
|
||||
break;
|
||||
}
|
||||
|
||||
/* 1 … coordinate_readouts is for X */
|
||||
end_step = ts_dev->coordinate_readouts;
|
||||
for (i = 0; i < end_step; i++) {
|
||||
tsc_steps = ts_dev->coordinate_readouts * 2 + 2;
|
||||
first_step = TOTAL_STEPS - tsc_steps;
|
||||
/* 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_STEPDELAY(i), STEPCONFIG_OPENDLY);
|
||||
}
|
||||
@ -164,22 +168,20 @@ static void titsc_step_config(struct titsc *ts_dev)
|
||||
break;
|
||||
}
|
||||
|
||||
/* coordinate_readouts … coordinate_readouts * 2 is for Y */
|
||||
end_step = ts_dev->coordinate_readouts * 2;
|
||||
for (i = ts_dev->coordinate_readouts; i < end_step; i++) {
|
||||
/* 1 ... coordinate_readouts is for Y */
|
||||
end_step = first_step + ts_dev->coordinate_readouts;
|
||||
for (i = first_step; i < end_step; i++) {
|
||||
titsc_writel(ts_dev, REG_STEPCONFIG(i), config);
|
||||
titsc_writel(ts_dev, REG_STEPDELAY(i), STEPCONFIG_OPENDLY);
|
||||
}
|
||||
|
||||
/* Charge step configuration */
|
||||
config = ts_dev->bit_xp | ts_dev->bit_yn |
|
||||
STEPCHARGE_RFP_XPUL | STEPCHARGE_RFM_XNUR |
|
||||
STEPCHARGE_INM_AN1 | STEPCHARGE_INP(ts_dev->inp_yp);
|
||||
/* Make CHARGECONFIG same as IDLECONFIG */
|
||||
|
||||
config = titsc_readl(ts_dev, REG_IDLECONFIG);
|
||||
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 |
|
||||
STEPCONFIG_AVG_16 | ts_dev->bit_yp |
|
||||
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),
|
||||
STEPCONFIG_OPENDLY);
|
||||
|
||||
/* The steps1 … end and bit 0 for TS_Charge */
|
||||
stepenable = (1 << (end_step + 2)) - 1;
|
||||
/* The steps end ... end - readouts * 2 + 2 and bit 0 for TS_Charge */
|
||||
stepenable = 1;
|
||||
for (i = 0; i < tsc_steps; i++)
|
||||
stepenable |= 1 << (first_step + i + 1);
|
||||
|
||||
ts_dev->step_mask = stepenable;
|
||||
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,
|
||||
u32 *x, u32 *y, u32 *z1, u32 *z2)
|
||||
{
|
||||
unsigned int fifocount = titsc_readl(ts_dev, REG_FIFO0CNT);
|
||||
unsigned int prev_val_x = ~0, prev_val_y = ~0;
|
||||
unsigned int prev_diff_x = ~0, prev_diff_y = ~0;
|
||||
unsigned int read, diff;
|
||||
unsigned int i, channel;
|
||||
unsigned int yvals[7], xvals[7];
|
||||
unsigned int i, xsum = 0, ysum = 0;
|
||||
unsigned int creads = ts_dev->coordinate_readouts;
|
||||
|
||||
*z1 = *z2 = 0;
|
||||
if (fifocount % (creads * 2 + 2))
|
||||
fifocount -= fifocount % (creads * 2 + 2);
|
||||
/*
|
||||
* 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;
|
||||
}
|
||||
for (i = 0; i < creads; i++) {
|
||||
yvals[i] = titsc_readl(ts_dev, REG_FIFO0);
|
||||
yvals[i] &= 0xfff;
|
||||
}
|
||||
|
||||
*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)
|
||||
{
|
||||
struct titsc *ts_dev = dev;
|
||||
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 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.
|
||||
* 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;
|
||||
}
|
||||
|
||||
/*
|
||||
* 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) {
|
||||
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_NONE;
|
||||
@ -368,6 +375,23 @@ static int titsc_parse_dt(struct platform_device *pdev,
|
||||
if (err < 0)
|
||||
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",
|
||||
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_EOS);
|
||||
err = titsc_config_wires(ts_dev);
|
||||
if (err) {
|
||||
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,
|
||||
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);
|
||||
|
||||
|
@ -52,6 +52,7 @@
|
||||
|
||||
/* IRQ enable */
|
||||
#define IRQENB_HW_PEN BIT(0)
|
||||
#define IRQENB_EOS BIT(1)
|
||||
#define IRQENB_FIFO0THRES BIT(2)
|
||||
#define IRQENB_FIFO0OVRRUN BIT(3)
|
||||
#define IRQENB_FIFO0UNDRFLW BIT(4)
|
||||
@ -107,7 +108,7 @@
|
||||
/* Charge delay */
|
||||
#define CHARGEDLY_OPEN_MASK (0x3FFFF << 0)
|
||||
#define CHARGEDLY_OPEN(val) ((val) << 0)
|
||||
#define CHARGEDLY_OPENDLY CHARGEDLY_OPEN(1)
|
||||
#define CHARGEDLY_OPENDLY CHARGEDLY_OPEN(0x400)
|
||||
|
||||
/* Control register */
|
||||
#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…
Reference in New Issue
Block a user