From mboxrd@z Thu Jan 1 00:00:00 1970 Received: from mail-wm1-f48.google.com (mail-wm1-f48.google.com [209.85.128.48]) (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 ABC0447CC8C for ; Sat, 17 Oct 2026 14:08:49 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=209.85.128.48 ARC-Seal:i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1792246132; cv=none; b=MIRc1NvE7jLkuC6WVl74gSSRFQGzaaZtJ2fXSgMCSg855aIBr6rvBiMGBee0WVxv3hzTZghfGZwdl0R3WNnd15WIQExT/IL4Ne9eYKC5NFa/ho8c93AF8oyAkxtD39LRlor32AuhHzvXLVcJpv0n72Wt+PAnbIqs+AdFPhoE+nI= ARC-Message-Signature:i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1792246132; c=relaxed/simple; bh=M/fO2fPNKEJuGGAETlGpiAMAmlXI8AfDa1ul3FclVRA=; h=From:To:Cc:Subject:Date:Message-ID:In-Reply-To:References: MIME-Version:Content-Type; b=s4/gJiF1YWyOvAxsJMPH4cbnr1PdduxAiFeajj2g6DGQZooidEe/xidftsmLwfz3y27gQ+7UNL3klfRdQi+FsjglXNmIEw6IjF5ny4PoJ4XZFNaQDjXUr3GOTniwBU9mJNkNjNJVAQNn5IDf+N7tWzBIbPdwoKqNNj6hHwEx3FU= 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.48 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-f48.google.com with SMTP id 5b1f17b1804b1-49d05d51553so5291895e9.2 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=eIddioIqAWLE4oDVryDCfH5WobxGLX2gxaK6N1JGVeia/18kY9ZNqNcQ2HiCd3J0iq vNi7zu4B0EAN4/NBcyLvbq73ZKMLZhmVomM7V8ctG7vcjzZmb3YPAPicy7d9Oxem2/2u nHUMYKGPd7zMciqltsBqGk0/N7G4vXpLQriAi1rVDahxYzV15kw34TR3q9uikbh8Qyq6 zuVYNxuI+tvOXeav6uYY53BHKkNdiVwnI3ArisTk8Ak8W6h8lPLgtBCEYfuIBqAJULIZ CtxOH5w/mw4/C026hX5eH6alI61vyAPq+1C0tOFpt2IwmTqhoximyMZuMTqDHUGo5z3M X8Hw== X-Forwarded-Encrypted: i=1; AKwUvBzVog17RaLN2apowqKq2P3H98m8SQ222XTvW6E60uOgaSUGUxpNA45ojdzgjaryfQIacM5xLx3Q4w0Y@vger.kernel.org X-Gm-Message-State: AFq9FYKur5rzxGaiGLRzkIzPNjhBDbdU6uG78xmLuviSesXToLwOE4rJ xfN1eh9IugLB6Z5A4ledQLe9oV9nOppgDVyrmf/YQBsIvpYja+NEEef0 X-Gm-Gg: AYBFou1QOIdljNedV4qU1yxpWghoZM3CNgots38Rh4bc1T+fkADj5J71QDE7rbVJlNC F/ZK2uVBnTAv8uSX5HJ3QuBLpk3d5WrMikhjNFjvP4JHJpJAda3skBIqiZw6uWvxyKJy9Qf1yQZ LiDjsFsybKr5mhe/yrEoR7hChhJBdHmqEAPTXZYAuVAEosWJj8PsrkoNBRJr4D7zdlSr4lJ/C+O z+1qfynlcGH0+5ZIpEKdw2VEDd3euBvynURKFTyukqwLD07sFqjLaj0OmxaxR+Zqui6G2d9JFAO YlHa9i5BlABwq1K+0xVJqiAQ2/rKtDBV3W0fZ8z0awAu20sxPJ+4dLyF1pej3haW9Pu3+yqZk/9 ESbR8xJvHcE8iEC6xH1ieNbJArMgabr9aqsl0tU6QkvVUDZlB97jYJ1DZPAaEW3bmdofWRDA+O5 dSBfgXNZ1Rdrj+UUOwdGAljUZUJuWZ2MnznfvWpvT8T9olQhQVYuaMNiu8Zq5Ha0KZVOI7FtuBZ 98dw0WJ+v2cWRzcCunicusGAN/id71B0EF+DblOrHhw2uzCD6zb4MjAHJ10EWR1l2621To8jh2k jA== 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: devicetree@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