From db43bc3cf69b389edf63fa39cee1bb293a9d67fe Mon Sep 17 00:00:00 2001 From: Norbert Bolanowski Date: Sat, 26 Aug 2023 14:37:32 +0200 Subject: [PATCH] [hp-wmi] Keyboard lighting --- hp-wmi/hp-wmi.c | 116 ++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 116 insertions(+) diff --git a/hp-wmi/hp-wmi.c b/hp-wmi/hp-wmi.c index e76e545..4012fa8 100644 --- a/hp-wmi/hp-wmi.c +++ b/hp-wmi/hp-wmi.c @@ -27,6 +27,7 @@ #include #include #include +#include MODULE_AUTHOR("Matthew Garrett "); MODULE_DESCRIPTION("HP laptop WMI hotkeys driver"); @@ -143,6 +144,7 @@ enum hp_wmi_command { HPWMI_WRITE = 0x02, HPWMI_ODM = 0x03, HPWMI_GM = 0x20008, + HPWMI_KB = 0x20009, }; enum hp_wmi_hardware_mask { @@ -274,6 +276,9 @@ static const char * const tablet_chassis_types[] = { #define DEVICE_MODE_TABLET 0x06 +#define OMEN_ZONE_COLOR_OFFSET 0x19 +#define OMEN_ZONE_COLOR_LEN 0x0c + /* map output size to the corresponding WMI method id */ static inline int encode_outsize_for_pvsz(int outsize) { @@ -781,12 +786,56 @@ static int camera_shutter_input_setup(void) return err; } +static ssize_t zone_colors_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + u8 val[128]; + + int ret = hp_wmi_perform_query(HPWMI_HDDTEMP_QUERY, HPWMI_KB, &val, + zero_if_sup(val), sizeof(val)); + + if (ret) + return ret; + + memcpy(buf, &val[OMEN_ZONE_COLOR_OFFSET], OMEN_ZONE_COLOR_LEN); + + return OMEN_ZONE_COLOR_LEN; +} + +static ssize_t zone_colors_store(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t count) +{ + u8 val[128]; + int ret; + + ret = hp_wmi_perform_query(HPWMI_HDDTEMP_QUERY, HPWMI_KB, &val, + zero_if_sup(val), sizeof(val)); + + if (ret) + return ret; + + if (count != OMEN_ZONE_COLOR_LEN) + return -1; + + memcpy(&val[OMEN_ZONE_COLOR_OFFSET], buf, count); + + ret = hp_wmi_perform_query(HPWMI_ALS_QUERY, HPWMI_KB, &val, sizeof(val), + 0); + + if (ret) + return ret; + + return OMEN_ZONE_COLOR_LEN; +} + static DEVICE_ATTR_RO(display); static DEVICE_ATTR_RO(hddtemp); static DEVICE_ATTR_RW(als); static DEVICE_ATTR_RO(dock); static DEVICE_ATTR_RO(tablet); static DEVICE_ATTR_RW(postcode); +static DEVICE_ATTR_RW(zone_colors); static struct attribute *hp_wmi_attrs[] = { &dev_attr_display.attr, @@ -799,6 +848,12 @@ static struct attribute *hp_wmi_attrs[] = { }; ATTRIBUTE_GROUPS(hp_wmi); +static struct attribute *omen_kbd_led_attrs[] = { + &dev_attr_zone_colors.attr, + NULL, +}; +ATTRIBUTE_GROUPS(omen_kbd_led); + static void hp_wmi_notify(u32 value, void *context) { struct acpi_buffer response = { ACPI_ALLOCATE_BUFFER, NULL }; @@ -910,6 +965,10 @@ static void hp_wmi_notify(u32 value, void *context) case HPWMI_PROXIMITY_SENSOR: break; case HPWMI_BACKLIT_KB_BRIGHTNESS: + input_report_key(hp_wmi_input_dev, KEY_KBDILLUMTOGGLE, true); + input_sync(hp_wmi_input_dev); + input_report_key(hp_wmi_input_dev, KEY_KBDILLUMTOGGLE, false); + input_sync(hp_wmi_input_dev); break; case HPWMI_PEAKSHIFT_PERIOD: break; @@ -1448,6 +1507,60 @@ static int thermal_profile_setup(void) static int hp_wmi_hwmon_init(void); +static enum led_brightness get_omen_backlight_brightness(struct led_classdev *cdev) +{ + u8 val; + + int ret = hp_wmi_perform_query(HPWMI_HARDWARE_QUERY, HPWMI_KB, &val, zero_if_sup(val), sizeof(val)); + + if (ret) + return ret; + + return (val & 0x80) ? LED_ON : LED_OFF; +} + +static void set_omen_backlight_brightness(struct led_classdev *cdev, enum led_brightness value) +{ + char buffer[4] = { (value == LED_OFF) ? 0x64 : 0xe4, 0, 0, 0 }; + + hp_wmi_perform_query(HPWMI_WIRELESS_QUERY, HPWMI_KB, &buffer, + sizeof(buffer), 0); +} + +static struct led_classdev omen_kbd_led = { + .name = "hp_omen::kbd_backlight", + .brightness_set = set_omen_backlight_brightness, + .brightness_get = get_omen_backlight_brightness, + .max_brightness = 1, + .groups = omen_kbd_led_groups, +}; + +static bool is_omen_lighting_supported(void) +{ + u8 val; + + int ret = hp_wmi_perform_query(HPWMI_DISPLAY_QUERY, HPWMI_KB, &val, zero_if_sup(val), sizeof(val)); + + if (ret) + return false; + + return (val & 1) == 1; +} + +static int omen_backlight_init(struct device *dev) +{ + int ret; + + input_set_capability(hp_wmi_input_dev, KE_KEY, KEY_KBDILLUMTOGGLE); + + ret = devm_led_classdev_register(dev, &omen_kbd_led); + + if (ret < 0) + return -1; + + return 0; +} + static int __init hp_wmi_bios_setup(struct platform_device *device) { int err; @@ -1475,6 +1588,9 @@ static int __init hp_wmi_bios_setup(struct platform_device *device) thermal_profile_setup(); + if (is_omen_lighting_supported()) + omen_backlight_init(&device->dev); + return 0; }