1d2912cb1SThomas Gleixner // SPDX-License-Identifier: GPL-2.0-only
2a5f523bcSTias Guns /*
3a5f523bcSTias Guns  * Dynapro serial touchscreen driver
4a5f523bcSTias Guns  *
5a5f523bcSTias Guns  * Copyright (c) 2009 Tias Guns
6a5f523bcSTias Guns  * Based on the inexio driver (c) Vojtech Pavlik and Dan Streetman and
7a5f523bcSTias Guns  * Richard Lemon
8a5f523bcSTias Guns  */
9a5f523bcSTias Guns 
10a5f523bcSTias Guns 
11a5f523bcSTias Guns /*
12a5f523bcSTias Guns  * 2009/09/19 Tias Guns <tias@ulyssis.org>
13a5f523bcSTias Guns  *   Copied inexio.c and edited for Dynapro protocol (from retired Xorg module)
14a5f523bcSTias Guns  */
15a5f523bcSTias Guns 
16a5f523bcSTias Guns #include <linux/errno.h>
17a5f523bcSTias Guns #include <linux/kernel.h>
18a5f523bcSTias Guns #include <linux/module.h>
19a5f523bcSTias Guns #include <linux/slab.h>
20a5f523bcSTias Guns #include <linux/input.h>
21a5f523bcSTias Guns #include <linux/serio.h>
22a5f523bcSTias Guns 
23a5f523bcSTias Guns #define DRIVER_DESC	"Dynapro serial touchscreen driver"
24a5f523bcSTias Guns 
25a5f523bcSTias Guns MODULE_AUTHOR("Tias Guns <tias@ulyssis.org>");
26a5f523bcSTias Guns MODULE_DESCRIPTION(DRIVER_DESC);
27a5f523bcSTias Guns MODULE_LICENSE("GPL");
28a5f523bcSTias Guns 
29a5f523bcSTias Guns /*
30a5f523bcSTias Guns  * Definitions & global arrays.
31a5f523bcSTias Guns  */
32a5f523bcSTias Guns 
33a5f523bcSTias Guns #define DYNAPRO_FORMAT_TOUCH_BIT 0x40
34a5f523bcSTias Guns #define DYNAPRO_FORMAT_LENGTH 3
35a5f523bcSTias Guns #define DYNAPRO_RESPONSE_BEGIN_BYTE 0x80
36a5f523bcSTias Guns 
37a5f523bcSTias Guns #define DYNAPRO_MIN_XC 0
38a5f523bcSTias Guns #define DYNAPRO_MAX_XC 0x3ff
39a5f523bcSTias Guns #define DYNAPRO_MIN_YC 0
40a5f523bcSTias Guns #define DYNAPRO_MAX_YC 0x3ff
41a5f523bcSTias Guns 
42a5f523bcSTias Guns #define DYNAPRO_GET_XC(data) (data[1] | ((data[0] & 0x38) << 4))
43a5f523bcSTias Guns #define DYNAPRO_GET_YC(data) (data[2] | ((data[0] & 0x07) << 7))
44a5f523bcSTias Guns #define DYNAPRO_GET_TOUCHED(data) (DYNAPRO_FORMAT_TOUCH_BIT & data[0])
45a5f523bcSTias Guns 
46a5f523bcSTias Guns /*
47a5f523bcSTias Guns  * Per-touchscreen data.
48a5f523bcSTias Guns  */
49a5f523bcSTias Guns 
50a5f523bcSTias Guns struct dynapro {
51a5f523bcSTias Guns 	struct input_dev *dev;
52a5f523bcSTias Guns 	struct serio *serio;
53a5f523bcSTias Guns 	int idx;
54a5f523bcSTias Guns 	unsigned char data[DYNAPRO_FORMAT_LENGTH];
55a5f523bcSTias Guns 	char phys[32];
56a5f523bcSTias Guns };
57a5f523bcSTias Guns 
dynapro_process_data(struct dynapro * pdynapro)58a5f523bcSTias Guns static void dynapro_process_data(struct dynapro *pdynapro)
59a5f523bcSTias Guns {
60a5f523bcSTias Guns 	struct input_dev *dev = pdynapro->dev;
61a5f523bcSTias Guns 
62a5f523bcSTias Guns 	if (DYNAPRO_FORMAT_LENGTH == ++pdynapro->idx) {
63a5f523bcSTias Guns 		input_report_abs(dev, ABS_X, DYNAPRO_GET_XC(pdynapro->data));
64a5f523bcSTias Guns 		input_report_abs(dev, ABS_Y, DYNAPRO_GET_YC(pdynapro->data));
65a5f523bcSTias Guns 		input_report_key(dev, BTN_TOUCH,
66a5f523bcSTias Guns 				 DYNAPRO_GET_TOUCHED(pdynapro->data));
67a5f523bcSTias Guns 		input_sync(dev);
68a5f523bcSTias Guns 
69a5f523bcSTias Guns 		pdynapro->idx = 0;
70a5f523bcSTias Guns 	}
71a5f523bcSTias Guns }
72a5f523bcSTias Guns 
dynapro_interrupt(struct serio * serio,unsigned char data,unsigned int flags)73a5f523bcSTias Guns static irqreturn_t dynapro_interrupt(struct serio *serio,
74a5f523bcSTias Guns 		unsigned char data, unsigned int flags)
75a5f523bcSTias Guns {
76a5f523bcSTias Guns 	struct dynapro *pdynapro = serio_get_drvdata(serio);
77a5f523bcSTias Guns 
78a5f523bcSTias Guns 	pdynapro->data[pdynapro->idx] = data;
79a5f523bcSTias Guns 
80a5f523bcSTias Guns 	if (DYNAPRO_RESPONSE_BEGIN_BYTE & pdynapro->data[0])
81a5f523bcSTias Guns 		dynapro_process_data(pdynapro);
82a5f523bcSTias Guns 	else
83a5f523bcSTias Guns 		dev_dbg(&serio->dev, "unknown/unsynchronized data: %x\n",
84a5f523bcSTias Guns 			pdynapro->data[0]);
85a5f523bcSTias Guns 
86a5f523bcSTias Guns 	return IRQ_HANDLED;
87a5f523bcSTias Guns }
88a5f523bcSTias Guns 
dynapro_disconnect(struct serio * serio)89a5f523bcSTias Guns static void dynapro_disconnect(struct serio *serio)
90a5f523bcSTias Guns {
91a5f523bcSTias Guns 	struct dynapro *pdynapro = serio_get_drvdata(serio);
92a5f523bcSTias Guns 
93a5f523bcSTias Guns 	input_get_device(pdynapro->dev);
94a5f523bcSTias Guns 	input_unregister_device(pdynapro->dev);
95a5f523bcSTias Guns 	serio_close(serio);
96a5f523bcSTias Guns 	serio_set_drvdata(serio, NULL);
97a5f523bcSTias Guns 	input_put_device(pdynapro->dev);
98a5f523bcSTias Guns 	kfree(pdynapro);
99a5f523bcSTias Guns }
100a5f523bcSTias Guns 
101a5f523bcSTias Guns /*
102a5f523bcSTias Guns  * dynapro_connect() is the routine that is called when someone adds a
103a5f523bcSTias Guns  * new serio device that supports dynapro protocol and registers it as
104a5f523bcSTias Guns  * an input device. This is usually accomplished using inputattach.
105a5f523bcSTias Guns  */
106a5f523bcSTias Guns 
dynapro_connect(struct serio * serio,struct serio_driver * drv)107a5f523bcSTias Guns static int dynapro_connect(struct serio *serio, struct serio_driver *drv)
108a5f523bcSTias Guns {
109a5f523bcSTias Guns 	struct dynapro *pdynapro;
110a5f523bcSTias Guns 	struct input_dev *input_dev;
111a5f523bcSTias Guns 	int err;
112a5f523bcSTias Guns 
113a5f523bcSTias Guns 	pdynapro = kzalloc(sizeof(struct dynapro), GFP_KERNEL);
114a5f523bcSTias Guns 	input_dev = input_allocate_device();
115a5f523bcSTias Guns 	if (!pdynapro || !input_dev) {
116a5f523bcSTias Guns 		err = -ENOMEM;
117a5f523bcSTias Guns 		goto fail1;
118a5f523bcSTias Guns 	}
119a5f523bcSTias Guns 
120a5f523bcSTias Guns 	pdynapro->serio = serio;
121a5f523bcSTias Guns 	pdynapro->dev = input_dev;
122a5f523bcSTias Guns 	snprintf(pdynapro->phys, sizeof(pdynapro->phys),
123a5f523bcSTias Guns 		 "%s/input0", serio->phys);
124a5f523bcSTias Guns 
125a5f523bcSTias Guns 	input_dev->name = "Dynapro Serial TouchScreen";
126a5f523bcSTias Guns 	input_dev->phys = pdynapro->phys;
127a5f523bcSTias Guns 	input_dev->id.bustype = BUS_RS232;
128a5f523bcSTias Guns 	input_dev->id.vendor = SERIO_DYNAPRO;
129a5f523bcSTias Guns 	input_dev->id.product = 0;
130a5f523bcSTias Guns 	input_dev->id.version = 0x0001;
131a5f523bcSTias Guns 	input_dev->dev.parent = &serio->dev;
132a5f523bcSTias Guns 	input_dev->evbit[0] = BIT_MASK(EV_KEY) | BIT_MASK(EV_ABS);
133a5f523bcSTias Guns 	input_dev->keybit[BIT_WORD(BTN_TOUCH)] = BIT_MASK(BTN_TOUCH);
134a5f523bcSTias Guns 	input_set_abs_params(pdynapro->dev, ABS_X,
135a5f523bcSTias Guns 			     DYNAPRO_MIN_XC, DYNAPRO_MAX_XC, 0, 0);
136a5f523bcSTias Guns 	input_set_abs_params(pdynapro->dev, ABS_Y,
137a5f523bcSTias Guns 			     DYNAPRO_MIN_YC, DYNAPRO_MAX_YC, 0, 0);
138a5f523bcSTias Guns 
139a5f523bcSTias Guns 	serio_set_drvdata(serio, pdynapro);
140a5f523bcSTias Guns 
141a5f523bcSTias Guns 	err = serio_open(serio, drv);
142a5f523bcSTias Guns 	if (err)
143a5f523bcSTias Guns 		goto fail2;
144a5f523bcSTias Guns 
145a5f523bcSTias Guns 	err = input_register_device(pdynapro->dev);
146a5f523bcSTias Guns 	if (err)
147a5f523bcSTias Guns 		goto fail3;
148a5f523bcSTias Guns 
149a5f523bcSTias Guns 	return 0;
150a5f523bcSTias Guns 
151a5f523bcSTias Guns  fail3:	serio_close(serio);
152a5f523bcSTias Guns  fail2:	serio_set_drvdata(serio, NULL);
153a5f523bcSTias Guns  fail1:	input_free_device(input_dev);
154a5f523bcSTias Guns 	kfree(pdynapro);
155a5f523bcSTias Guns 	return err;
156a5f523bcSTias Guns }
157a5f523bcSTias Guns 
158a5f523bcSTias Guns /*
159a5f523bcSTias Guns  * The serio driver structure.
160a5f523bcSTias Guns  */
161a5f523bcSTias Guns 
162fe11d25eSArvind Yadav static const struct serio_device_id dynapro_serio_ids[] = {
163a5f523bcSTias Guns 	{
164a5f523bcSTias Guns 		.type	= SERIO_RS232,
165a5f523bcSTias Guns 		.proto	= SERIO_DYNAPRO,
166a5f523bcSTias Guns 		.id	= SERIO_ANY,
167a5f523bcSTias Guns 		.extra	= SERIO_ANY,
168a5f523bcSTias Guns 	},
169a5f523bcSTias Guns 	{ 0 }
170a5f523bcSTias Guns };
171a5f523bcSTias Guns 
172a5f523bcSTias Guns MODULE_DEVICE_TABLE(serio, dynapro_serio_ids);
173a5f523bcSTias Guns 
174a5f523bcSTias Guns static struct serio_driver dynapro_drv = {
175a5f523bcSTias Guns 	.driver		= {
176a5f523bcSTias Guns 		.name	= "dynapro",
177a5f523bcSTias Guns 	},
178a5f523bcSTias Guns 	.description	= DRIVER_DESC,
179a5f523bcSTias Guns 	.id_table	= dynapro_serio_ids,
180a5f523bcSTias Guns 	.interrupt	= dynapro_interrupt,
181a5f523bcSTias Guns 	.connect	= dynapro_connect,
182a5f523bcSTias Guns 	.disconnect	= dynapro_disconnect,
183a5f523bcSTias Guns };
184a5f523bcSTias Guns 
18565ac9f7aSAxel Lin module_serio_driver(dynapro_drv);
186