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-0: a phandle pointing to the pin settings for the device (see
|
||||||
pinctrl binding [1]).
|
pinctrl binding [1]).
|
||||||
- vcc-supply: a phandle for the regulator supplying 3.3V power.
|
- 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
|
[0]: Documentation/devicetree/bindings/interrupt-controller/interrupts.txt
|
||||||
[1]: Documentation/devicetree/bindings/pinctrl/pinctrl-bindings.txt
|
[1]: Documentation/devicetree/bindings/pinctrl/pinctrl-bindings.txt
|
||||||
|
@ -36,6 +36,7 @@
|
|||||||
#include <linux/jiffies.h>
|
#include <linux/jiffies.h>
|
||||||
#include <linux/completion.h>
|
#include <linux/completion.h>
|
||||||
#include <linux/of.h>
|
#include <linux/of.h>
|
||||||
|
#include <linux/property.h>
|
||||||
#include <linux/regulator/consumer.h>
|
#include <linux/regulator/consumer.h>
|
||||||
#include <asm/unaligned.h>
|
#include <asm/unaligned.h>
|
||||||
|
|
||||||
@ -51,6 +52,7 @@
|
|||||||
#define ETP_MAX_FINGERS 5
|
#define ETP_MAX_FINGERS 5
|
||||||
#define ETP_FINGER_DATA_LEN 5
|
#define ETP_FINGER_DATA_LEN 5
|
||||||
#define ETP_REPORT_ID 0x5D
|
#define ETP_REPORT_ID 0x5D
|
||||||
|
#define ETP_TP_REPORT_ID 0x5E
|
||||||
#define ETP_REPORT_ID_OFFSET 2
|
#define ETP_REPORT_ID_OFFSET 2
|
||||||
#define ETP_TOUCH_INFO_OFFSET 3
|
#define ETP_TOUCH_INFO_OFFSET 3
|
||||||
#define ETP_FINGER_DATA_OFFSET 4
|
#define ETP_FINGER_DATA_OFFSET 4
|
||||||
@ -61,6 +63,7 @@
|
|||||||
struct elan_tp_data {
|
struct elan_tp_data {
|
||||||
struct i2c_client *client;
|
struct i2c_client *client;
|
||||||
struct input_dev *input;
|
struct input_dev *input;
|
||||||
|
struct input_dev *tp_input; /* trackpoint input node */
|
||||||
struct regulator *vcc;
|
struct regulator *vcc;
|
||||||
|
|
||||||
const struct elan_transport_ops *ops;
|
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);
|
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)
|
static irqreturn_t elan_isr(int irq, void *dev_id)
|
||||||
{
|
{
|
||||||
struct elan_tp_data *data = 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)
|
if (error)
|
||||||
goto out;
|
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",
|
dev_err(dev, "invalid report id data (%x)\n",
|
||||||
report[ETP_REPORT_ID_OFFSET]);
|
report[ETP_REPORT_ID_OFFSET]);
|
||||||
else
|
}
|
||||||
elan_report_absolute(data, report);
|
|
||||||
|
|
||||||
out:
|
out:
|
||||||
return IRQ_HANDLED;
|
return IRQ_HANDLED;
|
||||||
@ -966,6 +1002,36 @@ out:
|
|||||||
* Elan initialization functions
|
* 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)
|
static int elan_setup_input_device(struct elan_tp_data *data)
|
||||||
{
|
{
|
||||||
struct device *dev = &data->client->dev;
|
struct device *dev = &data->client->dev;
|
||||||
@ -1140,6 +1206,12 @@ static int elan_probe(struct i2c_client *client,
|
|||||||
if (error)
|
if (error)
|
||||||
return 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
|
* Platform code (ACPI, DTS) should normally set up interrupt
|
||||||
* for us, but in case it did not let's fall back to using falling
|
* 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;
|
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,
|
* Systems using device tree should set up wakeup via DTS,
|
||||||
* the rest will configure device as wakeup source by default.
|
* the rest will configure device as wakeup source by default.
|
||||||
|
Loading…
x
Reference in New Issue
Block a user