blob: e6cea7ed3d0eb50a52441bcfe5d369c277a8a725 [file] [log] [blame]
Benjamin Tissoires4feacbc2016-05-19 09:24:06 -07001/*
2 * Driver for Ntrig/Microsoft Touchscreens over SPI
3 *
4 * Copyright (c) 2016 Red Hat Inc.
5 */
6
7/*
8 * This program is free software; you can redistribute it and/or modify it
9 * under the terms of the GNU General Public License as published by the Free
10 * Software Foundation; version 2 of the License.
11 */
12
13#include <linux/kernel.h>
14
15#include <linux/delay.h>
16#include <linux/gpio/consumer.h>
17#include <linux/input.h>
18#include <linux/input/mt.h>
19#include <linux/interrupt.h>
20#include <linux/module.h>
21#include <linux/slab.h>
22#include <linux/spi/spi.h>
23#include <linux/acpi.h>
24
25#include <asm/unaligned.h>
26
27#define SURFACE3_PACKET_SIZE 264
28
Stephen Just427ee202016-05-27 16:28:09 -070029#define SURFACE3_REPORT_TOUCH 0xd2
30
Benjamin Tissoires4feacbc2016-05-19 09:24:06 -070031struct surface3_ts_data {
32 struct spi_device *spi;
33 struct gpio_desc *gpiod_rst[2];
34 struct input_dev *input_dev;
35
36 u8 rd_buf[SURFACE3_PACKET_SIZE] ____cacheline_aligned;
37};
38
39struct surface3_ts_data_finger {
40 u8 status;
41 __le16 tracking_id;
42 __le16 x;
43 __le16 cx;
44 __le16 y;
45 __le16 cy;
46 __le16 width;
47 __le16 height;
48 u32 padding;
49} __packed;
50
51static int surface3_spi_read(struct surface3_ts_data *ts_data)
52{
53 struct spi_device *spi = ts_data->spi;
54
55 memset(ts_data->rd_buf, 0, sizeof(ts_data->rd_buf));
56 return spi_read(spi, ts_data->rd_buf, sizeof(ts_data->rd_buf));
57}
58
59static void surface3_spi_report_touch(struct surface3_ts_data *ts_data,
60 struct surface3_ts_data_finger *finger)
61{
62 int st = finger->status & 0x01;
63 int slot;
64
65 slot = input_mt_get_slot_by_key(ts_data->input_dev,
66 get_unaligned_le16(&finger->tracking_id));
67 if (slot < 0)
68 return;
69
70 input_mt_slot(ts_data->input_dev, slot);
71 input_mt_report_slot_state(ts_data->input_dev, MT_TOOL_FINGER, st);
72 if (st) {
73 input_report_abs(ts_data->input_dev,
74 ABS_MT_POSITION_X,
75 get_unaligned_le16(&finger->x));
76 input_report_abs(ts_data->input_dev,
77 ABS_MT_POSITION_Y,
78 get_unaligned_le16(&finger->y));
79 input_report_abs(ts_data->input_dev,
80 ABS_MT_WIDTH_MAJOR,
81 get_unaligned_le16(&finger->width));
82 input_report_abs(ts_data->input_dev,
83 ABS_MT_WIDTH_MINOR,
84 get_unaligned_le16(&finger->height));
85 }
86}
87
Stephen Just427ee202016-05-27 16:28:09 -070088static void surface3_spi_process_touch(struct surface3_ts_data *ts_data, u8 *data)
Benjamin Tissoires4feacbc2016-05-19 09:24:06 -070089{
Benjamin Tissoires4feacbc2016-05-19 09:24:06 -070090 u16 timestamp;
91 unsigned int i;
Benjamin Tissoires4feacbc2016-05-19 09:24:06 -070092 timestamp = get_unaligned_le16(&data[15]);
93
94 for (i = 0; i < 13; i++) {
95 struct surface3_ts_data_finger *finger;
96
97 finger = (struct surface3_ts_data_finger *)&data[17 +
98 i * sizeof(struct surface3_ts_data_finger)];
99
100 /*
101 * When bit 5 of status is 1, it marks the end of the report:
102 * - touch present: 0xe7
103 * - touch released: 0xe4
104 * - nothing valuable: 0xff
105 */
106 if (finger->status & 0x10)
107 break;
108
109 surface3_spi_report_touch(ts_data, finger);
110 }
111
112 input_mt_sync_frame(ts_data->input_dev);
113 input_sync(ts_data->input_dev);
114}
115
Stephen Just427ee202016-05-27 16:28:09 -0700116static void surface3_spi_process(struct surface3_ts_data *ts_data)
117{
118 const char header[] = {
119 0xff, 0xff, 0xff, 0xff, 0xa5, 0x5a, 0xe7, 0x7e, 0x01
120 };
121 u8 *data = ts_data->rd_buf;
122
123 if (memcmp(header, data, sizeof(header)))
124 dev_err(&ts_data->spi->dev,
125 "%s header error: %*ph, ignoring...\n",
126 __func__, (int)sizeof(header), data);
127
128 if (data[9] == SURFACE3_REPORT_TOUCH)
129 surface3_spi_process_touch(ts_data, data);
130 else
131 dev_err(&ts_data->spi->dev,
132 "%s unknown packet type: %x, ignoring...\n",
133 __func__, data[9]);
134}
135
Benjamin Tissoires4feacbc2016-05-19 09:24:06 -0700136static irqreturn_t surface3_spi_irq_handler(int irq, void *dev_id)
137{
138 struct surface3_ts_data *data = dev_id;
139
140 if (surface3_spi_read(data))
141 return IRQ_HANDLED;
142
143 dev_dbg(&data->spi->dev, "%s received -> %*ph\n",
144 __func__, SURFACE3_PACKET_SIZE, data->rd_buf);
145 surface3_spi_process(data);
146
147 return IRQ_HANDLED;
148}
149
150static void surface3_spi_power(struct surface3_ts_data *data, bool on)
151{
152 gpiod_set_value(data->gpiod_rst[0], on);
153 gpiod_set_value(data->gpiod_rst[1], on);
154 /* let the device settle a little */
155 msleep(20);
156}
157
158/**
159 * surface3_spi_get_gpio_config - Get GPIO config from ACPI/DT
160 *
161 * @ts: surface3_spi_ts_data pointer
162 */
163static int surface3_spi_get_gpio_config(struct surface3_ts_data *data)
164{
165 int error;
166 struct device *dev;
167 struct gpio_desc *gpiod;
168 int i;
169
170 dev = &data->spi->dev;
171
172 /* Get the reset lines GPIO pin number */
173 for (i = 0; i < 2; i++) {
174 gpiod = devm_gpiod_get_index(dev, NULL, i, GPIOD_OUT_LOW);
175 if (IS_ERR(gpiod)) {
176 error = PTR_ERR(gpiod);
177 if (error != -EPROBE_DEFER)
178 dev_err(dev,
179 "Failed to get power GPIO %d: %d\n",
180 i,
181 error);
182 return error;
183 }
184
185 data->gpiod_rst[i] = gpiod;
186 }
187
188 return 0;
189}
190
Stephen Just427ee202016-05-27 16:28:09 -0700191static int surface3_spi_create_touch_input(struct surface3_ts_data *data)
Benjamin Tissoires4feacbc2016-05-19 09:24:06 -0700192{
193 struct input_dev *input;
194 int error;
195
196 input = devm_input_allocate_device(&data->spi->dev);
197 if (!input)
198 return -ENOMEM;
199
200 data->input_dev = input;
201
202 input_set_abs_params(input, ABS_MT_POSITION_X, 0, 9600, 0, 0);
203 input_abs_set_res(input, ABS_MT_POSITION_X, 40);
204 input_set_abs_params(input, ABS_MT_POSITION_Y, 0, 7200, 0, 0);
205 input_abs_set_res(input, ABS_MT_POSITION_Y, 48);
206 input_set_abs_params(input, ABS_MT_WIDTH_MAJOR, 0, 1024, 0, 0);
207 input_set_abs_params(input, ABS_MT_WIDTH_MINOR, 0, 1024, 0, 0);
208 input_mt_init_slots(input, 10, INPUT_MT_DIRECT);
209
210 input->name = "Surface3 SPI Capacitive TouchScreen";
211 input->phys = "input/ts";
212 input->id.bustype = BUS_SPI;
213 input->id.vendor = 0x045e; /* Microsoft */
Stephen Just427ee202016-05-27 16:28:09 -0700214 input->id.product = 0x0001;
Benjamin Tissoires4feacbc2016-05-19 09:24:06 -0700215 input->id.version = 0x0000;
216
217 error = input_register_device(input);
218 if (error) {
219 dev_err(&data->spi->dev,
220 "Failed to register input device: %d", error);
221 return error;
222 }
223
224 return 0;
225}
226
227static int surface3_spi_probe(struct spi_device *spi)
228{
229 struct surface3_ts_data *data;
230 int error;
231
232 /* Set up SPI*/
233 spi->bits_per_word = 8;
234 spi->mode = SPI_MODE_0;
235 error = spi_setup(spi);
236 if (error)
237 return error;
238
239 data = devm_kzalloc(&spi->dev, sizeof(*data), GFP_KERNEL);
240 if (!data)
241 return -ENOMEM;
242
243 data->spi = spi;
244 spi_set_drvdata(spi, data);
245
246 error = surface3_spi_get_gpio_config(data);
247 if (error)
248 return error;
249
250 surface3_spi_power(data, true);
251 surface3_spi_power(data, false);
252 surface3_spi_power(data, true);
253
Stephen Just427ee202016-05-27 16:28:09 -0700254 error = surface3_spi_create_touch_input(data);
Benjamin Tissoires4feacbc2016-05-19 09:24:06 -0700255 if (error)
256 return error;
257
258 error = devm_request_threaded_irq(&spi->dev, spi->irq,
259 NULL, surface3_spi_irq_handler,
260 IRQF_ONESHOT,
261 "Surface3-irq", data);
262 if (error)
263 return error;
264
265 return 0;
266}
267
268static int __maybe_unused surface3_spi_suspend(struct device *dev)
269{
270 struct spi_device *spi = to_spi_device(dev);
271 struct surface3_ts_data *data = spi_get_drvdata(spi);
272
273 disable_irq(data->spi->irq);
274
275 surface3_spi_power(data, false);
276
277 return 0;
278}
279
280static int __maybe_unused surface3_spi_resume(struct device *dev)
281{
282 struct spi_device *spi = to_spi_device(dev);
283 struct surface3_ts_data *data = spi_get_drvdata(spi);
284
285 surface3_spi_power(data, true);
286
287 enable_irq(data->spi->irq);
288
289 return 0;
290}
291
292static SIMPLE_DEV_PM_OPS(surface3_spi_pm_ops,
293 surface3_spi_suspend,
294 surface3_spi_resume);
295
296#ifdef CONFIG_ACPI
297static const struct acpi_device_id surface3_spi_acpi_match[] = {
298 { "MSHW0037", 0 },
299 { }
300};
301MODULE_DEVICE_TABLE(acpi, surface3_spi_acpi_match);
302#endif
303
304static struct spi_driver surface3_spi_driver = {
305 .driver = {
306 .name = "Surface3-spi",
307 .acpi_match_table = ACPI_PTR(surface3_spi_acpi_match),
308 .pm = &surface3_spi_pm_ops,
309 },
310 .probe = surface3_spi_probe,
311};
312
313module_spi_driver(surface3_spi_driver);
314
315MODULE_AUTHOR("Benjamin Tissoires <benjamin.tissoires@gmail.com>");
316MODULE_DESCRIPTION("Surface 3 SPI touchscreen driver");
317MODULE_LICENSE("GPL v2");