xref: /openbmc/linux/drivers/input/touchscreen/egalax_ts_serial.c (revision 75bf465f0bc33e9b776a46d6a1b9b990f5fb7c37)
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