All of lore.kernel.org
 help / color / mirror / Atom feed
From: Luka Panio <lukapanio@gmail.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>,
	Henrik Rydberg <rydberg@bitmath.org>,
	Douglas Anderson <dianders@chromium.org>,
	linux-input@vger.kernel.org, devicetree@vger.kernel.org,
	dri-devel@lists.freedesktop.org, linux-kernel@vger.kernel.org,
	Luka Panio <lukapanio@gmail.com>
Subject: [PATCH 7/7] Input: edt-ft5x06 - support following a DRM panel
Date: Sat, 17 Oct 2026 16:07:55 +0200	[thread overview]
Message-ID: <20261017140827.290666-8-lukapanio@gmail.com> (raw)
In-Reply-To: <20261017140827.290666-1-lukapanio@gmail.com>

On touch and display driver (TDDI) chips such as the FT8722 the touch
controller shares its supply and reset with the display panel, so it
can only be accessed while the panel is powered and loses its state,
and for RAM-only parts its firmware, every time the panel goes off.

When the device tree links the touchscreen to its panel, register as a
DRM panel follower, like i2c-hid does.

Signed-off-by: Luka Panio <lukapanio@gmail.com>
---
 drivers/input/touchscreen/Kconfig      |   1 +
 drivers/input/touchscreen/edt-ft5x06.c | 156 ++++++++++++++++++++++++-
 2 files changed, 151 insertions(+), 6 deletions(-)

diff --git a/drivers/input/touchscreen/Kconfig b/drivers/input/touchscreen/Kconfig
index 92840ea9c65f..b4fd55028a04 100644
--- a/drivers/input/touchscreen/Kconfig
+++ b/drivers/input/touchscreen/Kconfig
@@ -794,6 +794,7 @@ config TOUCHSCREEN_PENMOUNT
 config TOUCHSCREEN_EDT_FT5X06
 	tristate "EDT FocalTech FT5x06 I2C Touchscreen support"
 	depends on I2C
+	depends on DRM_PANEL || !DRM_PANEL
 	select REGMAP_I2C
 	help
 	  Say Y here if you have an EDT "Polytouch" touchscreen based
diff --git a/drivers/input/touchscreen/edt-ft5x06.c b/drivers/input/touchscreen/edt-ft5x06.c
index 7bcc48e07d62..b2fca6c37205 100644
--- a/drivers/input/touchscreen/edt-ft5x06.c
+++ b/drivers/input/touchscreen/edt-ft5x06.c
@@ -4,6 +4,7 @@
  * Daniel Wagener <daniel.wagener@kernelconcepts.de> (M09 firmware support)
  * Lothar Waßmann <LW@KARO-electronics.de> (DT support)
  * Dario Binacchi <dario.binacchi@amarulasolutions.com> (regmap support)
