| 1 | |
| 2 | |
| 3 | * Driver for Ntrig/Microsoft Touchscreens over SPI
|
| 4 | *
|
| 5 | * Copyright (c) 2016 Red Hat Inc.
|
| 6 |
|
| 7 |
|
| 8 |
|
| 9 | #include <linux/kernel.h>
|
| 10 |
|
| 11 | #include <linux/delay.h>
|
| 12 | #include <linux/gpio/consumer.h>
|
| 13 | #include <linux/input.h>
|
| 14 | #include <linux/input/mt.h>
|
| 15 | #include <linux/interrupt.h>
|
| 16 | #include <linux/module.h>
|
| 17 | #include <linux/slab.h>
|
| 18 | #include <linux/spi/spi.h>
|
| 19 | #include <linux/acpi.h>
|
| 20 |
|
| 21 | #include <linux/unaligned.h>
|
| 22 |
|
| 23 | #define SURFACE3_PACKET_SIZE 264
|
| 24 |
|
| 25 | #define SURFACE3_REPORT_TOUCH 0xd2
|
| 26 | #define SURFACE3_REPORT_PEN 0x16
|
| 27 |
|
| 28 | struct surface3_ts_data {
|
| 29 | struct spi_device *spi;
|
| 30 | struct gpio_desc *gpiod_rst[2];
|
| 31 | struct input_dev *input_dev;
|
| 32 | struct input_dev *pen_input_dev;
|
| 33 | int pen_tool;
|
| 34 |
|
| 35 | u8 rd_buf[SURFACE3_PACKET_SIZE] ____cacheline_aligned;
|
| 36 | };
|
| 37 |
|
| 38 | struct surface3_ts_data_finger {
|
| 39 | u8 status;
|
| 40 | __le16 tracking_id;
|
| 41 | __le16 x;
|
| 42 | __le16 cx;
|
| 43 | __le16 y;
|
| 44 | __le16 cy;
|
| 45 | __le16 width;
|
| 46 | __le16 height;
|
| 47 | u32 padding;
|
| 48 | } __packed;
|
| 49 |
|
| 50 | struct surface3_ts_data_pen {
|
| 51 | u8 status;
|
| 52 | __le16 x;
|
| 53 | __le16 y;
|
| 54 | __le16 pressure;
|
| 55 | u8 padding;
|
| 56 | } __packed;
|
| 57 |
|
| 58 | static int surface3_spi_read(struct surface3_ts_data *ts_data)
|
| 59 | {
|
| 60 | struct spi_device *spi = ts_data->spi;
|
| 61 |
|
| 62 | memset(ts_data->rd_buf, 0, sizeof(ts_data->rd_buf));
|
| 63 | return spi_read(spi, ts_data->rd_buf, sizeof(ts_data->rd_buf));
|
| 64 | }
|
| 65 |
|
| 66 | static void surface3_spi_report_touch(struct surface3_ts_data *ts_data,
|
| 67 | struct surface3_ts_data_finger *finger)
|
| 68 | {
|
| 69 | int st = finger->status & 0x01;
|
| 70 | int slot;
|
| 71 |
|
| 72 | slot = input_mt_get_slot_by_key(ts_data->input_dev,
|
| 73 | get_unaligned_le16(&finger->tracking_id));
|
| 74 | if (slot < 0)
|
| 75 | return;
|
| 76 |
|
| 77 | input_mt_slot(ts_data->input_dev, slot);
|
| 78 | input_mt_report_slot_state(ts_data->input_dev, MT_TOOL_FINGER, st);
|
| 79 | if (st) {
|
| 80 | input_report_abs(ts_data->input_dev,
|
| 81 | ABS_MT_POSITION_X,
|
| 82 | get_unaligned_le16(&finger->x));
|
| 83 | input_report_abs(ts_data->input_dev,
|
| 84 | ABS_MT_POSITION_Y,
|
| 85 | get_unaligned_le16(&finger->y));
|
| 86 | input_report_abs(ts_data->input_dev,
|
| 87 | ABS_MT_WIDTH_MAJOR,
|
| 88 | get_unaligned_le16(&finger->width));
|
| 89 | input_report_abs(ts_data->input_dev,
|
| 90 | ABS_MT_WIDTH_MINOR,
|
| 91 | get_unaligned_le16(&finger->height));
|
| 92 | }
|
| 93 | }
|
| 94 |
|
| 95 | static void surface3_spi_process_touch(struct surface3_ts_data *ts_data, u8 *data)
|
| 96 | {
|
| 97 | unsigned int i;
|
| 98 |
|
| 99 | for (i = 0; i < 13; i++) {
|
| 100 | struct surface3_ts_data_finger *finger;
|
| 101 |
|
| 102 | finger = (struct surface3_ts_data_finger *)&data[17 +
|
| 103 | i * sizeof(struct surface3_ts_data_finger)];
|
| 104 |
|
| 105 | |
| 106 | * When bit 5 of status is 1, it marks the end of the report:
|
| 107 | * - touch present: 0xe7
|
| 108 | * - touch released: 0xe4
|
| 109 | * - nothing valuable: 0xff
|
| 110 |
|
| 111 | if (finger->status & 0x10)
|
| 112 | break;
|
| 113 |
|
| 114 | surface3_spi_report_touch(ts_data, finger);
|
| 115 | }
|
| 116 |
|
| 117 | input_mt_sync_frame(ts_data->input_dev);
|
| 118 | input_sync(ts_data->input_dev);
|
| 119 | }
|
| 120 |
|
| 121 | static void surface3_spi_report_pen(struct surface3_ts_data *ts_data,
|
| 122 | struct surface3_ts_data_pen *pen)
|
| 123 | {
|
| 124 | struct input_dev *dev = ts_data->pen_input_dev;
|
| 125 | int st = pen->status;
|
| 126 | int prox = st & 0x01;
|
| 127 | int rubber = st & 0x18;
|
| 128 | int tool = (prox && rubber) ? BTN_TOOL_RUBBER : BTN_TOOL_PEN;
|
| 129 |
|
| 130 |
|
| 131 | if (ts_data->pen_tool != tool) {
|
| 132 | input_report_key(dev, ts_data->pen_tool, 0);
|
| 133 | input_sync(dev);
|
| 134 | ts_data->pen_tool = tool;
|
| 135 | }
|
| 136 |
|
| 137 | input_report_key(dev, BTN_TOUCH, st & 0x12);
|
| 138 |
|
| 139 | input_report_key(dev, ts_data->pen_tool, prox);
|
| 140 |
|
| 141 | if (st) {
|
| 142 | input_report_key(dev,
|
| 143 | BTN_STYLUS,
|
| 144 | st & 0x04);
|
| 145 |
|
| 146 | input_report_abs(dev,
|
| 147 | ABS_X,
|
| 148 | get_unaligned_le16(&pen->x));
|
| 149 | input_report_abs(dev,
|
| 150 | ABS_Y,
|
| 151 | get_unaligned_le16(&pen->y));
|
| 152 | input_report_abs(dev,
|
| 153 | ABS_PRESSURE,
|
| 154 | get_unaligned_le16(&pen->pressure));
|
| 155 | }
|
| 156 | }
|
| 157 |
|
| 158 | static void surface3_spi_process_pen(struct surface3_ts_data *ts_data, u8 *data)
|
| 159 | {
|
| 160 | struct surface3_ts_data_pen *pen;
|
| 161 |
|
| 162 | pen = (struct surface3_ts_data_pen *)&data[15];
|
| 163 |
|
| 164 | surface3_spi_report_pen(ts_data, pen);
|
| 165 | input_sync(ts_data->pen_input_dev);
|
| 166 | }
|
| 167 |
|
| 168 | static void surface3_spi_process(struct surface3_ts_data *ts_data)
|
| 169 | {
|
| 170 | static const char header[] = {
|
| 171 | 0xff, 0xff, 0xff, 0xff, 0xa5, 0x5a, 0xe7, 0x7e, 0x01
|
| 172 | };
|
| 173 | u8 *data = ts_data->rd_buf;
|
| 174 |
|
| 175 | if (memcmp(header, data, sizeof(header)))
|
| 176 | dev_err(&ts_data->spi->dev,
|
| 177 | "%s header error: %*ph, ignoring...\n",
|
| 178 | __func__, (int)sizeof(header), data);
|
| 179 |
|
| 180 | switch (data[9]) {
|
| 181 | case SURFACE3_REPORT_TOUCH:
|
| 182 | surface3_spi_process_touch(ts_data, data);
|
| 183 | break;
|
| 184 | case SURFACE3_REPORT_PEN:
|
| 185 | surface3_spi_process_pen(ts_data, data);
|
| 186 | break;
|
| 187 | default:
|
| 188 | dev_err(&ts_data->spi->dev,
|
| 189 | "%s unknown packet type: %x, ignoring...\n",
|
| 190 | __func__, data[9]);
|
| 191 | break;
|
| 192 | }
|
| 193 | }
|
| 194 |
|
| 195 | static irqreturn_t surface3_spi_irq_handler(int irq, void *dev_id)
|
| 196 | {
|
| 197 | struct surface3_ts_data *data = dev_id;
|
| 198 |
|
| 199 | if (surface3_spi_read(data))
|
| 200 | return IRQ_HANDLED;
|
| 201 |
|
| 202 | dev_dbg(&data->spi->dev, "%s received -> %*ph\n",
|
| 203 | __func__, SURFACE3_PACKET_SIZE, data->rd_buf);
|
| 204 | surface3_spi_process(data);
|
| 205 |
|
| 206 | return IRQ_HANDLED;
|
| 207 | }
|
| 208 |
|
| 209 | static void surface3_spi_power(struct surface3_ts_data *data, bool on)
|
| 210 | {
|
| 211 | gpiod_set_value(data->gpiod_rst[0], on);
|
| 212 | gpiod_set_value(data->gpiod_rst[1], on);
|
| 213 |
|
| 214 | msleep(20);
|
| 215 | }
|
| 216 |
|
| 217 | |
| 218 | * surface3_spi_get_gpio_config - Get GPIO config from ACPI/DT
|
| 219 | *
|
| 220 | * @data: surface3_spi_ts_data pointer
|
| 221 |
|
| 222 | static int surface3_spi_get_gpio_config(struct surface3_ts_data *data)
|
| 223 | {
|
| 224 | struct device *dev;
|
| 225 | struct gpio_desc *gpiod;
|
| 226 | int i;
|
| 227 |
|
| 228 | dev = &data->spi->dev;
|
| 229 |
|
| 230 |
|
| 231 | for (i = 0; i < 2; i++) {
|
| 232 | gpiod = devm_gpiod_get_index(dev, NULL, i, GPIOD_OUT_LOW);
|
| 233 | if (IS_ERR(gpiod))
|
| 234 | return dev_err_probe(dev, PTR_ERR(gpiod),
|
| 235 | "Failed to get power GPIO %d\n", i);
|
| 236 |
|
| 237 | data->gpiod_rst[i] = gpiod;
|
| 238 | }
|
| 239 |
|
| 240 | return 0;
|
| 241 | }
|
| 242 |
|
| 243 | static int surface3_spi_create_touch_input(struct surface3_ts_data *data)
|
| 244 | {
|
| 245 | struct input_dev *input;
|
| 246 | int error;
|
| 247 |
|
| 248 | input = devm_input_allocate_device(&data->spi->dev);
|
| 249 | if (!input)
|
| 250 | return -ENOMEM;
|
| 251 |
|
| 252 | data->input_dev = input;
|
| 253 |
|
| 254 | input_set_abs_params(input, ABS_MT_POSITION_X, 0, 9600, 0, 0);
|
| 255 | input_abs_set_res(input, ABS_MT_POSITION_X, 40);
|
| 256 | input_set_abs_params(input, ABS_MT_POSITION_Y, 0, 7200, 0, 0);
|
| 257 | input_abs_set_res(input, ABS_MT_POSITION_Y, 48);
|
| 258 | input_set_abs_params(input, ABS_MT_WIDTH_MAJOR, 0, 1024, 0, 0);
|
| 259 | input_set_abs_params(input, ABS_MT_WIDTH_MINOR, 0, 1024, 0, 0);
|
| 260 | input_mt_init_slots(input, 10, INPUT_MT_DIRECT);
|
| 261 |
|
| 262 | input->name = "Surface3 SPI Capacitive TouchScreen";
|
| 263 | input->phys = "input/ts";
|
| 264 | input->id.bustype = BUS_SPI;
|
| 265 | input->id.vendor = 0x045e;
|
| 266 | input->id.product = 0x0001;
|
| 267 | input->id.version = 0x0000;
|
| 268 |
|
| 269 | error = input_register_device(input);
|
| 270 | if (error) {
|
| 271 | dev_err(&data->spi->dev,
|
| 272 | "Failed to register input device: %d", error);
|
| 273 | return error;
|
| 274 | }
|
| 275 |
|
| 276 | return 0;
|
| 277 | }
|
| 278 |
|
| 279 | static int surface3_spi_create_pen_input(struct surface3_ts_data *data)
|
| 280 | {
|
| 281 | struct input_dev *input;
|
| 282 | int error;
|
| 283 |
|
| 284 | input = devm_input_allocate_device(&data->spi->dev);
|
| 285 | if (!input)
|
| 286 | return -ENOMEM;
|
| 287 |
|
| 288 | data->pen_input_dev = input;
|
| 289 | data->pen_tool = BTN_TOOL_PEN;
|
| 290 |
|
| 291 | __set_bit(INPUT_PROP_DIRECT, input->propbit);
|
| 292 | __set_bit(INPUT_PROP_POINTER, input->propbit);
|
| 293 | input_set_abs_params(input, ABS_X, 0, 9600, 0, 0);
|
| 294 | input_abs_set_res(input, ABS_X, 40);
|
| 295 | input_set_abs_params(input, ABS_Y, 0, 7200, 0, 0);
|
| 296 | input_abs_set_res(input, ABS_Y, 48);
|
| 297 | input_set_abs_params(input, ABS_PRESSURE, 0, 1024, 0, 0);
|
| 298 | input_set_capability(input, EV_KEY, BTN_TOUCH);
|
| 299 | input_set_capability(input, EV_KEY, BTN_STYLUS);
|
| 300 | input_set_capability(input, EV_KEY, BTN_TOOL_PEN);
|
| 301 | input_set_capability(input, EV_KEY, BTN_TOOL_RUBBER);
|
| 302 |
|
| 303 | input->name = "Surface3 SPI Pen Input";
|
| 304 | input->phys = "input/ts";
|
| 305 | input->id.bustype = BUS_SPI;
|
| 306 | input->id.vendor = 0x045e;
|
| 307 | input->id.product = 0x0002;
|
| 308 | input->id.version = 0x0000;
|
| 309 |
|
| 310 | error = input_register_device(input);
|
| 311 | if (error) {
|
| 312 | dev_err(&data->spi->dev,
|
| 313 | "Failed to register input device: %d", error);
|
| 314 | return error;
|
| 315 | }
|
| 316 |
|
| 317 | return 0;
|
| 318 | }
|
| 319 |
|
| 320 | static int surface3_spi_probe(struct spi_device *spi)
|
| 321 | {
|
| 322 | struct surface3_ts_data *data;
|
| 323 | int error;
|
| 324 |
|
| 325 |
|
| 326 | spi->bits_per_word = 8;
|
| 327 | spi->mode = SPI_MODE_0;
|
| 328 | error = spi_setup(spi);
|
| 329 | if (error)
|
| 330 | return error;
|
| 331 |
|
| 332 | data = devm_kzalloc(&spi->dev, sizeof(*data), GFP_KERNEL);
|
| 333 | if (!data)
|
| 334 | return -ENOMEM;
|
| 335 |
|
| 336 | data->spi = spi;
|
| 337 | spi_set_drvdata(spi, data);
|
| 338 |
|
| 339 | error = surface3_spi_get_gpio_config(data);
|
| 340 | if (error)
|
| 341 | return error;
|
| 342 |
|
| 343 | surface3_spi_power(data, true);
|
| 344 | surface3_spi_power(data, false);
|
| 345 | surface3_spi_power(data, true);
|
| 346 |
|
| 347 | error = surface3_spi_create_touch_input(data);
|
| 348 | if (error)
|
| 349 | return error;
|
| 350 |
|
| 351 | error = surface3_spi_create_pen_input(data);
|
| 352 | if (error)
|
| 353 | return error;
|
| 354 |
|
| 355 | error = devm_request_threaded_irq(&spi->dev, spi->irq,
|
| 356 | NULL, surface3_spi_irq_handler,
|
| 357 | IRQF_ONESHOT,
|
| 358 | "Surface3-irq", data);
|
| 359 | if (error)
|
| 360 | return error;
|
| 361 |
|
| 362 | return 0;
|
| 363 | }
|
| 364 |
|
| 365 | static int surface3_spi_suspend(struct device *dev)
|
| 366 | {
|
| 367 | struct spi_device *spi = to_spi_device(dev);
|
| 368 | struct surface3_ts_data *data = spi_get_drvdata(spi);
|
| 369 |
|
| 370 | disable_irq(data->spi->irq);
|
| 371 |
|
| 372 | surface3_spi_power(data, false);
|
| 373 |
|
| 374 | return 0;
|
| 375 | }
|
| 376 |
|
| 377 | static int surface3_spi_resume(struct device *dev)
|
| 378 | {
|
| 379 | struct spi_device *spi = to_spi_device(dev);
|
| 380 | struct surface3_ts_data *data = spi_get_drvdata(spi);
|
| 381 |
|
| 382 | surface3_spi_power(data, true);
|
| 383 |
|
| 384 | enable_irq(data->spi->irq);
|
| 385 |
|
| 386 | return 0;
|
| 387 | }
|
| 388 |
|
| 389 | static DEFINE_SIMPLE_DEV_PM_OPS(surface3_spi_pm_ops,
|
| 390 | surface3_spi_suspend,
|
| 391 | surface3_spi_resume);
|
| 392 |
|
| 393 | #ifdef CONFIG_ACPI
|
| 394 | static const struct acpi_device_id surface3_spi_acpi_match[] = {
|
| 395 | { "MSHW0037", 0 },
|
| 396 | { }
|
| 397 | };
|
| 398 | MODULE_DEVICE_TABLE(acpi, surface3_spi_acpi_match);
|
| 399 | #endif
|
| 400 |
|
| 401 | static struct spi_driver surface3_spi_driver = {
|
| 402 | .driver = {
|
| 403 | .name = "Surface3-spi",
|
| 404 | .acpi_match_table = ACPI_PTR(surface3_spi_acpi_match),
|
| 405 | .pm = pm_sleep_ptr(&surface3_spi_pm_ops),
|
| 406 | },
|
| 407 | .probe = surface3_spi_probe,
|
| 408 | };
|
| 409 |
|
| 410 | module_spi_driver(surface3_spi_driver);
|
| 411 |
|
| 412 | MODULE_AUTHOR("Benjamin Tissoires <[email protected]>");
|
| 413 | MODULE_DESCRIPTION("Surface 3 SPI touchscreen driver");
|
| 414 | MODULE_LICENSE("GPL v2");
|