1// SPDX-License-Identifier: GPL-2.0-only
2/*
3 * Generic helper functions for touchscreens and other two-dimensional
4 * pointing devices
5 *
6 * Copyright (c) 2014 Sebastian Reichel <sre@kernel.org>
7 */
8
9#include <linux/export.h>
10#include <linux/property.h>
11#include <linux/input.h>
12#include <linux/input/mt.h>
13#include <linux/input/touchscreen.h>
14#include <linux/module.h>
15
16static bool touchscreen_get_prop_u32(struct device *dev,
17 const char *property,
18 unsigned int default_value,
19 unsigned int *value)
20{
21 u32 val;
22 int error;
23
24 error = device_property_read_u32(dev, propname: property, val: &val);
25 if (error) {
26 *value = default_value;
27 return false;
28 }
29
30 *value = val;
31 return true;
32}
33
34static void touchscreen_set_params(struct input_dev *dev,
35 unsigned long axis,
36 int min, int max, int fuzz)
37{
38 struct input_absinfo *absinfo;
39
40 if (!test_bit(axis, dev->absbit)) {
41 dev_warn(&dev->dev,
42 "Parameters are specified but the axis %lu is not set up\n",
43 axis);
44 return;
45 }
46
47 absinfo = &dev->absinfo[axis];
48 absinfo->minimum = min;
49 absinfo->maximum = max;
50 absinfo->fuzz = fuzz;
51}
52
53/**
54 * touchscreen_parse_properties - parse common touchscreen properties
55 * @input: input device that should be parsed
56 * @multitouch: specifies whether parsed properties should be applied to
57 * single-touch or multi-touch axes
58 * @prop: pointer to a struct touchscreen_properties into which to store
59 * axis swap and invert info for use with touchscreen_report_x_y();
60 * or %NULL
61 *
62 * This function parses common properties for touchscreens and sets up the
63 * input device accordingly. The function keeps previously set up default
64 * values if no value is specified.
65 */
66void touchscreen_parse_properties(struct input_dev *input, bool multitouch,
67 struct touchscreen_properties *prop)
68{
69 struct device *dev = input->dev.parent;
70 struct input_absinfo *absinfo;
71 unsigned int axis, axis_x, axis_y;
72 unsigned int minimum, maximum, fuzz;
73 bool data_present;
74
75 input_alloc_absinfo(dev: input);
76 if (!input->absinfo)
77 return;
78
79 axis_x = multitouch ? ABS_MT_POSITION_X : ABS_X;
80 axis_y = multitouch ? ABS_MT_POSITION_Y : ABS_Y;
81
82 data_present = touchscreen_get_prop_u32(dev, property: "touchscreen-min-x",
83 default_value: input_abs_get_min(dev: input, axis: axis_x),
84 value: &minimum);
85 data_present |= touchscreen_get_prop_u32(dev, property: "touchscreen-size-x",
86 default_value: input_abs_get_max(dev: input,
87 axis: axis_x) + 1,
88 value: &maximum);
89 data_present |= touchscreen_get_prop_u32(dev, property: "touchscreen-fuzz-x",
90 default_value: input_abs_get_fuzz(dev: input, axis: axis_x),
91 value: &fuzz);
92 if (data_present)
93 touchscreen_set_params(dev: input, axis: axis_x, min: minimum, max: maximum - 1, fuzz);
94
95 data_present = touchscreen_get_prop_u32(dev, property: "touchscreen-min-y",
96 default_value: input_abs_get_min(dev: input, axis: axis_y),
97 value: &minimum);
98 data_present |= touchscreen_get_prop_u32(dev, property: "touchscreen-size-y",
99 default_value: input_abs_get_max(dev: input,
100 axis: axis_y) + 1,
101 value: &maximum);
102 data_present |= touchscreen_get_prop_u32(dev, property: "touchscreen-fuzz-y",
103 default_value: input_abs_get_fuzz(dev: input, axis: axis_y),
104 value: &fuzz);
105 if (data_present)
106 touchscreen_set_params(dev: input, axis: axis_y, min: minimum, max: maximum - 1, fuzz);
107
108 axis = multitouch ? ABS_MT_PRESSURE : ABS_PRESSURE;
109 data_present = touchscreen_get_prop_u32(dev,
110 property: "touchscreen-max-pressure",
111 default_value: input_abs_get_max(dev: input, axis),
112 value: &maximum);
113 data_present |= touchscreen_get_prop_u32(dev,
114 property: "touchscreen-fuzz-pressure",
115 default_value: input_abs_get_fuzz(dev: input, axis),
116 value: &fuzz);
117 if (data_present)
118 touchscreen_set_params(dev: input, axis, min: 0, max: maximum, fuzz);
119
120 if (!prop)
121 return;
122
123 prop->max_x = input_abs_get_max(dev: input, axis: axis_x);
124 prop->max_y = input_abs_get_max(dev: input, axis: axis_y);
125
126 prop->invert_x =
127 device_property_read_bool(dev, propname: "touchscreen-inverted-x");
128 if (prop->invert_x) {
129 absinfo = &input->absinfo[axis_x];
130 absinfo->maximum -= absinfo->minimum;
131 absinfo->minimum = 0;
132 }
133
134 prop->invert_y =
135 device_property_read_bool(dev, propname: "touchscreen-inverted-y");
136 if (prop->invert_y) {
137 absinfo = &input->absinfo[axis_y];
138 absinfo->maximum -= absinfo->minimum;
139 absinfo->minimum = 0;
140 }
141
142 prop->swap_x_y =
143 device_property_read_bool(dev, propname: "touchscreen-swapped-x-y");
144 if (prop->swap_x_y)
145 swap(input->absinfo[axis_x], input->absinfo[axis_y]);
146}
147EXPORT_SYMBOL(touchscreen_parse_properties);
148
149static void
150touchscreen_apply_prop_to_x_y(const struct touchscreen_properties *prop,
151 unsigned int *x, unsigned int *y)
152{
153 if (prop->invert_x)
154 *x = prop->max_x - *x;
155
156 if (prop->invert_y)
157 *y = prop->max_y - *y;
158
159 if (prop->swap_x_y)
160 swap(*x, *y);
161}
162
163/**
164 * touchscreen_set_mt_pos - Set input_mt_pos coordinates
165 * @pos: input_mt_pos to set coordinates of
166 * @prop: pointer to a struct touchscreen_properties
167 * @x: X coordinate to store in pos
168 * @y: Y coordinate to store in pos
169 *
170 * Adjust the passed in x and y values applying any axis inversion and
171 * swapping requested in the passed in touchscreen_properties and store
172 * the result in a struct input_mt_pos.
173 */
174void touchscreen_set_mt_pos(struct input_mt_pos *pos,
175 const struct touchscreen_properties *prop,
176 unsigned int x, unsigned int y)
177{
178 touchscreen_apply_prop_to_x_y(prop, x: &x, y: &y);
179 pos->x = x;
180 pos->y = y;
181}
182EXPORT_SYMBOL(touchscreen_set_mt_pos);
183
184/**
185 * touchscreen_report_pos - Report touchscreen coordinates
186 * @input: input_device to report coordinates for
187 * @prop: pointer to a struct touchscreen_properties
188 * @x: X coordinate to report
189 * @y: Y coordinate to report
190 * @multitouch: Report coordinates on single-touch or multi-touch axes
191 *
192 * Adjust the passed in x and y values applying any axis inversion and
193 * swapping requested in the passed in touchscreen_properties and then
194 * report the resulting coordinates on the input_dev's x and y axis.
195 */
196void touchscreen_report_pos(struct input_dev *input,
197 const struct touchscreen_properties *prop,
198 unsigned int x, unsigned int y,
199 bool multitouch)
200{
201 touchscreen_apply_prop_to_x_y(prop, x: &x, y: &y);
202 input_report_abs(dev: input, code: multitouch ? ABS_MT_POSITION_X : ABS_X, value: x);
203 input_report_abs(dev: input, code: multitouch ? ABS_MT_POSITION_Y : ABS_Y, value: y);
204}
205EXPORT_SYMBOL(touchscreen_report_pos);
206
207MODULE_LICENSE("GPL v2");
208MODULE_DESCRIPTION("Helper functions for touchscreens and other devices");
209