[PATCH 3/3] Add Touch Bar and backlight reprobe support

deqrocks
2026-04-03T13:14:47Z

Drop stale device state across suspend and let the Touch Bar\nrelated drivers tear down and reinitialize through reprobe after\nresume.

Signed-off-by: deqrocks <andre@negmaster.com>
---
hid-apple.c | 50 +++++++++++++++++++--
hid-appletb-bl.c | 44 ++++++++++++++++++-
hid-appletb-kbd.c | 109 ++++++++++++++++++++++++++++++++++++----------
3 files changed, 174 insertions(+), 29 deletions(-)

diff --git a/hid-apple.c b/hid-apple.c
index c620890..dc9d244 100644
--- a/hid-apple.c
+++ b/hid-apple.c
@@ -114,6 +114,15 @@ struct apple_magic_backlight {
u16 saved_brightness;
};

+static u16 apple_saved_kbd_backlight_brightness;
+
+static u16 apple_initial_kbd_backlight_brightness(u16 max_brightness)
+{
+ if (apple_saved_kbd_backlight_brightness)
+ return apple_saved_kbd_backlight_brightness;
+ return max_t(u16, max_brightness / 2, 1);
+}
+
struct apple_sc {
struct hid_device *hdev;
unsigned long quirks;
@@ -123,6 +132,7 @@ struct apple_sc {
struct timer_list battery_timer;
struct apple_sc_backlight *backlight;
struct apple_magic_backlight *magic_backlight;
+ bool suspend_preparing_remove;
};

struct apple_key_translation {
@@ -833,8 +843,10 @@ static int apple_backlight_led_set(struct led_classdev *led_cdev,
int ret;

ret = apple_backlight_set(backlight->hdev, brightness, 0);
- if (!ret)
+ if (!ret) {
backlight->current_brightness = brightness;
+ apple_saved_kbd_backlight_brightness = brightness;
+ }
return ret;
}

@@ -878,12 +890,19 @@ static int apple_backlight_init(struct hid_device *hdev)
asc->backlight->cdev.brightness_set_blocking = apple_backlight_led_set;
asc->backlight->current_brightness = 0;
asc->backlight->saved_brightness = 0;
+ asc->backlight->cdev.brightness = 0;
+
+ asc->backlight->cdev.brightness =
+ apple_initial_kbd_backlight_brightness(rep->backlight_on_max);

- ret = apple_backlight_set(hdev, 0, 0);
+ ret = apple_backlight_set(hdev, asc->backlight->cdev.brightness, 0);
if (ret < 0) {
hid_err(hdev, "backlight set request failed: %d\n", ret);
goto cleanup_and_exit;
}
+ asc->backlight->current_brightness = asc->backlight->cdev.brightness;
+ asc->backlight->saved_brightness = asc->backlight->current_brightness;
+ apple_saved_kbd_backlight_brightness = asc->backlight->current_brightness;

ret = devm_led_classdev_register(&hdev->dev, &asc->backlight->cdev);

@@ -917,6 +936,7 @@ static int apple_magic_backlight_led_set(struct led_classdev *led_cdev,

apple_magic_backlight_set(backlight, brightness, 1);
backlight->current_brightness = brightness;
+ apple_saved_kbd_backlight_brightness = brightness;
return 0;
}

@@ -950,9 +970,16 @@ static int apple_magic_backlight_init(struct hid_device *hdev)
backlight->cdev.brightness_set_blocking = apple_magic_backlight_led_set;
backlight->current_brightness = 0;
backlight->saved_brightness = 0;
+ backlight->cdev.brightness = 0;
asc->magic_backlight = backlight;

- apple_magic_backlight_set(backlight, 0, 0);
+ backlight->cdev.brightness =
+ apple_initial_kbd_backlight_brightness(backlight->cdev.max_brightness);
+ backlight->current_brightness = backlight->cdev.brightness;
+ backlight->saved_brightness = backlight->current_brightness;
+ apple_saved_kbd_backlight_brightness = backlight->current_brightness;
+
+ apple_magic_backlight_set(backlight, backlight->current_brightness, 0);

return devm_led_classdev_register(&hdev->dev, &backlight->cdev);

@@ -1028,6 +1055,17 @@ static void apple_remove(struct hid_device *hdev)
if (asc->quirks & APPLE_RDESC_BATTERY)
timer_delete_sync(&asc->battery_timer);

+ /* Only tear down LEDs on suspend-driven remove. */
+ if (asc->suspend_preparing_remove && asc->backlight) {
+ devm_led_classdev_unregister(&hdev->dev, &asc->backlight->cdev);
+ asc->backlight = NULL;
+ }
+
+ if (asc->suspend_preparing_remove && asc->magic_backlight) {
+ devm_led_classdev_unregister(&hdev->dev, &asc->magic_backlight->cdev);
+ asc->magic_backlight = NULL;
+ }
+
hid_hw_stop(hdev);
}

@@ -1036,14 +1074,18 @@ static int apple_suspend(struct hid_device *hdev, pm_message_t msg)
{
struct apple_sc *asc = hid_get_drvdata(hdev);

+ asc->suspend_preparing_remove = true;
+
if (asc->backlight) {
asc->backlight->saved_brightness = asc->backlight->current_brightness;
+ apple_saved_kbd_backlight_brightness = asc->backlight->current_brightness;
apple_backlight_set(hdev, 0, 0);
asc->backlight->current_brightness = 0;
}

if (asc->magic_backlight) {
asc->magic_backlight->saved_brightness = asc->magic_backlight->current_brightness;
+ apple_saved_kbd_backlight_brightness = asc->magic_backlight->current_brightness;
apple_magic_backlight_set(asc->magic_backlight, 0, 0);
asc->magic_backlight->current_brightness = 0;
}
@@ -1056,6 +1098,8 @@ static int apple_resume(struct hid_device *hdev)
struct apple_sc *asc = hid_get_drvdata(hdev);
int ret = 0;

+ asc->suspend_preparing_remove = false;
+
if (asc->backlight && asc->backlight->saved_brightness) {
ret = apple_backlight_set(hdev, asc->backlight->saved_brightness, 0);
if (!ret)
diff --git a/hid-appletb-bl.c b/hid-appletb-bl.c
index bad2aea..5fc5a00 100644
--- a/hid-appletb-bl.c
+++ b/hid-appletb-bl.c
@@ -36,6 +36,7 @@ struct appletb_bl {
struct backlight_device *bdev;

bool full_on;
+ bool suspend_preparing_remove;
};

static const u8 appletb_bl_brightness_map[] = {
@@ -44,6 +45,15 @@ static const u8 appletb_bl_brightness_map[] = {
APPLETB_BL_ON,
};

+static int appletb_bl_default_brightness_index(void)
+{
+ if (appletb_bl_def_brightness < 0)
+ return 0;
+ if (appletb_bl_def_brightness >= ARRAY_SIZE(appletb_bl_brightness_map))
+ return ARRAY_SIZE(appletb_bl_brightness_map) - 1;
+ return appletb_bl_def_brightness;
+}
+
static int appletb_bl_set_brightness(struct appletb_bl *bl, u8 brightness)
{
struct hid_report *report = bl->brightness_field->report;
@@ -142,7 +152,7 @@ static int appletb_bl_probe(struct hid_device *hdev, const struct hid_device_id
bl->brightness_field = brightness_field;

ret = appletb_bl_set_brightness(bl,
- appletb_bl_brightness_map[(appletb_bl_def_brightness > 2) ? 2 : appletb_bl_def_brightness]);
+ appletb_bl_brightness_map[appletb_bl_default_brightness_index()]);

if (ret) {
dev_err_probe(dev, ret, "Failed to set default touch bar brightness to %d\n",
@@ -177,12 +187,39 @@ static void appletb_bl_remove(struct hid_device *hdev)
{
struct appletb_bl *bl = hid_get_drvdata(hdev);

- appletb_bl_set_brightness(bl, APPLETB_BL_OFF);
+ /* Only tear down the backlight on suspend-driven remove. */
+ if (bl && bl->suspend_preparing_remove)
+ appletb_bl_set_brightness(bl, APPLETB_BL_OFF);
+
+ if (bl && bl->suspend_preparing_remove && bl->bdev) {
+ devm_backlight_device_unregister(&hdev->dev, bl->bdev);
+ bl->bdev = NULL;
+ }

hid_hw_close(hdev);
hid_hw_stop(hdev);
}

+static int appletb_bl_suspend(struct hid_device *hdev, pm_message_t msg)
+{
+ struct appletb_bl *bl = hid_get_drvdata(hdev);
+
+ if (bl)
+ bl->suspend_preparing_remove = true;
+
+ return 0;
+}
+
+static int appletb_bl_resume(struct hid_device *hdev)
+{
+ struct appletb_bl *bl = hid_get_drvdata(hdev);
+
+ if (bl)
+ bl->suspend_preparing_remove = false;
+
+ return 0;
+}
+
static const struct hid_device_id appletb_bl_hid_ids[] = {
/* MacBook Pro's 2018, 2019, with T2 chip: iBridge DFR Brightness */
{ HID_USB_DEVICE(USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_TOUCHBAR_BACKLIGHT) },
@@ -195,6 +232,9 @@ static struct hid_driver appletb_bl_hid_driver = {
.id_table = appletb_bl_hid_ids,
.probe = appletb_bl_probe,
.remove = appletb_bl_remove,
+ .suspend = pm_ptr(appletb_bl_suspend),
+ .resume = pm_ptr(appletb_bl_resume),
+ .reset_resume = pm_ptr(appletb_bl_resume),
};
module_hid_driver(appletb_bl_hid_driver);

diff --git a/hid-appletb-kbd.c b/hid-appletb-kbd.c
index 0fdc096..63238ab 100644
--- a/hid-appletb-kbd.c
+++ b/hid-appletb-kbd.c
@@ -65,10 +65,25 @@ struct appletb_kbd {
struct timer_list inactivity_timer;
bool has_dimmed;
bool has_turned_off;
+ bool suspend_preparing_remove;
+ bool fn_down;
+ unsigned long last_mode_jiffies;
u8 saved_mode;
u8 current_mode;
};

+#define APPLETB_MODE_SUBMIT_INTERVAL_MS 50
+
+static void appletb_kbd_reset_state(struct appletb_kbd *kbd)
+{
+ kbd->saved_mode = APPLETB_KBD_MODE_OFF;
+ kbd->current_mode = APPLETB_KBD_MODE_OFF;
+ kbd->has_dimmed = false;
+ kbd->has_turned_off = false;
+ kbd->fn_down = false;
+ kbd->last_mode_jiffies = 0;
+}
+
static const struct key_entry appletb_kbd_keymap[] = {
{ KE_KEY, KEY_ESC, { KEY_ESC } },
{ KE_KEY, KEY_F1, { KEY_BRIGHTNESSDOWN } },
@@ -92,6 +107,18 @@ static int appletb_kbd_set_mode(struct appletb_kbd *kbd, u8 mode)
struct hid_device *hdev = report->device;
int ret;

+ if (kbd->suspend_preparing_remove && mode != APPLETB_KBD_MODE_OFF)
+ return -EBUSY;
+
+ if (kbd->current_mode == mode)
+ return 0;
+
+ if (kbd->last_mode_jiffies &&
+ time_before(jiffies, kbd->last_mode_jiffies +
+ msecs_to_jiffies(APPLETB_MODE_SUBMIT_INTERVAL_MS))) {
+ return -EAGAIN;
+ }
+
ret = hid_hw_power(hdev, PM_HINT_FULLON);
if (ret) {
hid_err(hdev, "Device didn't resume (%pe)\n", ERR_PTR(ret));
@@ -105,8 +132,8 @@ static int appletb_kbd_set_mode(struct appletb_kbd *kbd, u8 mode)
}

hid_hw_request(hdev, report, HID_REQ_SET_REPORT);
-
kbd->current_mode = mode;
+ kbd->last_mode_jiffies = jiffies;

power_normal:
hid_hw_power(hdev, PM_HINT_NORMAL);
@@ -114,6 +141,22 @@ power_normal:
return ret;
}

+static void appletb_kbd_teardown(struct hid_device *hdev, struct appletb_kbd *kbd,
+ bool send_mode_off)
+{
+ if (send_mode_off)
+ appletb_kbd_set_mode(kbd, APPLETB_KBD_MODE_OFF);
+
+ input_unregister_handler(&kbd->inp_handler);
+ if (kbd->backlight_dev) {
+ put_device(&kbd->backlight_dev->dev);
+ kbd->backlight_dev = NULL;
+ timer_delete_sync(&kbd->inactivity_timer);
+ }
+
+ appletb_kbd_reset_state(kbd);
+}
+
static ssize_t mode_show(struct device *dev,
struct device_attribute *attr, char *buf)
{
@@ -238,18 +281,35 @@ 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 (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);
- } else if (value == 0) {
- if (kbd->saved_mode != kbd->current_mode)
- appletb_kbd_set_mode(kbd, kbd->saved_mode);
- }
+ if (type != EV_KEY || code != KEY_FN || !appletb_tb_fn_toggle)
+ return;
+
+ if (kbd->current_mode != APPLETB_KBD_MODE_SPCL &&
+ kbd->current_mode != APPLETB_KBD_MODE_FN)
+ return;
+
+ if (value == 2)
+ return;
+
+ if (value == 1) {
+ if (kbd->fn_down)
+ return;
+
+ kbd->fn_down = true;
+ 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);
+ return;
}
+
+ if (value != 0 || !kbd->fn_down)
+ return;
+
+ kbd->fn_down = false;
+ if (kbd->saved_mode != kbd->current_mode)
+ appletb_kbd_set_mode(kbd, kbd->saved_mode);
}

static int appletb_kbd_inp_connect(struct input_handler *handler,
@@ -392,6 +452,8 @@ static int appletb_kbd_probe(struct hid_device *hdev, const struct hid_device_id
return -ENOMEM;

kbd->mode_field = mode_field;
+ kbd->suspend_preparing_remove = false;
+ appletb_kbd_reset_state(kbd);

ret = hid_hw_start(hdev, HID_CONNECT_HIDINPUT);
if (ret)
@@ -405,7 +467,9 @@ static int appletb_kbd_probe(struct hid_device *hdev, const struct hid_device_id

kbd->backlight_dev = backlight_device_get_by_name("appletb_backlight");
if (!kbd->backlight_dev) {
- dev_err_probe(dev, -ENODEV, "Failed to get backlight device\n");
+ ret = dev_err_probe(dev, -EPROBE_DEFER,
+ "Backlight device not ready, deferring probe\n");
+ goto close_hw;
} else {
backlight_device_set_brightness(kbd->backlight_dev, 2);
timer_setup(&kbd->inactivity_timer, appletb_inactivity_timer, 0);
@@ -453,14 +517,14 @@ stop_hw:
static void appletb_kbd_remove(struct hid_device *hdev)
{
struct appletb_kbd *kbd = hid_get_drvdata(hdev);
+ bool send_mode_off = false;

- appletb_kbd_set_mode(kbd, APPLETB_KBD_MODE_OFF);
+ /* Only force MODE_OFF on suspend-driven remove. */
+ if (kbd)
+ send_mode_off = kbd->suspend_preparing_remove &&
+ kbd->current_mode != APPLETB_KBD_MODE_OFF;

- input_unregister_handler(&kbd->inp_handler);
- if (kbd->backlight_dev) {
- put_device(&kbd->backlight_dev->dev);
- timer_delete_sync(&kbd->inactivity_timer);
- }
+ appletb_kbd_teardown(hdev, kbd, send_mode_off);

hid_hw_close(hdev);
hid_hw_stop(hdev);
@@ -470,9 +534,7 @@ 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);
-
+ kbd->suspend_preparing_remove = true;
return 0;
}

@@ -480,8 +542,7 @@ 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);
-
+ kbd->suspend_preparing_remove = false;
return 0;
}

--
2.53.0