Devicetree
 help / color / mirror / Atom feed
From: Miotsuki Mizuno <founder@crepuse.com>
To: Dmitry Torokhov <dmitry.torokhov@gmail.com>
Cc: Rob Herring <robh@kernel.org>,
	Krzysztof Kozlowski <krzk+dt@kernel.org>,
	Conor Dooley <conor+dt@kernel.org>,
	Icenowy Zheng <icenowy@aosc.io>,
	"linux-input@vger.kernel.org" <linux-input@vger.kernel.org>,
	"devicetree@vger.kernel.org" <devicetree@vger.kernel.org>,
	"linux-kernel@vger.kernel.org" <linux-kernel@vger.kernel.org>
Subject: [PATCH 3/3] Input: add Nsiway NS2009 resistive touchscreen driver
Date: Fri, 9 Oct 2026 14:47:44 +0000	[thread overview]
Message-ID: <20261009144729.85832-4-founder@crepuse.com> (raw)
In-Reply-To: <20261009144729.85832-1-founder@crepuse.com>

Add polling support for the Nsiway NS2009 I2C touchscreen controller using
the input polling API and managed input allocation. Read 12-bit X/Y ADC
values and report BTN_TOUCH from the raw Z1 contact threshold. Apply
coordinate transforms through the common touchscreen helpers.

This implementation derives from Icenowy Zheng's 2017 polling driver.
Keep its historical threshold default while allowing a firmware override,
and retain the original copyright and author attribution.

Report the touch key on every successful sample and let the input core
filter repeated values. A private pen-down flag would become stale when
the core clears key state during inhibit or reset. On a failed, short or
malformed I2C read, discard the coordinates and release the contact so a
failed bus cannot leave it stuck down. A later valid sample reports a new
contact.

Validate the zero padding in the low nibble of the second byte as specified
by the 12-bit read-command format. Reject malformed responses before
assigning the decoded value, and rate-limit the diagnostic output.

Tested on UNIHIKER M10 V1.2 with a Linux 6.18 validation module: clicks,
long presses, drags, release, axis orientation and three held-contact
inhibit/uninhibit cycles. The validation module only changes the driver
registration name to replace the existing built-in driver. Physical
NACK/short-read fault injection and system suspend/resume have not been
exercised.

Link: https://lore.kernel.org/all/20170315155140.54518-4-icenowy@aosc.xyz/

Signed-off-by: Mizuno Miotsuki <founder@crepuse.com>
---
 MAINTAINERS                        |   1 +
 drivers/input/touchscreen/Kconfig  |  10 ++
 drivers/input/touchscreen/Makefile |   1 +
 drivers/input/touchscreen/ns2009.c | 175 +++++++++++++++++++++++++++++
 4 files changed, 187 insertions(+)
 create mode 100644 drivers/input/touchscreen/ns2009.c

diff --git a/MAINTAINERS b/MAINTAINERS
index 696f2badc49c..e282c02ec13f 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -19472,6 +19472,7 @@ M:	Mizuno Miotsuki <founder@crepuse.com>
 L:	linux-input@vger.kernel.org
 S:	Maintained
 F:	Documentation/devicetree/bindings/input/touchscreen/nsiway,ns2009.yaml
+F:	drivers/input/touchscreen/ns2009.c
 
 NTB AMD DRIVER
 M:	Basavaraj Natikar <Basavaraj.Natikar@amd.com>
diff --git a/drivers/input/touchscreen/Kconfig b/drivers/input/touchscreen/Kconfig
index 9b9ae8ac3f7f..d4cdf1a7e057 100644
--- a/drivers/input/touchscreen/Kconfig
+++ b/drivers/input/touchscreen/Kconfig
@@ -691,6 +691,16 @@ config TOUCHSCREEN_MTOUCH
 	  To compile this driver as a module, choose M here: the
 	  module will be called mtouch.
 
+config TOUCHSCREEN_NS2009
+	tristate "Nsiway NS2009 touchscreen controller"
+	depends on I2C
+	help
+	  Say Y here if you have a resistive touchscreen using the Nsiway
+	  NS2009 controller connected over I2C.
+
+	  To compile this driver as a module, choose M here: the module will be
+	  called ns2009.
+
 config TOUCHSCREEN_NOVATEK_NVT_TS
 	tristate "Novatek NT11205 touchscreen support"
 	depends on I2C
diff --git a/drivers/input/touchscreen/Makefile b/drivers/input/touchscreen/Makefile
index bfd9de83389d..c5fc2062d7cf 100644
--- a/drivers/input/touchscreen/Makefile
+++ b/drivers/input/touchscreen/Makefile
@@ -68,6 +68,7 @@ obj-$(CONFIG_TOUCHSCREEN_MIGOR)		+= migor_ts.o
 obj-$(CONFIG_TOUCHSCREEN_MMS114)	+= mms114.o
 obj-$(CONFIG_TOUCHSCREEN_MSG2638)	+= msg2638.o
 obj-$(CONFIG_TOUCHSCREEN_MTOUCH)	+= mtouch.o
+obj-$(CONFIG_TOUCHSCREEN_NS2009)	+= ns2009.o
 obj-$(CONFIG_TOUCHSCREEN_NOVATEK_NVT_TS)	+= novatek-nvt-ts.o
 obj-$(CONFIG_TOUCHSCREEN_HP600)		+= hp680_ts_input.o
 obj-$(CONFIG_TOUCHSCREEN_HP7XX)		+= jornada720_ts.o
diff --git a/drivers/input/touchscreen/ns2009.c b/drivers/input/touchscreen/ns2009.c
new file mode 100644
index 000000000000..d5c1f9f96bb6
--- /dev/null
+++ b/drivers/input/touchscreen/ns2009.c
@@ -0,0 +1,175 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*
+ * Nsiway NS2009 resistive touchscreen controller
+ *
+ * Copyright (C) 2017 Icenowy Zheng <icenowy@aosc.xyz>
+ * Portions derived from silead.c:
+ * Copyright (C) 2014-2015 Intel Corporation
+ */
+
+#include <linux/i2c.h>
+#include <linux/input.h>
+#include <linux/input/touchscreen.h>
+#include <linux/module.h>
+#include <linux/property.h>
+
+#define NS2009_POLL_INTERVAL_MS		30
+#define NS2009_MAX_12BIT		0xfff
+
+#define NS2009_READ_X_LOW_POWER_12BIT	0xc0
+#define NS2009_READ_Y_LOW_POWER_12BIT	0xd0
+#define NS2009_READ_Z1_LOW_POWER_12BIT	0xe0
+
+/* Historical driver default; boards may override via firmware properties. */
+#define NS2009_DEFAULT_PEN_DOWN_Z1	80
+
+struct ns2009_data {
+	struct i2c_client *client;
+	struct touchscreen_properties prop;
+	u16 pen_down_z1;
+};
+
+static int ns2009_read_data(struct ns2009_data *data, u8 command, u16 *value)
+{
+	u8 raw[2];
+	int error;
+
+	error = i2c_smbus_read_i2c_block_data(data->client, command,
+					      sizeof(raw), raw);
+	if (error < 0)
+		return error;
+	if (error != sizeof(raw))
+		return -EIO;
+	/* The 12-bit result is left-aligned; the remaining bits must be zero. */
+	if (raw[1] & 0x0f) {
+		dev_err_ratelimited(&data->client->dev,
+				    "invalid sample for command %#02x: %02x %02x\n",
+				    command, raw[0], raw[1]);
+		return -EIO;
+	}
+
+	*value = (raw[0] << 4) | (raw[1] >> 4);
+	return 0;
+}
+
+static void ns2009_poll(struct input_dev *input)
+{
+	struct ns2009_data *data = input_get_drvdata(input);
+	u16 x, y, z1;
+	int error;
+
+	error = ns2009_read_data(data, NS2009_READ_Z1_LOW_POWER_12BIT, &z1);
+	if (error)
+		goto read_error;
+
+	if (z1 < data->pen_down_z1)
+		goto release;
+
+	error = ns2009_read_data(data, NS2009_READ_X_LOW_POWER_12BIT, &x);
+	if (error)
+		goto read_error;
+
+	error = ns2009_read_data(data, NS2009_READ_Y_LOW_POWER_12BIT, &y);
+	if (error)
+		goto read_error;
+
+	/* The input core filters repeats and may independently clear key state. */
+	input_report_key(input, BTN_TOUCH, 1);
+	touchscreen_report_pos(input, &data->prop, x, y, false);
+	input_sync(input);
+	return;
+
+read_error:
+	dev_err_ratelimited(&data->client->dev,
+			    "failed to poll touch data: %d\n", error);
+	/* An unreadable sample must not leave an active touch stuck down. */
+release:
+	input_report_key(input, BTN_TOUCH, 0);
+	input_sync(input);
+}
+
+static int ns2009_probe(struct i2c_client *client)
+{
+	struct device *dev = &client->dev;
+	struct ns2009_data *data;
+	struct input_dev *input;
+	u32 value;
+	int error;
+
+	if (!i2c_check_functionality(client->adapter,
+				     I2C_FUNC_SMBUS_READ_I2C_BLOCK))
+		return dev_err_probe(dev, -EOPNOTSUPP,
+				     "required SMBus block read is unavailable\n");
+
+	data = devm_kzalloc(dev, sizeof(*data), GFP_KERNEL);
+	if (!data)
+		return -ENOMEM;
+
+	input = devm_input_allocate_device(dev);
+	if (!input)
+		return -ENOMEM;
+
+	data->client = client;
+	data->pen_down_z1 = NS2009_DEFAULT_PEN_DOWN_Z1;
+	if (device_property_present(dev, "touchscreen-min-pressure")) {
+		error = device_property_read_u32(dev, "touchscreen-min-pressure",
+						 &value);
+		if (error)
+			return dev_err_probe(dev, error,
+					     "failed to read touchscreen-min-pressure\n");
+		if (!value || value > NS2009_MAX_12BIT)
+			return dev_err_probe(dev, -EINVAL,
+					     "touchscreen-min-pressure must be 1..4095\n");
+		data->pen_down_z1 = value;
+	}
+	input_set_drvdata(input, data);
+
+	input->name = "ns2009_ts";
+	input->phys = "input/ts";
+	input->id.bustype = BUS_I2C;
+
+	__set_bit(INPUT_PROP_DIRECT, input->propbit);
+	input_set_capability(input, EV_KEY, BTN_TOUCH);
+	input_set_abs_params(input, ABS_X, 0, NS2009_MAX_12BIT, 32, 0);
+	input_set_abs_params(input, ABS_Y, 0, NS2009_MAX_12BIT, 16, 0);
+	touchscreen_parse_properties(input, false, &data->prop);
+
+	error = input_setup_polling(input, ns2009_poll);
+	if (error)
+		return dev_err_probe(dev, error, "failed to set up polling\n");
+
+	input_set_poll_interval(input, NS2009_POLL_INTERVAL_MS);
+
+	error = input_register_device(input);
+	if (error)
+		return dev_err_probe(dev, error,
+				     "failed to register input device\n");
+
+	return 0;
+}
+
+static const struct i2c_device_id ns2009_id[] = {
+	{ "ns2009" },
+	{ }
+};
+MODULE_DEVICE_TABLE(i2c, ns2009_id);
+
+static const struct of_device_id ns2009_of_match[] = {
+	{ .compatible = "nsiway,ns2009" },
+	{ }
+};
+MODULE_DEVICE_TABLE(of, ns2009_of_match);
+
+static struct i2c_driver ns2009_driver = {
+	.driver = {
+		.name = "ns2009",
+		.of_match_table = ns2009_of_match,
+	},
+	.probe = ns2009_probe,
+	.id_table = ns2009_id,
+};
+module_i2c_driver(ns2009_driver);
+
+MODULE_AUTHOR("Icenowy Zheng <icenowy@aosc.xyz>");
+MODULE_DESCRIPTION("Nsiway NS2009 resistive touchscreen controller");
+MODULE_LICENSE("GPL");

  parent reply	other threads:[~2026-10-09 14:47 UTC|newest]

Thread overview: 5+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2026-10-09 14:47 [PATCH 0/3] Add Nsiway NS2009 touchscreen support Miotsuki Mizuno
2026-10-09 14:47 ` [PATCH 1/3] dt-bindings: vendor-prefixes: add Nsiway Miotsuki Mizuno
2026-10-09 14:47 ` [PATCH 2/3] dt-bindings: input: touchscreen: add Nsiway NS2009 Miotsuki Mizuno
2026-10-09 14:47 ` Miotsuki Mizuno [this message]
2026-10-09 14:53   ` [PATCH 3/3] Input: add Nsiway NS2009 resistive touchscreen driver sashiko-bot

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=20261009144729.85832-4-founder@crepuse.com \
    --to=founder@crepuse.com \
    --cc=conor+dt@kernel.org \
    --cc=devicetree@vger.kernel.org \
    --cc=dmitry.torokhov@gmail.com \
    --cc=icenowy@aosc.io \
    --cc=krzk+dt@kernel.org \
    --cc=linux-input@vger.kernel.org \
    --cc=linux-kernel@vger.kernel.org \
    --cc=robh@kernel.org \
    /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