From: sashiko-bot@kernel.org
To: "Luka Panio" <lukapanio@gmail.com>
Cc: linux-input@vger.kernel.org, devicetree@vger.kernel.org,
dri-devel@lists.freedesktop.org, robh@kernel.org,
conor+dt@kernel.org, dmitry.torokhov@gmail.com
Subject: Re: [PATCH 7/7] Input: edt-ft5x06 - support following a DRM panel
Date: Sat, 17 Oct 2026 14:30:12 +0000 [thread overview]
Message-ID: <sashiko-outbox-175550@kernel.org> (raw)
In-Reply-To: <20261017140827.290666-8-lukapanio@gmail.com>
Thank you for your contribution! Sashiko AI review found 5 potential issue(s) to consider:
- [High] Input: edt-ft5x06 - unbind race between panel_work and remove/devres teardown
- [High] Input: edt-ft5x06 - non-idempotent edt_ft5x06_ts_init_hw() retried in panel_work
- [Medium] Input: edt-ft5x06 - reset_gpio left deasserted on boot failure in panel_work
- [Medium] Input: edt-ft5x06 - missing powered_off check and factory_mode restore for panel follower
- [Medium] Input: edt-ft5x06 - unlocked sysfs reads in model_show() and fw_version_show()
--
commit 8d0f54b6a0e4f2aafd88896b2e7280382657e313
Author: Luka Panio <lukapanio@gmail.com>
Input: edt-ft5x06 - support following a DRM panel
This commit adds DRM panel follower support to the edt-ft5x06 touchscreen
driver for TDDI controllers that share power and reset lines with a display
panel. When following a panel, initial hardware probing and subsequent power
transitions are deferred to a workqueue triggered by the panel follower
callbacks.
> diff --git a/drivers/input/touchscreen/edt-ft5x06.c b/drivers/input/touchscreen/edt-ft5x06.c
> index 7bcc48e07d629..b2fca6c372052 100644
> --- a/drivers/input/touchscreen/edt-ft5x06.c
> +++ b/drivers/input/touchscreen/edt-ft5x06.c
[ ... ]
> @@ -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;
> + }
[Severity: High]
Is it safe to retry edt_ft5x06_ts_init_hw() on a subsequent panel prepare
cycle if the first attempt fails?
edt_ft5x06_ts_init_hw() performs several one-shot actions that do not roll
back on error:
1. In edt_ft5x06_ts_init_hw() -> edt_ft5x06_ts_identify(), if an EDT_M06
chip is detected, it destroys tsdata->regmap and replaces it:
regmap_exit(tsdata->regmap);
tsdata->regmap = regmap_init_i2c(client,
&edt_M06_i2c_regmap_config);
if (IS_ERR(tsdata->regmap)) {
dev_err(tsdata->dev, "regmap allocation failed\n");
return PTR_ERR(tsdata->regmap);
}
If regmap_init_i2c() returns an error pointer, the next retry will pass
that error pointer into regmap_bulk_read() and dereference it. Even if
regmap_init_i2c() succeeds and a later step in edt_ft5x06_ts_init_hw()
fails, the next retry will call edt_ft5x06_ts_identify() using
edt_M06_i2c_regmap_config, which masks the 0xBB identification register
to 0x3B and only reads a single byte, breaking M06 identification.
2. edt_ft5x06_ts_init_hw() calls input_register_device(tsdata->input) on the
input_dev allocated once in edt_ft5x06_probe(). If input_register_device()
fails, struct input_dev cannot be passed to input_register_device() a
second time on the next retry.
3. In edt_ft5x06_ts_init_hw(), if tsdata->boot() fails, it calls
dev_err_probe() from the background panel_work workqueue after
edt_ft5x06_probe() has already returned.
> +
> + 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);
> + }
[Severity: Medium]
Should reset_gpio be re-asserted to 1 if tsdata->boot() fails here, just as
is done in the !tsdata->hw_initialized error path above?
Because tsdata->powered_off remains true on this early return, a subsequent
call to edt_ft5x06_panel_unpreparing() will see tsdata->powered_off == true
and return early without asserting reset_gpio to 1:
if (tsdata->powered_off)
return 0;
disable_irq(tsdata->irq);
gpiod_set_value_cansleep(tsdata->reset_gpio, 1);
tsdata->powered_off = true;
That leaves reset_gpio deasserted (0) while the shared TDDI panel power goes
off, and also prevents a 1 -> 0 reset transition on the next panel wake-up.
> +
> + edt_ft5x06_restore_reg_parameters(tsdata);
> +out:
> + tsdata->powered_off = false;
> + enable_irq(tsdata->irq);
> +}
[Severity: Medium]
What happens if tsdata->factory_mode is active across a panel power cycle, or
if the debugfs files are accessed while tsdata->powered_off is true?
In edt_ft5x06_debugfs_mode_set() and edt_ft5x06_debugfs_raw_data_read(),
tsdata->mutex is acquired, but unlike edt_ft5x06_setting_show() and
edt_ft5x06_setting_store(), neither function checks tsdata->powered_off
before accessing the controller over regmap or toggling IRQ state.
In addition, if factory mode was enabled before the panel turned off
(calling disable_irq(tsdata->irq) once and setting tsdata->factory_mode =
true), edt_ft5x06_panel_unpreparing() will call disable_irq(tsdata->irq) a
second time and assert reset_gpio. When edt_ft5x06_panel_work() runs on
resume, the controller comes out of reset in work mode while
tsdata->factory_mode is still true, causing edt_ft5x06_restore_reg_parameters()
on M06 to format writes as factory-mode requests, and leaving tsdata->irq
disabled after the single enable_irq(tsdata->irq) call because
edt_ft5x06_factory_mode() is not called as in edt_ft5x06_ts_resume():
edt_ft5x06_restore_reg_parameters(tsdata);
enable_irq(tsdata->irq);
if (tsdata->factory_mode)
ret = edt_ft5x06_factory_mode(tsdata);
[ ... ]
> +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);
> +}
[Severity: High]
Can deferring panel follower removal and cancel_work_sync(&tsdata->panel_work)
to devres actions race with edt_ft5x06_remove() and devres teardown when the
driver is unbound while edt_ft5x06_panel_work() is running?
When unbinding the driver:
1. edt_ft5x06_remove() runs before devres release:
void edt_ft5x06_remove(struct device *dev)
{
struct edt_ft5x06_ts_data *tsdata = dev_get_drvdata(dev);
edt_ft5x06_ts_teardown_debugfs(tsdata);
}
For I2C devices, i2c_device_remove() then immediately calls
debugfs_remove_recursive(client->debugfs) before devres_release_group().
Because panel_work has not been cancelled yet, a concurrent execution of
edt_ft5x06_panel_work() -> edt_ft5x06_ts_init_hw() ->
edt_ft5x06_ts_prepare_debugfs() can dereference the freed tsdata->debugfs
dentry and allocate tsdata->raw_buffer after teardown has already run.
2. During devres teardown, remove_nodes() drains dev->devres_head into a
local todo list before release_nodes() invokes the devm actions for
drm_panel_remove_follower and edt_ft5x06_cancel_panel_work. If
edt_ft5x06_panel_work() is mid-flight inside edt_ft5x06_ts_init_hw()
waiting to finish while edt_ft5x06_cancel_panel_work() blocks in
cancel_work_sync(), input_register_device(tsdata->input) will append
devm_input_device_unregister to dev->devres_head after dev->devres_head
was already drained into todo. release_nodes() will then execute
devm_input_device_release from todo without having run
devm_input_device_unregister.
> @@ -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;
> + }
[Severity: Medium]
When tsdata->is_panel_follower is true and edt_ft5x06_ts_init_hw() is
deferred to edt_ft5x06_panel_work(), should model_show() and
fw_version_show() hold tsdata->mutex when reading tsdata->name and
tsdata->fw_version?
static ssize_t model_show(struct device *dev,
struct device_attribute *attr, char *buf)
{
struct edt_ft5x06_ts_data *tsdata = dev_get_drvdata(dev);
return sysfs_emit(buf, "%s\n", tsdata->name);
}
static ssize_t fw_version_show(struct device *dev,
struct device_attribute *attr, char *buf)
{
struct edt_ft5x06_ts_data *tsdata = dev_get_drvdata(dev);
return sysfs_emit(buf, "%s\n", tsdata->fw_version);
}
Previously, tsdata->name and tsdata->fw_version were populated synchronously
in edt_ft5x06_probe() before the sysfs attributes in edt_ft5x06_groups were
registered. With hardware initialization deferred to edt_ft5x06_panel_work(),
edt_ft5x06_ts_identify() now writes to tsdata->name and tsdata->fw_version
under tsdata->mutex after the sysfs files are already visible to userspace,
so unlocked reads in model_show() and fw_version_show() can race with
edt_ft5x06_ts_identify().
[ ... ]
>
>
>
--
Sashiko AI review · https://sashiko.dev/#/patchset/20261017140827.290666-1-lukapanio@gmail.com?part=7
prev parent reply other threads:[~2026-10-17 14:30 UTC|newest]
Thread overview: 11+ 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:29 ` sashiko-bot
2026-10-17 14:07 ` [PATCH 7/7] Input: edt-ft5x06 - support following a DRM panel Luka Panio
2026-10-17 14:30 ` sashiko-bot [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=sashiko-outbox-175550@kernel.org \
--to=sashiko-bot@kernel.org \
--cc=conor+dt@kernel.org \
--cc=devicetree@vger.kernel.org \
--cc=dmitry.torokhov@gmail.com \
--cc=dri-devel@lists.freedesktop.org \
--cc=linux-input@vger.kernel.org \
--cc=lukapanio@gmail.com \
--cc=robh@kernel.org \
--cc=sashiko-reviews@lists.linux.dev \
/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.