From af173d245a768c23800a24d739932bb842db2b4f Mon Sep 17 00:00:00 2001 From: Tim Crawford Date: Thu, 5 Jun 2025 13:35:13 -0600 Subject: [PATCH] Add serw14 with hack The WMI interface is deprecated and does not work on serw14. Hack in a call to the underlying ECMD to fix it for now. Signed-off-by: Tim Crawford --- src/kb-led.c | 84 +++++++++++++++++++++++++++++++++++++++++++++++--- src/system76.c | 74 ++++++++++++++++++++++---------------------- 2 files changed, 116 insertions(+), 42 deletions(-) diff --git a/src/kb-led.c b/src/kb-led.c index 24d0ce8..684ef5b 100644 --- a/src/kb-led.c +++ b/src/kb-led.c @@ -72,11 +72,11 @@ static int kb_led_set(struct led_classdev *led_cdev, enum led_brightness value) return 0; } -static void kb_led_color_set(enum kb_led_region region, union kb_led_color color) +static void kb_led_color_set_wmi(enum kb_led_region region, union kb_led_color color) { u32 cmd; - pr_debug("kb_led_color_set %d %06X\n", (int)region, (int)color.rgb); + pr_debug("%s %d %06X\n", __func__, (int)region, (int)color.rgb); switch (region) { case KB_LED_REGION_LEFT: @@ -104,6 +104,71 @@ static void kb_led_color_set(enum kb_led_region region, union kb_led_color color } } +// HACK: Directly call ECMD to fix serw14 +static void kb_led_color_set(enum kb_led_region region, union kb_led_color color) +{ + struct acpi_object_list input; + union acpi_object obj; + acpi_handle handle; + acpi_status status; + u8 *buf; + + buf = (u8 *)kzalloc(8, GFP_KERNEL); + + pr_debug("%s %d %06X\n", __func__, (int)region, (int)color.rgb); + + buf[0] = 5; + buf[2] = 0xCA; + buf[4] = color.b; + buf[5] = color.r; + buf[6] = color.g; + + switch (region) { + case KB_LED_REGION_LEFT: + buf[3] = 0x03; + break; + case KB_LED_REGION_CENTER: + buf[3] = 0x04; + break; + case KB_LED_REGION_RIGHT: + buf[3] = 0x05; + break; + case KB_LED_REGION_EXTRA: + buf[3] = 0x0B; + break; + } + + obj.type = ACPI_TYPE_BUFFER; + obj.buffer.length = 8; + obj.buffer.pointer = buf; + + input.count = 1; + input.pointer = &obj; + + status = acpi_get_handle(NULL, (acpi_string)"\\_SB.PC00.LPCB.EC", &handle); + if (ACPI_FAILURE(status)) { + pr_err("%s failed to get handle: %x\n", __func__, status); + return; + } + + status = acpi_evaluate_object(handle, "ECMD", &input, NULL); + if (ACPI_FAILURE(status)) { + pr_err("%s failed to call EC_CMD: %x\n", __func__, status); + return; + } + + // Update lightbar to match keyboard color + buf[3] = 0x07; + status = acpi_evaluate_object(handle, "ECMD", &input, NULL); + if (ACPI_FAILURE(status)) { + pr_err("%s failed to call EC_CMD: %x\n", __func__, status); + return; + } + + kfree(buf); + kb_led_regions[region] = color; +} + static struct led_classdev kb_led = { .name = "system76::kbd_backlight", .flags = LED_BRIGHT_HW_CHANGED, @@ -129,7 +194,10 @@ static ssize_t kb_led_color_store(enum kb_led_region region, const char *buf, si } color.rgb = (u32)val; - kb_led_color_set(region, color); + if (driver_flags & DRIVER_KB_LED_WMI) + kb_led_color_set_wmi(region, color); + else + kb_led_color_set(region, color); return size; } @@ -243,7 +311,10 @@ static void kb_led_resume(void) // Reset current color for (region = 0; region < sizeof(kb_led_regions)/sizeof(union kb_led_color); region++) { - kb_led_color_set(region, kb_led_regions[region]); + if (driver_flags & DRIVER_KB_LED_WMI) + kb_led_color_set_wmi(region, kb_led_regions[region]); + else + kb_led_color_set(region, kb_led_regions[region]); } // Reset current brightness @@ -355,7 +426,10 @@ static void kb_wmi_color(void) } for (region = 0; region < sizeof(kb_led_regions)/sizeof(union kb_led_color); region++) { - kb_led_color_set(region, kb_led_colors[kb_led_colors_i]); + if (driver_flags & DRIVER_KB_LED_WMI) + kb_led_color_set_wmi(region, kb_led_colors[kb_led_colors_i]); + else + kb_led_color_set(region, kb_led_colors[kb_led_colors_i]); } led_classdev_notify_brightness_hw_changed(&kb_led, kb_led_brightness); diff --git a/src/system76.c b/src/system76.c index 1814300..ed10262 100644 --- a/src/system76.c +++ b/src/system76.c @@ -51,12 +51,13 @@ /* method IDs for S76_GET */ #define GET_EVENT 0x01 /* 1 */ -#define DRIVER_AP_KEY (1 << 0) -#define DRIVER_AP_LED (1 << 1) -#define DRIVER_HWMON (1 << 2) -#define DRIVER_KB_LED (1 << 3) -#define DRIVER_OLED (1 << 4) -#define DRIVER_AP_WMI (1 << 5) +#define DRIVER_AP_KEY (1 << 0) +#define DRIVER_AP_LED (1 << 1) +#define DRIVER_HWMON (1 << 2) +#define DRIVER_KB_LED_WMI (1 << 3) +#define DRIVER_OLED (1 << 4) +#define DRIVER_AP_WMI (1 << 5) +#define DRIVER_KB_LED (1 << 6) #define DRIVER_INPUT (DRIVER_AP_KEY | DRIVER_OLED) @@ -130,17 +131,17 @@ static void s76_wmi_notify(u32 value, void *context) switch (event) { case 0x81: - if (driver_flags & DRIVER_KB_LED) { + if (driver_flags & (DRIVER_KB_LED_WMI | DRIVER_KB_LED)) { kb_wmi_dec(); } break; case 0x82: - if (driver_flags & DRIVER_KB_LED) { + if (driver_flags & (DRIVER_KB_LED_WMI | DRIVER_KB_LED)) { kb_wmi_inc(); } break; case 0x83: - if (driver_flags & DRIVER_KB_LED) { + if (driver_flags & (DRIVER_KB_LED_WMI | DRIVER_KB_LED)) { kb_wmi_color(); } break; @@ -151,7 +152,7 @@ static void s76_wmi_notify(u32 value, void *context) //TODO: Fn+ESC break; case 0x9F: - if (driver_flags & DRIVER_KB_LED) { + if (driver_flags & (DRIVER_KB_LED_WMI | DRIVER_KB_LED)) { kb_wmi_toggle(); } break; @@ -189,7 +190,7 @@ static int __init s76_probe(struct platform_device *dev) } } - if (driver_flags & DRIVER_KB_LED) { + if (driver_flags & (DRIVER_KB_LED_WMI | DRIVER_KB_LED)) { err = kb_led_init(&dev->dev); if (unlikely(err)) { pr_err("Could not register LED device\n"); @@ -248,7 +249,7 @@ static int s76_remove(struct platform_device *dev) if (driver_flags & DRIVER_INPUT) { s76_input_exit(); } - if (driver_flags & DRIVER_KB_LED) { + if (driver_flags & (DRIVER_KB_LED_WMI | DRIVER_KB_LED)) { kb_led_exit(); } if (driver_flags & DRIVER_AP_LED) { @@ -264,7 +265,7 @@ static int s76_suspend(struct platform_device *dev, pm_message_t status) { pr_debug("s76_suspend\n"); - if (driver_flags & DRIVER_KB_LED) { + if (driver_flags & (DRIVER_KB_LED_WMI | DRIVER_KB_LED)) { kb_led_suspend(); } @@ -280,7 +281,7 @@ static int s76_resume(struct platform_device *dev) if (driver_flags & DRIVER_AP_LED) { ap_led_resume(); } - if (driver_flags & DRIVER_KB_LED) { + if (driver_flags & (DRIVER_KB_LED_WMI | DRIVER_KB_LED)) { kb_led_resume(); } @@ -334,36 +335,36 @@ static int __init s76_dmi_matched(const struct dmi_system_id *id) } static struct dmi_system_id s76_dmi_table[] __initdata = { - DMI_TABLE_LEGACY("bonw13", DRIVER_HWMON | DRIVER_KB_LED), - DMI_TABLE("addw1", DRIVER_AP_LED | DRIVER_KB_LED | DRIVER_OLED), - DMI_TABLE("addw2", DRIVER_AP_LED | DRIVER_KB_LED | DRIVER_OLED), - DMI_TABLE("bonw15-b", DRIVER_HWMON | DRIVER_KB_LED), - DMI_TABLE("bonw16", DRIVER_HWMON | DRIVER_KB_LED), - DMI_TABLE("darp5", DRIVER_AP_LED | DRIVER_HWMON | DRIVER_KB_LED), - DMI_TABLE("darp6", DRIVER_AP_LED | DRIVER_HWMON | DRIVER_KB_LED), + DMI_TABLE_LEGACY("bonw13", DRIVER_HWMON | DRIVER_KB_LED_WMI), + DMI_TABLE("addw1", DRIVER_AP_LED | DRIVER_KB_LED_WMI | DRIVER_OLED), + DMI_TABLE("addw2", DRIVER_AP_LED | DRIVER_KB_LED_WMI | DRIVER_OLED), + DMI_TABLE("bonw15-b", DRIVER_HWMON | DRIVER_KB_LED_WMI), + DMI_TABLE("bonw16", DRIVER_HWMON | DRIVER_KB_LED_WMI), + DMI_TABLE("darp5", DRIVER_AP_LED | DRIVER_HWMON | DRIVER_KB_LED_WMI), + DMI_TABLE("darp6", DRIVER_AP_LED | DRIVER_HWMON | DRIVER_KB_LED_WMI), DMI_TABLE("galp2", DRIVER_HWMON), DMI_TABLE("galp3", DRIVER_HWMON), DMI_TABLE("galp3-b", DRIVER_AP_KEY | DRIVER_AP_LED | DRIVER_HWMON), DMI_TABLE("galp3-c", DRIVER_AP_LED | DRIVER_HWMON), DMI_TABLE("galp4", DRIVER_AP_LED | DRIVER_HWMON), DMI_TABLE("gaze13", DRIVER_AP_KEY | DRIVER_AP_LED | DRIVER_HWMON), - DMI_TABLE("gaze14", DRIVER_AP_LED | DRIVER_KB_LED), - DMI_TABLE("gaze15", DRIVER_AP_LED | DRIVER_KB_LED), + DMI_TABLE("gaze14", DRIVER_AP_LED | DRIVER_KB_LED_WMI), + DMI_TABLE("gaze15", DRIVER_AP_LED | DRIVER_KB_LED_WMI), DMI_TABLE("kudu5", DRIVER_AP_KEY | DRIVER_AP_LED | DRIVER_HWMON), - DMI_TABLE("kudu6", DRIVER_AP_KEY | DRIVER_AP_WMI | DRIVER_KB_LED), - DMI_TABLE("oryp3-jeremy", DRIVER_AP_KEY | DRIVER_AP_LED | DRIVER_HWMON | DRIVER_KB_LED), - DMI_TABLE("oryp4", DRIVER_AP_KEY | DRIVER_AP_LED | DRIVER_HWMON | DRIVER_KB_LED), - DMI_TABLE("oryp4-b", DRIVER_AP_KEY | DRIVER_AP_LED | DRIVER_HWMON | DRIVER_KB_LED), - DMI_TABLE("oryp5", DRIVER_AP_LED | DRIVER_HWMON | DRIVER_KB_LED), - DMI_TABLE("oryp6", DRIVER_AP_LED | DRIVER_KB_LED), - DMI_TABLE("pang10", DRIVER_AP_KEY | DRIVER_AP_WMI | DRIVER_KB_LED), - DMI_TABLE("pang11", DRIVER_AP_KEY | DRIVER_AP_WMI | DRIVER_KB_LED), - DMI_TABLE("serw11", DRIVER_AP_KEY | DRIVER_AP_LED | DRIVER_HWMON | DRIVER_KB_LED), - DMI_TABLE("serw11-b", DRIVER_AP_KEY | DRIVER_AP_LED | DRIVER_HWMON | DRIVER_KB_LED), - DMI_TABLE("serw12", DRIVER_AP_KEY | DRIVER_AP_LED | DRIVER_AP_WMI | DRIVER_KB_LED), + DMI_TABLE("kudu6", DRIVER_AP_KEY | DRIVER_AP_WMI | DRIVER_KB_LED_WMI), + DMI_TABLE("oryp3-jeremy", DRIVER_AP_KEY | DRIVER_AP_LED | DRIVER_HWMON | DRIVER_KB_LED_WMI), + DMI_TABLE("oryp4", DRIVER_AP_KEY | DRIVER_AP_LED | DRIVER_HWMON | DRIVER_KB_LED_WMI), + DMI_TABLE("oryp4-b", DRIVER_AP_KEY | DRIVER_AP_LED | DRIVER_HWMON | DRIVER_KB_LED_WMI), + DMI_TABLE("oryp5", DRIVER_AP_LED | DRIVER_HWMON | DRIVER_KB_LED_WMI), + DMI_TABLE("oryp6", DRIVER_AP_LED | DRIVER_KB_LED_WMI), + DMI_TABLE("pang10", DRIVER_AP_KEY | DRIVER_AP_WMI | DRIVER_KB_LED_WMI), + DMI_TABLE("pang11", DRIVER_AP_KEY | DRIVER_AP_WMI | DRIVER_KB_LED_WMI), + DMI_TABLE("serw11", DRIVER_AP_KEY | DRIVER_AP_LED | DRIVER_HWMON | DRIVER_KB_LED_WMI), + DMI_TABLE("serw11-b", DRIVER_AP_KEY | DRIVER_AP_LED | DRIVER_HWMON | DRIVER_KB_LED_WMI), + DMI_TABLE("serw12", DRIVER_AP_KEY | DRIVER_AP_LED | DRIVER_AP_WMI | DRIVER_KB_LED_WMI), + DMI_TABLE("serw14", DRIVER_HWMON | DRIVER_KB_LED), {} }; - MODULE_DEVICE_TABLE(dmi, s76_dmi_table); static int __init s76_init(void) @@ -397,14 +398,13 @@ static int __init s76_init(void) return 0; } +module_init(s76_init); static void __exit s76_exit(void) { platform_device_unregister(s76_platform_device); platform_driver_unregister(&s76_platform_driver); } - -module_init(s76_init); module_exit(s76_exit); MODULE_AUTHOR("Jeremy Soller ");