[hp-wmi] Keyboard lighting

This commit is contained in:
Norbert Bolanowski 2023-08-26 14:37:32 +02:00 committed by naoto
parent 4dc14eb6f9
commit db43bc3cf6

View file

@ -27,6 +27,7 @@
#include <linux/rfkill.h> #include <linux/rfkill.h>
#include <linux/string.h> #include <linux/string.h>
#include <linux/dmi.h> #include <linux/dmi.h>
#include <linux/leds.h>
MODULE_AUTHOR("Matthew Garrett <mjg59@srcf.ucam.org>"); MODULE_AUTHOR("Matthew Garrett <mjg59@srcf.ucam.org>");
MODULE_DESCRIPTION("HP laptop WMI hotkeys driver"); MODULE_DESCRIPTION("HP laptop WMI hotkeys driver");
@ -143,6 +144,7 @@ enum hp_wmi_command {
HPWMI_WRITE = 0x02, HPWMI_WRITE = 0x02,
HPWMI_ODM = 0x03, HPWMI_ODM = 0x03,
HPWMI_GM = 0x20008, HPWMI_GM = 0x20008,
HPWMI_KB = 0x20009,
}; };
enum hp_wmi_hardware_mask { enum hp_wmi_hardware_mask {
@ -274,6 +276,9 @@ static const char * const tablet_chassis_types[] = {
#define DEVICE_MODE_TABLET 0x06 #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 */ /* map output size to the corresponding WMI method id */
static inline int encode_outsize_for_pvsz(int outsize) static inline int encode_outsize_for_pvsz(int outsize)
{ {
@ -781,12 +786,56 @@ static int camera_shutter_input_setup(void)
return err; 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(display);
static DEVICE_ATTR_RO(hddtemp); static DEVICE_ATTR_RO(hddtemp);
static DEVICE_ATTR_RW(als); static DEVICE_ATTR_RW(als);
static DEVICE_ATTR_RO(dock); static DEVICE_ATTR_RO(dock);
static DEVICE_ATTR_RO(tablet); static DEVICE_ATTR_RO(tablet);
static DEVICE_ATTR_RW(postcode); static DEVICE_ATTR_RW(postcode);
static DEVICE_ATTR_RW(zone_colors);
static struct attribute *hp_wmi_attrs[] = { static struct attribute *hp_wmi_attrs[] = {
&dev_attr_display.attr, &dev_attr_display.attr,
@ -799,6 +848,12 @@ static struct attribute *hp_wmi_attrs[] = {
}; };
ATTRIBUTE_GROUPS(hp_wmi); 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) static void hp_wmi_notify(u32 value, void *context)
{ {
struct acpi_buffer response = { ACPI_ALLOCATE_BUFFER, NULL }; 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: case HPWMI_PROXIMITY_SENSOR:
break; break;
case HPWMI_BACKLIT_KB_BRIGHTNESS: 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; break;
case HPWMI_PEAKSHIFT_PERIOD: case HPWMI_PEAKSHIFT_PERIOD:
break; break;
@ -1448,6 +1507,60 @@ static int thermal_profile_setup(void)
static int hp_wmi_hwmon_init(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) static int __init hp_wmi_bios_setup(struct platform_device *device)
{ {
int err; int err;
@ -1475,6 +1588,9 @@ static int __init hp_wmi_bios_setup(struct platform_device *device)
thermal_profile_setup(); thermal_profile_setup();
if (is_omen_lighting_supported())
omen_backlight_init(&device->dev);
return 0; return 0;
} }