+ * Luka Panio <lukapanio@gmail.com> (SPI and DRM panel support)
  */
 
 /*
@@ -33,6 +34,9 @@
 #include <linux/uaccess.h>
 
 #include <linux/unaligned.h>
+#include <linux/workqueue.h>
+
+#include <drm/drm_panel.h>
 
 #include "edt-ft5x06.h"
 
@@ -150,6 +154,12 @@ struct edt_ft5x06_ts_data {
 	enum edt_ver version;
 	int (*boot)(struct device *dev, struct gpio_desc *reset_gpio);
 	u8 tdata_reg;
+
+	struct drm_panel_follower panel_follower;
+	struct work_struct panel_work;
+	bool is_panel_follower;
+	bool powered_off;
+	bool hw_initialized;
 	unsigned int crc_errors;
 	unsigned int header_errors;
 };
@@ -383,7 +393,7 @@ static ssize_t edt_ft5x06_setting_show(struct device *dev,
 
 	guard(mutex)(&tsdata->mutex);
 
-	if (tsdata->factory_mode)
+	if (tsdata->factory_mode || tsdata->powered_off)
 		return -EIO;
 
 	switch (tsdata->version) {
@@ -441,7 +451,7 @@ static ssize_t edt_ft5x06_setting_store(struct device *dev,
 
 	guard(mutex)(&tsdata->mutex);
 
-	if (tsdata->factory_mode)
+	if (tsdata->factory_mode || tsdata->powered_off)
 		return -EIO;
 
 	error = kstrtouint(buf, 0, &val);
@@ -1120,7 +1130,8 @@ static void edt_ft5x06_disable_regulators(void *arg)
 
 /*
  * Take the controller out of reset, identify it and register the input
- * device.
+ * device. Called from probe, or once the panel is up for controllers
+ * following a panel.
  */
 static int edt_ft5x06_ts_init_hw(struct edt_ft5x06_ts_data *tsdata)
 {
@@ -1206,9 +1217,117 @@ static int edt_ft5x06_ts_init_hw(struct edt_ft5x06_ts_data *tsdata)
 
 	edt_ft5x06_ts_prepare_debugfs(tsdata);
 
+	tsdata->hw_initialized = true;
+
+	return 0;
+}
+
+/*
+ * TDDI controllers share their power and reset with the display panel. When
+ * following a panel, power the touch controller down before the panel goes
+ * off and bring it back once the panel is up again.
+ */
+static void edt_ft5x06_panel_work(struct work_struct *work)
+{
+	struct edt_ft5x06_ts_data *tsdata =
+		container_of(work, struct edt_ft5x06_ts_data, panel_work);
+	int error;
+
+	guard(mutex)(&tsdata->mutex);
+
+	if (!tsdata->powered_off)
+		return;
+
+	if (!tsdata->hw_initialized) {
+		error = edt_ft5x06_ts_init_hw(tsdata);
+		if (error) {
+			/* try again the next time the panel comes up */
+			gpiod_set_value_cansleep(tsdata->reset_gpio, 1);
+			dev_err(tsdata->dev,
+				"Failed to initialize the controller: %d\n",
+				error);
+			return;
+		}
+		goto out;
+	}
+
+	gpiod_set_value_cansleep(tsdata->reset_gpio, 0);
+
+	if (tsdata->boot) {
+		error = tsdata->boot(tsdata->dev, tsdata->reset_gpio);
+		if (error) {
+			dev_err(tsdata->dev,
+				"Failed to boot the controller: %d\n", error);
+			return;
+		}
+	} else {
+		msleep(300);
+	}
+
+	edt_ft5x06_restore_reg_parameters(tsdata);
+out:
+	tsdata->powered_off = false;
+	enable_irq(tsdata->irq);
+}
+
+static int edt_ft5x06_panel_prepared(struct drm_panel_follower *follower)
+{
+	struct edt_ft5x06_ts_data *tsdata =
+		container_of(follower, struct edt_ft5x06_ts_data, panel_follower);
+
+	/* Downloading firmware takes a while, don't hold up the panel */
+	schedule_work(&tsdata->panel_work);
+
 	return 0;
 }
 
+static int edt_ft5x06_panel_unpreparing(struct drm_panel_follower *follower)
+{
+	struct edt_ft5x06_ts_data *tsdata =
+		container_of(follower, struct edt_ft5x06_ts_data, panel_follower);
+
+	cancel_work_sync(&tsdata->panel_work);
+
+	guard(mutex)(&tsdata->mutex);
+
+	if (tsdata->powered_off)
+		return 0;
+
+	disable_irq(tsdata->irq);
+	gpiod_set_value_cansleep(tsdata->reset_gpio, 1);
+	tsdata->powered_off = true;
+
+	return 0;
+}
+
+static const struct drm_panel_follower_funcs edt_ft5x06_panel_follower_funcs = {
+	.panel_prepared = edt_ft5x06_panel_prepared,
+	.panel_unpreparing = edt_ft5x06_panel_unpreparing,
+};
+
+static void edt_ft5x06_cancel_panel_work(void *data)
+{
+	struct edt_ft5x06_ts_data *tsdata = data;
+
+	cancel_work_sync(&tsdata->panel_work);
+}
+
+static int edt_ft5x06_register_panel_follower(struct edt_ft5x06_ts_data *tsdata)
+{
+	struct device *dev = tsdata->dev;
+	int error;
+
+	INIT_WORK(&tsdata->panel_work, edt_ft5x06_panel_work);
+	error = devm_add_action_or_reset(dev, edt_ft5x06_cancel_panel_work,
+					 tsdata);
+	if (error)
+		return error;
+
+	tsdata->panel_follower.funcs = &edt_ft5x06_panel_follower_funcs;
+
+	return devm_drm_panel_add_follower(dev, &tsdata->panel_follower);
+}
+
 int edt_ft5x06_probe(struct device *dev, struct regmap *regmap, int irq,
 		     const struct edt_ft5x06_chip_data *chip_data, u16 bustype,
 		     struct dentry *debugfs)
