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 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 */ 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 */ 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 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 */ 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 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 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 */ 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 */ 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