From: deqrocks <andre@negmaster.com>
To: jikos@kernel.org, benjamin.tissoires@redhat.com
Cc: linux-input@vger.kernel.org
Subject: [PATCH 3/3] Add Touch Bar and backlight reprobe support
Date: Fri, 3 Apr 2026 15:06:20 +0200 [thread overview]
Message-ID: <20260403130620.91999-4-andre@negmaster.com> (raw)
In-Reply-To: <20260403130620.91999-1-andre@negmaster.com>
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
next prev parent reply other threads:[~2026-04-03 13:14 UTC|newest]
Thread overview: 5+ messages / expand[flat|nested] mbox.gz Atom feed top
2026-04-03 13:06 [PATCH 0/3] HID: apple: reinitialize T2 HID devices after resume deqrocks
2026-04-03 13:06 ` [PATCH 1/3] Add Apple T2 HID identifiers deqrocks
2026-04-03 13:06 ` [PATCH 2/3] hid-apple: add pm path to 8102 deqrocks
2026-04-03 13:06 ` deqrocks [this message]
2026-04-08 22:24 ` [PATCH 0/3] HID: apple: reinitialize T2 HID devices after resume Jiri Kosina
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=20260403130620.91999-4-andre@negmaster.com \
--to=andre@negmaster.com \
--cc=benjamin.tissoires@redhat.com \
--cc=jikos@kernel.org \
--cc=linux-input@vger.kernel.org \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.