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
prev parent reply other threads:[~2026-04-03 13:14 UTC|newest]
Thread overview: 4+ 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]
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 a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox