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