public inbox for linux-input@vger.kernel.org
 help / color / mirror / Atom feed
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


      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