@@ -1331,14 +1450,24 @@ int edt_ft5x06_probe(struct device *dev, struct regmap *regmap, int irq,
 	tsdata->factory_mode = false;
 	dev_set_drvdata(dev, tsdata);
 
-	error = edt_ft5x06_ts_init_hw(tsdata);
-	if (error)
-		return error;
+	/*
+	 * A controller sharing its power with a panel can only be accessed
+	 * once the panel is up, so leave it in reset and do the rest of the
+	 * initialization from the panel follower callback.
+	 */
+	tsdata->is_panel_follower = drm_is_panel_follower(dev);
+	if (!tsdata->is_panel_follower) {
+		error = edt_ft5x06_ts_init_hw(tsdata);
+		if (error)
+			return error;
+	}
 
 	irq_flags = irq_get_trigger_type(irq);
 	if (irq_flags == IRQF_TRIGGER_NONE)
 		irq_flags = IRQF_TRIGGER_FALLING;
 	irq_flags |= IRQF_ONESHOT;
+	if (tsdata->is_panel_follower)
+		irq_flags |= IRQF_NO_AUTOEN;
 
 	error = devm_request_threaded_irq(dev, irq, NULL, edt_ft5x06_ts_isr,
 					  irq_flags, dev_name(dev), tsdata);
@@ -1347,6 +1476,14 @@ int edt_ft5x06_probe(struct device *dev, struct regmap *regmap, int irq,
 		return error;
 	}
 
+	if (tsdata->is_panel_follower) {
+		tsdata->powered_off = true;
+		error = edt_ft5x06_register_panel_follower(tsdata);
+		if (error)
+			return dev_err_probe(dev, error,
+					     "failed to follow the panel\n");
+	}
+
 	dev_dbg(dev,
 		"EDT FT5x06 initialized: IRQ %d, WAKE pin %d, Reset pin %d.\n",
 		irq,
@@ -1374,6 +1511,10 @@ static int edt_ft5x06_ts_suspend(struct device *dev)
 	if (device_may_wakeup(dev))
 		return 0;
 
+	/* The panel powers us down and up */
+	if (tsdata->is_panel_follower)
+		return 0;
+
 	if (tsdata->suspend_mode == EDT_PMODE_NOT_SUPPORTED)
 		return 0;
 
@@ -1415,6 +1556,9 @@ static int edt_ft5x06_ts_resume(struct device *dev)
 	if (device_may_wakeup(dev))
 		return 0;
 
+	if (tsdata->is_panel_follower)
+		return 0;
+
 	if (tsdata->suspend_mode == EDT_PMODE_NOT_SUPPORTED)
 		return 0;
 
-- 
2.55.0


      parent reply	other threads:[~2026-10-17 14:08 UTC|newest]

Thread overview: 9+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2026-10-17 14:07 [PATCH 0/7] Input: edt-ft5x06 - add SPI transport and FocalTech FT8722 support Luka Panio
2026-10-17 14:07 ` [PATCH 1/7] Input: edt-ft5x06 - split the bus independent core from the I2C driver Luka Panio
2026-10-17 14:07 ` [PATCH 2/7] Input: edt-ft5x06 - move the controller bring-up into a helper Luka Panio
2026-10-17 14:16   ` sashiko-bot
2026-10-17 14:07 ` [PATCH 3/7] Input: edt-ft5x06 - add an optional controller boot hook Luka Panio
2026-10-17 14:07 ` [PATCH 4/7] Input: edt-ft5x06 - allow a chip specific touch data register Luka Panio
2026-10-17 14:07 ` [PATCH 5/7] dt-bindings: input: touchscreen: edt-ft5x06: Add FocalTech FT8722 Luka Panio
2026-10-17 14:07 ` [PATCH 6/7] Input: edt-ft5x06 - add SPI transport and FT8722 support Luka Panio
2026-10-17 14:07 ` Luka Panio [this message]

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=20261017140827.290666-8-lukapanio@gmail.com \
    --to=lukapanio@gmail.com \
    --cc=conor+dt@kernel.org \
    --cc=devicetree@vger.kernel.org \
    --cc=dianders@chromium.org \
    --cc=dmitry.torokhov@gmail.com \
    --cc=dri-devel@lists.freedesktop.org \
    --cc=krzk+dt@kernel.org \
    --cc=linux-input@vger.kernel.org \
    --cc=linux-kernel@vger.kernel.org \
    --cc=robh@kernel.org \
    --cc=rydberg@bitmath.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 an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.