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