162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only 262306a36Sopenharmony_ci/* 362306a36Sopenharmony_ci * Generic helper functions for touchscreens and other two-dimensional 462306a36Sopenharmony_ci * pointing devices 562306a36Sopenharmony_ci * 662306a36Sopenharmony_ci * Copyright (c) 2014 Sebastian Reichel <sre@kernel.org> 762306a36Sopenharmony_ci */ 862306a36Sopenharmony_ci 962306a36Sopenharmony_ci#include <linux/property.h> 1062306a36Sopenharmony_ci#include <linux/input.h> 1162306a36Sopenharmony_ci#include <linux/input/mt.h> 1262306a36Sopenharmony_ci#include <linux/input/touchscreen.h> 1362306a36Sopenharmony_ci#include <linux/module.h> 1462306a36Sopenharmony_ci 1562306a36Sopenharmony_cistatic bool touchscreen_get_prop_u32(struct device *dev, 1662306a36Sopenharmony_ci const char *property, 1762306a36Sopenharmony_ci unsigned int default_value, 1862306a36Sopenharmony_ci unsigned int *value) 1962306a36Sopenharmony_ci{ 2062306a36Sopenharmony_ci u32 val; 2162306a36Sopenharmony_ci int error; 2262306a36Sopenharmony_ci 2362306a36Sopenharmony_ci error = device_property_read_u32(dev, property, &val); 2462306a36Sopenharmony_ci if (error) { 2562306a36Sopenharmony_ci *value = default_value; 2662306a36Sopenharmony_ci return false; 2762306a36Sopenharmony_ci } 2862306a36Sopenharmony_ci 2962306a36Sopenharmony_ci *value = val; 3062306a36Sopenharmony_ci return true; 3162306a36Sopenharmony_ci} 3262306a36Sopenharmony_ci 3362306a36Sopenharmony_cistatic void touchscreen_set_params(struct input_dev *dev, 3462306a36Sopenharmony_ci unsigned long axis, 3562306a36Sopenharmony_ci int min, int max, int fuzz) 3662306a36Sopenharmony_ci{ 3762306a36Sopenharmony_ci struct input_absinfo *absinfo; 3862306a36Sopenharmony_ci 3962306a36Sopenharmony_ci if (!test_bit(axis, dev->absbit)) { 4062306a36Sopenharmony_ci dev_warn(&dev->dev, 4162306a36Sopenharmony_ci "Parameters are specified but the axis %lu is not set up\n", 4262306a36Sopenharmony_ci axis); 4362306a36Sopenharmony_ci return; 4462306a36Sopenharmony_ci } 4562306a36Sopenharmony_ci 4662306a36Sopenharmony_ci absinfo = &dev->absinfo[axis]; 4762306a36Sopenharmony_ci absinfo->minimum = min; 4862306a36Sopenharmony_ci absinfo->maximum = max; 4962306a36Sopenharmony_ci absinfo->fuzz = fuzz; 5062306a36Sopenharmony_ci} 5162306a36Sopenharmony_ci 5262306a36Sopenharmony_ci/** 5362306a36Sopenharmony_ci * touchscreen_parse_properties - parse common touchscreen properties 5462306a36Sopenharmony_ci * @input: input device that should be parsed 5562306a36Sopenharmony_ci * @multitouch: specifies whether parsed properties should be applied to 5662306a36Sopenharmony_ci * single-touch or multi-touch axes 5762306a36Sopenharmony_ci * @prop: pointer to a struct touchscreen_properties into which to store 5862306a36Sopenharmony_ci * axis swap and invert info for use with touchscreen_report_x_y(); 5962306a36Sopenharmony_ci * or %NULL 6062306a36Sopenharmony_ci * 6162306a36Sopenharmony_ci * This function parses common properties for touchscreens and sets up the 6262306a36Sopenharmony_ci * input device accordingly. The function keeps previously set up default 6362306a36Sopenharmony_ci * values if no value is specified. 6462306a36Sopenharmony_ci */ 6562306a36Sopenharmony_civoid touchscreen_parse_properties(struct input_dev *input, bool multitouch, 6662306a36Sopenharmony_ci struct touchscreen_properties *prop) 6762306a36Sopenharmony_ci{ 6862306a36Sopenharmony_ci struct device *dev = input->dev.parent; 6962306a36Sopenharmony_ci struct input_absinfo *absinfo; 7062306a36Sopenharmony_ci unsigned int axis, axis_x, axis_y; 7162306a36Sopenharmony_ci unsigned int minimum, maximum, fuzz; 7262306a36Sopenharmony_ci bool data_present; 7362306a36Sopenharmony_ci 7462306a36Sopenharmony_ci input_alloc_absinfo(input); 7562306a36Sopenharmony_ci if (!input->absinfo) 7662306a36Sopenharmony_ci return; 7762306a36Sopenharmony_ci 7862306a36Sopenharmony_ci axis_x = multitouch ? ABS_MT_POSITION_X : ABS_X; 7962306a36Sopenharmony_ci axis_y = multitouch ? ABS_MT_POSITION_Y : ABS_Y; 8062306a36Sopenharmony_ci 8162306a36Sopenharmony_ci data_present = touchscreen_get_prop_u32(dev, "touchscreen-min-x", 8262306a36Sopenharmony_ci input_abs_get_min(input, axis_x), 8362306a36Sopenharmony_ci &minimum); 8462306a36Sopenharmony_ci data_present |= touchscreen_get_prop_u32(dev, "touchscreen-size-x", 8562306a36Sopenharmony_ci input_abs_get_max(input, 8662306a36Sopenharmony_ci axis_x) + 1, 8762306a36Sopenharmony_ci &maximum); 8862306a36Sopenharmony_ci data_present |= touchscreen_get_prop_u32(dev, "touchscreen-fuzz-x", 8962306a36Sopenharmony_ci input_abs_get_fuzz(input, axis_x), 9062306a36Sopenharmony_ci &fuzz); 9162306a36Sopenharmony_ci if (data_present) 9262306a36Sopenharmony_ci touchscreen_set_params(input, axis_x, minimum, maximum - 1, fuzz); 9362306a36Sopenharmony_ci 9462306a36Sopenharmony_ci data_present = touchscreen_get_prop_u32(dev, "touchscreen-min-y", 9562306a36Sopenharmony_ci input_abs_get_min(input, axis_y), 9662306a36Sopenharmony_ci &minimum); 9762306a36Sopenharmony_ci data_present |= touchscreen_get_prop_u32(dev, "touchscreen-size-y", 9862306a36Sopenharmony_ci input_abs_get_max(input, 9962306a36Sopenharmony_ci axis_y) + 1, 10062306a36Sopenharmony_ci &maximum); 10162306a36Sopenharmony_ci data_present |= touchscreen_get_prop_u32(dev, "touchscreen-fuzz-y", 10262306a36Sopenharmony_ci input_abs_get_fuzz(input, axis_y), 10362306a36Sopenharmony_ci &fuzz); 10462306a36Sopenharmony_ci if (data_present) 10562306a36Sopenharmony_ci touchscreen_set_params(input, axis_y, minimum, maximum - 1, fuzz); 10662306a36Sopenharmony_ci 10762306a36Sopenharmony_ci axis = multitouch ? ABS_MT_PRESSURE : ABS_PRESSURE; 10862306a36Sopenharmony_ci data_present = touchscreen_get_prop_u32(dev, 10962306a36Sopenharmony_ci "touchscreen-max-pressure", 11062306a36Sopenharmony_ci input_abs_get_max(input, axis), 11162306a36Sopenharmony_ci &maximum); 11262306a36Sopenharmony_ci data_present |= touchscreen_get_prop_u32(dev, 11362306a36Sopenharmony_ci "touchscreen-fuzz-pressure", 11462306a36Sopenharmony_ci input_abs_get_fuzz(input, axis), 11562306a36Sopenharmony_ci &fuzz); 11662306a36Sopenharmony_ci if (data_present) 11762306a36Sopenharmony_ci touchscreen_set_params(input, axis, 0, maximum, fuzz); 11862306a36Sopenharmony_ci 11962306a36Sopenharmony_ci if (!prop) 12062306a36Sopenharmony_ci return; 12162306a36Sopenharmony_ci 12262306a36Sopenharmony_ci prop->max_x = input_abs_get_max(input, axis_x); 12362306a36Sopenharmony_ci prop->max_y = input_abs_get_max(input, axis_y); 12462306a36Sopenharmony_ci 12562306a36Sopenharmony_ci prop->invert_x = 12662306a36Sopenharmony_ci device_property_read_bool(dev, "touchscreen-inverted-x"); 12762306a36Sopenharmony_ci if (prop->invert_x) { 12862306a36Sopenharmony_ci absinfo = &input->absinfo[axis_x]; 12962306a36Sopenharmony_ci absinfo->maximum -= absinfo->minimum; 13062306a36Sopenharmony_ci absinfo->minimum = 0; 13162306a36Sopenharmony_ci } 13262306a36Sopenharmony_ci 13362306a36Sopenharmony_ci prop->invert_y = 13462306a36Sopenharmony_ci device_property_read_bool(dev, "touchscreen-inverted-y"); 13562306a36Sopenharmony_ci if (prop->invert_y) { 13662306a36Sopenharmony_ci absinfo = &input->absinfo[axis_y]; 13762306a36Sopenharmony_ci absinfo->maximum -= absinfo->minimum; 13862306a36Sopenharmony_ci absinfo->minimum = 0; 13962306a36Sopenharmony_ci } 14062306a36Sopenharmony_ci 14162306a36Sopenharmony_ci prop->swap_x_y = 14262306a36Sopenharmony_ci device_property_read_bool(dev, "touchscreen-swapped-x-y"); 14362306a36Sopenharmony_ci if (prop->swap_x_y) 14462306a36Sopenharmony_ci swap(input->absinfo[axis_x], input->absinfo[axis_y]); 14562306a36Sopenharmony_ci} 14662306a36Sopenharmony_ciEXPORT_SYMBOL(touchscreen_parse_properties); 14762306a36Sopenharmony_ci 14862306a36Sopenharmony_cistatic void 14962306a36Sopenharmony_citouchscreen_apply_prop_to_x_y(const struct touchscreen_properties *prop, 15062306a36Sopenharmony_ci unsigned int *x, unsigned int *y) 15162306a36Sopenharmony_ci{ 15262306a36Sopenharmony_ci if (prop->invert_x) 15362306a36Sopenharmony_ci *x = prop->max_x - *x; 15462306a36Sopenharmony_ci 15562306a36Sopenharmony_ci if (prop->invert_y) 15662306a36Sopenharmony_ci *y = prop->max_y - *y; 15762306a36Sopenharmony_ci 15862306a36Sopenharmony_ci if (prop->swap_x_y) 15962306a36Sopenharmony_ci swap(*x, *y); 16062306a36Sopenharmony_ci} 16162306a36Sopenharmony_ci 16262306a36Sopenharmony_ci/** 16362306a36Sopenharmony_ci * touchscreen_set_mt_pos - Set input_mt_pos coordinates 16462306a36Sopenharmony_ci * @pos: input_mt_pos to set coordinates of 16562306a36Sopenharmony_ci * @prop: pointer to a struct touchscreen_properties 16662306a36Sopenharmony_ci * @x: X coordinate to store in pos 16762306a36Sopenharmony_ci * @y: Y coordinate to store in pos 16862306a36Sopenharmony_ci * 16962306a36Sopenharmony_ci * Adjust the passed in x and y values applying any axis inversion and 17062306a36Sopenharmony_ci * swapping requested in the passed in touchscreen_properties and store 17162306a36Sopenharmony_ci * the result in a struct input_mt_pos. 17262306a36Sopenharmony_ci */ 17362306a36Sopenharmony_civoid touchscreen_set_mt_pos(struct input_mt_pos *pos, 17462306a36Sopenharmony_ci const struct touchscreen_properties *prop, 17562306a36Sopenharmony_ci unsigned int x, unsigned int y) 17662306a36Sopenharmony_ci{ 17762306a36Sopenharmony_ci touchscreen_apply_prop_to_x_y(prop, &x, &y); 17862306a36Sopenharmony_ci pos->x = x; 17962306a36Sopenharmony_ci pos->y = y; 18062306a36Sopenharmony_ci} 18162306a36Sopenharmony_ciEXPORT_SYMBOL(touchscreen_set_mt_pos); 18262306a36Sopenharmony_ci 18362306a36Sopenharmony_ci/** 18462306a36Sopenharmony_ci * touchscreen_report_pos - Report touchscreen coordinates 18562306a36Sopenharmony_ci * @input: input_device to report coordinates for 18662306a36Sopenharmony_ci * @prop: pointer to a struct touchscreen_properties 18762306a36Sopenharmony_ci * @x: X coordinate to report 18862306a36Sopenharmony_ci * @y: Y coordinate to report 18962306a36Sopenharmony_ci * @multitouch: Report coordinates on single-touch or multi-touch axes 19062306a36Sopenharmony_ci * 19162306a36Sopenharmony_ci * Adjust the passed in x and y values applying any axis inversion and 19262306a36Sopenharmony_ci * swapping requested in the passed in touchscreen_properties and then 19362306a36Sopenharmony_ci * report the resulting coordinates on the input_dev's x and y axis. 19462306a36Sopenharmony_ci */ 19562306a36Sopenharmony_civoid touchscreen_report_pos(struct input_dev *input, 19662306a36Sopenharmony_ci const struct touchscreen_properties *prop, 19762306a36Sopenharmony_ci unsigned int x, unsigned int y, 19862306a36Sopenharmony_ci bool multitouch) 19962306a36Sopenharmony_ci{ 20062306a36Sopenharmony_ci touchscreen_apply_prop_to_x_y(prop, &x, &y); 20162306a36Sopenharmony_ci input_report_abs(input, multitouch ? ABS_MT_POSITION_X : ABS_X, x); 20262306a36Sopenharmony_ci input_report_abs(input, multitouch ? ABS_MT_POSITION_Y : ABS_Y, y); 20362306a36Sopenharmony_ci} 20462306a36Sopenharmony_ciEXPORT_SYMBOL(touchscreen_report_pos); 20562306a36Sopenharmony_ci 20662306a36Sopenharmony_ciMODULE_LICENSE("GPL v2"); 20762306a36Sopenharmony_ciMODULE_DESCRIPTION("Helper functions for touchscreens and other devices"); 208