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
egalax_process_data(struct egalax * egalax)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
egalax_interrupt(struct serio * serio,unsigned char data,unsigned int flags)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 */
egalax_connect(struct serio * serio,struct serio_driver * drv)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
egalax_disconnect(struct serio * serio)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