flukejones/asusctl

UX8406 (Zenbook Duo 2024) support

hacker1024 opened this issue · 25 comments

I'm creating this umbrella issue to discuss all features UX8406-related.

To start, here's a list of known issues:

Keyboard

  • The keyboard hotkeys do not work (USB and Bluetooth)
  • The keyboard backlight does not work (USB and Bluetooth)
  • Attaching or detaching the keyboard results in an unfiltered RFKILL keypress (internal USB only)

Display

  • The secondary display remains active when the keyboard is attached (ideally, this would be integrated into i915/KMS rather than work at a desktop environment level)
  • An ASUS ScreenPad brightness control is erroneously available
  • PSR causes gamma flickering (though this happens in Windows as well)

Power

  • The power profiles driver does not work - nothing shows up in sysfs or power-profiles-daemon
  • The battery charge limit does not persist when the system is powered off. It does when set from Windows.

Here's my keyboard work so far (from my post on a NixOS forum thread)

Alright, with a lot of experimentation I've got the keyboard hotkeys working over Bluetooth. Here's what I've discovered so far:

  1. ASUS keyboards send hotkey events over custom HID reports. The ASUS HID driver turns these custom reports into standard keypresses, and is mostly compatible with this laptop too.
  2. The relevant HID descriptor is slightly different from what the driver expects, and so it needs to be modified. Luckily, a similar thing has already been done for previous detachable ASUS keyboards. Unfortunately, the logic is not exactly the same (the byte layout is a little different) - but it's close enough, so I used it for a working prototype. The kernel can handle the slightly mangled resultant descriptor.
  3. The ASUS HID driver will prefer to use WMI for backlight control if it is available. This is not great for our usecase (I don't think it works with Bluetooth or even USB - it's probably there for legacy reasons), so this behaviour needs to be changed. There's also no reason that the backlight should be disabled if the keyboard is plugged in to another ASUS laptop, either.
  4. The keyboard actually shows up as two HID devices: One for the keyboard itself, and one for the touchpad. Oddly, the function key by itself actually does generate HID reports - but on the touchpad device, for some reason. I don't think any driver currently takes advantage of this.
  5. Over USB, the relevant hotkey HID report descriptors are missing entirely, which is why I haven't been able to get it working. There's also some sort of third HID device, but I can't seem to generate any events from it. The function key reports over the touchpad are still available, though, so that might be an easy way to get similar functionality.
  6. I have been unable to get the backlight working in any capacity. This requires more investigation.

I think my next plan of action is to look at the USB HID descriptors and run some packet capture in Windows to see how it compares to Linux.

CC @mfenniak as you were working on this too - you were right about the initialization step, but luckily hid_asus can already do it.

Here's my prototype kernel patch. Note that for development, I have been using CCache along with the technique to patch in-tree modules to keep things nice and fast (not that anything is particularly slow on this thing anyway!).

diff --git a/drivers/hid/hid-asus.c b/drivers/hid/hid-asus.c
index 78cdfb8b9a7a..0450ad7424a8 100644
--- a/drivers/hid/hid-asus.c
+++ b/drivers/hid/hid-asus.c
@@ -84,6 +84,7 @@ MODULE_DESCRIPTION("Asus HID Keyboard and TouchPad");
 #define QUIRK_MEDION_E1239T		BIT(10)
 #define QUIRK_ROG_NKEY_KEYBOARD		BIT(11)
 #define QUIRK_ROG_CLAYMORE_II_KEYBOARD BIT(12)
+#define QUIRK_ZENBOOK_DUO_REMOVABLE_KEYBOARD	BIT(13)
 
 #define I2C_KEYBOARD_QUIRKS			(QUIRK_FIX_NOTEBOOK_REPORT | \
 						 QUIRK_NO_INIT_REPORTS | \
@@ -835,7 +836,7 @@ static int asus_input_configured(struct hid_device *hdev, struct hid_input *hi)
 	drvdata->input = input;
 
 	if (drvdata->enable_backlight &&
-	    !asus_kbd_wmi_led_control_present(hdev) &&
+	    (!asus_kbd_wmi_led_control_present(hdev) || (drvdata->quirks & QUIRK_ZENBOOK_DUO_REMOVABLE_KEYBOARD)) &&
 	    asus_kbd_register_leds(hdev))
 		hid_warn(hdev, "Failed to initialize backlight.\n");
 
@@ -897,7 +898,9 @@ static int asus_input_mapping(struct hid_device *hdev,
 		case 0xb3: asus_map_key_clear(KEY_PROG3);	break; /* Fn+Left next aura */
 		case 0x6a: asus_map_key_clear(KEY_F13);		break; /* Screenpad toggle */
 		case 0x4b: asus_map_key_clear(KEY_F14);		break; /* Arrows/Pg-Up/Dn toggle */
-
+		case 0x9c: asus_map_key_clear(KEY_F15);     break; /* Screen swap */
+		case 0x7e: asus_map_key_clear(KEY_F16);     break; /* Emoji panel */
+		case 0x86: asus_map_key_clear(KEY_F17);     break; /* MyASUS */
 
 		default:
 			/* ASUS lazily declares 256 usages, ignore the rest,
@@ -1183,17 +1186,20 @@ static __u8 *asus_report_fixup(struct hid_device *hdev, __u8 *rdesc,
 		hid_info(hdev, "Fixing up Asus T100 keyb report descriptor\n");
 		rdesc[74] &= ~HID_MAIN_ITEM_CONSTANT;
 	}
-	/* For the T100CHI/T90CHI keyboard dock */
-	if (drvdata->quirks & (QUIRK_T100CHI | QUIRK_T90CHI)) {
+	/* For the T100CHI/T90CHI keyboard dock and Zenbook Duo 2024+ keyboards */
+	if (drvdata->quirks & (QUIRK_T100CHI | QUIRK_T90CHI | QUIRK_ZENBOOK_DUO_REMOVABLE_KEYBOARD)) {
 		int rsize_orig;
 		int offs;
 
 		if (drvdata->quirks & QUIRK_T100CHI) {
 			rsize_orig = 403;
 			offs = 388;
-		} else {
+		} else if (drvdata->quirks & QUIRK_T90CHI) {
 			rsize_orig = 306;
 			offs = 291;
+		} else if (drvdata->quirks & QUIRK_ZENBOOK_DUO_REMOVABLE_KEYBOARD) {
+			rsize_orig = 257;
+			offs = 176;
 		}
 
 		/*
@@ -1298,6 +1304,12 @@ static const struct hid_device_id asus_devices[] = {
 	 */
 	{ HID_DEVICE(BUS_USB, HID_GROUP_GENERIC,
 		USB_VENDOR_ID_ASUSTEK, USB_DEVICE_ID_ASUSTEK_T101HA_KEYBOARD) },
+	{ HID_DEVICE(BUS_USB, HID_GROUP_GENERIC,
+	    USB_VENDOR_ID_ASUSTEK, USB_DEVICE_ID_ASUSTEK_ZENBOOK_DUO_UX8406_KEYBOARD),
+		QUIRK_USE_KBD_BACKLIGHT | QUIRK_ZENBOOK_DUO_REMOVABLE_KEYBOARD },
+	{ HID_DEVICE(BUS_BLUETOOTH, HID_GROUP_GENERIC,
+	    USB_VENDOR_ID_ASUSTEK, BT_DEVICE_ID_ASUSTEK_ZENBOOK_DUO_UX8406_KEYBOARD),
+	  	QUIRK_USE_KBD_BACKLIGHT | QUIRK_ZENBOOK_DUO_REMOVABLE_KEYBOARD },
 	{ }
 };
 MODULE_DEVICE_TABLE(hid, asus_devices);
diff --git a/drivers/hid/hid-ids.h b/drivers/hid/hid-ids.h
index 828a5c022c64..8d9a3e29f28f 100644
--- a/drivers/hid/hid-ids.h
+++ b/drivers/hid/hid-ids.h
@@ -208,6 +208,8 @@
 #define USB_DEVICE_ID_ASUSTEK_ROG_NKEY_KEYBOARD	0x1866
 #define USB_DEVICE_ID_ASUSTEK_ROG_NKEY_KEYBOARD2	0x19b6
 #define USB_DEVICE_ID_ASUSTEK_ROG_NKEY_KEYBOARD3	0x1a30
+#define USB_DEVICE_ID_ASUSTEK_ZENBOOK_DUO_UX8406_KEYBOARD	0x1b2c
+#define BT_DEVICE_ID_ASUSTEK_ZENBOOK_DUO_UX8406_KEYBOARD    0x1b2d
 #define USB_DEVICE_ID_ASUSTEK_ROG_CLAYMORE_II_KEYBOARD	0x196b
 #define USB_DEVICE_ID_ASUSTEK_FX503VD_KEYBOARD	0x1869
 

Excellent work! I applied this patch to my kernel though and didn't experience working hotkeys over bluetooth... in fact no keys were working.

I checked the logs and it seems that when connecting the bluetooth keyboard, the hid data failed to parse...

[  366.197243] usb 1-6: USB disconnect, device number 3
[  368.015051] asus 0005:0B05:1B2D.000C: Fixing up T90CHI keyb report descriptor
[  368.015194] asus 0005:0B05:1B2D.000C: item fetching failed at offset 257/258
[  368.015207] asus 0005:0B05:1B2D.000C: Asus hid parse failed: -22
[  368.015212] asus: probe of 0005:0B05:1B2D.000C failed with error -22
[  368.082864] input: ASUS Zenbook Duo Keyboard Mouse as /devices/virtual/misc/uhid/0005:0B05:1B2D.000B/input/input51
[  368.082971] input: ASUS Zenbook Duo Keyboard Touchpad as /devices/virtual/misc/uhid/0005:0B05:1B2D.000B/input/input52
[  368.083037] hid-multitouch 0005:0B05:1B2D.000B: input,hidraw0: BLUETOOTH HID v1.23 Mouse [ASUS Zenbook Duo Keyboard] on 08:8e:90:80:82:f1

This seems related to the tweak you've made to asus_report_fixup, but obviously not the same outcome... any thoughts on what could be different?

Strange. I just tried applying the patch directly myself, and the keyboard with hotkeys worked fine, but the touchpad wasn't showing up...

Are you able to upload the contents of all files matching /sys/bus/hid/devices/*:0B05:1B2D.*/report_descriptor while the keyboard is connected over Bluetooth? I suspect there may be differing firmware or hardware revisions.

On the topic of power profiles, here's a patch that should get them working. The firmware API seems to match the Vivobook Pro 16X 2023 (K6604).

I'm actually pretty happy with this, so I guess it's time to learn how to send a kernel patch upstream 😅 .

Click to expand
From 2f8f8e6bb2da47c677a07f76f141c527ddd5d51c Mon Sep 17 00:00:00 2001
From: hacker1024 <hacker1024@users.sourceforge.net>
Date: Fri, 12 Apr 2024 13:59:24 +1000
Subject: [PATCH] asus-wmi: Add additional DEVID_THROTTLE_THERMAL_POLICY

---
 drivers/platform/x86/asus-wmi.c            | 102 +++++++++++++++------
 include/linux/platform_data/x86/asus-wmi.h |   1 +
 2 files changed, 76 insertions(+), 27 deletions(-)

diff --git a/drivers/platform/x86/asus-wmi.c b/drivers/platform/x86/asus-wmi.c
index 18be35fdb381..c7958b31dae0 100644
--- a/drivers/platform/x86/asus-wmi.c
+++ b/drivers/platform/x86/asus-wmi.c
@@ -93,9 +93,13 @@ module_param(fnlock_default, bool, 0444);
 #define ASUS_FAN_BOOST_MODE_SILENT_MASK                0x02
 #define ASUS_FAN_BOOST_MODES_MASK              0x03
 
+#define ASUS_THROTTLE_THERMAL_POLICY_COUNT 3
 #define ASUS_THROTTLE_THERMAL_POLICY_DEFAULT   0
 #define ASUS_THROTTLE_THERMAL_POLICY_OVERBOOST 1
 #define ASUS_THROTTLE_THERMAL_POLICY_SILENT    2
+#define ASUS_THROTTLE_THERMAL_POLICY_LITE_DEFAULT 0
+#define ASUS_THROTTLE_THERMAL_POLICY_LITE_OVERBOOST 2
+#define ASUS_THROTTLE_THERMAL_POLICY_LITE_SILENT 1
 
 #define USB_INTEL_XUSB2PR              0xD0
 #define PCI_DEVICE_ID_INTEL_LYNXPOINT_LP_XHCI  0x9c31
@@ -282,6 +286,7 @@ struct asus_wmi {
        bool kbd_rgb_state_available;
 
        bool throttle_thermal_policy_available;
+       bool throttle_thermal_policy_lite;
        u8 throttle_thermal_policy_mode;
 
        bool cpu_fan_curve_available;
@@ -3404,6 +3409,14 @@ static int throttle_thermal_policy_check_present(struct asus_wmi *asus)
        err = asus_wmi_get_devstate(asus,
                                    ASUS_WMI_DEVID_THROTTLE_THERMAL_POLICY,
                                    &result);
+       if (err == -ENODEV) {
+               err = asus_wmi_get_devstate(asus,
+                                               ASUS_WMI_DEVID_THROTTLE_THERMAL_POLICY_LITE,
+                                               &result);
+               asus->throttle_thermal_policy_lite = true;
+       } else {
+               asus->throttle_thermal_policy_lite = false;
+       }
        if (err) {
                if (err == -ENODEV)
                        return 0;
@@ -3424,7 +3437,10 @@ static int throttle_thermal_policy_write(struct asus_wmi *asus)
 
        value = asus->throttle_thermal_policy_mode;
 
-       err = asus_wmi_set_devstate(ASUS_WMI_DEVID_THROTTLE_THERMAL_POLICY,
+       err = asus_wmi_set_devstate(
+                                       asus->throttle_thermal_policy_lite
+                                               ? ASUS_WMI_DEVID_THROTTLE_THERMAL_POLICY_LITE
+                                               : ASUS_WMI_DEVID_THROTTLE_THERMAL_POLICY,
                                    value, &retval);
 
        sysfs_notify(&asus->platform_device->dev.kobj, NULL,
@@ -3466,7 +3482,7 @@ static int throttle_thermal_policy_switch_next(struct asus_wmi *asus)
        u8 new_mode = asus->throttle_thermal_policy_mode + 1;
        int err;
 
-       if (new_mode > ASUS_THROTTLE_THERMAL_POLICY_SILENT)
+       if (new_mode > ASUS_THROTTLE_THERMAL_POLICY_COUNT - 1)
                new_mode = ASUS_THROTTLE_THERMAL_POLICY_DEFAULT;
 
        asus->throttle_thermal_policy_mode = new_mode;
@@ -3505,7 +3521,7 @@ static ssize_t throttle_thermal_policy_store(struct device *dev,
        if (result < 0)
                return result;
 
-       if (new_mode > ASUS_THROTTLE_THERMAL_POLICY_SILENT)
+       if (new_mode > ASUS_THROTTLE_THERMAL_POLICY_COUNT - 1)
                return -EINVAL;
 
        asus->throttle_thermal_policy_mode = new_mode;
@@ -3536,18 +3552,34 @@ static int asus_wmi_platform_profile_get(struct platform_profile_handler *pprof,
 
        tp = asus->throttle_thermal_policy_mode;
 
-       switch (tp) {
-       case ASUS_THROTTLE_THERMAL_POLICY_DEFAULT:
-               *profile = PLATFORM_PROFILE_BALANCED;
-               break;
-       case ASUS_THROTTLE_THERMAL_POLICY_OVERBOOST:
-               *profile = PLATFORM_PROFILE_PERFORMANCE;
-               break;
-       case ASUS_THROTTLE_THERMAL_POLICY_SILENT:
-               *profile = PLATFORM_PROFILE_QUIET;
-               break;
-       default:
-               return -EINVAL;
+       if (!asus->throttle_thermal_policy_lite) {
+               switch (tp) {
+               case ASUS_THROTTLE_THERMAL_POLICY_DEFAULT:
+                       *profile = PLATFORM_PROFILE_BALANCED;
+                       break;
+               case ASUS_THROTTLE_THERMAL_POLICY_OVERBOOST:
+                       *profile = PLATFORM_PROFILE_PERFORMANCE;
+                       break;
+               case ASUS_THROTTLE_THERMAL_POLICY_SILENT:
+                       *profile = PLATFORM_PROFILE_QUIET;
+                       break;
+               default:
+                       return -EINVAL;
+               }
+       } else {
+               switch (tp) {
+               case ASUS_THROTTLE_THERMAL_POLICY_LITE_DEFAULT:
+                       *profile = PLATFORM_PROFILE_BALANCED;
+                       break;
+               case ASUS_THROTTLE_THERMAL_POLICY_LITE_OVERBOOST:
+                       *profile = PLATFORM_PROFILE_PERFORMANCE;
+                       break;
+               case ASUS_THROTTLE_THERMAL_POLICY_LITE_SILENT:
+                       *profile = PLATFORM_PROFILE_QUIET;
+                       break;
+               default:
+                       return -EINVAL;
+               }
        }
 
        return 0;
@@ -3561,18 +3593,34 @@ static int asus_wmi_platform_profile_set(struct platform_profile_handler *pprof,
 
        asus = container_of(pprof, struct asus_wmi, platform_profile_handler);
 
-       switch (profile) {
-       case PLATFORM_PROFILE_PERFORMANCE:
-               tp = ASUS_THROTTLE_THERMAL_POLICY_OVERBOOST;
-               break;
-       case PLATFORM_PROFILE_BALANCED:
-               tp = ASUS_THROTTLE_THERMAL_POLICY_DEFAULT;
-               break;
-       case PLATFORM_PROFILE_QUIET:
-               tp = ASUS_THROTTLE_THERMAL_POLICY_SILENT;
-               break;
-       default:
-               return -EOPNOTSUPP;
+       if (!asus->throttle_thermal_policy_lite) {
+               switch (profile) {
+               case PLATFORM_PROFILE_PERFORMANCE:
+                       tp = ASUS_THROTTLE_THERMAL_POLICY_OVERBOOST;
+                       break;
+               case PLATFORM_PROFILE_BALANCED:
+                       tp = ASUS_THROTTLE_THERMAL_POLICY_DEFAULT;
+                       break;
+               case PLATFORM_PROFILE_QUIET:
+                       tp = ASUS_THROTTLE_THERMAL_POLICY_SILENT;
+                       break;
+               default:
+                       return -EOPNOTSUPP;
+               }
+       } else {
+               switch (profile) {
+               case PLATFORM_PROFILE_PERFORMANCE:
+                       tp = ASUS_THROTTLE_THERMAL_POLICY_LITE_OVERBOOST;
+                       break;
+               case PLATFORM_PROFILE_BALANCED:
+                       tp = ASUS_THROTTLE_THERMAL_POLICY_LITE_DEFAULT;
+                       break;
+               case PLATFORM_PROFILE_QUIET:
+                       tp = ASUS_THROTTLE_THERMAL_POLICY_LITE_SILENT;
+                       break;
+               default:
+                       return -EOPNOTSUPP;
+               }
        }
 
        asus->throttle_thermal_policy_mode = tp;
diff --git a/include/linux/platform_data/x86/asus-wmi.h b/include/linux/platform_data/x86/asus-wmi.h
index ab1c7deff118..ddf5b3766cd2 100644
--- a/include/linux/platform_data/x86/asus-wmi.h
+++ b/include/linux/platform_data/x86/asus-wmi.h
@@ -64,6 +64,7 @@
 #define ASUS_WMI_DEVID_SCREENPAD_LIGHT 0x00050032
 #define ASUS_WMI_DEVID_FAN_BOOST_MODE  0x00110018
 #define ASUS_WMI_DEVID_THROTTLE_THERMAL_POLICY 0x00120075
+#define ASUS_WMI_DEVID_THROTTLE_THERMAL_POLICY_LITE 0x00110019
 
 /* Misc */
 #define ASUS_WMI_DEVID_PANEL_OD                0x00050019
-- 
2.44.0

Of course, if you don't want to bother with a kernel patch, you can do it over the CLI - 0x0 for standard, 0x1 for silent, and 0x2 for performance.

# echo 0x00110019 > /sys/kernel/debug/asus-nb-wmi/dev_id
# echo 0x0 > /sys/kernel/debug/asus-nb-wmi/ctrl_param
# cat /sys/kernel/debug/asus-nb-wmi/devs

Interestingly, there's another ACPI endpoint that relates to the fan. It seems to set the Intel Dynamic Platform and Thermal Framework OEM variable 3 and call an EC function based on the given argument (0-3) and fan setting.

Search for 0x0006006C in the DSDT to find this endpoint. Without any information on ASUS's DPTF and EC implementations, though, it's very hard to make any sense of this - I might look into MyASUS to see how and when it uses this endpoint, but that won't be very easy either.

Has anyone more experienced with ACPI, DPTF or ECs seen anything like this before?

Following this! Thanks a lot for your work so far, @hacker1024

For point 3 I have had to make a patch for the new G14 and G16 also. This might also work here:

I was about to submit this. If you test with extra DMI match, please let me know.

For point 3 I have had to make a patch for the new G14 and G16 also. This might also work here:

I was about to submit this. If you test with extra DMI match, please let me know.

Sweet! I'll take a look at this tonight, thanks.

Please don't quote an entire patch. It makes it hard to read the post train.

Updated patch:

From 441d0ade8b01cc39c13a8c29f15e588c19754d54 Mon Sep 17 00:00:00 2001
From: "Luke D. Jones" <luke@ljones.dev>
Date: Mon, 13 May 2024 19:20:04 +1200
Subject: [PATCH] hid-asus: use hid for brightness control on keyboard

Signed-off-by: Luke D. Jones <luke@ljones.dev>
---
 drivers/hid/hid-asus.c                     | 18 +++++++++++++-
 drivers/platform/x86/asus-wmi.c            |  3 ++-
 include/linux/platform_data/x86/asus-wmi.h | 28 ++++++++++++++++++++++
 3 files changed, 47 insertions(+), 2 deletions(-)

diff --git a/drivers/hid/hid-asus.c b/drivers/hid/hid-asus.c
index 02de2bf4f790..7dca9fe156e9 100644
--- a/drivers/hid/hid-asus.c
+++ b/drivers/hid/hid-asus.c
@@ -101,6 +101,7 @@ struct asus_kbd_leds {
 	unsigned int brightness;
 	spinlock_t lock;
 	bool removed;
+	int report_id;
 };
 
 struct asus_touchpad_info {
@@ -473,7 +474,7 @@ static enum led_brightness asus_kbd_backlight_get(struct led_classdev *led_cdev)
 static void asus_kbd_backlight_work(struct work_struct *work)
 {
 	struct asus_kbd_leds *led = container_of(work, struct asus_kbd_leds, work);
-	u8 buf[] = { FEATURE_KBD_REPORT_ID, 0xba, 0xc5, 0xc4, 0x00 };
+	u8 buf[] = { led->report_id, 0xba, 0xc5, 0xc4, 0x00 };
 	int ret;
 	unsigned long flags;
 
@@ -492,12 +493,18 @@ static void asus_kbd_backlight_work(struct work_struct *work)
  */
 static bool asus_kbd_wmi_led_control_present(struct hid_device *hdev)
 {
+	struct asus_drvdata *drvdata = hid_get_drvdata(hdev);
 	u32 value;
 	int ret;
 
 	if (!IS_ENABLED(CONFIG_ASUS_WMI))
 		return false;
 
+	if (drvdata->quirks & QUIRK_ROG_NKEY_KEYBOARD && asus_use_hidraw_led()) {
+		hid_info(hdev, "using hidraw for asus::kbd_backlight\n");
+		return false;
+	}
+
 	ret = asus_wmi_evaluate_method(ASUS_WMI_METHODID_DSTS,
 				       ASUS_WMI_DEVID_KBD_BACKLIGHT, 0, &value);
 	hid_dbg(hdev, "WMI backlight check: rc %d value %x", ret, value);
@@ -507,6 +514,11 @@ static bool asus_kbd_wmi_led_control_present(struct hid_device *hdev)
 	return !!(value & ASUS_WMI_DSTS_PRESENCE_BIT);
 }
 
+static bool asus_kbd_is_input_led(void) {
+	return dmi_match(DMI_PRODUCT_NAME, "GU605")
+		|| dmi_match(DMI_PRODUCT_NAME, "GA403");
+}
+
 static int asus_kbd_register_leds(struct hid_device *hdev)
 {
 	struct asus_drvdata *drvdata = hid_get_drvdata(hdev);
@@ -549,6 +561,10 @@ static int asus_kbd_register_leds(struct hid_device *hdev)
 	if (!drvdata->kbd_backlight)
 		return -ENOMEM;
 
+	drvdata->kbd_backlight->report_id = FEATURE_KBD_REPORT_ID;
+	if (drvdata->quirks & QUIRK_ROG_NKEY_KEYBOARD && asus_kbd_is_input_led())
+		drvdata->kbd_backlight->report_id = FEATURE_KBD_LED_REPORT_ID1;
+
 	drvdata->kbd_backlight->removed = false;
 	drvdata->kbd_backlight->brightness = 0;
 	drvdata->kbd_backlight->hdev = hdev;
diff --git a/drivers/platform/x86/asus-wmi.c b/drivers/platform/x86/asus-wmi.c
index 3f9b6285c9a6..a58df18a70ad 100644
--- a/drivers/platform/x86/asus-wmi.c
+++ b/drivers/platform/x86/asus-wmi.c
@@ -1681,7 +1681,8 @@ static int asus_wmi_led_init(struct asus_wmi *asus)
 			goto error;
 	}
 
-	if (!kbd_led_read(asus, &led_val, NULL)) {
+	if (!kbd_led_read(asus, &led_val, NULL) && !asus_use_hidraw_led()) {
+		pr_info("using asus-wmi for asus::kbd_backlight\n");
 		asus->kbd_led_wk = led_val;
 		asus->kbd_led.name = "asus::kbd_backlight";
 		asus->kbd_led.flags = LED_BRIGHT_HW_CHANGED;
diff --git a/include/linux/platform_data/x86/asus-wmi.h b/include/linux/platform_data/x86/asus-wmi.h
index 3eb5cd6773ad..8c0ebb660299 100644
--- a/include/linux/platform_data/x86/asus-wmi.h
+++ b/include/linux/platform_data/x86/asus-wmi.h
@@ -160,4 +160,32 @@ static inline int asus_wmi_evaluate_method(u32 method_id, u32 arg0, u32 arg1,
 }
 #endif
 
+/* To be used by both hid-asus and asus-wmi to determine which controls kbd_brightness */
+#if IS_REACHABLE(CONFIG_ASUS_WMI)
+static bool asus_use_hidraw_led(void) {
+   	const char *product, *board;
+
+	product = dmi_get_system_info(DMI_PRODUCT_NAME);
+	if (!product)
+		return true;
+
+	if (strcmp(product, "ROG Zephyrus")
+		|| strcmp(product, "ROG Strix")
+		|| strcmp(product, "ROG Flow")
+		|| strcmp(product, "GA403")
+		|| strcmp(product, "GU605"))
+		return false;
+
+	board = dmi_get_system_info(DMI_PRODUCT_NAME);
+	if (!board)
+		return true;
+
+	return strcmp(board, "RC71L");
+}
+#else
+static inline bool asus_use_hidraw_led(void) {
+	return true;
+}
+#endif
+
 #endif	/* __PLATFORM_DATA_X86_ASUS_WMI_H */
-- 
2.45.0

Hi @flukejones,
I'm sorry for the delay. Some unexpected things came up, and I may not be able to look at this for another week or so still. Feel free to submit the patch as-is - it can always be expanded later, right?

As noted on the nix thread, the kernel 6.9.x series and beyond break the bottom screen on the Zenbook Duo. It also looks like this will not be fixed in the upcoming 6.10.x updates.
The following patch has been confirmed to work: https://gist.github.com/mfenniak/53a8cae856c66ec1f0a100703aab3690
What is the recommended way to make these patches work? Specifically on Fedora atomic?

The correct fix is to submit that patch upstream.

The correct fix is to submit that patch upstream.

Not so simple, I'm afraid: That patch is just a revert of a well-intentioned commit that happens to break the second screen. We'll have to wait for a true fix from Intel, and patch the kernel ourselves in the meantime.

Well that is unfortunate. Has anyone actually filed a bug upstream for it, or notified the people who commited the work?

For those like me who are not comfortable building/compiling their own kernel:
Fortunately sentry the legend (maintainer of fsync kernel) has just incorporated the patch 53a8cae856c66ec1f0a100703aab3690 by mfenniak
and it's already available.
For even more added convenience the ublue team has just prepared a new kernel cache repo. Will report back here once I test it out and confirm that it works on my device. You can also just leverage the Fsync kernel copr directly if you know how.

Well that is unfortunate. Has anyone actually filed a bug upstream for it, or notified the people who commited the work?

Yes -- I have both filed a bug upstream (https://gitlab.freedesktop.org/drm/i915/kernel/-/issues/11488) and reached out to the author Haridhar.

On the topic of power profiles, here's a patch that should get them working ...

The patch doesn't apply for linuxPackages_6_9:

applying patch /nix/store/qb9757a421isfvxii2isnv131g8b7sk0-thermal-policy-2f8f8e6bb2da47c677a07f76f141c527ddd5d51c.patch
patching file drivers/platform/x86/asus-wmi.c
Hunk #1 FAILED at 93.
Hunk #2 FAILED at 282.
Hunk #3 FAILED at 3404.
Hunk #4 FAILED at 3424.
Hunk #5 FAILED at 3466.
Hunk #6 FAILED at 3505.
Hunk #7 FAILED at 3536.
Hunk #8 FAILED at 3561.
8 out of 8 hunks FAILED -- saving rejects to file drivers/platform/x86/asus-wmi.c.rej
patching file include/linux/platform_data/x86/asus-wmi.h
Hunk #1 succeeded at 64 with fuzz 2.

Context

    kernelPackages = pkgs.linuxPackages_6_9;
    kernelPatches = [
      # Fixes second display being permanently black.
      # Ref: https://gitlab.freedesktop.org/drm/i915/kernel/-/issues/11488
      {
        name = "zenbook-i915-revert-93cbc1ac";
        patch = ./zenbook-i915-revert-93cbc1accbcec2740231755774420934658e2b18.patch;
      }

      # Fixes thermal policy and keyboard backlight.
      # Ref: https://github.com/flukejones/asusctl/issues/25
      {
        name = "thermal-policy";
        patch = ./thermal-policy-2f8f8e6bb2da47c677a07f76f141c527ddd5d51c.patch;
      }
      {
        name = "keyboard-backlight";
        patch = ./keyboard-backlight-441d0ade8b01cc39c13a8c29f15e588c19754d54.patch;
      }
    ];
    blacklistedKernelModules = [
      "asus_nb_wmi" # Kills the Wi-Fi any time I connect the keyboard
    ];