* [PATCH] HID: i2c-hid: acpi: Add Dell Latitude 7400 2-in-1 touchscreen power quirk
@ 2026-11-10 16:08 HoroTW
2026-11-10 17:20 ` Andy Shevchenko
0 siblings, 1 reply; 2+ messages in thread
From: HoroTW @ 2026-11-10 16:08 UTC (permalink / raw)
To: jikos, bentiss; +Cc: linux-input, linux-kernel, mika.westerberg, andy, git
On the Dell Latitude 7400 2-in-1, the Wacom touchscreen (WCOM48CA) does
not work after resume from S3. Resume runs into I2C timeouts for 4 to 37
seconds and then fails:
[37928.985861] i2c_designware i2c_designware.0: controller timed out
[37928.986100] i2c_hid_acpi i2c-WCOM48CA:00: failed to change power setting.
[37928.986340] i2c_hid_acpi i2c-WCOM48CA:00: PM: dpm_run_callback(): acpi_subsys_resume returns -110
The controller has a power enable on GPP_B21 and an active-low reset on
GPP_B15. The firmware sets both up at boot, but ACPI does not describe
them: TPL0's _CRS only lists the I2C bus and the interrupt, and there is
no _PS0, _PS3 or _PR0. After S3 the firmware leaves both pads low, so
the controller stays unpowered and in reset. pinctrl-intel does not
restore them either, as it only saves pads that the kernel has requested.
Add a DMI quirk that provides the two GPIOs through a GPIO lookup table,
and power_up()/power_down() callbacks that sequence them like i2c-hid-of
does: power on, wait, release reset, wait. Holding the GPIOs from probe
makes pinctrl-intel save and restore the pads, and power_down() drives
them low first, so power_up() always starts from the powered-down state.
As far as I can tell from the BIOS image, the firmware waits about 9 ms
after enabling power before pulsing reset, so use 10 ms. Starting the
first transfer less than 3 ms after releasing reset makes SET_POWER fail
with -121, while 100 ms and 1000 ms both work; use 200 ms for some
margin, as I could not find a datasheet value.
As the touchscreen is not a wakeup source, the controller is now also
powered down in s2idle and for hibernation.
Tested on a Latitude 7400 2-in-1 (BIOS 1.39.1) in S3, s2idle and
hibernation (pm_test=devices).
AI notice: I threw an AI coding agent against it which wrote most of
the initial patch, I reviewed everything, directed the investigation
and ensured this is a minimal patch. The agent ran the initial tests
(via a KVM switch), and I tested it on the device myself as well.
Signed-off-by: HoroTW <git@horotw.dev>
---
Notes:
This is my first kernel patch, so sorry in advance for anything I got
wrong; I would be glad about any feedback.. I tried hard to match the
required ettiquete.
Background: the Latitude 7400 2-in-1 is a nice device, but it has
issues with S3 sleep. As I did not have much time, I threw an AI coding
agent on it and steered it in the directions that looked promising,
until it found out what goes wrong and how it could be fixed.
I reviewed all of the code and tried to keep the patch minimal,
while keeping it readable and oriented me on how similar cases were
handled before.
- I looked at i2c-hid-dmi-quirks.c and tried to follow its pattern (DMI
match, then device name), but kept the table in i2c-hid-acpi.c, as
only this driver uses it, and did not add a bigger structure for a
single device. My thought was that if more models need this, the
delays could move into the DMI match data, as i2c-hid-of-elan keeps
them per compatible. I'm happy to rework it if you would prefer it
differently, e.g. in i2c-hid-dmi-quirks.c or drivers/platform/x86.
- The GPIOs are only requested for the quirked device and with named
con_ids, so as far as I understand, gpiolib-acpi will not fall back
to _CRS and pick up the touchscreen's GpioInt.
- I did not add checks for the return values of the GPIO writes in
power_up(), following i2c-hid-of; i2c_hid_core_resume() ignores the
return value of power_up() anyway.
- Only the Wacom WCOM48CA panel was tested. The DSDT can also describe
other panels in the TPL0 node; this quirk does not cover them.
- Test setup: Latitude 7400 2-in-1 (SKU 08E2, board 0HCNR3, Core
i5-8365U, touch controller 056A:48CA), BIOS 1.39.1. S3 works with the
BIOS "Sleep Mode" set to "Force S3" as well as "OS Automatic
Selection". Kernel v7.2.14 with i2c-hid.ko and i2c-hid-acpi.ko rebuilt
with this patch: 7 S3 and 5 s2idle cycles, hibernation freeze/restore
(pm_test=devices) and a driver reload. After every resume the touch
resume callback returned 0 in about 315 ms and the controller answered
HID GET_REPORT; finger input worked after S3. Without the patch, one
S3 cycle reproduced the failure. The patch builds cleanly on v7.4-rc2
with W=1 and sparse.
- I was not sure whether this is suitable for stable... (I think it is?),
so I did not add a Cc: stable tag and would leave that to you.
drivers/hid/i2c-hid/i2c-hid-acpi.c | 98 ++++++++++++++++++++++++++++++
1 file changed, 98 insertions(+)
diff --git a/drivers/hid/i2c-hid/i2c-hid-acpi.c b/drivers/hid/i2c-hid/i2c-hid-acpi.c
index 4362c85dfa7e..4ff5b4913e78 100644
--- a/drivers/hid/i2c-hid/i2c-hid-acpi.c
+++ b/drivers/hid/i2c-hid/i2c-hid-acpi.c
@@ -20,11 +20,17 @@
*/
#include <linux/acpi.h>
+#include <linux/delay.h>
#include <linux/device.h>
+#include <linux/dmi.h>
+#include <linux/err.h>
+#include <linux/gpio/consumer.h>
+#include <linux/gpio/machine.h>
#include <linux/i2c.h>
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/pm.h>
+#include <linux/string.h>
#include "i2c-hid.h"
#include "i2c-hid-acpi.h"
@@ -32,6 +38,8 @@
struct i2c_hid_acpi {
struct i2chid_ops ops;
struct acpi_device *adev;
+ struct gpio_desc *power_gpio;
+ struct gpio_desc *reset_gpio;
};
static const struct acpi_device_id i2c_hid_acpi_blacklist[] = {
@@ -48,6 +56,92 @@ static const struct acpi_device_id i2c_hid_acpi_blacklist[] = {
{ }
};
+/*
+ * Dell Latitude 7400 2-in-1: the touch controller's power enable (GPP_B21)
+ * and active-low reset (GPP_B15) are missing from ACPI, and the firmware
+ * leaves both low after resume from S3. GPIO offsets use pinctrl-intel's
+ * 32-per-group numbering.
+ */
+static struct gpiod_lookup_table dell_latitude_7400_2in1_touch_gpios = {
+ .dev_id = "i2c-WCOM48CA:00",
+ .table = {
+ GPIO_LOOKUP("INT34BB:00", 53, "power", GPIO_ACTIVE_HIGH),
+ GPIO_LOOKUP("INT34BB:00", 47, "reset", GPIO_ACTIVE_LOW),
+ { }
+ },
+};
+
+static const struct dmi_system_id i2c_hid_acpi_gpio_quirks[] = {
+ {
+ .matches = {
+ DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc."),
+ DMI_EXACT_MATCH(DMI_PRODUCT_NAME, "Latitude 7400 2-in-1"),
+ },
+ .driver_data = &dell_latitude_7400_2in1_touch_gpios,
+ },
+ { }
+};
+
+static int i2c_hid_acpi_power_up(struct i2chid_ops *ops)
+{
+ struct i2c_hid_acpi *ihid_acpi = container_of(ops, struct i2c_hid_acpi, ops);
+
+ gpiod_set_value_cansleep(ihid_acpi->power_gpio, 1);
+ fsleep(10 * USEC_PER_MSEC);
+ gpiod_set_value_cansleep(ihid_acpi->reset_gpio, 0);
+ msleep(200);
+
+ return 0;
+}
+
+static void i2c_hid_acpi_power_down(struct i2chid_ops *ops)
+{
+ struct i2c_hid_acpi *ihid_acpi = container_of(ops, struct i2c_hid_acpi, ops);
+
+ gpiod_set_value_cansleep(ihid_acpi->reset_gpio, 1);
+ gpiod_set_value_cansleep(ihid_acpi->power_gpio, 0);
+}
+
+static int i2c_hid_acpi_setup_power_seq(struct device *dev,
+ struct i2c_hid_acpi *ihid_acpi)
+{
+ struct gpiod_lookup_table *gpios;
+ const struct dmi_system_id *id;
+
+ id = dmi_first_match(i2c_hid_acpi_gpio_quirks);
+ if (!id)
+ return 0;
+
+ /* GPIO lookup tables are keyed by device name */
+ gpios = id->driver_data;
+ if (strcmp(dev_name(dev), gpios->dev_id))
+ return 0;
+
+ /*
+ * Hold the controller in reset until power_up() releases it. Leave
+ * power as the firmware set it up, so the reset happens with stable
+ * power. Owning the GPIOs also makes pinctrl-intel save and restore
+ * them across suspend; power_down() drives both low first. The lookup
+ * table is only needed while requesting them.
+ */
+ gpiod_add_lookup_table(gpios);
+ ihid_acpi->reset_gpio = devm_gpiod_get(dev, "reset", GPIOD_OUT_HIGH);
+ ihid_acpi->power_gpio = devm_gpiod_get(dev, "power", GPIOD_OUT_HIGH);
+ gpiod_remove_lookup_table(gpios);
+
+ if (IS_ERR(ihid_acpi->reset_gpio))
+ return dev_err_probe(dev, PTR_ERR(ihid_acpi->reset_gpio),
+ "failed to get reset GPIO\n");
+ if (IS_ERR(ihid_acpi->power_gpio))
+ return dev_err_probe(dev, PTR_ERR(ihid_acpi->power_gpio),
+ "failed to get power GPIO\n");
+
+ ihid_acpi->ops.power_up = i2c_hid_acpi_power_up;
+ ihid_acpi->ops.power_down = i2c_hid_acpi_power_down;
+
+ return 0;
+}
+
static void i2c_hid_acpi_restore_sequence(struct i2chid_ops *ops)
{
struct i2c_hid_acpi *ihid_acpi = container_of(ops, struct i2c_hid_acpi, ops);
@@ -86,6 +180,10 @@ static int i2c_hid_acpi_probe(struct i2c_client *client)
ihid_acpi->ops.shutdown_tail = i2c_hid_acpi_shutdown_tail;
ihid_acpi->ops.restore_sequence = i2c_hid_acpi_restore_sequence;
+ ret = i2c_hid_acpi_setup_power_seq(dev, ihid_acpi);
+ if (ret)
+ return ret;
+
acpi_device_fix_up_power(adev);
return i2c_hid_core_probe(client, &ihid_acpi->ops,
base-commit: 7475ea8b58616b33c04afb976cf0fbaad9dd63fb
--
2.43.0
^ permalink raw reply [flat|nested] 2+ messages in thread* Re: [PATCH] HID: i2c-hid: acpi: Add Dell Latitude 7400 2-in-1 touchscreen power quirk
2026-11-10 16:08 [PATCH] HID: i2c-hid: acpi: Add Dell Latitude 7400 2-in-1 touchscreen power quirk HoroTW
@ 2026-11-10 17:20 ` Andy Shevchenko
0 siblings, 0 replies; 2+ messages in thread
From: Andy Shevchenko @ 2026-11-10 17:20 UTC (permalink / raw)
To: HoroTW; +Cc: jikos, bentiss, linux-input, linux-kernel, mika.westerberg, andy
On Tue, Nov 10, 2026 at 04:08:04PM +0000, HoroTW wrote:
> On the Dell Latitude 7400 2-in-1, the Wacom touchscreen (WCOM48CA) does
> not work after resume from S3. Resume runs into I2C timeouts for 4 to 37
> seconds and then fails:
>
> [37928.985861] i2c_designware i2c_designware.0: controller timed out
> [37928.986100] i2c_hid_acpi i2c-WCOM48CA:00: failed to change power setting.
> [37928.986340] i2c_hid_acpi i2c-WCOM48CA:00: PM: dpm_run_callback(): acpi_subsys_resume returns -110
>
> The controller has a power enable on GPP_B21 and an active-low reset on
> GPP_B15.
Oh, cool! Do you have access to the schematics?
> The firmware sets both up at boot, but ACPI does not describe
> them: TPL0's _CRS only lists the I2C bus and the interrupt, and there is
> no _PS0, _PS3 or _PR0. After S3 the firmware leaves both pads low, so
> the controller stays unpowered and in reset. pinctrl-intel does not
> restore them either, as it only saves pads that the kernel has requested.
Oh, can you bring the excerpts from /sys/kernel/debug/pinctrl/*/pins file
for B21 and B15 before the suspend (working case) and after (non-working),
so we can see their configurations?
> Add a DMI quirk that provides the two GPIOs through a GPIO lookup table,
> and power_up()/power_down() callbacks that sequence them like i2c-hid-of
> does: power on, wait, release reset, wait. Holding the GPIOs from probe
> makes pinctrl-intel save and restore the pads, and power_down() drives
> them low first, so power_up() always starts from the powered-down state.
>
> As far as I can tell from the BIOS image, the firmware waits about 9 ms
> after enabling power before pulsing reset, so use 10 ms. Starting the
> first transfer less than 3 ms after releasing reset makes SET_POWER fail
> with -121, while 100 ms and 1000 ms both work; use 200 ms for some
> margin, as I could not find a datasheet value.
>
> As the touchscreen is not a wakeup source, the controller is now also
> powered down in s2idle and for hibernation.
> Tested on a Latitude 7400 2-in-1 (BIOS 1.39.1) in S3, s2idle and
> hibernation (pm_test=devices).
> AI notice: I threw an AI coding agent against it which wrote most of
> the initial patch, I reviewed everything, directed the investigation
> and ensured this is a minimal patch. The agent ran the initial tests
> (via a KVM switch), and I tested it on the device myself as well.
For now this paragraph should go after '---' line below.
> Signed-off-by: HoroTW <git@horotw.dev>
> ---
>
> Notes:
> This is my first kernel patch, so sorry in advance for anything I got
> wrong; I would be glad about any feedback.. I tried hard to match the
> required ettiquete.
>
> Background: the Latitude 7400 2-in-1 is a nice device, but it has
> issues with S3 sleep. As I did not have much time, I threw an AI coding
> agent on it and steered it in the directions that looked promising,
> until it found out what goes wrong and how it could be fixed.
> I reviewed all of the code and tried to keep the patch minimal,
> while keeping it readable and oriented me on how similar cases were
> handled before.
>
> - I looked at i2c-hid-dmi-quirks.c and tried to follow its pattern (DMI
> match, then device name), but kept the table in i2c-hid-acpi.c, as
> only this driver uses it, and did not add a bigger structure for a
> single device. My thought was that if more models need this, the
> delays could move into the DMI match data, as i2c-hid-of-elan keeps
> them per compatible. I'm happy to rework it if you would prefer it
> differently, e.g. in i2c-hid-dmi-quirks.c or drivers/platform/x86.
>
> - The GPIOs are only requested for the quirked device and with named
> con_ids, so as far as I understand, gpiolib-acpi will not fall back
> to _CRS and pick up the touchscreen's GpioInt.
>
> - I did not add checks for the return values of the GPIO writes in
> power_up(), following i2c-hid-of; i2c_hid_core_resume() ignores the
> return value of power_up() anyway.
>
> - Only the Wacom WCOM48CA panel was tested. The DSDT can also describe
> other panels in the TPL0 node; this quirk does not cover them.
>
> - Test setup: Latitude 7400 2-in-1 (SKU 08E2, board 0HCNR3, Core
> i5-8365U, touch controller 056A:48CA), BIOS 1.39.1. S3 works with the
> BIOS "Sleep Mode" set to "Force S3" as well as "OS Automatic
> Selection". Kernel v7.2.14 with i2c-hid.ko and i2c-hid-acpi.ko rebuilt
> with this patch: 7 S3 and 5 s2idle cycles, hibernation freeze/restore
> (pm_test=devices) and a driver reload. After every resume the touch
> resume callback returned 0 in about 315 ms and the controller answered
> HID GET_REPORT; finger input worked after S3. Without the patch, one
> S3 cycle reproduced the failure. The patch builds cleanly on v7.4-rc2
> with W=1 and sparse.
>
> - I was not sure whether this is suitable for stable... (I think it is?),
> so I did not add a Cc: stable tag and would leave that to you.
--
With Best Regards,
Andy Shevchenko
^ permalink raw reply [flat|nested] 2+ messages in thread
end of thread, other threads:[~2026-11-10 17:21 UTC | newest]
Thread overview: 2+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2026-11-10 16:08 [PATCH] HID: i2c-hid: acpi: Add Dell Latitude 7400 2-in-1 touchscreen power quirk HoroTW
2026-11-10 17:20 ` Andy Shevchenko
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox
all inboxes | Powered by JetHome®