From mboxrd@z Thu Jan 1 00:00:00 1970 Received: from mail-wm1-f44.google.com (mail-wm1-f44.google.com [209.85.128.44]) (using TLSv1.2 with cipher ECDHE-RSA-AES128-GCM-SHA256 (128/128 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id 4A25D47F2DE for ; Sat, 17 Oct 2026 14:08:49 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=209.85.128.44 ARC-Seal:i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1792246133; cv=none; b=VPwcQGVReE/1GhlfNJYtSq8luNJi8dADwlF0TCcLZeQtbSfNRFiSqNBfmh9BrXjBbAl5tv0bp9rbWiVM7Es3+cfGQqrCduPXZ/1AXw2agNb09NAU5AhJFx3jBNkLlJ1nMJ1snzw+KLd6gQXtbvA0nG/3Nwk4SatZ0vVxz7BVoWM= ARC-Message-Signature:i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1792246133; c=relaxed/simple; bh=M/fO2fPNKEJuGGAETlGpiAMAmlXI8AfDa1ul3FclVRA=; h=From:To:Cc:Subject:Date:Message-ID:In-Reply-To:References: MIME-Version:Content-Type; b=DXWai3/09ltTYk+msZPhW/+33yNoRyoBurRMXNlS9Ak8KuCciF23yyCCh4ls591VciIP/tlzkJ7XwMdqQxeBNoaUgng7YNZyB+hRGor99+RfpfDMqrbNqjaKjNr8PqWAbMTqKfCvbuyJiA/wPvTo4xW2bwD9SZ9RIS6nb409Xjw= ARC-Authentication-Results:i=1; smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=gmail.com; spf=pass smtp.mailfrom=gmail.com; dkim=pass (2048-bit key) header.d=gmail.com header.i=@gmail.com header.b=Xk3NblTs; arc=none smtp.client-ip=209.85.128.44 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=gmail.com Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=gmail.com Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=gmail.com header.i=@gmail.com header.b="Xk3NblTs" Received: by mail-wm1-f44.google.com with SMTP id 5b1f17b1804b1-49ff680331aso7014505e9.3 for ; Sat, 17 Oct 2026 07:08:49 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20251104; t=1792246127; x=1792850927; darn=vger.kernel.org; h=content-transfer-encoding:content-type:mime-version:references :in-reply-to:message-id:date:subject:cc:to:from:from:to:cc:subject :date:message-id:reply-to:content-type; bh=qfOl1kjHpYXnvDQ7klGKtwkq44SiCg69dIWYFZz39rU=; b=Xk3NblTsJX1dwlOid+CvUf8moW7AfhQGV3rhkBY8eSb+6xtvvJMjQ8DGFCE2bfrrqs BCkYLuYNKYGriFPSDsBnHevB5ZTyV9gFIRpjQafiKZosUXAHBMj7sCpRKcm39fHKCvYu x3GgGn4GbKpGAupTtrGcoXMO0KbhkRHxxD6jCLyKk+tTWZTR5ZEa6/OhYo6CoULDvhww HmdeiI9M6aqd3Whkqj2rF0oaQAeXtvYiJ7CM7edEC4Z79LqvuNwgWg8tJdeP8dS813eE jkHuX7fYIzzKJ1VNr3UghYV5NWypp1j0NxoX7sCV2PA/vYNzJgF3fgbQcBfrOu2nmtUA m/cQ== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20260707; t=1792246127; x=1792850927; h=content-transfer-encoding:content-type:mime-version:references :in-reply-to:message-id:date:subject:cc:to:from:x-gm-gg :x-gm-message-state:from:to:cc:subject:date:message-id:reply-to :content-type; bh=qfOl1kjHpYXnvDQ7klGKtwkq44SiCg69dIWYFZz39rU=; b=RWjItf8XPwkMPRioVvKgpfRYsnXVAkKiUee2sKYOmFWtG9En5120JJFFvrJczJ6/FK 2U0a3oDrKi8mvBOz3FfwdC03jvIyEMYW81HcW43TeV9joRGj3pGjQj9Qc8/2OPq7vShS eTG+fvDMKJU/VN/6COfIm0xpYuNHtH1l0yRRF+2pfDcuDCuQPZ9cfj2hVp+6yVClqj8U wdKwQBbakbhadPj46DjJkso3Bi0KuIRR/nYDXPCBoWUILBWTX+MzUDPDuDVW0J0vMAIK peTgz0myGBz6+1poE+cf4W+2EsKTFk/H3ZYw0GLJi0HJtyMFkR84BvPhzEWsa4an4RJ8 nTLw== X-Forwarded-Encrypted: i=1; AKwUvBx8nGa+voigvVafbTE8G6EZG/6Q9/t2xxZBjqTCejJ92FEySo2ORHwEAlEypzPcClKMrZ1ZYhhK6kN5wCY=@vger.kernel.org X-Gm-Message-State: AFq9FYL3l+ktWDPldcPRNb5zV7w74r2OYxsVBandYMHiYK4Waj8F9xOV tns/cUEPytYNGOrjUcLO1nqpt1oCbImBs5Zrruiol7boAI3GGIeST+uL X-Gm-Gg: AYBFou2m3IOAN3MD/DXoRChk3qz+OZKDVZx3M5F6P9fUUwVlxBHei9HrUU+bLnd7xIw ihdQHIweZqfAzFhxv0tmw2waPoxipGYKnMJ05H1frNkawCLefK5yHrmxkOWghI/EHyN8NqXqlRT wPBVNOV0s9jxgcrKgzzSJPgcaqg2g77lVZBn0nZe9u3Z9RGZ1TbKZRXImHv5ZwfJpex7Ep0TC9u 6Yjd3bONn7UjW4mCwAzUcil3jPTucmA1si15fk8wv2DQ1anAD558zRtM8tJkn6yC48MaAujcyQp 0okEu3TMnb6U6CGVU/9ec4jFShNshwc5/q5vt0dhuP8+DYV3/kIlXEskrTlWND//fWxcS9k5GXy mKULhwIuw/mlaIcEr1+fcxfxp8MYNd/4DqlEl3qII6WLDV8nk0ZLmmurqyZE0/AzsJWPBU95JIf ji2KRv40BQc2iIGqZVUUel5rVBGF0llVa4JWQxLOmv2vIe+wCx/RYxHZ0XlbXpTWMwL6DwMHFVu NOdOr+2HUtxbNKbtF59RC1CWmjDlWoVxBATrnMD7dCHU4mXviY+INhJA2ayzfr7rpDYToYuWOUJ YQ== X-Received: by 2002:a05:600c:468e:b0:4a0:12d7:4c88 with SMTP id 5b1f17b1804b1-4a1d7625f71mr59997585e9.30.1792246127263; Sat, 17 Oct 2026 07:08:47 -0700 (PDT) Received: from machine.home.arpa ([2a0d:3344:57f6::1046]) by smtp.gmail.com with ESMTPSA id 5b1f17b1804b1-4a1ce3302b5sm84430745e9.4.2026.10.17.07.08.45 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Sat, 17 Oct 2026 07:08:46 -0700 (PDT) From: Luka Panio To: Dmitry Torokhov Cc: Rob Herring , Krzysztof Kozlowski , Conor Dooley , Henrik Rydberg , Douglas Anderson , linux-input@vger.kernel.org, devicetree@vger.kernel.org, dri-devel@lists.freedesktop.org, linux-kernel@vger.kernel.org, Luka Panio Subject: [PATCH 7/7] Input: edt-ft5x06 - support following a DRM panel Date: Sat, 17 Oct 2026 16:07:55 +0200 Message-ID: <20261017140827.290666-8-lukapanio@gmail.com> X-Mailer: git-send-email 2.55.0 In-Reply-To: <20261017140827.290666-1-lukapanio@gmail.com> References: <20261017140827.290666-1-lukapanio@gmail.com> Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 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 --- 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 (M09 firmware support) * Lothar Waßmann (DT support) * Dario Binacchi (regmap support) + * Luka Panio (SPI and DRM panel support) */ /* @@ -33,6 +34,9 @@ #include #include +#include + +#include #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