summaryrefslogtreecommitdiffstats
path: root/drivers/input
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/input')
-rw-r--r--drivers/input/touchscreen/surface3_spi.c114
1 files changed, 112 insertions, 2 deletions
diff --git a/drivers/input/touchscreen/surface3_spi.c b/drivers/input/touchscreen/surface3_spi.c
index e6cea7ed3d0e..e12fb9b63f31 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,