* [PATCH i-g-t v3 0/7] Validate PM_PME signalling on display hotplug
@ 2026-10-20 14:11 Pranay Samala
2026-10-20 14:11 ` [PATCH i-g-t v3 1/7] lib/igt_pci: Add PCI Power Management capability register layout Pranay Samala
` (8 more replies)
0 siblings, 9 replies; 10+ messages in thread
From: Pranay Samala @ 2026-10-20 14:11 UTC (permalink / raw)
To: igt-dev; +Cc: kunal1.joshi, sameer.lattannavar, pranay.samala
A display hotplug can be signalled as a PCI PME while the graphics device
is runtime suspended in D3hot. That lets the driver leave the
drm_kms_helper connector poll worker disabled across runtime suspend, so
the device stays parked instead of being resumed every ~10s just to probe
connectors.
The property worth validating is therefore not "does a hotplug still
work" - the polling fallback delivers that too, just slowly and at the
cost of the power saving. It is "does the hotplug work while polling
stays off". A test that only toggles HPD and waits for a uevent passes
either way, and would not notice the feature regressing into the
fallback.
So the new subtest runs against the same suspended device in three steps:
Control: suspend with power/wakeup disabled, so PME is not armed, and
check the device is woken by the poll within two periods.
This proves polling is live on the setup and that the test
catches a poll wake, and measures what one costs.
Phase 1: arm PME, hold the device idle in D3hot for 25s and check it
never wakes.
Phase 2: plug and then unplug from D3hot and check that each produces
a uevent, that the connector status updated, and that
power/wakeup_active_count incremented, i.e. that the resume
was signalled by the device and not initiated by the host.
Patches 1-2 add the PCI Power Management capability register layout and
accessors for PME_Support, PME_En, PME_Status and the PowerState field.
PME_Status distinguishes a device that signalled but was never
acknowledged from one that never signalled at all, which makes a failure
report actionable instead of just "no uevent".
Patches 3-5 add the power/wakeup and power/wakeup_active_count
accessors, with patch 3 factoring out the path construction they share.
active_count is used rather than wakeup_count because the latter never
moves for a runtime resume.
Patch 6 adds igt_kms_poll_save(), _set() and _restore() to igt_kms.
Converting the two tests that already open code this is left to a
separate series.
Patch 7 adds the subtest.
v2:
- Rebase
v3:
- 2/7: check power_state before touching config space, report D3cold
from it, return errors from PME_En/PME_Status reads, generic
kerneldoc (Kunal)
- 4/7: drop the xe note, document the counter reset on enable (Kunal)
- 5/7: mention the PME poll route, return -1 when there is no wakeup
source (Kunal)
- 6/7: move to igt_kms and rename, document that setting the parameter
does not restart a stopped poll worker (Kunal)
- 7/7: decide D3hot once, settle before baselines, watch the idle
window once, add the control run, fix the uevent/latency race, check
requirements before changing state and restore it on failure, misc
cleanups (Kunal)
Pranay Samala (7):
lib/igt_pci: Add PCI Power Management capability register layout
lib/igt_pm: Add PCI PME capability and D state accessors
lib/igt_pm: Factor out power attribute path construction
lib/igt_pm: Add power/wakeup accessors
lib/igt_pm: Add power/wakeup_active_count accessor
lib/igt_kms: Add drm_kms_helper.poll save/restore helpers
tests/chamelium/kms_chamelium_hpd: Add HPD from runtime suspended
D3hot
lib/igt_kms.c | 101 ++++++
lib/igt_kms.h | 3 +
lib/igt_pci.h | 12 +
lib/igt_pm.c | 450 +++++++++++++++++++++++-
lib/igt_pm.h | 9 +
tests/chamelium/kms_chamelium_hpd.c | 528 ++++++++++++++++++++++++++++
6 files changed, 1094 insertions(+), 9 deletions(-)
--
2.53.0
^ permalink raw reply [flat|nested] 10+ messages in thread
* [PATCH i-g-t v3 1/7] lib/igt_pci: Add PCI Power Management capability register layout
2026-10-20 14:11 [PATCH i-g-t v3 0/7] Validate PM_PME signalling on display hotplug Pranay Samala
@ 2026-10-20 14:11 ` Pranay Samala
2026-10-20 14:11 ` [PATCH i-g-t v3 2/7] lib/igt_pm: Add PCI PME capability and D state accessors Pranay Samala
` (7 subsequent siblings)
8 siblings, 0 replies; 10+ messages in thread
From: Pranay Samala @ 2026-10-20 14:11 UTC (permalink / raw)
To: igt-dev; +Cc: kunal1.joshi, sameer.lattannavar, pranay.samala
Add the register offsets and field masks of the PCI Power Management
capability. Two registers are needed.
PMC (PM Capabilities) holds the PME_Support field, which says what D states
the device can send a PME from.
PMCS (PM Control/Status) holds the PowerState field, the PME_En bit and the
PME_Status bit, which say what D state the device is in, whether it is
armed to send a PME, and whether it has one pending. PME_Status is
write-1-to-clear and stays set until someone acknowledges the PME, so it
distinguishes a device that never signalled from one whose signal was never
delivered.
Signed-off-by: Pranay Samala <pranay.samala@intel.com>
Reviewed-by: Kunal Joshi <kunal1.joshi@intel.com>
---
lib/igt_pci.h | 12 ++++++++++++
1 file changed, 12 insertions(+)
diff --git a/lib/igt_pci.h b/lib/igt_pci.h
index 509345b2d..ae612d611 100644
--- a/lib/igt_pci.h
+++ b/lib/igt_pci.h
@@ -26,6 +26,18 @@ enum pci_cap_id {
#define PCI_SLOT_CAP_OFFSET 0x14
#define PCI_SLOT_PWR_CTRL_PRESENT (1 << 1)
+/*
+ * PCI Power Management capability registers, at the given offsets from the
+ * start of the PM capability as returned by find_pci_cap_offset().
+ */
+#define PCI_PM_PMC_OFFSET 0x2 /* PM Capabilities */
+#define PCI_PM_PMC_PME_SUPPORT_SHIFT 11 /* PME_Support, bits [15:11] */
+#define PCI_PM_PMC_PME_SUPPORT_MASK (0x1f << PCI_PM_PMC_PME_SUPPORT_SHIFT)
+#define PCI_PM_PMCS_OFFSET 0x4 /* PM Control/Status */
+#define PCI_PM_PMCS_PSTATE_MASK 0x3 /* PowerState, bits [1:0] */
+#define PCI_PM_PMCS_PME_EN (1 << 8) /* PME_En */
+#define PCI_PM_PMCS_PME_STATUS (1 << 15) /* PME_Status, write 1 to clear */
+
int find_pci_cap_offset(struct pci_device *dev, enum pci_cap_id cap_id);
int igt_pci_device_unbind(const char *pci_slot);
int igt_pci_driver_bind(const char *driver, const char *pci_slot);
--
2.53.0
^ permalink raw reply related [flat|nested] 10+ messages in thread
* [PATCH i-g-t v3 2/7] lib/igt_pm: Add PCI PME capability and D state accessors
2026-10-20 14:11 [PATCH i-g-t v3 0/7] Validate PM_PME signalling on display hotplug Pranay Samala
2026-10-20 14:11 ` [PATCH i-g-t v3 1/7] lib/igt_pci: Add PCI Power Management capability register layout Pranay Samala
@ 2026-10-20 14:11 ` Pranay Samala
2026-10-20 14:11 ` [PATCH i-g-t v3 3/7] lib/igt_pm: Factor out power attribute path construction Pranay Samala
` (6 subsequent siblings)
8 siblings, 0 replies; 10+ messages in thread
From: Pranay Samala @ 2026-10-20 14:11 UTC (permalink / raw)
To: igt-dev; +Cc: kunal1.joshi, sameer.lattannavar, pranay.samala
Add helpers that read the PCI Power Management capability:
- igt_pm_pci_pme_supported(): whether PME_Support allows a PME from a
given D state. Support is per state, e.g. D3hot but not D3cold.
- igt_pm_pci_pme_enabled(): PME_En, whether the device is armed.
- igt_pm_pci_pme_status(): PME_Status, whether a PME is pending. It is
write-1-to-clear, so it tells a device that never signalled from one
whose PME was never delivered.
- igt_pm_pci_get_d_state(): the PowerState field.
Config space is read through sysfs, which resumes a device in D3cold
before the read. So the helpers check power_state first and, if it
reports D3cold, skip config space and igt_pm_pci_get_d_state() returns
D3cold.
pme_enabled() and pme_status() return a negative error code if the
register cannot be read, so a failed read is not mistaken for a clear
bit.
v3:
- Check power_state before touching config space, to avoid resuming a
D3cold device, and report D3cold from it (Kunal)
- Keep the kerneldoc generic, drop the Intel and xe specific notes (Kunal)
- Return an error from pme_enabled/pme_status on read failure, and
igt_debug() the failure (Kunal)
Signed-off-by: Pranay Samala <pranay.samala@intel.com>
---
lib/igt_pm.c | 233 +++++++++++++++++++++++++++++++++++++++++++++++++++
lib/igt_pm.h | 5 ++
2 files changed, 238 insertions(+)
diff --git a/lib/igt_pm.c b/lib/igt_pm.c
index 1e3cc7757..688bcd7d2 100644
--- a/lib/igt_pm.c
+++ b/lib/igt_pm.c
@@ -1524,6 +1524,239 @@ bool igt_has_pci_pm_capability(struct pci_device *pci_dev)
return (offset > 0);
}
+/*
+ * Read the kernel's view of the device's D state from the power_state sysfs
+ * attribute. Unlike a config space read this has no runtime PM side effects,
+ * so it is safe to use on a device in D3cold.
+ */
+static enum igt_acpi_d_state igt_pm_pci_get_power_state(struct pci_device *pci_dev)
+{
+ char name[PATH_MAX];
+ char d_state[16];
+ int fd, n_read;
+
+ snprintf(name, PATH_MAX, "/sys/bus/pci/devices/%04x:%02x:%02x.%01x/power_state",
+ pci_dev->domain, pci_dev->bus, pci_dev->dev, pci_dev->func);
+
+ fd = open(name, O_RDONLY);
+ if (fd < 0)
+ return IGT_ACPI_UNKNOWN_STATE;
+
+ n_read = read(fd, d_state, sizeof(d_state) - 1);
+ close(fd);
+ if (n_read <= 0)
+ return IGT_ACPI_UNKNOWN_STATE;
+
+ d_state[n_read] = '\0';
+ d_state[strcspn(d_state, "\n")] = '\0';
+
+ if (!strcmp(d_state, "D0"))
+ return IGT_ACPI_D0;
+ if (!strcmp(d_state, "D1"))
+ return IGT_ACPI_D1;
+ if (!strcmp(d_state, "D2"))
+ return IGT_ACPI_D2;
+ if (!strcmp(d_state, "D3hot"))
+ return IGT_ACPI_D3Hot;
+ if (!strcmp(d_state, "D3cold"))
+ return IGT_ACPI_D3Cold;
+
+ return IGT_ACPI_UNKNOWN_STATE;
+}
+
+/*
+ * Read a 16 bit register of the PCI Power Management capability, at @reg_offset
+ * from the start of the capability.
+ *
+ * Config space is read through sysfs, where pci_config_pm_runtime_get() resumes
+ * a device in D3cold before the access. To avoid waking the device being
+ * inspected, refuse with -EBUSY if the kernel reports it in D3cold. A device in
+ * D3hot is not resumed, so the registers can be read while it stays suspended.
+ *
+ * Returns: 0 on success, -EBUSY if the device is in D3cold, -ENODEV if it has no
+ * PM capability or did not respond, or -EIO if the config space read failed.
+ */
+static int igt_pm_read_pci_pm_reg(struct pci_device *pci_dev, int reg_offset,
+ uint16_t *val)
+{
+ int offset;
+
+ if (igt_pm_pci_get_power_state(pci_dev) == IGT_ACPI_D3Cold) {
+ igt_debug("PCI '%04x:%02x:%02x.%01x' is in D3cold, not reading config space\n",
+ pci_dev->domain, pci_dev->bus, pci_dev->dev, pci_dev->func);
+ return -EBUSY;
+ }
+
+ offset = find_pci_cap_offset(pci_dev, PCI_PM_CAP_ID);
+ if (offset <= 0) {
+ igt_debug("PCI '%04x:%02x:%02x.%01x' PM capability %s\n",
+ pci_dev->domain, pci_dev->bus, pci_dev->dev, pci_dev->func,
+ offset ? "lookup failed" : "not present");
+ return offset ? -EIO : -ENODEV;
+ }
+
+ if (pci_device_cfg_read_u16(pci_dev, val, offset + reg_offset)) {
+ igt_debug("PCI '%04x:%02x:%02x.%01x' config read at 0x%x failed\n",
+ pci_dev->domain, pci_dev->bus, pci_dev->dev, pci_dev->func,
+ offset + reg_offset);
+ return -EIO;
+ }
+
+ /*
+ * A device that has gone away reads back as all ones, which would
+ * otherwise decode as a valid register value: PMCS 0xffff means
+ * PowerState = D3hot with PME_En set. Reject it.
+ */
+ if (*val == 0xffff) {
+ igt_debug("PCI '%04x:%02x:%02x.%01x' config read at 0x%x returned all ones\n",
+ pci_dev->domain, pci_dev->bus, pci_dev->dev, pci_dev->func,
+ offset + reg_offset);
+ return -ENODEV;
+ }
+
+ return 0;
+}
+
+/**
+ * igt_pm_pci_pme_supported:
+ * @pci_dev: PCI device struct
+ * @state: D state to query PME support for
+ *
+ * Reads the PME_Support field (bits [15:11]) of the PCI Power Management
+ * Capabilities (PMC) register and reports whether the device is able to
+ * generate a Power Management Event from @state.
+ *
+ * PME support is advertised per D state, so a device may be able to signal from
+ * D3hot but not from D3cold. Query the state the device is expected to suspend
+ * into.
+ *
+ * Returns: true if the device can generate a PME from @state, false if it cannot
+ * or the register could not be read.
+ */
+bool igt_pm_pci_pme_supported(struct pci_device *pci_dev,
+ enum igt_acpi_d_state state)
+{
+ uint16_t pmc;
+ int bit;
+
+ switch (state) {
+ case IGT_ACPI_D0:
+ bit = 0;
+ break;
+ case IGT_ACPI_D1:
+ bit = 1;
+ break;
+ case IGT_ACPI_D2:
+ bit = 2;
+ break;
+ case IGT_ACPI_D3Hot:
+ bit = 3;
+ break;
+ case IGT_ACPI_D3Cold:
+ bit = 4;
+ break;
+ default:
+ igt_debug("Invalid D state %d for PME support query\n", state);
+ return false;
+ }
+
+ if (igt_pm_read_pci_pm_reg(pci_dev, PCI_PM_PMC_OFFSET, &pmc))
+ return false;
+
+ igt_debug("PCI '%04x:%02x:%02x.%01x' PMC = 0x%04x, PME_Support = 0x%02x\n",
+ pci_dev->domain, pci_dev->bus, pci_dev->dev, pci_dev->func, pmc,
+ (pmc & PCI_PM_PMC_PME_SUPPORT_MASK) >> PCI_PM_PMC_PME_SUPPORT_SHIFT);
+
+ return !!(pmc & (1 << (PCI_PM_PMC_PME_SUPPORT_SHIFT + bit)));
+}
+
+/**
+ * igt_pm_pci_pme_enabled:
+ * @pci_dev: PCI device struct
+ *
+ * Reads PME_En (bit 8) of the PCI Power Management Control/Status (PMCS)
+ * register, i.e. whether the device is armed to generate PMEs.
+ *
+ * The PCI/PM core sets this on suspend if the device can generate a PME from the
+ * state it is suspending into, but a driver may also arm PME itself from its
+ * own suspend hook, in which case the driver's own conditions apply.
+ *
+ * Returns: 1 if the device is armed to generate PMEs, 0 if it is not, or a
+ * negative error code if the register could not be read.
+ */
+int igt_pm_pci_pme_enabled(struct pci_device *pci_dev)
+{
+ uint16_t pmcs;
+ int ret;
+
+ ret = igt_pm_read_pci_pm_reg(pci_dev, PCI_PM_PMCS_OFFSET, &pmcs);
+ if (ret)
+ return ret;
+
+ return !!(pmcs & PCI_PM_PMCS_PME_EN);
+}
+
+/**
+ * igt_pm_pci_pme_status:
+ * @pci_dev: PCI device struct
+ *
+ * Reads PME_Status (bit 15) of the PCI Power Management Control/Status (PMCS)
+ * register, i.e. whether the device has a PME pending.
+ *
+ * The bit is write-1-to-clear and the device does not clear it itself. It stays
+ * set from the moment the device signals a PME until software acknowledges it,
+ * which the PCI/PM core does when it handles the PME. So finding it still set
+ * on a device that is still suspended means the device did signal but the
+ * platform never delivered the PME, whereas finding it clear means the device
+ * never signalled at all.
+ *
+ * Returns: 1 if the device has a PME pending, 0 if it does not, or a negative
+ * error code if the register could not be read.
+ */
+int igt_pm_pci_pme_status(struct pci_device *pci_dev)
+{
+ uint16_t pmcs;
+ int ret;
+
+ ret = igt_pm_read_pci_pm_reg(pci_dev, PCI_PM_PMCS_OFFSET, &pmcs);
+ if (ret)
+ return ret;
+
+ return !!(pmcs & PCI_PM_PMCS_PME_STATUS);
+}
+
+/**
+ * igt_pm_pci_get_d_state:
+ * @pci_dev: PCI device struct
+ *
+ * Reads the PowerState field (bits [1:0]) of the PCI Power Management
+ * Control/Status (PMCS) register, i.e. the D state as reported by the device.
+ *
+ * PMCS can only express D0-D3hot, and a device in D3cold cannot answer config
+ * space reads at all. So if the kernel's power_state attribute reports D3cold,
+ * that is returned instead, without touching config space and so without
+ * resuming the device.
+ *
+ * Returns: the D state of the device, or IGT_ACPI_UNKNOWN_STATE if the device
+ * has no PM capability or the read failed.
+ */
+enum igt_acpi_d_state igt_pm_pci_get_d_state(struct pci_device *pci_dev)
+{
+ static const enum igt_acpi_d_state d_states[] = {
+ IGT_ACPI_D0, IGT_ACPI_D1, IGT_ACPI_D2, IGT_ACPI_D3Hot,
+ };
+ uint16_t pmcs;
+ int ret;
+
+ ret = igt_pm_read_pci_pm_reg(pci_dev, PCI_PM_PMCS_OFFSET, &pmcs);
+ if (ret == -EBUSY)
+ return IGT_ACPI_D3Cold;
+ if (ret)
+ return IGT_ACPI_UNKNOWN_STATE;
+
+ return d_states[pmcs & PCI_PM_PMCS_PSTATE_MASK];
+}
+
/**
* igt_pm_dpms_toggle:
* @output: igt output for which DPMS toggle has to be performed
diff --git a/lib/igt_pm.h b/lib/igt_pm.h
index 328875f9c..861741b89 100644
--- a/lib/igt_pm.h
+++ b/lib/igt_pm.h
@@ -105,6 +105,11 @@ uint64_t igt_pm_get_runtime_active_time(struct pci_device *pci_dev);
int igt_pm_get_runtime_usage(struct pci_device *pci_dev);
void igt_pm_ignore_slpc_efficient_freq(int i915, int gtfd, bool val);
bool igt_has_pci_pm_capability(struct pci_device *pci_dev);
+bool igt_pm_pci_pme_supported(struct pci_device *pci_dev,
+ enum igt_acpi_d_state state);
+int igt_pm_pci_pme_enabled(struct pci_device *pci_dev);
+int igt_pm_pci_pme_status(struct pci_device *pci_dev);
+enum igt_acpi_d_state igt_pm_pci_get_d_state(struct pci_device *pci_dev);
void igt_pm_dpms_toggle(igt_output_t *output);
uint32_t igt_get_dc_counter(const char *dc_data);
bool igt_support_dc6(int debugfs_fd);
--
2.53.0
^ permalink raw reply related [flat|nested] 10+ messages in thread
* [PATCH i-g-t v3 3/7] lib/igt_pm: Factor out power attribute path construction
2026-10-20 14:11 [PATCH i-g-t v3 0/7] Validate PM_PME signalling on display hotplug Pranay Samala
2026-10-20 14:11 ` [PATCH i-g-t v3 1/7] lib/igt_pci: Add PCI Power Management capability register layout Pranay Samala
2026-10-20 14:11 ` [PATCH i-g-t v3 2/7] lib/igt_pm: Add PCI PME capability and D state accessors Pranay Samala
@ 2026-10-20 14:11 ` Pranay Samala
2026-10-20 14:11 ` [PATCH i-g-t v3 4/7] lib/igt_pm: Add power/wakeup accessors Pranay Samala
` (5 subsequent siblings)
8 siblings, 0 replies; 10+ messages in thread
From: Pranay Samala @ 2026-10-20 14:11 UTC (permalink / raw)
To: igt-dev; +Cc: kunal1.joshi, sameer.lattannavar, pranay.samala
igt_pm_get_power_attr_fd() and igt_pm_get_power_attr_fd_rdonly() build the
same sysfs path with the same snprintf(). The only difference is the open()
flags.
Move the shared part into __igt_pm_get_power_attr_fd(), which takes the
flags and returns -1 instead of asserting.
Nothing changes for existing callers. Both wrappers still assert, so they
behave exactly as before.
The point of the change is the -1. Not every power attribute exists on
every device, so later callers need to ask whether one is present and get
an answer back rather than failing the test. Without this they would each
have to repeat the snprintf().
Signed-off-by: Pranay Samala <pranay.samala@intel.com>
Reviewed-by: Kunal Joshi <kunal1.joshi@intel.com>
---
lib/igt_pm.c | 24 +++++++++++++++---------
1 file changed, 15 insertions(+), 9 deletions(-)
diff --git a/lib/igt_pm.c b/lib/igt_pm.c
index 688bcd7d2..ccb2f17c4 100644
--- a/lib/igt_pm.c
+++ b/lib/igt_pm.c
@@ -1033,15 +1033,26 @@ static void __igt_pm_pci_card_exit_handler(int sig)
igt_pm_restore_pci_card_runtime_pm();
}
-static int igt_pm_get_power_attr_fd(struct pci_device *pci_dev, const char *attr)
+/*
+ * Open a power attribute of @pci_dev with @flags, returning -1 if it does not
+ * exist. Not all attributes are present on all devices, so callers that want to
+ * probe for one need an error rather than an assertion.
+ */
+static int __igt_pm_get_power_attr_fd(struct pci_device *pci_dev,
+ const char *attr, int flags)
{
char name[PATH_MAX];
- int fd;
snprintf(name, PATH_MAX, "/sys/bus/pci/devices/%04x:%02x:%02x.%01x/power/%s",
pci_dev->domain, pci_dev->bus, pci_dev->dev, pci_dev->func, attr);
- fd = open(name, O_RDWR);
+ return open(name, flags);
+}
+
+static int igt_pm_get_power_attr_fd(struct pci_device *pci_dev, const char *attr)
+{
+ int fd = __igt_pm_get_power_attr_fd(pci_dev, attr, O_RDWR);
+
igt_assert_f(fd >= 0, "Can't open %s\n", attr);
return fd;
@@ -1049,13 +1060,8 @@ static int igt_pm_get_power_attr_fd(struct pci_device *pci_dev, const char *attr
static int igt_pm_get_power_attr_fd_rdonly(struct pci_device *pci_dev, const char *attr)
{
- char name[PATH_MAX];
- int fd;
+ int fd = __igt_pm_get_power_attr_fd(pci_dev, attr, O_RDONLY);
- snprintf(name, PATH_MAX, "/sys/bus/pci/devices/%04x:%02x:%02x.%01x/power/%s",
- pci_dev->domain, pci_dev->bus, pci_dev->dev, pci_dev->func, attr);
-
- fd = open(name, O_RDONLY);
igt_assert_f(fd >= 0, "Can't open %s\n", attr);
return fd;
--
2.53.0
^ permalink raw reply related [flat|nested] 10+ messages in thread
* [PATCH i-g-t v3 4/7] lib/igt_pm: Add power/wakeup accessors
2026-10-20 14:11 [PATCH i-g-t v3 0/7] Validate PM_PME signalling on display hotplug Pranay Samala
` (2 preceding siblings ...)
2026-10-20 14:11 ` [PATCH i-g-t v3 3/7] lib/igt_pm: Factor out power attribute path construction Pranay Samala
@ 2026-10-20 14:11 ` Pranay Samala
2026-10-20 14:11 ` [PATCH i-g-t v3 5/7] lib/igt_pm: Add power/wakeup_active_count accessor Pranay Samala
` (4 subsequent siblings)
8 siblings, 0 replies; 10+ messages in thread
From: Pranay Samala @ 2026-10-20 14:11 UTC (permalink / raw)
To: igt-dev; +Cc: kunal1.joshi, sameer.lattannavar, pranay.samala
Add PCI power/wakeup accessors for igt_pm:
igt_pm_has_wakeup_support()
igt_pm_set_wakeup_enabled()
igt_pm_restore_wakeup()
These helpers expose whether the device supports wakeup accounting and allow
tests to enable/restore the power/wakeup sysfs attribute. This is needed for
PME-based HPD tests, where both device wakeup support and the PM wakeup source
must be enabled.
v3:
- Keep the kerneldoc generic, drop the xe specific note (Kunal)
- Document that enabling power/wakeup on a device that had it disabled
creates a new wakeup source, so the power/wakeup_* counters restart
from 0 and callers need a fresh baseline (Kunal)
Signed-off-by: Pranay Samala <pranay.samala@intel.com>
---
lib/igt_pm.c | 140 +++++++++++++++++++++++++++++++++++++++++++++++++++
lib/igt_pm.h | 3 ++
2 files changed, 143 insertions(+)
diff --git a/lib/igt_pm.c b/lib/igt_pm.c
index ccb2f17c4..1173b2b68 100644
--- a/lib/igt_pm.c
+++ b/lib/igt_pm.c
@@ -1498,6 +1498,146 @@ int igt_pm_get_runtime_usage(struct pci_device *pci_dev)
return usage;
}
+#define IGT_PM_WAKEUP_ENABLED_STR "enabled\n"
+#define IGT_PM_WAKEUP_DISABLED_STR "disabled\n"
+
+static struct {
+ struct pci_device *pci_dev;
+ bool orig_enabled;
+} __igt_pm_wakeup;
+
+static void __igt_pm_wakeup_exit_handler(int sig)
+{
+ igt_pm_restore_wakeup();
+}
+
+/**
+ * igt_pm_has_wakeup_support:
+ * @pci_dev: PCI device struct
+ *
+ * The power/wakeup* sysfs attributes only exist for devices the PM core
+ * considers wakeup capable, so their presence is the check for whether wakeup
+ * source accounting is available at all for @pci_dev.
+ *
+ * Returns: true if the device exposes power/wakeup, false otherwise.
+ */
+bool igt_pm_has_wakeup_support(struct pci_device *pci_dev)
+{
+ int fd = __igt_pm_get_power_attr_fd(pci_dev, "wakeup", O_RDONLY);
+
+ if (fd < 0)
+ return false;
+
+ close(fd);
+
+ return true;
+}
+
+/*
+ * Read power/wakeup, i.e. whether the device is permitted to wake the system.
+ * Returns false if wakeup is disabled or the device is not wakeup capable.
+ *
+ * This gates two separate things. It is the device_may_wakeup() policy, which
+ * the system suspend path requires before arming PME, and which a driver may
+ * also consult from its own suspend hooks.
+ *
+ * It also gates the device's wakeup source object: writing "disabled" makes the
+ * PM core drop dev->power.wakeup, after which wakeup events are not accounted
+ * at all and the power/wakeup_* counters read back as a bare newline. Tests
+ * that sample those counters therefore need wakeup enabled, whether or not they
+ * care about system wakeup.
+ */
+static bool igt_pm_get_wakeup_enabled(struct pci_device *pci_dev)
+{
+ char buf[64];
+ int fd;
+ bool enabled;
+
+ fd = __igt_pm_get_power_attr_fd(pci_dev, "wakeup", O_RDONLY);
+ if (fd < 0)
+ return false;
+
+ enabled = igt_pm_read_power_attr(fd, buf, sizeof(buf), false) &&
+ !strcmp(buf, "enabled");
+
+ close(fd);
+
+ return enabled;
+}
+
+/**
+ * igt_pm_set_wakeup_enabled:
+ * @pci_dev: PCI device struct
+ * @enable: whether to allow the device to wake the system
+ *
+ * Writes power/wakeup. The original value is saved on the first call and
+ * restored by an exit handler, or explicitly via igt_pm_restore_wakeup().
+ *
+ * Enabling wakeup on a device that has it disabled makes the PM core create a
+ * new wakeup source, so all power/wakeup_* counters restart from 0. Callers
+ * that sample those counters must take a fresh baseline after this call.
+ * Enabling it when already enabled keeps the existing wakeup source and its
+ * counters.
+ *
+ * Callers must gate on igt_pm_has_wakeup_support() first.
+ */
+void igt_pm_set_wakeup_enabled(struct pci_device *pci_dev, bool enable)
+{
+ const char *val = enable ? IGT_PM_WAKEUP_ENABLED_STR :
+ IGT_PM_WAKEUP_DISABLED_STR;
+ int len = strlen(val);
+ int fd, size;
+
+ fd = __igt_pm_get_power_attr_fd(pci_dev, "wakeup", O_RDWR);
+ igt_assert_f(fd >= 0, "Device has no power/wakeup attribute\n");
+
+ if (!__igt_pm_wakeup.pci_dev) {
+ __igt_pm_wakeup.orig_enabled = igt_pm_get_wakeup_enabled(pci_dev);
+ __igt_pm_wakeup.pci_dev = pci_dev;
+ igt_install_exit_handler(__igt_pm_wakeup_exit_handler);
+ }
+
+ size = write(fd, val, len);
+ close(fd);
+
+ igt_assert_f(size == len, "Failed to %s power/wakeup\n",
+ enable ? "enable" : "disable");
+
+ igt_debug("PCI '%04x:%02x:%02x.%01x' power/wakeup = %s\n",
+ pci_dev->domain, pci_dev->bus, pci_dev->dev, pci_dev->func,
+ enable ? "enabled" : "disabled");
+}
+
+/**
+ * igt_pm_restore_wakeup:
+ *
+ * Restores power/wakeup to the value it had before the first
+ * igt_pm_set_wakeup_enabled() call. Does nothing if it was never changed.
+ *
+ * As with igt_pm_set_wakeup_enabled(), restoring an enabled value onto a
+ * disabled device restarts the power/wakeup_* counters from 0.
+ */
+void igt_pm_restore_wakeup(void)
+{
+ struct pci_device *pci_dev = __igt_pm_wakeup.pci_dev;
+ const char *val;
+ int fd;
+
+ if (!pci_dev)
+ return;
+
+ val = __igt_pm_wakeup.orig_enabled ? IGT_PM_WAKEUP_ENABLED_STR :
+ IGT_PM_WAKEUP_DISABLED_STR;
+
+ fd = __igt_pm_get_power_attr_fd(pci_dev, "wakeup", O_RDWR);
+ if (fd >= 0) {
+ igt_ignore_warn(write(fd, val, strlen(val)));
+ close(fd);
+ }
+
+ __igt_pm_wakeup.pci_dev = NULL;
+}
+
/**
* igt_pm_ignore_slpc_efficient_freq:
* @i915: open i915 drm file descriptor
diff --git a/lib/igt_pm.h b/lib/igt_pm.h
index 861741b89..c3facbd69 100644
--- a/lib/igt_pm.h
+++ b/lib/igt_pm.h
@@ -103,6 +103,9 @@ bool i915_is_slpc_enabled(int drm_fd);
uint64_t igt_pm_get_runtime_suspended_time(struct pci_device *pci_dev);
uint64_t igt_pm_get_runtime_active_time(struct pci_device *pci_dev);
int igt_pm_get_runtime_usage(struct pci_device *pci_dev);
+bool igt_pm_has_wakeup_support(struct pci_device *pci_dev);
+void igt_pm_set_wakeup_enabled(struct pci_device *pci_dev, bool enable);
+void igt_pm_restore_wakeup(void);
void igt_pm_ignore_slpc_efficient_freq(int i915, int gtfd, bool val);
bool igt_has_pci_pm_capability(struct pci_device *pci_dev);
bool igt_pm_pci_pme_supported(struct pci_device *pci_dev,
--
2.53.0
^ permalink raw reply related [flat|nested] 10+ messages in thread
* [PATCH i-g-t v3 5/7] lib/igt_pm: Add power/wakeup_active_count accessor
2026-10-20 14:11 [PATCH i-g-t v3 0/7] Validate PM_PME signalling on display hotplug Pranay Samala
` (3 preceding siblings ...)
2026-10-20 14:11 ` [PATCH i-g-t v3 4/7] lib/igt_pm: Add power/wakeup accessors Pranay Samala
@ 2026-10-20 14:11 ` Pranay Samala
2026-10-20 14:11 ` [PATCH i-g-t v3 6/7] lib/igt_kms: Add drm_kms_helper.poll save/restore helpers Pranay Samala
` (3 subsequent siblings)
8 siblings, 0 replies; 10+ messages in thread
From: Pranay Samala @ 2026-10-20 14:11 UTC (permalink / raw)
To: igt-dev; +Cc: kunal1.joshi, sameer.lattannavar, pranay.samala
Add igt_pm_get_wakeup_active_count(), the number of times the device's
wakeup source was activated.
Every PCI PME delivery route ends in pci_wakeup_event(), which bumps
this count: the native PCIe PME interrupt, an ACPI GPE, and the PCI
core PME poll (pci_pme_list_scan(), every 1s), which is the likely
route for an integrated GPU without a GPE. An increment across a
suspended window therefore means the device woke itself with a PME,
rather than the host waking it.
power/wakeup_count does not work for this, as it only moves during
system suspend, and neither does PME_Status, which the PCI core clears
while handling the wakeup.
The count is unavailable while power/wakeup is disabled. Since 0 is a
valid count, return -1 then, so callers can tell "no wakeup source"
from "no PME".
v3:
- Mention the PCI core PME poll route, and correct the PCIe route, which
is pcie_pme_handle_request() rather than pci_pme_wakeup() (Kunal)
- Return int64_t, -1 when there is no wakeup source, instead of 0 (Kunal)
Signed-off-by: Pranay Samala <pranay.samala@intel.com>
---
lib/igt_pm.c | 53 ++++++++++++++++++++++++++++++++++++++++++++++++++++
lib/igt_pm.h | 1 +
2 files changed, 54 insertions(+)
diff --git a/lib/igt_pm.c b/lib/igt_pm.c
index 1173b2b68..f4d5d5c7b 100644
--- a/lib/igt_pm.c
+++ b/lib/igt_pm.c
@@ -1498,6 +1498,35 @@ int igt_pm_get_runtime_usage(struct pci_device *pci_dev)
return usage;
}
+/*
+ * Read an unsigned power attribute of @pci_dev, returning -1 if the attribute
+ * does not exist or does not hold a number.
+ *
+ * The latter is not hypothetical: the wakeup counters exist for every wakeup
+ * capable device, but the PM core emits a bare newline for them while the
+ * device has no wakeup source, i.e. while power/wakeup is disabled. Since 0 is
+ * a valid count, that case has to be told apart from it.
+ */
+static int64_t igt_pm_read_power_attr_u64(struct pci_device *pci_dev,
+ const char *attr)
+{
+ char buf[64];
+ int64_t val;
+ int fd;
+
+ fd = __igt_pm_get_power_attr_fd(pci_dev, attr, O_RDONLY);
+ if (fd < 0)
+ return -1;
+
+ if (!igt_pm_read_power_attr(fd, buf, sizeof(buf), false) ||
+ sscanf(buf, "%" SCNd64, &val) != 1 || val < 0)
+ val = -1;
+
+ close(fd);
+
+ return val;
+}
+
#define IGT_PM_WAKEUP_ENABLED_STR "enabled\n"
#define IGT_PM_WAKEUP_DISABLED_STR "disabled\n"
@@ -1638,6 +1667,30 @@ void igt_pm_restore_wakeup(void)
__igt_pm_wakeup.pci_dev = NULL;
}
+/**
+ * igt_pm_get_wakeup_active_count:
+ * @pci_dev: PCI device struct
+ *
+ * Reads power/wakeup_active_count, the number of times the device's wakeup
+ * source was activated. An increment across a runtime suspended window means
+ * the resume was signalled by the device, i.e. a PME arrived, rather than
+ * initiated by something host side such as HPD polling.
+ *
+ * Use this and not power/wakeup_count, which is only incremented under
+ * events_check_enabled and so never moves for a runtime resume.
+ *
+ * The count only exists while the device has a wakeup source, i.e. while
+ * power/wakeup is enabled, and restarts from 0 each time one is created. See
+ * igt_pm_set_wakeup_enabled().
+ *
+ * Return: the wakeup active count, or -1 if the device is not wakeup capable,
+ * has power/wakeup disabled, or the count could not be read.
+ */
+int64_t igt_pm_get_wakeup_active_count(struct pci_device *pci_dev)
+{
+ return igt_pm_read_power_attr_u64(pci_dev, "wakeup_active_count");
+}
+
/**
* igt_pm_ignore_slpc_efficient_freq:
* @i915: open i915 drm file descriptor
diff --git a/lib/igt_pm.h b/lib/igt_pm.h
index c3facbd69..3d857080a 100644
--- a/lib/igt_pm.h
+++ b/lib/igt_pm.h
@@ -106,6 +106,7 @@ int igt_pm_get_runtime_usage(struct pci_device *pci_dev);
bool igt_pm_has_wakeup_support(struct pci_device *pci_dev);
void igt_pm_set_wakeup_enabled(struct pci_device *pci_dev, bool enable);
void igt_pm_restore_wakeup(void);
+int64_t igt_pm_get_wakeup_active_count(struct pci_device *pci_dev);
void igt_pm_ignore_slpc_efficient_freq(int i915, int gtfd, bool val);
bool igt_has_pci_pm_capability(struct pci_device *pci_dev);
bool igt_pm_pci_pme_supported(struct pci_device *pci_dev,
--
2.53.0
^ permalink raw reply related [flat|nested] 10+ messages in thread
* [PATCH i-g-t v3 6/7] lib/igt_kms: Add drm_kms_helper.poll save/restore helpers
2026-10-20 14:11 [PATCH i-g-t v3 0/7] Validate PM_PME signalling on display hotplug Pranay Samala
` (4 preceding siblings ...)
2026-10-20 14:11 ` [PATCH i-g-t v3 5/7] lib/igt_pm: Add power/wakeup_active_count accessor Pranay Samala
@ 2026-10-20 14:11 ` Pranay Samala
2026-10-20 14:11 ` [PATCH i-g-t v3 7/7] tests/chamelium/kms_chamelium_hpd: Add HPD from runtime suspended D3hot Pranay Samala
` (2 subsequent siblings)
8 siblings, 0 replies; 10+ messages in thread
From: Pranay Samala @ 2026-10-20 14:11 UTC (permalink / raw)
To: igt-dev; +Cc: kunal1.joshi, sameer.lattannavar, pranay.samala
The new PME hotplug subtest needs to save the drm_kms_helper.poll module
parameter and put it back on exit, so that an earlier test cannot leave
polling disabled and make the measurement pass for the wrong reason.
tests/intel/kms_pm_dc.c and tests/kms_prime.c already open code that,
each with its own defines and saved state. Rather than add a third copy,
add igt_kms_poll_save(), _set() and _restore(). Converting those two
callers is left to a separate patch.
Save and set stay separate, because a caller may want to restore the
original value on exit while leaving polling switched on. Save installs
the exit handler itself, and restore does nothing without a save.
Writing the parameter does not by itself start or stop polling: the poll
worker stops at its next run after it is disabled, and is only restarted
by drm_kms_helper_poll_enable(), on driver load or system resume. The
kerneldoc says so, so callers that need polling running check for it.
v3:
- Move to igt_kms and rename from igt_pm_kms_poll_*() (Kunal)
- Document that setting the parameter does not restart a stopped poll
worker (Kunal)
Signed-off-by: Pranay Samala <pranay.samala@intel.com>
---
lib/igt_kms.c | 101 ++++++++++++++++++++++++++++++++++++++++++++++++++
lib/igt_kms.h | 3 ++
2 files changed, 104 insertions(+)
diff --git a/lib/igt_kms.c b/lib/igt_kms.c
index af0f7c4e5..e1adf1b6f 100644
--- a/lib/igt_kms.c
+++ b/lib/igt_kms.c
@@ -6173,6 +6173,107 @@ void igt_cleanup_uevents(struct udev_monitor *mon)
udev_unref(udev);
}
+#define IGT_KMS_HELPER_PARAMS "/sys/module/drm_kms_helper/parameters"
+
+static struct {
+ bool saved;
+ bool orig_poll;
+} __igt_kms_poll;
+
+static void __igt_kms_poll_exit_handler(int sig)
+{
+ igt_kms_poll_restore();
+}
+
+/**
+ * igt_kms_poll_save:
+ *
+ * Saves the current drm_kms_helper.poll value and installs an exit handler to
+ * restore it. Safe to call more than once, only the first value is kept.
+ *
+ * Returns: false if the module parameter is not available, in which case
+ * nothing was saved.
+ */
+bool igt_kms_poll_save(void)
+{
+ int dir;
+
+ if (__igt_kms_poll.saved)
+ return true;
+
+ dir = open(IGT_KMS_HELPER_PARAMS, O_RDONLY);
+ if (dir < 0)
+ return false;
+
+ if (!__igt_sysfs_get_boolean(dir, "poll", &__igt_kms_poll.orig_poll)) {
+ close(dir);
+ return false;
+ }
+ close(dir);
+
+ __igt_kms_poll.saved = true;
+ igt_install_exit_handler(__igt_kms_poll_exit_handler);
+
+ igt_debug("Saved drm_kms_helper.poll = %s\n",
+ __igt_kms_poll.orig_poll ? "Y" : "N");
+
+ return true;
+}
+
+/**
+ * igt_kms_poll_set:
+ * @enable: whether to allow connector polling
+ *
+ * Writes the drm_kms_helper.poll module parameter. Callers are expected to have
+ * called igt_kms_poll_save() first so the original value is restored on exit.
+ *
+ * This only changes the parameter, it does not itself start or stop the poll
+ * worker. Disabling takes effect the next time the worker runs, at most one
+ * poll period later, and the worker then stops. Enabling does not restart a
+ * worker that has stopped: the kernel only does that from
+ * drm_kms_helper_poll_enable(), i.e. on driver load and, for most drivers,
+ * system resume. Callers that rely on polling actually running after enabling
+ * it must check for it.
+ */
+void igt_kms_poll_set(bool enable)
+{
+ int dir;
+
+ dir = open(IGT_KMS_HELPER_PARAMS, O_RDONLY);
+ igt_assert_f(dir >= 0, "Can't open %s\n", IGT_KMS_HELPER_PARAMS);
+
+ igt_sysfs_set_boolean(dir, "poll", enable);
+ close(dir);
+
+ igt_debug("drm_kms_helper.poll = %s\n", enable ? "Y" : "N");
+}
+
+/**
+ * igt_kms_poll_restore:
+ *
+ * Restores drm_kms_helper.poll to the value saved by igt_kms_poll_save(). Does
+ * nothing if it was never saved, so it is safe to use as an exit handler that
+ * may run before any save happened.
+ *
+ * As with igt_kms_poll_set(), restoring the parameter to enabled does not
+ * restart a poll worker that stopped while it was disabled.
+ */
+void igt_kms_poll_restore(void)
+{
+ int dir;
+
+ if (!__igt_kms_poll.saved)
+ return;
+
+ dir = open(IGT_KMS_HELPER_PARAMS, O_RDONLY);
+ if (dir >= 0) {
+ __igt_sysfs_set_boolean(dir, "poll", __igt_kms_poll.orig_poll);
+ close(dir);
+ }
+
+ __igt_kms_poll.saved = false;
+}
+
/**
* kmstest_get_vbl_flag:
* @crtc_index: CRTC index to convert into DRM_IOCTL_WAIT_VBLANK parameter
diff --git a/lib/igt_kms.h b/lib/igt_kms.h
index 9b8779a62..c6081a16a 100644
--- a/lib/igt_kms.h
+++ b/lib/igt_kms.h
@@ -1239,6 +1239,9 @@ bool igt_connector_event_detected(struct udev_monitor *mon, uint32_t conn_id,
uint32_t prop_id, int timeout_msecs);
void igt_flush_uevents(struct udev_monitor *mon);
void igt_cleanup_uevents(struct udev_monitor *mon);
+bool igt_kms_poll_save(void);
+void igt_kms_poll_set(bool enable);
+void igt_kms_poll_restore(void);
bool igt_format_mods_has_format_and_modifier(const struct igt_format_mods *format_mods,
uint32_t format, uint64_t modifier);
--
2.53.0
^ permalink raw reply related [flat|nested] 10+ messages in thread
* [PATCH i-g-t v3 7/7] tests/chamelium/kms_chamelium_hpd: Add HPD from runtime suspended D3hot
2026-10-20 14:11 [PATCH i-g-t v3 0/7] Validate PM_PME signalling on display hotplug Pranay Samala
` (5 preceding siblings ...)
2026-10-20 14:11 ` [PATCH i-g-t v3 6/7] lib/igt_kms: Add drm_kms_helper.poll save/restore helpers Pranay Samala
@ 2026-10-20 14:11 ` Pranay Samala
2026-10-20 15:40 ` ✓ Xe.CI.BAT: success for Validate PM_PME signalling on display hotplug (rev4) Patchwork
2026-10-20 15:59 ` ✓ i915.CI.BAT: " Patchwork
8 siblings, 0 replies; 10+ messages in thread
From: Pranay Samala @ 2026-10-20 14:11 UTC (permalink / raw)
To: igt-dev; +Cc: kunal1.joshi, sameer.lattannavar, pranay.samala
Add dp-hpd-after-runtime-suspend-d3hot and
hdmi-hpd-after-runtime-suspend-d3hot, covering a hotplug signalled by a
PME while the device is runtime suspended in D3hot.
Checking only that the display comes back would pass even if the driver
fell back to the ~10s connector poll, so the subtest also checks that
polling stays off:
- Control: with power/wakeup disabled, so PME is not armed, the device
must be seen to wake from polling within two poll periods. This
proves polling is live and the wake detection works, and measures
what one poll wake costs. Skip if polling is not running.
- Phase 1: with PME armed, the device must stay in D3hot for 25s.
- Phase 2: an HPD plug and unplug must each give a prompt uevent, an
updated connector status and an increment of
power/wakeup_active_count, showing the device woke itself.
D3hot is decided once up front, skipping if the device suspends into
D3cold. On failure the D state, PME_Status and counter delta are
reported. All state changed by the subtest is restored on success,
failure and skip.
v3:
- Decide D3hot once and assert it per try (Kunal)
- Settle before baselines, watch the idle window once (Kunal)
- Add the control run without PME (Kunal)
- Flush uevents right before the toggle, assert latency >= 0 (Kunal)
- Check requirements before changing state, restore on failure (Kunal)
- igt_fail_on_f(), igt_assert_lte_u64(), intel_graphics_ver()/IP_VER(),
fix the latency, D3cold and IP version comments (Kunal)
- Follow the lib changes: error returns, int64_t wakeup count,
igt_kms_poll_*() names, xe notes moved here
Signed-off-by: Pranay Samala <pranay.samala@intel.com>
---
tests/chamelium/kms_chamelium_hpd.c | 528 ++++++++++++++++++++++++++++
1 file changed, 528 insertions(+)
diff --git a/tests/chamelium/kms_chamelium_hpd.c b/tests/chamelium/kms_chamelium_hpd.c
index bd8731c02..a8592ef01 100644
--- a/tests/chamelium/kms_chamelium_hpd.c
+++ b/tests/chamelium/kms_chamelium_hpd.c
@@ -33,6 +33,7 @@
*/
#include "kms_chamelium_helper.h"
+#include "igt_device.h"
/**
* SUBTEST: dp-hpd-fast
@@ -114,6 +115,18 @@
* Description: Toggle HPD during Suspend, check that uevents are sent and
* connector status is updated
*
+ * SUBTEST: dp-hpd-after-runtime-suspend-d3hot
+ * Description: Toggle HPD while runtime suspended in D3hot with PME signalling
+ * available, check that connector polling stayed disabled, that the
+ * device woke itself up and that uevents are sent and connector
+ * status is updated
+ *
+ * SUBTEST: hdmi-hpd-after-runtime-suspend-d3hot
+ * Description: Toggle HPD while runtime suspended in D3hot with PME signalling
+ * available, check that connector polling stayed disabled, that the
+ * device woke itself up and that uevents are sent and connector
+ * status is updated
+ *
* SUBTEST: common-hpd-after-suspend
* Description: Toggle HPD during suspend on all connectors, check that uevents
* are sent and connector status is updated
@@ -152,6 +165,47 @@
#define HPD_TOGGLE_COUNT_DP_HDMI 15
#define HPD_TOGGLE_COUNT_FAST 3
+/*
+ * The KMS helper polls connectors every 10s, so watch for longer than two poll
+ * periods: a single delayed or coalesced poll then cannot make the test pass.
+ */
+#define PME_POLL_PROOF_WINDOW_MS 25000
+/*
+ * How long the control run, with PME not armed, waits for a poll induced
+ * resume: two poll periods.
+ */
+#define PME_POLL_CONTROL_WINDOW_MS 20000
+/*
+ * How often to sample the D state and runtime_active_time while watching a
+ * suspended device. Config space reads do not resume a device in D3hot.
+ */
+#define PME_D_STATE_SAMPLE_MS 100
+/*
+ * Upper bound on the runtime_active_time the device may accrue while idle, to
+ * allow for one unrelated wakeup rather than requiring exactly zero. The bound
+ * actually applied is the lower of this and the cost of one poll induced
+ * resume, as measured by the control run, so that the two or more polls a
+ * still polling driver makes during the window always exceed it.
+ */
+#define PME_ACTIVE_TIME_SLACK_MS 100
+/* Tolerance for accounting granularity and usleep() overshoot. */
+#define PME_POLL_PROOF_SLACK_MS 500
+/*
+ * How long to leave the device suspended before measuring anything, so that
+ * work left over from the previous step, such as the 1s HPD retry or a resume
+ * of a linked device like HDA, is not blamed on polling.
+ */
+#define PME_SETTLE_MS 3000
+/*
+ * A PME reaches the PCI core either as an interrupt, through native PCIe PME
+ * or an ACPI GPE, or, without either route, through the PCI core's own PME
+ * poll, which runs every 1s. Either way the wake is expected within a couple of
+ * seconds. The whole point of the feature is that this is not the ~10s a
+ * connector poll would take.
+ */
+#define PME_WAKE_LATENCY_MAX_MS 3000
+#define PME_HPD_TOGGLE_DELAY_MS 1000
+
enum test_modeset_mode {
TEST_MODESET_ON,
TEST_MODESET_ON_OFF,
@@ -383,6 +437,466 @@ static void test_suspend_resume_hpd_common(chamelium_data_t *data,
igt_cleanup_uevents(mon);
}
+/*
+ * Name a D state for failure messages. igt_pm_pci_get_d_state() reports D3cold
+ * from the kernel's power_state without touching config space, so an unknown
+ * state here means the config space read failed, i.e. the device has gone away.
+ */
+static const char *pme_d_state_name(enum igt_acpi_d_state state)
+{
+ switch (state) {
+ case IGT_ACPI_D0:
+ return "D0";
+ case IGT_ACPI_D1:
+ return "D1";
+ case IGT_ACPI_D2:
+ return "D2";
+ case IGT_ACPI_D3Hot:
+ return "D3hot";
+ case IGT_ACPI_D3Cold:
+ return "D3cold";
+ default:
+ return "an unreadable state";
+ }
+}
+
+/*
+ * State changed by the PME subtest that has no exit handler of its own.
+ * pme_hpd_cleanup() puts it back, together with power/wakeup and
+ * drm_kms_helper.poll, both at the end of the subtest and from a fixture after
+ * it, so that a failure or skip does not leave the next subtest running with it.
+ */
+static struct {
+ struct udev_monitor *mon;
+ bool storm_set;
+} pme_hpd;
+
+static void pme_hpd_cleanup(int drm_fd)
+{
+ if (pme_hpd.mon) {
+ igt_cleanup_uevents(pme_hpd.mon);
+ pme_hpd.mon = NULL;
+ }
+
+ if (pme_hpd.storm_set) {
+ igt_hpd_storm_reset(drm_fd);
+ pme_hpd.storm_set = false;
+ }
+
+ igt_kms_poll_restore();
+ igt_pm_restore_wakeup();
+}
+
+/*
+ * Read power/wakeup_active_count. It only exists while power/wakeup is enabled,
+ * which the test keeps it from before it takes any baseline, so a missing count
+ * means something else disabled it and no comparison would mean anything.
+ */
+static int64_t pme_wakeup_count(struct pci_device *pci_dev)
+{
+ int64_t count = igt_pm_get_wakeup_active_count(pci_dev);
+
+ igt_assert_f(count >= 0,
+ "power/wakeup_active_count is unavailable, so power/wakeup "
+ "was disabled during the test\n");
+
+ return count;
+}
+
+/*
+ * Wait for the device to runtime suspend, leave it alone for PME_SETTLE_MS so
+ * that work left over from the previous step can finish, and wait again in case
+ * that work resumed it.
+ */
+static void pme_wait_settled(void)
+{
+ igt_assert_f(igt_wait_for_pm_status(IGT_RUNTIME_PM_STATUS_SUSPENDED),
+ "Device did not runtime suspend\n");
+ usleep(PME_SETTLE_MS * 1000);
+ igt_assert_f(igt_wait_for_pm_status(IGT_RUNTIME_PM_STATUS_SUSPENDED),
+ "Device did not runtime suspend\n");
+}
+
+/*
+ * Whether PME is armed, and so whether the driver polls connectors, is decided
+ * on every runtime suspend. After changing power/wakeup, resume the device once
+ * so that the next suspend picks up the new value. Probing the connector takes
+ * a display power reference, which does exactly that.
+ */
+static void pme_cycle_runtime_pm(chamelium_data_t *data,
+ struct chamelium_port *port)
+{
+ chamelium_reprobe_connector(&data->display, data->chamelium, port);
+ pme_wait_settled();
+}
+
+/*
+ * Watch a runtime suspended device for up to @window_ms, sampling every
+ * PME_D_STATE_SAMPLE_MS. The device counts as woken once it is seen outside
+ * D3hot, or once it has accrued more than @active_limit_ms of
+ * runtime_active_time, which also catches resumes too brief to be sampled.
+ *
+ * Returns the time in ms at which the device was seen to wake, or -1 if it did
+ * not. @d_state and @active_ms hold the last sample.
+ */
+static int pme_watch_for_wake(struct pci_device *pci_dev, int window_ms,
+ uint64_t active_limit_ms,
+ enum igt_acpi_d_state *d_state,
+ uint64_t *active_ms)
+{
+ uint64_t active_before = igt_pm_get_runtime_active_time(pci_dev);
+ struct timespec start, now;
+ int elapsed;
+
+ igt_assert_eq(igt_gettime(&start), 0);
+ for (;;) {
+ *d_state = igt_pm_pci_get_d_state(pci_dev);
+ *active_ms = igt_pm_get_runtime_active_time(pci_dev) -
+ active_before;
+
+ igt_assert_eq(igt_gettime(&now), 0);
+ elapsed = igt_time_elapsed(&start, &now) * 1000;
+
+ if (*d_state != IGT_ACPI_D3Hot || *active_ms > active_limit_ms)
+ return elapsed;
+ if (elapsed >= window_ms)
+ return -1;
+
+ usleep(PME_D_STATE_SAMPLE_MS * 1000);
+ }
+}
+
+/*
+ * Control run, with PME not armed. The driver then falls back to polling
+ * connectors while runtime suspended, so the device has to be seen to wake
+ * within two poll periods. That proves polling is live on this setup and that
+ * pme_watch_for_wake() catches a poll induced resume, so that the same check
+ * passing with PME armed means something. The only difference between the two
+ * runs is whether PME is armed.
+ *
+ * Returns the runtime_active_time in ms that one poll induced resume costs.
+ */
+static uint64_t pme_measure_poll_wake(chamelium_data_t *data,
+ struct chamelium_port *port,
+ struct pci_device *pci_dev)
+{
+ enum igt_acpi_d_state d_state;
+ uint64_t active_before, active_ms;
+ int pme_en, woke_ms;
+
+ /*
+ * xe arms PME from its runtime suspend hook with pci_enable_wake(), which
+ * refuses unless device_may_wakeup(), i.e. unless power/wakeup is
+ * enabled. xe also saves the PCI state itself, so the PCI/PM core does
+ * not arm PME on its behalf either. Disabling power/wakeup is therefore
+ * enough to runtime suspend without PME.
+ */
+ igt_pm_set_wakeup_enabled(pci_dev, false);
+ pme_cycle_runtime_pm(data, port);
+
+ pme_en = igt_pm_pci_pme_enabled(pci_dev);
+ igt_assert_f(pme_en == 0, "PME_En is %s with power/wakeup disabled\n",
+ pme_en < 0 ? "unreadable" : "set");
+
+ active_before = igt_pm_get_runtime_active_time(pci_dev);
+ woke_ms = pme_watch_for_wake(pci_dev, PME_POLL_CONTROL_WINDOW_MS, 0,
+ &d_state, &active_ms);
+ igt_skip_on_f(woke_ms < 0,
+ "Device stayed suspended for %dms with PME not armed, so "
+ "connector polling is not running and cannot be told apart "
+ "from PME\n", PME_POLL_CONTROL_WINDOW_MS);
+
+ /* Let the resume complete, so that its whole cost is accounted. */
+ igt_assert_f(igt_wait_for_pm_status(IGT_RUNTIME_PM_STATUS_SUSPENDED),
+ "Device did not runtime suspend again after a poll\n");
+ active_ms = igt_pm_get_runtime_active_time(pci_dev) - active_before;
+
+ igt_info("Without PME the device woke after %dms, %s, and one poll "
+ "costs %" PRIu64 "ms active\n", woke_ms,
+ d_state != IGT_ACPI_D3Hot ? "seen outside D3hot" :
+ "seen as active time",
+ active_ms);
+
+ return active_ms;
+}
+
+static void try_hpd_runtime_suspend_pme(chamelium_data_t *data,
+ struct chamelium_port *port,
+ struct pci_device *pci_dev,
+ struct udev_monitor *mon,
+ bool connected, bool check_idle,
+ uint64_t active_limit_ms)
+{
+ drmModeConnection target_state = connected ? DRM_MODE_DISCONNECTED :
+ DRM_MODE_CONNECTED;
+ int timeout = CHAMELIUM_HOTPLUG_TIMEOUT;
+ enum igt_acpi_d_state d_state;
+ int64_t wakeups_before, wakeups_after;
+ uint64_t susp_before, susp_delta, active_ms;
+ struct timespec start, end;
+ int pme_en, pending, woke_ms, latency;
+ const char *cause = NULL;
+ bool hotplugged;
+
+ pme_wait_settled();
+
+ /*
+ * Whether the device suspends into D3hot at all was decided up front, so
+ * finding it anywhere else now means something woke it, which is a
+ * failure rather than a reason to skip.
+ */
+ d_state = igt_pm_pci_get_d_state(pci_dev);
+ igt_assert_f(d_state == IGT_ACPI_D3Hot,
+ "Device is in %s rather than D3hot after runtime suspending\n",
+ pme_d_state_name(d_state));
+
+ /*
+ * PME_En is the direct sign that the feature engaged; without it, the test
+ * would only be measuring the polling fallback.
+ */
+ pme_en = igt_pm_pci_pme_enabled(pci_dev);
+ igt_assert_f(pme_en == 1, "PME_En is %s after suspending into D3hot\n",
+ pme_en < 0 ? "unreadable" : "not set");
+
+ if (check_idle) {
+ bool self_woke;
+
+ /*
+ * Phase 1: ensure the device stays in D3hot while idle. If polling
+ * remains on, the KMS helper will resume it to probe connectors.
+ */
+ wakeups_before = pme_wakeup_count(pci_dev);
+ susp_before = igt_pm_get_runtime_suspended_time(pci_dev);
+
+ woke_ms = pme_watch_for_wake(pci_dev, PME_POLL_PROOF_WINDOW_MS,
+ active_limit_ms, &d_state,
+ &active_ms);
+
+ /*
+ * Distinguish self-woken resumes from host-driven ones. A
+ * self-wakeup bumps wakeup_active_count; a host-driven resume
+ * does not.
+ */
+ wakeups_after = pme_wakeup_count(pci_dev);
+ self_woke = wakeups_after > wakeups_before;
+
+ igt_fail_on_f(woke_ms >= 0,
+ "Device woke after %dms of a %dms idle window: it "
+ "is in %s and was active for %" PRIu64 "ms, %" PRIu64
+ "ms allowed.\n"
+ "wakeup_active_count %" PRId64 " -> %" PRId64
+ ", so the resume was %s.\n%s\n",
+ woke_ms, PME_POLL_PROOF_WINDOW_MS,
+ pme_d_state_name(d_state), active_ms,
+ active_limit_ms, wakeups_before, wakeups_after,
+ self_woke ? "signalled by the device" :
+ "initiated by the host",
+ self_woke ?
+ "No hotplug was requested yet, so this is a "
+ "spurious or still pending PME rather than the "
+ "polling fallback." :
+ "The polling fallback resumes on a 10s period, or "
+ "1s while a delayed event is pending, so compare "
+ "the elapsed time above: a wake at an unrelated "
+ "delay is some other client or driver taking a "
+ "display power reference rather than connector "
+ "polling.");
+
+ susp_delta = igt_pm_get_runtime_suspended_time(pci_dev) -
+ susp_before;
+
+ igt_info("Idle for %dms in D3hot: suspended +%" PRIu64
+ "ms, active +%" PRIu64 "ms\n",
+ PME_POLL_PROOF_WINDOW_MS, susp_delta, active_ms);
+
+ igt_assert_lte_u64(PME_POLL_PROOF_WINDOW_MS -
+ PME_POLL_PROOF_SLACK_MS, susp_delta);
+ }
+
+ /*
+ * Phase 2: fire the HPD and check the device wakes up promptly. The
+ * toggle is scheduled over XMLRPC, which travels over the network and so
+ * does not disturb the GPU while we wait for the uevent.
+ *
+ * Take a fresh wakeup baseline rather than reusing the one from phase 1,
+ * which is a whole idle window old by now. Phase 1 only requires that
+ * the device stayed in D3hot, which a PME that resumed nothing at all
+ * would not violate, so the counter is not guaranteed to be unchanged.
+ *
+ * Flush uevents only now, right before the toggle, so that one sent
+ * during the steps above cannot satisfy the wait, and start the clock
+ * before the XMLRPC call, so that the latency cannot come out negative.
+ */
+ wakeups_before = pme_wakeup_count(pci_dev);
+ igt_flush_uevents(mon);
+
+ igt_assert_eq(igt_gettime(&start), 0);
+ chamelium_schedule_hpd_toggle(data->chamelium, port,
+ PME_HPD_TOGGLE_DELAY_MS, !connected);
+ hotplugged = chamelium_wait_for_hotplug(mon, &timeout);
+ igt_assert_eq(igt_gettime(&end), 0);
+
+ d_state = IGT_ACPI_UNKNOWN_STATE;
+ pending = -1;
+ wakeups_after = -1;
+ if (!hotplugged) {
+ d_state = igt_pm_pci_get_d_state(pci_dev);
+ pending = igt_pm_pci_pme_status(pci_dev);
+ wakeups_after = igt_pm_get_wakeup_active_count(pci_dev);
+
+ /*
+ * If the device stays in D3hot, the wake is from PME. If it
+ * returns to D0, the HPD was handled by the driver resume path.
+ */
+ if (d_state != IGT_ACPI_D3Hot)
+ cause = "The device resumed but sent no uevent, so the "
+ "HPD was dropped on the resume path.";
+ else if (pending < 0)
+ cause = "PME_Status could not be read, so whether the "
+ "device signalled a PME is unknown.";
+ else if (pending)
+ cause = "The device signalled a PME that was never "
+ "delivered, so the PME is not reaching the OS.";
+ else
+ cause = "The device never signalled a PME, so the HPD "
+ "is not reaching the PME logic.";
+ }
+ igt_fail_on_f(!hotplugged,
+ "No hotplug uevent %ds after an HPD toggle in D3hot.\n"
+ "Device is in %s, PME_Status is %s, "
+ "wakeup_active_count %" PRId64 " -> %" PRId64 ".\n%s\n",
+ CHAMELIUM_HOTPLUG_TIMEOUT, pme_d_state_name(d_state),
+ pending < 0 ? "unreadable" : pending ? "set" : "clear",
+ wakeups_before, wakeups_after, cause);
+
+ latency = igt_time_elapsed(&start, &end) * 1000 -
+ PME_HPD_TOGGLE_DELAY_MS;
+
+ /*
+ * wakeup_active_count incrementing means the resume was signalled by the
+ * device itself, i.e. by a PME. Unlike PME_Status, which the PCI/PM core
+ * clears on the way back to D0, this counter survives the resume and so
+ * is not racy to sample here.
+ */
+ wakeups_after = pme_wakeup_count(pci_dev);
+
+ igt_info("HPD to uevent latency %dms, wakeup_active_count %" PRId64
+ " -> %" PRId64 "\n", latency, wakeups_before, wakeups_after);
+
+ igt_assert_f(wakeups_after > wakeups_before,
+ "power/wakeup_active_count did not increment, so the resume "
+ "was not triggered by a PME from the device\n");
+ igt_assert_lte(0, latency);
+ igt_assert_lt(latency, PME_WAKE_LATENCY_MAX_MS);
+
+ chamelium_assert_reachable(data->chamelium, ONLINE_TIMEOUT);
+ igt_assert_eq(chamelium_reprobe_connector(&data->display,
+ data->chamelium, port),
+ target_state);
+}
+
+/*
+ * Mirror of HAS_PM_PME_SUPPORT() in xe_pm.c: only xe reports PME capability to
+ * the display code, and only from graphics IP 35.00 on.
+ */
+static bool pme_hpd_supported(int fd)
+{
+ return is_xe_device(fd) &&
+ intel_graphics_ver(intel_get_drm_devid(fd)) >= IP_VER(35, 0);
+}
+
+static const char test_hpd_runtime_suspend_pme_desc[] =
+ "Toggle HPD while runtime suspended in D3hot with PME signalling "
+ "available, check that connector polling stayed disabled, that the "
+ "device woke itself up and that uevents are sent and connector status "
+ "is updated";
+static void test_hpd_runtime_suspend_pme(chamelium_data_t *data,
+ struct chamelium_port *port)
+{
+ struct pci_device *pci_dev;
+ enum igt_acpi_d_state d_state;
+ uint64_t active_limit_ms;
+
+ pci_dev = igt_device_get_pci_device(data->drm_fd);
+
+ /*
+ * Check everything that can make the subtest skip before changing any
+ * state. Without driver support the device keeps HPD polling enabled
+ * and phase 1 would fail rather than skip, so gate on the platform
+ * before looking at the hardware capability at all.
+ */
+ igt_require_f(pme_hpd_supported(data->drm_fd),
+ "Driver does not use PME for HPD on this platform\n");
+ igt_require_f(igt_pm_pci_pme_supported(pci_dev, IGT_ACPI_D3Hot),
+ "Device does not advertise PME support in D3hot\n");
+ igt_require_f(igt_pm_has_wakeup_support(pci_dev),
+ "Device does not expose power/wakeup\n");
+ igt_require_hpd_storm_ctl(data->drm_fd);
+ igt_require(igt_kms_poll_save());
+ igt_require(igt_setup_runtime_pm(data->drm_fd));
+
+ igt_modeset_disable_all_outputs(&data->display);
+ chamelium_reset_state(&data->display, data->chamelium, port,
+ data->ports, data->port_count);
+
+ /*
+ * xe only arms PME for D3hot, and only when D3cold is not allowed. An
+ * integrated device is never D3cold capable, since xe requires a PCIe
+ * root port above the device and an RCiEP has none. A discrete device
+ * can drop to D3cold unless its vram_d3cold_threshold is set to 0, which
+ * this test does not do. Decide once, here, whether the device suspends
+ * into D3hot: in D3cold there is no PME to observe, so skip, and from
+ * here on finding it in any other state is a failure.
+ * igt_pm_pci_get_d_state() reports D3cold without resuming the device.
+ */
+ pme_wait_settled();
+ d_state = igt_pm_pci_get_d_state(pci_dev);
+ igt_require_f(d_state == IGT_ACPI_D3Hot,
+ "Device runtime suspends into %s rather than D3hot, so "
+ "there is no PME to observe\n", pme_d_state_name(d_state));
+
+ /*
+ * Polling must be enabled, so that a previous test cannot leave it
+ * disabled and fake a pass: the test checks that the driver does not
+ * use it when PME is available. Setting the parameter does not restart
+ * a poll worker that a previous test already stopped, which is why the
+ * control run below checks that polling actually happens.
+ */
+ igt_kms_poll_set(true);
+
+ /* Don't let our own toggles trip the storm detection into polling. */
+ igt_hpd_storm_set_threshold(data->drm_fd, 0);
+ pme_hpd.storm_set = true;
+
+ pme_hpd.mon = igt_watch_uevents();
+
+ active_limit_ms = min_t(uint64_t, PME_ACTIVE_TIME_SLACK_MS,
+ pme_measure_poll_wake(data, port, pci_dev));
+
+ /*
+ * Now arm PME. Need both: PME is only armed when device_may_wakeup() is
+ * true, and without a wakeup source the PM core never records the PME
+ * wakeup. Enabling power/wakeup creates a new wakeup source, so
+ * wakeup_active_count restarts from 0; every baseline is taken after
+ * this.
+ */
+ igt_pm_set_wakeup_enabled(pci_dev, true);
+ pme_cycle_runtime_pm(data, port);
+
+ /*
+ * Ports are left disconnected by the reset, so plug first. The idle
+ * window only needs to be watched once.
+ */
+ try_hpd_runtime_suspend_pme(data, port, pci_dev, pme_hpd.mon, false,
+ true, active_limit_ms);
+
+ /* Now check we notice a disconnect signalled from D3hot too. */
+ try_hpd_runtime_suspend_pme(data, port, pci_dev, pme_hpd.mon, true,
+ false, active_limit_ms);
+
+ pme_hpd_cleanup(data->drm_fd);
+}
+
static const char test_hpd_without_ddc_desc[] =
"Disable DDC on a VGA connector, check we still get a uevent on hotplug";
static void test_hpd_without_ddc(chamelium_data_t *data,
@@ -500,6 +1014,13 @@ int igt_main()
connector_subtest("dp-hpd-after-hibernate", DisplayPort, &data, test_suspend_resume_hpd,
SUSPEND_STATE_DISK, SUSPEND_TEST_DEVICES);
+ igt_describe(test_hpd_runtime_suspend_pme_desc);
+ connector_subtest("dp-hpd-after-runtime-suspend-d3hot", DisplayPort,
+ &data, test_hpd_runtime_suspend_pme);
+
+ igt_fixture()
+ pme_hpd_cleanup(data.drm_fd);
+
igt_describe(test_hpd_storm_detect_desc);
connector_subtest("dp-hpd-storm", DisplayPort, &data, test_hpd_storm_detect,
HPD_STORM_PULSE_INTERVAL_DP);
@@ -538,6 +1059,13 @@ int igt_main()
connector_subtest("hdmi-hpd-after-hibernate", HDMIA, &data, test_suspend_resume_hpd,
SUSPEND_STATE_DISK, SUSPEND_TEST_DEVICES);
+ igt_describe(test_hpd_runtime_suspend_pme_desc);
+ connector_subtest("hdmi-hpd-after-runtime-suspend-d3hot", HDMIA,
+ &data, test_hpd_runtime_suspend_pme);
+
+ igt_fixture()
+ pme_hpd_cleanup(data.drm_fd);
+
igt_describe(test_hpd_storm_detect_desc);
connector_subtest("hdmi-hpd-storm", HDMIA, &data, test_hpd_storm_detect,
HPD_STORM_PULSE_INTERVAL_HDMI);
--
2.53.0
^ permalink raw reply related [flat|nested] 10+ messages in thread
* ✓ Xe.CI.BAT: success for Validate PM_PME signalling on display hotplug (rev4)
2026-10-20 14:11 [PATCH i-g-t v3 0/7] Validate PM_PME signalling on display hotplug Pranay Samala
` (6 preceding siblings ...)
2026-10-20 14:11 ` [PATCH i-g-t v3 7/7] tests/chamelium/kms_chamelium_hpd: Add HPD from runtime suspended D3hot Pranay Samala
@ 2026-10-20 15:40 ` Patchwork
2026-10-20 15:59 ` ✓ i915.CI.BAT: " Patchwork
8 siblings, 0 replies; 10+ messages in thread
From: Patchwork @ 2026-10-20 15:40 UTC (permalink / raw)
To: Pranay Samala; +Cc: igt-dev
[-- Attachment #1: Type: text/plain, Size: 878 bytes --]
== Series Details ==
Series: Validate PM_PME signalling on display hotplug (rev4)
URL : https://patchwork.freedesktop.org/series/172774/
State : success
== Summary ==
CI Bug Log - changes from XEIGT_9155_BAT -> XEIGTPW_16015_BAT
====================================================
Summary
-------
**SUCCESS**
No regressions found.
Participating hosts (15 -> 13)
------------------------------
Missing (2): bat-bmg-vm bat-ptl-vm
Changes
-------
No changes found
Build changes
-------------
* IGT: IGT_9155 -> IGTPW_16015
IGTPW_16015: 16015
IGT_9155: 2766e99e3ec081309b2652f08a101bfd04299716 @ https://gitlab.freedesktop.org/drm/igt-gpu-tools.git
xe-5980-24d867f5e671c78760ab39087a57c25ad98e1f42: 24d867f5e671c78760ab39087a57c25ad98e1f42
== Logs ==
For more details see: https://intel-gfx-ci.01.org/tree/intel-xe/IGTPW_16015/index.html
[-- Attachment #2: Type: text/html, Size: 1423 bytes --]
^ permalink raw reply [flat|nested] 10+ messages in thread
* ✓ i915.CI.BAT: success for Validate PM_PME signalling on display hotplug (rev4)
2026-10-20 14:11 [PATCH i-g-t v3 0/7] Validate PM_PME signalling on display hotplug Pranay Samala
` (7 preceding siblings ...)
2026-10-20 15:40 ` ✓ Xe.CI.BAT: success for Validate PM_PME signalling on display hotplug (rev4) Patchwork
@ 2026-10-20 15:59 ` Patchwork
8 siblings, 0 replies; 10+ messages in thread
From: Patchwork @ 2026-10-20 15:59 UTC (permalink / raw)
To: Pranay Samala; +Cc: igt-dev
[-- Attachment #1: Type: text/plain, Size: 5879 bytes --]
== Series Details ==
Series: Validate PM_PME signalling on display hotplug (rev4)
URL : https://patchwork.freedesktop.org/series/172774/
State : success
== Summary ==
CI Bug Log - changes from IGT_9155 -> IGTPW_16015
====================================================
Summary
-------
**SUCCESS**
No regressions found.
External URL: https://intel-gfx-ci.01.org/tree/drm-tip/IGTPW_16015/index.html
Participating hosts (39 -> 39)
------------------------------
Additional (1): bat-rplp-1
Missing (1): bat-dg2-13
Known issues
------------
Here are the changes found in IGTPW_16015 that come from known issues:
### IGT changes ###
#### Issues hit ####
* igt@gem_lmem_swapping@parallel-random-engines:
- bat-rplp-1: NOTRUN -> [SKIP][1] ([i915#4613]) +3 other tests skip
[1]: https://intel-gfx-ci.01.org/tree/drm-tip/IGTPW_16015/bat-rplp-1/igt@gem_lmem_swapping@parallel-random-engines.html
* igt@gem_tiled_pread_basic@basic:
- bat-rplp-1: NOTRUN -> [SKIP][2] ([i915#15656])
[2]: https://intel-gfx-ci.01.org/tree/drm-tip/IGTPW_16015/bat-rplp-1/igt@gem_tiled_pread_basic@basic.html
* igt@i915_pm_rps@basic-api:
- bat-rplp-1: NOTRUN -> [SKIP][3] ([i915#6621])
[3]: https://intel-gfx-ci.01.org/tree/drm-tip/IGTPW_16015/bat-rplp-1/igt@i915_pm_rps@basic-api.html
* igt@intel_hwmon@hwmon-read:
- bat-rplp-1: NOTRUN -> [SKIP][4] ([i915#7707]) +1 other test skip
[4]: https://intel-gfx-ci.01.org/tree/drm-tip/IGTPW_16015/bat-rplp-1/igt@intel_hwmon@hwmon-read.html
* igt@kms_cursor_legacy@basic-busy-flip-before-cursor-legacy:
- bat-rplp-1: NOTRUN -> [SKIP][5] ([i915#4103]) +1 other test skip
[5]: https://intel-gfx-ci.01.org/tree/drm-tip/IGTPW_16015/bat-rplp-1/igt@kms_cursor_legacy@basic-busy-flip-before-cursor-legacy.html
* igt@kms_dsc@dsc-basic:
- bat-rplp-1: NOTRUN -> [SKIP][6] ([i915#16361])
[6]: https://intel-gfx-ci.01.org/tree/drm-tip/IGTPW_16015/bat-rplp-1/igt@kms_dsc@dsc-basic.html
* igt@kms_force_connector_basic@force-connector-state:
- bat-rplp-1: NOTRUN -> [SKIP][7] ([i915#4093]) +3 other tests skip
[7]: https://intel-gfx-ci.01.org/tree/drm-tip/IGTPW_16015/bat-rplp-1/igt@kms_force_connector_basic@force-connector-state.html
* igt@kms_hdmi_inject@inject-audio:
- bat-rplp-1: NOTRUN -> [SKIP][8] ([i915#4369])
[8]: https://intel-gfx-ci.01.org/tree/drm-tip/IGTPW_16015/bat-rplp-1/igt@kms_hdmi_inject@inject-audio.html
* igt@kms_psr@psr-primary-page-flip:
- bat-rplp-1: NOTRUN -> [SKIP][9] ([i915#1072] / [i915#9732]) +3 other tests skip
[9]: https://intel-gfx-ci.01.org/tree/drm-tip/IGTPW_16015/bat-rplp-1/igt@kms_psr@psr-primary-page-flip.html
* igt@kms_setmode@basic-clone-single-crtc:
- bat-rplp-1: NOTRUN -> [SKIP][10] ([i915#3555])
[10]: https://intel-gfx-ci.01.org/tree/drm-tip/IGTPW_16015/bat-rplp-1/igt@kms_setmode@basic-clone-single-crtc.html
* igt@prime_vgem@basic-read:
- bat-rplp-1: NOTRUN -> [SKIP][11] ([i915#3708]) +2 other tests skip
[11]: https://intel-gfx-ci.01.org/tree/drm-tip/IGTPW_16015/bat-rplp-1/igt@prime_vgem@basic-read.html
#### Possible fixes ####
* igt@i915_selftest@live@sanitycheck:
- fi-kbl-7567u: [DMESG-WARN][12] ([i915#13735]) -> [PASS][13] +79 other tests pass
[12]: https://intel-gfx-ci.01.org/tree/drm-tip/IGT_9155/fi-kbl-7567u/igt@i915_selftest@live@sanitycheck.html
[13]: https://intel-gfx-ci.01.org/tree/drm-tip/IGTPW_16015/fi-kbl-7567u/igt@i915_selftest@live@sanitycheck.html
* igt@kms_busy@basic@flip:
- fi-kbl-7567u: [DMESG-WARN][14] ([i915#13735] / [i915#180]) -> [PASS][15]
[14]: https://intel-gfx-ci.01.org/tree/drm-tip/IGT_9155/fi-kbl-7567u/igt@kms_busy@basic@flip.html
[15]: https://intel-gfx-ci.01.org/tree/drm-tip/IGTPW_16015/fi-kbl-7567u/igt@kms_busy@basic@flip.html
* igt@kms_pm_rpm@basic-pci-d3-state:
- fi-kbl-7567u: [DMESG-WARN][16] ([i915#13735] / [i915#15673] / [i915#180]) -> [PASS][17] +52 other tests pass
[16]: https://intel-gfx-ci.01.org/tree/drm-tip/IGT_9155/fi-kbl-7567u/igt@kms_pm_rpm@basic-pci-d3-state.html
[17]: https://intel-gfx-ci.01.org/tree/drm-tip/IGTPW_16015/fi-kbl-7567u/igt@kms_pm_rpm@basic-pci-d3-state.html
[i915#1072]: https://gitlab.freedesktop.org/drm/i915/kernel/-/issues/1072
[i915#13735]: https://gitlab.freedesktop.org/drm/i915/kernel/-/issues/13735
[i915#15656]: https://gitlab.freedesktop.org/drm/i915/kernel/-/issues/15656
[i915#15673]: https://gitlab.freedesktop.org/drm/i915/kernel/-/issues/15673
[i915#16361]: https://gitlab.freedesktop.org/drm/i915/kernel/-/issues/16361
[i915#180]: https://gitlab.freedesktop.org/drm/i915/kernel/-/issues/180
[i915#3555]: https://gitlab.freedesktop.org/drm/i915/kernel/-/issues/3555
[i915#3708]: https://gitlab.freedesktop.org/drm/i915/kernel/-/issues/3708
[i915#4093]: https://gitlab.freedesktop.org/drm/i915/kernel/-/issues/4093
[i915#4103]: https://gitlab.freedesktop.org/drm/i915/kernel/-/issues/4103
[i915#4369]: https://gitlab.freedesktop.org/drm/i915/kernel/-/issues/4369
[i915#4613]: https://gitlab.freedesktop.org/drm/i915/kernel/-/issues/4613
[i915#6621]: https://gitlab.freedesktop.org/drm/i915/kernel/-/issues/6621
[i915#7707]: https://gitlab.freedesktop.org/drm/i915/kernel/-/issues/7707
[i915#9732]: https://gitlab.freedesktop.org/drm/i915/kernel/-/issues/9732
Build changes
-------------
* CI: CI-20190529 -> None
* IGT: IGT_9155 -> IGTPW_16015
CI-20190529: 20190529
CI_DRM_19366: 24d867f5e671c78760ab39087a57c25ad98e1f42 @ git://anongit.freedesktop.org/gfx-ci/linux
IGTPW_16015: 16015
IGT_9155: 2766e99e3ec081309b2652f08a101bfd04299716 @ https://gitlab.freedesktop.org/drm/igt-gpu-tools.git
== Logs ==
For more details see: https://intel-gfx-ci.01.org/tree/drm-tip/IGTPW_16015/index.html
[-- Attachment #2: Type: text/html, Size: 7051 bytes --]
^ permalink raw reply [flat|nested] 10+ messages in thread
end of thread, other threads:[~2026-10-20 16:00 UTC | newest]
Thread overview: 10+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2026-10-20 14:11 [PATCH i-g-t v3 0/7] Validate PM_PME signalling on display hotplug Pranay Samala
2026-10-20 14:11 ` [PATCH i-g-t v3 1/7] lib/igt_pci: Add PCI Power Management capability register layout Pranay Samala
2026-10-20 14:11 ` [PATCH i-g-t v3 2/7] lib/igt_pm: Add PCI PME capability and D state accessors Pranay Samala
2026-10-20 14:11 ` [PATCH i-g-t v3 3/7] lib/igt_pm: Factor out power attribute path construction Pranay Samala
2026-10-20 14:11 ` [PATCH i-g-t v3 4/7] lib/igt_pm: Add power/wakeup accessors Pranay Samala
2026-10-20 14:11 ` [PATCH i-g-t v3 5/7] lib/igt_pm: Add power/wakeup_active_count accessor Pranay Samala
2026-10-20 14:11 ` [PATCH i-g-t v3 6/7] lib/igt_kms: Add drm_kms_helper.poll save/restore helpers Pranay Samala
2026-10-20 14:11 ` [PATCH i-g-t v3 7/7] tests/chamelium/kms_chamelium_hpd: Add HPD from runtime suspended D3hot Pranay Samala
2026-10-20 15:40 ` ✓ Xe.CI.BAT: success for Validate PM_PME signalling on display hotplug (rev4) Patchwork
2026-10-20 15:59 ` ✓ i915.CI.BAT: " Patchwork
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox