1*d2912cb1SThomas Gleixner // SPDX-License-Identifier: GPL-2.0-only 26b0f8f9cSBöszörményi Zoltán /* 36b0f8f9cSBöszörményi Zoltán * EETI Egalax serial touchscreen driver 46b0f8f9cSBöszörményi Zoltán * 56b0f8f9cSBöszörményi Zoltán * Copyright (c) 2015 Zoltán Böszörményi <zboszor@pr.hu> 66b0f8f9cSBöszörményi Zoltán * 76b0f8f9cSBöszörményi Zoltán * based on the 86b0f8f9cSBöszörményi Zoltán * 96b0f8f9cSBöszörményi Zoltán * Hampshire serial touchscreen driver (Copyright (c) 2010 Adam Bennett) 106b0f8f9cSBöszörményi Zoltán */ 116b0f8f9cSBöszörményi Zoltán 126b0f8f9cSBöszörményi Zoltán 136b0f8f9cSBöszörményi Zoltán #include <linux/errno.h> 146b0f8f9cSBöszörményi Zoltán #include <linux/kernel.h> 156b0f8f9cSBöszörményi Zoltán #include <linux/module.h> 166b0f8f9cSBöszörményi Zoltán #include <linux/slab.h> 176b0f8f9cSBöszörményi Zoltán #include <linux/input.h> 186b0f8f9cSBöszörményi Zoltán #include <linux/serio.h> 196b0f8f9cSBöszörményi Zoltán 206b0f8f9cSBöszörményi Zoltán #define DRIVER_DESC "EETI Egalax serial touchscreen driver" 216b0f8f9cSBöszörményi Zoltán 226b0f8f9cSBöszörményi Zoltán /* 236b0f8f9cSBöszörményi Zoltán * Definitions & global arrays. 246b0f8f9cSBöszörményi Zoltán */ 256b0f8f9cSBöszörményi Zoltán 266b0f8f9cSBöszörményi Zoltán #define EGALAX_FORMAT_MAX_LENGTH 6 276b0f8f9cSBöszörményi Zoltán #define EGALAX_FORMAT_START_BIT BIT(7) 286b0f8f9cSBöszörményi Zoltán #define EGALAX_FORMAT_PRESSURE_BIT BIT(6) 296b0f8f9cSBöszörményi Zoltán #define EGALAX_FORMAT_TOUCH_BIT BIT(0) 306b0f8f9cSBöszörményi Zoltán #define EGALAX_FORMAT_RESOLUTION_MASK 0x06 316b0f8f9cSBöszörményi Zoltán 326b0f8f9cSBöszörményi Zoltán #define EGALAX_MIN_XC 0 336b0f8f9cSBöszörményi Zoltán #define EGALAX_MAX_XC 0x4000 346b0f8f9cSBöszörményi Zoltán #define EGALAX_MIN_YC 0 356b0f8f9cSBöszörményi Zoltán #define EGALAX_MAX_YC 0x4000 366b0f8f9cSBöszörményi Zoltán 376b0f8f9cSBöszörményi Zoltán /* 386b0f8f9cSBöszörményi Zoltán * Per-touchscreen data. 396b0f8f9cSBöszörményi Zoltán */ 406b0f8f9cSBöszörményi Zoltán struct egalax { 416b0f8f9cSBöszörményi Zoltán struct input_dev *input; 426b0f8f9cSBöszörményi Zoltán struct serio *serio; 436b0f8f9cSBöszörményi Zoltán int idx; 446b0f8f9cSBöszörményi Zoltán u8 data[EGALAX_FORMAT_MAX_LENGTH]; 456b0f8f9cSBöszörményi Zoltán char phys[32]; 466b0f8f9cSBöszörményi Zoltán }; 476b0f8f9cSBöszörményi Zoltán 486b0f8f9cSBöszörményi Zoltán static void egalax_process_data(struct egalax *egalax) 496b0f8f9cSBöszörményi Zoltán { 506b0f8f9cSBöszörményi Zoltán struct input_dev *dev = egalax->input; 516b0f8f9cSBöszörményi Zoltán u8 *data = egalax->data; 526b0f8f9cSBöszörményi Zoltán u16 x, y; 536b0f8f9cSBöszörményi Zoltán u8 shift; 546b0f8f9cSBöszörményi Zoltán u8 mask; 556b0f8f9cSBöszörményi Zoltán 566b0f8f9cSBöszörményi Zoltán shift = 3 - ((data[0] & EGALAX_FORMAT_RESOLUTION_MASK) >> 1); 576b0f8f9cSBöszörményi Zoltán mask = 0xff >> (shift + 1); 586b0f8f9cSBöszörményi Zoltán 596b0f8f9cSBöszörményi Zoltán x = (((u16)(data[1] & mask) << 7) | (data[2] & 0x7f)) << shift; 606b0f8f9cSBöszörményi Zoltán y = (((u16)(data[3] & mask) << 7) | (data[4] & 0x7f)) << shift; 616b0f8f9cSBöszörményi Zoltán 626b0f8f9cSBöszörményi Zoltán input_report_key(dev, BTN_TOUCH, data[0] & EGALAX_FORMAT_TOUCH_BIT); 636b0f8f9cSBöszörményi Zoltán input_report_abs(dev, ABS_X, x); 646b0f8f9cSBöszörményi Zoltán input_report_abs(dev, ABS_Y, y); 656b0f8f9cSBöszörményi Zoltán input_sync(dev); 666b0f8f9cSBöszörményi Zoltán } 676b0f8f9cSBöszörményi Zoltán 686b0f8f9cSBöszörményi Zoltán static irqreturn_t egalax_interrupt(struct serio *serio, 696b0f8f9cSBöszörményi Zoltán unsigned char data, unsigned int flags) 706b0f8f9cSBöszörményi Zoltán { 716b0f8f9cSBöszörményi Zoltán struct egalax *egalax = serio_get_drvdata(serio); 726b0f8f9cSBöszörményi Zoltán int pkt_len; 736b0f8f9cSBöszörményi Zoltán 746b0f8f9cSBöszörményi Zoltán egalax->data[egalax->idx++] = data; 756b0f8f9cSBöszörményi Zoltán 766b0f8f9cSBöszörményi Zoltán if (likely(egalax->data[0] & EGALAX_FORMAT_START_BIT)) { 776b0f8f9cSBöszörményi Zoltán pkt_len = egalax->data[0] & EGALAX_FORMAT_PRESSURE_BIT ? 6 : 5; 786b0f8f9cSBöszörményi Zoltán if (pkt_len == egalax->idx) { 796b0f8f9cSBöszörményi Zoltán egalax_process_data(egalax); 806b0f8f9cSBöszörményi Zoltán egalax->idx = 0; 816b0f8f9cSBöszörményi Zoltán } 826b0f8f9cSBöszörményi Zoltán } else { 836b0f8f9cSBöszörményi Zoltán dev_dbg(&serio->dev, "unknown/unsynchronized data: %x\n", 846b0f8f9cSBöszörményi Zoltán egalax->data[0]); 856b0f8f9cSBöszörményi Zoltán egalax->idx = 0; 866b0f8f9cSBöszörményi Zoltán } 876b0f8f9cSBöszörményi Zoltán 886b0f8f9cSBöszörményi Zoltán return IRQ_HANDLED; 896b0f8f9cSBöszörményi Zoltán } 906b0f8f9cSBöszörményi Zoltán 916b0f8f9cSBöszörményi Zoltán /* 926b0f8f9cSBöszörményi Zoltán * egalax_connect() is the routine that is called when someone adds a 936b0f8f9cSBöszörményi Zoltán * new serio device that supports egalax protocol and registers it as 946b0f8f9cSBöszörményi Zoltán * an input device. This is usually accomplished using inputattach. 956b0f8f9cSBöszörményi Zoltán */ 966b0f8f9cSBöszörményi Zoltán static int egalax_connect(struct serio *serio, struct serio_driver *drv) 976b0f8f9cSBöszörményi Zoltán { 986b0f8f9cSBöszörményi Zoltán struct egalax *egalax; 996b0f8f9cSBöszörményi Zoltán struct input_dev *input_dev; 1006b0f8f9cSBöszörményi Zoltán int error; 1016b0f8f9cSBöszörményi Zoltán 1026b0f8f9cSBöszörményi Zoltán egalax = kzalloc(sizeof(struct egalax), GFP_KERNEL); 1036b0f8f9cSBöszörményi Zoltán input_dev = input_allocate_device(); 1048dcb3c76SDan Carpenter if (!egalax || !input_dev) { 1056b0f8f9cSBöszörményi Zoltán error = -ENOMEM; 1066b0f8f9cSBöszörményi Zoltán goto err_free_mem; 1076b0f8f9cSBöszörményi Zoltán } 1086b0f8f9cSBöszörményi Zoltán 1096b0f8f9cSBöszörményi Zoltán egalax->serio = serio; 1106b0f8f9cSBöszörményi Zoltán egalax->input = input_dev; 1116b0f8f9cSBöszörményi Zoltán snprintf(egalax->phys, sizeof(egalax->phys), 1126b0f8f9cSBöszörményi Zoltán "%s/input0", serio->phys); 1136b0f8f9cSBöszörményi Zoltán 1146b0f8f9cSBöszörményi Zoltán input_dev->name = "EETI eGalaxTouch Serial TouchScreen"; 1156b0f8f9cSBöszörményi Zoltán input_dev->phys = egalax->phys; 1166b0f8f9cSBöszörményi Zoltán input_dev->id.bustype = BUS_RS232; 1176b0f8f9cSBöszörményi Zoltán input_dev->id.vendor = SERIO_EGALAX; 1186b0f8f9cSBöszörményi Zoltán input_dev->id.product = 0; 1196b0f8f9cSBöszörményi Zoltán input_dev->id.version = 0x0001; 1206b0f8f9cSBöszörményi Zoltán input_dev->dev.parent = &serio->dev; 1216b0f8f9cSBöszörményi Zoltán 1226b0f8f9cSBöszörményi Zoltán input_set_capability(input_dev, EV_KEY, BTN_TOUCH); 1236b0f8f9cSBöszörményi Zoltán input_set_abs_params(input_dev, ABS_X, 1246b0f8f9cSBöszörményi Zoltán EGALAX_MIN_XC, EGALAX_MAX_XC, 0, 0); 1256b0f8f9cSBöszörményi Zoltán input_set_abs_params(input_dev, ABS_Y, 1266b0f8f9cSBöszörményi Zoltán EGALAX_MIN_YC, EGALAX_MAX_YC, 0, 0); 1276b0f8f9cSBöszörményi Zoltán 1286b0f8f9cSBöszörményi Zoltán serio_set_drvdata(serio, egalax); 1296b0f8f9cSBöszörményi Zoltán 1306b0f8f9cSBöszörményi Zoltán error = serio_open(serio, drv); 1316b0f8f9cSBöszörményi Zoltán if (error) 1326b0f8f9cSBöszörményi Zoltán goto err_reset_drvdata; 1336b0f8f9cSBöszörményi Zoltán 1346b0f8f9cSBöszörményi Zoltán error = input_register_device(input_dev); 1356b0f8f9cSBöszörményi Zoltán if (error) 1366b0f8f9cSBöszörményi Zoltán goto err_close_serio; 1376b0f8f9cSBöszörményi Zoltán 1386b0f8f9cSBöszörményi Zoltán return 0; 1396b0f8f9cSBöszörményi Zoltán 1406b0f8f9cSBöszörményi Zoltán err_close_serio: 1416b0f8f9cSBöszörményi Zoltán serio_close(serio); 1426b0f8f9cSBöszörményi Zoltán err_reset_drvdata: 1436b0f8f9cSBöszörményi Zoltán serio_set_drvdata(serio, NULL); 1446b0f8f9cSBöszörményi Zoltán err_free_mem: 1456b0f8f9cSBöszörményi Zoltán input_free_device(input_dev); 1466b0f8f9cSBöszörményi Zoltán kfree(egalax); 1476b0f8f9cSBöszörményi Zoltán return error; 1486b0f8f9cSBöszörményi Zoltán } 1496b0f8f9cSBöszörményi Zoltán 1506b0f8f9cSBöszörményi Zoltán static void egalax_disconnect(struct serio *serio) 1516b0f8f9cSBöszörményi Zoltán { 1526b0f8f9cSBöszörményi Zoltán struct egalax *egalax = serio_get_drvdata(serio); 1536b0f8f9cSBöszörményi Zoltán 1546b0f8f9cSBöszörményi Zoltán serio_close(serio); 1556b0f8f9cSBöszörményi Zoltán serio_set_drvdata(serio, NULL); 1566b0f8f9cSBöszörményi Zoltán input_unregister_device(egalax->input); 1576b0f8f9cSBöszörményi Zoltán kfree(egalax); 1586b0f8f9cSBöszörményi Zoltán } 1596b0f8f9cSBöszörményi Zoltán 1606b0f8f9cSBöszörményi Zoltán /* 1616b0f8f9cSBöszörményi Zoltán * The serio driver structure. 1626b0f8f9cSBöszörményi Zoltán */ 1636b0f8f9cSBöszörményi Zoltán 1646b0f8f9cSBöszörményi Zoltán static const struct serio_device_id egalax_serio_ids[] = { 1656b0f8f9cSBöszörményi Zoltán { 1666b0f8f9cSBöszörményi Zoltán .type = SERIO_RS232, 1676b0f8f9cSBöszörményi Zoltán .proto = SERIO_EGALAX, 1686b0f8f9cSBöszörményi Zoltán .id = SERIO_ANY, 1696b0f8f9cSBöszörményi Zoltán .extra = SERIO_ANY, 1706b0f8f9cSBöszörményi Zoltán }, 1716b0f8f9cSBöszörményi Zoltán { 0 } 1726b0f8f9cSBöszörményi Zoltán }; 1736b0f8f9cSBöszörményi Zoltán 1746b0f8f9cSBöszörményi Zoltán MODULE_DEVICE_TABLE(serio, egalax_serio_ids); 1756b0f8f9cSBöszörményi Zoltán 1766b0f8f9cSBöszörményi Zoltán static struct serio_driver egalax_drv = { 1776b0f8f9cSBöszörményi Zoltán .driver = { 1786b0f8f9cSBöszörményi Zoltán .name = "egalax", 1796b0f8f9cSBöszörményi Zoltán }, 1806b0f8f9cSBöszörményi Zoltán .description = DRIVER_DESC, 1816b0f8f9cSBöszörményi Zoltán .id_table = egalax_serio_ids, 1826b0f8f9cSBöszörményi Zoltán .interrupt = egalax_interrupt, 1836b0f8f9cSBöszörményi Zoltán .connect = egalax_connect, 1846b0f8f9cSBöszörményi Zoltán .disconnect = egalax_disconnect, 1856b0f8f9cSBöszörményi Zoltán }; 1866b0f8f9cSBöszörményi Zoltán module_serio_driver(egalax_drv); 1876b0f8f9cSBöszörményi Zoltán 1886b0f8f9cSBöszörményi Zoltán MODULE_AUTHOR("Zoltán Böszörményi <zboszor@pr.hu>"); 1896b0f8f9cSBöszörményi Zoltán MODULE_DESCRIPTION(DRIVER_DESC); 1906b0f8f9cSBöszörményi Zoltán MODULE_LICENSE("GPL v2"); 191