xref: /linux/drivers/input/touch-overlay.c (revision 0074281bb6316108e0cff094bd4db78ab3eee236)
1*ea4d3310SJavier Carrasco // SPDX-License-Identifier: GPL-2.0-only
2*ea4d3310SJavier Carrasco /*
3*ea4d3310SJavier Carrasco  *  Helper functions for overlay objects on touchscreens
4*ea4d3310SJavier Carrasco  *
5*ea4d3310SJavier Carrasco  *  Copyright (c) 2023 Javier Carrasco <javier.carrasco@wolfvision.net>
6*ea4d3310SJavier Carrasco  */
7*ea4d3310SJavier Carrasco 
8*ea4d3310SJavier Carrasco #include <linux/input.h>
9*ea4d3310SJavier Carrasco #include <linux/input/mt.h>
10*ea4d3310SJavier Carrasco #include <linux/input/touch-overlay.h>
11*ea4d3310SJavier Carrasco #include <linux/list.h>
12*ea4d3310SJavier Carrasco #include <linux/module.h>
13*ea4d3310SJavier Carrasco #include <linux/property.h>
14*ea4d3310SJavier Carrasco 
15*ea4d3310SJavier Carrasco struct touch_overlay_segment {
16*ea4d3310SJavier Carrasco 	struct list_head list;
17*ea4d3310SJavier Carrasco 	u32 x_origin;
18*ea4d3310SJavier Carrasco 	u32 y_origin;
19*ea4d3310SJavier Carrasco 	u32 x_size;
20*ea4d3310SJavier Carrasco 	u32 y_size;
21*ea4d3310SJavier Carrasco 	u32 key;
22*ea4d3310SJavier Carrasco 	bool pressed;
23*ea4d3310SJavier Carrasco 	int slot;
24*ea4d3310SJavier Carrasco };
25*ea4d3310SJavier Carrasco 
touch_overlay_get_segment(struct fwnode_handle * segment_node,struct touch_overlay_segment * segment,struct input_dev * input)26*ea4d3310SJavier Carrasco static int touch_overlay_get_segment(struct fwnode_handle *segment_node,
27*ea4d3310SJavier Carrasco 				     struct touch_overlay_segment *segment,
28*ea4d3310SJavier Carrasco 				     struct input_dev *input)
29*ea4d3310SJavier Carrasco {
30*ea4d3310SJavier Carrasco 	int error;
31*ea4d3310SJavier Carrasco 
32*ea4d3310SJavier Carrasco 	error = fwnode_property_read_u32(segment_node, "x-origin",
33*ea4d3310SJavier Carrasco 					 &segment->x_origin);
34*ea4d3310SJavier Carrasco 	if (error)
35*ea4d3310SJavier Carrasco 		return error;
36*ea4d3310SJavier Carrasco 
37*ea4d3310SJavier Carrasco 	error = fwnode_property_read_u32(segment_node, "y-origin",
38*ea4d3310SJavier Carrasco 					 &segment->y_origin);
39*ea4d3310SJavier Carrasco 	if (error)
40*ea4d3310SJavier Carrasco 		return error;
41*ea4d3310SJavier Carrasco 
42*ea4d3310SJavier Carrasco 	error = fwnode_property_read_u32(segment_node, "x-size",
43*ea4d3310SJavier Carrasco 					 &segment->x_size);
44*ea4d3310SJavier Carrasco 	if (error)
45*ea4d3310SJavier Carrasco 		return error;
46*ea4d3310SJavier Carrasco 
47*ea4d3310SJavier Carrasco 	error = fwnode_property_read_u32(segment_node, "y-size",
48*ea4d3310SJavier Carrasco 					 &segment->y_size);
49*ea4d3310SJavier Carrasco 	if (error)
50*ea4d3310SJavier Carrasco 		return error;
51*ea4d3310SJavier Carrasco 
52*ea4d3310SJavier Carrasco 	error = fwnode_property_read_u32(segment_node, "linux,code",
53*ea4d3310SJavier Carrasco 					 &segment->key);
54*ea4d3310SJavier Carrasco 	if (!error)
55*ea4d3310SJavier Carrasco 		input_set_capability(input, EV_KEY, segment->key);
56*ea4d3310SJavier Carrasco 	else if (error != -EINVAL)
57*ea4d3310SJavier Carrasco 		return error;
58*ea4d3310SJavier Carrasco 
59*ea4d3310SJavier Carrasco 	return 0;
60*ea4d3310SJavier Carrasco }
61*ea4d3310SJavier Carrasco 
62*ea4d3310SJavier Carrasco /**
63*ea4d3310SJavier Carrasco  * touch_overlay_map - map overlay objects from the device tree and set
64*ea4d3310SJavier Carrasco  * key capabilities if buttons are defined.
65*ea4d3310SJavier Carrasco  * @list: pointer to the list that will hold the segments
66*ea4d3310SJavier Carrasco  * @input: pointer to the already allocated input_dev
67*ea4d3310SJavier Carrasco  *
68*ea4d3310SJavier Carrasco  * Returns 0 on success and error number otherwise.
69*ea4d3310SJavier Carrasco  *
70*ea4d3310SJavier Carrasco  * If buttons are defined, key capabilities are set accordingly.
71*ea4d3310SJavier Carrasco  */
touch_overlay_map(struct list_head * list,struct input_dev * input)72*ea4d3310SJavier Carrasco int touch_overlay_map(struct list_head *list, struct input_dev *input)
73*ea4d3310SJavier Carrasco {
74*ea4d3310SJavier Carrasco 	struct fwnode_handle *fw_segment;
75*ea4d3310SJavier Carrasco 	struct device *dev = input->dev.parent;
76*ea4d3310SJavier Carrasco 	struct touch_overlay_segment *segment;
77*ea4d3310SJavier Carrasco 	int error;
78*ea4d3310SJavier Carrasco 
79*ea4d3310SJavier Carrasco 	struct fwnode_handle *overlay __free(fwnode_handle) =
80*ea4d3310SJavier Carrasco 		device_get_named_child_node(dev, "touch-overlay");
81*ea4d3310SJavier Carrasco 	if (!overlay)
82*ea4d3310SJavier Carrasco 		return 0;
83*ea4d3310SJavier Carrasco 
84*ea4d3310SJavier Carrasco 	fwnode_for_each_available_child_node(overlay, fw_segment) {
85*ea4d3310SJavier Carrasco 		segment = devm_kzalloc(dev, sizeof(*segment), GFP_KERNEL);
86*ea4d3310SJavier Carrasco 		if (!segment) {
87*ea4d3310SJavier Carrasco 			fwnode_handle_put(fw_segment);
88*ea4d3310SJavier Carrasco 			return -ENOMEM;
89*ea4d3310SJavier Carrasco 		}
90*ea4d3310SJavier Carrasco 		error = touch_overlay_get_segment(fw_segment, segment, input);
91*ea4d3310SJavier Carrasco 		if (error) {
92*ea4d3310SJavier Carrasco 			fwnode_handle_put(fw_segment);
93*ea4d3310SJavier Carrasco 			return error;
94*ea4d3310SJavier Carrasco 		}
95*ea4d3310SJavier Carrasco 		list_add_tail(&segment->list, list);
96*ea4d3310SJavier Carrasco 	}
97*ea4d3310SJavier Carrasco 
98*ea4d3310SJavier Carrasco 	return 0;
99*ea4d3310SJavier Carrasco }
100*ea4d3310SJavier Carrasco EXPORT_SYMBOL(touch_overlay_map);
101*ea4d3310SJavier Carrasco 
102*ea4d3310SJavier Carrasco /**
103*ea4d3310SJavier Carrasco  * touch_overlay_get_touchscreen_abs - get abs size from the touchscreen area.
104*ea4d3310SJavier Carrasco  * @list: pointer to the list that holds the segments
105*ea4d3310SJavier Carrasco  * @x: horizontal abs
106*ea4d3310SJavier Carrasco  * @y: vertical abs
107*ea4d3310SJavier Carrasco  */
touch_overlay_get_touchscreen_abs(struct list_head * list,u16 * x,u16 * y)108*ea4d3310SJavier Carrasco void touch_overlay_get_touchscreen_abs(struct list_head *list, u16 *x, u16 *y)
109*ea4d3310SJavier Carrasco {
110*ea4d3310SJavier Carrasco 	struct touch_overlay_segment *segment;
111*ea4d3310SJavier Carrasco 	struct list_head *ptr;
112*ea4d3310SJavier Carrasco 
113*ea4d3310SJavier Carrasco 	list_for_each(ptr, list) {
114*ea4d3310SJavier Carrasco 		segment = list_entry(ptr, struct touch_overlay_segment, list);
115*ea4d3310SJavier Carrasco 		if (!segment->key) {
116*ea4d3310SJavier Carrasco 			*x = segment->x_size - 1;
117*ea4d3310SJavier Carrasco 			*y = segment->y_size - 1;
118*ea4d3310SJavier Carrasco 			break;
119*ea4d3310SJavier Carrasco 		}
120*ea4d3310SJavier Carrasco 	}
121*ea4d3310SJavier Carrasco }
122*ea4d3310SJavier Carrasco EXPORT_SYMBOL(touch_overlay_get_touchscreen_abs);
123*ea4d3310SJavier Carrasco 
touch_overlay_segment_event(struct touch_overlay_segment * seg,struct input_mt_pos * pos)124*ea4d3310SJavier Carrasco static bool touch_overlay_segment_event(struct touch_overlay_segment *seg,
125*ea4d3310SJavier Carrasco 					struct input_mt_pos *pos)
126*ea4d3310SJavier Carrasco {
127*ea4d3310SJavier Carrasco 	if (pos->x >= seg->x_origin && pos->x < (seg->x_origin + seg->x_size) &&
128*ea4d3310SJavier Carrasco 	    pos->y >= seg->y_origin && pos->y < (seg->y_origin + seg->y_size))
129*ea4d3310SJavier Carrasco 		return true;
130*ea4d3310SJavier Carrasco 
131*ea4d3310SJavier Carrasco 	return false;
132*ea4d3310SJavier Carrasco }
133*ea4d3310SJavier Carrasco 
134*ea4d3310SJavier Carrasco /**
135*ea4d3310SJavier Carrasco  * touch_overlay_mapped_touchscreen - check if a touchscreen area is mapped
136*ea4d3310SJavier Carrasco  * @list: pointer to the list that holds the segments
137*ea4d3310SJavier Carrasco  *
138*ea4d3310SJavier Carrasco  * Returns true if a touchscreen area is mapped or false otherwise.
139*ea4d3310SJavier Carrasco  */
touch_overlay_mapped_touchscreen(struct list_head * list)140*ea4d3310SJavier Carrasco bool touch_overlay_mapped_touchscreen(struct list_head *list)
141*ea4d3310SJavier Carrasco {
142*ea4d3310SJavier Carrasco 	struct touch_overlay_segment *segment;
143*ea4d3310SJavier Carrasco 	struct list_head *ptr;
144*ea4d3310SJavier Carrasco 
145*ea4d3310SJavier Carrasco 	list_for_each(ptr, list) {
146*ea4d3310SJavier Carrasco 		segment = list_entry(ptr, struct touch_overlay_segment, list);
147*ea4d3310SJavier Carrasco 		if (!segment->key)
148*ea4d3310SJavier Carrasco 			return true;
149*ea4d3310SJavier Carrasco 	}
150*ea4d3310SJavier Carrasco 
151*ea4d3310SJavier Carrasco 	return false;
152*ea4d3310SJavier Carrasco }
153*ea4d3310SJavier Carrasco EXPORT_SYMBOL(touch_overlay_mapped_touchscreen);
154*ea4d3310SJavier Carrasco 
touch_overlay_event_on_ts(struct list_head * list,struct input_mt_pos * pos)155*ea4d3310SJavier Carrasco static bool touch_overlay_event_on_ts(struct list_head *list,
156*ea4d3310SJavier Carrasco 				      struct input_mt_pos *pos)
157*ea4d3310SJavier Carrasco {
158*ea4d3310SJavier Carrasco 	struct touch_overlay_segment *segment;
159*ea4d3310SJavier Carrasco 	struct list_head *ptr;
160*ea4d3310SJavier Carrasco 
161*ea4d3310SJavier Carrasco 	list_for_each(ptr, list) {
162*ea4d3310SJavier Carrasco 		segment = list_entry(ptr, struct touch_overlay_segment, list);
163*ea4d3310SJavier Carrasco 		if (segment->key)
164*ea4d3310SJavier Carrasco 			continue;
165*ea4d3310SJavier Carrasco 
166*ea4d3310SJavier Carrasco 		if (touch_overlay_segment_event(segment, pos)) {
167*ea4d3310SJavier Carrasco 			pos->x -= segment->x_origin;
168*ea4d3310SJavier Carrasco 			pos->y -= segment->y_origin;
169*ea4d3310SJavier Carrasco 			return true;
170*ea4d3310SJavier Carrasco 		}
171*ea4d3310SJavier Carrasco 		/* ignore touch events outside the defined area */
172*ea4d3310SJavier Carrasco 		return false;
173*ea4d3310SJavier Carrasco 	}
174*ea4d3310SJavier Carrasco 
175*ea4d3310SJavier Carrasco 	return true;
176*ea4d3310SJavier Carrasco }
177*ea4d3310SJavier Carrasco 
touch_overlay_button_event(struct input_dev * input,struct touch_overlay_segment * segment,struct input_mt_pos * pos,int slot)178*ea4d3310SJavier Carrasco static bool touch_overlay_button_event(struct input_dev *input,
179*ea4d3310SJavier Carrasco 				       struct touch_overlay_segment *segment,
180*ea4d3310SJavier Carrasco 				       struct input_mt_pos *pos, int slot)
181*ea4d3310SJavier Carrasco {
182*ea4d3310SJavier Carrasco 	struct input_mt *mt = input->mt;
183*ea4d3310SJavier Carrasco 	struct input_mt_slot *s = &mt->slots[slot];
184*ea4d3310SJavier Carrasco 	bool button_contact = touch_overlay_segment_event(segment, pos);
185*ea4d3310SJavier Carrasco 
186*ea4d3310SJavier Carrasco 	if (segment->slot == slot && segment->pressed) {
187*ea4d3310SJavier Carrasco 		/* sliding out of the button releases it */
188*ea4d3310SJavier Carrasco 		if (!button_contact) {
189*ea4d3310SJavier Carrasco 			input_report_key(input, segment->key, false);
190*ea4d3310SJavier Carrasco 			segment->pressed = false;
191*ea4d3310SJavier Carrasco 			/* keep available for a possible touch event */
192*ea4d3310SJavier Carrasco 			return false;
193*ea4d3310SJavier Carrasco 		}
194*ea4d3310SJavier Carrasco 		/* ignore sliding on the button while pressed */
195*ea4d3310SJavier Carrasco 		s->frame = mt->frame;
196*ea4d3310SJavier Carrasco 		return true;
197*ea4d3310SJavier Carrasco 	} else if (button_contact) {
198*ea4d3310SJavier Carrasco 		input_report_key(input, segment->key, true);
199*ea4d3310SJavier Carrasco 		s->frame = mt->frame;
200*ea4d3310SJavier Carrasco 		segment->slot = slot;
201*ea4d3310SJavier Carrasco 		segment->pressed = true;
202*ea4d3310SJavier Carrasco 		return true;
203*ea4d3310SJavier Carrasco 	}
204*ea4d3310SJavier Carrasco 
205*ea4d3310SJavier Carrasco 	return false;
206*ea4d3310SJavier Carrasco }
207*ea4d3310SJavier Carrasco 
208*ea4d3310SJavier Carrasco /**
209*ea4d3310SJavier Carrasco  * touch_overlay_sync_frame - update the status of the segments and report
210*ea4d3310SJavier Carrasco  * buttons whose tracked slot is unused.
211*ea4d3310SJavier Carrasco  * @list: pointer to the list that holds the segments
212*ea4d3310SJavier Carrasco  * @input: pointer to the input device associated to the contact
213*ea4d3310SJavier Carrasco  */
touch_overlay_sync_frame(struct list_head * list,struct input_dev * input)214*ea4d3310SJavier Carrasco void touch_overlay_sync_frame(struct list_head *list, struct input_dev *input)
215*ea4d3310SJavier Carrasco {
216*ea4d3310SJavier Carrasco 	struct touch_overlay_segment *segment;
217*ea4d3310SJavier Carrasco 	struct input_mt *mt = input->mt;
218*ea4d3310SJavier Carrasco 	struct input_mt_slot *s;
219*ea4d3310SJavier Carrasco 	struct list_head *ptr;
220*ea4d3310SJavier Carrasco 
221*ea4d3310SJavier Carrasco 	list_for_each(ptr, list) {
222*ea4d3310SJavier Carrasco 		segment = list_entry(ptr, struct touch_overlay_segment, list);
223*ea4d3310SJavier Carrasco 		if (!segment->key)
224*ea4d3310SJavier Carrasco 			continue;
225*ea4d3310SJavier Carrasco 
226*ea4d3310SJavier Carrasco 		s = &mt->slots[segment->slot];
227*ea4d3310SJavier Carrasco 		if (!input_mt_is_used(mt, s) && segment->pressed) {
228*ea4d3310SJavier Carrasco 			input_report_key(input, segment->key, false);
229*ea4d3310SJavier Carrasco 			segment->pressed = false;
230*ea4d3310SJavier Carrasco 		}
231*ea4d3310SJavier Carrasco 	}
232*ea4d3310SJavier Carrasco }
233*ea4d3310SJavier Carrasco EXPORT_SYMBOL(touch_overlay_sync_frame);
234*ea4d3310SJavier Carrasco 
235*ea4d3310SJavier Carrasco /**
236*ea4d3310SJavier Carrasco  * touch_overlay_process_contact - process contacts according to the overlay
237*ea4d3310SJavier Carrasco  * mapping. This function acts as a filter to release the calling driver
238*ea4d3310SJavier Carrasco  * from the contacts that are either related to overlay buttons or out of the
239*ea4d3310SJavier Carrasco  * overlay touchscreen area, if defined.
240*ea4d3310SJavier Carrasco  * @list: pointer to the list that holds the segments
241*ea4d3310SJavier Carrasco  * @input: pointer to the input device associated to the contact
242*ea4d3310SJavier Carrasco  * @pos: pointer to the contact position
243*ea4d3310SJavier Carrasco  * @slot: slot associated to the contact (0 if multitouch is not supported)
244*ea4d3310SJavier Carrasco  *
245*ea4d3310SJavier Carrasco  * Returns true if the contact was processed (reported for valid key events
246*ea4d3310SJavier Carrasco  * and dropped for contacts outside the overlay touchscreen area) or false
247*ea4d3310SJavier Carrasco  * if the contact must be processed by the caller. In that case this function
248*ea4d3310SJavier Carrasco  * shifts the (x,y) coordinates to the overlay touchscreen axis if required.
249*ea4d3310SJavier Carrasco  */
touch_overlay_process_contact(struct list_head * list,struct input_dev * input,struct input_mt_pos * pos,int slot)250*ea4d3310SJavier Carrasco bool touch_overlay_process_contact(struct list_head *list,
251*ea4d3310SJavier Carrasco 				   struct input_dev *input,
252*ea4d3310SJavier Carrasco 				   struct input_mt_pos *pos, int slot)
253*ea4d3310SJavier Carrasco {
254*ea4d3310SJavier Carrasco 	struct touch_overlay_segment *segment;
255*ea4d3310SJavier Carrasco 	struct list_head *ptr;
256*ea4d3310SJavier Carrasco 
257*ea4d3310SJavier Carrasco 	/*
258*ea4d3310SJavier Carrasco 	 * buttons must be prioritized over overlay touchscreens to account for
259*ea4d3310SJavier Carrasco 	 * overlappings e.g. a button inside the touchscreen area.
260*ea4d3310SJavier Carrasco 	 */
261*ea4d3310SJavier Carrasco 	list_for_each(ptr, list) {
262*ea4d3310SJavier Carrasco 		segment = list_entry(ptr, struct touch_overlay_segment, list);
263*ea4d3310SJavier Carrasco 		if (segment->key &&
264*ea4d3310SJavier Carrasco 		    touch_overlay_button_event(input, segment, pos, slot))
265*ea4d3310SJavier Carrasco 			return true;
266*ea4d3310SJavier Carrasco 	}
267*ea4d3310SJavier Carrasco 
268*ea4d3310SJavier Carrasco 	/*
269*ea4d3310SJavier Carrasco 	 * valid contacts on the overlay touchscreen are left for the client
270*ea4d3310SJavier Carrasco 	 * to be processed/reported according to its (possibly) unique features.
271*ea4d3310SJavier Carrasco 	 */
272*ea4d3310SJavier Carrasco 	return !touch_overlay_event_on_ts(list, pos);
273*ea4d3310SJavier Carrasco }
274*ea4d3310SJavier Carrasco EXPORT_SYMBOL(touch_overlay_process_contact);
275*ea4d3310SJavier Carrasco 
276*ea4d3310SJavier Carrasco MODULE_LICENSE("GPL");
277*ea4d3310SJavier Carrasco MODULE_DESCRIPTION("Helper functions for overlay objects on touch devices");
278