192 lines
5.4 KiB
Diff
192 lines
5.4 KiB
Diff
From 72fb4765ac9eea56a94282f29cdc70706f21494e Mon Sep 17 00:00:00 2001
|
|
From: Stephen Just <stephenjust@gmail.com>
|
|
Date: Fri, 27 May 2016 16:29:39 -0700
|
|
Subject: [PATCH] Input: surface3_spi - add surface pen support for Surface 3
|
|
|
|
This change creates a second input device which will handle input from
|
|
a Surface Pen. The Surface Pen supplies a different packet header than
|
|
touch events, so it is simple to handle one or the other.
|
|
|
|
This patch handles both the newer Surface Pen with one button, and the
|
|
older variant with a second button to switch to Eraser mode.
|
|
|
|
Signed-off-by: Stephen Just <stephenjust@gmail.com>
|
|
Reviewed-and-tested-by: Benjamin Tissoires <benjamin.tissoires@redhat.com>
|
|
Signed-off-by: Dmitry Torokhov <dmitry.torokhov@gmail.com>
|
|
---
|
|
drivers/input/touchscreen/surface3_spi.c | 114 ++++++++++++++++++++++++++++++-
|
|
1 file changed, 112 insertions(+), 2 deletions(-)
|
|
|
|
diff --git a/drivers/input/touchscreen/surface3_spi.c b/drivers/input/touchscreen/surface3_spi.c
|
|
index e6cea7e..e12fb9b 100644
|
|
--- a/drivers/input/touchscreen/surface3_spi.c
|
|
+++ b/drivers/input/touchscreen/surface3_spi.c
|
|
@@ -27,11 +27,14 @@
|
|
#define SURFACE3_PACKET_SIZE 264
|
|
|
|
#define SURFACE3_REPORT_TOUCH 0xd2
|
|
+#define SURFACE3_REPORT_PEN 0x16
|
|
|
|
struct surface3_ts_data {
|
|
struct spi_device *spi;
|
|
struct gpio_desc *gpiod_rst[2];
|
|
struct input_dev *input_dev;
|
|
+ struct input_dev *pen_input_dev;
|
|
+ int pen_tool;
|
|
|
|
u8 rd_buf[SURFACE3_PACKET_SIZE] ____cacheline_aligned;
|
|
};
|
|
@@ -48,6 +51,14 @@ struct surface3_ts_data_finger {
|
|
u32 padding;
|
|
} __packed;
|
|
|
|
+struct surface3_ts_data_pen {
|
|
+ u8 status;
|
|
+ __le16 x;
|
|
+ __le16 y;
|
|
+ __le16 pressure;
|
|
+ u8 padding;
|
|
+} __packed;
|
|
+
|
|
static int surface3_spi_read(struct surface3_ts_data *ts_data)
|
|
{
|
|
struct spi_device *spi = ts_data->spi;
|
|
@@ -113,6 +124,53 @@ static void surface3_spi_process_touch(struct surface3_ts_data *ts_data, u8 *dat
|
|
input_sync(ts_data->input_dev);
|
|
}
|
|
|
|
+static void surface3_spi_report_pen(struct surface3_ts_data *ts_data,
|
|
+ struct surface3_ts_data_pen *pen)
|
|
+{
|
|
+ struct input_dev *dev = ts_data->pen_input_dev;
|
|
+ int st = pen->status;
|
|
+ int prox = st & 0x01;
|
|
+ int rubber = st & 0x18;
|
|
+ int tool = (prox && rubber) ? BTN_TOOL_RUBBER : BTN_TOOL_PEN;
|
|
+
|
|
+ /* fake proximity out to switch tools */
|
|
+ if (ts_data->pen_tool != tool) {
|
|
+ input_report_key(dev, ts_data->pen_tool, 0);
|
|
+ input_sync(dev);
|
|
+ ts_data->pen_tool = tool;
|
|
+ }
|
|
+
|
|
+ input_report_key(dev, BTN_TOUCH, st & 0x12);
|
|
+
|
|
+ input_report_key(dev, ts_data->pen_tool, prox);
|
|
+
|
|
+ if (st) {
|
|
+ input_report_key(dev,
|
|
+ BTN_STYLUS,
|
|
+ st & 0x04);
|
|
+
|
|
+ input_report_abs(dev,
|
|
+ ABS_X,
|
|
+ get_unaligned_le16(&pen->x));
|
|
+ input_report_abs(dev,
|
|
+ ABS_Y,
|
|
+ get_unaligned_le16(&pen->y));
|
|
+ input_report_abs(dev,
|
|
+ ABS_PRESSURE,
|
|
+ get_unaligned_le16(&pen->pressure));
|
|
+ }
|
|
+}
|
|
+
|
|
+static void surface3_spi_process_pen(struct surface3_ts_data *ts_data, u8 *data)
|
|
+{
|
|
+ struct surface3_ts_data_pen *pen;
|
|
+
|
|
+ pen = (struct surface3_ts_data_pen *)&data[15];
|
|
+
|
|
+ surface3_spi_report_pen(ts_data, pen);
|
|
+ input_sync(ts_data->pen_input_dev);
|
|
+}
|
|
+
|
|
static void surface3_spi_process(struct surface3_ts_data *ts_data)
|
|
{
|
|
const char header[] = {
|
|
@@ -125,12 +183,19 @@ static void surface3_spi_process(struct surface3_ts_data *ts_data)
|
|
"%s header error: %*ph, ignoring...\n",
|
|
__func__, (int)sizeof(header), data);
|
|
|
|
- if (data[9] == SURFACE3_REPORT_TOUCH)
|
|
+ switch (data[9]) {
|
|
+ case SURFACE3_REPORT_TOUCH:
|
|
surface3_spi_process_touch(ts_data, data);
|
|
- else
|
|
+ break;
|
|
+ case SURFACE3_REPORT_PEN:
|
|
+ surface3_spi_process_pen(ts_data, data);
|
|
+ break;
|
|
+ default:
|
|
dev_err(&ts_data->spi->dev,
|
|
"%s unknown packet type: %x, ignoring...\n",
|
|
__func__, data[9]);
|
|
+ break;
|
|
+ }
|
|
}
|
|
|
|
static irqreturn_t surface3_spi_irq_handler(int irq, void *dev_id)
|
|
@@ -224,6 +289,47 @@ static int surface3_spi_create_touch_input(struct surface3_ts_data *data)
|
|
return 0;
|
|
}
|
|
|
|
+static int surface3_spi_create_pen_input(struct surface3_ts_data *data)
|
|
+{
|
|
+ struct input_dev *input;
|
|
+ int error;
|
|
+
|
|
+ input = devm_input_allocate_device(&data->spi->dev);
|
|
+ if (!input)
|
|
+ return -ENOMEM;
|
|
+
|
|
+ data->pen_input_dev = input;
|
|
+ data->pen_tool = BTN_TOOL_PEN;
|
|
+
|
|
+ __set_bit(INPUT_PROP_DIRECT, input->propbit);
|
|
+ __set_bit(INPUT_PROP_POINTER, input->propbit);
|
|
+ input_set_abs_params(input, ABS_X, 0, 9600, 0, 0);
|
|
+ input_abs_set_res(input, ABS_X, 40);
|
|
+ input_set_abs_params(input, ABS_Y, 0, 7200, 0, 0);
|
|
+ input_abs_set_res(input, ABS_Y, 48);
|
|
+ input_set_abs_params(input, ABS_PRESSURE, 0, 1024, 0, 0);
|
|
+ input_set_capability(input, EV_KEY, BTN_TOUCH);
|
|
+ input_set_capability(input, EV_KEY, BTN_STYLUS);
|
|
+ input_set_capability(input, EV_KEY, BTN_TOOL_PEN);
|
|
+ input_set_capability(input, EV_KEY, BTN_TOOL_RUBBER);
|
|
+
|
|
+ input->name = "Surface3 SPI Pen Input";
|
|
+ input->phys = "input/ts";
|
|
+ input->id.bustype = BUS_SPI;
|
|
+ input->id.vendor = 0x045e; /* Microsoft */
|
|
+ input->id.product = 0x0002;
|
|
+ input->id.version = 0x0000;
|
|
+
|
|
+ error = input_register_device(input);
|
|
+ if (error) {
|
|
+ dev_err(&data->spi->dev,
|
|
+ "Failed to register input device: %d", error);
|
|
+ return error;
|
|
+ }
|
|
+
|
|
+ return 0;
|
|
+}
|
|
+
|
|
static int surface3_spi_probe(struct spi_device *spi)
|
|
{
|
|
struct surface3_ts_data *data;
|
|
@@ -255,6 +361,10 @@ static int surface3_spi_probe(struct spi_device *spi)
|
|
if (error)
|
|
return error;
|
|
|
|
+ error = surface3_spi_create_pen_input(data);
|
|
+ if (error)
|
|
+ return error;
|
|
+
|
|
error = devm_request_threaded_irq(&spi->dev, spi->irq,
|
|
NULL, surface3_spi_irq_handler,
|
|
IRQF_ONESHOT,
|
|
--
|
|
2.7.4
|
|
|