File Index
Files and directories
Sign in
FILE

touch-overlay.c

File preview with syntax highlighting.

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