mirror of https://gitee.com/openkylin/linux.git
Input: elan_i2c - add trackstick report
The Elan touchpads over I2C/SMBus also can handle a trackstick. Unfortunately, nothing tells us if the device supports trackstick (the information lies in the PS/2 node), so rely on device properties to determine whether to enable the trackstick node. Link: https://bugzilla.redhat.com/show_bug.cgi?id=1313939 Signed-off-by: Benjamin Tissoires <benjamin.tissoires@redhat.com> Acked-by: Rob Herring <robh@kernel.org> Acked-by: KT Liao <kt.liao@emc.com.tw> Signed-off-by: Dmitry Torokhov <dmitry.torokhov@gmail.com>
This commit is contained in:
parent
89f84b84d3
commit
559b3df76f
|
@ -14,6 +14,7 @@ Optional properties:
|
|||
- pinctrl-0: a phandle pointing to the pin settings for the device (see
|
||||
pinctrl binding [1]).
|
||||
- vcc-supply: a phandle for the regulator supplying 3.3V power.
|
||||
- elan,trackpoint: touchpad can support a trackpoint (boolean)
|
||||
|
||||
[0]: Documentation/devicetree/bindings/interrupt-controller/interrupts.txt
|
||||
[1]: Documentation/devicetree/bindings/pinctrl/pinctrl-bindings.txt
|
||||
|
|
|
@ -36,6 +36,7 @@
|
|||
#include <linux/jiffies.h>
|
||||
#include <linux/completion.h>
|
||||
#include <linux/of.h>
|
||||
#include <linux/property.h>
|
||||
#include <linux/regulator/consumer.h>
|
||||
#include <asm/unaligned.h>
|
||||
|
||||
|
@ -51,6 +52,7 @@
|
|||
#define ETP_MAX_FINGERS 5
|
||||
#define ETP_FINGER_DATA_LEN 5
|
||||
#define ETP_REPORT_ID 0x5D
|
||||
#define ETP_TP_REPORT_ID 0x5E
|
||||
#define ETP_REPORT_ID_OFFSET 2
|
||||
#define ETP_TOUCH_INFO_OFFSET 3
|
||||
#define ETP_FINGER_DATA_OFFSET 4
|
||||
|
@ -61,6 +63,7 @@
|
|||
struct elan_tp_data {
|
||||
struct i2c_client *client;
|
||||
struct input_dev *input;
|
||||
struct input_dev *tp_input; /* trackpoint input node */
|
||||
struct regulator *vcc;
|
||||
|
||||
const struct elan_transport_ops *ops;
|
||||
|
@ -930,6 +933,33 @@ static void elan_report_absolute(struct elan_tp_data *data, u8 *packet)
|
|||
input_sync(input);
|
||||
}
|
||||
|
||||
static void elan_report_trackpoint(struct elan_tp_data *data, u8 *report)
|
||||
{
|
||||
struct input_dev *input = data->tp_input;
|
||||
u8 *packet = &report[ETP_REPORT_ID_OFFSET + 1];
|
||||
int x, y;
|
||||
|
||||
if (!data->tp_input) {
|
||||
dev_warn_once(&data->client->dev,
|
||||
"received a trackpoint report while no trackpoint device has been created. Please report upstream.\n");
|
||||
return;
|
||||
}
|
||||
|
||||
input_report_key(input, BTN_LEFT, packet[0] & 0x01);
|
||||
input_report_key(input, BTN_RIGHT, packet[0] & 0x02);
|
||||
input_report_key(input, BTN_MIDDLE, packet[0] & 0x04);
|
||||
|
||||
if ((packet[3] & 0x0F) == 0x06) {
|
||||
x = packet[4] - (int)((packet[1] ^ 0x80) << 1);
|
||||
y = (int)((packet[2] ^ 0x80) << 1) - packet[5];
|
||||
|
||||
input_report_rel(input, REL_X, x);
|
||||
input_report_rel(input, REL_Y, y);
|
||||
}
|
||||
|
||||
input_sync(input);
|
||||
}
|
||||
|
||||
static irqreturn_t elan_isr(int irq, void *dev_id)
|
||||
{
|
||||
struct elan_tp_data *data = dev_id;
|
||||
|
@ -951,11 +981,17 @@ static irqreturn_t elan_isr(int irq, void *dev_id)
|
|||
if (error)
|
||||
goto out;
|
||||
|
||||
if (report[ETP_REPORT_ID_OFFSET] != ETP_REPORT_ID)
|
||||
switch (report[ETP_REPORT_ID_OFFSET]) {
|
||||
case ETP_REPORT_ID:
|
||||
elan_report_absolute(data, report);
|
||||
break;
|
||||
case ETP_TP_REPORT_ID:
|
||||
elan_report_trackpoint(data, report);
|
||||
break;
|
||||
default:
|
||||
dev_err(dev, "invalid report id data (%x)\n",
|
||||
report[ETP_REPORT_ID_OFFSET]);
|
||||
else
|
||||
elan_report_absolute(data, report);
|
||||
}
|
||||
|
||||
out:
|
||||
return IRQ_HANDLED;
|
||||
|
@ -966,6 +1002,36 @@ static irqreturn_t elan_isr(int irq, void *dev_id)
|
|||
* Elan initialization functions
|
||||
******************************************************************
|
||||
*/
|
||||
|
||||
static int elan_setup_trackpoint_input_device(struct elan_tp_data *data)
|
||||
{
|
||||
struct device *dev = &data->client->dev;
|
||||
struct input_dev *input;
|
||||
|
||||
input = devm_input_allocate_device(dev);
|
||||
if (!input)
|
||||
return -ENOMEM;
|
||||
|
||||
input->name = "Elan TrackPoint";
|
||||
input->id.bustype = BUS_I2C;
|
||||
input->id.vendor = ELAN_VENDOR_ID;
|
||||
input->id.product = data->product_id;
|
||||
input_set_drvdata(input, data);
|
||||
|
||||
input_set_capability(input, EV_REL, REL_X);
|
||||
input_set_capability(input, EV_REL, REL_Y);
|
||||
input_set_capability(input, EV_KEY, BTN_LEFT);
|
||||
input_set_capability(input, EV_KEY, BTN_RIGHT);
|
||||
input_set_capability(input, EV_KEY, BTN_MIDDLE);
|
||||
|
||||
__set_bit(INPUT_PROP_POINTER, input->propbit);
|
||||
__set_bit(INPUT_PROP_POINTING_STICK, input->propbit);
|
||||
|
||||
data->tp_input = input;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int elan_setup_input_device(struct elan_tp_data *data)
|
||||
{
|
||||
struct device *dev = &data->client->dev;
|
||||
|
@ -1140,6 +1206,12 @@ static int elan_probe(struct i2c_client *client,
|
|||
if (error)
|
||||
return error;
|
||||
|
||||
if (device_property_read_bool(&client->dev, "elan,trackpoint")) {
|
||||
error = elan_setup_trackpoint_input_device(data);
|
||||
if (error)
|
||||
return error;
|
||||
}
|
||||
|
||||
/*
|
||||
* Platform code (ACPI, DTS) should normally set up interrupt
|
||||
* for us, but in case it did not let's fall back to using falling
|
||||
|
@ -1177,6 +1249,16 @@ static int elan_probe(struct i2c_client *client,
|
|||
return error;
|
||||
}
|
||||
|
||||
if (data->tp_input) {
|
||||
error = input_register_device(data->tp_input);
|
||||
if (error) {
|
||||
dev_err(&client->dev,
|
||||
"failed to register TrackPoint input device: %d\n",
|
||||
error);
|
||||
return error;
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Systems using device tree should set up wakeup via DTS,
|
||||
* the rest will configure device as wakeup source by default.
|
||||
|
|
Loading…
Reference in New Issue