Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 559b3df7 authored by Benjamin Tissoires's avatar Benjamin Tissoires Committed by Dmitry Torokhov
Browse files

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: default avatarBenjamin Tissoires <benjamin.tissoires@redhat.com>
Acked-by: default avatarRob Herring <robh@kernel.org>
Acked-by: default avatarKT Liao <kt.liao@emc.com.tw>
Signed-off-by: default avatarDmitry Torokhov <dmitry.torokhov@gmail.com>
parent 89f84b84
Loading
Loading
Loading
Loading
+1 −0
Original line number Diff line number Diff line
@@ -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
+85 −3
Original line number Diff line number Diff line
@@ -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.