From 2dc4e4317684b541f89e77da06de59bdf508f362 Mon Sep 17 00:00:00 2001 From: Andre Eikmeyer Date: Mon, 10 Aug 2026 16:03:52 +0200 Subject: [PATCH 1/3] HID: apple-touchbar: avoid nested runtime PM in PM callbacks Runtime suspend of the Touch Bar display stalls because its HID suspend callback requests a runtime PM resume while already running as part of the same USB PM transition. The nested request waits for the transition that invoked the callback to finish. The matching resume callback has the same problem. It can stall after the virtual host controller has successfully resumed the port. The mode update is therefore split into an active-device helper and a runtime-PM-managed wrapper. Both PM callbacks call the active-device helper and no longer acquire a nested runtime PM reference. We maintain the apple-bce/t2bce virtual host controller used on these systems. Although that module is not yet upstream, its runtime PM path has already been adjusted so the complete suspend and resume cycle works with this change. Signed-off-by: Andre Eikmeyer Signed-off-by: Jiri Kosina --- drivers/hid/hid-appletb-kbd.c | 41 +++++++++++++++++++++-------------- 1 file changed, 25 insertions(+), 16 deletions(-) diff --git a/drivers/hid/hid-appletb-kbd.c b/drivers/hid/hid-appletb-kbd.c index 5cc27066f602..a8d6cf3983f9 100644 --- a/drivers/hid/hid-appletb-kbd.c +++ b/drivers/hid/hid-appletb-kbd.c @@ -87,29 +87,37 @@ static const struct key_entry appletb_kbd_keymap[] = { { KE_END, 0 } }; -static int appletb_kbd_set_mode(struct appletb_kbd *kbd, u8 mode) +static int appletb_kbd_set_mode_active(struct appletb_kbd *kbd, u8 mode) { struct hid_report *report = kbd->mode_field->report; struct hid_device *hdev = report->device; int ret; + ret = hid_set_field(kbd->mode_field, 0, mode); + if (ret) { + hid_err(hdev, "Failed to set mode field to %u (%pe)\n", mode, ERR_PTR(ret)); + return ret; + } + + hid_hw_request(hdev, report, HID_REQ_SET_REPORT); + + kbd->current_mode = mode; + return 0; +} + +static int appletb_kbd_set_mode(struct appletb_kbd *kbd, u8 mode) +{ + struct hid_device *hdev = kbd->mode_field->report->device; + int ret; + ret = hid_hw_power(hdev, PM_HINT_FULLON); if (ret) { hid_err(hdev, "Device didn't resume (%pe)\n", ERR_PTR(ret)); return ret; } - ret = hid_set_field(kbd->mode_field, 0, mode); - if (ret) { - hid_err(hdev, "Failed to set mode field to %u (%pe)\n", mode, ERR_PTR(ret)); - goto power_normal; - } + ret = appletb_kbd_set_mode_active(kbd, mode); - hid_hw_request(hdev, report, HID_REQ_SET_REPORT); - - kbd->current_mode = mode; - -power_normal: hid_hw_power(hdev, PM_HINT_NORMAL); return ret; @@ -487,18 +495,19 @@ static int appletb_kbd_suspend(struct hid_device *hdev, pm_message_t msg) struct appletb_kbd *kbd = hid_get_drvdata(hdev); kbd->saved_mode = kbd->current_mode; - appletb_kbd_set_mode(kbd, APPLETB_KBD_MODE_OFF); - return 0; + /* This callback already runs as part of a USB PM transition. Taking a + * nested runtime-PM reference would wait for that transition to complete + * and deadlock the USB PM state machine. + */ + return appletb_kbd_set_mode_active(kbd, APPLETB_KBD_MODE_OFF); } static int appletb_kbd_resume(struct hid_device *hdev) { struct appletb_kbd *kbd = hid_get_drvdata(hdev); - appletb_kbd_set_mode(kbd, kbd->saved_mode); - - return 0; + return appletb_kbd_set_mode_active(kbd, kbd->saved_mode); } static const struct hid_device_id appletb_kbd_hid_ids[] = { From efd9e785c8e20d2408c5e21b6689d7b146709088 Mon Sep 17 00:00:00 2001 From: Aditya Garg Date: Fri, 11 Sep 2026 20:00:19 +0000 Subject: [PATCH 2/3] HID: appletb-kbd: support layer switching on Fn double press Holding Fn temporarily switches the Touch Bar between media controls and function keys. Users who want to keep the alternate layer currently need to change the mode through sysfs. An optional double_press_switch_time module parameter makes a double press persistently switch the default layer. Its value specifies the double-press interval in milliseconds, while zero leaves the behavior disabled. Signed-off-by: Aditya Garg Signed-off-by: Andre Eikmeyer Signed-off-by: Jiri Kosina --- drivers/hid/hid-appletb-kbd.c | 54 ++++++++++++++++++++++++++++++----- 1 file changed, 47 insertions(+), 7 deletions(-) diff --git a/drivers/hid/hid-appletb-kbd.c b/drivers/hid/hid-appletb-kbd.c index a8d6cf3983f9..3cc3a13b71a6 100644 --- a/drivers/hid/hid-appletb-kbd.c +++ b/drivers/hid/hid-appletb-kbd.c @@ -56,6 +56,12 @@ static int appletb_tb_idle_timeout = 15; module_param_named(idle_timeout, appletb_tb_idle_timeout, int, 0644); MODULE_PARM_DESC(idle_timeout, "Idle timeout in sec"); +static int appletb_tb_double_press_switch_time; +module_param_named(double_press_switch_time, + appletb_tb_double_press_switch_time, int, 0644); +MODULE_PARM_DESC(double_press_switch_time, + "Fn double-press interval in ms (0 disables layer switching)"); + struct appletb_kbd { struct hid_field *mode_field; struct input_handler inp_handler; @@ -68,6 +74,7 @@ struct appletb_kbd { bool has_turned_off; u8 saved_mode; u8 current_mode; + unsigned long last_fn_press; }; static const struct key_entry appletb_kbd_keymap[] = { @@ -251,6 +258,18 @@ static int appletb_kbd_hid_event(struct hid_device *hdev, struct hid_field *fiel return kbd->current_mode == APPLETB_KBD_MODE_OFF; } +static u8 appletb_switch_mode(u8 mode) +{ + switch (mode) { + case APPLETB_KBD_MODE_SPCL: + return APPLETB_KBD_MODE_FN; + case APPLETB_KBD_MODE_FN: + return APPLETB_KBD_MODE_SPCL; + default: + return mode; + } +} + static void appletb_kbd_inp_event(struct input_handle *handle, unsigned int type, unsigned int code, int value) { @@ -258,15 +277,36 @@ static void appletb_kbd_inp_event(struct input_handle *handle, unsigned int type reset_inactivity_timer(kbd); - if (type == EV_KEY && code == KEY_FN && appletb_tb_fn_toggle && - (kbd->current_mode == APPLETB_KBD_MODE_SPCL || - kbd->current_mode == APPLETB_KBD_MODE_FN)) { + if (type == EV_KEY && code == KEY_FN && + (kbd->current_mode == APPLETB_KBD_MODE_SPCL || + kbd->current_mode == APPLETB_KBD_MODE_FN)) { if (value == 1) { - kbd->saved_mode = kbd->current_mode; - appletb_kbd_set_mode(kbd, kbd->current_mode == APPLETB_KBD_MODE_SPCL - ? APPLETB_KBD_MODE_FN : APPLETB_KBD_MODE_SPCL); + if (appletb_tb_double_press_switch_time > 0) { + unsigned long now = jiffies; + + if (time_before(now, kbd->last_fn_press + + msecs_to_jiffies(appletb_tb_double_press_switch_time))) { + appletb_tb_def_mode = + appletb_switch_mode(appletb_tb_def_mode); + + appletb_kbd_set_mode(kbd, appletb_tb_def_mode); + + kbd->saved_mode = appletb_tb_def_mode; + kbd->last_fn_press = 0; + + return; + } + + kbd->last_fn_press = now; + } + if (appletb_tb_fn_toggle) { + kbd->saved_mode = kbd->current_mode; + + appletb_kbd_set_mode(kbd, appletb_switch_mode(kbd->current_mode)); + } } else if (value == 0) { - if (kbd->saved_mode != kbd->current_mode) + if (appletb_tb_fn_toggle && + kbd->saved_mode != kbd->current_mode) appletb_kbd_set_mode(kbd, kbd->saved_mode); } } From 87d9c42eccfe21a4391e48732c1fdb8a1a2565cb Mon Sep 17 00:00:00 2001 From: Aditya Garg Date: Thu, 3 Sep 2026 12:14:07 +0530 Subject: [PATCH 3/3] HID: apple: ignore the trackpad on T2 Macs The trackpad on T2 Macs is incorrectly bound by the hid-apple driver. This is due to both trackpad and keyboard sharing the same VID/PID. Use the existing APPLE_IGNORE_MOUSE quirk for the same. Signed-off-by: Aditya Garg Signed-off-by: Jiri Kosina --- drivers/hid/hid-apple.c | 27 +++++++++++++++++---------- 1 file changed, 17 insertions(+), 10 deletions(-) diff --git a/drivers/hid/hid-apple.c b/drivers/hid/hid-apple.c index f6134c5820ff..69d2eaa12a1d 100644 --- a/drivers/hid/hid-apple.c +++ b/drivers/hid/hid-apple.c @@ -30,7 +30,7 @@ #include "hid-ids.h" #define APPLE_RDESC_JIS BIT(0) -/* BIT(1) reserved, was: APPLE_IGNORE_MOUSE */ +#define APPLE_IGNORE_MOUSE BIT(1) #define APPLE_HAS_FN BIT(2) /* BIT(3) reserved, was: APPLE_HIDDEV */ #define APPLE_ISO_TILDE_QUIRK BIT(4) @@ -957,6 +957,9 @@ static int apple_probe(struct hid_device *hdev, struct apple_sc *asc; int ret; + if (quirks & APPLE_IGNORE_MOUSE && hdev->type == HID_TYPE_USBMOUSE) + return -ENODEV; + asc = devm_kzalloc(&hdev->dev, sizeof(*asc), GFP_KERNEL); if (asc == NULL) { hid_err(hdev, "can't alloc apple descriptor\n"); @@ -1182,27 +1185,31 @@ static const struct hid_device_id apple_devices[] = { { HID_USB_DEVICE(USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_WELLSPRING9_JIS), .driver_data = APPLE_HAS_FN | APPLE_RDESC_JIS }, { HID_USB_DEVICE(USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_WELLSPRINGT2_J140K), - .driver_data = APPLE_HAS_FN | APPLE_BACKLIGHT_CTL | APPLE_ISO_TILDE_QUIRK }, + .driver_data = APPLE_HAS_FN | APPLE_BACKLIGHT_CTL | APPLE_ISO_TILDE_QUIRK | + APPLE_IGNORE_MOUSE }, { HID_USB_DEVICE(USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_WELLSPRINGT2_J132), .driver_data = APPLE_HAS_FN | APPLE_BACKLIGHT_CTL | APPLE_ISO_TILDE_QUIRK | - APPLE_DISABLE_FKEYS }, + APPLE_DISABLE_FKEYS | APPLE_IGNORE_MOUSE }, { HID_USB_DEVICE(USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_WELLSPRINGT2_J680), .driver_data = APPLE_HAS_FN | APPLE_BACKLIGHT_CTL | APPLE_ISO_TILDE_QUIRK | - APPLE_DISABLE_FKEYS }, + APPLE_DISABLE_FKEYS | APPLE_IGNORE_MOUSE }, { HID_USB_DEVICE(USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_WELLSPRINGT2_J680_ALT), .driver_data = APPLE_HAS_FN | APPLE_BACKLIGHT_CTL | APPLE_ISO_TILDE_QUIRK | - APPLE_DISABLE_FKEYS }, + APPLE_DISABLE_FKEYS | APPLE_IGNORE_MOUSE }, { HID_USB_DEVICE(USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_WELLSPRINGT2_J213), .driver_data = APPLE_HAS_FN | APPLE_BACKLIGHT_CTL | APPLE_ISO_TILDE_QUIRK | - APPLE_DISABLE_FKEYS }, + APPLE_DISABLE_FKEYS | APPLE_IGNORE_MOUSE }, { HID_USB_DEVICE(USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_WELLSPRINGT2_J214K), - .driver_data = APPLE_HAS_FN | APPLE_ISO_TILDE_QUIRK | APPLE_DISABLE_FKEYS }, + .driver_data = APPLE_HAS_FN | APPLE_ISO_TILDE_QUIRK | APPLE_DISABLE_FKEYS | + APPLE_IGNORE_MOUSE }, { HID_USB_DEVICE(USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_WELLSPRINGT2_J223), - .driver_data = APPLE_HAS_FN | APPLE_ISO_TILDE_QUIRK | APPLE_DISABLE_FKEYS }, + .driver_data = APPLE_HAS_FN | APPLE_ISO_TILDE_QUIRK | APPLE_DISABLE_FKEYS | + APPLE_IGNORE_MOUSE }, { HID_USB_DEVICE(USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_WELLSPRINGT2_J230K), - .driver_data = APPLE_HAS_FN | APPLE_ISO_TILDE_QUIRK }, + .driver_data = APPLE_HAS_FN | APPLE_ISO_TILDE_QUIRK | APPLE_IGNORE_MOUSE }, { HID_USB_DEVICE(USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_WELLSPRINGT2_J152F), - .driver_data = APPLE_HAS_FN | APPLE_ISO_TILDE_QUIRK | APPLE_DISABLE_FKEYS }, + .driver_data = APPLE_HAS_FN | APPLE_ISO_TILDE_QUIRK | APPLE_DISABLE_FKEYS | + APPLE_IGNORE_MOUSE }, { HID_BLUETOOTH_DEVICE(USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_ALU_WIRELESS_2009_ANSI), .driver_data = APPLE_NUMLOCK_EMULATION | APPLE_HAS_FN }, { HID_BLUETOOTH_DEVICE(USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_ALU_WIRELESS_2009_ISO),