diff --git a/Documentation/ABI/testing/sysfs-driver-hid-valve-index b/Documentation/ABI/testing/sysfs-driver-hid-valve-index
new file mode 100644
index 0000000000000..47d8c26b1eace
--- /dev/null
+++ b/Documentation/ABI/testing/sysfs-driver-hid-valve-index
@@ -0,0 +1,12 @@
+What: /sys/bus/hid/devices/<bus>:<vid>:<pid>.<n>/reboot
+Date: October 2026
+Contact: linux-input@vger.kernel.org
+Description:
+ Writing a boolean true value reboots the Valve Index headset to
+ recover its EDID service. Writing a boolean false value has no
+ effect. This file is write-only.
+
+ The Valve Index is a composite HID device. The reboot command is
+ only supported by the interface that provides the headset's 64-byte
+ output report. Writing true to this file on another interface fails
+ with -ENODEV.
diff --git a/drivers/hid/Kconfig b/drivers/hid/Kconfig
index a81bf51cbcf10..8ea2dd570058f 100644
--- a/drivers/hid/Kconfig
+++ b/drivers/hid/Kconfig
@@ -547,6 +547,17 @@ config HID_WALTOP
help
Support for Waltop tablets.
+config HID_VALVE_INDEX
+ tristate "Valve Index headset"
+ depends on USB_HID
+ help
+ Support for the Valve Index headset. This driver works around the
+ headset failing to provide its EDID after a DisplayPort link shutdown
+ by rebooting the headset on resume from system suspend and at shutdown.
+
+ To compile this driver as a module, choose M here: the module will be
+ called hid-valve-index.
+
config HID_VIEWSONIC
tristate "ViewSonic/Signotec"
help
diff --git a/drivers/hid/Makefile b/drivers/hid/Makefile
index 48a863b245eed..21f512cab6250 100644
--- a/drivers/hid/Makefile
+++ b/drivers/hid/Makefile
@@ -156,6 +156,7 @@ obj-$(CONFIG_HID_XIAOMI) += hid-xiaomi.o
obj-$(CONFIG_HID_XINMO) += hid-xinmo.o
obj-$(CONFIG_HID_ZEROPLUS) += hid-zpff.o
obj-$(CONFIG_HID_ZYDACRON) += hid-zydacron.o
+obj-$(CONFIG_HID_VALVE_INDEX) += hid-valve-index.o
obj-$(CONFIG_HID_VIEWSONIC) += hid-viewsonic.o
obj-$(CONFIG_HID_VRC2) += hid-vrc2.o
obj-$(CONFIG_HID_HUAWEI) += hid-huawei.o
diff --git a/drivers/hid/hid-ids.h b/drivers/hid/hid-ids.h
index b3aca5aa91767..15cd29a338a8d 100644
--- a/drivers/hid/hid-ids.h
+++ b/drivers/hid/hid-ids.h
@@ -1391,6 +1391,7 @@
#define USB_DEVICE_ID_STEAM_CONTROLLER_IBEX_BLE 0x1303
#define USB_DEVICE_ID_STEAM_CONTROLLER_PROTEUS 0x1304
#define USB_DEVICE_ID_STEAM_CONTROLLER_NEREID 0x1305
+#define USB_DEVICE_ID_VALVE_INDEX_HEADSET 0x2300
#define USB_VENDOR_ID_STEELSERIES 0x1038
#define USB_DEVICE_ID_STEELSERIES_SRWS1 0x1410
diff --git a/drivers/hid/hid-valve-index.c b/drivers/hid/hid-valve-index.c
new file mode 100644
index 0000000000000..43c1142b7215b
--- /dev/null
+++ b/drivers/hid/hid-valve-index.c
@@ -0,0 +1,142 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*
+ * HID driver for the Valve Index headset
+ */
+
+#include <linux/hid.h>
+#include <linux/module.h>
+
+#include "hid-ids.h"
+
+#define VALVE_INDEX_REBOOT_REPORT_ID 0x16
+#define VALVE_INDEX_REBOOT_CMD 0x01
+#define VALVE_INDEX_REPORT_SIZE 64
+
+static bool valve_index_has_reboot_report(struct hid_device *hdev)
+{
+ struct hid_report *report;
+
+ /*
+ * The reboot command is a vendor protocol carried in the unnumbered
+ * 64-byte output report of the headset's third interface; the first
+ * data byte is the command id. Report 0x16 is only declared as a
+ * feature report and is not what the command is sent as.
+ */
+ report = hdev->report_enum[HID_OUTPUT_REPORT].report_id_hash[0];
+
+ return report && hid_report_len(report) == VALVE_INDEX_REPORT_SIZE;
+}
+
+static void valve_index_reboot(struct hid_device *hdev, bool wake)
+{
+ u8 *report;
+ int ret;
+
+ if (!valve_index_has_reboot_report(hdev))
+ return;
+
+ /* USB transfer buffers must be DMA-able, so not on the stack. */
+ report = kzalloc(VALVE_INDEX_REPORT_SIZE, GFP_KERNEL);
+ if (!report)
+ return;
+ report[0] = VALVE_INDEX_REBOOT_REPORT_ID;
+ report[1] = VALVE_INDEX_REBOOT_CMD;
+
+ if (wake) {
+ ret = hid_hw_power(hdev, PM_HINT_FULLON);
+ if (ret < 0) {
+ hid_warn(hdev, "failed to resume headset for reboot: %d\n",
+ ret);
+ goto out;
+ }
+ }
+
+ /* Use the same interrupt-out then SET_REPORT fallback as hidraw. */
+ ret = hid_hw_output_report(hdev, report, VALVE_INDEX_REPORT_SIZE);
+ if (ret == -ENOSYS)
+ ret = hid_hw_raw_request(hdev, report[0], report,
+ VALVE_INDEX_REPORT_SIZE,
+ HID_OUTPUT_REPORT, HID_REQ_SET_REPORT);
+ if (ret < 0)
+ hid_warn(hdev, "failed to reboot headset: %d\n", ret);
+ else if (ret != VALVE_INDEX_REPORT_SIZE)
+ hid_warn(hdev, "short headset reboot report: %d\n", ret);
+
+ if (wake)
+ hid_hw_power(hdev, PM_HINT_NORMAL);
+out:
+ kfree(report);
+}
+
+/*
+ * The suspend and shutdown hooks only cover orderly power transitions. After
+ * a crash, a hard reset or a power cut the headset is left in the state where
+ * its EDID no longer reads, and nothing recovers it until the next orderly
+ * transition. Expose the reboot command as a write-only "reboot" attribute
+ * on the HID device so userspace can recover it, for instance from a udev
+ * rule that fires only when the connector reports no EDID. Writing to an
+ * interface that does not carry the reboot report returns -ENODEV.
+ */
+static ssize_t reboot_store(struct device *dev, struct device_attribute *attr,
+ const char *buf, size_t count)
+{
+ struct hid_device *hdev = to_hid_device(dev);
+ bool val;
+
+ if (kstrtobool(buf, &val))
+ return -EINVAL;
+ if (!val)
+ return count;
+ if (!valve_index_has_reboot_report(hdev))
+ return -ENODEV;
+
+ valve_index_reboot(hdev, true);
+
+ return count;
+}
+static DEVICE_ATTR_WO(reboot);
+
+static struct attribute *valve_index_attrs[] = {
+ &dev_attr_reboot.attr,
+ NULL
+};
+ATTRIBUTE_GROUPS(valve_index);
+
+/*
+ * The headset's EDID service is lost when the host disables the DisplayPort
+ * PHY during system suspend, so it needs the reboot on the way out of
+ * suspend. Doing it on the way in does not work: the headset dropping off
+ * USB is a remote-wakeup event from its hub and aborts the suspend.
+ */