From: Dmitry Torokhov <dmitry.torokhov@gmail.com>
To: Hans de Goede <hansg@kernel.org>, David Heidelberg <david@ixit.cz>
Cc: Neil Armstrong <neil.armstrong@linaro.org>,
Val Packett <val@packett.cool>,
Hendrik Noack <hendrik-noack@gmx.de>,
Ferass El Hafidi <funderscore@postmarketos.org>,
Sasha Finkelstein <fnkl.kernel@gmail.com>,
Jeff LaBundy <jeff@labundy.com>,
linux-input@vger.kernel.org, asahi@lists.linux.dev,
linux-arm-kernel@lists.infradead.org,
linux-kernel@vger.kernel.org
Subject: [PATCH 1/4] Input: touchscreen - parse touchscreen-x-mm and touchscreen-y-mm
Date: Mon, 2 Nov 2026 20:47:04 -0800 [thread overview]
Message-ID: <20261103044708.2461481-2-dmitry.torokhov@gmail.com> (raw)
In-Reply-To: <20261103044708.2461481-1-dmitry.torokhov@gmail.com>
The touchscreen device tree bindings define common touchscreen-x-mm and
touchscreen-y-mm properties specifying the physical dimensions of the
touchscreen in millimeters. However, touchscreen_parse_properties() does
not parse these properties, forcing individual drivers to parse them
manually or fall back to arbitrary resolution values.
Parse touchscreen-x-mm and touchscreen-y-mm in
touchscreen_parse_properties() and calculate axis resolution in units
per millimeter as the coordinate count (maximum - minimum + 1) divided
by the physical dimension, rounded to the nearest integer. Guard against
zero physical size or invalid axis ranges, and preserve pre-existing
resolution settings when the properties are omitted. Apply resolution
before early exit so callers passing a NULL properties pointer still
get axes configured and so coordinate inversion and axis swapping
continue to work seamlessly. Also add KUnit test coverage for resolution
calculation.
Signed-off-by: Dmitry Torokhov <dmitry.torokhov@gmail.com>
---
drivers/input/tests/Makefile | 1 +
drivers/input/tests/touchscreen_test.c | 209 +++++++++++++++++++++++++
drivers/input/touchscreen.c | 18 ++-
3 files changed, 223 insertions(+), 5 deletions(-)
create mode 100644 drivers/input/tests/touchscreen_test.c
diff --git a/drivers/input/tests/Makefile b/drivers/input/tests/Makefile
index 90cf954181bc..b5e769d9f8cc 100644
--- a/drivers/input/tests/Makefile
+++ b/drivers/input/tests/Makefile
@@ -1,3 +1,4 @@
# SPDX-License-Identifier: GPL-2.0
obj-$(CONFIG_INPUT_KUNIT_TEST) += input_test.o
+obj-$(CONFIG_INPUT_KUNIT_TEST) += touchscreen_test.o
diff --git a/drivers/input/tests/touchscreen_test.c b/drivers/input/tests/touchscreen_test.c
new file mode 100644
index 000000000000..f8661c349fcc
--- /dev/null
+++ b/drivers/input/tests/touchscreen_test.c
@@ -0,0 +1,209 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * KUnit test for touchscreen property parsing.
+ */
+
+#include <linux/input.h>
+#include <linux/input/touchscreen.h>
+#include <linux/property.h>
+
+#include <kunit/device.h>
+#include <kunit/test.h>
+
+static struct input_dev *
+touchscreen_test_create_device(struct kunit *test,
+ const struct property_entry *props)
+{
+ struct input_dev *input_dev;
+ struct device *dev;
+
+ dev = kunit_device_register(test, "touchscreen-test-dev");
+ KUNIT_ASSERT_NOT_ERR_OR_NULL(test, dev);
+
+ if (props)
+ KUNIT_ASSERT_EQ(test,
+ device_create_managed_software_node(dev, props, NULL),
+ 0);
+
+ input_dev = devm_input_allocate_device(dev);
+ KUNIT_ASSERT_NOT_ERR_OR_NULL(test, input_dev);
+
+ return input_dev;
+}
+
+static void touchscreen_test_resolution_single(struct kunit *test)
+{
+ const struct property_entry props[] = {
+ PROPERTY_ENTRY_U32("touchscreen-x-mm", 100),
+ PROPERTY_ENTRY_U32("touchscreen-y-mm", 200),
+ { }
+ };
+ struct touchscreen_properties prop;
+ struct input_dev *input_dev;
+
+ input_dev = touchscreen_test_create_device(test, props);
+
+ /* Driver-supplied ranges: 0..999 (1000 units), 0..1999 (2000 units) */
+ input_set_abs_params(input_dev, ABS_X, 0, 999, 0, 0);
+ input_set_abs_params(input_dev, ABS_Y, 0, 1999, 0, 0);
+
+ touchscreen_parse_properties(input_dev, false, &prop);
+
+ KUNIT_EXPECT_EQ(test, input_abs_get_res(input_dev, ABS_X), 10);
+ KUNIT_EXPECT_EQ(test, input_abs_get_res(input_dev, ABS_Y), 10);
+}
+
+static void touchscreen_test_resolution_multitouch(struct kunit *test)
+{
+ const struct property_entry props[] = {
+ PROPERTY_ENTRY_U32("touchscreen-min-x", 10),
+ PROPERTY_ENTRY_U32("touchscreen-size-x", 1010),
+ PROPERTY_ENTRY_U32("touchscreen-x-mm", 50),
+ PROPERTY_ENTRY_U32("touchscreen-min-y", 20),
+ PROPERTY_ENTRY_U32("touchscreen-size-y", 2020),
+ PROPERTY_ENTRY_U32("touchscreen-y-mm", 100),
+ { }
+ };
+ struct touchscreen_properties prop;
+ struct input_dev *input_dev;
+
+ input_dev = touchscreen_test_create_device(test, props);
+
+ /* Multitouch axes setup */
+ input_set_abs_params(input_dev, ABS_MT_POSITION_X, 0, 1, 0, 0);
+ input_set_abs_params(input_dev, ABS_MT_POSITION_Y, 0, 1, 0, 0);
+
+ touchscreen_parse_properties(input_dev, true, &prop);
+
+ /* min=10, max=1009, count=1000, 1000 / 50 = 20 */
+ KUNIT_EXPECT_EQ(test, input_abs_get_res(input_dev, ABS_MT_POSITION_X), 20);
+ /* min=20, max=2019, count=2000, 2000 / 100 = 20 */
+ KUNIT_EXPECT_EQ(test, input_abs_get_res(input_dev, ABS_MT_POSITION_Y), 20);
+}
+
+static void touchscreen_test_resolution_rounding(struct kunit *test)
+{
+ const struct property_entry props[] = {
+ /* 1951 / 100 = 19.51 -> rounds to 20 */
+ PROPERTY_ENTRY_U32("touchscreen-size-x", 1951),
+ PROPERTY_ENTRY_U32("touchscreen-x-mm", 100),
+ /* 1949 / 100 = 19.49 -> rounds to 19 */
+ PROPERTY_ENTRY_U32("touchscreen-size-y", 1949),
+ PROPERTY_ENTRY_U32("touchscreen-y-mm", 100),
+ { }
+ };
+ struct touchscreen_properties prop;
+ struct input_dev *input_dev;
+
+ input_dev = touchscreen_test_create_device(test, props);
+
+ input_set_abs_params(input_dev, ABS_MT_POSITION_X, 0, 1, 0, 0);
+ input_set_abs_params(input_dev, ABS_MT_POSITION_Y, 0, 1, 0, 0);
+
+ touchscreen_parse_properties(input_dev, true, &prop);
+
+ KUNIT_EXPECT_EQ(test, input_abs_get_res(input_dev, ABS_MT_POSITION_X), 20);
+ KUNIT_EXPECT_EQ(test, input_abs_get_res(input_dev, ABS_MT_POSITION_Y), 19);
+}
+
+static void touchscreen_test_resolution_swap_invert(struct kunit *test)
+{
+ const struct property_entry props[] = {
+ PROPERTY_ENTRY_U32("touchscreen-x-mm", 50),
+ PROPERTY_ENTRY_U32("touchscreen-y-mm", 200),
+ PROPERTY_ENTRY_BOOL("touchscreen-inverted-x"),
+ PROPERTY_ENTRY_BOOL("touchscreen-swapped-x-y"),
+ { }
+ };
+ struct touchscreen_properties prop;
+ struct input_dev *input_dev;
+
+ input_dev = touchscreen_test_create_device(test, props);
+
+ /* X count = 1000, Y count = 2000 */
+ input_set_abs_params(input_dev, ABS_MT_POSITION_X, 0, 999, 0, 0);
+ input_set_abs_params(input_dev, ABS_MT_POSITION_Y, 0, 1999, 0, 0);
+
+ touchscreen_parse_properties(input_dev, true, &prop);
+
+ /*
+ * Before swap: X res = 1000/50 = 20, Y res = 2000/200 = 10.
+ * After swap: X absinfo receives Y absinfo, Y absinfo receives X absinfo.
+ */
+ KUNIT_EXPECT_EQ(test, input_abs_get_res(input_dev, ABS_MT_POSITION_X), 10);
+ KUNIT_EXPECT_EQ(test, input_abs_get_res(input_dev, ABS_MT_POSITION_Y), 20);
+ KUNIT_EXPECT_TRUE(test, prop.invert_x);
+ KUNIT_EXPECT_TRUE(test, prop.swap_x_y);
+}
+
+static void touchscreen_test_resolution_edge_cases(struct kunit *test)
+{
+ const struct property_entry props[] = {
+ /* Zero x-mm */
+ PROPERTY_ENTRY_U32("touchscreen-x-mm", 0),
+ /* y-mm omitted */
+ { }
+ };
+ struct touchscreen_properties prop;
+ struct input_dev *input_dev;
+
+ input_dev = touchscreen_test_create_device(test, props);
+
+ input_set_abs_params(input_dev, ABS_MT_POSITION_X, 0, 999, 0, 0);
+ input_set_abs_params(input_dev, ABS_MT_POSITION_Y, 0, 1999, 0, 0);
+
+ /* Pre-configure resolutions */
+ input_abs_set_res(input_dev, ABS_MT_POSITION_X, 15);
+ input_abs_set_res(input_dev, ABS_MT_POSITION_Y, 25);
+
+ touchscreen_parse_properties(input_dev, true, &prop);
+
+ /* Zero size and omitted property must preserve pre-existing resolutions */
+ KUNIT_EXPECT_EQ(test, input_abs_get_res(input_dev, ABS_MT_POSITION_X), 15);
+ KUNIT_EXPECT_EQ(test, input_abs_get_res(input_dev, ABS_MT_POSITION_Y), 25);
+}
+
+static void touchscreen_test_resolution_invalid_range_and_null_prop(struct kunit *test)
+{
+ const struct property_entry props[] = {
+ PROPERTY_ENTRY_U32("touchscreen-x-mm", 50),
+ PROPERTY_ENTRY_U32("touchscreen-y-mm", 50),
+ { }
+ };
+ struct input_dev *input_dev;
+
+ input_dev = touchscreen_test_create_device(test, props);
+
+ /* X has valid range 0..999 (1000 units), Y has invalid range 500..500 */
+ input_set_abs_params(input_dev, ABS_MT_POSITION_X, 0, 999, 0, 0);
+ input_set_abs_params(input_dev, ABS_MT_POSITION_Y, 500, 500, 0, 0);
+ input_abs_set_res(input_dev, ABS_MT_POSITION_Y, 42);
+
+ /* NULL prop pointer */
+ touchscreen_parse_properties(input_dev, true, NULL);
+
+ /* Valid X gets resolution computed even with prop == NULL */
+ KUNIT_EXPECT_EQ(test, input_abs_get_res(input_dev, ABS_MT_POSITION_X), 20);
+ /* Invalid Y range (max <= min) preserves pre-configured resolution */
+ KUNIT_EXPECT_EQ(test, input_abs_get_res(input_dev, ABS_MT_POSITION_Y), 42);
+}
+
+static struct kunit_case touchscreen_tests[] = {
+ KUNIT_CASE(touchscreen_test_resolution_single),
+ KUNIT_CASE(touchscreen_test_resolution_multitouch),
+ KUNIT_CASE(touchscreen_test_resolution_rounding),
+ KUNIT_CASE(touchscreen_test_resolution_swap_invert),
+ KUNIT_CASE(touchscreen_test_resolution_edge_cases),
+ KUNIT_CASE(touchscreen_test_resolution_invalid_range_and_null_prop),
+ { /* sentinel */ }
+};
+
+static struct kunit_suite touchscreen_test_suite = {
+ .name = "touchscreen",
+ .test_cases = touchscreen_tests,
+};
+
+kunit_test_suite(touchscreen_test_suite);
+
+MODULE_DESCRIPTION("KUnit test for touchscreen property parsing");
+MODULE_LICENSE("GPL");
diff --git a/drivers/input/touchscreen.c b/drivers/input/touchscreen.c
index d699b24bb548..05ddb2ea7c66 100644
--- a/drivers/input/touchscreen.c
+++ b/drivers/input/touchscreen.c
@@ -7,6 +7,7 @@
*/
#include <linux/export.h>
+#include <linux/math.h>
#include <linux/property.h>
#include <linux/input.h>
#include <linux/input/mt.h>
@@ -33,7 +34,8 @@ static bool touchscreen_get_prop_u32(struct device *dev,
static void touchscreen_set_params(struct input_dev *dev,
unsigned long axis,
- int min, int max, int fuzz)
+ int min, int max, int fuzz,
+ unsigned int size_mm)
{
struct input_absinfo *absinfo;
@@ -48,6 +50,8 @@ static void touchscreen_set_params(struct input_dev *dev,
absinfo->minimum = min;
absinfo->maximum = max;
absinfo->fuzz = fuzz;
+ if (size_mm && min >= 0 && max > min)
+ absinfo->resolution = DIV_ROUND_CLOSEST(max - min + 1, size_mm);
}
/**
@@ -69,7 +73,7 @@ void touchscreen_parse_properties(struct input_dev *input, bool multitouch,
struct device *dev = input->dev.parent;
struct input_absinfo *absinfo;
unsigned int axis, axis_x, axis_y;
- unsigned int minimum, maximum, fuzz;
+ unsigned int minimum, maximum, fuzz, size_mm;
bool data_present;
input_alloc_absinfo(input);
@@ -89,8 +93,10 @@ void touchscreen_parse_properties(struct input_dev *input, bool multitouch,
data_present |= touchscreen_get_prop_u32(dev, "touchscreen-fuzz-x",
input_abs_get_fuzz(input, axis_x),
&fuzz);
+ data_present |= touchscreen_get_prop_u32(dev, "touchscreen-x-mm", 0,
+ &size_mm);
if (data_present)
- touchscreen_set_params(input, axis_x, minimum, maximum - 1, fuzz);
+ touchscreen_set_params(input, axis_x, minimum, maximum - 1, fuzz, size_mm);
data_present = touchscreen_get_prop_u32(dev, "touchscreen-min-y",
input_abs_get_min(input, axis_y),
@@ -102,8 +108,10 @@ void touchscreen_parse_properties(struct input_dev *input, bool multitouch,
data_present |= touchscreen_get_prop_u32(dev, "touchscreen-fuzz-y",
input_abs_get_fuzz(input, axis_y),
&fuzz);
+ data_present |= touchscreen_get_prop_u32(dev, "touchscreen-y-mm", 0,
+ &size_mm);
if (data_present)
- touchscreen_set_params(input, axis_y, minimum, maximum - 1, fuzz);
+ touchscreen_set_params(input, axis_y, minimum, maximum - 1, fuzz, size_mm);
axis = multitouch ? ABS_MT_PRESSURE : ABS_PRESSURE;
data_present = touchscreen_get_prop_u32(dev,
@@ -115,7 +123,7 @@ void touchscreen_parse_properties(struct input_dev *input, bool multitouch,
input_abs_get_fuzz(input, axis),
&fuzz);
if (data_present)
- touchscreen_set_params(input, axis, 0, maximum, fuzz);
+ touchscreen_set_params(input, axis, 0, maximum, fuzz, 0);
if (!prop)
return;
--
2.56.0.385.gac671c819a-goog
next prev parent reply other threads:[~2026-11-03 4:47 UTC|newest]
Thread overview: 6+ messages / expand[flat|nested] mbox.gz Atom feed top
2026-11-03 4:47 [PATCH 0/4] Input: touchscreen - parse touchscreen-x-mm and touchscreen-y-mm properties Dmitry Torokhov
2026-11-03 4:47 ` Dmitry Torokhov [this message]
2026-11-03 4:47 ` [PATCH 2/4] Input: wacom_w9000 - rely on touchscreen_parse_properties() for resolution Dmitry Torokhov
2026-11-03 4:47 ` [PATCH 3/4] Input: goodix-berlin - set default resolution before parsing properties Dmitry Torokhov
2026-11-03 9:58 ` Neil Armstrong
2026-11-03 4:47 ` [PATCH 4/4] Input: apple_z2 " Dmitry Torokhov
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=20261103044708.2461481-2-dmitry.torokhov@gmail.com \
--to=dmitry.torokhov@gmail.com \
--cc=asahi@lists.linux.dev \
--cc=david@ixit.cz \
--cc=fnkl.kernel@gmail.com \
--cc=funderscore@postmarketos.org \
--cc=hansg@kernel.org \
--cc=hendrik-noack@gmx.de \
--cc=jeff@labundy.com \
--cc=linux-arm-kernel@lists.infradead.org \
--cc=linux-input@vger.kernel.org \
--cc=linux-kernel@vger.kernel.org \
--cc=neil.armstrong@linaro.org \
--cc=val@packett.cool \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox