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 16 struct 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 27 static 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 */ 73 int 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 } 101 EXPORT_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 */ 109 void 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 } 123 EXPORT_SYMBOL(touch_overlay_get_touchscreen_abs); 124 125 static 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 */ 141 bool 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 } 154 EXPORT_SYMBOL(touch_overlay_mapped_touchscreen); 155 156 static 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 179 static 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 */ 215 void 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 } 234 EXPORT_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 */ 251 bool 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 } 275 EXPORT_SYMBOL(touch_overlay_process_contact); 276 277 MODULE_LICENSE("GPL"); 278 MODULE_DESCRIPTION("Helper functions for overlay objects on touch devices"); 279