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